Skip to content

Commit

Permalink
SDF collapse (#894)
Browse files Browse the repository at this point in the history
* refactor to collapse verts

* fixed missing triangles

* tests pass

* cleanup, docs

* Moved LevelSet from MeshGL to Manifold

* update bindings

* minor fixes

* more python fixes

* update clang-format version

* updated sinesurface test

* revert unneeded change

* fixed WASM

* update Python

* remove opposed triangles

* update format version

* fix format

* remove unreferenced verts

* fix 4-manifolds

* reduce blob resolution

* avoid collapsing thin surfaces

* cleanup, tighten tests

* compromise heuristic

* update test

* slight heuristic tweak and update docs

* loosen genus test
  • Loading branch information
elalish authored Sep 1, 2024
1 parent a0e4d1b commit fbf07bf
Show file tree
Hide file tree
Showing 27 changed files with 483 additions and 340 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/check_format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ jobs:

steps:
- uses: actions/checkout@v4
- uses: DoozyX/clang-format-lint-action@v0.17
- uses: DoozyX/clang-format-lint-action@v0.18
with:
source: '.'
exclude: '*/third_party'
extensions: 'h,cpp,js,ts,html'
clangFormatVersion: 15
clangFormatVersion: 18
- uses: psf/black@stable
with:
options: "--check --verbose"
Expand Down
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"args": [
"--gtest_break_on_failure",
"--gtest_catch_exceptions=0",
"--gtest_filter=SDF.Gyroid"
"--gtest_filter=Samples.GyroidModule"
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/build/test",
Expand Down
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,9 @@
"halfedge",
"halfedges",
"Tris",
"Verts"
"Verts",
"Voxel",
"voxels"
],
"javascript.format.semicolons": "insert",
"[python]": {
Expand Down
10 changes: 5 additions & 5 deletions bindings/c/include/manifoldc.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ ManifoldMeshGL *manifold_meshgl_merge(void *mem, ManifoldMeshGL *m);
// using these bindings from a language that has a runtime lock preventing the
// parallel execution of closures, then you should use manifold_level_set_seq to
// force sequential execution.
ManifoldMeshGL *manifold_level_set(void *mem,
float (*sdf)(float, float, float, void *),
ManifoldBox *bounds, float edge_length,
float level, float precision, void *ctx);
ManifoldMeshGL *manifold_level_set_seq(
ManifoldManifold *manifold_level_set(void *mem,
float (*sdf)(float, float, float, void *),
ManifoldBox *bounds, float edge_length,
float level, float precision, void *ctx);
ManifoldManifold *manifold_level_set_seq(
void *mem, float (*sdf)(float, float, float, void *), ManifoldBox *bounds,
float edge_length, float level, float precision, void *ctx);

Expand Down
20 changes: 10 additions & 10 deletions bindings/c/manifoldc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,18 @@
using namespace manifold;

namespace {
ManifoldMeshGL *level_set(void *mem,
float (*sdf_context)(float, float, float, void *),
ManifoldBox *bounds, float edge_length, float level,
float precision, bool seq, void *ctx) {
ManifoldManifold *level_set(void *mem,
float (*sdf_context)(float, float, float, void *),
ManifoldBox *bounds, float edge_length, float level,
float precision, bool seq, void *ctx) {
// Bind function with context argument to one without
using namespace std::placeholders;
std::function<float(float, float, float)> sdf =
std::bind(sdf_context, _1, _2, _3, ctx);
std::function<float(glm::vec3)> fun = [sdf](glm::vec3 v) {
return (sdf(v.x, v.y, v.z));
};
return to_c(new (mem) MeshGL(MeshGL::LevelSet(
return to_c(new (mem) Manifold(Manifold::LevelSet(
fun, *from_c(bounds), edge_length, level, precision, !seq)));
}
} // namespace
Expand Down Expand Up @@ -271,14 +271,14 @@ ManifoldManifold *manifold_warp(void *mem, ManifoldManifold *m,
return to_c(new (mem) Manifold(warped));
}

ManifoldMeshGL *manifold_level_set(void *mem,
float (*sdf)(float, float, float, void *),
ManifoldBox *bounds, float edge_length,
float level, float precision, void *ctx) {
ManifoldManifold *manifold_level_set(void *mem,
float (*sdf)(float, float, float, void *),
ManifoldBox *bounds, float edge_length,
float level, float precision, void *ctx) {
return level_set(mem, sdf, bounds, edge_length, level, precision, false, ctx);
}

ManifoldMeshGL *manifold_level_set_seq(
ManifoldManifold *manifold_level_set_seq(
void *mem, float (*sdf)(float, float, float, void *), ManifoldBox *bounds,
float edge_length, float level, float precision, void *ctx) {
return level_set(mem, sdf, bounds, edge_length, level, precision, true, ctx);
Expand Down
2 changes: 1 addition & 1 deletion bindings/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pip install .
python -c 'import manifold3d; help(manifold3d)'
```

Alternateively you could generate stubs with roughly the same info
Alternatively you could generate stubs with roughly the same info
```
pip install nanobind-stubgen
pip install .
Expand Down
12 changes: 5 additions & 7 deletions bindings/python/examples/gyroid_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,11 @@ def gyroid(x, y, z):


def gyroid_levelset(level, period, size, n):
return Manifold(
Mesh.level_set(
gyroid,
[-period, -period, -period, period, period, period],
period / n,
level,
)
return Manifold.level_set(
gyroid,
[-period, -period, -period, period, period, period],
period / n,
level,
).scale([size / period] * 3)


Expand Down
8 changes: 5 additions & 3 deletions bindings/python/gen_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@

base = dirname(dirname(dirname(__file__)))


def snake_case(name):
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", name).lower()


def python_param_modifier(comment):
# p = f":{snake_case(m[0][1:])}:"
comment = re.sub(r"@(param \w+)", lambda m: f':{snake_case(m[1])}:', comment)
comment = re.sub(r"@(param \w+)", lambda m: f":{snake_case(m[1])}:", comment)
# python API renames `MeshGL` to `Mesh`
comment = re.sub('mesh_gl', 'mesh', comment)
comment = re.sub('MeshGL', 'Mesh', comment)
comment = re.sub("mesh_gl", "mesh", comment)
comment = re.sub("MeshGL", "Mesh", comment)
return comment


Expand Down Expand Up @@ -90,6 +91,7 @@ def select_functions(s):
collect(f"{base}/src/manifold/src/manifold.cpp", lambda s: method_re.search(s))
collect(f"{base}/src/manifold/src/constructors.cpp", lambda s: method_re.search(s))
collect(f"{base}/src/manifold/src/sort.cpp", lambda s: method_re.search(s))
collect(f"{base}/src/manifold/src/sdf.cpp", lambda s: method_re.search(s))
collect(
f"{base}/src/cross_section/src/cross_section.cpp", lambda s: method_re.search(s)
)
Expand Down
67 changes: 20 additions & 47 deletions bindings/python/manifold3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,7 @@ NB_MODULE(manifold3d, m) {
manifold__translate__v)
.def("scale", &Manifold::Scale, nb::arg("v"), manifold__scale__v)
.def(
"scale",
[](const Manifold &m, float s) {
m.Scale({s, s, s});
},
"scale", [](const Manifold &self, float s) { self.Scale({s, s, s}); },
nb::arg("s"),
"Scale this Manifold in space. This operation can be chained. "
"Transforms are combined and applied lazily.\n\n"
Expand Down Expand Up @@ -433,6 +430,24 @@ NB_MODULE(manifold3d, m) {
nb::arg("crossSection"), nb::arg("circular_segments") = 0,
nb::arg("revolve_degrees") = 360.0,
manifold__revolve__cross_section__circular_segments__revolve_degrees)
.def_static(
"level_set",
[](const std::function<float(float, float, float)> &f,
std::vector<float> bounds, float edgeLength, float level = 0.0,
float precision = -1) {
// Same format as Manifold.bounding_box
Box bound = {glm::vec3(bounds[0], bounds[1], bounds[2]),
glm::vec3(bounds[3], bounds[4], bounds[5])};

std::function<float(glm::vec3)> cppToPython = [&f](glm::vec3 v) {
return f(v.x, v.y, v.z);
};
return Manifold::LevelSet(cppToPython, bound, edgeLength, level,
precision, false);
},
nb::arg("f"), nb::arg("bounds"), nb::arg("edgeLength"),
nb::arg("level") = 0.0, nb::arg("precision") = -1,
manifold__level_set__sdf__bounds__edge_length__level__precision__can_parallel)
.def_static(
"cylinder", &Manifold::Cylinder, nb::arg("height"),
nb::arg("radius_low"), nb::arg("radius_high") = -1.0f,
Expand Down Expand Up @@ -550,46 +565,6 @@ NB_MODULE(manifold3d, m) {
.def_ro("run_index", &MeshGL::runIndex)
.def_ro("run_original_id", &MeshGL::runOriginalID)
.def_ro("face_id", &MeshGL::faceID)
.def_static(
"level_set",
[](const std::function<float(float, float, float)> &f,
std::vector<float> bounds, float edgeLength, float level = 0.0,
float precision = -1) {
// Same format as Manifold.bounding_box
Box bound = {glm::vec3(bounds[0], bounds[1], bounds[2]),
glm::vec3(bounds[3], bounds[4], bounds[5])};

std::function<float(glm::vec3)> cppToPython = [&f](glm::vec3 v) {
return f(v.x, v.y, v.z);
};
return MeshGL::LevelSet(cppToPython, bound, edgeLength, level,
precision, false);
},
nb::arg("f"), nb::arg("bounds"), nb::arg("edgeLength"),
nb::arg("level") = 0.0, nb::arg("precision") = -1,
"Constructs a level-set Mesh from the input Signed-Distance Function "
"(SDF) This uses a form of Marching Tetrahedra (akin to Marching "
"Cubes, but better for manifoldness). Instead of using a cubic grid, "
"it uses a body-centered cubic grid (two shifted cubic grids). This "
"means if your function's interior exceeds the given bounds, you "
"will see a kind of egg-crate shape closing off the manifold, which "
"is due to the underlying grid."
"\n\n"
":param f: The signed-distance functor, containing this function "
"signature: `def sdf(xyz : tuple) -> float:`, which returns the "
"signed distance of a given point in R^3. Positive values are "
"inside, negative outside."
":param bounds: An axis-aligned box that defines the extent of the "
"grid."
":param edgeLength: Approximate maximum edge length of the triangles "
"in the final result. This affects grid spacing, and hence has a "
"strong effect on performance."
":param level: You can inset your Mesh by using a positive value, or "
"outset it with a negative value."
":param precision: The verts will be within this distance of the "
"real surface."
":return Mesh: This mesh is guaranteed to be manifold."
"Use Manifold.from_mesh(mesh) to create a Manifold")
.def("merge", &MeshGL::Merge, mesh_gl__merge);

nb::enum_<Manifold::Error>(m, "Error")
Expand Down Expand Up @@ -674,9 +649,7 @@ NB_MODULE(manifold3d, m) {
cross_section__scale__scale)
.def(
"scale",
[](const CrossSection &self, float s) {
self.Scale({s, s});
},
[](const CrossSection &self, float s) { self.Scale({s, s}); },
nb::arg("s"),
"Scale this CrossSection in space. This operation can be chained. "
"Transforms are combined and applied lazily."
Expand Down
4 changes: 2 additions & 2 deletions bindings/wasm/examples/worker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ suite('Examples', () => {
test('Gyroid Module', async () => {
const result = await runExample('Gyroid Module');
expect(result.genus).to.equal(15, 'Genus');
expect(result.volume).to.be.closeTo(4167, 1, 'Volume');
expect(result.surfaceArea).to.be.closeTo(5642, 1, 'Surface Area');
expect(result.volume).to.be.closeTo(4175, 1, 'Volume');
expect(result.surfaceArea).to.be.closeTo(5645, 1, 'Surface Area');
});
});
2 changes: 1 addition & 1 deletion bindings/wasm/helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ Manifold LevelSet(uintptr_t funcPtr, Box bounds, float edgeLength, float level,
float precision) {
float (*f)(const glm::vec3&) =
reinterpret_cast<float (*)(const glm::vec3&)>(funcPtr);
return Manifold(MeshGL::LevelSet(f, bounds, edgeLength, level, precision));
return Manifold::LevelSet(f, bounds, edgeLength, level, precision);
}

std::vector<Manifold> Split(Manifold& a, Manifold& b) {
Expand Down
5 changes: 2 additions & 3 deletions samples/src/gyroid_module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,8 @@ namespace manifold {
Manifold GyroidModule(float size, int n) {
auto gyroid = [&](float level) {
const float period = glm::two_pi<float>();
return Manifold(MeshGL::LevelSet(Gyroid(),
{glm::vec3(-period), glm::vec3(period)},
period / n, level))
return Manifold::LevelSet(Gyroid(), {glm::vec3(-period), glm::vec3(period)},
period / n, level)
.Scale(glm::vec3(size / period));
};

Expand Down
7 changes: 3 additions & 4 deletions src/manifold/include/manifold.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,6 @@ struct MeshGL {
MeshGL() = default;
MeshGL(const Mesh& mesh);

static MeshGL LevelSet(std::function<float(glm::vec3)> sdf, Box bounds,
float edgeLength, float level = 0,
float precision = -1, bool canParallel = true);

bool Merge();
};
/** @} */
Expand Down Expand Up @@ -185,6 +181,9 @@ class Manifold {
static Manifold Revolve(const Polygons& crossSection,
int circularSegments = 0,
float revolveDegrees = 360.0f);
static Manifold LevelSet(std::function<float(glm::vec3)> sdf, Box bounds,
float edgeLength, float level = 0,
float precision = -1, bool canParallel = true);
///@}

/** @name Topological
Expand Down
2 changes: 2 additions & 0 deletions src/manifold/src/constructors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,8 @@ std::vector<Manifold> Manifold::Decompose() const {
return vertLabel[halfedge[3 * face].startVert] == i;
}) -
faceNew2Old.begin();

if (nFace == 0) continue;
faceNew2Old.resize(nFace);

impl->GatherFaces(*pImpl_, faceNew2Old);
Expand Down
Loading

0 comments on commit fbf07bf

Please sign in to comment.