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
+51
View File
@@ -348,6 +348,54 @@ def _com_bulges(entity, coords, closed: bool, length) -> list[float]:
return [_com_bulge(entity, i) for i in range(count)]
def _com_geometry_bbox(entity) -> dict | None:
"""WCS extents of a block reference's *drawn* geometry, attributes excluded.
``GetBoundingBox`` on an INSERT takes its ATTRIBs with it, so a TAG lettered
above a valve pushes the box past the body — a line ending on the body's
real edge then lies *inside* the box. The definition's members are read in
block space (skipping ``AcDbAttributeDefinition``), each member's box is
carried through the INSERT (origin, scale, rotation) corner by corner, and
the extents of those corners is the answer. None when there is nothing
drawn or ActiveX refuses any step — the caller then omits the key.
"""
try:
try:
doc = entity.Document
except Exception:
doc = _acad_doc()
blk = doc.Blocks.Item(entity.Name)
try:
origin = tuple(blk.Origin)
except Exception:
origin = (0.0, 0.0, 0.0)
ins = entity.InsertionPoint
sx, sy = float(entity.XScaleFactor), float(entity.YScaleFactor)
rot = float(entity.Rotation)
cos_r, sin_r = math.cos(rot), math.sin(rot)
xs: list[float] = []
ys: list[float] = []
for i in range(blk.Count):
member = blk.Item(i)
if member.ObjectName == "AcDbAttributeDefinition":
continue
try:
lo, hi = member.GetBoundingBox()
except Exception:
continue
for bx, by in ((lo[0], lo[1]), (hi[0], lo[1]), (lo[0], hi[1]), (hi[0], hi[1])):
lx = (float(bx) - float(origin[0])) * sx
ly = (float(by) - float(origin[1])) * sy
xs.append(float(ins[0]) + lx * cos_r - ly * sin_r)
ys.append(float(ins[1]) + lx * sin_r + ly * cos_r)
if not xs:
return None
return {"min": [min(xs), min(ys)], "max": [max(xs), max(ys)]}
except Exception as exc:
log.debug("geometry bbox failed for block reference: %s", exc)
return None
# What a lightweight polyline calls itself over ActiveX. The live name is
# ``AcDbPolyline`` (measured on AutoCAD 2026, 2026-08-06 — the member profile in
# tests/test_com_backend.py); ``AcDbLWPolyline`` is the DXF-flavoured spelling
@@ -449,6 +497,9 @@ def _entity_info(entity) -> EntityInfo:
props["x_scale"] = entity.XScaleFactor
props["y_scale"] = entity.YScaleFactor
props["rotation_deg"] = rad2deg(entity.Rotation)
geometry_bbox = _com_geometry_bbox(entity)
if geometry_bbox is not None:
props["geometry_bbox"] = geometry_bbox
except Exception as exc:
log.debug("Type-specific entity properties extraction failed: %s", exc)