Iteratively smooth a curve

I tried to do this all day. Basically, I have a line and a point. I want the line to be curved and go through this point, but I don't need a smooth curve. I would not be able to determine the number of steps on my curve, for example (be careful with the rough mspaint pattern): curve

And so on. I tried different things, for example, taking an angle from the center of the starting line, and then breaking the line at the point where the corner is, but I have a problem with the length. I would just take the initial length and divide it by the number of steps that I took, but this was not entirely correct.

Does anyone know how to do this?

Thanks.

+7
c # algorithm geometry curve-fitting
source share
2 answers

You will probably have to code this yourself. I think you could do this by implementing the quadratic function of the bezier curve in the code, which can be found here . You decide how good you want the increments, solving only a few values. If you need a straight line, only allow for 0 and 1 and connect these points with the lines. If you need an example with one angle, solve for 0, 0.5 and 1 and connect the points in order. If you want your third example, solve for 0, 0.25, 0.5, 0.75 and 1. It is probably best to put it in a for loop like this:

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: Actually, it looks like you want the line to go through a breakpoint. If so, you do not want to use a quadratic Bezier curve. Instead, you probably need a Lagrange curve. This site can help with the equation: http://www.math.ucla.edu/~baker/java/hoefer/Lagrange.htm . But in any case, you can use the same type of cycle to control the degree of smoothness.

2nd Edit: It seems to work. Just change the numberOfSteps member to the total number of line segments you want and set the array of points accordingly. By the way, you can use more than three points. It simply distributes the total number of line segments across them. But I initialized the array so that the result looks like your last example.

3rd Edit: I updated the code a bit so that you can left-click on the form to add points, and right-click to delete the last point. In addition, I added NumericUpDown at the bottom so you can change the number of segments at runtime.

 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(); } } 
+4
source share

You can go the other way around: first find the corresponding curve, and then use the points on the curve to draw lines. For example:

alt text

This graph was obtained as follows:

Suppose you have three starting points {x0,0}, {x1, y1}, {x2,0}

Then you will find two parabolic curves intersecting at {x1, y1}, with the additional condition of the presence of maxima at this point (for a smooth transition). These curves are:

  yLeft[x_] := ax^2 + bx + c; yRight[x_] := dx^2 + ex + f; 

Where do we find (after some calculus):

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

and

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

therefore we have our two curves.

Now you should notice that if you want your points to be evenly distributed, x1 / x2 should be a rational number. And your choice for steps is limited. You can choose the steps going through x1 and x2, starting at x0. (they have the form x1 / (n * x2))

And it's all. Now you form your lines according to the points {x, yLeft [x]} or {x, yRight [x]} depending on which side x1 you are on.

Note. You can choose only one parabolic curve running along your three points, but in general it will be very asymmetric.

If the point x1 is in the middle, the results will be more pleasant:

alt text

+7
source share

All Articles