TheRON 0545b79674 stl: export a member as a sealed mesh
prism_mesh takes a region and a length, not a Result. A member, its support, or
the support alone are then three calls with different regions rather than three
special cases inside one function -- support is a designed part of an artifact
where no printable orientation exists, not something a slicer adds, and
flattening everything into one mesh now would be expensive to undo later.

Named and documented as a MEMBER exporter. ROADMAP section 2 has three artifact
classes: members are prismatic and exist, nodes are non-prismatic and do not.
A prism sweep will never produce a node and no amount of extending this should
be attempted. A module called stl.py would invite exactly that.

No CAD kernel. Caps are a constrained Delaunay triangulation of the section
with its holes, walls are a quad strip per ring. That is why STL costs no new
dependency while STEP would.

Winding is forced, never assumed. region_parts returns each part outer-clockwise
with holes counter-clockwise because region_area depends on that to make holes
subtract; a prism swept along +Z needs the opposite, since traversing the outer
boundary counter-clockwise puts material on the left and the wall normal on the
right, which is outward. Both are reversed here.

The identifiers in the 80-byte header are PROVENANCE, NOT A CHECKSUM. Principle
4: mesh bytes are not reproducible across toolchain versions, so two exports
carrying the same build_id may differ byte for byte and both be correct -- a
GEOS release can move a triangulation without moving the geometry, which is the
F-034 mechanism applied to tessellation. The docstring says so where someone
diffing two exports will find it. Facet normals are written as zeros: every
consumer recomputes from vertex order, and a stored normal would be a second
source of truth for which way a face points.

A section failing is_region_simple is refused rather than exported. Its own
docstring names the case: an outline that measures perfectly, touches itself at
a point, and cannot be sealed. Emitting it would produce a file that opens in a
slicer and prints wrong.

member_stl defaults to the report's LENGTH_MM, the value the record's
VOLUME_MM3 and MASS_G were computed against. Any other sweep makes the record
describe a different object than the file beside it.

61 tests across all eleven profiles in both families, four properties each,
every negative assertion with a positive control. Edge pairing catches a missing
wall and inconsistent winding; Euler catches a filled or invented hole; area
overlap catches a bore paved over; volume against region_area times length
catches inverted normals and a wrong length together.

Two test defects found by mutation and fixed before landing, both worth
recording because both passed while being wrong.

The hole check originally tested triangle centroids. Ignoring holes at
triangulation time slipped through it -- two triangles can pave a bore with
both centroids outside it. Replaced with intersection area, which catches it.

The sweep-length test built at the family defaults, where preview_length_mm is
100, so a mutation hardcoding 100.0 passed the entire suite: the assertion was
comparing a value against the constant that had replaced it. Now parametrised
over 37.5 mm and 3048 mm, and proven against hardcoded mutations at both
values, each caught by the other case.

Mutation results against the real sections: holes not reversed fails 23,
exterior-only triangulation fails 45, outer ring not forced CCW fails 25,
simplicity precondition removed fails exactly 1 -- the refusal test written for
it. Restoration verified by checksum rather than by a green run, after a
same-length mutation plus a cached .pyc produced a false clean reading earlier
in the session. PYTHONDONTWRITEBYTECODE=1 throughout, per HANDOFF section 8.

Suite 627 passed. None of the 566 moved.
2026-09-12 10:40:35 -05:00
2026-08-14 09:21:31 -04:00

Mechanical Compiler

Build the capacity to construct real structures from reclaimed and commodity materials, using whatever fabrication is actually to hand — 3D printing, tabletop CNC, welding, cement casting, COTS stock.

The reference case is a faceted timber shell: planar panels meeting along straight fold lines, converging on nodes, sitting on a platform. Buildable without a factory, provided someone has worked out what the pieces are and how they meet. That last clause is the project. The Mechanical Compiler exists to make the pieces computable, qualifiable, and reproducible by someone who was not present when they were designed.

Scope

In scope: geometry, qualification, and reproducibility of structural members and their interfaces.

Not in scope: building codes, permitting, jurisdictional approval. Qualification and compliance are different things. A qualification says this member is what it claims to be, made this way, from this stock. Compliance is a jurisdiction-specific argument someone else may build on top.

The project records physical claims, never verdicts — section modulus, moment of inertia, material provenance, process parameters. Those are what any future argument would need. A pass/fail verdict would bake in a jurisdiction we do not want to be bound to. Measure and attest; never adjudicate.

Repository layout

docs/                     specification, state, failure log, roadmap
legacy/openscad/          rev 8.0.0 generators — reference, not a live target
fixtures/                 frozen acceptance oracles
tools/reference-toolchain/  pinned OpenSCAD + BOSL2, build-time only
src/mechcomp/             the application
tests/                    acceptance against the oracle

Read docs/ROADMAP.md first for what this is, then docs/ENVIRONMENT.md for how an instance is built. If you are writing provisioning automation, read docs/FAILURES.md before the specification — every entry is something a script written from the specification alone would have got wrong.

Getting started

make deps            # virtualenv, base + CAD requirements
make verify-oracle   # confirm the frozen oracle is intact
make test            # pytest -n auto

make verify-oracle needs nothing but Python. It should pass on any machine at any time; if it does not, stop.

The oracle

fixtures/strap-beam-8.0.0/ holds 123 frozen cases — 113 accepted, 10 rejected — produced by OpenSCAD 2021.01 with BOSL2 at 92d697c2. It is the acceptance criterion for any reimplementation of the generators.

The ten rejected cases are part of the contract. A port that accepts them is wrong, however good its numbers look elsewhere. It is easy to reproduce the geometry and quietly lose the constraint that made it trustworthy.

The generators under legacy/openscad/ are the reference implementation, kept so the oracle can be regenerated. They are not a live target and the running application has no OpenSCAD dependency.

Provenance

This project's documents, code and roadmap are LLM-generated under human direction. That is stated plainly here and in any downstream submission. We do not obscure it.

The maintainer reviews and owns every line. Anything that could not be defended in a review thread does not ship.

Licence

AGPL-3.0-or-later. See LICENSE.

Section 13 obliges us to offer source to users interacting over a network, so any deployed web tier carries a visible link back to this repository. That is a licence obligation, not a courtesy.

S
Description
No description provided
Readme AGPL-3.0
1.1 MiB
Languages
Python 81%
OpenSCAD 18%
Shell 0.5%
Makefile 0.3%
Dockerfile 0.2%