1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
|
#include "Bezier.h"
#include "pymol/algorithm.h"
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/io.hpp>
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<std::uint32_t>(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<int, float> 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<int>(bezierPoints.size() - 2), t);
}
t *= curveCount();
int index = static_cast<int>(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
|