feat(task-8): SECTION 25 grid and symbols - arch_grid, arch_symbol

engineering/arch/symbols.py is pure: grid_prims() draws each structural axis on
the grid role - its CENTER linetype comes from the A-GRID-E-N row, no primitive
sets one - with a 5 mm (paper) bubble and its label touching both ends, on the
symbol role so the circle does not print dashed. Numbers run along x and
letters along y (A..Z, AA, ... none skipped) unless labels names them;
positions are sorted, duplicates and label-count mismatches refused by path.

symbol_prims() draws the four plan symbols: the north arrow (a circle, an
arrow turned CCW from +Y, the vocab(lang) north letter upright beyond the tip),
the section mark (the cut line, a labelled bubble past each end and a viewing
arrow at each end, p1/p2 relative to the origin), the level mark (the triangle,
its line and the value formatted +3.00 / ±0.00, with a decimal comma in
Turkish) and the elevation mark (a circle whose pointers are the tangents from
r*sqrt(2) touching it at +/-45 degrees). An unknown kind is refused with the
list, a parameter the kind does not take with the ones it does. Every size is
a declared paper-mm drafting choice times the plot scale, not a standards value.

arch_grid and arch_symbol draw through draw_prims with ARCH_ROLE_LAYER; alias
records (COLUMNGRIDADD, English and Turkish synonyms), a regenerated inventory,
the README snapshot at 233 tools and the branch-local group snapshot
(entity_creation 21) complete the registration.

Beyond the plan: grid_axes refuses a labels value that is not a dict and a
labels.x / labels.y that is not a list (a string was split into one label per
character, a number raised a bare TypeError). No new ActiveX member: a live run
on AutoCAD 2026 (scratch document, closed after) drew the grid and all four
symbols through draw_prims - A-GRID-E-N created with CENTER, bubbles, texts and
symbols on A-SYMB-T-N, the Turkish north letter K and the level "+3,00".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
This commit is contained in:
Umut CAN
2026-09-23 22:42:42 +03:00
co-authored by Claude Opus 5.5
parent afd42356b5
commit f5f11178a9
9 changed files with 1063 additions and 9 deletions
+134 -1
View File
@@ -9781,7 +9781,7 @@ async def drawing_export_dwg(
# ---------------------------------------------------------------------------
# ── SECTION 25: Architecture (2 tools) ──────────────────────────────────────
# ── SECTION 25: Architecture (4 tools) ──────────────────────────────────────
# ---------------------------------------------------------------------------
# ── arch: catalogue (group C) ──
@@ -9888,6 +9888,139 @@ async def arch_catalogue_insert(
raise ToolError(str(exc)) from exc
@cad_tool(
summary="Draw a structural grid: axis lines with numbered and lettered bubbles at both ends.",
cost="mutate",
)
@mcp.tool(
annotations={"title": "Architecture: Structural Grid", "destructiveHint": False},
tags={"arch", "create"},
)
async def arch_grid(
x_axes: Annotated[
list[float],
Field(description="X positions (WCS) of the vertical axes; numbered 1, 2, 3 left to right"),
],
y_axes: Annotated[
list[float],
Field(
description="Y positions (WCS) of the horizontal axes; lettered A, B, C bottom to top"
),
],
labels: Annotated[
dict | None,
Field(
default=None,
description=(
"Override the labels: {'x': [...], 'y': [...]} (either key), one label per "
"position in the order the positions were given"
),
),
] = None,
extension: Annotated[
float,
Field(
default=1500.0,
gt=0,
description="Drawing units each axis runs past the outermost crossing axis",
),
] = 1500.0,
scale: Annotated[
float,
Field(
default=50.0,
gt=0,
description="Plot-scale denominator (50 = 1:50): bubbles and labels are paper mm x scale",
),
] = 50.0,
ctx: Context = None,
) -> dict:
"""Axis lines on the arch grid layer (its CENTER linetype comes from the layer) and a
5 mm bubble with its label at both ends of each axis, on the symbol layer.
Numbers run along x and letters along y (A..Z, then AA, AB - no letter is
skipped) unless `labels` names them. The bubble and label sizes are this
server's declared drafting sizes in paper millimetres, not a standards value.
Refusals, all before any write: an empty axis list in either direction, a
non-finite or duplicate position, a label list whose length does not match
its positions, a duplicate or empty label, an unknown `labels` key, and a
non-positive `extension` or `scale`.
"""
from engineering.arch.symbols import draw_grid
await ctx.info(f"arch grid: {len(x_axes)} x {len(y_axes)} axes at 1:{scale:g}")
try:
return await draw_grid(
_backend(ctx), x_axes, y_axes, labels=labels, extension=extension, scale=scale
)
except ValueError as exc:
raise ToolError(str(exc)) from exc
@cad_tool(
summary="Draw a plan symbol: north arrow, section mark, level mark or elevation mark.",
cost="mutate",
)
@mcp.tool(
annotations={"title": "Architecture: Symbol", "destructiveHint": False},
tags={"arch", "create"},
)
async def arch_symbol(
kind: Annotated[
str,
Field(description="north_arrow | section_mark | level | elevation_mark"),
],
x: Annotated[float, Field(description="Symbol origin X (WCS)")],
y: Annotated[float, Field(description="Symbol origin Y (WCS)")],
params: Annotated[
dict | None,
Field(
default=None,
description=(
"north_arrow: {rotation} (degrees CCW from +Y; 0 = north up). "
"section_mark: {p1, p2, label, side} - p1/p2 are [x, y] relative to (x, y), "
"side left|right is where the section looks, seen from p1 towards p2. "
"level: {value} in metres (0 -> '±0.00'). "
"elevation_mark: {label, directions} - directions from up|right|down|left"
),
),
] = None,
lang: Annotated[
str,
Field(default="en", description="en | tr - the north letter and the decimal separator"),
] = "en",
scale: Annotated[
float,
Field(
default=50.0,
gt=0,
description="Plot-scale denominator (50 = 1:50): symbol sizes are paper mm x scale",
),
] = 50.0,
ctx: Context = None,
) -> dict:
"""Draw one architectural symbol on the arch symbol layer.
Sizes are this server's declared drafting sizes in paper millimetres times
`scale` (a 16 mm north-arrow circle, 10 mm section and elevation bubbles),
not standards values. Refusals, all before any write: an unknown `kind`
(with the list), a parameter the kind does not take (with the ones it
does), a section mark without p1/p2, with an empty label, an unknown side
or a cut line too short for its two arrows, an elevation direction outside
up/right/down/left or named twice, a non-finite level value, and a `lang`
other than en/tr.
"""
from engineering.arch.symbols import draw_symbol
await ctx.info(f"arch symbol: {kind} at ({x}, {y})")
try:
return await draw_symbol(
_backend(ctx), kind, (x, y), lang=lang, scale=scale, **(params or {})
)
except (TypeError, ValueError) as exc:
raise ToolError(str(exc)) from exc
# ---------------------------------------------------------------------------
# ── RESOURCES ───────────────────────────────────────────────────────────────
# ---------------------------------------------------------------------------