forked from: forked from: why not another Klein bottle

by Aksor.Al1 forked from forked from: why not another Klein bottle (diff: 1)
♥0 | Line 167 | Modified 2012-10-23 16:11:42 | MIT License
play

ActionScript3 source code

/**
 * Copyright Aksor.Al1 ( http://wonderfl.net/user/Aksor.Al1 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/jVjR
 */

// forked from kahin's forked from: why not another Klein bottle
// forked from makc3d's why not another Klein bottle
package  {
    import alternativ7.engine3d.containers.ConflictContainer;
    import alternativ7.engine3d.core.Camera3D;
    import alternativ7.engine3d.core.Face;
    import alternativ7.engine3d.core.Object3DContainer;
    import alternativ7.engine3d.core.Sorting;
    import alternativ7.engine3d.core.View;
    import alternativ7.engine3d.materials.FillMaterial;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.Matrix3D;
    import flash.geom.Vector3D;
    /**
     * Because Klein bottles are fun...
     * @author makc
     */
    [SWF(backgroundColor="10
    ")]
    public class AlternativaKleinBottle extends Sprite {
        public var camera:Camera3D;
        public var bottle:KleinBottleMesh;
        public function AlternativaKleinBottle () {
            stage.quality = "low";

            camera = new Camera3D;
            camera.view = new View (465, 465); addChild (camera.view);
            camera.z = -30; camera.fov = 0.6 * Math.PI;
            Object3DContainer (new ConflictContainer).addChild (camera);

            var m1:FillMaterial = new FillMaterial (0xFFFFFF, 0.95);
            var m2:FillMaterial = new FillMaterial (0x7F7F7F, 0.95);

            bottle = new KleinBottleMesh (3);
            var f:Face;
            for each (f in bottle.facesEven) f.material = m1;
            for each (f in bottle.facesOdd) f.material = m2;
            bottle.sorting = Sorting.DYNAMIC_BSP;

            camera.parent.addChild (bottle);

            addEventListener (Event.ENTER_FRAME, loop);
        }
        public var t:Number = 0;
        public function loop (e:Event):void {
            t += 1e-3; if (t >= 1) t -= 1;
            var m:Matrix3D = interpolatePathMatrix (t, 5e-4);
            m.prependRotation (-30, Vector3D.X_AXIS);
            camera.matrix = m;
            camera.render ();
        }
        public function interpolatePathMatrix (t:Number, dt:Number):Matrix3D {
            var p:Vector3D = interpolateVector3D (bottle.path, t);
            var q:Vector3D = interpolateVector3D (bottle.path, t + dt);
            q.decrementBy (p); q.normalize ();
            var n:Vector3D = interpolateVector3D (bottle.pathNormals, t);
            var m:Vector3D = q.crossProduct (n);
            n = m.crossProduct (q); n.normalize ();
            var c:Vector3D = q.crossProduct (n);
            return new Matrix3D (Vector.<Number> ([
                /* x */  c.x,  c.y,  c.z, 0,
                /* y */ -n.x, -n.y, -n.z, 0,
                /* z */  q.x,  q.y,  q.z, 0,
                /* t */  p.x,  p.y,  p.z, 1
            ]));
        }
        public function interpolateVector3D (vs:Vector.<Vector3D>, t:Number, s:Number = 0.4):Vector3D {
            // cardinal spline
            // see http://wonderfl.net/c/vnCf/read
            var i2:int = int (t * vs.length * 0.999999);
            var i1:int = (i2 + vs.length - 1) % vs.length;
            var i3:int = (i2 + 1) % vs.length;
            var i4:int = (i2 + 2) % vs.length;
            var V1:Vector3D = vs [i1];
            var V2:Vector3D = vs [i2];
            var V3:Vector3D = vs [i3];
            var V4:Vector3D = vs [i4];
            t = t * vs.length - i2;
            return new Vector3D (
                // x
                s * ( -t * t * t + 2 * t * t - t) * V1.x +
                s * ( -t * t * t + t * t) * V2.x +
                (2 * t * t * t - 3 * t * t + 1) * V2.x +
                s * (t * t * t - 2 * t * t + t) * V3.x +
                ( -2 * t * t * t + 3 * t * t) * V3.x +
                s * (t * t * t - t * t) * V4.x,
                // y
                s * ( -t * t * t + 2 * t * t - t) * V1.y +
                s * ( -t * t * t + t * t) * V2.y +
                (2 * t * t * t - 3 * t * t + 1) * V2.y +
                s * (t * t * t - 2 * t * t + t) * V3.y +
                ( -2 * t * t * t + 3 * t * t) * V3.y +
                s * (t * t * t - t * t) * V4.y,
                // z
                s * ( -t * t * t + 2 * t * t - t) * V1.z +
                s * ( -t * t * t + t * t) * V2.z +
                (2 * t * t * t - 3 * t * t + 1) * V2.z +
                s * (t * t * t - 2 * t * t + t) * V3.z +
                ( -2 * t * t * t + 3 * t * t) * V3.z +
                s * (t * t * t - t * t) * V4.z
            );
        }
    }
}

import alternativ7.engine3d.core.Face;
import alternativ7.engine3d.core.Vertex;
import alternativ7.engine3d.objects.Mesh;
import flash.geom.Vector3D;
class KleinBottleMesh extends Mesh {
    public var facesEven:Vector.<Face> = new Vector.<Face>;
    public var facesOdd:Vector.<Face> = new Vector.<Face>;
    public var path:Vector.<Vector3D> = new Vector.<Vector3D>;
    public var pathNormals:Vector.<Vector3D> = new Vector.<Vector3D>;
    public function KleinBottleMesh (quality:uint = 1) {
        var vs:Array = [];
        var q:int = Math.max (1, 2 * quality);
        var i_max:int = Math.max (4, 8 * int (q / 2) + 1);
        var j_max:int = Math.max (3, 2 * q + 1);
        // this code is from http://wonderfl.net/c/6RzY/read
        // it could use some love too, but I don't feel like
        var pi:Number = Math.PI, pi2:Number = pi*2;   
        var u:Number, v:Number, r:Number, vx:Number, vy:Number, vz:Number;
        for (var i:int = 0; i <= i_max; i++) {
            vs [i] = [];
            for (var j:int = 0; j <= j_max; j++) {
                u = pi2 * i / (i_max + 1); v = pi2 * j / (j_max + 1);
                r = 5*(1 - Math.cos(u)/2);
                if(u <= pi) {
                    vx = (6*Math.cos(u)*(1 + Math.sin(u)) + r*Math.cos(u)*Math.cos(v));
                    vy = (16*Math.sin(u) + r*Math.sin(u)*Math.cos(v));
                }else if(u > pi) {
                    vx = (6*Math.cos(u)*(1 + Math.sin(u)) + r*Math.cos(v + Math.PI));
                    vy = (16*Math.sin(u));
                }
                vz = r*Math.sin(v);

                u = i / i_max;
                v = j / j_max;

                vs [i][j] = addVertex (vx, vy, vz, u, v);
            }
        }
        // now make faces
        var fp:Array = [];
        for (i = 0; i <= i_max; i++) {
            var i1:int = (i + 1) % (i_max + 1);
            for (j = 0; j <= j_max; j++) {
                var j1:int = (j + 1) % (j_max + 1);
                if (i < i1) {
                    var j0:int = j;
                    var j2:int = j1;
                } else {
                    j0 = (2 * j_max - j - (q - 1)) % (j_max + 1);
                    j2 = (j0 + j_max) % (j_max + 1);
                }
                var fa:Vector.<Face> = ((i + j) % 2 == 0) ? facesEven : facesOdd;
                // can't just add quad face here because it wouldn't be flat
                fa.push (addTriFace (vs[i][j], vs[i][j1], vs[i1][j0]));
                fa.push (addTriFace (vs[i1][j0], vs[i][j1], vs[i1][j2]));
                // ironically, one-sided surface has to be double-sided mesh
                fa.push (addTriFace (vs[i1][j0], vs[i][j1], vs[i][j]));
                fa.push (addTriFace (vs[i1][j2], vs[i][j1], vs[i1][j0]));
                if (j == (j_max - q) >> 1) {
                    // save some data to build the path through bottle
                    fp.push (fa [fa.length - 1]);
                }
            }
        }
        calculateNormals ();

        // build path
        path.length = fp.length * 2;
        pathNormals.length = path.length;
        for (j = 0; j < 2; j++) {
            for (i = 0; i < fp.length; i++) {
                var f:Face = Face (fp[i]);
                var n:Vector3D = f.normal.clone ();

                if (j > 0) n.negate ();

                var k:int = j * fp.length + i;
                path [k] = new Vector3D (
                    (f.vertices [0].x + f.vertices [1].x + f.vertices [2].x) / 3, 
                    (f.vertices [0].y + f.vertices [1].y + f.vertices [2].y) / 3, 
                    (f.vertices [0].z + f.vertices [1].z + f.vertices [2].z) / 3 
                );

                path [k].incrementBy (n);
                path [k].incrementBy (n);

                pathNormals [k] = n;
            }
        }
        k = 0;
    }
}