package { import flash.display.Sprite; import flash.events.Event; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.display.Shape; import flash.display.Graphics; import flash.events.MouseEvent; import flash.utils.Timer; import flash.events.TimerEvent; import flash.display.GradientType; import flash.display.LineScaleMode; import flash.display.CapsStyle; import caurina.transitions.Tweener; import caurina.transitions.SpecialPropertiesDefault; import flash.text.TextField; [SWF(frameRate=60, background=0x000000)] public class AccurateBezier extends Sprite { public var bezierPoints:Array = []; public var s:Shape = new Shape(); public var lines:Sprite = new Sprite(); public var radius:uint = 12; public var accurateColor:uint = 0x3190FF; public var tweenerColor:uint = 0xFF3176; public var motionShape:Shape = new Shape(); public var tweenerMotionShape:Shape = new Shape(); public function AccurateBezier() { run(); } public static function _accurate_bezier_get(b:Number, e:Number, t:Number, p:Array):Number { var tm:Number = 1 - t; if (p.length == 1) { // quadratic bezier curves return b + t*(2*(tm)*(p[0]-b) + t*(e - b)); } else if (p.length == 2) { // cubic bezier curves return b*tm*tm*tm + 3*p[0]*t*tm*tm + 3*p[1]*t*t*tm + e*t*t*t; } else { // more points curves var points:Array = [b]; points = points.concat(p); points.push(e); var res:Number = 0; var len:uint = points.length; var pos:Number, tmp:Number, a:Number, b:Number, c:Number; for (var i:uint=0; i < len;i++) { pos = points[i]; tmp = 1.0; a = len - 1; b = i; c = a - b; while (a > 1) { if(a > 1) tmp *= a; a -= 1; if(b > 1) tmp /= b; b -= 1; if(c > 1) tmp /= c; c -= 1; } tmp *= Math.pow(t, i) * Math.pow(tm, len -1 -i); res += tmp * pos; } return res; } } public static var _bezierN:Function = function(t:Number, points:Array):Number { var res:Number = 0; var len:uint = points.length; var tm:Number = 1 - t; var pos:Number, tmp:Number, a:Number, b:Number, c:Number; for (var i:uint=0; i < len;i++) { pos = points[i]; tmp = 1.0; a = len - 1; b = i; c = a - b; while (a > 1) { if(a > 1) tmp *= a; a -= 1; if(b > 1) tmp /= b; b -= 1; if(c > 1) tmp /= c; c -= 1; } tmp *= Math.pow(t, i) * Math.pow(tm, len -1 -i); res += tmp * pos; } return res; } public function run():void { stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; addChild(s); addChild(lines); Tweener.registerSpecialPropertyModifier("_accurate_bezier", SpecialPropertiesDefault._bezier_modifier, _accurate_bezier_get); appendBezierPoint(50, 50); appendBezierPoint(100, 150); appendBezierPoint(250, 150); appendBezierPoint(300, 50); motionShape.graphics.beginFill(accurateColor, 0.9); motionShape.graphics.drawCircle(0,0, radius); motionShape.graphics.endFill(); motionShape.visible = false; addChild(motionShape); tweenerMotionShape.graphics.beginFill(tweenerColor, 0.9); tweenerMotionShape.graphics.drawCircle(0,0, radius); tweenerMotionShape.graphics.endFill(); tweenerMotionShape.visible = false; addChild(tweenerMotionShape); s.graphics.lineStyle(3, 0xFF0000); stage.doubleClickEnabled = true; stage.addEventListener(MouseEvent.DOUBLE_CLICK, function(e:MouseEvent):void { appendBezierPoint(e.stageX, e.stageY); startTween(); }); helpTexts(); drawLines(); } public function helpTexts():void { var t:TextField = new TextField(); t.textColor = 0xFFFFFF; t.text = "Accurate bezier curves\n"; t.appendText("Tweener's _bezier curves"); t.width = 200; t.mouseEnabled = false; t.x = 50; addChild(t); setChildIndex(t, 0); with(lines.graphics) { lineStyle(3, accurateColor); moveTo(10,10); lineTo(40, 10); lineStyle(3, tweenerColor); moveTo(10,23); lineTo(40, 23); } } public function drawAccurateBezierLines(graphics:Graphics, points:Array):void { graphics.moveTo(points[0][0], points[0][1]); var up:Number = 1.0 / (200 + 1); var xPoint:Array = []; var yPoint:Array = []; for each(var point:Array in points) { xPoint.push(point[0]); yPoint.push(point[1]); } for (var t:Number = 0; t < 1;t += up) { graphics.lineTo( _bezierN(t, xPoint), _bezierN(t, yPoint) ); } graphics.lineTo( xPoint[xPoint.length - 1], yPoint[yPoint.length - 1] ); } public function drawTweenerBezierLines(graphics:Graphics, points:Array):void { points = points.slice(); // make clone graphics.moveTo(points[0][0], points[0][1]); var up:Number = 1.0 / (200 + 1); var xPoint:Array = []; var yPoint:Array = []; var first:Array = points.shift(); var last:Array = points.pop(); var bX:Number = first[0]; var bY:Number = first[1]; var eX:Number = last[0]; var eY:Number = last[1]; for each(var point:Array in points) { xPoint.push(point[0]); yPoint.push(point[1]); } for (var t:Number = 0; t < 1;t += up) { graphics.lineTo( SpecialPropertiesDefault._bezier_get(bX, eX, t, xPoint), SpecialPropertiesDefault._bezier_get(bY, eY, t, yPoint) ); } graphics.lineTo( eX, eY); } public function drawLines():void { var points:Array = bezierPointsCoordinates(); if(points.length > 1) { graphics.clear(); // accurate bezier line graphics.lineStyle(3, accurateColor); drawAccurateBezierLines(graphics, points); graphics.endFill(); // tweener's bezier line graphics.lineStyle(3, tweenerColor); drawTweenerBezierLines(graphics, points); graphics.endFill(); s.graphics.clear(); s.graphics.lineStyle(0, 0xFFFFFF, 0.4); s.graphics.moveTo(points[0][0], points[0][1]); for each(var p:Array in points) { s.graphics.lineTo(p[0], p[1]); } s.graphics.endFill(); } } public function bezierPointsCoordinates():Array { var res:Array = []; for each(var p:BezierPoint in bezierPoints) { res.push([p.x, p.y]); } return res; } public function bezierPointsCoordinatesXY():Array { var res:Array = []; for each(var p:BezierPoint in bezierPoints) { res.push({x:p.x, y:p.y}); } return res; } public function appendBezierPoint(x:Number, y:Number):void { var p:BezierPoint = new BezierPoint(this); p.x = x; p.y = y; bezierPoints.push(p); drawLines(); } public function startTween():void { var bezierArray:Array = bezierPointsCoordinatesXY(); var firstPoint:Object = bezierArray.shift(); var lastPoint:Object = bezierArray.pop(); motionShape.x = firstPoint.x; motionShape.y = firstPoint.y; motionShape.visible = true; tweenerMotionShape.x = firstPoint.x; tweenerMotionShape.y = firstPoint.y; tweenerMotionShape.visible = true; Tweener.addTween(motionShape, { x: lastPoint.x, y: lastPoint.y, _accurate_bezier: bezierArray, onComplete: function():void { motionShape.visible = false }, transition: 'linear', time:1 }); Tweener.addTween(tweenerMotionShape, { x: lastPoint.x, y: lastPoint.y, _bezier: bezierArray, onComplete: function():void { tweenerMotionShape.visible = false }, transition: 'linear', time:1 }); } } } // from d:id:nitoyon import flash.display.Sprite; import flash.events.MouseEvent; import flash.events.Event; internal class BezierPoint extends Sprite { private const COLOR:int = 0xFFFFFF; private const RADIUS:int = 10; public function BezierPoint(_parent:Sprite) { _parent.addChild(this); graphics.beginFill(COLOR, 0.9); graphics.drawRect(-RADIUS/2, -RADIUS/2,RADIUS,RADIUS); graphics.endFill(); buttonMode = true; addEventListener("mouseDown", function(e:MouseEvent):void{ e.stopPropagation(); addEventListener(Event.ENTER_FRAME, enterFrameHandler); startDrag() }); _parent.addEventListener("mouseUp", function(event:MouseEvent):void{ removeEventListener(Event.ENTER_FRAME, enterFrameHandler); Object(parent).startTween(); stopDrag() }); } public function enterFrameHandler(e:Event):void { Object(parent).drawLines(); } }