Grail (C++)  1.3.0
A multi-platform, modular, universal engine for embedding advanced AI in games.
BezierSpline.hh
1 // Copyright QED Software 2023.
2 
3 #ifndef GRAIL_BEZIER_SPLINE_H
4 #define GRAIL_BEZIER_SPLINE_H
5 
6 #include "Curve.hh"
7 #include "../../libs/vector2/vector2.h"
8 
9 #include <cassert>
10 
11 namespace grail
12 {
13 namespace evaluator
14 {
15  template <typename ContextType>
20  class BezierSpline final : public Curve<ContextType>
21  {
22  public:
30  BezierSpline(std::shared_ptr<Evaluator<ContextType>> childEvaluator,
31  const std::vector<Vector2>& points,
32  const std::vector<Vector2>& tangents,
33  float epsilon = 0.001f)
34  : Curve<ContextType>{childEvaluator}, points(points), tangents{tangents}, epsilon{epsilon}
35  {
36  Initialize();
37  }
38 
46  BezierSpline(std::shared_ptr<Evaluator<ContextType>> childEvaluator,
47  std::vector<Vector2>&& points,
48  std::vector<Vector2>&& tangents,
49  float epsilon = 0.001f)
50  : Curve<ContextType>{childEvaluator},
51  points(std::move(points)),
52  tangents(std::move(tangents)),
53  epsilon{epsilon}
54  {
55  Initialize();
56  }
57 
58  virtual float Sample(float argument) const override final
59  {
60  if(argument <= points.front().x)
61  {
62  return points.front().y;
63  }
64  else if(argument > points.back().x)
65  {
66  return points.back().y;
67  }
68 
69  for(std::size_t i = 1; i < points.size(); ++i)
70  {
71  if(argument == points[i].x)
72  {
73  return points[i].y;
74  }
75 
76  if(argument > points[i - 1].x && argument < points[i].x)
77  {
78  float begin = 0;
79  float end = 1;
80  Vector2 result;
81 
82  do
83  {
84  float middle = (begin + end) / 2.0f;
85  result = BezierInterpolation(i - 1, i, middle);
86 
87  if(result.x < argument)
88  {
89  begin = middle;
90  }
91  else
92  {
93  end = middle;
94  }
95  }
96  while(end - begin > epsilon);
97 
98  return result.y;
99  }
100  }
101  return 0;
102  }
103 
108  std::vector<Vector2>& GetPoints() { return points; }
113  const std::vector<Vector2>& GetPoints() const { return points; }
114 
119  std::vector<Vector2>& GetTangents() { return tangents; }
124  const std::vector<Vector2>& GetTangents() const { return tangents; }
125 
126  virtual data::EvaluatorType GetEvaluatorType() const override final { return data::EvaluatorType::CURVE_BEZIER; }
127 
128  private:
129  void Initialize()
130  {
131  assert(points.size() > 1);
132  assert(points.size() == tangents.size());
133  Vector2 leftVelocity = tangents.front();
134  leftGradient = leftVelocity.x != 0 ?
135  leftVelocity.y / leftVelocity.x :
136  std::numeric_limits<float>::quiet_NaN();
137  leftIntercept = leftVelocity.y - leftGradient * leftVelocity.x;
138 
139  Vector2 rightVelocity = tangents.back();
140  rightGradient = rightVelocity.x != 0 ?
141  rightVelocity.y / rightVelocity.x :
142  std::numeric_limits<float>::quiet_NaN();
143  rightIntercept = rightVelocity.y - rightGradient * rightVelocity.x;
144  }
145 
146  Vector2 BezierInterpolation(const std::size_t beginIndex, const std::size_t endIndex, float t) const
147  {
148  static const float BINOMIAL_COEFFICIENTS[4] = {1, 3, 3, 1};
149  Vector2 p0 = points[beginIndex];
150  Vector2 p3 = points[endIndex];
151  Vector2 p1 = p0 + tangents[beginIndex];
152  Vector2 p2 = p3 - tangents[endIndex];
153  Vector2 interpPoints[4]{p0, p1, p2, p3};
154 
155  float tComponent = 1; //to start with t^0
156  float oneMinusT = 1 - t;
157  float oneMinusTComponent = oneMinusT * oneMinusT * oneMinusT; //to start with (1 - t)^3
158  Vector2 result{0, 0};
159  for(int pointIndex = 0; pointIndex < 4; ++pointIndex)
160  {
161  result += BINOMIAL_COEFFICIENTS[pointIndex] *
162  tComponent *
163  oneMinusTComponent *
164  interpPoints[pointIndex]; //p(i) * t^(3-i) * (1-t)^(i)
165  tComponent *= t; //increment exponent
166  oneMinusTComponent /= (1 - t); //decrement exponent
167  }
168 
169  return result;
170  }
171 
172  std::vector<Vector2> points{};
173  std::vector<Vector2> tangents{};
174  float epsilon = 0.0f;
175 
176  float leftGradient = 0.0f;
177  float leftIntercept = 0.0f;
178  float rightGradient = 0.0f;
179  float rightIntercept = 0.0f;
180  };
181 }
182 }
183 
184 #endif // GRAIL_BEZIER_SPLINE_H
grail::evaluator::BezierSpline::BezierSpline
BezierSpline(std::shared_ptr< Evaluator< ContextType >> childEvaluator, std::vector< Vector2 > &&points, std::vector< Vector2 > &&tangents, float epsilon=0.001f)
BezierSpline - Constructor.
Definition: BezierSpline.hh:46
grail::evaluator::BezierSpline::GetPoints
const std::vector< Vector2 > & GetPoints() const
GetPoints.
Definition: BezierSpline.hh:113
grail::evaluator::BezierSpline::BezierSpline
BezierSpline(std::shared_ptr< Evaluator< ContextType >> childEvaluator, const std::vector< Vector2 > &points, const std::vector< Vector2 > &tangents, float epsilon=0.001f)
BezierSpline - Constructor.
Definition: BezierSpline.hh:30
grail::evaluator::Evaluator
The Evaluator class - base class being able to evaluate given context and output the result.
Definition: Evaluator.hh:22
grail::evaluator::BezierSpline::Sample
virtual float Sample(float argument) const override final
Sample - Transforms argument into output value depending on the type of Curve.
Definition: BezierSpline.hh:58
grail::evaluator::BezierSpline
The BezierSpline class -Composite Cubic Bezier Curve.
Definition: BezierSpline.hh:20
grail::evaluator::BezierSpline::GetTangents
std::vector< Vector2 > & GetTangents()
GetTangents.
Definition: BezierSpline.hh:119
grail::evaluator::BezierSpline::GetPoints
std::vector< Vector2 > & GetPoints()
GetPoints.
Definition: BezierSpline.hh:108
grail::evaluator::BezierSpline::GetTangents
const std::vector< Vector2 > & GetTangents() const
GetTangents.
Definition: BezierSpline.hh:124
grail::evaluator::Curve
The Curve class - Defines objects transforming one value into the other.
Definition: Curve.hh:21