No description
Find a file
Pandi a6bb709950 initial: stl-scale as a crate (report, rim, ray, scale)
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.
2026-11-04 09:31:32 +01:00
src initial: stl-scale as a crate (report, rim, ray, scale) 2026-11-04 09:31:32 +01:00
.gitignore initial: stl-scale as a crate (report, rim, ray, scale) 2026-11-04 09:31:32 +01:00
Cargo.lock initial: stl-scale as a crate (report, rim, ray, scale) 2026-11-04 09:31:32 +01:00
Cargo.toml initial: stl-scale as a crate (report, rim, ray, scale) 2026-11-04 09:31:32 +01:00
README.md initial: stl-scale as a crate (report, rim, ray, scale) 2026-11-04 09:31:32 +01:00

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 --scale it continues.
  • --scale F — uniform scale about the X/Y centre of the bbox, z shifted so the bottom sits at 0. Output defaults to scaled.stl. Normals are written as zero and the per-triangle attribute bytes as 0; 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_stl panics 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 < 0 on both sides). --ray 0,5 on a symmetric part can return nothing where --ray 0.5,5 returns 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.