15 AddContourComponent({});
21 size_t contour_index)
const {
22 if (contour_index >=
contours.size()) {
25 const size_t start_index =
contours.at(contour_index).start_index;
26 const size_t end_index = (contour_index >=
contours.size() - 1)
28 :
contours.at(contour_index + 1).start_index;
29 return std::make_tuple(start_index, end_index);
33 if (type.has_value()) {
34 switch (type.value()) {
36 return linears_.size();
40 return cubics_.size();
42 return contours_.size();
45 return components_.size();
48 void Path::SetFillType(
FillType fill) {
60 void Path::SetConvexity(
Convexity value) {
64 void Path::Shift(
Point shift) {
65 size_t currentIndex = 0;
66 for (
const auto& component : components_) {
67 switch (component.type) {
69 linears_[component.index].p1 += shift;
70 linears_[component.index].p2 += shift;
73 quads_[component.index].cp += shift;
74 quads_[component.index].p1 += shift;
75 quads_[component.index].p2 += shift;
78 cubics_[component.index].cp1 += shift;
79 cubics_[component.index].cp2 += shift;
80 cubics_[component.index].p1 += shift;
81 cubics_[component.index].p2 += shift;
84 contours_[component.index].destination += shift;
92 linears_.emplace_back(p1, p2);
98 quads_.emplace_back(p1, cp, p2);
104 cubics_.emplace_back(p1, cp1, cp2, p2);
109 Path& Path::AddContourComponent(
Point destination,
bool is_closed) {
110 if (components_.size() > 0 &&
113 contours_.back() = ContourComponent(destination, is_closed);
115 contours_.emplace_back(ContourComponent(destination, is_closed));
121 void Path::SetContourClosed(
bool is_closed) {
122 contours_.back().is_closed = is_closed;
130 size_t currentIndex = 0;
131 for (
const auto& component : components_) {
132 switch (component.type) {
134 if (linear_applier) {
135 linear_applier(currentIndex, linears_[component.index]);
140 quad_applier(currentIndex, quads_[component.index]);
145 cubic_applier(currentIndex, cubics_[component.index]);
149 if (contour_applier) {
150 contour_applier(currentIndex, contours_[component.index]);
160 if (index >= components_.size()) {
168 linear = linears_[components_[index].index];
175 if (index >= components_.size()) {
183 quadratic = quads_[components_[index].index];
189 if (index >= components_.size()) {
197 cubic = cubics_[components_[index].index];
203 if (index >= components_.size()) {
211 move = contours_[components_[index].index];
215 bool Path::UpdateLinearComponentAtIndex(
size_t index,
217 if (index >= components_.size()) {
225 linears_[components_[index].index] = linear;
229 bool Path::UpdateQuadraticComponentAtIndex(
231 const QuadraticPathComponent& quadratic) {
232 if (index >= components_.size()) {
240 quads_[components_[index].index] = quadratic;
244 bool Path::UpdateCubicComponentAtIndex(
size_t index,
245 CubicPathComponent& cubic) {
246 if (index >= components_.size()) {
254 cubics_[components_[index].index] = cubic;
258 bool Path::UpdateContourComponentAtIndex(
size_t index,
259 const ContourComponent& move) {
260 if (index >= components_.size()) {
268 contours_[components_[index].index] = move;
275 std::optional<Point> previous_contour_point;
276 auto collect_points = [&polyline, &previous_contour_point](
277 const std::vector<Point>& collection) {
278 if (collection.empty()) {
282 for (
const auto& point : collection) {
283 if (previous_contour_point.has_value() &&
284 previous_contour_point.value() == point) {
288 previous_contour_point = point;
289 polyline.
points.push_back(point);
294 if (component_i >= components_.size()) {
295 return std::monostate{};
297 const auto& component = components_[component_i];
298 switch (component.type) {
300 return &linears_[component.index];
302 return &quads_[component.index];
304 return &cubics_[component.index];
306 return std::monostate{};
310 auto compute_contour_start_direction =
311 [&get_path_component](
size_t current_path_component_index) {
312 size_t next_component_index = current_path_component_index + 1;
313 while (!std::holds_alternative<std::monostate>(
314 get_path_component(next_component_index))) {
315 auto next_component = get_path_component(next_component_index);
318 if (maybe_vector.has_value()) {
319 return maybe_vector.value();
321 next_component_index++;
327 std::optional<size_t> previous_path_component_index;
328 auto end_contour = [&polyline, &previous_path_component_index,
329 &get_path_component]() {
336 if (!previous_path_component_index.has_value()) {
340 auto& contour = polyline.
contours.back();
341 contour.end_direction =
Vector2(0, 1);
343 size_t previous_index = previous_path_component_index.value();
344 while (!std::holds_alternative<std::monostate>(
345 get_path_component(previous_index))) {
346 auto previous_component = get_path_component(previous_index);
349 if (maybe_vector.has_value()) {
350 contour.end_direction = maybe_vector.value();
353 if (previous_index == 0) {
361 for (
size_t component_i = 0; component_i < components_.size();
363 const auto& component = components_[component_i];
364 switch (component.type) {
366 collect_points(linears_[component.index].CreatePolyline());
367 previous_path_component_index = component_i;
370 collect_points(quads_[component.index].CreatePolyline(scale));
371 previous_path_component_index = component_i;
374 collect_points(cubics_[component.index].CreatePolyline(scale));
375 previous_path_component_index = component_i;
378 if (component_i == components_.size() - 1) {
385 Vector2 start_direction = compute_contour_start_direction(component_i);
386 const auto& contour = contours_[component.index];
387 polyline.
contours.push_back({.start_index = polyline.
points.size(),
388 .is_closed = contour.is_closed,
389 .start_direction = start_direction});
390 previous_contour_point = std::nullopt;
391 collect_points({contour.destination});
400 return computed_bounds_;
403 void Path::ComputeBounds() {
405 if (!min_max.has_value()) {
406 computed_bounds_ = std::nullopt;
409 auto min = min_max->first;
410 auto max = min_max->second;
411 const auto difference = max - min;
412 computed_bounds_ =
Rect{min.x, min.y, difference.x, difference.y};
416 const Matrix& transform)
const {
418 if (!bounds.has_value()) {
421 return bounds->TransformBounds(transform);
425 if (linears_.empty() && quads_.empty() && cubics_.empty()) {
429 std::optional<Point> min, max;
431 auto clamp = [&min, &max](
const Point& point) {
432 if (min.has_value()) {
433 min = min->Min(point);
438 if (max.has_value()) {
439 max = max->Max(point);
445 for (
const auto& linear : linears_) {
450 for (
const auto& quad : quads_) {
451 for (
const Point& point : quad.Extrema()) {
456 for (
const auto& cubic : cubics_) {
457 for (
const Point& point : cubic.Extrema()) {
462 if (!min.has_value() || !max.has_value()) {
466 return std::make_pair(min.value(), max.value());
469 void Path::SetBounds(
Rect rect) {
470 computed_bounds_ = rect;