ContentsFoundations to FrontierBezhig

eng-lab/hwe/cad/boundary.py

All lab code. Download this file.

"""The units boundary between the engine and build123d.

build123d, like most CAD kernels, works in plain numbers and assumes they are
millimetres. The rest of this engine never uses a bare number. This module is
the one border crossing, and it is deliberately tiny so every crossing is easy
to audit:

    mm(Q_(0.15, "m"))        -> 150.0      a length leaving the unit system
    volume(part)             -> Q_(8587.4, "mm^3")
    mass(part, Q_(1.55, "g/cm^3"))  -> Q_(13.3, "g")

The classic bug this prevents: handing build123d a length in metres (0.15)
where it expects millimetres (150). The part comes out 1000 times too small
and its volume 10^9 times too small (EL01, bug U10), with no error anywhere.
"""
from __future__ import annotations

from hwe.units import Q_, Quantity, quantities, require


def mm(length: Quantity) -> float:
    """A length as a bare number of millimetres, for build123d."""
    return require(length, "[length]", "length").m_as("mm")


def volume(part) -> Quantity:
    """The solid's volume (build123d reports it in cubic millimetres)."""
    return Q_(part.volume, "mm^3")


@quantities(density="[density]", returns="[mass]")
def mass(part, density):
    """Mass of a solid part of uniform density: m = density x volume. Returns grams."""
    return (density * volume(part)).to("g")


def centre_of_mass(part) -> tuple[Quantity, Quantity, Quantity]:
    """The centre of mass (uniform density) as three lengths in mm. Used from EL02 on."""
    from build123d import CenterOf
    c = part.center(CenterOf.MASS)
    return Q_(c.X, "mm"), Q_(c.Y, "mm"), Q_(c.Z, "mm")