IKBone Ver2 (角度制限付き)
IKに角度制限をつけたVerです。
ちょっと動きがぬるっとしてますが、仕様ですw
ボーン選択後プロパティでボーンの長さや制限角度が修正できます。
また、フルスクリーンにすると、右に、IKボーンツリーが表示しますので、
キーボーン(IKの終点)のオン・オフが出来るようになります。
Ctrlキーを押しながら、ボーンを動かすと、FKになります。
そのうちImage Mesh Generatorと結合させる予定。
http://wonderfl.net/c/voB0
@author narutohyper
/**
* Copyright narutohyper ( http://wonderfl.net/user/narutohyper )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/scrW
*/
package
{
/* IKに角度制限をつけたVerです。
* ちょっと動きがぬるっとしてますが、仕様ですw
* ボーン選択後プロパティでボーンの長さや制限角度が修正できます。
*
* また、フルスクリーンにすると、右に、IKボーンツリーが表示しますので、
* キーボーン(IKの終点)のオン・オフが出来るようになります。
*
* Ctrlキーを押しながら、ボーンを動かすと、FKになります。
*
* そのうちImage Mesh Generatorと結合させる予定。
* http://wonderfl.net/c/voB0
*
*
*
* @author narutohyper
*/
import flash.display.Loader;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.system.ApplicationDomain;
import flash.events.Event
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.display.BitmapData;
[SWF(width = 465, height = 465, frameRate = 60)]
public class Main extends Sprite {
private var ika:IKArmature2d;
private var test:uint = 0;
private var boneTree:IKBoneTree;
private var property:propertyPanel;
private var imgLoader:Loader
public function Main() {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
//Sample画像の読み込み
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.quality = StageQuality.HIGH;
//---------------------------------
//アーマチュア(枠組み)の作成
//---------------------------------
ika=new IKArmature2d()
this.addChild(ika);
//rootJointの作成
//3つのJointを作成
if (test) {
ika.setBone(new IKBone2d('test1', 100, 0,-90,90));
ika.setBone(new IKBone2d('test2', 50, 700, -120, 120));
ika.setBone(new IKBone2d('test3', 50, 700,-120,120));
ika.joint('root', 'test1');
ika.joint('test1', 'test2');
ika.joint('test2', 'test3');
} else {
//-------------------------------------------------
//ボーンの作成
//new IKBone2d('ID名':String,長さ:Number,初期角度:Number,稼動範囲角度(負):Number,稼動範囲角度(正):Number)
//ボーンをアーマチュアに登録
//IKArmature2d.set(IKBone2d);
//初期角度、稼動範囲は、親ボーン角度からの相対
//初期角度が、稼動範囲を超える場合は修正される
//-------------------------------------------------
var colors:Array=new Array(0xFFFF00,0x00FF00,0x0000FF,0xFF00FF,0x00FFFF);
ika.setBone(new IKBone2d('body', 43, 0,-30,30));
ika.setBone(new IKBone2d('breast', 48, 0,-30,30));
ika.setBone(new IKBone2d('waistRight', 33, 134,134,134,true));
ika.setBone(new IKBone2d('upperFootRight', 99, 46,-60,60));
ika.setBone(new IKBone2d('lowerFootRight', 116, -1,-1,170));
ika.setBone(new IKBone2d('ankleRight', 30, -10,-30,30));
ika.setBone(new IKBone2d('shoulderRight',33,100,80,100,true));
ika.setBone(new IKBone2d('upperArmRight',65,25));
ika.setBone(new IKBone2d('lowerArmRight',58,-4));
ika.setBone(new IKBone2d('handRight', 42, -8,-30,30));
ika.setBone(new IKBone2d('waistLeft', 33, -134,-134,-134,true));
ika.setBone(new IKBone2d('upperFootLeft', 99, -46,-60,60));
ika.setBone(new IKBone2d('lowerFootLeft', 116, 1,-170,1));
ika.setBone(new IKBone2d('ankleLeft', 30, 10,-30,30));
ika.setBone(new IKBone2d('shoulderLeft',33,-100,-100,-80,true));
ika.setBone(new IKBone2d('upperArmLeft',65,-25));
ika.setBone(new IKBone2d('lowerArmLeft',58,4));
ika.setBone(new IKBone2d('handLeft', 42, 8,-30,30));
ika.setBone(new IKBone2d('neck',30,0,-30,30,true));
ika.setBone(new IKBone2d('head',50,0,-30,30));
//-------------------------------------------------
//ボーンをジョイント
//IKArmature2d.joint('親Bone名 Or root','子Bone名'
//-------------------------------------------------
ika.joint('root', 'body');
ika.joint('root', 'waistRight');
ika.joint('root', 'waistLeft');
ika.joint('body', 'breast');
ika.joint('breast', 'neck');
ika.joint('neck', 'head');
ika.joint('breast', 'shoulderRight');
ika.joint('breast', 'shoulderLeft');
ika.joint('shoulderRight','upperArmRight');
ika.joint('shoulderLeft', 'upperArmLeft');
ika.joint('upperArmRight','lowerArmRight');
ika.joint('upperArmLeft','lowerArmLeft');
ika.joint('lowerArmRight','handRight');
ika.joint('lowerArmLeft', 'handLeft');
ika.joint('waistRight','upperFootRight');
ika.joint('waistLeft','upperFootLeft');
ika.joint('upperFootRight','lowerFootRight');
ika.joint('upperFootLeft', 'lowerFootLeft');
ika.joint('lowerFootRight','ankleRight');
ika.joint('lowerFootLeft','ankleLeft');
}
ika.init()
ika.x=250
ika.y=184
//ika.rotation=45
boneTree = new IKBoneTree(ika);
this.addChild(boneTree)
boneTree.x = 500;
boneTree.y = 10;
property = new propertyPanel(ika);
this.addChild(property)
property.x = 10;
property.y = this.stage.stageHeight - property.height - 10;
}
}
}
//---------------------------------------------------------
//IKTreeパネル
//---------------------------------------------------------
import flash.display.Sprite;
import flash.display.LineScaleMode;
import flash.events.Event;
import flash.text.*;
class IKBoneTree extends Sprite {
private var _ika:IKArmature2d;
public function IKBoneTree($ika:IKArmature2d) {
_ika=$ika
var rootObj:IKBone2d = _ika.bones['root'];
var rootNode:IKBoneTreeNode = new IKBoneTreeNode(_ika,rootObj,false);
this.addChild(rootNode);
}
public function changeKey(id:String,onFlag:Boolean):void {
_ika.bones[id].keyBone = onFlag;
}
}
class IKBoneTreeNode extends Sprite {
public var _ika:IKArmature2d
public function IKBoneTreeNode($ika:IKArmature2d,$tempBone:IKBone2d, $mode:Boolean = true) {
_ika = $ika;
var i:uint;
var nextY:Number = 0;
var checkLabel:myCheckBox = new myCheckBox($tempBone.id);
checkLabel.addEventListener(myCheckBox.CHANGE,change)
if($tempBone.keyBone) {
checkLabel.onClick()
}
this.addChild(checkLabel);
nextY += checkLabel.height;
this.graphics.lineStyle(0, 0x666666);
if ($mode) {
this.graphics.moveTo(-12, 10);
this.graphics.lineTo(0,10);
}
var nodes:Array=new Array()
for (i=0;i<$tempBone.childs.length;i++) {
nodes[i] = new IKBoneTreeNode(_ika,$tempBone.childs[i]);
this.addChild(nodes[i]);
nodes[i].x = 20;
nodes[i].y = nextY;
nextY += nodes[i].height + 1;
}
if (nodes[i-1]) {
nextY -= nodes[i - 1].height
this.graphics.moveTo(7, Math.floor(checkLabel.height / 2));
this.graphics.lineTo(7, nextY+10);
}
if ($mode==false) {
trace('------',i,nextY)
}
}
private function change(e:Event):void {
var _parentClass:IKBoneTree = e.target as IKBoneTree
var tempMc:myCheckBox = e.target as myCheckBox
_ika.bones[tempMc.str].keyBone = tempMc.onFlag;
}
}
//---------------------------------------------------------
//簡易プロパティパネル
//---------------------------------------------------------
class propertyPanel extends Sprite {
private var _ika:IKArmature2d;
private var _setObject:IKBone2d;
private var _uiA:Sprite
private var _uiB:Sprite
private var _sliderL:DraggableTextInput
private var _sliderR:DraggableTextInput
private var _sliderMin:DraggableTextInput
private var _sliderMax:DraggableTextInput
private var _sliderX:DraggableTextInput
private var _sliderY:DraggableTextInput
private var objNameA:TextField
private var objNameB:TextField
public function propertyPanel($ika:IKArmature2d) {
graphics.lineStyle(0, 0x666666, 1, true, LineScaleMode.NONE);
graphics.beginFill(0xFFFFFF, 1);
graphics.drawRect(0, 0, 240, 100);
_ika = $ika;
_ika.addEventListener(IKArmature2d.MOUSE_DOWN,onMouseDown)
_uiA=new Sprite()
_uiB=new Sprite()
this.addChild(_uiA);
this.addChild(_uiB);
//------------------------------------
//ボーン情報の表示
//------------------------------------
//名前の表示
_uiA.addChild(label('ボーン名:',60,0))
objNameA = label('bone', 70, 0,'LEFT')
_uiA.addChild(objNameA);
//長さ
_uiA.addChild(label('長さ:', 60, 30))
_sliderL = new DraggableTextInput();
_sliderL.x = 60;
_sliderL.y = 27;
_sliderL.minimum = 20;
_sliderL.maximum = 300;
_sliderL.snapInterval = 1;
_uiA.addChild(_sliderL);
//角度
_uiA.addChild(label('角度:',160,30))
_sliderR = new DraggableTextInput();
_sliderR.x = 160;
_sliderR.y = 27;
_sliderR.minimum = -180;
_sliderR.maximum = 180;
_sliderR.value = 0;
_sliderR.snapInterval = 1;
_uiA.addChild(_sliderR);
//min角度
_uiA.addChild(label('最小角:',60,60))
_sliderMin = new DraggableTextInput();
_sliderMin.x = 60;
_sliderMin.y = 57;
_sliderMin.minimum = -180;
_sliderMin.maximum = 180;
_sliderMin.value = 0;
_sliderMin.snapInterval = 1;
_uiA.addChild(_sliderMin);
_uiA.visible=false
//max角度
_uiA.addChild(label('最大角:',160,60))
_sliderMax = new DraggableTextInput();
_sliderMax.x = 160;
_sliderMax.y = 57;
_sliderMax.minimum = -180;
_sliderMax.maximum = 180;
_sliderMax.value = 0;
_sliderMax.snapInterval = 1;
_uiA.addChild(_sliderMax);
//------------------------------------
//アーマチュア情報の表示
//------------------------------------
_uiB.addChild(label('ボーン名:',60,0))
objNameB = label('bone', 70, 0,'LEFT')
_uiB.addChild(objNameB);
//x
_uiB.addChild(label('x:',60,30))
_sliderX = new DraggableTextInput();
_sliderX.x = 60;
_sliderX.y = 27;
_sliderX.minimum = -5760;
_sliderX.maximum = 5760;
_sliderX.value = 0;
_uiB.addChild(_sliderX);
//y
_uiB.addChild(label('y:',160,30))
_sliderY = new DraggableTextInput();
_sliderY.x = 160;
_sliderY.y = 27;
_sliderY.minimum = -5760;
_sliderY.maximum = 5760;
_sliderY.value = 0;
_uiB.addChild(_sliderY);
_uiB.visible=false
_uiA.y = 10;
_uiB.y = 10;
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
stage.addEventListener(Event.RESIZE, onResize);
onResize()
}
private function onResize(e:Event=null):void {
y=stage.stageHeight-height-50;
}
public function label($str:String,$x:Number,$y:Number,$align:String='RIGHT'):TextField {
var lb:TextField=new TextField();
lb.autoSize=TextFieldAutoSize.LEFT;
lb.selectable=false;
lb.mouseEnabled=false;
var format:TextFormat=new TextFormat();
format.color=0x666666;
format.size=12;
format.font='_ゴシック';
lb.defaultTextFormat=format;
lb.text = $str;
if ($align=='RIGHT') {
lb.x = $x-lb.width;
} else {
lb.x = $x;
}
lb.y=$y;
return lb;
}
private function onMouseDown(e:Event=null):void {
if (_ika.startBone) {
_uiA.visible=true
_uiB.visible=false
//ボーン情報
_setObject = _ika.startBone;
//名前の表示
objNameA.text=_setObject.id
} else {
_uiA.visible=false
_uiB.visible=true
//無ければ、アーマチュア情報の表示
_setObject = null;
objNameB.text='root'
}
setProperty()
_ika.removeEventListener(IKArmature2d.MOUSE_DOWN,onMouseDown)
_ika.addEventListener(IKArmature2d.MOUSE_UP,onMouseUp)
_ika.addEventListener(IKArmature2d.MOUSE_MOVE,setProperty)
_sliderL.removeEventListener(Event.CHANGE, _onChangeA);
_sliderR.removeEventListener(Event.CHANGE, _onChangeA);
_sliderMin.removeEventListener(Event.CHANGE, _onChangeA);
_sliderMax.removeEventListener(Event.CHANGE, _onChangeA);
_sliderX.removeEventListener(Event.CHANGE, _onChangeB);
_sliderY.removeEventListener(Event.CHANGE, _onChangeB);
}
private function onMouseUp(e:Event=null):void {
_ika.addEventListener(IKArmature2d.MOUSE_DOWN,onMouseDown)
_ika.removeEventListener(IKArmature2d.MOUSE_UP,onMouseUp)
_ika.removeEventListener(IKArmature2d.MOUSE_MOVE,setProperty)
_sliderL.addEventListener(Event.CHANGE, _onChangeA);
_sliderR.addEventListener(Event.CHANGE, _onChangeA);
_sliderMin.addEventListener(Event.CHANGE, _onChangeA);
_sliderMax.addEventListener(Event.CHANGE, _onChangeA);
_sliderX.addEventListener(Event.CHANGE, _onChangeB);
_sliderY.addEventListener(Event.CHANGE, _onChangeB);
}
private function setProperty(e:Event=null):void {
if (_ika.startBone) {
_sliderL.value = _setObject.length;
_sliderR.value = _setObject.rotation;
_sliderMin.value = _setObject.minRotation;
_sliderMax.value = _setObject.maxRotation;
} else {
_sliderX.value = _ika.x;
_sliderY.value = _ika.y;
}
}
private function _onChangeA(event:Event):void {
_setObject.length=_sliderL.value;
_setObject.setRotationRange(_sliderMin.value,_sliderMax.value)
_setObject.boneRotation(_sliderR.value);
}
private function _onChangeB(event:Event):void {
_ika.x = _sliderX.value;
_ika.y = _sliderY.value;
}
}
//=======================================================================================
//IKアーマチュア(枠組み)
//=======================================================================================
import flash.display.Sprite;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.text.*
import flash.geom.Point;
import flash.geom.Matrix;
import flash.ui.Keyboard;
class IKArmature2d extends Sprite {
public static const MOUSE_DOWN:String = 'mouse_down';
public static const MOUSE_UP:String = 'mouse_up';
public static const MOUSE_MOVE:String = 'mouse_move';
private var _rootJoint:IKJoint2d;
private var _bones:Object;
private var target:Sprite;
private var IKBones:Array;
private var oldPoint:Point;
private var newPoint:Point;
private var tempVector:Array
private var tempArray:Array
public var startBone:IKBone2d;
public function IKArmature2d () {
//目標点
bones = new Object();
bones['root'] = new IKBone2d('root',0);
bones['root'].superRotation = -90;
bones['root'].setDegree(0);
bones['root'].addEventListener(IKBone2d.ROOT_DOWN,IKDrag)
this.addChild(bones['root']);
}
public function init():void {
bones['root'].move();
var i:int;
//配置の順番を変える
//rootから順番に
tempArray=new Array()
node(bones['root'])
for (i = tempArray.length - 1; i >= 0; i--) {
this.addChild(tempArray[i]);
}
}
public function node(bone:IKBone2d):void {
var i:uint;
tempArray.push(bone);
for (i=0;i<bone.childs.length;i++) {
node(bone.childs[i]);
}
}
public function setBone(value:IKBone2d):void {
bones[value.id]=value;
bones[value.id].addEventListener(IKBone2d.MOUSE_DOWN,IKStart)
}
public function joint($boneA:String = null, $boneB:String = null):void {
if (bones[$boneA] && bones[$boneB]) {
bones[$boneA].setChild(bones[$boneB]);
} else {
trace('Error:boneが存在しません。', $boneA + '=', bones[$boneA], $boneB + '=', bones[$boneB]);
}
}
public function set bones(value:Object):void {
_bones = value;
}
public function get bones():Object {
return _bones;
}
//-----------------------------------------------------
//IKBoneのドラッグでアーマチュアを動かす系
//-----------------------------------------------------
private function IKDrag(e:MouseEvent):void {
if (startBone) {
startBone.selectOff()
startBone = null;
}
stage.addEventListener(MouseEvent.MOUSE_MOVE, onIKDragMove);
stage.addEventListener(MouseEvent.MOUSE_UP, onIKDragUp);
dispatchEvent(new Event(MOUSE_DOWN));
}
private function onIKDragMove(e:MouseEvent):void {
x = stage.mouseX;
y = stage.mouseY;
dispatchEvent(new Event(MOUSE_MOVE));
}
private function onIKDragUp(e:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onIKDragMove);
stage.removeEventListener(MouseEvent.MOUSE_UP, onIKDragUp);
dispatchEvent(new Event(MOUSE_UP));
}
//-----------------------------------------------------
//IKBoneのドラッグでターゲットを動かすMouseEvent系
//-----------------------------------------------------
private function IKStart(e:MouseEvent):void {
//MouseDownしたObject
if (startBone) {
startBone.selectOff()
}
startBone = e.target as IKBone2d
startBone.selectOn()
//startBoneからkeyBone(無い場合はroot)までのIKbonesを作成する
if (tempVector) {
for (i = 0; i < tempVector.length; i++ ) {
this.removeChild(tempVector[i])
}
}
IKBones=new Array()
tempVector=new Array()
var tempBone:IKBone2d = startBone
if(e.ctrlKey) {
IKBones.push(tempBone)
} else {
while (true) {
if (tempBone) {
IKBones.push(tempBone)
if (tempBone.keyBone) {
break;
} else {
tempBone = tempBone.parentBone
}
} else {
break;
}
}
}
var i:uint
IKBones.reverse();
for (i = 0; i < IKBones.length; i++ ) {
IKBones[i].arrowVisible = true;
tempVector[i] = new Arrow(false)
this.addChild(tempVector[i])
}
oldPoint = newPoint;
newPoint=new Point(mouseX,mouseY);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
dispatchEvent(new Event(MOUSE_DOWN));
}
private function onMouseUp(e:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
var i:uint
startBone.setDefaultLength()
for (i=0; i < IKBones.length;i++ ) {
IKBones[i].arrowVisible = false;
}
dispatchEvent(new Event(MOUSE_UP));
}
private function onMouseMove(e:MouseEvent):void {
oldPoint = newPoint;
newPoint=new Point(mouseX,mouseY);
var startVector:Point=new Point((mouseX-startBone.boneTailPoint.x),(mouseY-startBone.boneTailPoint.y));
var targetPoint:Point
for (var i:int = IKBones.length - 1; i >= 0; i-- ) {
if (IKBones[i].id!='root') {
//------------------------------------------
//新しいポイントにボーンを向ける
//------------------------------------------
setVector(tempVector[i], IKBones[i].boneTailPoint, newPoint)
tension(tempVector[i],IKBones[i]);
targetPoint = tempVector[i].vector.add(IKBones[i].boneTailPoint)
lookAt(IKBones[i], targetPoint)
//------------------------------------------
//向けた方向でtailpointを取得、張力を再調査
//------------------------------------------
setVector(tempVector[i], IKBones[i].boneTailPoint, newPoint)
//本来戻るはずの位置(張力MAXの状態)で次のターゲットの位置を決めておく
newPoint = tempVector[i].vector.add(IKBones[i].point)
//張力を再調査
tension(tempVector[i],IKBones[i]);
//ボーンを一旦移動
if(i>0) {
IKBones[i].point = tempVector[i].vector.add(IKBones[i].point)
}
}
}
//------------------------------------------
//ボーンの位置を修正する
//------------------------------------------
if (IKBones[0].id!='root') {
IKBones[0].move()
} else {
IKBones[1].move()
}
dispatchEvent(new Event(MOUSE_MOVE));
}
private function tension($temp:Arrow,$bone:IKBone2d):void {
//張力:Max:boneの長さ
var t:Number = ($temp.length > $bone.length) ? $bone.length : $temp.length;
$temp.length = t*0.1
}
private function setVector($v:Arrow, $active:Point,$target:Point=null ):void {
var tempPoint:Point;
if (!$target) {
$target = new Point($active.x+$v.length,$active.y);
}
tempPoint = $target.subtract($active);
$v.length = Point.distance($active, $target);
$v.rotation = Math.atan2(tempPoint.y, tempPoint.x) * 180 / Math.PI;
$v.x=$active.x
$v.y=$active.y
}
private function lookAt($active:IKJoint2d,$target:Point):Boolean {
//$active(ジョイント)を$targetへ向ける
var nowPoint:Point;
var targetPoint:Point;
var tempPoint:Point;
nowPoint = $active.point;
targetPoint = new Point($target.x, $target.y);
tempPoint = targetPoint.subtract(nowPoint);
return $active.setAngle(Math.atan2(tempPoint.y, tempPoint.x) * 180 / Math.PI);
}
private function lookAtBone($active:IKBone2d,$target:Point):Boolean {
//$active(ジョイント)を$targetへ向ける
var nowPoint:Point;
var targetPoint:Point;
var tempPoint:Point;
nowPoint = $active.point;
targetPoint = new Point($target.x, $target.y);
tempPoint = targetPoint.subtract(nowPoint);
return $active.setAngle(Math.atan2(tempPoint.y, tempPoint.x) * 180 / Math.PI);
}
private function angle360(value:Number):Number {
return (value + 360) % 360
}
private function degree(value:Number):Number {
value += 180;
value %= 360;
value += 360;
value %= 360;
value -= 180;
return value;
}
public function Trace(... arguments):void {
//bg.appendText(arguments.toString()+"\n");
//trace(arguments);
}
}
import flash.display.Shape;
import flash.geom.Point;
import flash.geom.Matrix;
class Arrow extends Shape {
private var _length:Number;
private var _defColor:Number
private var _color:Number;
private var _test:Boolean;
public var _vector:Point
public function Arrow($test:Boolean=true,$color:uint = 0x666666, $length:Number = 100) {
_test=$test;
_color = $color;
_defColor = _color;
length = $length;
}
public function changeColor():void {
_color = 0xFF0000;
}
public function defColor():void {
_color = _defColor;
}
public function set length(value:Number):void {
_length = value;
if (_test) {
graphics.clear();
graphics.beginFill(_color);
graphics.moveTo(_length, 0);
graphics.lineTo(_length - 10,-6);
graphics.lineTo(_length - 10,6);
graphics.lineTo(_length, 0);
graphics.endFill();
graphics.lineStyle(0, _color);
graphics.lineTo(0, 0);
}
}
public function get length():Number {
return _length;
}
public function get vector():Point {
var result:Point = new Point(_length,0);
var mtx:Matrix = new Matrix();
mtx.rotate(rotation*Math.PI/180);
return mtx.transformPoint(result);
}
public function set point(value:Point):void {
x=value.x
y=value.y
}
public function get point():Point {
return new Point(x,y);
}
}
//=======================================================================================
//IKJoint
//=======================================================================================
//計算に必要な位置x,y、長さlength、ベクトルvectorPointと「制限角度」を持ったJoint
//テスト用では、制限角度、ベクトルが描画される
import flash.display.Sprite;
import flash.display.Shape;
import flash.geom.Point;
import flash.geom.Matrix;
class IKJoint2d extends Sprite {
private var _id:String;
private var _length:Number;
private var _arrow:Arrow;
private var _test:Boolean;
private var _visible:Boolean;
private var _x:Number;
private var _y:Number;
//ジョイント(Sprite)自体は回転させない。
//その為、回転は以下の変数で保持
private var _rotation:Number;
private var _rotationRange:Shape;
private var _minRotation:Number;
private var _maxRotation:Number;
private var _lockPoint:int=-1;
private var _lockRotation:String;
public var _circle:Shape;
public function IKJoint2d($visible:Boolean=false,$test:Boolean=true,$color:uint=0x6666666) {
arrow = new Arrow(true, $color);
arrow.name = 'arrow';
arrowVisible = $visible
if (id == 'root') {
_circle = new Shape();
_circle.graphics.beginFill(0x333399,0.5);
_circle.graphics.drawCircle(0, 0, 10);
addChild(_circle);
} else {
_circle = new Shape();
_circle.graphics.beginFill(0xFFFFFF,1);
_circle.graphics.drawCircle(0, 0, 2);
addChild(_circle);
}
length = 0;
_test = $test;
_rotationRange = new Shape();
_rotationRange.name = 'rotationRange';
x=0
y=0
}
//------------------------------------------------------------------------------
//Setter Getter
//------------------------------------------------------------------------------
//-------------------------------------------------------
//id
//-------------------------------------------------------
public function get id():String {
return _id;
}
public function set id(value:String):void {
_id = value;
}
override public function get y():Number {
return _y;
}
override public function set y(value:Number):void {
_y = value;
if (arrowVisible) {
super.y = _y;
}
}
override public function get x():Number {
return _x;
}
override public function set x(value:Number):void {
_x = value;
if (arrowVisible) {
super.x = _x;
}
}
public function get superX():Number {
return super.x;
}
public function set superX(value:Number):void {
super.x = value;
}
public function get superY():Number {
return super.y;
}
public function set superY(value:Number):void {
super.y = value;
}
//-----------------------------------------------------
//テスト用Arrowの表示
//-----------------------------------------------------
public function set arrowVisible(value:Boolean):void {
_visible = value;
if (_visible) {
addChild(_rotationRange);
addChild(arrow);
} else {
if (this.getChildByName('arrow')) {
removeChild(_rotationRange);
removeChild(arrow);
}
}
}
public function get arrowVisible():Boolean {
return _visible;
}
//-------------------------------------------------------
//clone
//-------------------------------------------------------
public function get clone():Object {
var result:Object = new Object();
result.vector2d=this.vector2d
result.minRotation = this.minRotation;
result.maxRotation = this.maxRotation;
result.superRotation = this.superRotation
result.id = this.id;
return result;
}
public function set clone(value:Object):void {
this.id = value.id;
this.vector2d = value.vector2d
this.superRotation=value.superRotation
this.setRotationRange(value.minRotation, value.maxRotation)
}
//-------------------------------------------------------
//Point
//-------------------------------------------------------
public function get point():Point {
return new Point(x, y);
}
public function set point(value:Point):void {
x = value.x;
y = value.y;
}
//-------------------------------------------------------
//長さ
//-------------------------------------------------------
public function get length():Number {
return _length;
}
public function set length(value:Number):void {
_length = value;
if(arrow) {
arrow.length = value;
}
}
//-------------------------------------------------------
//回転
//rotationでは、Joint自体は回転しない
//自身の回転は、親ボーンに依存する
//強制的に回転させる場合は、superRotationを使用
//-------------------------------------------------------
override public function set rotation(value:Number):void {
_rotation = value
}
override public function get rotation():Number {
return _rotation
}
public function set superRotation(value:Number):void {
super.rotation = value
}
public function get superRotation():Number {
return super.rotation
}
//-------------------------------------------------
//superRotationに依存しない角度の指定
//依存する設定はvectorRotation
//-------------------------------------------------
public function setAngle(value:Number):Boolean {
return vectorRotation(value-superRotation)
}
//------------------------------------------------
//ベクトル(Boneの終端)を返す
//------------------------------------------------
public function get tailPoint():Point {
//trace(id,'length=',length,'x=',int(x),'y=',int(y),'sR=',superRotation,'R=',rotation)
var result:Point = new Point(length,0);
var mtx:Matrix = new Matrix();
mtx.rotate(superRotation*Math.PI/180);
mtx.rotate(rotation*Math.PI/180);
mtx.translate(x, y);
result = mtx.transformPoint(result);
return result;
}
//------------------------------------------------
//vector2d
//------------------------------------------------
public function get vector2d():Vector2d {
return new Vector2d(x, y, length, rotation);
}
public function set vector2d(value:Vector2d):void {
x = value.x;
y = value.y;
length = value.length;
rotation = value.rotation;
}
//------------------------------------------------
//回転制限
//------------------------------------------------
public function get minRotation():Number {
return _minRotation;
}
public function set minRotation(value:Number):void {
_minRotation = value;
}
public function get maxRotation():Number {
return _maxRotation;
}
public function set maxRotation(value:Number):void {
_maxRotation = value;
}
//------------------------------------------------
//矢印
//------------------------------------------------
public function get arrow():Arrow {
return _arrow;
}
public function set arrow(value:Arrow):void {
_arrow = value;
}
//------------------------------------------------------------------------------
//publicメソッド
//------------------------------------------------------------------------------
//---------------------------------------------------
//テスト用
//---------------------------------------------------
public function active():void {
_circle.graphics.clear()
if (id == 'root') {
_circle.graphics.beginFill(0xFF0000,0.5);
_circle.graphics.drawCircle(0, 0, 10);
} else {
_circle.graphics.beginFill(0xFF0000,1);
_circle.graphics.drawCircle(0, 0, 4);
}
}
public function normal():void {
_circle.graphics.clear()
if (id == 'root') {
_circle.graphics.beginFill(0x339933,0.5);
_circle.graphics.drawCircle(0, 0, 10);
} else {
_circle.graphics.beginFill(0x339933,1);
_circle.graphics.drawCircle(0, 0, 4);
}
}
//-------------------------------------------------
//操作
//-------------------------------------------------
//-------------------------------------------------
//可動範囲を設定する
//-------------------------------------------------
public function setRotationRange($min:Number = -180, $max:Number = 180):void {
_minRotation = $min;
_maxRotation = $max;
if (_rotationRange) {
_rotationRange.graphics.clear();
_rotationRange.graphics.lineStyle(0, 0xFF0000);
_rotationRange.graphics.lineTo(Math.cos($min*Math.PI/180) * 20, Math.sin($min*Math.PI/180) * 20);
_rotationRange.graphics.lineStyle(0, 0x0000FF);
_rotationRange.graphics.moveTo(0, 0);
_rotationRange.graphics.lineTo(Math.cos($max * Math.PI / 180) * 20, Math.sin($max * Math.PI / 180) * 20);
}
}
//-------------------------------------------------
//矢印を回転させる。
//可動範囲を超えた場合は、可動範囲内でとどめ、falseを返す
//-------------------------------------------------
public function vectorRotation(value:Number):Boolean {
var result:Boolean = true;
//可動範囲の比較
if (degree(value) < _minRotation) {
arrow.rotation = _minRotation;
rotation=_minRotation
result = false;
} else if (degree(value) > _maxRotation) {
arrow.rotation = _maxRotation;
rotation=_maxRotation
result = false;
} else {
rotation = value
arrow.rotation = rotation;
}
return result
}
//-------------------------------------------------
//角度を常に-180~180で返す
//-------------------------------------------------
public function degree(value:Number):Number {
value += 180;
value %= 360;
value += 360;
value %= 360;
value -= 180;
return value;
}
//------------------------------------------------------------------------------
//privateメソッド
//------------------------------------------------------------------------------
}
//=======================================================================================
//IKBone
//=======================================================================================
import flash.display.Shape;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.*
import flash.geom.Point;
import flash.geom.Matrix;
class IKBone2d extends IKJoint2d {
public static const MOUSE_DOWN:String = 'mouse_down';
public static const ROOT_DOWN:String = 'root_down';
//new IKBone2d('ID名':String,長さ:Number,初期角度:Number,稼動範囲角度(負):Number,稼動範囲角度(正):Number,色)
public function IKBone2d($id:String,$length:Number=100,$angle:Number=0,$minAngle:Number=-180,$maxAngle:Number=180,$key:Boolean=false,$color:uint=0x66FF66) {
id = $id;
childs = new Array();
super(false,true,$color);
_sAngle=$angle
_sMinAngle=$minAngle
_sMaxAngle=$maxAngle
if (id != 'root') {
bone=new Bone(true,$color);
addChild(bone);
length = $length;
addEventListener(MouseEvent.MOUSE_DOWN, clickBone);
keyBone = $key;
var format:TextFormat=new TextFormat();
format.color=0x666666
format.size=14;
format.font='_ゴシック';
_Label = new TextField();
_Label.autoSize=TextFieldAutoSize.LEFT
_Label.selectable = false;
_Label.mouseEnabled = false;
_Label.multiline = false;
_Label.x=-5
_Label.y=10
_Label.defaultTextFormat=format
//addChild(Label);
_Label.text = id;
} else {
keyBone = true;
addEventListener(MouseEvent.MOUSE_DOWN, clickRoot);
}
this.addChild(_circle)
//trace(superRotation,rotation)
}
//------------------------------------------------------------------------------
//publicメソッド
//------------------------------------------------------------------------------
//---------------------------------------------------
//操作
//---------------------------------------------------
//------------------------------------------
//BoneをMouseDownした時のArrowの長さを元に戻す
//------------------------------------------
public function setDefaultLength():void {
_clickLength=0
arrow.length=length;
}
//------------------------------------------
//見た目を選択状態を元に戻す
//------------------------------------------
public function selectOff():void {
bone.selectOff()
}
//------------------------------------------
//見た目を選択状態にする
//------------------------------------------
public function selectOn():void {
bone.selectOn()
}
//-------------------------------------------------
//角度設定はrotationやrotationXを使わずにこちらを使う
//ただし、相対的な角度での設定になるため、見た目上での角度設定を行う場合は、
//setDegreeSpecialやsetAngle使用
//-------------------------------------------------
public function setDegree(value:Number):Boolean {
return boneRotation(degree(value))
}
//-----------------------------------------------------------------------
//設定の時など、y軸を中心に、左右対称に-180~180で角度を設定したい場合
//-----------------------------------------------------------------------
public function setDegreeSpecial(value:Number):Boolean {
return boneRotation(degree(value-90)-superRotation)
}
//-------------------------------------------------
//superRotationに依存しない角度の指定
//-------------------------------------------------
override public function setAngle(value:Number):Boolean {
return boneRotation(value-superRotation)
}
//-------------------------------------------------
//子のジョイントを登録する
//-------------------------------------------------
public function setChild(value:IKBone2d):void {
value.parentBone=this;
childs.push(value);
}
//-------------------------------------------------
//矢印(ボーン)を回転させる。
//可動範囲を超えた場合は、可動範囲内でとどめ、falseを返す
//-------------------------------------------------
public function boneRotation(value:Number):Boolean {
var result:Boolean
result = vectorRotation(value);
if (bone) {
bone.rotation=arrow.rotation
}
for (var i:uint = 0; i < childs.length; i++) {
childs[i].move()
}
return result;
}
//-------------------------------------------------
//親が動いた時に、親から呼ばれる
//-------------------------------------------------
public function move():void {
if (parentBone) {
x = parentBone.tailPoint.x
y = parentBone.tailPoint.y
super.superX=x
super.superY=y
superRotation = parentBone.rotation + parentBone.superRotation;
_Label.rotation = -superRotation
if (!rotation) {
setRotationRange(_sMinAngle,_sMaxAngle)
setDegree(_sAngle);
}
bone.rotation = arrow.rotation
}
for (var i:uint = 0; i < childs.length; i++) {
childs[i].move()
}
}
//------------------------------------------------------------------------------
//Setter Getter
//------------------------------------------------------------------------------
//-------------------------------------------------------
//
//-------------------------------------------------------
public function set parentBone(value:IKBone2d):void {
_parentBone = value;
}
public function get parentBone():IKBone2d {
return _parentBone;
}
public function get childs():Array {
return _childs;
}
public function set childs(value:Array):void {
_childs = value;
}
public function set bone(value:Bone):void {
_bone = value;
}
public function get bone():Bone {
return _bone;
}
//------------------------------------------------
//ベクトル(Boneの終端)を返す
//------------------------------------------------
public function get boneTailPoint():Point {
var result:Point
if (_clickLength) {
result = new Point(_clickLength,0);
} else {
result = new Point(length,0);
}
var mtx:Matrix = new Matrix();
mtx.rotate(superRotation*Math.PI/180);
mtx.rotate(rotation*Math.PI/180);
mtx.translate(x, y);
result = mtx.transformPoint(result);
return result;
}
//-------------------------------------------------------
//長さ
//-------------------------------------------------------
override public function set length(value:Number):void {
super.length = value;
if (bone) {
bone.length = value;
}
for (var i:uint = 0; i < childs.length; i++) {
childs[i].move()
}
}
//-------------------------------------------------------
//clone
//-------------------------------------------------------
override public function get clone():Object {
var result:Object = super.clone
result.parentBone = parentBone;
result.childs = childs;
return result;
}
override public function set clone(value:Object):void {
super.clone = value;
this.parentBone = value.parentBone;
this.childs = value.childs;
}
public function get keyBone():Boolean {
return _keyBone;
}
public function set keyBone(value:Boolean):void {
_keyBone = value;
if (bone) {
if (_keyBone) {
active()
} else {
normal()
}
}
}
//------------------------------------------------------------------------------
//privateメソッド
//------------------------------------------------------------------------------
private var _sAngle:Number
private var _sMinAngle:Number
private var _sMaxAngle:Number
private var _parentBone:IKBone2d;
private var _childs:Array;
private var _clickLength:Number;
private var _bone:Bone;
private var _Label:TextField;
private var _keyBone:Boolean=false
private function clickBone(e:MouseEvent):void {
_clickLength=bone.mouseX
if (_clickLength < 20) {
_clickLength = 20;
if (_clickLength > length) {
_clickLength = length;
}
}
arrow.length=_clickLength;
dispatchEvent(new MouseEvent(MOUSE_DOWN,true,false,NaN,NaN,null,e.ctrlKey));
}
private function clickRoot(e:MouseEvent):void {
dispatchEvent(new MouseEvent(ROOT_DOWN));
}
}
//=======================================================================================
//Bone
//=======================================================================================
import flash.display.Sprite;
class Bone extends Sprite {
private const SELECT_COLOR:uint = 0x0000FF;
private var _length:Number;
private var _color:uint;
private var _nowColor:uint;
private var _test:Boolean;
public function Bone($test:Boolean=true,$color:uint = 0x66FF66, $length:Number = 100) {
_test=$test;
_color = $color;
length = $length;
_nowColor = _color;
}
public function set length(value:Number):void {
_length = value;
makeBone()
}
public function selectOn():void {
_nowColor = SELECT_COLOR;
makeBone();
}
public function selectOff():void {
_nowColor = _color;
makeBone();
}
private function makeBone():void {
if (_test) {
//bone画像の生成
graphics.clear();
graphics.beginFill(_nowColor, 0.3);
graphics.moveTo(0, 0);
if (_length > 10) {
graphics.lineTo(5,-7);
graphics.lineTo(_length - 5,-2);
graphics.lineTo(_length,0);
graphics.lineTo(_length - 5,2);
graphics.lineTo(5,7);
} else {
graphics.lineTo(_length / 2, -7);
graphics.lineTo(_length - 5,-2);
graphics.lineTo(_length - 5,2);
graphics.lineTo(_length / 2, 7);
}
graphics.endFill();
graphics.beginFill(0xFFFFFF, 0);
graphics.drawCircle(_length, 0, 4);
}
}
public function get length():Number {
return _length;
}
}
//=======================================================================================
//Vector2d
//=======================================================================================
import flash.geom.Point;
class Vector2d extends Object {
private var _x:Number;
private var _y:Number;
private var _rotation:Number;
private var _length:Number;
public function Vector2d ($x:Number = 0, $y:Number = 0, $length:Number = 0, $rotation:Number = 0) {
x = $x;
y = $y;
length = $length;
rotation = $rotation;
}
public function get x():Number {
return _x;
}
public function set x(value:Number):void {
_x = value;
}
public function get y():Number {
return _y;
}
public function set y(value:Number):void {
_y = value;
}
public function get rotation():Number {
return _rotation;
}
public function set rotation(value:Number):void {
_rotation = value;
}
public function get length():Number {
return _length;
}
public function set length(value:Number):void {
_length = value;
}
public function get point():Point {
return new Point(x,y)
}
public function set point(value:Point):void {
x = value.x;
y = value.y;
}
}
//----------------------------------------------------------------------
//簡易Component
//----------------------------------------------------------------------
class myConstant extends Object{
public var cursorLock:Boolean=false;
public var scrollId:uint=0;
public var scrollNum:uint=0;
public var scrollDefaultId:uint=0;
public function myConstant() {
}
}
//----------------------------------------------------------------------
//簡易Component
//----------------------------------------------------------------------
import flash.display.Sprite;
import flash.text.*;
import flash.events.Event;
class myComponent extends Sprite {
public static const fontSize:Number=13
public var format:TextFormat
public var _rootObj:*
public var myConst:myConstant
public function myComponent() {
format=new TextFormat();
format.color = 0x333333
format.leading = 2
format.size=fontSize;
//format.font='_等幅';
format.font='_ゴシック';
this.addEventListener(Event.ADDED_TO_STAGE,onAddedToStage);
}
private function onAddedToStage(e:Event):void {
this.removeEventListener(Event.ADDED_TO_STAGE,onAddedToStage);
_rootObj = parent.root
if(_rootObj) {
if(_rootObj.hasOwnProperty('myConst')) {
myConst = _rootObj.myConst
}
}
}
}
//----------------------------------------------------------------------
//簡易チェックボックス
//----------------------------------------------------------------------
import flash.display.Sprite;
import flash.display.Shape;
import flash.display.GradientType;
import flash.display.LineScaleMode
import flash.text.*;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.geom.Matrix;
class myCheckBox extends myComponent {
public static const CHANGE:String = "check_change";
private var states:Sprite
private var back:Shape
private var onState:Shape
private var onStateOver:Shape
private var offState:Shape
private var offStateOver:Shape
private var onStateDisable:Shape
private var offStateDisable:Shape
private var label:TextField
public var onFlag:Boolean=false;
public var str:String
public var value:String
public function myCheckBox($str:String,$x:Number=0,$y:Number=0,$w:Number=100,$h:Number=20) {
back = new Shape()
back.graphics.beginFill(0xFFFFFF,0)
back.graphics.drawRect(0,0,20,$h)
this.addChild(back);
str=$str
states=new Sprite();
this.addChild(states);
onState=makeState('onState')
onStateOver=makeState('onStateOver')
offState=makeState('offState')
offStateOver=makeState('offStateOver')
onStateDisable=makeState('onStateDisable')
offStateDisable=makeState('offStateDisable')
states.addChild(onState)
states.addChild(onStateOver)
states.addChild(offState)
states.addChild(offStateOver)
states.addChild(onStateDisable)
states.addChild(offStateDisable)
states.y=4
offState.visible=true;
onState.visible=false;
onStateOver.visible=false;
offStateOver.visible=false;
onStateDisable.visible=false;
offStateDisable.visible=false;
label=new TextField()
label.defaultTextFormat = format
label.autoSize=TextFieldAutoSize.LEFT
label.selectable=false;
label.mouseEnabled=false;
label.htmlText=$str;
this.addChild(label)
label.x=20
label.y=3
back.width=this.width
this.x=$x
this.y=$y
this.addEventListener(MouseEvent.MOUSE_OVER,onMouseOver)
this.addEventListener(MouseEvent.MOUSE_OUT,onMouseOut)
this.addEventListener(MouseEvent.CLICK,onClick)
}
public function tabSet(value:uint):Boolean {
states.tabEnabled=true;
states.tabChildren=false;
states.tabIndex=value
return true
}
private function onMouseOver(e:MouseEvent):void {
if (onFlag) {
onState.visible=false
onStateOver.visible=true
} else {
offState.visible=false
offStateOver.visible=true
}
}
private function onMouseOut(e:MouseEvent):void {
if (onFlag) {
onState.visible=true
onStateOver.visible=false
} else {
offState.visible=true
offStateOver.visible=false
}
}
public function onClick(e:MouseEvent=null):void {
if (onFlag) {
onFlag=false
onStateOver.visible=false
offStateOver.visible=true
value='';
} else {
onFlag=true
if (e) {
offStateOver.visible=false
onStateOver.visible=true
} else {
offState.visible=false
onState.visible=true
}
value=str
}
if (e) {
dispatchEvent(new Event(CHANGE));
}
}
private function checkMark(mc:Shape):void {
mc.graphics.beginFill(0x666666,1)
mc.graphics.moveTo(2,6)
mc.graphics.lineTo(5,8)
mc.graphics.lineTo(11,2)
mc.graphics.lineTo(12,2)
mc.graphics.lineTo(6,12)
mc.graphics.lineTo(5,12)
mc.graphics.lineTo(2,7)
mc.graphics.endFill()
}
private function makeState(mode:String):Shape {
var result:Shape = new Shape()
var colors:Array=new Array(0xFFFFFF,0x999999)
var alphas:Array=new Array(1,1)
var ratios:Array=new Array(100,255)
var matrix:Matrix=new Matrix()
matrix.createGradientBox(18,18,0,-1,0)
result.graphics.beginFill(0x666666,1)
result.graphics.drawRect(0,0,14,14)
switch (mode) {
case 'onState':
result.graphics.beginFill(0xFFFFFF,1)
result.graphics.drawRect(1,1,12,12)
result.graphics.beginGradientFill(GradientType.RADIAL ,colors, alphas, ratios, matrix)
result.graphics.drawRect(2,2,10,10)
checkMark(result)
break
case 'onStateOver':
result.graphics.beginFill(0xFFFFFF,1)
result.graphics.drawRect(1,1,12,12)
result.graphics.beginGradientFill(GradientType.RADIAL ,colors, alphas, ratios, matrix)
result.graphics.drawRect(2,2,10,10)
result.graphics.beginFill(0x3399FF,0.2)
result.graphics.drawRect(1,1,12,12)
checkMark(result)
break
case 'offState':
result.graphics.beginFill(0xFFFFFF,1)
result.graphics.drawRect(1,1,12,12)
result.graphics.beginGradientFill(GradientType.RADIAL ,colors, alphas, ratios, matrix)
result.graphics.drawRect(2,2,10,10)
break
case 'offStateOver':
result.graphics.beginFill(0xFFFFFF,1)
result.graphics.drawRect(1,1,12,12)
result.graphics.beginGradientFill(GradientType.RADIAL ,colors, alphas, ratios, matrix)
result.graphics.drawRect(2,2,10,10)
result.graphics.beginFill(0x3399FF,0.2)
result.graphics.drawRect(1,1,12,12)
break
case 'onStateDisable':
result.graphics.beginFill(0xCCCCCC,1)
result.graphics.drawRect(1,1,12,12)
colors=new Array(0xCCCCCC,0x999999)
result.graphics.beginGradientFill(GradientType.RADIAL ,colors, alphas, ratios, matrix)
result.graphics.drawRect(2,2,10,10)
checkMark(result)
break
case 'offStateDisable':
result.graphics.beginFill(0xCCCCCC,1)
result.graphics.drawRect(1,1,12,12)
colors=new Array(0xCCCCCC,0x999999)
result.graphics.beginGradientFill(GradientType.RADIAL ,colors, alphas, ratios, matrix)
result.graphics.drawRect(2,2,10,10)
break
}
return result
}
}
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.system.Capabilities;
import flash.system.IME;
import flash.text.TextField;
import flash.text.TextFieldType;
import flash.text.TextFormat;
import flash.ui.Keyboard;
import flash.ui.Mouse;
/**
* 値が変更された直後に送出されます。
* @eventType flash.events.Event.CHANGE
*/
[Event(name="change", type="flash.events.Event")]
/**
* DraggableTextInput クラスはスライダーと直接入力の両方をサポートする数値入力インターフェースです。
* @author yasu
* @see http://clockmaker.jp/
*
*/
internal class DraggableTextInput extends Sprite
{
protected static const TEXTFORMAT_DEFAUT:TextFormat = new TextFormat("Arial", 11, 0x157bbe, null, null, true);
protected static const TEXTFORMAT_SLIDING:TextFormat = new TextFormat("Arial", 11, 0xFFFFFF, null, null, true);
protected static const TEXTFORMAT_INPUT:TextFormat = new TextFormat("Arial", 11, 0x000000, null, null, false);
protected static const TEXTFIELD_HEIGHT:Number = 21;
protected static const TEXTFIELD_WIDTH:Number = 50;
/**
* 新しい DraggableTextInput インスタンスを作成します。
*/
public function DraggableTextInput()
{
initialize();
}
/**
* ゼロ以外の場合、有効値は、このプロパティの整数倍と minimum の合計および maximum 以下になります。
* @return
*
*/
public function get snapInterval():Number
{
return _snapInterval;
}
public function set snapInterval(value:Number):void
{
if (value == 0)
throw new Error();
_snapInterval = value;
}
/**
* value の有効な最大値です。
*/
public function get maximum():Number
{
return _maximum;
}
public function set maximum(value:Number):void
{
_maximum = value;
_value = Math.min(_maximum, _value);
}
/**
* value の有効な最少値です。
*/
public function get minimum():Number
{
return _minimum;
}
public function set minimum(value:Number):void
{
_minimum = value;
_value = Math.max(_minimum, _value);
}
/**
* この範囲の現在の値です。
*/
public function get value():Number
{
return Number(_label.text);
}
public function set value(value:Number):void
{
_value = Math.max(_minimum, Math.min(_maximum, value));
_label.text = _format(_value);
}
/**
* 桁揃えの桁の数値です。
*/
public var formatRatio:int = -2;
protected var _value:Number;
protected var _label:TextField;
protected var _bg:Shape;
protected var _pointDown:Point;
protected var _focusShape:Shape;
protected var _valueOld:Number;
protected var _minimum:Number = int.MIN_VALUE;
protected var _maximum:Number = int.MAX_VALUE;
protected var _snapInterval:Number = 1;
protected var _isMouseDown:Boolean;
protected var _isMouseMove:Boolean;
private var _arrow:Bitmap;
/**
* インスタンスが保持しているデータを破棄します。
*/
public function finalize():void
{
_label.removeEventListener(Event.CHANGE, _onLabelChange);
removeEventListener(MouseEvent.MOUSE_DOWN, _onMouseDown);
removeEventListener(MouseEvent.CLICK, _onClick);
}
protected function initialize():void
{
_bg = new Shape();
addChild(_bg);
_focusShape = new Shape();
_focusShape.graphics.beginFill(0x53b6f9);
_focusShape.graphics.drawRect(0, 0, 5, 5);
_focusShape.graphics.endFill();
_focusShape.graphics.beginFill(0x737373);
_focusShape.graphics.drawRect(1, 1, 3, 3);
_focusShape.graphics.endFill();
_focusShape.graphics.beginFill(0xFFFFFF);
_focusShape.graphics.drawRect(2, 2, 1, 1);
_focusShape.graphics.endFill();
_focusShape.scale9Grid = new Rectangle(2, 2, 1, 1);
addChild(_focusShape);
_focusShape.width = TEXTFIELD_WIDTH;
_focusShape.height = TEXTFIELD_HEIGHT;
_focusShape.visible = false;
_label = new TextField();
_label.type = TextFieldType.DYNAMIC;
_label.selectable = false;
_label.x = 2;
_label.y = 2;
_label.width = TEXTFIELD_WIDTH - 4;
_label.height = TEXTFIELD_HEIGHT - 4;
_label.restrict = "0-9.\\-";
_label.addEventListener(Event.CHANGE, _onLabelChange);
addChild(_label);
// use http://wonderfl.net/c/8TK2
var arr:Array = [
[0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0],
[0, 0, 2, 1, 2, 0, 0, 0, 0, 0, 2, 1, 2, 0, 0],
[0, 2, 1, 1, 2, 2, 2, 0, 2, 2, 2, 1, 1, 2, 0],
[2, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 2],
[0, 2, 1, 1, 2, 2, 2, 0, 2, 2, 2, 1, 1, 2, 0],
[0, 0, 2, 1, 2, 0, 0, 0, 0, 0, 2, 1, 2, 0, 0],
[0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0]
];
var bmd:BitmapData = new BitmapData(15, 7, true, 0x0);
bmd.lock();
for (var i:int = 0; i < arr.length; i++)
{
for (var j:int = 0; j < arr[i].length; j++)
{
bmd.setPixel32(j, i, arr[i][j] == 0 ? 0x0 : arr[i][j] == 1 ? 0xFF000000 : 0xFFFFFFFF);
}
}
bmd.unlock();
_arrow = new Bitmap(bmd);
_label.defaultTextFormat = TEXTFORMAT_DEFAUT;
addEventListener(MouseEvent.MOUSE_DOWN, _onMouseDown);
addEventListener(MouseEvent.CLICK, _onClick);
}
protected function _onClick(event:MouseEvent):void
{
if (_isMouseDown)
return;
if (_focusShape.visible == true)
return;
if (Capabilities.hasIME)
{
try
{
IME.enabled = false;
}
catch (error:Error)
{
}
}
_valueOld = Number(_label.text);
_label.type = TextFieldType.INPUT;
_label.selectable = true;
_label.setSelection(0, _label.length);
_label.defaultTextFormat = TEXTFORMAT_INPUT;
_label.text = _label.text;
_focusShape.visible = true;
stage.addEventListener(MouseEvent.MOUSE_DOWN, _onMouseDownForCommit);
stage.addEventListener(KeyboardEvent.KEY_DOWN, _onKeyDown);
}
protected function _onKeyDown(event:KeyboardEvent):void
{
switch (event.keyCode)
{
case Keyboard.ENTER:
_commitProperties();
break;
case Keyboard.ESCAPE:
_label.text = _valueOld.toString(10);
_commitProperties();
break;
case Keyboard.UP:
_updateValue(Number(_label.text) + _snapInterval);
_label.setSelection(0, _label.length);
break;
case Keyboard.DOWN:
_updateValue(Number(_label.text) - _snapInterval);
_label.setSelection(0, _label.length);
break;
}
}
protected function _onMouseDownForCommit(event:MouseEvent):void
{
if (event.target == _label)
return;
_commitProperties();
}
protected function _commitProperties():void
{
var valueNew:Number = Number(_label.text);
if (valueNew < _minimum || valueNew > _maximum)
{
valueNew = _valueOld;
}
stage.removeEventListener(KeyboardEvent.KEY_DOWN, _onKeyDown);
stage.removeEventListener(MouseEvent.MOUSE_DOWN, _onMouseDownForCommit);
_label.defaultTextFormat = TEXTFORMAT_DEFAUT;
_label.selectable = false;
_label.type = TextFieldType.DYNAMIC;
_label.text = _format(valueNew)
_focusShape.visible = false;
_dispatchChangeEvent();
}
protected function _onMouseDown(event:MouseEvent):void
{
if (_label.type == TextFieldType.INPUT)
return;
stage.addEventListener(MouseEvent.MOUSE_UP, _onMouseUp);
stage.addEventListener(MouseEvent.MOUSE_MOVE, _onMouseMove);
_label.defaultTextFormat = TEXTFORMAT_SLIDING;
_label.text = _label.text;
_pointDown = new Point(mouseX, mouseY);
_valueOld = Number(_label.text);
_bg.graphics.clear();
_bg.graphics.beginFill(0x157bbe);
_bg.graphics.drawRect(0, 0, _label.textWidth + 8, TEXTFIELD_HEIGHT);
Mouse.hide();
stage.addChild(_arrow);
_arrow.x = stage.mouseX - int( _arrow.width / 2 );
_arrow.y = stage.mouseY - int( _arrow.height / 2);
_isMouseDown = false;
_isMouseMove = false;
}
protected function _onMouseMove(event:MouseEvent):void
{
_isMouseMove = true;
var vx:Number = +1 * (mouseX - _pointDown.x);
var vy:Number = -1 * (mouseY - _pointDown.y);
var d:Number = (vx + vy) * _snapInterval;
_updateValue(_valueOld + d);
_bg.graphics.clear();
_bg.graphics.beginFill(0x157bbe);
_bg.graphics.drawRect(0, 0, _label.textWidth + 8, TEXTFIELD_HEIGHT);
_dispatchChangeEvent();
_arrow.x = stage.mouseX - int( _arrow.width / 2 );
_arrow.y = stage.mouseY - int( _arrow.height / 2);
}
protected function _updateValue(valueNew:Number):void
{
valueNew = Math.max(_minimum, Math.min(_maximum, valueNew));
_label.text = _format(valueNew);
}
protected function _onMouseUp(event:MouseEvent):void
{
_bg.graphics.clear();
stage.removeEventListener(MouseEvent.MOUSE_UP, _onMouseUp);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, _onMouseMove);
_label.defaultTextFormat = TEXTFORMAT_DEFAUT;
_label.text = _label.text;
if (event.target == _label && _isMouseMove)
_isMouseDown = true;
Mouse.show();
stage.removeChild(_arrow);
}
/**
* 値をフォーマットします。
* @param val フォーマットしたい値。
* @return フォーマットされた文字列。
*/
protected function _format(val:Number):String
{
var nn:Number = Math.pow(10, formatRatio);
var valueNew:Number = Math.round(val / nn) * nn;
var str:String = valueNew.toString(10);
var arr:Array = str.split(".");
var decimal:String = arr[1] || "00";
var ratio:Number = Math.abs(formatRatio);
if (decimal.length < ratio)
{
while (decimal.length < ratio)
{
decimal = decimal + "0";
}
}
else
{
decimal = decimal.substr(0, ratio);
}
if (ratio == 0)
decimal = "0";
return arr[0] + "." + decimal;
}
protected function _dispatchChangeEvent():void
{
dispatchEvent(new Event(Event.CHANGE));
}
private function _onLabelChange(event:Event):void
{
event.stopPropagation();
}
}