SuperCube

Visualization of a 3D cube on the 2D canvas.
A programatic implementation of the solution to a math problem by Mike Torni.
use left arrow for rotate y axis
use up arrow for rotate y axis
use down arrow for rotate x axis
use 'w' arrow for rotate z axis
use 'd' arrow for move +x direction
use 's' arrow for move +y direction
use 'a' arrow for move -x direction
use 'i' arrow for move +z direction
use 'l' arrow for rotate +z axis
use 'k' arrow for move +z direction
use 'j' arrow for rotate -z axis
 
RX:   RY:   RY:
 
TX:   TY:   TZ:

The problem is as follows:

Given the 3-dimensional coordinates of a point (x,y,z), the 3D position of your eye, and a 2-dimensional plane between your eye and the cube, perpendicular to your line of site (your computer screen), draw a line from the point (x,y,z) to your eye. The point at which that line intersects the 2d plane of the computer screen is where the point should be drawn on the screen to create the illusion of a real object existing in space at the given point beyond the screen. Perform this calculation for the 8 points where the cube has corners, and connect those points to get draw a 3D cube as you see before you!

Forturnately, as with many common problems, the solution to this one has been implemented into many 3d graphics libraries, giving this exercise little practical value. Regardless, it's good to know how things work. For those who dare, you can see how this demo works under the hood in the complete javascript source code below: author Mike Torni.

function Camera() {	
	    
	    this.screenSize = 400;
	    this.screenSizeHalf = 400 / 2;
		this.canvas = document.getElementById('canvas').getContext('2d');

		this.x = 0;
		this.y = 0;
		this.z = 500;
		this.focalLength = 1000;
		
		this.init = function(){

		};
		
		
		
		this.reset = function(){
			this.canvas.lineWidth = 1.5;
			this.canvas.clearRect(0, 0, 400, 400);		
			//draw grid;
			this.canvas.beginPath();
			this.canvas.moveTo(this.screenSizeHalf, 0);
			this.canvas.lineTo(this.screenSizeHalf, this.screenSize);
			this.canvas.stroke();

			this.canvas.beginPath();
			this.canvas.moveTo(0, this.screenSizeHalf);
			this.canvas.lineTo(this.screenSize, this.screenSizeHalf);
			this.canvas.stroke();
			//end draw grid;
		};
		
		
		this.render = function(points) {		
			alert(points.length);				
			for (var x = 0; x < points.length; x++) {			
				var p = points[x];			
				
				var X = parseInt(p.getX() + this.screenSizeHalf);
				var Y = parseInt(this.screenSizeHalf - p.getY());

				
		
				this.canvas.beginPath();
				this.canvas.fillStyle = "green";
				this.canvas.arc(X, Y, 3, 0, 2 * Math.PI, true);
				this.canvas.fill();					
			}				
		};
		
		this.transform = function(points, axisRotations, transform){		

			this.reset();
			
			var sx = Math.sin(axisRotations.getX());
			var cx = Math.cos(axisRotations.getX());
			var sy = Math.sin(axisRotations.getY());
			var cy = Math.cos(axisRotations.getY());
			var sz = Math.sin(axisRotations.getZ());
			var cz = Math.cos(axisRotations.getZ());
			var x,y,z, xy,xz, yx,yz, zx,zy, scaleFactor;
			var newPoints = new Array();

					
			var i = points.length;
			
			while (i--){
				x = points[i].x;
				y = points[i].y;
				z = points[i].z;

				// rotation around x
				xy = cx*y - sx*z;
				xz = sx*y + cx*z;
				// rotation around y
				yz = cy*xz - sy*x;
				yx = sy*xz + cy*x;
				// rotation around z
				zx = cz*yx - sz*xy;
				zy = sz*yx + cz*xy;
							
				

				scaleFactor = camera.focalLength/(camera.focalLength + yz) * transform.getZ();
				x = zx*scaleFactor;
				y = zy*scaleFactor;
				z = yz;

				
				x = x + this.screenSizeHalf + transform.getX();
				y = this.screenSizeHalf - y - transform.getY();
				
				
				
				
				newPoints[newPoints.length] = new Point(x, y, z);
		
							
			}
			
		    for(var i = 0; i < newPoints.length; i++){
		    	var p = newPoints[i];

		    	var color = "green";
		    	if(i == 3){
		    		color = "red";
		    	}
		    	
				this.canvas.beginPath();
				this.canvas.fillStyle = color;
				this.canvas.arc(p.getX(), p.getY(), 3, 0, 2 * Math.PI, true);
				this.canvas.fill();		
		    	
		    }
		    
		    
		    //draw top box
			this.canvas.moveTo(newPoints[0].getX(), newPoints[0].getY());
			this.canvas.lineTo(newPoints[1].getX(), newPoints[1].getY());
			this.canvas.lineTo(newPoints[2].getX(), newPoints[2].getY());
			this.canvas.lineTo(newPoints[3].getX(), newPoints[3].getY());
			this.canvas.lineTo(newPoints[0].getX(), newPoints[0].getY());
			//draw bottom box
			this.canvas.moveTo(newPoints[4].getX(), newPoints[4].getY());
			this.canvas.lineTo(newPoints[5].getX(), newPoints[5].getY());
			this.canvas.lineTo(newPoints[6].getX(), newPoints[6].getY());
			this.canvas.lineTo(newPoints[7].getX(), newPoints[7].getY());
			this.canvas.lineTo(newPoints[4].getX(), newPoints[4].getY());
			//draw vertical lines
			this.canvas.moveTo(newPoints[0].getX(), newPoints[0].getY());
			this.canvas.lineTo(newPoints[4].getX(), newPoints[4].getY());
			
			this.canvas.moveTo(newPoints[1].getX(), newPoints[1].getY());
			this.canvas.lineTo(newPoints[5].getX(), newPoints[5].getY());
			
			this.canvas.moveTo(newPoints[2].getX(), newPoints[2].getY());
			this.canvas.lineTo(newPoints[6].getX(), newPoints[6].getY());
			
			this.canvas.moveTo(newPoints[3].getX(), newPoints[3].getY());
			this.canvas.lineTo(newPoints[7].getX(), newPoints[7].getY());		
			this.canvas.stroke();
			
		

		};	
		
	}


	function Cube(size) {	
		this.points = new Array();
		this.points[0] = new Point(-size,-size,-size);
		this.points[1] = new Point(size,-size,-size);
		this.points[2] = new Point(size,-size,size);
		this.points[3] = new Point(-size,-size,size);
		this.points[4] = new Point(-size,size,-size);
		this.points[5] = new Point(size,size,-size);
		this.points[6] = new Point(size,size,size);
		this.points[7] = new Point(-size,size,size);
		
		this.getPoints = function() {		
			return this.points;
		};

		this.setPoints = function(points) {
			this.points = points;
		};
		
	}


	function Point(x, y, z) {
		this.x = x;
		this.y = y;
		this.z = z;	

		this.getX = function() {
			return this.x;
		};

		this.setX = function(x) {
			this.x = x;
		};

		this.getY = function() {
			return this.y;
		};

		this.setY = function(y) {
			this.y = y;
		};

		this.getZ = function() {
			return this.z;
		};

		this.setZ = function(z) {
			this.z = z;
		};

	}


`