New Project

Model Metrics#

Volume, mass, area, enclosed volumes, and a bill of materials

Names: volume no_join a b reason

When to use#

Use ww metrics <file> to ask what a design weighs, how much surface a finish must cover, what its box holds, and what to buy. The numbers are read from the built solids, so they follow every parameter. Add a volume node when you need an enclosed volume: the litres inside a speaker cabinet, a chest, a drawer, a sealed compartment. Metrics are a report: they never change the model.

Syntax#

volume Interior {
    bounded_by End_L End_R Front Back Bottom Lid
    subtract Shelf
}
  • volume <Name> is a top-level node of the project. Name keys the result in metrics.json.
  • bounded_by lists the parts whose union closes the region (part ids, or any unique trailing run of one: Lid, Chest/Lid). At least one.
  • subtract lists parts standing in the air that displace it (a shelf, a brace, a driver).
  • Nothing else goes inside volume; it takes no properties.

CLI: ww metrics <file> [--json] [--set name=value] [--waste F] [--coats N] [--end-grain-max X]. The fabrication pack carries the same report as metrics.json.

Contact completeness (issue #61 Stage 1). ww metrics finds every real planar contact between built parts (exact geometry, not bounding boxes) and warns (ww::analysis::unaccounted_contact) on any contact with no covering join and no covering no_join:

no_join a=Shelf b=Case reason="rests on cleats, lifts out, no joint by design"
  • no_join is a top-level node of the project, like volume.
  • a= and b= name the two touching parts the same way volume's bounded_by does: an exact id, or any unique trailing run of one. Both are required.
  • reason= is required and must not be empty — the escape hatch names why a real contact has no joint, it does not silence by merely existing.
  • This decides no joint type and changes no solid (D8 stands): it only asks whether the author said something about a touching pair.

Finish quantity. Give a material or part a finish="'danish oil'"; ww metrics matches the text to docs/finishes and, where that entry has a sourced coverage, reports litres as a range (finish.groups.<text>.litres). Entries with no sourced figure (shellac, paints, milk paint) report no quantity and say why.

Bill of materials. bom lists stock to buy per material and thickness (nominal, and with a waste factor: --waste F, default 0.15; board feet for timber), purchased items split into fastener (hardware with a length), insert (generated dowels, biscuits, dominos) and hardware, and consumables: glue (the area of the glue lines) and finish litres. Give hardware a part_no=, supplier= and price= and they appear in the list and the total.

Example#

project name="Box" units=mm schema_version=1 {
    materials { material Oak type=hardwood }
    component Plate { stock Oak 120.0 100.0 10.0 }
    component Side  { stock Oak 10.0 100.0 60.0 }
    component Rail  { stock Oak 100.0 10.0 60.0 }
    assembly Chest {
        part Bottom instance=Plate { pos x=0.0 y=0.0 z=-70.0 }
        part Top    instance=Plate { pos x=0.0 y=0.0 z=0.0 }
        part Left   instance=Side  { pos x=0.0 y=0.0 z=-10.0 }
        part Right  instance=Side  { pos x=110.0 y=0.0 z=-10.0 }
        part Front  instance=Rail  { pos x=10.0 y=0.0 z=-10.0 }
        part Back   instance=Rail  { pos x=10.0 y=90.0 z=-10.0 }
    }
    volume Inside { bounded_by Bottom Top Left Right Front Back }
}

Inside is 100 x 80 x 60 = 480 000 mm³ = 0.48 L (volumes.Inside.net_l).

Pitfalls and diagnostics#

  • A leak is an answer, not a guess. If the bounding parts do not close (a missing panel, an open driver cutout), status is leak and there is no volume. leak_closes_at_mm says how wide a seam would have to be thickened to close it; absent means a face is missing.
  • Seams under 2 micrometres are sealed and flagged: sealed_gap_mm and seal_error_bound_mm3 say how much the volume might be short.
  • Closed dowel sockets are not the enclosure: chambers under 1 mL or under 1% of the bounding box are listed as ignored_voids, not counted.
  • Mass is null where the material has no known density, with a reason: never a default. Give the material species="white-oak" or density_kg_m3=.... Mass is air-dry, about 12% moisture content.
  • Contact is planar only: round tenons in round mortises are not found as contact, so free area there is a slight over-statement.
  • Finish litres are a range, and the end-grain multiplier (default x2 high end) is an assumption: no maker figure for end grain is recorded. Curved end grain is not detected. Never trust a finish total with area_without_litres_m2 above zero as the whole job.
  • Glue is an area, not a quantity. No spread rate is recorded for any glue, so none is invented. Joined pairs that meet over a curved surface are counted in pairs_without_contact and left out of the area.
  • Stock totals are blanks, not a board layout: waste is a flat factor, and a ring blank (sized by its circumscribing square) over-counts. Only a hardware entry with a length is listed as a fastener.
  • Unpriced items are counted, never priced at zero: read unpriced_items beside priced_total.
  • A role=reference part is reported but is in no total and cannot bound a volume.
  • A real contact with no join and no no_join is ww::analysis::unaccounted_contact. This can flood a design that has many incidental true contacts by its own nature (a woven seat's warp/weft crossings, a veneer top's leaf-to-leaf edges) — each is a real finding, not a bug in the check, but excusing hundreds of such pairs one no_join at a time is often not the right fix; it is a sign the design's structure is not well served by a per-pair declaration and is worth raising, not silencing.
  • A no_join naming a part that does not resolve is reported, not silently ignored, and the contact it was meant to excuse is still flagged.

See also#

  • docs/adr/0036-model-metrics.md: the method, accuracy, and the JSON schema
  • docs/KDL_AUTHORING.md, the volume section
  • docs/proposals/0132-derived-quantities-and-linked-parameters.md section 3

Names#

Every name this page declares, how the example library uses it, and the shortest real use.

volume#

project-level node; node in 2 of 116 examples (2 uses); property in 2 of 116 examples (2 uses).

Shortest real use, from six_board_chest.kdl:

volume Interior {
    bounded_by End_L End_R Front Back Bottom Lid
}

no_join#

project-level node; not used by any example yet.

a#

word-valued property; not used by any example yet.

b#

word-valued property; not used by any example yet.

reason#

word-valued property; not used by any example yet.

Settings

Region

A region picks sensible defaults; change any of these separately.

Units

In inches, type lengths the way you say them: 34 1/2, 3/4", 1'-2" or 12.7mm. A length that can't be read is flagged, never rounded to something else.

Paper
Date order
Clock