// forked from KoheiTAKAMIYA's Illustrotor風ペンツール ver.6(未完成) // forked from KoheiTAKAMIYA's Illustrotor風ペンツール ver.5 // forked from KoheiTAKAMIYA's Illustrotor風ペンツール ver.4 // forked from KoheiTAKAMIYA's Illustrotor風ペンツール ver.3 // forked from KoheiTAKAMIYA's Illustrotor風ペンツール ver.2 // forked from KoheiTAKAMIYA's Illustrotor風ペンツール ver.1 //ver.3:3次ベジェ曲線を再現 //ver.4:始点-終点を結ぶと閉じる //ver.5:閉じたあとにアンカーとハンドルが操作可能 //今回はさらにアンカーポイントを追加できるようにする package{ import flash.display.Sprite; import flash.geom.Point; import flash.events.MouseEvent; import flash.text.TextField; public class PenTool extends Sprite{ private var bezierPointStage:Sprite = new Sprite(); private var bezierCurve:BezierCurveDrawer = new BezierCurveDrawer(); private var txt:TextField = new TextField(); private var targetIndex:uint; private var targetType:String; public function PenTool(){ addChild(bezierCurve); addChild(bezierPointStage); addChild(txt); txt.width = 400; txt.height = 20; stage.addEventListener(MouseEvent.MOUSE_DOWN, addBezierPoint); } private function addBezierPoint(event:MouseEvent):void{ switch(event.type){ case MouseEvent.MOUSE_DOWN: if(bezierCurve.finalize){ targetIndex = 0; txt.text = "ベジェ曲線を閉じました。"; stage.removeEventListener(MouseEvent.MOUSE_DOWN, addBezierPoint); bezierCurve.push(bezierCurve.getIndexAt(0)); bezierCurve.getIndexAt(0).anchor.removeEventListener(MouseEvent.ROLL_OVER, finalizeBezier); bezierCurve.getIndexAt(0).anchor.removeEventListener(MouseEvent.ROLL_OUT, finalizeBezier); } else { targetIndex = bezierCurve.length; var bezierPoint:BezierPoint = new BezierPoint(new Point(mouseX, mouseY)); bezierCurve.push(bezierPoint); if(!targetIndex) { bezierPoint.anchor.addEventListener(MouseEvent.ROLL_OVER, finalizeBezier); bezierPoint.anchor.addEventListener(MouseEvent.ROLL_OUT, finalizeBezier); } bezierPointStage.addChild(bezierPoint); } stage.addEventListener(MouseEvent.MOUSE_MOVE, moveBezierPoint); stage.addEventListener(MouseEvent.MOUSE_UP, function defineBezierPoint():void{ if(bezierCurve.finalize) { bezierCurve.addBezierCurveEvent(MouseEvent.ROLL_OVER, addBezierPoint); bezierCurve.addBezierCurveEvent(MouseEvent.ROLL_OUT, addBezierPoint); bezierCurve.addBezierCurveEvent(MouseEvent.CLICK, addBezierPoint); bezierCurve.addBezierPointEvent(MouseEvent.ROLL_OVER, moveBezierPoint); bezierCurve.addBezierPointEvent(MouseEvent.ROLL_OUT, moveBezierPoint); bezierCurve.addBezierPointEvent(MouseEvent.MOUSE_DOWN, moveBezierPoint); } txt.text = ""; stage.removeEventListener(MouseEvent.MOUSE_MOVE, moveBezierPoint); } ); break; case MouseEvent.CLICK: txt.text = "アンカーポイント追加しました。"; break; case MouseEvent.ROLL_OVER: txt.text = "アンカーポイント追加します。"; break; case MouseEvent.ROLL_OUT: txt.text = ""; break; } } private function moveBezierPoint(event:MouseEvent):void{ var targetPoint:BezierPoint = bezierCurve.getIndexAt(targetIndex); switch(event.type){ case MouseEvent.MOUSE_MOVE: var f:Number = -targetPoint.f(targetType); //if(inFinite(f))) f = -1; txt.text = String(f); //if(KeyboardEvent.altKey) txt.text = "alt"; switch(targetType){ case "anchor": bezierCurve.setIndexAt(new Point(mouseX, mouseY), null, null, targetIndex); break; case "handleA": //bezierCurve.setIndexAt(null, new Point(mouseX, mouseY), null, targetIndex);), targetIndex); if(isFinite(f)) bezierCurve.setIndexAt(null, new Point(mouseX, mouseY), Point.interpolate(targetPoint.handleAPoint, targetPoint.anchorPoint, f), targetIndex); break; case "handleB": //bezierCurve.setIndexAt(null, null, new Point(mouseX, mouseY), targetIndex); if(isFinite(f)) bezierCurve.setIndexAt(null, Point.interpolate(targetPoint.handleBPoint, targetPoint.anchorPoint, f), new Point(mouseX, mouseY), targetIndex); break; default: bezierCurve.setIndexAt(null,new Point(mouseX, mouseY),Point.interpolate(targetPoint.handleAPoint, targetPoint.anchorPoint, -1), targetIndex); break; } break; case MouseEvent.MOUSE_DOWN: stage.addEventListener(MouseEvent.MOUSE_MOVE, moveBezierPoint); stage.addEventListener(MouseEvent.MOUSE_UP, function removeEvent():void{ event.target.removeEventListener(MouseEvent.MOUSE_MOVE, moveBezierPoint); } ); break; case MouseEvent.ROLL_OVER: txt.text = event.target.type; targetType = event.target.type; targetIndex = bezierCurve.indexOf(event.target.parent as BezierPoint); break; case MouseEvent.ROLL_OUT: txt.text = ""; break; } event.updateAfterEvent(); } private function finalizeBezier(event:MouseEvent):void{ if(targetIndex) { switch(event.type){ case MouseEvent.ROLL_OVER: txt.text = "ベジェ曲線を閉じます。"; bezierCurve.finalize = true; break; case MouseEvent.ROLL_OUT: txt.text = ""; bezierCurve.finalize = false; break; } } } } } import flash.geom.Point; import flash.display.Sprite; //--------- //3次ベジェ曲線を繋げてグラフィックを描くクラス class BezierCurveDrawer extends Sprite{ private var _pointArray:Array = new Array(); //BezierPointを格納する変数 private var _curveArray:Array = new Array(); //BezierCurveを格納する変数 public var finalize:Boolean = false; //BezierCurveの開閉を監視 public function BezierCurveDrawer(){ } public function indexOf(bezierPoint:BezierPoint):int{ return _pointArray.indexOf(bezierPoint); } public function indexOfCurve(bezierCurve:BezierCurve):int{ return _curveArray.indexOf(bezierCurve); } public function getIndexAt(_index:uint):BezierPoint{ return _pointArray[_index]; } public function setIndexAt(_anchor:Point, _handleA:Point, _handleB:Point, _index:uint):void{ if(_index < length){ if(_anchor) _pointArray[_index].anchorPoint = _anchor; if(_handleA) _pointArray[_index].handleAPoint = _handleA; if(_handleB) _pointArray[_index].handleBPoint = _handleB; var prev:int = _index > 0 ? _index-1 : finalize ? length-1 : -1; var next:int = !finalize ? -1 : _index < length ? _index : 0; if(_curveArray[prev]) { _curveArray[prev].setPoint(null, null, _pointArray[_index].handleBPoint, _pointArray[_index].anchorPoint); _curveArray[prev].draw(); } if(_curveArray[next]){ _curveArray[next].setPoint(_pointArray[_index].anchorPoint, _pointArray[_index].handleAPoint, null, null); _curveArray[next].draw(); } } } //イベント public function addBezierPointEvent(type:String, listener:Function):void{ for each(var bezierPoint:BezierPoint in _pointArray){ bezierPoint.anchor.addEventListener(type,listener); bezierPoint.handleA.addEventListener(type,listener); bezierPoint.handleB.addEventListener(type,listener); } } public function removeBezierPointEvent(type:String, listener:Function):void{ for each(var bezierPoint:BezierPoint in _pointArray){ bezierPoint.anchor.removeEventListener(type,listener); bezierPoint.handleA.removeEventListener(type,listener); bezierPoint.handleB.removeEventListener(type,listener); } } public function addBezierCurveEvent(type:String, listener:Function):void{ for each(var bezierCurve:BezierCurve in _curveArray){ bezierCurve.addEventListener(type,listener); } } public function push(bezierPoint:BezierPoint):void{ if(length) { var bezierCurve:BezierCurve = new BezierCurve(_pointArray[length-1].anchorPoint, _pointArray[length-1].handleAPoint, bezierPoint.handleBPoint, bezierPoint.anchorPoint); addChild(bezierCurve); _curveArray.push(bezierCurve); } if(!finalize) _pointArray.push(bezierPoint); } public function get length():uint{ return _pointArray.length; } } //--------- //4点で3次ベジェ曲線の計算・描画クラス class BezierCurve extends Sprite{ public var a:Point; //anchorA public var b:Point; //handleA public var c:Point; //handleB public var d:Point; //anchorB public function BezierCurve(a:Point, b:Point, c:Point, d:Point){ this.a = a; this.b = b; this.c = c; this.d = d; draw(); } public function setPoint(a:Point, b:Point, c:Point, d:Point):void{ if(a) this.a = a; if(b) this.b = b; if(c) this.c = c; if(d) this.d = d; } public function draw():void{ graphics.clear(); graphics.lineStyle(3,0x666666); graphics.moveTo(a.x, a.y); for each(var bezierPoint:Point in BezierCurve.bezierValues(a,b,c,d,20)){ graphics.lineTo(bezierPoint.x,bezierPoint.y); } } public static function bezierValue(a:Point, b:Point, c:Point, d:Point,t:Number):Point{ if(t < 0 || t > 1) return null; var _p4:Point = Point.interpolate(b, a, t); var _p5:Point = Point.interpolate(c, b, t); var _p6:Point = Point.interpolate(d, c, t); var _p7:Point = Point.interpolate(_p5, _p4, t); var _p8:Point = Point.interpolate(_p6, _p5, t); return Point.interpolate(_p8, _p7, t); } public static function bezierValues(a:Point, b:Point, c:Point, d:Point,_quality:uint = 10):Array{ if(a.equals(d)) return [a]; var valueArray:Array = new Array(); for(var i:uint = 0; i <= _quality; i++){ valueArray.push(bezierValue(a,b,c,d,i/_quality)); } return valueArray; } } //--------- //アンカー,ハンドルA,ハンドルBを格納したクラス class BezierPoint extends Sprite{ private var _anchor:PointSprite; private var _handleA:PointSprite; private var _handleB:PointSprite; public function BezierPoint(_anchor:Point){ anchorPoint = _anchor; } private function drawLine():void{ graphics.clear(); graphics.lineStyle(1,0x00ffff); if(_handleA){ graphics.moveTo(anchor.x, anchor.y); graphics.lineTo(handleA.x, handleA.y); } if(_handleB){ graphics.moveTo(anchor.x, anchor.y); graphics.lineTo(handleB.x, handleB.y); } } public function f(_handle:String):Number{ if(_handle == "handleA") { //if(anchorPoint.equals(handleAPoint)) return -1;//Point.distance(anchorPoint, handleBPoint); return Point.distance(anchorPoint, handleBPoint)/Point.distance(anchorPoint, handleAPoint); }else if(_handle == "handleB") { //if(anchorPoint.equals(handleBPoint)) return Point.distance(anchorPoint, handleAPoint); return Point.distance(anchorPoint, handleAPoint)/Point.distance(anchorPoint, handleBPoint); }else return 0; } //プロパティ //anchor public function set anchorPoint(_point:Point):void{ if(!_anchor){ _anchor = new PointSprite(_point, "anchor"); addChild(_anchor); } else{ var subPoint:Point = _point.subtract(_anchor.point); _anchor.drawPoint(_point); if(_handleA) _handleA.drawPoint(_handleA.point.add(subPoint)); if(_handleB) _handleB.drawPoint(_handleB.point.add(subPoint)); drawLine(); } } public function get anchorPoint():Point{ return _anchor.point; } public function get anchor():Sprite{ return _anchor as Sprite; } //handleA public function set handleAPoint(_point:Point):void{ if(!_handleA) { _handleA = new PointSprite(_point, "handleA"); addChild(_handleA); } else { _handleA.drawPoint(_point); } drawLine(); } public function get handleAPoint():Point{ if(_handleA) return _handleA.point; return _anchor.point; } public function get handleA():Sprite{ if(_handleA) return _handleA; return _anchor; } //handleB public function set handleBPoint(_point:Point):void{ if(!_handleB) { _handleB = new PointSprite(_point, "handleB"); addChild(_handleB) } else { _handleB.drawPoint(_point); } drawLine(); } public function get handleBPoint():Point{ if(_handleB) return _handleB.point; return _anchor.point; } public function get handleB():Sprite{ if(_handleA) return _handleB; return _anchor; } } //--------- //アンカー,ハンドルの描画用クラス class PointSprite extends Sprite{ public var point:Point; public var alt:Boolean = false; private var _type:String; private var _color:uint; private var _thick:uint; private var _shape:String; public function PointSprite(_point:Point, _type:String){ type = _type; //"anchor" or "handle" drawPoint(_point); } public function drawPoint(_point:Point = null):void{ if(_point) point = _point.clone(); graphics.clear(); graphics.lineStyle(_thick,_color); graphics.beginFill(0xffffff); if(_shape == "rect") graphics.drawRect(point.x-2, point.y-2, 4, 4); else graphics.drawCircle(point.x, point.y, 2); graphics.endFill(); } public function set type(_type:String):void{ this._type = _type; switch(_type){ case "anchor": _color = 0x00ffff; _thick = 1; _shape = "rect"; break; case "handleA": case "handleB": _color = 0x00ffff; _thick = 1; _shape = "circle"; break; } } public function get type():String{ return _type; } public override function get x():Number{ return point.x; } public override function get y():Number{ return point.y; } } forked from: Illustrotor風ペンツール ver.6(未完成)