I need to convert between DrawClosedCurve and DrawBezier. For example, if I have a closed curve:
Point point1 = new Point(100, 100);
Point point2 = new Point(200, 50);
Point point3 = new Point(250, 200);
Point point4 = new Point(50, 150);
Point[] points = {point1, point2, point3, point4};
e.Graphics.DrawClosedCurve(pen, points, tension, FillMode);
I need to be able to define [four?] bezier curves that match this shape, i.e. given the closed curve above, I need to find two anchor points for each segment. For example, a bezier curve for the first segment:
e.Graphics.DrawBezier(pen, point1, anchor1, anchor2, point2)
Likewise, given bezier curves, I need to define a matching closed curve.
I mention DrawCurve because I don't understand why these two functions don't produce the same results:
e.Graphics.DrawClosedCurve(pen, points, tension, FillMode);
e.Graphics.DrawCurve(pen, {point1, point2, point3, point4, point1}, tension);
Edit- Or what would make the latter produce the same results.
I understand the bezier curve definition but I don't understand the cardinal cubic hermite spline definition which I assume is the spline curve used. I also don't understand the answer given here.
Let me answer your second question first.
I don't understand why these two functions don't produce the same results
The cardinal spline needs adjacent points to calcuate the tangents. Hence, you would need to add one more point at both sides:
e.Graphics.DrawCurve(pen, new PointF[] { point4, point1, point2, point3, point4, point1, point2}, tension);
Of course, this will also draw these additional segments. If you ignore those, the curves are exactly the same. Other than that, there is no way to make DrawCurve
and DrawClosedCurve
produce the same output:
In this figure, DrawCurve
uses a blue pen, DrawClosedCurve
uses a red pen. Apart from the additional two blue segments, the curves are equal.
Now for the Bezier curve:
Obviously, you need one Bezier curve for each segment between two points.
for(int i = 0; i < points.Length; ++i)
We will first calculate the according Hermite segment and then convert that one to a Bezier curve:
Let's first gather the relevant points. We need the two points that span the segment. And, again, we need the two adjacent points to calculate tangents:
var pPrev1 = points[(i - 1 + points.Length) % points.Length];
var p1 = points[i];
var p2 = points[(i + 1) % points.Length];
var pAfter2 = points[(i + 2) % points.Length];
Now we can calculate the tangents with the definition of a cardinal spline:
var t1 = new PointF(tension * (p2.X - pPrev1.X), tension * (p2.Y - pPrev1.Y));
var t2 = new PointF(tension * (pAfter2.X - p1.X), tension * (pAfter2.Y - p1.Y));
And finally convert to a Bezier curve. The endpoints remain unchanged. The interior control points need to reflect the end tangents, scaled by a factor of 1/3
(based on the relationship between Hermite and Bezier curves):
var c1 = new PointF(p1.X + t1.X / 3.0f, p1.Y + t1.Y / 3.0f);
var c2 = new PointF(p2.X - t2.X / 3.0f, p2.Y - t2.Y / 3.0f);
And finally draw the curve:
e.Graphics.DrawBezier(Pens.Purple, p1, c1, c2, p2);
Here is the full code:
for(int i = 0; i < points.Length; ++i)
{
// draw segment points[i] - points[(i + 1) % n]
var pPrev1 = points[(i - 1 + points.Length) % points.Length];
var p1 = points[i];
var p2 = points[(i + 1) % points.Length];
var pAfter2 = points[(i + 2) % points.Length];
// tangents
var t1 = new PointF(tension * (p2.X - pPrev1.X), tension * (p2.Y - pPrev1.Y));
var t2 = new PointF(tension * (pAfter2.X - p1.X), tension * (pAfter2.Y - p1.Y));
// interior Bezier control points
var c1 = new PointF(p1.X + t1.X / 3.0f, p1.Y + t1.Y / 3.0f);
var c2 = new PointF(p2.X - t2.X / 3.0f, p2.Y - t2.Y / 3.0f);
e.Graphics.DrawBezier(Pens.Purple, p1, c1, c2, p2);
}
The other way around is not always possible. Let's say we have our Bezier curves defined as a list of four control points each:
p1, c1+, c2-, p2
p2, c2+, c3-, p3
p3, c3+, c1-, p1
Obviously, the points passed into DrawClosedCurve
are the endpoints {p1, p2, p3}
. The only degree of freedom that we have is the tension parameter. The Bezier curves have many more degrees of freedom (all the interior control points with two dimensions each). So it might be possible that there is one unique tension parameter that fits all, but it is very unlikely. Calculating the tension parameter would require doing something very similar as above. You could set up an optimization problem to find the tension parameter that resembles the Bezier curves most accurately. But this is a story for another question.