- Rust 100%
Was a single loose stl-scale.rs with no manifest, so nothing could build it. Promoted to a crate; three fixes were needed to make it run: duplicated read_stl/arg check removed, --ray always exited 2 after printing, unused import. Added place() and its test for the scaling transform. |
||
|---|---|---|
| src | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| README.md | ||
stl-scale
Measure a binary STL and scale it: bounding box, the widest inner gap in X at 19 heights, and a scaled copy written back out. Standard library only, no crates.
Why
A printed part has to fit something — a drawer, a box, a socket, a lid. CAD knows the outer dimensions of the part it made; an imported STL does not answer "how wide is the inside at 40 % height", and viewers show the outside. This slices the mesh, measures the gap between the inner walls at 19 heights, and then writes the model at a different scale, centred and with the bottom on z=0 so it lands on the bed.
It started as a single loose stl-scale.rs (no crate, no git history) written for one
part; promoted to a crate because the geometry is real and the tool is reusable.
Build
cargo build --release # → target/release/stl-scale
No dependencies, no features. 551 KB release binary.
Usage
stl-scale in.stl --report # bbox + inner width per height
stl-scale in.stl # same as --report
stl-scale in.stl --scale 1.5 --out big.stl # scale, write binary STL
stl-scale in.stl --rim # footprint of vertices near the bottom
stl-scale in.stl --ray 1,5 # X crossings at y=1 on the slice z=5
Units are whatever the file uses — the labels say "mm", the numbers are raw.
What each flag does:
--report— triangle count, bbox (x × y × z), z range, then one line per height: at 5…95 % of the z range, a horizontal slice is cut, 29 rays are cast along X at evenly spaced y, and for each ray the middle pair of crossings is taken as the inner wall pair (4+ crossings are needed). The widest such gap in the slice is printed, and the maximum over all heights at the end.--rim— min/max x and y of every vertex within 2 mm of the bottom plane (z < z_min + 2). Footprint of a first layer, not a silhouette.--ray y,z— crossings of the y = const ray on the slice z = const, in 0.1 steps. Diagnostic for one height; alone it prints and stops, with--scaleit continues.--scale F— uniform scale about the X/Y centre of the bbox, z shifted so the bottom sits at 0. Output defaults toscaled.stl. Normals are written as zero and the per-triangle attribute bytes as0; the triangle order is unchanged and nothing is deduplicated.
Only the binary STL layout is read (80-byte header, u32 count, 50 bytes per triangle);
the size is asserted against the count, so an ASCII STL fails loudly instead of
producing garbage.
Tests
cargo test # 1 test
scale_centers_xy_and_puts_bottom_on_z0 — one triangle at the corner of a 4 × 2 × 4 mm
box with z 10…14, scaled by 2.5: expects x −5…5, y −2.5…2.5 (centred), z 0…10 (bottom on
zero). That covers place(), which is the whole scaling arithmetic; main() only feeds
it the bbox centre and lo[2].
Measurements
Verified against a generated hollow square ring (20 × 20 × 10 mm, 2 mm wall, 32 triangles, 1684 bytes):
tris 32
bbox mm 20.0 x 20.0 x 10.0
z 0.0 .. 10.0
max Innenweite X 12.0 mm bei z=0.5 (x -6.0 .. 6.0) # 12 mm at every one of the 19 heights
stl-scale ring.stl --scale 1.5 --out ring15.stl
geschrieben …/ring15.stl 30.0 x 30.0 x 15.0 mm (scale 1.5)
# --report on ring15.stl: 30.0 x 30.0 x 15.0, max Innenweite X 18.0 mm
Deviations
- Binary STL only. No ASCII reader;
read_stlpanics with "kein binary STL". - Inner width needs a hollow profile. A ray has to cross at least 4 walls (outer, inner, inner, outer). A solid block, or a shape with one wall per side, reports 0.0 mm — that is "no measurable gap", not "0 mm wide".
- A ray exactly through a slice vertex counts 0 crossings (the test is strict
< 0on both sides).--ray 0,5on a symmetric part can return nothing where--ray 0.5,5returns four crossings. Nudge y when that happens. - Sampling is fixed: 19 heights, 29 rays per slice. Not configurable; if a feature is narrower than a ray step it is invisible.
- No mesh checks. Open, self-intersecting or inverted meshes are sliced as they are — wrong input gives a wrong measurement, not an error.
- Scaling is uniform and translates. No rotation, no unit conversion, no mirroring; the model is centred in X/Y and dropped onto z = 0.
- Numbers in the output are in German ("Innenweite", "geschrieben"), matching the rest of these tools.