Nov 05

To już ostatnia część mojej wypowiedzi na temat krzywych Béziera. Pokażę sposób wyliczania położenia punktów na krzywych za pomocą algorytmu de Casteljau i jego implementację w AS 3.0.

Przyjmijmy, że naszą krzywą określa n+1 punktów P0, P1, P2 … Pn. Każdy z odcinków |P0P1| , |P1P2| , … , |Pn-1Pn| dzielimy w stosunku t : (1-t), otrzymując n punktów A0, A1, A2, … , An-1. Po raz kolejny odcinki |A0A1| , |A1A2| , … , |An-2An-1| dzielimy w stosunku t : (1-t), wyznaczając n – 1 punktów B0, B1, B2, … , Bn-2. Operację powtarzamy do momentu, w którym ilość wygenerowanych punktów jest równa 1. W ten sposób otrzymujemy punkt krzywej dla parametru t.

Jeśli wykonamy tę operację dla wszystkich t z przedziału <1 , 0> wyznaczymy wszystkie punkty krzywej Béziera.

Działanie algorytmu, który wyznacza punkt dla t = 0.5 krzywej określonej punktami P0, P1, P2, P3, P4 ilustruje rysunek
deCasteljau

Implementacja funkcji, które wyznaczają punkty krzywej Béziera

package
{
    public class DeCasteljauBezier
    {
        import flash.geom.Point;
       
        /**
            Funkcja zwraca wspolrzedne punktu X,
            ktory dzieli odcinek |p0p1| tak, ze |p0X|/|Xp1| = t.
        */

        public static function calculatePoint   (
                                                    p0 : Point,
                                                    p1 : Point ,
                                                    t : Number
                                                ) : Point
        {
            return new Point(p0.x + (p1.x-p0.x) * t , p0.y + (p1.y - p0.y) * t);
        }
       
        /**
            Funkcja zwraca wspolrzedne punktu krzywej Beziera dla parametru t.
        */

        public static function deCasteljauAlgorithm (
                                                        points : Array,
                                                        t : Number
                                                    ) : Point
        {
            if(points.length == 1) return points[0];
            else
            {
                var outputPoints : Array = new Array(points.length - 1);
           
                for( var i : int = 0 ; i < outputPoints.length ; i++)
                {
                    outputPoints[i] = calculatePoint(points[i] , points[i+1] , t);
                }
               
                return deCasteljauAlgorithm(outputPoints , t);
            }
        }
       
        /**
            Funkcja wylicza tablice punktow krzywej Beziera
            o punktach kontrolnych controlPoints z dokladnoscia do parametru deltaT.
        */

        public static function calculateCurvePoints (  
                                                        controlPoints : Array ,
                                                        deltaT : Number = 0.05
                                                    ) : Array
        {
            var outputPoints : Array = new Array();
           
            for ( var t : Number = 0.00 ; t <= 1.00 ; t += deltaT )
            {
                outputPoints.push(deCasteljauAlgorithm(controlPoints , t));
            }
           
            return outputPoints;
        }
    }
}

DeCasteljauBezier.as

oraz Document Class, który jest zmodyfikowaną wersją DC z części drugiej mojego artykułu na temat krzywych Béziera.

package
{
    import flash.display.Sprite;
    import flash.geom.Point;
    import flash.events.MouseEvent;
    import flash.text.TextField;
   
    public class DeCasteljauTestDC extends Sprite
    {
        const       n       : int       = 8;
        const       deltaT  : Number    = 0.02;
       
        private var _points : Array     = new Array(n + 1);
       
       
        public function DeCasteljauTestDC()
        {          
            for( var j : int = 0 ; j < (n + 1) ; j++ )
            {
                var point : Sprite = new Sprite();
                   
                    point.graphics.beginFill(0xad1f47 , 1);
                    point.graphics.drawCircle(0,0,3);
                    point.graphics.endFill();
                   
                    point.addEventListener  (
                                                MouseEvent.MOUSE_DOWN,
                                                mouseDownEventHandler
                                            );
                   
                    point.addEventListener  (
                                                MouseEvent.MOUSE_UP,
                                                mouseUpEventHandler
                                            );
                   
                    point.mouseChildren = false;
                    point.buttonMode    = true;
                   
                    var tf : TextField  = new TextField();
                   
                        tf.autoSize     = 'left';
                        tf.selectable   = false;
                        tf.textColor    = 0xad1f47;
                        tf.text         = 'P_'+ j;
                   
                    point.addChild(tf);
                   
                    point.x = Math.random() * 400;
                    point.y = Math.random() * 400;
                   
                _points[j]  = addChild(point);
            }
        }
       
        /**
            Events handlers
        */

       
        private function mouseDownEventHandler(event : MouseEvent) : void
        {
            (event.target as Sprite).addEventListener   (
                                                            MouseEvent.MOUSE_MOVE,
                                                            mouseMoveEventHandler
                                                        );
            (event.target as Sprite).startDrag(false);
        }
       
        private function mouseUpEventHandler(event : MouseEvent) : void
        {
            (event.target as Sprite).stopDrag();
        }
       
        private function mouseMoveEventHandler(event : MouseEvent) : void
        {
            /**
                Funkcja przy kazdej zmianie pozycji dowolnego z punktow kontrolnych,
                czysci okno i rysuje nowa krzywa.
            */

           
            graphics.clear();
            graphics.lineStyle(1,0x999999 , 1);
            graphics.moveTo(_points[0].x,_points[0].y);

            var controlPoints : Array = new Array();
           
            for( var j : int = 0 ; j < _points.length; j++ )
            {
                controlPoints.push(new Point( _points[j].x , _points[j].y ));
            }

            var points : Array =
            DeCasteljauBezier.calculateCurvePoints(controlPoints,0.01);

            for (var i : int = 0; i < points.length; i++)
            {
                graphics.lineTo (points[i].x,points[i].y);
            }
        }
    }
}

DeCasteljauTestDC.as

Nie zamieszczam obrazka okna, wyświetlającego krzywą, ponieważ wygląda ono identycznie jak w części drugiej – zmienił się tylko sposób implementacji metody rysującej.

Kody źródłowe oraz pliki fla i swf do pobrania stąd.

  • Leave a Reply

    You must be logged in to post a comment.