masque/masque/builder/__init__.py

90 lines
4 KiB
Python

"""
Builder helpers for port-based assembly and primitive-offer routing.
A routing `Tool` describes the primitive route families it can provide by
returning `PrimitiveOffer` objects. Each offer is a parameterized planning
candidate: it exposes legal parameter domains, endpoint behavior, ptypes, cost,
optional footprint metadata, and a commit hook for producing tool-specific
render data after a concrete parameter has been selected.
`Pather` owns user-facing route operations such as `trace()`, `jog()`,
`uturn()`, and `trace_into()`. The internal planner resolves each operation into
one or more `SolverRequest`s. This normalization is why the public routing API
can remain a convenient keyword-based interface without making the solver
stringly typed internally. A pure solver search selects a `Candidate`, and a
`RouteLeg` attaches that candidate to its copied source port and Tool. Only
after selection succeeds are the chosen offers materialized through
`offer.commit(parameter)` into a `PreparedRouteResult` containing
`RenderStep.data`. `Pather` then applies that prepared result to its live ports
and pending render queue.
Selection is pure with respect to caller-owned Pattern, Library, and Pather
state. Tool offer discovery and endpoint/cost/bbox callbacks must likewise be
deterministic and must not mutate that state. `commit()` is the first
selected-offer materialization hook, but it still must not mutate the live
layout. `Tool.render()` is the geometry mutation boundary: later,
`Pather.render()` batches compatible `RenderStep`s and inserts the resulting
geometry into the Pattern and Library.
`PrimitiveOffer` and `RenderStep.data` are the tool-facing contract.
`RenderStep` is `Pather`'s deferred-render record, and
`masque.builder.planner` is an internal planner implementation rather than a
stable public API.
Custom Tool authors should run `validate_tool_contract()` as a development or
application-startup preflight. It performs the comprehensive semantic checks
that are intentionally not repeated during route selection, keeping the normal
routing path focused on search rather than contract verification.
The practical layering is:
- user code drives `Pather` and chooses Tools per port or by default,
- Tools describe local legal motion primitives without touching Pather state,
- the internal router composes those primitives into high-level route shapes,
- Pather applies the prepared result to ports, deferred render queues, and the
target pattern/library.
`Pather` intentionally remains a Pattern-oriented facade rather than exposing
separate assembly and routing objects: its user model is a working Pattern with
routing tools attached. The ownership phases above are internal boundaries,
not additional objects callers must coordinate.
Code outside the builder package should prefer the exports here over importing
from `masque.builder.planner`. The planner package is intentionally available
for tests and internal maintenance, but it is not the compatibility boundary
for custom Tools.
"""
from .pather import (
Pather as Pather,
PortPather as PortPather,
RouteCompletionCallback as RouteCompletionCallback,
)
from .error import (
ToolContractError as ToolContractError,
RouteError as RouteError,
RouteFailureDetails as RouteFailureDetails,
RouteOperation as RouteOperation,
RouteFailurePolicy as RouteFailurePolicy,
MinimumStatus as MinimumStatus,
)
from .utils import ell as ell
from .tool_testing import (
ToolContractCase as ToolContractCase,
validate_tool_contract as validate_tool_contract,
)
from .tools import (
Tool as Tool,
AutoTool as AutoTool,
PathTool as PathTool,
RenderStep as RenderStep,
RenderStepKind as RenderStepKind,
PrimitiveKind as PrimitiveKind,
CostCallable as CostCallable,
GeneratedEndpointFn as GeneratedEndpointFn,
PrimitiveOffer as PrimitiveOffer,
StraightOffer as StraightOffer,
BendOffer as BendOffer,
SOffer as SOffer,
UOffer as UOffer,
circular_arc_sbend_endpoint as circular_arc_sbend_endpoint,
)