8000 added Slice() by elalish · Pull Request #644 · elalish/manifold · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

added Slice() #644

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions bindings/c/include/manifoldc.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ ManifoldManifold *manifold_trim_by_plane(void *mem, ManifoldManifold *m,
float normal_x, float normal_y,
float normal_z, float offset);

// 3D to 2D

ManifoldCrossSection *manifold_slice(void *mem, ManifoldManifold *m,
float height);
ManifoldCrossSection *manifold_project(void *mem, ManifoldManifold *m);

// Convex Hulls

ManifoldManifold *manifold_hull(void *mem, ManifoldManifold *m);
Expand Down
11 changes: 11 additions & 0 deletions bindings/c/manifoldc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,17 @@ 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) {
auto poly = from_c(m)->Slice(height);
return to_c(new (mem) CrossSection(poly));
}

ManifoldCrossSection *manifold_project(void *mem, ManifoldManifold *m) {
auto poly = from_c(m)->Project();
return to_c(new (mem) CrossSection(poly));
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@geoffder Does this look right?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes (late I know, already merged). Sorry I've been swamped in trying to wrap up things with my PhD (and will be for a while) so I've been skimming too many of my manifold GitHub email notifications (though I've still been trying to read most of them to stay up to date).

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No prob! I know how it is with wrapping up a PhD - good luck! It's better on the other side, I promise!

}

ManifoldManifold *manifold_hull(void *mem, ManifoldManifold *m) {
auto hulled = from_c(m)->Hull();
return to_c(new (mem) Manifold(hulled));
Expand Down
13 changes: 13 additions & 0 deletions bindings/python/manifold3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,19 @@ NB_MODULE(manifold3d, m) {
"vector from the plane.\n"
":param originOffset: The distance of the plane from the origin in "
"the direction of the normal vector.")
.def(
"slice",
[](Manifold &self, float height) { return self.Slice(height); },
nb::arg("height"),
"Returns the cross section of this object parallel to the X-Y plane "
"at the specified height. Using a height equal to the bottom of the "
"bounding box will return the bottom faces, while using a height "
"equal to the top of the bounding box will return empty."
"\n\n"
":param height: The Z-level of the slice, defaulting to zero.")
.def("project", &Manifold::Project,
"Returns a cross section representing the projected outline of this "
"object onto the X-Y plane.")
.def("status", &Manifold::Status,
"Returns the reason for an input Mesh producing an empty Manifold. "
"This Status only applies to Manifolds newly-created from an input "
Expand Down
2 changes: 2 additions & 0 deletions bindings/wasm/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ EMSCRIPTEN_BINDINGS(whatever) {
.function("_Split", &man_js::Split)
.function("_SplitByPlane", &man_js::SplitByPlane)
.function("_TrimByPlane", &Manifold::TrimByPlane)
.function("slice", &Manifold::Slice)
.function("project", &Manifold::Project)
.function("hull", select_overload<Manifold() const>(&Manifold::Hull))
.function("_GetMeshJS", &js::GetMeshJS)
.function("refine", &Manifold::Refine)
Expand Down
18 changes: 8 additions & 10 deletions bindings/wasm/examples/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const manifoldStaticFunctions = [
const manifoldMemberFunctions = [
'add', 'subtract', 'intersect', 'decompose', 'warp', 'transform', 'translate',
'rotate', 'scale', 'mirror', 'refine', 'setProperties', 'asOriginal',
'trimByPlane', 'split', 'splitByPlane', 'hull'
'trimByPlane', 'split', 'splitByPlane', 'slice', 'project', 'hull'
];
// CrossSection static methods (that return a new cross-section)
const crossSectionStaticFunctions = [
Expand Down Expand Up @@ -95,15 +95,13 @@ function addMembers(
const cls = module[className];
const obj = areStatic ? cls : cls.prototype;
for (const name of methodNames) {
if (name != 'cylinder') {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@geoffder Can I assume this was a debugging leftover of some kind?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea looks like it sorry!

const originalFn = obj[name];
obj[name] = function(...args: any) {
//@ts-ignore
const result = originalFn(...args);
memoryRegistry.push(result);
return result;
};
}
const originalFn = obj[name];
obj[name] = function(...args: any) {
//@ts-ignore
const result = originalFn(...args);
memoryRegistry.push(result);
return result;
};
}
}

Expand Down
16 changes: 16 additions & 0 deletions bindings/wasm/manifold-encapsulated-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,22 @@ export class Manifold {
*/
trimByPlane(normal: Vec3, originOffset: number): Manifold;

/**
* Returns the cross section of this object parallel to the X-Y plane at the
* specified height. Using a height equal to the bottom
* of the bounding box will return the bottom faces, while using a height
* equal to the top of the bounding box will return empty.
*
* @param height Z-level of slice.
*/
slice(height: number): CrossSection;

/**
* Returns a cross section representing the projected outline of this object
* onto the X-Y plane.
*/
project(): CrossSection;

// Convex Hulls

/**
Expand Down
1 change: 1 addition & 0 deletions src/manifold/include/manifold.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ class Manifold {
/** @name 2D from 3D
*/
///@{
CrossSection Slice(float height = 0) const;
CrossSection Project() const;
///@}

Expand Down
64 changes: 63 additions & 1 deletion src/manifold/src/face_op.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
#define TBB_PREVIEW_CONCURRENT_ORDERED_CONTAINERS 1
#include <tbb/concurrent_map.h>
#endif
#include <map>
#include <unordered_set>

#include "impl.h"
#include "polygon.h"
Expand Down Expand Up @@ -226,6 +226,68 @@ PolygonsIdx Manifold::Impl::Face2Polygons(VecView<Halfedge>::IterC start,
return polys;
}

CrossSection Manifold::Impl::Slice(float height) const {
Box plane = bBox_;
plane.min.z = plane.max.z = height;
Vec<Box> query;
query.push_back(plane);
const SparseIndices collisions =
collider_.Collisions<false, false>(query.cview());

std::unordered_set<int> tris;
for (int i = 0; i < collisions.size(); ++i) {
const int tri = collisions.Get(i, 1);
float min = std::numeric_limits<float>::infinity();
float max = -std::numeric_limits<float>::infinity();
for (const int j : {0, 1, 2}) {
const float z = vertPos_[halfedge_[3 * tri + j].startVert].z;
min = glm::min(min, z);
max = glm::max(max, z);
}

if (min <= height && max > height) {
tris.insert(tri);
}
}

Polygons polys;
while (!tris.empty()) {
const int startTri = *tris.begin();
SimplePolygon poly;

int k = 0;
for (const int j : {0, 1, 2}) {
if (vertPos_[halfedge_[3 * startTri + j].startVert].z > height &&
vertPos_[halfedge_[3 * startTri + Next3(j)].startVert].z <= height) {
k = Next3(j);
break;
}
}

int tri = startTri;
do {
tris.erase(tris.find(tri));
if (vertPos_[halfedge_[3 * tri + k].endVert].z <= height) {
k = Next3(k);
}

Halfedge up = halfedge_[3 * tri + k];
const glm::vec3 below = vertPos_[up.startVert];
const glm::vec3 above = vertPos_[up.endVert];
const float a = (height - below.z) / (above.z - below.z);
poly.push_back(glm::vec2(glm::mix(below, above, a)));

const int pair = up.pairedHalfedge;
tri = pair / 3;
k = Next3(pair % 3);
} while (tri != startTri);

polys.push_back(poly);
}

return CrossSection(polys);
}

CrossSection Manifold::Impl::Project() const {
const glm::mat3x2 projection = GetAxisAlignedProjection({0, 0, 1});
auto policy = autoPolicy(halfedge_.size());
Expand Down
1 change: 1 addition & 0 deletions src/manifold/src/impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ struct Manifold::Impl {
PolygonsIdx Face2Polygons(VecView<Halfedge>::IterC start,
VecView<Halfedge>::IterC end,
glm::mat3x2 projection) const;
CrossSection Slice(float height) const;
CrossSection Project() const;

// edge_op.cu
Expand Down
14 changes: 12 additions & 2 deletions src/manifold/src/manifold.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -780,8 +780,18 @@ Manifold Manifold::TrimByPlane(glm::vec3 normal, float originOffset) const {
}

/**
* Returns a cross section representing the projection of this object onto the
* X-Y plane.
* Returns the cross section of this object parallel to the X-Y plane at the
* specified Z height, defaulting to zero. Using a height equal to the bottom of
* 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 {
return GetCsgLeafNode().GetImpl()->Slice(height);
}

/**
* Returns a cross section representing the projected outline of this object
* onto the X-Y plane.
*/
CrossSection Manifold::Project() const {
return GetCsgLeafNode().GetImpl()->Project();
Expand Down
8 changes: 8 additions & 0 deletions test/manifold_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,14 @@ TEST(Manifold, Transform) {
Identical(cube.GetMesh(), cube2.GetMesh());
}

TEST(Manifold, Slice) {
Manifold cube = Manifold::Cube();
CrossSection bottom = cube.Slice();
CrossSection top = cube.Slice(1);
EXPECT_EQ(bottom.Area(), 1);
EXPECT_EQ(top.Area(), 0);
}

TEST(Manifold, MeshRelation) {
Mesh gyroidMesh = Gyroid();
MeshGL gyroidMeshGL = WithIndexColors(gyroidMesh);
Expand Down
15 changes: 14 additions & 1 deletion test/samples_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@ TEST(Samples, Bracelet) {
EXPECT_EQ(extrusion.NumDegenerateTris(), 0);
EXPECT_EQ(extrusion.Genus(), 1);

CrossSection slice = bracelet.Slice();
EXPECT_EQ(slice.NumContour(), 2);
EXPECT_NEAR(slice.Area(), 230.6, 0.1);
extrusion = Manifold::Extrude(slice, 1);
EXPECT_EQ(extrusion.Genus(), 1);

#ifdef MANIFOLD_EXPORT
if (options.exportModels) ExportMesh("bracelet.glb", bracelet.GetMesh(), {});
#endif
Expand All @@ -216,9 +222,16 @@ TEST(Samples, GyroidModule) {
const float precision = gyroid.Precision();
EXPECT_NEAR(bounds.min.z, 0, precision);
EXPECT_NEAR(bounds.max.z, size * glm::sqrt(2.0f), precision);

CrossSection slice = gyroid.Slice(5);
EXPECT_EQ(slice.NumContour(), 4);
EXPECT_NEAR(slice.Area(), 121.9, 0.1);
Manifold extrusion = Manifold::Extrude(slice, 1);
EXPECT_EQ(extrusion.Genus(), -3);

#ifdef MANIFOLD_EXPORT
if (options.exportModels)
ExportMesh("gyroidModule.gltf", gyroid.GetMesh(), {});
ExportMesh("gyroidModule.glb", gyroid.GetMesh(), {});
#endif
}

Expand Down
0