libcruft-util/bezier.cpp

199 lines
5.1 KiB
C++
Raw Normal View History

2015-01-21 23:42:45 +11:00
/*
2015-04-13 18:05:28 +10:00
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
2015-01-21 23:42:45 +11:00
*
2015-04-13 18:05:28 +10:00
* http://www.apache.org/licenses/LICENSE-2.0
2015-01-21 23:42:45 +11:00
*
2015-04-13 18:05:28 +10:00
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
2015-01-21 23:42:45 +11:00
*
* Copyright 2015-2016 Danny Robson <danny@nerdcruft.net>
2015-01-21 23:42:45 +11:00
*/
#include "bezier.hpp"
2015-01-21 23:42:45 +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>
util::bezier<N>::bezier (const util::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
2016-12-21 20:20:56 +11:00
util::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
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;
}
2015-01-22 14:56:05 +11:00
//-----------------------------------------------------------------------------
2016-12-21 20:20:56 +11:00
template <size_t N>
2015-02-03 12:53:41 +11:00
util::region2f
2016-12-21 20:20:56 +11:00
util::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);
}
util::point2f p0 { x0, y0 };
util::point2f p1 { x1, y1 };
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>
2015-01-21 23:42:45 +11:00
util::point2f&
2016-12-21 20:20:56 +11:00
util::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>
2015-01-21 23:42:45 +11:00
const util::point2f&
2016-12-21 20:20:56 +11:00
util::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>
2016-03-11 12:43:29 +11:00
const util::point2f*
2016-12-21 20:20:56 +11:00
util::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>
2016-03-11 12:43:29 +11:00
const util::point2f*
2016-12-21 20:20:56 +11:00
util::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>
2016-03-11 12:43:29 +11:00
const util::point2f*
2016-12-21 20:20:56 +11:00
util::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>
2016-03-11 12:43:29 +11:00
const util::point2f*
2016-12-21 20:20:56 +11:00
util::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&
2016-12-21 20:20:56 +11:00
util::operator<< (std::ostream &os, const bezier<N> &b)
2015-01-22 14:56:19 +11:00
{
using value_type = decltype(*b.cbegin());
os << "[";
std::transform (std::cbegin (b),
std::cend (b),
infix_iterator<value_type> (os, ", "),
[] (auto i) { return +i; });
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) \
template class util::bezier<N>; \
template std::ostream& util::operator<< (std::ostream&, const bezier<N>&);
2015-01-22 14:56:19 +11:00
INSTANTIATE(1)
INSTANTIATE(2)
INSTANTIATE(3)