Skip to content

Commit

Permalink
Merge pull request #75 from goostengine/bresenham-line
Browse files Browse the repository at this point in the history
Implement Bresenham line algorithm in `GoostGeometry2D`
  • Loading branch information
Xrayez authored May 5, 2021
2 parents 69fba98 + 5d7a5f3 commit 741b0a0
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 1 deletion.
40 changes: 40 additions & 0 deletions core/math/2d/geometry/goost_geometry_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,43 @@ Vector<Point2> GoostGeometry2D::circle(real_t p_radius, real_t p_max_error) {

return regular_polygon(vertex_count, p_radius); // vertex count == edge count
}

// Implementation borrowed from `TileMap` editor plugin:
// https://github.com/godotengine/godot/blob/0d819ae5f5/editor/plugins/tile_map_editor_plugin.cpp#L982-L1023
//
Vector<Point2i> GoostGeometry2D::bresenham_line(const Point2i &p_start, const Point2i &p_end) {
Vector<Point2i> points;

real_t dx = ABS(p_end.x - p_start.x);
real_t dy = ABS(p_end.y - p_start.y);

int x = p_start.x;
int y = p_start.y;

int sx = p_start.x > p_end.x ? -1 : 1;
int sy = p_start.y > p_end.y ? -1 : 1;

if (dx > dy) {
real_t err = dx / 2;
for (; x != p_end.x; x += sx) {
points.push_back(Point2i(x, y));
err -= dy;
if (err < 0) {
y += sy;
err += dx;
}
}
} else {
real_t err = dy / 2;
for (; y != p_end.y; y += sy) {
points.push_back(Point2i(x, y));
err -= dx;
if (err < 0) {
x += sx;
err += dy;
}
}
}
points.push_back(Point2i(x, y));
return points;
}
3 changes: 2 additions & 1 deletion core/math/2d/geometry/goost_geometry_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ class GoostGeometry2D {
// Returns 0 if false, +1 if true, -1 if point is exactly on the polygon's boundary.
static int point_in_polygon(const Point2 &p_point, const Vector<Point2> &p_polygon);

/* Polygon/shapes generation methods */
/* Polygon/primitives generation methods */
static Vector<Point2> regular_polygon(int p_edge_count, real_t p_size);
static Vector<Point2> circle(real_t p_radius, real_t p_max_error = 0.25);
static Vector<Point2i> bresenham_line(const Point2i &p_start, const Point2i &p_end);
};

#endif // GOOST_GEOMETRY_2D_H
11 changes: 11 additions & 0 deletions core/math/2d/geometry/goost_geometry_2d_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,16 @@ Vector<Point2> _GoostGeometry2D::circle(real_t p_radius, real_t p_max_error) con
return GoostGeometry2D::circle(p_radius, p_max_error);
}

// Note: this can be converted to use `Vector2i` in Godot 4.x.
Vector<Point2> _GoostGeometry2D::bresenham_line(const Point2 &p_start, const Point2 &p_end) const {
const Vector<Point2i> &line = GoostGeometry2D::bresenham_line(p_start, p_end);
Vector<Point2> ret;
for (int i = 0; i < line.size(); ++i) {
ret.push_back(line[i]);
}
return ret;
}

void _GoostGeometry2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("merge_polygons", "polygon_a", "polygon_b"), &_GoostGeometry2D::merge_polygons);
ClassDB::bind_method(D_METHOD("clip_polygons", "polygon_a", "polygon_b"), &_GoostGeometry2D::clip_polygons);
Expand All @@ -159,6 +169,7 @@ void _GoostGeometry2D::_bind_methods() {

ClassDB::bind_method(D_METHOD("regular_polygon", "sides", "size"), &_GoostGeometry2D::regular_polygon);
ClassDB::bind_method(D_METHOD("circle", "radius", "max_error"), &_GoostGeometry2D::circle, DEFVAL(0.25));
ClassDB::bind_method(D_METHOD("bresenham_line", "start", "end"), &_GoostGeometry2D::bresenham_line);
}

_GoostGeometry2D::_GoostGeometry2D() {
Expand Down
1 change: 1 addition & 0 deletions core/math/2d/geometry/goost_geometry_2d_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class _GoostGeometry2D : public Object {

Vector<Point2> regular_polygon(int p_edge_count, real_t p_size) const;
Vector<Point2> circle(real_t p_radius, real_t p_max_error) const;
Vector<Point2> bresenham_line(const Point2 &p_start, const Point2 &p_end) const;

_GoostGeometry2D();
};
Expand Down
11 changes: 11 additions & 0 deletions doc/GoostGeometry2D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@
Computes the axis-aligned bounding rectangle of given points.
</description>
</method>
<method name="bresenham_line" qualifiers="const">
<return type="PoolVector2Array">
</return>
<argument index="0" name="start" type="Vector2">
</argument>
<argument index="1" name="end" type="Vector2">
</argument>
<description>
Returns an array of 2D-dimensional raster coordinates going through a segment determined by [code]start[/code] and [code]end[/code] points. The line is a close approximation to a straight line between those points.
</description>
</method>
<method name="circle" qualifiers="const">
<return type="PoolVector2Array">
</return>
Expand Down
58 changes: 58 additions & 0 deletions tests/project/goost/core/math/2d/geometry/test_geometry_2d.gd
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,61 @@ func test_regular_polygon():
func test_circle():
solution = GoostGeometry2D.circle(SIZE, 0.25)
assert_eq(solution.size(), 32)


func test_bresenham_line():
var line = GoostGeometry2D.bresenham_line(Vector2(0, 0), Vector2(5, 0))
assert_eq(line.size(), 6)
for x in 6:
assert_eq(line[x], Vector2(x, 0))

line = GoostGeometry2D.bresenham_line(Vector2(0, 0), Vector2(-10, 0))
assert_eq(line.size(), 11)
for x in 11:
assert_eq(line[x], Vector2(-x, 0))

line = GoostGeometry2D.bresenham_line(Vector2(0, 0), Vector2(0, 5))
assert_eq(line.size(), 6)
for x in 6:
assert_eq(line[x], Vector2(0, x))

line = GoostGeometry2D.bresenham_line(Vector2(0, 0), Vector2(0, -10))
assert_eq(line.size(), 11)
for x in 11:
assert_eq(line[x], Vector2(0, -x))

line = GoostGeometry2D.bresenham_line(Vector2(-5, -5), Vector2(5, 5))
assert_eq(line.size(), 11)
assert_eq(line[0], Vector2(-5, -5))
assert_eq(line[5], Vector2(0, 0))
assert_eq(line[10], Vector2(5, 5))

line = GoostGeometry2D.bresenham_line(Vector2(5, -5), Vector2(-5, 5))
assert_eq(line.size(), 11)
assert_eq(line[0], Vector2(5, -5))
assert_eq(line[5], Vector2(0, 0))
assert_eq(line[10], Vector2(-5, 5))

line = GoostGeometry2D.bresenham_line(Vector2(5, -5), Vector2(-5, 5))
assert_eq(line.size(), 11)
assert_eq(line[0], Vector2(5, -5))
assert_eq(line[5], Vector2(0, 0))
assert_eq(line[10], Vector2(-5, 5))

line = GoostGeometry2D.bresenham_line(Vector2(-5, -5), Vector2(10, 3))
assert_eq(line[0], Vector2(-5, -5))
assert_eq(line[1], Vector2(-4, -4))
assert_eq(line[2], Vector2(-3, -4))
assert_eq(line[3], Vector2(-2, -3))
assert_eq(line[4], Vector2(-1, -3))
assert_eq(line[5], Vector2(0, -2))
assert_eq(line[6], Vector2(1, -2))
assert_eq(line[7], Vector2(2, -1))
assert_eq(line[8], Vector2(3, -1))
assert_eq(line[9], Vector2(4, 0))
assert_eq(line[10], Vector2(5, 0))
assert_eq(line[11], Vector2(6, 1))
assert_eq(line[12], Vector2(7, 1))
assert_eq(line[13], Vector2(8, 2))
assert_eq(line[14], Vector2(9, 2))
assert_eq(line[15], Vector2(10, 3))

0 comments on commit 741b0a0

Please sign in to comment.