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 "color/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 "numeric_array.hpp"
14#include "geometry/transform.hpp"
15#include <tuple>
16#include <limits>
17#include <algorithm>
18
19namespace tt {
20
21struct bezier_point;
22
23enum class LineJoinStyle { Bevel, Miter, Rounded };
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: tt_no_default();
108 }
109 }
110
118 [[nodiscard]] 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: tt_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: tt_no_default();
139 }
140 }
141
142 [[nodiscard]] 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: tt_no_default();
148 default: tt_no_default();
149 }
150 }
151
154 [[nodiscard]] float sdf_distance(point2 P) const noexcept
155 {
156 auto min_square_distance = std::numeric_limits<float>::max();
157 auto min_t = 0.0f;
158 auto min_normal = vector2{0.0f, 1.0f};
159
160 ttlet ts = solveTForNormalsIntersectingPoint(P);
161 for (auto t : ts) {
162 t = std::clamp(t, 0.0f, 1.0f);
163
164 ttlet normal = P - pointAt(t);
165 ttlet square_distance = squared_hypot(normal);
166 if (square_distance < min_square_distance) {
167 min_square_distance = square_distance;
168 min_t = t;
169 min_normal = normal;
170 }
171 }
172
173 ttlet tangent = tangentAt(min_t);
174 ttlet distance = std::sqrt(min_square_distance);
175 ttlet sdistance = cross(tangent, min_normal) < 0.0 ? distance : -distance;
176 return sdistance;
177 }
178
184 [[nodiscard]] std::pair<bezier_curve, bezier_curve> cubicSplit(float const t) const noexcept
185 {
186 ttlet outerA = bezier_curve{P1, C1};
187 ttlet outerBridge = bezier_curve{C1, C2};
188 ttlet outerB = bezier_curve{C2, P2};
189
190 ttlet innerA = bezier_curve{outerA.pointAt(t), outerBridge.pointAt(t)};
191 ttlet innerB = bezier_curve{outerBridge.pointAt(t), outerB.pointAt(t)};
192
193 ttlet newPoint = bezier_curve{innerA.pointAt(t), innerB.pointAt(t)}.pointAt(t);
194
195 return {{P1, outerA.pointAt(t), innerA.pointAt(t), newPoint}, {newPoint, innerB.pointAt(t), outerB.pointAt(t), P2}};
196 }
197
203 [[nodiscard]] std::pair<bezier_curve, bezier_curve> quadraticSplit(float const t) const noexcept
204 {
205 ttlet outerA = bezier_curve{P1, C1};
206 ttlet outerB = bezier_curve{C1, P2};
207
208 ttlet newPoint = bezier_curve{outerA.pointAt(t), outerB.pointAt(t)}.pointAt(t);
209
210 return {{P1, outerA.pointAt(t), newPoint}, {newPoint, outerB.pointAt(t), P2}};
211 }
212
218 [[nodiscard]] std::pair<bezier_curve, bezier_curve> linearSplit(float const t) const noexcept
219 {
220 ttlet newPoint = pointAt(t);
221
222 return {{P1, newPoint}, {newPoint, P2}};
223 }
224
230 [[nodiscard]] std::pair<bezier_curve, bezier_curve> split(float const t) const noexcept
231 {
232 switch (type) {
233 case Type::Linear: return linearSplit(t);
234 case Type::Quadratic: return quadraticSplit(t);
235 case Type::Cubic: return cubicSplit(t);
236 default: tt_no_default();
237 }
238 }
239
244 void subdivideUntilFlat_impl(std::vector<bezier_curve> &r, float const minimumFlatness) const noexcept
245 {
246 if (flatness() >= minimumFlatness) {
247 r.push_back(*this);
248 } else {
249 ttlet[a, b] = split(0.5f);
250 a.subdivideUntilFlat_impl(r, minimumFlatness);
251 b.subdivideUntilFlat_impl(r, minimumFlatness);
252 }
253 }
254
259 [[nodiscard]] std::vector<bezier_curve> subdivideUntilFlat(float const tolerance) const noexcept
260 {
262 subdivideUntilFlat_impl(r, 1.0f - tolerance);
263 return r;
264 }
265
269 [[nodiscard]] float flatness() const noexcept
270 {
271 switch (type) {
272 case Type::Linear: return bezierFlatness(P1, P2);
273 case Type::Quadratic: return bezierFlatness(P1, C1, P2);
274 case Type::Cubic: return bezierFlatness(P1, C1, C2, P2);
275 default: tt_no_default();
276 }
277 }
278
283 [[nodiscard]] bezier_curve toParrallelLine(float const offset) const noexcept
284 {
285 auto [newP1, newP2] = parrallelLine(P1, P2, offset);
286 return {newP1, newP2};
287 }
288
289 [[nodiscard]] friend bool operator==(bezier_curve const &lhs, bezier_curve const &rhs) noexcept
290 {
291 if (lhs.type != rhs.type) {
292 return false;
293 }
294 switch (lhs.type) {
295 case bezier_curve::Type::Linear: return (lhs.P1 == rhs.P1) && (lhs.P2 == rhs.P2);
296 case bezier_curve::Type::Quadratic: return (lhs.P1 == rhs.P1) && (lhs.C1 == rhs.C1) && (lhs.P2 == rhs.P2);
297 case bezier_curve::Type::Cubic:
298 return (lhs.P1 == rhs.P1) && (lhs.C1 == rhs.C1) && (lhs.C2 == rhs.C2) && (lhs.P2 == rhs.P2);
299 default: tt_no_default();
300 }
301 }
302
303 [[nodiscard]] friend bezier_curve operator*(geo::transformer<2> auto const &lhs, bezier_curve const &rhs) noexcept
304 {
305 return {rhs.type, lhs * rhs.P1, lhs * rhs.C1, lhs * rhs.C2, lhs * rhs.P2};
306 }
307
310 [[nodiscard]] friend bezier_curve operator~(bezier_curve const &rhs) noexcept
311 {
312 return {rhs.type, rhs.P2, rhs.C2, rhs.C1, rhs.P1};
313 }
314};
315
322[[nodiscard]] std::vector<bezier_curve>
323makeContourFromPoints(std::vector<bezier_point>::const_iterator first, std::vector<bezier_point>::const_iterator last) noexcept;
324
331[[nodiscard]] std::vector<bezier_curve> makeInverseContour(std::vector<bezier_curve> const &contour) noexcept;
332
343[[nodiscard]] std::vector<bezier_curve> makeParrallelContour(
344 std::vector<bezier_curve> const &contour,
345 float offset,
346 LineJoinStyle lineJoinStyle,
347 float tolerance) noexcept;
348
353void fill(pixel_map<uint8_t> &image, std::vector<bezier_curve> const &curves) noexcept;
354
359void fill(pixel_map<sdf_r8> &image, std::vector<bezier_curve> const &curves) noexcept;
360
361} // namespace tt
Definition bezier_curve.hpp:28
point2 C2
Control point.
Definition bezier_curve.hpp:36
vector2 tangentAt(float const t) const noexcept
Definition bezier_curve.hpp:118
float flatness() const noexcept
Definition bezier_curve.hpp:269
results< float, 3 > solveXByY(float const y) const noexcept
Definition bezier_curve.hpp:132
std::vector< bezier_curve > subdivideUntilFlat(float const tolerance) const noexcept
Definition bezier_curve.hpp:259
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 > cubicSplit(float const t) const noexcept
Definition bezier_curve.hpp:184
std::pair< bezier_curve, bezier_curve > split(float const t) const noexcept
Definition bezier_curve.hpp:230
friend bezier_curve operator~(bezier_curve const &rhs) noexcept
Definition bezier_curve.hpp:310
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 pointAt(float const t) const noexcept
Definition bezier_curve.hpp:101
bezier_curve(point2 const P1, point2 const C1, point2 const P2, Color color=Color::White) noexcept
Definition bezier_curve.hpp:54
bezier_curve toParrallelLine(float const offset) const noexcept
Definition bezier_curve.hpp:283
std::pair< bezier_curve, bezier_curve > linearSplit(float const t) const noexcept
Definition bezier_curve.hpp:218
void subdivideUntilFlat_impl(std::vector< bezier_curve > &r, float const minimumFlatness) const noexcept
Definition bezier_curve.hpp:244
point2 P1
First point.
Definition bezier_curve.hpp:34
point2 P2
Last point.
Definition bezier_curve.hpp:37
float sdf_distance(point2 P) const noexcept
Find the distance from the point to the curve.
Definition bezier_curve.hpp:154
std::pair< bezier_curve, bezier_curve > quadraticSplit(float const t) const noexcept
Definition bezier_curve.hpp:203
point2 C1
Control point.
Definition bezier_curve.hpp:35
This is a RGBA floating point color.
Definition color.hpp:39
A high-level geometric vector Part of the high-level vector, point, mat and color types.
Definition vector.hpp:20
Definition polynomial.hpp:14
T max(T... args)
T sqrt(T... args)