Files
Umut CANandClaude Opus 5.5 d5c5e90406 feat(arch): the plan model and its foundation - layers, materials, EN/TR, two generalisations
Track F starts here; the three group branches are cut from this commit.

- engineering/arch/model.py: Wall, Opening, Stair and Room as frozen
  dataclasses, and every refusal of spec 4 before anything is drawn, by path
  (walls[2].thickness): finite positive numbers, an axis of at least two
  distinct points with no zero-length segment, an arc segment refused by name
  rather than approximated, an opening on an unknown wall, past its wall,
  across an axis vertex or overlapping another (both ids named), an unknown
  material, and a typed room area - areas are measured, never typed. The
  plan travels as ACADMCP_ARCH XDATA; an opening's door/window kind and a
  stair's straight/l/u kind ride beside the payload kind as opening_kind /
  stair_kind, because one key cannot hold both.
- engineering/arch/layers.py: the arch set - thirteen ISO 13567-style layers
  with ISO 128 lineweights only (0.05 mm on the construction scratch layer,
  as every set in this repository) - and ARCH_ROLE_LAYER, the one place a
  role becomes a layer. Registered in engineering/layers.py last, which is
  what keeps the engineering.layers <-> engineering.arch import cycle closed.
- engineering/arch/materials.py: eight wall materials, one pattern each,
  every row citing its source: ANSI31 for brick (ISO 128-50 general hatching,
  acadiso.pat's own "Iron, Brick, Stone masonry"), AAC as ANSI31 at twice the
  spacing (a repository convention, and it says so), AR-CONC / AR-SAND as
  AutoCAD patterns, and the JIS A 0150 RC / stone / wood symbols. Reinforced
  concrete cannot be ANSI31 and AR-CONC in one HATCH, so it takes the one
  symbol that names it. Periods on a 1:50 sheet are recomputed from ezdxf's
  own pattern definitions by the tests. normalise_material is the one reading
  of a material name, shared by the model and wall_hatch, so the model never
  refuses "Reinforced Concrete" that the hatch table resolves.
- engineering/arch/lang.py: EN / TR vocabulary, D/W and K/P tag prefixes, and
  the decimal comma for lang="tr" (24,50 m2).
- draw_prims(role_layer=) and the XDATA codec's app_id= / kinds=: both
  generalised with the mechanical defaults, and the mechanical suite passes
  unchanged against them - that is the regression gate.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
2026-09-23 22:27:00 +03:00

126 lines
5.0 KiB
Python

"""Wall material -> the area hatch (poche) of its cut face.
SOURCE, stated row by row because it is not one document:
* ISO 128-50 (representation of areas on cuts and sections) standardises only
the *general* hatching — narrow continuous lines at 45 degrees — and leaves
distinguishing materials to other conventions. ``ANSI31`` in AutoCAD 2026's
``acadiso.pat`` is that line set, and its own description in the installed
file (``R25.1/enu/Support/acadiso.pat``) reads ``*ANSI31,ANSI Iron, Brick,
Stone masonry``. ``brick`` and ``generic`` take it.
* ``reinforced_concrete``, ``stone`` and ``timber`` take the JIS A 0150
(Japanese architectural drawing conventions) symbols AutoCAD ships in the
same file: ``*JIS_RC_15, RC JIS A 0150(@15)``, ``*JIS_STN_2.5, STONE JIS A
0150(@2.5)`` and ``*JIS_WOOD, WOOD JIS A 0150``. A reinforced-concrete
section cannot be ANSI31 *and* AR-CONC in one HATCH entity, so it takes the
one symbol that names reinforced concrete.
* ``concrete`` and ``gypsum_board`` take AutoCAD's own ``*AR-CONC, random dot
and stone pattern`` and ``*AR-SAND, random dot pattern`` — declared as
AutoCAD patterns, not standard symbols.
* ``aac`` (autoclaved aerated concrete) has no symbol in any of these. It is
ANSI31 at twice the brick spacing — a lighter diagonal — which is a
repository convention and says so.
* ``timber`` adds 90 degrees so its single line family runs on the opposite
diagonal to brick: JIS_WOOD is one 45-degree family, and unrotated it would
be indistinguishable from brick on the sheet.
``scale`` is chosen for a 1:50 plan, the default plot scale of every ``arch``
tool (``wall_hatch`` takes no scale, as pinned): brick lines 3.175 x 20 =
63.5 mm apart, 1.27 mm on a 1:50 sheet; aac 2.54 mm; reinforced concrete one
triple-line group every 150 mm (3.0 mm on the sheet); stone 75 mm (1.5 mm);
timber 0.7071 x 90 = 63.6 mm (1.27 mm). AR-CONC and AR-SAND are defined in
building millimetres and ship at 1.0. The periods are recomputed from ezdxf's
own pattern definitions by ``tests/test_arch_foundation.py``, not trusted here.
"""
from __future__ import annotations
WALL_MATERIALS: tuple[str, ...] = (
"brick",
"aac",
"concrete",
"reinforced_concrete",
"gypsum_board",
"stone",
"timber",
"generic",
)
WALL_HATCH_TABLE: dict[str, dict] = {
"brick": {
"pattern": "ANSI31",
"angle": 0.0,
"scale": 20.0,
"source": "ISO 128-50 general hatching = acadiso.pat *ANSI31 (Iron, Brick, Stone masonry)",
},
"aac": {
"pattern": "ANSI31",
"angle": 0.0,
"scale": 40.0,
"source": "repository convention: ANSI31 at twice the brick spacing; no standard AAC symbol",
},
"concrete": {
"pattern": "AR-CONC",
"angle": 0.0,
"scale": 1.0,
"source": "acadiso.pat *AR-CONC, random dot and stone pattern (AutoCAD, not a standard)",
},
"reinforced_concrete": {
"pattern": "JIS_RC_15",
"angle": 0.0,
"scale": 10.0,
"source": "acadiso.pat *JIS_RC_15, RC JIS A 0150(@15): the JIS A 0150 RC symbol",
},
"gypsum_board": {
"pattern": "AR-SAND",
"angle": 0.0,
"scale": 1.0,
"source": "acadiso.pat *AR-SAND, random dot pattern (AutoCAD, not a standard)",
},
"stone": {
"pattern": "JIS_STN_2.5",
"angle": 0.0,
"scale": 30.0,
"source": "acadiso.pat *JIS_STN_2.5, STONE JIS A 0150(@2.5): the JIS A 0150 stone symbol",
},
"timber": {
"pattern": "JIS_WOOD",
"angle": 90.0,
"scale": 90.0,
"source": "acadiso.pat *JIS_WOOD, WOOD JIS A 0150; turned 90 deg so it cannot read as brick",
},
"generic": {
"pattern": "ANSI31",
"angle": 0.0,
"scale": 20.0,
"source": "ISO 128-50 general hatching = acadiso.pat *ANSI31",
},
}
def normalise_material(material: str) -> str:
"""The table key a material name is read as: case, spaces and hyphens folded.
``"Reinforced Concrete"`` and ``"reinforced-concrete"`` both read as
``reinforced_concrete``. The plan model and ``wall_hatch`` share this one
reading, so the model never refuses a name the hatch table resolves. The
result may still be unknown; callers refuse it.
"""
return material.strip().lower().replace("-", "_").replace(" ", "_")
def wall_hatch(material: str) -> dict:
"""``{"pattern", "angle", "scale"}`` for a wall material. Unknown names are refused."""
if not isinstance(material, str):
raise ValueError(
f"material: expected a name, got {type(material).__name__}; "
f"wall materials are {', '.join(WALL_MATERIALS)}"
)
row = WALL_HATCH_TABLE.get(normalise_material(material))
if row is None:
raise ValueError(
f"material: unknown wall material {material!r}; "
f"wall materials are {', '.join(WALL_MATERIALS)}"
)
return {"pattern": row["pattern"], "angle": row["angle"], "scale": row["scale"]}