Files
cad-agent/dwg_review/annotator.py
T
gjm 81d8c3d2ca fix(review): 批注去重 + 删除残留调试截图
- annotator.annotate:同一 handle 只批注一次(修复 created 无去重的既有 gap,
  同 handle 出现两次不再产生重复引线)
- 删除 test_screenshot.png(已删除脚本 test_autoagent.py 的残留产物)
2026-10-14 19:53:41 +08:00

117 lines
4.1 KiB
Python
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.
"""批注回写:把审查问题标到 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 = []
seen = set() # 同一 handle 只处理一次,避免重复批注
for issue in issues:
handle = issue.get("handle")
if not handle or handle in seen:
continue
seen.add(handle)
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
# 引线建好后再高亮原实体(失败不影响已创建的批注)
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}")