HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
graphic_path.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 "bezier_point.hpp" // export
8#include "bezier_curve.hpp" // export
9#include "bezier.hpp" // export
10#include "../utility/utility.hpp"
11#include "../geometry/geometry.hpp"
12#include "../image/image.hpp"
13#include "../color/color.hpp"
14#include "../macros.hpp"
15#include <vector>
16#include <utility>
17#include <cmath>
18
19hi_warning_push();
20// warning C4459: declaration of 'point' hides global declaration
21// The unit library has a 'point' and 'points' global variables.
22hi_warning_ignore_msvc(4459);
23
24hi_export_module(hikogui.graphic_path);
25
26hi_export namespace hi { inline namespace v1 {
27
34hi_export struct graphic_path {
38
42
46
49 void clear() noexcept
50 {
51 points.clear();
52 contourEndPoints.clear();
53 layerEndContours.clear();
54 }
55
58 [[nodiscard]] ssize_t numberOfContours() const noexcept
59 {
60 return ssize(contourEndPoints);
61 }
62
65 [[nodiscard]] ssize_t numberOfLayers() const noexcept
66 {
67 return ssize(layerEndContours);
68 }
69
72 [[nodiscard]] bool allLayersHaveSameColor() const noexcept
73 {
74 if (!hasLayers()) {
75 return true;
76 }
77
78 auto const& firstColor = layerEndContours.front().second;
79
80 for (auto const & [ endContour, color ] : layerEndContours) {
81 if (color != firstColor) {
82 return false;
83 }
84 }
85 return true;
86 }
87
90 [[nodiscard]] aarectangle boundingBox() const noexcept
91 {
92 if (ssize(points) == 0) {
93 return aarectangle{0.0, 0.0, 0.0, 0.0};
94 }
95
96 auto r = aarectangle{points.front().p, points.front().p};
97
98 for (auto const& point : points) {
99 r |= point.p;
100 }
101
102 return r;
103 }
104
109 void tryRemoveLayers() noexcept
110 {
111 if (!hasLayers()) {
112 return;
113 }
114
115 if (!allLayersHaveSameColor()) {
116 return;
117 }
118
119 layerEndContours.clear();
120 }
121
124 [[nodiscard]] std::vector<bezier_point>::const_iterator beginContour(ssize_t contourNr) const noexcept
125 {
126 return points.begin() + (contourNr == 0 ? 0 : contourEndPoints.at(contourNr - 1) + 1);
127 }
128
129 /* Return and end-iterator beyond the end point of a contour.
130 */
131 [[nodiscard]] std::vector<bezier_point>::const_iterator endContour(ssize_t contourNr) const noexcept
132 {
133 return points.begin() + contourEndPoints.at(contourNr) + 1;
134 }
135
136 /* Return the first contour index of a layer.
137 */
138 [[nodiscard]] ssize_t beginLayer(ssize_t layerNr) const noexcept
139 {
140 return layerNr == 0 ? 0 : layerEndContours.at(layerNr - 1).first + 1;
141 }
142
143 /* Return beyond the last contour index of a layer.
144 */
145 [[nodiscard]] ssize_t endLayer(ssize_t layerNr) const noexcept
146 {
147 return layerEndContours.at(layerNr).first + 1;
148 }
149
150 [[nodiscard]] std::vector<bezier_point> getbezier_pointsOfContour(ssize_t contourNr) const noexcept
151 {
152 auto const begin = points.begin() + (contourNr == 0 ? 0 : contourEndPoints.at(contourNr - 1) + 1);
153 auto const end = points.begin() + contourEndPoints.at(contourNr) + 1;
154 return std::vector(begin, end);
155 }
156
157 [[nodiscard]] std::vector<bezier_curve> getBeziersOfContour(ssize_t contourNr) const noexcept
158 {
159 auto first = beginContour(contourNr);
160 auto last = endContour(contourNr);
161 auto num_points = std::distance(first, last);
162 if (num_points < 3) {
163 // Contours with less than three points do not have volume and are invisible.
164 // Contours with one point are used for anchors when compositing compound glyphs.
165 return {};
166 }
167
168 return makeContourFromPoints(beginContour(contourNr), endContour(contourNr));
169 }
170
171 [[nodiscard]] std::vector<bezier_curve> getBeziers() const noexcept
172 {
173 hi_assert(!hasLayers());
174
175 std::vector<bezier_curve> r;
176
177 for (auto contourNr = 0; contourNr < numberOfContours(); contourNr++) {
178 auto const beziers = getBeziersOfContour(contourNr);
179 r.insert(r.end(), beziers.begin(), beziers.end());
180 }
181 return r;
182 }
183
184 [[nodiscard]] std::pair<graphic_path, color> getLayer(ssize_t layerNr) const noexcept
185 {
186 hi_assert(hasLayers());
187
188 auto path = graphic_path{};
189
190 auto const begin = beginLayer(layerNr);
191 auto const end = endLayer(layerNr);
192 for (ssize_t contourNr = begin; contourNr != end; contourNr++) {
193 path.addContour(beginContour(contourNr), endContour(contourNr));
194 }
195
196 return {path, getColorOfLayer(layerNr)};
197 }
198
199 [[nodiscard]] color getColorOfLayer(ssize_t layerNr) const noexcept
200 {
201 return layerEndContours.at(layerNr).second;
202 }
203
204 void setColorOfLayer(ssize_t layerNr, color fill_color) noexcept
205 {
206 layerEndContours.at(layerNr).second = fill_color;
207 }
208
211 [[nodiscard]] bool isContourOpen() const noexcept
212 {
213 if (points.size() == 0) {
214 return false;
215 } else if (contourEndPoints.size() == 0) {
216 return true;
217 } else {
218 return contourEndPoints.back() != (ssize(points) - 1);
219 }
220 }
221
225 void closeContour() noexcept
226 {
227 if (isContourOpen()) {
228 contourEndPoints.push_back(ssize(points) - 1);
229 }
230 }
231
234 [[nodiscard]] bool hasLayers() const noexcept
235 {
236 return numberOfLayers() > 0;
237 }
238
241 [[nodiscard]] bool isLayerOpen() const noexcept
242 {
243 if (points.size() == 0) {
244 return false;
245 } else if (isContourOpen()) {
246 return true;
247 } else if (layerEndContours.size() == 0) {
248 return true;
249 } else {
250 return layerEndContours.back().first != (ssize(contourEndPoints) - 1);
251 }
252 }
253
257 void closeLayer(color fill_color) noexcept
258 {
259 closeContour();
260 if (isLayerOpen()) {
261 layerEndContours.emplace_back(ssize(contourEndPoints) - 1, fill_color);
262 }
263 }
264
268 void optimizeLayers() noexcept
269 {
270 if (ssize(layerEndContours) == 0) {
271 return;
272 }
273
274 decltype(layerEndContours) tmp;
275 tmp.reserve(ssize(layerEndContours));
276
277 auto prev_i = layerEndContours.begin();
278 for (auto i = prev_i + 1; i != layerEndContours.end(); ++i) {
279 // Add the last layer of a contiguous color.
280 if (prev_i->second != i->second) {
281 tmp.push_back(*prev_i);
282 }
283
284 prev_i = i;
285 }
286 tmp.push_back(*prev_i);
287
289 }
290
294 [[nodiscard]] point2 currentPosition() const noexcept
295 {
296 if (isContourOpen()) {
297 return points.back().p;
298 } else {
299 return point2{};
300 }
301 }
302
306 void moveTo(point2 position) noexcept
307 {
308 closeContour();
309 points.emplace_back(position, bezier_point::Type::Anchor);
310 }
311
315 void moveRelativeTo(vector2 direction) noexcept
316 {
317 hi_assert(isContourOpen());
318
319 auto const lastPosition = currentPosition();
320 closeContour();
321 points.emplace_back(lastPosition + direction, bezier_point::Type::Anchor);
322 }
323
324 void lineTo(point2 position) noexcept
325 {
326 hi_assert(isContourOpen());
327
328 points.emplace_back(position, bezier_point::Type::Anchor);
329 }
330
331 void lineRelativeTo(vector2 direction) noexcept
332 {
333 hi_assert(isContourOpen());
334
335 points.emplace_back(currentPosition() + direction, bezier_point::Type::Anchor);
336 }
337
338 void quadraticCurveTo(point2 controlPosition, point2 position) noexcept
339 {
340 hi_assert(isContourOpen());
341
342 points.emplace_back(controlPosition, bezier_point::Type::QuadraticControl);
343 points.emplace_back(position, bezier_point::Type::Anchor);
344 }
345
350 void quadraticCurveRelativeTo(vector2 controlDirection, vector2 direction) noexcept
351 {
352 hi_assert(isContourOpen());
353
354 auto const p = currentPosition();
355 points.emplace_back(p + controlDirection, bezier_point::Type::QuadraticControl);
356 points.emplace_back(p + direction, bezier_point::Type::Anchor);
357 }
358
359 void cubicCurveTo(point2 controlPosition1, point2 controlPosition2, point2 position) noexcept
360 {
361 hi_assert(isContourOpen());
362
363 points.emplace_back(controlPosition1, bezier_point::Type::CubicControl1);
364 points.emplace_back(controlPosition2, bezier_point::Type::CubicControl2);
365 points.emplace_back(position, bezier_point::Type::Anchor);
366 }
367
373 void cubicCurveRelativeTo(vector2 controlDirection1, vector2 controlDirection2, vector2 direction) noexcept
374 {
375 hi_assert(isContourOpen());
376
377 auto const p = currentPosition();
378 points.emplace_back(p + controlDirection1, bezier_point::Type::CubicControl1);
379 points.emplace_back(p + controlDirection2, bezier_point::Type::CubicControl2);
380 points.emplace_back(p + direction, bezier_point::Type::Anchor);
381 }
382
394 void arcTo(float radius, point2 position) noexcept
395 {
396 hi_assert(isContourOpen());
397
398 auto const r = std::abs(radius);
399 auto const P1 = currentPosition();
400 auto const P2 = position;
401 auto const Pm = midpoint(P1, P2);
402
403 auto const Vm2 = P2 - Pm;
404
405 // Calculate the half angle between vectors P0 - C and P2 - C.
406 auto const alpha = std::asin(hypot(Vm2) / r);
407
408 // Calculate the center point C. As the length of the normal of Vm2 at Pm.
409 auto const C = Pm + normal(Vm2) * std::cos(alpha) * radius;
410
411 // Calculate vectors from center to end points.
412 auto const VC1 = P1 - C;
413 auto const VC2 = P2 - C;
414
415 auto const q1 = squared_hypot(VC1);
416 auto const q2 = q1 + dot(VC1, VC2);
417 auto const k2 = (4.0f / 3.0f) * (std::sqrt(2.0f * q1 * q2) - q2) / cross(VC1, VC2);
418
419 // Calculate the control points.
420 auto const C1 = point2{(C.x() + VC1.x()) - k2 * VC1.y(), (C.y() + VC1.y()) + k2 * VC1.x()};
421 auto const C2 = point2{(C.x() + VC2.x()) + k2 * VC2.y(), (C.y() + VC2.y()) - k2 * VC2.x()};
422
423 cubicCurveTo(C1, C2, P2);
424 }
425
431 void addRectangle(aarectangle rectangle, corner_radii corners = corner_radii{0.0f, 0.0f, 0.0f, 0.0f}) noexcept
432 {
433 hi_assert(!isContourOpen());
434
435 auto const bl_radius = std::abs(corners.left_bottom());
436 auto const br_radius = std::abs(corners.right_bottom());
437 auto const tl_radius = std::abs(corners.left_top());
438 auto const tr_radius = std::abs(corners.right_top());
439
440 auto const blc = get<0>(rectangle);
441 auto const brc = get<1>(rectangle);
442 auto const tlc = get<2>(rectangle);
443 auto const trc = get<3>(rectangle);
444
445 auto const blc1 = blc + vector2{0.0f, bl_radius};
446 auto const blc2 = blc + vector2{bl_radius, 0.0f};
447 auto const brc1 = brc + vector2{-br_radius, 0.0f};
448 auto const brc2 = brc + vector2{0.0f, br_radius};
449 auto const tlc1 = tlc + vector2{tl_radius, 0.0f};
450 auto const tlc2 = tlc + vector2{0.0f, -tl_radius};
451 auto const trc1 = trc + vector2{0.0f, -tr_radius};
452 auto const trc2 = trc + vector2{-tr_radius, 0.0f};
453
454 moveTo(blc1);
455 if (corners.left_bottom() > 0.0) {
456 arcTo(bl_radius, blc2);
457 } else if (corners.left_bottom() < 0.0) {
458 lineTo(blc2);
459 }
460
461 lineTo(brc1);
462 if (corners.right_bottom() > 0.0) {
463 arcTo(br_radius, brc2);
464 } else if (corners.right_bottom() < 0.0) {
465 lineTo(blc2);
466 }
467
468 lineTo(tlc1);
469 if (corners.left_top() > 0.0) {
470 arcTo(tl_radius, tlc2);
471 } else if (corners.left_top() < 0.0) {
472 lineTo(tlc2);
473 }
474
475 lineTo(trc1);
476 if (corners.right_top() > 0.0) {
477 arcTo(tr_radius, trc2);
478 } else if (corners.right_top() < 0.0) {
479 lineTo(trc2);
480 }
481
482 closeContour();
483 }
484
489 void addCircle(point2 position, float radius) noexcept
490 {
491 hi_assert(!isContourOpen());
492
493 moveTo(point2{position.x(), position.y() - radius});
494 arcTo(radius, point2{position.x() + radius, position.y()});
495 arcTo(radius, point2{position.x(), position.y() + radius});
496 arcTo(radius, point2{position.x() - radius, position.y()});
497 arcTo(radius, point2{position.x(), position.y() - radius});
498 closeContour();
499 }
500
504 void addContour(std::vector<bezier_curve> const& contour) noexcept
505 {
506 hi_assert(!isContourOpen());
507
508 for (auto const& curve : contour) {
509 // Don't emit the first point, the last point of the contour will wrap around.
510 switch (curve.type) {
511 case bezier_curve::Type::Linear:
512 points.emplace_back(curve.P2, bezier_point::Type::Anchor);
513 break;
514 case bezier_curve::Type::Quadratic:
515 points.emplace_back(curve.C1, bezier_point::Type::QuadraticControl);
516 points.emplace_back(curve.P2, bezier_point::Type::Anchor);
517 break;
518 case bezier_curve::Type::Cubic:
519 points.emplace_back(curve.C1, bezier_point::Type::CubicControl1);
520 points.emplace_back(curve.C2, bezier_point::Type::CubicControl2);
521 points.emplace_back(curve.P2, bezier_point::Type::Anchor);
522 break;
523 default:
524 hi_no_default();
525 }
526 }
527
528 closeContour();
529 }
530
535 std::vector<bezier_point>::const_iterator const& begin,
536 std::vector<bezier_point>::const_iterator const& end) noexcept
537 {
538 hi_assert(!isContourOpen());
539 points.insert(points.end(), begin, end);
540 closeContour();
541 }
542
546 void addContour(std::vector<bezier_point> const& contour) noexcept
547 {
548 addContour(contour.begin(), contour.end());
549 }
550
553 void addPath(graphic_path const& path, color fill_color) noexcept
554 {
555 *this += path;
556 closeLayer(fill_color);
557 }
558
562 graphic_path const& path,
563 color strokeColor,
564 float strokeWidth,
566 float tolerance = 0.05f) noexcept
567 {
568 *this += path.toStroke(strokeWidth, line_join_style, tolerance);
569 closeLayer(strokeColor);
570 }
571
583 [[nodiscard]] graphic_path toStroke(
584 float strokeWidth = 1.0f,
586 float tolerance = 0.05f) const noexcept
587 {
588 hi_assert(!hasLayers());
589 hi_assert(!isContourOpen());
590
591 auto r = graphic_path{};
592
593 float starboardOffset = strokeWidth / 2;
594 float portOffset = -starboardOffset;
595
596 for (int i = 0; i < numberOfContours(); i++) {
597 auto const baseContour = getBeziersOfContour(i);
598
599 auto const starboardContour = makeParallelContour(baseContour, starboardOffset, line_join_style, tolerance);
600 r.addContour(starboardContour);
601
602 auto const portContour = makeInverseContour(makeParallelContour(baseContour, portOffset, line_join_style, tolerance));
603 r.addContour(portContour);
604 }
605
606 return r;
607 }
608
611 [[nodiscard]] graphic_path centerScale(extent2 extent, float padding = 0.0) const noexcept
612 {
613 auto max_size =
614 extent2{std::max(1.0f, extent.width() - (padding * 2.0f)), std::max(1.0f, extent.height() - (padding * 2.0f))};
615
616 auto bbox = boundingBox();
617 if (bbox.width() <= 0.0 || bbox.height() <= 0.0) {
618 return {};
619 }
620
621 auto const scale = std::min(max_size.width() / bbox.width(), max_size.height() / bbox.height());
622 bbox = scale2(scale) * bbox;
623
624 auto const offset = (point2{} - get<0>(bbox)) + (extent - bbox.size()) * 0.5;
625
626 return (translate2(offset) * scale2(scale, scale)) * *this;
627 }
628
629 graphic_path& operator+=(graphic_path const& rhs) noexcept
630 {
631 hi_assert(!isContourOpen());
632 hi_assert(!rhs.isContourOpen());
633
634 // Left hand layer can only be open if the right hand side contains no layers.
635 hi_assert(!rhs.hasLayers() || !isLayerOpen());
636
637 auto const pointOffset = ssize(points);
638 auto const contourOffset = ssize(contourEndPoints);
639
640 layerEndContours.reserve(layerEndContours.size() + rhs.layerEndContours.size());
641 for (auto const & [ x, fill_color ] : rhs.layerEndContours) {
642 layerEndContours.emplace_back(contourOffset + x, fill_color);
643 }
644
645 contourEndPoints.reserve(contourEndPoints.size() + rhs.contourEndPoints.size());
646 for (auto const x : rhs.contourEndPoints) {
647 contourEndPoints.push_back(pointOffset + x);
648 }
649
650 points.insert(points.end(), rhs.points.begin(), rhs.points.end());
651 return *this;
652 }
653
654 [[nodiscard]] friend graphic_path operator+(graphic_path lhs, graphic_path const& rhs) noexcept
655 {
656 return lhs += rhs;
657 }
658
659 friend graphic_path operator*(transformer2 auto const& lhs, graphic_path const& rhs) noexcept
660 {
661 auto rhs_ = rhs;
662 for (auto& point : rhs_.points) {
663 point = lhs * point;
664 }
665 return rhs_;
666 }
667};
668
673hi_export inline void fill(pixmap_span<sdf_r8> dst, graphic_path const& path) noexcept
674{
675 fill(dst, path.getBeziers());
676}
677
678}} // namespace hi::v1
679
680hi_warning_pop();
Defined the color type.
@ 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
The HikoGUI namespace.
Definition array_generic.hpp:21
The HikoGUI API version 1.
Definition array_generic.hpp:22
@ color
A color value was modified.
Definition style_modify_mask.hpp:27
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
constexpr std::vector< bezier_curve > makeInverseContour(std::vector< bezier_curve > const &contour) noexcept
Inverse a contour.
Definition bezier_curve.hpp:618
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
This is a RGBA floating point color.
Definition color_intf.hpp:49
Class which represents an axis-aligned rectangle.
Definition aarectangle.hpp:33
The 4 radii of the corners of a quad or rectangle.
Definition corner_radii.hpp:26
A high-level geometric extent.
Definition extent2.hpp:32
constexpr float & width() noexcept
Access the x-as-width element from the extent.
Definition extent2.hpp:107
constexpr float & height() noexcept
Access the y-as-height element from the extent.
Definition extent2.hpp:118
A rectangle / parallelogram in 3D space.
Definition rectangle.hpp:25
Definition scale2.hpp:18
Definition translate2.hpp:18
A high-level geometric vector Part of the high-level vector, point, mat and color types.
Definition vector2.hpp:27
A path is a vector graphics object.
Definition graphic_path.hpp:34
void addContour(std::vector< bezier_point >::const_iterator const &begin, std::vector< bezier_point >::const_iterator const &end) noexcept
Curve with the given bezier curve.
Definition graphic_path.hpp:534
void addRectangle(aarectangle rectangle, corner_radii corners=corner_radii{0.0f, 0.0f, 0.0f, 0.0f}) noexcept
Draw a rectangle.
Definition graphic_path.hpp:431
void clear() noexcept
Clear the path.
Definition graphic_path.hpp:49
void addStroke(graphic_path const &path, color strokeColor, float strokeWidth, hi::line_join_style line_join_style=line_join_style::miter, float tolerance=0.05f) noexcept
Stroke a path and close layer.
Definition graphic_path.hpp:561
std::vector< bezier_point > points
A set of all bezier points describing all bezier curves, contours and layers.
Definition graphic_path.hpp:37
void optimizeLayers() noexcept
Optimize layers.
Definition graphic_path.hpp:268
void closeLayer(color fill_color) noexcept
Close current contour.
Definition graphic_path.hpp:257
std::vector< bezier_point >::const_iterator beginContour(ssize_t contourNr) const noexcept
Return an iterator to the start point of a contour.
Definition graphic_path.hpp:124
void addContour(std::vector< bezier_curve > const &contour) noexcept
Contour with the given bezier curves.
Definition graphic_path.hpp:504
void addContour(std::vector< bezier_point > const &contour) noexcept
Curve with the given bezier curve.
Definition graphic_path.hpp:546
aarectangle boundingBox() const noexcept
Calculate bounding box.
Definition graphic_path.hpp:90
graphic_path toStroke(float strokeWidth=1.0f, line_join_style line_join_style=line_join_style::miter, float tolerance=0.05f) const noexcept
Convert path to stroke-path.
Definition graphic_path.hpp:583
bool allLayersHaveSameColor() const noexcept
Check if all layers have the same color.
Definition graphic_path.hpp:72
ssize_t numberOfLayers() const noexcept
Return the number of closed layers.
Definition graphic_path.hpp:65
void cubicCurveRelativeTo(vector2 controlDirection1, vector2 controlDirection2, vector2 direction) noexcept
Draw curve from the current position to the new direction.
Definition graphic_path.hpp:373
ssize_t numberOfContours() const noexcept
Return the number of closed contours.
Definition graphic_path.hpp:58
std::vector< std::pair< ssize_t, color > > layerEndContours
An color and index into.
Definition graphic_path.hpp:45
void arcTo(float radius, point2 position) noexcept
Draw an circular arc.
Definition graphic_path.hpp:394
void closeContour() noexcept
Close current contour.
Definition graphic_path.hpp:225
void addPath(graphic_path const &path, color fill_color) noexcept
Add path and close layer.
Definition graphic_path.hpp:553
void addCircle(point2 position, float radius) noexcept
Draw a circle.
Definition graphic_path.hpp:489
void moveTo(point2 position) noexcept
Start a new contour at position.
Definition graphic_path.hpp:306
point2 currentPosition() const noexcept
Get the currentPosition of the open contour.
Definition graphic_path.hpp:294
bool hasLayers() const noexcept
This path has layers.
Definition graphic_path.hpp:234
std::vector< ssize_t > contourEndPoints
An index into.
Definition graphic_path.hpp:41
bool isContourOpen() const noexcept
Return true if there is an open contour.
Definition graphic_path.hpp:211
void quadraticCurveRelativeTo(vector2 controlDirection, vector2 direction) noexcept
Draw curve from the current position to the new direction.
Definition graphic_path.hpp:350
bool isLayerOpen() const noexcept
Return true if there is an open layer.
Definition graphic_path.hpp:241
void moveRelativeTo(vector2 direction) noexcept
Start a new contour relative to current position.
Definition graphic_path.hpp:315
graphic_path centerScale(extent2 extent, float padding=0.0) const noexcept
Center and scale a path inside the extent with padding.
Definition graphic_path.hpp:611
void tryRemoveLayers() noexcept
Try to move the layers in a path.
Definition graphic_path.hpp:109
A non-owning 2D pixel-based image.
Definition pixmap_span.hpp:34
T asin(T... args)
T cos(T... args)
T distance(T... args)
T end(T... args)
T insert(T... args)
T max(T... args)
T min(T... args)
T sqrt(T... args)
T swap(T... args)