///////////////////////////////////////////////////////////////////// // // // A Cube in MacroMedia Flash. // // Three Dimensional Rendering with Perspective and Shadow. // // // // The Flash engine does lots of pixel-by-pixel computations, // // so you just need a few lines of ActionScript to tell it // // where to do its line draws and area fills. // // // // Copyright Philip C. Lehar www.Plehar.com // // version 2. 24 feb 2004 // // // // USE MY CODE. MAKE COPIES. NO ROYALTIES. // // LEAVE THIS BOX IN EVERY COPY. // // // // References: // // (1) ActionScript for Flash MX - Colin Moock - O'Reilly // // (2) JavaScript The Difinitive Guide - David Flanagan - O'Reilly // // (3) Mathematical Elements for Computer Graphics // // - David F. Rogers, J. Alan Adams - McGraw Hill // // // ///////////////////////////////////////////////////////////////////// // Just write #include "cube.txt" in main Actions Frame of cube.fla //----------------------------globals-------------------------------// var black = 0x000000, white = 0xFEFEFF, blue = 0x0033CC; var scale_value = 50; // 50 pixels per unit var projection = 0.1; var clip_depth = 0; // stacks movie clips var display_offset = 200; var zlimit = -8; // can't get closer on z axis //------------------------------RUN---------------------------------// display_background(); var cube = makecube(display_offset,display_offset); // starting position move(cube,-0.5,0.5,0.5); rotx(cube, degree_to_radian(30)); roty(cube, degree_to_radian(30)); move(cube,0,-1,0); drawcube(cube); //------------------------data-structures---------------------------// function point(x,y,z,h) { return {x:x, y:y, z:z, h:h}; } function makecube(xloc, yloc) { var clip = createEmptyMovieClip("x"+clip_depth,clip_depth++); clip._x = xloc; //position the clip clip._y = yloc; return{ cube:{ a:point( 0, 0, 0,1), b:point( 0, 0,-1,1), c:point( 1, 0,-1,1), d:point( 1, 0, 0,1), e:point( 0,-1, 0,1), f:point( 0,-1,-1,1), g:point( 1,-1,-1,1), h:point( 1,-1, 0,1), center:point(0.5, -0.5, -0.5, 1)}, clip:clip } } //--------------------matrix-translation-functions-----------------// function move(figure, x,y,z) { trans(figure, matrix( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 )); } function rotx(figure, angle) { tracnumber("rotx",angle); var c = Math.cos(angle); var s = Math.sin(angle); trans(figure, matrix(1, 0, 0, 0, 0, c, s, 0, 0,-s, c, 0, 0, 0, 0, 1 )); } function roty(figure, angle) { tracnumber("roty",angle); var c = Math.cos(angle); var s = Math.sin(angle); trans(figure, matrix(c, 0,-s, 0, 0, 1, 0, 0, s, 0, c, 0, 0, 0, 0, 1 )); } function rotz(figure, angle) { tracnumber("rotz",angle); var c = Math.cos(angle); var s = Math.sin(angle); trans(figure, matrix(c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 )); } function project_xy(figure, r) { trans(figure, matrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, r, 0, 0, 0, 1 )); } function project_xz(figure) { trans(figure, matrix(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 )); } function scale(figure, x,y,z) { trans(figure, matrix(x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 )); } //----------------------matrix-translation---------------------------// function matrix( a,b,c, p, // create a matrix d,e,f, q, g,i,j, r, l,m,n, s) { return { a:a,b:b,c:c, p:p, d:d,e:e,f:f, q:q, g:g,i:i,j:j, r:r, l:l,m:m,n:n, s:s} } function translate(point, matrix) { var x=point.x, y=point.y, z=point.z, h=point.h; var a=matrix.a, b=matrix.b, c=matrix.c, p=matrix.p, d=matrix.d, e=matrix.e, f=matrix.f, q=matrix.q, g=matrix.g, i=matrix.i, j=matrix.j, r=matrix.r, l=matrix.l, m=matrix.m, n=matrix.n, s=matrix.s; point.x = a*x + d*y + g*z + l*h; point.y = b*x + e*y + i*z + m*h; point.z = c*x + f*y + j*z + n*h; point.h = p*x + q*y + r*z + s*h; } function trans(f, matrix) { translate(f.cube.a, matrix); translate(f.cube.b, matrix); translate(f.cube.c, matrix); translate(f.cube.d, matrix); translate(f.cube.e, matrix); translate(f.cube.f, matrix); translate(f.cube.g, matrix); translate(f.cube.h, matrix); translate(f.cube.center, matrix); } //-------------------mouse-interface---rotate---------------------// // The mouse clicked OUTSIDE the cube, so // we want to ROTATE cube whenever the mouse moves. var outside = rotate_cube(); onMouseDown = function() { outside.x = _xmouse - display_offset; outside.y = _ymouse - display_offset; onMouseMove = function() { outside.rotate(_xmouse,_ymouse); } } function rotate_cube() { function rotate(xm,ym) { xm -= display_offset; ym -= display_offset; var ccube = cube.cube.center; var x = ccube.x, y = ccube.y, z = ccube.z; xmm = 0.01 * -(xm - this.x); ymm = 0.01 * (ym - this.y); move(cube,-x,-y,-z); roty(cube, xmm); rotx(cube, ymm); move(cube,x,y,z); drawcube(cube); this.x = xm; this.y = ym; } return { x:0, y:0, rotate:rotate } } //-------------------mouse-interface---move-----------------------// // The mouse clicked INSIDE the cube, so // we want to MOVE cube whenever the mouse moves. var inside = move_cube(); cube.clip.onPress = function() { inside.x = _xmouse - display_offset; inside.y = _ymouse - display_offset; onMouseMove = function() { inside.move(_xmouse, _ymouse); } } function move_cube() { function movec(x,y) { x -= display_offset; y -= display_offset; var c = cube.cube.center; var incx = (x - this.x)/scale_value; var incy = (y - this.y)/scale_value; var closeness = (c.z/c.h)+ incy; if(closeness < zlimit) incy = 0; move(cube, incx, 0, incy); drawcube(cube); this.x = x; this.y = y; } return { x:0, y:0, move:movec } } //-------------------mouse-interface---reset---------------------// // save whatever "_root" has as its // default mouse move function, // so we can put it back. var mousemove_default = onMouseMove; onMouseUp = function() { onMouseMove = mousemove_default; } cube.clip.onRelease = function() { onMouseMove = mousemove_default; } //---------------------debug-functions----------------------------// function tracepoint(str,pt) { var places = 100; function num(n) { return Math.round(n * places) / places; } if(! isFinite(pt.x))trace(" x infinite"); if(! isFinite(pt.y))trace(" y infinite"); if(! isFinite(pt.z))trace(" z infinite"); if(! isFinite(pt.h))trace(" h infinite"); if( isNaN(pt.x))trace(" x NOT Number"); if( isNaN(pt.y))trace(" y NOT Number"); if( isNaN(pt.z))trace(" z NOT Number"); if( isNaN(pt.h))trace(" h NOT Number"); if( pt.x == undefined )trace(" x undefined"); if( pt.y == undefined )trace(" y undefined"); if( pt.z == undefined )trace(" z undefined"); if( pt.h == undefined )trace(" h undefined"); trace( str + num(pt.x) + "," + num(pt.y) + "," + num(pt.z) + "," + num(pt.h)); } function dot(pt,color,clip) { clip.lineStyle(5,color,100); clip.moveTo(pt.x+1,pt.y); clip.lineTo(pt.x,pt.y); } function crosshair(d,clip) { clip.lineStyle(1,black,20); clip.moveTo(0,0); clip.lineTo(-d,0); clip.moveTo(0,0); clip.lineTo(d,0); clip.moveTo(0,0); clip.lineTo(0,-d); clip.moveTo(0,0); clip.lineTo(0,d); } //---------------------general-functions----------------------------// function degree_to_radian(degrees) { return (degrees / 180) * Math.PI; } function radian_to_degree(radians) { return ((radians / Math.PI) * 180); } function copypoint(p) { return point(p.x, p.y, p.z, p.h); } function copy(cube) { var o = cube.cube; return {cube:{ a:copypoint(o.a), b:copypoint(o.b), c:copypoint(o.c), d:copypoint(o.d), e:copypoint(o.e), f:copypoint(o.f), g:copypoint(o.g), h:copypoint(o.h), center:copypoint(o.center)}} } function unify(cube) { function unify(pt) { var h = pt.h; if(h == 0){ trace("h / zero"); return; } if(! isFinite(h)){ trace("h not finite"); return; } if(h == undefined){ trace("h undefined"); return; } if(isNaN(h)){ trace("h nan"); return; } pt.x /= h; pt.y /= h; pt.z /= h; pt.h = 1; } var o = cube.cube; unify(o.a); unify(o.b); unify(o.c); unify(o.d); unify(o.e); unify(o.f); unify(o.g); unify(o.h); unify(o.center); } //-----------------------------display------------------------------// function drawcube(cube) { function depict() { var image = copy(cube); project_xy(image, projection); scale(image, scale_value,scale_value,1); unify(image); return image; } function cast(image) { // project the shadow var shadow = copy(cube); var height = image.cube.center.y/scale_value/2; // project shadow image downwards to xz plane. project_xz(shadow); // move image down to not see it edge-on move(shadow, 0,-height,0); // project image of the shadow-image across to xy plane. project_xy(shadow, projection); scale(shadow, scale_value,scale_value,1); unify(shadow); return shadow; } function draw(image,clip,opaque) { function clockwise(a,b,c) { // if clockwise it's face-on // otherwise it's hidden from view. var x1 = b.x - a.x; var y1 = b.y - a.y; var x2 = c.x - a.x; var y2 = c.y - a.y; if (x1 * y2 >= y1 * x2) return true; return false; } function reflect(o,p) { // no reflection from the shadow if(opaque)return 25; // color of shadow to add to background var y = (p.y / p.h) - (o.y / o.h); var x = (p.x / p.h) - (o.x / o.h); var z = (p.z / p.h) - (o.z / o.h); // no light from above if edge pointing upward. if(y < 0) return 75; // ambient light to add to background // Otherwise, use any edge perpendicular to the face // to figure out how much reflection it should have. // The shorter it looks to the light source, the // more light reflection from the face. var d = Math.sqrt(x*x + z*z); return (d*70)+2; } function face(a,b,c,d,o,p) { if(! clockwise(a,b,c)) return; var intensity = reflect(o,p); clip.lineStyle(0,black,0); clip.moveTo(a.x,a.y); clip.beginFill(blue, intensity); clip.lineTo( b.x, b.y); clip.lineTo( c.x, c.y); clip.lineTo( d.x, d.y); clip.lineTo( a.x, a.y); clip.endFill(); } function faces(i,s) { // The first 3 points define clockwise order. // The face is invisible if not clockwise. // Pass the 4 points of the face, and the 2 points of // an edge (any edge) perpendicular to the face. face(i.a, i.b, i.c, i.d, s.a, s.e); // abcd face(i.h, i.g, i.f, i.e, s.h, s.d); // hgfe face(i.b, i.f, i.g, i.c, s.b, s.a); // bfgc face(i.d, i.h, i.e, i.a, s.d, s.c); // dhea face(i.e, i.f, i.b, i.a, s.e, s.h); // efba face(i.c, i.g, i.h, i.d, s.c, s.b); // cghd } faces(image.cube,cube.cube); } cube.clip.clear(); var image = depict(); var shadow = cast(image) //tracepoint("cc ",cube.cube.center) //tracepoint("im ",image.cube.center) //tracepoint("sh ",shadow.cube.center) draw(image,cube.clip,false) draw(shadow,cube.clip,true) //dot(image.cube.center,black,cube.clip) //dot(shadow.cube.center,black,cube.clip) //crosshair(200,cube.clip) } function display_background() { var background = _root; var left = 10, right = 500, top = 10, base = 380; var filltype = "linear", colors = [blue,white,blue], alphas = [10,100,100], ratios = [0,100,255]; var matrix = { matrixType:"box", x:0, y:0, w:base-top, h:right-left, r:degree_to_radian(90) } background.lineStyle(10,black,100); background.beginGradientFill(filltype,colors,alphas,ratios,matrix); background.moveTo( left,top); background.lineTo( right,top); background.lineTo( right,base); background.lineTo( left,base); background.lineTo( left,top); background.endFill(); }