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

why not another Klein bottle

Get Adobe Flash player
by makc3d 23 Oct 2012
/**
 * Copyright makc3d ( http://wonderfl.net/user/makc3d )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/coLd
 */

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="0")]
	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;
	}
}