// forked from checkmate's adobe challenge 1 /** * * "Use Flash Player 10 drawing API, * specifically drawTriangles. * My favorite part of the new capabilities * is the ability to specify * UVT texture mapping data." * by Justin Everett-Church * * This code is a example of drawTriangle. */ /** * Elastic Rectangle を改造して布っぽいものにしてみました。 * http://wonderfl.net/code/e040d9da0f2a2bb74095a325a5a0dd9cdab7c5cb * "a" でアンカー、"j" でジョイントの表示・非表示切り替えはそのまま * perlinNoise による模様の代わりに WebCamera の映像を変形させます。 */ package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.GraphicsBitmapFill; import flash.display.GraphicsEndFill; import flash.display.GraphicsPath; import flash.display.GraphicsSolidFill; import flash.display.GraphicsTrianglePath; import flash.display.GraphicsStroke; import flash.display.IGraphicsData; import flash.display.Sprite; import flash.events.Event; import flash.events.KeyboardEvent; import flash.events.MouseEvent; import flash.media.Camera; import flash.media.Video; [SWF(width="600", height="600", backgroundColor="0x000000", frameRate="60")] public class drawTriangleTest extends Sprite{ // セグメント数関連 private const SEGMENT_W:uint = 7; private const SEGMENT_H:uint = 5; // イメージの BitmapData private var imageBitmapData:BitmapData; // セグメント化クラス private var segmentation:Segmentation; // コンテナ private var anchorContainer:Sprite; private var jointContainer:Sprite; private var imageContainer:Sprite; // 格納 Vector private var anchorVector:Vector.<Anchor>; private var jointVector:Vector.<Joint>; // ジョイント生成用 private var anchorPairVector:Vector.<int>; // イメージ表示用 GraphicsData private var imageGraphicsData:Vector.<IGraphicsData>; private var trianglePath:GraphicsTrianglePath; // ジョイント表示用 GraphicsData private var jointGraphicsData:Vector.<IGraphicsData>; private var jointPath:GraphicsPath; private var draggingAnchor:Anchor; private var anchorVisible:Boolean = false; private var jointVisible:Boolean = false; private var camera:Camera; private var video:Video; private const CAMERA_WIDTH:uint = 180; private const CAMERA_HEIGHT:uint = 280; public function drawTriangleTest() { Wonderfl.capture_delay(10); // カメラ camera = Camera.getCamera(); if (camera != null) { setup(); } else { throw new Error("カメラがありません。"); } } private function setup():void { // camera のセットアップ camera.setMode(CAMERA_WIDTH, CAMERA_HEIGHT, 60); // video のセットアップ video = new Video(CAMERA_WIDTH, CAMERA_HEIGHT); video.attachCamera(camera); imageBitmapData = new BitmapData(CAMERA_WIDTH, CAMERA_HEIGHT); imageBitmapData.draw(video); next(); } private function next():void { // セグメント化クラスのインスタンス生成 segmentation = new Segmentation(SEGMENT_W, SEGMENT_H); // 各コンテナの生成 initStage(); // GraphicsTrianglePath 用 Vector の生成 var verticies:Vector.<Number> = new Vector.<Number>(); // vertics の生成 var indicies:Vector.<int> = segmentation.getIndicies(); // inducides の生成 var uvDatas:Vector.<Number> = segmentation.getUvDatas(); // uvDatas の生成 // imageGraphicsData の生成 imageGraphicsData = new Vector.<IGraphicsData>(3); imageGraphicsData.push(new GraphicsBitmapFill(imageBitmapData)); imageGraphicsData.push(trianglePath = new GraphicsTrianglePath(verticies, indicies, uvDatas)); imageGraphicsData.push(new GraphicsEndFill()); // アンカーの生成(↑で作った uvDatas を使う) createAnchor(uvDatas); // アンカーのペアリング anchorPairVector = segmentation.getVertexPair(); // ジョイントの生成 createJoint(); // 固定アンカーの設定 var anchor:Anchor = anchorVector[0]; anchor.isFix = true; anchor.x = 20; anchor = anchorVector[SEGMENT_W]; anchor.isFix = true; anchor.x = stage.stageWidth - 20; // ジョイント表示用 graphicsData の生成 // 線の状態 var stroke:GraphicsStroke = new GraphicsStroke(0); stroke.fill = new GraphicsSolidFill(0xFF0000, 0.5); // パス var commands:Vector.<int> = segmentation.getPathCommands(anchorPairVector); // コマンド var data:Vector.<Number> = segmentation.getPathData(anchorVector, anchorPairVector);// データ jointPath = new GraphicsPath(commands, data); // GraphicsData jointGraphicsData = new Vector.<IGraphicsData>(2); jointGraphicsData.push(stroke); jointGraphicsData.push(jointPath); // イベントハンドラ anchorContainer.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); anchorContainer.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler); addEventListener(Event.ENTER_FRAME, enterFrameHandlerandler); } // 各コンテナの生成 private function initStage():void { imageContainer = new Sprite(); jointContainer = new Sprite(); anchorContainer = new Sprite(); addChild(imageContainer); addChild(jointContainer); addChild(anchorContainer); } // アンカーの生成 private function createAnchor(uvData:Vector.<Number>):void { Anchor.left = 0; Anchor.right = stage.stageWidth; Anchor.top = 0; Anchor.bottom = stage.stageHeight; var n:uint = uvData.length / 2; anchorVector = new Vector.<Anchor>(n); var imageWidth:uint = imageBitmapData.width; var imageHeight:uint = imageBitmapData.height; var offsetX:Number = (stage.stageWidth - imageWidth) / 2; var offsetY:Number = 20;//(stage.stageHeight - imageHeight) / 2; for (var i:uint = 0; i < n; i++) { var anchor:Anchor = new Anchor(15, 0xFFFFFF); anchor.x = imageWidth * uvData[i * 2] + offsetX; anchor.y = imageHeight * uvData[i * 2 + 1] + offsetY; anchorContainer.addChild(anchor); anchorVector[i] = anchor; } } // ジョイントの生成 private function createJoint():void { var n:uint = anchorPairVector.length / 2; jointVector = new Vector.<Joint>(n); for (var i:uint = 0; i < n; i++) { var a:uint = anchorPairVector[i * 2]; var b:uint = anchorPairVector[i * 2 + 1]; var joint:Joint = new Joint(anchorVector[a], anchorVector[b]); jointVector[i] = joint; } } // キーボードイベント private function keyDownHandler(event:KeyboardEvent):void { if (event.charCode == 97) { // "a" キーでアンカー表示 anchorVisible = !anchorVisible; for each (var anchor:Anchor in anchorVector) { anchor.isVisible = anchorVisible; } } if (event.charCode == 106) { // "j" キーでジョイント表示 jointVisible = !jointVisible; if (!jointVisible) { jointContainer.graphics.clear(); } } } // マウスイベント private function mouseDownHandler(event:MouseEvent):void { draggingAnchor = Anchor(event.target); draggingAnchor.mouseDown(true); } private function mouseUpHandler(event:MouseEvent):void { if (draggingAnchor != null) { draggingAnchor.mouseDown(false); } } // フレームイベント private function enterFrameHandlerandler(event:Event):void { for each (var joint:Joint in jointVector) { joint.update(); } for each (var anchor:Anchor in anchorVector) { anchor.update(); } // カメラ映像更新 imageBitmapData.draw(video); // イメージの表示 // vertics の更新 trianglePath.vertices = segmentation.getVerticies(anchorVector); // 描画 imageContainer.graphics.clear(); imageContainer.graphics.drawGraphicsData(imageGraphicsData); // ジョイントの表示 if (jointVisible) { // data の更新 jointPath.data = segmentation.getPathData(anchorVector, anchorPairVector); // 描画 jointContainer.graphics.clear(); jointContainer.graphics.drawGraphicsData(jointGraphicsData); } } } } import flash.display.GraphicsPathCommand; class Segmentation { private var segW:uint; // セグメント数(幅) private var segH:uint; // セグメント数(高) private var numOfVertex:uint; // 頂点数 private var pairVector:Vector.<int>; // コンストラクタ public function Segmentation(segW:uint, segH:uint) { this.segW = segW; this.segH = segH; numOfVertex = (segW + 1) * (segH + 1); } // ---- drawTriangle 用 ----- // verticies の生成(updata のたびに呼ばれる) public function getVerticies(anchorVector:Vector.<Anchor>):Vector.<Number> { var n:uint = numOfVertex; var verticies:Vector.<Number> = new Vector.<Number>(n * 2); for (var i:int = 0; i < n; i++) { verticies[i * 2] = anchorVector[i].x; verticies[i * 2 + 1] = anchorVector[i].y; } return verticies; } // indicies の生成(最初に一度だけ呼ばれる) public function getIndicies():Vector.<int> { var sW:uint = segW; var sH:uint = segH; var indicies:Vector.<int> = new Vector.<int>(sW * sH * 6); var cnt:uint = 0; for (var i:uint = 0; i < sH; i++) { for (var j:uint = 0; j < sW; j++) { var leftTop:uint = i * (sW + 1) + j; var rightTop:uint = i * (sW + 1) + j + 1; var leftBottom:uint = (i + 1) * (sW + 1) + j; var rightBottom:uint = (i + 1) * (sW + 1) + j + 1; indicies[cnt] = leftTop; indicies[cnt + 1] = rightTop; indicies[cnt + 2] = leftBottom; indicies[cnt + 3] = rightTop; indicies[cnt + 4] = rightBottom; indicies[cnt + 5] = leftBottom; cnt += 6; } } return indicies; } // uvDatas の生成(最初に一度だけ呼ばれる) public function getUvDatas():Vector.<Number> { var sW:uint = segW; var sH:uint = segH; var uvDatas:Vector.<Number> = new Vector.<Number>(numOfVertex * 2); var cnt:uint = 0; for (var i:uint = 0; i < sH + 1; i++) { for (var j:uint = 0; j < sW + 1; j++) { uvDatas[cnt++] = j / sW; uvDatas[cnt++] = i / sH; } } return uvDatas; } // ----- ジョイント用 ----- // パスの commands の生成(最初に一度だけ呼ばれる) public function getPathCommands(pair:Vector.<int>):Vector.<int> { var n:uint = pair.length; var commands:Vector.<int> = new Vector.<int>(n); n /= 2; for (var i:uint = 0; i < n; i++) { commands[i * 2] = GraphicsPathCommand.MOVE_TO; commands[i * 2 + 1] = GraphicsPathCommand.LINE_TO; } return commands; } // パスの data の生成(updata のたびに呼ばれる) public function getPathData(anchors:Vector.<Anchor>, pair:Vector.<int>):Vector.<Number> { var n:uint = pair.length; var data:Vector.<Number> = new Vector.<Number>(n * 2); for (var i:uint = 0; i < n; i++) { var anchor:Anchor = anchors[pair[i]]; data[i * 2] = anchor.x; data[i * 2 + 1] = anchor.y; } return data; } // ----- 頂点のペアリング ----- public function getVertexPair():Vector.<int> { var sW:uint = segW; var sH:uint = segH; pairVector = new Vector.<int>(); // 横 for (var i:uint = 0; i < sH + 1; i++) { for (var j:uint = 0; j < sW; j++) { var a:uint = i * (sW + 1) + j; var b:uint = i * (sW + 1) + j + 1; pairVector.push(a, b); } } // 縦 for (i = 0; i < sH; i++) { for (j = 0; j < sW+1; j++) { a = i * (sW + 1) + j; b = (i + 1) * (sW + 1) + j; pairVector.push(a, b); } } // 斜め(左上から右下) for (i = 0; i < sH; i++) { for (j = 0; j < sW; j++) { a = i * (sW + 1) + j; b = (i + 1) * (sW + 1) + j + 1; pairVector.push(a, b); } } // 斜め(右上から左下) for (i = 0; i < sH; i++) { for (j = 0; j < sW; j++) { a = i * (sW + 1) + j + 1; b = (i + 1) * (sW + 1) + j; pairVector.push(a, b); } } if (sW % 2 == 0) { // 横斜め(左上から右下) for (i = 0; i < sH; i++) { for (j = 0; j < sW - 1; j += 2) { a = i * (sW + 1) + j; b = (i + 1) * (sW + 1) + j + 2; pairVector.push(a, b); } } // 横斜め(右上から左下) for (i = 0; i < sH; i++) { for (j = 0; j < sW; j+=2) { a = i * (sW + 1) + j + 2; b = (i + 1) * (sW + 1) + j; pairVector.push(a, b); } } } if (sH % 2 == 0) { // 縦斜め(左上から右下) for (i = 0; i < sH; i += 2) { for (j = 0; j < sW; j++) { a = i * (sW + 1) + j; b = (i + 2) * (sW + 1) + j + 1; pairVector.push(a, b); } } // 縦斜め(右上から左下) for (i = 0; i < sH; i+=2) { for (j = 0; j < sW; j++) { a = i * (sW + 1) + j + 1; b = (i + 2) * (sW + 1) + j; pairVector.push(a, b); } } } return pairVector; } } import flash.display.Sprite; class Anchor extends Sprite { // 物理的数値 static public var gravity:Number = 0.47; // 重力 static public var friction:Number = 0.92; // 空気抵抗 static public var floorFriction:Number = 1; // 床面抵抗 static public var bounce:Number = 1; // 跳ね返り // 壁面値 static public var left:Number; static public var right:Number; static public var top:Number; static public var bottom:Number; // 固定フラグ private var _isFix:Boolean = false; public function set isFix(value:Boolean):void { this.isVisible = _isFix = value; circleDraw(true); } private var _isVisible:Boolean = false; public function set isVisible(value:Boolean):void { if(!_isFix){ _isVisible = value; circleDraw(_isVisible); } } // 計算用変数 // 速度 private var vx:Number = 0; private var vy:Number = 0; // 前フレームの座標値 private var prevX:Number = 0; private var prevY:Number = 0; // 剛性反映用の値 private var sx:Number = 0; private var sy:Number = 0; // ドラッグ判定 private var isMouseDown:Boolean = false; private var radius:Number; private var color:uint; private const LIMIT:Number = 10.0; public function Anchor(radius:Number = 10.0, color:uint = 0x0000FF):void { this.radius = radius; this.color = color; circleDraw(_isVisible); buttonMode = true; } // 円描画 private function circleDraw(flg:Boolean):void { var a:Number = flg ? 0.25 : 0.0; graphics.clear(); graphics.beginFill(color, a); graphics.drawCircle(0, 0, radius); graphics.endFill(); } // このアンカーがマウスドラッグ中か否か public function mouseDown(flg:Boolean):void { isMouseDown = flg; (isMouseDown) ? startDrag() : stopDrag(); } // アクセル public function accelalete(valX:Number, valY:Number):void { vx = valX; vy = valY; } // ジョイントの剛性値を反映 public function setStiffness(valX:Number, valY:Number):void { sx += valX; sy += valY; } public function update():void { if (isMouseDown) { // ドラッグしている場合 // 壁処理 if (x < left) { x = left; } // 左側面 if (x > right) { x = right; } // 右側面 if (y < top) { y = top; } // 天井 if (y > bottom) { y = bottom; } // 床 // 計算 var tmpX:Number = x; var tmpY:Number = y; vx = x - prevX; vy = y - prevY; prevX = tmpX; prevY = tmpY; } else { // ドラッグしていない場合 if(!_isFix){ // 壁処理 if (x < left) { x = left; vx *= floorFriction; vx *= bounce; } else if (x > right) { x = right; vx *= floorFriction; vx *= bounce; } if (y < top) { y = top; vy *= floorFriction; vy *= bounce; } else if (y > bottom) { y = bottom; vy *= floorFriction; vy *= bounce; } // 計算 vx = Math.max( -LIMIT, Math.min(LIMIT, vx)); vy = Math.max( -LIMIT, Math.min(LIMIT, vy)); vx += sx; vy += sy; vx *= friction; vy *= friction; vy += gravity; x += vx; y += vy; } // 剛性値の初期化 sx = 0; sy = 0; } } } class Joint { // 物理的数値 static public var stiffness:Number = 0.025;// 剛性値 // 両端のアンカー private var a:Anchor; // 片端のアンカー private var b:Anchor; // もう片端のアンカー // アンカー間の値 private var defaultDistance:Number = 0; // アンカー間の距離(既定値) private var distanceXY:Number; // アンカー間の距離(実際の値) private var distanceX:Number; // distanceXY を求めるための X座標値 private var distanceY:Number; // distanceXY を求めるための Y座標値 public function Joint(a:Anchor, b:Anchor):void { this.a = a; this.b = b; getAnchorData(); defaultDistance = distanceXY; } // ジョイントの剛性計算をおこない、対象アンカーに反映させる public function update():void { getAnchorData(); var s:Number = stiffness * (distanceXY - defaultDistance); var sx:Number = s * distanceX / distanceXY; var sy:Number = s * distanceY / distanceXY; a.setStiffness(-sx, -sy); b.setStiffness( sx, sy); } // アンカー間の値を更新 private function getAnchorData():void { var x:Number = a.x - b.x; var y:Number = a.y - b.y; distanceXY = Math.sqrt(x * x + y * y); distanceX = x; distanceY = y; } } forked from: adobe challenge 1 (Webcam on Cloth)