Expand description
PORTS — a part’s declared connection points, as PART DATA rather than history features.
§The block
A part document carries ports as a sibling of features, beside the eCAD
symbol and pads blocks:
"ports": [
{ "name": "J1", "purpose": "wiring",
"points": [ { "name": "VCC", "transform": { … }, "pointRef": "Body_PZ" } ] }
]A port is a NAMED GROUP of connection points with one purpose — pcb,
wiring, piping, or anything else a later slice invents; the kernel
carries the string and never enumerates it. One part may declare several
ports with different purposes (a valve with a piping port and a wiring port
for its pressure sensor). None of this is a feature: there is no history
row, no dialog, no icon and no schema-catalogue entry.
§Addressing
A connection point is addressed [<occurrence chain>:]<port>.<point> —
J1.VCC in its own part, ACOMP3:J1.VCC once placed, ACOMP7:ACOMP1:J1.VCC
at depth. : is the occurrence namespace the assembly lane already speaks
(component::namespaced, peeled by split_component_namespace); .
separates the port group from the point. The NAME is the identity — there is
no second hidden id — so :, . and / are refused inside a port or point
name (/ because the harness routing graph keys its nodes {address}/A).
The address is the key in SceneMap::ports, so every existing consumer —
wireHarness from/to, a spline anchor’s attachment.portRef, a sketch
on the point’s frame — refers to a connection point exactly as it referred
to a PORT feature id before, only with a name a user can read.
§One symbol UNIT per port group
A symbol pin binds to a connection point BY NAME (part_pins.rs). With
several port groups J1.1 and J2.1 are both “point 1”, so the name alone
cannot say which. The part’s SYMBOL says it instead: eCAD’s Symbol already
carries unit_count, KiCad’s own model for a multi-unit device, and ONE
SYMBOL UNIT MAPS TO ONE PORT GROUP. Pin 1 of unit 1 is J1.1 and pin 1
of unit 2 is J2.1, so pin labels stay short and two identical headers on
one part are ordinary rather than a collision.
unit_map states the mapping:
- a group may name its unit with
symbolUnit(1-based), and that is the mapping, written down; - a document with ONE group and no
symbolUnitanywhere maps that group to unit 1. This is the ordinary single-group part, and it declares nothing; - a document with SEVERAL groups binds only the groups that name a unit. Position is deliberately NOT a fallback here: a piping component declaring its piping port first and the wiring port of its pressure sensor second would otherwise bind the sensor’s pins into the piping port. A group with no unit is not a schematic thing at all, which is exactly right for that piping port.
Disagreements are REPORTED, never refused, and each names what it concerns:
a unit with no group (UnitProblem::UnitWithoutGroup — its pins can bind
to nothing), a symbolUnit past the symbol’s unit_count, and two groups
claiming one unit. unit_count 0 reads as 1, which is what a symbol written
before units existed carries.
§A placement is an OFFSET IN A SEAT
A point may name geometry that places it: pointRef locates it,
directionRef turns it. Those two used to REPLACE the typed placement,
which made picking a reference the end of the conversation — there was
nothing left to adjust, and the transform beside them was dead data.
They now supply a SEAT (seat_of): a Frame whose origin is where the
reference puts the point and whose x_axis is the direction it gives
(x, because a point’s direction is its rotation applied to +X, the
convention every feature transform reads — see
Frame::from_origin_direction). The point’s own transform is then read
IN that frame: position along the seat’s three axes, rotationEuler
composed onto its basis. So “on this face, 2 mm off it, turned 30 degrees”
is one point with a reference and a small offset, and moving the face
carries all of it.
A point that names NOTHING has the WORLD seat — origin at the part origin, the world axes — in which an offset is an absolute placement. That is what a document written before the seat existed says, and it reads exactly as it did. A reference that seats the origin but implies no direction (a vertex, a bare scene point) keeps the world axes too.
What a reference may BE is the kernel’s own analytic vocabulary
(crate::SelectionGeometry, the same one an assembly constraint and a
PMI annotation measure against): a planar face seats on its plane along its
normal, a cylindrical face on its axis, a CIRCULAR edge at its CENTRE along
its axis, a straight edge at its chord midpoint, a vertex
({solid}@x,y,z) at the vertex. A face or edge the analytic lane cannot
describe falls back to the parametric midpoint and the chord this module
always read.
§Resolved AFTER history
finish_history_run runs at the tail of every history run, after the
assembly solve and before the wire-harness router. That is deliberate: a
port point may reference ANY geometry in the part without a history-ordering
rule, because by the time it resolves the whole part is built. The tail is
authoritative: it resolves every point and overwrites whatever was there.
There is ONE deviation, and it is a fixed two-phase, not an ordering rule.
seed_history_run publishes, BEFORE the walk, every point whose placement
is ALREADY FULLY DETERMINED — no pointRef, no directionRef, no
mapsTo, so nothing the walk builds can move it. A SPLINE in the same
document can then attach to it, and a sketch can sit on its frame, which is
the whole standalone-harness workflow. A point that DOES reference geometry
stays tail-only: a same-document spline attached to one reports the address
unresolved and the harness lists it as a segment problem, by name, rather
than quietly routing to a stale placement. In an assembly the question does
not arise — a placed component’s points arrive with the component, during
the walk.
§Assemblies declare DOWN
An assembly declares its own ports and maps each point to a descendant’s
point with mapsTo: J1.VCC -> ACOMP2:J5.VCC. The mapped point takes the
target’s resolved placement, so the interface moves with the child. Depth is
unbounded because the target is just another address.
§Encapsulation
A PCB assembly is a BOUNDARY: its child components are not directly wireable, and the only way to reach inside is a port the board itself declares. An ordinary mechanical assembly is TRANSPARENT — an enclosure holding a motor and a sensor does not hide them.
The mark is document_is_boundary: the explicit portBoundary flag when
the document sets one, else derived — a document carrying a pcb block is a
board and therefore a boundary. Boundary is INDEPENDENT of purpose: a
terminal block on a board carries wiring-purpose points and must still be
declared at board level before anything may wire to it, so purpose filters
what KIND of connection a point is, never whether it is reachable.
The filter is applied at EXPORT, in export_ports: what a part’s library
entry carries is what a parent can see. A boundary part exports only the
points it declares itself; a transparent part exports everything its run
published, descendants included. attach_component_ports then namespaces
whatever arrived, and arbitrary depth falls out of the address.
Structs§
- Port
Declaration - One declared port: a named group of connection points with one purpose.
- Port
Point - One connection point of a declared port.
- Port
Point Row - One resolved connection point, as the tail publishes it.
- Port
Seat - A
Frameas the ports REPORT carries it: four plain triples rather thanVec3s, because a report is compared and serialized (aVec3carries noPartialEq, by choice, and this is the shape the app and the verifier read). - Ports
Outcome - What the ports tail produced: its report, and the appended result the display pipeline consumes.
- Ports
Report - What the ports tail resolved, and everything about the block that does not hold — a name it refused, a reference that resolved to nothing, a mapping whose target is not in the scene. Never fatal: a problem is reported and the point still publishes at whatever placement could be worked out, so nothing downstream vanishes (the same contract rule the features follow).
Enums§
- Unit
Problem - Something about the unit-to-group mapping that does not hold. Never fatal: what maps still binds, and this says what did not.
Constants§
- ADDRESS_
SEPARATOR - What separates a port group from a point in an address.
- BOUNDARY_
KEY - The part document’s top-level key for the explicit encapsulation flag.
- DEFAULT_
DISPLAY_ LENGTH - Default length of the drawn point line.
- DEFAULT_
EXTENSION - Default straight run a wire keeps from a connection point before it may bend.
- PORTS_
BLOCK - The part document’s top-level key for its declared ports.
- PORTS_
FEATURE_ ID - The id of the tail’s appended result (never a history feature). The scene entities a declared point publishes ride it, so the display pipeline draws and picks a connection point exactly as it draws a feature’s curves.
- PORTS_
FEATURE_ TYPE - The type of the appended tail result.
- RESERVED
- Characters a port or point name may not contain: the occurrence namespace separator, the address separator, and the harness graph’s side separator.
Functions§
- address
- The address of a point within its own part:
{port}.{point}. - document_
is_ boundary - Whether a DOCUMENT hides its children behind its declared ports.
- export_
ports - What a part’s library entry carries out of its own run — the encapsulation filter, applied ONCE, here.
- finish_
history_ run - Resolve the request’s
portsblock against the FINISHED scene and publish every point. Runs after the assembly solve (so a mapped point reads its child’s posed placement) and before the wire-harness router (which routes over what this published). Empty when the document declares no ports. - place_
scene_ port - The same entities written straight into a scene — the ports tail and the component re-pose lane, which moves a placed part’s points without re-running its feature.
- port_
line - The drawn line for a connection point: a termination’s wire leaves along the direction; a waypoint’s wire passes through, so its line straddles the base point.
- publish_
port - Publish one connection point’s scene entities under
address: the record (ports[address]), its base point ({address}:Base), its axis and its frame (bothaddress) and its drawn line ({address}:PortLine). A sketch on the frame is a connector face square to the wire. - request_
is_ boundary - The same question for a parsed request (the tail’s lane).
- resolve_
direction - A
directionRef’s unit direction, orNonewhen the name resolves to nothing that implies one. The WAYPOINT feature’s lane — a waypoint has no seat (it is a feature with its own transform), so it takes the direction alone and lists an unresolved name in its own report. - seat_of
- The FRAME a connection point’s placement is an offset in.
- seat_
point - A local offset carried into world space by
seat. - seat_
vector - A local DIRECTION carried into world space by
seat(no translation). - seated_
direction - The world direction a point with this seat and this rotation faces: the rotation applied to +X (every feature transform’s convention), carried through the seat’s basis. With the world seat this is the rotated +X itself.
- seed_
history_ run - Publish, BEFORE the history walk, every declared point whose placement is already fully determined — see the module doc. Returns each published scene name with the CONTENT VERSION of the declaration behind it, so the incremental cache invalidates a feature that consumed the point when the declaration moves (without this a spline attached to a point would replay from cache at the point’s old placement).
- split_
address - Split a PART-LOCAL address into
(port, point).Nonewhen it carries no separator — a WAYPOINT feature id, for one, which is an address in its own right and belongs to no port. - unit_
map - Which port group each symbol unit binds its pins into, and everything about
the mapping that does not hold. The map is
unit (1-based) -> index intodeclarations``; a unit absent from it binds no pins. - unseat_
point - A world position read back as an offset in
seat— the inverse ofseat_point, which is what a gizmo drag writes. The basis is orthonormal, so the inverse is the transpose. - unseat_
vector - A world direction read back in
seat’s basis. - world_
seat - The seat of a point that names no reference: the world frame, in which an offset is an absolute placement.