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