In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Reproduction

3D version of "Knots", a woodcut by M. C. Escher
http://www.mcescher.com/Gallery/recogn-bmp/LW444.jpg http://image53.webshots.com/653/1/85/61/2927185610096556516dtSNna_fs.jpg
Light source follows mouse, drag to rotate.
/**
 * Copyright yonatan ( http://wonderfl.net/user/yonatan )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/4BF9
 */

// forked from yonatan's MCE on SiGL
// forked from keim_at_Si's wonderflで3D 【Phong shading】
// forked from keim_at_Si's wonderflで3D 【Flat shading】 高速化
// forked from keim_at_Si's wonderflで3D 【Flat shading】
// 高速化,マウスで光源移動,クリックで材質変更
//--------------------------------------------------
package {
    import flash.display.*;
    import flash.text.*;
    import flash.geom.*;
    import flash.events.*;
    import flash.utils.*;
    import flash.filters.*;
    import flash.ui.*;
    // import net.hires.debug.Stats;

    [SWF(width="465", height="465", backgroundColor="0xE0E0E0", frameRate="30")]
    public class main extends Sprite {
        private const _sides:int = 4;
        private const _sections:int = 72;
        private var _matrix:Matrix3D = new Matrix3D();
        private var _rotMatrix:Matrix3D = new Matrix3D();
        private var _engine:EngineFaceBasedRender = new EngineFaceBasedRender(800, _sections*_sides*8);
        private var _material:Material = new Material;
        private var _light:Light = new Light();
        private var _models:Array = [];
        private var _model:Knot;
        private var _transformedSideNormals:Vector.<Number> = new Vector.<Number>();
        private var _mousePoint:Point;
        private var _timeSum:int, _timeCount:int;
        private var _textField:TextField = new TextField();
        
        function main() {
            var i:int;

            for (i=0; i<60; i++) _models.push(new Knot(2, 3, _sections, _sides, 2/3, i/60));

            stage.quality = "medium";
            _engine.x = 232;
            _engine.y = 232;
            // GlowFilter(color:uint = 0xFF0000, alpha:Number = 1.0, blurX:Number = 6.0, blurY:Number = 6.0,
            // strength:Number = 2, quality:int = 1, inner:Boolean = false, knockout:Boolean = false)
            _engine.filters = [new GlowFilter(0, 1, 4, 4, 2, 2)];
            addChild(_engine);
            addEventListener("enterFrame", _onEnterFrame);
            Mouse.cursor = MouseCursor.BUTTON;
            stage.addEventListener("mouseDown", function(e:Event):void { 
                    _mousePoint = new Point(mouseX, mouseY); 
                    Mouse.cursor = MouseCursor.HAND;
                });
            stage.addEventListener("mouseUp", function(e:Event):void {
                    _mousePoint = null; 
                    Mouse.cursor = MouseCursor.BUTTON;
                });

            // initialize parameters
            // _timeSum = 0;
            // _timeCount = 0;
            // _textField.autoSize = "left";
            // _textField.background = true;
            // _textField.backgroundColor = 0x80f080;
            // addChild(_textField);
            // var status:Stats = new Stats();
            // status.x = 400;
            // addChild(status);
        }

        private function _onEnterFrame(e:Event) : void {
            var i:int, t:int;
            _model = _models[int(getTimer()/10) % _models.length];

            // update paremters
            if(_mousePoint) {
                var p:Point = new Point(mouseX, mouseY).subtract(_mousePoint);
                var axis:Vector3D = new Vector3D(p.y, -p.x, 0); 
                axis.normalize();
                _rotMatrix.appendRotation(p.length/2, axis)
                _mousePoint.x = mouseX;
                _mousePoint.y = mouseY;
            }

            // light position
            var lx:Number = (Math.max(0, Math.min(2, mouseX / (465 / 2))) - 1) / Math.SQRT2;
            var ly:Number = (Math.max(0, Math.min(2, mouseY / (465 / 2))) - 1) / Math.SQRT2;
            var lz:Number = -Math.sqrt(1 - (lx*lx+ly*ly));
            _light.setPosition(lx, ly, lz);
            
            t = getTimer();
            _engine.pushMatrix();

            _matrix.identity();
            _matrix.append(_rotMatrix);
            _matrix.appendScale(10, 10, 10);
            _matrix.appendTranslation(0, 0, 150);
            _engine.matrix.append(_matrix);
            _engine.project(_model);
            _calculateVertexNormal();
            _engine.render(_model, _light, _material);
            _engine.popMatrix();
            _timeSum += getTimer() - t;
            
            // if (++_timeCount == 30) {
            //     _textField.text = "Redering time: " + String(_timeSum) + "[ms/30frames]";
            //     _timeSum = 0;
            //     _timeCount = 0;
            // }
        }
        
        private function _calculateVertexNormal() : void {
            var len:int = _transformedSideNormals.length;
            var ni:int = 0;
            var ti:int = 0;
            _rotMatrix.transformVectors(_model.sideNormals, _transformedSideNormals);
            while (ni<len) {
                var nx:Number = _transformedSideNormals[ni++];
                var ny:Number = _transformedSideNormals[ni++];
                var nz:Number = _transformedSideNormals[ni++];
                var v:Vector3D = _light.direction;
                var intensity:Number = v.x * nx + v.y * ny + v.z * nz;
                intensity *= intensity;
                //if(intensity<0) intensity = -intensity; // saturate
                if(nz>0) intensity = -intensity; // front/back material
                var u:Number = 0.5 + intensity/2;
                _model.texCoord[ti] = u; ti+=3;
                _model.texCoord[ti] = u; ti+=3;
                _model.texCoord[ti] = u; ti+=3;
                _model.texCoord[ti] = u; ti+=3;
            }
        }
    }
}

import flash.display.*;
import flash.filters.*;
import flash.geom.*;

class EngineFaceBasedRender extends Shape {
    public var matrix:Matrix3D;
    
    private var _vertexOnWorld:Vector.<Number>;
    private var _vout:Vector.<Number>;
    
    private var _projector:PerspectiveProjection;
    private var _projectionMatrix:Matrix3D;
    private var _matrixStac:Vector.<Matrix3D>;

    function EngineFaceBasedRender(focus:Number=400, numVerts:int = 0) {
        _vertexOnWorld = new Vector.<Number>(numVerts*3, numVerts != 0);
        _vout = new Vector.<Number>;

        _projector  = new PerspectiveProjection();
        _matrixStac = new Vector.<Matrix3D>();
        initialize(focus);
    }

    public function initialize(focus:Number) : EngineFaceBasedRender {
        _projector.focalLength = focus;
        _projectionMatrix = _projector.toMatrix3D();
        matrix = new Matrix3D();
        _matrixStac.length = 1;
        _matrixStac[0] = matrix;
        return this;
    }

    public function clearMatrix() : EngineFaceBasedRender { 
        matrix = _matrixStac[0];
        _matrixStac.length = 1;
        return this;
    }
    
    public function pushMatrix() : EngineFaceBasedRender {
        _matrixStac.push(matrix.clone());
        return this;
    }
    
    public function popMatrix() : EngineFaceBasedRender {
        if (_matrixStac.length == 1) return this;
        matrix = _matrixStac.pop();
        return this;
    }
    
    public function project(model:Model) : EngineFaceBasedRender {
        matrix.transformVectors(model.vertices, _vertexOnWorld);
        var vertices:Vector.<Number> = _vertexOnWorld;
        for each (var face:Face in model.faces) {
            var z:Number;
            z = vertices[int(int((face.i0<<1) + face.i0) + 2)];
            z += vertices[int(int((face.i1<<1) + face.i1) + 2)];
            z += vertices[int(int((face.i2<<1) + face.i2) + 2)];
            face.z = z;
        }
        model.faces.sort(function(f1:Face, f2:Face) : Number { return f2.z - f1.z; });
        return this;
    }
    
    public function render(model:Model, light:Light, material:Material) : EngineFaceBasedRender {
        Utils3D.projectVectors(_projectionMatrix, _vertexOnWorld, _vout, model.texCoord);
        graphics.clear();
        graphics.beginBitmapFill(material.texture, null, false, true);
        graphics.drawTriangles(_vout, model.indices, model.texCoord);
        graphics.endFill();
        return this;
    }
}

class Face {
    public var i0:int, i1:int, i2:int, z:Number;

    // Factory
    static private var _freeList:Vector.<Face> = new Vector.<Face>();
    static public function alloc() : Face { return _freeList.pop() || new Face(); }
    static public function free(face:Face) : void { _freeList.push(face); }
}

class Model {
    public var vertices:Vector.<Number>;
    public var texCoord:Vector.<Number>;
    public var faces:Vector.<Face>;
    private var _indices:Vector.<int> = new Vector.<int>();
    private var _faceIdx:int = 0;

    function Model(vertices:Vector.<Number>=null, texCoord:Vector.<Number>=null, faces:Vector.<Face>=null) {
        this.vertices = vertices || new Vector.<Number>();
        this.texCoord = texCoord || new Vector.<Number>();
        this.faces    = faces    || new Vector.<Face>();
        if(faces) {
            _indices = new Vector.<int>(faces.length*3, true);
        }
    }
    
    public function clear() : Model {
        for each (var face:Face in faces) Face.free(face);
        faces = new Vector.<Face>();
        _faceIdx = 0;
        return this;
    }
    
    public function face(i0:int, i1:int, i2:int) : Model {
        var face:Face = Face.alloc();
        face.i0 = i0;
        face.i1 = i1;
        face.i2 = i2;
        faces[_faceIdx++] = face;
        return this;
    }
    
    public function get indices() : Vector.<int> {
        var i:int = 0;
        for each (var face:Face in faces) {
            _indices[i++] = face.i0;
            _indices[i++] = face.i1;
            _indices[i++] = face.i2;
        }
        return _indices;
    }
}

class Light {
    private var _direction:Vector3D = new Vector3D();
    private var _halfVector:Vector3D = new Vector3D();
    public function get direction() : Vector3D { return _direction; }
    public function get halfVector() : Vector3D { return _halfVector; }
    
    function Light(x:Number=1, y:Number=1, z:Number=1) { setPosition(x, y, z); }
    
    public function setPosition(x:Number, y:Number, z:Number) : void {
        _direction.x = -x;
        _direction.y = -y;
        _direction.z = -z; 
        _direction.normalize();
        _halfVector.x = _direction.x;
        _halfVector.y = _direction.y;
        _halfVector.z = _direction.z + 1; 
        _halfVector.normalize();
    }
}

class Material {
    private static const LINES:int = 13;
    private static const W:int = 0x800;
    private static const H:int = 0x100;

    public var texture:BitmapData = new BitmapData(W,H,false);
    private var _shape:Shape = new Shape;

    function Material() { setColor(0xdb9438, 0xb2a64a); }

    protected function _drawLines(color:uint, start:Number, end:Number):void {
        for(var line:int = 0; line<LINES; line++) {
            var y1:Number = line/LINES*H;
            var y2:Number = (line+1)/LINES*H;
            _shape.graphics.beginFill(color);
            _shape.graphics.moveTo(start*W, y1)
            _shape.graphics.lineTo(end*W, (y1+y2)/2)
            _shape.graphics.lineTo(start*W, y2)
            _shape.graphics.endFill();
        }
    }
    
    public function setColor(front:uint, back:uint) : Material
    {
        _shape.graphics.clear();
        _shape.graphics.beginFill(back);
        _shape.graphics.drawRect(0, 0, W/2, H);
        _shape.graphics.endFill();
        _shape.graphics.beginFill(front);
        _shape.graphics.drawRect(W/2, 0, W/2, H);
        _shape.graphics.endFill();
        _drawLines(0xFFFFFF, 9/8, 6/7);
        _drawLines(0, 1/2, 6/7-0.025);
        _drawLines(0, 1/2, -1/4);
        texture.draw(_shape, null, null, null, null, true);
        texture.applyFilter(texture, texture.rect, new Point, new BlurFilter(4,4,3));
        return this;
    }
    
    static public function calculateTexCoord(texCoord:Point, light:Light, normal:Vector3D, doubleSided:Boolean=false) : void {
        var v:Vector3D = light.direction;
        texCoord.x = v.x * normal.x + v.y * normal.y + v.z * normal.z;
        if (texCoord.x < 0) texCoord.x = (doubleSided) ? -texCoord.x : 0;
        v = light.halfVector;
        texCoord.y = v.x * normal.x + v.y * normal.y + v.z * normal.z;
        if (texCoord.y < 0) texCoord.y = (doubleSided) ? -texCoord.y : 0;
    }
}

class Knot extends Model {
    private var _p:int, _q:int, _sections:int, _slices:int, _sides:int, _thickness:Number, _offset:Number;
    public var sideNormals:Vector.<Number>;

    public function Knot(p:int = 2, q:int = 3, sections:int = 72, sides:int = 4, thickness:Number = 2/3, offset:Number = 0) {
        var numVerts:int = sections*sides*8;
        _p = p;
        _q = q;
        _sections = sections;
        _slices = sections * 2;
        _sides = sides;
        _thickness = thickness;
        _offset = offset;
        super(
            new Vector.<Number>(numVerts*3, true), 
            new Vector.<Number>(numVerts*3, true),
            new Vector.<Face>(sections*sides*8, true)
        );
        sideNormals = new Vector.<Number>(_slices*_sides*3, true)
        _createVertices();
        _createFaces();
        _createNormals();
    }

    protected function _createNormals():void {
        var i:int = 0;
        for(var section:int=0; section<_sections; section++) {
            for(var side:int=0; side<_sides; side++) {
                for(var slice:int=0; slice<2; slice++) {
                    var u:Number = Math.PI*2 * (section*2+slice)/_slices;
                    var v:Number = Math.PI*2 * side/_sides - u*(_sides-1)/_sides;
                    var n:Vector3D = _extrudedPoint(u, v, 1);
                    n.decrementBy(_curvePoint(u));
                    n.normalize();
                    sideNormals[i++] = n.x;
                    sideNormals[i++] = n.y;
                    sideNormals[i++] = n.z;
                }
            }
        }
    }

    protected function _createFaces():void {
        for(var section:int=0; section<_sections; section++) {
            for(var side:int=0; side<_sides; side++) {
                var i:int = section*_sides*8 + side*8;
                // section
                face(i+1, i+0, i+4).face(i+4, i+5, i+1)
                i++;
                face(i+1, i+0, i+4).face(i+4, i+5, i+1)
                i++;
                face(i+1, i+0, i+4).face(i+4, i+5, i+1)
                i--; i--;
                // connector
                var i0:int, i1:int, i2:int, i3:int;
                i0=i+5; i1=i+6;
                i += _sides*8;
                if(section+1 == _sections) {
                    i+=8; // compensate for twist
                    if(side+1 == _sides) i = 0;
                }
                i %= vertices.length/3;
                i2=i+1; i3=i+2;
                face(i0, i2, i1); face(i2, i3, i1);
            }
        }
    }

    protected function _createVertices():void {
        var i:int = 0;
        function _vertex(v:Vector3D, texv:Number):void {
            vertices[i] = v.x; texCoord[i++] = 0;
            vertices[i] = v.y; texCoord[i++] = texv;
            vertices[i] = v.z; texCoord[i++] = 0;
        }
        for(var section:int=0; section<_sections; section++) {
            for(var side:int=0; side<_sides; side++) {
                var p:Number;
                for each(p in [0, 0.4, 0.6, 1]) _vertex(_edgePoint(section*2, side, p), p);
                for each(p in [0, 0.4, 0.6, 1]) _vertex(_edgePoint(section*2+1, side, p), p);
            }
        }
    }

    // 0 <= p <= 1 is the offset along the edge
    protected function _edgePoint(slice:int, side:int, p:Number):Vector3D {
        var u:Number = Math.PI*2 * slice/_slices;
        var v:Number = Math.PI*2 * side/_sides - u*(_sides-1)/_sides;
        v -= Math.PI / _sides; // make side 0 parallel to Z axis
        var v0:Vector3D = _extrudedPoint(u, v, _thickness);
        var v1:Vector3D = _extrudedPoint(u, v + Math.PI*2/_sides, _thickness);
        return _vlerp(v0, v1, p);
    }

    protected function _extrudedPoint(u:Number, v:Number, length:Number):Vector3D {
        v -= Math.PI*2*_offset/_sections*(_sides-1)/_sides;
        // calculate curve point and normal vectors from u
        var cp:Vector3D = _curvePoint(u);
        var ncp:Vector3D = _curvePoint(u+0.0001);
        var t:Vector3D = ncp.subtract(cp);
        var n:Vector3D = ncp.add(cp);
        var b:Vector3D = t.crossProduct(n);
        n = b.crossProduct(t);
        n.normalize();
        b.normalize();
        // extrude in direction v
        var cx:Number = length * Math.sin(v);
        var cy:Number = length * -Math.cos(v);
        return new Vector3D(
            cp.x + cx*n.x + cy*b.x, 
            cp.y + cx*n.y + cy*b.y, 
            cp.z + cx*n.z + cy*b.z
        );
    }

    protected function _curvePoint(t:Number):Vector3D {
        t += Math.PI*2*_offset/_sections;
        return new Vector3D(
            2 * Math.sin(t*_p) - Math.sin(t),
            -(2 * Math.cos(t*_p) + Math.cos(t)),
            Math.sin(t*_q)
        );
    }

    protected function _vlerp(v0:Vector3D, v1:Vector3D, p:Number):Vector3D {
        return new Vector3D(
            v0.x*(1-p) + v1.x*p,
            v0.y*(1-p) + v1.y*p,
            v0.z*(1-p) + v1.z*p,
            v0.w*(1-p) + v1.w*p
        );
    }
}