Как найти контрольные точки для сегмента Безье, учитывая начальную, конечную и 2 точки пересечения в C# — кубическая интерполяция Безье с 4 точками

Я изо всех сил пытался найти понятный способ сделать это. У меня есть четыре точки: StartPt, EndPoint и Intersection, которые представляют пик и впадину кривой Безье.

BezierSegment в C# требует start, controlPoint 1, controlPoint 2, endpoint, однако у меня нет никаких контрольных точек. У меня есть только эти две точки, которые лежат вдоль кривых Безье (я называю их точками пересечения выше)... как я могу рассчитать две контрольные точки?

Заранее спасибо, это сводило меня с ума.

Здесь есть какое-то объяснение: http://www.tinaja.com/glib/nubz4pts1.pdf но это написано в постскриптуме, и этот язык для меня вообще не имеет смысла - это выше моего понимания.


person softwarequestioneer    schedule 23.02.2010    source источник
comment
Можете ли вы объяснить, что вы подразумеваете под точками пересечения? Кривые Безье обычно ничего не пересекают.   -  person John Feminella    schedule 23.02.2010
comment
Итак, это две точки, которые лежат на кривой Безье... например, одна может быть третью длины кривой от начальной точки, другая может быть третьей от конечной точки... имеет ли это смысл?   -  person softwarequestioneer    schedule 23.02.2010
comment
Я считаю, что точки пересечения, о которых вы говорите, являются вашими контрольными точками. Вам просто нужно 4 точки вдоль кривой, чтобы определить Безье. Пока все 4 находятся на кривой, все должно быть в порядке, независимо от того, где они находятся на кривой.   -  person Pace    schedule 23.02.2010
comment
Эти точки пересечения не являются контрольными точками, если я включу их в качестве контрольных точек, то безье не пройдет через эти точки. Контрольные точки находятся за пределами кривой Безье. Чтобы лучше объяснить это, нарисуйте в уме кривую, отметьте начальную и конечную точки и выберите две точки на трети пути от каждой конечной точки. Теперь у вас есть 4 точки - учитывая эти координаты, если вы хотите аппроксимировать сегмент Безье, вам нужно вычислить 2 контрольные точки, которые лежат за пределами кривой...   -  person softwarequestioneer    schedule 23.02.2010
comment
Для более подробного объяснения — вот изображение того, что я имею в виду: freeimagehosting.net/uploads/293dee372e.png У меня есть значения для Start, End, Intersection 1 и Intersection 2, есть ли способ определить ctrlpt 1 и ctrlpt 2 - b/c это то, что нужно сегменту Безье...   -  person softwarequestioneer    schedule 23.02.2010
comment
это то, что я имею в виду, кубическая интерполяция Безье по 4 точкам ... они говорят об этом во Flash по этой ссылке: algorithmist.wordpress.com/2008/09/08/ Для обзора проблема заключается в следующем. Для четырех векторов V0, V1, V2 и V3 найдите контрольные точки P0, P1, P2 и P3 кубической кривой Безье B(t) так, чтобы кривая проходила через V0, V1, V2 и V3. Не могли бы вы, ребята, представить, как это можно сделать на C#?   -  person softwarequestioneer    schedule 23.02.2010


Ответы (4)


Существует бесконечное количество решений для кривой, проходящей через 4 точки, но лучшее простое решение — попытаться сделать длины сегментов кривой пропорциональными длинам хорд. Код, на который вы ссылаетесь, является приближением первого порядка, которое работает хорошо и довольно быстро.

Вот перевод C# кода PostScript:

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

Я не тестировал его, но он компилируется. Просто вызовите DrawingUtility.BezierFromIntersection с 4 точками, которые у вас есть, и он вернет PathFigure для рисования кривой.

person Gabe    schedule 23.02.2010
comment
Вау! Гейб, спасибо большое. Я понятия не имею, как вы можете читать этот постскриптум (выглядит как MIPS на ассемблере), но это прекрасно. Только что попробовал, и это сработало отлично. Хммм... Теперь мне нужно решить ту же проблему, но с поиском единственной контрольной точки в Квадратичном Безье с заданными начальной, средней и конечной точками. Вы случайно не знаете, как Interpolate a QuadraticBezier? - person softwarequestioneer; 23.02.2010
comment
Если вы хотите знать, как делать квадратные вычисления, сделайте еще один пост и дайте ссылку на него отсюда. - person Gabe; 23.02.2010
comment
Спасибо Гейб! Вот ссылка на мой вопрос о Quadratics: stackoverflow.com/questions/2320956/ - person softwarequestioneer; 23.02.2010
comment
+1 Работал как шарм. Искал чистый код, который вычисляет две контрольные точки BezierCurve. Было довольно легко перевести это на Qt - person gibertoni; 04.02.2015


Вам следует рассмотреть возможность использования кардинальных (канонических) сплайнов, которые используют набор точек, существующих на пути, а также параметр «натяжение», который определяет, насколько острые углы сглаживаются до угловых касательных.

В Windows Forms можно использовать методы DrawCurve и DrawClosedCurve. В WPF нет прямых эквивалентов. Вот две статьи, описывающие использование кардинальных сплайнов в WPF с C#.

Floris — AddCurve для кардинального сплайна WPF

Petzold — Канонические сплайны в WPF и Silverlight< /а>

person dapi    schedule 03.07.2011

версия as3:

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;
        }
    }
}
person user2630051    schedule 29.07.2013