17

I've been struggling looking for an understandable way to do this. I have four points, a StartPt, EndPoint, and Intersection points to represent the peak and valley in the bezier.

The BezierSegment in C# requires start, controlPoint 1, controlPoint 2, endpoint - however I don't have any control points I only have these two points that lie along the bezier curves (i'm calling them intersection points above)... how can I calculate the two control points?

Thanks in advance, this has been driving me crazy.

There's some kind of explanation here: http://www.tinaja.com/glib/nubz4pts1.pdf but it's written in postscript and that language makes no sense to me at all - it's over my head.

softwarequestioneer
  • 991
  • 1
  • 8
  • 16
  • Can you explain what you mean by "intersection points"? Bezier curves typically don't intersect anything. – John Feminella Feb 23 '10 at 00:55
  • 1
    So these are 2 points that lie on the bezier curve... like one could be a third of the curve length from the startpoint, the other could be a third from the endpoint... does that make sense? – softwarequestioneer Feb 23 '10 at 01:02
  • I believe the intersection points you speak of are your control points. You just need 4 points along a curve to define a bezier. As long as all 4 are on the curve you should be fine, regardless of where on the curve they are. – Pace Feb 23 '10 at 01:35
  • 2
    Those intersection points aren't the control points, if i include them as control points then the bezier will not pass through those points. The control points are outside of the bezier curve. To Explain this better, draw a curve in your mind, note the start & end points, and pick two points a third of the way in from each endpoint. Now you have 4 points - given those coords if you want to approximate a beziersegment you need to calculate 2 control points that lie outside of the curve... – softwarequestioneer Feb 23 '10 at 01:47
  • For more explanation - Here's a picture of what I mean: http://www.freeimagehosting.net/uploads/293dee372e.png I've got values for Start, End, Intersection 1, and Intersection 2, is there a way I can determine ctrlpt 1 and ctrlpt 2 - b/c that's what the BezierSegment needs... – softwarequestioneer Feb 23 '10 at 02:00
  • this is what I mean, Cubic Bezier 4-Point Interpolation...they talk about doing it in Flash in this link: http://algorithmist.wordpress.com/2008/09/08/cubic-bezier-4-point-interpolation-part-ii/ "To review, the problem is as follows. Given four vectors, V0, V1, V2, and V3, find the control points, P0, P1, P2, and P3 of a cubic Bezier curve, B(t), so that the curve passes through V0, V1, V2, and V3." Would you guys have any idea how this could be done in C#? – softwarequestioneer Feb 23 '10 at 05:05

4 Answers4

18

There are an infinite number of solutions to a curve passing through 4 points, but the best simple solution is to try to make the curve segment lengths proportional to the chord lengths. The code you link to is the a first order approximation that works well and is pretty fast.

Here's the C# translation of the PostScript code:

static class DrawingUtility
{
    // linear equation solver utility for ai + bj = c and di + ej = f
    static void solvexy(double a, double b, double c, double d, double e, double f, out double i, out double j)
    {
        j = (c - a / d * f) / (b - a * e / d);
        i = (c - (b * j)) / a;
    }

    // basis functions
    static double b0(double t) { return Math.Pow(1 - t, 3); }
    static double b1(double t) { return t * (1 - t) * (1 - t) * 3; }
    static double b2(double t) { return (1 - t) * t * t * 3; }
    static double b3(double t) { return Math.Pow(t, 3); }

    static void bez4pts1(double x0, double y0, double x4, double y4, double x5, double y5, double x3, double y3, out double x1, out double y1, out double x2, out double y2)
    {
        // find chord lengths
        double c1 = Math.Sqrt((x4 - x0) * (x4 - x0) + (y4 - y0) * (y4 - y0));
        double c2 = Math.Sqrt((x5 - x4) * (x5 - x4) + (y5 - y4) * (y5 - y4));
        double c3 = Math.Sqrt((x3 - x5) * (x3 - x5) + (y3 - y5) * (y3 - y5));
        // guess "best" t
        double t1 = c1 / (c1 + c2 + c3);
        double t2 = (c1 + c2) / (c1 + c2 + c3);
        // transform x1 and x2
        solvexy(b1(t1), b2(t1), x4 - (x0 * b0(t1)) - (x3 * b3(t1)), b1(t2), b2(t2), x5 - (x0 * b0(t2)) - (x3 * b3(t2)), out x1, out x2);
        // transform y1 and y2
        solvexy(b1(t1), b2(t1), y4 - (y0 * b0(t1)) - (y3 * b3(t1)), b1(t2), b2(t2), y5 - (y0 * b0(t2)) - (y3 * b3(t2)), out y1, out y2);
    }

    static public PathFigure BezierFromIntersection(Point startPt, Point int1, Point int2, Point endPt)
    {
        double x1, y1, x2, y2;
        bez4pts1(startPt.X, startPt.Y, int1.X, int1.Y, int2.X, int2.Y, endPt.X, endPt.Y, out x1, out y1, out x2, out y2);
        PathFigure p = new PathFigure { StartPoint = startPt };
        p.Segments.Add(new BezierSegment { Point1 = new Point(x1, y1), Point2 = new Point(x2, y2), Point3 = endPt } );
        return p;
    }
}

I haven't tested it, but it compiles. Just call DrawingUtility.BezierFromIntersection with the 4 points you have, and it will return a PathFigure for drawing the curve.

Gabe
  • 84,912
  • 12
  • 139
  • 238
  • Wow! Gabe, Thank you soo much. I have no idea how you can read that postscript code (looks like assembly language MIPS), but this is perfect. Just tried it out and it worked great. Hmmm... Now I've got to solve the same problem but with finding the single control point in a Quadratic Bezier given a start, midpoint, and endpoint. You wouldn't know how Interpolate a QuadraticBezier by any chance? – softwarequestioneer Feb 23 '10 at 18:48
  • If you want to know how to do quadratics, make another post and link to it from here. – Gabe Feb 23 '10 at 19:02
  • Thanks Gabe! Here's the link to my question about Quadratics: http://stackoverflow.com/questions/2320956/find-control-point-for-quadraticbeziersegment-when-given-start-end-and-1-point – softwarequestioneer Feb 23 '10 at 19:12
  • +1 Worked like a charm. Was looking for a clean code that calculates the two control points of a BezierCurve. It was pretty easy to translate this to Qt – gibertoni Feb 04 '15 at 19:38
1

Here are two good examples:

http://www.codeproject.com/KB/graphics/ClosedBezierSpline.aspx http://www.codeproject.com/KB/graphics/BezierSpline.aspx

Also see this animation to better understand how BezierSplines work http://en.wikipedia.org/wiki/B%C3%A9zier_curve

thomas nn
  • 933
  • 3
  • 13
  • 21
1

You should consider using Cardinal (Canonical) Splines, which use a set of points that exist on the path, plus a "tension" parameter that controls how sharply corners are smoothed to corner tangents.

In Windows Forms, one would use the DrawCurve and DrawClosedCurve methods. In WPF there are no direct equivalents. Here are two articles that describe using cardinal splines in WPF with C#.

Floris - AddCurve For WPF Cardinal Spline

Petzold - Canonical Splines In WPF And Silverlight

dapi
  • 274
  • 4
  • 6
0

as3 version:

package 
{
    import flash.geom.Vector3D;

    public class DrawingUtility 
    {
        private var x1:Number; 
        private var y1:Number; 
        private var x2:Number;
        private var y2:Number;

        // linear equation solver utility for ai + bj = c and di + ej = f
        private function solvexy(a:Number, b:Number, c:Number, d:Number, e:Number, f:Number):Vector.<Number>
        {
            var returnVal:Vector.<Number> = new Vector.<Number>();
            var j:Number = (c - a / d * f) / (b - a * e / d);
            var i:Number = (c - (b * j)) / a;
            returnVal[0] = i;
            returnVal[1] = j;
            return returnVal;
        }

        // basis functions
        private function b0(t:Number):Number { 
            return Math.pow(1 - t, 3);
        }
        private function b1(t:Number):Number {
            return t * (1 - t) * (1 - t) * 3;
        }
        private function b2(t:Number):Number {
            return (1 - t) * t * t * 3;
        }
        private function b3(t:Number):Number {
            return Math.pow(t, 3);
        }

        private function bez4pts1(x0:Number, y0:Number, x4:Number, y4:Number, x5:Number, y5:Number, x3:Number, y3:Number):void
        {
            // find chord lengths
            var c1:Number = Math.sqrt((x4 - x0) * (x4 - x0) + (y4 - y0) * (y4 - y0));
            var c2:Number = Math.sqrt((x5 - x4) * (x5 - x4) + (y5 - y4) * (y5 - y4));
            var c3:Number = Math.sqrt((x3 - x5) * (x3 - x5) + (y3 - y5) * (y3 - y5));
            // guess "best" t
            var t1:Number = c1 / (c1 + c2 + c3);
            var t2:Number = (c1 + c2) / (c1 + c2 + c3);
            // transform x1 and x2
            var x1x2:Vector.<Number> = solvexy(b1(t1), b2(t1), x4 - (x0 * b0(t1)) - (x3 * b3(t1)), b1(t2), b2(t2), x5 - (x0 * b0(t2)) - (x3 * b3(t2)));
            x1 = x1x2[0];
            x2 = x1x2[1];
            // transform y1 and y2
            var y1y2:Vector.<Number> = solvexy(b1(t1), b2(t1), y4 - (y0 * b0(t1)) - (y3 * b3(t1)), b1(t2), b2(t2), y5 - (y0 * b0(t2)) - (y3 * b3(t2)));
            y1 = y1y2[0];
            y2 = y1y2[1];
        }

        public function BezierFromIntersection(startPt:Vector3D, int1:Vector3D, int2:Vector3D, endPt:Vector3D):Vector.<Vector3D>
        {
            var returnVec:Vector.<Vector3D> = new Vector.<Vector3D>();
            bez4pts1(startPt.x, startPt.y, int1.x, int1.y, int2.x, int2.y, endPt.x, endPt.y);

            returnVec.push(startPt);
            returnVec.push(new Vector3D(x1, y1));
            returnVec.push(new Vector3D(x2, y2));
            returnVec.push(endPt);
            return returnVec;
        }
    }
}
user2630051
  • 119
  • 3