Complementary Penrose Stairs Fractal
思わず自画自賛したくなる美しさ!
/**
* Copyright o8que ( http://wonderfl.net/user/o8que )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/atBv
*/
/* ------------------------------------------------------------------------------------------------
* [階段シリーズ]
* http://wonderfl.net/c/zQW4 hiyoko vs. unko (HVU)
* http://wonderfl.net/c/2epI A
* http://wonderfl.net/c/z1Fp Penrose Stairs Complex
* http://wonderfl.net/c/lVRx Penrose Stairs Fractal
* http://wonderfl.net/c/atBv Complementary Penrose Stairs Fractal
* ------------------------------------------------------------------------------------------------
*/
package {
import com.bit101.components.Label;
import com.bit101.components.NumericStepper;
import flash.display.Sprite;
import flash.events.Event;
[SWF(width="465",height="465",frameRate="30",backgroundColor="0x000000")]
public class Main extends Sprite {
private static const TILE_SIZE:Number = 82;
public function Main() {
graphics.beginFill(0x000000);
graphics.drawRect(0, 0, 465, 465);
graphics.endFill();
var penroseStairs:IsoObject;
var isoContainer:Sprite = new Sprite();
isoContainer.x = 232.5 - TILE_SIZE / 4;
isoContainer.y = 732;
addChild(isoContainer);
new Label(this, 10, 10, "level");
var level:NumericStepper = new NumericStepper(this, 50, 10, function(event:Event):void {
isoContainer.removeChild(penroseStairs);
isoContainer.addChild(penroseStairs = new CPSF(level.value, TILE_SIZE, 0x0));
});
level.minimum = 1;
level.maximum = 4;
isoContainer.addChild(penroseStairs = new CPSF(1, TILE_SIZE, 0x0));
}
}
}
/* ------------------------------------------------------------------------------------------------
* IsoObject
* ------------------------------------------------------------------------------------------------
*/
//package {
import flash.display.Sprite;
import flash.geom.Point;
import flash.geom.Vector3D;
//public
class IsoObject extends Sprite {
private var _world:Vector3D;
private var _screen:Point;
public function IsoObject() {
_world = new Vector3D(0, 0, 0);
_screen = new Point(0, 0);
}
public function update():void {
x = _screen.x;
y = _screen.y;
}
public function get wx():Number { return _world.x; }
public function set wx(value:Number):void { _world.x = value; IsoUtils.isoToScreen(_world, _screen); update(); }
public function get wy():Number { return _world.y; }
public function set wy(value:Number):void { _world.y = value; IsoUtils.isoToScreen(_world, _screen); update(); }
public function get wz():Number { return _world.z; }
public function set wz(value:Number):void { _world.z = value; IsoUtils.isoToScreen(_world, _screen); update(); }
public function get sx():Number { return _screen.x; }
public function set sx(value:Number):void { _screen.x = value; IsoUtils.screenToIso(_screen, _world); update(); }
public function get sy():Number { return _screen.y; }
public function set sy(value:Number):void { _screen.y = value; IsoUtils.screenToIso(_screen, _world); update(); }
}
//}
/* ------------------------------------------------------------------------------------------------
* CPSF
* ------------------------------------------------------------------------------------------------
*/
//package {
//public
class CPSF extends IsoObject {
private var _children:Vector.<CPSF>;
private var tr:uint; // top-right pattern
private var bl:uint; // bottom-left pattern
public function CPSF(level:int, size:Number, pattern:uint) {
if (level == 1) {
buildLv1(size, pattern);
}else {
_children = new Vector.<CPSF>(10, true);
build(level, size, pattern);
}
}
private function buildLv1(size:Number, pattern:uint):void {
var type:uint = pattern & 0x1;
var step:Array, tileX:Array, tileZ:Array;
if (type == 0) {
step = STEP0, tileX = TILEX0, tileZ = TILEZ0;
}else {
step = STEP1, tileX = TILEX1, tileZ = TILEZ1;
}
var riser:Number = -size / 10;
for (var i:int = 0; i < 10; i++) {
var tile:IsoObject = new IsoBox(size, 500);
tile.wx = size * tileX[i];
tile.wy = riser * step[i];
tile.wz = size * tileZ[i];
addChild(tile);
}
sx = ((type == 0) ? -1 : 1) * size / 4;
sy = -size * 1.2;
tr = bl = type;
}
private function build(level:int, size:Number, pattern:uint):void {
var type:uint = pattern & 0x1;
var step:Array, tileX:Array, tileZ:Array, determineTL:Function;
if (type == 0) {
step = STEP0, tileX = TILEX0, tileZ = TILEZ0, determineTL = determineTL0;
}else {
step = STEP1, tileX = TILEX1, tileZ = TILEZ1, determineTL = determineTL1;
}
var riser:Number = -size / 10;
for (var i:int = 0; i < 10; i++) {
var tile:CPSF = new CPSF(level - 1, size / 3.49, determineTL(i, pattern));
tile.wx += size * tileX[i];
tile.wy += riser * step[i];
tile.wz += size * tileZ[i];
addChild(tile);
_children[i] = tile;
switch(i) {
case 3: { tr = (_children[3].tr << 1) + type; break; }
case 6: { if (type == 1) bl = (_children[6].bl << 1) + type; break; }
case 7: { if (type == 0) bl = (_children[7].bl << 1) + type; break; }
}
}
sx = ((type == 0) ? -1 : 1) * size / 4;
sy = -size * 1.2;
}
private function determineTL0(index:int, pattern:uint):uint {
switch(index) {
case 0: { return pattern >> 1; }
case 1: { return ~_children[0].tr; }
case 2: { return ~_children[1].tr; }
case 3: { return ~_children[2].tr; }
case 4: { return ~_children[3].bl; }
case 5: { return ~_children[0].bl; }
case 6: { return ~_children[4].bl; }
case 7: { return ~_children[5].bl; }
case 8: { return ~_children[7].tr; }
case 9: { return ~_children[8].tr; }
default: { return 0x0; }
}
}
private function determineTL1(index:int, pattern:uint):uint {
switch(index) {
case 0: { return pattern >> 1; }
case 1: { return ~_children[0].bl; }
case 2: { return ~_children[0].tr; }
case 3: { return ~_children[2].tr; }
case 4: { return ~_children[1].bl; }
case 5: { return ~_children[3].bl; }
case 6: { return ~_children[4].bl; }
case 7: { return ~_children[6].tr; }
case 8: { return ~_children[7].tr; }
case 9: { return ~_children[8].tr; }
default: { return 0x0; }
}
}
private static const STEP0:Array = [0, 1, 2, 3, 4, 9, 5, 8, 7, 6];
private static const STEP1:Array = [0, 1, 9, 8, 2, 7, 3, 4, 5, 6];
private static const TILEX0:Array = [0, 1, 2, 3, 3, 1, 3, 1, 2, 3];
private static const TILEX1:Array = [0, 0, 2, 3, 0, 3, 0, 1, 2, 3];
private static const TILEZ0:Array = [0, 0, 0, 0, -1, -2, -2, -3, -3, -3];
private static const TILEZ1:Array = [0, -1, -1, -1, -2, -2, -3, -3, -3, -3];
}
//}
/* ------------------------------------------------------------------------------------------------
* IsoBox
* ------------------------------------------------------------------------------------------------
*/
//package {
//public
class IsoBox extends IsoObject {
private var _size:Number;
private var _height:Number;
public function IsoBox(size:Number, height:Number) {
super();
_size = size;
_height = height;
draw();
}
private function draw():void {
var halfSize:Number = _size / 2;
graphics.clear();
graphics.moveTo(0, halfSize - _height);
drawTopFace();
drawLeftFace();
drawRightFace();
function drawTopFace():void {
graphics.lineStyle(0, 0xC0C0C0);
graphics.beginFill(0xFFFFFF);
graphics.lineTo( -_size, -_height);
graphics.lineTo(0, -halfSize - _height);
graphics.lineTo(_size, -_height);
graphics.lineTo(0, halfSize - _height);
graphics.endFill();
}
function drawLeftFace():void {
graphics.lineStyle();
graphics.beginFill(0xC0C0C0);
graphics.lineTo(0, halfSize);
graphics.lineTo( -_size, 0);
graphics.lineTo( -_size, -_height);
graphics.lineTo(0, halfSize - _height);
graphics.endFill();
}
function drawRightFace():void {
graphics.lineStyle();
graphics.beginFill(0x808080);
graphics.lineTo(0, halfSize);
graphics.lineTo(_size, 0);
graphics.lineTo(_size, -_height);
graphics.lineTo(0, halfSize - _height);
graphics.endFill();
}
}
override public function get height():Number { return _height; }
override public function set height(value:Number):void { _height = value; draw(); }
}
//}
/* ------------------------------------------------------------------------------------------------
* IsoUtils
* ------------------------------------------------------------------------------------------------
*/
//package {
import flash.geom.Point;
import flash.geom.Vector3D;
//public
class IsoUtils {
public static const TRUE_SCALE:Number = Math.cos( -30 * Math.PI / 180) * Math.SQRT2;
public static function isoToScreen(iso:Vector3D, out:Point = null):Point {
out ||= new Point(0, 0);
out.x = iso.x + iso.z;
out.y = (iso.x - iso.z) / 2 + iso.y;
return out;
}
public static function screenToIso(screen:Point, out:Vector3D = null):Vector3D {
out ||= new Vector3D(0, 0, 0);
out.x = (screen.x / 2 + screen.y) - out.y;
out.z = (screen.x / 2 - screen.y) + out.y;
return out;
}
public static function compareDepth(a:Vector3D, b:Vector3D):Number {
return (a.x - a.y - a.z) - (b.x - b.y - b.z);
}
}
//}