25592fa6c2
ci / test (push) Successful in 6m14s
History-free snapshot of the private development repository at 77a82eb. Code, docs and the public catalogue only: no plant or job knowledge layers, no client, job or producer names (tools/scrub.py clean against the widened term list; a second word-level grep clean). The intro deck is left out because its slide images cannot be checked for job drawings.
68 lines
2.8 KiB
Python
68 lines
2.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Panel Analysis: decimal feet -> feet-inches display text, shared.
|
|
|
|
Owner ruling (`CLAUDE.md` / the pagespec docs): every length this tool
|
|
displays is feet-inches to 1/8" (`8'-4 1/2"`), never decimal feet -- the same
|
|
rule `tools/pa_web/static/units.js`'s `Units.fmtFtIn` enforces client-side for
|
|
plain editable fields. A `kv` block or a read-only table CELL built server-
|
|
side (Panel Weight & Centroid, the lifter/dunnage summary tables) is plain
|
|
text by the time it reaches the page -- the client never reformats it -- so
|
|
the same rounding has to happen here, in CPython/IronPython, before the text
|
|
is ever sent.
|
|
|
|
Split out of `vibe.pa_extents._feet_str` (2026-09-05) once a second module
|
|
(`vibe.pa_handling`) needed the identical formatting for its own kv/table
|
|
cells -- one algorithm, not two copies that can drift on a rounding edge.
|
|
Standard library only, both Pythons: no f-strings, no `pathlib`, no
|
|
`dataclasses`/typing/`enum`.
|
|
"""
|
|
|
|
# Eighths-of-an-inch -> the fraction word this module prints. A precast
|
|
# dimension finer than 1/8" is not meaningful, so `feet_str` rounds to it
|
|
# rather than pretending to more precision than the field carries.
|
|
EIGHTHS = {0: '', 1: '1/8', 2: '1/4', 3: '3/8', 4: '1/2',
|
|
5: '5/8', 6: '3/4', 7: '7/8'}
|
|
|
|
|
|
def feet_str(value):
|
|
"""Decimal feet -> an architectural dimension string, e.g. 8'-4 1/2".
|
|
|
|
Internal units are decimal feet throughout this codebase (`CLAUDE.md`
|
|
SS3), so a bare number is read as feet and converted here to the
|
|
feet-inches form the owner requires everywhere a length is displayed.
|
|
A value that is already a string is left to the CALLER to decide
|
|
whether to pass through unreformatted (a sibling module may hand this a
|
|
value that is already formatted correctly, and reformatting a string
|
|
that already reads right is how a "2'-11.5\"" becomes a wrong
|
|
"2'-11 1/2\"" glued onto a stray decimal) -- this function itself always
|
|
tries to read `value` as a plain number.
|
|
|
|
Returns `None` only when `value` cannot be read as a number at all --
|
|
never a fabricated dimension.
|
|
"""
|
|
try:
|
|
feet = float(value)
|
|
except (TypeError, ValueError):
|
|
return None
|
|
negative = feet < 0.0
|
|
feet = abs(feet)
|
|
whole_ft = int(feet)
|
|
inches = (feet - whole_ft) * 12.0
|
|
eighths_total = int(round(inches * 8.0))
|
|
whole_in, eighths = divmod(eighths_total, 8)
|
|
if whole_in >= 12:
|
|
whole_ft += whole_in // 12
|
|
whole_in = whole_in % 12
|
|
frac = EIGHTHS[eighths]
|
|
if frac and whole_in:
|
|
in_part = '{0} {1}'.format(whole_in, frac)
|
|
elif frac:
|
|
in_part = frac
|
|
else:
|
|
in_part = str(whole_in)
|
|
if whole_ft > 0:
|
|
text = u'{0}\'-{1}"'.format(whole_ft, in_part)
|
|
else:
|
|
text = u'{0}"'.format(in_part)
|
|
return u'-' + text if negative else text
|