您可以反过来:首先找到匹配的曲线,然后使用曲线上的点绘制线条.例如:
该图以下列方式获得:
假设您有三个起点{x0,0},{x1,y1},{x2,0}
然后你会发现在{x1,y1}处相交的两条抛物线曲线,其附加条件是在该点处具有最大值(用于平滑过渡).这些曲线是:
yLeft[x_] := a x^2 + b x + c;
yRight[x_] := d x^2 + e x + f;
Run Code Online (Sandbox Code Playgroud)
我们在哪里找到(经过一些微积分):
{c -> -((-x0^2 y1 + 2 x0 x1 y1)/(x0 - x1)^2),
a -> -(y1/(x0 - x1)^2),
b -> (2 x1 y1)/(-x0 + x1)^2}
Run Code Online (Sandbox Code Playgroud)
和
{f -> -((2 x1 x2 y1 - x2^2 y1)/(x1 - x2)^2),
d -> -(y1/(x1 - x2)^2),
e -> (2 x1 y1)/(x1 - x2)^2}
Run Code Online (Sandbox Code Playgroud)
所以我们有两条曲线.
现在您应该注意,如果您希望您的点间距相等,则x1/x2应该是一个有理数.并且您对步骤的选择是有限的.您可以选择从x0开始经过x1和x2的步骤.(形式为x1 /(n*x2))
就这样.现在你根据点{x,yLeft [x]}或{x,yRight [x]}形成你的线,这取决于你是x1的哪一边.
注意:您可以选择仅绘制一条通过三个点的抛物线,但在一般情况下会产生高度不对称.
如果点x1在中间,结果更好:
您可能需要自己编写代码。我认为您可以通过在代码中实现二次贝塞尔曲线函数来做到这一点,可以在此处找到该函数。您只需求解几个值即可决定增量的精细程度。如果你想要一条直线,只需求解 0 和 1 并用线连接这些点。如果您想要单角度示例,请求解 0、0.5 和 1,并按顺序连接这些点。如果您想要第三个示例,请求解 0、0.25、0.5、0.75 和 1。最好将其放入 for 循环中,如下所示:
float stepValue = (float)0.25;
float lastCalculatedValue;
for (float t = 0; t <= 1; t += stepValue)
{
// Solve the quadratic bezier function to get the point at t.
// If this is not the first point, connect it to the previous point with a line.
// Store the new value in lastCalculatedValue.
}
Run Code Online (Sandbox Code Playgroud)
编辑:实际上,看起来您希望线穿过控制点。如果是这种情况,您就不想使用二次贝塞尔曲线。相反,您可能需要拉格朗日曲线。该网站可能有助于解方程: http: //www.math.ucla.edu/~baker/java/hoefer/Lagrange.htm。但无论哪种情况,您都可以使用相同类型的循环来控制平滑程度。
第二次编辑:这似乎有效。只需将 numberOfSteps 成员更改为所需的线段总数,并适当设置点数组即可。顺便说一句,你可以使用三个以上的点。它只会分布它们之间的线段总数。但我初始化了数组,以便结果看起来像你的最后一个例子。
第三次编辑:我更新了代码,以便您可以在表单上左键单击以添加点,然后右键单击以删除最后一个点。另外,我在底部添加了 NumericUpDown,以便您可以在运行时更改段数。
public class Form1 : Form
{
private int numberOfSegments = 4;
private double[,] multipliers;
private List<Point> points;
private NumericUpDown numberOfSegmentsUpDown;
public Form1()
{
this.numberOfSegmentsUpDown = new NumericUpDown();
this.numberOfSegmentsUpDown.Value = this.numberOfSegments;
this.numberOfSegmentsUpDown.ValueChanged += new System.EventHandler(this.numberOfSegmentsUpDown_ValueChanged);
this.numberOfSegmentsUpDown.Dock = DockStyle.Bottom;
this.Controls.Add(this.numberOfSegmentsUpDown);
this.points = new List<Point> {
new Point(100, 110),
new Point(50, 60),
new Point(100, 10)};
this.PrecomputeMultipliers();
}
public void PrecomputeMultipliers()
{
this.multipliers = new double[this.points.Count, this.numberOfSegments + 1];
double pointCountMinusOne = (double)(this.points.Count - 1);
for (int currentStep = 0; currentStep <= this.numberOfSegments; currentStep++)
{
double t = currentStep / (double)this.numberOfSegments;
for (int pointIndex1 = 0; pointIndex1 < this.points.Count; pointIndex1++)
{
double point1Weight = pointIndex1 / pointCountMinusOne;
double currentMultiplier = 1;
for (int pointIndex2 = 0; pointIndex2 < this.points.Count; pointIndex2++)
{
if (pointIndex2 == pointIndex1)
continue;
double point2Weight = pointIndex2 / pointCountMinusOne;
currentMultiplier *= (t - point2Weight) / (point1Weight - point2Weight);
}
this.multipliers[pointIndex1, currentStep] = currentMultiplier;
}
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Point? previousPoint = null;
for (int currentStep = 0; currentStep <= numberOfSegments; currentStep++)
{
double sumX = 0;
double sumY = 0;
for (int pointIndex = 0; pointIndex < points.Count; pointIndex++)
{
sumX += points[pointIndex].X * multipliers[pointIndex, currentStep];
sumY += points[pointIndex].Y * multipliers[pointIndex, currentStep];
}
Point newPoint = new Point((int)Math.Round(sumX), (int)Math.Round(sumY));
if (previousPoint.HasValue)
e.Graphics.DrawLine(Pens.Black, previousPoint.Value, newPoint);
previousPoint = newPoint;
}
for (int pointIndex = 0; pointIndex < this.points.Count; pointIndex++)
{
Point point = this.points[pointIndex];
e.Graphics.FillRectangle(Brushes.Black, new Rectangle(point.X - 1, point.Y - 1, 2, 2));
}
}
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
if (e.Button == MouseButtons.Left)
{
this.points.Add(e.Location);
}
else
{
this.points.RemoveAt(this.points.Count - 1);
}
this.PrecomputeMultipliers();
this.Invalidate();
}
private void numberOfSegmentsUpDown_ValueChanged(object sender, EventArgs e)
{
this.numberOfSegments = (int)this.numberOfSegmentsUpDown.Value;
this.PrecomputeMultipliers();
this.Invalidate();
}
}
Run Code Online (Sandbox Code Playgroud)