fix(task-14): arcs past a semicircle and foreign ports behind tag text

Two verified blocking findings on pid_graph.

`_point_segment_distance` put the centre of an arc larger than a
semicircle (|bulge| > 1) on the wrong side of the chord: `copysign`
discarded the sign of `radius - sagitta`, which is exactly what carries
the centre across the chord past 180 degrees. A bulge-2 arc was tested
as its own reflection, so a line ending on its true apex dangled and one
ending on empty paper made a junction. The offset now keeps that sign
and only takes the bulge's direction from the bulge.

Foreign-block port inference tested line ends against the INSERT's
`bounding_box`, which on both engines takes the ATTRIB text with it. A
TAG lettered above a valve pushed the box past the body, so a line
ending on the body's real edge on that side was *inside* the box and
reported dangling. Both engines now report `geometry_bbox` for an
INSERT - the drawn geometry only, ATTRIB/ATTDEF excluded, in WCS, omitted
when the block draws nothing (ezdxf via `virtual_entities`, COM by
carrying each definition member's box through the INSERT's origin,
scale and rotation). The graph prefers it over `bounding_box`, counts
an end as touching when it is within tolerance of the box or inside it,
and when several foreign boxes hold the end picks the nearest insertion.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
This commit is contained in:
Umut CAN
2026-09-16 09:53:24 +03:00
co-authored by Claude Opus 5
parent 8b6f355c3d
commit d17a26103f
5 changed files with 378 additions and 32 deletions
+18
View File
@@ -845,6 +845,24 @@ def _entity_info_dxf(ent) -> EntityInfo:
props["x_scale"] = ent.dxf.xscale if ent.dxf.hasattr("xscale") else 1.0
props["y_scale"] = ent.dxf.yscale if ent.dxf.hasattr("yscale") else 1.0
props["rotation_deg"] = ent.dxf.get("rotation", 0.0)
# The drawn symbol alone. ``bounding_box`` below takes the ATTRIBs
# with it, so a TAG above a valve pushes the box past the body and
# a line ending on the body's real edge reads as *inside* it.
# `virtual_entities` already applies the INSERT transform, so this
# is WCS; the key is omitted when the block draws nothing.
try:
from ezdxf import bbox as _bbox
_gb = _bbox.extents(
e for e in ent.virtual_entities() if e.dxftype() not in ("ATTRIB", "ATTDEF")
)
if _gb.has_data:
props["geometry_bbox"] = {
"min": [float(_gb.extmin.x), float(_gb.extmin.y)],
"max": [float(_gb.extmax.x), float(_gb.extmax.y)],
}
except Exception as exc:
log.debug("geometry bbox failed for INSERT %s: %s", ent.dxf.name, exc)
elif ent_type == "ELLIPSE":
props["center"] = _v2(ent.dxf.center)
props["major_axis"] = _v2(ent.dxf.major_axis)