Browse Source
Implement Bresenham's algorithm for line rasterization.
Implement Bresenham's algorithm for line rasterization.
This is implemented as a static Line.Rasterize(Point p1, Point p2) function
that returns an array of Points. There's also a Line.Rasterize(x1, y1, x1, y2)
version for convenience.
Unit tests included.
GitOrigin-RevId: 525098f8c7
master
Colin McMillen
4 years ago
4 changed files with 318 additions and 1 deletions
-
46Shared/Line.cs
-
1Shared/Shared.projitems
-
269SharedTests/LineTests.cs
-
3SharedTests/SharedTests.csproj
@ -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; |
|||
} |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue