2010-11-08 123 views
7

我一直試圖做這一整天。基本上,我有一條線和一個點。我想讓曲線通過那個點,但我不想要一條光滑的曲線。我不能定義曲線中的步數,就像這樣(小心粗糙mspaint繪圖): curve迭代平滑曲線

等等。我嘗試了各種各樣的東西,比如從初始線的中心取出角度,然後在角度位置處將線分開,但是我的長度有問題。我只是將最初的長度除以我所在的步數,但這並不完全正確。

任何人都知道一種方法來做到這一點?

謝謝。

+1

我不能這樣做了專題公正,但如果你看看貝塞爾曲線,以及如何你會得到良好的信息以形成它們。你可以按照你的建議進行迭代,但是有更好的方法來計算曲線。 – 2010-11-08 02:46:41

+0

我希望能夠控制曲線中的線段數量。 – 2010-11-08 02:54:30

+0

曲線具有無限數量的「分段」。您選擇評估並渲染線條的點數仍然取決於您。 – 2010-11-08 03:03:26

回答

4

你可能需要自己編碼。我認爲你可以通過在代碼中實現二次貝塞爾曲線函數來實現,可以找到here。你只需要解決一些數值就可以決定你想要的增量。如果你想要一條直線,只求解0和1,並用線連接這些點。如果你想要一個角度的例子,求解0,0.5和1,並按順序連接點。如果你希望你的第三個例子,解決0,0.25,0.5,0.75,1。它可能是最好把它放在一個循環是這樣的:

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。但無論哪種情況,您都可以使用相同類型的循環來控制平滑度。

2nd編輯:這似乎工作。只需將numberOfSteps成員更改爲所需線段的總數並適當設置點數組。順便說一下,你可以使用三點以上。它只會分配線段的總數。但是我初始化了數組,以便結果看起來像你最後一個例子。

第3編輯:我更新了一下代碼,所以你可以在表單上點擊左鍵添加點並右鍵點擊刪除最後一點。另外,我在底部添加了一個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(); 
    } 
} 
+0

非常感謝,完美的作品! – 2010-11-15 01:33:00

7

你可以去周圍的其他方法:先找到一個匹配的曲線,然後用積分曲線上畫出的線條。例如:

alt text

通過以下方式獲得該地塊:

假設你有三個出發點{x0,0},{X1,Y1},{2,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))

而就是這樣。現在,根據x1的哪一邊,根據點{x,yLeft [x]}或{x,yRight [x]}來形成線條。

注意:您可能選擇只繪製一條經過您的三點的拋物線,但在一般情況下會導致高度不對稱。

如果點X1是在中間,結果也比較好:

alt text

+0

謝謝你的回答! – 2010-11-15 01:33:36