Skip to content

Vitamins.get: surface admesh as an optional recovery path when STL fails the manifold check #482

@JansenSmith

Description

@JansenSmith

Summary

Vitamins.get(File) (in bowler-script-kernel) loads an STL into a CSG. When the mesh fails the Manifold topological check — surfaced as ManifoldError.NOT_MANIFOLD from the manifold3d-java binding to Google's libmanifold — the load fails terminally and the user has no in-app guidance on what went wrong or what to try next.

Filing this as a UX / recovery-path discussion, not a bug. The manifold check itself is correct.

The relevant code

Reproducer

Self-contained A/B/C study with a runnable Groovy script:
https://github.com/JansenSmith/bowlerstudio-manifold-aliasing

asset provenance admesh -fudv report result via Vitamins.get
A bisonCouche_aliased.stl HueForge raw, min_detail = 0.3 6 disconnected facets, 4 wrongly-oriented normals NOT_MANIFOLD
B bisonCouche_Front_242x175.stl HueForge raw, min_detail = 0.2 (same project as A) 0 disconnected facets loads cleanly
C bisonCouche_admesh_fix.stl admesh-repaired version of A 0 disconnected facets loads cleanly

Open manifold_aliasing_test.groovy in BowlerStudio and press Run. A fails as expected; B and C load with dimensions printed. (The reproducer wraps Vitamins.get through a small user-land download helper, but the failure point is at Vitamins.)

C was produced from A with a single command:

admesh -fudvb bisonCouche_admesh_fix.stl bisonCouche_aliased.stl

-f fill-holes, -u remove-unconnected, -d normal-directions, -v normal-values, -b NAME write-binary-stl. On already-manifold input (the B case), admesh is a no-op — non-destructive.

Two paths, low effort to high

Path 1 — surface admesh in the failure message

When Vitamins.get fails with NOT_MANIFOLD, include in the message:

  • a one-line pointer to admesh (https://github.com/admesh/admesh)
  • the standard repair command, with the user's failing path interpolated, ready to copy-paste
  • a mention of the opt-in flag described in Path 2, if implemented

That alone gives the user a concrete next step. A handful of lines, no new dependencies, no structural change.

Path 2 — optional flag for BS to run admesh

A Vitamins.get(file, recoverWithAdmesh: true) overload (or equivalent). When the flag is set and the mesh fails NOT_MANIFOLD, BS invokes admesh and retries; without the flag, Path 1's behavior applies. Provisioning admesh fits the pattern BS already uses for managed dependencies (~/bin/BowlerStudioInstall/...). CaDoodle could surface this as a "non-manifold input — try repair?" dialog on the GUI side.

Why this matters in practice

I hit this during a real piece bring-up. The HueForge export silently produced 6 orphan facets at min_detail = 0.3; re-exporting at 0.2 produced a clean ~2.13M-triangle mesh. The fix was easy once I knew where to look, but the discovery path — STL fails → suspect HueForge → A/B test settings → confirm with admesh → identify admesh as the rescue — took a couple of hours that a one-line hint would have collapsed to a couple of minutes.

Related (separate sub-note)

DownloadManager (also in bowler-script-kernel) has an on-disk cache at ~/bin/BowlerStudioInstall/0.1.0/<fileName>.stl that's independent of Vitamins.clear(). While debugging non-manifold inputs, every re-run re-tested the previously-downloaded bytes regardless of release-asset updates, until I manually rm'd the cached file. Happy to split this into its own issue if preferred — flagging here because they came up together.

Environment

  • BowlerStudio 5.1.0
  • HueForge v0.9.2.3 (Linux AppImage)
  • admesh 0.98.5

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions