pub enum ShapeOp {
Show 31 variants
Extrude(Expr),
Taper(Expr),
Rotate([Expr; 4]),
Translate([Expr; 3]),
Scale([Expr; 3]),
Split {
axis: Axis,
entries: Vec<SplitEntry>,
snap: Option<SnapBinding>,
},
SplitArea {
axis: Axis,
slots: Vec<SplitSlot>,
},
Fit {
axis: Axis,
candidates: Vec<FitCandidate>,
},
Repeat {
axis: Axis,
tile_sizes: Vec<Expr>,
rule: RuleCall,
},
Comp(CompTarget),
I(String),
Mat(Material),
Rule(RuleCall),
Size([Expr; 3]),
Center {
x: bool,
y: bool,
z: bool,
},
Mirror,
ShapeL {
front: Expr,
side: Expr,
cases: Vec<CarveCase>,
},
ShapeU {
front: Expr,
left: Expr,
right: Expr,
cases: Vec<CarveCase>,
},
Align {
local_axis: Axis,
target: Vec3,
},
Offset {
distance: Expr,
cases: Vec<OffsetCase>,
},
Roof {
spec: RoofSpec,
cases: Vec<RoofCase>,
},
RegSnap(String),
IfClear {
rule: RuleCall,
label: Option<String>,
},
IfOccluded {
rule: RuleCall,
label: Option<String>,
},
IfInside {
rule: RuleCall,
label: Option<String>,
},
IfTouches {
rule: RuleCall,
label: Option<String>,
},
Pick {
key: String,
choices: Vec<(f64, RuleCall)>,
},
Label(String),
Scatter {
volume: bool,
count: Expr,
rule: RuleCall,
},
Polygon(Vec<DVec2>),
Attach {
world_axis: Vec3,
cases: Vec<AttachCase>,
},
}Expand description
The atomic CGA operations that the interpreter executes.
Every operation transforms the current Scope into zero or more child scopes,
each tagged with a rule name that will be recursively evaluated.
Variants§
Extrude(Expr)
Lifts a 2-D footprint (XZ plane) into a 3-D volume by setting the Y size.
Taper(Expr)
Pyramidal taper: scales the top face toward the centroid.
amount ∈ [0, 1]: 0 = no taper, 1 = full pyramid (top collapses to a point).
Rotate([Expr; 4])
Applies an additional rotation to the scope (cumulative with existing rotation).
Components are (w, x, y, z) in grammar order; the evaluated quaternion
is normalized at derivation time.
Translate([Expr; 3])
Translates the scope origin in local space.
Scale([Expr; 3])
Scales the scope size along each axis (multiplicative).
Split
Divides the scope along axis into ordered slots.
When snap is Some, interior slot boundaries are snapped to the
nearest registered snap-plane along axis (see SnapBinding).
SplitArea
Divides the scope along axis by target areas instead of lengths.
Slot sizes are read as areas: absolute = square units, relative (')
= fraction of the face area, floating (~) = share of the remaining
area. Lengths are recovered by dividing through the cross-axis extent,
so this is only meaningful on the horizontal axes of a footprint-like
scope; SplitArea(Y) is rejected.
Syntax: SplitArea(X) { 30: Lot | ~1: Rest }
Fit
Size-fallback choice: invokes the first candidate whose minimum
extent fits the scope along axis; candidates are tried in order and
the scope vanishes when none fits (use a 0: catch-all to avoid
that).
Syntax: Fit(X) { 2.2: DoorBay | 1.2: WinBay | 0: Wall }
Repeat
Tiles the scope along axis with child scopes drawn from tile_sizes.
tile_sizes is a per-slot pattern that is cycled to fill the axis range.
Tiles are appended greedily (next tile from the cycle is added while it
still fits inside the remaining range), then all placed tiles are
scaled by the same factor total / Σ(placed) so they fill the scope
exactly with no gap and no overshoot.
A single-element list [t] is the legacy uniform Repeat(axis, t).
A multi-element list [a, b, c] produces an …, a, b, c, a, b, c, …
cadence where each tile’s relative width is preserved.
Comp(CompTarget)
Decomposes the scope into its geometric components (faces, edges, vertices).
I(String)
Terminal: replace the scope with the named mesh asset.
This is the “terminal symbol” — produces a Terminal node in the output model.
Mat(Material)
Sets the material on the current work item.
The material is propagated to the final Terminal, allowing downstream
renderers to apply textures / shaders and physics consumers to derive
volumetric mass properties without changing the scope.
Syntax:
Mat("Brick")/Mat(Brick)— id-only material; no density.Mat("Brick", 1800)— id + density in kg/m³; the interpreter computescrate::model::MassPropertiesfor terminals stamped with this material.
Rule(RuleCall)
Calls a named sub-rule on the current scope unchanged, optionally
passing call arguments (Tier(depth + 1)).
Used for grammar rule references that don’t transform the scope themselves.
Size([Expr; 3])
Sets the scope size to absolute world-unit values (CGA s() parity).
Components must be finite and non-negative; 0 flattens the axis
(face-scope semantics). Essential for sizing the zero-extent scopes
produced by Scatter and Comp(Edges).
Syntax: Size(2.1, 0.9, 0.12) — expressions welcome:
Size(scope.x, 0.3, 0.3).
Center
Re-centres the scope inside the axis-aligned bounds it occupied when
the current rule was entered, along the masked axes. The scope must
have been shrunk (e.g. by Size) for this to move anything.
Syntax: Center(X), Center(XY), Center(XYZ) …
Mirror
Mirrors the pending face profile horizontally (Triangle peak, Trapezoid offset, Polygon points). Scope geometry is untouched — terminals carry rotations, not reflections, so a scope-level mirror cannot exist in this engine. Apply after the profile is set.
Syntax: Mirror(X) (only X — profiles are 2-D, mirrored across
their vertical centre line).
ShapeL
Carves an L footprint: a front bar of depth front (along local Z
from the scope origin) plus a side leg of width side (along local X)
over the remaining depth. The Shape selector receives both boxes;
Remainder receives the cut-away rectangle.
Syntax: ShapeL(4, 3) { Shape: Wing | Remainder: Court }
ShapeU
Carves a U footprint: a front bar plus left and right legs; the remainder is the inner court between the legs behind the bar.
Syntax: ShapeU(4, 3, 3) { Shape: Range | Remainder: Court }
Align
Rotates the scope so that the specified local axis points in the given world direction.
Applies the shortest-arc rotation from the current world direction of local_axis
to target. Useful for recovering from accumulated rotations.
Syntax: Align(Y, Up), Align(Z, Forward), etc.
Named targets: Up=(0,1,0), Down=(0,-1,0), Right=(1,0,0), Left=(-1,0,0),
Forward=(0,0,-1), Back=(0,0,1).
Offset
Creates an inset (distance < 0) frame on a 2D face scope.
Produces up to two kinds of child scopes:
Inside: the inset rectangle.Border: four surrounding strips (bottom, top, left, right), each invoking the same rule.
Syntax: Offset(-0.2) { Inside: Glass | Border: Frame }
Roof
Generates a roof structure above the current scope using rich parametric configuration.
Operates on a volume scope. The config contains the roof type, primary pitch angle,
optional secondary pitch, overhang, ridge offset, fascia depth, and tier height.
Syntax examples:
Roof(Gable, 30) { Slope: Tiles | GableEnd: Bricks }— basic GableRoof(Hip, 30, 0.5) { Slope: Tiles }— Hip with overhangRoof(Gambrel, 45, 20) { LowerSlope: Shingles | UpperSlope: Tiles }— GambrelRoof(Saltbox, 45, offset=0.3) { Slope: Tiles | GableEnd: Bricks }— SaltboxRoof(DutchGable, 45, tier=0.7) { Slope: Tiles | GableEnd: Bricks }— Dutch Gable
RegSnap(String)
Registers all six face planes of the current scope as snap-planes
under the given label. Subsequent Split(snap="label") ops can align
their interior boundaries to these planes. Read-only with respect to
the scope (the scope itself passes through unchanged).
Syntax: RegSnap("bays")
IfClear
Conditionally invokes rule on the current scope only when no
already-emitted terminal occludes the scope (true OBB overlap test).
Useful for placing decorative elements that should only appear where
no structural element has already been placed.
The grammar author is responsible for ordering — terminals derived before this op participate in the test; later terminals do not.
Syntax: IfClear { Window } / IfClear("chimneys") { Window } —
the optional label restricts the test to terminals stamped with that
Label.
IfOccluded
Inverse of ShapeOp::IfClear: invokes rule only when the current
scope is occluded by an already-emitted terminal.
Syntax: IfOccluded { Patch } / IfOccluded("roof") { Patch }.
IfInside
Graded occlusion: invokes rule only when the scope is FULLY inside
a single already-emitted terminal (optionally of one label class).
Syntax: IfInside { Core } / IfInside("mass") { Core }
IfTouches
Graded occlusion: invokes rule only when the scope is in surface
contact with a terminal — overlapping at a hair’s growth but not at a
hair’s shrinkage (optionally restricted to one label class).
Syntax: IfTouches { Trim } / IfTouches("walls") { Trim }
Pick
Coordination key: a weighted choice resolved once per derivation —
every Pick with the same key picks the SAME index, wherever it
appears in the tree. The poor man’s CGA++ event: all floors agree on
one window variant, front and back facades match.
The choice is a pure function of (interpreter seed, key) — no
derivation-order dependence.
Syntax: Pick("winStyle") { 60% WinA | 40% WinB }
Fields
Label(String)
Stamps an occlusion label on subsequent terminals of this branch
(propagates like Mat). Labelled terminals form a named class the
occlusion conditionals can filter on.
Syntax: Label("chimneys")
Scatter
Scatters count zero-size point scopes uniformly over the scope’s
top face (Top) or through its volume (Volume), invoking rule on
each. Points are drawn from the shape’s RNG stream (seed-stable);
give them extent with Size(..). count is capped at 1024.
Syntax: Scatter(Top, 12) { Bush }
Polygon(Vec<DVec2>)
Stamps an explicit polygonal FaceProfile on the next terminal in this rule.
Mirrors how ShapeOp::Taper sets a profile override: the next I(...)
(or implicit terminal) emits a Terminal whose face_profile is
crate::model::FaceProfile::Polygon with the provided vertex list.
Vertices are 2-D points in normalized [0, 1]² scope coordinates
(X: 0 = left edge → 1 = right edge; Y: 0 = bottom → 1 = top of the
face); the renderer triangulates and stretches them across the
scope’s extent. They are NOT world units.
Syntax: Polygon((0,0), (4,0), (4,2), (2,2), (2,4), (0,4))
(variadic (x,y) list, capped at 256 vertices for parser DoS hardening).
Attach
Projects a new horizontal scope out of a sloped face for attaching dormers or details.
world_axis defines the “up” direction for the attached scope (usually world Y).
The resulting scope sits on the face’s surface with its Y axis aligned to world_axis,
inheriting the face’s width and height but with depth = 0.
Syntax: Attach(Up) { Surface: DormerMass }
Trait Implementations§
Source§impl<'de> Deserialize<'de> for ShapeOp
impl<'de> Deserialize<'de> for ShapeOp
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl StructuralPartialEq for ShapeOp
Auto Trait Implementations§
impl Freeze for ShapeOp
impl RefUnwindSafe for ShapeOp
impl Send for ShapeOp
impl Sync for ShapeOp
impl Unpin for ShapeOp
impl UnsafeUnpin for ShapeOp
impl UnwindSafe for ShapeOp
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more