bezier: add intersection count test
This commit is contained in:
parent
54cb3c2153
commit
cf5a682959
104
bezier.cpp
104
bezier.cpp
@ -138,6 +138,110 @@ namespace util {
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace util {
|
||||
template <>
|
||||
std::array<util::vector2f,4>
|
||||
bezier<3>::coeffs (void) const
|
||||
{
|
||||
auto &v = reinterpret_cast<const util::vector2f(&)[4]> (m_points);
|
||||
|
||||
return {
|
||||
-1 * v[0] +3 * v[1] -3 * v[2] +1 * v[3],
|
||||
3 * v[0] -6 * v[1] +3 * v[2],
|
||||
-3 * v[0] +3 * v[1],
|
||||
1 * v[0]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace util {
|
||||
template <>
|
||||
std::array<util::vector2f,3>
|
||||
bezier<2>::coeffs (void) const
|
||||
{
|
||||
auto &v = reinterpret_cast<const util::vector2f(&)[3]> (m_points);
|
||||
|
||||
return {
|
||||
+1 * v[2] -2 * v[1] + 1 * v[0],
|
||||
-2 * v[2] +2 * v[1],
|
||||
+1 * v[2]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace util {
|
||||
template <>
|
||||
std::array<util::vector2f,2>
|
||||
bezier<1>::coeffs (void) const
|
||||
{
|
||||
auto &v = reinterpret_cast<const util::vector2f(&)[2]> (m_points);
|
||||
|
||||
return {
|
||||
-1 * v[1] + 1 * v[0],
|
||||
+1 * v[1],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// XXX: If the line is co-linear we'll have no solutions. But we return 1
|
||||
// anyway as this function is used to find any point that intersects as part
|
||||
// of other more comprehensive tests.
|
||||
template <size_t S>
|
||||
size_t
|
||||
util::bezier<S>::intersections (point2f p0, point2f p1) const
|
||||
{
|
||||
float A = p1.y - p0.y; // A = y2 - y1
|
||||
float B = p0.x - p1.x; // B = x1 - x2
|
||||
float C = p0.x * (p0.y - p1.y) + // C = x1 (y1 - y2) + y1 (x2 - x1)
|
||||
p0.y * (p1.x - p0.x);
|
||||
|
||||
// Build the intersection polynomial
|
||||
const std::array<vector2f,S+1> bcoeff = coeffs ();
|
||||
|
||||
std::array<float,S+1> pcoeff;
|
||||
for (size_t i = 0; i < pcoeff.size (); ++i)
|
||||
pcoeff[i] = A * bcoeff[i].x + B * bcoeff[i].y;
|
||||
pcoeff.back () += C;
|
||||
|
||||
const auto r = polynomial::solve<S> (pcoeff);
|
||||
|
||||
// The curve and line are colinear
|
||||
if (std::all_of (r.begin (), r.end (), [] (auto i) { return std::isnan (i); }))
|
||||
return 1;
|
||||
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < S; ++i) {
|
||||
// Ensure the solutions are on the curve
|
||||
const auto t = r[i];
|
||||
if (std::isnan (t))
|
||||
break;
|
||||
|
||||
if (t < 0.f || t > 1.f)
|
||||
continue;
|
||||
|
||||
// Find the line's intersection point
|
||||
const util::vector2f q = polynomial::eval (bcoeff, t);
|
||||
|
||||
const auto s = almost_equal (p0.x, p1.x) ?
|
||||
(q.y-p0.y) / (p1.y-p0.y) :
|
||||
(q.x-p0.x) / (p1.x-p0.x) ; // vertical
|
||||
|
||||
// Check if the point is on the line
|
||||
if (s >= 0.f && s <= 1.f)
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace util {
|
||||
// TODO: use a more reliable method like [Xiao-Dia Chen 2010]
|
||||
|
@ -31,6 +31,13 @@ namespace util {
|
||||
bezier (const util::point2f (&)[S+1]);
|
||||
|
||||
point2f eval (float t) const;
|
||||
|
||||
// Calculate the expanded polynomial coeffecients in terms of t
|
||||
std::array<vector2f,S+1>
|
||||
coeffs (void) const;
|
||||
|
||||
size_t intersections (point2f from, point2f to) const;
|
||||
|
||||
float distance (point2f) const;
|
||||
|
||||
point2f& operator[] (size_t idx);
|
||||
|
137
test/bezier.cpp
137
test/bezier.cpp
@ -4,11 +4,17 @@
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
int
|
||||
main (int, char**)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <size_t> void test_eval (void);
|
||||
template <size_t> void test_intersect (void);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <>
|
||||
void
|
||||
test_eval<1> (void)
|
||||
{
|
||||
// Check degree-1 beziers
|
||||
{
|
||||
static const util::bezier<1> b1 ({{ 0.f, 0.f},
|
||||
{100.f, 100.f}});
|
||||
|
||||
@ -21,10 +27,14 @@ main (int, char**)
|
||||
auto px = b1.eval(0.5f);
|
||||
auto rx = b1[0] + 0.5f * (b1[1]-b1[0]);
|
||||
CHECK_EQ (px, rx);
|
||||
}
|
||||
}
|
||||
|
||||
// Check degree-2 beziers
|
||||
{
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <>
|
||||
void
|
||||
test_eval<2> (void)
|
||||
{
|
||||
static const util::bezier<2> b2 ({{ 0.f, 0.f},
|
||||
{ 50.f, 50.f},
|
||||
{100.f, 100.f}});
|
||||
@ -38,10 +48,14 @@ main (int, char**)
|
||||
auto px = b2.eval(0.5f);
|
||||
auto rx = b2[0] + 0.5f * (b2[2]-b2[0]);
|
||||
CHECK_EQ (px, rx);
|
||||
}
|
||||
}
|
||||
|
||||
// Check degree-3 beziers
|
||||
{
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <>
|
||||
void
|
||||
test_eval<3> (void)
|
||||
{
|
||||
static const util::bezier<3> b3 ({{ 0.f, 0.f },
|
||||
{ 33.f, 33.f },
|
||||
{ 67.f, 67.f },
|
||||
@ -56,7 +70,108 @@ main (int, char**)
|
||||
auto px = b3.eval (0.5f);
|
||||
auto rx = b3[0] + 0.5f * (b3[3] - b3[0]);
|
||||
CHECK_EQ (px, rx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <>
|
||||
void
|
||||
test_intersect<1> (void)
|
||||
{
|
||||
// A line from (0,0) to (100,100)
|
||||
static const util::bezier<1> b1 ({{0.f, 0.f}, {100.f, 100.f}});
|
||||
|
||||
// Through the centre
|
||||
CHECK_EQ (b1.intersections ({0.f, 100.f}, {100.f, 0.f}), 1);
|
||||
CHECK_EQ (b1.intersections ({100.f, 0.f}, {0.f, 100.f}), 1);
|
||||
|
||||
// Coincident with endpoints
|
||||
CHECK_EQ (b1.intersections ({0.f, 0.f}, {1.f,0.f}), 1);
|
||||
CHECK_EQ (b1.intersections ({100.f, 100.f}, {100.f,0.f}), 1);
|
||||
|
||||
// Co-planar
|
||||
CHECK_EQ (b1.intersections ({0.f, 0.f}, {1.f, 1.f}), 1);
|
||||
|
||||
// Underneath
|
||||
CHECK_EQ (b1.intersections ({1000.f, -10.f}, {-1000.f, -10.f}), 0);
|
||||
|
||||
// Above
|
||||
CHECK_EQ (b1.intersections ({1000.f, 110.f}, {-1000.f, 110.f}), 0);
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <>
|
||||
void
|
||||
test_intersect<2> (void)
|
||||
{
|
||||
// A linear curve from (0,0) to (100,100)
|
||||
static const util::bezier<2> b2 ({{ 0.0f, 0.0f},
|
||||
{ 50.f, 50.f},
|
||||
{100.f, 100.f}});
|
||||
|
||||
// Through the centre
|
||||
CHECK_EQ (b2.intersections ({100.f, 0.f}, {0.f, 100.f}), 1);
|
||||
CHECK_EQ (b2.intersections ({0.f, 100.f}, {100.f, 0.f}), 1);
|
||||
|
||||
// Coincident with endpoints
|
||||
CHECK_EQ (b2.intersections ({0.f, 0.f}, {0.f,100.f}), 1);
|
||||
CHECK_EQ (b2.intersections ({0.f, 0.f}, {100.f,0.f}), 1);
|
||||
CHECK_EQ (b2.intersections ({100.f, 100.f}, {100.f,0.f}), 1);
|
||||
|
||||
// Co-planar
|
||||
CHECK_EQ (b2.intersections ({0.f, 0.f}, {1.f, 1.f}), 1);
|
||||
|
||||
// Underneath
|
||||
CHECK_EQ (b2.intersections ({1000.f, -10.f}, {-1000.f, -10.f}), 0);
|
||||
|
||||
// Above
|
||||
CHECK_EQ (b2.intersections ({1000.f, 110.f}, {-1000.f, 110.f}), 0);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <>
|
||||
void
|
||||
test_intersect<3> (void)
|
||||
{
|
||||
// A linear curve from (0,0) to (100,100)
|
||||
static const util::bezier<3> b3 ({{ 0.f, 0.f },
|
||||
{ 33.f, 33.f },
|
||||
{ 67.f, 67.f },
|
||||
{ 100.f, 100.f }});
|
||||
|
||||
// Through the centre
|
||||
CHECK_EQ (b3.intersections ({100.f, 0.f}, {0.f, 100.f}), 1);
|
||||
CHECK_EQ (b3.intersections ({0.f, 100.f}, {100.f, 0.f}), 1);
|
||||
|
||||
// Coincident with endpoints
|
||||
CHECK_EQ (b3.intersections ({0.f, 0.f}, {0.f,100.f}), 1);
|
||||
CHECK_EQ (b3.intersections ({0.f, 0.f}, {100.f,0.f}), 1);
|
||||
CHECK_EQ (b3.intersections ({100.f, 100.f}, {100.f,0.f}), 1);
|
||||
|
||||
// Co-planar
|
||||
CHECK_EQ (b3.intersections ({0.f, 0.f}, {1.f, 1.f}), 1);
|
||||
|
||||
// Underneath
|
||||
CHECK_EQ (b3.intersections ({1000.f, -10.f}, {-1000.f, -10.f}), 0);
|
||||
|
||||
// Above
|
||||
CHECK_EQ (b3.intersections ({1000.f, 110.f}, {-1000.f, 110.f}), 0);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
int
|
||||
main (int, char**)
|
||||
{
|
||||
test_eval<1> ();
|
||||
test_eval<2> ();
|
||||
test_eval<3> ();
|
||||
|
||||
test_intersect<1> ();
|
||||
test_intersect<2> ();
|
||||
test_intersect<3> ();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user