# BoardPose Turn a board placement list and an explicit setup profile into panel placements, compatible logical feeder assignments and a suggested placement order. Processing uses Python's standard library and makes no filesystem or network calls. The same `run(dict)` interface is usable from a Pyodide worker. ```python from products.boardpose.engine import run result = run(job) # result: {"summary": {...}, "files": {filename: text}, "warnings": [...]} ``` The input contract is illustrated by `fixtures/boardpose/good.json`. Rejects raise `ValueError` before returning any plan. No input objects are mutated. Sort order, tie handling, artifact names and report contents are deterministic. ## Coordinate convention All coordinates are millimetres in a Cartesian y-up system. Angles are degrees counterclockwise, normalized modulo 360; zero points along positive x. Board width and height define its rectangular local extent. Component coordinates are points, not component footprints or clearance envelopes. The optional `board.datum_x` and `datum_y` default to zero. Subtract them from each source placement before applying any other transform. For a bottom placement, `board.bottom_mirror` is one of: | Value | Local position | Direction angle | | --- | --- | --- | | `none` (default) | unchanged | unchanged | | `x` | x becomes width minus x | 180 minus angle | | `y` | y becomes height minus y | minus angle | These names indicate the coordinate being reflected, not the name of a mirror axis. `none` uses the supplied common board-view coordinates. Top placements always remain unreflected. The compiler cannot detect whether a CAD export already mirrored the bottom: the customer must supply the convention. Next add the panel cell offset: column times (board width plus x gap), and row times (board height plus y gap), with zero-based cell indexes. Rotate the entire panel counterclockwise about its local origin, then translate by `panel.origin_x` and `origin_y`. A panel's rotated extent may extend left or below the origin. No automatic translation to an axis-aligned bounding box occurs. Original reference, side, normalized source angle, mirror convention and cell index remain in the exported placement rows. ## Feeder assignment and placement order A feeder record describes a customer-defined logical slot. Its `parts` are the permitted exact part identifiers; `capacity` is the maximum number of distinct part types assignable there. Capacity is neither stock quantity nor a claim that a physical feeder can hold several parts. Set capacity to one for one-part slots. One part type uses the same assigned slot across all panel copies. The assignment cost for a part/slot pair is the sum of Euclidean distances from the slot to every placement of that part, rounded once to 0.000001 mm. The solver finds a minimum sum of these integer costs under the declared compatibility and capacity constraints. This is a geometric proxy, not machine time or a combined route optimum. It accounts for no nozzle, travel obstacle, speed, acceleration, pickup orientation or stock information. The sequence uses a deterministic greedy rule: choose the next placement with the least current-point-to-feeder plus feeder-to-placement travel. Ties use the expanded reference. Top placements form the first setup pass and bottom placements the second, each starting at the declared panel origin. It does not model flipping, re-fixturing, return-home travel, or time between passes. ## Artifacts - `panel_placements.csv`: expanded coordinates, orientations, identities and slots. - `feeder_assignment.csv`: one row per part, its slot, placement count and integer cost. - `placement_sequence.csv`: numbered suggestions, side, position, slot and geometric travel. - `preview.svg`: board outlines, top/bottom placement points and directions, logical slots. - `normalized_input.json`: normalized, deterministically ordered source profile. - `proof.json`: independent export checks and assignment optimality certificate. - `report.json`: counts, measured geometric objectives, proof and limitations. The independent checker in `products/boardpose/proof.py` reads the emitted CSV, inverts every transform, checks identities and cardinalities, verifies the feeder constraints and route travel, and rebuilds the assignment residual network. It certifies optimality for the rounded costs by excluding negative-cost residual cycles. Its potential list orders sorted part names, sorted slot names, then a sink node. This proof concerns file mathematics. Physical machine compatibility is explicitly `UNKNOWN`. The preview is an illustration, not a clearance check. ## Bounded scope Maximum source placements: 1,000; expanded placements: 2,000; distinct parts: 32; logical feeders: 64; rows/columns: 100 each, subject to the expanded-placement cap. Dimensions are 0.001–10,000 mm; gaps 0–10,000 mm. Other numeric coordinates and input angles are bounded to ±1,000,000. Count fields require integers, excluding booleans. NaN, infinities, unsupported fields, duplicate identifiers and out-of-board placements are rejected. Identifiers start with an ASCII letter and then use letters, digits, dot, underscore or hyphen, up to 64 characters. No KiCad `.pos` parsing, controller XML, board connectivity, component polarity, real machine compatibility, physical setup validation or fabrication guarantee is provided. A valid logical plan can still be unsuitable for a physical machine. The download is a planning artifact, not an instruction to run equipment.