Add deterministic engineering CAD layer (gear, keyway, titleblock, validator)
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.
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
"""Generic cutting-plane and section-hatch helpers."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from backends.base import AutoCADBackend
|
||||
|
||||
|
||||
async def cutting_plane_line(
|
||||
backend: "AutoCADBackend",
|
||||
*,
|
||||
p1: tuple[float, float],
|
||||
p2: tuple[float, float],
|
||||
label: str = "A",
|
||||
layer: str = "PHANTOM",
|
||||
) -> dict:
|
||||
"""Draw a section line, arrowheads at each end, and 'X-X' labels."""
|
||||
x1, y1 = float(p1[0]), float(p1[1])
|
||||
x2, y2 = float(p2[0]), float(p2[1])
|
||||
|
||||
line = await backend.entity_create_line(x1, y1, x2, y2, layer=layer)
|
||||
|
||||
dx, dy = x2 - x1, y2 - y1
|
||||
length = math.hypot(dx, dy) or 1.0
|
||||
ux, uy = dx / length, dy / length
|
||||
nx, ny = -uy, ux
|
||||
|
||||
arrow_len = max(length * 0.05, 4.0)
|
||||
arrow_w = arrow_len * 0.4
|
||||
arrows: list[str] = []
|
||||
|
||||
for tip_x, tip_y, dir_x, dir_y in (
|
||||
(x1, y1, -ux, -uy),
|
||||
(x2, y2, ux, uy),
|
||||
):
|
||||
bx = tip_x + dir_x * arrow_len
|
||||
by = tip_y + dir_y * arrow_len
|
||||
wing1_x = bx + nx * arrow_w
|
||||
wing1_y = by + ny * arrow_w
|
||||
wing2_x = bx - nx * arrow_w
|
||||
wing2_y = by - ny * arrow_w
|
||||
a1 = await backend.entity_create_line(tip_x, tip_y, wing1_x, wing1_y, layer=layer)
|
||||
a2 = await backend.entity_create_line(tip_x, tip_y, wing2_x, wing2_y, layer=layer)
|
||||
arrows.extend([a1.handle, a2.handle])
|
||||
|
||||
text = f"{label}-{label}"
|
||||
text_h = max(arrow_len, 3.5)
|
||||
offset = arrow_len * 1.5
|
||||
labels: list[str] = []
|
||||
for tip_x, tip_y, dir_x, dir_y in (
|
||||
(x1, y1, -ux, -uy),
|
||||
(x2, y2, ux, uy),
|
||||
):
|
||||
tx = tip_x + dir_x * offset + nx * offset * 0.5
|
||||
ty = tip_y + dir_y * offset + ny * offset * 0.5
|
||||
t = await backend.entity_create_text(text, tx, ty, height=text_h, layer="TEXT")
|
||||
labels.append(t.handle)
|
||||
|
||||
return {"line": line.handle, "arrows": arrows, "labels": labels}
|
||||
|
||||
|
||||
async def apply_section_hatch(
|
||||
backend: "AutoCADBackend",
|
||||
*,
|
||||
boundary_points: list[list[float]],
|
||||
pattern: str = "ANSI31",
|
||||
angle: float = 45.0,
|
||||
scale: float = 1.0,
|
||||
layer: str = "HATCH",
|
||||
) -> str:
|
||||
"""Wrapper around backend.entity_create_hatch returning the new handle."""
|
||||
info = await backend.entity_create_hatch(
|
||||
pattern=pattern,
|
||||
boundary_points=boundary_points,
|
||||
scale=scale,
|
||||
angle=angle,
|
||||
layer=layer,
|
||||
)
|
||||
return info.handle
|
||||
Reference in New Issue
Block a user