Introduces an engineering/ package that lets MCP clients produce production- quality technical drawings without LLM-improvised geometry. Adds 7 new MCP tools, an 8-step pre-completion validator, and an automatic linetype + layer bootstrap on drawing_new so CENTER/HIDDEN/PHANTOM and standard mechanical layers (GEOMETRY, DIM, HATCH, TEXT, TITLEBLOCK, ...) are always available. - engineering/gear.py: pure-Python involute math, 40+ points/flank, helical + spur front views, deterministic section A-A (no helix lines in section). - engineering/keyway.py: DIN 6885 keyway table + parametric keyed bore / side-section drawers. - engineering/titleblock.py: ISO 7200 A3 (420x297 mm) title block; title text is written verbatim to the title cell. - engineering/validator.py: DrawingValidator with 8 checks (real DIMENSION entities, no hidden orphans, linetypes loaded, bore + keyway in both views when expected, title consistency e.g. forbid 'HELICAL SPUR GEAR' when helix_angle truthy, saved-to-disk + file-on-disk, screenshot exported, dwg path returned). - backends/ezdxf_backend.py: AutoCAD-only linetype fallback (HIDDEN, BORDER families) so ezdxf doc gets correct patterns when linetype_load is called for names ezdxf.tools.standards does not ship. - backends/base.py: concrete helpers set_layer_active / ensure_linetypes. - server.py: Section 12 with 7 engineering tools, drawing_new(bootstrap= True), and an ENGINEERING WORKFLOW block in the MCP instructions string. - 30 new tests covering gear math, DIN6885 lookup, ISO A3 layout, title verbatim, and every validator check. Tool count 89 -> 96.
75 lines
2.7 KiB
Python
75 lines
2.7 KiB
Python
"""Standard engineering layer + linetype scaffold for production drawings."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from backends.base import AutoCADBackend
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
ENGINEERING_LAYERS: list[tuple[str, int, str, float, str]] = [
|
|
# (name, color, linetype, lineweight, description)
|
|
("0", 7, "Continuous", 0.25, "default"),
|
|
("GEOMETRY", 7, "Continuous", 0.50, "visible solid edges"),
|
|
("HIDDEN", 3, "HIDDEN", 0.25, "hidden edges"),
|
|
("CENTER", 1, "CENTER", 0.18, "centerlines"),
|
|
("PHANTOM", 6, "PHANTOM", 0.18, "section / cutting planes"),
|
|
("DIM", 2, "Continuous", 0.18, "dimensions"),
|
|
("TEXT", 7, "Continuous", 0.25, "annotations"),
|
|
("HATCH", 8, "Continuous", 0.13, "section hatching"),
|
|
("TITLEBLOCK", 7, "Continuous", 0.50, "drawing border + title"),
|
|
("CONSTRUCTION", 250, "Continuous", 0.05, "scratch / construction"),
|
|
]
|
|
|
|
STANDARD_LINETYPES: list[str] = ["CENTER", "HIDDEN", "PHANTOM", "DASHED", "DASHDOT"]
|
|
|
|
|
|
async def ensure_standard_linetypes(backend: "AutoCADBackend") -> dict[str, str]:
|
|
"""Idempotently load STANDARD_LINETYPES via backend.linetype_load."""
|
|
try:
|
|
existing = {ln.lower() for ln in await backend.linetype_list()}
|
|
except Exception as exc:
|
|
log.warning("linetype_list failed; assuming none loaded: %s", exc)
|
|
existing = set()
|
|
|
|
results: dict[str, str] = {}
|
|
for name in STANDARD_LINETYPES:
|
|
if name.lower() in existing:
|
|
results[name] = "already_loaded"
|
|
continue
|
|
try:
|
|
await backend.linetype_load(name)
|
|
results[name] = "loaded"
|
|
except Exception as exc:
|
|
results[name] = f"failed: {exc}"
|
|
return results
|
|
|
|
|
|
async def ensure_engineering_layers(backend: "AutoCADBackend") -> dict[str, str]:
|
|
"""Idempotently create every layer in ENGINEERING_LAYERS via backend.layer_create."""
|
|
await ensure_standard_linetypes(backend)
|
|
|
|
try:
|
|
existing = {lyr.name.lower() for lyr in await backend.layer_list()}
|
|
except Exception as exc:
|
|
log.warning("layer_list failed; assuming empty: %s", exc)
|
|
existing = set()
|
|
|
|
results: dict[str, str] = {}
|
|
for name, color, linetype, lineweight, _desc in ENGINEERING_LAYERS:
|
|
if name.lower() in existing:
|
|
results[name] = "exists"
|
|
continue
|
|
try:
|
|
await backend.layer_create(
|
|
name=name, color=color, linetype=linetype, lineweight=lineweight,
|
|
)
|
|
results[name] = "created"
|
|
except Exception as exc:
|
|
results[name] = f"failed: {exc}"
|
|
return results
|