File: Bezier.cpp

package info (click to toggle)
pymol 3.1.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 74,084 kB
  • sloc: cpp: 482,660; python: 89,328; ansic: 29,512; javascript: 6,792; sh: 84; makefile: 25
file content (134 lines) | stat: -rw-r--r-- 4,080 bytes parent folder | download
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