Home | Github | Feed | About

I really like the Manifold library.


Geometry Tree Image showing three main CSG boolean operations

Status Quo

I've been an OpenSCAD user for quite some time now. It's the pragmatic choice for parametric 3D modeling and my go-to solution for mechanical parts.

The tool comes with a script -> geometry compiler which uses a dedicated DSL. The language (while having C inspired syntax) is functional - constants only, pure functions, etc. Additionally, to produce any geometry, one must use its dedicated module system:

// A `module` is a block,
// which evaluates (implicitly returns?) some geometry
module highlight() {
    color("red") children();
}

// Modules can be chained
// leading to haskell style right-to-left syntax
scale(2.2) highlight() sphere(10.0);

While OpenSCAD does have it's quirks it generally gets the job done and is widely supported.

However..

There are some things I don't like about it. Especially when it comes to building larger, more complex models or using some of its (also not so small) third party libraries:

And boy there have been improvements - after switching to a developer snapshot compilation of my models went from tens of seconds to pretty much instantaneous! Like..huh? How is such improvement even possible?

Manifold

A library by Emmett Lalish that's responsible for turning solids into a triangle mesh and supports 3mf export. As opposed to STL, this file format shares vertices between adjacent triangles2 which avoids broken, disjointed meshes and subsequent fixing. i.e. manifold always produces "watertight" models with exact known volume.

Additionally it has broad set of bindings which means we can use well supported general purpose language with mature tooling to generate our geometry.

Since the library can be compiled to wasm - there even is a reference web interface ManifoldCAD. It's a great demo, but I would like to create local setup, which can be launched from CLI, use my $EDITOR of choice and have version control with git.

Local Setup

webp

Choosing among the many available languages comes down to a few constraints imposed by our local setup:

  1. The language must be interpreted and dynamic, so the library itself doesn't need to be loaded in memory on every model change - only the geometry code does.
  2. The wasm target is currently single-threaded, and available memory is limited by the V8 engine. Id like to run it "bare metal"

Together, these constraints basically leaves us with Python. Luckily due to it's use in robotics and ml-vision there are also many 3d viewers available. I chose viser since it has good rendering of semi-transparent models and updates are fast. After spending some time wiring convenience wrappers, implementing hot-reload and file export we are left syntax, which is quite a bit more expressive than scad language.

If you'd like to try this out - there is a repository

Bonus - Setting Tolerances

desmos Given an n sided polygon inscribed in circle - this formula calculates the required number of segments that satisfy some maximum tolerance ε. Screenshot from desmos playground

Since manifold works on triangulated meshes - any API for generating continuous shapes (cylinders, spheres) expects an explicit segment count.

Previously I'd eyeball the segment count, or set it globally with $fn. Neither is great since $fn applies the same segment count to every circular shape regardless of size, so small shapes end up overly dense.

For manufacturing, often we actually care about tolerance (ε). i.e. the maximum permissible deviation of the triangulated model from its SDF

For a simple circle the math is pretty straight forward it also approximates sphere close enough. Then - using formula from above and clamping it to something reasonable we get:

def segments(radius: float) -> int:
    tolerance = 0.05
    n = math.pi / math.acos(1.0 - tolerance / radius)
    return min(1024, max(8, math.ceil(n)))

Then this function can be used as the default segments parameter


  1. According to homebrew statistics around 25% of users are running nightly instead of release version of OpenSCAD

  2. In 3mf vertex data is stored in a separate array. Then - mesh is created by indexing into it.