ran4erep
ran4erep

Reputation: 1

How to use Bresenham's line algorithm?

I'm making a game and I bumped into a strange problem. So, I've found a JS code for drawing a line via Bresenham's algorithm, without plot() function (maybe I write it wrong) and all this stuff works weird:

  1. Start x,y and end x,y for line drawing is reversed for some reason
  2. Line not drawing in some cases, like in the code below, where I called the function castRay(8,0,4,4)
  3. Line not drawing except only one dot in God know where position

What am I doing wrong?

let ctx = canvas.getContext("2d");
const TILE = 30;
let map = [
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
  [0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
ctx.fillStyle = "rgba(0,50,200)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < 10; i++) {
  for (let j = 0; j < 10; j++) {
    if (map[i][j] === 1) {
      ctx.fillStyle = "black";
      ctx.fillRect(j * TILE, i * TILE, TILE - 1, TILE - 1)
    }
    if (map[i][j] === 0) {
      ctx.fillStyle = "gray";
      ctx.fillRect(j * TILE, i * TILE, TILE - 1, TILE - 1)
    }
  }
}
let plot = (x, y) => {
  ctx.fillRect(x * TILE, y * TILE, TILE, TILE);
  return true;
}

let castRay = (x0, y0, x1, y1) => {
  let tmp;
  let steep = Math.abs(y1 - y0) > Math.abs(x1 - x0);
  if (steep) {
    tmp = x0;
    x0 = y0;
    y0 = tmp;
    tmp = x1;
    x1 = y1;
    y1 = tmp;
  }

  let sign = 1;
  if (x0 > x1) {
    sign = -1;
    x0 *= -1;
    x1 *= -1;
  }

  let dx = x1 - x0;
  let dy = Math.abs(y1 - y0);
  let err = ((dx / 2));
  let ystep = y0 < y1 ? 1 : -1;
  let y = y0;

  for (let x = 0; x <= x1; x++) {
    if (!(steep ? plot(y, sign * x) : plot(sign * x, y))) return;
    err = (err - dy);
    if (err < 0) {
      y += ystep;
      err += dx;
    }
  }
}

ctx.fillStyle = "green";
castRay(0, 0, 4, 4);
ctx.fillStyle = "rgb(0,0,255)";
castRay(8, 0, 4, 4)
<canvas width="300" height="300" id="canvas"></canvas>

Upvotes: 0

Views: 572

Answers (1)

obscure
obscure

Reputation: 12891

I would get rid of the sign variable and instead just swap the appropriate variables inside the if (x0 > x1) condition. Furthermore your line drawing always starts at x=0 because you didn't set it to x0.

for (let x = 0; x <= x1; x++)

should be

for (let x = x0; x <= x1; x++)

Here's the updated example code:

let ctx = canvas.getContext("2d");
const TILE = 30;
let map = [
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
  [0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
ctx.fillStyle = "rgba(0,50,200)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < 10; i++) {
  for (let j = 0; j < 10; j++) {
    if (map[i][j] === 1) {
      ctx.fillStyle = "black";
      ctx.fillRect(j * TILE, i * TILE, TILE - 1, TILE - 1)
    }
    if (map[i][j] === 0) {
      ctx.fillStyle = "gray";
      ctx.fillRect(j * TILE, i * TILE, TILE - 1, TILE - 1)
    }
  }
}
let plot = (x, y) => {
  ctx.fillRect(x * TILE, y * TILE, TILE, TILE);
  return true;
}

let castRay = (x0, y0, x1, y1) => {
  let tmp = x0;
  let steep = Math.abs(y1 - y0) > Math.abs(x1 - x0);
  if (steep) {
    x0 = y0;
    y0 = tmp;
    tmp = x1;
    x1 = y1;
    y1 = tmp;
  }

  if (x0 > x1) {
    x0 = x1;
    x1 = tmp;
    tmp = y0;
    y0 = y1;
    y1 = tmp;
  }

  let dx = x1 - x0;
  let dy = Math.abs(y1 - y0);
  let err = ((dx / 2));
  let ystep = y0 < y1 ? 1 : -1;
  let y = y0;

  for (let x = x0; x <= x1; x++) {
    if (!(steep ? plot(y, x) : plot(x, y))) return;
    err = (err - dy);
    if (err < 0) {
      y += ystep;
      err += dx;
    }
  }
}

ctx.fillStyle = "green";
castRay(0, 0, 4, 4);
ctx.fillStyle = "rgb(0,0,255)";
castRay(8, 0, 4, 4)
<canvas width="300" height="300" id="canvas">

Upvotes: 1

Related Questions