Files
autocad-mcp-pro/tests/test_understand_route.py
Umut CANandClaude Opus 5.5 e640def4ff feat(understand): orthogonal lengths and pipe takeoff rows
route.py: Manhattan distance, the rectilinear minimum spanning tree (Prim
over |dx| + |dy|, exact on the points given) and the x-then-y route.

pipe_rows turns the P&ID's runs into rows by room x service x diameter.
length_source auto measures on the P&ID only when the scale check calls it
to scale; otherwise each run is the RMST of its tags' layout positions plus
vertical_allowance per connection, with the drawn length kept beside it as
schematic_m. length_source=pid on a schematic P&ID is refused with the
statistics unless force=True. Status A / B / C; a C run goes to control
with its schematic length, counted and flagged. Routed lengths are shared
among the layout rooms their route crosses (track F's face finder); the
allowance is its own column. Lengths are metres from the unit the geometry
implies: a layout declaring inches over millimetres is read in mm and warned
about, never multiplied by 25.4.

The takeoff carries narrow stand-ins for the unit inference, the plan-copy
clusters and the scale check (group U's modules are on another branch); a
scale result passed in is used first. Tags written twice in the chosen
cluster are ambiguous, never picked.

Beyond the plan: the scale stand-in calls a non-positive median ratio
insufficient (as group U's scale_check does) instead of to_scale with a
factor of 0; an INSUNITS code the reader does not convert is named in the
unit warning instead of being called "no unit"; wall layers are classified
once per layer, not once per record.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
2026-09-24 12:14:17 +03:00

49 lines
1.6 KiB
Python

"""Orthogonal lengths: Manhattan distance, the rectilinear MST, the x-then-y route.
Every expected value is worked by hand in the comment beside it.
"""
from __future__ import annotations
from engineering.understand.route import manhattan, rmst, route_legs
EPS = 1e-9
def test_manhattan_is_the_sum_of_the_axis_distances():
assert manhattan((0.0, 0.0), (3.0, -4.0)) == 7.0 # 3 + 4, not the diagonal 5
def test_fewer_than_two_points_have_no_tree():
assert rmst([]) == (0.0, ())
assert rmst([(5.0, 5.0)]) == (0.0, ())
def test_a_two_ended_run_is_dx_plus_dy():
length, edges = rmst([(1000.0, 3000.0), (29000.0, 15000.0)])
assert abs(length - 40000.0) < EPS # 28000 + 12000
assert edges == ((0, 1),)
def test_a_square_of_four_tags_is_three_sides():
# Prim from point 0: (0,1) at 10 (tie with 3 goes to the lower index),
# then (1,2) at 10, then (0,3) at 10 -> 30, never the 40 of the full ring
length, edges = rmst([(0.0, 0.0), (10.0, 0.0), (10.0, 10.0), (0.0, 10.0)])
assert abs(length - 30.0) < EPS
assert edges == ((0, 1), (1, 2), (0, 3))
def test_three_tags_in_a_row_chain_instead_of_a_star():
length, edges = rmst([(0.0, 0.0), (100.0, 0.0), (200.0, 0.0)])
assert abs(length - 200.0) < EPS # 100 + 100, not 100 + 200
assert edges == ((0, 1), (1, 2))
def test_the_route_runs_along_x_first_then_y():
assert route_legs((0.0, 0.0), (30.0, 40.0)) == (
((0.0, 0.0), (30.0, 0.0)),
((30.0, 0.0), (30.0, 40.0)),
)
assert route_legs((0.0, 0.0), (0.0, 40.0)) == (((0.0, 0.0), (0.0, 40.0)),)
assert route_legs((1.0, 1.0), (1.0, 1.0)) == ()