Files
autocad-mcp-pro/docs/superpowers/specs/2026-09-23-v1.6-arch-design.md
T
Umut CANandClaude Opus 5.5 83f192ac2f docs: track F (architectural 2D) design spec — the plan model
One plan model — a wall network with hosted openings — and one engine that
resolves L/T/X junctions, cuts openings, hatches cut walls by material and
computes rooms as the faces the walls enclose, so areas are measured, never
typed. A pure planar-face finder reads rooms from foreign plans on both
engines (the live engine has no boundary_trace). Furniture/sanitary catalogue,
grid, symbols, exterior dimension chains, schedules from the model.

12 tools (SECTION 25, pack arch), English by default with lang="tr", poché by
material, three arch_* critique focuses, benchmark arch_roundtrip.

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

227 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# v1.6 Track F — architectural 2D: the plan model
**Date:** 2026-09-23 · **Status:** design approved in conversation ·
**Parent:** `2026-09-15-v1.6-roadmap-design.md` (track F) · **Baseline:** main
after tracks B + G (`2bba18d`, 229 tools, 4005 tests)
## 1. Summary
An architectural plan is not a set of lines: a door **opens the wall it sits
in**, two walls that meet **join into one clean corner**, and a room's area is
**the floor the walls enclose** — a number that must be measured, never typed.
Drawing a plan from primitives gets all three wrong, silently: the door lies on
top of an uncut wall, the corner overlaps, and the area in the label is whatever
the model wrote.
This track applies the lesson of tracks B + G: one **plan model** and one
**engine**, not a zoo of element tools. The model is a network of walls (an
axis polyline, a thickness, a justification and a material) with **hosted
openings** (doors and windows placed by distance along their wall). The engine
turns it into clean wall outlines — L, T and X junctions resolved, openings cut,
cut faces hatched by material — and **rooms are computed** as the faces the wall
outlines enclose. Door, window and room schedules are read back from the model;
a separate reader finds rooms in a **foreign** plan made of plain lines. Both
run on both engines by construction: the planar-face finder is pure geometry
and does not need `boundary_trace`, which the live engine does not have.
Around it: a furniture and sanitary block catalogue (the standard-parts pattern
of track B), the structural grid, the architectural symbols and exterior
dimension chains.
**Tool budget: +12 → 241.** New pack `arch` (SECTION 25). Labels and schedules
speak English by default and Turkish with `lang="tr"` (the user's decision,
2026-09-23), including the decimal comma.
## 2. Scope
**In:** SECTION 25 Architecture (12 tools, §8), the `arch` ISO 13567 layer set,
three `arch_*` critique focuses, benchmark task `arch_roundtrip`, correctness
checks and a live COM smoke.
**Out of 1.6 (recorded):**
- AutoCAD Architecture / Revit objects (AEC walls, door styles), IFC, 3D walls,
generated elevations and sections — the section mark is a symbol; no view is
derived from a plan.
- Curved walls. A wall axis is a polyline of straight segments; an arc segment
is refused by name rather than approximated.
- Annotative objects (recorded in track E): text heights are computed from the
plot scale instead (`scale="1:50"` → 2.5 mm × 50 = 125 drawing units).
- Code compliance (fire escape widths, accessibility, national stair rules):
the stair tool checks the Blondel relation and reports it; it does not claim
a building code.
## 3. Architecture
```
engineering/arch/
__init__.py
layers.py the `arch` ISO 13567 layer set + role → layer map
materials.py wall materials → ISO 128-50 area hatch (brick, aac, concrete, …)
lang.py EN / TR vocabulary: schedule headers, designations, area format
model.py Wall, Opening, Stair, Room — dataclasses, validation, ACADMCP_ARCH payload
walls.py the wall engine: offset outlines, junction clean-up, opening cuts, poché
openings.py door and window geometry (leaf, swing, frame, glazing, sill)
stairs.py straight / L / U stair plan symbols, the Blondel check
faces.py the planar-face finder: segments → closed faces (DCEL), areas
rooms.py room labels (measured area), the foreign-plan reader
schedule.py door / window / room schedules from the model
catalogue.py furniture + sanitary blocks as typed primitive specs (block_define)
symbols.py grid bubbles, north arrow, section mark, level mark
dimension.py exterior dimension chains (openings / walls / overall)
draw.py the async layer — the only module that touches a backend
critique.py the three arch_* critique focuses
```
Everything but `draw.py` is **pure**: numbers in, primitive descriptions out,
using the typed primitives of `engineering/mech/primitives.py` (Line, Arc,
Poly, HatchArea, Text, DimIntent) with the arch role → layer map. No new
contract member: every tool composes the generic ones (`entity_create_*`,
`dimension_*`, `block_define`, `entity_set_xdata`, `entity_list`), so it works
on both engines as written, exactly like the mechanical tools.
The plan is written onto the drawing as `ACADMCP_ARCH` XDATA (the chunked codec
of track B, generalised to take an application id): a wall record on each wall
outline, an opening record on each opening, a room record on each label — so a
schedule is a *read* of the drawing, and a second `arch_opening` call months
later finds the wall it cuts.
## 4. The plan model
```python
class Wall: # frozen dataclass
id: str
axis: tuple[Pt, ...] # 2+ points, straight segments, WCS
thickness: float # > 0
justification: "center" | "left" | "right" # side of the axis, looking along it
material: str = "brick" # → poché hatch
closed: bool = False
class Opening:
id: str
wall: str # the Wall id it is hosted in
kind: "door" | "window"
offset: float # along the wall axis, from its start, to the opening's near jamb
width: float
swing: "in" | "out" = "in" # doors
hand: "left" | "right" = "left"
sill: float | None = None # windows, informational (schedule)
height: float | None = None # schedule
tag: str | None = None # D1 / W1 (EN), K1 / P1 (TR) when omitted
class Stair:
id: str
start: Pt; direction_deg: float; width: float
risers: int; riser_height: float; going: float
kind: "straight" | "l" | "u" = "straight"; turn: "left" | "right" = "left"
class Room:
id: str; name: str; number: str | None; at: Pt # a point inside the face
area: float # measured, written by the tool
```
Validation before any write (the repository rule): finite positive numbers by
path (`walls[2].thickness`), an axis of ≥ 2 distinct points with no zero-length
segment, an opening whose `offset + width` lies inside its wall segment and does
not straddle a vertex, two openings on one wall that overlap refused with both
ids, an unknown material or language refused with the list.
## 5. The wall engine
- **Outlines.** Each segment is offset by the justification into two faces.
- **Junctions.** Where segments meet — within one wall or between walls — the
faces are trimmed or extended to their intersections: an L corner mitres, a T
junction stops the stem's faces on the crossing wall's face, an X junction
cuts all four. A junction the engine cannot resolve (a segment ending inside
another wall at a shallow angle below 5°) is refused by name, not overlapped.
- **Openings.** An opening removes its width from both faces of its wall and
closes the two jambs; a door draws its leaf at 90° and the swing as a quarter
arc on the swing side; a window draws the frame line on each face and two
glazing lines between.
- **Poché.** Each closed cut-face region between openings and junctions is a
`HatchArea` with the material's ISO 128-50 pattern; `poche=False` draws the
outlines only.
- **Honesty.** Anything the engine cannot place is returned in
`omitted: [{element, reason}]` — never silently dropped.
## 6. Rooms
`faces.py` builds a planar graph (a doubly connected edge list) from line and
polyline segments: segments are split at every intersection, near-coincident
endpoints are merged within a tolerance (default 1 mm), and the bounded faces
are walked. A room is the face containing the label point; its area is the
face's polygon area and is written into the label and the `room` record.
`arch_rooms_detect` runs the same finder on a **foreign** plan — any lines and
polylines on the layers it is given (default: every layer whose name contains
`WALL` or `DUVAR`) — and returns faces above a minimum area with their areas,
centroids and a `confidence` (1.0 for a face enclosed only by our walls, 0.6
for plain lines), the way `pid_graph` does. It never modifies the drawing.
## 7. Schedules, symbols, catalogue, dimensions
- `arch_schedule(kind="doors"|"windows"|"rooms", at, lang)` reads the
`ACADMCP_ARCH` records and draws a real TABLE (the parts-list code of track G):
doors and windows by tag with width, height, swing/hand and wall; rooms by
number with name and measured area, plus the total.
- `arch_symbol(kind="north_arrow"|"section_mark"|"level"|"elevation_mark", …)`.
- `arch_grid(x_axes, y_axes, labels)` — axis lines on the grid layer with
bubbles at both ends; numbers one way and letters the other (ISO 4157-style).
- `arch_catalogue_list` / `arch_catalogue_insert` — ~20 authored blocks in two
families: furniture (bed single/double, wardrobe, sofa, armchair, dining table
+ chairs, desk, kitchen counter, fridge) and sanitary (WC, basin, shower tray,
bathtub, kitchen sink). Sizes are **nominal catalogue dimensions stated in the
block's own data**, not standards values, and the tool says so.
- `arch_dimension_chains(sides, offsets)` — the three exterior chains an
architectural plan carries: openings, wall segments / axes, overall.
## 8. Tool surface
| # | Tool | What it does |
|---|---|---|
| 1 | `arch_wall` | add walls to the plan network and redraw the junctions they touch |
| 2 | `arch_opening` | host a door or a window in a wall (`kind=`), cutting it |
| 3 | `arch_stair` | a straight / L / U stair in plan, with the Blondel check |
| 4 | `arch_room` | label a room with its measured area |
| 5 | `arch_rooms_detect` | read rooms and areas from any plan (never modifies) |
| 6 | `arch_schedule` | door / window / room schedule table |
| 7 | `arch_grid` | structural grid with bubbles |
| 8 | `arch_symbol` | north arrow, section mark, level, elevation mark |
| 9 | `arch_catalogue_list` | search the furniture / sanitary catalogue |
| 10 | `arch_catalogue_insert` | insert a catalogue block |
| 11 | `arch_dimension_chains` | the exterior dimension chains |
| 12 | `arch_plan_from_spec` | walls + openings + stairs + rooms + chains + schedules in one transaction |
Pack `arch` holds all twelve. Lean gains `arch_wall`, `arch_opening`,
`arch_room`, `arch_plan_from_spec` (→ 64). `drawing_apply_iso_layers("arch")`
applies the new layer set. Every tool has an alias record with the AutoCAD
commands a drafter would reach for (WALL, DOOR, WINDOW, STAIR, BOUNDARY, AREA)
and shop synonyms in both languages (duvar, kapı, pencere, merdiven, oda, alan,
kat planı, vaziyet).
## 9. Critique focuses
- `arch_room_unlabelled` — a closed face above the minimum area with no room
label inside it.
- `arch_wall_gap` — two wall ends within 50 mm of each other that are not
joined: the room leaks and its area is wrong.
- `arch_opening_clash` — two openings overlapping on one wall, or an opening
that runs past its wall segment.
All three return nothing on a drawing with no `ACADMCP_ARCH` records.
## 10. Evidence
- **Benchmark `arch_roundtrip`** (roadmap evidence for F): a two-room plan with
an entrance door, an interior door, two windows and a stair drawn by
`arch_plan_from_spec`; `arch_rooms_detect` reads it back and each room's area
agrees with the model's within 0.1 %; the door and window schedules list the
drawn tags; `drawing_critique(focus=None)` returns 0 issues and
`drawing_finalize` scores ≥ 90. Matrix v6 (19 tasks).
- **Correctness checks:** `arch_junction_l_t_x` (outline vertices of the three
junction kinds against hand-computed coordinates), `arch_room_area_net`
(a 4 × 5 m room between 200 mm walls measures 20.00 m², not the axis area),
`arch_opening_cuts_wall` (the wall faces are interrupted across the opening),
`arch_rooms_detect_foreign` (a plan of plain lines yields the same areas).
- **Live smoke** `scripts/smoke_arch_com.py` on AutoCAD 2026.