flash on 2009-12-25
"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 の映像を変形させます。
/**
* Copyright late4 ( http://wonderfl.net/user/late4 )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/bx5A
*/
// 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;
}
}