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/image.hpp"
8#include "../geometry/geometry.hpp"
9#include "../container/container.hpp"
10#include "../numeric/numeric.hpp"
11#include "../utility/utility.hpp"
12#include "bezier.hpp"
13#include "bezier_point.hpp"
14#include "../macros.hpp"
15#include <tuple>
16#include <limits>
17#include <algorithm>
18#include <vector>
19#include <cmath>
20#include <span>
21
22hi_warning_push();
23// warning C4459: declaration of 'point' hides global declaration
24// The unit library has a 'point' and 'points' global variables.
25hi_warning_ignore_msvc(4459);
26
27hi_export_module(hikogui.graphic_path.bezier_curve);
28
29hi_export namespace hi { inline namespace v1 {
30
34hi_export struct bezier_curve {
35 enum class Type : uint8_t { None, Linear, Quadratic, Cubic };
36
37 Type type;
38 point2 P1;
39 point2 C1;
40 point2 C2;
41 point2 P2;
42
43 bezier_curve() noexcept = delete;
44 bezier_curve(bezier_curve const& other) noexcept = default;
45 bezier_curve(bezier_curve&& other) noexcept = default;
46 bezier_curve& operator=(bezier_curve const& other) noexcept = default;
47 bezier_curve& operator=(bezier_curve&& other) noexcept = default;
48
51 bezier_curve(point2 const P1, point2 const P2) noexcept : type(Type::Linear), P1(P1), C1(), C2(), P2(P2) {}
52
55 bezier_curve(point2 const P1, point2 const C1, point2 const P2) noexcept : type(Type::Quadratic), 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) noexcept :
62 type(Type::Cubic), P1(P1), C1(C1), C2(C2), P2(P2)
63 {
64 }
65
68 bezier_curve(Type const type, point2 const P1, point2 const C1, point2 const C2, point2 const P2) noexcept :
69 type(type), P1(P1), C1(C1), C2(C2), P2(P2)
70 {
71 }
72
80 [[nodiscard]] point2 pointAt(float const t) const noexcept
81 {
82 switch (type) {
83 case Type::Linear:
84 return bezierPointAt(P1, P2, t);
85 case Type::Quadratic:
86 return bezierPointAt(P1, C1, P2, t);
87 case Type::Cubic:
88 return bezierPointAt(P1, C1, C2, P2, t);
89 default:
90 hi_no_default();
91 }
92 }
93
101 [[nodiscard]] constexpr vector2 tangentAt(float const t) const noexcept
102 {
103 switch (type) {
104 case Type::Linear:
105 return bezierTangentAt(P1, P2, t);
106 case Type::Quadratic:
107 return bezierTangentAt(P1, C1, P2, t);
108 case Type::Cubic:
109 return bezierTangentAt(P1, C1, C2, P2, t);
110 default:
111 hi_no_default();
112 }
113 }
114
119 [[nodiscard]] lean_vector<float> solveXByY(float const y) const noexcept
120 {
121 switch (type) {
122 case Type::Linear:
123 return bezierFindX(P1, P2, y);
124 case Type::Quadratic:
125 return bezierFindX(P1, C1, P2, y);
126 case Type::Cubic:
127 return bezierFindX(P1, C1, C2, P2, y);
128 default:
129 hi_no_default();
130 }
131 }
132
133 [[nodiscard]] hi_force_inline lean_vector<float> solveTForNormalsIntersectingPoint(point2 P) const noexcept
134 {
135 switch (type) {
136 case Type::Linear:
138 case Type::Quadratic:
140 case Type::Cubic:
141 hi_no_default();
142 default:
143 hi_no_default();
144 }
145 }
146
147 struct sdf_distance_result {
151
152 bezier_curve const *curve = nullptr;
153
156 float t = 0.0f;
157
161
162 constexpr sdf_distance_result() noexcept = default;
163 constexpr sdf_distance_result(sdf_distance_result const&) noexcept = default;
164 constexpr sdf_distance_result(sdf_distance_result&&) noexcept = default;
165 constexpr sdf_distance_result& operator=(sdf_distance_result const&) noexcept = default;
166 constexpr sdf_distance_result& operator=(sdf_distance_result&&) noexcept = default;
167 constexpr sdf_distance_result(bezier_curve const *curve) noexcept : curve(curve) {}
168
171 [[nodiscard]] hi_force_inline constexpr float orthogonality() const noexcept
172 {
173 if (sq_distance == 0.0f) {
174 // If the line PN is zero length it means the point is on the curve
175 // The orthogonality of a point on a line does not mean anything.
176 return 0.0f;
177 }
178
179 auto const tangent = curve->tangentAt(t);
180 if (tangent == vector2{}) {
181 // The tangent can be a zero vector if the points or control points
182 // of the curve lie to top of each other. This may happen
183 // when the curve is not accurately represented or a bug.
184 return 0.0f;
185 }
186
187 return cross(normalize(tangent), normalize(PN));
188 };
189
190 [[nodiscard]] hi_force_inline float distance() const noexcept
191 {
192 return std::sqrt(sq_distance);
193 }
194
195 [[nodiscard]] hi_force_inline float signed_distance() const noexcept
196 {
197 auto const d = distance();
198 return orthogonality() < 0.0 ? d : -d;
199 }
200
201 [[nodiscard]] hi_force_inline constexpr bool operator<(sdf_distance_result const& rhs) const noexcept
202 {
203 if (abs(sq_distance - rhs.sq_distance) < 0.01f) {
204 // The point is very close to both curves, meaning they
205 // are close the end/corner of both curve-segments.
206 // Here we create a line through the corner where both curves
207 // meet and determine on what side of the line our point lies.
208 // We can do this by calculating the orthogonality with the
209 // tangent of the curve at the corner.
210 return abs(orthogonality()) > abs(rhs.orthogonality());
211
212 } else {
213 // The simple case, just get the nearest curve.
214 return sq_distance < rhs.sq_distance;
215 }
216 }
217 };
218
227 [[nodiscard]] sdf_distance_result sdf_distance(point2 P) const noexcept
228 {
229 auto nearest = sdf_distance_result{this};
230
231 auto const ts = solveTForNormalsIntersectingPoint(P);
232 for (auto t : ts) {
233 t = std::clamp(t, 0.0f, 1.0f);
234
235 auto const PN = P - pointAt(t);
236 auto const sq_distance = squared_hypot(PN);
237 if (sq_distance < nearest.sq_distance) {
238 nearest.t = t;
239 nearest.PN = PN;
240 nearest.sq_distance = sq_distance;
241 }
242 }
243
244 return nearest;
245 }
246
252 [[nodiscard]] std::pair<bezier_curve, bezier_curve> cubicSplit(float const t) const noexcept
253 {
254 auto const outerA = bezier_curve{P1, C1};
255 auto const outerBridge = bezier_curve{C1, C2};
256 auto const outerB = bezier_curve{C2, P2};
257
258 auto const innerA = bezier_curve{outerA.pointAt(t), outerBridge.pointAt(t)};
259 auto const innerB = bezier_curve{outerBridge.pointAt(t), outerB.pointAt(t)};
260
261 auto const newPoint = bezier_curve{innerA.pointAt(t), innerB.pointAt(t)}.pointAt(t);
262
263 return {{P1, outerA.pointAt(t), innerA.pointAt(t), newPoint}, {newPoint, innerB.pointAt(t), outerB.pointAt(t), P2}};
264 }
265
271 [[nodiscard]] std::pair<bezier_curve, bezier_curve> quadraticSplit(float const t) const noexcept
272 {
273 auto const outerA = bezier_curve{P1, C1};
274 auto const outerB = bezier_curve{C1, P2};
275
276 auto const newPoint = bezier_curve{outerA.pointAt(t), outerB.pointAt(t)}.pointAt(t);
277
278 return {{P1, outerA.pointAt(t), newPoint}, {newPoint, outerB.pointAt(t), P2}};
279 }
280
286 [[nodiscard]] std::pair<bezier_curve, bezier_curve> linearSplit(float const t) const noexcept
287 {
288 auto const newPoint = pointAt(t);
289
290 return {{P1, newPoint}, {newPoint, P2}};
291 }
292
298 [[nodiscard]] std::pair<bezier_curve, bezier_curve> split(float const t) const noexcept
299 {
300 switch (type) {
301 case Type::Linear:
302 return linearSplit(t);
303 case Type::Quadratic:
304 return quadraticSplit(t);
305 case Type::Cubic:
306 return cubicSplit(t);
307 default:
308 hi_no_default();
309 }
310 }
311
316 void subdivideUntilFlat_impl(std::vector<bezier_curve>& r, float const minimumFlatness) const noexcept
317 {
318 if (flatness() >= minimumFlatness) {
319 r.push_back(*this);
320 } else {
321 auto const[a, b] = split(0.5f);
322 a.subdivideUntilFlat_impl(r, minimumFlatness);
323 b.subdivideUntilFlat_impl(r, minimumFlatness);
324 }
325 }
326
331 [[nodiscard]] std::vector<bezier_curve> subdivideUntilFlat(float const tolerance) const noexcept
332 {
334 subdivideUntilFlat_impl(r, 1.0f - tolerance);
335 return r;
336 }
337
341 [[nodiscard]] float flatness() const noexcept
342 {
343 switch (type) {
344 case Type::Linear:
345 return bezierFlatness(P1, P2);
346 case Type::Quadratic:
347 return bezierFlatness(P1, C1, P2);
348 case Type::Cubic:
349 return bezierFlatness(P1, C1, C2, P2);
350 default:
351 hi_no_default();
352 }
353 }
354
359 [[nodiscard]] bezier_curve toParallelLine(float const offset) const noexcept
360 {
361 auto const[newP1, newP2] = parallelLine(P1, P2, offset);
362 return {newP1, newP2};
363 }
364
365 [[nodiscard]] friend bool operator==(bezier_curve const& lhs, bezier_curve const& rhs) noexcept
366 {
367 if (lhs.type != rhs.type) {
368 return false;
369 }
370 switch (lhs.type) {
371 case bezier_curve::Type::Linear:
372 return (lhs.P1 == rhs.P1) && (lhs.P2 == rhs.P2);
373 case bezier_curve::Type::Quadratic:
374 return (lhs.P1 == rhs.P1) && (lhs.C1 == rhs.C1) && (lhs.P2 == rhs.P2);
375 case bezier_curve::Type::Cubic:
376 return (lhs.P1 == rhs.P1) && (lhs.C1 == rhs.C1) && (lhs.C2 == rhs.C2) && (lhs.P2 == rhs.P2);
377 default:
378 hi_no_default();
379 }
380 }
381
382 [[nodiscard]] friend bezier_curve operator*(transformer2 auto const& lhs, bezier_curve const& rhs) noexcept
383 {
384 return {rhs.type, lhs * rhs.P1, lhs * rhs.C1, lhs * rhs.C2, lhs * rhs.P2};
385 }
386
389 [[nodiscard]] friend bezier_curve operator~(bezier_curve const& rhs) noexcept
390 {
391 return {rhs.type, rhs.P2, rhs.C2, rhs.C1, rhs.P1};
392 }
393};
394
395namespace detail {
396
397[[nodiscard]] constexpr std::vector<float> solveCurvesXByY(std::vector<bezier_curve> const& v, float y) noexcept
398{
400 r.reserve(v.size());
401
402 for (auto const& curve : v) {
403 auto const xValues = curve.solveXByY(y);
404 for (auto const x : xValues) {
405 r.push_back(x);
406 }
407 }
408 return r;
409}
410
411[[nodiscard]] constexpr std::optional<std::vector<std::pair<float, float>>>
412getFillSpansAtY(std::vector<bezier_curve> const& v, float y) noexcept
413{
414 auto xValues = solveCurvesXByY(v, y);
415
416 // Sort x values, each pair is a span.
417 std::sort(xValues.begin(), xValues.end());
418
419 // End-to-end connected curves will yield duplicate values.
420 auto const uniqueEnd = std::unique(xValues.begin(), xValues.end());
421
422 // After removing duplicates, we should end up with pairs of x values.
423 std::size_t const uniqueValueCount = (uniqueEnd - xValues.begin());
424
425 if (uniqueValueCount % 2 != 0) {
426 // Something is wrong in solving the curves. Probably numeric instability.
427 // In any case, just ignore this sample.
428 return {};
429 }
430
431 // Create pairs of values.
432 auto r = std::vector<std::pair<float, float>>{};
433 r.reserve(uniqueValueCount / 2);
434 for (std::size_t i = 0; i < uniqueValueCount; i += 2) {
435 r.emplace_back(xValues[i], xValues[i + 1]);
436 }
437 return r;
438}
439
440constexpr void fillPartialPixels(std::span<uint8_t> row, ssize_t const i, float const startX, float const endX) noexcept
441{
442 auto const pixelCoverage = std::clamp(endX, i + 0.0f, i + 1.0f) - std::clamp(startX, i + 0.0f, i + 1.0f);
443
444 auto& p = row[i];
445 p = static_cast<uint8_t>(std::min(pixelCoverage * 51.0f + p, 255.0f));
446}
447
448constexpr void fillFullPixels(std::span<uint8_t> row, ssize_t const start, ssize_t const size) noexcept
449{
450 if (size < 16) {
451 auto const end = start + size;
452 for (ssize_t i = start; i < end; i++) {
453 row[i] += 0x33;
454 }
455 } else {
456 auto u8p = &row[start];
457 auto const u8end = u8p + size;
458
459 // First add 51 to all pixels up to the alignment.
460 auto const alignedStart = hi::ceil(u8p, sizeof(uint64_t));
461 while (u8p < alignedStart) {
462 *(u8p++) += 0x33;
463 }
464
465 // add 51 for each pixel, 8 pixels at a time.
466 auto u64p = reinterpret_cast<uint64_t *>(u8p);
467 auto const *const u64end = reinterpret_cast<uint64_t const *>(hi::floor(u8end, sizeof(uint64_t)));
468 while (u64p < u64end) {
469 *(u64p++) += 0x3333333333333333ULL;
470 }
471
472 // Add 51 to the last pixels.
473 u8p = reinterpret_cast<uint8_t *>(u64p);
474 while (u8p < u8end) {
475 *(u8p++) += 0x33;
476 }
477 }
478}
479
483constexpr void fillRowSpan(std::span<uint8_t> row, float const startX, float const endX) noexcept
484{
485 if (startX >= row.size() || endX < 0.0f) {
486 return;
487 }
488
489 auto const startX_int = floor_cast<std::size_t>(startX);
490 auto const endXplusOne = endX + 1.0f;
491 auto const endX_int = floor_cast<std::size_t>(endXplusOne);
492 auto const startColumn = std::max(startX_int, std::size_t{0});
493 auto const endColumn = std::min(endX_int, row.size());
494 auto const nrColumns = endColumn - startColumn;
495
496 if (nrColumns == 1) {
497 fillPartialPixels(row, startColumn, startX, endX);
498 } else {
499 fillPartialPixels(row, startColumn, startX, endX);
500 fillFullPixels(row, startColumn + 1, nrColumns - 2);
501 fillPartialPixels(row, endColumn - 1, startX, endX);
502 }
503}
504
505constexpr void fillRow(std::span<uint8_t> row, std::size_t rowY, std::vector<bezier_curve> const& curves) noexcept
506{
507 // 5 times super sampling.
508 for (float y = rowY + 0.1f; y < (rowY + 1); y += 0.2f) {
509 auto optionalSpans = getFillSpansAtY(curves, y);
510 if (!optionalSpans) {
511 // try again, with a slight offset.
512 optionalSpans = getFillSpansAtY(curves, y + 0.01f);
513 }
514
515 if (optionalSpans) {
516 auto const& spans = optionalSpans.value();
517
518 for (auto const& span : spans) {
519 fillRowSpan(row, span.first, span.second);
520 }
521 }
522 }
523}
524
525[[nodiscard]] constexpr float generate_sdf_r8_pixel(point2 point, std::vector<bezier_curve> const& curves) noexcept
526{
527 if (curves.empty()) {
529 }
530
531 auto it = curves.cbegin();
532 auto nearest = (it++)->sdf_distance(point);
533
534 for (; it != curves.cend(); ++it) {
535 auto const distance = it->sdf_distance(point);
536
537 if (distance < nearest) {
538 nearest = distance;
539 }
540 }
541
542 return nearest.signed_distance();
543}
544
545} // namespace detail
546
553[[nodiscard]] constexpr std::vector<bezier_curve>
554makeContourFromPoints(std::vector<bezier_point>::const_iterator begin, std::vector<bezier_point>::const_iterator end) noexcept
555{
556 auto const points = bezier_point::normalizePoints(begin, end);
557
559
560 auto type = bezier_curve::Type::None;
561 auto P1 = point2{};
562 auto C1 = point2{};
563 auto C2 = point2{};
564
565 for (auto const& point : points) {
566 switch (point.type) {
567 case bezier_point::Type::Anchor:
568 switch (type) {
569 case bezier_curve::Type::None:
570 P1 = point.p;
571 type = bezier_curve::Type::Linear;
572 break;
573 case bezier_curve::Type::Linear:
574 r.emplace_back(P1, point.p);
575 P1 = point.p;
576 type = bezier_curve::Type::Linear;
577 break;
578 case bezier_curve::Type::Quadratic:
579 r.emplace_back(P1, C1, point.p);
580 P1 = point.p;
581 type = bezier_curve::Type::Linear;
582 break;
583 case bezier_curve::Type::Cubic:
584 r.emplace_back(P1, C1, C2, point.p);
585 P1 = point.p;
586 type = bezier_curve::Type::Linear;
587 break;
588 default:
589 hi_no_default();
590 }
591 break;
592 case bezier_point::Type::QuadraticControl:
593 C1 = point.p;
594 type = bezier_curve::Type::Quadratic;
595 break;
596 case bezier_point::Type::CubicControl1:
597 C1 = point.p;
598 type = bezier_curve::Type::Cubic;
599 break;
600 case bezier_point::Type::CubicControl2:
601 C2 = point.p;
602 hi_assert(type == bezier_curve::Type::Cubic);
603 break;
604 default:
605 hi_no_default();
606 }
607 }
608
609 return r;
610}
611
618[[nodiscard]] constexpr std::vector<bezier_curve> makeInverseContour(std::vector<bezier_curve> const& contour) noexcept
619{
620 auto r = std::vector<bezier_curve>{};
621 r.reserve(contour.size());
622
623 for (auto i = contour.rbegin(); i != contour.rend(); i++) {
624 r.push_back(~(*i));
625 }
626
627 return r;
628}
629
641 std::vector<bezier_curve> const& contour,
642 float offset,
644 float tolerance) noexcept
645{
646 auto contourAtOffset = std::vector<bezier_curve>{};
647 for (auto const& curve : contour) {
648 for (auto const& flatCurve : curve.subdivideUntilFlat(tolerance)) {
649 contourAtOffset.push_back(flatCurve.toParallelLine(offset));
650 }
651 }
652
653 // The resulting path now consists purely of line-segments that may have gaps and overlaps.
654 // This needs to be repaired.
655 std::optional<point2> intersectPoint;
656 auto r = std::vector<bezier_curve>{};
657 for (auto const& curve : contourAtOffset) {
658 if (r.size() == 0) {
659 r.push_back(curve);
660
661 } else if (r.back().P2 == curve.P1) {
662 r.push_back(curve);
663
664 } else if ((intersectPoint = getIntersectionPoint(r.back().P1, r.back().P2, curve.P1, curve.P2))) {
665 r.back().P2 = intersectPoint.value();
666 r.push_back(curve);
667 r.back().P1 = intersectPoint.value();
668
669 } else if (
671 to_bool(intersectPoint = getExtrapolatedIntersectionPoint(r.back().P1, r.back().P2, curve.P1, curve.P2))) {
672 r.back().P2 = intersectPoint.value();
673 r.push_back(curve);
674 r.back().P1 = intersectPoint.value();
675
676 } else {
677 r.emplace_back(r.back().P2, curve.P1);
678 r.push_back(curve);
679 }
680 }
681
682 // Repair the endpoints of the contour as well.
683 if (r.size() > 0 && r.back().P2 != r.front().P1) {
684 if ((intersectPoint = getIntersectionPoint(r.back().P1, r.back().P2, r.front().P1, r.front().P2))) {
685 r.back().P2 = r.front().P1 = intersectPoint.value();
686 } else {
687 r.emplace_back(r.back().P2, r.front().P1);
688 }
689 }
690
691 return r;
692}
693
698constexpr void fill(pixmap_span<uint8_t> image, std::vector<bezier_curve> const& curves) noexcept
699{
700 for (auto y = 0_uz; y < image.height(); y++) {
701 detail::fillRow(image[y], y, curves);
702 }
703}
704
709constexpr void fill(pixmap_span<sdf_r8> image, std::vector<bezier_curve> const& curves) noexcept
710{
711 for (auto row_nr = 0_uz; row_nr != image.height(); ++row_nr) {
712 auto const row = image[row_nr];
713 auto const y = static_cast<float>(row_nr);
714 for (auto column_nr = 0_uz; column_nr != image.width(); ++column_nr) {
715 auto const x = static_cast<float>(column_nr);
716 row[column_nr] = detail::generate_sdf_r8_pixel(point2(x, y), curves);
717 }
718 }
719}
720
721}} // namespace hi::v1
722
723hi_warning_pop();
@ end
Start from the end of the file.
Definition seek_whence.hpp:17
@ begin
Start from the beginning of the file.
Definition seek_whence.hpp:15
line_join_style
The way two lines should be joined.
Definition line_join_style.hpp:22
@ miter
The outer edge of both lines are extended until they meet to form a sharp corner.
Definition line_join_style.hpp:33
@ other
The gui_event does not have associated data.
Definition gui_event_variant.hpp:24
The HikoGUI namespace.
Definition array_generic.hpp:21
The HikoGUI API version 1.
Definition array_generic.hpp:22
std::ptrdiff_t ssize_t
Signed size/index into an array.
Definition misc.hpp:32
constexpr void fill(pixmap_span< uint8_t > image, std::vector< bezier_curve > const &curves) noexcept
Fill a linear gray scale image by filling a curve with anti-aliasing.
Definition bezier_curve.hpp:698
constexpr std::vector< bezier_curve > makeContourFromPoints(std::vector< bezier_point >::const_iterator begin, std::vector< bezier_point >::const_iterator end) noexcept
Make a contour of Bezier curves from a list of points.
Definition bezier_curve.hpp:554
lean_vector< float > bezierFindX(point2 P1, point2 P2, float y) noexcept
Definition bezier.hpp:179
std::optional< point2 > getIntersectionPoint(point2 A1, point2 A2, point2 B1, point2 B2) noexcept
Definition bezier.hpp:291
constexpr std::vector< bezier_curve > makeInverseContour(std::vector< bezier_curve > const &contour) noexcept
Inverse a contour.
Definition bezier_curve.hpp:618
lean_vector< float > bezierFindTForNormalsIntersectingPoint(point2 P1, point2 P2, point2 P) noexcept
Find t on the line P1->P2 which is closest to P.
Definition bezier.hpp:139
std::optional< point2 > getExtrapolatedIntersectionPoint(point2 A1, point2 A2, point2 B1, point2 B2) noexcept
Definition bezier.hpp:322
constexpr std::vector< bezier_curve > makeParallelContour(std::vector< bezier_curve > const &contour, float offset, hi::line_join_style line_join_style, float tolerance) noexcept
Definition bezier_curve.hpp:640
float bezierFlatness(point2 P1, point2 P2) noexcept
Definition bezier.hpp:246
Lean-vector with (SVO) short-vector-optimization.
Definition lean_vector.hpp:36
A high-level geometric vector Part of the high-level vector, point, mat and color types.
Definition vector2.hpp:27
Definition bezier_curve.hpp:34
point2 pointAt(float const t) const noexcept
Definition bezier_curve.hpp:80
point2 P2
Last point.
Definition bezier_curve.hpp:41
void subdivideUntilFlat_impl(std::vector< bezier_curve > &r, float const minimumFlatness) const noexcept
Definition bezier_curve.hpp:316
std::vector< bezier_curve > subdivideUntilFlat(float const tolerance) const noexcept
Definition bezier_curve.hpp:331
bezier_curve(point2 const P1, point2 const C1, point2 const P2) noexcept
Definition bezier_curve.hpp:55
std::pair< bezier_curve, bezier_curve > linearSplit(float const t) const noexcept
Definition bezier_curve.hpp:286
constexpr vector2 tangentAt(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:227
bezier_curve(Type const type, point2 const P1, point2 const C1, point2 const C2, point2 const P2) noexcept
Definition bezier_curve.hpp:68
std::pair< bezier_curve, bezier_curve > split(float const t) const noexcept
Definition bezier_curve.hpp:298
point2 C2
Control point.
Definition bezier_curve.hpp:40
std::pair< bezier_curve, bezier_curve > quadraticSplit(float const t) const noexcept
Definition bezier_curve.hpp:271
friend bezier_curve operator~(bezier_curve const &rhs) noexcept
Definition bezier_curve.hpp:389
point2 C1
Control point.
Definition bezier_curve.hpp:39
bezier_curve toParallelLine(float const offset) const noexcept
Definition bezier_curve.hpp:359
bezier_curve(point2 const P1, point2 const C1, point2 const C2, point2 const P2) noexcept
Definition bezier_curve.hpp:61
point2 P1
First point.
Definition bezier_curve.hpp:38
float flatness() const noexcept
Definition bezier_curve.hpp:341
lean_vector< float > solveXByY(float const y) const noexcept
Definition bezier_curve.hpp:119
std::pair< bezier_curve, bezier_curve > cubicSplit(float const t) const noexcept
Definition bezier_curve.hpp:252
Definition bezier_curve.hpp:147
vector2 PN
The vector between P and N.
Definition bezier_curve.hpp:150
float t
Linear position on the curve-segment, 0.0 and 1.0 are end-points.
Definition bezier_curve.hpp:156
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:171
float sq_distance
The square distance between P and N.
Definition bezier_curve.hpp:160
static constexpr std::vector< bezier_point > normalizePoints(std::vector< bezier_point >::const_iterator const begin, std::vector< bezier_point >::const_iterator const end) noexcept
Definition bezier_point.hpp:41
A non-owning 2D pixel-based image.
Definition pixmap_span.hpp:34
T back(T... args)
T distance(T... args)
T emplace_back(T... args)
T front(T... args)
T max(T... args)
T min(T... args)
T push_back(T... args)
T reserve(T... args)
T size(T... args)
T sort(T... args)
T sqrt(T... args)
T unique(T... args)