2015-01-21 23:42:45 +11:00
|
|
|
/*
|
2018-08-04 15:14:06 +10:00
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
2015-01-21 23:42:45 +11:00
|
|
|
*
|
2016-03-11 12:48:19 +11:00
|
|
|
* Copyright 2015-2016 Danny Robson <danny@nerdcruft.net>
|
2015-01-21 23:42:45 +11:00
|
|
|
*/
|
|
|
|
|
2017-11-22 16:49:37 +11:00
|
|
|
#include "bezier.hpp"
|
2015-01-21 23:42:45 +11:00
|
|
|
|
2017-11-22 16:49:37 +11:00
|
|
|
#include "debug.hpp"
|
|
|
|
#include "polynomial.hpp"
|
|
|
|
#include "stream.hpp"
|
|
|
|
#include "coord/iostream.hpp"
|
2015-01-21 23:42:45 +11:00
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <iterator>
|
|
|
|
|
|
|
|
|
2016-12-21 20:20:56 +11:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
template <size_t N>
|
2018-08-05 14:42:02 +10:00
|
|
|
cruft::bezier<N>::bezier (const cruft::point2f (&_points)[N+1])
|
2015-01-21 23:42:45 +11:00
|
|
|
{
|
2016-12-21 20:20:56 +11:00
|
|
|
std::copy (_points, _points + N + 1, m_points);
|
2015-01-29 15:47:40 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// 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.
|
2016-12-21 20:20:56 +11:00
|
|
|
template <size_t N>
|
2015-01-29 15:47:40 +11:00
|
|
|
size_t
|
2018-08-05 14:42:02 +10:00
|
|
|
cruft::bezier<N>::intersections (point2f p0, point2f p1) const
|
2015-01-29 15:47:40 +11:00
|
|
|
{
|
|
|
|
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
|
2016-12-21 20:20:56 +11:00
|
|
|
const std::array<vector2f,N+1> bcoeff = coeffs ();
|
2015-01-29 15:47:40 +11:00
|
|
|
|
2016-12-21 20:20:56 +11:00
|
|
|
std::array<float,N+1> pcoeff;
|
2015-01-29 15:47:40 +11:00
|
|
|
for (size_t i = 0; i < pcoeff.size (); ++i)
|
|
|
|
pcoeff[i] = A * bcoeff[i].x + B * bcoeff[i].y;
|
|
|
|
pcoeff.back () += C;
|
|
|
|
|
2016-12-21 20:20:56 +11:00
|
|
|
const auto r = polynomial::roots<N> (pcoeff);
|
2015-01-29 15:47:40 +11:00
|
|
|
|
|
|
|
// 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;
|
2016-12-21 20:20:56 +11:00
|
|
|
for (size_t i = 0; i < N; ++i) {
|
2015-01-29 15:47:40 +11:00
|
|
|
// 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
|
2018-08-05 14:42:02 +10:00
|
|
|
const cruft::vector2f q = polynomial::eval (bcoeff, t);
|
2015-01-29 15:47:40 +11:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-22 14:56:05 +11:00
|
|
|
//-----------------------------------------------------------------------------
|
2016-12-21 20:20:56 +11:00
|
|
|
template <size_t N>
|
2018-08-05 14:42:02 +10:00
|
|
|
cruft::region2f
|
|
|
|
cruft::bezier<N>::region (void) const
|
2015-02-03 12:53:41 +11:00
|
|
|
{
|
|
|
|
float x0 = m_points[0].x;
|
|
|
|
float y0 = m_points[0].y;
|
|
|
|
|
|
|
|
float x1 = x0;
|
|
|
|
float y1 = y0;
|
|
|
|
|
2016-12-21 20:20:56 +11:00
|
|
|
for (size_t i = 1; i < N+1; ++i) {
|
2015-02-03 12:53:41 +11:00
|
|
|
x0 = min (x0, m_points[i].x);
|
|
|
|
y0 = min (y0, m_points[i].y);
|
|
|
|
|
|
|
|
x1 = max (x1, m_points[i].x);
|
|
|
|
y1 = max (y1, m_points[i].y);
|
|
|
|
}
|
|
|
|
|
2018-08-05 14:42:02 +10:00
|
|
|
cruft::point2f p0 { x0, y0 };
|
|
|
|
cruft::point2f p1 { x1, y1 };
|
2015-03-06 01:09:37 +11:00
|
|
|
|
|
|
|
return { p0, p1 };
|
2015-02-03 12:53:41 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-21 23:42:45 +11:00
|
|
|
//-----------------------------------------------------------------------------
|
2016-12-21 20:20:56 +11:00
|
|
|
template <size_t N>
|
2018-08-05 14:42:02 +10:00
|
|
|
cruft::point2f&
|
|
|
|
cruft::bezier<N>::operator[] (size_t idx)
|
2015-01-21 23:42:45 +11:00
|
|
|
{
|
2016-12-21 20:20:56 +11:00
|
|
|
CHECK_LE (idx, N);
|
2015-01-21 23:42:45 +11:00
|
|
|
|
|
|
|
return m_points[idx];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2016-12-21 20:20:56 +11:00
|
|
|
template <size_t N>
|
2018-08-05 14:42:02 +10:00
|
|
|
const cruft::point2f&
|
|
|
|
cruft::bezier<N>::operator[] (size_t idx) const
|
2015-01-21 23:42:45 +11:00
|
|
|
{
|
2016-12-21 20:20:56 +11:00
|
|
|
CHECK_LE (idx, N);
|
2015-01-21 23:42:45 +11:00
|
|
|
|
|
|
|
return m_points[idx];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-03-11 12:43:29 +11:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2016-12-21 20:20:56 +11:00
|
|
|
template <size_t N>
|
2018-08-05 14:42:02 +10:00
|
|
|
const cruft::point2f*
|
|
|
|
cruft::bezier<N>::begin (void) const
|
2016-03-11 12:43:29 +11:00
|
|
|
{
|
|
|
|
return std::cbegin (m_points);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2016-12-21 20:20:56 +11:00
|
|
|
template <size_t N>
|
2018-08-05 14:42:02 +10:00
|
|
|
const cruft::point2f*
|
|
|
|
cruft::bezier<N>::end (void) const
|
2016-03-11 12:43:29 +11:00
|
|
|
{
|
|
|
|
return std::cend (m_points);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-21 23:42:45 +11:00
|
|
|
//-----------------------------------------------------------------------------
|
2016-12-21 20:20:56 +11:00
|
|
|
template <size_t N>
|
2018-08-05 14:42:02 +10:00
|
|
|
const cruft::point2f*
|
|
|
|
cruft::bezier<N>::cbegin (void) const
|
2016-03-11 12:43:29 +11:00
|
|
|
{
|
|
|
|
return std::cbegin (m_points);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2016-12-21 20:20:56 +11:00
|
|
|
template <size_t N>
|
2018-08-05 14:42:02 +10:00
|
|
|
const cruft::point2f*
|
|
|
|
cruft::bezier<N>::cend (void) const
|
2016-03-11 12:43:29 +11:00
|
|
|
{
|
|
|
|
return std::cend (m_points);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2016-12-21 20:20:56 +11:00
|
|
|
template <size_t N>
|
2015-01-22 14:56:19 +11:00
|
|
|
std::ostream&
|
2018-08-05 14:42:02 +10:00
|
|
|
cruft::operator<< (std::ostream &os, const bezier<N> &b)
|
2015-01-22 14:56:19 +11:00
|
|
|
{
|
2016-03-11 12:48:19 +11:00
|
|
|
using value_type = decltype(*b.cbegin());
|
|
|
|
|
|
|
|
os << "[";
|
|
|
|
std::transform (std::cbegin (b),
|
|
|
|
std::cend (b),
|
2016-06-29 17:52:26 +10:00
|
|
|
infix_iterator<value_type> (os, ", "),
|
|
|
|
[] (auto i) { return +i; });
|
2016-03-11 12:48:19 +11:00
|
|
|
os << "]";
|
2015-01-22 14:56:19 +11:00
|
|
|
|
|
|
|
return os;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-03-11 12:43:29 +11:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2016-12-21 20:20:56 +11:00
|
|
|
#define INSTANTIATE(N) \
|
2018-08-05 14:42:02 +10:00
|
|
|
template class cruft::bezier<N>; \
|
|
|
|
template std::ostream& cruft::operator<< (std::ostream&, const bezier<N>&);
|
2015-01-22 14:56:19 +11:00
|
|
|
|
|
|
|
INSTANTIATE(1)
|
|
|
|
INSTANTIATE(2)
|
|
|
|
INSTANTIATE(3)
|