HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
bezier_curve.hpp
1// Copyright Take Vos 2019-2022.
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 "image/module.hpp"
8#include "geometry/module.hpp"
9#include "utility/module.hpp"
10#include "bezier.hpp"
11#include <tuple>
12#include <limits>
13#include <algorithm>
14
15namespace hi::inline v1 {
16
17struct bezier_point;
18
23 enum class Type : uint8_t { None, Linear, Quadratic, Cubic };
24 enum class Color : uint8_t { Yellow, Magenta, Cyan, White };
25
26 Type type;
27 Color color;
32
33 bezier_curve() noexcept = delete;
34 bezier_curve(bezier_curve const& other) noexcept = default;
35 bezier_curve(bezier_curve&& other) noexcept = default;
36 bezier_curve& operator=(bezier_curve const& other) noexcept = default;
37 bezier_curve& operator=(bezier_curve&& other) noexcept = default;
38
41 bezier_curve(point2 const P1, point2 const P2, Color color = Color::White) noexcept :
42 type(Type::Linear), color(color), P1(P1), C1(), C2(), P2(P2)
43 {
44 }
45
48 bezier_curve(point2 const P1, point2 const C1, point2 const P2, Color color = Color::White) noexcept :
49 type(Type::Quadratic), color(color), P1(P1), C1(C1), C2(), P2(P2)
50 {
51 }
52
55 bezier_curve(point2 const P1, point2 const C1, point2 const C2, point2 const P2, Color color = Color::White) noexcept :
56 type(Type::Cubic), color(color), P1(P1), C1(C1), C2(C2), P2(P2)
57 {
58 }
59
63 Type const type,
64 point2 const P1,
65 point2 const C1,
66 point2 const C2,
67 point2 const P2,
68 Color color = Color::White) noexcept :
69 type(type), color(color), P1(P1), C1(C1), C2(C2), P2(P2)
70 {
71 }
72
73 [[nodiscard]] bool has_red() const noexcept
74 {
75 return color != Color::Cyan;
76 }
77
78 [[nodiscard]] bool has_green() const noexcept
79 {
80 return color != Color::Magenta;
81 }
82
83 [[nodiscard]] bool has_blue() const noexcept
84 {
85 return color != Color::Yellow;
86 }
87
95 [[nodiscard]] point2 pointAt(float const t) const noexcept
96 {
97 switch (type) {
98 case Type::Linear: return bezierPointAt(P1, P2, t);
99 case Type::Quadratic: return bezierPointAt(P1, C1, P2, t);
100 case Type::Cubic: return bezierPointAt(P1, C1, C2, P2, t);
101 default: hi_no_default();
102 }
103 }
104
112 [[nodiscard]] constexpr vector2 tangentAt(float const t) const noexcept
113 {
114 switch (type) {
115 case Type::Linear: return bezierTangentAt(P1, P2, t);
116 case Type::Quadratic: return bezierTangentAt(P1, C1, P2, t);
117 case Type::Cubic: return bezierTangentAt(P1, C1, C2, P2, t);
118 default: hi_no_default();
119 }
120 }
121
126 [[nodiscard]] results<float, 3> solveXByY(float const y) const noexcept
127 {
128 switch (type) {
129 case Type::Linear: return bezierFindX(P1, P2, y);
130 case Type::Quadratic: return bezierFindX(P1, C1, P2, y);
131 case Type::Cubic: return bezierFindX(P1, C1, C2, P2, y);
132 default: hi_no_default();
133 }
134 }
135
136 [[nodiscard]] hi_force_inline results<float, 3> solveTForNormalsIntersectingPoint(point2 P) const noexcept
137 {
138 switch (type) {
139 case Type::Linear: return bezierFindTForNormalsIntersectingPoint(P1, P2, P);
140 case Type::Quadratic: return bezierFindTForNormalsIntersectingPoint(P1, C1, P2, P);
141 case Type::Cubic: hi_no_default();
142 default: hi_no_default();
143 }
144 }
145
150
151 bezier_curve const *curve = nullptr;
152
155 float t = 0.0f;
156
159 float sq_distance = std::numeric_limits<float>::max();
160
161 constexpr sdf_distance_result() noexcept = default;
162 constexpr sdf_distance_result(sdf_distance_result const&) noexcept = default;
163 constexpr sdf_distance_result(sdf_distance_result&&) noexcept = default;
164 constexpr sdf_distance_result& operator=(sdf_distance_result const&) noexcept = default;
165 constexpr sdf_distance_result& operator=(sdf_distance_result&&) noexcept = default;
166 constexpr sdf_distance_result(bezier_curve const *curve) noexcept : curve(curve) {}
167
170 [[nodiscard]] hi_force_inline constexpr float orthogonality() const noexcept
171 {
172 hilet tangent = curve->tangentAt(t);
173 return cross(normalize(tangent), normalize(PN));
174 };
175
176 [[nodiscard]] hi_force_inline float distance() const noexcept
177 {
178 return std::sqrt(sq_distance);
179 }
180
181 [[nodiscard]] hi_force_inline float signed_distance() const noexcept
182 {
183 hilet d = distance();
184 return orthogonality() < 0.0 ? d : -d;
185 }
186
187 [[nodiscard]] hi_force_inline constexpr bool operator<(sdf_distance_result const& rhs) const noexcept
188 {
189 if (abs(sq_distance - rhs.sq_distance) < 0.01f) {
190 return abs(orthogonality()) > abs(rhs.orthogonality());
191 } else {
192 return sq_distance < rhs.sq_distance;
193 }
194 }
195 };
196
205 [[nodiscard]] sdf_distance_result sdf_distance(point2 P) const noexcept
206 {
207 auto nearest = sdf_distance_result{this};
208
209 hilet ts = solveTForNormalsIntersectingPoint(P);
210 for (auto t : ts) {
211 t = std::clamp(t, 0.0f, 1.0f);
212
213 hilet PN = P - pointAt(t);
214 hilet sq_distance = squared_hypot(PN);
215 if (sq_distance < nearest.sq_distance) {
216 nearest.t = t;
217 nearest.PN = PN;
218 nearest.sq_distance = sq_distance;
219 }
220 }
221
222 return nearest;
223 }
224
230 [[nodiscard]] std::pair<bezier_curve, bezier_curve> cubicSplit(float const t) const noexcept
231 {
232 hilet outerA = bezier_curve{P1, C1};
233 hilet outerBridge = bezier_curve{C1, C2};
234 hilet outerB = bezier_curve{C2, P2};
235
236 hilet innerA = bezier_curve{outerA.pointAt(t), outerBridge.pointAt(t)};
237 hilet innerB = bezier_curve{outerBridge.pointAt(t), outerB.pointAt(t)};
238
239 hilet newPoint = bezier_curve{innerA.pointAt(t), innerB.pointAt(t)}.pointAt(t);
240
241 return {{P1, outerA.pointAt(t), innerA.pointAt(t), newPoint}, {newPoint, innerB.pointAt(t), outerB.pointAt(t), P2}};
242 }
243
249 [[nodiscard]] std::pair<bezier_curve, bezier_curve> quadraticSplit(float const t) const noexcept
250 {
251 hilet outerA = bezier_curve{P1, C1};
252 hilet outerB = bezier_curve{C1, P2};
253
254 hilet newPoint = bezier_curve{outerA.pointAt(t), outerB.pointAt(t)}.pointAt(t);
255
256 return {{P1, outerA.pointAt(t), newPoint}, {newPoint, outerB.pointAt(t), P2}};
257 }
258
264 [[nodiscard]] std::pair<bezier_curve, bezier_curve> linearSplit(float const t) const noexcept
265 {
266 hilet newPoint = pointAt(t);
267
268 return {{P1, newPoint}, {newPoint, P2}};
269 }
270
276 [[nodiscard]] std::pair<bezier_curve, bezier_curve> split(float const t) const noexcept
277 {
278 switch (type) {
279 case Type::Linear: return linearSplit(t);
280 case Type::Quadratic: return quadraticSplit(t);
281 case Type::Cubic: return cubicSplit(t);
282 default: hi_no_default();
283 }
284 }
285
290 void subdivideUntilFlat_impl(std::vector<bezier_curve>& r, float const minimumFlatness) const noexcept
291 {
292 if (flatness() >= minimumFlatness) {
293 r.push_back(*this);
294 } else {
295 hilet[a, b] = split(0.5f);
296 a.subdivideUntilFlat_impl(r, minimumFlatness);
297 b.subdivideUntilFlat_impl(r, minimumFlatness);
298 }
299 }
300
305 [[nodiscard]] std::vector<bezier_curve> subdivideUntilFlat(float const tolerance) const noexcept
306 {
308 subdivideUntilFlat_impl(r, 1.0f - tolerance);
309 return r;
310 }
311
315 [[nodiscard]] float flatness() const noexcept
316 {
317 switch (type) {
318 case Type::Linear: return bezierFlatness(P1, P2);
319 case Type::Quadratic: return bezierFlatness(P1, C1, P2);
320 case Type::Cubic: return bezierFlatness(P1, C1, C2, P2);
321 default: hi_no_default();
322 }
323 }
324
329 [[nodiscard]] bezier_curve toParallelLine(float const offset) const noexcept
330 {
331 hilet[newP1, newP2] = parallelLine(P1, P2, offset);
332 return {newP1, newP2};
333 }
334
335 [[nodiscard]] friend bool operator==(bezier_curve const& lhs, bezier_curve const& rhs) noexcept
336 {
337 if (lhs.type != rhs.type) {
338 return false;
339 }
340 switch (lhs.type) {
341 case bezier_curve::Type::Linear: return (lhs.P1 == rhs.P1) && (lhs.P2 == rhs.P2);
342 case bezier_curve::Type::Quadratic: return (lhs.P1 == rhs.P1) && (lhs.C1 == rhs.C1) && (lhs.P2 == rhs.P2);
343 case bezier_curve::Type::Cubic:
344 return (lhs.P1 == rhs.P1) && (lhs.C1 == rhs.C1) && (lhs.C2 == rhs.C2) && (lhs.P2 == rhs.P2);
345 default: hi_no_default();
346 }
347 }
348
349 [[nodiscard]] friend bezier_curve operator*(geo::transformer auto const& lhs, bezier_curve const& rhs) noexcept
350 {
351 return {rhs.type, lhs * rhs.P1, lhs * rhs.C1, lhs * rhs.C2, lhs * rhs.P2};
352 }
353
356 [[nodiscard]] friend bezier_curve operator~(bezier_curve const& rhs) noexcept
357 {
358 return {rhs.type, rhs.P2, rhs.C2, rhs.C1, rhs.P1};
359 }
360};
361
368[[nodiscard]] std::vector<bezier_curve>
369makeContourFromPoints(std::vector<bezier_point>::const_iterator first, std::vector<bezier_point>::const_iterator last) noexcept;
370
378
390 std::vector<bezier_curve> const& contour,
391 float offset,
393 float tolerance) noexcept;
394
399void fill(pixmap_span<uint8_t> image, std::vector<bezier_curve> const& curves) noexcept;
400
405void fill(pixmap_span<sdf_r8> image, std::vector<bezier_curve> const& curves) noexcept;
406
407} // namespace hi::inline v1
#define hi_no_default(...)
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:279
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
line_join_style
The way two lines should be joined.
Definition line_join_style.hpp:18
DOXYGEN BUG.
Definition algorithm.hpp:13
constexpr results< float, 1 > bezierFindX(point2 P1, point2 P2, float y) noexcept
Definition bezier.hpp:159
std::vector< bezier_curve > makeContourFromPoints(std::vector< bezier_point >::const_iterator first, std::vector< bezier_point >::const_iterator last) noexcept
std::vector< bezier_curve > makeParallelContour(std::vector< bezier_curve > const &contour, float offset, hi::line_join_style line_join_style, float tolerance) noexcept
float bezierFlatness(point2 P1, point2 P2) noexcept
Definition bezier.hpp:226
hi_force_inline constexpr results< float, 1 > bezierFindTForNormalsIntersectingPoint(point2 P1, point2 P2, point2 P) noexcept
Find t on the line P1->P2 which is closest to P.
Definition bezier.hpp:119
std::vector< bezier_curve > makeInverseContour(std::vector< bezier_curve > const &contour) noexcept
Definition bezier_curve.hpp:22
results< float, 3 > solveXByY(float const y) const noexcept
Definition bezier_curve.hpp:126
std::pair< bezier_curve, bezier_curve > quadraticSplit(float const t) const noexcept
Definition bezier_curve.hpp:249
point2 P2
Last point.
Definition bezier_curve.hpp:31
point2 P1
First point.
Definition bezier_curve.hpp:28
float flatness() const noexcept
Definition bezier_curve.hpp:315
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:62
point2 C1
Control point.
Definition bezier_curve.hpp:29
bezier_curve(point2 const P1, point2 const C1, point2 const P2, Color color=Color::White) noexcept
Definition bezier_curve.hpp:48
std::pair< bezier_curve, bezier_curve > cubicSplit(float const t) const noexcept
Definition bezier_curve.hpp:230
std::vector< bezier_curve > subdivideUntilFlat(float const tolerance) const noexcept
Definition bezier_curve.hpp:305
bezier_curve toParallelLine(float const offset) const noexcept
Definition bezier_curve.hpp:329
void subdivideUntilFlat_impl(std::vector< bezier_curve > &r, float const minimumFlatness) const noexcept
Definition bezier_curve.hpp:290
std::pair< bezier_curve, bezier_curve > linearSplit(float const t) const noexcept
Definition bezier_curve.hpp:264
friend bezier_curve operator~(bezier_curve const &rhs) noexcept
Definition bezier_curve.hpp:356
point2 pointAt(float const t) const noexcept
Definition bezier_curve.hpp:95
sdf_distance_result sdf_distance(point2 P) const noexcept
Find the distance from the point to the curve.
Definition bezier_curve.hpp:205
bezier_curve(point2 const P1, point2 const C1, point2 const C2, point2 const P2, Color color=Color::White) noexcept
Definition bezier_curve.hpp:55
std::pair< bezier_curve, bezier_curve > split(float const t) const noexcept
Definition bezier_curve.hpp:276
point2 C2
Control point.
Definition bezier_curve.hpp:30
constexpr vector2 tangentAt(float const t) const noexcept
Definition bezier_curve.hpp:112
Definition bezier_curve.hpp:146
hi_force_inline constexpr float orthogonality() const noexcept
The orthogonality of the line PN and the tangent of the curve at N.
Definition bezier_curve.hpp:170
vector2 PN
The vector between P and N.
Definition bezier_curve.hpp:149
Definition graphic_path.hpp:18
Definition polynomial.hpp:14
T max(T... args)
T sqrt(T... args)