D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
cjhin
Full window
Github gist
city
<!DOCTYPE html> <meta charset="utf-8"> <body> <canvas id="canvas" width="960" height="500"></canvas> </body> <script> /* Basically playing around with a couple of graphics things with the canvas: - manually drawing cubes - 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_left = "#888"; var color_front = "#AAA"; var color_top = "#CCC"; var color_road = "#666"; 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 var terminusPoint = { x: canvasWidth / 2.0, z: 800, y: 190 /* this is of the visual canvas, not the physical point in space */ }; // translate to 3d // 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; var bottomLeftFront = { x: terminusPoint.x + (zModFront * (x - terminusPoint.x)), y: terminusPoint.y + (zModFront * (canvasHeight - terminusPoint.y)) }; var topLeftFront = { x: bottomLeftFront.x, y: bottomLeftFront.y - pixelHeightFront }; var topRightFront = { x: topLeftFront.x + pixelWidthFront, y: topLeftFront.y }; var bottomRightFront = { x: topRightFront.x, y: bottomLeftFront.y }; var zModBack = (terminusPoint.z - z - depth)/terminusPoint.z; var pixelHeightBack = zModBack * height; var pixelWidthBack = zModBack * width; var bottomLeftBack = { x: terminusPoint.x + (zModBack * (x - terminusPoint.x)), y: terminusPoint.y + (zModBack * (canvasHeight - terminusPoint.y)) }; var topLeftBack = { x: bottomLeftBack.x, y: bottomLeftBack.y - pixelHeightBack }; var topRightBack = { x: topLeftBack.x + pixelWidthBack, y: topLeftBack.y }; var bottomRightBack = { x: topRightBack.x, y: bottomLeftBack.y }; // right side drawPolygon([topRightFront, topRightBack, bottomRightBack, bottomRightFront], color_right); // left side drawPolygon([topLeftFront, topLeftBack, bottomLeftBack, bottomLeftFront], color_left); // "top of building" drawPolygon([topLeftFront, topLeftBack, topRightBack, topRightFront], color_top); // "front of building" drawPolygon([topLeftFront, topRightFront, bottomRightFront, bottomLeftFront], color_front); }; // points must be in consecutive order of drawing var drawPolygon = function(points, fill_color) { ctx.beginPath(); ctx.moveTo(points[0].x, points[0].y); for(var i=1; i<points.length; i++) { ctx.lineTo(points[i].x, points[i].y); } // close the shape ctx.lineTo(points[0].x, points[0].y); ctx.closePath(); ctx.stroke(); ctx.fillStyle = fill_color; ctx.fill(); } // return an array of buildings var generateCity = function() { var cityArray = []; var plotSize = 60; var streetSize = 25; 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 var xBlockModifier = 0; var yBlockModifier = 0; if(i % 2 == 0) { xBlockModifier += streetSize; } if(j % 2 == 0) { yBlockModifier = streetSize; } cityArray.push({ x: i*plotSize + xBlockModifier, z: j*plotSize + yBlockModifier, w: buildingSize, d: buildingSize, h: randHeight }); } } return cityArray; }; var buildings = generateCity(); var sortBuildings = function(arrayToSort) { var arrayToReturn = []; 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)) { destIdx = j; break; } } arrayToReturn = arrayToReturn.slice(0,destIdx).concat([currItem]).concat(arrayToReturn.slice(destIdx,arrayToReturn.length)) } return arrayToReturn; }; var sorted_buildings = sortBuildings(buildings); // first draw the roads ctx.strokeStyle = "grey"; var bottomSceneLeft = { x: 38, y: canvasHeight - 9 }; var topSceneLeft = { x: terminusPoint.x - 50, y: terminusPoint.y + 30 }; var topSceneRight = { x: terminusPoint.x + 50, y: terminusPoint.y + 30 }; var bottomSceneRight = { x: canvasWidth - 38, y: canvasHeight - 9 }; 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); } </script>