HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
BezierCurve.hpp
1// Copyright 2019 Pokitec
2// All rights reserved.
3
4#pragma once
5
6#include "TTauri/Foundation/SDF8.hpp"
7#include "TTauri/Foundation/PixelMap.hpp"
8#include "TTauri/Foundation/attributes.hpp"
9#include "TTauri/Foundation/math.hpp"
10#include "TTauri/Foundation/bezier.hpp"
11#include "TTauri/Foundation/required.hpp"
12#include "TTauri/Foundation/vec.hpp"
13#include "TTauri/Foundation/mat.hpp"
14#include <tuple>
15#include <limits>
16#include <algorithm>
17
18namespace tt {
19
20struct BezierPoint;
21
26 enum class Type : uint8_t { None, Linear, Quadratic, Cubic };
27 enum class Color : uint8_t { Yellow, Magenta, Cyan, White };
28
29 Type type;
30 Color color;
35
36 BezierCurve() noexcept = delete;
37 BezierCurve(BezierCurve const &other) noexcept = default;
38 BezierCurve(BezierCurve &&other) noexcept = default;
39 BezierCurve &operator=(BezierCurve const &other) noexcept = default;
40 BezierCurve &operator=(BezierCurve &&other) noexcept = default;
41
44 BezierCurve(vec const P1, vec const P2, Color color=Color::White) noexcept :
45 type(Type::Linear), color(color), P1(P1), C1(), C2(), P2(P2) {
46 tt_assume(P1.is_point() && P2.is_point());
47 }
48
51 BezierCurve(vec const P1, vec const C1, vec const P2, Color color=Color::White) noexcept :
52 type(Type::Quadratic), color(color), P1(P1), C1(C1), C2(), P2(P2) {
53 tt_assume(P1.is_point() && C1.is_point() && P2.is_point());
54 }
55
58 BezierCurve(vec const P1, vec const C1, vec const C2, vec const P2, Color color=Color::White) noexcept :
59 type(Type::Cubic), color(color), P1(P1), C1(C1), C2(C2), P2(P2) {
60 tt_assume(P1.is_point() && C1.is_point() && C2.is_point() && P2.is_point());
61 }
62
65 BezierCurve(Type const type, vec const P1, vec const C1, vec const C2, vec const P2, Color color=Color::White) noexcept :
66 type(type), color(color), P1(P1), C1(C1), C2(C2), P2(P2) {
67 switch (type) {
68 case Type::Linear:
69 tt_assume(P1.is_point() && P2.is_point());
70 break;
71
72 case Type::Quadratic:
73 tt_assume(P1.is_point() && C1.is_point() && P2.is_point());
74 break;
75
76 case Type::Cubic:
77 tt_assume(P1.is_point() && C1.is_point() && C2.is_point() && P2.is_point());
78 break;
79
80 default:
81 tt_no_default;
82 }
83 }
84
85
86 [[nodiscard]] bool has_red() const noexcept {
87 return color != Color::Cyan;
88 }
89
90 [[nodiscard]] bool has_green() const noexcept {
91 return color != Color::Magenta;
92 }
93
94 [[nodiscard]] bool has_blue() const noexcept {
95 return color != Color::Yellow;
96 }
97
105 [[nodiscard]] vec pointAt(float const t) const noexcept {
106 switch (type) {
107 case Type::Linear: return bezierPointAt(P1, P2, t);
108 case Type::Quadratic: return bezierPointAt(P1, C1, P2, t);
109 case Type::Cubic: return bezierPointAt(P1, C1, C2, P2, t);
110 default: tt_no_default;
111 }
112 }
113
121 [[nodiscard]] vec tangentAt(float const t) const noexcept {
122 switch (type) {
123 case Type::Linear: return bezierTangentAt(P1, P2, t);
124 case Type::Quadratic: return bezierTangentAt(P1, C1, P2, t);
125 case Type::Cubic: return bezierTangentAt(P1, C1, C2, P2, t);
126 default: tt_no_default;
127 }
128 }
129
134 [[nodiscard]] results<float,3> solveXByY(float const y) const noexcept {
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(vec P) const noexcept {
144 switch (type) {
145 case Type::Linear: return bezierFindTForNormalsIntersectingPoint(P1, P2, P);
146 case Type::Quadratic: return bezierFindTForNormalsIntersectingPoint(P1, C1, P2, P);
147 case Type::Cubic: tt_no_default;
148 default: tt_no_default;
149 }
150 }
151
154 [[nodiscard]] float sdf_distance(vec P) const noexcept {
155 auto min_square_distance = std::numeric_limits<float>::max();
156 auto min_t = 0.0f;
157 auto min_normal = vec{0.0f, 1.0f};
158
159 ttlet ts = solveTForNormalsIntersectingPoint(P);
160 for (auto t: ts) {
161 t = std::clamp(t, 0.0f, 1.0f);
162
163 ttlet normal = P - pointAt(t);
164 ttlet square_distance = length_squared(normal);
165 if (square_distance < min_square_distance) {
166 min_square_distance = square_distance;
167 min_t = t;
168 min_normal = normal;
169 }
170 }
171
172 ttlet tangent = tangentAt(min_t);
173 ttlet distance = std::sqrt(min_square_distance);
174 ttlet sdistance = viktor_cross(tangent, min_normal) < 0.0 ? distance : -distance;
175 return sdistance;
176 }
177
183 [[nodiscard]] std::pair<BezierCurve,BezierCurve> cubicSplit(float const t) const noexcept {
184 ttlet outerA = BezierCurve{P1, C1};
185 ttlet outerBridge = BezierCurve{C1, C2};
186 ttlet outerB = BezierCurve{C2, P2};
187
188 ttlet innerA = BezierCurve{outerA.pointAt(t), outerBridge.pointAt(t)};
189 ttlet innerB = BezierCurve{outerBridge.pointAt(t), outerB.pointAt(t)};
190
191 ttlet newPoint = BezierCurve{innerA.pointAt(t), innerB.pointAt(t)}.pointAt(t);
192
193 return {{ P1, outerA.pointAt(t), innerA.pointAt(t), newPoint }, { newPoint, innerB.pointAt(t), outerB.pointAt(t), P2 }};
194 }
195
201 [[nodiscard]] std::pair<BezierCurve,BezierCurve> quadraticSplit(float const t) const noexcept {
202 ttlet outerA = BezierCurve{P1, C1};
203 ttlet outerB = BezierCurve{C1, P2};
204
205 ttlet newPoint = BezierCurve{outerA.pointAt(t), outerB.pointAt(t)}.pointAt(t);
206
207 return {{ P1, outerA.pointAt(t), newPoint }, { newPoint, outerB.pointAt(t), P2 }};
208 }
209
215 [[nodiscard]] std::pair<BezierCurve,BezierCurve> linearSplit(float const t) const noexcept {
216 ttlet newPoint = pointAt(t);
217
218 return {{ P1, newPoint }, { newPoint, P2 }};
219 }
220
226 [[nodiscard]] std::pair<BezierCurve,BezierCurve> split(float const t) const noexcept {
227 switch (type) {
228 case Type::Linear: return linearSplit(t);
229 case Type::Quadratic: return quadraticSplit(t);
230 case Type::Cubic: return cubicSplit(t);
231 default: tt_no_default;
232 }
233 }
234
239 void subdivideUntilFlat_impl(std::vector<BezierCurve> &r, float const minimumFlatness) const noexcept {
240 if (flatness() >= minimumFlatness) {
241 r.push_back(*this);
242 } else {
243 ttlet [a, b] = split(0.5f);
244 a.subdivideUntilFlat_impl(r, minimumFlatness);
245 b.subdivideUntilFlat_impl(r, minimumFlatness);
246 }
247 }
248
253 [[nodiscard]] std::vector<BezierCurve> subdivideUntilFlat(float const tolerance) const noexcept {
255 subdivideUntilFlat_impl(r, 1.0f - tolerance);
256 return r;
257 }
258
262 [[nodiscard]] float flatness() const noexcept {
263 switch (type) {
264 case Type::Linear: return bezierFlatness(P1, P2);
265 case Type::Quadratic: return bezierFlatness(P1, C1, P2);
266 case Type::Cubic: return bezierFlatness(P1, C1, C2, P2);
267 default: tt_no_default;
268 }
269 }
270
271 template<typename M, std::enable_if_t<is_mat_v<M>, int> = 0>
272 BezierCurve &operator*=(M const rhs) noexcept {
273 P1 = rhs * P1;
274 C1 = rhs * C1;
275 C2 = rhs * C2;
276 P2 = rhs * P2;
277 return *this;
278 }
279
284 [[nodiscard]] BezierCurve toParrallelLine(float const offset) const noexcept {
285 auto [newP1, newP2] = parrallelLine(P1, P2, offset);
286 return { newP1, newP2 };
287 }
288
289 [[nodiscard]] friend bool operator==(BezierCurve const &lhs, BezierCurve const &rhs) noexcept {
290 if (lhs.type != rhs.type) {
291 return false;
292 }
293 switch (lhs.type) {
294 case BezierCurve::Type::Linear:
295 return (lhs.P1 == rhs.P1) && (lhs.P2 == rhs.P2);
296 case BezierCurve::Type::Quadratic:
297 return (lhs.P1 == rhs.P1) && (lhs.C1 == rhs.C1) && (lhs.P2 == rhs.P2);
298 case BezierCurve::Type::Cubic:
299 return (lhs.P1 == rhs.P1) && (lhs.C1 == rhs.C1) && (lhs.C2 == rhs.C2) && (lhs.P2 == rhs.P2);
300 default:
301 tt_no_default;
302 }
303 }
304
305 template<typename M, std::enable_if_t<is_mat_v<M>, int> = 0>
306 [[nodiscard]] friend BezierCurve operator*(M const &lhs, BezierCurve const &rhs) noexcept {
307 return {
308 rhs.type,
309 lhs * rhs.P1,
310 lhs * rhs.C1,
311 lhs * rhs.C2,
312 lhs * rhs.P2
313 };
314 }
315
318 [[nodiscard]] friend BezierCurve operator~(BezierCurve const &rhs) noexcept {
319 return { rhs.type, rhs.P2, rhs.C2, rhs.C1, rhs.P1 };
320 }
321};
322
323
330[[nodiscard]] std::vector<BezierCurve> makeContourFromPoints(
331 std::vector<BezierPoint>::const_iterator first,
332 std::vector<BezierPoint>::const_iterator last) noexcept;
333
340[[nodiscard]] std::vector<BezierCurve> makeInverseContour(std::vector<BezierCurve> const &contour) noexcept;
341
352[[nodiscard]] std::vector<BezierCurve> makeParrallelContour(
353 std::vector<BezierCurve> const &contour,
354 float offset,
355 LineJoinStyle lineJoinStyle,
356 float tolerance) noexcept;
357
362void fill(PixelMap<uint8_t>& image, std::vector<BezierCurve> const& curves) noexcept;
363
368void fill(PixelMap<SDF8> &image, std::vector<BezierCurve> const &curves) noexcept;
369
370}
Definition BezierCurve.hpp:25
vec P2
Last point.
Definition BezierCurve.hpp:34
results< float, 3 > solveXByY(float const y) const noexcept
Definition BezierCurve.hpp:134
vec tangentAt(float const t) const noexcept
Definition BezierCurve.hpp:121
BezierCurve toParrallelLine(float const offset) const noexcept
Definition BezierCurve.hpp:284
vec C1
Control point.
Definition BezierCurve.hpp:32
std::pair< BezierCurve, BezierCurve > cubicSplit(float const t) const noexcept
Definition BezierCurve.hpp:183
vec C2
Control point.
Definition BezierCurve.hpp:33
float flatness() const noexcept
Definition BezierCurve.hpp:262
float sdf_distance(vec P) const noexcept
Find the distance from the point to the curve.
Definition BezierCurve.hpp:154
std::vector< BezierCurve > subdivideUntilFlat(float const tolerance) const noexcept
Definition BezierCurve.hpp:253
BezierCurve(vec const P1, vec const C1, vec const C2, vec const P2, Color color=Color::White) noexcept
Definition BezierCurve.hpp:58
BezierCurve(vec const P1, vec const C1, vec const P2, Color color=Color::White) noexcept
Definition BezierCurve.hpp:51
friend BezierCurve operator~(BezierCurve const &rhs) noexcept
Definition BezierCurve.hpp:318
vec P1
First point.
Definition BezierCurve.hpp:31
std::pair< BezierCurve, BezierCurve > split(float const t) const noexcept
Definition BezierCurve.hpp:226
std::pair< BezierCurve, BezierCurve > quadraticSplit(float const t) const noexcept
Definition BezierCurve.hpp:201
vec pointAt(float const t) const noexcept
Definition BezierCurve.hpp:105
BezierCurve(Type const type, vec const P1, vec const C1, vec const C2, vec const P2, Color color=Color::White) noexcept
Definition BezierCurve.hpp:65
void subdivideUntilFlat_impl(std::vector< BezierCurve > &r, float const minimumFlatness) const noexcept
Definition BezierCurve.hpp:239
std::pair< BezierCurve, BezierCurve > linearSplit(float const t) const noexcept
Definition BezierCurve.hpp:215
Definition polynomial.hpp:11
A 4D vector.
Definition vec.hpp:37
T max(T... args)
T sqrt(T... args)