#include "Bezier.h" #include "pymol/algorithm.h" #define GLM_ENABLE_EXPERIMENTAL #include namespace pymol { ////////////////////////// ///////PUBLIC API///////// ////////////////////////// glm::vec3 BezierSpline::getBezierPoint(float globalT) const { int index; float localT; std::tie(index, localT) = getIndexAndLocalT(globalT); return GetBezierPoint(bezierPoints[index], bezierPoints[index + 1], localT); } glm::vec3 BezierSpline::getFirstDerivative(float globalT) const { int index; float localT; std::tie(index, localT) = getIndexAndLocalT(globalT); return GetBezierFirstDerivative( bezierPoints[index], bezierPoints[index + 1], localT); } BezierSplinePoint* BezierSpline::getLastBezierPoint() { if (bezierPoints.empty()) { return nullptr; } return &bezierPoints.back(); } std::uint32_t BezierSpline::curveCount() const { auto count = bezierPoints.empty() ? 0 : bezierPoints.size() - 1; assert(count >= 0); return static_cast(count); } void BezierSpline::addBezierPoint(const BezierSplinePoint& pt) { bezierPoints.push_back(pt); // TODO: Needs to take care of alignment } void BezierSpline::addBezierPoint() { if (bezierPoints.empty()) { pymol::BezierSplinePoint newPointA{}; newPointA.control = glm::vec3(0.0f, 0.0f, 0.0f); newPointA.leftHandle = glm::vec3(0.0f, 0.0f, 10.0f); newPointA.rightHandle = glm::vec3(0.0f, 0.0f, -10.0f); addBezierPoint(newPointA); pymol::BezierSplinePoint newPointB{}; newPointB.control = newPointA.control + glm::vec3(10.0f, 0.0, 0.0); newPointB.leftHandle = newPointB.control + glm::vec3(0.0f, 0.0f, -10.0f); newPointB.rightHandle = newPointB.control + glm::vec3(0.0f, 0.0f, 10.0f); addBezierPoint(newPointB); return; } const auto prevPoint = getLastBezierPoint(); assert(prevPoint != nullptr); // To make sure we add the new point in the direction of the curve, // we'll take the first derivative at the end. auto numCurrBezPts = bezierPoints.size(); const auto dirAtEnd = glm::normalize(GetBezierFirstDerivative( bezierPoints[numCurrBezPts - 2], bezierPoints[numCurrBezPts - 1], 1.0f)); pymol::BezierSplinePoint newPoint{}; newPoint.control = prevPoint->control + dirAtEnd * 10.0f; newPoint.leftHandle = newPoint.control + glm::vec3(10.0f, 0.0f, 0.0f); auto dirToLeftHandle = newPoint.leftHandle - newPoint.control; newPoint.rightHandle = newPoint.control - dirToLeftHandle; addBezierPoint(newPoint); } ////////////////////////// ///////PRIVATE API//////// ////////////////////////// std::pair BezierSpline::getIndexAndLocalT(float globalT) const { auto t = std::clamp(globalT, 0.0f, 1.0f); if (t == 1.0f) { assert(bezierPoints.size() >= 2); return std::make_pair(static_cast(bezierPoints.size() - 2), t); } t *= curveCount(); int index = static_cast(t); t -= index; return std::make_pair(index, t); } glm::vec3 BezierSpline::GetBezierPoint( const BezierSplinePoint& a, const BezierSplinePoint& b, float t) { return GetBezierPoint(a.control, a.rightHandle, b.leftHandle, b.control, t); } glm::vec3 BezierSpline::GetBezierPoint(const glm::vec3& p0, const glm::vec3& p1, const glm::vec3& p2, const glm::vec3& p3, float t) { t = std::clamp(t, 0.0f, 1.0f); float oneMinusT = 1.0f - t; return oneMinusT * oneMinusT * oneMinusT * p0 + 3.0f * oneMinusT * oneMinusT * t * p1 + 3.0f * oneMinusT * t * t * p2 + t * t * t * p3; } glm::vec3 BezierSpline::GetBezierFirstDerivative(const glm::vec3& p0, const glm::vec3& p1, const glm::vec3& p2, const glm::vec3& p3, float t) { t = std::clamp(t, 0.0f, 1.0f); float oneMinusT = 1.0f - t; return 3.0f * oneMinusT * oneMinusT * (p1 - p0) + 6.0f * oneMinusT * t * (p2 - p1) + 3.0f * t * t * (p3 - p2); } glm::vec3 BezierSpline::GetBezierFirstDerivative( const BezierSplinePoint& a, const BezierSplinePoint& b, float t) { return GetBezierFirstDerivative( a.control, a.rightHandle, b.leftHandle, b.control, t); } } // namespace pymol