【问题标题】:Iteratively smooth a curve迭代平滑曲线
【发布时间】:2010-11-08 02:21:00
【问题描述】:

我一整天都在尝试这样做。基本上,我有一条线和一个点。我希望线弯曲并通过该点,但我不想要平滑的曲线。我不能像这样定义曲线中的步数(当心粗略的 mspaint 绘图):

等等。我尝试了各种方法,比如从初始线的中心取角度,然后在角度引导的点处分割线,但长度有问题。我只取初始长度并除以我所处的步数,但这不太正确。

有人知道怎么做吗?

谢谢。

【问题讨论】:

  • 我无法公正地讨论这个话题,但是如果你查看贝塞尔曲线以及如何形成它们,你会得到很好的信息。您可以按照您的建议进行迭代,但有更好的方法来计算曲线。
  • 我希望能够控制曲线中的段数。
  • 曲线有无数个“段”。您选择评估的点数,然后在它们之间渲染线仍然取决于您。
  • 用你的线条近似什么类型的曲线有关系吗?例如,您可以精确定义一个经过三个点的圆,但您是否总是希望您的曲线是圆的一部分?如果没有,您可以使用无数其他曲线,每条曲线都会给您不同的结果,我认为迄今为止的任何答案都没有正确解决这个问题。

标签: c# algorithm geometry curve-fitting


【解决方案1】:

你可以反过来:首先找到一条匹配的曲线,然后使用曲线上的点来绘制线条。例如:

该图是通过以下方式获得的:

假设你有三个起点{x0,0},{x1,y1},{x2,0}

然后你会发现两条抛物线在 {x1,y1} 处相交,附加条件是在该点处具有最大值(用于平滑过渡)。这些曲线是:

 yLeft[x_] := a x^2 + b x + c; 
 yRight[x_] := d x^2 + e x + f;

我们在哪里找到(经过一些微积分):

   {c -> -((-x0^2 y1 + 2 x0 x1 y1)/(x0 - x1)^2), 
    a -> -(y1/(x0 - x1)^2), 
    b -> (2 x1 y1)/(-x0 + x1)^2}  

   {f -> -((2 x1 x2 y1 - x2^2 y1)/(x1 - x2)^2), 
    d -> -(y1/(x1 - x2)^2), 
    e -> (2 x1 y1)/(x1 - x2)^2}

所以我们有两条曲线。

现在你应该注意,如果你想让你的点等间距,x1/x2 应该是一个有理数。你的步数选择是有限的。您可以选择从 x0 开始时经过 x1 和 x2 的步数。 (格式为 x1/(n * x2))

仅此而已。现在你根据点 {x,yLeft[x]} 或 {x,yRight[x]} 形成你的线,这取决于你在 x1 的哪一边。

注意:您可以选择只绘制一条经过您的三个点的抛物线,但在一般情况下会导致高度不对称。

如果点x1在中间,结果会更好:

【讨论】:

    【解决方案2】:

    您可能需要自己编写代码。我认为您可以通过在代码中实现二次贝塞尔曲线函数来做到这一点,可以在 here 找到。您可以通过仅求解几个值来决定您希望增量的精细程度。如果你想要一条直线,只求解 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.
    }
    

    编辑:实际上,您似乎希望线通过您的控制点。如果是这种情况,您不想使用二次贝塞尔曲线。相反,您可能需要拉格朗日曲线。这个网站可能有助于解决这个问题: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();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-09
      • 1970-01-01
      • 1970-01-01
      • 2018-03-19
      • 2021-05-25
      • 2014-11-23
      • 1970-01-01
      相关资源
      最近更新 更多