diff --git a/Shared/Line.cs b/Shared/Line.cs
new file mode 100644
index 0000000..0a4c44c
--- /dev/null
+++ b/Shared/Line.cs
@@ -0,0 +1,46 @@
+using Microsoft.Xna.Framework;
+using System;
+using System.Collections.Generic;
+
+namespace SemiColinGames {
+ class Line {
+ public static Point[] Rasterize(Point p1, Point p2) {
+ return Line.Rasterize(p1.X, p1.Y, p2.X, p2.Y);
+ }
+
+ // Rasterizes a line using Bresenham's line-drawing algorithm.
+ //
+ // References:
+ // https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
+ // http://members.chello.at/~easyfilter/bresenham.html
+ // http://members.chello.at/~easyfilter/Bresenham.pdf (section 1.6)
+ public static Point[] Rasterize(int x1, int y1, int x2, int y2) {
+ int dx = Math.Abs(x2 - x1);
+ int stepX = x1 < x2 ? 1 : -1;
+ int dy = -Math.Abs(y2 - y1);
+ int stepY = y1 < y2 ? 1 : -1;
+ int error = dx + dy;
+ int errorXY = 0; // Error value e_xy from the PDF.
+ // The size of the output is the size of the longer dimension, plus one.
+ int resultSize = Math.Max(dx, -dy) + 1;
+ var result = new Point[resultSize];
+
+ int i = 0;
+ result[0] = new Point(x1, y1);
+ while (x1 != x2 || y1 != y2) {
+ i++;
+ errorXY = 2 * error;
+ if (errorXY >= dy) { // e_xy + e_x > 0
+ error += dy;
+ x1 += stepX;
+ }
+ if (errorXY <= dx) { // e_xy + e_y < 0
+ error += dx;
+ y1 += stepY;
+ }
+ result[i] = new Point(x1, y1);
+ }
+ return result;
+ }
+ }
+}
diff --git a/Shared/Shared.projitems b/Shared/Shared.projitems
index 12c1794..009b974 100644
--- a/Shared/Shared.projitems
+++ b/Shared/Shared.projitems
@@ -15,6 +15,7 @@
+
diff --git a/SharedTests/LineTests.cs b/SharedTests/LineTests.cs
new file mode 100644
index 0000000..5036aa7
--- /dev/null
+++ b/SharedTests/LineTests.cs
@@ -0,0 +1,269 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Microsoft.Xna.Framework;
+using System;
+using System.Collections.Generic;
+
+namespace SemiColinGames.Tests {
+ [TestClass]
+ public class LineTests {
+ [TestMethod]
+ public void TestRasterizeSinglePoint() {
+ var p1 = new Point(10, 10);
+ var expected = new Point[] { p1 };
+ var result = Line.Rasterize(p1, p1);
+ CollectionAssert.AreEqual(expected, result);
+ }
+
+ [TestMethod]
+ public void TestRasterizeHorizontal() {
+ var p1 = new Point(10, 10);
+ var p2 = new Point(15, 10);
+ var expected = new Point[] {
+ new Point(10, 10),
+ new Point(11, 10),
+ new Point(12, 10),
+ new Point(13, 10),
+ new Point(14, 10),
+ new Point(15, 10)
+ };
+ var result = Line.Rasterize(p1, p2);
+ CollectionAssert.AreEqual(expected, result);
+ }
+
+ [TestMethod]
+ public void TestRasterizeHorizontalReverse() {
+ var p1 = new Point(15, 10);
+ var p2 = new Point(10, 10);
+ var expected = new Point[] {
+ new Point(15, 10),
+ new Point(14, 10),
+ new Point(13, 10),
+ new Point(12, 10),
+ new Point(11, 10),
+ new Point(10, 10)
+ };
+ var result = Line.Rasterize(p1, p2);
+ CollectionAssert.AreEqual(expected, result);
+ }
+
+ [TestMethod]
+ public void TestRasterizeVertical() {
+ var p1 = new Point(10, 10);
+ var p2 = new Point(10, 15);
+ var expected = new Point[] {
+ new Point(10, 10),
+ new Point(10, 11),
+ new Point(10, 12),
+ new Point(10, 13),
+ new Point(10, 14),
+ new Point(10, 15)
+ };
+ var result = Line.Rasterize(p1, p2);
+ CollectionAssert.AreEqual(expected, result);
+ }
+
+ [TestMethod]
+ public void TestRasterizeVerticalReverse() {
+ var p1 = new Point(10, 15);
+ var p2 = new Point(10, 10);
+ var expected = new Point[] {
+ new Point(10, 15),
+ new Point(10, 14),
+ new Point(10, 13),
+ new Point(10, 12),
+ new Point(10, 11),
+ new Point(10, 10)
+ };
+ var result = Line.Rasterize(p1, p2);
+ CollectionAssert.AreEqual(expected, result);
+ }
+
+ [TestMethod]
+ public void TestRasterizeDiagonalPosPos() {
+ var p1 = new Point(0, 0);
+ var p2 = new Point(5, 5);
+ var expected = new Point[] {
+ new Point(0, 0),
+ new Point(1, 1),
+ new Point(2, 2),
+ new Point(3, 3),
+ new Point(4, 4),
+ new Point(5, 5)
+ };
+ var result = Line.Rasterize(p1, p2);
+ CollectionAssert.AreEqual(expected, result);
+ }
+
+ [TestMethod]
+ public void TestRasterizeDiagonalPosNeg() {
+ var p1 = new Point(0, 5);
+ var p2 = new Point(5, 0);
+ var expected = new Point[] {
+ new Point(0, 5),
+ new Point(1, 4),
+ new Point(2, 3),
+ new Point(3, 2),
+ new Point(4, 1),
+ new Point(5, 0)
+ };
+ var result = Line.Rasterize(p1, p2);
+ CollectionAssert.AreEqual(expected, result);
+ }
+
+ [TestMethod]
+ public void TestRasterizeDiagonalNegPos() {
+ var p1 = new Point(5, 0);
+ var p2 = new Point(0, 5);
+ var expected = new Point[] {
+ new Point(5, 0),
+ new Point(4, 1),
+ new Point(3, 2),
+ new Point(2, 3),
+ new Point(1, 4),
+ new Point(0, 5)
+ };
+ var result = Line.Rasterize(p1, p2);
+ CollectionAssert.AreEqual(expected, result);
+ }
+
+ [TestMethod]
+ public void TestRasterizeDiagonalNegNeg() {
+ var p1 = new Point(5, 5);
+ var p2 = new Point(0, 0);
+ var expected = new Point[] {
+ new Point(5, 5),
+ new Point(4, 4),
+ new Point(3, 3),
+ new Point(2, 2),
+ new Point(1, 1),
+ new Point(0, 0)
+ };
+ var result = Line.Rasterize(p1, p2);
+ CollectionAssert.AreEqual(expected, result);
+ }
+
+ [TestMethod]
+ public void TestRasterizeDoubleSlope() {
+ var p1 = new Point(0, 0);
+ var p2 = new Point(4, 9);
+ var expected = new Point[] {
+ new Point(0, 0),
+ new Point(0, 1),
+ new Point(1, 2),
+ new Point(1, 3),
+ new Point(2, 4),
+ new Point(2, 5),
+ new Point(3, 6),
+ new Point(3, 7),
+ new Point(4, 8),
+ new Point(4, 9)
+ };
+ var result = Line.Rasterize(p1, p2);
+ CollectionAssert.AreEqual(expected, result);
+ }
+
+ [TestMethod]
+ public void TestRasterizeDoubleSlopePlusOne() {
+ var p1 = new Point(0, 0);
+ var p2 = new Point(5, 10);
+ var expected = new Point[] {
+ new Point(0, 0),
+ new Point(1, 1),
+ new Point(1, 2),
+ new Point(2, 3),
+ new Point(2, 4),
+ new Point(3, 5),
+ new Point(3, 6),
+ new Point(4, 7),
+ new Point(4, 8),
+ new Point(5, 9),
+ new Point(5, 10)
+ };
+ var result = Line.Rasterize(p1, p2);
+ CollectionAssert.AreEqual(expected, result);
+ }
+
+ [TestMethod]
+ public void TestRasterizeDoubleSlopePlusOneReverse() {
+ var p1 = new Point(5, 10);
+ var p2 = new Point(0, 0);
+ var expected = new Point[] {
+ new Point(5, 10),
+ new Point(4, 9),
+ new Point(4, 8),
+ new Point(3, 7),
+ new Point(3, 6),
+ new Point(2, 5),
+ new Point(2, 4),
+ new Point(1, 3),
+ new Point(1, 2),
+ new Point(0, 1),
+ new Point(0, 0)
+ };
+ var result = Line.Rasterize(p1, p2);
+ CollectionAssert.AreEqual(expected, result);
+ }
+
+ [TestMethod]
+ public void TestRasterizeHalfSlope() {
+ var p1 = new Point(0, 0);
+ var p2 = new Point(9, 4);
+ var expected = new Point[] {
+ new Point(0, 0),
+ new Point(1, 0),
+ new Point(2, 1),
+ new Point(3, 1),
+ new Point(4, 2),
+ new Point(5, 2),
+ new Point(6, 3),
+ new Point(7, 3),
+ new Point(8, 4),
+ new Point(9, 4)
+ };
+ var result = Line.Rasterize(p1, p2);
+ CollectionAssert.AreEqual(expected, result);
+ }
+
+ [TestMethod]
+ public void TestRasterizeHalfSlopePlusOne() {
+ var p1 = new Point(0, 0);
+ var p2 = new Point(10, 5);
+ var expected = new Point[] {
+ new Point(0, 0),
+ new Point(1, 1),
+ new Point(2, 1),
+ new Point(3, 2),
+ new Point(4, 2),
+ new Point(5, 3),
+ new Point(6, 3),
+ new Point(7, 4),
+ new Point(8, 4),
+ new Point(9, 5),
+ new Point(10, 5)
+ };
+ var result = Line.Rasterize(p1, p2);
+ CollectionAssert.AreEqual(expected, result);
+ }
+
+ [TestMethod]
+ public void TestRasterizeHalfSlopePlusOneReverse() {
+ var p1 = new Point(10, 5);
+ var p2 = new Point(0, 0);
+ var expected = new Point[] {
+ new Point(10, 5),
+ new Point(9, 4),
+ new Point(8, 4),
+ new Point(7, 3),
+ new Point(6, 3),
+ new Point(5, 2),
+ new Point(4, 2),
+ new Point(3, 1),
+ new Point(2, 1),
+ new Point(1, 0),
+ new Point(0, 0)
+ };
+ var result = Line.Rasterize(p1, p2);
+ CollectionAssert.AreEqual(expected, result);
+ }
+ }
+}
diff --git a/SharedTests/SharedTests.csproj b/SharedTests/SharedTests.csproj
index 741829e..c60ff41 100644
--- a/SharedTests/SharedTests.csproj
+++ b/SharedTests/SharedTests.csproj
@@ -8,7 +8,7 @@
{C86694A5-DD99-4421-AA2C-1230F11C10F8}
Library
Properties
- SharedTests
+ SemiColinGames.Tests
SharedTests
v4.7.2
512
@@ -53,6 +53,7 @@
+