HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
bezier_curve.hpp
1// Copyright Take Vos 2019-2021.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "rapid/sdf_r8.hpp"
8#include "pixel_map.hpp"
9#include "alignment.hpp"
10#include "math.hpp"
11#include "bezier.hpp"
12#include "required.hpp"
13#include "geometry/vector.hpp"
14#include "geometry/point.hpp"
15#include "geometry/transform.hpp"
16#include <tuple>
17#include <limits>
18#include <algorithm>
19
20namespace tt {
21
22struct bezier_point;
23
24enum class LineJoinStyle { Bevel, Miter, Rounded };
25
30 enum class Type : uint8_t { None, Linear, Quadratic, Cubic };
31 enum class Color : uint8_t { Yellow, Magenta, Cyan, White };
32
33 Type type;
34 Color color;
39
40 bezier_curve() noexcept = delete;
41 bezier_curve(bezier_curve const &other) noexcept = default;
42 bezier_curve(bezier_curve &&other) noexcept = default;
43 bezier_curve &operator=(bezier_curve const &other) noexcept = default;
44 bezier_curve &operator=(bezier_curve &&other) noexcept = default;
45
48 bezier_curve(point2 const P1, point2 const P2, Color color = Color::White) noexcept :
49 type(Type::Linear), color(color), P1(P1), C1(), C2(), P2(P2)
50 {
51 }
52
55 bezier_curve(point2 const P1, point2 const C1, point2 const P2, Color color = Color::White) noexcept :
56 type(Type::Quadratic), color(color), P1(P1), C1(C1), C2(), P2(P2)
57 {
58 }
59
62 bezier_curve(point2 const P1, point2 const C1, point2 const C2, point2 const P2, Color color = Color::White) noexcept :
63 type(Type::Cubic), color(color), P1(P1), C1(C1), C2(C2), P2(P2)
64 {
65 }
66
70 Type const type,
71 point2 const P1,
72 point2 const C1,
73 point2 const C2,
74 point2 const P2,
75 Color color = Color::White) noexcept :
76 type(type), color(color), P1(P1), C1(C1), C2(C2), P2(P2)
77 {
78 }
79
80 [[nodiscard]] bool has_red() const noexcept
81 {
82 return color != Color::Cyan;
83 }
84
85 [[nodiscard]] bool has_green() const noexcept
86 {
87 return color != Color::Magenta;
88 }
89
90 [[nodiscard]] bool has_blue() const noexcept
91 {
92 return color != Color::Yellow;
93 }
94
102 [[nodiscard]] point2 pointAt(float const t) const noexcept
103 {
104 switch (type) {
105 case Type::Linear: return bezierPointAt(P1, P2, t);
106 case Type::Quadratic: return bezierPointAt(P1, C1, P2, t);
107 case Type::Cubic: return bezierPointAt(P1, C1, C2, P2, t);
108 default: tt_no_default();
109 }
110 }
111
119 [[nodiscard]] vector2 tangentAt(float const t) const noexcept
120 {
121 switch (type) {
122 case Type::Linear: return bezierTangentAt(P1, P2, t);
123 case Type::Quadratic: return bezierTangentAt(P1, C1, P2, t);
124 case Type::Cubic: return bezierTangentAt(P1, C1, C2, P2, t);
125 default: tt_no_default();
126 }
127 }
128
133 [[nodiscard]] results<float, 3> solveXByY(float const y) const noexcept
134 {
135 switch (type) {
136 case Type::Linear: return bezierFindX(P1, P2, y);
137 case Type::Quadratic: return bezierFindX(P1, C1, P2, y);
138 case Type::Cubic: return bezierFindX(P1, C1, C2, P2, y);
139 default: tt_no_default();
140 }
141 }
142
143 [[nodiscard]] results<float, 3> solveTForNormalsIntersectingPoint(point2 P) const noexcept
144 {
145 switch (type) {
146 case Type::Linear: return bezierFindTForNormalsIntersectingPoint(P1, P2, P);
147 case Type::Quadratic: return bezierFindTForNormalsIntersectingPoint(P1, C1, P2, P);
148 case Type::Cubic: tt_no_default();
149 default: tt_no_default();
150 }
151 }
152
155 [[nodiscard]] float sdf_distance(point2 P) const noexcept
156 {
157 auto min_square_distance = std::numeric_limits<float>::max();
158 auto min_t = 0.0f;
159 auto min_normal = vector2{0.0f, 1.0f};
160
161 ttlet ts = solveTForNormalsIntersectingPoint(P);
162 for (auto t : ts) {
163 t = std::clamp(t, 0.0f, 1.0f);
164
165 ttlet normal = P - pointAt(t);
166 ttlet square_distance = squared_hypot(normal);
167 if (square_distance < min_square_distance) {
168 min_square_distance = square_distance;
169 min_t = t;
170 min_normal = normal;
171 }
172 }
173
174 ttlet tangent = tangentAt(min_t);
175 ttlet distance = std::sqrt(min_square_distance);
176 ttlet sdistance = cross(tangent, min_normal) < 0.0 ? distance : -distance;
177 return sdistance;
178 }
179
185 [[nodiscard]] std::pair<bezier_curve, bezier_curve> cubicSplit(float const t) const noexcept
186 {
187 ttlet outerA = bezier_curve{P1, C1};
188 ttlet outerBridge = bezier_curve{C1, C2};
189 ttlet outerB = bezier_curve{C2, P2};
190
191 ttlet innerA = bezier_curve{outerA.pointAt(t), outerBridge.pointAt(t)};
192 ttlet innerB = bezier_curve{outerBridge.pointAt(t), outerB.pointAt(t)};
193
194 ttlet newPoint = bezier_curve{innerA.pointAt(t), innerB.pointAt(t)}.pointAt(t);
195
196 return {{P1, outerA.pointAt(t), innerA.pointAt(t), newPoint}, {newPoint, innerB.pointAt(t), outerB.pointAt(t), P2}};
197 }
198
204 [[nodiscard]] std::pair<bezier_curve, bezier_curve> quadraticSplit(float const t) const noexcept
205 {
206 ttlet outerA = bezier_curve{P1, C1};
207 ttlet outerB = bezier_curve{C1, P2};
208
209 ttlet newPoint = bezier_curve{outerA.pointAt(t), outerB.pointAt(t)}.pointAt(t);
210
211 return {{P1, outerA.pointAt(t), newPoint}, {newPoint, outerB.pointAt(t), P2}};
212 }
213
219 [[nodiscard]] std::pair<bezier_curve, bezier_curve> linearSplit(float const t) const noexcept
220 {
221 ttlet newPoint = pointAt(t);
222
223 return {{P1, newPoint}, {newPoint, P2}};
224 }
225
231 [[nodiscard]] std::pair<bezier_curve, bezier_curve> split(float const t) const noexcept
232 {
233 switch (type) {
234 case Type::Linear: return linearSplit(t);
235 case Type::Quadratic: return quadraticSplit(t);
236 case Type::Cubic: return cubicSplit(t);
237 default: tt_no_default();
238 }
239 }
240
245 void subdivideUntilFlat_impl(std::vector<bezier_curve> &r, float const minimumFlatness) const noexcept
246 {
247 if (flatness() >= minimumFlatness) {
248 r.push_back(*this);
249 } else {
250 ttlet[a, b] = split(0.5f);
251 a.subdivideUntilFlat_impl(r, minimumFlatness);
252 b.subdivideUntilFlat_impl(r, minimumFlatness);
253 }
254 }
255
260 [[nodiscard]] std::vector<bezier_curve> subdivideUntilFlat(float const tolerance) const noexcept
261 {
263 subdivideUntilFlat_impl(r, 1.0f - tolerance);
264 return r;
265 }
266
270 [[nodiscard]] float flatness() const noexcept
271 {
272 switch (type) {
273 case Type::Linear: return bezierFlatness(P1, P2);
274 case Type::Quadratic: return bezierFlatness(P1, C1, P2);
275 case Type::Cubic: return bezierFlatness(P1, C1, C2, P2);
276 default: tt_no_default();
277 }
278 }
279
284 [[nodiscard]] bezier_curve toParallelLine(float const offset) const noexcept
285 {
286 auto [newP1, newP2] = parallelLine(P1, P2, offset);
287 return {newP1, newP2};
288 }
289
290 [[nodiscard]] friend bool operator==(bezier_curve const &lhs, bezier_curve const &rhs) noexcept
291 {
292 if (lhs.type != rhs.type) {
293 return false;
294 }
295 switch (lhs.type) {
296 case bezier_curve::Type::Linear: return (lhs.P1 == rhs.P1) && (lhs.P2 == rhs.P2);
297 case bezier_curve::Type::Quadratic: return (lhs.P1 == rhs.P1) && (lhs.C1 == rhs.C1) && (lhs.P2 == rhs.P2);
298 case bezier_curve::Type::Cubic:
299 return (lhs.P1 == rhs.P1) && (lhs.C1 == rhs.C1) && (lhs.C2 == rhs.C2) && (lhs.P2 == rhs.P2);
300 default: tt_no_default();
301 }
302 }
303
304 [[nodiscard]] friend bezier_curve operator*(geo::transformer auto const &lhs, bezier_curve const &rhs) noexcept
305 {
306 return {rhs.type, lhs * rhs.P1, lhs * rhs.C1, lhs * rhs.C2, lhs * rhs.P2};
307 }
308
311 [[nodiscard]] friend bezier_curve operator~(bezier_curve const &rhs) noexcept
312 {
313 return {rhs.type, rhs.P2, rhs.C2, rhs.C1, rhs.P1};
314 }
315};
316
323[[nodiscard]] std::vector<bezier_curve>
324makeContourFromPoints(std::vector<bezier_point>::const_iterator first, std::vector<bezier_point>::const_iterator last) noexcept;
325
332[[nodiscard]] std::vector<bezier_curve> makeInverseContour(std::vector<bezier_curve> const &contour) noexcept;
333
344[[nodiscard]] std::vector<bezier_curve> makeParallelContour(
345 std::vector<bezier_curve> const &contour,
346 float offset,
347 LineJoinStyle lineJoinStyle,
348 float tolerance) noexcept;
349
354void fill(pixel_map<uint8_t> &image, std::vector<bezier_curve> const &curves) noexcept;
355
360void fill(pixel_map<sdf_r8> &image, std::vector<bezier_curve> const &curves) noexcept;
361
362} // namespace tt
Definition bezier_curve.hpp:29
point2 C2
Control point.
Definition bezier_curve.hpp:37
vector2 tangentAt(float const t) const noexcept
Definition bezier_curve.hpp:119
float flatness() const noexcept
Definition bezier_curve.hpp:270
results< float, 3 > solveXByY(float const y) const noexcept
Definition bezier_curve.hpp:133
std::vector< bezier_curve > subdivideUntilFlat(float const tolerance) const noexcept
Definition bezier_curve.hpp:260
bezier_curve(point2 const P1, point2 const C1, point2 const C2, point2 const P2, Color color=Color::White) noexcept
Definition bezier_curve.hpp:62
std::pair< bezier_curve, bezier_curve > cubicSplit(float const t) const noexcept
Definition bezier_curve.hpp:185
std::pair< bezier_curve, bezier_curve > split(float const t) const noexcept
Definition bezier_curve.hpp:231
friend bezier_curve operator~(bezier_curve const &rhs) noexcept
Definition bezier_curve.hpp:311
bezier_curve(Type const type, point2 const P1, point2 const C1, point2 const C2, point2 const P2, Color color=Color::White) noexcept
Definition bezier_curve.hpp:69
point2 pointAt(float const t) const noexcept
Definition bezier_curve.hpp:102
bezier_curve(point2 const P1, point2 const C1, point2 const P2, Color color=Color::White) noexcept
Definition bezier_curve.hpp:55
std::pair< bezier_curve, bezier_curve > linearSplit(float const t) const noexcept
Definition bezier_curve.hpp:219
void subdivideUntilFlat_impl(std::vector< bezier_curve > &r, float const minimumFlatness) const noexcept
Definition bezier_curve.hpp:245
point2 P1
First point.
Definition bezier_curve.hpp:35
bezier_curve toParallelLine(float const offset) const noexcept
Definition bezier_curve.hpp:284
point2 P2
Last point.
Definition bezier_curve.hpp:38
float sdf_distance(point2 P) const noexcept
Find the distance from the point to the curve.
Definition bezier_curve.hpp:155
std::pair< bezier_curve, bezier_curve > quadraticSplit(float const t) const noexcept
Definition bezier_curve.hpp:204
point2 C1
Control point.
Definition bezier_curve.hpp:36
This is a RGBA floating point color.
Definition color.hpp:36
Definition polynomial.hpp:14
T max(T... args)
T sqrt(T... args)