From 364574486421724e5d24ceae2a4baa6ec585673e Mon Sep 17 00:00:00 2001 From: Geoff deRosenroll Date: Tue, 7 Mar 2023 17:56:42 -0800 Subject: [PATCH 1/6] Experiment with lazy transforms for cross section --- src/cross_section/include/cross_section.h | 4 +- src/cross_section/src/cross_section.cpp | 82 +++++++++++++---------- 2 files changed, 48 insertions(+), 38 deletions(-) diff --git a/src/cross_section/include/cross_section.h b/src/cross_section/include/cross_section.h index 0a62cc2bf..5d7391a56 100644 --- a/src/cross_section/include/cross_section.h +++ b/src/cross_section/include/cross_section.h @@ -96,8 +96,10 @@ class CrossSection { ///@} private: - C2::PathsD paths_; + mutable C2::PathsD paths_; + mutable glm::mat3x2 transform_; CrossSection(C2::PathsD paths); + C2::PathsD GetPaths() const; }; /** @} */ diff --git a/src/cross_section/src/cross_section.cpp b/src/cross_section/src/cross_section.cpp index 5197a56fc..f7fcc4c10 100644 --- a/src/cross_section/src/cross_section.cpp +++ b/src/cross_section/src/cross_section.cpp @@ -21,6 +21,7 @@ #include "clipper2/clipper.core.h" #include "clipper2/clipper.engine.h" #include "clipper2/clipper.offset.h" +#include "glm/ext/matrix_float3x2.hpp" #include "glm/ext/vector_float2.hpp" #include "glm/geometric.hpp" #include "glm/glm.hpp" @@ -91,6 +92,20 @@ C2::PathD pathd_of_contour(const SimplePolygon& ctr) { } return p; } + +C2::PathsD transform(C2::PathsD ps, glm::mat3x2 m) { + auto transformed = C2::PathsD(); + transformed.reserve(ps.size()); + for (auto path : ps) { + auto s = C2::PathD(); + s.reserve(path.size()); + for (auto p : path) { + s.push_back(v2_to_pd(m * glm::vec3(p.x, p.y, 1))); + } + transformed.push_back(s); + } + return transformed; +} } // namespace namespace manifold { @@ -117,6 +132,12 @@ CrossSection::CrossSection(const Polygons& contours, FillRule fillrule) { paths_ = C2::Union(ps, fr(fillrule), precision_); } +C2::PathsD CrossSection::GetPaths() const { + transform_ = glm::mat3x2(1.); + paths_ = transform(paths_, transform_); + return paths_; +} + CrossSection CrossSection::Square(const glm::vec2 dims, bool center) { auto p = C2::PathD(4); if (center) { @@ -151,8 +172,8 @@ CrossSection CrossSection::Circle(float radius, int circularSegments) { CrossSection CrossSection::Boolean(const CrossSection& second, OpType op) const { auto ct = cliptype_of_op(op); - auto res = C2::BooleanOp(ct, C2::FillRule::Positive, paths_, second.paths_, - precision_); + auto res = C2::BooleanOp(ct, C2::FillRule::Positive, GetPaths(), + second.GetPaths(), precision_); return CrossSection(res); } @@ -163,7 +184,7 @@ CrossSection CrossSection::BatchBoolean( else if (crossSections.size() == 1) return crossSections[0]; - auto subjs = crossSections[0].paths_; + auto subjs = crossSections[0].GetPaths(); int n_clips = 0; for (int i = 1; i < crossSections.size(); ++i) { n_clips += crossSections[i].paths_.size(); @@ -171,7 +192,7 @@ CrossSection CrossSection::BatchBoolean( auto clips = C2::PathsD(); clips.reserve(n_clips); for (int i = 1; i < crossSections.size(); ++i) { - auto ps = crossSections[i].paths_; + auto ps = crossSections[i].GetPaths(); clips.insert(clips.end(), ps.begin(), ps.end()); } @@ -233,10 +254,11 @@ CrossSection CrossSection::RectClip(const Rect& rect) const { } CrossSection CrossSection::Translate(const glm::vec2 v) const { - auto ps = C2::TranslatePaths(paths_, v.x, v.y); - return CrossSection(ps); + glm::mat3x2 m(1.0f, 0.0f, 0.0f, 1.0f, v.x, v.y); + return Transform(m); } +// TODO: Rotate and Mirror converted to use Transform, then testing. CrossSection CrossSection::Rotate(float degrees) const { auto rotated = C2::PathsD(); rotated.reserve(paths_.size()); @@ -256,17 +278,8 @@ CrossSection CrossSection::Rotate(float degrees) const { } CrossSection CrossSection::Scale(const glm::vec2 scale) const { - auto scaled = C2::PathsD(); - scaled.reserve(paths_.size()); - for (auto path : paths_) { - auto s = C2::PathD(); - s.reserve(path.size()); - for (auto p : path) { - s.push_back(C2::PointD(p.x * scale.x, p.y * scale.y)); - } - scaled.push_back(s); - } - return CrossSection(scaled); + glm::mat3x2 m(scale.x, 0.0f, 0.0f, scale.y, 0.0f, 0.0f); + return Transform(m); } CrossSection CrossSection::Mirror(const glm::vec2 ax) const { @@ -288,45 +301,40 @@ CrossSection CrossSection::Mirror(const glm::vec2 ax) const { } CrossSection CrossSection::Transform(const glm::mat3x2& m) const { - auto transformed = C2::PathsD(); - transformed.reserve(paths_.size()); - for (auto path : paths_) { - auto s = C2::PathD(); - s.reserve(path.size()); - for (auto p : path) { - s.push_back(v2_to_pd(m * glm::vec3(p.x, p.y, 1))); - } - transformed.push_back(s); - } - return CrossSection(transformed); + auto transformed = CrossSection(); + transformed.transform_ = glm::mat3(m) * glm::mat3(transform_); + transformed.paths_ = C2::PathsD(paths_); + return transformed; } CrossSection CrossSection::Simplify(double epsilon) const { - auto ps = SimplifyPaths(paths_, epsilon, false); + auto ps = SimplifyPaths(GetPaths(), epsilon, false); return CrossSection(ps); } CrossSection CrossSection::Offset(double delta, JoinType jointype, double miter_limit, double arc_tolerance) const { - auto ps = C2::InflatePaths(paths_, delta, jt(jointype), C2::EndType::Polygon, - miter_limit, precision_, arc_tolerance); + auto ps = + C2::InflatePaths(GetPaths(), delta, jt(jointype), C2::EndType::Polygon, + miter_limit, precision_, arc_tolerance); return CrossSection(ps); } -double CrossSection::Area() const { return C2::Area(paths_); } +double CrossSection::Area() const { return C2::Area(GetPaths()); } Rect CrossSection::Bounds() const { - auto r = C2::GetBounds(paths_); + auto r = C2::GetBounds(GetPaths()); return Rect({r.left, r.bottom}, {r.right, r.top}); } -bool CrossSection::IsEmpty() const { return paths_.empty(); } +bool CrossSection::IsEmpty() const { return GetPaths().empty(); } Polygons CrossSection::ToPolygons() const { auto polys = Polygons(); - polys.reserve(paths_.size()); - for (auto p : paths_) { + auto paths = GetPaths(); + polys.reserve(paths.size()); + for (auto p : paths) { auto sp = SimplePolygon(); - sp.reserve(paths_.size()); + sp.reserve(p.size()); for (auto v : p) { sp.push_back({v.x, v.y}); } From 306ca69789ee85e9728be7659bd690ff0812ef5c Mon Sep 17 00:00:00 2001 From: Geoff deRosenroll Date: Tue, 7 Mar 2023 22:05:44 -0800 Subject: [PATCH 2/6] WIP, mirroring not working --- src/cross_section/include/cross_section.h | 3 +- src/cross_section/src/cross_section.cpp | 88 +++++++++++++---------- test/cross_section_test.cpp | 13 +++- 3 files changed, 64 insertions(+), 40 deletions(-) diff --git a/src/cross_section/include/cross_section.h b/src/cross_section/include/cross_section.h index 5d7391a56..b6cb22865 100644 --- a/src/cross_section/include/cross_section.h +++ b/src/cross_section/include/cross_section.h @@ -18,6 +18,7 @@ #include "clipper2/clipper.core.h" #include "clipper2/clipper.offset.h" +#include "glm/ext/matrix_float3x2.hpp" #include "glm/ext/vector_float2.hpp" #include "public.h" @@ -97,7 +98,7 @@ class CrossSection { private: mutable C2::PathsD paths_; - mutable glm::mat3x2 transform_; + mutable glm::mat3x2 transform_ = glm::mat3x2(1.0f); CrossSection(C2::PathsD paths); C2::PathsD GetPaths() const; }; diff --git a/src/cross_section/src/cross_section.cpp b/src/cross_section/src/cross_section.cpp index f7fcc4c10..ff11ed031 100644 --- a/src/cross_section/src/cross_section.cpp +++ b/src/cross_section/src/cross_section.cpp @@ -93,14 +93,22 @@ C2::PathD pathd_of_contour(const SimplePolygon& ctr) { return p; } -C2::PathsD transform(C2::PathsD ps, glm::mat3x2 m) { +C2::PathsD transform(const C2::PathsD ps, const glm::mat3x2 m) { + if (m == glm::mat3x2(1.0f)) { + // return ps; + return C2::PathsD(ps); + } + const bool invert = glm::determinant(glm::mat2(m)) < 0; + // const bool invert = false; + printf("invert = %i\n", invert); auto transformed = C2::PathsD(); transformed.reserve(ps.size()); for (auto path : ps) { - auto s = C2::PathD(); - s.reserve(path.size()); - for (auto p : path) { - s.push_back(v2_to_pd(m * glm::vec3(p.x, p.y, 1))); + auto sz = path.size(); + auto s = C2::PathD(sz); + for (int i = 0; i < sz; ++i) { + auto idx = invert ? sz - 1 - i : i; + s[idx] = v2_to_pd(m * glm::vec3(path[i].x, path[i].y, 1)); } transformed.push_back(s); } @@ -113,9 +121,7 @@ CrossSection::CrossSection() { paths_ = C2::PathsD(); } CrossSection::~CrossSection() = default; CrossSection::CrossSection(CrossSection&&) noexcept = default; CrossSection& CrossSection::operator=(CrossSection&&) noexcept = default; -CrossSection::CrossSection(const CrossSection& other) { - paths_ = C2::PathsD(other.paths_); -} +CrossSection::CrossSection(const CrossSection& other) { paths_ = other.paths_; } CrossSection::CrossSection(C2::PathsD ps) { paths_ = ps; } CrossSection::CrossSection(const SimplePolygon& contour, FillRule fillrule) { @@ -133,8 +139,8 @@ CrossSection::CrossSection(const Polygons& contours, FillRule fillrule) { } C2::PathsD CrossSection::GetPaths() const { - transform_ = glm::mat3x2(1.); paths_ = transform(paths_, transform_); + transform_ = glm::mat3x2(1.0f); return paths_; } @@ -249,7 +255,7 @@ CrossSection& CrossSection::operator^=(const CrossSection& Q) { CrossSection CrossSection::RectClip(const Rect& rect) const { auto r = C2::RectD(rect.min.x, rect.min.y, rect.max.x, rect.max.y); - auto ps = C2::RectClip(r, paths_, false, precision_); + auto ps = C2::RectClip(r, GetPaths(), false, precision_); return CrossSection(ps); } @@ -258,51 +264,57 @@ CrossSection CrossSection::Translate(const glm::vec2 v) const { return Transform(m); } -// TODO: Rotate and Mirror converted to use Transform, then testing. CrossSection CrossSection::Rotate(float degrees) const { - auto rotated = C2::PathsD(); - rotated.reserve(paths_.size()); auto s = sind(degrees); auto c = cosd(degrees); - for (auto path : paths_) { - auto r = C2::PathD(); - r.reserve(path.size()); - for (auto p : path) { - auto rx = (p.x * c) - (p.y * s); - auto ry = (p.y * c) + (p.x * s); - r.push_back(C2::PointD(rx, ry)); - } - rotated.push_back(r); - } - return CrossSection(rotated); + glm::mat3x2 m(c, s, -s, c, 0.0f, 0.0f); + return Transform(m); } CrossSection CrossSection::Scale(const glm::vec2 scale) const { - glm::mat3x2 m(scale.x, 0.0f, 0.0f, scale.y, 0.0f, 0.0f); + glm::mat3x2 m(scale.x, 0.0f, // + 0.0f, scale.y, // + 0.0f, 0.0f); return Transform(m); } +// let ax = V2.normalize ax in +// let x = V2.x ax +// and y = V2.y ax in +// let xx = 1. -. (2. *. x *. x) +// and xy = -2. *. x *. y +// and yy = 1. -. (2. *. y *. y) in +// v xx xy 0. xy yy 0. 0. 0. 1. CrossSection CrossSection::Mirror(const glm::vec2 ax) const { if (glm::length(ax) == 0.) { return CrossSection(); } - auto mirrored = C2::PathsD(); - mirrored.reserve(paths_.size()); - for (auto path : paths_) { - auto sz = path.size(); - auto m = C2::PathD(sz); - for (int i = 0; i < sz; ++i) { - auto v = v2_of_pd(path[sz - 1 - i]); - m[i] = v2_to_pd(ax * (2 * glm::dot(v, ax) / glm::dot(ax, ax)) - v); - } - mirrored.push_back(m); - } - return CrossSection(mirrored); + auto n = glm::normalize(ax); + glm::mat3x2 m(1 - 2 * n.x * n.x, -2 * n.x * n.y, // + -2 * n.x * n.y, 1 - 2 * n.y * n.y, // + 0.0f, 0.0f); + return Transform(m); + // auto cs = CrossSection(transform(paths_, transform_)); + // return cs.Transform(m); + // auto mirrored = C2::PathsD(); + // mirrored.reserve(paths_.size()); + // for (auto path : paths_) { + // auto sz = path.size(); + // auto m = C2::PathD(sz); + // for (int i = 0; i < sz; ++i) { + // auto v = v2_of_pd(path[sz - 1 - i]); + // m[i] = v2_to_pd(ax * (2 * glm::dot(v, ax) / glm::dot(ax, ax)) - v); + // } + // mirrored.push_back(m); + // } + // return CrossSection(mirrored); } CrossSection CrossSection::Transform(const glm::mat3x2& m) const { auto transformed = CrossSection(); - transformed.transform_ = glm::mat3(m) * glm::mat3(transform_); + // transformed.transform_ = glm::mat3(m) * glm::mat3(transform_); + transformed.transform_ = m * glm::mat3(transform_); + // transformed.paths_ = paths_; transformed.paths_ = C2::PathsD(paths_); return transformed; } diff --git a/test/cross_section_test.cpp b/test/cross_section_test.cpp index 22263ae6e..917f1a60d 100644 --- a/test/cross_section_test.cpp +++ b/test/cross_section_test.cpp @@ -16,6 +16,7 @@ #include +#include "glm/geometric.hpp" #include "manifold.h" #include "polygon.h" #include "public.h" @@ -29,13 +30,23 @@ using namespace manifold; TEST(CrossSection, MirrorUnion) { auto a = CrossSection::Square({5., 5.}, true); + printf("translating a\n"); auto b = a.Translate({2.5, 2.5}); - auto cross = a + b + b.Mirror({-1, 1}); + printf("mirroring b and unioning\n"); + // auto cross = a + b + b.Mirror({-1, 1}); + auto cross = a.Translate({0, -5}) + b + b.Rotate(45).Translate({0., 10.}); + // auto cross = b ^ b.Mirror({-1, 1}); + // auto cross = (a + b) ^ b.Mirror({-1, 1}); + printf("b area = %f\n", b.Area()); + printf("mirrored b area = %f\n", b.Mirror({-1, 1}).Area()); + printf("extruding\n"); auto result = Manifold::Extrude(cross, 5.); #ifdef MANIFOLD_EXPORT if (options.exportModels) ExportMesh("cross_section_mirror_union.glb", result.GetMesh(), {}); + ExportMesh("cross_section_mirror_union_m.glb", + Manifold::Extrude(b.Mirror({-1, 1}), 5.).GetMesh(), {}); #endif auto area_a = a.Area(); From 4f2a6d375cf0f6b7b4129a49272c7fe1712d03e6 Mon Sep 17 00:00:00 2001 From: Geoff deRosenroll Date: Tue, 7 Mar 2023 23:00:07 -0800 Subject: [PATCH 3/6] Mirror working --- src/cross_section/src/cross_section.cpp | 32 +++---------------------- test/cross_section_test.cpp | 15 ++---------- 2 files changed, 5 insertions(+), 42 deletions(-) diff --git a/src/cross_section/src/cross_section.cpp b/src/cross_section/src/cross_section.cpp index ff11ed031..a4a5bdede 100644 --- a/src/cross_section/src/cross_section.cpp +++ b/src/cross_section/src/cross_section.cpp @@ -95,12 +95,9 @@ C2::PathD pathd_of_contour(const SimplePolygon& ctr) { C2::PathsD transform(const C2::PathsD ps, const glm::mat3x2 m) { if (m == glm::mat3x2(1.0f)) { - // return ps; - return C2::PathsD(ps); + return ps; } const bool invert = glm::determinant(glm::mat2(m)) < 0; - // const bool invert = false; - printf("invert = %i\n", invert); auto transformed = C2::PathsD(); transformed.reserve(ps.size()); for (auto path : ps) { @@ -278,44 +275,21 @@ CrossSection CrossSection::Scale(const glm::vec2 scale) const { return Transform(m); } -// let ax = V2.normalize ax in -// let x = V2.x ax -// and y = V2.y ax in -// let xx = 1. -. (2. *. x *. x) -// and xy = -2. *. x *. y -// and yy = 1. -. (2. *. y *. y) in -// v xx xy 0. xy yy 0. 0. 0. 1. CrossSection CrossSection::Mirror(const glm::vec2 ax) const { if (glm::length(ax) == 0.) { return CrossSection(); } - auto n = glm::normalize(ax); + auto n = glm::normalize(glm::abs(ax)); glm::mat3x2 m(1 - 2 * n.x * n.x, -2 * n.x * n.y, // -2 * n.x * n.y, 1 - 2 * n.y * n.y, // 0.0f, 0.0f); return Transform(m); - // auto cs = CrossSection(transform(paths_, transform_)); - // return cs.Transform(m); - // auto mirrored = C2::PathsD(); - // mirrored.reserve(paths_.size()); - // for (auto path : paths_) { - // auto sz = path.size(); - // auto m = C2::PathD(sz); - // for (int i = 0; i < sz; ++i) { - // auto v = v2_of_pd(path[sz - 1 - i]); - // m[i] = v2_to_pd(ax * (2 * glm::dot(v, ax) / glm::dot(ax, ax)) - v); - // } - // mirrored.push_back(m); - // } - // return CrossSection(mirrored); } CrossSection CrossSection::Transform(const glm::mat3x2& m) const { auto transformed = CrossSection(); - // transformed.transform_ = glm::mat3(m) * glm::mat3(transform_); transformed.transform_ = m * glm::mat3(transform_); - // transformed.paths_ = paths_; - transformed.paths_ = C2::PathsD(paths_); + transformed.paths_ = paths_; return transformed; } diff --git a/test/cross_section_test.cpp b/test/cross_section_test.cpp index 917f1a60d..e31690634 100644 --- a/test/cross_section_test.cpp +++ b/test/cross_section_test.cpp @@ -30,27 +30,16 @@ using namespace manifold; TEST(CrossSection, MirrorUnion) { auto a = CrossSection::Square({5., 5.}, true); - printf("translating a\n"); auto b = a.Translate({2.5, 2.5}); - printf("mirroring b and unioning\n"); - // auto cross = a + b + b.Mirror({-1, 1}); - auto cross = a.Translate({0, -5}) + b + b.Rotate(45).Translate({0., 10.}); - // auto cross = b ^ b.Mirror({-1, 1}); - // auto cross = (a + b) ^ b.Mirror({-1, 1}); - printf("b area = %f\n", b.Area()); - printf("mirrored b area = %f\n", b.Mirror({-1, 1}).Area()); - printf("extruding\n"); + auto cross = a + b + b.Mirror({1, 1}); auto result = Manifold::Extrude(cross, 5.); #ifdef MANIFOLD_EXPORT if (options.exportModels) ExportMesh("cross_section_mirror_union.glb", result.GetMesh(), {}); - ExportMesh("cross_section_mirror_union_m.glb", - Manifold::Extrude(b.Mirror({-1, 1}), 5.).GetMesh(), {}); #endif - auto area_a = a.Area(); - EXPECT_EQ(area_a + 1.5 * area_a, cross.Area()); + EXPECT_FLOAT_EQ(2.5 * a.Area(), cross.Area()); EXPECT_TRUE(a.Mirror(glm::vec2(0)).IsEmpty()); } From b59c1bf50fa82b1756cd167148bb30e090b0b452 Mon Sep 17 00:00:00 2001 From: Geoff deRosenroll Date: Tue, 7 Mar 2023 23:15:44 -0800 Subject: [PATCH 4/6] Shorten Mirror transformation computation --- src/cross_section/src/cross_section.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cross_section/src/cross_section.cpp b/src/cross_section/src/cross_section.cpp index a4a5bdede..546bdb3ae 100644 --- a/src/cross_section/src/cross_section.cpp +++ b/src/cross_section/src/cross_section.cpp @@ -257,7 +257,9 @@ CrossSection CrossSection::RectClip(const Rect& rect) const { } CrossSection CrossSection::Translate(const glm::vec2 v) const { - glm::mat3x2 m(1.0f, 0.0f, 0.0f, 1.0f, v.x, v.y); + glm::mat3x2 m(1.0f, 0.0f, // + 0.0f, 1.0f, // + v.x, v.y); return Transform(m); } @@ -280,9 +282,7 @@ CrossSection CrossSection::Mirror(const glm::vec2 ax) const { return CrossSection(); } auto n = glm::normalize(glm::abs(ax)); - glm::mat3x2 m(1 - 2 * n.x * n.x, -2 * n.x * n.y, // - -2 * n.x * n.y, 1 - 2 * n.y * n.y, // - 0.0f, 0.0f); + auto m = glm::mat3x2(glm::mat2(1.0f) - 2.0f * glm::outerProduct(n, n)); return Transform(m); } From 1d604d54360d6215a32de414dd58fe4920c50552 Mon Sep 17 00:00:00 2001 From: Geoff deRosenroll Date: Tue, 7 Mar 2023 23:17:52 -0800 Subject: [PATCH 5/6] Add other.transform_ assignment --- src/cross_section/src/cross_section.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cross_section/src/cross_section.cpp b/src/cross_section/src/cross_section.cpp index 546bdb3ae..d6602aa3e 100644 --- a/src/cross_section/src/cross_section.cpp +++ b/src/cross_section/src/cross_section.cpp @@ -118,7 +118,10 @@ CrossSection::CrossSection() { paths_ = C2::PathsD(); } CrossSection::~CrossSection() = default; CrossSection::CrossSection(CrossSection&&) noexcept = default; CrossSection& CrossSection::operator=(CrossSection&&) noexcept = default; -CrossSection::CrossSection(const CrossSection& other) { paths_ = other.paths_; } +CrossSection::CrossSection(const CrossSection& other) { + paths_ = other.paths_; + transform_ = other.transform_; +} CrossSection::CrossSection(C2::PathsD ps) { paths_ = ps; } CrossSection::CrossSection(const SimplePolygon& contour, FillRule fillrule) { From cd3b2df727d24192e6de91df0958eac3790dc576 Mon Sep 17 00:00:00 2001 From: Geoff deRosenroll Date: Thu, 9 Mar 2023 11:17:40 -0800 Subject: [PATCH 6/6] Place paths behind a ptr to avoid vector copying --- src/cross_section/include/cross_section.h | 4 ++- src/cross_section/src/cross_section.cpp | 31 +++++++++++++++-------- test/cross_section_test.cpp | 8 ++++-- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/cross_section/include/cross_section.h b/src/cross_section/include/cross_section.h index b6cb22865..c38991ead 100644 --- a/src/cross_section/include/cross_section.h +++ b/src/cross_section/include/cross_section.h @@ -16,6 +16,8 @@ #include +#include + #include "clipper2/clipper.core.h" #include "clipper2/clipper.offset.h" #include "glm/ext/matrix_float3x2.hpp" @@ -97,7 +99,7 @@ class CrossSection { ///@} private: - mutable C2::PathsD paths_; + mutable std::shared_ptr paths_; mutable glm::mat3x2 transform_ = glm::mat3x2(1.0f); CrossSection(C2::PathsD paths); C2::PathsD GetPaths() const; diff --git a/src/cross_section/src/cross_section.cpp b/src/cross_section/src/cross_section.cpp index d6602aa3e..5cea54b39 100644 --- a/src/cross_section/src/cross_section.cpp +++ b/src/cross_section/src/cross_section.cpp @@ -16,6 +16,7 @@ #include +#include #include #include "clipper2/clipper.core.h" @@ -94,9 +95,6 @@ C2::PathD pathd_of_contour(const SimplePolygon& ctr) { } C2::PathsD transform(const C2::PathsD ps, const glm::mat3x2 m) { - if (m == glm::mat3x2(1.0f)) { - return ps; - } const bool invert = glm::determinant(glm::mat2(m)) < 0; auto transformed = C2::PathsD(); transformed.reserve(ps.size()); @@ -111,10 +109,14 @@ C2::PathsD transform(const C2::PathsD ps, const glm::mat3x2 m) { } return transformed; } + +std::shared_ptr shared_paths(const C2::PathsD& ps) { + return std::make_shared(ps); +} } // namespace namespace manifold { -CrossSection::CrossSection() { paths_ = C2::PathsD(); } +CrossSection::CrossSection() { paths_ = shared_paths(C2::PathsD()); } CrossSection::~CrossSection() = default; CrossSection::CrossSection(CrossSection&&) noexcept = default; CrossSection& CrossSection::operator=(CrossSection&&) noexcept = default; @@ -122,11 +124,11 @@ CrossSection::CrossSection(const CrossSection& other) { paths_ = other.paths_; transform_ = other.transform_; } -CrossSection::CrossSection(C2::PathsD ps) { paths_ = ps; } +CrossSection::CrossSection(C2::PathsD ps) { paths_ = shared_paths(ps); } CrossSection::CrossSection(const SimplePolygon& contour, FillRule fillrule) { auto ps = C2::PathsD{(pathd_of_contour(contour))}; - paths_ = C2::Union(ps, fr(fillrule), precision_); + paths_ = shared_paths(C2::Union(ps, fr(fillrule), precision_)); } CrossSection::CrossSection(const Polygons& contours, FillRule fillrule) { @@ -135,13 +137,18 @@ CrossSection::CrossSection(const Polygons& contours, FillRule fillrule) { for (auto ctr : contours) { ps.push_back(pathd_of_contour(ctr)); } - paths_ = C2::Union(ps, fr(fillrule), precision_); + paths_ = shared_paths(C2::Union(ps, fr(fillrule), precision_)); } +// All access to paths_ should be done through the GetPaths() method, which +// applies the accumulated transform_ C2::PathsD CrossSection::GetPaths() const { - paths_ = transform(paths_, transform_); + if (transform_ == glm::mat3x2(1.0f)) { + return *paths_; + } + paths_ = shared_paths(transform(*paths_, transform_)); transform_ = glm::mat3x2(1.0f); - return paths_; + return *paths_; } CrossSection CrossSection::Square(const glm::vec2 dims, bool center) { @@ -193,7 +200,7 @@ CrossSection CrossSection::BatchBoolean( auto subjs = crossSections[0].GetPaths(); int n_clips = 0; for (int i = 1; i < crossSections.size(); ++i) { - n_clips += crossSections[i].paths_.size(); + n_clips += crossSections[i].GetPaths().size(); } auto clips = C2::PathsD(); clips.reserve(n_clips); @@ -269,7 +276,9 @@ CrossSection CrossSection::Translate(const glm::vec2 v) const { CrossSection CrossSection::Rotate(float degrees) const { auto s = sind(degrees); auto c = cosd(degrees); - glm::mat3x2 m(c, s, -s, c, 0.0f, 0.0f); + glm::mat3x2 m(c, s, // + -s, c, // + 0.0f, 0.0f); return Transform(m); } diff --git a/test/cross_section_test.cpp b/test/cross_section_test.cpp index e31690634..1cb90d073 100644 --- a/test/cross_section_test.cpp +++ b/test/cross_section_test.cpp @@ -89,7 +89,11 @@ TEST(CrossSection, Transform) { 0.0f, 0.0f, 1.0f); auto b = sq.Transform(trans * scale * rot); + auto b_copy = CrossSection(b); - Identical(Manifold::Extrude(a, 1.).GetMesh(), - Manifold::Extrude(b, 1.).GetMesh()); + auto ex_b = Manifold::Extrude(b, 1.).GetMesh(); + Identical(Manifold::Extrude(a, 1.).GetMesh(), ex_b); + + // same transformations are applied in b_copy (giving same result) + Identical(ex_b, Manifold::Extrude(b_copy, 1.).GetMesh()); }