libcruft-util/cruft/util/bezier1.cpp

125 lines
3.1 KiB
C++

/*
* 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/.
*
* Copyright 2015-2016 Danny Robson <danny@nerdcruft.net>
*/
#include "bezier.hpp"
#include <array>
///////////////////////////////////////////////////////////////////////////////
namespace cruft {
template <>
point2f
bezier<1>::eval (float t) const
{
CHECK_GE (t, 0.f);
CHECK_LE (t, 1.f);
auto v0 = (1 - t) * m_points[0];
auto v1 = t * m_points[1];
return {
v0.x + v1.x,
v0.y + v1.y
};
}
}
///////////////////////////////////////////////////////////////////////////////
constexpr
cruft::vector2f
orthonormal (cruft::vector2f v)
{
const auto len = norm (v);
CHECK_NEZ (len);
return cruft::vector2f { -v.y / len, v.x / len };
}
//-----------------------------------------------------------------------------
namespace cruft {
template <>
float
bezier<1>::closest (cruft::point2f q) const noexcept
{
const auto ab = m_points[1] - m_points[0];
const auto aq = q - m_points[0];
return dot (aq, ab) / dot (ab, ab);
}
}
//-----------------------------------------------------------------------------
namespace cruft {
template <>
float
bezier<1>::distance (cruft::point2f q) const noexcept
{
const auto ab = m_points[1] - m_points[0];
const auto t = clamp (closest (q), 0.f, 1.f);
const auto p = m_points[0] + t * ab;
return cruft::distance (q, p);
}
}
//-----------------------------------------------------------------------------
namespace cruft {
template <>
sdot_t
bezier<1>::sdot (const point2f q) const noexcept
{
// find the closest parameter 't' to the point 'q' for the parametric line
const auto qa = m_points[0] - q;
const auto ab = m_points[1] - m_points[0];
const auto t = closest (q);
// find the vector to, and distance to, the nearest endpoint 'e'
const auto qe = m_points[t > 0.5] - q;
const auto d_e = norm (qe);
// if we're on the segment return the distance to the segment
if (t >= 0 && t <= 1) {
const auto ortho = cruft::vector2f { -ab.y, ab.x } / norm (ab);
const auto d = dot (ortho, qa);
// not _entirely_ sure why we need this condition
if (abs (d) <= d_e) {
return { d, 0 };
}
}
// return the distance and angle to the endpoint
return {
sign (cross (ab, qa)) * d_e,
abs (
dot (normalised (ab), normalised (qe))
)
};
}
}
//-----------------------------------------------------------------------------
namespace cruft {
template <>
std::array<cruft::vector2f,2>
bezier<1>::coeffs (void) const
{
auto &v = m_coeffs;
return {
-1.f * v[1] + 1.f * v[0],
+1.f * v[1],
};
}
}