-
-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixed #671: Bresenham's line algorithm port
- Loading branch information
1 parent
6726ebc
commit 16ae16c
Showing
2 changed files
with
145 additions
and
0 deletions.
There are no files selected for viewing
45 changes: 45 additions & 0 deletions
45
indigo/indigo-extras/src/main/scala/indigoextras/utils/Bresenham.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
100 changes: 100 additions & 0 deletions
100
indigo/indigo-extras/src/test/scala/indigoextras/utils/BresenhamTests.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |