How to find control points for a BezierSegment given Start, End, and 2 Intersection Pts in C# - AKA Cubic Bezier 4-point Interpolation

前端 未结 4 1722
清酒与你
清酒与你 2020-12-04 18:52

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

相关标签:
4条回答
  • 2020-12-04 19:09

    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;
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-04 19:13

    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

    0 讨论(0)
  • 2020-12-04 19:18

    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

    0 讨论(0)
  • 2020-12-04 19:23

    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.

    0 讨论(0)
提交回复
热议问题