Skip to main content

Module station

Module station 

Source
Expand description

Stations: positions named by a distance along a basis curve (#241, ADR 0082).

A station is exact stored data – a distance and three offsets – that a kernel resolves by evaluating the basis curve; the graph never does.

§Distance

Station::distance is measured from the basis curve’s start in that curve’s own convention: PLAN distance on an elevated or a banked curve (their parameter), arc length on every other curve, 2D or 3D. The start is parameter 0, or the first parameter of the domain of a polyline or a B-spline. A resolver refuses by name a distance before the start or past the curve’s length; the graph refuses a non-finite or negative one when the node is pushed.

A basis may also be a curve relation a distance runs along (#285): a CurveRelation::Composite, a CurveRelation::Trimmed, a curve placed by an InstanceAtStation, nested in any combination. The distance then runs end to end through the atomic curves it is made of, each span in its own curve’s convention, and those must agree: plan distance when every span is an elevated or banked curve, arc length when none is. A composite of segments placed at stations of another curve is measured along its own segments, whatever curve they were placed on. Each joint of a composite is a seam (below), and its pieces must meet end to start; a resolver refuses by name pieces measured differently, a gap, and a piece whose undeclared sense runs it backwards.

§Frame and offsets

The basis curve’s section frame at the distance has three unit axes: the tangent, the lateral axis to the LEFT of it, and up = tangent x lateral. For a 2D curve (lying in z = 0) they are the tangent, the left normal and +Z; for a banked curve, its rolled section (lateral towards the left rail head); for every other 3D curve, an elevated one included, the reference-up frame against +Z (lateral horizontal, up leaning back with the grade). StationFrame::Plan instead takes the tangent’s horizontal projection, the horizontal left normal and +Z.

The station’s point is point + lateral * l + up * v + tangent * g for the offsets (l, v, g) of StationOffsets. A resolver presents the frame as x the tangent, y up, z to the right (-lateral), the curve-evaluation provider’s layout.

§Explicit orientation (#246)

A station may carry a StationOrientation: an optional axis and an optional ref_direction, both given as COMPONENTS IN THE STATION’S BASE FRAME at its distance (the StationFrame it names), never in world coordinates: a vector (a, b, c) means a * tangent + b * lateral + c * up. This is how a linear placement reads its own axes: they “are relative to the curve used for linear referencing …, maintaining the relationship to the tangent of the curve” (buildingSMART IFC 4.3, IfcAxis2PlacementLinear), whose local X is the tangent, Y the left lateral and Z up, this module’s (tangent, lateral, up).

  • axis is the exact direction of the oriented frame’s up (local Z); absent, it is (0, 0, 1), the base frame’s up.
  • ref_direction fixes the oriented frame’s tangent role (local X); absent, it is (1, 0, 0), the curve tangent.
  • The frame is orthonormalised by Gram-Schmidt with the axis primary: up' = axis / |axis|, tangent' = normalise(r - (r . up') up') for the unit r = ref_direction / |ref_direction|, and lateral' = up' x tangent', so (tangent', lateral', up') stays right-handed.
  • A zero or non-finite vector, and an axis and reference direction (given or defaulted) parallel or anti-parallel within ORIENTATION_TOLERANCE (the sine of the angle between them), are refused by name when the node is pushed.

The oriented triad replaces the base one wherever a frame ORIENTS something: the frame a resolved station presents, and the plane a section’s profile is placed in (profile x along lateral', y along up'). The OFFSETS stay in the base frame: they locate the station’s origin, which the orientation does not move.

§Seams (#263)

A basis curve made of pieces – a polyline, a B-spline with a corner knot, an elevated curve whose profile’s grade breaks, a banked curve whose cant or pivot law jumps – has two frames where two pieces meet. A station within the arc-length tolerance of such a seam is ON it and reads the frame of one piece, chosen by SeamSide: the piece that starts there (SeamSide::Outgoing, the default and what every curve evaluator reads) or the one that ends there (SeamSide::Incoming). An OrientedCurveStation carries the side (OrientedCurveStation::seam); a plain CurveStation reads the outgoing piece, and CurveStation::with_seam_side turns it into an oriented station in its base frame that reads the other one.

A run of sections or offsets (Stations along one directrix) decides most sides itself: a section standing exactly on an interior seam where the tangent turns is cut in the mitre plane, the bisector of the incoming and outgoing tangents, and so is the run where it crosses such a seam between two of its stations. The run’s first station reads the outgoing piece and its last the incoming one, the pieces the run lies on. A seam where the tangent does not turn keeps the outgoing frame, unless the station standing on it names the other side (#301, below).

§Seam-snapping window (#294)

An authoring rule may put a station ON a seam when it lies within the model’s precision of one – IFC 4.3 ADD2 8.9.3.48.3 lets the previous segment’s tangent govern a station “within precision limits” of a tangent discontinuity – and that precision (up to a millimetre) is far wider than the arc-length tolerance. An OrientedCurveStation carries it as OrientedCurveStation::seam_window, a non-negative distance in the basis’s station measure, set by OrientedCurveStation::with_seam_window; 0, the default, is the arc-length tolerance alone.

A resolver snaps a station whose distance lies within the window of a seam of its basis whose frame may jump (a joint of a composite, a polyline vertex, a corner knot, a grade or cant seam) onto that seam, and reads it from the station’s SeamSide; the station’s point is then the seam’s. Where a seam’s distance is numerical (after an offset piece, an ellipse or a B-spline), the resolver uses a certified interval of it. It refuses by name a window holding two seams, and a seam whose certified interval straddles the window’s edge (inside or outside cannot be told). The graph refuses a negative or non-finite window when the node is pushed. A plain CurveStation has no window.

§Seam windows on run stations (#301)

The stations of a run carry the same window and a SeamSide: a SectionAtStation (in SolidOperation::SectionsAtStations and SurfaceRelation::OpenSectionsAtStations) through SectionAtStation::with_seam_window and SectionAtStation::with_seam_side, an offset’s RunStation (in CurveRelation::OffsetsAtStations) likewise. Defaults are a window of 0 and Outgoing, today’s reading. Station, StationedSection and StationedOpenSection have public fields and stay window-free, so SolidOperation::StationedSpine, SurfaceRelation::SectionedSurface and CurveRelation::OffsetByStations read as before; their general forms above carry windows.

  • Snapping. Each windowed station is snapped by the #294 rule against the run’s directrix: onto the one seam whose frame may jump within its window, at the seam’s own (certified) distance; a window holding two seams, or a seam whose certified position straddles its edge, is refused by name. The run then uses the snapped distance everywhere: the section’s place, the linear interpolation between stations, the run’s span. Snapped distances must still increase strictly; two stations snapped onto one seam, or one snapped past its neighbour, are refused by name.
  • Side. The run’s first station reads the outgoing piece and its last the incoming one, whatever side they name: the run lies on those pieces, as a curve’s start and end have one piece. An interior station on a seam where the tangent turns stands in the mitre plane, so its section (or offset point) is the mitre section, placed in BOTH pieces’ frames and projected: its side is not read. An interior station on a seam where the tangent does not turn but the frame may jump (a banked curve’s roll) is placed in the frame of the piece its side names.

§Run stations at the basis’s ends (#303)

An offset by distances often continues its first and last offsets unchanged out to its basis’s ends (IFC 4.3 ADD2 8.9.3.42.3), so its run needs a station AT the basis’s end. Where the basis’s length has no closed form (an offset beside a gradient curve, a clothoid or a B-spline, a B-spline itself), a caller without a provider cannot state that distance. A RunStation may therefore stand at the basis’s start or end without stating it (RunStation::at_basis_start, RunStation::at_basis_end): its RunStation::basis_end names which, and its stated distance is 0 and not read.

  • Resolution. A resolver reads the start as distance 0, where the station measure starts on every curve, and the end as the basis’s length by the kernel’s own measure of it, the number a curve path’s open end resolves to (#298): the length of the flattened basis (axiolid-mesh-compile’s station::curve_path) as axiolid-evaluate’s CompositeBasis measures it, bitwise; on an unbounded line its parameter domain end, |direction|, as inside a composite. A station at the end is then the same station stated at that number.
  • Order. The graph refuses, when the node is pushed, a station at the start that is not the run’s first, one at the end that is not its last, and either stating a distance other than 0; the stated distances must increase strictly from a start station’s 0. Whether the end comes after the last stated station needs the length, so a resolver refuses by name a run whose resolved distances do not increase strictly (a stated station at or past the basis’s end).
  • Windows. Snapping (#301) applies after resolution: a station at the end with a window snaps onto the one seam within the window of the resolved end, by the #294 rule.

Only the stations of a CurveRelation::OffsetsAtStations carry it; a SectionAtStation states its distance.

§Placing a node at a station (#264)

InstanceAtStation reuses a node – a curve, a solid, a surface – in the frame of an OrientedCurveStation, the way Instance reuses one under a fixed transform. The station stays symbolic: a kernel resolves its frame when it evaluates the node, with the station’s StationFrame, orientation and SeamSide. The source’s local axes map onto the station’s oriented frame as a linear placement reads them (buildingSMART IFC 4.3, IfcAxis2PlacementLinear):

  • local x onto the oriented tangent (tangent'),
  • local y onto the oriented lateral, to the LEFT (lateral'),
  • local z onto the oriented up (up' = tangent' x lateral'),
  • the local origin onto the station’s point, its offsets read in the base frame.

This is a rigid motion (right-handed, no scale). It is not the profile mapping above (a section’s x along lateral and y along up), and not the provider layout a resolved station presents (x tangent, y up, z right): a placed curve is laid ALONG the curve, a section ACROSS it. A 2D source curve lies in its local z = 0, so it is placed in the plane of the oriented tangent and lateral, and the placed curve is a 3D curve whatever the source’s dimension.

§Sections between stations

A profile placed at a station maps its x onto the lateral axis and its y onto up, so its normal x x y is the tangent. Between two stations everything is interpolated LINEARLY in distance: the offsets, each section point with its counterpart in the next section, and an explicit orientation (the unit axis and the unit reference direction, component-wise in the base frame, an absent one counting as (0, 0, 1) and (1, 0, 0), then orthonormalised as above; a resolver refuses by name an interpolated pair that degenerates).

§Matching points between sections

Untagged sections are matched by position: a closed profile’s points by ring and vertex index, an open section’s by vertex index. Tagged sections (SectionAtStation::tags, StationedOpenSection::tags) are matched by tag. One run of sections is tagged throughout or not at all, and every section carries the same SET of tags, none repeated, so the tags are a bijection between any two sections:

  • an open section’s tags run along its polyline in the first section’s order or in reverse (a reversed section is joined reversed); any other order would cross the sheet and is refused;
  • a closed section’s tags name its contour vertices, the outer ring’s then each hole’s, each ring from its first segment’s start in its authored sense. Its profile must be polygonal (straight segments only, optionally under a Profile::Derived transform). The tags must map each ring of the first section onto one ring of every other, the outer onto the outer, in the same cyclic order once both are wound alike (outer counter-clockwise, holes clockwise): a section may start a ring at another vertex, list its holes in another order or wind a ring the other way, nothing else. A resolver checks this, since it needs the profile’s vertices.

Structs§

CurveStation
A point and frame at a station along a basis curve.
InstanceAtStation
A node reused in the frame of an OrientedCurveStation (#264): the station-framed form of Instance.
OrientedCurveStation
A CurveStation with an explicit orientation (#246) and the side of a seam it reads (#263).
RunStation
A station of an offset run with the side of a seam it reads and its seam-snapping window (#301): the general form of the plain Station a CurveRelation::OffsetByStations takes, used by CurveRelation::OffsetsAtStations. It may also stand at its basis’s start or end, its distance measured by the kernel (#303).
SectionAtStation
A section standing at a station, with optional tags and an optional explicit orientation (#246): the general form of StationedSection (closed) and StationedOpenSection (open).
Station
A distance along a basis curve with offsets in its section frame.
StationOffsets
Offsets of a station from its basis curve, in the curve’s section frame.
StationOrientation
An explicit in-section orientation at a station (#246).
StationedOpenSection
An open section standing at a station along a sectioned surface’s directrix, its points named by tags.
StationedSection
A closed profile standing at a station along a sectioned spine’s directrix.

Enums§

BasisEnd
Which end of its basis a RunStation stands at, measured by the kernel (#303; see the module documentation).
SeamSide
The piece a position exactly on a seam is read from.
StationFrame
Which frame a station’s offsets and orientation are read in.

Constants§

ORIENTATION_TOLERANCE
Largest sine of the angle between a StationOrientation’s axis and reference direction that still counts as parallel.