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 "rapid/sdf_r8.hpp"
8#include "pixel_map.hpp"
9#include "alignment.hpp"
10#include "math.hpp"
11#include "bezier.hpp"
12#include "utility.hpp"
13#include "geometry/vector.hpp"
14#include "geometry/point.hpp"
15#include "geometry/transform.hpp"
16#include "geometry/line_join_style.hpp"
17#include <tuple>
18#include <limits>
19#include <algorithm>
20
21namespace hi::inline v1 {
22
23struct bezier_point;
24
29 enum class Type : uint8_t { None, Linear, Quadratic, Cubic };
30 enum class Color : uint8_t { Yellow, Magenta, Cyan, White };
31
32 Type type;
33 Color color;
38
39 bezier_curve() noexcept = delete;
40 bezier_curve(bezier_curve const& other) noexcept = default;
41 bezier_curve(bezier_curve&& other) noexcept = default;
42 bezier_curve& operator=(bezier_curve const& other) noexcept = default;
43 bezier_curve& operator=(bezier_curve&& other) noexcept = default;
44
47 bezier_curve(point2 const P1, point2 const P2, Color color = Color::White) noexcept :
48 type(Type::Linear), color(color), P1(P1), C1(), C2(), P2(P2)
49 {
50 }
51
54 bezier_curve(point2 const P1, point2 const C1, point2 const P2, Color color = Color::White) noexcept :
55 type(Type::Quadratic), color(color), P1(P1), C1(C1), C2(), P2(P2)
56 {
57 }
58
61 bezier_curve(point2 const P1, point2 const C1, point2 const C2, point2 const P2, Color color = Color::White) noexcept :
62 type(Type::Cubic), color(color), P1(P1), C1(C1), C2(C2), P2(P2)
63 {
64 }
65
69 Type const type,
70 point2 const P1,
71 point2 const C1,
72 point2 const C2,
73 point2 const P2,
74 Color color = Color::White) noexcept :
75 type(type), color(color), P1(P1), C1(C1), C2(C2), P2(P2)
76 {
77 }
78
79 [[nodiscard]] bool has_red() const noexcept
80 {
81 return color != Color::Cyan;
82 }
83
84 [[nodiscard]] bool has_green() const noexcept
85 {
86 return color != Color::Magenta;
87 }
88
89 [[nodiscard]] bool has_blue() const noexcept
90 {
91 return color != Color::Yellow;
92 }
93
101 [[nodiscard]] point2 pointAt(float const t) const noexcept
102 {
103 switch (type) {
104 case Type::Linear: return bezierPointAt(P1, P2, t);
105 case Type::Quadratic: return bezierPointAt(P1, C1, P2, t);
106 case Type::Cubic: return bezierPointAt(P1, C1, C2, P2, t);
107 default: hi_no_default();
108 }
109 }
110
118 [[nodiscard]] constexpr vector2 tangentAt(float const t) const noexcept
119 {
120 switch (type) {
121 case Type::Linear: return bezierTangentAt(P1, P2, t);
122 case Type::Quadratic: return bezierTangentAt(P1, C1, P2, t);
123 case Type::Cubic: return bezierTangentAt(P1, C1, C2, P2, t);
124 default: hi_no_default();
125 }
126 }
127
132 [[nodiscard]] results<float, 3> solveXByY(float const y) const noexcept
133 {
134 switch (type) {
135 case Type::Linear: return bezierFindX(P1, P2, y);
136 case Type::Quadratic: return bezierFindX(P1, C1, P2, y);
137 case Type::Cubic: return bezierFindX(P1, C1, C2, P2, y);
138 default: hi_no_default();
139 }
140 }
141
142 [[nodiscard]] hi_force_inline results<float, 3> solveTForNormalsIntersectingPoint(point2 P) const noexcept
143 {
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: hi_no_default();
148 default: hi_no_default();
149 }
150 }
151
156
157 bezier_curve const *curve = nullptr;
158
161 float t = 0.0f;
162
165 float sq_distance = std::numeric_limits<float>::max();
166
167 constexpr sdf_distance_result() noexcept = default;
168 constexpr sdf_distance_result(sdf_distance_result const&) noexcept = default;
169 constexpr sdf_distance_result(sdf_distance_result&&) noexcept = default;
170 constexpr sdf_distance_result& operator=(sdf_distance_result const&) noexcept = default;
171 constexpr sdf_distance_result& operator=(sdf_distance_result&&) noexcept = default;
172 constexpr sdf_distance_result(bezier_curve const *curve) noexcept : curve(curve) {}
173
176 [[nodiscard]] hi_force_inline constexpr float orthogonality() const noexcept
177 {
178 hilet tangent = curve->tangentAt(t);
179 return cross(normalize(tangent), normalize(PN));
180 };
181
182 [[nodiscard]] hi_force_inline float distance() const noexcept
183 {
184 return std::sqrt(sq_distance);
185 }
186
187 [[nodiscard]] hi_force_inline float signed_distance() const noexcept
188 {
189 hilet d = distance();
190 return orthogonality() < 0.0 ? d : -d;
191 }
192
193 [[nodiscard]] hi_force_inline constexpr bool operator<(sdf_distance_result const& rhs) const noexcept
194 {
195 if (abs(sq_distance - rhs.sq_distance) < 0.01f) {
196 return abs(orthogonality()) > abs(rhs.orthogonality());
197 } else {
198 return sq_distance < rhs.sq_distance;
199 }
200 }
201 };
202
211 [[nodiscard]] sdf_distance_result sdf_distance(point2 P) const noexcept
212 {
213 auto nearest = sdf_distance_result{this};
214
215 hilet ts = solveTForNormalsIntersectingPoint(P);
216 for (auto t : ts) {
217 t = std::clamp(t, 0.0f, 1.0f);
218
219 hilet PN = P - pointAt(t);
220 hilet sq_distance = squared_hypot(PN);
221 if (sq_distance < nearest.sq_distance) {
222 nearest.t = t;
223 nearest.PN = PN;
224 nearest.sq_distance = sq_distance;
225 }
226 }
227
228 return nearest;
229 }
230
236 [[nodiscard]] std::pair<bezier_curve, bezier_curve> cubicSplit(float const t) const noexcept
237 {
238 hilet outerA = bezier_curve{P1, C1};
239 hilet outerBridge = bezier_curve{C1, C2};
240 hilet outerB = bezier_curve{C2, P2};
241
242 hilet innerA = bezier_curve{outerA.pointAt(t), outerBridge.pointAt(t)};
243 hilet innerB = bezier_curve{outerBridge.pointAt(t), outerB.pointAt(t)};
244
245 hilet newPoint = bezier_curve{innerA.pointAt(t), innerB.pointAt(t)}.pointAt(t);
246
247 return {{P1, outerA.pointAt(t), innerA.pointAt(t), newPoint}, {newPoint, innerB.pointAt(t), outerB.pointAt(t), P2}};
248 }
249
255 [[nodiscard]] std::pair<bezier_curve, bezier_curve> quadraticSplit(float const t) const noexcept
256 {
257 hilet outerA = bezier_curve{P1, C1};
258 hilet outerB = bezier_curve{C1, P2};
259
260 hilet newPoint = bezier_curve{outerA.pointAt(t), outerB.pointAt(t)}.pointAt(t);
261
262 return {{P1, outerA.pointAt(t), newPoint}, {newPoint, outerB.pointAt(t), P2}};
263 }
264
270 [[nodiscard]] std::pair<bezier_curve, bezier_curve> linearSplit(float const t) const noexcept
271 {
272 hilet newPoint = pointAt(t);
273
274 return {{P1, newPoint}, {newPoint, P2}};
275 }
276
282 [[nodiscard]] std::pair<bezier_curve, bezier_curve> split(float const t) const noexcept
283 {
284 switch (type) {
285 case Type::Linear: return linearSplit(t);
286 case Type::Quadratic: return quadraticSplit(t);
287 case Type::Cubic: return cubicSplit(t);
288 default: hi_no_default();
289 }
290 }
291
296 void subdivideUntilFlat_impl(std::vector<bezier_curve>& r, float const minimumFlatness) const noexcept
297 {
298 if (flatness() >= minimumFlatness) {
299 r.push_back(*this);
300 } else {
301 hilet[a, b] = split(0.5f);
302 a.subdivideUntilFlat_impl(r, minimumFlatness);
303 b.subdivideUntilFlat_impl(r, minimumFlatness);
304 }
305 }
306
311 [[nodiscard]] std::vector<bezier_curve> subdivideUntilFlat(float const tolerance) const noexcept
312 {
314 subdivideUntilFlat_impl(r, 1.0f - tolerance);
315 return r;
316 }
317
321 [[nodiscard]] float flatness() const noexcept
322 {
323 switch (type) {
324 case Type::Linear: return bezierFlatness(P1, P2);
325 case Type::Quadratic: return bezierFlatness(P1, C1, P2);
326 case Type::Cubic: return bezierFlatness(P1, C1, C2, P2);
327 default: hi_no_default();
328 }
329 }
330
335 [[nodiscard]] bezier_curve toParallelLine(float const offset) const noexcept
336 {
337 hilet[newP1, newP2] = parallelLine(P1, P2, offset);
338 return {newP1, newP2};
339 }
340
341 [[nodiscard]] friend bool operator==(bezier_curve const& lhs, bezier_curve const& rhs) noexcept
342 {
343 if (lhs.type != rhs.type) {
344 return false;
345 }
346 switch (lhs.type) {
347 case bezier_curve::Type::Linear: return (lhs.P1 == rhs.P1) && (lhs.P2 == rhs.P2);
348 case bezier_curve::Type::Quadratic: return (lhs.P1 == rhs.P1) && (lhs.C1 == rhs.C1) && (lhs.P2 == rhs.P2);
349 case bezier_curve::Type::Cubic:
350 return (lhs.P1 == rhs.P1) && (lhs.C1 == rhs.C1) && (lhs.C2 == rhs.C2) && (lhs.P2 == rhs.P2);
351 default: hi_no_default();
352 }
353 }
354
355 [[nodiscard]] friend bezier_curve operator*(geo::transformer auto const& lhs, bezier_curve const& rhs) noexcept
356 {
357 return {rhs.type, lhs * rhs.P1, lhs * rhs.C1, lhs * rhs.C2, lhs * rhs.P2};
358 }
359
362 [[nodiscard]] friend bezier_curve operator~(bezier_curve const& rhs) noexcept
363 {
364 return {rhs.type, rhs.P2, rhs.C2, rhs.C1, rhs.P1};
365 }
366};
367
374[[nodiscard]] std::vector<bezier_curve>
375makeContourFromPoints(std::vector<bezier_point>::const_iterator first, std::vector<bezier_point>::const_iterator last) noexcept;
376
384
396 std::vector<bezier_curve> const& contour,
397 float offset,
398 hi::line_join_style line_join_style,
399 float tolerance) noexcept;
400
405void fill(pixel_map<uint8_t>& image, std::vector<bezier_curve> const& curves) noexcept;
406
411void fill(pixel_map<sdf_r8>& image, std::vector<bezier_curve> const& curves) noexcept;
412
413} // namespace hi::inline v1
#define hi_no_default()
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:145
Miscellaneous math functions.
Utilities used by the HikoGUI library itself.
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:15
constexpr results< float, 1 > bezierFindX(point2 P1, point2 P2, float y) noexcept
Definition bezier.hpp:147
line_join_style
The way two lines should be joined.
Definition line_join_style.hpp:12
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:214
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:107
std::vector< bezier_curve > makeInverseContour(std::vector< bezier_curve > const &contour) noexcept
Definition bezier_curve.hpp:28
results< float, 3 > solveXByY(float const y) const noexcept
Definition bezier_curve.hpp:132
std::pair< bezier_curve, bezier_curve > quadraticSplit(float const t) const noexcept
Definition bezier_curve.hpp:255
point2 P2
Last point.
Definition bezier_curve.hpp:37
point2 P1
First point.
Definition bezier_curve.hpp:34
float flatness() const noexcept
Definition bezier_curve.hpp:321
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:68
point2 C1
Control point.
Definition bezier_curve.hpp:35
bezier_curve(point2 const P1, point2 const C1, point2 const P2, Color color=Color::White) noexcept
Definition bezier_curve.hpp:54
std::pair< bezier_curve, bezier_curve > cubicSplit(float const t) const noexcept
Definition bezier_curve.hpp:236
std::vector< bezier_curve > subdivideUntilFlat(float const tolerance) const noexcept
Definition bezier_curve.hpp:311
bezier_curve toParallelLine(float const offset) const noexcept
Definition bezier_curve.hpp:335
void subdivideUntilFlat_impl(std::vector< bezier_curve > &r, float const minimumFlatness) const noexcept
Definition bezier_curve.hpp:296
std::pair< bezier_curve, bezier_curve > linearSplit(float const t) const noexcept
Definition bezier_curve.hpp:270
friend bezier_curve operator~(bezier_curve const &rhs) noexcept
Definition bezier_curve.hpp:362
point2 pointAt(float const t) const noexcept
Definition bezier_curve.hpp:101
sdf_distance_result sdf_distance(point2 P) const noexcept
Find the distance from the point to the curve.
Definition bezier_curve.hpp:211
bezier_curve(point2 const P1, point2 const C1, point2 const C2, point2 const P2, Color color=Color::White) noexcept
Definition bezier_curve.hpp:61
std::pair< bezier_curve, bezier_curve > split(float const t) const noexcept
Definition bezier_curve.hpp:282
point2 C2
Control point.
Definition bezier_curve.hpp:36
constexpr vector2 tangentAt(float const t) const noexcept
Definition bezier_curve.hpp:118
Definition bezier_curve.hpp:152
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:176
vector2 PN
The vector between P and N.
Definition bezier_curve.hpp:155
This is a RGBA floating point color.
Definition color.hpp:39
A 2D canvas of pixels.
Definition pixel_map.hpp:111
Definition polynomial.hpp:14
T max(T... args)
T sqrt(T... args)