From fef67861b623242dfe925aa5aa6fc37838358d87 Mon Sep 17 00:00:00 2001 From: Emmett Lalish Date: Sun, 16 Jun 2024 11:10:08 -0700 Subject: [PATCH 1/4] removed cross_section dependency from manifold --- bindings/c/include/manifoldc.h | 9 ++-- bindings/c/manifoldc.cpp | 13 +++--- bindings/python/CMakeLists.txt | 2 +- bindings/python/manifold3d.cpp | 40 ++++++++++++----- samples/CMakeLists.txt | 2 +- samples/src/bracelet.cpp | 2 +- samples/src/knot.cpp | 2 +- src/manifold/CMakeLists.txt | 2 +- src/manifold/include/manifold.h | 9 ++-- src/manifold/src/constructors.cpp | 75 ++++++++++++++++++------------- src/manifold/src/face_op.cpp | 8 ++-- src/manifold/src/impl.h | 4 +- src/manifold/src/manifold.cpp | 10 +++-- test/boolean_complex_test.cpp | 5 ++- test/cross_section_test.cpp | 26 +++++------ test/manifold_test.cpp | 34 +++++++------- test/manifoldc_test.cpp | 5 +-- test/samples_test.cpp | 19 ++++---- test/smooth_test.cpp | 6 ++- 19 files changed, 156 insertions(+), 117 deletions(-) diff --git a/bindings/c/include/manifoldc.h b/bindings/c/include/manifoldc.h index df6b154d8..cfc1abd44 100644 --- a/bindings/c/include/manifoldc.h +++ b/bindings/c/include/manifoldc.h @@ -105,9 +105,8 @@ ManifoldManifold *manifold_trim_by_plane(void *mem, ManifoldManifold *m, // 3D to 2D -ManifoldCrossSection *manifold_slice(void *mem, ManifoldManifold *m, - float height); -ManifoldCrossSection *manifold_project(void *mem, ManifoldManifold *m); +ManifoldPolygons *manifold_slice(void *mem, ManifoldManifold *m, float height); +ManifoldPolygons *manifold_project(void *mem, ManifoldManifold *m); // Convex Hulls @@ -157,11 +156,11 @@ ManifoldManifold *manifold_of_meshgl(void *mem, ManifoldMeshGL *mesh); ManifoldManifold *manifold_smooth(void *mem, ManifoldMeshGL *mesh, int *half_edges, float *smoothness, int n_idxs); -ManifoldManifold *manifold_extrude(void *mem, ManifoldCrossSection *cs, +ManifoldManifold *manifold_extrude(void *mem, ManifoldPolygons *cs, float height, int slices, float twist_degrees, float scale_x, float scale_y); -ManifoldManifold *manifold_revolve(void *mem, ManifoldCrossSection *cs, +ManifoldManifold *manifold_revolve(void *mem, ManifoldPolygons *cs, int circular_segments); ManifoldManifold *manifold_compose(void *mem, ManifoldManifoldVec *ms); ManifoldManifoldVec *manifold_decompose(void *mem, ManifoldManifold *m); diff --git a/bindings/c/manifoldc.cpp b/bindings/c/manifoldc.cpp index 28874a1ef..60abb4ed3 100644 --- a/bindings/c/manifoldc.cpp +++ b/bindings/c/manifoldc.cpp @@ -192,15 +192,14 @@ ManifoldManifold *manifold_trim_by_plane(void *mem, ManifoldManifold *m, return to_c(new (mem) Manifold(trimmed)); } -ManifoldCrossSection *manifold_slice(void *mem, ManifoldManifold *m, - float height) { +ManifoldPolygons *manifold_slice(void *mem, ManifoldManifold *m, float height) { auto poly = from_c(m)->Slice(height); - return to_c(new (mem) CrossSection(poly)); + return to_c(new (mem) Polygons(poly)); } -ManifoldCrossSection *manifold_project(void *mem, ManifoldManifold *m) { +ManifoldPolygons *manifold_project(void *mem, ManifoldManifold *m) { auto poly = from_c(m)->Project(); - return to_c(new (mem) CrossSection(poly)); + return to_c(new (mem) Polygons(poly)); } ManifoldManifold *manifold_hull(void *mem, ManifoldManifold *m) { @@ -382,7 +381,7 @@ ManifoldManifold *manifold_of_meshgl(void *mem, ManifoldMeshGL *mesh) { return to_c(new (mem) Manifold(m)); } -ManifoldManifold *manifold_extrude(void *mem, ManifoldCrossSection *cs, +ManifoldManifold *manifold_extrude(void *mem, ManifoldPolygons *cs, float height, int slices, float twist_degrees, float scale_x, float scale_y) { @@ -391,7 +390,7 @@ ManifoldManifold *manifold_extrude(void *mem, ManifoldCrossSection *cs, return to_c(new (mem) Manifold(m)); } -ManifoldManifold *manifold_revolve(void *mem, ManifoldCrossSection *cs, +ManifoldManifold *manifold_revolve(void *mem, ManifoldPolygons *cs, int circular_segments) { auto m = Manifold::Revolve(*from_c(cs), circular_segments); diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index d88817968..2b5f90f59 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -20,7 +20,7 @@ nanobind_add_module( NB_STATIC STABLE_ABI LTO autogen_docstrings.inl manifold3d.cpp) -target_link_libraries(manifold3d PRIVATE manifold sdf polygon) +target_link_libraries(manifold3d PRIVATE manifold sdf polygon cross_section) target_compile_options(manifold3d PRIVATE ${MANIFOLD_FLAGS} -DMODULE_NAME=manifold3d) target_compile_features(manifold3d PUBLIC cxx_std_17) set_target_properties(manifold3d PROPERTIES OUTPUT_NAME "manifold3d") diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index f620ec987..33032890d 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -107,7 +107,7 @@ struct nb::detail::type_caster> { static handle from_cpp(glm_type mat, rv_policy policy, cleanup_list *cleanup) noexcept { T *buffer = new T[R * C]; - nb::capsule mem_mgr(buffer, [](void *p) noexcept { delete[](T *) p; }); + nb::capsule mem_mgr(buffer, [](void *p) noexcept { delete[] (T *)p; }); for (int i = 0; i < R; i++) { for (int j = 0; j < C; j++) { // py is (Rows, Cols), glm is (Cols, Rows) @@ -154,7 +154,7 @@ struct nb::detail::type_caster>> { cleanup_list *cleanup) noexcept { size_t num_vec = vec.size(); T *buffer = new T[num_vec * N]; - nb::capsule mem_mgr(buffer, [](void *p) noexcept { delete[](T *) p; }); + nb::capsule mem_mgr(buffer, [](void *p) noexcept { delete[] (T *)p; }); for (int i = 0; i < num_vec; i++) { for (int j = 0; j < N; j++) { buffer[i * N + j] = vec[i][j]; @@ -362,9 +362,18 @@ NB_MODULE(manifold3d, m) { .def("trim_by_plane", &Manifold::TrimByPlane, nb::arg("normal"), nb::arg("origin_offset"), manifold__trim_by_plane__normal__origin_offset) - .def("slice", &Manifold::Slice, nb::arg("height"), - manifold__slice__height) - .def("project", &Manifold::Project, manifold__project) + .def( + "slice", + [](const Manifold &self, float height) { + return CrossSection(self.Slice(height)); + }, + nb::arg("height"), manifold__slice__height) + .def( + "project", + [](const Manifold &self) { + return CrossSection(self.Project()).Simplify(self.Precision()); + }, + manifold__project) .def("status", &Manifold::Status, manifold__status) .def( "bounding_box", @@ -402,14 +411,25 @@ NB_MODULE(manifold3d, m) { .def_static("cube", &Manifold::Cube, nb::arg("size") = glm::vec3{1, 1, 1}, nb::arg("center") = false, manifold__cube__size__center) .def_static( - "extrude", &Manifold::Extrude, nb::arg("crossSection"), - nb::arg("height"), nb::arg("n_divisions") = 0, - nb::arg("twist_degrees") = 0.0f, + "extrude", + [](const CrossSection &crossSection, float height, int nDivisions, + float twistDegrees, glm::vec2 scaleTop) { + return Manifold::Extrude(crossSection.ToPolygons(), height, + nDivisions, twistDegrees, scaleTop); + }, + nb::arg("crossSection"), nb::arg("height"), + nb::arg("n_divisions") = 0, nb::arg("twist_degrees") = 0.0f, nb::arg("scale_top") = std::make_tuple(1.0f, 1.0f), manifold__extrude__cross_section__height__n_divisions__twist_degrees__scale_top) .def_static( - "revolve", &Manifold::Revolve, nb::arg("crossSection"), - nb::arg("circular_segments") = 0, nb::arg("revolve_degrees") = 360.0, + "revolve", + [](const CrossSection &crossSection, int circularSegments, + float revolveDegrees) { + return Manifold::Revolve(crossSection.ToPolygons(), + circularSegments, revolveDegrees); + }, + nb::arg("crossSection"), nb::arg("circular_segments") = 0, + nb::arg("revolve_degrees") = 360.0, manifold__revolve__cross_section__circular_segments__revolve_degrees) .def_static( "cylinder", &Manifold::Cylinder, nb::arg("height"), diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index bc412f6f1..6c3c3d3bf 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -23,7 +23,7 @@ target_include_directories(samples ) target_link_libraries(samples - PUBLIC manifold sdf + PUBLIC manifold sdf cross_section ) target_compile_options(samples PRIVATE ${MANIFOLD_FLAGS}) diff --git a/samples/src/bracelet.cpp b/samples/src/bracelet.cpp index fe2b7f6f0..2d792bccf 100644 --- a/samples/src/bracelet.cpp +++ b/samples/src/bracelet.cpp @@ -26,7 +26,7 @@ Manifold Base(float width, float radius, float decorRadius, float twistRadius, CrossSection circle = CrossSection::Circle(decorRadius, nDivision).Translate({twistRadius, 0}); - Manifold decor = Manifold::Extrude(circle, width, nDivision, 180) + Manifold decor = Manifold::Extrude(circle.ToPolygons(), width, nDivision, 180) .Scale({1.0f, 0.5f, 1.0f}) .Translate({0.0f, radius, 0.0f}); diff --git a/samples/src/knot.cpp b/samples/src/knot.cpp index b9eac98fa..e48c2c759 100644 --- a/samples/src/knot.cpp +++ b/samples/src/knot.cpp @@ -50,7 +50,7 @@ Manifold TorusKnot(int p, int q, float majorRadius, float minorRadius, linearSegments > 2 ? linearSegments : n * q * majorRadius / threadRadius; CrossSection circle = CrossSection::Circle(1., n).Translate({2, 0}); - Manifold knot = Manifold::Revolve(circle, m); + Manifold knot = Manifold::Revolve(circle.ToPolygons(), m); knot = knot.Warp([p, q, majorRadius, minorRadius, threadRadius](glm::vec3& v) { diff --git a/src/manifold/CMakeLists.txt b/src/manifold/CMakeLists.txt index 109aa3cc5..d0405bf91 100644 --- a/src/manifold/CMakeLists.txt +++ b/src/manifold/CMakeLists.txt @@ -21,7 +21,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC $ $) target_link_libraries(${PROJECT_NAME} - PUBLIC utilities cross_section sdf + PUBLIC utilities sdf PRIVATE collider polygon ${MANIFOLD_INCLUDE} quickhull ) diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index fa9a0a45a..8b3697e16 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -16,7 +16,6 @@ #include #include -#include "cross_section.h" #include "public.h" #include "vec_view.h" @@ -174,10 +173,10 @@ class Manifold { float radiusHigh = -1.0f, int circularSegments = 0, bool center = false); static Manifold Sphere(float radius, int circularSegments = 0); - static Manifold Extrude(const CrossSection& crossSection, float height, + static Manifold Extrude(const Polygons& crossSection, float height, int nDivisions = 0, float twistDegrees = 0.0f, glm::vec2 scaleTop = glm::vec2(1.0f)); - static Manifold Revolve(const CrossSection& crossSection, + static Manifold Revolve(const Polygons& crossSection, int circularSegments = 0, float revolveDegrees = 360.0f); ///@} @@ -279,8 +278,8 @@ class Manifold { /** @name 2D from 3D */ ///@{ - CrossSection Slice(float height = 0) const; - CrossSection Project() const; + Polygons Slice(float height = 0) const; + Polygons Project() const; ///@} /** @name Convex hull diff --git a/src/manifold/src/constructors.cpp b/src/manifold/src/constructors.cpp index 035020166..879b981a1 100644 --- a/src/manifold/src/constructors.cpp +++ b/src/manifold/src/constructors.cpp @@ -14,7 +14,6 @@ #include -#include "cross_section.h" #include "csg_tree.h" #include "impl.h" #include "par.h" @@ -176,14 +175,19 @@ Manifold Manifold::Cylinder(float height, float radiusLow, float radiusHigh, if (height <= 0.0f || radiusLow <= 0.0f) { return Invalid(); } - float scale = radiusHigh >= 0.0f ? radiusHigh / radiusLow : 1.0f; - float radius = fmax(radiusLow, radiusHigh); - int n = circularSegments > 2 ? circularSegments - : Quality::GetCircularSegments(radius); + const float scale = radiusHigh >= 0.0f ? radiusHigh / radiusLow : 1.0f; + const float radius = fmax(radiusLow, radiusHigh); + const int n = circularSegments > 2 ? circularSegments + : Quality::GetCircularSegments(radius); + + SimplePolygon circle(n); + const float dPhi = 360.0f / n; + for (int i = 0; i < n; ++i) { + circle[i] = {radiusLow * cosd(dPhi * i), radiusLow * sind(dPhi * i)}; + } - CrossSection circle = CrossSection::Circle(radiusLow, n); Manifold cylinder = - Manifold::Extrude(circle, height, 0, 0.0f, glm::vec2(scale)); + Manifold::Extrude({circle}, height, 0, 0.0f, glm::vec2(scale)); if (center) cylinder = cylinder.Translate(glm::vec3(0.0f, 0.0f, -height / 2.0f)).AsOriginal(); @@ -235,12 +239,11 @@ Manifold Manifold::Sphere(float radius, int circularSegments) { * Note that scale is applied after twist. * Default {1, 1}. */ -Manifold Manifold::Extrude(const CrossSection& crossSection, float height, +Manifold Manifold::Extrude(const Polygons& crossSection, float height, int nDivisions, float twistDegrees, glm::vec2 scaleTop) { ZoneScoped; - auto polygons = crossSection.ToPolygons(); - if (polygons.size() == 0 || height <= 0.0f) { + if (crossSection.size() == 0 || height <= 0.0f) { return Invalid(); } @@ -256,7 +259,7 @@ Manifold Manifold::Extrude(const CrossSection& crossSection, float height, bool isCone = scaleTop.x == 0.0 && scaleTop.y == 0.0; int idx = 0; PolygonsIdx polygonsIndexed; - for (auto& poly : polygons) { + for (auto& poly : crossSection) { nCrossSection += poly.size(); SimplePolygonIdx simpleIndexed; for (const glm::vec2& polyVert : poly) { @@ -273,7 +276,7 @@ Manifold Manifold::Extrude(const CrossSection& crossSection, float height, glm::mat2 transform = glm::mat2(scale.x, 0.0f, 0.0f, scale.y) * rotation; int j = 0; int idx = 0; - for (const auto& poly : polygons) { + for (const auto& poly : crossSection) { for (int vert = 0; vert < poly.size(); ++vert) { int offset = idx + nCrossSection * i; int thisVert = vert + offset; @@ -294,7 +297,7 @@ Manifold Manifold::Extrude(const CrossSection& crossSection, float height, } } if (isCone) - for (int j = 0; j < polygons.size(); ++j) // Duplicate vertex for Genus + for (int j = 0; j < crossSection.size(); ++j) // Duplicate vertex for Genus vertPos.push_back({0.0f, 0.0f, height}); std::vector top = TriangulateIdx(polygonsIndexed); for (const glm::ivec3& tri : top) { @@ -322,28 +325,40 @@ Manifold Manifold::Extrude(const CrossSection& crossSection, float height, * calculated by the static Defaults. * @param revolveDegrees Number of degrees to revolve. Default is 360 degrees. */ -Manifold Manifold::Revolve(const CrossSection& crossSection, - int circularSegments, float revolveDegrees) { +Manifold Manifold::Revolve(const Polygons& crossSection, int circularSegments, + float revolveDegrees) { ZoneScoped; - Polygons polygons = crossSection.ToPolygons(); - if (polygons.size() == 0) { - return Invalid(); + Polygons polygons; + float radius; + for (const SimplePolygon& poly : crossSection) { + int i = 0; + while (i < poly.size() && poly[i].x < 0) { + ++i; + } + if (i == poly.size()) { + continue; + } + polygons.push_back({}); + const int start = i; + do { + if (poly[i].x >= 0) { + polygons.back().push_back(poly[i]); + radius = glm::max(radius, poly[i].x); + } + const int next = i + 1 == poly.size() ? 0 : i + 1; + if ((poly[next].x < 0) != (poly[i].x < 0)) { + const float y = poly[next].y + poly[next].x * + (poly[i].y - poly[next].y) / + (poly[i].x - poly[next].x); + polygons.back().push_back({0, y}); + } + i = next; + } while (i != start); } - const Rect bounds = crossSection.Bounds(); - const float radius = bounds.max.x; - - if (radius <= 0) { + if (polygons.empty()) { return Invalid(); - } else if (bounds.min.x < 0) { - // Take the x>=0 slice. - glm::vec2 min = bounds.min; - glm::vec2 max = bounds.max; - CrossSection posBoundingBox = CrossSection( - {{0.0, min.y}, {max.x, min.y}, {max.x, max.y}, {0.0, max.y}}); - - polygons = (crossSection ^ posBoundingBox).ToPolygons(); } if (revolveDegrees > 360.0f) { diff --git a/src/manifold/src/face_op.cpp b/src/manifold/src/face_op.cpp index 29bc90488..7d6a19cff 100644 --- a/src/manifold/src/face_op.cpp +++ b/src/manifold/src/face_op.cpp @@ -228,7 +228,7 @@ PolygonsIdx Manifold::Impl::Face2Polygons(VecView::IterC start, return polys; } -CrossSection Manifold::Impl::Slice(float height) const { +Polygons Manifold::Impl::Slice(float height) const { Box plane = bBox_; plane.min.z = plane.max.z = height; Vec query; @@ -287,10 +287,10 @@ CrossSection Manifold::Impl::Slice(float height) const { polys.push_back(poly); } - return CrossSection(polys); + return polys; } -CrossSection Manifold::Impl::Project() const { +Polygons Manifold::Impl::Project() const { const glm::mat3x2 projection = GetAxisAlignedProjection({0, 0, 1}); auto policy = autoPolicy(halfedge_.size()); @@ -316,6 +316,6 @@ CrossSection Manifold::Impl::Project() const { polys.push_back(simple); } - return CrossSection(polys).Simplify(precision_); + return polys; } } // namespace manifold diff --git a/src/manifold/src/impl.h b/src/manifold/src/impl.h index e9bbca2a3..2edaf529c 100644 --- a/src/manifold/src/impl.h +++ b/src/manifold/src/impl.h @@ -146,8 +146,8 @@ struct Manifold::Impl { PolygonsIdx Face2Polygons(VecView::IterC start, VecView::IterC end, glm::mat3x2 projection) const; - CrossSection Slice(float height) const; - CrossSection Project() const; + Polygons Slice(float height) const; + Polygons Project() const; // edge_op.cu void SimplifyTopology(); diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 7ab817330..c19489375 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -901,15 +901,17 @@ Manifold Manifold::TrimByPlane(glm::vec3 normal, float originOffset) const { * the bounding box will return the bottom faces, while using a height equal to * the top of the bounding box will return empty. */ -CrossSection Manifold::Slice(float height) const { +Polygons Manifold::Slice(float height) const { return GetCsgLeafNode().GetImpl()->Slice(height); } /** - * Returns a cross section representing the projected outline of this object - * onto the X-Y plane. + * Returns polygons representing the projected outline of this object + * onto the X-Y plane. These polygons will often self-intersect, so it is + * recommended to run them through the positive fill rule of CrossSection to get + * a sensible result before using them. */ -CrossSection Manifold::Project() const { +Polygons Manifold::Project() const { return GetCsgLeafNode().GetImpl()->Project(); } diff --git a/test/boolean_complex_test.cpp b/test/boolean_complex_test.cpp index ff83a6196..cf8421bde 100644 --- a/test/boolean_complex_test.cpp +++ b/test/boolean_complex_test.cpp @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "cross_section.h" #include "manifold.h" #include "polygon.h" #include "test.h" @@ -333,7 +334,7 @@ TEST(BooleanComplex, Sweep) { vertex.x = vertex.x * cos(angle); }; - return Manifold::Extrude(profile, nSegments - 1, nSegments - 2) + return Manifold::Extrude(profile.ToPolygons(), nSegments - 1, nSegments - 2) .Warp(warpFunc); }; @@ -353,7 +354,7 @@ TEST(BooleanComplex, Sweep) { float distance = sqrt(diff.x * diff.x + diff.y * diff.y); float angle = atan2(diff.y, diff.x); Manifold extrusionPrimitive = - Manifold::Extrude(profile, distance) + Manifold::Extrude(profile.ToPolygons(), distance) .Rotate(90, 0, -90) .Translate(glm::vec3(distance, 0, 0)) .Rotate(0, 0, angle * 180 / glm::pi()) diff --git a/test/cross_section_test.cpp b/test/cross_section_test.cpp index 65c40da2b..9d5bb8441 100644 --- a/test/cross_section_test.cpp +++ b/test/cross_section_test.cpp @@ -28,7 +28,7 @@ using namespace manifold; TEST(CrossSection, Square) { auto a = Manifold::Cube({5, 5, 5}); - auto b = Manifold::Extrude(CrossSection::Square({5, 5}), 5); + auto b = Manifold::Extrude(CrossSection::Square({5, 5}).ToPolygons(), 5); EXPECT_FLOAT_EQ((a - b).GetProperties().volume, 0.); } @@ -37,7 +37,7 @@ TEST(CrossSection, MirrorUnion) { auto a = CrossSection::Square({5., 5.}, true); auto b = a.Translate({2.5, 2.5}); auto cross = a + b + b.Mirror({1, 1}); - auto result = Manifold::Extrude(cross, 5.); + auto result = Manifold::Extrude(cross.ToPolygons(), 5.); #ifdef MANIFOLD_EXPORT if (options.exportModels) @@ -52,7 +52,7 @@ TEST(CrossSection, RoundOffset) { auto a = CrossSection::Square({20., 20.}, true); int segments = 20; auto rounded = a.Offset(5., CrossSection::JoinType::Round, 2, segments); - auto result = Manifold::Extrude(rounded, 5.); + auto result = Manifold::Extrude(rounded.ToPolygons(), 5.); #ifdef MANIFOLD_EXPORT if (options.exportModels) @@ -103,11 +103,11 @@ TEST(CrossSection, Transform) { auto b = sq.Transform(glm::mat3x2(trans * scale * rot)); auto b_copy = CrossSection(b); - auto ex_b = Manifold::Extrude(b, 1.).GetMesh(); - Identical(Manifold::Extrude(a, 1.).GetMesh(), ex_b); + auto ex_b = Manifold::Extrude(b.ToPolygons(), 1.).GetMesh(); + Identical(Manifold::Extrude(a.ToPolygons(), 1.).GetMesh(), ex_b); // same transformations are applied in b_copy (giving same result) - Identical(ex_b, Manifold::Extrude(b_copy, 1.).GetMesh()); + Identical(ex_b, Manifold::Extrude(b_copy.ToPolygons(), 1.).GetMesh()); } TEST(CrossSection, Warp) { @@ -136,12 +136,12 @@ TEST(CrossSection, Decompose) { EXPECT_EQ(decomp[0].NumContour(), 2); EXPECT_EQ(decomp[1].NumContour(), 2); - Identical(Manifold::Extrude(a, 1.).GetMesh(), - Manifold::Extrude(decomp[0], 1.).GetMesh()); - Identical(Manifold::Extrude(b, 1.).GetMesh(), - Manifold::Extrude(decomp[1], 1.).GetMesh()); - Identical(Manifold::Extrude(ab, 1.).GetMesh(), - Manifold::Extrude(recomp, 1.).GetMesh()); + Identical(Manifold::Extrude(a.ToPolygons(), 1.).GetMesh(), + Manifold::Extrude(decomp[0].ToPolygons(), 1.).GetMesh()); + Identical(Manifold::Extrude(b.ToPolygons(), 1.).GetMesh(), + Manifold::Extrude(decomp[1].ToPolygons(), 1.).GetMesh()); + Identical(Manifold::Extrude(ab.ToPolygons(), 1.).GetMesh(), + Manifold::Extrude(recomp.ToPolygons(), 1.).GetMesh()); } TEST(CrossSection, FillRule) { @@ -176,7 +176,7 @@ TEST(CrossSection, Hull) { #ifdef MANIFOLD_EXPORT if (options.exportModels) { - auto circ_tri_ex = Manifold::Extrude(circ_tri, 10); + auto circ_tri_ex = Manifold::Extrude(circ_tri.ToPolygons(), 10); ExportMesh("cross_section_hull_circ_tri.glb", circ_tri_ex.GetMesh(), {}); } #endif diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index cccbc58f7..551cc6e80 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -262,7 +262,7 @@ TEST(Manifold, Revolve2) { TEST(Manifold, Revolve3) { CrossSection circle = CrossSection::Circle(1, 32); - Manifold sphere = Manifold::Revolve(circle, 32); + Manifold sphere = Manifold::Revolve(circle.ToPolygons(), 32); auto prop = sphere.GetProperties(); EXPECT_NEAR(prop.volume, 4.0f / 3.0f * glm::pi(), 0.1); EXPECT_NEAR(prop.surfaceArea, 4 * glm::pi(), 0.15); @@ -302,9 +302,10 @@ TEST(Manifold, PartialRevolveOffset) { TEST(Manifold, Warp) { CrossSection square = CrossSection::Square({1, 1}); - Manifold shape = Manifold::Extrude(square, 2, 10).Warp([](glm::vec3& v) { - v.x += v.z * v.z; - }); + Manifold shape = + Manifold::Extrude(square.ToPolygons(), 2, 10).Warp([](glm::vec3& v) { + v.x += v.z * v.z; + }); auto propBefore = shape.GetProperties(); Manifold simplified = Manifold::Compose({shape}); @@ -319,15 +320,16 @@ TEST(Manifold, Warp2) { CrossSection circle = CrossSection::Circle(5, 20).Translate(glm::vec2(10.0, 10.0)); - Manifold shape = Manifold::Extrude(circle, 2, 10).Warp([](glm::vec3& v) { - int nSegments = 10; - double angleStep = 2.0 / 3.0 * glm::pi() / nSegments; - int zIndex = nSegments - 1 - std::round(v.z); - double angle = zIndex * angleStep; - v.z = v.y; - v.y = v.x * sin(angle); - v.x = v.x * cos(angle); - }); + Manifold shape = + Manifold::Extrude(circle.ToPolygons(), 2, 10).Warp([](glm::vec3& v) { + int nSegments = 10; + double angleStep = 2.0 / 3.0 * glm::pi() / nSegments; + int zIndex = nSegments - 1 - std::round(v.z); + double angle = zIndex * angleStep; + v.z = v.y; + v.y = v.x * sin(angle); + v.x = v.x * cos(angle); + }); auto propBefore = shape.GetProperties(); @@ -602,9 +604,9 @@ TEST(Manifold, Invalid) { EXPECT_EQ(Manifold::Cylinder(2, -5).Status(), invalid); EXPECT_EQ(Manifold::Cube(glm::vec3(0.0f)).Status(), invalid); EXPECT_EQ(Manifold::Cube({-1, 1, 1}).Status(), invalid); - EXPECT_EQ(Manifold::Extrude(circ, 0.).Status(), invalid); - EXPECT_EQ(Manifold::Extrude(empty_circ, 10.).Status(), invalid); - EXPECT_EQ(Manifold::Revolve(empty_sq).Status(), invalid); + EXPECT_EQ(Manifold::Extrude(circ.ToPolygons(), 0.).Status(), invalid); + EXPECT_EQ(Manifold::Extrude(empty_circ.ToPolygons(), 10.).Status(), invalid); + EXPECT_EQ(Manifold::Revolve(empty_sq.ToPolygons()).Status(), invalid); } TEST(Manifold, MultiCompose) { diff --git a/test/manifoldc_test.cpp b/test/manifoldc_test.cpp index 9338165c9..f95eec8e7 100644 --- a/test/manifoldc_test.cpp +++ b/test/manifoldc_test.cpp @@ -211,13 +211,10 @@ TEST(CBIND, extrude) { malloc(manifold_simple_polygon_size()), &pts[0], 4)}; ManifoldPolygons *polys = manifold_polygons(malloc(manifold_polygons_size()), sq, 1); - ManifoldCrossSection *cross = - manifold_cross_section_of_polygons(malloc(manifold_cross_section_size()), - polys, MANIFOLD_FILL_RULE_POSITIVE); ManifoldManifold *cube = manifold_cube(malloc(sz), 1., 1., 1., 0); ManifoldManifold *extrusion = - manifold_extrude(malloc(sz), cross, 1, 0, 0, 1, 1); + manifold_extrude(malloc(sz), polys, 1, 0, 0, 1, 1); ManifoldManifold *diff = manifold_difference(malloc(sz), cube, extrusion); ManifoldProperties props = manifold_get_properties(diff); diff --git a/test/samples_test.cpp b/test/samples_test.cpp index f6c757165..584039785 100644 --- a/test/samples_test.cpp +++ b/test/samples_test.cpp @@ -14,6 +14,7 @@ #include "samples.h" +#include "cross_section.h" #include "polygon.h" #include "test.h" @@ -182,7 +183,8 @@ TEST(Samples, Bracelet) { EXPECT_EQ(bracelet.Genus(), 1); CheckGL(bracelet); - CrossSection projection = bracelet.Project(); + CrossSection projection(bracelet.Project()); + projection = projection.Simplify(bracelet.Precision()); Rect rect = projection.Bounds(); Box box = bracelet.BoundingBox(); EXPECT_EQ(rect.min.x, box.min.x); @@ -191,14 +193,14 @@ TEST(Samples, Bracelet) { EXPECT_EQ(rect.max.y, box.max.y); EXPECT_NEAR(projection.Area(), 649, 1); EXPECT_EQ(projection.NumContour(), 2); - Manifold extrusion = Manifold::Extrude(projection, 1); + Manifold extrusion = Manifold::Extrude(projection.ToPolygons(), 1); EXPECT_EQ(extrusion.NumDegenerateTris(), 0); EXPECT_EQ(extrusion.Genus(), 1); - CrossSection slice = bracelet.Slice(); + CrossSection slice(bracelet.Slice()); EXPECT_EQ(slice.NumContour(), 2); EXPECT_NEAR(slice.Area(), 230.6, 0.1); - extrusion = Manifold::Extrude(slice, 1); + extrusion = Manifold::Extrude(slice.ToPolygons(), 1); EXPECT_EQ(extrusion.Genus(), 1); #ifdef MANIFOLD_EXPORT @@ -219,10 +221,10 @@ TEST(Samples, GyroidModule) { EXPECT_NEAR(bounds.min.z, 0, precision); EXPECT_NEAR(bounds.max.z, size * glm::sqrt(2.0f), precision); - CrossSection slice = gyroid.Slice(5); + CrossSection slice(gyroid.Slice(5)); EXPECT_EQ(slice.NumContour(), 4); EXPECT_NEAR(slice.Area(), 121.9, 0.1); - Manifold extrusion = Manifold::Extrude(slice, 1); + Manifold extrusion = Manifold::Extrude(slice.ToPolygons(), 1); EXPECT_EQ(extrusion.Genus(), -3); #ifdef MANIFOLD_EXPORT @@ -260,7 +262,8 @@ TEST(Samples, Sponge4) { EXPECT_EQ(cutSponge.first.Genus(), 13394); EXPECT_EQ(cutSponge.second.Genus(), 13394); - CrossSection projection = cutSponge.first.Project(); + CrossSection projection(cutSponge.first.Project()); + projection = projection.Simplify(cutSponge.first.Precision()); Rect rect = projection.Bounds(); Box box = cutSponge.first.BoundingBox(); EXPECT_EQ(rect.min.x, box.min.x); @@ -268,7 +271,7 @@ TEST(Samples, Sponge4) { EXPECT_EQ(rect.max.x, box.max.x); EXPECT_EQ(rect.max.y, box.max.y); EXPECT_NEAR(projection.Area(), 0.535, 0.001); - Manifold extrusion = Manifold::Extrude(projection, 1); + Manifold extrusion = Manifold::Extrude(projection.ToPolygons(), 1); EXPECT_EQ(extrusion.NumDegenerateTris(), 0); EXPECT_EQ(extrusion.Genus(), 502); diff --git a/test/smooth_test.cpp b/test/smooth_test.cpp index 9772fde0f..b45d62527 100644 --- a/test/smooth_test.cpp +++ b/test/smooth_test.cpp @@ -106,7 +106,8 @@ TEST(Smooth, TruncatedCone) { TEST(Smooth, ToLength) { Manifold cone = Manifold::Extrude( - CrossSection::Circle(10, 10).Translate({10, 0}), 2, 0, 0, {0, 0}); + CrossSection::Circle(10, 10).Translate({10, 0}).ToPolygons(), 2, 0, 0, + {0, 0}); cone += cone.Scale({1, 1, -5}); Manifold smooth = Manifold::Smooth(cone.GetMesh()); smooth = smooth.RefineToLength(0.1); @@ -269,7 +270,8 @@ glm::vec4 CircularTangent(const glm::vec3& tangent, const glm::vec3& edgeVec) { TEST(Smooth, Torus) { Mesh torusMesh = - Manifold::Revolve(CrossSection::Circle(1, 8).Translate({2, 0}), 6) + Manifold::Revolve( + CrossSection::Circle(1, 8).Translate({2, 0}).ToPolygons(), 6) .GetMesh(); const int numTri = torusMesh.triVerts.size(); From b621809347e9dceb38559413c19224b8fedcd631 Mon Sep 17 00:00:00 2001 From: Emmett Lalish Date: Tue, 18 Jun 2024 14:00:49 -0700 Subject: [PATCH 2/4] fix compile errors --- src/manifold/src/constructors.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/manifold/src/constructors.cpp b/src/manifold/src/constructors.cpp index 879b981a1..0e816031a 100644 --- a/src/manifold/src/constructors.cpp +++ b/src/manifold/src/constructors.cpp @@ -152,8 +152,8 @@ Manifold Manifold::Cube(glm::vec3 size, bool center) { glm::length(size) == 0.) { return Invalid(); } - glm::mat4x3 m = - glm::translate(center ? (-size / 2.0f) : glm::vec3(0)) * glm::scale(size); + glm::mat4x3 m(glm::translate(center ? (-size / 2.0f) : glm::vec3(0)) * + glm::scale(size)); return Manifold(std::make_shared(Manifold::Impl::Shape::Cube, m)); } @@ -330,7 +330,7 @@ Manifold Manifold::Revolve(const Polygons& crossSection, int circularSegments, ZoneScoped; Polygons polygons; - float radius; + float radius = 0; for (const SimplePolygon& poly : crossSection) { int i = 0; while (i < poly.size() && poly[i].x < 0) { From 819909c49bc06086a5dbf1de993946f53633570d Mon Sep 17 00:00:00 2001 From: Emmett Lalish Date: Tue, 18 Jun 2024 14:20:14 -0700 Subject: [PATCH 3/4] updated JS bindings --- bindings/python/manifold3d.cpp | 4 ++-- bindings/wasm/bindings.cpp | 2 +- bindings/wasm/bindings.js | 13 +++++++++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 33032890d..34924f223 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -107,7 +107,7 @@ struct nb::detail::type_caster> { static handle from_cpp(glm_type mat, rv_policy policy, cleanup_list *cleanup) noexcept { T *buffer = new T[R * C]; - nb::capsule mem_mgr(buffer, [](void *p) noexcept { delete[] (T *)p; }); + nb::capsule mem_mgr(buffer, [](void *p) noexcept { delete[](T *) p; }); for (int i = 0; i < R; i++) { for (int j = 0; j < C; j++) { // py is (Rows, Cols), glm is (Cols, Rows) @@ -154,7 +154,7 @@ struct nb::detail::type_caster>> { cleanup_list *cleanup) noexcept { size_t num_vec = vec.size(); T *buffer = new T[num_vec * N]; - nb::capsule mem_mgr(buffer, [](void *p) noexcept { delete[] (T *)p; }); + nb::capsule mem_mgr(buffer, [](void *p) noexcept { delete[](T *) p; }); for (int i = 0; i < num_vec; i++) { for (int j = 0; j < N; j++) { buffer[i * N + j] = vec[i][j]; diff --git a/bindings/wasm/bindings.cpp b/bindings/wasm/bindings.cpp index b544d354e..e257b0c52 100644 --- a/bindings/wasm/bindings.cpp +++ b/bindings/wasm/bindings.cpp @@ -139,7 +139,7 @@ EMSCRIPTEN_BINDINGS(whatever) { .function("_SplitByPlane", &man_js::SplitByPlane) .function("_TrimByPlane", &Manifold::TrimByPlane) .function("_Slice", &Manifold::Slice) - .function("project", &Manifold::Project) + .function("_Project", &Manifold::Project) .function("hull", select_overload(&Manifold::Hull)) .function("_GetMeshJS", &js::GetMeshJS) .function("refine", &Manifold::Refine) diff --git a/bindings/wasm/bindings.js b/bindings/wasm/bindings.js index 3db4a759e..c6ddb2a0c 100644 --- a/bindings/wasm/bindings.js +++ b/bindings/wasm/bindings.js @@ -165,14 +165,15 @@ Module.setup = function() { height, nDivisions = 0, twistDegrees = 0.0, scaleTop = [1.0, 1.0], center = false) { scaleTop = vararg2vec2([scaleTop]); - const man = - Module._Extrude(this, height, nDivisions, twistDegrees, scaleTop); + const man = Module._Extrude( + this._ToPolygons(), height, nDivisions, twistDegrees, scaleTop); return (center ? man.translate([0., 0., -height / 2.]) : man); }; Module.CrossSection.prototype.revolve = function( circularSegments = 0, revolveDegrees = 360.0) { - return Module._Revolve(this, circularSegments, revolveDegrees); + return Module._Revolve( + this._ToPolygons(), circularSegments, revolveDegrees); }; Module.CrossSection.prototype.add = function(other) { @@ -283,7 +284,11 @@ Module.setup = function() { }; Module.Manifold.prototype.slice = function(height = 0.) { - return this._Slice(height); + return Module.CrossSection(this._Slice(height)); + }; + + Module.Manifold.prototype.project = function() { + return Module.CrossSection(this._Project()).simplify(this.precision()); }; Module.Manifold.prototype.split = function(manifold) { From ed19ad00c7ed53883019380dbe8b3a18a815cf11 Mon Sep 17 00:00:00 2001 From: Emmett Lalish Date: Tue, 18 Jun 2024 14:26:12 -0700 Subject: [PATCH 4/4] fixed python bindings --- bindings/python/manifold3d.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 34924f223..b631fef19 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -708,13 +708,25 @@ NB_MODULE(manifold3d, m) { cross_section__compose__cross_sections) .def("to_polygons", &CrossSection::ToPolygons, cross_section__to_polygons) .def( - "extrude", &Manifold::Extrude, nb::arg("height"), - nb::arg("n_divisions") = 0, nb::arg("twist_degrees") = 0.0f, + "extrude", + [](const CrossSection &self, float height, int nDivisions, + float twistDegrees, glm::vec2 scaleTop) { + return Manifold::Extrude(self.ToPolygons(), height, nDivisions, + twistDegrees, scaleTop); + }, + nb::arg("height"), nb::arg("n_divisions") = 0, + nb::arg("twist_degrees") = 0.0f, nb::arg("scale_top") = std::make_tuple(1.0f, 1.0f), manifold__extrude__cross_section__height__n_divisions__twist_degrees__scale_top) - .def("revolve", &Manifold::Revolve, nb::arg("circular_segments") = 0, - nb::arg("revolve_degrees") = 360.0, - manifold__revolve__cross_section__circular_segments__revolve_degrees) + .def( + "revolve", + [](const CrossSection &self, int circularSegments, + float revolveDegrees) { + return Manifold::Revolve(self.ToPolygons(), circularSegments, + revolveDegrees); + }, + nb::arg("circular_segments") = 0, nb::arg("revolve_degrees") = 360.0, + manifold__revolve__cross_section__circular_segments__revolve_degrees) .def_static("square", &CrossSection::Square, nb::arg("size"), nb::arg("center") = false,