fix(pid): existing line payloads are read tolerantly and crossings follow arcs, not chords

A foreign or hand-edited ACADMCP_PID payload (from: 'garbage', seq: True)
made the next pid_line_draw raise TypeError; payload_port_refs /
payload_seq now accept only well-formed values and a reader result never
raises. An existing polyline with a bulge was tested as its chord, so a
line through the arc reported crossings 0; flatten_bulges (pure, pinned
against ezdxf.math's flattening) feeds count_crossings on both engines.

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-21 23:05:54 +03:00
co-authored by Claude Opus 5
parent cfb5d5082c
commit 9cb4bee587
4 changed files with 218 additions and 10 deletions
+40
View File
@@ -3,6 +3,8 @@
from __future__ import annotations
import math
import pytest
from engineering.pid.lines import (
@@ -176,3 +178,41 @@ def test_route_refuses_non_axis_port_directions_with_a_value_error():
# Equivalent angles and engine float drift still resolve to the axis.
assert route((0, 0), -360.0, (50, 0), 540.0) == [(0.0, 0.0), (50.0, 0.0)]
assert route((0, 0), 1e-9, (50, 0), 180.0 - 1e-9) == [(0.0, 0.0), (50.0, 0.0)]
# ── Track A hardening (track E wave 0, Task 7): bulge flattening ─────────────
@pytest.mark.parametrize("bulge", [1.0, -1.0, 0.5, -0.3, 2.0, 0.2])
def test_flatten_bulges_matches_ezdxf_flattening(bulge):
"""Pinned against ezdxf's own arc flattening: same vertex count, same points."""
from ezdxf.math import ConstructionArc, Vec2, bulge_to_arc
from engineering.pid.lines import FLATTEN_SAGITTA, flatten_bulges
a, b = (0.0, 0.0), (10.0, 0.0)
mine = flatten_bulges([a, b], [bulge])
centre, start_rad, end_rad, radius = bulge_to_arc(Vec2(a), Vec2(b), bulge)
arc = ConstructionArc(
center=centre,
radius=radius,
start_angle=math.degrees(start_rad),
end_angle=math.degrees(end_rad),
)
reference = [(p.x, p.y) for p in arc.flattening(FLATTEN_SAGITTA)]
assert len(mine) == len(reference)
assert mine[0] == a and mine[-1] == b
assert all(math.hypot(p[0] - centre.x, p[1] - centre.y) == pytest.approx(radius) for p in mine)
assert mine[len(mine) // 2] == pytest.approx(reference[len(reference) // 2])
def test_flatten_bulges_passes_straight_edges_through_and_pads_short_bulge_lists():
from engineering.pid.lines import flatten_bulges
pts = [(0.0, 0.0), (10.0, 0.0), (10.0, 10.0)]
assert flatten_bulges(pts, []) == pts
assert flatten_bulges(pts, [0.0, 0.0, 0.0]) == pts
flat = flatten_bulges(pts, [0.0, 1.0])
assert flat[0] == (0.0, 0.0) and flat[1] == (10.0, 0.0) and flat[-1] == (10.0, 10.0)
assert len(flat) > 3, "the second edge became a semicircle of chords"
assert flatten_bulges([(1.0, 1.0)], [1.0]) == [(1.0, 1.0)]