LUMA
Architecture

Selection System

Tag expressions, capability tokens, spatial attributes, and circle fitting algorithm

The selection system translates abstract fixture queries into concrete fixture lists with spatial metadata. It is the key mechanism that enables venue-portable patterns.

Tag Expression Parser

File: src-tauri/src/services/groups.rs

Tag expressions support boolean algebra over fixture group tags:

all                    -- all fixtures in the venue
front                  -- fixtures in groups tagged "front"
front & has_color      -- front fixtures with RGB capability
left | right           -- fixtures on either side
circular & ~blinder    -- circular fixtures that are not blinders
has_movement > has_color -- prefer movers, fall back to color

Operators

OperatorNameDescription
&ANDBoth conditions must match
|OREither condition matches
^XORExactly one condition matches
~NOTNegate the following condition
>FallbackUse left side; if zero matches, use right side
( )GroupingOverride operator precedence

The > (fallback) operator is unique: it evaluates the left side first, and only includes the right side's results if the left side matched zero fixtures. This enables graceful degradation across venues with different fixture inventories. For example, has_movement > has_color prefers moving head fixtures but falls back to any color-capable fixture if no movers are patched.

Capability Tokens

These special tokens are resolved by inspecting each fixture's definition at runtime:

TokenMatches fixtures with...
has_colorRGB intensity channels or a color wheel channel
has_movementPan/tilt channels
has_strobeA shutter/strobe channel

Capability tokens are evaluated dynamically -- they do not need to be manually assigned. Any fixture whose QLC+ definition includes the relevant channel types will match automatically.

Spatial Attributes

The get_attribute node (in src-tauri/src/node_graph/nodes/selection.rs) extracts per-fixture scalar values from a Selection and outputs them as a Signal with N = fixture count, T = 1, C = 1.

AttributeDescription
indexInteger order within the selection (0, 1, 2, ...)
normalized_indexOrder normalized to 0.0-1.0 range
pos_xAbsolute global X position (meters)
pos_yAbsolute global Y position (meters)
pos_zAbsolute global Z position (meters)
rel_xX position relative to selection bounding box (0.0-1.0)
rel_yY position relative to selection bounding box (0.0-1.0)
rel_zZ position relative to selection bounding box (0.0-1.0)
rel_major_spanPosition along the axis with largest physical range
rel_major_countPosition along the axis with most distinct fixture positions
circle_radiusDistance from the selection's centroid
angular_positionAngle on fitted circle via PCA + RANSAC (0.0-1.0)
angular_indexIndex-based angular position: fixtures sorted by angle, then assigned equal spacing (0/n, 1/n, 2/n, ...)

rel_major_span vs rel_major_count

The "span" variant picks the axis with the largest physical extent (useful for linear arrangements where fixtures are spread unevenly). The "count" variant picks the axis with the most distinct position values (useful when fixtures are evenly spaced but the physical range is similar across axes).

Circle Fitting Algorithm

File: src-tauri/src/node_graph/circle_fit.rs

For detecting circular fixture arrangements in arbitrary 3D space (used by angular_position and angular_index attributes):

Step 1: Centroid

Compute the mean position of all input points.

Step 2: PCA Plane Fitting

Build the 3x3 covariance matrix of centered points. Extract the two dominant eigenvectors via power iteration with matrix deflation. These two vectors define the best-fit 2D plane containing the points.

Step 3: Project to 2D

Map each 3D point onto the fitted plane using dot products with the two basis vectors. This reduces the problem from 3D circle fitting to 2D.

Step 4: RANSAC Circle Fit

Run 100 iterations of random 3-point sampling:

  1. For each sample of 3 points, compute the circumcenter via the analytic formula
  2. Count inliers (points within 2.5 distance units of the circle)
  3. Keep the fit with the most inliers
  4. Early exit if 90% of points are inliers

Step 5: Kasa Refinement

Apply the Kasa algebraic circle fit on inliers, solving the normal equations via Cramer's rule (3x3 determinant). Recompute inliers with the refined circle.

Step 6: Angular Positions

Compute atan2(v - center_v, u - center_u) for each point's projected 2D coordinates, normalized from [-pi, pi] to [0, 1].

The algorithm is deterministic (fixed seed for the pseudo-random generator) so results are reproducible across graph executions.

On this page