/**
* Copyright clockmaker ( http://wonderfl.net/user/clockmaker )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/6qfk
*/
package
{
import com.bit101.components.CheckBox;
import com.bit101.components.PushButton;
import com.bit101.components.RadioButton;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.net.URLRequest;
import flash.system.LoaderContext;
[SWF(width = "465", height = "465", frameRate = "60")]
/**
* ベジェ曲線で、ライン上にビットマップ塗りを適用するサンプルです。
* @author yasu
* @since 2010/5/10
*/
public class BezierPaint extends Sprite
{
//----------------------------------------------------------
//
// Constructor
//
//----------------------------------------------------------
/**
* 新しいインスタンスを作成します。
*/
public function BezierPaint()
{
bezierLineMaker = new BezierLineMaker();
addChild(bezierLineMaker);
btnToolAdd = new RadioButton(this, 10, 20, "Add Point ", true, toolType_clickHanekler);
btnToolRemove = new RadioButton(this, 10, 40, "Remove Point", false, toolType_clickHanekler);
btnToolChange = new RadioButton(this, 10, 60, "Change Point", false, toolType_clickHanekler);
btnToolArrow = new RadioButton(this, 10, 80, "Select Tool", false, toolType_clickHanekler);
new PushButton(this, 10, 110, "Reset", btnReset_clickHandler);
new CheckBox(this, 10, 140, "Show UV", checkBox_changeHandler);
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
loader.load(new URLRequest("http://assets.wonderfl.net/images/related_images/1/1f/1f60/1f60f1efaba1276e534cbb1023fa4f62891215e5"), new LoaderContext(true));
}
//----------------------------------------------------------
//
// Property
//
//----------------------------------------------------------
private var bezierLineMaker:BezierLineMaker;
private var btnToolAdd:RadioButton;
private var btnToolArrow:RadioButton;
private var btnToolChange:RadioButton;
private var btnToolRemove:RadioButton;
//----------------------------------------------------------
//
// Function
//
//----------------------------------------------------------
private function completeHandler(event:Event):void
{
bezierLineMaker.bitmapData = event.target.content.bitmapData;
}
private function toolType_clickHanekler(event:MouseEvent):void
{
switch (event.currentTarget)
{
case btnToolAdd:
{
bezierLineMaker.toolType = ToolType.PLUS;
break;
}
case btnToolRemove:
{
bezierLineMaker.toolType = ToolType.DELETE;
break;
}
case btnToolChange:
{
bezierLineMaker.toolType = ToolType.CHANGE;
break;
}
case btnToolArrow:
{
bezierLineMaker.toolType = ToolType.ARROW;
break;
}
}
}
private function btnReset_clickHandler(event:MouseEvent):void
{
btnToolAdd.selected = true;
bezierLineMaker.toolType = ToolType.PLUS;
bezierLineMaker.reset();
}
private function checkBox_changeHandler(event:Event):void
{
var selected:Boolean = event.currentTarget.selected;
bezierLineMaker.showUV = selected;
}
}
}
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.ContextMenuEvent;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
internal class AbstractBezierView extends Sprite
{
//----------------------------------------------------------
//
// Constructor
//
//----------------------------------------------------------
public function AbstractBezierView()
{
_controls = new Vector.<BezierPoint>();
}
//----------------------------------------------------------
//
// Property
//
//----------------------------------------------------------
//--------------------------------------
// bitmapData
//--------------------------------------
protected var _bitmapData:BitmapData = new BitmapData(1, 1);
public function get bitmapData():BitmapData
{
return _bitmapData;
}
public function set bitmapData(value:BitmapData):void
{
_bitmapData = value;
_updateAll();
}
public function set bitmapDataGoal(value:BitmapData):void
{
_bmdGoal = value;
_updateAll();
}
//--------------------------------------
// showUV
//--------------------------------------
private var _showUV:Boolean = false;
public function get showUV():Boolean
{
return _showUV;
}
public function set showUV(value:Boolean):void
{
_showUV = value;
_updateAll();
}
//--------------------------------------
// lineWidth
//--------------------------------------
protected var _lineWidth:Number = 20;
public function get lineWidth():Number
{
return _lineWidth;
}
public function set lineWidth(value:Number):void
{
_lineWidth = value;
_updateAll();
}
//--------------------------------------
// vec
//--------------------------------------
protected var _vec:Vector.<Point>;
public function get vec():Vector.<Point>
{
return _vec;
}
protected var _controls:Vector.<BezierPoint>;
private var _bmdGoal:BitmapData;
//----------------------------------------------------------
//
// Function
//
//----------------------------------------------------------
protected function _updateAll():void
{
_calcPoints();
if (_vec.length > 1)
{
graphics.clear();
LineUtil.drawBitmapLine(graphics, _vec, _bitmapData, _lineWidth, _showUV);
}
else
graphics.clear();
}
protected function _calcPoints():void
{
var i:int;
const segment:Number = 0.005;
var vec:Vector.<Point> = new Vector.<Point>();
// bezier loop
for (i = 0; i < _controls.length; i++)
{
// ベジェを閉じる
var next:int = i + 1;
if (next >= _controls.length)
next = 0;
var bezier:BezierSegment = new BezierSegment(
_controls[i].toPoint(),
_controls[i].controlPointPost,
_controls[next].controlPointPre,
_controls[next].toPoint());
var interNum:int = Point.distance(_controls[i].toPoint(), _controls[next].toPoint()) / 20;
for (var t:Number = 0.0; t <= 1.0; t += 1 / interNum)
{
var pt:Point = bezier.getValue(t);
vec.push(pt);
}
}
_vec = vec;
}
}
/**
* BezierLineMakerクラスはベジェ曲線編集用のパネルです。
* @author yasu
*/
internal class BezierLineMaker extends AbstractBezierView
{
//----------------------------------------------------------
//
// Static Property
//
//----------------------------------------------------------
/**
* グラフの矩形領域です。
*/
public static const GRAPH_RECT:Rectangle = new Rectangle(0, 0, GRAPH_STEP_W * 24, GRAPH_STEP_H * 20);
/**
* 0〜100%の領域を示すグラフの矩形領域です。
*/
public static const PERCENT_RECT:Rectangle = new Rectangle(0, 0, GRAPH_STEP_W * 24, GRAPH_STEP_H * 12);
private static const GRAPH_STEP_W:Number = 24;
private static const GRAPH_STEP_H:Number = 17;
//----------------------------------------------------------
//
// Constructor
//
//----------------------------------------------------------
/**
* 新しい BezierLineMaker インスタンスを作成します。
*/
public function BezierLineMaker()
{
super();
_bitmapData = new BitmapData(1, 1);
_container = new Sprite();
_canvas = new Sprite();
_curveCanvas = new Sprite();
_curveCanvas.x = GRAPH_RECT.left + PERCENT_RECT.left;
_curveCanvas.y = GRAPH_RECT.top + PERCENT_RECT.top;
_curveCanvas.addEventListener(MouseEvent.MOUSE_DOWN, canvas_mouseDownHandler);
_clickCanvas = new Sprite();
_clickCanvas.x = GRAPH_RECT.left + PERCENT_RECT.left;
_clickCanvas.y = GRAPH_RECT.top + PERCENT_RECT.top;
_clickCanvas.addEventListener(MouseEvent.MOUSE_DOWN, canvas_mouseDownHandler);
_clickCanvas.graphics.beginFill(0xFF, 0)
_clickCanvas.graphics.drawRect(0, 0, 2880, 2880)
_controlCanvasSub = new Shape();
_controlCanvasSub.x = GRAPH_RECT.left + PERCENT_RECT.left;
_controlCanvasSub.y = GRAPH_RECT.top + PERCENT_RECT.top;
_controlCanvas = new Sprite();
_controlCanvas.x = GRAPH_RECT.left + PERCENT_RECT.left;
_controlCanvas.y = GRAPH_RECT.top + PERCENT_RECT.top;
addChild(_container);
_container.addChild(_canvas);
addChild(_curveCanvas);
addChild(_clickCanvas);
addChild(_controlCanvasSub);
addChild(_controlCanvas);
//_update(null);
}
//----------------------------------------------------------
//
// Property
//
//----------------------------------------------------------
public function get isCrossed():Boolean
{
if (!_vec)
return false;
var flag:Boolean = false;
var lines:Vector.<Line> = new Vector.<Line>();
var i:int;
var j:int;
for (i = 0; i < _vec.length - 1; i++)
lines[i] = new Line(_vec[i], _vec[i + 1]);
lines.fixed = true;
for (i = 0; i < lines.length; i++)
{
for (j = 0; j < lines.length; j++)
{
if (j < i)
continue;
flag ||= Line.isCross(lines[i], lines[j]);
if (flag)
break;
}
}
return flag;
}
/**
* @inheritDoc
*/
override public function set scaleX(value:Number):void
{
super.scaleX = value;
for (var i:int = 0; i < _controls.length; i++)
_controls[i].scalePoint = value;
}
//--------------------------------------
// toolType
//--------------------------------------
private var _toolType:String = ToolType.PLUS;
public function get toolType():String
{
return _toolType;
}
public function set toolType(value:String):void
{
_toolType = value;
}
/**
* 作成した距離の長さ(単位は px )
*/
public function get totalDistance():Number
{
if (!_vec)
return 0;
var dist:Number = 0;
for (var i:int = 0; i < _vec.length - 1; i++)
dist += Point.distance(_vec[i], _vec[i + 1]);
return dist;
}
private var _canvas:Sprite;
private var _clickCanvas:Sprite;
private var _container:Sprite;
private var _controlCanvas:Sprite;
private var _controlCanvasSub:Shape;
private var _currentKnob:BezierPoint;
private var _curveCanvas:Sprite;
//----------------------------------------------------------
//
// Function
//
//----------------------------------------------------------
/**
* ポイントを削除します。
* @param target
*/
public function deletePoint(target:BezierPoint):void
{
// 一個以下なら終了
if (_controls.length <= 1)
return;
var index:int = _controls.indexOf(target);
// 消去処理
_controls.splice(index, 1);
_controlCanvas.removeChild(target);
// メモリ対策
target.removeEventListener(Event.CHANGE, knob_dragChangeHandler);
target.dispose();
_updateAll();
}
public function reset():void
{
for (var i:int = 0; i < _controls.length; i++)
{
var target:BezierPoint = _controls[i];
_controlCanvas.removeChild(target);
// メモリ対策
target.removeEventListener(Event.CHANGE, knob_dragChangeHandler);
target.dispose();
}
_controls = new Vector.<BezierPoint>();
_updateAll();
}
/**
* @inheritDoc
*/
override protected function _updateAll():void
{
super._updateAll();
_updateControlLines();
// コース変更イベント送出
dispatchEvent(new CourseEvent(CourseEvent.COURSE_CHANGE, true, false));
}
private function _updateControlLines():void
{
super._calcPoints();
var vecLo:Vector.<Point> = _vec;
var i:int;
_controlCanvas.graphics.clear();
for (i = 0; i < vecLo.length - 1; i++)
{
_controlCanvas.graphics.lineStyle(1, 0x5083fc, 1);
_controlCanvas.graphics.moveTo(vecLo[i].x, vecLo[i].y);
_controlCanvas.graphics.lineTo(vecLo[i + 1].x, vecLo[i + 1].y);
}
}
private function _cloneControlLines():void
{
var vec:Vector.<Point> = _vec;
var i:int;
_controlCanvasSub.graphics.clear();
for (i = 0; i < vec.length - 1; i++)
{
_controlCanvasSub.graphics.lineStyle(1, 0x5083fc, 0.5);
_controlCanvasSub.graphics.moveTo(vec[i].x, vec[i].y);
_controlCanvasSub.graphics.lineTo(vec[i + 1].x, vec[i + 1].y);
}
}
private function _digit(value:Number, ratio:Number = 100):Number
{
return Math.round(value * ratio) / ratio;
}
private function canvas_mouseDownHandler(event:MouseEvent):void
{
var p:Point;
if (_toolType == ToolType.PLUS)
{
p = new Point(
_controlCanvas.mouseX / PERCENT_RECT.width,
1 - _controlCanvas.mouseY / PERCENT_RECT.height);
_currentKnob = new BezierPoint(this, new Point(p.x, p.y), false, false, new Point(p.x, p.y), new Point(p.x, p.y));
_currentKnob.addEventListener(KnobEvent.DRAG_START, knob_dragStartHandler);
_currentKnob.addEventListener(KnobEvent.DRAG_CHANGE, knob_dragChangeHandler);
_currentKnob.addEventListener(KnobEvent.DRAG_END, knob_dragEndHandler);
_controlCanvas.addChild(_currentKnob);
_controls.push(_currentKnob);
stage.addEventListener(MouseEvent.MOUSE_MOVE, canvas_mouseMoveHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, canvas_mouseUpHandler);
}
if (_toolType == ToolType.PLUS)
{
p = new Point(
_controlCanvas.mouseX / PERCENT_RECT.width,
1 - _controlCanvas.mouseY / PERCENT_RECT.height);
_currentKnob = new BezierPoint(this, new Point(p.x, p.y), false, false, new Point(p.x, p.y), new Point(p.x, p.y));
_currentKnob.addEventListener(KnobEvent.DRAG_START, knob_dragStartHandler);
_currentKnob.addEventListener(KnobEvent.DRAG_CHANGE, knob_dragChangeHandler);
_currentKnob.addEventListener(KnobEvent.DRAG_END, knob_dragEndHandler);
_controlCanvas.addChild(_currentKnob);
_controls.push(_currentKnob);
stage.addEventListener(MouseEvent.MOUSE_MOVE, canvas_mouseMoveHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, canvas_mouseUpHandler);
}
}
private function canvas_mouseMoveHandler(event:MouseEvent):void
{
_currentKnob.useControlPoint = true;
_currentKnob.updateControlPoint(_controlCanvas.mouseX, _controlCanvas.mouseY);
_updateAll();
//event.updateAfterEvent();
}
private function canvas_mouseUpHandler(event:MouseEvent):void
{
_currentKnob = null;
stage.removeEventListener(MouseEvent.MOUSE_MOVE, canvas_mouseMoveHandler);
stage.removeEventListener(MouseEvent.MOUSE_UP, canvas_mouseUpHandler);
_updateAll();
}
private function knob_dragStartHandler(event:Event):void
{
_cloneControlLines();
_controlCanvasSub.visible = true;
}
private function knob_dragChangeHandler(event:Event):void
{
var t:BezierPoint = (event.currentTarget as BezierPoint);
t.tx = t.x / GRAPH_RECT.width;
t.ty = (GRAPH_RECT.height - t.y) / GRAPH_RECT.height;
_updateControlLines();
}
private function knob_dragEndHandler(event:Event):void
{
var t:BezierPoint = (event.currentTarget as BezierPoint);
t.tx = t.x / GRAPH_RECT.width;
t.ty = (GRAPH_RECT.height - t.y) / GRAPH_RECT.height;
_updateAll();
_controlCanvasSub.visible = false;
}
}
/**
* BezierPointクラスは、ベジェの制御点です。
* @author yasu
*/
internal class BezierPoint extends Sprite
{
//----------------------------------------------------------
//
// Static Property
//
//----------------------------------------------------------
private static const FIT_HEIGHT:uint = 4;
//----------------------------------------------------------
//
// Constructor
//
//----------------------------------------------------------
/**
* 新しい BezierPoint インスタンスを作成します。
*
* @param parentObj
* @param main
* @param lock
* @param useControlPoint
* @param pointPre
* @param pointPost
*/
public function BezierPoint(parentObj:BezierLineMaker, main:Point, lock:Boolean, useControlPoint:Boolean, pointPre:Point, pointPost:Point):void
{
this._parentObj = parentObj;
this.lock = lock;
this.pointPre = pointPre;
this.pointPost = pointPost;
_knobCenter = new KnobCenter();
addChild(_knobCenter);
if (!lock)
_knobCenter.buttonMode = true;
_controlPre = new ControlKnob();
_controlPre.buttonMode = true;
if (pointPre)
addChild(_controlPre);
_controlPost = new ControlKnob();
if (pointPost)
addChild(_controlPost);
this.x = main.x * (BezierLineMaker.PERCENT_RECT.width);
this.y = (1 - main.y) * (BezierLineMaker.PERCENT_RECT.height);
if (pointPre)
{
_controlPre.x = pointPre.x * (BezierLineMaker.PERCENT_RECT.width) - this.x;
_controlPre.y = (1 - pointPre.y) * (BezierLineMaker.PERCENT_RECT.height) - this.y;
}
if (pointPost)
{
_controlPost.x = pointPost.x * (BezierLineMaker.PERCENT_RECT.width) - this.x;
_controlPost.y = (1 - pointPost.y) * (BezierLineMaker.PERCENT_RECT.height) - this.y;
}
_knobCenter.addEventListener(MouseEvent.MOUSE_DOWN, _mainPoint_mouseDownHandler);
_controlPre.addEventListener(MouseEvent.MOUSE_DOWN, _controlPre_mouseDownHandler);
_controlPost.addEventListener(MouseEvent.MOUSE_DOWN, _controlPost_mouseDownHandler);
graphics.clear();
_drawLine(_knobCenter, _controlPre);
_drawLine(_knobCenter, _controlPost);
this.useControlPoint = useControlPoint;
}
//----------------------------------------------------------
//
// Property
//
//----------------------------------------------------------
public function get controlPointPost():Point
{
return new Point(this.x + _controlPost.x, this.y + _controlPost.y);
}
public function get controlPointPostNormaled():Point
{
return new Point(
(this.x + _controlPost.x) / BezierLineMaker.PERCENT_RECT.width,
(BezierLineMaker.PERCENT_RECT.height - this.y - _controlPost.y) / BezierLineMaker.PERCENT_RECT.height);
}
public function get controlPointPre():Point
{
return new Point(this.x + _controlPre.x, this.y + _controlPre.y);
}
public function get controlPointPreNormaled():Point
{
return new Point(
(this.x + _controlPre.x) / BezierLineMaker.PERCENT_RECT.width,
(BezierLineMaker.PERCENT_RECT.height - this.y - _controlPre.y) / BezierLineMaker.PERCENT_RECT.height);
}
/** 編集可能かどうかを取得または設定します。 */
public var lock:Boolean;
/** コントロールポイント */
public var pointPost:Point;
/** コントロールポイント */
public var pointPre:Point;
public function set scalePoint(value:Number):void
{
_knobCenter.scaleX = _knobCenter.scaleY = 1 / value;
_controlPost.scaleX = _controlPost.scaleY = 1 / value;
_controlPre.scaleX = _controlPre.scaleY = 1 / value;
}
public var tx:Number;
public var ty:Number;
//--------------------------------------
// useControlPoint
//--------------------------------------
private var _useControlPoint:Boolean;
/**
*
*/
public function get useControlPoint():Boolean
{
return _useControlPoint;
}
/**
* @private
*/
public function set useControlPoint(value:Boolean):void
{
_useControlPoint = value;
_controlPost.visible = _useControlPoint;
_controlPre.visible = _useControlPoint;
}
private var _controlPost:ControlKnob;
private var _controlPre:ControlKnob;
private var _currentDrag:Sprite;
private var _knobCenter:KnobCenter;
private var _oldPoint:Point;
private var _parentObj:BezierLineMaker;
//----------------------------------------------------------
//
// Function
//
//----------------------------------------------------------
/**
* @inheritDoc
*/
public function dispose():void
{
_knobCenter.removeEventListener(MouseEvent.MOUSE_DOWN, _mainPoint_mouseDownHandler);
_controlPre.removeEventListener(MouseEvent.MOUSE_DOWN, _controlPre_mouseDownHandler);
_controlPost.removeEventListener(MouseEvent.MOUSE_DOWN, _controlPost_mouseDownHandler);
pointPost = null;
pointPre = null;
_knobCenter = null;
_controlPre = null;
_controlPost = null;
_currentDrag = null;
_oldPoint = null;
_parentObj = null;
}
/**
*
* @return
*
*/
public function toPoint():Point
{
return new Point(this.x, this.y);
}
/**
*
* @return
*
*/
public function toNormalPoint():Point
{
var xx:Number = (this.x) / BezierLineMaker.PERCENT_RECT.width;
var yy:Number = (BezierLineMaker.PERCENT_RECT.height - this.y) / BezierLineMaker.PERCENT_RECT.height;
return new Point(xx, yy);
}
public function updateControlPoint(mx:Number, my:Number):void
{
var rot:Number = Math.atan2(my - this.y, mx - this.x);
var t:Point = new Point(mx, my);
var me:Point = new Point(this.x, this.y);
var post:Point = Point.polar(Point.distance(t, me), rot);
_controlPost.x = post.x;
_controlPost.y = post.y;
var pre:Point = Point.polar(Point.distance(t, me), rot - Math.PI);
_controlPre.x = pre.x;
_controlPre.y = pre.y;
graphics.clear();
_drawLine(_knobCenter, _controlPre);
_drawLine(_knobCenter, _controlPost);
}
public function toBezierPointData():BezierPointData
{
var o:BezierPointData = new BezierPointData();
o.main = toNormalPoint();
o.post = this.controlPointPostNormaled;
o.pre = this.controlPointPreNormaled;
return o;
}
private function _controlPost_mouseDownHandler(event:MouseEvent):void
{
switch (_parentObj.toolType)
{
case ToolType.ARROW:
{
_currentDrag = _controlPost;
_currentDrag.startDrag(true);
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
dispatchEvent(new KnobEvent(KnobEvent.DRAG_START));
break;
}
case ToolType.CHANGE:
{
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
dispatchEvent(new KnobEvent(KnobEvent.DRAG_START));
break;
}
}
event.stopPropagation();
}
private function _controlPre_mouseDownHandler(event:MouseEvent):void
{
switch (_parentObj.toolType)
{
case ToolType.ARROW:
{
_currentDrag = _controlPre;
_currentDrag.startDrag(true);
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
dispatchEvent(new KnobEvent(KnobEvent.DRAG_START));
break;
}
case ToolType.CHANGE:
{
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
dispatchEvent(new KnobEvent(KnobEvent.DRAG_START));
break;
}
}
event.stopPropagation();
}
private function _mainPoint_mouseDownHandler(event:MouseEvent):void
{
if (lock)
return;
switch (_parentObj.toolType)
{
case ToolType.ARROW:
{
_currentDrag = this;
_currentDrag.startDrag(true);
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
event.stopPropagation();
dispatchEvent(new KnobEvent(KnobEvent.DRAG_START));
break;
}
case ToolType.PLUS:
break;
case ToolType.PLUS:
{
event.stopPropagation();
break;
}
case ToolType.DELETE:
{
_parentObj.deletePoint(this);
event.stopPropagation();
break;
}
case ToolType.CHANGE:
{
if (event.altKey)
{
this.useControlPoint = false;
_controlPre.x = 0;
_controlPre.y = 0;
_controlPost.x = 0;
_controlPost.y = 0;
graphics.clear();
event.stopPropagation();
dispatchEvent(new KnobEvent(KnobEvent.DRAG_END));
}
else
{
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
event.stopPropagation();
dispatchEvent(new KnobEvent(KnobEvent.DRAG_START));
}
break;
}
}
}
private function menuItem_selectHandler(event:ContextMenuEvent):void
{
_parentObj.deletePoint(this);
}
private function mouseMoveHandler(event:MouseEvent):void
{
switch (_parentObj.toolType)
{
case ToolType.ARROW:
{
if (_currentDrag == this)
{
if (Math.abs(this.y) < FIT_HEIGHT)
this.y = 0;
if (Math.abs(this.y - BezierLineMaker.PERCENT_RECT.height) < FIT_HEIGHT)
this.y = BezierLineMaker.PERCENT_RECT.height;
}
graphics.clear();
_drawLine(_knobCenter, _controlPre);
_drawLine(_knobCenter, _controlPost);
dispatchEvent(new KnobEvent(KnobEvent.DRAG_CHANGE));
break;
}
case ToolType.CHANGE:
{
if (!this.useControlPoint)
this.useControlPoint = true;
updateControlPoint(parent.mouseX, parent.mouseY);
dispatchEvent(new KnobEvent(KnobEvent.DRAG_CHANGE));
break;
}
}
// event.updateAfterEvent();
}
private function mouseUpHandler(event:MouseEvent):void
{
switch (_parentObj.toolType)
{
case ToolType.ARROW:
{
_currentDrag.stopDrag();
if (_currentDrag == this)
{
if (Math.abs(this.y) < FIT_HEIGHT)
this.y = 0;
if (Math.abs(this.y - BezierLineMaker.PERCENT_RECT.height) < FIT_HEIGHT)
this.y = BezierLineMaker.PERCENT_RECT.height;
}
graphics.clear();
_drawLine(_knobCenter, _controlPre);
_drawLine(_knobCenter, _controlPost);
dispatchEvent(new KnobEvent(KnobEvent.DRAG_END));
break;
}
case ToolType.CHANGE:
{
dispatchEvent(new KnobEvent(KnobEvent.DRAG_END));
break;
}
}
stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
}
private function _drawLine(p0:DisplayObject, p1:DisplayObject, color:uint = 0x5083fc):void
{
graphics.lineStyle(1, color);
graphics.moveTo(p0.x, p0.y);
graphics.lineTo(p1.x, p1.y);
graphics.lineStyle();
}
}
internal class BezierPointData
{
//----------------------------------------------------------
//
// Constructor
//
//----------------------------------------------------------
public function BezierPointData()
{
}
//----------------------------------------------------------
//
// Property
//
//----------------------------------------------------------
public var main:Point;
public var post:Point;
public var pre:Point;
}
internal class ControlKnob extends Sprite
{
//----------------------------------------------------------
//
// Static Property
//
//----------------------------------------------------------
private static const COLOR:int = 0x0;
private static const RADIUS:int = 4;
private static const RADIUS_OVER:int = 8;
private static const SNAP_HEIGHT:uint = 14;
//----------------------------------------------------------
//
// Constructor
//
//----------------------------------------------------------
public function ControlKnob()
{
hit = new Shape();
hit.graphics.beginFill(COLOR, 0);
hit.graphics.drawRect(-SNAP_HEIGHT / 2, -SNAP_HEIGHT / 2, SNAP_HEIGHT, SNAP_HEIGHT);
def = new Shape();
def.graphics.lineStyle(1, 0x5083fc);
def.graphics.beginFill(0xFFFFFF);
def.graphics.drawRect(-RADIUS / 2 >> 0, -RADIUS / 2 >> 0, RADIUS, RADIUS);
def.graphics.endFill();
over = new Shape();
over.graphics.lineStyle(1, 0x5083fc);
over.graphics.beginFill(0xFFFFFF);
over.graphics.drawRect(-RADIUS_OVER / 2, -RADIUS_OVER / 2, RADIUS_OVER, RADIUS_OVER);
over.graphics.endFill();
addChild(hit);
addChild(def);
addChild(over);
over.visible = false;
buttonMode = true;
addEventListener(MouseEvent.ROLL_OVER, rollOverHandler);
addEventListener(MouseEvent.ROLL_OUT, rollOutHandler);
}
//----------------------------------------------------------
//
// Property
//
//----------------------------------------------------------
private var def:Shape;
private var hit:Shape;
private var over:Shape;
//----------------------------------------------------------
//
// Function
//
//----------------------------------------------------------
/**
* @inheritDoc
*/
public function dispose():void
{
removeEventListener(MouseEvent.ROLL_OVER, rollOverHandler);
removeEventListener(MouseEvent.ROLL_OUT, rollOutHandler);
}
protected function rollOverHandler(event:MouseEvent):void
{
over.visible = true;
}
protected function rollOutHandler(event:MouseEvent):void
{
over.visible = false;
}
}
internal class CourseEvent extends Event
{
//----------------------------------------------------------
//
// Static Property
//
//----------------------------------------------------------
public static const COURSE_CHANGE:String = "courseChange";
//----------------------------------------------------------
//
// Constructor
//
//----------------------------------------------------------
public function CourseEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false)
{
super(type, bubbles, cancelable);
}
}
internal class KnobCenter extends Sprite
{
//----------------------------------------------------------
//
// Static Property
//
//----------------------------------------------------------
private static const COLOR:int = 0x0;
private static const RADIUS:int = 4;
private static const RADIUS_OVER:int = 8;
private static const SNAP_HEIGHT:uint = 14;
//----------------------------------------------------------
//
// Constructor
//
//----------------------------------------------------------
public function KnobCenter()
{
hit = new Shape();
hit.graphics.beginFill(COLOR, 0);
hit.graphics.drawRect(-SNAP_HEIGHT / 2, -SNAP_HEIGHT / 2, SNAP_HEIGHT, SNAP_HEIGHT);
hit.graphics.endFill();
def = new Shape();
def.graphics.beginFill(0x5083fc);
def.graphics.drawRect(-RADIUS / 2 >> 0, -RADIUS / 2 >> 0, RADIUS, RADIUS);
def.graphics.endFill();
over = new Shape();
over.graphics.beginFill(0x5083fc);
over.graphics.drawRect(-RADIUS_OVER / 2, -RADIUS_OVER / 2, RADIUS_OVER, RADIUS_OVER);
over.graphics.endFill();
addChild(hit);
addChild(def);
addChild(over);
over.visible = false;
buttonMode = true;
addEventListener(MouseEvent.ROLL_OVER, rollOverHandler);
addEventListener(MouseEvent.ROLL_OUT, rollOutHandler);
}
//----------------------------------------------------------
//
// Property
//
//----------------------------------------------------------
private var def:Shape;
private var hit:Shape;
private var over:Shape;
//----------------------------------------------------------
//
// Function
//
//----------------------------------------------------------
/**
* @inheritDoc
*/
public function dispose():void
{
removeEventListener(MouseEvent.ROLL_OVER, rollOverHandler);
removeEventListener(MouseEvent.ROLL_OUT, rollOutHandler);
}
protected function rollOverHandler(event:MouseEvent):void
{
over.visible = true;
}
protected function rollOutHandler(event:MouseEvent):void
{
over.visible = false;
}
}
/** つまみのイベントを管理するイベントクラスです。 */
internal class KnobEvent extends Event
{
//----------------------------------------------------------
//
// Static Property
//
//----------------------------------------------------------
/** ドラッグの開始イベントを示す定数です。 */
public static const DRAG_START:String = "DRAG_START";
/** ドラッグ中に変更があったことを示す定数です。 */
public static const DRAG_CHANGE:String = "DRAG_CHANGE";
/** ドラッグの終了イベントを示す定数です。 */
public static const DRAG_END:String = "DRAG_END";
//----------------------------------------------------------
//
// Constructor
//
//----------------------------------------------------------
public function KnobEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false)
{
super(type, bubbles, cancelable);
}
}
/**
* 直線が交差しているか計算することのできるクラス
* @see http://yamasv.blog92.fc2.com/blog-entry-105.html
*/
internal class Line
{
//----------------------------------------------------------
//
// Static Property
//
//----------------------------------------------------------
/**
* 直線の交点(線分の交点ではない)
*/
public static function crossPoint(lhs:Line, rhs:Line):Point
{
var a:Point = new Point(lhs.p2.x - lhs.p1.x, lhs.p2.y - lhs.p1.y);
var b:Point = new Point(rhs.p2.x - rhs.p1.x, rhs.p2.y - rhs.p1.y);
var c:Point = new Point(rhs.p1.x - lhs.p1.x, rhs.p1.y - lhs.p1.y);
var result:Point = new Point();
var cross_b_c:Number = b.x * c.y - b.y * c.x;
var cross_b_a:Number = b.x * a.y - b.y * a.x;
if (cross_b_a == 0)
return null;
result.x = lhs.p1.x + a.x * cross_b_c / cross_b_a;
result.y = lhs.p1.y + a.y * cross_b_c / cross_b_a;
return result;
}
public static function isCross(lhs:Line, rhs:Line):Boolean
{
var p:Point = crossPoint(lhs, rhs);
return (p != null &&
(p.x - rhs.p1.x) * (p.x - rhs.p2.x) + (p.y - rhs.p1.y) * (p.y - rhs.p2.y) < 0) &&
((p.x - lhs.p1.x) * (p.x - lhs.p2.x) + (p.y - lhs.p1.y) * (p.y - lhs.p2.y) < 0);
}
//----------------------------------------------------------
//
// Constructor
//
//----------------------------------------------------------
public function Line(p1:Point, p2:Point)
{
this.p1 = p1;
this.p2 = p2;
}
//----------------------------------------------------------
//
// Property
//
//----------------------------------------------------------
public var p1:Point;
public var p2:Point;
}
/**
* LineMaker はラインを描くためのクラスです。
* @author yasu
* @version 1.0.0
* @since 2010/5/11
*/
internal class LineUtil
{
//----------------------------------------------------------
//
// Static Property
//
//----------------------------------------------------------
/**
* ビットマップ塗りのラインを描きます。
* @param g グラフィック
* @param vec パスの頂点を格納した配列
* @param _bmd ビットマップデータ
* @param _lineWidth ライン幅
*
*/
public static function drawBitmapLine(g:Graphics, vec:Vector.<Point>, _bmd:BitmapData, _lineWidth:Number, _showUV:Boolean):void
{
var topPoints:Vector.<Point> = new Vector.<Point>();
var bottomPoints:Vector.<Point> = new Vector.<Point>();
var len:int = vec.length;
var i:int;
for (i = 0; i < len; i++)
{
var angle:Number;
if (i == 0)
angle = Math.atan2(vec[i + 1].y - vec[len - 1].y, vec[i + 1].x - vec[len - 1].x);
else if (i == vec.length - 1)
angle = Math.atan2(vec[0].y - vec[i - 1].y, vec[0].x - vec[i - 1].x);
else
angle = Math.atan2(vec[i + 1].y - vec[i - 1].y, vec[i + 1].x - vec[i - 1].x);
var tPoint:Point = Point.polar(_lineWidth, angle + Math.PI / 2);
tPoint.x += vec[i].x;
tPoint.y += vec[i].y;
var bPoint:Point = Point.polar(_lineWidth, angle - Math.PI / 2);
bPoint.x += vec[i].x;
bPoint.y += vec[i].y;
topPoints.push(tPoint);
bottomPoints.push(bPoint);
}
var indices:Vector.<int> = new Vector.<int>();
indices.push(0, 1, 2);
indices.push(1, 2, 3);
var uvtData:Vector.<Number> = new Vector.<Number>();
uvtData.push(0, 0, 1, 0);
uvtData.push(0, 1, 1, 1);
for (i = 0; i < topPoints.length; i++)
{
var next:int = i + 1;
if (next == topPoints.length)
next = 0;
if(_showUV) g.lineStyle(1, 0x0);
g.beginBitmapFill(_bmd, null, true, true);
var vertieces:Vector.<Number> = new Vector.<Number>();
vertieces.push(topPoints[i].x, topPoints[i].y, topPoints[next].x, topPoints[next].y);
vertieces.push(bottomPoints[i].x, bottomPoints[i].y, bottomPoints[next].x, bottomPoints[next].y);
g.drawTriangles(vertieces, indices, uvtData);
g.endFill();
}
}
}
/** ツールの種類を定義したクラスです。 */
internal class ToolType
{
//----------------------------------------------------------
//
// Static Property
//
//----------------------------------------------------------
/** 選択可能なツールタイプを示す定数です。 */
public static const ARROW:String = "ARROW";
/** 削除可能なツールタイプを示す定数です。 */
public static const DELETE:String = "remove";
/** 追加可能なツールタイプを示す定数です。 */
public static const PLUS:String = "plus";
/** 変更可能なツールタイプを示す定数です。 */
public static const CHANGE:String = "change";
}
/**
* A Bezier segment consists of four Point objects that define a single cubic Bezier curve.
* The BezierSegment class also contains methods to find coordinate values along the curve.
* @playerversion Flash 9.0.28.0
* @langversion 3.0
* @keyword BezierSegment, Copy Motion as ActionScript
* @see ../../motionXSD.html Motion XML Elements
*/
internal class BezierSegment
{
//----------------------------------------------------------
//
// Static Property
//
//----------------------------------------------------------
/**
* Calculates the value of a one-dimensional cubic Bezier equation at a specific time.
* By contrast, a Bezier curve is usually two-dimensional
* and uses two of these equations, one for the x coordinate and one for the y coordinate.
*
* @param t The <code>time</code> or degree of progress along the curve, as a decimal value between <code>0</code> and <code>1</code>.
* <p><strong>Note:</strong> The <code>t</code> parameter does not necessarily move along the curve at a uniform speed. For example, a <code>t</code> value of <code>0.5</code> does not always produce a value halfway along the curve.</p>
*
* @param a The first value of the Bezier equation.
*
* @param b The second value of the Bezier equation.
*
* @param c The third value of the Bezier equation.
*
* @param d The fourth value of the Bezier equation.
*
* @return The value of the Bezier equation at the specified time.
* @playerversion Flash 9.0.28.0
* @langversion 3.0
* @keyword Bezier curve, node, Copy Motion as ActionScript
*/
public static function getSingleValue(t:Number, a:Number = 0, b:Number = 0, c:Number = 0, d:Number = 0):Number
{
return (t * t * (d - a) + 3 * (1 - t) * (t * (c - a) + (1 - t) * (b - a))) * t + a;
}
/**
* Calculates the coefficients for a cubic polynomial equation,
* given the values of the corresponding cubic Bezier equation.
*
* @param a The first value of the Bezier equation.
*
* @param b The second value of the Bezier equation.
*
* @param c The third value of the Bezier equation.
*
* @param d The fourth value of the Bezier equation.
*
* @return An array containing four number values,
* which are the coefficients for a cubic polynomial.
* The coefficients are ordered from the highest degree to the lowest,
* so the first number in the array would be multiplied by t^3, the second by t^2, and so on.
*
* @playerversion Flash 9.0.28.0
* @langversion 3.0
* @keyword Bezier curve, node, Copy Motion as ActionScript
* @see #getCubicRoots()
*/
public static function getCubicCoefficients(a:Number, b:Number, c:Number, d:Number):Array
{
return [-a + 3 * b - 3 * c + d,
3 * a - 6 * b + 3 * c,
-3 * a + 3 * b,
a];
}
/**
* Finds the real solutions, if they exist, to a cubic polynomial equation of the form: at^3 + bt^2 + ct + d.
* This method is used to evaluate custom easing curves.
*
* @param a The first coefficient of the cubic equation, which is multiplied by the cubed variable (t^3).
*
* @param b The second coefficient of the cubic equation, which is multiplied by the squared variable (t^2).
*
* @param c The third coefficient of the cubic equation, which is multiplied by the linear variable (t).
*
* @param d The fourth coefficient of the cubic equation, which is the constant.
*
* @return An array of number values, indicating the real roots of the equation.
* There may be no roots, or as many as three.
* Imaginary or complex roots are ignored.
* @playerversion Flash 9.0.28.0
* @langversion 3.0
* @keyword Bezier curve, node, Copy Motion as ActionScript
*/
public static function getCubicRoots(a:Number = 0, b:Number = 0, c:Number = 0, d:Number = 0):Array
{
// make sure we really have a cubic
if (!a)
return BezierSegment.getQuadraticRoots(b, c, d);
// normalize the coefficients so the cubed term is 1 and we can ignore it hereafter
if (a != 1)
{
b /= a;
c /= a;
d /= a;
}
var q:Number = (b * b - 3 * c) / 9; // won't change over course of curve
var qCubed:Number = q * q * q; // won't change over course of curve
var r:Number = (2 * b * b * b - 9 * b * c + 27 * d) / 54; // will change because d changes
// but parts with b and c won't change
// determine if there are 1 or 3 real roots using r and q
var diff:Number = qCubed - r * r; // will change
if (diff >= 0)
{
// avoid division by zero
if (!q)
return [0];
// three real roots
var theta:Number = Math.acos(r / Math.sqrt(qCubed)); // will change because r changes
var qSqrt:Number = Math.sqrt(q); // won't change
var root1:Number = -2 * qSqrt * Math.cos(theta / 3) - b / 3;
var root2:Number = -2 * qSqrt * Math.cos((theta + 2 * Math.PI) / 3) - b / 3;
var root3:Number = -2 * qSqrt * Math.cos((theta + 4 * Math.PI) / 3) - b / 3;
return [root1, root2, root3];
}
else
{
// one real root
var tmp:Number = Math.pow(Math.sqrt(-diff) + Math.abs(r), 1 / 3);
var rSign:int = (r > 0) ? 1 : r < 0 ? -1 : 0;
var root:Number = -rSign * (tmp + q / tmp) - b / 3;
return [root];
}
return [];
}
/**
* Finds the real solutions, if they exist, to a quadratic equation of the form: at^2 + bt + c.
*
* @param a The first coefficient of the quadratic equation, which is multiplied by the squared variable (t^2).
*
* @param b The second coefficient of the quadratic equation, which is multiplied by the linear variable (t).
*
* @param c The third coefficient of the quadratic equation, which is the constant.
*
* @return An array of number values, indicating the real roots of the equation.
* There may be no roots, or as many as two.
* Imaginary or complex roots are ignored.
* @playerversion Flash 9.0.28.0
* @langversion 3.0
* @keyword Bezier curve, node, Copy Motion as ActionScript
*/
public static function getQuadraticRoots(a:Number, b:Number, c:Number):Array
{
var roots:Array = [];
// make sure we have a quadratic
if (!a)
{
if (!b)
return [];
roots[0] = -c / b;
return roots;
}
var q:Number = b * b - 4 * a * c;
var signQ:int =
(q > 0) ? 1
: q < 0 ? -1
: 0;
if (signQ < 0)
return [];
else if (!signQ)
roots[0] = -b / (2 * a);
else
{
roots[0] = roots[1] = -b / (2 * a);
var tmp:Number = Math.sqrt(q) / (2 * a);
roots[0] -= tmp;
roots[1] += tmp;
}
return roots;
}
//----------------------------------------------------------
//
// Constructor
//
//----------------------------------------------------------
/**
* Constructor for BezierSegment instances.
*
* @param a The first point of the curve, a node.
*
* @param b The second point of the curve, a control point.
*
* @param c The third point of the curve, a control point.
*
* @param d The fourth point of the curve, a node.
* @playerversion Flash 9.0.28.0
* @langversion 3.0
* @keyword Bezier curve, node, Copy Motion as ActionScript
* @see #propertyDetail property details
*/
function BezierSegment(a:Point, b:Point, c:Point, d:Point)
{
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
//----------------------------------------------------------
//
// Property
//
//----------------------------------------------------------
/**
* The first point of the Bezier curve.
* It is a node, which means it falls directly on the curve.
* @playerversion Flash 9.0.28.0
* @langversion 3.0
* @keyword Bezier curve, node, Copy Motion as ActionScript
*/
public var a:Point;
/**
* The second point of the Bezier curve.
* It is a control point, which means the curve moves toward it,
* but usually does not pass through it.
* @playerversion Flash 9.0.28.0
* @langversion 3.0
* @keyword Bezier curve, node, Copy Motion as ActionScript
*/
public var b:Point;
/**
* The third point of the Bezier curve.
* It is a control point, which means the curve moves toward it,
* but usually does not pass through it.
* @playerversion Flash 9.0.28.0
* @langversion 3.0
* @keyword Bezier curve, node, Copy Motion as ActionScript
*/
public var c:Point;
/**
* The fourth point of the Bezier curve.
* It is a node, which means it falls directly on the curve.
* @playerversion Flash 9.0.28.0
* @langversion 3.0
* @keyword Bezier curve, node, Copy Motion as ActionScript
*/
public var d:Point;
//----------------------------------------------------------
//
// Function
//
//----------------------------------------------------------
/**
* Calculates the location of a two-dimensional cubic Bezier curve at a specific time.
*
* @param t The <code>time</code> or degree of progress along the curve, as a decimal value between <code>0</code> and <code>1</code>.
* <p><strong>Note:</strong> The <code>t</code> parameter does not necessarily move along the curve at a uniform speed. For example, a <code>t</code> value of <code>0.5</code> does not always produce a value halfway along the curve.</p>
*
* @return A point object containing the x and y coordinates of the Bezier curve at the specified time.
* @playerversion Flash 9.0.28.0
* @langversion 3.0
* @keyword Bezier curve, node, Copy Motion as ActionScript
*/
public function getValue(t:Number):Point
{
var ax:Number = this.a.x;
var x:Number = (t * t * (this.d.x - ax) + 3 * (1 - t) * (t * (this.c.x - ax) + (1 - t) * (this.b.x - ax))) * t + ax;
var ay:Number = this.a.y;
var y:Number = (t * t * (this.d.y - ay) + 3 * (1 - t) * (t * (this.c.y - ay) + (1 - t) * (this.b.y - ay))) * t + ay;
return new Point(x, y);
}
/**
* Finds the <code>y</code> value of a cubic Bezier curve at a given x coordinate.
* Some Bezier curves overlap themselves horizontally,
* resulting in more than one <code>y</code> value for a given <code>y</code> value.
* In that case, this method will return whichever value is most logical.
*
* Used by CustomEase and BezierEase interpolation.
*
* @param x An x coordinate that lies between the first and last point, inclusive.
*
* @param coefficients An optional array of number values that represent the polynomial
* coefficients for the Bezier. This array can be used to optimize performance by precalculating
* values that are the same everywhere on the curve and do not need to be recalculated for each iteration.
*
* @return The <code>y</code> value of the cubic Bezier curve at the given x coordinate.
* @playerversion Flash 9.0.28.0
* @langversion 3.0
* @keyword Bezier curve, Copy Motion as ActionScript
*/
public function getYForX(x:Number, coefficients:Array = null):Number
{
// Clamp to range between end points.
// The padding with the small decimal value is necessary to avoid bugs
// that result from reaching the limits of decimal precision in calculations.
// We have tests that demonstrate this.
if (this.a.x < this.d.x)
{
if (x <= this.a.x + 0.0000000000000001)
return this.a.y;
if (x >= this.d.x - 0.0000000000000001)
return this.d.y;
}
else
{
if (x >= this.a.x + 0.0000000000000001)
return this.a.y;
if (x <= this.d.x - 0.0000000000000001)
return this.d.y;
}
if (!coefficients)
coefficients = getCubicCoefficients(this.a.x, this.b.x, this.c.x, this.d.x);
// x(t) = a*t^3 + b*t^2 + c*t + d
var roots:Array = getCubicRoots(coefficients[0], coefficients[1], coefficients[2], coefficients[3] - x);
var time:Number = NaN;
if (roots.length == 0)
time = 0;
else if (roots.length == 1)
time = roots[0];
else
{
for each (var root:Number in roots)
{
if (0 <= root && root <= 1)
{
time = root;
break;
}
}
}
if (isNaN(time))
return NaN;
var y:Number = getSingleValue(time, this.a.y, this.b.y, this.c.y, this.d.y);
return y;
}
}