/** * 2次元だけど、出来たかな? * * あいかわらず右によってる。 * * ドラッグでマウスに一番近いポイントを移動させる。 * ctrlキー押しながらドラッグで固定。 * ダブルクリックで固定を解除。 */ package { import flash.display.Sprite; import flash.display.StageQuality; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.KeyboardEvent; import flash.events.MouseEvent; [SWF(backgroundColor="0xFFFFFF", width="465", height="465", frameRate="60")] public class Cloth extends Sprite { public static const STAGE_WIDTH:uint = 465; public static const STAGE_HEIGHT:uint = 465; public function Cloth() { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private var _lineColor:uint = 0x000000; private var _cols:uint = 16;//横の数 private var _rows:uint = 16;//縦の数 private var _diffX:uint = 10; private var _diffY:uint = 10; private var _isCtrlPress:Boolean = false; private var _isMouseDown:Boolean = false; private var _draggedPoint:Point; private var _isFirst:Boolean = false; private var _numJoints:uint; private var _joints:Vector. = new Vector.(); private var _points:Vector. = new Vector.(); private var _pointsXs:Vector.> = new Vector.>(_rows, true); private var _pointsYs:Vector.> = new Vector.>(_cols, true); private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); stage.scaleMode = StageScaleMode.NO_SCALE; //stage.quality = StageQuality.MEDIUM; stage.doubleClickEnabled = true; stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDwonHandler); stage.addEventListener(MouseEvent.DOUBLE_CLICK, doubleClickHandler); stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDonwHandler); stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler); putPoint(); joint(); //上端2つを固定します。 //左端 var point1:Point = _pointsXs[0][0]; point1.x = 100; point1.y = 10; point1.isPinned = true; //右端 var point2:Point = _pointsXs[0][_cols - 1]; point2.x = STAGE_WIDTH - 100; point2.y = 10; point2.isPinned = true; addEventListener(Event.ENTER_FRAME, enterFrameHandler); } /** * ポイントを配置します。 */ private function putPoint():void { //縦方向に並べたポイントを入れる配列を先に用意する for (var i:uint = 0; i < _cols; i++) { var pointsY:Vector. = new Vector.(_rows, true); _pointsYs[i] = pointsY; } var clothWidth:Number = (_cols - 1) * _diffX; var clothHeight:Number = (_rows - 1) * _diffY; var startX:Number = (STAGE_WIDTH - clothWidth) / 2; var startY:Number = (STAGE_HEIGHT - clothHeight) / 2; //ポイントを格子状に配置 for (i = 0; i < _rows; i++) { var pointsX:Vector. = new Vector.(_cols, true); _pointsXs[i] = pointsX; for (var j:uint = 0; j < _cols; j++) { var point:Point = new Point(); _points.push(point); point.name = String(i) + "-" + String(j);//デバッグ用 point.x = startX + _diffX * j; point.y = startY + _diffY * i; //横に並ぶポイントを入れる pointsX[j] = point; //縦方向に並ぶポイントを入れる pointsY = _pointsYs[j]; pointsY[i] = point; } } _points.fixed = true; } private function joint():void { for (var i:uint; i < _rows; i++) { for (var j:uint = 0; j < _cols; j++) { var point:Point = _pointsXs[i][j]; if (j > 0) { var leftPoint:Point = _pointsXs[i][j - 1 ]; _joints.push(new Joint(leftPoint, point)); _joints.push(new Joint(point, leftPoint)); } if (i > 0) { var upPoint:Point = _pointsXs[i - 1][j]; _joints.push(new Joint(upPoint, point)); _joints.push(new Joint(point, upPoint)); } } } _numJoints = _joints.length; _joints.fixed = true; } /** * 一番カーソルに近いポイントを捜す。 */ private function searchPoint():Point { var lastMinDist:Number = Infinity; var target:Point; for each(var point:Point in _points) { var dx:Number = point.x - mouseX; var dy:Number = point.y - mouseY; var dist:Number = Math.sqrt(dx * dx + dy * dy); if (dist < lastMinDist) { lastMinDist = dist; target = point; } } return target; } /** * ポイント同士を繋ぐ線を書く */ private function drawLine():void { graphics.clear(); graphics.lineStyle(1, _lineColor); //横のラインを引く for each(var pointsX:Vector. in _pointsXs){ //横列のポイントが入った配列にアクセス。 var firstPoint:Point = pointsX[0]; graphics.moveTo(firstPoint.x, firstPoint.y); for (var i:uint = 1; i < _cols; i++) { var point:Point = pointsX[i]; graphics.lineTo(point.x, point.y); } } for each(var pointsY:Vector. in _pointsYs) { firstPoint = pointsY[0]; graphics.moveTo(firstPoint.x, firstPoint.y); for (i = 1; i < _rows; i++) { point = pointsY[i]; graphics.lineTo(point.x, point.y); } } } private function enterFrameHandler(e:Event):void { if (_isMouseDown) { _draggedPoint.x = mouseX; _draggedPoint.y = mouseY; } for each(var joint:Joint in _joints) { joint.update(); } drawLine(); } private function keyDonwHandler(e:KeyboardEvent = null):void{ if(e.ctrlKey) _isCtrlPress = true; } private function keyUpHandler(e:KeyboardEvent = null):void{ _isCtrlPress = false; } private function doubleClickHandler(e:MouseEvent = null):void{ searchPoint().isPinned = false; } private function mouseDwonHandler(e:MouseEvent):void { _isMouseDown = true; _draggedPoint = searchPoint(); _draggedPoint.isDragging = true; } private function mouseUpHandler(e:MouseEvent):void { _isMouseDown = false; if (_isCtrlPress) _draggedPoint.isPinned = true; _draggedPoint.isDragging = false; _draggedPoint = undefined; } } } class Joint { public static var SPRING:Number = 0.08; public static var FRICTION:Number = 0.96; public static var GRAVITY:Number = 0.45; public function Joint(point:Point, target:Point) { var initDx:Number = target.x - point.x; var initDy:Number = target.y - point.y; var length:Number = Math.sqrt(initDx * initDx + initDy * initDy); //バネ運動させる関数 update = function():void { if (point.isDragging || point.isPinned) return; var dx:Number = target.x - point.x; var dy:Number = target.y - point.y; /* var angle:Number = Math.atan2(dy, dx); var targetX:Number = target.x - length * Math.cos(angle); var targetY:Number = target.y - length * Math.sin(angle); /**/ //* //Math.cos(angle) は dx / distanceと同じ //Math.sin(angle) は dy / distanceと同じ var distance:Number = Math.sqrt(dx * dx + dy * dy); var targetX:Number = target.x - length * dx / distance; var targetY:Number = target.y - length * dy / distance; /**/ point.vx += (targetX - point.x) * Joint.SPRING; point.vy += (targetY - point.y) * Joint.SPRING; point.x += (point.vx *= FRICTION); point.y += (point.vy *= FRICTION); point.y += GRAVITY; } } public var update:Function; } class Point { public var name:String; public var x:Number; public var y:Number; public var z:Number; public var vx:Number = 0; public var vy:Number = 0; public var vz:Number = 0; public var isPinned:Boolean = false; public var isDragging:Boolean = false; }