6

I'm just trying out HTML5 canvas and I'm trying to correctly draw an isometric cube

Here's my current code to draw an isometric cube:

function drawCube(x, y, wx, wy, h, color) {
    // left face
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x - wx, y - wx * 0.5);
    ctx.lineTo(x - wx, y - h - wx * 0.5);
    ctx.lineTo(x, y - h * 1);
    ctx.closePath();
    ctx.fillStyle = "#838357"
    ctx.strokeStyle = "#7a7a51";
    ctx.stroke();
    ctx.fill();

    // right face
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x + wy, y - wy * 0.5);
    ctx.lineTo(x + wy, y - h - wy * 0.5);
    ctx.lineTo(x, y - h * 1);
    ctx.closePath();
    ctx.fillStyle = "#6f6f49";
    ctx.strokeStyle = "#676744";
    ctx.stroke();
    ctx.fill();

    // center face
    ctx.beginPath();
    ctx.moveTo(x, y - h);
    ctx.lineTo(x - wx, y - h - wx * 0.5);
    ctx.lineTo(x - wx + wy, y - h - (wx * 0.5 + wy * 0.5));
    ctx.lineTo(x + wy, y - h - wy * 0.5);
    ctx.closePath();
    ctx.fillStyle = "#989865";
    ctx.strokeStyle = "#8e8e5e";
    ctx.stroke();
    ctx.fill();
}

I have two problems with this:

First issue


There's some pixel issues/faces are overlapping that you can see when you scale the canvas:

Image of incorrect cube

var canvas = document.createElement("canvas");
var ctx    = canvas.getContext('2d');

canvas.width  = 800;
canvas.height = 800;

document.body.appendChild(canvas);

 
function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  
  var sizeX = 32;
  var sizeY = 32;
  var sizeZ = 8; 
   
  ctx.scale(5, 5);
 
  drawCube(50, 50, sizeX, sizeY, sizeZ);
}

requestAnimationFrame(draw);


function drawCube(x, y, wx, wy, h, color) {
    // left face
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x - wx, y - wx * 0.5);
    ctx.lineTo(x - wx, y - h - wx * 0.5);
    ctx.lineTo(x, y - h * 1);
    ctx.closePath();
    ctx.fillStyle = "#838357"
    ctx.strokeStyle = "#7a7a51";
    ctx.stroke();
    ctx.fill();

    // right face
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x + wy, y - wy * 0.5);
    ctx.lineTo(x + wy, y - h - wy * 0.5);
    ctx.lineTo(x, y - h * 1);
    ctx.closePath();
    ctx.fillStyle = "#6f6f49";
    ctx.strokeStyle = "#676744";
    ctx.stroke();
    ctx.fill();

    // center face
    ctx.beginPath();
    ctx.moveTo(x, y - h);
    ctx.lineTo(x - wx, y - h - wx * 0.5);
    ctx.lineTo(x - wx + wy, y - h - (wx * 0.5 + wy * 0.5));
    ctx.lineTo(x + wy, y - h - wy * 0.5);
    ctx.closePath();
    ctx.fillStyle = "#989865";
    ctx.strokeStyle = "#8e8e5e";
    ctx.stroke();
    ctx.fill();
}

Second issue


How can I figure out the required canvas width/height to draw the entire cube, and then set the cube to the beginning of the canvas? (x & y = 0)



What am I doing wrong for the first issue? And what about the second? Could I get an example/snippet with these issues fixed?

Community
  • 1
  • 1
Johnny
  • 63
  • 1
  • 5

1 Answers1

2

It has to do with miter mode for line joins and the miter limit.

You can solve this two ways, both before invoking fill()/stroke():

Either reduce miter limit (note that miter limit cannot be 0):

ctx.miterLimit = 1;

or use a different line-join mode:

ctx.lineJoin = "round";

var canvas = document.createElement("canvas");
var ctx    = canvas.getContext('2d');

canvas.width  = 800;
canvas.height = 800;

document.body.appendChild(canvas);

 
function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  
  var sizeX = 32;
  var sizeY = 32;
  var sizeZ = 8; 
   
  ctx.scale(5, 5);
 
  drawCube(50, 50, sizeX, sizeY, sizeZ);
}

requestAnimationFrame(draw);


function drawCube(x, y, wx, wy, h, color) {

    // LINE MODE
    ctx.lineJoin = "round";
    
    // left face
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x - wx, y - wx * 0.5);
    ctx.lineTo(x - wx, y - h - wx * 0.5);
    ctx.lineTo(x, y - h * 1);
    ctx.closePath();
    ctx.fillStyle = "#838357"
    ctx.strokeStyle = "#7a7a51";
    ctx.stroke();
    ctx.fill();

    // right face
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x + wy, y - wy * 0.5);
    ctx.lineTo(x + wy, y - h - wy * 0.5);
    ctx.lineTo(x, y - h * 1);
    ctx.closePath();
    ctx.fillStyle = "#6f6f49";
    ctx.strokeStyle = "#676744";
    ctx.stroke();
    ctx.fill();

    // center face
    ctx.beginPath();
    ctx.moveTo(x, y - h);
    ctx.lineTo(x - wx, y - h - wx * 0.5);
    ctx.lineTo(x - wx + wy, y - h - (wx * 0.5 + wy * 0.5));
    ctx.lineTo(x + wy, y - h - wy * 0.5);
    ctx.closePath();
    ctx.fillStyle = "#989865";
    ctx.strokeStyle = "#8e8e5e";
    ctx.stroke();
    ctx.fill();
}

For the second issue you could pre-calculate all the values for the cube and store them in an array or object etc.

Then run min-max on all the values for each axis. The max value (minus min) would be the width/height of the canvas. Place the canvas at -minX, -minY (or translate using the same).