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_export_module(hikogui.graphic_path.bezier_curve);
23
24hi_export namespace hi { inline namespace v1 {
25
29hi_export struct bezier_curve {
30 enum class Type : uint8_t { None, Linear, Quadratic, Cubic };
31
32 Type type;
33 point2 P1;
34 point2 C1;
35 point2 C2;
36 point2 P2;
37
38 bezier_curve() noexcept = delete;
39 bezier_curve(bezier_curve const& other) noexcept = default;
40 bezier_curve(bezier_curve&& other) noexcept = default;
41 bezier_curve& operator=(bezier_curve const& other) noexcept = default;
42 bezier_curve& operator=(bezier_curve&& other) noexcept = default;
43
46 bezier_curve(point2 const P1, point2 const P2) noexcept : type(Type::Linear), P1(P1), C1(), C2(), P2(P2) {}
47
50 bezier_curve(point2 const P1, point2 const C1, point2 const P2) noexcept : type(Type::Quadratic), P1(P1), C1(C1), C2(), P2(P2)
51 {
52 }
53
56 bezier_curve(point2 const P1, point2 const C1, point2 const C2, point2 const P2) noexcept :
57 type(Type::Cubic), P1(P1), C1(C1), C2(C2), P2(P2)
58 {
59 }
60
63 bezier_curve(Type const type, point2 const P1, point2 const C1, point2 const C2, point2 const P2) noexcept :
64 type(type), P1(P1), C1(C1), C2(C2), P2(P2)
65 {
66 }
67
75 [[nodiscard]] point2 pointAt(float const t) const noexcept
76 {
77 switch (type) {
78 case Type::Linear:
79 return bezierPointAt(P1, P2, t);
80 case Type::Quadratic:
81 return bezierPointAt(P1, C1, P2, t);
82 case Type::Cubic:
83 return bezierPointAt(P1, C1, C2, P2, t);
84 default:
85 hi_no_default();
86 }
87 }
88
96 [[nodiscard]] constexpr vector2 tangentAt(float const t) const noexcept
97 {
98 switch (type) {
99 case Type::Linear:
100 return bezierTangentAt(P1, P2, t);
101 case Type::Quadratic:
102 return bezierTangentAt(P1, C1, P2, t);
103 case Type::Cubic:
104 return bezierTangentAt(P1, C1, C2, P2, t);
105 default:
106 hi_no_default();
107 }
108 }
109
114 [[nodiscard]] lean_vector<float> solveXByY(float const y) const noexcept
115 {
116 switch (type) {
117 case Type::Linear:
118 return bezierFindX(P1, P2, y);
119 case Type::Quadratic:
120 return bezierFindX(P1, C1, P2, y);
121 case Type::Cubic:
122 return bezierFindX(P1, C1, C2, P2, y);
123 default:
124 hi_no_default();
125 }
126 }
127
128 [[nodiscard]] hi_force_inline lean_vector<float> solveTForNormalsIntersectingPoint(point2 P) const noexcept
129 {
130 switch (type) {
131 case Type::Linear:
133 case Type::Quadratic:
135 case Type::Cubic:
136 hi_no_default();
137 default:
138 hi_no_default();
139 }
140 }
141
146
147 bezier_curve const *curve = nullptr;
148
151 float t = 0.0f;
152
156
157 constexpr sdf_distance_result() noexcept = default;
158 constexpr sdf_distance_result(sdf_distance_result const&) noexcept = default;
159 constexpr sdf_distance_result(sdf_distance_result&&) noexcept = default;
160 constexpr sdf_distance_result& operator=(sdf_distance_result const&) noexcept = default;
161 constexpr sdf_distance_result& operator=(sdf_distance_result&&) noexcept = default;
162 constexpr sdf_distance_result(bezier_curve const *curve) noexcept : curve(curve) {}
163
166 [[nodiscard]] hi_force_inline constexpr float orthogonality() const noexcept
167 {
168 if (sq_distance == 0.0f) {
169 // If the line PN is zero length it means the point is on the curve
170 // The orthogonality of a point on a line does not mean anything.
171 return 0.0f;
172 }
173
174 auto const tangent = curve->tangentAt(t);
175 if (tangent == vector2{}) {
176 // The tangent can be a zero vector if the points or control points
177 // of the curve lie to top of each other. This may happen
178 // when the curve is not accurately represented or a bug.
179 return 0.0f;
180 }
181
182 return cross(normalize(tangent), normalize(PN));
183 };
184
185 [[nodiscard]] hi_force_inline float distance() const noexcept
186 {
187 return std::sqrt(sq_distance);
188 }
189
190 [[nodiscard]] hi_force_inline float signed_distance() const noexcept
191 {
192 auto const d = distance();
193 return orthogonality() < 0.0 ? d : -d;
194 }
195
196 [[nodiscard]] hi_force_inline constexpr bool operator<(sdf_distance_result const& rhs) const noexcept
197 {
198 if (abs(sq_distance - rhs.sq_distance) < 0.01f) {
199 // The point is very close to both curves, meaning they
200 // are close the end/corner of both curve-segments.
201 // Here we create a line through the corner where both curves
202 // meet and determine on what side of the line our point lies.
203 // We can do this by calculating the orthogonality with the
204 // tangent of the curve at the corner.
205 return abs(orthogonality()) > abs(rhs.orthogonality());
206
207 } else {
208 // The simple case, just get the nearest curve.
209 return sq_distance < rhs.sq_distance;
210 }
211 }
212 };
213
222 [[nodiscard]] sdf_distance_result sdf_distance(point2 P) const noexcept
223 {
224 auto nearest = sdf_distance_result{this};
225
226 auto const ts = solveTForNormalsIntersectingPoint(P);
227 for (auto t : ts) {
228 t = std::clamp(t, 0.0f, 1.0f);
229
230 auto const PN = P - pointAt(t);
231 auto const sq_distance = squared_hypot(PN);
232 if (sq_distance < nearest.sq_distance) {
233 nearest.t = t;
234 nearest.PN = PN;
235 nearest.sq_distance = sq_distance;
236 }
237 }
238
239 return nearest;
240 }
241
247 [[nodiscard]] std::pair<bezier_curve, bezier_curve> cubicSplit(float const t) const noexcept
248 {
249 auto const outerA = bezier_curve{P1, C1};
250 auto const outerBridge = bezier_curve{C1, C2};
251 auto const outerB = bezier_curve{C2, P2};
252
253 auto const innerA = bezier_curve{outerA.pointAt(t), outerBridge.pointAt(t)};
254 auto const innerB = bezier_curve{outerBridge.pointAt(t), outerB.pointAt(t)};
255
256 auto const newPoint = bezier_curve{innerA.pointAt(t), innerB.pointAt(t)}.pointAt(t);
257
258 return {{P1, outerA.pointAt(t), innerA.pointAt(t), newPoint}, {newPoint, innerB.pointAt(t), outerB.pointAt(t), P2}};
259 }
260
266 [[nodiscard]] std::pair<bezier_curve, bezier_curve> quadraticSplit(float const t) const noexcept
267 {
268 auto const outerA = bezier_curve{P1, C1};
269 auto const outerB = bezier_curve{C1, P2};
270
271 auto const newPoint = bezier_curve{outerA.pointAt(t), outerB.pointAt(t)}.pointAt(t);
272
273 return {{P1, outerA.pointAt(t), newPoint}, {newPoint, outerB.pointAt(t), P2}};
274 }
275
281 [[nodiscard]] std::pair<bezier_curve, bezier_curve> linearSplit(float const t) const noexcept
282 {
283 auto const newPoint = pointAt(t);
284
285 return {{P1, newPoint}, {newPoint, P2}};
286 }
287
293 [[nodiscard]] std::pair<bezier_curve, bezier_curve> split(float const t) const noexcept
294 {
295 switch (type) {
296 case Type::Linear:
297 return linearSplit(t);
298 case Type::Quadratic:
299 return quadraticSplit(t);
300 case Type::Cubic:
301 return cubicSplit(t);
302 default:
303 hi_no_default();
304 }
305 }
306
311 void subdivideUntilFlat_impl(std::vector<bezier_curve>& r, float const minimumFlatness) const noexcept
312 {
313 if (flatness() >= minimumFlatness) {
314 r.push_back(*this);
315 } else {
316 auto const[a, b] = split(0.5f);
317 a.subdivideUntilFlat_impl(r, minimumFlatness);
318 b.subdivideUntilFlat_impl(r, minimumFlatness);
319 }
320 }
321
326 [[nodiscard]] std::vector<bezier_curve> subdivideUntilFlat(float const tolerance) const noexcept
327 {
329 subdivideUntilFlat_impl(r, 1.0f - tolerance);
330 return r;
331 }
332
336 [[nodiscard]] float flatness() const noexcept
337 {
338 switch (type) {
339 case Type::Linear:
340 return bezierFlatness(P1, P2);
341 case Type::Quadratic:
342 return bezierFlatness(P1, C1, P2);
343 case Type::Cubic:
344 return bezierFlatness(P1, C1, C2, P2);
345 default:
346 hi_no_default();
347 }
348 }
349
354 [[nodiscard]] bezier_curve toParallelLine(float const offset) const noexcept
355 {
356 auto const[newP1, newP2] = parallelLine(P1, P2, offset);
357 return {newP1, newP2};
358 }
359
360 [[nodiscard]] friend bool operator==(bezier_curve const& lhs, bezier_curve const& rhs) noexcept
361 {
362 if (lhs.type != rhs.type) {
363 return false;
364 }
365 switch (lhs.type) {
366 case bezier_curve::Type::Linear:
367 return (lhs.P1 == rhs.P1) && (lhs.P2 == rhs.P2);
368 case bezier_curve::Type::Quadratic:
369 return (lhs.P1 == rhs.P1) && (lhs.C1 == rhs.C1) && (lhs.P2 == rhs.P2);
370 case bezier_curve::Type::Cubic:
371 return (lhs.P1 == rhs.P1) && (lhs.C1 == rhs.C1) && (lhs.C2 == rhs.C2) && (lhs.P2 == rhs.P2);
372 default:
373 hi_no_default();
374 }
375 }
376
377 [[nodiscard]] friend bezier_curve operator*(transformer2 auto const& lhs, bezier_curve const& rhs) noexcept
378 {
379 return {rhs.type, lhs * rhs.P1, lhs * rhs.C1, lhs * rhs.C2, lhs * rhs.P2};
380 }
381
384 [[nodiscard]] friend bezier_curve operator~(bezier_curve const& rhs) noexcept
385 {
386 return {rhs.type, rhs.P2, rhs.C2, rhs.C1, rhs.P1};
387 }
388};
389
390namespace detail {
391
392[[nodiscard]] constexpr std::vector<float> solveCurvesXByY(std::vector<bezier_curve> const& v, float y) noexcept
393{
395 r.reserve(v.size());
396
397 for (auto const& curve : v) {
398 auto const xValues = curve.solveXByY(y);
399 for (auto const x : xValues) {
400 r.push_back(x);
401 }
402 }
403 return r;
404}
405
406[[nodiscard]] constexpr std::optional<std::vector<std::pair<float, float>>>
407getFillSpansAtY(std::vector<bezier_curve> const& v, float y) noexcept
408{
409 auto xValues = solveCurvesXByY(v, y);
410
411 // Sort x values, each pair is a span.
412 std::sort(xValues.begin(), xValues.end());
413
414 // End-to-end connected curves will yield duplicate values.
415 auto const uniqueEnd = std::unique(xValues.begin(), xValues.end());
416
417 // After removing duplicates, we should end up with pairs of x values.
418 std::size_t const uniqueValueCount = (uniqueEnd - xValues.begin());
419
420 if (uniqueValueCount % 2 != 0) {
421 // Something is wrong in solving the curves. Probably numeric instability.
422 // In any case, just ignore this sample.
423 return {};
424 }
425
426 // Create pairs of values.
428 r.reserve(uniqueValueCount / 2);
429 for (std::size_t i = 0; i < uniqueValueCount; i += 2) {
430 r.emplace_back(xValues[i], xValues[i + 1]);
431 }
432 return r;
433}
434
435constexpr void fillPartialPixels(std::span<uint8_t> row, ssize_t const i, float const startX, float const endX) noexcept
436{
437 auto const pixelCoverage = std::clamp(endX, i + 0.0f, i + 1.0f) - std::clamp(startX, i + 0.0f, i + 1.0f);
438
439 auto& p = row[i];
440 p = static_cast<uint8_t>(std::min(pixelCoverage * 51.0f + p, 255.0f));
441}
442
443constexpr void fillFullPixels(std::span<uint8_t> row, ssize_t const start, ssize_t const size) noexcept
444{
445 if (size < 16) {
446 auto const end = start + size;
447 for (ssize_t i = start; i < end; i++) {
448 row[i] += 0x33;
449 }
450 } else {
451 auto u8p = &row[start];
452 auto const u8end = u8p + size;
453
454 // First add 51 to all pixels up to the alignment.
455 auto const alignedStart = hi::ceil(u8p, sizeof(uint64_t));
456 while (u8p < alignedStart) {
457 *(u8p++) += 0x33;
458 }
459
460 // add 51 for each pixel, 8 pixels at a time.
461 auto u64p = reinterpret_cast<uint64_t *>(u8p);
462 auto const *const u64end = reinterpret_cast<uint64_t const *>(hi::floor(u8end, sizeof(uint64_t)));
463 while (u64p < u64end) {
464 *(u64p++) += 0x3333333333333333ULL;
465 }
466
467 // Add 51 to the last pixels.
468 u8p = reinterpret_cast<uint8_t *>(u64p);
469 while (u8p < u8end) {
470 *(u8p++) += 0x33;
471 }
472 }
473}
474
478constexpr void fillRowSpan(std::span<uint8_t> row, float const startX, float const endX) noexcept
479{
480 if (startX >= row.size() || endX < 0.0f) {
481 return;
482 }
483
484 auto const startX_int = floor_cast<std::size_t>(startX);
485 auto const endXplusOne = endX + 1.0f;
486 auto const endX_int = floor_cast<std::size_t>(endXplusOne);
487 auto const startColumn = std::max(startX_int, std::size_t{0});
488 auto const endColumn = std::min(endX_int, row.size());
489 auto const nrColumns = endColumn - startColumn;
490
491 if (nrColumns == 1) {
492 fillPartialPixels(row, startColumn, startX, endX);
493 } else {
494 fillPartialPixels(row, startColumn, startX, endX);
495 fillFullPixels(row, startColumn + 1, nrColumns - 2);
496 fillPartialPixels(row, endColumn - 1, startX, endX);
497 }
498}
499
500constexpr void fillRow(std::span<uint8_t> row, std::size_t rowY, std::vector<bezier_curve> const& curves) noexcept
501{
502 // 5 times super sampling.
503 for (float y = rowY + 0.1f; y < (rowY + 1); y += 0.2f) {
504 auto optionalSpans = getFillSpansAtY(curves, y);
505 if (!optionalSpans) {
506 // try again, with a slight offset.
507 optionalSpans = getFillSpansAtY(curves, y + 0.01f);
508 }
509
510 if (optionalSpans) {
511 auto const& spans = optionalSpans.value();
512
513 for (auto const& span : spans) {
514 fillRowSpan(row, span.first, span.second);
515 }
516 }
517 }
518}
519
520[[nodiscard]] constexpr float generate_sdf_r8_pixel(point2 point, std::vector<bezier_curve> const& curves) noexcept
521{
522 if (curves.empty()) {
524 }
525
526 auto it = curves.cbegin();
527 auto nearest = (it++)->sdf_distance(point);
528
529 for (; it != curves.cend(); ++it) {
530 auto const distance = it->sdf_distance(point);
531
532 if (distance < nearest) {
533 nearest = distance;
534 }
535 }
536
537 return nearest.signed_distance();
538}
539
540} // namespace detail
541
548[[nodiscard]] constexpr std::vector<bezier_curve>
549makeContourFromPoints(std::vector<bezier_point>::const_iterator begin, std::vector<bezier_point>::const_iterator end) noexcept
550{
551 auto const points = bezier_point::normalizePoints(begin, end);
552
554
555 auto type = bezier_curve::Type::None;
556 auto P1 = point2{};
557 auto C1 = point2{};
558 auto C2 = point2{};
559
560 for (auto const& point : points) {
561 switch (point.type) {
562 case bezier_point::Type::Anchor:
563 switch (type) {
564 case bezier_curve::Type::None:
565 P1 = point.p;
566 type = bezier_curve::Type::Linear;
567 break;
568 case bezier_curve::Type::Linear:
569 r.emplace_back(P1, point.p);
570 P1 = point.p;
571 type = bezier_curve::Type::Linear;
572 break;
573 case bezier_curve::Type::Quadratic:
574 r.emplace_back(P1, C1, point.p);
575 P1 = point.p;
576 type = bezier_curve::Type::Linear;
577 break;
578 case bezier_curve::Type::Cubic:
579 r.emplace_back(P1, C1, C2, point.p);
580 P1 = point.p;
581 type = bezier_curve::Type::Linear;
582 break;
583 default:
584 hi_no_default();
585 }
586 break;
587 case bezier_point::Type::QuadraticControl:
588 C1 = point.p;
589 type = bezier_curve::Type::Quadratic;
590 break;
591 case bezier_point::Type::CubicControl1:
592 C1 = point.p;
593 type = bezier_curve::Type::Cubic;
594 break;
595 case bezier_point::Type::CubicControl2:
596 C2 = point.p;
597 hi_assert(type == bezier_curve::Type::Cubic);
598 break;
599 default:
600 hi_no_default();
601 }
602 }
603
604 return r;
605}
606
613[[nodiscard]] constexpr std::vector<bezier_curve> makeInverseContour(std::vector<bezier_curve> const& contour) noexcept
614{
615 auto r = std::vector<bezier_curve>{};
616 r.reserve(contour.size());
617
618 for (auto i = contour.rbegin(); i != contour.rend(); i++) {
619 r.push_back(~(*i));
620 }
621
622 return r;
623}
624
636 std::vector<bezier_curve> const& contour,
637 float offset,
639 float tolerance) noexcept
640{
641 auto contourAtOffset = std::vector<bezier_curve>{};
642 for (auto const& curve : contour) {
643 for (auto const& flatCurve : curve.subdivideUntilFlat(tolerance)) {
644 contourAtOffset.push_back(flatCurve.toParallelLine(offset));
645 }
646 }
647
648 // The resulting path now consists purely of line-segments that may have gaps and overlaps.
649 // This needs to be repaired.
650 std::optional<point2> intersectPoint;
651 auto r = std::vector<bezier_curve>{};
652 for (auto const& curve : contourAtOffset) {
653 if (r.size() == 0) {
654 r.push_back(curve);
655
656 } else if (r.back().P2 == curve.P1) {
657 r.push_back(curve);
658
659 } else if ((intersectPoint = getIntersectionPoint(r.back().P1, r.back().P2, curve.P1, curve.P2))) {
660 r.back().P2 = intersectPoint.value();
661 r.push_back(curve);
662 r.back().P1 = intersectPoint.value();
663
664 } else if (
666 to_bool(intersectPoint = getExtrapolatedIntersectionPoint(r.back().P1, r.back().P2, curve.P1, curve.P2))) {
667 r.back().P2 = intersectPoint.value();
668 r.push_back(curve);
669 r.back().P1 = intersectPoint.value();
670
671 } else {
672 r.emplace_back(r.back().P2, curve.P1);
673 r.push_back(curve);
674 }
675 }
676
677 // Repair the endpoints of the contour as well.
678 if (r.size() > 0 && r.back().P2 != r.front().P1) {
679 if ((intersectPoint = getIntersectionPoint(r.back().P1, r.back().P2, r.front().P1, r.front().P2))) {
680 r.back().P2 = r.front().P1 = intersectPoint.value();
681 } else {
682 r.emplace_back(r.back().P2, r.front().P1);
683 }
684 }
685
686 return r;
687}
688
693constexpr void fill(pixmap_span<uint8_t> image, std::vector<bezier_curve> const& curves) noexcept
694{
695 for (auto y = 0_uz; y < image.height(); y++) {
696 detail::fillRow(image[y], y, curves);
697 }
698}
699
704constexpr void fill(pixmap_span<sdf_r8> image, std::vector<bezier_curve> const& curves) noexcept
705{
706 for (auto row_nr = 0_uz; row_nr != image.height(); ++row_nr) {
707 auto const row = image[row_nr];
708 auto const y = static_cast<float>(row_nr);
709 for (auto column_nr = 0_uz; column_nr != image.width(); ++column_nr) {
710 auto const x = static_cast<float>(column_nr);
711 row[column_nr] = detail::generate_sdf_r8_pixel(point2(x, y), curves);
712 }
713 }
714}
715
716}} // namespace hi::v1
@ end
Start from the end of the file.
@ begin
Start from the beginning of the file.
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.
@ other
The gui_event does not have associated data.
The HikoGUI namespace.
Definition array_generic.hpp:20
std::ptrdiff_t ssize_t
Signed size/index into an array.
Definition misc.hpp:32
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:549
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:613
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:635
float bezierFlatness(point2 P1, point2 P2) noexcept
Definition bezier.hpp:246
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
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:29
point2 pointAt(float const t) const noexcept
Definition bezier_curve.hpp:75
point2 P2
Last point.
Definition bezier_curve.hpp:36
void subdivideUntilFlat_impl(std::vector< bezier_curve > &r, float const minimumFlatness) const noexcept
Definition bezier_curve.hpp:311
std::vector< bezier_curve > subdivideUntilFlat(float const tolerance) const noexcept
Definition bezier_curve.hpp:326
bezier_curve(point2 const P1, point2 const C1, point2 const P2) noexcept
Definition bezier_curve.hpp:50
std::pair< bezier_curve, bezier_curve > linearSplit(float const t) const noexcept
Definition bezier_curve.hpp:281
constexpr vector2 tangentAt(float const t) const noexcept
Definition bezier_curve.hpp:96
sdf_distance_result sdf_distance(point2 P) const noexcept
Find the distance from the point to the curve.
Definition bezier_curve.hpp:222
bezier_curve(Type const type, point2 const P1, point2 const C1, point2 const C2, point2 const P2) noexcept
Definition bezier_curve.hpp:63
std::pair< bezier_curve, bezier_curve > split(float const t) const noexcept
Definition bezier_curve.hpp:293
point2 C2
Control point.
Definition bezier_curve.hpp:35
std::pair< bezier_curve, bezier_curve > quadraticSplit(float const t) const noexcept
Definition bezier_curve.hpp:266
friend bezier_curve operator~(bezier_curve const &rhs) noexcept
Definition bezier_curve.hpp:384
point2 C1
Control point.
Definition bezier_curve.hpp:34
bezier_curve toParallelLine(float const offset) const noexcept
Definition bezier_curve.hpp:354
bezier_curve(point2 const P1, point2 const C1, point2 const C2, point2 const P2) noexcept
Definition bezier_curve.hpp:56
point2 P1
First point.
Definition bezier_curve.hpp:33
float flatness() const noexcept
Definition bezier_curve.hpp:336
lean_vector< float > solveXByY(float const y) const noexcept
Definition bezier_curve.hpp:114
std::pair< bezier_curve, bezier_curve > cubicSplit(float const t) const noexcept
Definition bezier_curve.hpp:247
Definition bezier_curve.hpp:142
vector2 PN
The vector between P and N.
Definition bezier_curve.hpp:145
float t
Linear position on the curve-segment, 0.0 and 1.0 are end-points.
Definition bezier_curve.hpp:151
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:166
float sq_distance
The square distance between P and N.
Definition bezier_curve.hpp:155
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)