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

Klein bottle trip in anaglyph

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

package  {
	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.BlendMode;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix3D;
	import flash.geom.Vector3D;
	/**
	 * Because Klein bottles are fun...
	 * @author makc
	 */
	[SWF(backgroundColor="0")]
	public class AlternativaKleinBottle extends Sprite {
		public var cameraLt:Camera3D;
		public var cameraRt:Camera3D;
		public var bottle:KleinBottleMesh;
		public function AlternativaKleinBottle () {
			stage.quality = "low";

			cameraLt = new Camera3D;
			cameraLt.view = new View (465, 465); addChild (cameraLt.view);
			cameraLt.view.transform.colorTransform = new ColorTransform (1, 0, 0);

			// from http://en.wikipedia.org/wiki/Anaglyph_image#Anaglyphs_containing_color_information
			// "To make an anaglyph containing color information using color images, replace the red
			// channel of the right-eye image with the red channel of the left-eye image... Eye sensitivity
			// balance can be improved by selecting the green channel and reducing it using a linear curve
			// selection (e.g. reduce to 12.5%). Select the blue channel and reduce somewhat less (e.g.
			// reduce by 5%). This action compensates for the eye's lower sensitivity to red and its high
			// sensitivity to green".
			cameraRt = new Camera3D;
			cameraRt.view = new View (465, 465); addChild (cameraRt.view);
			cameraRt.view.transform.colorTransform = new ColorTransform (0, 1 - 0.125, 1 - 0.05);

			cameraRt.view.blendMode = BlendMode.ADD;

			cameraLt.view.hideLogo ();
			cameraRt.view.hideLogo ();

			cameraLt.fov = 0.6 * Math.PI;
			cameraRt.fov = 0.6 * Math.PI;

			Object3DContainer (new Object3DContainer).addChild (cameraLt);
			cameraLt.parent.addChild (cameraRt);

			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;

			cameraLt.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);
			cameraLt.matrix = m;
			cameraLt.render ();
			m.prependTranslation (0.1, 0, 0);
			cameraRt.matrix = m;
			cameraRt.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;
	}
}