Skip to content

Commit

Permalink
Fixed #671: Bresenham's line algorithm port
Browse files Browse the repository at this point in the history
  • Loading branch information
davesmith00000 committed Jan 2, 2024
1 parent 6726ebc commit 16ae16c
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package indigoextras.utils

import indigo.shared.collections.Batch
import indigo.shared.datatypes.Point

import scala.annotation.tailrec

/** An implementation of the Bresenham Line algorithm, which plots clean lines across grids. Used by drawing tools and
* in this case, for lines of sight.
*/
object Bresenham:

/** Draw a line across a grid, such as you might see in a pixel art graphics tool. */
@SuppressWarnings(Array("scalafix:DisableSyntax.var"))
def line(from: Point, to: Point): Batch[Point] =
val x0: Int = from.x
val y0: Int = from.y
val x1: Int = to.x
val y1: Int = to.y
val dx = Math.abs(x1 - x0)
val dy = Math.abs(y1 - y0)
val sx = if x0 < x1 then 1 else -1
val sy = if y0 < y1 then 1 else -1

@tailrec
def rec(x: Int, y: Int, err: Int, acc: Batch[Point]): Batch[Point] =
val next = Point(x, y)
if x == x1 && y == y1 then next :: acc
else if next.distanceTo(to) <= 1 then to :: next :: acc
else
var e = err
var x2 = x
var y2 = y

if err > -dx then
e -= dy
x2 += sx

if e < dy then
e += dx
y2 += sy

rec(x2, y2, e, next :: acc)

rec(x0, y0, (if dx > dy then dx else -dy) / 2, Batch.empty)
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package indigoextras.utils

import indigo.Point

class BresenhamTests extends munit.FunSuite:

test("bresenham's line - 1x1") {
val actual =
Bresenham.line(Point(0, 0), Point(1, 1))

val expected =
List(Point(1, 1), Point(0, 0))

assertEquals(actual.toList, expected)
}

test("bresenham's line - vertical") {
val actual =
Bresenham.line(Point(10, 13), Point(10, 21))

val expected =
List(
Point(10, 21),
Point(10, 20),
Point(10, 19),
Point(10, 18),
Point(10, 17),
Point(10, 16),
Point(10, 15),
Point(10, 14),
Point(10, 13)
)

assertEquals(actual.toList, expected)
}

test("bresenham's line - horizontal") {
val actual =
Bresenham.line(Point(7, 13), Point(12, 13))

val expected =
List(
Point(12, 13),
Point(11, 13),
Point(10, 13),
Point(9, 13),
Point(8, 13),
Point(7, 13)
)

assertEquals(actual.toList, expected)
}

test("bresenham's line - diagonal") {
val actual =
Bresenham.line(Point(0, 1), Point(6, 4))

val expected =
List(
Point(6, 4),
Point(5, 4),
Point(4, 3),
Point(3, 3),
Point(2, 2),
Point(1, 2),
Point(0, 1)
)

assertEquals(actual.toList, expected)
}

test("bresenham's line - same") {
val actual =
Bresenham.line(Point(0, 1), Point(0, 1))

val expected =
List(
Point(0, 1)
)

assertEquals(actual.toList, expected)
}

test("bresenham's line - tricky - misses target") {
val actual =
Bresenham.line(Point(8, 3), Point(5, 5))

val expected =
List(
Point(8, 3),
Point(7, 4),
Point(6, 5),
Point(
5,
5
) // The line would in fact go past this, but the destination is artificially added.
).reverse

assertEquals(actual.toList, expected)
}

0 comments on commit 16ae16c

Please sign in to comment.