2010-11-08 38 views
7

Tôi đã cố gắng để làm điều này cả ngày. Về cơ bản, tôi có một dòng và một điểm. Tôi muốn đường cong và đi qua điểm đó, nhưng tôi không muốn một đường cong trơn tru. Tôi không thể xác định số lượng các bước trong đường cong của tôi, giống như vậy (hãy xem bản vẽ mspaint thô): curveIteratively trơn tru một đường cong

Và cứ tiếp tục như vậy. Tôi đã thử những thứ khác nhau, giống như lấy góc từ tâm của đường thẳng ban đầu và sau đó tách dòng tại điểm mà góc dẫn, nhưng tôi có một vấn đề với độ dài. Tôi sẽ chỉ lấy chiều dài ban đầu và chia cho số bước tôi đã ở, nhưng điều đó không đúng.

Có ai biết cách làm điều đó không?

Cảm ơn.

+1

Tôi không thể làm công lý chủ đề, nhưng bạn sẽ nhận được thông tin tốt nếu bạn nhìn lên đường cong Bezier và làm thế nào để tạo thành chúng. Bạn có thể lặp lại như bạn đề xuất, nhưng có những phương pháp tốt hơn cho các đường cong tính toán. –

+0

Tôi muốn có thể kiểm soát số lượng phân đoạn trong đường cong. –

+0

Đường cong có số lượng 'phân đoạn' vô hạn. Số điểm bạn đã chọn để đánh giá và sau đó hiển thị các đường giữa vẫn tùy thuộc vào bạn. –

Trả lời

4

Có thể bạn sẽ cần phải tự mình viết mã này. Tôi nghĩ rằng bạn có thể làm điều đó bằng cách thực hiện một hàm đường cong bezier bậc hai trong mã, có thể được tìm thấy here. Bạn quyết định như thế nào tốt bạn muốn gia số bằng cách chỉ giải quyết cho một vài giá trị. Nếu bạn muốn một đường thẳng, chỉ giải quyết cho 0 và 1 và kết nối những điểm đó với các dòng. Nếu bạn muốn ví dụ một góc, hãy giải quyết cho 0, 0.5 và 1 và kết nối các điểm theo thứ tự. Nếu bạn muốn ví dụ thứ ba của bạn, giải quyết cho 0, 0,25, 0,5, 0,75, và 1. Đó có lẽ sẽ là tốt nhất để đặt nó trong một vòng lặp for như thế này:

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. 
} 

Edit: Trên thực tế, có vẻ như bạn muốn dòng đi qua điểm kiểm soát của bạn. Nếu đúng như vậy, bạn không muốn sử dụng đường cong bezier bậc hai. Thay vào đó, bạn có thể muốn có một đường cong Lagrange. Trang web này có thể giúp với phương trình: http://www.math.ucla.edu/~baker/java/hoefer/Lagrange.htm. Nhưng trong cả hai trường hợp, bạn có thể sử dụng cùng một loại vòng lặp để kiểm soát mức độ êm ái.

Chỉnh sửa lần 2: Điều này dường như hoạt động. Chỉ cần thay đổi thành viên numberOfSteps thành tổng số các đoạn thẳng mà bạn muốn và thiết lập mảng điểm một cách thích hợp. Nhân tiện, bạn có thể sử dụng nhiều hơn ba điểm. Nó sẽ chỉ phân phối tổng số phân đoạn dòng trên chúng. Nhưng tôi đã khởi tạo mảng để kết quả trông giống như ví dụ cuối cùng của bạn.

Chỉnh sửa thứ ba: Tôi đã cập nhật mã một chút để bạn có thể nhấp chuột trái vào biểu mẫu để thêm điểm và nhấp chuột phải để xóa điểm cuối cùng. Ngoài ra, tôi đã thêm một NumericUpDown ở phía dưới để bạn có thể thay đổi số lượng phân đoạn khi chạy.

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

Cảm ơn rất nhiều, hoạt động hoàn hảo! –

7

Bạn có thể đi theo con đường khác xung quanh: đầu tiên tìm thấy một đường cong phù hợp và sau đó sử dụng các điểm trên đường cong để vẽ các dòng. Ví dụ:

alt text

âm mưu này được thu thập theo cách sau:

Giả sử bạn có ba điểm khởi đầu {x0,0}, {x1, y1}, {x2,0}

Sau đó, bạn tìm thấy hai đường parabol giao nhau tại {x1, y1}, với điều kiện bổ sung là có cực đại tại điểm đó (để chuyển tiếp suôn sẻ).Những đường cong là:

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

đâu chúng ta thấy (sau khi một số giải tích):

{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} 

vì vậy chúng tôi có hai đường cong của chúng tôi.

Bây giờ bạn nên lưu ý rằng nếu bạn muốn các điểm của bạn được đặt cách đều nhau, x1/x2 phải là một số hợp lý và lựa chọn của bạn cho các bước bị giới hạn. Bạn có thể chọn các bước đi qua x1 AND x2 trong khi bắt đầu từ x0. (chúng có dạng x1/(n * x2))

Và đó là tất cả. Bây giờ bạn tạo thành các dòng của bạn theo các điểm {x, yLeft [x]} hoặc {x, yRight [x]} tùy thuộc vào phía nào của x1.

Lưu ý: Bạn có thể chọn chỉ vẽ một đường cong parabol đi qua ba điểm của bạn, nhưng nó sẽ cho kết quả rất không đối xứng trong trường hợp chung.

Nếu x1 Vấn đề là ở giữa, kết quả là đẹp hơn:

alt text

+0

Cảm ơn bạn đã trả lời! –

Các vấn đề liên quan