From 0e258de9eac7c6fafd97285da91eae06fe4315f6 Mon Sep 17 00:00:00 2001 From: luckysmg <2539699336@qq.com> Date: Tue, 7 Mar 2023 20:54:05 +0800 Subject: [PATCH 1/6] [Impeller] Fix incorrect rendering when duplicated path component exists --- .../display_list/display_list_unittests.cc | 194 ++++++++++++++---- impeller/geometry/path.cc | 76 +++++-- impeller/geometry/path.h | 9 +- impeller/geometry/path_component.cc | 6 + 4 files changed, 226 insertions(+), 59 deletions(-) diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc index 0c5c9565b69f3..99e2abb4acb69 100644 --- a/impeller/display_list/display_list_unittests.cc +++ b/impeller/display_list/display_list_unittests.cc @@ -188,49 +188,173 @@ TEST_P(DisplayListTest, CanDrawArc) { } TEST_P(DisplayListTest, StrokedPathsDrawCorrectly) { - flutter::DisplayListBuilder builder; - builder.setColor(SK_ColorRED); - builder.setStyle(flutter::DlDrawStyle::kStroke); - builder.setStrokeWidth(10); + auto callback = [&]() { + flutter::DisplayListBuilder builder; + builder.setColor(SK_ColorRED); + builder.setStyle(flutter::DlDrawStyle::kStroke); - // Rectangle - builder.translate(100, 100); - builder.drawRect(SkRect::MakeSize({100, 100})); + static float stroke_width = 10.0f; + static int selected_stroke_type = 0; + static int selected_join_type = 0; + const char* stroke_types[] = {"Butte", "Round", "Square"}; + const char* join_type[] = {"kMiter", "Round", "kBevel"}; - // Rounded rectangle - builder.translate(150, 0); - builder.drawRRect(SkRRect::MakeRectXY(SkRect::MakeSize({100, 50}), 10, 10)); + ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize); + ImGui::Combo("Cap", &selected_stroke_type, stroke_types, + sizeof(stroke_types) / sizeof(char*)); + ImGui::Combo("Join", &selected_join_type, join_type, + sizeof(join_type) / sizeof(char*)); + ImGui::SliderFloat("Stroke Width", &stroke_width, 10.0f, 50.0f); + ImGui::End(); - // Double rounded rectangle - builder.translate(150, 0); - builder.drawDRRect( - SkRRect::MakeRectXY(SkRect::MakeSize({100, 50}), 10, 10), - SkRRect::MakeRectXY(SkRect::MakeXYWH(10, 10, 80, 30), 10, 10)); + flutter::DlStrokeCap cap; + flutter::DlStrokeJoin join; + switch (selected_stroke_type) { + case 0: + cap = flutter::DlStrokeCap::kButt; + break; + case 1: + cap = flutter::DlStrokeCap::kRound; + break; + case 2: + cap = flutter::DlStrokeCap::kSquare; + break; + default: + cap = flutter::DlStrokeCap::kButt; + break; + } + switch (selected_join_type) { + case 0: + join = flutter::DlStrokeJoin::kMiter; + break; + case 1: + join = flutter::DlStrokeJoin::kRound; + break; + case 2: + join = flutter::DlStrokeJoin::kBevel; + break; + default: + join = flutter::DlStrokeJoin::kMiter; + break; + } + builder.setStrokeCap(cap); + builder.setStrokeJoin(join); + builder.setStrokeWidth(stroke_width); - // Contour with duplicate join points - { + // Make rendering better to watch. + builder.scale(1.5f, 1.5f); + + // Rectangle + builder.translate(100, 100); + builder.drawRect(SkRect::MakeSize({100, 100})); + + // Rounded rectangle builder.translate(150, 0); - SkPath path; - path.lineTo({100, 0}); - path.lineTo({100, 0}); - path.lineTo({100, 100}); - builder.drawPath(path); - } + builder.drawRRect(SkRRect::MakeRectXY(SkRect::MakeSize({100, 50}), 10, 10)); - // Contour with duplicate end points - { - builder.setStrokeCap(flutter::DlStrokeCap::kRound); + // Double rounded rectangle builder.translate(150, 0); - SkPath path; - path.moveTo(0, 0); - path.lineTo({0, 0}); - path.lineTo({50, 50}); - path.lineTo({100, 0}); - path.lineTo({100, 0}); - builder.drawPath(path); - } + builder.drawDRRect( + SkRRect::MakeRectXY(SkRect::MakeSize({100, 50}), 10, 10), + SkRRect::MakeRectXY(SkRect::MakeXYWH(10, 10, 80, 30), 10, 10)); - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); + // Contour with duplicate join points + { + builder.translate(150, 0); + SkPath path; + path.moveTo(0, 0); + path.lineTo(0, 0); + path.lineTo({100, 0}); + path.lineTo({100, 0}); + path.lineTo({100, 100}); + builder.drawPath(path); + } + + // Contour with duplicate start and end points + + // Line. + builder.translate(200, 0); + { + builder.save(); + + SkPath line_path; + line_path.moveTo(0, 0); + line_path.moveTo(0, 0); + line_path.lineTo({0, 0}); + line_path.lineTo({0, 0}); + line_path.lineTo({50, 50}); + line_path.lineTo({50, 50}); + line_path.lineTo({100, 0}); + line_path.lineTo({100, 0}); + builder.drawPath(line_path); + + builder.translate(0, 100); + builder.drawPath(line_path); + + builder.translate(0, 100); + SkPath line_path2; + line_path2.moveTo(0, 0); + line_path2.lineTo(0, 0); + line_path2.lineTo(0, 0); + builder.drawPath(line_path2); + + builder.restore(); + } + + // Cubic. + builder.translate(150, 0); + { + builder.save(); + + SkPath cubic_path; + cubic_path.moveTo({0, 0}); + cubic_path.cubicTo(0, 0, 140.0, 100.0, 140, 20); + builder.drawPath(cubic_path); + + builder.translate(0, 100); + SkPath cubic_path2; + cubic_path2.moveTo({0, 0}); + cubic_path2.cubicTo(0, 0, 0, 0, 150, 0); + builder.drawPath(cubic_path2); + + builder.translate(0, 100); + SkPath cubic_path3; + cubic_path3.moveTo({0, 0}); + cubic_path3.cubicTo(0, 0, 0, 0, 0, 0); + builder.drawPath(cubic_path3); + + builder.restore(); + } + + // Quad. + builder.translate(200, 0); + { + builder.save(); + + SkPath quad_path; + quad_path.moveTo(0, 0); + quad_path.moveTo(0, 0); + quad_path.quadTo({100, 40}, {50, 80}); + builder.drawPath(quad_path); + + builder.translate(0, 150); + SkPath quad_path2; + quad_path2.moveTo(0, 0); + quad_path2.moveTo(0, 0); + quad_path2.quadTo({0, 0}, {100, 100}); + builder.drawPath(quad_path2); + + builder.translate(0, 100); + SkPath quad_path3; + quad_path3.moveTo(0, 0); + quad_path3.quadTo({0, 0}, {0, 0}); + builder.drawPath(quad_path3); + + builder.restore(); + } + return builder.Build(); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); } TEST_P(DisplayListTest, CanDrawWithOddPathWinding) { diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index e018b0ff38e55..068817d6d4d10 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -244,8 +244,10 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { }; auto get_path_component = - [this](size_t component_i) -> std::optional { - if (component_i >= components_.size()) { + [this](size_t component_i, + bool& isContour) -> std::optional { + if (component_i >= components_.size() || component_i < 0) { + isContour = false; return std::nullopt; } const auto& component = components_[component_i]; @@ -257,24 +259,44 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { case ComponentType::kCubic: return &cubics_[component.index]; case ComponentType::kContour: + isContour = true; return std::nullopt; } }; - std::optional previous_path_component; - auto end_contour = [&polyline, &previous_path_component]() { + // std::optional previous_path_component; + auto end_contour = [&polyline, + &get_path_component](int current_component_index) { // Whenever a contour has ended, extract the exact end direction from the // last component. if (polyline.contours.empty()) { return; } - if (!previous_path_component.has_value()) { - return; - } + auto& contour = polyline.contours.back(); - contour.end_direction = - previous_path_component.value()->GetEndDirection().value_or( - Vector2(0, 1)); + contour.end_direction = Vector2(0, 1); + int previous_path_component_index = current_component_index; + bool isContour = false; + while (get_path_component(previous_path_component_index, isContour) + .has_value() && + !isContour) { + auto previous_path_component = + get_path_component(previous_path_component_index, isContour).value(); + if (previous_path_component->GetEndDirection().has_value()) { + contour.end_direction = + previous_path_component->GetEndDirection().value(); + break; + } else { + previous_path_component_index--; + } + } + // if (!previous_path_component.has_value()) { + // return; + // } + // auto& contour = polyline.contours.back(); + // contour.end_direction = + // previous_path_component.value()->GetEndDirection().value_or( + // Vector2(0, 1)); }; for (size_t component_i = 0; component_i < components_.size(); @@ -283,15 +305,15 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { switch (component.type) { case ComponentType::kLinear: collect_points(linears_[component.index].CreatePolyline()); - previous_path_component = &linears_[component.index]; + // previous_path_component = &linears_[component.index]; break; case ComponentType::kQuadratic: collect_points(quads_[component.index].CreatePolyline(scale)); - previous_path_component = &quads_[component.index]; + // previous_path_component = &quads_[component.index]; break; case ComponentType::kCubic: collect_points(cubics_[component.index].CreatePolyline(scale)); - previous_path_component = &cubics_[component.index]; + // previous_path_component = &cubics_[component.index]; break; case ComponentType::kContour: if (component_i == components_.size() - 1) { @@ -299,16 +321,30 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { // contour, so skip it. continue; } - end_contour(); + end_contour(component_i); Vector2 start_direction(0, -1); - auto first_component = get_path_component(component_i + 1); - if (first_component.has_value()) { - start_direction = - first_component.value()->GetStartDirection().value_or( - Vector2(0, -1)); + int next_component_index = component_i + 1; + bool isContour = false; + while ( + get_path_component(next_component_index, isContour).has_value()) { + auto next_component = + get_path_component(next_component_index, isContour).value(); + if (next_component->GetStartDirection().has_value()) { + start_direction = next_component->GetStartDirection().value(); + break; + } else { + next_component_index++; + } } + // auto first_component = get_path_component(component_i + 1); + // if (first_component.has_value()) { + // start_direction = + // first_component.value()->GetStartDirection().value_or( + // Vector2(0, -1)); + // } + const auto& contour = contours_[component.index]; polyline.contours.push_back({.start_index = polyline.points.size(), .is_closed = contour.is_closed, @@ -317,7 +353,7 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { collect_points({contour.destination}); break; } - end_contour(); + end_contour(component_i); } return polyline; } diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 0c4cf6939950a..8a1e17054d74a 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -123,12 +123,13 @@ class Path { bool UpdateContourComponentAtIndex(size_t index, const ContourComponent& contour); - /// Callers must provide the scale factor for how this path will be - /// transformed. + /// Callers must provide the current_component_index factor for how this path + /// will be transformed. /// /// It is suitable to use the max basis length of the matrix used to transform - /// the path. If the provided scale is 0, curves will revert to lines. - Polyline CreatePolyline(Scalar scale) const; + /// the path. If the provided current_component_index is 0, curves will revert + /// to lines. + Polyline CreatePolyline(Scalar current_component_index) const; std::optional GetBoundingBox() const; diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index de5ca44b200f0..4ba1527952663 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -70,10 +70,16 @@ std::vector LinearPathComponent::Extrema() const { } std::optional LinearPathComponent::GetStartDirection() const { + if (p1 == p2) { + return std::nullopt; + } return (p1 - p2).Normalize(); } std::optional LinearPathComponent::GetEndDirection() const { + if (p1 == p2) { + return std::nullopt; + } return (p2 - p1).Normalize(); } From bda9a3bff61ebc857922ee2727bb430063b110fe Mon Sep 17 00:00:00 2001 From: luckysmg <2539699336@qq.com> Date: Tue, 7 Mar 2023 20:58:35 +0800 Subject: [PATCH 2/6] ++ --- impeller/geometry/path.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 8a1e17054d74a..0c4cf6939950a 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -123,13 +123,12 @@ class Path { bool UpdateContourComponentAtIndex(size_t index, const ContourComponent& contour); - /// Callers must provide the current_component_index factor for how this path - /// will be transformed. + /// Callers must provide the scale factor for how this path will be + /// transformed. /// /// It is suitable to use the max basis length of the matrix used to transform - /// the path. If the provided current_component_index is 0, curves will revert - /// to lines. - Polyline CreatePolyline(Scalar current_component_index) const; + /// the path. If the provided scale is 0, curves will revert to lines. + Polyline CreatePolyline(Scalar scale) const; std::optional GetBoundingBox() const; From 3e09ddc493f8c03ff89604cf1471a1ddd1461833 Mon Sep 17 00:00:00 2001 From: luckysmg <2539699336@qq.com> Date: Wed, 8 Mar 2023 14:30:35 +0800 Subject: [PATCH 3/6] ++ --- impeller/geometry/path.cc | 83 ++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 50 deletions(-) diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 068817d6d4d10..9f39f5048ead3 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -244,10 +244,8 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { }; auto get_path_component = - [this](size_t component_i, - bool& isContour) -> std::optional { - if (component_i >= components_.size() || component_i < 0) { - isContour = false; + [this](size_t component_i) -> std::optional { + if (component_i < 0 || component_i >= components_.size()) { return std::nullopt; } const auto& component = components_[component_i]; @@ -259,44 +257,50 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { case ComponentType::kCubic: return &cubics_[component.index]; case ComponentType::kContour: - isContour = true; return std::nullopt; } }; - // std::optional previous_path_component; - auto end_contour = [&polyline, - &get_path_component](int current_component_index) { + auto start_contour = [&get_path_component](int current_path_component_index) { + int next_component_index = current_path_component_index + 1; + while (get_path_component(next_component_index).has_value()) { + auto next_component = get_path_component(next_component_index).value(); + if (next_component->GetStartDirection().has_value()) { + return next_component->GetStartDirection().value(); + } else { + next_component_index++; + } + } + return Vector2(0, -1); + }; + + std::optional previous_path_component_index; + auto end_contour = [&polyline, &previous_path_component_index, + &get_path_component]() { // Whenever a contour has ended, extract the exact end direction from the // last component. if (polyline.contours.empty()) { return; } + if (!previous_path_component_index.has_value()) { + return; + } + auto& contour = polyline.contours.back(); contour.end_direction = Vector2(0, 1); - int previous_path_component_index = current_component_index; - bool isContour = false; - while (get_path_component(previous_path_component_index, isContour) - .has_value() && - !isContour) { - auto previous_path_component = - get_path_component(previous_path_component_index, isContour).value(); + + int previous_index = previous_path_component_index.value(); + while (get_path_component(previous_index).has_value()) { + auto previous_path_component = get_path_component(previous_index).value(); if (previous_path_component->GetEndDirection().has_value()) { contour.end_direction = previous_path_component->GetEndDirection().value(); break; } else { - previous_path_component_index--; + previous_index--; } } - // if (!previous_path_component.has_value()) { - // return; - // } - // auto& contour = polyline.contours.back(); - // contour.end_direction = - // previous_path_component.value()->GetEndDirection().value_or( - // Vector2(0, 1)); }; for (size_t component_i = 0; component_i < components_.size(); @@ -305,15 +309,15 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { switch (component.type) { case ComponentType::kLinear: collect_points(linears_[component.index].CreatePolyline()); - // previous_path_component = &linears_[component.index]; + previous_path_component_index = component_i; break; case ComponentType::kQuadratic: collect_points(quads_[component.index].CreatePolyline(scale)); - // previous_path_component = &quads_[component.index]; + previous_path_component_index = component_i; break; case ComponentType::kCubic: collect_points(cubics_[component.index].CreatePolyline(scale)); - // previous_path_component = &cubics_[component.index]; + previous_path_component_index = component_i; break; case ComponentType::kContour: if (component_i == components_.size() - 1) { @@ -321,30 +325,9 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { // contour, so skip it. continue; } - end_contour(component_i); - - Vector2 start_direction(0, -1); - int next_component_index = component_i + 1; - bool isContour = false; - while ( - get_path_component(next_component_index, isContour).has_value()) { - auto next_component = - get_path_component(next_component_index, isContour).value(); - if (next_component->GetStartDirection().has_value()) { - start_direction = next_component->GetStartDirection().value(); - break; - } else { - next_component_index++; - } - } - - // auto first_component = get_path_component(component_i + 1); - // if (first_component.has_value()) { - // start_direction = - // first_component.value()->GetStartDirection().value_or( - // Vector2(0, -1)); - // } + end_contour(); + Vector2 start_direction = start_contour(component_i); const auto& contour = contours_[component.index]; polyline.contours.push_back({.start_index = polyline.points.size(), .is_closed = contour.is_closed, @@ -353,7 +336,7 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { collect_points({contour.destination}); break; } - end_contour(component_i); + end_contour(); } return polyline; } From 5ab5490a33c832b743ce37cb7821a167b9f9ec01 Mon Sep 17 00:00:00 2001 From: luckysmg <2539699336@qq.com> Date: Wed, 8 Mar 2023 17:10:05 +0800 Subject: [PATCH 4/6] ++ --- impeller/display_list/display_list_unittests.cc | 2 +- impeller/geometry/path.cc | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc index 99e2abb4acb69..5eeb31ae19822 100644 --- a/impeller/display_list/display_list_unittests.cc +++ b/impeller/display_list/display_list_unittests.cc @@ -314,7 +314,7 @@ TEST_P(DisplayListTest, StrokedPathsDrawCorrectly) { builder.translate(0, 100); SkPath cubic_path2; cubic_path2.moveTo({0, 0}); - cubic_path2.cubicTo(0, 0, 0, 0, 150, 0); + cubic_path2.cubicTo(0, 0, 0, 0, 150, 150); builder.drawPath(cubic_path2); builder.translate(0, 100); diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 9f39f5048ead3..310fb46f7425e 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -261,7 +261,8 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { } }; - auto start_contour = [&get_path_component](int current_path_component_index) { + auto compute_contour_start_direction = [&get_path_component]( + int current_path_component_index) { int next_component_index = current_path_component_index + 1; while (get_path_component(next_component_index).has_value()) { auto next_component = get_path_component(next_component_index).value(); @@ -327,7 +328,7 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { } end_contour(); - Vector2 start_direction = start_contour(component_i); + Vector2 start_direction = compute_contour_start_direction(component_i); const auto& contour = contours_[component.index]; polyline.contours.push_back({.start_index = polyline.points.size(), .is_closed = contour.is_closed, From 23a3facc982454c4ec3dc9e247a8ba48cd2cf7cf Mon Sep 17 00:00:00 2001 From: luckysmg <2539699336@qq.com> Date: Wed, 8 Mar 2023 19:45:39 +0800 Subject: [PATCH 5/6] ++ --- impeller/geometry/path_component.cc | 38 ++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index 4ba1527952663..da0a6aae0889e 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -156,11 +156,23 @@ std::vector QuadraticPathComponent::Extrema() const { } std::optional QuadraticPathComponent::GetStartDirection() const { - return (p1 - cp).Normalize(); + if (p1 != cp) { + return (p1 - cp).Normalize(); + } + if (p1 != p2) { + return (p1 - p2).Normalize(); + } + return std::nullopt; } std::optional QuadraticPathComponent::GetEndDirection() const { - return (p2 - cp).Normalize(); + if (p2 != cp) { + return (p2 - cp).Normalize(); + } + if (p2 != p1) { + return (p2 - p1).Normalize(); + } + return std::nullopt; } Point CubicPathComponent::Solve(Scalar time) const { @@ -308,11 +320,29 @@ std::vector CubicPathComponent::Extrema() const { } std::optional CubicPathComponent::GetStartDirection() const { - return (p1 - cp1).Normalize(); + if (p1 != cp1) { + return (p1 - cp1).Normalize(); + } + if (p1 != cp2) { + return (p1 - cp2).Normalize(); + } + if (p1 != p2) { + return (p1 - p2).Normalize(); + } + return std::nullopt; } std::optional CubicPathComponent::GetEndDirection() const { - return (p2 - cp2).Normalize(); + if (p2 != cp2) { + return (p2 - cp2).Normalize(); + } + if (p2 != cp1) { + return (p2 - cp1).Normalize(); + } + if (p2 != p1) { + return (p2 - p1).Normalize(); + } + return std::nullopt; } } // namespace impeller From d9ef27e5a2fb3b3403db777a5e0da246f6a7cf02 Mon Sep 17 00:00:00 2001 From: luckysmg <2539699336@qq.com> Date: Mon, 13 Mar 2023 13:36:10 +0800 Subject: [PATCH 6/6] ++ --- impeller/geometry/path.cc | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index d3a0edc096916..b6d97433fe648 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -245,7 +245,7 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { auto get_path_component = [this](size_t component_i) -> std::optional { - if (component_i < 0 || component_i >= components_.size()) { + if (component_i >= components_.size()) { return std::nullopt; } const auto& component = components_[component_i]; @@ -261,21 +261,22 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { } }; - auto compute_contour_start_direction = [&get_path_component]( - int current_path_component_index) { - int next_component_index = current_path_component_index + 1; - while (get_path_component(next_component_index).has_value()) { - auto next_component = get_path_component(next_component_index).value(); - if (next_component->GetStartDirection().has_value()) { - return next_component->GetStartDirection().value(); - } else { - next_component_index++; - } - } - return Vector2(0, -1); - }; + auto compute_contour_start_direction = + [&get_path_component](size_t current_path_component_index) { + size_t next_component_index = current_path_component_index + 1; + while (get_path_component(next_component_index).has_value()) { + auto next_component = + get_path_component(next_component_index).value(); + if (next_component->GetStartDirection().has_value()) { + return next_component->GetStartDirection().value(); + } else { + next_component_index++; + } + } + return Vector2(0, -1); + }; - std::optional previous_path_component_index; + std::optional previous_path_component_index; auto end_contour = [&polyline, &previous_path_component_index, &get_path_component]() { // Whenever a contour has ended, extract the exact end direction from the @@ -291,7 +292,7 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { auto& contour = polyline.contours.back(); contour.end_direction = Vector2(0, 1); - int previous_index = previous_path_component_index.value(); + size_t previous_index = previous_path_component_index.value(); while (get_path_component(previous_index).has_value()) { auto previous_path_component = get_path_component(previous_index).value(); if (previous_path_component->GetEndDirection().has_value()) { @@ -299,6 +300,9 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const { previous_path_component->GetEndDirection().value(); break; } else { + if (previous_index == 0) { + break; + } previous_index--; } }