Skip to content

Instantly share code, notes, and snippets.

@cjhin
Last active August 7, 2016 21:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cjhin/afbed2cf6df0068676d2205b76df0e66 to your computer and use it in GitHub Desktop.
Save cjhin/afbed2cf6df0068676d2205b76df0e66 to your computer and use it in GitHub Desktop.
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment