<canvas id="canvas" width="960" height="500"></canvas>
Basically playing around with a couple of graphics things with the canvas:
- but mostly faking them :P, they're all missing backs and bottoms
- manually translating a 3d space to a 2d space, using classical perspective stuff
- auto generating cubes, creating a "city" with blocks and streets and things
var color_right = "#888";
var color_front = "#AAA";
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext("2d");
// *0.8 : so that the buildings don't fall off the bottom of the canvas
var canvasHeight = canvas.height * 0.9;
var canvasWidth = canvas.width;
// x and z are the physical coordinates of the terminusPoint
// adjusting y effectively adjusts the "height/angle" of the viewer
y: 190 /* this is of the visual canvas, not the physical point in space */
// z = 0 is "close", z = 100 is "far"
// width is x size, depth is z size
// basically calculate the front and the back sides, then connect all the dotes
// assume that the x/z coordinates define the bottomLeftFront corner
var drawBuilding = function(x, z, width, depth, height) {
ctx.strokeStyle = "black";
var zModFront = (terminusPoint.z - z)/terminusPoint.z;
var pixelHeightFront = zModFront * height;
var pixelWidthFront = zModFront * width;
x: terminusPoint.x + (zModFront * (x - terminusPoint.x)),
y: terminusPoint.y + (zModFront * (canvasHeight - terminusPoint.y))
y: bottomLeftFront.y - pixelHeightFront
x: topLeftFront.x + pixelWidthFront,
var zModBack = (terminusPoint.z - z - depth)/terminusPoint.z;
var pixelHeightBack = zModBack * height;
var pixelWidthBack = zModBack * width;
x: terminusPoint.x + (zModBack * (x - terminusPoint.x)),
y: terminusPoint.y + (zModBack * (canvasHeight - terminusPoint.y))
y: bottomLeftBack.y - pixelHeightBack
x: topLeftBack.x + pixelWidthBack,
drawPolygon([topRightFront, topRightBack, bottomRightBack, bottomRightFront], color_right);
drawPolygon([topLeftFront, topLeftBack, bottomLeftBack, bottomLeftFront], color_left);
drawPolygon([topLeftFront, topLeftBack, topRightBack, topRightFront], color_top);
drawPolygon([topLeftFront, topRightFront, bottomRightFront, bottomLeftFront], color_front);
// points must be in consecutive order of drawing
var drawPolygon = function(points, fill_color) {
ctx.moveTo(points[0].x, points[0].y);
for(var i=1; i<points.length; i++) {
ctx.lineTo(points[i].x, points[i].y);
ctx.lineTo(points[0].x, points[0].y);
ctx.fillStyle = fill_color;
// return an array of buildings
var generateCity = function() {
var buildingSize = plotSize - streetSize;
for(var j=0; j < parseInt((terminusPoint.z - plotSize)/plotSize); j++) {
for(var i = 0; i<parseInt(canvasWidth/plotSize); i++) {
var randHeight = Math.random()*90 + 10;
if(Math.random() > 0.95) { randHeight += 70; }
if(Math.random() > 0.97) { randHeight += 170; }
// to make buildings go into blocks of 2x2
if(i % 2 == 0) { xBlockModifier += streetSize; }
if(j % 2 == 0) { yBlockModifier = streetSize; }
x: i*plotSize + xBlockModifier, z: j*plotSize + yBlockModifier, w: buildingSize, d: buildingSize, h: randHeight
var buildings = generateCity();
var sortBuildings = function(arrayToSort) {
for(var i=0; i < arrayToSort.length; i++) {
var currItem = arrayToSort[i];
var destIdx = arrayToReturn.length;
for(var j=0; j < arrayToReturn.length; j++) {
var compItem = arrayToReturn[j];
if((currItem.z > compItem.z) ||
(Math.abs(currItem.x - canvasWidth / 2.0) > Math.abs(compItem.x - canvasWidth / 2.0) && currItem.z == compItem.z)) {
arrayToReturn = arrayToReturn.slice(0,destIdx).concat([currItem]).concat(arrayToReturn.slice(destIdx,arrayToReturn.length))
var sorted_buildings = sortBuildings(buildings);
ctx.strokeStyle = "grey";
drawPolygon([bottomSceneLeft, topSceneLeft, topSceneRight, bottomSceneRight], color_road);
for(var i=0; i<sorted_buildings.length; i++) {
var b = sorted_buildings[i];
drawBuilding(b.x, b.z, b.w, b.d, b.h);