forked from: Saqoosha challenge for professionals(ふわふわ)
/**
* Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/jh6M
*/
// forked from checkmate's Saqoosha challenge for professionals
package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.TimerEvent;
import flash.geom.Matrix;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.utils.Timer;
[SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#000000")]
public class Main extends Sprite {
// セグメント数関連
private const SEGMENT_W:uint = 12;
private const SEGMENT_H:uint = 3;
// 物理パラメータ
// for アンカー
private var GRAVITY:Number = 0.125;
private var FRICTION:Number = 0.97;
private var FLOORFRICTION:Number = 0.9;
private var BOUNCE:Number = -0.42;
private var TOP:Number = 0;
private var BOTTOM:Number = stage.stageHeight;
private var LEFT:Number = 0;
private var RIGHT:Number = stage.stageWidth;
// for ジョイント
private var STIFFNESS:Number = 0.071;
// 各マネージャ
private var anchorManager:AnchorManager;
private var jointManager:JointManager;
private var imageManager:ImageManager;
// 表示文字列
private const LETTER:String = "ふわふわ";
public function Main() {
// 表示文字列の TextField
var textField:TextField = new TextField();
var textFormat:TextFormat = new TextFormat(null, 90, 0xFFFFFF);
textFormat.letterSpacing = -2;
textField.defaultTextFormat = textFormat;
textField.text = LETTER;
textField.autoSize = TextFieldAutoSize.LEFT;
// TextField を BitmapData 化
var textBitmapData1:BitmapData = new BitmapData(textField.width, textField.height, true, 0x00000000);
textBitmapData1.draw(textField, new Matrix(1, 0, 0, 1, -2, -2));
var rect:Rectangle = textBitmapData1.getColorBoundsRect(0xFF000000, 0x00000000, false);
var textBitmapData2:BitmapData = new BitmapData(rect.width + 4, rect.height + 4, true, 0x00000000);
textBitmapData2.draw(textBitmapData1, new Matrix(1, 0, 0, 1, -rect.x + 2, -rect.y + 2));
textBitmapData1.dispose();
next(textBitmapData2);
}
private function next(bitmapData:BitmapData):void {
// イメージのサイズ
var imageWidth:Number = bitmapData.width;
var imageHeight:Number = bitmapData.height;
// セグメント化クラス
var segmentation:Segmentation = new Segmentation(SEGMENT_W, SEGMENT_H, imageWidth, imageHeight);
// アンカーマネージャ処理
initAnchor();
anchorManager = new AnchorManager();
var offsetX:Number = (stage.stageWidth - imageWidth) / 2;
var offsetY:Number = (stage.stageHeight - imageHeight) / 2;
anchorManager.buildAnchors(segmentation, offsetX, offsetY);
anchorManager.visible = false;
// ジョイントマネージャ処理
initJoint();
jointManager = new JointManager(anchorManager.anchors);
jointManager.buildJoints(SEGMENT_W, SEGMENT_H);
jointManager.visible = false;
// イメージマネージャ処理
imageManager = new ImageManager(anchorManager.anchors);
imageManager.buildImage(bitmapData, segmentation);
// 各マネージャを addChild
addChild(imageManager);
addChild(anchorManager);
addChild(jointManager);
// イベントハンドラ
var timer:Timer = new Timer(5000);
timer.addEventListener(TimerEvent.TIMER, timerHandler);
timer.start();
timerHandler(null);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
private function timerHandler(e:TimerEvent):void {
anchorManager.reverseGravity();
}
// キーボードイベント
private function keyDownHandler(event:KeyboardEvent):void {
if (event.keyCode == 82) { // "r" キーで重力反転
anchorManager.reverseGravity();
}
if (event.keyCode == 65) { // "a" キーでアンカー表示
anchorManager.visible = !anchorManager.visible;
}
//if (event.charCode == 106) { // "j" キーでジョイント表示
//jointManager.visible = !jointManager.visible;
//}
}
// フレームイベント
private function enterFrameHandler(event:Event):void {
anchorManager.update();
jointManager.update();
imageManager.update();
}
// アンカーの初期化
private function initAnchor():void {
Anchor.gravity = GRAVITY;
Anchor.friction = FRICTION;
Anchor.floorFriction = FLOORFRICTION;
Anchor.bounce = BOUNCE;
Anchor.top = TOP;
Anchor.bottom = BOTTOM;
Anchor.left = LEFT;
Anchor.right = RIGHT;
}
// ジョイントの初期化
private function initJoint():void {
Joint.stiffness = STIFFNESS;
}
}
}
// アンカーマネージャ
import flash.display.Sprite;
import flash.events.MouseEvent;
class AnchorManager extends Sprite {
// アンカー格納 Vector
private var _anchors:Vector.<Anchor>;
public function get anchors():Vector.<Anchor> { return _anchors; }
// アンカー表示・非表示フラグ
private var _anchorVisible:Boolean;
override public function get visible():Boolean { return _anchorVisible; }
override public function set visible(value:Boolean):void {
_anchorVisible = value;
alpha = (_anchorVisible) ? 1.0 : 0.0;
}
private var prevAnchors:Vector.<Anchor>;
private var reverseMin:uint;
private var reverseMax:uint;
public function AnchorManager() {
addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
}
// アンカーの生成
public function buildAnchors(segmentation:Segmentation, offsetX:Number = 0.0, offsetY:Number = 0.0):void {
var positions:Vector.<Number> = segmentation.verticies;
var n:uint = positions.length / 2;
_anchors = new Vector.<Anchor>(n);
for (var i:uint = 0; i < n; i++) {
var anchor:Anchor = new Anchor();
anchor.x = positions[i * 2] + offsetX;
anchor.y = positions[i * 2 + 1] + offsetY;
addChild(anchor);
_anchors[i] = anchor;
anchor.name = "a_" + i;
}
reverseMin = _anchors.length * 0.25;
reverseMax = _anchors.length * 0.5;
}
// アップデート
public function update():void {
for each (var anchor:Anchor in _anchors) {
anchor.update();
}
}
public function reverseGravity():void {
// prev 解放
if (prevAnchors) {
for each (var anchor:Anchor in prevAnchors) {
anchor.reverseGravity();
}
}
var selector:Array = createSeqNumArray(_anchors.length);
shuffle(selector);
prevAnchors = new Vector.<Anchor>();
var numOfReverse:uint = Math.random() * reverseMax + reverseMin;
for (var i:int = 0; i < numOfReverse; i++) {
var target:uint = selector[i];
var current:Anchor = _anchors[target];
current.reverseGravity();
prevAnchors[i] = current;
}
}
// シャッフル
private function shuffle(a:Array):void {
var n:int;
var t:*;
var l:uint = a.length;
while (l--) {
n = Math.floor(Math.random() * (l+1));
t = a[l];
a[l] = a[n];
a[n] = t;
}
}
// 長さが len の配列を作成
// 配列の要素は start から開始、増分は rate
private function createSeqNumArray(
len:uint, // 配列長
start:Number = 0, // 開始数
rate:Number = 1 // 増加率
):Array {
var a:Array = [];
while (len--) { a[len] = len * rate + start; }
return a;
}
// マウスハンドラ
private function mouseDownHandler(event:MouseEvent):void {
var anchor:Anchor = Anchor(event.target);
anchor.isMouseDown = true;
}
private function mouseUpHandler(event:MouseEvent):void {
if (event.target != null) {
var anchor:Anchor = Anchor(event.target);
anchor.isMouseDown = false;
}
}
}
// アンカー
import flash.display.Sprite;
import flash.events.MouseEvent;
class Anchor extends Sprite {
// 物理的数値
static public var gravity:Number = 0.98; // 重力
static public var friction:Number = 0.99; // 空気抵抗(数値が小さいほど抵抗が強くなる)
static public var floorFriction:Number = 0.9; // 床面抵抗(数値が小さいほど滑りにくい)
static public var bounce:Number = -0.9; // 跳ね返り(絶対値が小さいほど跳ねにくい)
// 壁面値
static public var left:Number;
static public var right:Number;
static public var top:Number;
static public var bottom:Number;
// 現在の重力
private var currentGravity:Number = gravity;
private var gravityBoolean:Boolean = true;
private const NORMAL_COLOR:uint = 0xFF0000;
private const REVERSE_COLOR:uint = 0x0000FF;
// 計算用変数
// 速度
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;
public function set isMouseDown(value:Boolean):void {
_isMouseDown = value;
(_isMouseDown) ? startDrag() : stopDrag();
}
private var radius:Number;
public function Anchor(radius:Number = 17.0, color:uint = NORMAL_COLOR):void {
this.radius = radius;
draw(color);
buttonMode = true;
}
private function draw(color:uint):void{
graphics.clear();
graphics.beginFill(color, 0.25);
graphics.drawCircle(0, 0, radius);
graphics.endFill();
}
// アクセル
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 (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 += sx;
vy += sy;
vx *= friction;
vy *= friction;
vy += currentGravity;
x += vx;
y += vy;
}
// 剛性値の初期化
sx = 0;
sy = 0;
}
public function reverseGravity():void {
var color:uint = gravityBoolean ? REVERSE_COLOR : NORMAL_COLOR;
draw(color);
currentGravity *= -1;
gravityBoolean = !gravityBoolean;
}
}
// ジョイント
class Joint {
// 物理的数値
static public var stiffness:Number = 0.17; // 剛性値
// 両端のアンカー
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;
getAnchorRelationData();
defaultDistance = distanceXY;
}
// ジョイントの剛性計算をおこない、対象アンカーに反映させる
public function update():void {
getAnchorRelationData();
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 getAnchorRelationData():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;
}
}
// ジョイントマネージャ
import flash.display.GraphicsPath;
import flash.display.GraphicsPathCommand;
import flash.display.GraphicsSolidFill;
import flash.display.GraphicsStroke;
import flash.display.IGraphicsData;
import flash.display.Shape;
class JointManager extends Shape {
// 各格納 Vector
private var joints:Vector.<Joint>;
private var pair:Vector.<int>;
private var anchors:Vector.<Anchor>;
// 描画用データ
private var graphicsData:Vector.<IGraphicsData>;
private var path:GraphicsPath;
// ジョイント表示・非表示フラグ
private var _jointVisible:Boolean;
override public function get visible():Boolean { return _jointVisible; }
override public function set visible(value:Boolean):void {
_jointVisible = value;
if (!_jointVisible) graphics.clear();
}
public function JointManager(anchors:Vector.<Anchor>) {
this.anchors = anchors;
}
// ジョイントの生成
public function buildJoints(segW:uint, segH:uint):void {
createPair(segW, segH); // 頂点の組み合わせの生成
createGraphicsData(); // 表示用データの生成
// ジョイントの生成
var n:uint = pair.length / 2;
joints = new Vector.<Joint>(n);
for (var i:uint = 0; i < n; i++) {
var a:uint = pair[i * 2];
var b:uint = pair[i * 2 + 1];
var joint:Joint = new Joint(anchors[a], anchors[b]);
joints[i] = joint;
}
}
// アップデート
public function update():void {
for each (var joint:Joint in joints) {
joint.update();
}
if (_jointVisible) {
// data の更新
path.data = getData();
// 描画
graphics.clear();
graphics.drawGraphicsData(graphicsData);
}
}
// 頂点の組み合わせの生成
private function createPair(segW:uint, segH:uint):void {
pair = new Vector.<int>();
// 横
for (var i:uint = 0; i < segH + 1; i++) {
for (var j:uint = 0; j < segW; j++) {
var a:uint = i * (segW + 1) + j;
var b:uint = i * (segW + 1) + j + 1;
pair.push(a, b);
}
}
// 縦
for (i = 0; i < segH; i++) {
for (j = 0; j < segW+1; j++) {
a = i * (segW + 1) + j;
b = (i + 1) * (segW + 1) + j;
pair.push(a, b);
}
}
// 斜め(左上から右下)
for (i = 0; i < segH; i++) {
for (j = 0; j < segW; j++) {
a = i * (segW + 1) + j;
b = (i + 1) * (segW + 1) + j + 1;
pair.push(a, b);
}
}
// 斜め(右上から左下)
for (i = 0; i < segH; i++) {
for (j = 0; j < segW; j++) {
a = i * (segW + 1) + j + 1;
b = (i + 1) * (segW + 1) + j;
pair.push(a, b);
}
}
if (segW % 2 == 0) {
// 横斜め(左上から右下)
for (i = 0; i < segH; i++) {
for (j = 0; j < segW - 1; j += 2) {
a = i * (segW + 1) + j;
b = (i + 1) * (segW + 1) + j + 2;
pair.push(a, b);
}
}
// 横斜め(右上から左下)
for (i = 0; i < segH; i++) {
for (j = 0; j < segW; j+=2) {
a = i * (segW + 1) + j + 2;
b = (i + 1) * (segW + 1) + j;
pair.push(a, b);
}
}
}
if (segH % 2 == 0) {
// 縦斜め(左上から右下)
for (i = 0; i < segH; i += 2) {
for (j = 0; j < segW; j++) {
a = i * (segW + 1) + j;
b = (i + 2) * (segW + 1) + j + 1;
pair.push(a, b);
}
}
// 縦斜め(右上から左下)
for (i = 0; i < segH; i+=2) {
for (j = 0; j < segW; j++) {
a = i * (segW + 1) + j + 1;
b = (i + 2) * (segW + 1) + j;
pair.push(a, b);
}
}
}
// 全体の対角線
var numOfVertex:uint = (segW+1) * (segH+1);
if (segW != segH) {
pair.push(0, numOfVertex - 1);
pair.push(segW, numOfVertex - 1 - segW);
}
}
// GraphicsData の生成
private function createGraphicsData():void{
// 線
var stroke:GraphicsStroke = new GraphicsStroke(0);
stroke.fill = new GraphicsSolidFill(0xFFFFFF, 0.5);
graphicsData = new Vector.<IGraphicsData>(2);
graphicsData.push(stroke);
graphicsData.push(path = new GraphicsPath(getCommands(), getData()));
}
// graphicsPath の commands の生成
private function getCommands():Vector.<int> {
var n:uint = pair.length / 2;
var commands:Vector.<int> = new Vector.<int>(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;
}
// graphicsPath の data の生成(updata のたびに呼ばれる)
private function getData():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;
}
}
// イメージマネージャ
import flash.display.BitmapData;
import flash.display.GraphicsBitmapFill;
import flash.display.GraphicsEndFill;
import flash.display.GraphicsTrianglePath;
import flash.display.IGraphicsData;
import flash.display.Shape;
class ImageManager extends Shape {
// アンカー格納 Vector
private var anchors:Vector.<Anchor>;
// 描画用データ
private var graphicsData:Vector.<IGraphicsData>;
private var trianglePath:GraphicsTrianglePath;
public function ImageManager(anchors:Vector.<Anchor>) {
this.anchors = anchors;
}
// イメージ表示用データの生成
public function buildImage(bitmapData:BitmapData, segmentation:Segmentation):void {
createGraphicsData(bitmapData, segmentation); // 表示用データの生成
}
// アップデート
public function update():void {
// drawTriangle のデータ更新
trianglePath.vertices = getVerticies();
// 描画
graphics.clear();
graphics.drawGraphicsData(graphicsData);
}
// GraphicsData の生成
private function createGraphicsData(bitmapData:BitmapData, segmentation:Segmentation):void {
graphicsData = new Vector.<IGraphicsData>(3);
graphicsData.push(new GraphicsBitmapFill(bitmapData));
graphicsData.push(trianglePath = new GraphicsTrianglePath(segmentation.verticies, segmentation.indicies, segmentation.uvData));
graphicsData.push(new GraphicsEndFill());
}
// graphicsPath の verticies の生成(updata のたびに呼ばれる)
// アンカー座標の更新
private function getVerticies():Vector.<Number> {
var n:uint = anchors.length;
var verticies:Vector.<Number> = new Vector.<Number>(n * 2);
for (var i:uint = 0; i < n; i++) {
var anchor:Anchor = anchors[i];
verticies[i * 2] = anchor.x;
verticies[i * 2 + 1] = anchor.y;
}
return verticies;
}
}
// セグメント化
class Segmentation {
// verticies
private var _verticies:Vector.<Number>;
public function get verticies():Vector.<Number> { return _verticies; }
// indicies
private var _indicies:Vector.<int>;
public function get indicies():Vector.<int> { return _indicies; }
// uvDatas
private var _uvData:Vector.<Number>;
public function get uvData():Vector.<Number> { return _uvData; }
// コンストラクタ
public function Segmentation(segW:uint, segH:uint, width:Number = 1, height:Number = 1) {
createVerticies(segW, segH, width, height);
createIndicies(segW, segH);
createUvData(segW, segH);
}
// verticies の生成
private function createVerticies(segW:uint, segH:uint, w:Number, h:Number):void {
_verticies = new Vector.<Number>((segW + 1) * (segH + 1) * 2);
var cnt:uint = 0;
for (var i:uint = 0; i < segH + 1; i++) {
for (var j:uint = 0; j < segW + 1; j++) {
verticies[cnt++] = j / segW * w;
verticies[cnt++] = i / segH * h;
}
}
}
// indicies の生成
private function createIndicies(segW:uint, segH:uint):void {
_indicies = new Vector.<int>(segW * segH * 6);
var cnt:uint = 0;
for (var i:uint = 0; i < segH; i++) {
for (var j:uint = 0; j < segW; j++) {
var leftTop:uint = i * (segW + 1) + j;
var rightTop:uint = i * (segW + 1) + j + 1;
var leftBottom:uint = (i + 1) * (segW + 1) + j;
var rightBottom:uint = (i + 1) * (segW + 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;
}
}
}
// uvDatas の生成
private function createUvData(segW:uint, segH:uint):void {
_uvData = new Vector.<Number>((segW + 1) * (segH + 1) * 2);
var cnt:uint = 0;
for (var i:uint = 0; i < segH + 1; i++) {
for (var j:uint = 0; j < segW + 1; j++) {
uvData[cnt++] = j / segW;
uvData[cnt++] = i / segH;
}
}
}
}