forked from: 布 :改善2 + 画像
// forked from kimo0517's 布 :改善2 + 画像
// forked from miniapp's 布 :改善2
/**
* 2次元だけど、出来たかな?
*
* あいかわらず右によってる。
*
* ドラッグでマウスに一番近いポイントを移動させる。
* ctrlキー押しながらドラッグで固定。
* ダブルクリックで固定を解除。
*
*
*/
package {
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.display.Loader;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.display.BitmapData;
import flash.display.TriangleCulling;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.net.FileReference;
import flash.net.FileFilter;
import flash.net.URLRequest;
import com.bit101.components.PushButton;
import mx.utils.Base64Decoder;
[SWF(backgroundColor="0xAADDFF", 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;
private var btnSelectPic:PushButton;
private var btnRemove:PushButton;
private var bmd:BitmapData;
private var _vseg:int;
private var _hseg:int;
private var vPos:Vector.<Number>;
private var vIndex:Vector.<int>;
private var uvtData:Vector.<Number>;
private var fr:FileReference;
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.<Joint> = new Vector.<Joint>();
private var _points:Vector.<Point> = new Vector.<Point>();
private var _pointsXs:Vector.<Vector.<Point>> = new Vector.<Vector.<Point>>(_rows, true);
private var _pointsYs:Vector.<Vector.<Point>> = new Vector.<Vector.<Point>>(_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);
bmd = null;
btnSelectPic = new PushButton(this, 10, 10, "Select Photo", _btnSelectPicClicked);
btnRemove = new PushButton(this, btnSelectPic.x + btnSelectPic.width + 10, 10, "Unload Photo", _btnUnloadPicClicked);
_vseg = _cols;
_hseg = _rows;
var _vseg_1:int = _vseg - 1;
var _hseg_1:int = _hseg - 1;
var v:int;
var h:int;
vPos = new Vector.<Number>(2*_vseg * _hseg);
uvtData = new Vector.<Number>(2*_vseg * _hseg);
vIndex = new Vector.<int>(6 * _vseg_1 * _hseg_1);
for (v = 0; v < _vseg; v++) {
for (h = 0; h < _hseg; h++) {
//x
uvtData[2 * (v * _hseg + h)] = Number(h) / _hseg_1;
uvtData[2 * (v * _hseg + h) + 1] = Number(v) / _vseg_1;
}
}
for (v = 0; v < _vseg_1; v ++ ) {
for (h = 0; h < _hseg_1; h++) {
vIndex[6 * (v * _hseg_1 + h)] = v * _hseg + h;
vIndex[6 * (v * _hseg_1 + h) + 1] = v * _hseg + h+1;
vIndex[6 * (v * _hseg_1 + h) + 2] = (v+1) * _hseg + h;
vIndex[6 * (v * _hseg_1 + h) + 3] = (v+1) * _hseg + h;
vIndex[6 * (v * _hseg_1 + h) + 4] = v * _hseg + h+1;
vIndex[6 * (v * _hseg_1 + h) + 5] = (v+1) * _hseg + h+1;
}
}
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);
var decoder:Base64Decoder = new Base64Decoder;
decoder.decode("/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABkAGQDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iikZlRSzMFUdSTgUALRWLe+KdLssr5/nOP4Yhn9awrrx3M2Ra2aqOzSNk/kKAO3orzC58Tavck5uzGD2jG2m6ZearcapbrDcXEj7xkbiRjPOfagD1Giq89/aWwzPcxR/wC8wrKufF2k24OyZpmHaNf60AbtFec6h4v1G7ci3b7NF2Cct+ddvpE88mjW014dsrIC5bj86AL9FV4760ln8mO5ieXGdqsCasUAFFFFAGXrusDRrET+V5ju21VzgV57qWtXuqyE3EpEfaNeFFeia9p41HSJoRHvkA3R/wC8OleZyWF5CcS2kyH3Q0AV6qQalbXOpXdhGWM9qFMvHA3dOa3LXQ9SuhuS0cIOS7jaMfjXnmm3kOh+GLvXpZVm1LV76UWdovzSTBW2qfZR60AdiCCSAQSOuD0qeOe4gjIjkkjVuu0kZrndES28J+H5NR1aRjezSefdyyNuRXJ4UAenArUsfEKeJrX+0IZZJIi5QF129PQelAFgkscsST6k5ooooAmt7l7Vt8SoJB0dlyR9KdcX93dHM9zLJ/vMcVVV0fO11bHB2kHFSxRSTvtijeRvRRmgCzpV4unanDdsrMIyTtU4J4rq7LxnJeapBbm0VIpX2Z3ZYZrGsfCOp3eGlRbdPWTr+VdZpXhax0yRZzumuF6O/QfQUAblFFFABTXdI0Z3IVFGST0Ap1Ynie+tLfSpraeYpJOhCADJNAHG+O/Hz2nh/Um0/CRrCyiUj5mJ4GPTrXkHw+tIbbUp4NUVl1eBFECTfwxEZ+T866HxfNDcppWhhC1xe3iO53dIk5PFWPEvhyPWoVuLc+Rqdt81tOpwQRyFPtQBb8QXCWfh++uZII5xDEX8uUZViOgNcx4o1640aLw/d2cYTzUYm2ThGJUYBHoCah1PxB/bXhCeyuB9n1JbiK2u4TwRlwCw9jXXzeH9Ou/HOm/a5vO02y092V9hCCcnCr74AzQB54fG/iGUrBtjWa1uR9qeNRtKlgoX8ya6/wAVXepDWbDwvaRS2dzqTxo13IhCxq5wMe/Bq3L4cstE+FNxp9uUvdfubuK5uGiXJYiUHAJ7AD+dbfxB1vUdasLG40qziFzpt3HeIjnJkKZ4/XpQBUtfAVj8P/H3hi3gupr6DV/Nguopm6uq7g4HpmvXpLrStIiwz28AH8K4yfwFeFaVNrfiPXT4p8QGSG5RTFZWw4ECHqceproCSTliSfUmgDur3xxbIpFnA8r9mf5RWFc+L9WnI2ypCB2jX/GsWOKSZtsUbufRVzXoXhvRIbfS4ZLqzQXTZLF1yRzxQBq6VPNc6VbTXAxK8YLDGOaKudKKACue8ReHJNZnhminWNkUqQwyCK6Gq91e29nE0k8yIAM4ZsE0AcBJ8Mt+qx6o91D9piiMSsQcBTyeKhu9EtrLIl1e1LD+FFLH9Ko3Wp3l3LK0lzMVdidu84x6YqoMAigDidV8Q2f9vXK6FoZ1W/jHlzT7MJhTn8TU9544VNE029s7bzrm6l2vajll2/fA9xWVp/iG38NXGt2ItZm1Sa/doLVIyA2fu8+laMHgrwzHYr/wkPiLydXh33NzDYsB5Rk52ljwMe1AEz+KLq/k0ZLG1mtprmdzJBOuGMaDn8Ccc1a8Oa9q2s6/JpF3ojWc6QGYkv0XOAee3vWP8Oby5vdXm1Fisy2EH2K3llG8sCxO7nvjFbGi3UmreJ/EOszys4Ei2kbH+6g5/DNAHYiytojm7vUHqkI3t+fSrH9o6VZxk2+miRl5Mt2+Rj6DivOpPHkNzdraaJp9xqEpbaX27UHPPNO13Sr/AFnVpItRvxZaCjKsaIwVrhj2J+vFAHvfhvV9L1zSI73S3geLJR/JHCuOq/hWxXj2nzy6VpcWl6b/AKJZx/dih4ye5J6kn1r0zw4LoaHb/bN3m8439cZ4zQBq0UUUAcP4p13ULfVHs7eYwxIo5Xq2R61yckjzOXldnY92Oa9U1HRbDVMG6hy4GA6nDD8a4jxPo9ro8tulsJMSAks7Z/CgDDiMYfMqsy+inFX49WFsP9DsreJv77De35ms/wAuTYX8t9g/i2nFAR2baqMT6BTQAl2TfXTXc4Vrpl2+dsG4fjXFWnw20xLtrnULq4v5Hbcwc7Qx9+5r0e18P6rd48uzdVP8T/KP1plzot/bah9iMDSTEAjYMgj60Acjp2nw+E9L1S4LJ5Jle5ARcBFxwtJ4MtpIPDFu8ykS3LPcOD/tnP8ALFXfElncXnh3UbO3TdcSRMipnGT6VbsYmg0+2hYYaOJFI9CABQBjXljKvivRJLW32WsSTeaY1woJAxnFS+JtOudTtLKC2QNtvYpJMnG1FOSau32rWunXNnBcsVN3IY42/hDAZ5PasbxF4xh0iQ2llCb2+Ub5I05ESDkliPagDqCeSRWqviXVI7OO2juNiRrtDAfMfxrDs7qO9soLqLPlzRiRc9cEZrofC6WE2qpBewGVn/1RJ+UH3FAHX+Gbu9u9GSW6y77yA7cFl7GittVCqFUAAcADtRQAtRzW8NwAJokkCnI3KDipKKAGeVHs2eWu30xxQIo1OQig+oFPooAKMDOcc0UUAczqfg22vbp7iGdoGc5ZduRn1rmb+w0jTWMf2yW8nHBSMBVH1NejXUTTWk0SNtd0Kg+hIrz2PwdrDdY4l5xkv+tAHJ6zpFlrtk9peRnyi25dpwUPYg1ytx8Oo42RdK1Ga2WVDFdl/maZCc17TaeBWJDXl2AP7sQ/qa6Ox0HTdPAMFspcfxv8xoA890fwpfzW0EEEBht40CK8vHAGPxrs9H8KW+l3CXLytNOo44wo/CuhooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//2Q==");
var loader:Loader = new Loader;
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, _loadBMDComplete);
loader.loadBytes(decoder.toByteArray());
}
private function _btnSelectPicClicked(e:MouseEvent):void {
fr = new FileReference();
fr.addEventListener(Event.SELECT, _startLoadPhoto);
fr.addEventListener(Event.COMPLETE, _loadPhotoComplete);
var filter:FileFilter = new FileFilter("Images (*.jpg, *.png)", "*.jpg;*.jpeg;*.png", null);
fr.browse([filter]);
}
private function _startLoadPhoto(e:Event):void {
fr.load();
}
private function _loadPhotoComplete(e:Event):void {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, _loadBMDComplete);
loader.loadBytes(fr.data);
}
private function _loadBMDComplete(e:Event):void {
if (bmd != null) {
bmd.dispose();
bmd = null;
}
bmd = e.target.content.bitmapData.clone();
}
private function _btnUnloadPicClicked(e:MouseEvent):void {
if (bmd != null) {
bmd.dispose();
bmd = null;
}
}
/**
* ポイントを配置します。
*/
private function putPoint():void {
//縦方向に並べたポイントを入れる配列を先に用意する
for (var i:uint = 0; i < _cols; i++) {
var pointsY:Vector.<Point> = new Vector.<Point>(_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.<Point> = new Vector.<Point>(_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.<Point> 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.<Point> 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 drawBMD():void {
var v:int;
var h:int;
var i:int;
v = 0;
for each(var pointsX:Vector.<Point> in _pointsXs){
i = 0;
for (h = 0; h < _hseg; h++) {
vPos[2 * (v * _hseg + h)] = pointsX[i].x; //x
vPos[2 * (v * _hseg + h) + 1] = pointsX[i].y; //y
i++;
}
v++;
}
graphics.clear();
graphics.beginBitmapFill(bmd, null, false, true);
graphics.drawTriangles(vPos, vIndex, uvtData, TriangleCulling.NONE);
graphics.endFill();
}
private function enterFrameHandler(e:Event):void {
if (_isMouseDown) {
_draggedPoint.x = mouseX;
_draggedPoint.y = mouseY;
}
for each(var joint:Joint in _joints) {
joint.update();
}
if (bmd == null) {
drawLine();
} else {
drawBMD();
}
}
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;
}