geom/segment: add bresenham iterator
This commit is contained in:
parent
f6cec803d7
commit
382e093f57
@ -83,6 +83,97 @@ namespace cruft::geom {
|
||||
}
|
||||
|
||||
|
||||
/// An implementation of Bresenham's line algorithm.
|
||||
/// Ref: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
|
||||
///
|
||||
/// This operates as a container that iterates over the integral points
|
||||
/// on the line. Use in range-for is explicitly supported.
|
||||
///
|
||||
/// The start and end segment points will both be generated.
|
||||
class bresenham {
|
||||
public:
|
||||
bresenham (cruft::geom::segment<2,int> _src)
|
||||
: m_src (_src)
|
||||
{ ; }
|
||||
|
||||
class end_iterator { };
|
||||
end_iterator end (void) { return {}; }
|
||||
|
||||
class begin_iterator {
|
||||
public:
|
||||
begin_iterator (cruft::geom::segment<2,int> _src)
|
||||
: m_start (_src.a)
|
||||
, m_diff (_src.b - _src.a)
|
||||
{
|
||||
// Find the axis directions and finalise the diff
|
||||
auto const sign = select (m_diff > 0, 1, -1);
|
||||
m_diff = abs (m_diff);
|
||||
|
||||
// Setup the increment for each possible major axis.
|
||||
if (m_diff.x > m_diff.y) {
|
||||
m_dirx = { sign.x, 0 };
|
||||
m_diry = { 0, sign.y };
|
||||
} else {
|
||||
std::swap (m_diff.x, m_diff.y);
|
||||
|
||||
m_dirx = { 0, sign.y };
|
||||
m_diry = { sign.x, 0 };
|
||||
}
|
||||
|
||||
// Set the initial offsets
|
||||
D = 2 * m_diff.y - m_diff.x;
|
||||
x = y = 0;
|
||||
}
|
||||
|
||||
begin_iterator& operator++ () &
|
||||
{
|
||||
// We've gone far enough. Drop down a line.
|
||||
if (D >= 0) {
|
||||
y += 1;
|
||||
D -= 2 * m_diff.x;
|
||||
}
|
||||
|
||||
// Step forwards.
|
||||
D += 2 * m_diff.y;
|
||||
++x;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator== (end_iterator const&) const noexcept
|
||||
{
|
||||
return x >= m_diff.x + 1;
|
||||
}
|
||||
|
||||
bool operator!= (end_iterator const &rhs) const noexcept
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
cruft::point2i operator* () const noexcept
|
||||
{
|
||||
return m_start + x * m_dirx + y * m_diry;
|
||||
}
|
||||
|
||||
private:
|
||||
cruft::point2i m_start; /// The initial point of the segment.
|
||||
cruft::vector2i m_diff; /// Magnitude of total traversal for each axis.
|
||||
cruft::vector2i m_dirx; /// Direction of increment for the X axis.
|
||||
cruft::vector2i m_diry; /// Direction of increment for the Y axis.
|
||||
|
||||
int D; /// Stepping accumulator to detect line changes .
|
||||
int x; /// Current X parameter.
|
||||
int y; /// Current Y parameter.
|
||||
};
|
||||
|
||||
begin_iterator begin (void) const { return { m_src }; }
|
||||
|
||||
|
||||
private:
|
||||
cruft::geom::segment<2,int> m_src;
|
||||
};
|
||||
|
||||
|
||||
using segment2i = segment<2,int>;
|
||||
using segment3i = segment<3,int>;
|
||||
|
||||
|
@ -69,6 +69,50 @@ void test_region_intersection (cruft::TAP::logger &tap)
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void test_bresenham (cruft::TAP::logger &tap)
|
||||
{
|
||||
static struct {
|
||||
cruft::point2i a;
|
||||
cruft::point2i b;
|
||||
std::vector<cruft::point2i> expected;
|
||||
char const *message;
|
||||
} const TESTS[] = {
|
||||
{
|
||||
.a = { 0 },
|
||||
.b = { 3 },
|
||||
.expected = { { 0 }, { 1 }, { 2 }, { 3 } },
|
||||
.message = "positive unit direction"
|
||||
},
|
||||
{
|
||||
.a = { 1, 2 },
|
||||
.b = { 1, 4 },
|
||||
.expected = { { 1, 2 }, { 1, 3 }, { 1, 4 } },
|
||||
.message = "vertical only"
|
||||
},
|
||||
{
|
||||
.a = { -2, 1 },
|
||||
.b = { 1, -1 },
|
||||
.expected = { { -2, 1 }, { -1, 0 }, { 0, 0 }, { 1, -1 } },
|
||||
.message = "cross origin"
|
||||
},
|
||||
{
|
||||
.a = { 1, 1 },
|
||||
.b = { 1, 1 },
|
||||
.expected = { { 1, 1 } },
|
||||
.message = "zero length segment"
|
||||
},
|
||||
};
|
||||
|
||||
for (auto const &t: TESTS) {
|
||||
std::vector<cruft::point2i> computed;
|
||||
for (auto const &p: cruft::geom::bresenham ({ t.a, t.b }))
|
||||
computed.push_back (p);
|
||||
tap.expect_eq (computed, t.expected, "bresenham: %!", t.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
int
|
||||
main (int, char**)
|
||||
@ -76,5 +120,6 @@ main (int, char**)
|
||||
cruft::TAP::logger tap;
|
||||
test_point_distance (tap);
|
||||
test_region_intersection (tap);
|
||||
test_bresenham (tap);
|
||||
return tap.status ();
|
||||
}
|
Loading…
Reference in New Issue
Block a user