Files
cad-agent/dwg_review/annotator.py
T

116 lines
4.1 KiB
Python
Raw Normal View History

"""批注回写:把审查问题标到 CAD 图上(统一落 AI_REVIEW 层)。
只在问题带有 handle 时才回写。产出两类可撤销的副作用:
- created :新建的批注实体(引线等)→ 用 delete_entities 删除
- highlighted :被临时改色的原实体 → 用 highlight_entities(restore=true) 还原
"""
from . import config
from .mcp_client import McpError, McpToolError, result_json
# 引线落点相对实体定位点的偏移(图纸单位)
_LEADER_OFFSET = (30.0, 20.0)
def _pick_point(detail):
"""从实体详情中挑一个用于放引线的点(WCS)。"""
if not isinstance(detail, dict):
return None
bbox = detail.get("bbox")
if isinstance(bbox, (list, tuple)) and len(bbox) == 4:
return [
(float(bbox[0]) + float(bbox[2])) / 2.0,
(float(bbox[1]) + float(bbox[3])) / 2.0,
0.0,
]
for key in ("position", "insert", "center", "start"):
pt = detail.get(key)
if isinstance(pt, (list, tuple)) and len(pt) >= 2:
return [float(pt[0]), float(pt[1]), float(pt[2]) if len(pt) > 2 else 0.0]
return None
def annotate(ctx, issues, *, dry_run=False, log=print):
"""按问题列表回写批注,返回 (created, highlighted) 两个 handle 列表。"""
created = []
highlighted = []
for issue in issues:
handle = issue.get("handle")
if not handle:
continue
text = f"[{issue.get('severity', '?')}] {issue.get('message', '')}"
if dry_run:
log(f"[dry-run] 批注 handle={handle} 层={config.ANNOTATE_LAYER}: {text}")
continue
# 先定位,拿不到坐标就跳过(避免产生无意义的批注)
detail = result_json(ctx.tools.call("get_entity_detail", {"handle": handle}), {})
point = _pick_point(detail)
if not point:
log(f"[跳过] handle={handle} 无法定位坐标")
continue
landing = [
point[0] + _LEADER_OFFSET[0],
point[1] + _LEADER_OFFSET[1],
0.0,
]
try:
res = result_json(
ctx.tools.call(
"add_leader_annotation",
{
"text": text,
"points": [point, landing], # 至少 2 个点,首个为箭头
"text_position": landing,
"layer": config.ANNOTATE_LAYER,
},
),
{},
)
for item in (res or {}).get("created", []):
if item.get("handle"):
created.append(item["handle"])
except (McpError, McpToolError) as exc:
log(f"[批注失败] handle={handle}: {exc}")
continue
# 引线建好后再高亮原实体(失败不影响已创建的批注)
if handle in highlighted:
continue
try:
ctx.tools.call("highlight_entities", {"handle": handle, "color": 1})
highlighted.append(handle)
except (McpError, McpToolError) as exc:
log(f"[高亮失败] handle={handle}: {exc}")
if not dry_run:
log(
f"[批注] 新建 {len(created)} 个批注实体、高亮 {len(highlighted)} 个原实体"
f"(层 {config.ANNOTATE_LAYER})"
)
return created, highlighted
def clear(ctx, handles=None, highlighted=None, *, log=print):
"""撤销批注:删除批注实体 + 还原被高亮的实体。"""
if handles:
try:
ctx.tools.call("delete_entities", {"handles": list(handles)})
log(f"[批注] 已删除 {len(handles)} 个批注实体")
except (McpError, McpToolError) as exc:
log(f"[撤销失败] {exc}")
if highlighted:
try:
ctx.tools.call(
"highlight_entities", {"handles": list(highlighted), "restore": True}
)
log(f"[批注] 已还原 {len(highlighted)} 个实体的颜色")
except (McpError, McpToolError) as exc:
log(f"[还原失败] {exc}")