build_network reads the segments of a foreign P&ID's piping and electrical
layers into runs: ends merged within tol, a branch ending on a header split
into a tee, a crossing without an end left unconnected, and a line drawn
twice - or a line over a polyline - cut into identical pieces that are kept
once, the doubled length reported as overlap_length. An INSERT holding two to
four free ends is an inline fitting joined through; a reducer word in its
block name (reducer, perekhod, reduksiyon, verloop, reduzier) stops diameter
continuity.
Each diameter label goes to the nearest piece and holds for its drawn entity
(direct); unlabelled pieces take the nearest direct diameter along the run
(continuity) or stay unassigned - two sizes equally near are never guessed.
Labels stay as drawn. A supply or return word near a run overrides its
layer's service for the whole run. Run ends attach to the tag whose labelled
geometry holds them; a shape holding two tags (a room, a skid) is not
equipment. Tests on hand-built drawings and on the synthetic plant pair.
Beyond the plan: a CIP / glycol layer naming no direction (classify_layer
files it `unknown` on purpose) now takes its direction from the word instead
of staying unknown; is_reducer matches on vocab.fold and knows the German
word equipment_kind already had; the fitting, equipment and attachment
lookups go through grid indexes - measured 149 s -> 2.8 s on a 59,200-record
synthetic drawing, results identical to the plan engine on it and on the
plant pair. No ActiveX call is added (pure module).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd