step.motion.path.passes.decompose¶
decompose pass — split a _Then leg at every known after_cm boundary.
A conditional segment whose stop condition is a sequential _Then chain that
contains a bare relative after_cm leaf covers a KNOWN travel distance over
that leaf, surrounded by unknown sensor-driven portions.
drive_forward().until(after_cm(12) + over_line(sensor)) drives a fixed 12 cm
and THEN keeps driving until it crosses a line — the first 12 cm are
geometrically known, the rest is not. ...until(over_line(sensor) + after_cm(5))
drives until the line, then a known 5 cm further.
The raw lowering can’t recover that known distance: recover_known_distance
only promotes a bare after_cm (any combined _Then wrapper is rejected
because the combined condition may stop early — see known_distance.py). So
the whole leg stays distance_m=None, has_known_endpoint=False and is
invisible to the geometry passes (merge / to_absolute / splinify).
a + b builds _Then(a, b) and + is LEFT-associative, so a chain
after_cm(20) + after_cm(10) + over_line is the LEFT-nested tree
_Then(_Then(after_cm(20), after_cm(10)), over_line). This pass flattens the
_Then tree into its ordered leaf sequence, then walks the leaves
left→right, splitting at EVERY bare-relative-after_cm boundary — leading,
trailing, or interleaved.
It splits such a leg in TIME into segments of the SAME drive (identical kind/axis/sign/speed_scale/heading), one segment per GROUP:
Each bare relative
after_cmleaf becomes its OWN known leg — condition = thatafter_cm, run throughrecover_known_distancesodistance_m/has_known_endpointget filled. This leg is now a known-endpoint leg the downstream passes can absolutize / splinify.Consecutive NON-(bare-relative-
after_cm) leaves are GROUPED into one unknown sensor leg — their order-preserving_Thenrebuilt (soover_line’son_black + on_whitestay together as ONE leg, not split), withdistance_m=None,has_known_endpoint=False.
Each split-out after_cm becomes a REAL known distance because the executor
calls condition.start() at every segment start, so each after_cm
measures from its own segment’s start. Examples (sensor = on_black/over_line):
after_cm(12) + over_line→[known(0.12), sensor](leading).on_black + after_cm(5)→[sensor, known(0.05)](trailing).on_black + after_cm + on_black + after_cm→[sensor, known, sensor, known].over_line + after_cm(5)→[sensor(_Then(on_black, on_white)), known(0.05)].pure
over_line(no after_cm) → unchanged[seg].
Guards (passed through UNCHANGED):
A bare
after_cm(not wrapped in_Then) — already promoted at lowering byrecover_known_distance; left alone.A
_Thenwith NO bare-relativeafter_cmanywhere (e.g.over_line=on_black > on_white) — may stop early; left whole, not fragmented.An absolute
after_cmleaf — measures from odometry origin, not segment start; treated as a sensor leaf (grouped, never promoted to a known leg).turn/arckinds — path length doesn’t map to a usable travel distance.SideAction/Nonedeferred placeholders — pass through untouched.
Representation declaration is intentionally left undeclared (defaults to
EITHER / SAME / non-terminal); run this BEFORE to_absolute /
splinify so the split-out known leg can be optimized.
Classes¶
Split a |
Module Contents¶
- class step.motion.path.passes.decompose.DecomposePass¶
Split a
_Thenleg at every bareafter_cmboundary.Pure node→node pass. For each
linear/follow_lineSegmentwhose condition is a sequential_Thencontaining a bare relativeafter_cmleaf, splits it into one known-endpoint leg perafter_cmleaf (distance recovered) and one grouped unknown sensor leg per run of consecutive non-after_cmleaves — leading, trailing, and interleaved. A_Thenwith no bareafter_cmanywhere, and all non-decomposable nodes, pass through unchanged.Representation/terminal contract left undeclared (defaults to
EITHER/SAME/ non-terminal).- name = 'decompose'¶