cgv
Loading...
Searching...
No Matches
bezier.h
1#pragma once
2
3#include "fvec.h"
4#include "fmat.h"
5#include "interpolate.h"
6#include "parametric_curve.h"
7
8namespace cgv {
9namespace math {
10
11template<typename PointT>
12class bezier_curve : public parametric_curve<bezier_curve<PointT>> {
13public:
15 std::vector<PointT> points;
16
17 bezier_curve() {}
18 bezier_curve(std::initializer_list<PointT> points) : points(points) {}
19
20 template<typename ParamT = float>
21 PointT evaluate(ParamT t) const {
22 // TODO: throw an exception
23 if(points.empty())
24 return {};
25
26 if(points.size() == 1)
27 return points.front();
28
29 size_t degree = points.size() - 1;
30 std::vector<PointT> d = points;
31
32 for(size_t i = 1; i <= degree; ++i) {
33 for(size_t j = degree; j > i - 1; --j)
34 d[j] = interpolate_linear(d[j - 1], d[j], t);
35 }
36
37 return d[degree];
38 }
39};
40
41template<typename PointT>
42class quadratic_bezier_curve : public parametric_curve<quadratic_bezier_curve<PointT>> {
43public:
45 PointT p0 = { 0 };
47 PointT p1 = { 0 };
49 PointT p2 = { 0 };
50
52 quadratic_bezier_curve(const PointT& p0, const PointT& p1, const PointT& p2) : p0(p0), p1(p1), p2(p2) {}
53
54 template<typename ParamT = float>
55 PointT evaluate(ParamT t) const {
56 return interpolate_quadratic_bezier(p0, p1, p2, t);
57 }
58
59 template<typename ParamT = float>
60 PointT derivative(ParamT t) const {
61 return interpolate_linear(PointT(2) * (p1 - p0), PointT(2) * (p2 - p1), t);
62 }
63
64 std::pair<PointT, PointT> axis_aligned_bounding_box() const {
65 PointT mi = per_component_min(p0, p2);
66 PointT ma = per_component_max(p0, p2);
67
68 if(is_outside_bounds(p1, mi, ma)) {
69 PointT t = clamp((p0 - p1) / (p0 - PointT(2) * p1 + p2), PointT(0), PointT(1));
70 PointT s = PointT(1) - t;
71 PointT q = s * s * p0 + PointT(2) * s * t * p1 + t * t * p2;
72 mi = per_component_min(mi, q);
73 ma = per_component_max(ma, q);
74 }
75
76 return { mi, ma };
77 }
78
79private:
80 template<typename T>
81 T per_component_min(const T& a, const T& b) const {
82 return std::min(a, b);
83 }
84
85 template<typename T, cgv::type::uint32_type N>
86 fvec<T, N> per_component_min(const fvec<T, N>& a, const fvec<T, N>& b) const {
87 return min(a, b);
88 }
89
90 template<typename T>
91 T per_component_max(const T& a, const T& b) const {
92 return std::max(a, b);
93 }
94
95 template<typename T, cgv::type::uint32_type N>
96 fvec<T, N> per_component_max(const fvec<T, N>& a, const fvec<T, N>& b) const {
97 return max(a, b);
98 }
99
100 template<typename T>
101 bool is_outside_bounds(const T& p, const T& min, const T& max) const {
102 return p < min || p > max;
103 }
104
105 template<typename T, cgv::type::uint32_type N>
106 bool is_outside_bounds(const fvec<T, N>& p, const fvec<T, N>& min, const fvec<T, N>& max) const {
107 for(cgv::type::uint32_type i = 0; i < N; ++i) {
108 if(p[i] < min[i] || p[i] > max[i])
109 return true;
110 }
111 return false;
112 }
113};
114
115template<typename PointT>
116class cubic_bezier_curve : public parametric_curve<cubic_bezier_curve<PointT>> {
117public:
119 PointT p0 = { 0 };
121 PointT p1 = { 0 };
123 PointT p2 = { 0 };
125 PointT p3 = { 0 };
126
128 cubic_bezier_curve(const PointT& p0, const PointT& p1, const PointT& p2, const PointT& p3) : p0(p0), p1(p1), p2(p2), p3(p3) {}
129
130 template<typename ParamT = float>
131 PointT evaluate(ParamT t) const {
132 return interpolate_cubic_bezier(p0, p1, p2, p3, t);
133 }
134
135 template<typename ParamT = float>
136 PointT derivative(ParamT t) const {
137 return interpolate_quadratic_bezier(PointT(3) * (p1 - p0), PointT(3) * (p2 - p1), PointT(3) * (p3 - p2), t);
138 }
139
140 std::pair<PointT, PointT> axis_aligned_bounding_box() const {
141 return compute_bounds(p0, p1, p2, p3);
142 }
143private:
144 // Altered from original version. (Copyright 2013 Inigo Quilez: https://iquilezles.org/articles/bezierbbox/ and https://www.shadertoy.com/view/MdKBWt)
145 template<typename T>
146 std::pair<T, T> compute_bounds(const T& p0, const T& p1, const T& p2, const T& p3) const {
147 T mi = std::min(p0, p3);
148 T ma = std::max(p0, p3);
149
150 T c = T(-1) * p0 + T(1) * p1;
151 T b = T(1) * p0 - T(2) * p1 + T(1) * p2;
152 T a = T(-1) * p0 + T(3) * p1 - T(3) * p2 + T(1) * p3;
153
154 T h = b * b - a * c;
155
156 if(h > T(0)) {
157 h = sqrt(h);
158 T t = (-b - h) / a;
159 if(t > T(0) && t < T(1)) {
160 T s = T(1) - t;
161 T q = s * s * s * p0 + T(3) * s * s * t * p1 + T(3) * s * t * t * p2 + t * t * t * p3;
162 mi = std::min(mi, q);
163 ma = std::max(ma, q);
164 }
165 t = (-b + h) / a;
166 if(t > T(0) && t < T(1)) {
167 T s = T(1) - t;
168 T q = s * s * s * p0 + T(3) * s * s * t * p1 + T(3) * s * t * t * p2 + t * t * t * p3;
169 mi = std::min(mi, q);
170 ma = std::max(ma, q);
171 }
172 }
173
174 return { mi, ma };
175 }
176
177 template<typename T, cgv::type::uint32_type N>
178 std::pair<fvec<T, N>, fvec<T, N>> compute_bounds(const fvec<T, N>& p0, const fvec<T, N>& p1, const fvec<T, N>& p2, const fvec<T, N>& p3) const {
179 std::pair<fvec<T, N>, fvec<T, N>> bounds;
180 for(cgv::type::uint32_type i = 0; i < N; ++i) {
181 std::pair<T, T> coord_bounds = compute_bounds(p0[i], p1[i], p2[i], p3[i]);
182 bounds.first[i] = coord_bounds.first;
183 bounds.second[i] = coord_bounds.second;
184 }
185 return bounds;
186 }
187};
188
189} // namespace math
190} // namespace cgv
complete implementation of method actions that only call one method when entering a node
Definition action.h:113
std::vector< PointT > points
the control points
Definition bezier.h:15
PointT p3
the fourth control point
Definition bezier.h:125
PointT p1
the second control point
Definition bezier.h:121
PointT p2
the third control point
Definition bezier.h:123
PointT p0
the first control point
Definition bezier.h:119
CRTP base class for parametric curves.
PointT p2
the end control point
Definition bezier.h:49
PointT p0
the start control point
Definition bezier.h:45
PointT p1
the middle control point
Definition bezier.h:47
the cgv namespace
Definition print.h:11