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"
13#include "bezier_point.hpp"
14#include "../macros.hpp"
25hi_warning_ignore_msvc(4459);
27hi_export_module(hikogui.graphic_path.bezier_curve);
29hi_export
namespace hi {
inline namespace v1 {
35 enum class Type : uint8_t { None, Linear, Quadratic, Cubic };
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;
51 bezier_curve(point2 const
P1, point2 const
P2) noexcept : type(Type::Linear),
P1(
P1),
C1(),
C2(),
P2(
P2) {}
68 bezier_curve(Type
const type, point2
const P1, point2
const C1, point2
const C2, point2
const P2) noexcept :
80 [[nodiscard]] point2
pointAt(
float const t)
const noexcept
84 return bezierPointAt(
P1,
P2, t);
86 return bezierPointAt(
P1,
C1,
P2, t);
88 return bezierPointAt(
P1,
C1,
C2,
P2, t);
105 return bezierTangentAt(
P1,
P2, t);
106 case Type::Quadratic:
107 return bezierTangentAt(
P1,
C1,
P2, t);
109 return bezierTangentAt(
P1,
C1,
C2,
P2, t);
124 case Type::Quadratic:
133 [[nodiscard]] hi_force_inline
lean_vector<float> solveTForNormalsIntersectingPoint(point2 P)
const noexcept
138 case Type::Quadratic:
147 struct sdf_distance_result {
152 bezier_curve
const *curve =
nullptr;
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) {}
171 [[nodiscard]] hi_force_inline
constexpr float orthogonality() const noexcept
179 auto const tangent = curve->tangentAt(
t);
187 return cross(normalize(tangent), normalize(
PN));
190 [[nodiscard]] hi_force_inline
float distance() const noexcept
195 [[nodiscard]] hi_force_inline
float signed_distance() const noexcept
197 auto const d = distance();
201 [[nodiscard]] hi_force_inline
constexpr bool operator<(sdf_distance_result
const& rhs)
const noexcept
231 auto const ts = solveTForNormalsIntersectingPoint(P);
233 t = std::clamp(t, 0.0f, 1.0f);
235 auto const PN = P -
pointAt(t);
236 auto const sq_distance = squared_hypot(PN);
237 if (sq_distance < nearest.sq_distance) {
240 nearest.sq_distance = sq_distance;
254 auto const outerA = bezier_curve{
P1,
C1};
255 auto const outerBridge = bezier_curve{
C1,
C2};
256 auto const outerB = bezier_curve{
C2,
P2};
258 auto const innerA = bezier_curve{outerA.pointAt(t), outerBridge.pointAt(t)};
259 auto const innerB = bezier_curve{outerBridge.pointAt(t), outerB.pointAt(t)};
261 auto const newPoint = bezier_curve{innerA.pointAt(t), innerB.pointAt(t)}.pointAt(t);
263 return {{
P1, outerA.pointAt(t), innerA.pointAt(t), newPoint}, {newPoint, innerB.pointAt(t), outerB.pointAt(t),
P2}};
273 auto const outerA = bezier_curve{
P1,
C1};
274 auto const outerB = bezier_curve{
C1,
P2};
276 auto const newPoint = bezier_curve{outerA.pointAt(t), outerB.pointAt(t)}.pointAt(t);
278 return {{
P1, outerA.pointAt(t), newPoint}, {newPoint, outerB.pointAt(t),
P2}};
288 auto const newPoint =
pointAt(t);
290 return {{
P1, newPoint}, {newPoint,
P2}};
303 case Type::Quadratic:
318 if (
flatness() >= minimumFlatness) {
321 auto const[a, b] =
split(0.5f);
322 a.subdivideUntilFlat_impl(r, minimumFlatness);
323 b.subdivideUntilFlat_impl(r, minimumFlatness);
346 case Type::Quadratic:
361 auto const[newP1, newP2] = parallelLine(
P1,
P2, offset);
362 return {newP1, newP2};
367 if (lhs.type != rhs.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);
382 [[nodiscard]]
friend bezier_curve operator*(transformer2
auto const& lhs, bezier_curve
const& rhs)
noexcept
384 return {rhs.type, lhs * rhs.P1, lhs * rhs.C1, lhs * rhs.C2, lhs * rhs.P2};
389 [[nodiscard]]
friend bezier_curve
operator~(bezier_curve
const& rhs)
noexcept
391 return {rhs.type, rhs.P2, rhs.C2, rhs.C1, rhs.P1};
402 for (
auto const& curve : v) {
404 for (
auto const x : xValues) {
411[[nodiscard]]
constexpr std::optional<std::vector<std::pair<float, float>>>
412getFillSpansAtY(std::vector<bezier_curve>
const& v,
float y)
noexcept
414 auto xValues = solveCurvesXByY(v, y);
417 std::sort(xValues.begin(), xValues.end());
420 auto const uniqueEnd =
std::unique(xValues.begin(), xValues.end());
423 std::size_t
const uniqueValueCount = (uniqueEnd - xValues.begin());
425 if (uniqueValueCount % 2 != 0) {
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) {
440constexpr void fillPartialPixels(std::span<uint8_t> row,
ssize_t const i,
float const startX,
float const endX)
noexcept
442 auto const pixelCoverage = std::clamp(endX, i + 0.0f, i + 1.0f) - std::clamp(startX, i + 0.0f, i + 1.0f);
445 p =
static_cast<uint8_t
>(
std::min(pixelCoverage * 51.0f + p, 255.0f));
448constexpr void fillFullPixels(std::span<uint8_t> row,
ssize_t const start,
ssize_t const size)
noexcept
451 auto const end = start + size;
456 auto u8p = &row[start];
457 auto const u8end = u8p + size;
460 auto const alignedStart = hi::ceil(u8p,
sizeof(uint64_t));
461 while (u8p < alignedStart) {
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;
473 u8p =
reinterpret_cast<uint8_t *
>(u64p);
474 while (u8p < u8end) {
483constexpr void fillRowSpan(std::span<uint8_t> row,
float const startX,
float const endX)
noexcept
485 if (startX >= row.size() || endX < 0.0f) {
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;
496 if (nrColumns == 1) {
497 fillPartialPixels(row, startColumn, startX, endX);
499 fillPartialPixels(row, startColumn, startX, endX);
500 fillFullPixels(row, startColumn + 1, nrColumns - 2);
501 fillPartialPixels(row, endColumn - 1, startX, endX);
505constexpr void fillRow(std::span<uint8_t> row, std::size_t rowY, std::vector<bezier_curve>
const& curves)
noexcept
508 for (
float y = rowY + 0.1f; y < (rowY + 1); y += 0.2f) {
509 auto optionalSpans = getFillSpansAtY(curves, y);
510 if (!optionalSpans) {
512 optionalSpans = getFillSpansAtY(curves, y + 0.01f);
516 auto const& spans = optionalSpans.value();
518 for (
auto const& span : spans) {
519 fillRowSpan(row, span.first, span.second);
525[[nodiscard]]
constexpr float generate_sdf_r8_pixel(point2 point, std::vector<bezier_curve>
const& curves)
noexcept
527 if (curves.empty()) {
531 auto it = curves.cbegin();
532 auto nearest = (it++)->sdf_distance(point);
534 for (; it != curves.cend(); ++it) {
535 auto const distance = it->sdf_distance(point);
537 if (distance < nearest) {
542 return nearest.signed_distance();
553[[nodiscard]]
constexpr std::vector<bezier_curve>
560 auto type = bezier_curve::Type::None;
565 for (
auto const& point : points) {
566 switch (point.type) {
567 case bezier_point::Type::Anchor:
569 case bezier_curve::Type::None:
571 type = bezier_curve::Type::Linear;
573 case bezier_curve::Type::Linear:
576 type = bezier_curve::Type::Linear;
578 case bezier_curve::Type::Quadratic:
581 type = bezier_curve::Type::Linear;
583 case bezier_curve::Type::Cubic:
586 type = bezier_curve::Type::Linear;
592 case bezier_point::Type::QuadraticControl:
594 type = bezier_curve::Type::Quadratic;
596 case bezier_point::Type::CubicControl1:
598 type = bezier_curve::Type::Cubic;
600 case bezier_point::Type::CubicControl2:
602 hi_assert(type == bezier_curve::Type::Cubic);
623 for (
auto i = contour.rbegin(); i != contour.rend(); i++) {
644 float tolerance)
noexcept
647 for (
auto const& curve : contour) {
649 contourAtOffset.push_back(flatCurve.toParallelLine(offset));
655 std::optional<point2> intersectPoint;
657 for (
auto const& curve : contourAtOffset) {
661 }
else if (r.
back().P2 == curve.
P1) {
665 r.
back().P2 = intersectPoint.value();
667 r.
back().P1 = intersectPoint.value();
672 r.
back().P2 = intersectPoint.value();
674 r.
back().P1 = intersectPoint.value();
685 r.
back().P2 = r.
front().P1 = intersectPoint.value();
700 for (
auto y = 0_uz; y < image.height(); y++) {
701 detail::fillRow(image[y], y, curves);
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);
@ 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 emplace_back(T... args)