step.motion.path.passes.resolve_heading ======================================= .. py:module:: step.motion.path.passes.resolve_heading .. autoapi-nested-parse:: ``resolve_heading`` pass — stamp compile-time angles onto heading turns. ``TurnToHeading`` lowers to an OPAQUE turn segment (``angle_rad=None``): its relative angle is only known at ``on_start``, when the ``HeadingReferenceService`` computes the shortest-path delta from the live heading. That opacity blocks every geometry pass — most importantly ``corner_cut``, which needs a known ``angle_rad`` to compute the arc radius — so a mission written in the recommended drift-robust style (``turn_to_heading_* `` + ``heading=`` pins) silently gets ZERO benefit from ``optimize(...).cut_corners(...)``. Path compilation happens inside ``Optimizer._execute_step`` — immediately before the path runs — so the robot's CURRENT heading is the path-start heading. This pass integrates a running absolute heading through the node list (exactly like ``absolute_heading`` / ``to_absolute``) and, wherever the running heading is provably known, computes the ``TurnToHeading`` delta at compile time and stamps it onto the segment as ``angle_rad``. The segment KEEPS its ``opaque_step``: a stamped turn that no later pass consumes still executes as the original drift-corrected ``TurnToHeading`` (the executor prefers the opaque adapter). Only a pass that geometrically REPLACES the turn — ``corner_cut`` folding it into an arc — uses the stamp, which is exactly the trade corner cutting always makes (open-loop arc, downstream ``heading=`` pins re-correct). Running-heading bookkeeping (absolute radians, CCW-positive): * start: live heading at compile time (known) * ``turn`` with known ``angle_rad`` — advance * ``TurnToHeading`` — after it, heading == its absolute target, so it RE-ESTABLISHES a known heading even when the current one is unknown (the turn itself is only stamped when the ENTRY heading is known) * ``arc`` with known ``arc_angle_rad`` — advance; ``crab_arc`` — unchanged * ``linear`` / ``diagonal`` with ``heading_deg`` pin — heading := pin (known) * ``linear`` / ``diagonal`` without pin — heading held, unchanged * ``follow_line`` without pin, ``spline`` — unknown * ``SideAction`` / deferred ``None`` — unknown (a side action may drive) If no heading reference is marked (or no robot is available, e.g. in ``explain()``), the pass is a no-op. Classes ------- .. autoapisummary:: step.motion.path.passes.resolve_heading.ResolveHeadingTurnsPass Module Contents --------------- .. py:class:: ResolveHeadingTurnsPass(robot: GenericRobot | None = None) Stamp known relative angles onto ``TurnToHeading`` segments. .. py:attribute:: name :value: 'resolve_heading' .. py:method:: run(nodes: list[step.motion.path.ir.PathNode | None]) -> list[step.motion.path.ir.PathNode | None]