diff --git a/src/cross_section/include/cross_section.h b/src/cross_section/include/cross_section.h index c38991ead..a724350bb 100644 --- a/src/cross_section/include/cross_section.h +++ b/src/cross_section/include/cross_section.h @@ -64,6 +64,8 @@ class CrossSection { // Output Polygons ToPolygons() const; double Area() const; + int NumVert() const; + int NumContour() const; bool IsEmpty() const; Rect Bounds() const; ///@} @@ -76,6 +78,7 @@ class CrossSection { CrossSection Scale(const glm::vec2 s) const; CrossSection Mirror(const glm::vec2 ax) const; CrossSection Transform(const glm::mat3x2& m) const; + CrossSection Warp(std::function warpFunc) const; CrossSection Simplify(double epsilon = 1e-6) const; enum class JoinType { Square, Round, Miter }; CrossSection Offset(double delta, JoinType jt, double miter_limit = 2.0, diff --git a/src/cross_section/src/cross_section.cpp b/src/cross_section/src/cross_section.cpp index 5cea54b39..3a6139252 100644 --- a/src/cross_section/src/cross_section.cpp +++ b/src/cross_section/src/cross_section.cpp @@ -305,6 +305,24 @@ CrossSection CrossSection::Transform(const glm::mat3x2& m) const { return transformed; } +CrossSection CrossSection::Warp( + std::function warpFunc) const { + auto paths = GetPaths(); + auto warped = C2::PathsD(); + warped.reserve(paths.size()); + for (auto path : paths) { + auto sz = path.size(); + auto s = C2::PathD(sz); + for (int i = 0; i < sz; ++i) { + auto v = v2_of_pd(path[i]); + warpFunc(v); + s[i] = v2_to_pd(v); + } + warped.push_back(s); + } + return CrossSection(C2::Union(warped, C2::FillRule::Positive, precision_)); +} + CrossSection CrossSection::Simplify(double epsilon) const { auto ps = SimplifyPaths(GetPaths(), epsilon, false); return CrossSection(ps); @@ -320,10 +338,23 @@ CrossSection CrossSection::Offset(double delta, JoinType jointype, } double CrossSection::Area() const { return C2::Area(GetPaths()); } + +int CrossSection::NumVert() const { + int n = 0; + auto paths = GetPaths(); + for (auto p : paths) { + n += p.size(); + } + return n; +} + +int CrossSection::NumContour() const { return GetPaths().size(); } + Rect CrossSection::Bounds() const { auto r = C2::GetBounds(GetPaths()); return Rect({r.left, r.bottom}, {r.right, r.top}); } + bool CrossSection::IsEmpty() const { return GetPaths().empty(); } Polygons CrossSection::ToPolygons() const { diff --git a/test/cross_section_test.cpp b/test/cross_section_test.cpp index 1cb90d073..d7a543ba1 100644 --- a/test/cross_section_test.cpp +++ b/test/cross_section_test.cpp @@ -97,3 +97,17 @@ TEST(CrossSection, Transform) { // same transformations are applied in b_copy (giving same result) Identical(ex_b, Manifold::Extrude(b_copy, 1.).GetMesh()); } + +TEST(CrossSection, Warp) { + auto sq = CrossSection::Square({10., 10.}); + auto a = sq.Scale({2, 3}).Translate({4, 5}); + auto b = sq.Warp([](glm::vec2 &v) { + v.x = v.x * 2 + 4; + v.y = v.y * 3 + 5; + }); + + EXPECT_EQ(sq.NumVert(), 4); + EXPECT_EQ(sq.NumContour(), 1); + Identical(Manifold::Extrude(a, 1.).GetMesh(), + Manifold::Extrude(b, 1.).GetMesh()); +}