Skip to main content

Video: Build a Light Rail Station with Flow

Using Flow to build a parametric light rail station

R
Written by Rob Asher
Updated this week

This video focuses on conditional design flows that rely on rules to define an outcome, while a Light Rail station is used in this example, the techniques can be applied more broadly across your workflow whether it be structure, facades or master planning.

Light Rail Station Configurator — Step-by-Step

1 — Goal & inputs

  1. Goal: generate a station with a platform, roof (panelized), columns, and conditional ticket machines.

  2. Primary inputs (parameters):

    • platform_width (example: 7.9 m)

    • platform_length (example: 33 m)

    • roof_length (example: 22 m; controlled by roof_offset)

    • twist / height / num_columns can be added later


2 — Attach an identity flow to the alignment

  1. Start with the centerline (line geometry) for the stop.

  2. Attach an identity flow (reads the line, writes the line) so you can build transforms relative to that line.


3 — Create roof offset / roof length parameter

  1. Add a Line Shorten (or line_shorten_transform) block to shorten the input line to create the roof centerline.

  2. Expose a single input parameter roof_offset (number, default 3) and write it to parameters.start_offset and parameters.end_offset so one parameter drives both ends.

  3. Test: change roof_offset and verify the roof shrinks symmetrically.


4 — Create the platform (arrayed precast panels)

  1. Model one platform panel block (e.g., 4.0 × 1.2 m, height 0.2 m, base height 0.2 m).

  2. Set the block’s block_base_point so it aligns correctly when inserted. Group and name it e.g., station_platform_panel.

  3. Use array_transform (points along / array along line) with spacing (e.g., 1.2 m) and a start offset (e.g., 0.6 m) to populate panels along the full platform line.

  4. Use instance_group_transform to place the station_platform_panel instances along those array points.


5 — Handle start/end special panels (strip + instance groups)

  1. Duplicate your panel block into: panel_start, panel_middle, panel_end (start/end unique details).

  2. Use the array “strip” pattern: separate the first and last items from the middle list.

    • Extract mids (middle panels) and place panel_middle.

    • Place panel_start at the first position via an instance_group_transform.

    • Place panel_end at the last position via an instance_group_transform.

  3. Embed these arrays so start/end special cases are stable.


6 — Add roof panels (stroke dash) — loose architectural approach

  1. Use stroke_dash_transform applied to the shortened roof line to create roof panel segments (dash pattern array).

  2. Tune the dash array (e.g., [2, 0.2, 1]) to control panel lengths, gaps, and rhythm.

  3. If needed, offset the roof line to the platform using offset_polygon_transform (example offset 2–3 m to place roof over platform).

  4. Create a usage for roof: clone a usage (e.g., roofs) to carry color, base_height, height (set thin thickness like 0.1 m), then get_usage + apply_usage to the roof geometry. Remove conflicting defaults (e.g., default roof height) so usage respects the small thickness.


7 — Add columns derived from stroke dashes

  1. Clone the roof stroke into a new element called columns.

  2. Convert dash positions into column insert points (columns sit at dash positions).

  3. Create a column block (e.g., 0.2–0.3 m square, height 3.3 m, base_height = platform height).

  4. Place columns with instance_group_transform at the column points. Make sure base heights align so columns sit on the platform (don’t penetrate).


8 — Clean geometry sequencing & offsets

  1. Sequence operations carefully: often offset the line first, then create polygons, then offset polygons. This avoids awkward intersections.

  2. If parts look misaligned at ends, tweak start/end offset parameters and the array start/end offsets.

  3. If dash-based roof produces tiny off-cuts, consider block panels for the platform (block approach) or additional trimming techniques later.


9 — Add ticket machines with conditional logic

  1. Create a ticket machine block (rectangle with base_height so it sits on platform). Name it ticket_machine.

  2. Get candidate placement points using along_point_transform on the platform/roof dashes (e.g., second-last and second positions).

  3. Add conditional logic (ternary if) to insert a third ticket machine for long platforms:

    • Condition: geometry_length > N (e.g., > 27 m).

    • If true: compute midpoint (e.g., take the list of along points, find floor(length/2), pick that item) and include it.

    • If false: return null (or use a nullish-fixer) so instance group doesn’t fail on null items.

  4. Use an instance_group_transform to place ticket machine instances at the selected points.


10 — Embed data & package flow

  1. When logic is stable, embed the flow data so parameters are stored with the geometry.

  2. Rename the flow sensibly (e.g., rail_stop_configurator_v1).

  3. Keep only the required high-level parameters exposed (e.g., platform_length, platform_width, roof_offset, panel_spacing, roof_dash_pattern) — bundle other internal parameters to avoid overwhelming users.


11 — Visual polish & UI values

  1. Set fills, opacities, and materials (e.g., roof color, platform stone color, column dark gray). Set fill opacity to 100% for clarity, and adjust shininess where helpful.

  2. Group usage-driven visual attributes by creating usages (e.g., platform, roof, columns) — they carry color, base height, and thickness which flow through when applied.


12 — Testing & edge cases

  1. Test the configurator across short and long platform lengths:

    • Check start/end panels and columns sizing at very short lengths.

    • Verify the conditional ticket machine logic toggles exactly at boundary lengths (adjust N as needed).

  2. Test corner cases: intersections, nearby obstacles (span panels), and large spans where special panel types are required.

  3. Fix null/empty list errors by wrapping instance inputs in null-safe checks.


13 — Scale & extend

  1. Once functional, clone the flow to create variations (e.g., rail_stop_with_canopy, rail_stop_simple, rail_stop_with_more_columns).

  2. Add more parameters later: num_columns, column_spacing, roof_height, canopy_fall, drainage, accessibility features (ramps/elevators), or lighting & MEP placeholders.

  3. Export parts lists for fabrication (panel counts, dimensions, weights, crane points) from block instances.


14 — Best practices & tips

  • Bundle low-level parameters so users see a small, meaningful set of controls.

  • Prefer blocks for repeatable, fabricable elements (precast panels) when you need accurate counts/weights.

  • Use stroke/dash for fast architectural iterations; use block arrays for engineering/fabrication detail.

  • Name your blocks and flows clearly — panel_middle, panel_start, ticket_machine, roof_usage — it speeds development.

  • Sequence transforms to avoid geometry conflicts (offset → polygon → dash → instance).

  • Handle exceptions explicitly: starts, ends, inside/outside corners, intersections.

Did this answer your question?