# Monoceros 3 -- Complete LLM Reference Monoceros 3 is a discrete assembly plug-in for Grasshopper / Rhino. It fills a spatial Envelope with discrete Modules according to user-defined Rules using the Wave Function Collapse (WFC) algorithm. It provides an innovative and fast solution to the architectural problem of discrete aggregation for purposes of design, architecture and urban planning. This file is the entire Monoceros 3 manual as one Markdown document, generated from the Monoceros 3 source for LLM ingestion. Do not edit it by hand. # Documentation ## 1. Introduction ### 1.1 What is Monoceros [Monoceros](https://karkadann.fandom.com/wiki/Monoceros) is a legendary animal living in the huge mountains in the interior of India. It has the body of a horse, the head of a stag, the feet of an elephant and the tail of a boar. Monoceros is also a suite of tools for arranging discrete Modules in an Envelope according to user-defined connection Rules. Wave Function Collapse searches for arrangements that satisfy those encoded constraints. It does not optimise an architectural objective or establish performance beyond the supplied Rules. Monoceros is a plug-in for [Rhino](https://www.rhino3d.com) / [Grasshopper](https://www.grasshopper3d.com) originally created at studio [Subdigital](https://www.sub.digital) by [Ján Tóth](https://github.com/yanchith) and Ján Pernecký in 2021; Monoceros 3 is developed and maintained by [Ján Pernecký](https://www.linkedin.com/in/janper/). Monoceros 3 introduces a significant set of capabilities over the original release: - **Weighted Observation and weighted Entropy** - per-Module probability control lets the user bias the Solver toward or away from specific Modules in each Slot. - **Heterogeneous Slot dimensions** - cells of different X, Y, Z sizes can coexist in the same Envelope, enabling mixed-scale assemblies. - **Automatic Module Rotations** - Rotational Freedom on Construct Module lets Construct Assembly generate up to 24 permitted orientations per Module. Module Rotations generates explicit variants, with voxel-based deduplication. - **Voxel engine for Connector and Rule suggestion** - robust Face-matching even for complex or irregular geometry. - **Multi-solution output** - multiple solve results from different Random Seeds can be collected in a single pass. - **Parallel solving** - the first Attempt runs alone; further Attempts run in parallel on available CPU cores with incrementing Random Seeds when it is not fully solved or when Return first is False, provided Max Attempts is greater than 1. Results can be fully solved or partial. - **Multi-cell Modules** - give Construct Module several adjacent Cells and it occupies them all, placed as one rigid body, for design elements larger than a single Slot. - **Full Grasshopper integration** - all Monoceros types cast to and from standard Grasshopper types, allowing direct connection to any GH component. ### 1.2 What is Wave Function Collapse Wave Function Collapse (WFC) fills an entire spatial Envelope with Modules according to a set of adjacency Rules. The Envelope is divided into discrete, box-shaped Slots. Each Slot starts out with a list of all Modules that are allowed to occupy it. The algorithm progressively narrows those lists, Slot by Slot. In a complete result every Slot holds exactly one Module and every pair of neighboring Modules is permitted by the Rules. The WFC Solver propagates constraints across the Envelope and observes individual Slots. A complete result has exactly one Module candidate in every Slot and satisfies the encoded adjacency Rules. Monoceros 3 can also return a partial Assembly when an Attempt stops at its observation limit: unresolved Slots still contain multiple candidates. A contradictory Attempt has at least one Slot with no candidate. The Assemblies list contains non-contradictory complete or partial results; if every Attempt contradicts, it contains one contradictory Assembly. Inspect the Report, Deterministic and Contradictory outputs before materialising a result. A partial Assembly can be passed to another WFC Solver to continue solving. These statuses describe the encoded assembly, not circulation, accessibility, structural or fabrication compliance. Monoceros is a loose implementation of the Wave Function Collapse algorithm originally developed for game design by [Maxim Gumin](https://github.com/mxgmn/WaveFunctionCollapse) and extended and promoted by [Oskar Stålberg](https://oskarstalberg.com) with his game [Townscaper](https://store.steampowered.com/app/1291340/Townscaper/). For an accessible visual explanation see [Oskar Stålberg's EPC2018 talk](https://www.youtube.com/watch?v=0bcZb-SsnrA); for the theoretical background see [Constraint Satisfaction Problems](https://en.wikipedia.org/wiki/Constraint_satisfaction_problem) on Wikipedia. #### The algorithm WFC proceeds in four phases: 1. **Canonicalization** – Before the first Observation, the Solver runs an initial Propagation pass on the starting state. This removes any Module that is already impossible in a Slot given the initial allowed-Module lists and Rules, reducing every Slot to the tightest consistent set of possibilities. The result is the *Canonical* starting state. If any Slot reaches zero allowed Modules during Canonicalization, the setup is Contradictory and no solving is attempted. 1. **Observation** – The highest Slot Priority is selected first. Among Slots at that priority, the Slot with the lowest *Entropy* is selected, with random tie-breaking, and assigned a Module chosen according to its per-Slot Weights. A Slot that has only one remaining allowed Module becomes Deterministic without randomness. 1. **Propagation** – The newly fixed assignment is propagated outward through the grid. For each neighbor of the observed Slot, any Module that can no longer legally occupy that Slot - because the Rules no longer permit it next to the now-fixed neighbor - is removed from its allowed list. This removal can in turn force further removals in the neighbors’ neighbors, cascading recursively until the grid reaches a stable state with no further removals possible. 1. **Repeat** – Steps 2 and 3 alternate until either all Slots are Deterministic (success) or a Slot reaches zero allowed Modules (a *Contradictory* state). The first Attempt uses the given Random Seed on one thread. If it does not fully solve, or if Return first is False, further Attempts run in parallel with incrementing Seeds, up to Max Attempts. ### 1.3 What are the design principles of Monoceros 3? Monoceros 3 is not a sequential update of Monoceros 1 but a redesigned tool built on three core principles: - **Less Monoceros, more Grasshopper** - what can be done with vanilla Grasshopper should be. Monoceros data types connect directly to standard Grasshopper components and other plug-ins. - **The tool is not the workflow** - no design strategy is enforced by the tool. Monoceros components combine freely with any Grasshopper workflow. - **Explicit by default** - every decision is visible in the Grasshopper canvas. These principles caused substantial changes from Monoceros 1. Monoceros 3 data types and components are **not compatible** with Monoceros 1. ### 1.4 How do I migrate from Monoceros 1? Monoceros 3 is a substantial redesign, not a feature update. Data types and components are not compatible with Monoceros 1. The most impactful improvements for daily workflow are: - **Weighted Observation** - each Module in each Slot can carry its own Weight. Higher Weight increases the probability of being chosen during Observation, enabling gradient effects, frequency control, and partial pre-determination - none of which were possible in Monoceros 1. - **Heterogeneous Slot dimensions** - the Envelope can mix cells of different sizes in a single solve. Monoceros 1 required all cells to be identical. Mixed-scale assemblies are now straightforward. - **Module Rotations component** - Rotational Freedom on Construct Module lets Construct Assembly expand permitted variants; Module Rotations generates up to 24 explicit variants for a fully rotatable Module, with voxel-based deduplication. In Monoceros 1, every rotated variant had to be defined by hand. - **Multi-solution parallel solving** - set Return first to False to run further Attempts in parallel and receive an Assembly for each non-contradictory result on the Assemblies list output. Results can be fully solved or partial; if all Attempts contradict, the output contains one contradictory Assembly. - **Voxel engine for Rule suggestion** - Detect Rules from Voxels matches Face layers by voxel pattern rather than raw geometry, including complex or irregular Module Faces. - **Full Grasshopper type integration** - all Monoceros types cast to and from standard Grasshopper geometry. A Slot casts to a Cell, Box, Brep, Point, or Plane. Monoceros components connect directly to standard GH tools without manual conversion steps. - **Indifference as an Assembly-level setting** - the Indifference toggle on Construct Assembly replaces the Indifferent Rule from Monoceros 1. The effect is identical but the setup is cleaner and entirely explicit. ## 2. How is Monoceros structured? ### 2.1 What is the Monoceros workflow? A typical Monoceros 3 workflow follows these steps: 1. **Define a Grid** - Use the Homogeneous Grid or Heterogeneous Grid component to create Cells. A Cell carries only geometry - its size and position. A collection of Cells forms a grid. It is important to understand that Cells and Slots can be placed freely in world space; nothing enforces that they form a valid grid. Using the grid construction components guarantees a correct arrangement; any manually assembled or post-processed set of Cells must be carefully verified. 1. **Construct Modules** - Define each Module with one or more face-connected Cells and optional geometry. Module Name may be left empty for a generated name. Set Rotational Freedom on Construct Module to allow rotation variants in Construct Assembly; Module Rotations generates explicit variants. 1. **Analyze Faces** - Extract and inspect the six Faces of each Module. Use the Detect Rules from Geometry or Detect Rules from Voxels components to identify matching Face pairs. 1. **Define Rules** - Create Rules specifying which Face pairs are allowed to touch. Each Rule connects a source Face to a target Face facing the opposite Direction. 1. **Construct Slots (Envelope)** - Create Slots from Cells, optionally specifying which Modules are allowed in each Slot and their relative Weights. This step converts the Cells into an Envelope: Cells carry only geometry; Slots additionally carry the list of allowed Module candidates and per-Module Weights needed for solving. If the Allowed Module Names input is not connected, the Slot is created as allow-all and Construct Assembly resolves it to the authored Module Names whose Cells fit that Slot's Cell size. 1. **Add Boundary** - Optionally add boundary Cells and Slots, or use Require Terminators and Terminators on Construct Assembly to restrict which Modules may fill exposed Slot Faces on non-flat axes without adding Cells. 1. **Construct Assembly** - Feed Modules, Slots, optional Slot Priorities, allowed, disallowed and exclusive Rules and Connector Pairs, Connectors, and optional Terminators into Construct Assembly. Set Indifference, Clean up, Remove Unused Connectors and Require Terminators as needed. The component expands rotations, applies the constraints, audits, and packages a Discrete Assembly. 1. **Solve** - Connect the Assembly to the WFC Solver. Assembly is the sole design-data input. The Assemblies list output can contain fully solved or partial Assemblies; if all Attempts contradict, it contains one contradictory Assembly. To place Modules step by step instead, connect the Assembly to the Growth Solver; it returns one grown Assembly. 1. **Materialize** - Connect the solved Assembly to the Materialize Assembly component to extract placed geometry. ### 2.2 Where are the Monoceros components in Grasshopper? All Monoceros 3 components live in the **Monoceros 3** tab in the Grasshopper ribbon. They are organized into seven subcategories: - **Main** - Construct Assembly, Deconstruct Assembly, Dissolve Assembly, WFC Solver, Growth Solver, Materialize Assembly, Audit Assembly, Sample Geometry - **Connector** - Connector construction, identification, pairing, suggestion, preview, and Terminators - **Envelope** - Grid creation, boundary, topology, geometry slicing - **Module** - Module construction (single- and multi-cell), deconstruction, rotations, deduplication - **Face** - Face extraction, analysis, comparison, grouping, preview - **Rule** - Rule construction, deconstruction, detection, preview - **Slot** - Slot construction, deconstruction, change and occurrence counting, pattern finding, preview, materialization ## 3. What data types does Monoceros use? Monoceros 3 defines custom Grasshopper data types for all elements used in a WFC workflow. All Monoceros types can be passed through standard Grasshopper wires. Several support viewport preview and baking. ### 3.1 Grid #### 3.1.1 Cell A **Cell** is the basic spatial cell unit of a Monoceros grid - a box-shaped region that defines the size and position of one cell. Multiple Cells form a grid; when converted to Slots via the Construct Slot component, they form an Envelope. Cells are created by Homogeneous Grid, Heterogeneous Grid, Cells from Geometry, or Add Boundary Layer. They can also be cast from a Box, BoundingBox, Rectangle, box-shaped Brep, Module, or Slot. A Cell exposes its Box geometry, from which center, dimensions, and Orientation can be derived. #### Validity A Cell is valid when its Box geometry is non-degenerate - all three dimensions greater than zero. Invalid Cells are rejected by all Monoceros components with a warning. #### Viewport preview Draws a slightly shrunken wireframe box with short colored lines pointing outward from the centers of the `+X`, `+Y`, and `+Z` faces. Degenerate Cells draw in red. #### Casting | From / To | Type | Notes | | --- | --- | --- | | Cast from | `Box` | A Rhino Box is wrapped directly into a Cell. | | Cast from | `BoundingBox` | A BoundingBox is wrapped directly into a Cell. | | Cast from | `Rectangle` | Forms a box centered on the rectangle plane, with thickness equal to the smaller side length. | | Cast from | `Brep` | Accepts only a box-shaped Brep whose volume matches its bounding box or an oriented box formed by eight vertices. | | Cast from | `Module` | Returns its anchor Cell. | | Cast from | `Slot` | Returns its Cell. | | Cast to | `Box` | Returns the Cell as Rhino Box geometry. | | Cast to | `Brep` | Returns the Cell as a closed Brep surface. | | Cast to | `Point` | Returns the center point of the Cell. | | Cast to | `Plane` | Returns the center plane of the Cell with its Orientation. | | Cast to | `Vector` | Returns the diagonal vector (X, Y, Z dimensions) of the Cell. | #### String representation Displays `Cell: Center {x,y,z}, Dimensions {x,y,z}, Axes: X {..}, Y {..}, Z {..}`. A degenerate Cell reports `the box is degenerate`. #### Baking Cells do not bake directly. Cast a Cell to Box or Brep to bake its geometry. ### 3.2 Module #### 3.2.1 Module A **Module** is the fundamental design element in Monoceros 3. It carries a name, optional geometry, and one or more face-connected Cells. The first Cell is its anchor. Each Cell has six Faces; only external Faces take Rules or Connectors. A single-cell Module addresses them as `+X` through `-Z`; a multi-cell Module adds a cell ordinal, such as `+X0`. These Faces determine which neighboring Modules may legally be placed adjacent to it. During WFC solving, each Slot starts with the full list of allowed Modules as candidates. The Solver progressively eliminates candidates - removing any Module from a Slot when its presence would violate a Rule with a neighboring Slot - until each Slot holds exactly one Module (Deterministic) or none (Contradictory). Selection is probabilistic and guided by the per-Module Weights stored in each Slot. The Module’s geometry does not need to fit exactly inside the Cell - it can extend beyond or remain smaller. When geometry fits tightly and aligns with the Faces, Faces can be suggested automatically using the Detect Rules from Geometry or Detect Rules from Voxels components. #### Key properties - **Module Name** - A unique, lowercase string. Names are trimmed and lowercased automatically on input. Construct Module generates a name when this input is empty. Only Module Rotations variants of one source Module may share a name; other repeated names are an Error. - **Cells** - One or more face-connected Cells. The first is the anchor Cell; Deconstruct Module also reports the Footprint and Cell Count. - **Geometry** - Optional Rhino geometry (curves, surfaces, Breps, meshes). Can exceed the bounding box. - **Faces** - Six per Cell, with external Faces addressable by Direction and, for multi-cell Modules, cell ordinal. Faces without a Rule are treated as Indifferent when indifference is enabled on Construct Assembly. - **Rotational Freedom** - None, X only, Y only, Z only, or Full. Construct Assembly expands permitted variants; Dissolve Assembly returns the solver-level Modules including those variants. #### Viewport preview The Module preview draws: - The Module **geometry** (wireframe for curves; shaded for Breps and Meshes), mirroring Rhino’s own display logic: shaded-style display modes show the source objects’ original display colors, and rendered-style modes show their assigned render materials including bitmap and transparency textures. Wireframe-style display modes draw only the edges, never shaded geometry. Selection highlighting (green for selected, red for invalid) is drawn on the wireframe edges only, so the realistic surface colors stay visible while selecting. - A semi-transparent teal Cell outline when valid, red when invalid. Multi-cell Modules draw each footprint edge once, solid on the outside and dotted at inner seams. - An outward arrow and address at each external Face, colored by Axis: X = red, Y = green, Z = blue. Face address text appears when its Face spans at least 450 pixels on screen. - Arc arrows around each allowed rotation axis and the Module Name as 3D text on three orthogonal planes. The name is teal when valid and red when invalid, and appears when the footprint spans at least 450 pixels on screen. #### Validity A Module is valid when its name, pivot plane, footprint, and six Faces per Cell are valid and its Cells form one face-connected block. Corner or edge contact alone is insufficient. Modules with no geometry participate in solving but produce no materialized geometry. Its string representation starts `Module "name" with 2 geometries in Cell: Center ...` for a Module with two geometry items; it includes a rotation label when set and an invalidity reason when invalid. #### Casting | From / To | Type | Notes | | --- | --- | --- | | Cast from | (none) | Use the Construct Module component. | | Cast to | `Module Name` | Extracts the Module's name as a Module Name value. | | Cast to | `Cell` | Extracts the anchor Cell of a valid Module. | | Cast to | `Point` | Returns the center point of the anchor Cell. | | Cast to | `Plane` | Returns the center plane of the anchor Cell with its Orientation. | | Cast to | `Vector` | Returns the diagonal vector (X, Y, Z) of the anchor Cell. | #### Baking Modules with geometry bake as one block instance at the identity transform, retaining captured per-object attributes. A Module without geometry bakes nothing. #### 3.2.2 Module Name A **Module Name** is a lowercase string identifier passed to the WFC Solver to represent a Module type. Input strings are converted to lowercase automatically. Module Names identify authored Modules. Construct Assembly rejects separately authored Modules with the same name, even when their Cells have different dimensions. Only rotation variants of one source Module from Module Rotations may share a name; Construct Assembly merges them into one Module with those orientations. In a Heterogeneous Grid, give Modules with different Cell sizes distinct names and define their Rules separately. **Rules apply to rotation variants.** A Rule defined for a Module Name is expanded to its allowed rotations during Construct Assembly. **Naming Modules of different sizes.** Modules of different Cell sizes need distinct names, even when they have the same connection Rules. Define Rules for each named Module. See [example 2.11 - Naming Modules of different sizes](workflows.html#naming-modules-of-different-sizes) for a detailed worked example. #### Validity A Module Name is valid when it is non-empty and free of the reserved characters `:` (colon), `->` (arrow), `@`, `#`, and newlines. Spaces are allowed. #### Casting | From / To | Type | Notes | | --- | --- | --- | | Cast from | `String` | The string is trimmed and lowercased before use as the Module Name. | | Cast from | `Module` | Extracts the Module's name. | | Cast from | `Integer` | Prefixes the integer with `module-`, for example `module-3`. | | Cast from | `Number` | Prefixes the number with `module-`. | | Cast from | `Data Path` | Prefixes the path with `module-`, for example `{0;1}` becomes `module-0-1`. | | Cast to | `String` | Returns the name string. | #### String representation Returns the name string, for example: `wall` or `corner-piece`. #### Baking Module Names are string values and do not bake directly. ### 3.3 Face #### 3.3.1 Face Index A **Face Index** identifies one of six Face Directions, numbered 0–5. Each index maps to a named Direction combining an Axis (X, Y, or Z) and an Orientation (positive or negative). Opposite Face indices differ by 3 (`+X` 0 ↔ `-X` 3, etc.). Face Indices appear on the Audit Assembly outputs Terminator Faces Covered and Terminator Faces Missing, and as the Face part of Face, Rule, and Connector strings. Get Module Faces and Construct Rules from Faces use Faces. | Index | Name | From / To | Opposite | | --- | --- | --- | --- | | `0` | `+X` | X Positive | `-X` (`3`) | | `1` | `+Y` | Y Positive | `-Y` (`4`) | | `2` | `+Z` | Z Positive | `-Z` (`5`) | | `3` | `-X` | X Negative | `+X` (`0`) | | `4` | `-Y` | Y Negative | `+Y` (`1`) | | `5` | `-Z` | Z Negative | `+Z` (`2`) | #### Casting | From / To | Type | Notes | | --- | --- | --- | | Cast from | `Integer` | Integers 0–5 map directly to Faces. | | Cast from | `Number` | Numbers are truncated to integers 0–5. | | Cast from | `String` | Accepts numeric (`"0"`), named (`"+X"`, `"-Y"`), unsigned or lowercase axis letters, and a cell ordinal such as `"+X12"`. | | Cast to | `Integer` | Returns the numeric index (0–5). | | Cast to | `Number` | Returns the numeric index. | | Cast to | `String` | Returns the named Direction, with a cell ordinal when present. | #### Validity Valid indices are integers 0 through 5. Named strings may include a cell ordinal for multi-cell Modules; `X` and `y` mean positive X and Y. #### Baking Face Indices do not bake directly. #### 3.3.2 Face (UID) A **Face** uniquely identifies a specific Face on a specific Module. It combines a [Module Name](#type-modulename) and a [Face Index](#type-faceindex). #### Properties - **Module Name** - The name of the Module this Face belongs to. - **Face Index** - The Face Direction as an integer (0-5) or named form (`+X`…`-Z`), with a cell ordinal on multi-cell Modules. #### String representation Format: `modulename:+X`, or `modulename:+X0` for a multi-cell Module. A Module supplied to a Face or Module input represents all its Faces as the wildcard `modulename:*`. Both numeric and named indices are accepted. Examples: ``` wall:+X corner:3 ``` In Rules the format is: ``` wall:+X -> corner:-X ``` #### Validity A FaceId is valid when its Module Name is non-empty, contains no reserved characters, and its Face Index is in the range 0–5. #### Casting | From / To | Type | Notes | | --- | --- | --- | | Cast from | `String` | Parses the `"modulename:faceindex"` format. | | Cast from | `Module` | Represents all Faces of that Module as a wildcard, expanded by a Face or Module input. | | Cast to | `String` | Returns the FaceId string, e.g. `wall:+X`. | #### Baking Face UIDs do not bake directly. Use the Preview Faces component to visualize them in the viewport. ### 3.4 Slot #### 3.4.1 Slot A **Slot** represents one cell in the Envelope. It holds the list of Module Names currently allowed to occupy it, along with a per-Module Weight for each candidate. The WFC Solver progressively reduces this list until each Slot holds exactly one Module (*Deterministic*) or none (*Contradictory*). Before solving, most Slots allow multiple Modules and are therefore *Non-deterministic*. #### Key properties - **Box** - The cell bounding box (same geometry as the Cell it was created from). - **Allowed Module Names** - The list of Module Names still allowed in this Slot. - **Module Weights** - One Number per allowed Module, stored only on the Slot. Higher Weight increases the probability of selection. Missing Weights default to `1.0`; a shorter list repeats its last value. A Weight of zero or less removes that Module from the Slot. A Weight covers all rotated variants of the Module. - **Total Modules Count** - Set by Construct Assembly to the number of unique Module Names across the Envelope. Before assembly construction it is zero, and the preview shows the allowed count with the white end of the gradient. - **Allows All** - True when the Slot was constructed without explicit Module Names (empty allowed list and zero Total Modules Count). An allow-all Slot is neither Deterministic nor Contradictory; Construct Assembly resolves it to the authored Module Names whose Cells fit the Slot's Cell size. #### States The Solver aims to make every Slot Deterministic. Slots can also become Deterministic before solving when their allowed list is restricted manually - for example by constructing a Slot with only one allowed Module Name. | State | Allowed count | Preview color | Meaning | | --- | --- | --- | --- | | Contradictory | 0 | Red | No Module fits - this solve Attempt failed. The Solver will retry with a different Seed. | | Deterministic | 1 | Green | Exactly one Module is assigned. The Slot is solved and can be materialized. | | Non-deterministic | 2+ | Gradient from black (2 allowed) to white (all allowed) | Multiple Modules are still possible - not yet solved. | | Allow-all | 0 (empty list, zero Total) | White | Unresolved: created without explicit Module Names. Construct Assembly resolves it to the authored Module Names whose Cells fit the Slot's Cell size. Previews with the label `All`. | #### Viewport preview Draws a colored wireframe box. The wireframe is intentionally drawn slightly smaller than the actual Cell. Adjacent Slots share Faces, so if the Cells were drawn full-size their edges would coincide and overlap, making individual cells impossible to distinguish in the viewport. Shrinking each box inward by a small factor ensures there is always a visual gap between neighbors, even at zero distance. The actual solving geometry uses the full Cell. The occupancy label is drawn on three faces (XY, XZ, YZ) once the Slot spans at least 450 pixels on screen. It shows the Module Name for a Deterministic Slot, `(-)` for a Contradictory Slot, `All` for allow-all, `N / Total` after Construct Assembly, or `N` before it. Colored direction lines start at the positive X, Y, and Z face centers. **Weight bar chart.** When a Slot carries non-uniform Module Weights and is zoomed in close enough to span 750 pixels on screen (a single Slot dominating the view), a bar chart is drawn above the occupancy label on the top (XY) face: one narrow wireframe rectangle per allowed Module, in allowed-Modules order, captioned with the Weight (two decimals) and the Module Name written vertically beside the bar. Bar heights are normalized so the largest Weight spans half the face; a dotted ceiling line marks the normalization maximum. Captions shrink with the Module count and are skipped when unreadable, so very high Module counts drop captions automatically. Slots with uniform (default) Weights draw no chart. #### String representation Displays state and Cell geometry, for example: `Slot allows 3 Modules in Cell: Center ...`, `Slot allows Module "wall" in Cell: Center ...`, `Slot allows no Modules in Cell: Center ...`, or `Slot [All allowed] at Cell: Center ...`. Each ends with a period; invalid Slots are prefixed by the invalidity reason. #### Casting Slots automatically cast to Cell when passed to components that accept Cells (such as Add Boundary Layer). This means solved Slots can be used directly wherever Cells are expected. | From / To | Type | Notes | | --- | --- | --- | | Cast from | (none) | Use the Construct Slot component. | | Cast to | `Cell` | Returns the Slot's Cell, enabling use wherever Cells are expected. | | Cast to | `Box` | Returns the Slot's cell as Rhino Box geometry. | | Cast to | `Brep` | Returns the Slot's cell as a closed Brep surface. | | Cast to | `Point` | Returns the center point of the Slot's cell. | | Cast to | `Plane` | Returns the center plane of the Slot's cell. | | Cast to | `Vector` | Returns the diagonal vector (X, Y, Z) of the Slot's cell. | #### Baking Slots bake as colored boxes reflecting their state. ### 3.5 Rule #### 3.5.1 Rule A **Rule** defines an allowed adjacency between two Module Faces. It holds two FaceIds - a source Face and a target Face - which typically Face opposite Directions (e.g. source Faces `+X`, target Faces `-X`). A non-opposing Rule (e.g. `a:+X → b:+Y`) is also accepted: it is a connector-symmetry hint that Construct Assembly expands into opposing-face Rules referencing rotation variants of the target Module (the Solver itself only ever consumes opposing-face Rules). Rules are bidirectional for equality: `wall:+X → corner:-X` is equivalent to `corner:-X → wall:+X`, so each adjacency only needs to be defined once. #### Validity Both FaceIds must be individually valid. The source and target Directions do not have to be opposite - a non-opposing pair is a valid connector-symmetry hint (see above). For full validation, the referenced Module Names must exist in the provided Module list. #### String representation Format: `modulea:+X -> moduleb:-X` #### Casting | From / To | Type | Notes | | --- | --- | --- | | Cast from | `String` | Parses `"module:index -> module:index"`; if one Face Index is omitted, it uses the opposite of the given index. | | Cast to | `String` | Returns the Rule string, which can be parsed back into a Rule. | #### Baking Rules do not bake directly. Use the Preview Rule component to visualize them in the viewport. ### 3.6 Connector #### 3.6.1 Connector A **Connector** is a named, rotation-aware interface that binds a type identity to a specific Module Face. Connectors emulate physical Connectors (USB-A, USB-C, 3.5 mm jack): two Faces can connect only when they carry compatible Connectors. Each Connector combines a name, four symmetry flags, a Module Name, a Face Direction, and an in-plane rotation into a single object. There is no separate type-definition step - all Connectors sharing the same name are implicitly the same type. The name carries the type identity. The four symmetry flags define which of the four in-plane rotations (0°, 90°, 180°, 270°) are self-identical. When all four flags are true, the Connector is rotationally invariant (like a round jack). When only some flags are true, the Connector distinguishes certain rotations from each other, which affects how Rules are generated for rotated Module variants. #### Key properties - **Name** - A lowercase string identifying the Connector type. All Connectors with the same name are the same type. Must not contain `:`, `->`, `@`, `#`, or a newline. Spaces are allowed. - **Symmetry 0°** - Self-identical at 0° rotation. Default: `true`. - **Symmetry 90°** - Self-identical at 90° rotation. Default: `false`. - **Symmetry 180°** - Self-identical at 180° rotation. Default: `false`. - **Symmetry 270°** - Self-identical at 270° rotation. Default: `false`. - **Module Name** - The name of the Module this Connector is placed on. - **Face Direction** - The Face Direction as a Face Index (`+X`…`-Z`). - **Rotation** - In-plane rotation on the Face: 0, 90, 180, or 270 degrees. #### String representation Format: `connectorName#rot-A@moduleName:faceIndex`. The rotation segment is optional on input and defaults to zero; the Face Index may include a cell ordinal, such as `wall:+X0`. A String input sets all four symmetry flags to true. Example: `usba#rot-0@wall:+X`. #### Validity A Connector is valid when its name is non-empty, contains no reserved characters (`:`, `->`, `@`, `#`), at least one symmetry flag is true, the Module Name is valid, the Face Direction is valid, and the rotation is one of 0, 90, 180, or 270. #### Casting | From / To | Type | Notes | | --- | --- | --- | | Cast from | `String` | Parses `"connectorName#rot-A@moduleName:faceIndex"`, with an optional rotation segment. | | Cast to | `String` | Returns the Connector string, which can be parsed back into a Connector. | #### Preview & Baking The Connector data type does not support viewport preview or baking on its own. Construct Connector, Connector from Point, Detect Connectors from Voxels, Match Connectors by Geometry, Match Connectors by Voxels, and Preview Connector draw Connector stickers on Module Faces. Preview Connector can also draw compatible-face curves from Connector Pairs and bake its preview. #### 3.6.2 Connector Pair A **Connector Pair** declares that two Connector types can connect across opposing Faces. A Connector Pair stores two name strings (not references to Connector objects). Compatibility is bidirectional: declaring `A → B` also allows `B → A`. #### String representation Format: `sourcename -> targetname`. Example: `usba -> usba`. #### Validity A Connector Pair is valid when both its source name and target name are non-empty and contain no reserved characters. #### Casting | From / To | Type | Notes | | --- | --- | --- | | Cast from | `String` | Parses the `"source -> target"` format. | | Cast to | (none) | | #### Baking Connector Pairs do not bake directly. #### 3.6.3 Connector Name A **Connector Name** is a lowercase string that identifies a Connector type. All Connectors sharing the same name are the same type. Input strings are converted to lowercase and trimmed automatically. #### Validity A Connector Name is valid when it is non-empty and free of the reserved characters `:`, `->`, `@`, `#`, and newlines. Spaces are allowed. #### Casting | From / To | Type | Notes | | --- | --- | --- | | Cast from | `String` | The string is lowercased and trimmed. Fails if the result contains reserved characters. | | Cast from | `Connector` | Extracts the Connector's name. | | Cast to | `String` | Returns the name string. | #### String representation Returns the name string, for example: `usba` or `rj45`. #### Baking Connector Names are string values and do not bake directly. ### 3.7 Assembly #### 3.7.1 Assembly A **Discrete Assembly** (or simply **Assembly**) contains authored Modules, Slots, Connectors, allowed, disallowed and exclusive Rules and Connector Pairs, Terminators, Indifference and Require Terminators settings, and an Audit report. Construct Assembly expands rotation variants, generates Connector Rules, removes disallowed Rules, enforces exclusive Rules and Connector Pairs, applies Indifference, and restricts which Modules may fill exposed Slot Faces on non-flat axes when Require Terminators is enabled, without adding Cells. The Solver requires an Assembly as its design-data input. Construct Assembly has already expanded rotations and applied Indifference. The Solver returns an Assemblies list with fully solved or partial results; if all Attempts contradict, it returns one contradictory Assembly. #### Key properties - **Modules** - Expanded, deduplicated list of all Modules including rotation variants. - **Slots** - Current Slot states (pre-solve or post-solve). - **Rules** - Complete merged Rule set (explicit, Connector-generated, rotation-expanded, and optionally Indifferent). - **Audit Result** - Comprehensive validation report including grid validity, uncovered Faces, unknown Modules, over-constraint detection, and more. #### String representation Format: `Assembly (N modules, M slots, K rules)`. #### Validity An Assembly is invalid if its build is incomplete or if Module Faces have neither Rules nor Indifference Rules. Construct Assembly reports build problems; Audit Assembly identifies uncovered Faces. #### Casting | From / To | Type | Notes | | --- | --- | --- | | Cast from | (none) | Use the Construct Assembly component or the Solver's Assemblies output. | | Cast to | (none) | | #### Baking Assemblies preview Rule adjacency curves, authored Module geometry and Slots, Terminator badges on terminated Faces and authored Slots referencing terminated Modules, and red X badges on authored Faces without a Rule or Connector. Assemblies do not bake directly; Construct Assembly can bake its Rule preview curves. Deconstruct Assembly returns authored inputs, while Dissolve Assembly returns solver-level Modules including rotation variants, Slots, Explicit Rules and Indifferent Rules. Connect a solved Assembly to Materialize Assembly to produce geometry. ## 4. What Grasshopper components does Monoceros provide? ### 4.1 Connector #### 4.1.1 Connector from Point *Connector · Nickname: `ConnFromPt` · GUID: `6CC1F83E-9B3D-4C6D-9A1A-6F47E5E0B1A4`* Create Connectors by pointing at a Module Face in the viewport. Combines Face-from-Point lookup with Connector construction: looks up every Module Face whose geometry contains the provided point and produces one Connector per match. Useful when authoring Connectors by clicking on Module Faces directly in the Rhino viewport. 8 inputs total. #### Behavior Resolves the Point Tag into one or more Module Faces using the same point-in-Face matching logic as [Faces from Point](#comp-facepoint), then constructs a Connector for each matching Face using the same name, rotation and symmetry inputs as [Construct Connector](#comp-connectorconstruct). Connector Name is optional; when it is not wired, a deterministic name with the prefix `c-` and four letters is generated and reported in a Remark. Only external Faces are matched; on a multi-cell Module each matching Face is addressed to its own Cell. Zero matches raise a Warning (*“Point does not match any Module Face.”*); multiple matches emit an informational Remark listing how many Connectors were created. An unmatched Point Tag is also marked directly in the viewport with a red circle-and-cross badge laid flat in the world XY plane at the point, sized from the average provided Module size. Viewport preview draws the Connector sticker on every matched Face, identically to Construct Connector. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Connector Name | `CN` | Connector Name | Item | Connector type name. Cast from string. If not provided, a deterministic name is auto-generated from the component instance. (Optional) | | Modules | `M` | Module | List | All available Modules to search for a Face hit. Only external faces participate in point matching. Provide a flattened list. | | Point Tag | `Pt` | Point | Item | Point marking a location on a Module face. The component returns one Connector per Module Face whose geometry contains the point. | | Face Rotation | `R` | Integer | Item | In-plane rotation of the connector. 0 = 0°, 1 = 90°, 2 = 180°, 3 = 270°. Default: 0. | | Symmetry 0° | `S0` | Boolean | Item | Self-identical at 0° rotation. Default: true. | | Symmetry 90° | `S90` | Boolean | Item | Self-identical at 90° rotation. Default: false. | | Symmetry 180° | `S180` | Boolean | Item | Self-identical at 180° rotation. Default: false. | | Symmetry 270° | `S270` | Boolean | Item | Self-identical at 270° rotation. Default: false. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Connectors | `C` | Connector | List | One Connector per matched Face. If the Point marks multiple Faces (e.g. overlapping Modules), each match produces its own Connector. | #### 4.1.2 Construct Connector *Connector · Nickname: `ConstructConn` · GUID: `C2101907-7551-4681-AD51-90E48A999FFF`* Create Monoceros 3 Connectors from Faces - named, rotation-aware interfaces placed on specific Module Faces. Combines type identity (name + symmetry) and placement (FaceId + rotation) in a single object. All Connectors sharing the same name are implicitly the same type. A Module wired directly into the Face input is cast to a short-lived wildcard FaceId that the component expands into the Module's six explicit Faces, producing one Connector per Face. Output is flattened. 8 inputs total. For point-based placement, use [Connector from Point](#comp-connectorfrompoint). #### Behavior Connector Name is optional. When it is not wired, a deterministic name with the prefix `c-` and four letters is generated per component instance and reported in a Remark; Construct Connector, Connector from Point and Detect Connectors from Voxels share this name namespace. A wired name must not be empty or contain a line break, `:`, `->`, `@` or `#`; names are lowercased. Validates the symmetry flags (at least one must be true), Module Name, Face Direction and Face Rotation. Face Rotation accepts `0`-`3` or `90`, `180` and `270` degrees; other values produce an Error. Symmetry 0 degrees defaults to true, while Symmetry 90, 180 and 270 degrees default to false. Creation failures are collected into one Error. A Connector with all four symmetry flags set to true behaves identically at every rotation - Rules generated for it do not distinguish Orientation. A Connector with selective flags (e.g. only 0° and 180°) produces different Rules for different in-plane orientations, enabling Direction-sensitive connections. When the optional Modules input is connected, the component draws a viewport preview showing the Connector as a label sticker on the target Face - displaying the Connector name and symmetry arrows in the Connector’s type color, with degree labels (0°, 90°, 180°, 270°) indicating the in-plane rotation. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Connector Name | `CN` | Connector Name | Item | Connector type name. Cast from string. If not provided, a deterministic name is auto-generated from the component instance. (Optional) | | Face or Module | `F` | Face ID | List | Face where the Connector is placed, obtained from the Get Module Faces component. A Module may be wired here instead, in which case it is expanded into all six of its faces and one Connector is produced per face. | | Face Rotation | `R` | Integer | Item | In-plane current rotation of the connector. 0 = 0°, 1 = 90°, 2 = 180°, 3 = 270°. Default: 0. | | Symmetry 0° | `S0` | Boolean | Item | Self-identical at 0° rotation. Default: true. | | Symmetry 90° | `S90` | Boolean | Item | Self-identical at 90° rotation. Default: false. | | Symmetry 180° | `S180` | Boolean | Item | Self-identical at 180° rotation. Default: false. | | Symmetry 270° | `S270` | Boolean | Item | Self-identical at 270° rotation. Default: false. | | Modules | `M` | Module | List | Modules (optional). Modules for viewport preview. When connected, the connector is displayed on the target face. Provide a flattened list. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Connector | `C` | Connector | List | The constructed Connectors. | #### 4.1.3 Construct Connector Pair *Connector · Nickname: `ConnPair` · GUID: `428C09D4-02A1-4572-8D6B-832CA4C8CBFC`* Declare which Connector types can connect across opposing Faces. Takes lists of Source Connector Names and Target Connector Names and produces a list of Connector Pairs. Compatibility is bidirectional - declaring A → B also allows B → A. #### Behavior Pairs every unique Source Connector Name with every unique Target Connector Name and outputs a flat list of Connector Pairs without duplicates. A Connector or text wired into either input is cast to its Connector Name. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Source Connector Names | `SCN` | Connector Name | List | Connector names defining the source side of each pair. | | Target Connector Names | `TCN` | Connector Name | List | Connector names defining the target side of each pair. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Connector Pairs | `CP` | Connector Pair | List | All cross-matched connector pairs, deduplicated. | #### 4.1.4 Deconstruct Connector *Connector · Nickname: `DeconConn` · GUID: `0C8539B2-6AAC-43D9-9415-5CF3706AAC90`* Deconstruct a Connector into its constituent parts: Connector Name, Rotation in degrees (`0`, `90`, `180` or `270`), Module Name, and Face including any Cell ordinal. The inverse of [Construct Connector](#comp-connectorconstruct). **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Connector | `C` | Connector | Item | The Connector to deconstruct. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Connector Name | `CN` | Connector Name | Item | The connector type name. | | Rotation | `R` | Integer | Item | The in-plane rotation in degrees (0, 90, 180, or 270). | | Module Name | `MN` | Module Name | Item | The name of the module this connector is placed on. | | Face | `F` | Face ID | Item | The face identifier (module name and face index). | #### 4.1.5 Construct Terminator *Connector · Nickname: `ConstructTerm` · GUID: `3DDD7344-7DAB-44E8-8301-7FAF6D1F135B`* Create Terminators — boundary-facing markers placed on Module Faces. A Terminator declares that a specific Module Face is allowed to sit against the Envelope boundary when Require Terminators is enabled on Construct Assembly. For point-based placement, use [Terminator from Point](#comp-terminatorfrompoint). #### Behavior The Face or Module input accepts either a Face (from the Get Module Faces component) or a whole Module. When a Module is wired, the component expands it into all six of its Faces and produces one Terminator per Face; a Remark reports how many were produced. This is the fastest way to mark an entire Module as fully boundary-compatible. Terminators do nothing unless Require Terminators is enabled on Construct Assembly; supplying them when it is off produces a Warning. When enabled, Construct Assembly caps exposed Slot Faces on non-flat axes: each needs a Module with a Terminator on the matching Face. The front and back Faces of a one-cell-thick Envelope need no Terminators. If no allowed Module can cap a required Face, Construct Assembly reports a build problem and marks the Assembly invalid, so the WFC Solver refuses it. No extra boundary cells are added — the grid stays exactly the cells you authored. The optional **Modules** input provides Modules for viewport preview. When connected, a terminator badge — a smooth circle with an inner × — is drawn on each target Face in its Axis colour. #### String representation Format: `Terminator@moduleName:faceIndex`. Example: `Terminator@pipe:+X`. A Terminator that targets one Cell of a multi-cell Module spells that Cell's ordinal after the face, as `Terminator@pipe:+X1`. The unnumbered form means the whole side: every Cell of the Module that exposes that direction. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Face or Module | `F` | Face ID | List | Face where the Terminator is placed, obtained from the Get Module Faces component. A Module may be wired here instead, in which case it is expanded into all six of its faces and one Terminator is produced per face. | | Modules | `M` | Module | List | Modules (optional). Modules for viewport preview. When connected, the terminator badge is displayed on the target face. Provide a flattened list. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Terminator | `T` | Terminator | List | The constructed Terminators. | #### 4.1.6 Detect Connectors from Voxels *Connector · Nickname: `DetConVox` · GUID: `C37CB245-1DE9-409E-ADC6-959356EF8971`* Voxelize all Module Faces using Face-local ray scanning, group same-Direction Faces by matching voxel fingerprint (testing 4 rotations), assign a named Connector to each group with detected rotation offsets, and output Connector Pairs for groups whose fingerprints match across opposite-Direction Faces. #### Behavior Shoots rays from each Face plane in both Directions (inward and outward) on a uniform grid. The resulting 3D boolean fingerprint is compared across same-Direction Faces at four 90-degree rotations. Faces whose fingerprints match at any rotation form a Connector group and receive the same Connector name with the detected rotation offset. Names come from the shared Connector auto-name table (`c-` followed by four letters), deterministically per component instance and uniquely across Connector components in the document; `vox_N` is used only when more groups are detected than names were reserved for this component. Symmetry flags are determined per group by testing the prototype fingerprint against itself at each rotation. Groups on opposite Directions are tested with a UV-mirrored comparison to account for the different coordinate frames of opposing Faces; each match produces a Connector Pair. The viewport shows Connector sticker previews (arrows and name labels) alongside voxel wireframes with a colour derived deterministically from each Connector name, stable between recomputes. Bezier curves connecting compatible Module Faces are drawn for each detected Connector Pair, using the same visual language as Preview Rule. **Recompute limit.** When the number of detected Connector groups changes between consecutive recomputes, the component schedules one additional solution pass to stabilize. This reschedule is capped at five consecutive Attempts; if the count keeps changing after five passes a Warning is shown and the current result is returned as-is. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | Only external faces are voxelized and analysed. Provide a flattened list. | | Voxel Dimension | `VD` | Vector | Item | Number of voxels per module axis (X, Y, Z). Each component specifies how many voxels fit into the module in the respective direction. The face grid resolution is derived from the two components that span the face. | | Precision | `P` | Integer | Item | Number of rays cast per cell. Higher values detect smaller features with higher accuracy but reduce speed. | | Inner Depth | `ID` | Number | Item | Scan depth inside the Cell as a fraction of the module depth along the face normal. 0.5 = half the module depth, 1.0 = full depth. | | Outer Depth | `OD` | Number | Item | Scan depth outside the Cell as a fraction of the module depth along the face normal. 0.0 = flush with the face. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Connectors | `C` | Connector | List | One Connector per module face that has occupied voxels. Faces with matching geometry share the same Connector name with detected rotation offsets. | | Connector Pairs | `CP` | Connector Pair | List | Pairs of Connector names whose voxel fingerprints match across opposite-direction faces. Feed into Construct Assembly. | #### 4.1.7 Terminator from Point *Connector · Nickname: `TermFromPt` · GUID: `CFAF3D4B-53D1-4323-989A-32212AF0BA69`* Create Terminators from a point tag. Looks up every Module Face whose geometry contains the provided point and creates a Terminator for each matching Face in one step. Useful for authoring Terminators by clicking on Module Faces in the Rhino viewport. For Face-based placement, use [Construct Terminator](#comp-terminatorconstruct). #### Behavior Uses the same point-in-Face matching as [Faces from Point](#comp-facepoint): the component tests each external Face of every Module with a 0.01-model-unit tolerance. Each match addresses the Cell on which the Face lies, so a hit on a multi-cell Module produces a numbered Face reference covering only that Cell. The input Modules also provide the viewport badge preview. Zero matches raise a Warning and produce no Terminators. Multiple matches (e.g. two Modules whose geometries overlap at the point location) emit an informational Remark and produce one Terminator per match. Output is flattened. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | All available Modules to search for a Face hit. Only external faces participate in point matching. Provide a flattened list. | | Point Tag | `Pt` | Point | Item | Point marking a location on a Module face. The component returns one Terminator per Module Face whose geometry contains the point. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Terminators | `T` | Terminator | List | One Terminator per matched Face. If the Point marks multiple Faces (e.g. overlapping Modules), each match produces its own Terminator. | #### 4.1.8 Match Connectors by Geometry *Connector · Nickname: `MatchConGeo` · GUID: `57185EC3-CAAC-4CEC-9C66-B6A01A007E8A`* Given an exemplar Connector, find all geometrically matching Faces across all Modules and output Connectors with detected rotation offsets. Uses the same naked-edge / curve-endpoint / point extraction as Detect Rules from Geometry but additionally detects the in-plane rotation offset between each match and the exemplar. #### Behavior Extracts geometry from each input Module's external Faces, transforms it to a normalized base plane, and compares it against the exemplar Connector's Face. The exemplar Module and Face must be present among those external Faces; otherwise the component reports an error. For each match, it tests all four 90° rotations of the geometry pattern. Each output Rotation is the exemplar's Rotation plus the detected offset. The output covers matching Faces, including the exemplar itself, and inherits its name and symmetry flags. No matches produce a warning. Multiple exemplars may be wired in separate iterations; the viewport previews all their Connectors together as stickers on Module Faces. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Exemplar | `E` | Connector | Item | The reference Connector whose face geometry defines what to match. | | Modules | `M` | Module | List | Only external faces participate in geometry matching. Provide a flattened list. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Connectors | `C` | Connector | List | All matched Connectors with detected rotations. | | Match Count | `MC` | Integer | Item | Number of faces matched (including the exemplar). | #### 4.1.9 Match Connectors by Voxels *Connector · Nickname: `MatchConVox` · GUID: `87E7DE28-F5C5-406D-A6E9-6F8CE7F61763`* Given a prototype Connector on a known Module, find all Faces across candidate Modules whose geometry matches the prototype’s Face. Uses Face-local bidirectional ray scanning to build voxel fingerprints and compares them at four rotations (0°, 90°, 180°, 270°). #### Behavior Shoots rays from each Face plane in both Directions (inward and outward) on a uniform grid. Intersection parity determines inside/outside state; intersection positions mark surface voxels. Candidate external Faces are first filtered by Face dimensions and voxel-grid dimensions, then their fingerprints are compared at all four 90° rotations. The output starts with a copy of the prototype Connector, followed by matches with its name and symmetry flags. The **Modules** input is optional: without it, only the prototype Connector is output. The prototype Module does not need to appear in the candidate Modules list. The prototype Face shows solid coloured voxels and white scan-bounds edges; candidate matches show wireframe voxel outlines with Axis colour coding. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Prototype Connector | `PC` | Connector | Item | The reference Connector whose face geometry defines what to match. Must belong to the Prototype Module. | | Prototype Module | `PMM` | Module | Item | The Module that the Prototype Connector belongs to. Does not need to appear in the Modules list. | | Modules | `M` | Module | List | Candidate modules to search for faces matching the prototype. Only external faces are scanned. The prototype module may or may not be included. | | Voxel Dimension | `VD` | Vector | Item | Number of voxels per module axis (X, Y, Z). Each component specifies how many voxels fit into the module in the respective direction. The face grid resolution is derived from the two components that span the face. | | Precision | `P` | Integer | Item | Number of rays cast per cell. Higher values detect smaller features with higher accuracy but reduce speed. | | Inner Depth | `ID` | Number | Item | Scan depth inside the Cell as a fraction of the module depth along the face normal. 0.5 = half the module depth, 1.0 = full depth. | | Outer Depth | `OD` | Number | Item | Scan depth outside the Cell as a fraction of the module depth along the face normal. 0.0 = flush with the face. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Connectors | `C` | Connector | List | All matched Connectors including the prototype, with detected rotations. | | Match Count | `MC` | Integer | Item | Number of faces matched (including the prototype). | #### 4.1.10 Preview Connector *Connector · Nickname: `ConnPreview` · GUID: `47AF634E-2621-4163-9BC9-5E01737EA202`* Display Connectors on Modules as arrows and name labels. When Connector Pairs are also provided, bezier curves are drawn between compatible Module Faces. Display-only component (no geometry outputs). Supports baking of the bezier curves. #### Behavior Draws a Connector sticker (arrow and name label) on the assigned Face of each Module. Several Connectors on one Face are drawn; only exact repeats and per-cell Connectors covered by a whole-side Connector in the same Direction are dropped. If any Connector references a Module absent from the Modules input, a Warning names it and nothing is drawn. When Connector Pairs are wired, the pairs are resolved to Rules internally and a bezier curve is drawn per Rule in the neutral grey used by Preview Rule. The bezier curves can be baked; Connector arrow/label geometry is not bakeable. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | All Modules that the Connectors reference. Provide a flattened list. | | Connectors | `C` | Connector | List | Connectors to display on their assigned module faces. | | Connector Pairs | `CP` | Connector Pair | List | Optional. When provided, bezier curves are drawn between compatible module faces. | ### 4.2 Envelope #### 4.2.1 Heterogeneous Grid *Envelope · Nickname: `HeteroGrid` · GUID: `5CEE3F30-A5F5-40F9-BE97-47BC8DBDAC7B`* Generate a grid where cells along each Axis can have different sizes. Each Axis receives an independent list of dimension values; the component creates all combinations. For example, three X sizes, four Y sizes, and two Z sizes produce 3×4×2 = 24 Cells, each with dimensions determined by its position along each Axis. The homogeneity of the Homogeneous Grid means all cells are identical; here, each column, row, or layer has a distinct dimension while all cells within the same column, row, or layer share that Axis value. #### Behavior Cells are placed with cumulative offsets along each Axis, starting with a Cell centered on the **Base Plane** origin. The resulting Cells can have different sizes in one, two, or all three dimensions. Each size list must be nonempty and contain only positive values; the grid cannot exceed 5,000,000 Cells. **What makes a grid Heterogeneous.** The grid is only Heterogeneous when the size lists contain more than one distinct value along at least one Axis, so that neighbouring Slots end up with different dimensions. If all three lists contain a single repeated value, the result is identical to Homogeneous Grid. A grid is *not* Heterogeneous merely because its cells are non-cubic - a Homogeneous Grid with Diagonal (2, 1, 3) already has rectangular cells; every cell is still the same size. A practical way to create varied-size input lists is the Grasshopper **Gene Pool** component for interactively dragging individual values and seeing the resulting grid update in real time. For the Heterogeneous Grid to work with WFC, Modules are required whose dimensions match the different Cell sizes. For example, if X sizes are `[1, 2]`, you need at least one Module with X = 1 and one with X = 2. Give separately authored Modules distinct names and define Rules for each. **Naming Modules of different sizes.** Construct Assembly rejects separately authored Modules with the same name, even when their sizes differ. Only rotation variants of one source Module may share a name. Use distinct names and Rules for different sizes. See [example 2.11 - Naming Modules of different sizes](workflows.html#naming-modules-of-different-sizes) for a detailed worked example. The component flattens its Cells output in Z-fastest order, then Y, then X. Connect it directly to the Construct Slot component. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Base Plane | `P` | Plane | Item | Grid orientation and origin plane. | | X Sizes | `X` | Number | List | Dimensions of grid cells in the X direction. | | Y Sizes | `Y` | Number | List | Dimensions of grid cells in the Y direction. | | Z Sizes | `Z` | Number | List | Dimensions of grid cells in the Z direction. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Cells | `B` | Cell | Tree | Heterogeneous grid Cell instances with variable cell sizes. | #### 4.2.2 Homogeneous Grid *Envelope · Nickname: `HomoGrid` · GUID: `41E2B761-5785-4665-B3AB-1615DD336ED2`* Generate a regular Envelope made of uniform cells. All cells share the same X, Y and Z dimensions, arranged in a cuboid block. This is the most common starting point for a WFC setup. #### Behavior Creates a 3D array of identically-sized Cells, aligned to the given base plane. Each individual cell can have different X, Y, and Z dimensions; the homogeneity means all cells in the grid share exactly those same dimensions. A single-cell grid (X Count = Y Count = Z Count = 1) is valid and can be used for any purpose that requires an isolated cell. **Non-cubic is not Heterogeneous.** Setting the Diagonal to (2, 1, 3) produces rectangular cells, but every cell in the grid is still identical - the grid is still Homogeneous. You still need only one Module size. Use Heterogeneous Grid only when you need *neighbouring* Slots to have *different* sizes from each other. Although cells do not need to be cubic, cubic cells (`X = Y = Z`) are strongly recommended: non-cubic cells constrain which Modules fit which Slots and can make the setup harder to manage. The first Cell is centered on the **Base Plane** origin. The output is a data tree of Cells, flattened by the component with Z changing fastest, then Y, then X. The grid cannot exceed 5,000,000 Cells. Connect directly to the Construct Slot component. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Base Plane | `P` | Plane | Item | Grid orientation and origin plane. | | Diagonal | `D` | Vector | Item | Grid cell dimensions (width, depth, height). | | X Count | `X` | Integer | Item | Number of grid cells in the X direction. | | Y Count | `Y` | Integer | Item | Number of grid cells in the Y direction. | | Z Count | `Z` | Integer | Item | Number of grid cells in the Z direction. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Cells | `B` | Cell | Tree | Homogeneous grid Cell instances with uniform cell sizes. | #### 4.2.3 Add Boundary Layer *Envelope · Nickname: `AddBound` · GUID: `78832F9B-9B95-41EE-AC2A-34F01E241BC6`* Surround an existing Grid or Envelope with one or more additional layers of Cells. Use this to control what appears at the outer edges of the Envelope - for example, to ensure open or closed boundary conditions, or to add a ring of dedicated “cap” Modules that frame the interior. #### Behavior Generates new Cells that are adjacent to - but not part of - the input Envelope. The component outputs *only the new boundary cells*, not the original ones. The boundary Cells are typically converted to a separate set of Slots (using Construct Slot with different Module allowances than the interior), and only then merged with the interior Slots before passing all Slots to the WFC Solver. The six directional toggles, Include +X, Include +Y, Include +Z, Include -X, Include -Y and Include -Z, default to true. For example, disable Include +Z (`Z`) to leave the top of the Envelope open. Diagonal Neighbors (`D`) defaults to false; when enabled, the layer also grows into diagonal Cells in the toggled 26-neighbourhood. Every empty neighbouring Cell is included, including holes enclosed inside the Envelope. When working with non-rectangular (masked) Envelopes, the boundary follows the actual shape rather than filling out to the bounding box, so each new layer can extend past the Envelope's original extent in every enabled direction. Layers below `1` raise an Error. When all six directions are disabled, a Warning is shown and no output is produced. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Cells | `B` | Cell | List | All Cell objects to surround with boundary layers. | | Diagonal Neighbors | `D` | Boolean | Item | Include diagonal Cell neighbors. | | Layers | `L` | Integer | Item | Number of layers to add. | | Include +X | `X` | Boolean | Item | Scan for boundary in positive X direction | | Include +Y | `Y` | Boolean | Item | Scan for boundary in positive Y direction | | Include +Z | `Z` | Boolean | Item | Scan for boundary in positive Z direction | | Include -X | `-X` | Boolean | Item | Scan for boundary in negative X direction | | Include -Y | `-Y` | Boolean | Item | Scan for boundary in negative Y direction | | Include -Z | `-Z` | Boolean | Item | Scan for boundary in negative Z direction | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Boundary Layer Cells | `B` | Cell | List | New cells forming the boundary layers; ready for use as Slot envelopes. | #### 4.2.4 Are Cells Boundary *Envelope · Nickname: `AreBoxesBound` · GUID: `04B1608C-1672-4FA8-8865-965BAF21CA85`* Identify which Cells in an Envelope border empty space, including interior holes. Returns a boolean per input Cell - `true` if the Cell is within the specified number of layers from an empty neighbour along an enabled axis. Use this to separate boundary Cells from interior Cells to treat them differently - assign dedicated boundary Modules, restrict allowed candidates, or visualize the distinction. #### Behavior A Cell is a boundary Cell when at least one neighbour along an enabled axis is empty. Include X, Include Y and Include Z each cover both directions of that axis. With **Layers** set to `1`, Cells adjoining empty space are flagged; deeper layers flood inward through occupied Cells. With Layers at `0` or below, no Cell is flagged. When all three axes are disabled, a Warning is shown and no output is produced. The output boolean list matches the input list order one-to-one. A convenient workflow is to feed the boolean pattern into a **Dispatch** component to split the Cells (or their resulting Slots) into boundary and interior groups, then apply different constraints to each. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Cells | `B` | Cell | List | All Cell objects to analyze. | | Layers | `L` | Integer | Item | Number of outer layers to identify as boundary. | | Include X | `X` | Boolean | Item | Scan for boundary in X direction. | | Include Y | `Y` | Boolean | Item | Scan for boundary in Y direction. | | Include Z | `Z` | Boolean | Item | Scan for boundary in Z direction. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Boolean Pattern | `B` | Boolean | List | True if the Cell is on the boundary of the grid envelope. | #### 4.2.5 Deconstruct Cell *Envelope · Nickname: `DeconCell` · GUID: `6C600E1F-A033-4D65-88A8-D8D1EC438924`* Extract the center plane, diagonal dimensions and Axis intervals from a Cell. Useful for working with the geometry of individual cells directly, for example to generate Module geometry at a cell’s exact position and size. #### Behavior Decomposes each Cell in the input tree into its geometric components. Each output item is placed at the input path with the Cell's item index appended. If any Cell is null or invalid, one Error names every offending tree position and the component produces no output. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Cell | `B` | Cell | Tree | A Cell to deconstruct into its geometric components. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Center Plane | `P` | Plane | Tree | The plane at the center of the Cell with the box's orientation. | | Diagonal | `D` | Vector | Tree | Vector representing the X, Y, and Z dimensions of the Cell. | | X Interval | `X` | Interval | Tree | The interval along the X axis (centered at origin: -halfX to +halfX). | | Y Interval | `Y` | Interval | Tree | The interval along the Y axis (centered at origin: -halfY to +halfY). | | Z Interval | `Z` | Interval | Tree | The interval along the Z axis (centered at origin: -halfZ to +halfZ). | #### 4.2.6 Grid Topology *Envelope · Nickname: `Topology` · GUID: `D2D543C9-EB1B-46E7-854E-D9E290F7EA57`* Extract the discrete grid coordinates and adjacency map of an Envelope. For each Cell, the component reports its integer position in the grid and which other Cells are its neighbors. Useful when reasoning about spatial relationships within the Envelope in Grasshopper - for example, to drive custom Module allowances based on grid position. #### Behavior The **Relative Coordinates** output gives each Cell’s integer (column, row, layer) position within the grid, expressed as a Point. These are not world-space coordinates - they are grid indices counted from the minimum corner of the grid's extent. Position `(0, 0, 0)` need not contain a Cell. The **Topology** output is a tree where the input branch path with Cell index `N` appended contains the indices of Cell `N`'s neighbors. By default, **Diagonal Neighbors** is `false` (face neighbors only) and **Bi-Directional Topology** is `true` (neighbors in both directions). Enabling Diagonal Neighbors includes edge and corner neighbors; disabling Bi-Directional Topology keeps only neighbors in the positive X, Y, and Z directions. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Cells | `B` | Cell | Tree | List of Cell items that define the grid layout. Provide a single flat list (one branch). | | Diagonal Neighbors | `D` | Boolean | Item | Include diagonal neighbor boxes when computing topology (true/false). | | Bi-Directional Topology | `BiDi` | Boolean | Item | When true, topology lists neighbors in both positive and negative directions. When false, topology lists only neighbors in the positive direction. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Relative Coordinates | `C` | Point | List | Relative grid coordinates for each provided Cell, useful for visualization and indexing. | | Topology | `T` | Integer | Tree | Tree of integer neighbor indices: for each cell (by index) the connected neighbor indices are listed. | #### 4.2.7 Neighbor Cells *Envelope · Nickname: `Neighbors` · GUID: `8BEADDF2-96B1-4D59-BD68-B8ED5F7377F3`* Find the Cells that are adjacent to a given set of focus cells. Given Cell Indices, the component returns Neighbor Indices for Cells within the specified number of Layers along the enabled axes. Useful for selecting “everything around” a particular sub-region of the Envelope. #### Behavior The search covers a box measured in grid steps along each enabled axis, including diagonal neighbors. With **Layers** set to `1` and all axes enabled, it can return up to 26 neighbors per focus Cell. **Include X**, **Include Y**, and **Include Z** restrict the search to the selected axes. Focus Cells are excluded, and the output indices are unique and sorted in ascending order. A typical workflow is to use the **Are Cells Boundary** component to get boundary indices, then feed those into Neighbor Cells to find the row of cells just inside the boundary. You can then use a **Dispatch** component to separate neighbor cells from the rest and apply different Module allowances to each group. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Cells | `B` | Cell | List | List of Cell objects representing the grid to analyze. Provide a flat list (flattened) of Cells. | | Cell Indices | `I` | Integer | List | Indices of Cells for which neighbor indices should be computed. Provide indices relative to the supplied Cells list. | | Layers | `L` | Integer | Item | Maximum search distance (in grid steps) to consider when collecting neighboring Cells. Defaults to 1. | | Include X | `X` | Boolean | Item | Enable scanning for neighbors along the X axis. | | Include Y | `Y` | Boolean | Item | Enable scanning for neighbors along the Y axis. | | Include Z | `Z` | Boolean | Item | Enable scanning for neighbors along the Z axis. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Neighbor Indices | `I` | Integer | List | List of integer indices pointing to neighboring Cells found for the supplied Cell Indices. | #### 4.2.8 Cells from Geometry *Envelope · Nickname: `GeoGrid` · GUID: `9B1DF19E-BB90-4AB6-A092-3B416ABCCB28`* Generate a set of Cells whose cells cover the shape of input geometry. Use this to define a non-rectangular Envelope that follows an arbitrary form - for example a curved building mass, a terrain surface, or a point cloud. #### Behavior The **Populate Method** input selects Surface Wrap, Fill Volume, or Surface Wrap + Fill Volume (the default). For Meshes, Surfaces, and Breps, Surface Wrap marks a Cell when the surface overlaps its box; Fill Volume marks a Cell when its centre lies inside a closed volume. Curves and Points are sampled. **Interior Only** keeps only Cells marked by Fill Volume but not Surface Wrap and skips Curve samples on a Cell face. A Mesh, Surface or Brep that does not enclose a volume (an open shell such as a box without a lid, a tube or a flat sheet) is never filled: it is voxelized by its surface only. Fill Volume and Surface Wrap + Fill Volume then give the same Cells as Surface Wrap, and Interior Only gives none. The component names such a shape in a remark. Closed solids are filled as usual. Cells are aligned to the **Base Plane** and centred at integer multiples of the **Cell Diagonal** from its origin. Snapping to Cell boundaries uses the document tolerance, capped at 2% of a Cell; the component reports when it applies this cap. **Supported geometry types:** Points, Curves, untrimmed Surfaces, Breps, and Meshes. Trimmed surfaces should be converted to Breps before use (use the *Convert to Brep* component in Grasshopper). **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Geometry | `G` | Geometry | List | Input Geometry. Geometry to populate with cells. Supported types: Points, Curves, (untrimmed) Surfaces, Breps, Meshes. | | Base Plane | `B` | Plane | Item | Grid space base plane. Defines orientation of the grid. | | Cell Diagonal | `D` | Vector | Item | Vector specifying the cell size along base-plane-aligned X, Y and Z axes. | | Populate Method | `F` | Integer | Item | 0 = Surface Wrap 1 = Fill Volume 2 = Surface Wrap + Fill Volume Defaults to 2. | | Interior Only | `I` | Boolean | Item | When enabled, a Mesh, Surface or Brep yields only the Fill Volume Cells that Surface Wrap does not mark: the Cells strictly inside the volume, regardless of Populate Method. Curve samples lying on a Cell face are skipped. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Cells | `B` | Cell | List | Generated Cell instances suitable for constructing Slot envelopes. | ### 4.3 Face #### 4.3.1 Get Module Faces *Face · Nickname: `ModFaces` · GUID: `A505AFEA-4A91-4302-87BC-797BC4552E2D`* Deconstruct a Module into its externally visible Faces, one list per Direction output. #### Behavior Reads the Module's externally visible Face set and outputs each Direction as a separate FaceId list. A single-cell Module produces one FaceId per Direction (6 total), spelled `+X`..`-Z`. A multi-cell Module may produce several per Direction - one per footprint cell whose face in that direction is not covered by another cell - numbered `+X0`, `+X1`, ... in spatial cell order. Returns error if the Module is null or invalid. Also previews each Face directly on the Module in the viewport, using the same rectangle/arrow/Direction-label glyph as Preview Faces, so the outputs can be told apart by eye without wiring a separate preview component. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Module | `MM` | Module | Item | A Module from which individual faces will be extracted. For single-cell modules all 6 faces are returned; for multi-cell modules only external faces appear in the per-direction outputs. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | {GH_FaceIndex.XPositive} Face | `{GH_FaceIndex.XPositive}` | Face ID | List | Positive X Face. Face IDs for external faces pointing in the positive X direction. | | {GH_FaceIndex.XNegative} Face | `{GH_FaceIndex.XNegative}` | Face ID | List | Negative X Face. Face IDs for external faces pointing in the negative X direction. | | {GH_FaceIndex.YPositive} Face | `{GH_FaceIndex.YPositive}` | Face ID | List | Positive Y Face. Face IDs for external faces pointing in the positive Y direction. | | {GH_FaceIndex.YNegative} Face | `{GH_FaceIndex.YNegative}` | Face ID | List | Negative Y Face. Face IDs for external faces pointing in the negative Y direction. | | {GH_FaceIndex.ZPositive} Face | `{GH_FaceIndex.ZPositive}` | Face ID | List | Positive Z Face. Face IDs for external faces pointing in the positive Z direction. | | {GH_FaceIndex.ZNegative} Face | `{GH_FaceIndex.ZNegative}` | Face ID | List | Negative Z Face. Face IDs for external faces pointing in the negative Z direction. | #### 4.3.2 Analyze Face *Face · Nickname: `AnalyzeFace` · GUID: `6EE2C70C-748D-4853-A55F-1C9A39576BB9`* Analyze Face properties including geometry, Direction and Rule/Connector usage. Provides Direction flags, Face planes, rectangles, and used/unused classification. #### Behavior Looks up each Face’s Module, extracts Face plane and rectangle geometry, determines Direction flags, and checks Rule and Connector references. A Module wired into Face or Module is expanded into its six Faces with a Remark. Faces naming an unknown Module or no external Face of their Module produce an Error and no output. Without Modules, a Warning says Face Planes and Rectangles could not be determined. Builds output trees parallel to the input Face tree. Coverage is read per Cell, the same way Used Faces reads it. A reference that names a Cell ordinal, such as `big:+X1`, covers that one Cell’s Face; a reference without an ordinal, `big:+X`, covers the whole side — every Cell of the Module exposing that direction. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Face or Module | `F` | Face ID | Tree | Face to analyze, obtained from the Get Module Faces component or other face-producing components. A Module wired here instead is expanded into all six of its faces. | | Modules | `M` | Module | List | Optional list of Module objects used to resolve face geometry and planes. If omitted, some geometric outputs will be unavailable. Provide a flattened list. | | Rules | `R` | Rule | Tree | Optional list of Rule objects used to determine face usage patterns. When provided, outputs will indicate whether faces are used by any rule. | | Connectors | `C` | Connector | Tree | Optional list of Connector objects used to determine face usage patterns. A Face that has any Connector assigned to it is considered used. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Module Name | `MN` | Module Name | Tree | The module name associated with the analyzed face (normalized to lowercase). | | Face Planes | `FP` | Plane | Tree | Local planes describing the face geometry when module geometry is provided. | | Face Rectangles | `FR` | Rectangle | Tree | Projected rectangle outlines of each face when module geometry is available. | | Face Direction | `FD` | Vector | Tree | Unit vectors describing the face normal in the face base plane; returned as a tree parallel to Face Planes. | | Is X | `X` | Boolean | Tree | True if the face points in the X direction of the Module. | | Is Y | `Y` | Boolean | Tree | True if the face points in the Y direction of the Module. | | Is Z | `Z` | Boolean | Tree | True if the face points in the Z direction of the Module. | | Is -X | `-X` | Boolean | Tree | True if the face points in the -X direction of the Module. | | Is -Y | `-Y` | Boolean | Tree | True if the face points in the -Y direction of the Module. | | Is -Z | `-Z` | Boolean | Tree | True if the face points in the -Z direction of the Module. | | Face Use Pattern | `FUP` | Boolean | Tree | true when the face is referenced by any provided Rule or Connector. | | Used Faces | `FU` | Face ID | Tree | Faces that are referenced by at least one Rule or Connector. Flattened for convenience. | | Unused Faces | `FUU` | Face ID | Tree | Faces that are not referenced by any provided Rule or Connector. Flattened for convenience. | #### 4.3.3 Compare Faces *Face · Nickname: `CompFaces` · GUID: `46A0D3B6-2737-4185-84FE-A43CCD5D20D1`* Compare two Faces for identity, Module membership and Direction relationship. #### Behavior Identical is true only when Module Name, Direction and Cell ordinal all match; a whole-side Face and a per-cell Face on the same side are different. Same Direction and Opposite Direction compare only Direction; Skew Direction is true when the axes differ. A null or invalid Face, including a Module wired as a six-Face wildcard, raises an Error and produces no output. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Face | `F` | Face ID | Item | First FaceId to compare. Obtain faces from the Get Module Faces component or other face-producing components. | | Face 2 | `F2` | Face ID | Item | Second FaceId to compare against the first. Obtain faces from the Get Module Faces component. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Identical | `I` | Boolean | Item | True if the Faces are identical. | | Same Module | `M` | Boolean | Item | True if the Faces refer to the same Module. | | Same Direction | `D` | Boolean | Item | True if the Faces face the same direction. | | Opposite Direction | `O` | Boolean | Item | True if the Faces face the opposite direction. | | Skew Direction | `S` | Boolean | Item | True if the Faces face skew directions. | #### 4.3.4 Faces from Point *Face · Nickname: `FacesPoint` · GUID: `0098E8AA-3969-4CDA-846C-9DBEAC6B6E7B`* Detect Module Faces at a point location. Returns all Faces whose geometry contains the given point. #### Behavior Tests the point against external Faces of each Module with a tolerance of `0.01` model units. Each Face returned for a multi-cell Module carries its Cell ordinal. If no Face matches, a Warning states that the point does not mark any FaceId; multiple matches produce a Remark that the point marks more than one FaceId. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | All available Modules to sample when detecting faces. Only external faces participate in point matching. Provide a flattened list. | | Point Tag | `Pt` | Point | Item | Point marking a location to sample for module faces; the component returns any faces whose geometry contains this point. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Faces | `C` | Face ID | List | List of FaceId objects whose geometry contains the provided point. | #### 4.3.5 Touching Faces from Slots *Face · Nickname: `FacesFromSlots` · GUID: `0A6DD735-FAA0-4FA3-945E-D0E1D79EB40F`* Scan Slots and extract touching Module Faces where adjacent slots meet. Useful for discovering which module faces need Rules. #### Behavior Builds a spatial index of Slot positions, finds adjacent slot pairs along each axis, and extracts face pairs where faces touch. Groups results by source (positive-direction Faces) and target (negative-direction Faces). Each branch of the input is processed independently. Non-deterministic slots produce all possible combinations with a warning. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Slots | `S` | Slot | Tree | Slot envelopes to scan for touching faces. Each branch is scanned independently. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Source Faces | `SF` | Face ID | Tree | Tree of FaceId entries representing the source-side faces found where slots touch their positive neighbors. | | Target Faces | `TF` | Face ID | Tree | Tree of FaceId entries representing the target-side faces found where slots touch their negative neighbors. | #### 4.3.6 Used Faces *Face · Nickname: `UsedFaces` · GUID: `805E4EC0-E576-4895-9048-3F513A94C428`* Filter Module Faces by Rule, Connector, and Terminator coverage. Partitions every Module’s external Faces into used (referenced by at least one Rule, Connector, or Terminator) and unused (no reference). Wire the Unused output into Construct Terminator to mark uncovered Faces as boundary-safe, or use it to assign indifference Connectors. #### Behavior Collects all Face references from Rules (source and target), Connectors (Module + Face Index), and Terminators into a set, then tests every external Face of every input Module against that set. All three reference inputs are optional; when none is provided all Faces are reported as unused. Output trees mirror the input Module tree structure with one grafted branch per Module. A multi-cell Module exposes one Face per Cell in each direction, so coverage is read per Cell. A reference that names a Cell ordinal, such as `big:+X1`, covers that one Cell’s Face. A reference without an ordinal, `big:+X`, covers the whole side: every Cell of the Module exposing that direction. Construct Assembly reads the same two forms the same way, so what this component reports as covered is what the Assembly treats as constrained. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | Tree | Modules whose external Faces will be checked for Rule and Connector coverage. The input tree structure is preserved in the output with one grafted branch per Module. | | Rules | `R` | Rule | Tree | Rules to check. A Face referenced as source or target of any Rule is considered used. | | Connectors | `C` | Connector | Tree | Connectors to check. A Face that has any Connector assigned to it is considered used. | | Terminators | `T` | Terminator | Tree | Optional. When provided, a Face that has a Terminator assigned to it is considered used. This prevents indifference from generating rules for terminated Faces. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Used Faces | `F` | Face ID | Tree | Faces referenced by at least one Rule, Connector, or Terminator. One branch per input Module, preserving the input tree structure. | | Unused Faces | `UF` | Face ID | Tree | Faces not referenced by any provided Rule, Connector, or Terminator. One branch per input Module, preserving the input tree structure. | #### 4.3.7 Group Faces by Voxels *Face · Nickname: `GrpFacesVox` · GUID: `805BFE98-62FC-461A-A733-B98A1BC3C51C`* Groups Module Faces by identical voxel fingerprint. Uses Face-local bidirectional ray scanning to build voxel fingerprints and groups Faces whose fingerprints are structurally identical. #### Behavior The **Modules** input is flattened into one list. The component builds a voxel fingerprint for each external Face via bidirectional ray scanning and groups Faces by exact fingerprint equality without testing rotations. Faces with no occupied voxels are skipped. **Face Groups** has one branch per group at path `{g}`. The viewport previews each group's voxel fingerprints as wireframes in a distinct colour. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | Only external faces are voxelized and grouped. Provide a flattened list. | | Voxel Dimension | `VD` | Vector | Item | Number of voxels per module axis (X, Y, Z). Each component specifies how many voxels fit into the module in the respective direction. The face grid resolution is derived from the two components that span the face. | | Precision | `P` | Integer | Item | Number of rays cast per cell. Higher values detect smaller features with higher accuracy but reduce speed. | | Inner Depth | `ID` | Number | Item | Scan depth inside the Cell as a fraction of the module depth along the face normal. 0.5 = half the module depth, 1.0 = full depth. | | Outer Depth | `OD` | Number | Item | Scan depth outside the Cell as a fraction of the module depth along the face normal. 0.0 = flush with the face. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Face Groups | `FG` | Face ID | Tree | Data tree where each branch contains face IDs that share the same voxel fingerprint. | #### 4.3.8 Preview Faces *Face · Nickname: `FacesPreview` · GUID: `CC52EA9A-FD9D-4810-9904-C69BAE21BB17`* Visualize Faces with their geometry, anchor planes and Direction indicators. Display-only component (no geometry outputs). #### Behavior Draws each Face from Faces or Modules as a rectangle with a name label and Direction arrow colored by Axis (X = red, Y = green, Z = blue). A Module wired into Faces or Modules is expanded into its six Faces with a Remark. Null Faces or Faces naming a Module absent from the Modules input produce an Error and nothing is drawn. Baking to Rhino writes a rectangle, a text dot with the Face name and a Direction line per Face, grouped and colored by Axis. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Faces or Modules | `F` | Face ID | Tree | Faces to preview, obtained from the Get Module Faces component or other face-producing components. A Module wired here instead is expanded into all six of its faces. | | Modules | `M` | Module | List | Available Modules used to resolve face geometry for preview. Provide a flattened list. | ### 4.4 Main #### 4.4.1 Construct Assembly *Main · Nickname: `Assembly` · GUID: `36ACC45E-5242-4592-8959-3CD9B345A722`* Build a complete Discrete Assembly in a single step. Expands rotation variants, transforms Connectors to match rotated Modules, generates Rules from Connectors and Connector Pairs, removes Disallowed Rules, enforces Exclusive Rules, applies Indifference for unconnected Faces, merges all Rule sources, deduplicates Rules, runs a full Audit, and packages everything into an Assembly consumed by the Solver. Modules may share a name only when they are rotation variants from Module Rotations, which merge into one Module; any other repeated Module name is an Error. #### Behavior Construct Assembly performs rotation expansion on Modules with Rotational Freedom, remaps existing Rules for the new variants, transforms Connectors to match the expanded variants, generates explicit Rules from Connectors and Allowed Connector Pairs (respecting symmetry flags and rotations), merges these with manually provided Allowed Rules, removes Disallowed Rules (explicit and from Disallowed Connector Pairs) — including automatic expansion of Disallowed Rules through all rotation variants — enforces Exclusive Rules (explicit and from Exclusive Connector Pairs) by removing all other Rules referencing the same Faces, deduplicates Rules, optionally generates Indifferent Rules for uncovered Faces, and runs the same Audit logic as Audit Assembly. Several Connectors may occupy one Module Face: the Face accepts every neighbour any of them mates with. Only exact repeats and per-cell Connectors covered by a whole-side Connector in the same Direction are dropped. A multi-cell Module whose Cells do not form one face-connected block raises an Error. During Rule remapping, each rotation variant receives a Face permutation that maps original Face Indices to their new positions after rotation. When both sides of a Rule are rotation-expanded, only variants with the identical rotation (same Face permutation) are paired - the spatial neighborhood rotates as a rigid unit. Mismatched rotations would produce geometrically wrong Rules even when Face Directions remain opposite. When only one side is expanded, only the rotated side’s Face Index is permuted. The output Assembly is an opaque container. Connect it to the WFC Solver, to a Deconstruct Assembly component for inspection, or to Materialize Assembly after solving. The Indifference input defaults to true and generates Rules for otherwise uncovered Faces. Slot Priorities (`SP`) supplies per-Slot solve priorities parallel to Slots, defaulting to `1.0`. When the list is shorter than Slots, its last value repeats; more priorities than Slots raise an Error. Fractional parts are dropped before priorities reach the engine. Remove Unused Connectors (`RU`) defaults to false. A Connector name absent from all Allowed Connector Pairs produces a Warning because it generates no Rules; enabling RU drops those Connector assignments and reports the count in a Remark, making their Faces eligible for Indifference. Require Terminators (`RT`) defaults to false. Terminators (`T`) have an effect only when RT is enabled; supplying them with RT off produces a Warning. With RT on, each exposed Slot Face on a non-flat axis must be filled by a Module with a Terminator on the matching Face. Flat-axis Faces of a 2D Envelope need none. Construct Assembly stores the source project on its output Assembly. Use **Export Project…** on the WFC Solver's right-click menu to save it before or after solving. The file includes inline OBJ geometry but no solved Slot states or WFC Solver **Random Seed**. **Allowed Connector Pair deduplication.** Construct Assembly removes exact-duplicate Allowed Connector Pairs from the input list before generating any Rules. A single Remark reports how many duplicates were dropped. `A→B` and `B→A` remain separate entries; both produce the same Rules, and the resulting duplicate Rules are removed. **Allow-all Slot resolution.** Construct Assembly resolves allow-all Slots (Slots created without explicit Module Names) against authored Modules before rotation expansion. A Module fits if any one of its Cells matches the Slot's Cell size on all three axes; a multi-cell Module is listed under its authored Module Name. Each fitting name receives a Weight of `1.0`. Later, every Slot's Total Modules Count is set to the number of distinct Module Names referenced by any Slot. One Remark reports the number of allow-all Slots resolved and an upper bound on Modules per Slot. An allow-all Slot with no fitting Module raises an Error and stops the component. **Extending Rules and Connectors.** A Rule, a Connector, or a Connector Pair may reference something the Assembly does not contain - a Rule or Connector naming a missing Module, or a Connector Pair naming a Connector that no Connector defines. These are treated as extending references rather than errors: the Solver ignores any Rule it cannot tie to a real Module, so Construct Assembly keeps building and reports them as a Warning naming what was unmatched. This applies to the Allowed, Disallowed, and Exclusive Rule and Connector Pair inputs alike (Terminators on a missing Module are likewise dropped with a Warning). A Module itself must still be complete - a Module Face with no Rule and no Connector is reported as uncovered, below. **Uncovered-Face diagnostics.** Two separate Warnings report Faces not described by any Rule or Connector: one for authored Modules and one for rotation variant Modules. With Indifference off, an uncovered authored Face marks the Assembly invalid, and the WFC Solver refuses it. Uncovered rotation variant Faces do not invalidate the Assembly; the Solver may eliminate those variants. The coverage verdict is computed by the engine from the final Rule set, so enabling Indifference (which injects covering Rules) clears it. The output Discrete Assembly marks such Faces in the viewport with a red circle-and-cross badge on the Module preview. **Slot-fault diagnostics.** Slot-level faults are marked the same way: every Slot that refers to an unknown Module Name, or whose allowed Modules have no variant with matching dimensions (the Audit's *Slots With Unknown Modules* and *Slots Without Fitting Modules*), gets a red circle-and-cross badge laid flat at the Slot box centre. The marks are recomputed on every solve, so they clear as soon as the fault is resolved. Use the Audit Assembly report to see which fault class each marked Slot falls into. **Rule preview and appearance.** The output Assembly draws its Rules as preview wires colored by kind - Allowed green, Disallowed red, engine-generated Indifferent blue - matching the web Rules graph. Preview Rule draws arbitrary Rule lists in one neutral grey. Authored Rules are drawn as wired, including Disallowed Rules on a collapsed Envelope axis; Clean up removes Rules that can never apply. Authored per-mesh display colors of the source geometry are carried into the project file written by **Export Project…** on the [WFC Solver](#comp-solver) as per-mesh materials, so a Module assembled from differently colored meshes keeps each color. **Clean up.** Clean up defaults to false. When enabled, Construct Assembly drops Rules that can never apply to the Slot layout and Modules left unplaceable by that pruning. A Rule cannot apply when its two Faces are not opposite under any allowed rotation (a cross-axis pair like -X to +Z is never a real adjacency), when its axis is collapsed (a flat Envelope has no neighbour along it), or when its two Modules are never allowed in two adjacent Slots; such Rules leave both the effective Rule set and the Disallowed-Rule preview. Any authored Module with no surviving rotation variant after this pruning is removed from the Assembly's Module set and every Slot's allow-list. Clean up also runs constraint propagation, narrowing each Slot to the Modules that can occupy it. If this leaves a Slot with no valid Module, a Warning reports the contradiction. Clean up does not remove Connectors. With Clean up off, Construct Assembly does not perform this trimming; the Solver still runs constraint propagation and prunes dangling references when a session starts. Clean up only shows a trimmed Assembly at Construct Assembly and does not change the solve result. **Partial and invalid Assemblies.** Recoverable build problems, including required Terminators with none provided, an empty Envelope (no Modules or no Slots), and uncovered authored Faces with Indifference off, produce an invalid Assembly. Other engine build problems are reported as Warnings and also invalidate the Assembly. Construct Assembly builds the most complete Assembly it can and reports the problems. An empty Envelope still emits an Assembly carrying whatever Modules and Slots were authored. The WFC Solver refuses an Assembly whose IsValid flag is false. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | Modules with rotation flags still set. | | Slots | `S` | Slot | List | Slot envelopes. | | Slot Priorities | `SP` | Number | List | Per-slot priority for guided solve ordering. Higher-priority slots are resolved first by WFC and picked first by the growth solver. Parallel to the Slots input (longest list: shorter lists repeat the last value). Default 1.0. | | Allowed Rules | `R` | Rule | List | Manually defined allowed rules (optional). | | Disallowed Rules | `DR` | Rule | List | Rules to remove from the final rule set (optional). | | Exclusive Rules | `ER` | Rule | List | Rules whose Faces become exclusive: all other Rules referencing those Faces are removed, then the exclusive Rules are added (optional). | | Connectors | `C` | Connector | List | All connector-to-face bindings (optional). | | Allowed Connector Pairs | `CP` | Connector Pair | List | Connector compatibility declarations (optional). | | Disallowed Connector Pairs | `DCP` | Connector Pair | List | Connector pairs whose generated rules will be removed (optional). | | Exclusive Connector Pairs | `ECP` | Connector Pair | List | Connector pairs whose Faces become exclusive: all other Rules referencing those Faces are removed, then the exclusive Rules are added (optional). | | Remove Unused Connectors | `RU` | Boolean | Item | A Connector placed on a face only does something once it is matched in a Connector Pair. If a Connector is never paired, its face stays occupied and cannot connect to anything. Turn this on to drop those unpaired Connectors so their faces are freed and can be treated as indifferent. This changes which Rules are generated. Separate from Clean up, which only trims the finished Assembly and never touches Connectors. Default: false. | | Require Terminators | `RT` | Boolean | Item | When true, some Modules are required to be designated to be on the boundary of the envelope. Terminators define which module faces may touch the boundary; any face without a Terminator cannot be placed on the outer edge. Requires at least one Terminator in each direction of a non-flat axis when enabled; flat-axis faces are exempt. Default: false. | | Terminators | `T` | Terminator | List | Terminators (optional). Boundary-facing markers on module faces. When Require Terminators is enabled, the occupied frontier is capped so each exposed slot face on a non-flat axis must be filled by a module that carries a Terminator on the matching face. Flat-axis faces (the front and back of a 2D envelope) are not capped and need no Terminator. | | Indifference | `I` | Boolean | Item | Enable indifference for unconnected faces. Default: true. | | Clean up | `Cl` | Boolean | Item | Trims the finished Assembly: drops module rotations the Rules make impossible, plus modules and Rules that nothing references. This makes the Assembly smaller and surfaces contradictions early, without changing the solve result. The Solver already does this automatically, so turn it on only to preview the cleaned Assembly here. Does not remove Connectors (use Remove Unused Connectors for that). Default: false. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Assembly | `A` | Discrete Assembly | Item | Complete, audited Discrete Assembly ready for solving. | #### 4.4.2 Growth Solver *Main · Nickname: `Grow` · GUID: `56775F66-D562-4A1B-AB78-A14BDB88AA67`* The Growth Solver takes a Discrete Assembly and places Modules one Slot at a time. The WFC Solver attempts to fill the Envelope in one solve; Growth Solver steps can leave a partial Assembly and apply supply caps. It uses the same native engine and constraint Propagation. It returns the grown **Assembly**, a **Report**, and **Settled**, **Placed**, and **Empty** outputs. #### Behavior Set **Run** to `true` to start. When **Run** is `false`, the component shows `Idle`, warns `Set Run to True to grow.`, and reports `GROWTH SOLVER IDLE`. An invalid Assembly produces `Assembly is invalid: `. Supply lists of different lengths produce `Supply Names and Supply Caps must have the same length.`; **Steps** below `0` produces `Steps must be 0 or more.` An Assembly without serialized engine data produces `Assembly has no serialized engine data.` Engine or growth session failures are reported as errors. These cases leave the **Assembly** output empty. If the native library is blocked, the component reports its error and shows `Library blocked`. Every Slot starts with its allowed Modules and an internal empty candidate. No Slot counts as placed at the start, including one that allows only one Module. On each step, the Growth Solver considers unresolved Slots that still have a Module candidate and share a Face with a Slot holding a placed Module. This set is the frontier. If it is empty, all unresolved Slots with a Module candidate are eligible. The Growth Solver keeps only those with the highest Slot Priority and picks one uniformly at random. It then chooses a remaining Module candidate that has not reached its supply cap, using the Slot's per-Module Weights. Without authored Weights, candidates have Weight `1.0`; with authored Weights, a Module's Weight is divided among its eligible rotation variants. Constraint Propagation then removes Modules disallowed by Rules in neighbouring Slots. A Slot whose Module candidates are all removed settles empty without a Contradiction. The internal empty candidate accepts any neighbouring Module on any Face; Rules restrict pairs of placed Modules. The Growth Solver respects Terminators, Require Terminators and Slot Priorities from Construct Assembly, and sparse Envelopes. With **Stop When Settled** `false` (the default), an empty frontier causes growth to start in another eligible region. With it `true`, growth stops after the first frontier-connected region resolves and at least one Module has been placed, leaving other regions unresolved. **Steps** defaults to `100` and limits the number of steps; each step places at most one Module. Growth can stop earlier when no placement remains, **Stop When Settled** ends it, or the engine reports a settled or contradictory Envelope. `0` steps places nothing. Pressing Escape stops growth between batches of 256 steps and returns the result reached so far. The output **Assembly** lists the surviving real Modules at each Slot. An empty Slot lists none. **Placed** counts authored Slots that Growth has placed; **Empty** counts authored Slots with no compatible real Module. A Slot with one visible Module candidate can still be unplaced while the internal empty candidate remains, so it does not count as **Placed**. **Settled** is `true` when no Slot can still receive a Module, and `false` while unresolved Slots remain. Materialize Assembly skips empty Slots with a `N contradictory slot(s) skipped.` Warning and Slots with multiple remaining Modules with a `N non-deterministic slot(s) skipped.` Warning. The **Report** states whether all Slots resolved, the step budget ended with undetermined Slots, or growth stopped early. It lists Total slots, Placed, Empty, Undetermined, Steps performed, and Duration, plus SUPPLY CAPS when **Supply Names** is wired. After a run, the component message is `Settled` or the **Placed** count followed by `placed`. #### Supply caps **Supply Names** and **Supply Caps** are parallel lists of authored Module names and maximum instance counts. A Module absent from the lists has unlimited supply. A cap covers every rotation variant and decomposed part of that Module. The first part of a multi-cell Module reserves one instance; its remaining parts can still be placed after the cap is reached. The Growth Solver then removes candidates that would start another instance and propagates the change; other Slots can settle empty. A cap of `0` prevents placement, and a negative cap is applied as `0`. Enter **Supply Names** in lowercase to match Module Names. Names that match no Module are ignored without a message. #### Seeds **Random Seed** defaults to `42`. The same Seed, Assembly, **Steps**, **Stop When Settled**, and supply settings produce the same result. Changing the Seed changes the random choices and may produce a different growth pattern. Each run performs one growth, without parallel Attempts. #### Limits On the free tier, Growth Solver and WFC Solver runs share one run budget per fixed time window. A run is charged when **Run** is `true`, the Assembly is valid, and the engine Assembly and growth session have been built, before the first step and regardless of **Steps**. When the budget is exhausted, the component reports an Error with the reset time, shows `Limit reached`, and reports `GROWTH SOLVER BLOCKED`. The component footer shows the remaining-run counter, and after a completed run a Remark reports how many runs remain. #### Run input gate **Run** is a boolean input that defaults to `false`. Set it to `true` to grow the Assembly. Connect a Boolean Toggle or a Button component. See also [FAQ 1.77 What is the Growth Solver?](faq.html#faq-growth-solver). **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Assembly | `A` | Discrete Assembly | Item | A Discrete Assembly from Construct Assembly. The assembly defines the modules, adjacency rules, grid envelope, terminators, and boundary constraints that the growth solver operates within. | | Random Seed | `S` | Integer | Item | Seed for the random number generator. The same seed with the same assembly always produces the same result. Change the seed to get a different growth pattern. | | Steps | `N` | Integer | Item | Maximum number of growth steps. Each step places one module at one slot. The solver may stop earlier if no more slots are placeable or if Stop When Settled is True and the frontier empties. | | Stop When Settled | `SS` | Boolean | Item | Controls what happens when the growth frontier is exhausted (no unresolved slots adjacent to placed modules). When False (default), the solver seeds a new disconnected region by picking a random unresolved slot and continues growing from there. This fills the entire envelope, potentially in multiple separate clusters. When True, the solver stops as soon as the current frontier-connected region is fully resolved. Remaining unresolved slots in other regions are left untouched. Use this to grow a single contiguous cluster from one seed point. | | Supply Names | `SN` | Text | List | A list of authored module names whose placement count should be limited. Each name must have a matching entry in the Supply Caps list at the same index. A cap covers every rotation variant and decomposed part of the module. Modules not listed have unlimited supply. Supply caps count authored module instances. The first part of a multi-cell module reserves one instance; its remaining parts can still be placed. Once the cap is reached, candidates that would start another instance are removed and the consequence propagates through adjacency rules. This can cause other slots to lose candidates and settle empty. | | Supply Caps | `SC` | Integer | List | Maximum placement count for each module listed in Supply Names. Must have the same length as Supply Names. A cap of 0 means the module is never placed. A cap of 1 means at most one complete instance. Negative caps apply as 0. | | Run | `R` | Boolean | Item | Set to True to execute the solver. Defaults to False so that upstream data changes do not trigger accidental solves. Wire a Boolean Toggle or Button component. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Report | `R` | Text | Item | A text report summarizing the growth result: total slots, placed count, empty count, undetermined count, duration, and any active supply caps. | | Assembly | `A` | Discrete Assembly | Item | The grown Discrete Assembly. Each slot is either resolved to a single module, settled empty (no compatible module remained after constraint propagation), or still undetermined (when the step budget ran out before reaching it). Wire into Materialize Modules to place geometry. | | Settled | `OK` | Boolean | Item | True when no more growth is possible: every slot is either placed or settled empty. False when undetermined slots remain (increase Steps or set Stop When Settled to False). | | Placed | `P` | Integer | Item | Number of slots resolved to a real module. | | Empty | `E` | Integer | Item | Number of slots that settled empty. This happens when constraint propagation removes all compatible modules from a slot. Empty slots are a valid outcome, not an error. | #### 4.4.3 Deconstruct Assembly *Main · Nickname: `DeconAssembly` · GUID: `74A225B2-6777-43C0-8D55-420967B13B6A`* Extract all authored inputs from a Discrete Assembly: Modules, Slots, Connectors, Connector Pairs, Rules, Terminators, and the Indifference and Require Terminators flags. #### Behavior Extracts the authored side of the Assembly container before rotation expansion and Rule generation. Allow-all Slots contain their resolved Module Names. When Clean up is on, authored Modules with no surviving rotation variant are removed from the Modules output and from every Slot's allow-list. Rotation variants generated from each Module's Rotational Freedom are not included; use **Dissolve Assembly** to access the expanded Solver-ready state. An invalid Assembly is accepted and its reason is reported in a Remark. After solving, the **Slots** output returns authored Slots with narrowed Module Names: rotation variants in each solved Slot are projected back to their source Module Name, and the result is intersected with the authored Slot's Module Names list so order and Weights are preserved. Diagnostics are available from Audit Assembly. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Assembly | `A` | Discrete Assembly | Item | Any Discrete Assembly from Construct Assembly or the Solver. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | Authored modules as supplied on Construct Assembly. Rotation variants generated from Rotational Freedom are not included - use Dissolve Assembly to see them. | | Slots | `S` | Slot | List | Authored slots in their original order. After solving, each slot's allowed Module Names are narrowed to the surviving source modules (rotation variants are projected back to their source). | | Connectors | `C` | Connector | List | Authored connectors as supplied on Construct Assembly. | | Allowed Connector Pairs | `CP` | Connector Pair | List | Connector pairs the user supplied as allowed. | | Disallowed Connector Pairs | `DCP` | Connector Pair | List | Connector pairs the user supplied as disallowed. | | Exclusive Connector Pairs | `ECP` | Connector Pair | List | Connector pairs the user supplied as exclusive. | | Rules | `R` | Rule | List | Explicit rules the user supplied as allowed. | | Disallowed Rules | `DR` | Rule | List | Explicit rules the user supplied as disallowed. | | Exclusive Rules | `ER` | Rule | List | Explicit rules the user supplied as exclusive. | | Indifference | `I` | Boolean | Item | Whether indifference was enabled on Construct Assembly. | | Terminators | `T` | Terminator | List | Authored terminators - boundary-facing face markers. | | Require Terminators | `RT` | Boolean | Item | Whether Require Terminators was enabled on Construct Assembly. | #### 4.4.4 Dissolve Assembly *Main · Nickname: `DisAssembly` · GUID: `AA68962C-3C09-4BF8-8E70-8CB0E4E129FD`* Extract the fully expanded solver-ready state from a Discrete Assembly: one solver Module per rotation variant and, for multi-cell Modules, per Cell; the authored Slots with narrowed domains when Require Terminators is enabled; Explicit Rules; and Indifferent Rules. Solver Module names are engine-internal keys, while each entry carries its authored source Module Name. Explicit Rules include manual and Connector-generated Rules; Indifferent Rules cover otherwise uncovered Faces. Invalid Assemblies are accepted with a Remark naming the reason. For the authored inputs use Deconstruct Assembly instead. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Assembly | `A` | Discrete Assembly | Item | Any Discrete Assembly from Construct Assembly or the Solver. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | All modules the solver runs on: authored modules and their rotation variants. | | Slots | `S` | Slot | List | All slots the solver runs on: the authored slots. Require Terminators adds no Slots; it narrows the domains of Slots with exposed faces. | | Explicit Rules | `R` | Rule | List | Manual and connector-generated rules (everything except indifference). | | Indifferent Rules | `IR` | Rule | List | Rules auto-generated for faces not covered by explicit rules. | #### 4.4.5 WFC Solver *Main · Nickname: `WFC Solver` · GUID: `30F84904-411C-4E95-8788-84F7EDA6CD07`* The WFC Solver runs the Wave Function Collapse algorithm on the supplied Envelope. It takes a Discrete Assembly as its sole data input, progressively eliminating Module candidates from each Slot - guided by adjacency constraints and weighted Entropy - until every Slot holds exactly one Module, or the Attempt ends in a Contradiction. Returns an output Assembly with solved Slot states, status flags, Seed values, and a Solver log. #### Behavior Before starting, the Solver validates its inputs and builds the engine Assembly. It then runs the first Attempt with the selected Seed; an already Deterministic or Contradictory Envelope ends that Attempt immediately. The first Attempt runs on one thread. If it is not Deterministic, or **Return first** is `false`, the Solver runs additional Attempts when **Max Attempts** is greater than `1`. It uses at most the smaller of the logical processor count and **Max Attempts** minus one worker threads, with Seeds increasing from the first Seed. Pressing Escape while these Attempts run stops further Attempts after the running ones finish. With **Return first** enabled, the lowest-Seed Deterministic result is returned, or the lowest-Seed partial result if none is Deterministic. Otherwise all non-Contradictory results are returned in Seed order; if every Attempt contradicts, the lowest-Seed Contradictory Assembly is returned. Each Attempt works by repeating two steps. First it picks the Slot with the fewest remaining candidates and assigns it a single Module, making it Deterministic (an **Observation**). Then it cascades the consequences of that choice through the whole Envelope, eliminating Module candidates that would violate Rules with neighboring Slots (this is called **Propagation**). The cycle ends when every Slot is Deterministic, a Slot is Contradictory, or **Max Observations** is reached. Reaching the observation limit gives a partial result. The default limit is effectively unlimited. The **Report** output summarizes the result, input Slots, Modules, Placements, total unique Rules, number of Attempts, successful Seeds, observations, and stage durations. It also reports whether parallel solving ran and how many processor cores it used. When the Assembly is invalid, it gives a reason and suggested fixes; when **Run** is `false`, it reports that the Solver is idle. If an Attempt stops with a partial solution at the observation limit, pass an Assembly from the **Assemblies** output to another WFC Solver to continue solving it. To inspect its Slots, connect that Assembly to **Deconstruct Assembly**. #### Seeds The *Random Seed* input controls the starting point for the Solver's random choices. For the same Slots, Modules, and Rules, the same Seed always produces the same result. Seeds themselves have no inherent meaning - they are arbitrary integers that initialize the random number generator. There is no relationship between the Seed value and the visual outcome; consecutive Seeds (e.g. 42, 43, 44) give independent, unrelated results. To explore the solution space, increment the Seed by 1 for each variation, or use *Return First* = `false` with *Max Attempts* set to the desired number - the Solver automatically increments the Seed for each Attempt. When a result is satisfactory, record the Seed from the *Seeds* output to reproduce it exactly later. #### Indifferent Faces A single-cell Module has six Faces, one per Direction. A multi-cell Module has an external Face for each footprint Cell that exposes a Direction. To be valid for solving, every Face that appears on a Module allowed in at least one Slot must be referenced by at least one Rule. A Face with no Rule provides no instruction: the Solver cannot determine what that Module may be placed next to. **Indifferent Faces** are Faces with no explicit Rule that are automatically allowed to connect to any other uncovered Face facing the opposite Direction on the same Axis. Construct Assembly generates these pairings and stores their Rules in the Assembly. The effect is equivalent to having manually written a Rule that permits all uncovered Faces to freely connect to each other - which is the same behavior as the Indifferent Rule in Monoceros 1. This behavior is controlled by the **Indifference** input on **Construct Assembly**. When enabled, Construct Assembly generates Indifferent Rules for uncovered Faces before packaging the Assembly. When disabled, any uncovered Face is treated as a hard error. To inspect which Faces are uncovered before solving, use the **Faces Not In Rules** output on the **Audit Assembly** component. Connect those Faces to a Preview Faces component to visualize them in the viewport, or use them as input to a Rule-construction component to add explicit Rules for those pairs. The Audit Assembly report marks uncovered Faces as warnings - not errors - because Construct Assembly can handle them via indifference. **Slots Suitable for Solver** checks only whether the Slot Cells form a complete axis-aligned 3D lattice, with uniform spacing per axis and no gaps, overlaps, or duplicates. Uncovered Faces, unknown Module Names, and Contradictory Slots do not change this output. The Solver's **Report** includes explicit and Indifferent Rules in its total unique Rule count; it does not show the Indifferent count separately. #### Limits The free tier gives the WFC Solver and Growth Solver one shared **limited number of runs per fixed time window** (windows are aligned to UTC clock boundaries); paid tiers (Annual, Edu, Lifetime) are unlimited. One WFC Solver run is charged when **Run** is `true`, the Assembly is valid, and the engine Assembly has been built, immediately before the first Attempt, regardless of **Max Attempts**. The component footer shows a live counter indicating how many runs remain and when the window resets. Regardless of tier, the engine supports at most **16,370 solver Modules**. Every rotation variant and every Cell part of a multi-cell Module counts separately when a Slot allows its name and that name appears in an opposing Rule. Construct Assembly warns and emits an invalid Assembly if this limit is exceeded; the Solver refuses to run that Assembly. Reaching the free-tier run limit instead blocks the Solver before the first Attempt. #### Run input gate The WFC Solver has a mandatory boolean **Run** input (default `false`). The Solver only executes when Run is explicitly set to True, which prevents upstream slider changes from accidentally burning through the free tier's run budget. Connect a Boolean Toggle or a Button component. #### Export Project… Right-click the WFC Solver and select **Export Project…** to write a portable Monoceros project file as JSON with inline OBJ geometry. The file contains the source setup authored in Construct Assembly: Modules and their geometry, Slots, Rules, Connectors, Connector Pairs, Terminators, and Construct Assembly settings. It contains no solved Slot states or WFC Solver inputs such as **Random Seed**. The file is identical before and after solving. The menu item first reads project data from the first Assembly on the **Assemblies** output that carries it. If none carries project data, it reads the Assembly on the input, so you can export before solving. If neither has project data, a `No exportable project found.` dialog asks you to wire an Assembly from Construct Assembly into the solver. Assemblies from older Grasshopper files, or ones for which Construct Assembly reported `Project export and analytics could not be read from the engine: …`, do not carry exportable project data. The save dialog is titled `Export Monoceros Project` and suggests `monoceros-project.json`. The component appends `.json` if the chosen name lacks it. A write failure shows `Failed to write project file:` followed by the reason. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Assembly | `A` | Discrete Assembly | Item | A Discrete Assembly from Construct Assembly. | | Random Seed | `S` | Integer | Item | Seed value for the random number generator. | | Max Attempts | `MA` | Integer | Item | Maximum number of solver attempts to perform. | | Max Observations | `O` | Integer | Item | Maximum number of solver observations per attempt. Leave default for virtually unlimited. | | Return first | `F` | Boolean | Item | True = return first successful result; False = return all successful results. | | Run | `R` | Boolean | Item | Set to True to execute the solver. Defaults to False so that upstream data changes do not trigger accidental solves. Wire a Boolean Toggle or Button component. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Report | `R` | Text | Item | WFC Solver report with diagnostics and statistics. | | Assemblies | `A` | Discrete Assembly | List | One solved Discrete Assembly per successful solution. | | Deterministic | `OK` | Boolean | List | True if the solver found a fully deterministic solution. | | Contradictory | `C` | Boolean | List | True if the solver ended with a contradictory state. | | Seeds | `S` | Integer | List | Random seed of each returned solution, aligned with the Deterministic and Contradictory outputs. When every attempt contradicted, this is the representative contradictory seed (Contradictory is true for it), not a successful one. | | Observations | `O` | Integer | List | Observation count of each returned solution (aligned with Seeds). | | Attempts | `AT` | Integer | Item | Total attempts spent. | #### 4.4.6 Audit Assembly *Main · Nickname: `Audit` · GUID: `46AAA4DD-EE93-4EBC-9045-BDC278A9CD2C`* Analyze a Discrete Assembly for consistency and errors. Reports missing Modules, unfitting Modules, Unused Faces and other issues that may block solving or materialization. Run this component before the Solver to catch setup problems early. #### Behavior Performs comprehensive validation: verifies Slots form a valid grid, cross-references Modules against Slots and Rules, detects orphaned or unused components, identifies dimension mismatches, checks for redundant Rules, self-connecting Modules, and potential over-constraint. The text Report has OVERVIEW, GRID, SLOTS, MODULES, RULES and DISALLOWED FRACTION (LOVÁSZ LOCAL LEMMA) sections; conditional sections such as SELF-LOOP MODULES, POTENTIAL OVER-CONSTRAINT and ALL FACES INDIFFERENT; and a closing TERMINATORS section showing whether Require Terminators is active and which Face directions are covered or missing. Invalid Assemblies are accepted: a Remark gives the reason and the component still fills its outputs. All diagnostic outputs are available. Faces not covered by any Rule are reported as warnings rather than errors because indifference on Construct Assembly (when enabled) automatically generates matching Rules for such Faces. The **Faces Not In Rules** output provides the actual Face objects for visualization or further use. **Allow-all Slot check.** Audit flags any Slots that are still in the allow-all state as an Error - Construct Assembly is expected to resolve them before the Audit runs, so an unresolved Slot indicates that the pipeline was bypassed. Slots that were originally allow-all but have since been resolved are reported as an informational Remark so the user is reminded that the Module set was wired implicitly for those positions. **Face Disallowed Fraction.** A continuous per-Face over-constraint measure, the graded generalization of Singly Constrained Faces and Potential Over-Constraint. For each oriented Face it reports the share of dimension-compatible opposing Module Faces that no Rule permits as a neighbour, from 0 (every fitting neighbour allowed) to 1 (none allowed); a value of -1 marks a Face with no candidates because its axis is a single-Slot grid axis, so it can only meet the boundary. The Report adds one honest summary line from this: the worst Face, its fraction, and a conservative Lovász Local Lemma solvable annotation with its margin - an annotation only, never a solvability guarantee, since it reads false for nearly every authored config. **Diagnose Resolution.** Off by default so the Audit stays fast. When enabled, Audit runs a deterministic diagnostic solve (Seed 0) with resolution tracking and reports on the **Resolution** output how the Solver settles the Envelope: how many Slots it directly **observed** (chose and collapsed, with the observe order), how many **Propagation** resolved as a side effect, and how many were left unresolved, then one line per resolved Slot as `[order] KIND slot@(x,y,z) module`. This is the same per-Slot provenance the engine emits after any solve, so you (or an LLM) can see the actual order the Envelope collapsed and locate where a solve stalls. It is most informative on an unsolved Assembly straight from Construct Assembly; an already-solved input reports every Slot as Propagation, because its initial Canonicalization determines every Slot before any Observation. **Rules Never Firing.** Lists every authored Allowed, Disallowed and Exclusive Rule the Solver can never apply, each tagged with its kind: no allowed rotation of its two Modules brings the two Faces into opposition (for example a +X Face against a +Z Face on Modules without Rotational Freedom), or the two Modules are never allowed in adjacent Slots. The engine decides this per Rule on the rotation-expanded pairs and the Slot layout, the same check Construct Assembly uses when Clean Up is on. The Report adds one row to its Rules section with the count per kind. Such Rules are normal when Modules cannot rotate into each other: the Solver ignores them, and no component shows a warning or error for them. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Assembly | `A` | Discrete Assembly | Item | A Discrete Assembly from Construct Assembly or the Solver. | | Diagnose Resolution | `Res` | Boolean | Item | When true, runs a deterministic diagnostic solve (seed 0) with core resolution tracking and reports on the Resolution output how each Slot settled: observed (the solver's own choice, with observe order), propagated (constraint propagation resolved it), or unresolved. Off by default so the audit stays fast. Most informative on an unsolved Assembly (from Construct Assembly); an already-solved input reports every Slot propagated. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Report | `R` | Text | Item | Human-readable audit report grouping every finding into Grid, Slots, Modules, Rules and warning sections. Paste into a panel to read the full diagnostic. All structured outputs below are derived from the same analysis. | | Slots Suitable for Solver | `SlotsInGrid` | Boolean | Item | True if the Slot Cells form a complete axis-aligned 3D lattice the WFC Solver can consume: uniform spacing per axis, no gaps, no overlaps, no duplicates. False means the Envelope is not shaped as a regular grid and the Solver will refuse it. | | Grid Block Dimensions | `GridDim` | Vector | Item | Integer count of Slots along each world axis, returned as a Vector {X, Y, Z}. For a 10 × 5 × 3 Envelope this is {10, 5, 3}. Zero components mean no Slots were detected on that axis. | | Is Grid Homogeneous | `HomoGrid` | Boolean | Item | True when every Slot has identical box dimensions. False means the Envelope mixes Slot sizes — Monoceros still supports this (heterogeneous grids), but every Module Variant must match one of the Slot sizes or it cannot be placed there. | | Slots Accommodating All Modules | `SlotsAccommodatingAll` | Integer | List | Indices of Slots whose Allowed Module Names list is a superset of every unique Module Name in the Assembly. Such Slots impose no per-Slot constraint on which Modules can be placed — the Solver picks freely based on adjacency Rules and neighboring Slots. Includes Slots that were constructed as allow-all (no Module Names wired on Construct Slot) and Slots manually wired with the full Module Name list. Contradictory Slots are excluded by construction. In heterogeneous Envelopes allow-all Slots may be absent from this list because their Module Names were filtered down to the dimensionally-fitting subset during Construct Assembly resolution. | | Slots Without Geometry | `SlotsWithoutGeo` | Integer | List | Indices of Slots whose allowed Module list contains only geometry-less Modules (e.g. “empty” or “void” placeholders). Usually intentional — boundary, filler or structural-only Slots — but flagged so you can verify. | | Slots Accommodating Unknown Modules | `SlotsUnknownMod` | Integer | List | Indices of Slots whose Allowed Module Names list references Module names that are not present in the Assembly’s Module list. Typically a typo or a missed merge on the canvas. | | Slots Without Fitting Modules | `SlotsUnfittingMod` | Integer | List | Indices of Slots for which no Module Variant has box dimensions matching the Slot’s dimensions. Such a Slot cannot be filled at all and will produce a contradiction during solving. Fix by adding a fitting Module Variant or by changing the Slot’s Cell dimensions. | | Slots With More Fitting Modules | `SlotsMoreMod` | Integer | List | Indices of deterministic Slots (a single allowed Module name) that still have multiple Module Variants sharing that name and matching the Slot’s dimensions. On Materialize, every matching Variant is placed in the Slot — usually desirable for “swap-in alternatives”, occasionally a bug if you expected exactly one Variant. | | Domain Of Module Weights | `Weights` | Interval | Item | Interval {min, max} of all per-Module weights found across all Slots. When min == max == 1.0 all Modules are uniformly weighted and the Solver uses its cheap unweighted path; otherwise weighted entropy / weighted observation kicks in. | | Module Names | `ModName` | Module Name | List | Deduplicated, alphabetically sorted list of every Module name present in the Assembly. All per-name outputs below (Module Variants, Module Name Count In Slots, ...) share this index ordering — branch {i} corresponds to name {i}. | | Module Variants | `ModVar` | Integer | Tree | Per-name tree. Branch {i} lists the indices (into the Assembly’s flat Module list) of every Variant sharing Module Name {i}. Variants are Modules that share a name but have different box dimensions — note that rotation does NOT produce Variants, rotated Modules get new names (e.g. “wall_z90”). Variants typically come from manually constructing several same-name Modules with different Cell sizes. | | Module Name Count In Slots | `ModNamInSlots` | Integer | Tree | Per-name tree. Branch {i} contains a single integer: the number of Slots whose Allowed Module Names list contains Module Name {i}. Zero means the name is declared but never allowed anywhere. | | Module Variant Count In Slots | `ModInSlots` | Integer | Tree | Per-Variant tree. Branch {i} contains a single integer: the number of Slots in which Module Variant {i} is actually allowed (after dimension filtering). Zero means the Variant is referenced but never fits anywhere. | | Module Variant Never In Slots | `ModNotInSlots` | Integer | List | Indices of Module Variants that are not allowed in any Slot. Candidates for removal from the Assembly — they cannot be placed, so rules involving them are dead weight. | | Module Variant Never In Rules | `ModNotInRules` | Integer | List | Indices of Module Variants not referenced by any adjacency Rule (explicit or indifference). Such Variants have no valid neighbors and will either never be placed or trigger contradictions when the Solver tries. | | Module Variant Face Never In Rules | `ModFaceNotInRules` | Integer | List | Indices of Module Variants that have at least one Face unreferenced by any Rule. With Indifference enabled, faces that have an opposing unused face on the same axis are auto-paired. Faces that still appear here despite Indifference have no opposing unused face on that axis, so they cannot be covered by any rule. Modules listed here can never be placed by the solver. With Indifference off, any uncovered face makes the Assembly invalid (Construct Assembly will report an error). | | Modules With Geometry | `ModWithGeo` | Integer | Tree | Per-name tree. Branch {i} lists the indices of Module Variants of Module Name {i} that carry geometry (non-empty Module.Geometry list). | | Modules Without Geometry | `ModWithoutGeo` | Integer | Tree | Per-name tree. Branch {i} lists the indices of Module Variants of Module Name {i} that carry no geometry. Valid and common for void / filler Modules. | | Modules With And Without Geometry | `ModWithWithoutGeo` | Integer | Tree | Per-name tree. Branch {i} is non-empty only for Module Names where some Variants carry geometry and others do not. Almost always a canvas mistake — Modules sharing a Name should behave identically, so they should either all carry geometry or all be geometry-less. | | Modules With Identical Names and Dimensions | `ModIdentDim` | Integer | Tree | Per-name tree. Branch {i} lists the indices of Module Variants that share both the same Module Name {i} AND the same box dimensions. Construct Assembly rejects these upstream as true duplicates; this output is here as a diagnostic in case an Assembly bypassed that check. | | Modules Allowed in Unfitting Slots | `ModInUnfitSlots` | Integer | Tree | Per-name tree. Branch {i} lists the indices of Slots that allow Module Name {i} but have no Variant of that name matching the Slot’s dimensions. Symptoms: dimension mismatch between Construct Slot and Construct Module. | | Module Faces | `ModFace` | Face ID | Tree | Per-Variant tree. Branch {i} contains the 6 FaceIds of Module Variant {i} in canonical order (+X, +Y, +Z, -X, -Y, -Z). Pair with Module Faces Use Count to see which Faces are referenced by Rules and which are not. | | Module Faces Use Count | `ModFaceUse` | Integer | Tree | Per-Variant tree of integers parallel to Module Faces. Branch {i}, item {k} is the number of Rules that reference face {k} of Module Variant {i}. Zero means the face is unreferenced (will be auto-paired by Indifference if it is enabled). | | Rules Referring Unknown Modules | `RulUnknownMod` | Integer | List | Indices (into the Assembly’s Rule list) of Rules whose source or target Module Name does not exist among the declared Modules. Usually a typo or a missed merge. | | Rules Referring Unused Modules | `RulUnusedMod` | Integer | List | Indices of Rules whose source or target Module Name is declared but is not allowed in any Slot. The Rule is dead weight — it can never fire during solving. | | Rule Occurrence Count | `RulCount` | Integer | List | Parallel to the Assembly’s Rule list. Entry {i} is the number of times Rule {i} occurs in the input Rule list before deduplication. Useful to spot unintended duplicate wiring. | | Is Rule's First Occurrence | `RulFirst` | Boolean | List | Is Rule’s First Occurrence. Parallel boolean mask to the Assembly’s Rule list. True only for the first occurrence of each distinct Rule. Use Cull Pattern on the Rule list with this mask to obtain a deduplicated Rule list without relying on the Solver. | | Modules With Only Self-Connecting Rules | `ModSelfOnly` | Integer | List | Indices of Module Variants whose only adjacency Rules pair them with themselves. Such Variants can be placed only next to other copies of themselves — typically a mistake unless you are intentionally building isolated clusters. | | Potential Over-Constraint | `OverConstrained` | Boolean | Item | True when at least one placed Module Variant has a Face not referenced by any Rule. Construct Assembly’s Indifference step auto-pairs those Faces; with Indifference off this flag indicates the Assembly is invalid and the WFC Solver refuses to run it. | | Faces Not In Rules | `FaceNotInRules` | Face ID | List | Flat list of every Module FaceId that appears on a placed Module Variant but is not referenced by any Rule. Visualize these with Preview Faces to see where the uncovered faces are, then decide whether to wire them explicitly or rely on Indifference. | | All Faces Indifferent | `AllIndiff` | Boolean | Item | True when no Face of any placed Module Variant is referenced by any explicit Rule (only indifference rules exist). The resulting solve is effectively a random fill — every arrangement is valid. | | Effectively Indifferent Faces | `EffectIndiff` | Face ID | List | Flat list of Module FaceIds which, through the final Rule set, end up paired with every opposing Face on the same axis. They behave identically to Indifferent Faces during solving even if they were wired explicitly — a sign that the explicit Rules are redundant. | | Singly Constrained Faces | `SingleCon` | Face ID | List | Flat list of Module FaceIds referenced by exactly one Rule. These are potential over-constraint hot spots — if the single matching neighbor is eliminated during propagation, the whole Slot becomes contradictory. | | Require Terminators Active | `ReqTerm` | Boolean | Item | True if Require Terminators was enabled on Construct Assembly, so exposed Slot faces on non-flat axes are restricted to Modules that carry a Terminator on the matching face. False means the Terminator Faces Covered / Missing outputs are empty. | | Terminator Faces Covered | `TermCovered` | Face Index | List | Face directions (+X, +Y, +Z, -X, -Y, -Z) that have at least one Terminator. Only populated when Require Terminators is Active. If any of the six directions is missing (see Terminator Faces Missing), every module face on that side of the envelope is forbidden from touching the boundary — the solver is likely to contradict. | | Terminator Faces Missing | `TermMissing` | Face Index | List | Face directions (+X, +Y, +Z, -X, -Y, -Z) that have no Terminator. Only populated when Require Terminators is Active. An empty list means all six face directions are covered and any module face can be placed against the boundary. A non-empty list flags boundary directions where no module face is allowed to touch the outer layer. | | Indifference Rule Count | `IndiffRules` | Integer | Item | Number of indifference (auto-pairing) rules added by Construct Assembly to cover Module Faces that had no explicit or Connector-generated Rule. A large count relative to explicit Rules means the Assembly is loosely constrained and the Solver has maximum placement freedom. Zero means every face was explicitly covered — the rule set is fully authored. | | Allow-All Slot Count | `AllowAllSlots` | Integer | Item | Number of Slots that were originally constructed without an explicit Module Names list (Construct Slot received no Module Names input) and were automatically resolved to allow all dimensionally fitting Modules by Construct Assembly. A high count is normal for an open Envelope; zero means every Slot was explicitly wired with a Module Names list. | | Rule Graph Components | `RGComp` | Module Name | Tree | Data tree where each branch (path {i}) contains the Module Names that form one connected component of the co-occurrence rule graph. Two Modules are in the same component when at least one explicit rule connects them (directly or transitively). Modules with no rules appear as isolated single-item branches. A fully connected rule set produces a single branch containing all Modules. Multiple branches indicate groups of Modules that are rule-isolated from each other, which often signals a design error or an intentional sub-assembly. | | Analytics | `JSON` | Text | Item | Structured engine analytics as a JSON string: rule provenance (explicit / rotation / connector / indifference counts and index lists), connector analytics (counts, by name, unmatched pairs, per axis, per module), connector symmetry, rotation expansion, a cheap solver workload estimate (state cells, branching factor, adjacency density, contradiction risk), and slot topology (allowed-count histogram, interior/face/edge/corner classification, never-reachable modules). Computed by Construct Assembly from the model inputs. Parse with a JSON component or any downstream tool. | | Extending Rules | `ExtRules` | Text | List | Rules (allowed, disallowed, or exclusive) that name a Module the Assembly does not contain. These are allowed - they extend past the current Module set and the solver ignores them - and are listed here so a typo is easy to spot. Each entry is tagged with the input it came from. | | Extending Connectors | `ExtConn` | Text | List | Connectors bound to a Module the Assembly does not contain. Allowed and ignored by the solver; listed here to catch a typo. | | Extending Connector Pairs | `ExtConnPairs` | Text | List | Connector Pairs (allowed, disallowed, or exclusive) that name a Connector no Connector defines. Allowed and ignored; listed here to catch a typo. | | Face Disallowed Fraction | `FaceDisallow` | Number | Tree | Per-Variant tree of numbers parallel to Module Faces. Branch {i}, item {k} is the disallowed fraction of face {k} of Module Variant {i}: the share of dimension-compatible opposing Module faces that no Rule permits as a neighbour, in [0, 1]. 0 means every fitting neighbour is allowed; 1 means none is. A value of -1 marks a Face with no candidates (its axis is a single-slot grid axis, so it can only meet the boundary) - a distinct state, never 1. This is the graded generalization of Singly Constrained Faces and Potential Over-Constraint; the worst face feeds the Lovász Local Lemma annotation in the Report. | | Resolution | `Res` | Text | Item | Empty unless Diagnose Resolution is true. When enabled, a human-readable report of a deterministic diagnostic solve (seed 0): how many Slots the solver observed / propagated / left unresolved, then one line per resolved Slot as [order] KIND slot@(x,y,z) module, where order is the 1-based observe/propagation step. Lets you (or an LLM) see the actual order the solver settled the Envelope. | | Rules Never Firing | `RulNeverFire` | Text | List | Authored Allowed, Disallowed and Exclusive Rules the solver can never apply: no allowed rotation of their Modules brings the two faces into opposition, or the two Modules are never allowed in adjacent Slots. Each entry is tagged with its kind. The solver ignores these Rules; this is normal when Modules cannot rotate into each other and is not an error. | #### 4.4.7 Materialize Assembly *Main · Nickname: `Materialize` · GUID: `98CB702E-08F4-433B-97FA-BF860DA95685`* Extract placed geometry from a solved Discrete Assembly. Takes an Assembly as input and produces geometry. For every Deterministic Slot, it looks up the assigned Module (including rotation variants), finds the variant whose dimensions match the Slot’s cell box, and moves and orients the Module geometry into place. The output is ready to bake, render, or process further in Grasshopper. #### Behavior Each Deterministic Slot is matched to the Module Variant whose name and cell dimensions agree. The Module geometry is transformed from its original position into the Slot’s position and Orientation via a plane-to-plane mapping. Non-deterministic Slots (still carrying multiple candidates) are skipped. Contradictory Slots (zero allowed Modules) produce no geometry. When multiple Module Variants share the same name and dimensions, only the first matching Variant in Assembly order is placed. The others are unused. The **Modules With Identical Names and Dimensions** output of **Audit Assembly** flags these duplicates. **Multi-cell Modules materialize once per placed instance.** A multi-cell Module occupies one Slot per footprint cell; the engine reports which placed cells belong to which instance (grouping them by Module, rotation and anchor cell) and the Module's geometry is emitted exactly once per instance. Every cell carries the whole Module, so an instance cut by an open Envelope boundary materializes from whichever cells are inside, with the geometry overhanging the Envelope where the missing cells would sit. Each output tree path is `{assembly iteration; slot index}`; for a multi-cell instance, this is the Slot index of its anchor cell, the lowest-numbered footprint cell present inside the Envelope. A Contradictory Slot costs only its own instance: instances elsewhere in the Envelope still materialize. The component warns about skipped Contradictory or Non-deterministic Slots and reports unknown Module Names or missing dimension matches as errors. Baking creates one shared Rhino block definition per placed Module Variant and one block instance per placement. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Assembly | `A` | Discrete Assembly | Item | Solved (or partially solved) Discrete Assembly. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Geometry | `G` | Geometry | Tree | Placed module geometry. | | Transforms | `T` | Transform | Tree | Placement transforms per slot. | | Modules | `M` | Module | Tree | Module placed in each slot, oriented to slot position. | #### 4.4.8 Sample Geometry *Main · Nickname: `SampleGeo` · GUID: `13C40CD2-DBF4-4221-8AF9-6E15AD72AFD3`* Create Module candidates by sampling input geometry into Cells. Chops geometry to Slot Envelopes, voxelizes for comparison, and deduplicates identical Modules. Returns Modules ready for Face analysis and Rule construction. #### Behavior For each Cell, chops geometry (curve–brep intersection, mesh splitting, brep trimming) in parallel. Voxelizes each nonempty result and deduplicates by comparing voxel patterns and box dimensions (epsilon comparison). Each new Module is named with **Module Name Base** followed by its Cell index (for example, `module-0`); a matching result reuses the earlier Module Name and increments **Module Count**. Only Cells containing geometry produce a Deterministic Slot with Weight `1`. Empty Cells produce neither a Module nor a Slot and have `-1` in **Box to Module Index** and **Box to Slot Index**. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Module Name Base | `ModNamBas` | Text | Item | Base string used when creating new Module names; a numeric counter is appended automatically to ensure uniqueness. | | Voxel Resolution | `V` | Vector | Item | Number of voxels in each direction of the module Cell. Monoceros 3 considers two modules identical when their voxelized geometry and module Cell dimensions match. Higher resolution detects finer differences but increases processing time and may amplify rounding differences. The default value is suitable for most cases. | | Geometry | `G` | Geometry | List | Input Geometry. Geometry to be sampled and converted into Module candidates. Supported types: Point, Curve, Brep, Mesh. | | Sampling Cells | `B` | Cell | List | Cells used to chop and sample geometry when creating module candidates. Provide one Cell per branch. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | Unique Module candidates extracted from the sampled geometry. | | Module Count | `MC` | Integer | List | Number of module instances derived from the input sample (one per input geometry where applicable). | | Slots | `S` | Slot | List | Slot instances created along with module placements from the sample. | | Box to Module Index | `B2MI` | Integer | List | For each input box, the index of the Module it produced or -1 if none. | | Box to Slot Index | `B2SI` | Integer | List | For each input box, the index of the Slot it produced or -1 if none. | ### 4.5 Module #### 4.5.1 Construct Module *Module · Nickname: `ConstModule` · GUID: `C7632F05-C933-4D85-A65F-F8AF29CEDCBC`* Construct a Module from optional Module Name, one or more adjacent Cells, and optional Geometry. Modules are the building blocks placed into Slots during WFC solving. A single-cell Module has six Faces; a multi-cell Module has externally visible Faces numbered per direction and Cell, such as `+X0` and `+X1`. #### Behavior Several adjacent Cells form a multi-cell Module that the Solver places as one rigid body; a Remark reports its dimensions, Cell count and external Face count. When Module Name is not wired, a deterministic name with the prefix `m-` and five letters is generated per component instance and reported in a Remark. Names are lowercased. Coinciding Cells, Cells that do not form a grid or one face-connected block, and Rotational Freedom outside `0`-`4` are Errors. Invalid Geometry items are removed with an Error; an empty Geometry list produces a Remark. Tracks geometry GUIDs. Reads Rotational Freedom (`0` None, `1` X, `2` Y, `3` Z, `4` Full) and stores it on the Module. The engine generates rotation variants during the Construct Assembly build, remapping Rules and Face Indices accordingly. The expanded variants are available via Dissolve Assembly. Warns if Geometry leaves the combined bounding box of the Cells. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Module Name | `MN` | Module Name | Item | The module name; will be converted to lowercase. If not provided, a deterministic name is auto-generated from the component instance. (Optional) | | Cells | `B` | Cell | List | One or more adjacent Cells that contain the Module geometry and define its footprint. A single box makes an ordinary single-cell Module; several adjacent boxes make a multi-cell Module whose cells the Solver places as one rigid body. | | Geometry | `G` | Geometry | List | Geometry used to materialize the Solver result. The Module geometry does not have to fit into the module Cell and can be larger, smaller, different or empty. (Optional) | | Rotational Freedom | `R` | Integer | Item | How much the Solver may rotate this Module. Each option is a closed set of orientations: 0 = None (no rotation) 1 = X only (4 rotations about X) 2 = Y only (4 rotations about Y) 3 = Z only (4 rotations about Z) 4 = Full (all 24 cube orientations) Any two axes already generate the full 24, so there is no two-axis option. Right-click to choose directly. Defaults to 0 (None). | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Module | `M` | Module | Item | Constructed Module instance. | #### 4.5.2 Deconstruct Module *Module · Nickname: `DeconModule` · GUID: `7BA8813F-E38B-4B6E-ABFD-C35A6ADAEF9E`* Extract the components of a Module: name, Cells (one per footprint cell), external Faces, geometry, Footprint, Cell Count, and validity flag. #### Behavior Outputs one Cell per footprint cell and the Module's externally visible Faces - the faces its own footprint does not cover, numbered per direction. Footprint gives each Cell's integer grid offset relative to the anchor Cell, parallel to Cells; Cell Count gives the number of Cells. The lock rules binding the cells are engine-internal and are not reported. An invalid Module raises an Error and produces no outputs, so Is Valid is true whenever it is output. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Module | `MM` | Module | Item | A Module to deconstruct. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Module Name | `MN` | Module Name | List | Module name (converted to lowercase). | | Cells | `B` | Cell | List | The Cell of every cell this Module occupies, parallel to Footprint. A single-cell Module reports one. | | Geometry | `G` | Geometry | List | Geometry contained in the module. | | Is Valid | `V` | Boolean | List | True if the Module is valid for the Solver. | | Faces | `F` | Face ID | List | All FaceId entries for this Module. | | Footprint | `FP` | Vector | List | The integer grid offset of every cell this Module occupies, relative to its anchor cell. A single-cell Module reports one {0,0,0}; the entries are parallel to Cells. | | Cell Count | `N` | Integer | Item | How many grid cells this Module occupies. | #### 4.5.3 Cull Duplicate Modules *Module · Nickname: `CulDupModule` · GUID: `30163A89-56A1-43DB-BB94-B5FA14E04FCB`* Remove duplicate Modules by comparing voxelized geometry and box dimensions. Use after generating rotated variants or importing from multiple sources. #### Behavior Voxelizes each Module in parallel and compares voxel patterns and box dimensions using epsilon comparison. A multi-cell Module voxelizes across its whole footprint at scaled resolution, so the comparison sees the combined shape. Tracks index mapping and valence. If Rules provided, remaps the source/target Module Name of each culled Module to its survivor, then deduplicates the resulting Rule set. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | Module variants to identify and remove duplicates. Multi-cell modules are compared by their full combined shape (relative part positions + geometry). | | Voxel Resolution | `V` | Vector | Item | Number of voxels in each direction of the module Cell. Monoceros 3 considers two modules identical when voxelized geometry and module Cell dimensions are identical. Higher resolution detects finer differences but increases processing time and may amplify rounding imprecision. The default value is suitable for most cases. | | Rules | `R` | Rule | List | Rules (optional). Rule definitions to remap to culled modules. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | Culled unique Module variants. | | Indices | `I` | Integer | List | Index map showing which input modules are represented by each output module. | | Valence | `V` | Integer | List | Number of input modules represented by each culled module. | | Rules | `RD` | Rule | List | Rule definitions remapped to the culled unique modules. | #### 4.5.4 Module Rotations *Module · Nickname: `ModuleRot` · GUID: `B63AECD6-8B87-4EFE-A2D7-8A5B7AFEAF3B`* Generate rotated variants of a Module from a single Rotational Freedom choice. The integer input (selectable directly from its right-click menu) is 0 None (1 orientation), 1 X / 2 Y / 3 Z (the four 90° rotations about that axis), or 4 Full (all 24 cube orientations). It defaults to 4 (Full). Optionally deduplicates identical variants by voxel comparison. Each option is a closed set of orientations. There is no two-axis option: any two perpendicular axes already generate the full 24, so anything beyond a single axis is Full. #### Behavior Builds the rotation and translation transform for each orientation in the chosen set. Each variant rotates about the Module's pivot, then moves along the Module's X axis by its position in the chosen set times twice the largest bounding-box dimension, placing variants side by side. Creates rotated geometry in parallel. A multi-cell Module rotates as one rigid body: its cells and its faces move together. **Cull Duplicates** defaults to `true` and uses **Voxel Resolution** (default 16 × 16 × 16) to compare voxel patterns and box dimensions, keeping the first of each distinct variant, including the unrotated variant. A Module without geometry is returned unchanged with a remark. Variants are anonymous: each carries the authored Module name plus a rotation index (its orientation among the 24, 0 the unrotated one), not a name suffix. Construct Assembly merges variants that share a name and a source Module into one Module whose allowed orientations are exactly their rotation indices, together with the Module's own Rotational Freedom. Filter the output to keep only some orientations, but keep the unrotated variant. Rules, Connectors and Slots name the Module and refer to its unrotated faces; Construct Assembly rotates them with it. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Module | `MM` | Module | Item | A Module whose geometry and structure will be rotated. All parts rotate as a group; internal rules and external faces are recomputed for the rotated orientation. | | Rotational Freedom | `R` | Integer | Item | How much a module may rotate. Each option is a closed set of orientations: 0 = None (1 orientation) 1 = X only (4 rotations about X) 2 = Y only (4 rotations about Y) 3 = Z only (4 rotations about Z) 4 = Full (all 24 cube orientations) Any two axes already generate the full 24, so there is no two-axis option. Right-click to choose directly. Defaults to 4 (Full). | | Cull Duplicates | `C` | Boolean | Item | Remove rotated Modules with identical geometry. | | Voxel Resolution | `V` | Vector | Item | Number of voxels in each direction of the module Cell. Monoceros 3 considers two modules identical when voxelized geometry and module Cell dimensions are identical. Higher resolution detects finer differences but increases processing time and may amplify rounding imprecision. The default value is suitable for most cases. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | Rotated Module variants. | ### 4.6 Rule #### 4.6.1 Construct Rules from Faces *Rule · Nickname: `RuleFromFaces` · GUID: `776B33CD-9A14-4C5B-9F6B-A8DAB13CA0C9`* Construct Rules from two Face lists. By default (Cross Match = true), every Face in the Source list is paired with every Face in the Target list (cross product). Set Cross Match to false for element-wise pairing (first with first, second with second). This is the most common way to define Rules. #### Behavior Both opposing and non-opposing Face pairs are emitted. A non-opposing rule (e.g. `A:+X → B:+Y`) is a connector-symmetry hint that Construct Assembly expands into opposing-face rules referencing rotation variants of the target module. Null or invalid Faces, including a Module wired instead of a Face, cause an error that lists their positions; no Rules are output. With **Cross Match** set to `false`, unequal list lengths produce a warning and only as many pairs as the shorter list has are used. Duplicate Rules are removed with a remark; no Rules produces a warning. Only accepts explicit FaceId inputs. To pass all six faces of a Module, extract them first using Get Module Faces. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Source Faces | `SF` | Face ID | List | Source-side Faces for rule construction, obtained from the Get Module Faces component. | | Target Faces | `TF` | Face ID | List | Target-side Faces for rule construction, obtained from the Get Module Faces component. | | Cross Match | `X` | Boolean | Item | When true (default), creates all combinations of Source x Target faces. When false, pairs faces element-wise by index. Default: true. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Rule | `R` | Rule | List | Generated Rule instances from the face pairing logic. | #### 4.6.2 Deconstruct Rule *Rule · Nickname: `DeconRuleFaces` · GUID: `CF150771-C67D-4287-A445-6466903DDB3C`* Deconstruct a Rule into its source and target FaceId entries. #### Behavior Extracts and outputs the SourceFaceId and TargetFaceId from the Rule. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Rule | `R` | Rule | Item | The Rule to deconstruct into its face components. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Source Face | `SF` | Face ID | Item | The source-side FaceId extracted from the provided Rule. | | Target Face | `TF` | Face ID | Item | The target-side FaceId extracted from the provided Rule. | #### 4.6.3 Are Rules Equal *Rule · Nickname: `AreRulesEq` · GUID: `F6F56887-F432-4D0D-817B-E50C1A5EDBCC`* Compare two Rules for equality. #### Behavior Compares Rules using the `Equals` method (bidirectional: A→B equals B→A). Module Name, Direction, and Cell ordinal must match, so a whole-side Face and a numbered Face are not equal. Null or invalid Rules produce an error. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Rule A | `RA` | Rule | Item | First Rule to compare. | | Rule B | `RB` | Rule | Item | Second Rule to compare against Rule A. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Equal | `E` | Boolean | Item | true when both provided Rules are equivalent. | #### 4.6.4 Detect Rules from Geometry *Rule · Nickname: `DetRulesGeo` · GUID: `B76336DF-5A54-484D-B29D-478D51311730`* Analyse Module Face geometry (naked edges, curves, points) and directly output Rules for all geometrically matching pairs. Combines Face suggestion and Rule construction in one step. #### Behavior Collects naked edge polylines from Breps/Meshes, open curve endpoints and point geometry. Projects onto Face planes. Compares all Face pairs, matching by equal Face dimensions and epsilon-equal point lists; there is no opposing-direction filter, and non-opposing matches are connector-symmetry hints expanded by Construct Assembly. Detected Rules are visualised in the viewport as bezier curves, using the same colour coding as Preview Rule. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | Only external faces participate in geometry matching. Provide a flattened list. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Rules | `R` | Rule | List | Suggested Rule instances derived from geometrically matching face patterns. | #### 4.6.5 Detect Rules from Voxels *Rule · Nickname: `DetRulesVox` · GUID: `2B53DFD4-35FD-4833-9983-4A1BE10784DE`* Suggest Rules by comparing voxel fingerprints across opposite-facing Module Faces on the same axis. Uses Face-local bidirectional ray scanning to build voxel fingerprints and outputs a Rule for each match. Combines the fingerprint step and Rule construction into one component. #### Behavior Shoots rays from each Face plane in both Directions (inward and outward) on a uniform grid. Compares fingerprints only for opposite-facing Faces on the same axis, trying four 90-degree in-plane rotations. Outputs a Rule when the fingerprints match under one of those rotations. Matched Faces display wireframe voxel meshes in the viewport, coloured by Axis (X = red, Y = green, Z = blue). Fingerprint building is parallelised per Module. Bezier curves connecting each matched Face pair are also drawn in the neutral colour used by Preview Rule. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | Only external faces are voxelized and analysed for face matches. Provide a flattened list. | | Voxel Dimension | `VD` | Vector | Item | Number of voxels per module axis (X, Y, Z). Each component specifies how many voxels fit into the module in the respective direction. The face grid resolution is derived from the two components that span the face. | | Precision | `P` | Integer | Item | Number of rays cast per cell. Higher values detect smaller features with higher accuracy but reduce speed. | | Inner Depth | `ID` | Number | Item | Scan depth inside the Cell as a fraction of the module depth along the face normal. 0.5 = half the module depth, 1.0 = full depth. | | Outer Depth | `OD` | Number | Item | Scan depth outside the Cell as a fraction of the module depth along the face normal. 0.0 = flush with the face. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Rules | `R` | Rule | List | Suggested Rule instances derived from voxel-matched face patterns. | #### 4.6.6 Rule from Curve *Rule · Nickname: `RuleFromCurve` · GUID: `D27F7EF1-BE68-41A2-9C7B-C640982F03A3`* Create Rules from curves drawn between Module Faces. Each curve’s endpoints are matched to Faces that contain the point. A single curve can produce multiple Rules when endpoints overlap Faces from different Modules. #### Behavior Extracts start/end points from each curve. Finds all Faces that contain the endpoint (same logic as Faces From Point). Cross-references all start/end matches and creates a Rule for every distinct endpoint pair. Non-opposing pairs (e.g. `a:+X → b:+Y`) are emitted as connector-symmetry hints that Construct Assembly expands into opposing-face Rules through rotation expansion. Endpoints are matched with a plane-distance tolerance. Matched curves preview in the Axis colour of the first source Face found and can be baked to the current Rhino layer with that colour. Null curves produce an error; curves that produce no Rules produce a warning. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Curves | `C` | Curve | List | Lines or curves drawn between two module faces. The start and end points are matched to module faces that contain the point. | | Modules | `M` | Module | List | All available Modules to search for faces. Only external faces participate in point matching. Provide a flattened list. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Rules | `R` | Rule | List | Generated Rule instances from the curve endpoint face pairings. | #### 4.6.7 Rule from Points *Rule · Nickname: `RuleFromPts` · GUID: `AE541D60-58F1-4C80-986A-786825839590`* Create Rules by cross-referencing Source Points with Target Points placed on Module faces. Each source point is paired with each target point (cross reference). For longest-list matching, graft both inputs. #### Behavior Uses point-containment logic (same as Faces From Point) to identify which Face each point lands on. Pairs Source Points and Target Points branches by their position in each tree, regardless of branch path; an unmatched branch is skipped with a warning. For each branch pair, resolves point locations to Faces, then cross-references the resulting face lists. Every distinct Face pair produces a Rule; non-opposing pairs are connector-symmetry hints that Construct Assembly expands into opposing-face Rules through rotation expansion. Null points produce an error and are skipped. Points are matched with a plane-distance tolerance, and points that do not land on any Face produce a warning. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | Only external faces participate in point matching. Provide a flattened list. | | Source Points | `SP` | Point | Tree | Data tree of points placed on module faces. Each branch is cross-referenced with the corresponding Target Points branch. Graft both inputs for longest-list matching. | | Target Points | `TP` | Point | Tree | Data tree of points placed on module faces. Branch N faces are paired with Source Points branch N faces. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Rules | `R` | Rule | List | Generated Rule instances from the source/target point pairings. | #### 4.6.8 Preview Rule *Rule · Nickname: `RulePreview` · GUID: `06499B7D-102B-4E21-81C9-775357CDD78E`* Display Rules as curves connecting individual Faces for visual verification and baking. Display-only component (no geometry outputs). #### Behavior Finds matching source and target Module Faces for each Rule, including referenced Cells of multi-cell Modules. Draws a cubic Bezier curve between Face centers, with tangent handles along the Faces' outward normals, in a neutral colour. Duplicate Rules in a branch draw once. The component receives a flat Rule list with no allowed/disallowed/indifferent provenance, so it does not paint the semantic palette. Only Construct Assembly, which knows each Rule's kind, colours wires green (allowed), red (disallowed), and blue (indifferent), matching the web Rules graph. Baking adds the preview curves to the current Rhino layer with their curve colour. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Rules | `R` | Rule | Tree | Rules to preview. | | Modules | `M` | Module | List | All existing Modules to display with rules. Provide a flattened list. | ### 4.7 Slot #### 4.7.1 Construct Slot *Slot · Nickname: `SlotConstruct` · GUID: `11183213-3BDA-40CD-AF9B-89845DA484EC`* Create a Slot with allowed Module Names and optional Weights. Assemble from a Cell and a set of allowed ModuleNames. The Allowed Module Names input is optional: when omitted, the Slot is created in an “allow all” state and Construct Assembly resolves it to the authored Module Names whose Cells fit the Slot's Cell size. #### Behavior Validates branch matching between Cells, Module Names and Weights. Defaults Weights to 1.0 if not provided; extends the Weight list with the last value if fewer Weights than names. Creates Slot instances with validated inputs. A Weight of 0 or less removes the Module from the Slot entirely, turning the Weight into a hard constraint (a Remark reports how many Modules were removed). If the Cell input tree is empty, a Warning is shown and no Slots are produced. **Allow-all Slots.** If Allowed Module Names is not connected, the component emits the Remark *“No Module Names provided. Slot allows all Modules. The allowed set will be resolved by Construct Assembly.”* and builds a Slot with an empty Module list. Any Weights connected alongside an empty Module Names input are ignored (a Remark is emitted). The Slot uses the allow-all end of the colour gradient and displays `All` on its visible labels until Construct Assembly resolves it to the deduplicated final Module Name list with uniform Weights. **AllModulesCount is no longer a user input.** Every Slot leaves Construct Slot with `AllModulesCount = 0` (the total Module count is not yet known). The viewport labels appear on three Cell faces when the Slot is large enough on screen; a Slot with several named Modules shows its allowed-Module count without `/ total`, while a Slot with one Module Name shows that name. Once the Slots pass through Construct Assembly, `AllModulesCount` is stamped with the number of distinct Module Names referenced by any Slot and the preview shows `allowed / total` for Slots with several named Modules. No cross-component wiring is required to keep Entropy percentages consistent. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Cell | `B` | Cell | Tree | Cell (grafted). Cell that will become a Slot. Provide one Cell per branch; this input is grafted by default. | | Allowed Module Names | `MN` | Module Name | Tree | Allowed Module Names (optional). ModuleName entries that the Slot will permit. Provide a single flat list to reuse across boxes or branch-matching lists to specify per-box allowed names. If left unconnected, the Slot is created in an "allow all" state: Construct Assembly will resolve it to the full final Module set. | | Allowed Modules Weights | `W` | Number | Tree | Allowed Module Weights (optional). Weights corresponding to allowed modules. Provide a single flat list to reuse across boxes or branch-matching lists to specify per-box weights. A weight biases the solver's module choice within this Slot relative to the other allowed Modules: weight 4 makes a Module four times as likely as weight 1. The weight covers the Module as a whole, including all of its rotated variants. A weight of 0 or less removes the Module from this Slot entirely, turning the weight into a hard constraint. If left unconnected or empty, every allowed Module gets a uniform weight of 1.0. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Slot | `S` | Slot | Tree | Constructed Slot instance built from the provided Cell and allowed module names/weights. | #### 4.7.2 Deconstruct Slot *Slot · Nickname: `DeconSlot` · GUID: `9DC6F6DB-1209-4B30-ABC2-FC3BC8F6C929`* Extract a Slot’s components: Cell, allowed Module Names, Weights, determinism flag, Contradiction flag and Entropy. #### Behavior Iterates through all Slots in the input tree. Calculates Entropy as `AllowedModulesCount / AllModulesCount`. If any Slot is null or invalid, one Error names every offending tree position and the component produces no output. Pre-Assembly and allow-all Slots both have `AllModulesCount = 0`, so Entropy falls back to `1.0` explicitly — every Module is still a possibility until Construct Assembly stamps `AllModulesCount` with the number of distinct Module Names referenced by any Slot and (for allow-all Slots) resolves the allowed list. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Slot | `S` | Slot | Tree | A Slot to deconstruct into its components and properties. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Cell | `B` | Cell | Tree | The cell defining the Slot's spatial extent. | | Allowed Module Names | `MN` | Module Name | Tree | List of modules allowed to be placed into the Slot. | | Allowed Module Weights | `MW` | Number | Tree | Weights of modules allowed to be placed into the Slot. | | Is Deterministic | `Det` | Boolean | Tree | True if the Slot allows placement of exactly one Module. | | Is Contradictory | `Con` | Boolean | Tree | True if the Slot allows placement of no Module. | | Entropy | `E` | Number | Tree | 1.0 = allows placement of all modules, 0.5 = half the module count, 0.0 = no modules allowed. | #### 4.7.3 Changed Slots *Slot · Nickname: `ChngSlt` · GUID: `929BF404-DB31-4E94-B78D-048F8DB51192`* Given an original list of Slots and one or more new Slot lists, identify which Slots changed between them. Returns a tree of indices - one branch per new list - where each index points to a Slot whose set of allowed Module Names differs from the original. #### Behavior The comparison is performed element-wise: each Slot at position `i` in the original list is compared to position `i` in the same branch of the new Slot tree. The allowed Module Names are sorted before comparison so orderings that differ only in insertion sequence do not produce false positives. Weights are not compared. A common use is to connect the pre-solve and post-solve Slot lists and inspect which cells the Solver collapsed or constrained. Another use is design-space exploration: feed a Data Recorder's stored results as separate branches to the New Slots input, then compare against a baseline to see which regions of the Envelope vary across Seed runs. All branches of the New Slots tree must have the same count as the Original Slots list. If any branch length mismatches, the component reports an error and produces no output. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Original Slots | `OS` | Slot | List | Original Slots for comparison. | | New Slots | `NS` | Slot | Tree | New Slots to compare against original slots. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Changed Indices | `ChngIdx` | Integer | Tree | Indices of slots that changed when compared to the original slots. | #### 4.7.4 Occurrence Count *Slot · Nickname: `OccurrCount` · GUID: `FC392667-F3D2-4CB1-B6BC-01B3B882237C`* Count how many Slots in the Envelope contain a given Module Name. After solving, the result counts occupied Cells, not placed Module instances: a multi-cell Module contributes one count per occupied footprint Cell. #### Behavior By default, only Slots where the target Module is the sole allowed option (Deterministic) are counted. Set **Only Resolved** to `false` to also count Non-deterministic Slots where the target Module appears alongside others - useful for inspecting unsolved or partially solved Envelopes. Connect a flat list of Module Names with one Slots list to get one count per name. To count placed instances of a multi-cell Module, group occupied Slots by placed instance. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Module Name | `MN` | Module Name | Item | The Module Name to count across the Slot list. | | Slots | `S` | Slot | List | List of Slot objects to search - typically the Slots output of Deconstruct Assembly applied to a solved Assembly. | | Only Resolved | `OR` | Boolean | Item | When true (default), only Slots where the target Module is the sole allowed option (fully resolved) are counted. Set to false to also count non-deterministic Slots where the target Module appears alongside others — useful for inspecting unsolved or partially solved envelopes. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Count | `C` | Integer | Item | Number of Slots in the input list that contain the given Module Name. | #### 4.7.5 Slot Pattern *Slot · Nickname: `Pattern` · GUID: `A4DA68A0-4D0E-44E9-B20E-F804E29A18DD`* Find a Slot sub-pattern inside an existing Slot Envelope. Useful for template matching and locating repeated Module patterns. #### Behavior Builds a 3D grid from pattern and Envelope Slots via Grid Topology. Each Slot Pattern branch is a separate pattern. The component translates each pattern across occupied Envelope positions without rotating it. A Slot matches when its box dimensions agree within tolerance and its allowed Module Names equal those of the corresponding Envelope Slot. **Pattern Found** returns one boolean per pattern branch. **Slot Indices** returns one branch per occurrence at `{pattern branch path; occurrence index}`, with Envelope Slot indices in pattern order. The Slots input must have one branch; empty, invalid, or non-grid pattern branches report an error and return `false`. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Slots | `S` | Slot | Tree | Envelope Slots. Flat list of Slots forming the envelope to search (one branch). Multi-branch trees are not supported. | | Slot Pattern | `P` | Slot | Tree | Slot elements defining the sub-pattern to locate inside the envelope. Each branch represents a separate pattern to search for. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Pattern Found | `F` | Boolean | List | One boolean per pattern branch: true when the pattern exists within the envelope. | | Slot Indices | `I` | Integer | Tree | Tree of integer indices pointing to slots in the envelope that match the detected pattern. Each branch corresponds to a found occurrence. | #### 4.7.6 Slot to Module *Slot · Nickname: `SlotToMod` · GUID: `CCBE5762-C55A-4A3B-86BF-081DDDA59485`* Place Module geometry into solved Slots. Similar to [Materialize Assembly](#comp-materialize) but works at the Slot level rather than the Assembly level. Connect the **Modules** and **Slots** outputs of Dissolve Assembly so the names, rotation variants, and Cell parts agree. #### Behavior By default only Deterministic Slots (exactly one allowed Module) are materialized. Set **Only Deterministic** to false to also place geometry for every allowed Module in Non-deterministic Slots. Contradictory Slots (zero allowed Modules) are always skipped. For each eligible Slot, the component looks up the allowed Module Name(s) in the input Module list, finds the variant whose cell dimensions match the Slot, and transforms the Module geometry from its original position into the Slot’s position via a plane-to-plane mapping. A multi-cell Module is placed once from its anchor part; its other Cell parts produce no geometry. Input Modules with the same name and dimensions cause an error and prevent placement. Solved Slots may have multiple branches, one per solution, but empty branches or invalid Slots cause errors. Output paths are `{slot branch path; module index; slot index}`. Baking creates one Rhino block definition per input Module with geometry and one instance per placement. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | Complete list of the solver Modules the Slots name, including rotation variants and the per-cell parts of multi-cell Modules. Connect the Modules output of Dissolve Assembly directly to this input. Provide a flattened list. | | Solved Slots | `S` | Slot | Tree | Slots of a solved Assembly, one branch per solution. Connect the Slots output of Dissolve Assembly directly here. A multi-cell Module is placed once, from the Slot of its anchor cell. | | Only Deterministic | `D` | Boolean | Item | When true, only deterministic Slots (exactly one allowed Module) are materialized. When false, all non-contradictory Slots are materialized, placing geometry for every allowed Module in each Slot. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Geometry | `G` | Geometry | Tree | Placed geometry (tree). Geometry instances created from module placements, organized per solution path for baking or preview. | | Transforms | `X` | Transform | Tree | Transforms (tree). Transformation matrices applied to each placed geometry instance, matching the Geometry output structure. | #### 4.7.7 Preview Rule in Slots *Slot · Nickname: `PreviewRule` · GUID: `8F7CBFE2-C40D-475A-A2E2-F7AC63874F75`* Preview one Rule by placing compatible source and target Modules and their Deterministic Slots. #### Behavior For each pair of input Modules named by the Rule whose referenced Faces have equal width and height within tolerance, maps the source Module's pivot onto the **Face Pivot Plane** (World XY by default). It then moves the target Module until its referenced Face plane coincides with the source Face plane. **Source Slots** and **Target Slots** each contain one Module Name with Weight `1`; **Source Modules** and **Target Modules** contain the placements. Multiple compatible variants produce multiple Slot pairs aligned to the same plane and a warning. The output Modules preserve their original rotation flags. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Rule | `R` | Rule | Item | A Rule that defines face relationships to preview module placement in slots. | | Face Pivot Plane | `P` | Plane | Item | Plane on which the source Module's pivot is placed. The target Module is placed against the source Face. Defaults to World XY. | | Modules | `M` | Module | List | Collection of Modules used to find compatible module pairings for the Rule. Provide a flattened list. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Source Slots | `SS` | Slot | List | Slots that permit placement of the source module defined by the Rule. | | Target Slots | `TS` | Slot | List | Slots that permit placement of the target module defined by the Rule. | | Source Modules | `SM` | Module | List | Source modules placed at the positions defined by the Rule. Rotation flags are preserved from the input modules. | | Target Modules | `TM` | Module | List | Target modules placed at the positions defined by the Rule. Rotation flags are preserved from the input modules. | # Workflows 2. Example Workflows - [2.1 Bare minimum](#bare-minimum) - [2.2 Extending the bare minimum](#extending-bare-minimum) - [2.3 The Assembly workflow](#assembly-workflow) - [2.4 Using Connectors for rotation-aware rule generation](#connectors-workflow) - [2.5 Combining Connectors with explicit Rules](#connectors-manual-rules) - [2.6 Empty Module](#empty-module) - [2.7 Rotating the envelope base plane](#rotated-envelope) - [2.8 Defining Rules from Faces](#defining-rules) - [2.9 Working with multiple Modules](#multiple-modules) - [2.10 Automatic Module rotations](#module-rotations) - [2.11 Heterogeneous grid](#heterogeneous) - [2.12 Weighted Module placement](#weighted-slots) - [2.13 Boundary handling](#boundary) - [2.14 Constructing Slots from geometry](#constructing-slots) - [2.15 Fixing Modules in the Envelope](#fixing-modules) - [2.16 Disallowing Rules](#disallowing) - [2.17 Rule Exclusivity](#rule-exclusivity) - [2.18 Multi-cell Modules](#megamodules) - [2.19 Materializing results](#materializing) - [2.20 Solver settings](#solver-settings) - [2.21 Audit-driven debugging](#audit) - [2.22 Visualizing Rules and Faces](#visualizing) - [2.23 Grid topology analysis](#grid-topology) - [2.24 Rules from Face Groups](#shared-face-groups) - [2.25 Proto-results workflow](#proto-results) - [2.26 2D grids (floor layouts and panel systems)](#2d-grids) - [2.27 Sparse grids with intentional gaps](#sparse-grids) - [2.28 Rectangular (non-square) Modules](#rectangular-modules) - [2.29 Combining manual and automatic Rules](#manual-auto-rules) - [2.30 Restricting Modules to specific regions](#region-restriction) - [2.31 Occurrence Count analysis](#occurrence-count) - [2.32 Exporting results for fabrication](#export-fabrication) - [2.33 Auditing an Assembly](#auditing-assemblies) - [2.34 Iterative solving with partial results](#iterative-solving) - [2.35 Marking unused Faces as Terminators](#unused-faces-terminators) - [2.36 Converting Monoceros 1 or 2 projects to Monoceros 3](#convert-from-v1-or-v2) FAQ & Tips → ## 1. What are the main strategies for using Monoceros? ### 1.1 How do I design top-down (envelope first)? When a spatial volume already defines the constraint - a site boundary, a building Envelope, a specific Rhino shape - define the Envelope first and derive the Module vocabulary from it. Working from the outside in respects spatial constraints before committing to Module design. 1. Define the Envelope shape in Rhino (a Brep, Mesh, or Surface). 1. Use Cells From Geometry to convert it into Cells. 1. Choose cell dimensions that suit the design intent. 1. Design Modules to fit the cell size. 1. Define Rules and solve. **Best for:** Site-specific projects, spatial planning, filling existing volumes. ### 1.2 How do I design bottom-up (Modules first)? When a library of physical or designed Module pieces already exists, let the Modules define the grid rather than the other way around. Grid dimensions derive from the Module size; the WFC engine combines the pieces into coherent assemblies. 1. Design Module geometry in Rhino at a specific scale. 1. Construct Modules with Construct Module. 1. Generate rotations with Module Rotations. 1. Detect Rules with Detect Rules from Voxels. 1. Create a simple grid matching the Module dimensions. 1. Solve and iterate on the Rules. **Best for:** Product design, pattern generation, fabrication-driven projects. ### 1.3 How do I iterate and refine a Monoceros design? When initial results are not quite right - certain Module types are too common, unexpected adjacencies appear, or the Solver fails after scaling up - refine incrementally rather than rebuilding from scratch. The approach is a tight loop between solving and adjusting. 1. Start with a rough Module set and simple Rules. 1. Solve on a small grid (e.g. 5×5×5). 1. Analyze the result: are Modules distributed well? Are there undesired adjacencies? 1. Adjust Rules: add disallowed pairs, modify Weights, add boundary constraints. 1. Scale up the Envelope gradually. **Best for:** Exploratory design, prototyping, complex Rule sets. ### 1.4 How do I use Monoceros at multiple scales? When a design operates at two or more scales simultaneously - large zones (Assembly, logistics, residential) at one resolution, detailed Module filling within each zone at a finer resolution - apply WFC twice rather than using a single unmanageable grid. 1. Solve a coarse grid first (large cells, few Modules representing zones: Assembly, logistics, residential, etc.). 1. Use the coarse result as input for a finer grid: each coarse Module defines the Module palette for the corresponding fine-grid region. 1. Solve the fine grid with per-region Module assignments. A variation that avoids maintaining two grids of different resolution: run the coarse pass on the *same* grid and Envelope, but with a drastically reduced Module palette - one Module type per functional zone (Assembly, logistics, residential, etc.). Clustering is not automatic and achieving large, contiguous zones is non-trivial; dedicated clustering strategies must be applied, or the Solver run many times until a result with a convenient zone layout is found. The boundary between zones can be geometrically rich, shaped by the Faces you define. Once the clusters are determined, extract each zone's occupied Slots as a new, independent Envelope and run a second WFC pass on it with a richer Module palette and a ruleset tailored to that zone. **Best for:** Mixed-use layout planning, large facility design, hierarchical Assembly line design. ### 1.5 Debugging strategy When the Solver fails or produces unexpected results, use **Audit Assembly** to diagnose. Connect both your input Assembly (before solving) and an Assembly from the Solver's **Assemblies** output (after solving) to Audit Assembly to compare what changed. The Audit report covers Envelope validity, Slot configuration, Module coverage, and Rule completeness. See [2.21 Audit-driven debugging](#audit) for the full workflow. ### 1.6 Fabrication-oriented workflow When a WFC result will be physically built - 3D printed, laser cut, CNC milled, or assembled from prefabricated parts - account for material constraints, Assembly sequence, and manufacturing limitations from the start. 1. **Module design** - Design Modules around your fabrication method. For 3D printing: ensure overhangs stay within printable angles. For CNC: avoid undercuts. For laser cutting: keep panels flat and unfoldable. 1. **Face design** - If physical Faces exist (tabs, Slots, magnets), model them as part of the Module geometry. Use Detect Rules from Geometry so that only Modules with matching physical Faces are paired. Module geometry may protrude outside the Cell: bolts, screws, or latching tabs intended to physically join adjacent Modules can be modelled this way. Design the matching receiving feature into the neighbouring Module. The Solver is unaware of geometry and will not flag protrusions. 1. **Scale and tolerance** - Set cell dimensions to match your real-world unit. Account for material thickness and Assembly tolerances. 1. **Solve and count** - Use **Occurrence Count** to produce a bill of materials. Verify that each Module type is fabricable in the required quantity. 1. **Export** - See example [2.32 Exporting results for fabrication](#export-fabrication). **Best for:** Pavilion design, discrete furniture, modular construction, prototyping. ### 1.7 Spatial weight gradients By default WFC distributes Module types evenly across the Envelope. Weight gradients let you override this: assign a high Weight to a Module only in the region where you want it to appear, and a near-zero Weight everywhere else. This steers the result without hard constraints - the Solver can still deviate, but it will prefer the weighted choice. The technique consists of computing a per-Slot number from geometry (distance to a point or curve, height, proximity to an edge, etc.), remapping that number to a Weight range, and feeding it to **Allowed Modules Weights** on Construct Slot alongside **Allowed Module Names**. Each Slot gets its own Weight for each Module. Setting a Weight to exactly 0.0 (not just low) prevents the Solver from ever placing that Module in that Slot. A concrete example: a 2D panel grid where access hatches should cluster at maintenance height and solid panels should dominate near the base. Assign each Slot’s Z-coordinate to the hatch Module Weight (remapped so Weight peaks at waist height) and invert the same curve for the solid Module Weight. Define edge-profile Rules so adjacent panels always produce continuous joint lines, and add dedicated boundary Modules for sill, cap, and corners. Running the Solver then produces a panel distribution driven by the gradient, not by a fixed pattern. **Best for:** Facade panelling, floor layout zoning, any design where different Module types belong in different spatial regions. ### 1.8 System-based Module design When a design contains multiple independent spatial systems - circulation, structure, services, landscaping - trying to design all Modules at once produces an unmanageable combinatorial explosion. Instead, treat each system as a separate design problem and connect them through a thin layer of interface Modules. #### Step 1: Identify the spatial systems List the distinct systems in the design. Each system has its own spatial logic and its own vocabulary of elements. Examples: a road network (straight, curve, intersection, dead-end), a building grid (column, beam, slab, void), a landscape layer (path, planting, water, edge). Systems are independent if their internal connectivity Rules do not depend on each other. #### Step 2: Design Modules per system For each system, design the Modules that handle its internal connectivity. A typical set includes: straight, corner (90°), T-junction, crossing, dead-end, and empty. Follow the [Module design principles](faq.html#faq-4) - exciting middle, boring edges, half-size scale - within each system independently. Name Modules with a system prefix (e.g. `road-straight`, `road-corner`, `green-path`, `green-planting`). #### Step 3: Design interface Modules Where two systems meet, create dedicated interface Modules. An interface Module has Faces that match system A on some Faces and system B on others. For example, a `road-edge` Module has road-compatible Faces on one side and greenery-compatible Faces on the opposite side. Keep the number of interface Modules small - the goal is a controlled bridge, not a full mesh between systems. #### Step 4: Define Rules within each system first Build and test Rules for each system independently on a small Envelope (e.g. 5×5×1). Verify that the Solver produces valid configurations for each system in isolation before combining. This makes debugging far easier - if a system works alone but fails when combined, the problem is in the interface Rules, not the system itself. #### Step 5: Add cross-system Rules through interface Modules Once each system works independently, add Rules that connect interface Modules to both systems. Run the combined Solver on a small Envelope and verify that systems interact correctly at their boundaries. Scale up the Envelope only after the combined Rule set is stable. **Best for:** Complex designs with multiple interacting spatial systems, projects with clear system boundaries (urban planning, building services, multi-layer facades). See also workflows and FAQs: [FAQ 1.5 What makes a good Module?](faq.html#faq-4), [2.9 Working with multiple Modules](#multiple-modules), [2.30 Restricting Modules to specific regions](#region-restriction). ## 2. Example Workflows ### 2.1 Bare minimum This is the true bare minimum to get Wave Function Collapse running end-to-end in Monoceros. Nothing here is designed - the goal is to confirm the entire pipeline works, from defining a Module and its Rules through to seeing materialized geometry in the viewport. Once you have this working, you can add more Module types, define richer Rules, change the Envelope shape, and iterate from there. The Solver supports up to **16,370 solver Modules** across all Slots. Each allowed orientation counts separately: a single-cell Module with Full rotation takes 24. Each Cell of a multi-cell Module also counts separately in each orientation: a two-cell Module with Full rotation takes 48. #### Concept The full Monoceros workflow uses six things: - A **Module** - the design element the Solver places, carrying optional geometry. A single-cell Module has six Faces; a multi-cell Module has numbered external Faces. - **Rules** - allowed adjacencies between Faces. A Rule is ultimately between two Faces that point in opposite Directions on the same Axis (for example `+X` and `-X`). Rule-creation components may also emit non-opposing pairs, which Construct Assembly expands into opposing-face Rules via rotation variants. Any Face that has no Rule at all is called an Indifferent Face and behaves differently: you only define Rules for the Faces where you want to control what may be adjacent (explained below). - An **Envelope** - the spatial field to fill. You create Cells with a grid component and then convert each Cell into a Slot, specifying which Modules that Slot is allowed to contain. The resulting collection of Slots is the Envelope. - The **WFC Solver** - the component that runs the algorithm when Run is True and returns solved Assemblies. A complete solution assigns one Module to every Slot. If every Attempt contradicts, it returns a representative contradictory Assembly with the Contradictory output set to true. - A **Materialize** step - turns the solved Slot assignments into visible geometry. - An **Assembly** - the single object that packages Modules, Slots, and Rules for the Solver. Created by Construct Assembly, which also generates rotation variants and runs an internal Audit. A 1×1×1 Envelope technically works: the Solver places the one allowed Module without Contradiction. But with only a single Slot there are no neighbours, so no Rule is ever evaluated. The smallest Envelope where at least one Rule is actually exercised is 2×1×1 - two adjacent Slots that must agree on their shared Face. For this example, **5×5×5** (or **5×5×1** for a flat layout) is a much better starting point. It is still fast to solve and small enough to inspect visually, but large enough that every interior Slot has six neighbours and every Rule is exercised multiple times. A 3×3×3 grid can produce degenerate or ambiguous results with certain Rule combinations that a slightly larger grid resolves clearly. For geometry, use a simple line segment running from the centre of the `-X` Face to the centre of the `+X` Face of the cell - imagine it represents a pipe in the real world. Once the bare minimum is working, you can replace the straight pipe with an L-shape, a cross, or other curve configurations and explore how the Rules and Solver respond. #### Implementation Define the Module and Rules first, then create the Envelope. This order mirrors the conceptual flow: design the discrete element, specify how the elements connect, then provide the space to fill. 1. **Establish a shared cell size** - Add a Vector parameter (or a Panel) with value `{1, 1, 1}`. You will feed this same vector as the Diagonal input into every Homogeneous Grid component in the definition. Using one shared vector guarantees that Module Cells and Envelope Slots always have identical dimensions, which the Solver requires. 1. **Create a Module Cell** - Place a Homogeneous Grid with the shared diagonal vector, counts of `1×1×1` (the defaults), and a base plane with its origin somewhere clearly away from where the Envelope will sit - for example at `{-10, 0, 0}`. The origin of the base plane marks the centre of the Cell. The plane Axes set the Orientation of the grid. Place it with enough room for future Modules. 1. **Draw the pipe geometry** - Create a line from point `{-10.5, 0, 0}` to `{-9.5, 0, 0}`. This runs through the centre of the Module cell from its `-X` Face to its `+X` Face, clearly showing the pipe's entry and exit. 1. **Construct the Module** - Use Construct Module with name `pipe`, the single Cell from step 2, and the line from step 3 as geometry. 1. **Get the Faces** - Connect the Module to Get Module Faces. It outputs six Faces as separate named parameters: `+X`, `-X`, `+Y`, `-Y`, `+Z`, `-Z`. 1. **Define one Rule** - Wire the `+X` Face into the Source Faces input of Construct Rules From Faces, and the `-X` Face into the Target Faces input. This single Rule says that the exit of one pipe Module may sit adjacent to the entrance of another - the pipe continues without leaking. The four remaining Faces (`+Y`, `-Y`, `+Z`, `-Z`) have no explicit Rule because it doesn't matter what is placed next to a pipe. In Monoceros, a Face with no Rule is called an **Indifferent Face**. When indifference is enabled on Construct Assembly, every Indifferent Face automatically pairs with any other Indifferent Face facing the opposite Direction on the same Axis. For this example that means pipe sides can sit next to each other freely without any extra Rule definitions. For a detailed explanation of Indifferent Faces and how to define them manually when needed, see [2.8 Defining Rules from Faces](#defining-rules). 1. **Create the Envelope Grid** - Place a second Homogeneous Grid with the same shared diagonal vector and counts of `5 × 5 × 5` (or `5 × 5 × 1` for a flat layout). Set the base plane to wherever the Envelope should appear in the scene - the origin of that plane marks the centre of the first Slot, and the plane Axes set the Envelope’s Orientation. For a first test, the world origin works fine. Because the diagonal vector is shared with step 2, every Cell here is identical in size to the Module's Cell. 1. **Construct Slots** - Use Construct Slot with all Cells from the Envelope grid. Leave the Allowed Module Names input unconnected: Construct Assembly will resolve the Slots to the full Module set automatically, which is what you want for the bare minimum where every Slot accepts every Module. (Wiring the Module Name explicitly still works; it is simply not required.) 1. **Construct Assembly** - Feed the Module, Slots, and Rules into Construct Assembly. Indifference is enabled by default, so uncovered Faces pair freely; turn it off if they must not pair. The component packages everything into a Discrete Assembly. 1. **Solve** - Connect the Assembly to the WFC Solver and set **Run** to True, for example with a Boolean Toggle. The Solver runs Wave Function Collapse and returns solved Assemblies. 1. **Materialize** - Connect the Solver's **Assemblies** output to Materialize Assembly. For each solved Slot, the component locates the corresponding Module (including rotation variants), transforms its geometry into the Slot's position, and outputs the result. You should see a uniform grid of pipe lines. To keep the geometry in Grasshopper, route the output into other components as normal. To bring the result into Rhino, bake Materialize Assembly directly - this creates block instances in the document - or route the output through a floating Geometry parameter and bake that parameter to get the geometry as individual objects. ### 2.2 Extending the bare minimum The bare minimum produces a first result, but a uniform pipe grid has limited design value. The sub-sections below describe the most productive next steps, each introducing one concept and pointing to the dedicated example that covers it in full. #### Expanding the Module vocabulary Model a second pipe as an L-shape: one line segment along one Axis and another segment bent 90° onto a second Axis, both meeting at the cell centre. Each Module type needs its own Rules, but that is not enough on its own. Three sets of Rules are required in total: - **Straight-to-straight** - the straight pipe continues linearly (already defined in [2.1](#bare-minimum)). - **L-to-L** - two L-pipes meeting at their open ends. - **Straight-to-L and L-to-straight** - the transition from a straight run into a corner and back. Without these cross-Rules the Solver sees the two Module types as incompatible and will contradict whenever it tries to place them adjacent to each other. Merge all three Rule lists together before passing them to the Solver. Merge the two Module lists the same way. Make sure both of the lists are flat - if not, flatten them first. For the full workflow of managing multiple Module types see [2.9 Working with multiple Modules](#multiple-modules). #### Simplifying Rule definition with Face type grouping Listing every cross-Module Rule explicitly becomes tedious as the Module set grows. A more scalable approach is to assign all Faces that should be interchangeable to the same *type* - a conceptual grouping you maintain on the Grasshopper canvas. Face objects carry no type field themselves; the type is simply the list you collect them into. In practice: extract the open-end Faces from both the straight pipe and the L-pipe using Get Module Faces. Collect them all into one list - this list *is* the type. Feed that same list into *both* the Source Faces and Target Faces inputs of Construct Rules From Faces. The component generates every pairwise combination - straight-to-straight, L-to-L, and all cross-pairings - in a single operation, replacing the three manually merged Rule lists from above. See [2.24 Rules from Face Groups](#shared-face-groups) for the full technique applied to larger Module libraries. #### Generating rotation variants automatically The simplest way to create rotation variants is to set the **Rotational Freedom** value (0 None, 1 X, 2 Y, 3 Z, 4 Full) directly on **Construct Module** — pick it on the integer input or from its right-click menu. Construct Assembly will automatically expand it into all valid rotation variants and generate the corresponding Rules. For finer control (for example custom deduplication), use the dedicated **Module Rotations** component described below. Note that any two perpendicular axes already generate all 24 orientations, so the options are single-axis (4 rotations) or Full (24); there is no two-axis setting. Both the straight pipe and the L-pipe have multiple valid orientations. Rather than modelling each by hand, feed either Module into Module Rotations and set **Rotational Freedom** to 4 (Full) for the pipe example. The variants keep the Module name (`pipe`, `L-pipe`) with a rotation index each, and already include the identity rotation, so the rotation output replaces the original Module on the Modules input of Construct Assembly, which merges the variants back into one Module per name with those orientations. See [2.10 Automatic Module rotations](#module-rotations). Alternatively, instead of defining Rules from Faces, you can use **Connectors** - named interfaces placed on Module Faces with symmetry flags. Declare which Connector types are compatible via **Connector Pairs**, and Construct Assembly generates the corresponding Rules automatically. See [2.4 Using Connectors](#connectors-workflow) for details. #### Restricting specific adjacencies The **Disallowed Rules** input on Construct Assembly lets you remove specific pairings from an otherwise complete Rule set. Two immediately useful applications on the pipe example: - **No long straight runs.** Disallow the straight pipe from connecting to itself along its own Axis. The Solver must always turn, producing only meandering paths. - **No winding paths.** Conversely, disallow the L-pipe from connecting to any other L-pipe. Consecutive corners are prevented, so runs of straight pipe are forced between every turn. Define the disallowed Rule once against the authored Module. Construct Assembly applies it to every allowed orientation. See [2.16 Disallowing Rules](#disallowing). The Rules you connect to the Disallowed Rules input are ordinary Rules - constructed by the same methods as the rest of your Rule set. The typed-Face workflow (see [2.8 Defining Rules from Faces](#defining-rules)) is particularly precise for this: assign distinct Face types to the two Faces whose pairing you want to forbid, run Construct Rules From Faces to produce exactly those Rules, and connect them to the **Disallowed Rules** input of Construct Assembly. Alternatively, connect unwanted Connector Pairs to the **Disallowed Connector Pairs** input. #### Adding an empty Module An empty Module represents a void - a Slot the Solver may leave without visible geometry - and gives the Solver freedom to leave gaps in the pipe network. See [2.6 Empty Module](#empty-module). The empty Module is also an option when the Solver cannot find a complete solution. A setup without it can be too constrained: the Modules and Rules together may leave no arrangement that satisfies every Slot simultaneously, so the Solver returns a contradictory Assembly. Adding an empty Module loosens the constraints where a void is permitted. The resulting layout will contain gaps, so whether this helps depends on the design intent. #### Enforcing containment at the envelope boundary Without boundary handling, outward-facing Faces at the Envelope edge have no neighbouring Slot. The Solver applies no constraint from that Direction. Instead, those Faces are simply unconstrained: any Module is valid at a boundary Slot regardless of what Face points outward. The boundary Slots are constrained only from inside the Envelope. For pipes this means open ends can appear at the grid surface; the pipe network “leaks” out of the Envelope. For direct boundary control, enable **Require Terminators** on Construct Assembly and provide **Terminators** for Faces allowed at the edge; see [2.35](#unused-faces-terminators). Another solution for the pipe example is to add a surrounding layer of boundary Slots filled with empty Modules whose Rules allow them next to the side Faces of pipe Modules, but not their pipe-end Faces. Every pipe end must then connect inside the Envelope. See [2.13 Boundary handling](#boundary). #### Rotating the envelope base plane Homogeneous Grid accepts any plane as its base, so the Envelope can live anywhere in space at any angle. Module geometry stays in local coordinates and is transformed by Materialize accordingly. See [2.7 Rotating the envelope base plane](#rotated-envelope). ### 2.3 The Assembly workflow The **Assembly** (formally *Discrete Assembly*) is the single object that packages Modules, Slots, Rules, Connectors, and Connector Pairs into a ready-to-solve unit. A single **Construct Assembly** component takes all these inputs, generates rotation variants and the full Rule set, runs an internal Audit, and produces the Assembly object. The WFC Solver accepts an Assembly directly. #### Workflow 1. Prepare Modules, Slots, and (optionally) explicit Rules as before. 1. Define Connectors and Connector Pairs (see [2.4](#connectors-workflow)). 1. Optionally set **Slot Priorities** (default 1.0) to resolve higher-priority Slots first. This is the per-Slot control of solve order. Set **Remove Unused Connectors** (default false) to free Faces occupied by unpaired Connectors so they can become Indifferent. 1. For boundary control, supply **Terminators** and enable **Require Terminators** (default false). **Clean up** (default false) trims impossible rotations and unreferenced Modules and Rules; the Solver performs this cleanup automatically. 1. Connect everything to **Construct Assembly**. The component generates all Module rotation variants, merges explicit Rules with Connector-generated Rules, and runs an Audit. Inspect the Audit output to catch setup errors early. 1. Connect the Assembly to the **WFC Solver** and set **Run** to True. The Solver reads Modules, Slots, and Rules from the Assembly directly. 1. After solving, use **Materialize Assembly** to extract placed geometry from an Assembly in the Solver's **Assemblies** output. Alternatively, use **Deconstruct Assembly** to retrieve the authored Modules and Rules and the narrowed Slots, or **Dissolve Assembly** to inspect the expanded Modules, Slots, Explicit Rules, and Indifferent Rules. The WFC Solver takes a Discrete Assembly as its data input. All Modules, Slots, Rules, Connectors, and Connector Pairs are packaged into the Assembly before solving. ### 2.4 Using Connectors for rotation-aware rule generation A **Connector** combines interface identity (name + symmetry) with placement (Module + Face + rotation) in a single object. Think of a Connector the way you think of a USB socket on a device: it has a specific type, a specific Orientation, and only mates with a compatible counterpart. All Connectors sharing the same name are the same type. Each Connector carries symmetry flags for 0°, 90°, 180°, and 270°, which tell the Solver which rotational orientations are considered identical. Instead of manually creating Rules for every valid Face pairing, you create Connectors on the Module Faces that need them, declare which Connector names can connect via **Connector Pairs**, and let Monoceros generate the corresponding Rules automatically. This is especially powerful when Modules have many rotation variants - the Connector system handles all rotational bookkeeping for you. There is no separate type-definition step: every Connector already carries its type identity and its placement. #### Steps 1. Create Connectors with **Construct Connector**. Give each Connector a name (e.g. `"pipe_end"`, `"flat_wall"`), set its symmetry flags, and specify its Face; wiring a whole Module creates one Connector per Face. A Connector that looks the same at every 90° turn (like a round pipe) has all four flags set to `true`. A Connector that has a distinct top and bottom (like a door frame) may only have the 0° and 180° flags set. 1. Alternatively, use **Connector From Point** to author Connectors by clicking on a Module Face in the Rhino viewport: wire a Point3d tag on the Face, the Modules list, and the name and symmetry flags, and the component returns one Connector per matched Face in a single step (Face lookup + Connector construction combined). 1. Or use **Match Connectors by Geometry** or **Match Connectors by Voxels** to derive Connectors by matching a prototype Connector to Module Faces. **Detect Connectors from Voxels** needs no prototype: it groups Faces by voxel fingerprint and outputs both **Connectors** and **Connector Pairs** for opposite-direction matches. 1. Declare which Connector names can connect using **Construct Connector Pair**. Its **Source Connector Names** and **Target Connector Names** inputs accept lists of names or Connector objects. It produces every source-target combination as a deduplicated flat list. `pipe_end → pipe_end` means a pipe end can connect to another pipe end, but `pipe_end → flat_wall` is a separate declaration. 1. Feed Modules, Slots, Connectors, and Connector Pairs into **Construct Assembly**. The Assembly bundles everything together, generates all rotation variants and the full Rule set, and runs an internal Audit. #### When to use Connectors - **Many rotation variants** - Connector symmetry flags eliminate the need to manually enumerate every rotated Face pairing. - **Physical Connector interfaces** - When Module Faces represent physical Connectors (pipe ends, beam flanges, panel clips), Connectors model them naturally. - **Large Module libraries** - Defining a handful of Connector names is faster than creating hundreds of individual Rules. #### Practical examples The following scenarios show how a small number of Connector names and Connector Pairs can replace dozens of explicit Rules. - **Wall-to-wall connections.** Name all wall cross-section Faces `"wall"`. Create a single Connector Pair `wall → wall`. Construct Assembly generates all wall-to-wall Rules automatically, including every rotation variant. Any Module whose wall Face has the same Connector name will connect to any other Module with a matching wall Face. - **Open-to-open and solid-to-solid.** Name open Faces `"open"` and solid Faces `"solid"`. Create two Connector Pairs: `open → open` and `solid → solid`. Modules will never place an opening against a solid wall, because the two Connector names are never paired. - **Directional connections (floor/ceiling).** Name floor Faces `"floor-top"` and ceiling Faces `"floor-bottom"`. Create one Connector Pair: `floor-top → floor-bottom`. This ensures floors always stack correctly regardless of Module rotation - a floor surface always Faces a ceiling surface, never another floor or an unrelated Face. ### 2.5 Combining Connectors with explicit Rules Connectors and explicit Rules are not mutually exclusive. You can use Connectors for the bulk of adjacency logic and add explicit Rules for special cases that Connectors cannot express - for example, a one-off connection between two specific Module Faces that does not fit any Connector name. #### Strategy 1. Define Connectors for the general connectivity pattern (see [2.4](#connectors-workflow)). 1. Create additional explicit Rules for specific pairings that fall outside the Connector system - e.g. a structural column Module that must always sit below a beam Module regardless of Connector name. 1. Connect any unwanted Rules to the **Disallowed Rules** input of Construct Assembly, or connect unwanted Connector Pairs to **Disallowed Connector Pairs**. 1. Feed all inputs - Modules, Slots, explicit Rules, Connectors, and Connector Pairs - into **Construct Assembly**. The Assembly merges Connector-generated and explicit Rules into a single set. #### Tips - Connector-generated Rules and explicit Rules can overlap. Duplicates are harmless - the Solver ignores them. - Use **Dissolve Assembly** to inspect the expanded Rule set, including Connector-generated and Indifferent Rules. **Deconstruct Assembly** returns the authored inputs; **Audit Assembly** provides diagnostic outputs. - When a Connector-generated Rule creates an unwanted adjacency, connect the unwanted Rules to the **Disallowed Rules** input of Construct Assembly rather than redesigning the Connector name. ### 2.6 Empty Module If you want the Solver result to contain voids, blank areas, or gaps - areas with no visible geometry - you need an empty Module. Without one there is simply no way to achieve this: the WFC Solver always fills every Slot with exactly one Module and has no concept of leaving a position unoccupied. An empty Module is a designated placeholder that carries no geometry, giving the Solver a legal option for any Slot you do not want filled with purposeful content. #### Steps 1. Create a Module with a distinctive name such as `empty`. Leave the geometry input unconnected in Construct Module. The bounding box is inferred from the Cell alone, so the Module is spatially valid with no geometry. 1. Leave **Allowed Module Names** on Construct Slot unconnected to allow every fitting Module, including `empty`, or list names explicitly to restrict a Slot. 1. Define the adjacency behaviour of the empty Module. Two approaches: **With indifference enabled** (the default): define no explicit Rules for `empty`. All six of its Faces become Indifferent, pairing freely with any other Indifferent Face facing them. The empty Module sits silently next to any neighbour that also has an Indifferent Face on the facing side, with no Rule entries required. **With indifference disabled**, or when you need explicit control: define Rules for each Face of `empty` that you want it to connect to. Only the pairings you create will be allowed; Faces with no Rule will accept nothing. This approach is more work but gives precise control over where the empty Module may appear. 1. Consider which Slots the empty Module should be permitted to enter. If you want to exclude it from specific zones or limit it to certain regions, omit `empty` from the **Allowed Module Names** list of those Slots when constructing them. Slot-level allowed names are the primary tool for controlling Module placement spatially. #### What Materialize produces Materialize outputs no geometry for Slots assigned the empty Module. Those Slots appear blank in the viewport. This is intentional: an empty Module is a spatial placeholder, not a visible element. #### Filler and padding Modules The empty Module is the minimal case of a broader pattern. Any Module designed to be placed broadly across the Envelope - contributing little or no programmatic content but making the layout work - serves the same function. Whether it carries geometry is a design decision: a grass lawn, a structural void, a plain open courtyard, or a neutral corridor section can all fill this role. What these Modules share is a deliberately permissive Face design. All six Faces carry Rules, but those Rules are written to match a wide range of neighbours rather than demanding specific pairings. A grass lawn in an urban layout has explicit Rules on bottom and top Faces and the horizontal Faces may remain unassigned and therefore Indifferent, so the Solver can place it in many contexts without Contradiction. The breadth of what it accepts is a design decision, expressed through the Rules, not a default behaviour. This permissiveness is a practical tool for unblocking overconstrained setups. When the Solver cannot find a complete arrangement, it usually means some Slot has no Module that satisfies all its neighbour constraints simultaneously. Adding a broadly-accepting filler Module gives the Solver a legal option for those Slots. Many setups that consistently report contradictions become solvable as soon as one such Module is included. The result will look different - some Slots will be occupied by the filler rather than by purposeful content - which is an explicit design trade-off, not a failure. ### 2.7 Rotating the envelope base plane If you need your solved result to appear at a specific position in the model, oriented at a particular angle, or sitting on a non-horizontal plane, all you need to change is the Base Plane input on the grid component. The entire Envelope moves and rotates as one - every placed Module inherits the new position and Orientation while the Module definitions and Rules stay exactly as they are. This works because Modules and Slots both operate in local coordinate systems: geometry is authored relative to the Cell, not the world origin, and Materialize maps it into whatever plane the Slot sits on. #### Concept Every Module is defined in the local space of its own Cell. When you author Module geometry, you place it relative to the Box origin and Axes - not relative to the world origin. From a world perspective the Box may sit at any position and angle, but the Module still describes the same shape inside it. An Envelope produced by Homogeneous Grid (or Heterogeneous Grid) is a collection of Slots that all share the same Orientation. Their local planes are parallel to one another; only their origins differ, offset by exact multiples of the cell size along the local X, Y, and Z Axes. This shared Orientation is what makes them a regular grid. The Base Plane input sets the plane of the first Slot - the one at the local origin of the grid. Viewed in local coordinates, that first Slot sits at the bottom, left, closest corner, and the grid grows in the positive local X, Y, and Z Directions from there. When Materialize places a Module into a Slot, it maps the Module geometry from the Module's own Cell plane into the Slot's plane. The result therefore inherits the Slot's world position and Orientation. Change the Envelope's base plane and every placed piece moves and rotates with it; the Module definitions and Rules stay untouched. #### Steps 1. Take any working definition that produces a solved result. Note where the output appears in the viewport. 1. Locate the Base Plane input on Homogeneous Grid (or Heterogeneous Grid). By default it is World XY. Replace it with a rotated or translated plane - use a Plane Origin component to move it, or a Rotate component (or the Angle input and an ordinary Plane constructor) to tilt it. 1. Observe the Envelope Cells in the viewport: they all shift and rotate together, maintaining their spacing and relative Orientation. 1. Run the Solver and Materialize without changing anything else. The output geometry appears at the new position and Orientation. The topology of the result - which Modules ended up adjacent to which - is identical to the original; only its world placement has changed. #### Module geometry and local space Because Modules are defined in local space, geometry authored relative to the Cell origin is automatically correct regardless of where the Box ends up in the world. If you have existing geometry built at the world origin that you need to rebase, use an Orient component with the source plane set to World XY and the target plane set to the Module's Cell plane. This remaps the coordinates so the geometry sits correctly inside the Box. A common alternative is to work directly from an existing model. If a piece of geometry is already placed and oriented somewhere in space, you can place a Cell around it - matching its position and rotation - and reference the geometry as-is. Because the Box defines the local coordinate frame, and the geometry sits inside that Box, no rebasing is necessary regardless of where or at what angle the piece sits in the world. This makes it straightforward to Sample Modules directly from a Rhino model: arrange and rotate the pieces as desired, fit a Cell around each one, and feed the geometry references into Construct Module. #### Multiple envelopes Any number of independent Envelopes can coexist in the same Grasshopper definition, each with a different base plane. You can tile a curved surface by approximating it with locally-flat planar patches, each with its own oriented plane, and run a separate Solver per patch. The Module library is shared across all of them; only the Envelope planes differ. ### 2.8 Defining Rules from Faces Monoceros 3 offers several routes for defining which Faces are allowed to touch - from the most explicit (pairing Faces by hand) to the most automated (letting the voxel engine infer compatible pairs from geometry). Choosing the right method for your situation keeps the canvas readable and prevents accidentally over-constraining the Solver. #### Concept Every Rule in Monoceros 3 is a pair of two Face UIDs - one from a source Module Face and one from a target Module Face - where the two Faces point in opposite Directions (`+X` touching `-X`, `+Y` touching `-Y`, and so on). Rules are Direction-agnostic for equality: defining “A connects to B” is equivalent to “B connects to A”, so you never need to define both sides of the same adjacency. The more Rules you define, the more flexibility the Solver has to find valid combinations. A Module Face with no explicit Rule becomes an Indifferent Face. With the default Construct Assembly setting (Indifference enabled), Indifferent Faces pair freely with any other Indifferent Face facing the opposite Direction on the same Axis - they do not block neighbours. Only define explicit Rules for Faces where you want to control what may be adjacent. #### Method A: From Face pairs This is the most explicit approach and gives you the finest control over individual Face connections. Extract the six Faces of each Module using Get Module Faces, which outputs them as separate named parameters (`+X`, `+Y`, `+Z`, `-X`, `-Y`, `-Z`). Wire the Face you want on the source side into the Source Faces input of Construct Rules From Faces, and the matching opposite-Direction Face of the target Module into the Target Faces input. By default (Cross Match = true), the component cross-matches: every item in the Source list is tested against every item in the Target list, producing all valid combinations. If you feed three source Faces and four target Faces, you get up to twelve Rules, not three. This is the common use case: allow all listed Faces to connect to all listed Faces. For strictly one-to-one pairing (first source with first target, second with second, and so on), set Cross Match to false. Both opposing and non-opposing face pairs are emitted. A non-opposing rule (e.g. `A:+X → B:+Y`) is a connector-symmetry hint that Construct Assembly expands into opposing-face rules using rotation variants. #### Method B: From string literals Rules can be cast from strings in the format `modulea:+X -> moduleb:-X`. Use a Grasshopper Panel with the Rule parameter type for quick prototyping or when you want to hard-code a short list of adjacencies as readable text. #### Method C: Suggest from geometry Detect Rules from Geometry inspects each Module's geometry and collects the elements that lie precisely flat on each Face plane: - **Naked edges of Breps** - boundary edges that belong to only one Face of a Brep surface or solid - **Naked edges of Meshes** - edges shared by only one polygon in a mesh - **End points of open curves** - the start and end of any non-closed, non-periodic curve - **Standalone Point objects** For each Face, only elements whose vertices all fall exactly on the Face plane within tolerance are kept. The surviving points are remapped into the Face's local coordinate space (relative to its Face plane), then compared across Faces. Two Faces match when they have the same Face dimensions and their sets of local-space points are identical within tolerance (order-independent); there is no opposing-direction filter, and non-opposing matches are connector-symmetry hints expanded by Construct Assembly. The component outputs Rules directly - no additional Rule construction step is needed. This method is reliable when Module geometry is modelled so that Faces intended to touch share the same boundary pattern - for example, a pipe Module whose open end sits exactly on the Face plane will have a circular naked edge there; any other Module with an identically sized circle on the opposite Face will be matched to it automatically. Geometry that is only near the plane but not exactly on it will not contribute to the match, so precision in modelling matters. Method C cannot help when Module geometry is fully closed. A watertight Brep or mesh - a solid box, for example - has no naked edges at all. Every Face will appear empty and the component will produce no matches. If your Modules are built from closed solids, use Method D instead. Detect Rules from Geometry takes a flattened **Modules** list and compares its external Faces. To limit the search, pass only the subset of Modules you want to compare. The output of Method C is a suggestion, not an authoritative Rule set. Always inspect the matched pairs using the Preview Rule and Preview Faces components before connecting the result to Construct Assembly, and add or remove Rules manually to correct any mismatches. #### Method D: Suggest from voxels Detect Rules from Voxels works like Method C but compares voxelised Face patterns instead of exact geometry. It rasterises each Module at a configurable **Voxel Dimension** (default 16×16×16 per Module), with **Precision** (default 48 rays per cell), **Inner Depth** (default 0.05) and **Outer Depth** (default 0.05). Both depths are fractions of Module depth along the Face normal: Inner Depth scans inside the Cell, while Outer Depth scans outside it (0.0 is flush with the Face). Higher resolution or Precision can distinguish finer details at greater computation cost; greater Inner Depth captures more of the Module body behind each Face. Face geometry does not need to be perfectly watertight or analytically exact - as long as both Faces produce the same voxel pattern at the chosen resolution, they are matched. Like Method C, this component outputs Rules directly. This makes Method D more forgiving than Method C for messy, imported, or scan-derived geometry. It is also the right choice when Module geometry is built from closed solids: a watertight Brep or mesh has no naked edges, so Method C finds nothing on any Face, while the voxel rasteriser still captures the Module body correctly. The trade-off is precision: fine surface details smaller than one cell are invisible to the matcher, and Modules that look compatible at voxel resolution may not fit perfectly at the geometric level. Both Method C and Method D produce suggestions, not authoritative Rule sets. Always inspect the matched pairs using the Preview Rule and Preview Faces components before connecting the result to Construct Assembly, and add or remove Rules manually where the automatic match is incorrect or incomplete. ### 2.9 Working with multiple Modules When a design uses more than one type of Module - different shapes, details, or programme types - mixed together in the same grid, the Solver places each Module according to the Rules you define. You retain control over which Module types are allowed in which Slots. #### Concept The WFC Solver and Growth Solver operate on a single flat list of all Modules, a single flat list of all Rules, and a single flat list of Slots. No matter how many Modules you design, everything must be collected, merged and flattened before going into Construct Assembly. Faces and Rules reference Modules by name, so it is the names that tie everything together. The Solvers do not care about the order in which Modules appear in the list. Leave **Allowed Module Names** on Construct Slot unconnected when a Slot should allow every Module whose Cell dimensions fit. Supply names only when you need to restrict a Slot; Weights supplied without names are ignored with a Remark. #### Method A: One component per Module Place one Construct Module component for each Module design, then merge all their outputs into a single flat list using a Merge component. Leave **Allowed Module Names** on Construct Slot unconnected so every Slot accepts every fitting Module. To restrict Slots, wire the relevant names or Modules to **Allowed Module Names**; Modules cast to Module Names automatically. Create Rules for each Module pair you want to allow as neighbours using any of the methods in section 2.8, then merge all Rule lists before connecting to Construct Assembly. This approach is readable on the canvas and works well for designs with a small, stable set of Modules. #### Method B: Data trees For larger Module sets, Grasshopper data trees are more scalable. Organise your Module inputs - Module Name strings, Cells, and geometry - into branches so that a single Construct Module component produces all Modules in one tree, one branch per Module. Flatten the tree before connecting to Construct Assembly; it expects a flat list. Leave **Allowed Module Names** on Construct Slot unconnected to admit every fitting Module, or wire a restricted list of names or Modules. Define Rules using any of the methods in section 2.8, operating on the flattened Module list or its Faces, then flatten and merge the Rule output before connecting to Construct Assembly. ### 2.10 Automatic Module rotations If a Module has a clear directionality - a curved corridor, a structural L-bracket - Module Rotations generates all valid orientations automatically, without modelling each variant by hand. Face logic and Rules update automatically for every new Orientation. #### Generating the variants 1. Construct the base Module with Construct Module as normal. 1. Connect it to Module Rotations. Set **Rotational Freedom** to 4 (Full) for the pipe example; the component defaults to Full. 1. The output is a flat list of Modules. Every variant keeps the Module name (`pipe`) and carries a rotation index that says which of the 24 cube orientations it is; the unrotated variant (rotation index 0) comes first. Because the identity rotation is already included, this list replaces the original Module downstream - do not include both, or Construct Assembly reports the repeated name. 1. **Cull Duplicates** is enabled by default. It removes variants that are geometrically identical after rotation. 1. Optionally filter the list (for example with List Item or Cull Pattern) to keep only the orientations you want. Keep the unrotated variant: Construct Assembly merges the others onto it. #### How Construct Assembly reads the variants Rotations are a property of the Module. Construct Assembly merges the same-name variants back into one Module named `pipe` whose allowed orientations are exactly the rotation indices that arrived, together with the Rotational Freedom the Module was authored with. The Solver then places `pipe` only in those orientations. The Module's tooltip shows the preset (`(rot Z)`) or, for any other set, the count (`(rot 2/24)`), and the rotation arcs show on the faces perpendicular to each axis whose quarter-turn is allowed. Two Modules that share a name but are not rotation variants of one source Module (for example two separately constructed Modules both named `pipe`) are still an error. #### Generating Rules for all variants Rules, Connectors and Slots name the Module, not its variants: define them against `pipe` and its unrotated faces, exactly as for a Module with a Rotational Freedom set on Construct Module. Construct Assembly rotates them together with the Module for every allowed orientation. Use any of the methods in [2.8](#defining-rules) on the unrotated Module. #### Wiring the solver 1. Pass the full Module Rotations output list (all variants you keep) to the Modules input of Construct Assembly. 1. Allow `pipe` in the Slots, either by leaving **Allowed Module Names** unconnected or by passing its name or Module to that input. 1. Feed the Modules, Slots, and Rules into Construct Assembly, then connect the Assembly to the WFC Solver and set **Run** to True. Connect the Solver's **Assemblies** output to Materialize Assembly, which places each Module at the correct position and Orientation. ### 2.11 Heterogeneous grid When a design requires Modules of genuinely different physical scales coexisting in the same Envelope, use Heterogeneous Grid. A Homogeneous Grid constrains all cells to the same size; Heterogeneous Grid lets neighbouring Slots have different dimensions, with each distinct Slot dimension requiring at least one matching Module. #### Homogeneous vs heterogeneous - the key distinction A common misconception: a grid is *not* Heterogeneous simply because its Cells are non-cubic. A Homogeneous Grid with a Diagonal of (2, 1, 3) produces cells that are wide in X, narrow in Y, and tall in Z - but every cell in the grid is **identical**. That is still a Homogeneous Grid. One Module size fits the whole Envelope. A grid is Heterogeneous when neighbouring Slots have *different* sizes from each other. This only happens when the X, Y, or Z size lists passed to Heterogeneous Grid contain more than one distinct value. If all values in all three lists are identical, the result is the same as using Homogeneous Grid. In practice: use Homogeneous Grid whenever all your Modules are the same size (even if that size is rectangular rather than cubic). Switch to Heterogeneous Grid only when you genuinely need Slots of different sizes coexisting in the same Envelope. #### How the grid dimensions are defined Heterogeneous Grid takes three lists - one per Axis - rather than counts. Each value in the list sets the size of one *column* (X), *row* (Y), or *layer* (Z). A five-element X list therefore produces five columns, each as wide as the corresponding value. The recommended way to provide these lists is a **Gene Pool** component. Gene Pool acts as a compact block of sliders: it stores a list of floats that you can edit individually or all at once, making it easy to set per-column widths without wiring up a separate slider for every column. Any other list source works just as well - Series, Sequence, a panel with numbers, an explicit Merge of values, or any parametric expression that produces a flat list of floats. #### Unique names for authored Modules; shared names for rotation variants Each authored Module needs a unique name, including when two Modules have different Cell dimensions. Construct Assembly accepts repeated names only for rotation variants of one source Module from Module Rotations; it merges those variants into one Module with the allowed orientations. In a Heterogeneous Grid, construct a separately named Module for each distinct Cell size and define its Rules. An allow-all Slot receives only Modules whose Cells fit its dimensions. #### Naming Modules of different sizes Names identify authored Modules. Construct Assembly rejects two separately authored Modules with the same name, regardless of their dimensions. Use distinct names and Rules for distinct sizes or Face behaviour. **Example 1 - T-junction on the long Module only.** Name a 1 m pipe `pipe-short` and a 2 m pipe `pipe-long`. Give `pipe-long` the Rule for its T-shaped branch in the Y Direction; do not give that Rule to `pipe-short` if its geometry has no such branch. The distinct names let you specify different allowed Slot lists and Rules. **Example 2 - different Face profiles across sizes.** Give the long and short versions of Modules A and B separate names. Define the +X Rule for the size pairs whose Face profiles actually fit. A Rule for the long pair does not automatically admit the short pair. #### Module size combinations - the combinatorial explosion The number of distinct Slot sizes is not the number of different values per Axis; it is the product of how many unique values appear in each Axis list. If the X list contains *a* unique values, the Y list *b* unique values, and the Z list *c* unique values, up to **a × b × c** different cell sizes can occur and you need a separately named Module for each one. This grows fast. A few examples: | Unique X sizes | Unique Y sizes | Unique Z sizes | Max distinct cell sizes | Typical scenario | | --- | --- | --- | --- | --- | | 1 | 1 | 1 | 1 | Uniform grid - one Module size, equivalent to Homogeneous Grid. | | 2 | 1 | 1 | 2 | One wider column in an otherwise uniform grid. Two Module sizes needed. | | 2 | 2 | 1 | 4 | One wider column *and* one deeper row. Four Module sizes. | | 2 | 2 | 2 | 8 | One different value in each Axis. Eight Module sizes. | | 3 | 3 | 2 | 18 | Three column widths, three row depths, two layer heights. Eighteen Module sizes. | | 4 | 4 | 4 | 64 | Four distinct values per Axis. Sixty-four Module sizes - impractical to author by hand. | The most common trap is starting from a uniform grid and adding just one exceptional row in each Direction. Suppose you have a 10×10×10 grid where nine columns are 1 m wide and one is 2 m wide, nine rows are 1 m deep and one is 2 m deep, and all layers are 1 m high. That gives 2×2×1 = **4** distinct cell sizes. Add one exceptional layer height and the count doubles to **8**. Add a second exceptional column width or row depth and it doubles again. Because the unique-value counts multiply rather than add, even a small number of irregular values quickly produces a large number of separately named Modules. Keep the number of distinct values per Axis as low as your design allows. If only two widths are genuinely necessary, avoid letting a slider drift to a third value. #### Steps 1. Use Heterogeneous Grid instead of Homogeneous Grid. Wire three lists of sizes (Gene Pool or any flat list of floats) into the X, Y, and Z inputs. The length of each list sets how many columns, rows, and layers the grid has. 1. Count the unique values in each Axis list and multiply them together. That product is the number of distinct Module sizes you need to construct. 1. Construct a uniquely named Module for **every distinct cell size**, with its own Rules. Allow-all Slots receive only fitting Modules; Construct Assembly reports an **Error** if any such Slot has no fit. For manually specified names, Construct Assembly warns when a Module has no fitting allowed Slot. An unfitting Slot causes a contradiction during solving, and Materialize reports an **Error** if no variant matches its dimensions. Use Audit Assembly's **Slots Without Fitting Modules** output to find affected Slots. ### 2.12 Weighted Module placement To bias the distribution of Module types across the grid - toward a pattern, a gradient, or a specific spatial proportion - assign per-Slot Weights. Without explicit Weights, all Modules in a Slot are equally likely. For example: opaque cladding panels dominate the lower floors while glazed panels increase toward the top; or a dense structural Module concentrates at the core while an open Module tends toward the perimeter. Weight is a **per-Module, per-Slot relative multiplier**. Each Slot carries its own independent Weight list. A Module with Weight 2.0 in a given Slot is twice as likely to be chosen at that Slot during Observation as a Module with Weight 1.0 in the same Slot. A Weight covers the Module and all its rotation variants. Weights on one Slot have no effect on any other Slot. #### What the weight value means The values are relative multipliers within a single Slot's allowed-Module list. If a Slot allows Modules [A, B, C] with Weights [4.0, 2.0, 1.0], Module A is four times as likely as C and twice as likely as B to be observed first at that Slot. The default Weight is `1.0`. Fractional values below 1.0 reduce a Module's likelihood relative to the default; for example, a Weight of 0.5 makes a Module half as likely as a neighbour with Weight 1.0. There is no upper bound. The Weights are normalised per Slot at Observation time; the absolute values do not matter, only the ratios between them. A Weight of 0 or less is a hard constraint: Construct Slot removes the Module from that Slot entirely and the Solver never considers it there (a Remark reports how many Modules were removed); this is equivalent to removing the Module from the allowed-Module list. Removing every listed Module this way is an Error, not an allow-all Slot. To keep a Module available as a last resort while making it very unlikely, use a small positive Weight (e.g. `0.001`) instead of zero. #### What weights do not control - **No Weight on Rules.** There is no mechanism to make a particular adjacency more or less probable. Weights govern which Module the Solver picks when a Slot has multiple candidates; they do not influence how constraints are propagated afterward. - **Weights do not guarantee occurrence.** A high Weight increases the probability that a Module is chosen during Observation, but WFC Propagation can still eliminate it from a Slot as a consequence of choices made elsewhere. When Propagation removes a Module that carries a high Weight, the Solver silently picks from whatever candidates remain, with no diagnostic message. In tightly constrained setups this can be difficult to detect. - **Weights have less influence in heavily constrained grids.** Weights only act when a Slot has more than one candidate at Observation time. The more Rules and fixed Slots constrain the grid, the fewer free observations occur, and the smaller the practical influence of the Weight values on the final result. Weights are most visible in loosely constrained, open setups with many Observation steps. #### Steps - weighting a single Slot 1. Use Construct Slot with the **Allowed Module Names** and **Allowed Modules Weights** inputs. 1. Connect a list of floats to **Allowed Modules Weights**. The component pairs each Weight with the Module Name at the same list index: Weight 0 goes to the first Module Name, Weight 1 to the second, and so on. For example, if **Allowed Module Names** receives [`wall`, `corridor`, `room`], then **Allowed Modules Weights** can receive [`1.0`, `3.0`, `1.0`] to make `corridor` three times more likely than the others at this Slot. More Weights than names is an Error. Use matching Panels to keep the lists in the same order. 1. If the Weight list is shorter than the Module list, the last value is repeated for the remaining Modules, both for a flat list and for per-branch lists. For example, [`4.0`, `1.0`] for five allowed Modules gives the first Module four times the probability, and all others equal Weight of 1.0. 1. The default Weight is `1.0`. Omitting **Allowed Modules Weights** is equivalent to supplying all ones. #### Steps - weighting the entire envelope To give each Slot in the Envelope its own Weights, produce a Grasshopper data tree in which branch *i* contains the Weight list for Slot *i*. The branch structure must match the Slot list branch-for-branch. 1. Start from the Cell list you would otherwise pass to Construct Slot. 1. For each Cell, compute one float per Module - for example, by evaluating an attractor distance at the box centre, or by sampling a Z-height gradient formula. 1. Organise these floats as a data tree with one branch per Cell, each branch containing one float per Module in the same order as the Module list for that Slot. 1. Connect that tree to **Allowed Modules Weights** on Construct Slot. The data tree structure must align with the Slot list: branch 0 Weights go to Slot 0, branch 1 to Slot 1, and so on. Because the Weight list is tied positionally to the Module list, every Slot must share the same Module list if you want to drive all Weights with a single expression. If different Slots allow different Module subsets, use separate Construct Slot nodes or build the tree manually per subset. #### Use cases - **Vertical gradient** - Evaluate Weights per Slot based on Z position. For example, make glazed cladding tiles increasingly probable toward the top of the Envelope and opaque tiles more probable at the base. - **Proximity-based bias** - Increase Weights for certain Modules near specific points or curves in the Envelope, such as concentrating a feature Module around a focal point. - **Random variation** - Add slight random variation to Weights to break uniform repetition without a specific gradient. ### 2.13 Boundary handling If the outermost Faces of your Envelope must terminate in a deliberate condition - a closed pipe end, a wall Face, a slab edge, or any other intentional edge - you need explicit boundary handling. Without it, outward-facing Faces at the Envelope surface have no neighbouring Slot, so the Solver applies no constraint from that Direction: any Module is valid at an outermost Slot regardless of what Face points outward. Indifferent Faces and typed Faces alike can Face the void freely, producing open pipe ends, exposed wall Faces, or other unintended edge conditions at the grid surface. Boundary handling is not needed to prevent the Solver from failing; it is needed to enforce intentional design behaviour at the edges. #### Basic boundary 1. Create your main Envelope Cells using Homogeneous Grid or Heterogeneous Grid. 1. Use Add Boundary Layer to generate additional Cells around the Envelope. Set **Layers** (default 1), **Diagonal Neighbors** (default false), and the six direction inputs **Include +X**, **Include +Y**, **Include +Z**, **Include -X**, **Include -Y**, **Include -Z** (all default true). 1. Define a boundary Module (typically with no geometry, named e.g. `boundary`). 1. Create Slots from the boundary Cells, allowing only the boundary Module. 1. Define Rules connecting the boundary Module’s Faces to interior Modules. 1. Merge the boundary Slots with the interior Slots before solving. #### Advanced boundary - Use different boundary Modules on different Faces (e.g. `boundary-top`, `boundary-side`). - Use Are Cells Boundary to identify which Cells are on which Face. - Create multiple boundary layers with increasing depth. - For direct boundary control, create Terminators with **Construct Terminator** or **Terminator from Point**, connect them to **Terminators** on Construct Assembly, and enable **Require Terminators** (default false). Each non-flat boundary direction needs a Terminator; flat-axis Faces of a 2D Envelope do not. Terminators are ignored with a Warning when Require Terminators is off. See [2.35](#unused-faces-terminators). ### 2.14 Constructing Slots from geometry If your design Envelope follows a non-rectangular shape - a curved surface, a Brep volume, a mesh, a set of curves, or a point cloud - use **Cells From Geometry** to derive the Slot positions automatically rather than placing Cells by hand. The component maps Rhino geometry onto a regular grid and returns one Cell for each occupied cell. **Populate Method** has three values: 0 **Surface Wrap** marks Cells whose boxes overlap the surface; 1 **Fill Volume** marks Cells whose centres lie inside a closed volume; 2 **Surface Wrap + Fill Volume** (default) combines them. **Interior Only** (default false) keeps Fill Volume Cells strictly inside a Mesh, Surface or Brep, without the Surface Wrap Cells. For full input details and supported geometry types, see [§ 4.2.8 Cells from Geometry](index.html#comp-slice) in the component reference. Trimmed Rhino surfaces should be converted to Breps before connecting them - the component works most reliably with closed Breps and meshes near trimmed edges. #### Sparse envelopes The resulting Cells do not have to fill a rectangular block. The Solver handles sparse Envelopes natively: any cell absent from the grid is automatically disabled and excluded from constraint Propagation, so no gap-filling step is needed before solving. #### Surface-derived envelopes: thickening with Add Boundary Layer When the source geometry is a surface (Populate Method 0, Surface Wrap), the resulting Envelope is typically only one cell deep - a thin shell that follows the surface curvature. In many designs a single layer is not enough: the Modules cannot form meaningful spatial sequences if there is no depth to propagate through. The solution is to use **Add Boundary Layer** to grow the grid outward by one or more layers on each side of the surface. Set the layer count to 1 or 2 and selectively enable only the growth Directions you want (for example, a wall panel system may grow only in the outward normal Direction, while a floor system may grow only upward). 1. Run Cells From Geometry with Populate Method 0 (Surface Wrap) to get the surface-aligned layer. 1. Connect the output to Add Boundary Layer. Set Layers to 1 or more. Disable the Directions you do not want to grow into. 1. Merge the original surface layer with **Boundary Layer Cells** from Add Boundary Layer to create the thickened Envelope. Its output excludes the original Cells. 1. Proceed with Construct Slot on the combined Cell list. Remember that the extra boundary Cells will need appropriate boundary Modules and Rules, following the same pattern described in [2.13 Boundary handling](#boundary). #### Volume-derived envelopes (Brep or Mesh) For closed solids use Populate Method 2 (Surface Wrap + Fill Volume, the default) to capture both the outer shell and the interior. The result is a volumetric Envelope that the Solver fills completely. To then control what happens at the outer Face of the solid, add a boundary layer around the geometry-derived Cells and define boundary Rules as described in [2.13 Boundary handling](#boundary). #### Curve and point cloud envelopes Curves produce a one-cell-wide chain of Cells along the curve's path. This is useful for corridor spines, pipe routes, or any linear structural system. Points each activate the grid cell they fall into - the cell is determined by the Base Plane and Diagonal, so two points within the same cell produce only one Cell, and the resulting Box is grid-aligned regardless of where exactly the point sits. Both are inherently sparse and can be solved directly without filling. As with surface-derived grids, use Add Boundary Layer if depth beyond a single cell is needed. ### 2.15 Fixing Modules in the Envelope To guarantee that certain positions in the grid always contain a specific Module - a corner element, a structural column, an entry point - construct those Slots as Deterministic by allowing only the desired Module Name. The rest of the grid remains free for the Solver to fill. #### Building the envelope with fixed positions 1. Identify the Cells where you want to fix a Module. 1. Create Slots from those Cells, allowing only the desired Module Name. 1. Create Slots for the remaining Cells, allowing all Module Names. 1. Merge all Slots and solve. The Solver treats Deterministic Slots as fixed and propagates their constraints outward. #### Updating an existing envelope When the Envelope already exists and you only need to change certain Slots - tightening constraints in some positions, freeing others, or replacing a fixed Module - you do not need to rebuild the whole Slot list. Remove the original Slots at the positions that changed, construct the updated Slots for those same positions, and append them to the remaining unchanged Slots. Because the Solver accepts Slots in any order and organises them internally, the updated Slots can simply be placed at the end of the merged list. #### Placing a multi-cell Module at a specific position To place a [multi-cell Module](#megamodules) at a known position in the Envelope, restrict a single Slot to that Module Name and leave the surrounding Slots open with all Module Names allowed. The lock rules binding the Module's cells force the neighbouring Slots to resolve deterministically - the Solver has no valid alternative for them - so the whole Module emerges without explicitly constraining every position it occupies. Which cell lands on the fixed Slot is up to the Solver; constrain more Slots if you need a specific one there. The only requirement is that the Module fits within the available Envelope from the fixed position. If the fixed position is too close to a boundary or another constraint for the remaining cells to fit, the Solver will report a Contradiction. ### 2.16 Disallowing Rules Construct Assembly performs set subtraction on its **Disallowed Rules** and **Disallowed Connector Pairs** inputs - removing specific unwanted adjacencies from the allowed Rule set without discarding the rest. Use this when automatic suggestion creates pairings that should not exist for design or functional reasons. Rules in the disallowed set that do not exist in the allowed set are silently ignored, so it is safe to supply a broader disallowed set than strictly necessary. #### Steps 1. Generate your full Rule set using Detect Rules from Voxels or Detect Rules from Geometry. Connect it to the **Allowed Rules** input of Construct Assembly. 1. Identify the unwanted adjacency. Create the specific Rule(s) you want to forbid using any Rule construction method and connect them to the **Disallowed Rules** input of Construct Assembly. 1. Alternatively, if you use Connectors, connect unwanted Connector Pairs to the **Disallowed Connector Pairs** input - Construct Assembly generates the corresponding Rules internally and removes them. #### When to use - **Post-suggestion cleanup** - Automatic suggestion is generous; remove the few pairings that look wrong in the result. - **Functional constraints** - Prevent a window Module from connecting directly to a staircase Module, even though their geometry matches. - **Iterative design** - Keep the broad Rule set but exclude specific combinations after reviewing initial results. #### Disallow and Indifference Disallowed Rules take priority over Indifference-generated Rules. Construct Assembly applies indifference first, then removes disallowed Rules from the combined set. If indifference generates a Rule that matches a disallowed entry, the disallow wins and the Rule is removed. ### 2.17 Rule Exclusivity Automatic Rule suggestion is intentionally broad: it pairs every compatible Face it finds. Some Faces serve a specific design role and should only ever connect in one explicitly defined way - a structural joint that must land on a beam Face, a door opening that can only Face a matching frame Module. The **Exclusive Rules** and **Exclusive Connector Pairs** inputs on Construct Assembly let you declare that the Faces referenced by a chosen subset of Rules are exclusive: any other Rule that touches those same Faces is removed from the set, and the exclusive Rules are added. #### How it works Construct Assembly collects the explicit Exclusive Rules and generates Rules from Exclusive Connector Pairs. It then collects all Faces referenced by these exclusive Rules, removes every other Rule that references any of those Faces, and adds the exclusive Rules to the allowed set. #### Steps 1. Connect your allowed Rules and/or Connector Pairs to Construct Assembly as usual. 1. Identify the Faces that need exclusivity - the Faces that must connect in only one specific way. 1. Construct the exact Rules you want for those Faces and connect them to the **Exclusive Rules** input. Alternatively, connect Connector Pairs to the **Exclusive Connector Pairs** input. 1. Construct Assembly removes all other Rules referencing those Faces and adds the exclusive Rules. #### When to use - **Multi-cell Module integrity** - needs no wiring: the lock rules binding a Module's cells are engine-internal, and its internal Faces are never exposed, so a suggester cannot reach them. See [2.18 Multi-cell Modules](#megamodules). - **Structural joints** - A column Face that must always meet a beam Module and nothing else. - **Openings and frames** - A door or window Face that only pairs with its designated frame Module, never with a generic wall. - **Tighter control than Disallowed Rules** - When you want to keep a few specific pairings and remove everything else for those Faces, rather than enumerating what to remove one by one. ### 2.18 Multi-cell Modules A Module occupies one grid cell per Cell it is given. Give **Construct Module** several adjacent Cells - a 2×1×1 structural bay, a tall atrium, any element too large for a single cell - and it becomes one multi-cell Module that the Solver places as one rigid body. There are no sub-Modules and no generated part names. The cells are held together by lock rules the engine synthesizes during the assembly build; they are engine-internal and never appear in your Rule list. #### Steps 1. Select a group of adjacent Cells. 1. Connect them to Construct Module with a name and optional geometry. 1. The output is one Module. Add it to your Module list like any other. The footprint must be one face-connected chunk. Cells touching only along an edge or a corner are not bound to each other and would come apart at solve, so the build rejects them. Two Cells at the same grid position are rejected too. Each cell keeps the size of the Cell it came from, so a multi-cell Module can span a [heterogeneous grid](#heterogeneous) - a 2m bay beside a 3m one. Construct Module reconstructs the footprint from the Cells and says which arrangement it rejected when they do not form a grid. #### Faces A multi-cell Module exposes only the Faces its own footprint does not cover, numbered per direction in spatial cell order: `+X0`, `+X1`, `+X2`, and so on. A single-cell Module has one Face per direction, still spelled `+X`..`-Z`, and the plain integers 0-5 still work. A bare `+X` means the whole side - every cell of the Module exposing that direction - and `+X0` means the first of those cells. On a single-cell Module that is the same physical Face. On a multi-cell Module they differ: `big:+X` caps or constrains every Face on that side at once, `big:+X1` only the second one. Because the internal Faces are not exposed at all, there is nothing to filter before running an automatic Rule suggester - Deconstruct Module and Module Faces report the externally visible set directly. #### Geometry handling The geometry attaches to the Module once. Materialize Assembly emits it exactly once per placed instance, from whichever cells of that instance are inside the Envelope. #### Auto-rotation Set **Rotational Freedom** (0 None, 1 X, 2 Y, 3 Z, 4 Full) on Construct Module to have Construct Assembly generate rotated variants of the whole body. Rotated variants are anonymous: each carries the authored Module name plus a rotation index, not a name suffix. ### 2.19 Materializing results Materialize Assembly converts solved Slots into visible Rhino geometry. It takes a solved Discrete Assembly as input, matches each Slot to the Module it holds, and orients - translates and rotates - that Module’s geometry to the correct position in space. #### Inputs - **Assembly** - A solved Discrete Assembly from the WFC Solver's **Assemblies** output. #### Outputs - **Geometry** - Placed geometry for each Slot, as a data tree. - **Transforms** - The transformation matrix that moves each Module from its definition origin to its Slot position. Matches the Geometry output in structure. - **Modules** - The Module placed in each Slot, oriented to the Slot position. All three outputs use the data tree path `{assemblyIndex; slotIndex}`. The *assemblyIndex* is the index of the Assembly when several Assemblies are wired; *slotIndex* identifies the placed Slot. A single solution uses paths `{0;0}`, `{0;1}`, and so on. A multi-cell Module appears once per placed instance, on its anchor Slot. Flattening the tree gives a plain list of placed geometry in Slot order. #### What Transforms are for The Transforms output exists for workflows where you want to place geometry that Materialize itself did not produce. Common uses: - **Alternative geometry per Module** - Keep a lightweight stand-in on the Module to keep the Grasshopper display fast, then use the Transforms to orient a richer geometry or a detail model at each placed Slot. Wire the Transform into a standard Grasshopper Transform component alongside the geometry you want placed. - **Geometry biased for the suggesters** - Connector and Rule suggesters derive adjacency from the geometry attached to a Module: they look at where Faces, edges, or points sit relative to the Cell. You may deliberately place geometry that is offset, simplified, or positioned so that the suggesters pick up the right Faces - even though that geometry looks nothing like the finished piece. Use Transforms to place the actual intended geometry at each Slot independently of what the Solver used. #### Edge cases and warnings - **Slot is Contradictory** - The Solver failed for that Slot. Materialize skips it and reports a Warning. - **Slot is Non-deterministic** - The Slot still holds more than one possible Module, for example after a limited-observation solve. Materialize skips it and reports a Warning. - **Module Name in Slot not found in the Assembly** - The Slot references a Module that is absent from the Assembly. Materialize skips the Slot and reports an Error. - **Rotation variants with the same name** - Construct Assembly merges rotation variants of one source Module under its name. Materialize places the first variant that fits the Slot dimensions. - **Repeated authored Module names** - Construct Assembly rejects separately authored Modules with the same name before they reach Materialize. - **Module exists but its dimensions do not match the Slot** - If no variant fits the Slot dimensions, Materialize reports an Error and produces no placement for that Slot. - **Module has no geometry** - Materialize still outputs a Transform and a Modules entry, but no Geometry and no Remark for that placement. This is normal for Empty Modules. - **Nothing is placed** - Materialize reports a Warning with counts of Slots, Modules, contradictory Slots, and skipped Non-deterministic Slots. #### Baking: blocks vs. raw geometry When you bake the Materialize component directly (right-click → Bake, or via the standard Grasshopper bake shortcut), it creates **Rhino block instances**. For each unique Module, one block definition is added to the document and every placement becomes a lightweight reference to that definition. Block names follow the pattern `ModuleName_1`, `ModuleName_2`, etc., incrementing automatically if a block with that name already exists. This is the recommended approach for large Envelopes - an Envelope with 10,000 Slots and 8 Module types creates 8 block definitions rather than 10,000 individual geometry objects. If you need **raw geometry without blocks** - for example to edit individual placements, run boolean operations, or hand off to a tool that does not support blocks - do not bake the component directly. Instead, wire the **Geometry** output into a standalone geometry parameter, and bake that parameter. The Geometry output already contains fully transformed copies of the Module geometry; baking from a plain parameter produces ordinary Rhino geometry with no block structure. ### 2.20 Solver settings The Solver's Random Seed, Max Attempts, Max Observations, and Return first inputs control its search and outputs. Set Run to True to execute the solve. | Parameter | Default | Description | | --- | --- | --- | | **Random Seed** | 42 | Controls the random choices during Observation. Different Seeds produce different results. Set to a fixed value for reproducibility. | | **Max Attempts** | processor cores + 1 | Maximum number of solve Attempts. Each Attempt uses a different Random Seed (incrementing from the base Seed). More Attempts increase the chance of finding a valid solution. | | **Max Observations** | unlimited | Maximum number of Observation steps per Attempt. Limits computation for very large grids. Set to a lower value to get partial results. | | **Return first** | true | If true, returns the lowest-seed fully collapsed Attempt found (or the lowest-seed partial Attempt if none fully collapses). If false, runs all Attempts and returns all successful results, sorted by Seed. | | **Run** | false | Set to True to run the Solver. | Each Attempt uses a successive Seed: Attempt 1 uses the base Seed, Attempt 2 uses base + 1, Attempt 3 uses base + 2, and so on. With a Seed of 0 and 1000 Attempts the Solver explores Seeds 0–999. If you then change the Seed to 1, it explores Seeds 1–1000 - 999 of the same Seeds plus one new one. To explore a genuinely fresh range, advance the Seed by at least the Attempt count: step from 0 to 1000, then 2000, 3000, and so on. A practical workflow: connect a number slider to **Random Seed** and set its step size equal to **Max Attempts**. Each slider position then covers a non-overlapping block of Seeds with no repeated territory. The **Seeds** output returns the Seed for each returned Assembly, including a representative contradictory result if every Attempt contradicts. On a difficult grid the Solver may take long to find a solution - if it eventually succeeds on Attempt 500 with Seed 542, that value is returned. Before saving the file, wire that returned Seed back into **Random Seed** and reduce **Max Attempts** to 1, so the Solver finds the result immediately on every subsequent open. A returned Seed is only reliable while the setup stays exactly the same: any change to a Rule, Module, Weight, or the Envelope will alter the outcome. See [FAQ 1.14](./faq.html#faq-13). There are no good or bad Seeds - all Seeds are equivalent; see [FAQ 1.44](./faq.html#faq-43). ### 2.21 Audit-driven debugging When the Solver fails or produces unexpected results, run Audit before making any changes. Connect **both** your input Assembly (from Construct Assembly, before solving) and an Assembly from the Solver's **Assemblies** output (after solving) to separate Audit Assembly instances. Comparing the two Audit reports shows how the Slot states changed. The **Report** output gives a human-readable summary; the individual data outputs give lists you can wire into other components. Enable **Diagnose Resolution** (default false) on the unsolved Assembly to run a deterministic diagnostic solve (Seed 0); **Resolution** then reports which Slots were observed, propagated, or unresolved, including observation order. Audit covers four areas: - **Envelope** - confirms the Slots form a valid sparse grid, reports its X × Y × Z dimensions, and whether it is Homogeneous or Heterogeneous. - **Slots** - identifies Slots that allow only geometry-free Modules, Slots that allow unknown Module Names, and Slots whose candidate Modules all have mismatched Cell dimensions. An unfitting Slot produces a contradiction during solving; add a fitting Module variant or change the Slot's Cell dimensions. - **Modules** - counts how many Slots admit each Module; flags Modules that appear in no Slot or Rule, or whose Faces are absent from Rules; lists Modules with and without geometry; and identifies Modules that only connect to themselves. Separately authored Modules must have unique names. - **Rules and Faces** - flags Rules that reference unknown or unused Module Names; reports per-Rule occurrence counts, **Rules Never Firing**, and a first-occurrence mask for deduplication; lists Faces not covered by any Rule (these become Indifferent), Faces that are technically in Rules but effectively Indifferent because they accept every possible neighbour, and Faces covered by exactly one Rule (**Singly Constrained Faces** - useful for diagnosing why results look identical across Seeds); reports whether all Faces in the setup are Indifferent; and flags the **Potential Over-Constraint** condition, which predicts contradictions when Indifferent Faces are disabled. Run Audit before Solve whenever anything is not working as expected. ### 2.22 Visualizing Rules and Faces A Rule that references the right Face names can still place two Modules in a physically wrong relationship - facing the wrong way, offset, or in a rotation you did not intend - and none of that is visible in the data. Viewport tools make Rule connections and Face Directions visible. - Preview Rule - Draws cubic Bezier curves between Face centres: solid for direct Rules and dashed for rotation-expanded Rules. - Preview Faces - Draws Face rectangles, anchor planes and Direction arrows, coloured by Direction (+X red, +Y green, and so on); Get Module Faces also previews each Face Direction on a Module. - Preview Rule in Slots - Positions two Modules according to a Rule at a specific pivot, creating a visual Assembly of what the Rule allows. Also outputs the placed Modules with their rotation settings preserved. - Preview Connector - Draws Connector arrows, labels, and rotation indicators; with Connector Pairs, it draws Bezier curves between compatible Faces. ### 2.23 Grid topology analysis Cells are geometrically placed in 3D space, but the Solver works with a discrete grid of integer coordinates. Grid Topology and Neighbor Cells let you query that discrete structure from Grasshopper, so you can drive Slot construction and Module weighting from grid position and adjacency rather than from raw geometry checks. #### Grid Topology Takes a flat list of Cells and outputs two things: - **Relative Coordinates** - one integer-coordinate point per Cell, normalised so the minimum corner of the bounding grid is at the origin. Index-matched to the input list: Cell 0 → coordinate 0. - **Topology** - a data tree of neighbour indices. Branch `{i}` contains the indices of all Cells adjacent to Cell `i`. Enable **Diagonal Neighbors** to include Face-diagonal and corner adjacency; disable **Bi-Directional Topology** to list only positive-Direction neighbours (useful for building undirected graphs without duplicates). Because the Relative Coordinates are index-matched to the Cell list, you can use the X, Y, Z components of each coordinate directly as sorting or filtering keys. For example, remap Z values and apply different **Allowed Modules Weights** to specific Modules within each Slot to create a density gradient. To change solve order instead, wire the remapped values to **Slot Priorities** on Construct Assembly. #### Neighbor Cells Takes **Cells**, **Cell Indices** (the focus Cells), **Layers** (default 1), and **Include X**, **Include Y**, and **Include Z** (all default true). Returns **Neighbor Indices**: a sorted flat list of nearby Cell indices within the specified layer count, excluding the input indices. Provide starting indices from a coordinate filter, an explicit list, or another query, then set Layers to the desired radius. Merge **Cell Indices** with **Neighbor Indices** to include both the focus Cells and their neighbours when constructing Slots for a region with different allowed Modules or Weights. For vertical adjacency, disable **Include X** and **Include Y** and leave **Include Z** enabled. #### Workflow: inward weight gradient 1. Build the Cell list and feed it into both Grid Topology and your Slot constructor. 1. Decompose the Relative Coordinates; take the X component (or whichever Axis represents depth). 1. Remap that value from its actual min-max range to `0.1 - 1.0`. 1. For each Slot, use the remapped value as the Weight of one allowed Module and a different value for another Module on **Allowed Modules Weights**. This changes their relative likelihood within that Slot. To resolve high-value Slots first instead, wire the values to **Slot Priorities** on Construct Assembly. #### Workflow: reopening a region of the solved result When part of a solved result is not working - a zone is too repetitive, a corner has wrong adjacencies, a floor level looks out of place - you can reopen just that region for re-solving while retaining the Module names elsewhere. 1. After a complete solve, connect the Solver's **Assemblies** output to **Deconstruct Assembly** and use its **Slots** output. Each solved Slot is Deterministic: exactly one Module Name is allowed. 1. Use Grid Topology on the original Cell list to get Relative Coordinates. Filter by coordinate value to identify the region to reopen - all Cells at a specific Z level, an X-range column, or any other positional criterion. 1. Pass the selected indices into Neighbor Cells with **Layers = 1** (or more) to expand the selection by one ring. This includes the immediate transition Slots, preventing constraint conflicts at the edge of the reopened zone. 1. Merge the selected focus indices with **Neighbor Indices**. For that combined selection, construct new open Slots from those Cells with the full (or broader) allowed Module list - Non-deterministic. 1. For every other Cell, keep the solved Slots as-is. These Slots retain the solved Module name, but not its orientation: rotation variants may be chosen again during the next solve. 1. Merge the open Slots with the retained Slots and re-solve. The Solver fills the reopened region while keeping the Module names in the retained Slots. Repeating steps 2-6 with larger or different regions lets you refine the result incrementally without starting from scratch. If the region to identify is defined by boundary membership rather than interior position, Are Cells Boundary provides the boundary indices directly. For Envelope tasks using a boundary layer, see [2.13](#boundary). ### 2.24 Rules from Face Groups When several Faces across your Module library should all be compatible with each other - all the Faces where corridor pieces join, all the stacking Faces between floor Modules, all pipe openings - collect them into a single list and wire it to *both* Source Faces and Target Faces of Construct Rules from Faces. The component creates a Rule for every pair of Faces in that list: opposing pairs become direct Rules, and non-opposing pairs (including two Faces pointing the same Direction) are connector-symmetry hints that Construct Assembly expands through rotation variants. One list, one component, every compatible pairing in one step. #### Faces from Point When you want to identify a specific Module Face without counting Face Indices, place a point on that Face. Faces From Point looks up which Face occupies that position and returns its `FaceId`, ready to use anywhere a Face is expected. 1. Place a Rhino point on the Module Face you want to identify. 1. Wire the Module list and the point into Faces From Point. 1. The output is the `FaceId` for that Face. Use it wherever a Face is expected. #### Rule from Curve When you want to define which Faces are compatible by drawing directly in the viewport, use Rule From Curve. Draw a curve whose start point sits on one Module Face and end point on another. The component detects which Faces the endpoints land on and creates a Rule for each endpoint Face pair; non-opposing pairs are connector-symmetry hints expanded into opposing-face Rules by Construct Assembly. A single curve can produce multiple Rules when its endpoints overlap Faces from different Modules. 1. Draw curves in Rhino between Module Faces you want to connect. 1. Wire the curves and the Module list into Rule From Curve. 1. The output is a deduplicated flat Rule list. Merge with any other Rule lists before passing to Construct Assembly. #### Other methods Get Module Faces exposes Faces by Direction as named outputs (`+X`, `−X`, `+Y`, `−Y`, `+Z`, `−Z`). Merge equivalent outputs to build groups manually. **Group Faces by Voxels** instead groups external Faces with identical voxel fingerprints into **Face Groups** branches; wire each branch to both Source Faces and Target Faces of Construct Rules from Faces. **Rule from Points** cross-references Source Points and Target Points on Module Faces to create Rules, alongside the curve-based method above. ### 2.25 Proto-results workflow Some designs only make sense as a whole: a pipe network where each Module contributes one segment, a structural frame where beams must meet at joints, a tiled surface where edges must align across boundaries. In these cases the per-Module geometry is intentionally preliminary - **skeleton geometry** such as centrelines, control points, or boundary curves. It has no value on its own; it becomes the final design only after the Solver has arranged all Modules and the materialized pieces are joined or processed in Grasshopper. #### Steps: pipe network 1. Design Modules with centreline curves instead of pipe surfaces. For example, a straight pipe Module contains a line from the centre of its `-X` Face to the centre of its `+X` Face. A corner Module contains a quarter-arc. 1. Design the Faces so that pipe openings align at Module boundaries - centrelines should start and end at the centre of the Faces. 1. Solve the grid normally. 1. Materialize to get placed centrelines at every Slot. 1. Flatten and join the curves using Grasshopper’s **Join Curves** component. 1. Apply the **Pipe** component to create tubular surfaces from the joined centrelines. #### Other applications - **Conveyor routing** - Centrelines become conveyor track surfaces after lofting. - **Structural frames** - Skeleton lines become beams after applying cross-sections. - **Wiring or plumbing** - Route paths through the grid, then create physical conduits. - **Surface continuity** - Place control points at Face boundaries, then create NURBS surfaces across Module boundaries. ### 2.26 2D grids (floor layouts and panel systems) For designs that are essentially flat - a factory floor layout, a panel system pattern, a tiling, a 2D game level - collapse one grid dimension to a single cell. Running the Solver in three dimensions for an inherently 2D problem wastes computation and complicates Module design. #### Steps 1. Use Homogeneous Grid with count = 1 in the Direction you want to collapse. For a plan (XY), set Z count to 1. For a facade (XZ), set Y count to 1. 1. Design Modules as flat tiles. Their bounding box depth should match the single-cell dimension. Set **Rotational Freedom** to 3 (Z only) on Construct Module or Module Rotations to generate in-plane orientations without defining separate Modules for each Direction. 1. Define Rules only for the four in-plane Directions. The collapsed Axis needs no Rules at all: every cell already sits on the grid boundary in that Direction, so those Faces are outside the solve space. For *wrapping* (seamlessly tiling) patterns, assign the same Face role to opposing boundary Faces of the Envelope - left and right edges share one role, top and bottom edges share another. For *bounded* patterns, add a boundary layer instead - see [2.13 Boundary handling](#boundary). 1. Solve and Materialize as usual. #### Linear grids (two dimensions collapsed) Collapsing two dimensions reduces the grid to a single row of cells - a genuinely linear design. Set two of the three Axis counts to 1 and leave only one count greater than 1. Rules are only needed for the two Faces along the single active Axis; the four remaining Face Directions are all boundary Faces and require no Rules. This is useful for designs that are inherently sequential: - **Conveyor or pipeline segments** - each Module is one section of a run; Rules enforce which section types may follow which. - **Corridor or street sequences** - a linear arrangement of room or block types with controlled transitions. - **Friezes and border patterns** - decorative bands where the pattern repeats or varies along one Axis only. - **Assembly sequences** - ordered steps or stages where only left-right (or before-after) adjacency matters. #### Common applications (2D) - **Floor layouts** - Modules represent zone types (conveyor run, workstation bay, buffer zone). Rules enforce connectivity requirements. - **Panel systems** - Modules represent panel types (solid, perforated, access). Rules control edge profile continuity. - **Tile patterns** - Modules are tile shapes. Rules enforce edge matching for seamless patterns. - **Game levels** - Modules represent terrain types. Rules create coherent maps. ### 2.27 Sparse grids with intentional gaps Irregular Envelope shapes - curved boundaries, L-shaped plans, building volumes with courtyards - can be created by starting from geometry directly or by filtering a rectangular grid. Either way, the remaining Cells do not need to fill a convex volume; the Solver handles any contiguous irregular shape. #### From geometry (recommended) Cells From Geometry generates Cells that follow an input shape directly. Set **Populate Method** to **Fill Volume** (1) or **Surface Wrap + Fill Volume** (2) for closed Breps and meshes. Fill Volume selects Cells whose centres lie inside the volume; the combined method also includes surface Cells. #### From a filtered rectangular grid Alternatively, create a full rectangular grid with Homogeneous Grid and remove unwanted Cells using Grasshopper list operations (Cull Pattern, Dispatch, or membership testing against a Brep). This approach gives more direct control over which cells to include. If filtering produces disconnected clusters, pass the whole filtered Cell set to one Add Boundary Layer component. It grows one occupancy mask and outputs only the added Cells. Merge that output with the filtered input Cells. #### Boundary handling Removing Cells exposes new boundary Faces on the cells that remain. These are handled automatically by the WFC Solver - no extra Rules are needed for them. If you want explicit control over what appears at the Envelope boundary, see [2.13 Boundary handling](#boundary). ### 2.28 Rectangular (non-square) Modules Monoceros does not require cubic cells - each Axis can have its own dimension. To use a 2:1 aspect ratio for structural bays, or panels that are 3 m wide and 1.5 m tall, set the grid diagonal vector to match the desired cell proportions. #### Steps 1. Choose a diagonal vector with different values per Axis, e.g. `{2, 1, 3}` for cells that are 2 units wide, 1 unit deep, and 3 units tall. 1. Use Homogeneous Grid with this diagonal to generate Cells. 1. Create a separate Module grid: a Homogeneous Grid with the same diagonal vector and counts of 1×1×1. Use that single Cell as the Module box. Keep it visually separate from the Envelope grid in the viewport. The Module dimensions will automatically match the cell dimensions. 1. When using Module Rotations, rotations that produce Modules with incompatible dimensions will have mismatched bounding boxes. For non-cubic cells, only rotations around an Axis where both perpendicular dimensions are equal produce valid variants (e.g. for a 2×2×3 cell, Z-Axis rotation works because X and Y are equal). Rotations that transpose dimensions also make sense in a Heterogeneous Grid where Slots of both orientations exist - a 2×1×3 Module rotated 90° around Z becomes a 1×2×3 Module, which is valid wherever a Slot of that shape appears in the Envelope (see [2.11 Heterogeneous Grid](#heterogeneous)). ### 2.29 Combining manual and automatic Rules Automatic Rule suggestion is convenient but imprecise - it may miss important pairings or include unwanted ones. Manual Rule definition is precise but tedious for large Module sets. Combining both uses automation for the bulk of the Rule set while leaving room to hand-tune the details. #### Strategy 1. Run Detect Rules from Voxels to generate the bulk Rule set automatically. 1. Create additional explicit Rules for specific pairings the suggestion missed (e.g. a structural Module that must always sit under a floor Module). 1. Connect any Rules that should not exist to the **Disallowed Rules** input of Construct Assembly (e.g. two incompatible aesthetic Modules). 1. Connect all Rule lists (automatic + manual) to the **Allowed Rules** input of Construct Assembly. Duplicate Rules are harmless - Construct Assembly deduplicates internally. ### 2.30 Restricting Modules to specific regions In most setups, leave **Allowed Module Names** on Construct Slot unconnected so every Slot admits every Module that fits its Cell. To make different parts of the Envelope look or behave differently - ground floor different from upper floors, a façade strip different from the interior, one wing of a building different from another - provide a restricted **Allowed Module Names** list for each region. The Solver then picks only from the permitted Modules for each Slot. #### Steps 1. Create the full Cell set. 1. Partition Cells into regions using any spatial query: Z-coordinate ranges for floor levels, containment in a Brep volume for a specific wing, distance from a centre point for a radial split, or Are Cells Boundary for Envelope vs. interior. 1. Construct Slots for each region with only the Module Names relevant to that zone. For example, ground-floor Slots allow `[entrance, shop_unit, lobby]` while upper-floor Slots allow `[apartment_a, apartment_b, corridor]`. 1. Make sure Rules exist across the region boundary - the Modules that sit at the interface between two zones must have Rules connecting them to each other. If `lobby` can sit below `corridor`, define a Rule for `lobby:+Z` connecting to `corridor:-Z`. 1. Merge all Slot lists and solve. #### Common region patterns - **Floor zoning** - Ground floor public programme, upper floors residential or office. Each level gets its own Module palette. - **Core vs. perimeter** - Service or structural Modules in the centre; façade Modules around the edge. - **Wings or phases** - Two building wings share a grid but use different Module sets, connected only through a shared transition Module at the junction. - **Roof level** - Plant room or roof terrace Modules restricted to the top layer. ### 2.31 Occurrence Count analysis Occurrence Count reports how many Slots in the solved Envelope contain a given Module Name - useful for fabrication estimates, cost calculations, or verifying that the distribution matches design intent. Wire a list of Module Names to get one Count per name. #### Steps 1. Solve the grid, connect the Solver's **Assemblies** output to **Deconstruct Assembly**, and use its **Slots** output. 1. Connect the Module Names you want to count and the solved Slots to **Occurrence Count**. The component outputs one Count per Module Name. 1. Use the full Module Name list to count all Modules at once; no loop component is required. 1. Display the counts in a Panel, feed them into a chart, or use them as logic inputs (e.g. a value comparator to flag underrepresented Modules). #### Only Resolved By default (**Only Resolved** = `true`), only Slots where the target Module is the sole allowed option are counted. Set it to `false` to also count Slots that still allow the target Module alongside others - useful for inspecting partially solved or pre-solve Envelopes. #### Applications - **Bill of materials** - Count how many of each Module type need to be fabricated. - **Distribution analysis** - Verify that Weights and Rules produce the intended proportions. - **Seed comparison** - Run multiple Seeds and compare counts to find the most balanced result. ### 2.32 Exporting results for fabrication To take a Monoceros result outside Grasshopper - for 3D printing, CNC milling, laser cutting, or handoff to another software package - convert the WFC output into a format suitable for downstream processing: meshes, STEP files, DXF unfoldings, or labelled part lists. The [WFC Solver](index.html#comp-solver) right-click **Export Project…** item writes the authored setup as a portable Monoceros project file, JSON with inline OBJ geometry. It contains no solved Slot states or **Random Seed**. Use the methods below to export results. #### Mesh export (3D printing, game engines) 1. Materialize the result to get placed geometry. 1. If Module geometry is Brep-based, convert to Mesh using Grasshopper’s **Mesh Brep** component. 1. Join all meshes using **Mesh Join**. 1. Bake to Rhino and export as STL, OBJ, or 3MF. #### Part list export (fabrication, CNC) 1. Use **Occurrence Count** to count each Module type. 1. Use the Materialize **Transforms** output to extract position and rotation per placement. 1. Export placement data as CSV using Grasshopper’s text writing components: columns for Module Name, X, Y, Z, rotation angle. 1. Use this CSV for pick-and-place machines, Assembly instructions, or BIM software. #### Unfolding (laser cutting, sheet fabrication) 1. Materialize the result. 1. Use Grasshopper’s **Unroll Brep** or third-party unfolding plug-ins to flatten Module geometry. 1. Add Module Names or labels to each flattened piece for Assembly reference. 1. Export as DXF or PDF for the laser cutter or CNC router. ### 2.33 Auditing an Assembly Every Assembly produced by Construct Assembly carries an embedded Audit result. To inspect it, connect the Assembly to **Audit Assembly**. It has two inputs, **Assembly** and **Diagnose Resolution** (default false), and 47 diagnostic outputs covering the Envelope, Slots, Modules, Rules, Faces, and resolution. #### Recommended audit workflow 1. Wire Construct Assembly's output to both the WFC Solver and Audit Assembly in parallel. 1. Check the **Report** output first - it gives a human-readable summary of all findings. 1. On an unsolved Assembly, enable **Diagnose Resolution** and inspect **Resolution** for observed, propagated, and unresolved Slots and their observation order. Check **Rules Never Firing** for authored Rules that cannot apply. 1. If the Report mentions errors, use the individual data outputs to identify exactly which Modules, Slots, or Rules are problematic. 1. Fix the inputs to Construct Assembly and re-run. The Assembly and its Audit update automatically. #### Key diagnostic outputs - **Slots Without Fitting Modules** - Slots whose allowed Modules all have mismatched Cell dimensions. These cause a contradiction during solving; add a fitting Module variant or change the Slot's Cell dimensions. - **Module Variant Never In Slots** - Module variants that are not allowed in any Slot. These cannot appear in any result and may indicate a wiring mistake. - **Faces Not In Rules** - uncovered Faces that will become Indifferent when indifference is enabled. Connect to Preview Faces to visualize them. - **Potential Over-Constraint** - true when uncovered Faces exist and indifference is disabled, which almost always causes Solver contradictions. - **Singly Constrained Faces** - Faces covered by exactly one Rule. When all Faces are singly constrained, every Seed produces the same result. - **Effectively Indifferent Faces** - Faces that appear in Rules but accept every possible opposing neighbour, so they do not constrain the Solver at all. #### Common patterns - If the Solver contradicts immediately, check **Potential Over-Constraint** and **Faces Not In Rules**. - If results look identical across Seeds, check **Singly Constrained Faces**. - If a Slot has no fitting Module, check **Slots Without Fitting Modules** and correct the allowed Modules or Cell dimensions. ### 2.34 Iterative solving with partial results You can solve an Assembly in stages by limiting the number of observations per Attempt, then deconstructing the partial result, modifying it, reassembling, and solving again. This gives you manual control over regions of the Envelope while letting the Solver fill in the rest. #### Workflow 1. **First pass - partial solve.** Set the WFC Solver's **Max Observations** to a low number (e.g. 20-50% of the total Slot count). The Solver will assign Modules to some Slots and leave the rest Non-deterministic. 1. **Deconstruct.** Connect an Assembly from the Solver's **Assemblies** output to **Deconstruct Assembly**. This gives you narrowed authored Slots (some Deterministic, some still with multiple candidates), along with the authored Modules and Rules. A Deterministic Slot retains the source Module name, but not its solved orientation. 1. **Modify.** Edit the Slots from the previous step. For example: - Force a specific Module into a Slot by replacing its allowed list with a single name. - Remove a Module from certain Slots to prevent it from appearing in a region. - Adjust Weights to bias the next solving pass. - Add or remove Rules to change adjacency constraints for the remaining unsolved region. 1. **Reassemble.** Feed the modified Modules, Slots, and Rules back into a second **Construct Assembly**. This produces a new Assembly with the partial solution baked in and the modified constraints applied. 1. **Second pass - complete solve.** Connect the new Assembly to another **WFC Solver** instance (or the same one with updated inputs). This time, set Max Observations to unlimited. The Solver will respect the already-determined Slots and fill in only the remaining Non-deterministic ones. 1. **Materialize.** Connect the fully solved Assembly to **Materialize Assembly** to extract geometry. #### Tips - The Solver treats Deterministic Slots (1 allowed Module) as fixed - it never changes them. This is why partial results carry forward correctly. - You can chain more than two passes. Each pass can have its own Observation limit, Weight adjustments, and Rule modifications. - Use **Audit Assembly** after reassembly to verify that your modifications did not introduce inconsistencies (unknown Module Names, orphaned Faces, etc.). - Use **Changed Slots** to compare the Slots before and after each solving pass, so you can see exactly which Slots the Solver filled in. ### 2.35 Marking unused Faces as Terminators When **Require Terminators** is enabled on Construct Assembly, each boundary-facing Module Face on a non-flat axis needs a Terminator, and at least one Terminator is required in each direction. Faces on a flat axis, such as the front and back of a 2D Envelope, need no Terminator. Faces covered by explicit Rules or Connectors usually get Terminators placed by hand, but Indifferent Faces (those with no Rule or Connector) are easy to miss. The **Used Faces** component finds these gaps. #### Workflow 1. **Identify Unused Faces.** Place a **Used Faces** component. Connect your **Modules** to its M input, your **Rules** to R, your **Connectors** to C, and any existing **Terminators** to T. The **Unused Faces** (UF) output lists every Face without Rule, Connector, or Terminator coverage — one branch per Module. 1. **Create Terminators.** Connect the UF output to **Construct Terminator**. This produces one Terminator per unused Face. 1. **Merge Terminators.** If you already have manually placed Terminators, merge both lists (e.g. with a Merge component). 1. **Wire into Construct Assembly.** Connect the merged Terminators to the **Terminators** input and enable **Require Terminators**. Check that each required boundary direction has at least one Terminator. #### Why this works Indifferent Faces already receive automatic adjacency Rules pairing them with every opposing Indifferent Face. Marking them as Terminators simply declares that they may also Face the boundary — which is the natural expectation for Faces that have no typed constraint. Without these Terminators, Require Terminators would reject any Module whose Indifferent Face happens to land on the Envelope edge. #### Tips - Run this pattern *before* Construct Assembly so the Terminator list is complete on the first solve. - Use **Audit Assembly** after to confirm **Terminator Faces Missing** is empty. - If you later add Rules or Connectors to previously Unused Faces, Used Faces will update automatically and the Terminator list will shrink accordingly. See also workflows and FAQs: [2.13 Boundary handling](#boundary), [2.20 How to allow all indifferent Faces on the boundary](faq.html#tip-20), [4.3.6 Used Faces](index.html#comp-usedfaces). ### 2.36 Converting Monoceros 1 or 2 projects to Monoceros 3 Monoceros 1 and 2 ship a **Convert to v3** component on a new *Monoceros 3* ribbon tab. Drop it onto the wire that crosses from your legacy definition into Monoceros 3 and it produces v3 Modules, Rules, and Slots that you wire into Monoceros 3's **Construct Assembly**. The conversion is one-shot and reversible — the original definition stays untouched, the converter sits on top of it, and you can delete the converter to fall back to the legacy Solver at any time. #### Prerequisites 1. **Install Monoceros 3** from the Yak Package Manager or [monoceros.tools](https://www.monoceros.tools/). Monoceros 1, 2, and 3 are independent `.gha` plug-ins and load side-by-side in the same Rhino session. 1. **Update the legacy plug-in** to the build that includes the *Monoceros 3* ribbon tab. Earlier releases of Monoceros 1 and 2 do not contain the converter. 1. Restart Rhino so Grasshopper picks up both plug-ins. When v3 is detected, every legacy component shows a yellow remark "Monoceros 3 is installed…" pointing at the converter. #### Steps 1. Open your existing Monoceros 1 or 2 definition. 1. From the **Monoceros 3** ribbon tab on the *legacy* plug-in (not the v3 tab), drop a single **Convert to v3** component onto the canvas. 1. Wire your legacy **Modules**, **Rules**, and **Slots** into the converter's three inputs. The converter needs all three together so it can resolve cross-references (multi-cell Rule indices into Modules, typed-Rule names into Connectors, and so on). 1. Wire the converter's outputs into Monoceros 3's **Construct Assembly**: - **From v1:** five outputs — v3 Modules, Rules, Connectors, Connector Pairs, Slots. - **From v2:** three outputs — v3 Modules, Rules, Slots. v2 has no typed Rules, so no Connectors or Connector Pairs are emitted. 1. Wire Construct Assembly into the v3 **WFC Solver**, set **Run** to True, and continue through the v3 pipeline. #### How the type translation works - **Slots.** Box, allowed Module Names, Weights, and allow-all flag transfer 1:1 from v2; v1 Slots reconstruct the Box from the legacy `BasePlane`, `RelativeCenter`, and `Diagonal`. The v3-only original allow-all flag takes its default. - **Single-cell modules.** Rebuilt with the same Box and geometry. In v3, Rotational Freedom defaults to 0 (None); set it to 1-4 on a converted Module to enable rotation variants. - **Megamodules (v1 only).** Converted to a single v3 multi-cell Module whose footprint is the original cell set. The engine synthesizes the lock rules during the assembly build, so no extra Rules are emitted. - **Explicit Rules.** 1:1 cast from both v1 and v2. A v1 megamodule Rule index is re-targeted to the corresponding numbered Face of the converted multi-cell Module. - **Typed Rules (v1 only).** Each typed Rule becomes a v3 **Connector** with all symmetry flags set to `true` (matching v1's untyped behaviour). Unique Connector type names are emitted as `ConnectorPair(name, name)` so any Face tagged `T` connects to any other Face tagged `T`, preserving v1 semantics. - **Indifference.** v1's `"indifferent"` typed Rule converts to a v3 Connector / ConnectorPair pair named `"indifferent"`. v3 *also* has a separate **Indifference** flag on Construct Assembly (default on) that auto-pairs Unused Faces. The converter raises a Remark whenever it sees Indifferent Rules so you can choose: - *Strict v1 parity* — turn the v3 Indifference flag **off**. Only the Faces explicitly marked Indifferent in v1 will accept any neighbour. - *Modern v3 behaviour* — leave the flag **on**. v3's auto-indifference covers any unused Face in addition to the converted ones, which is usually what you want for a fresh project but more permissive than v1's strict model. - **Reserved characters.** v3 reserves `:`, `->`, `@`, `#`, and newline in Module and Connector names. Names containing these are sanitised (`->` becomes `_to_`, others become `_`). The converter logs a Remark naming both the original and rewritten name. If two distinct legacy names sanitise to the same v3 name, the converter does not auto-rename — rename one of them in your legacy definition before converting. #### What is not converted - **Discrete Assembly.** The converter emits the ingredients (Modules, Rules, Slots, optionally Connectors and Pairs) and leaves the Assembly itself to v3's **Construct Assembly**. This means new v3 features — Terminators, indifference toggles, exclusive Rules, Audit, rotation expansion — all flow into your converted definition automatically as v3 evolves. - **Terminators.** v1 and v2 expressed strict-boundary semantics through the built-in `Out` Module. v3 uses explicit Terminators authored against Module Faces. After conversion, add Terminators in v3 if you need strict boundary enforcement — see [2.35 Marking unused Faces as Terminators](#unused-faces-terminators). - **v1's `Empty` reserved name.** v3 has no reserved Module names: `Out` and `Empty` are ordinary Module names. Use a Module without geometry for a Slot that may materialize as empty; see [2.6 Empty Module](#empty-module). #### After converting The converted definition runs in v3 with v1/v2 behaviour. To take advantage of v3 features, layer them on top: - Set **Rotational Freedom** to 1-4 on individual Modules to enable rotation variants. - Add Terminators and toggle **Require Terminators** on Construct Assembly for strict boundary control. - Replace typed Rules (v1) with Connectors authored directly against Module Faces — converted Connectors are a drop-in replacement and easier to reason about. - Use v3's Audit and Used Faces components to spot orphaned Faces, missing Rules, or Modules unreachable from the boundary. See also: [FAQ entries on converting from Monoceros 1 or 2](faq.html#tip-conversion), [2.18 Multi-cell Modules](#megamodules), [2.3 The Assembly workflow](#assembly-workflow). # FAQ & tips 2. Tips & Tricks - [2.1 Start small, scale up](#tip-1) - [2.2 Always run Audit before Solve](#tip-2) - [2.3 Name Modules descriptively](#tip-3) - [2.4 Use multiple void Module types](#tip-4) - [2.5 Use attractor-based weight gradients](#tip-5) - [2.6 Bulk Rule creation](#tip-6) - [2.7 Test Rules visually before solving](#tip-7) - [2.8 Bake as block instances for smaller files](#tip-8) - [2.9 Use Data Recorder to save multiple results](#tip-9) - [2.10 Use Add Boundary Layer for irregular Envelopes](#tip-10) - [2.11 Understand the Materialize output tree](#tip-11) - [2.12 Disable the Solver while editing](#tip-12) - [2.13 Proto-results save computation time](#tip-13) - [2.14 Use Sample Geometry to verify Module content](#tip-14) - [2.15 Compare results with Changed Slots](#tip-15) - [2.16 Use Module Rotations for isotropic geometry](#tip-16) - [2.17 To exclude a Module from a zone, remove it from allowed names](#tip-17) - [2.18 Plan Face roles before modelling geometry](#tip-18) - [2.19 Let Construct Assembly stamp AllModulesCount](#tip-19) - [2.20 How to allow all indifferent Faces on the boundary](#tip-20) ## 1. FAQ ### 1.1 What is a good exercise to start? Start with the [Bare minimum](./workflows.html#bare-minimum) example. Define two simple Modules (e.g. cubes with different colors), create Rules that allow them to be neighbours, build a small 3×3×3 Envelope, and solve. Once you see results, experiment with adding a third Module, adjusting Weights, and changing Rules. See also workflows and FAQs: [2.1 Bare minimum](workflows.html#bare-minimum), [1.1 Top-down: envelope first](workflows.html#strategy-topdown). ### 1.2 Does the solver always fill every cell in the Envelope? A successful WFC solve assigns exactly one Module to every Slot. When every attempt contradicts, the Solver returns the Assembly from the lowest contradictory Seed, marks its Contradictory output true, and reports that Seed in its Seeds output. An attempt stopped by Max Observations can return a partial Assembly with non-deterministic Slots. Materialize Assembly skips contradictory and non-deterministic Slots. The Growth Solver behaves differently: a Slot that loses all Module candidates settles empty instead of producing a Contradiction. Materialize Assembly skips that Slot. The WFC Solver's behavior surprises users who expect it to skip empty areas or leave gaps where no Module fits naturally. It does not. If you want certain cells to appear visually empty - voids, gaps, courtyards, open air - you must design an explicit Module for that purpose. A Module constructed without geometry occupies the cell in the constraint system and contributes nothing visible after Materialize. Any Module whose role is “nothing visible here” - a grass lawn, a structural void, a neutral filler - serves the same function. See [1.8 How do I create an “empty” Module?](#faq-7) and [2.6 Empty Module](./workflows.html#empty-module) in the Workflows manual. This property also explains contradictions in overconstrained setups. When the Solver reaches a Slot for which no Module satisfies all neighbour constraints, it cannot skip that Slot - it must place something, and when nothing qualifies the result is a Contradiction. Including a filler Module - one designed with permissive Face Rules that accept a broad range of neighbours - gives the Solver a legal option for those Slots, which prevents the Contradiction at the cost of introducing those filler Slots into the result. See also workflows and FAQs: [2.6 Empty Module](workflows.html#empty-module), [1.1 Top-down: envelope first](workflows.html#strategy-topdown). ### 1.3 What does “Solved assembly in path {0} is contradictory.” mean? The Solver propagated constraints and found a Slot with zero allowed Modules. This usually means your Rules are too restrictive for the given Envelope and Module set. Try: - Adding more Rules to increase flexibility. - Adding an “empty” Module (a Module with no geometry) that can fill leftover spaces. - Increasing **Max Attempts** - the Solver uses random choices, so a different Seed may find a valid solution. - Using the Audit component to check for orphaned Faces or missing Rules. See also workflows and FAQs: [2.21 Audit-driven debugging](workflows.html#audit), [1.5 Debugging strategy](workflows.html#strategy-debugging), [2.1 Bare minimum](workflows.html#bare-minimum). ### 1.4 Why can't the Solver find a solution? The Rule set may be inherently Contradictory for the given Envelope size. Common causes: - A Module that must appear but has no valid neighbour on one Face. - A boundary Module without Rules connecting it to interior Modules. - An Envelope too small for the Module set (some Modules require specific neighbours that don't fit). - Conflicting constraints from weighted Slots forcing incompatible placements. Use the Audit Assembly component to diagnose. Simplify the problem: try with fewer Module types or a smaller Envelope first. See also workflows and FAQs: [2.21 Audit-driven debugging](workflows.html#audit), [1.5 Debugging strategy](workflows.html#strategy-debugging). ### 1.5 What makes a good Module? Module design is the single most important skill in working with Monoceros. A well-designed Module set makes Rules easy, the Solver fast, and results surprising in the right way. A poorly designed set leads to contradictions, visual artifacts, and an unmanageable Rule count. The principles below are listed roughly in order of importance. #### Exciting middle, boring edges This is the most important principle. Design each Module so that the interesting content - walls, surfaces, objects, geometry with visual character - sits in the **center** of the Module, not at its edges. The six Faces (the Faces of the Cell) should show only **cross-sections** of things, never complete surfaces. Think of slicing through a wall with a knife: the cut surface (the “flesh”) appears at the Face, but the wall’s visible surface (the “skin”) stays in the interior of the Module. A Face is a mechanical interface, not a design surface. Why this matters: the Detect Rules from Geometry and Detect Rules from Voxels components work by analyzing what appears at each Face. If cross-sections are predictable and boring, the components produce correct Face tags reliably. The WFC Solver then handles only the mechanical task of continuing interrupted elements across cell boundaries, while you retain full control over what happens semantically inside each Module. #### Don’t touch the walls of the Cell Module geometry should not touch the Faces of the Cell. If geometry touches a boundary, Detect Rules from Geometry and Detect Rules from Voxels will incorrectly infer that adjacent Modules should also have geometry at that Face, producing wrong Face tags. Workaround for cases where geometry must extend into the neighboring cell (e.g. a bolt or bracket that protrudes): use simplified proxy geometry for Face suggestion and Rule creation, then use the detailed geometry only for materialization. Two approaches: 1. Use the transform output of the Materialize Assembly component to place complex geometry at solved positions. 1. Create two sets of Modules with identical names - one simple set for Rule suggestion, one detailed set for Materialize Assembly. #### Scale heuristic: half the smallest semantic object Module size should be approximately **half the smallest semantic object** in the design. For example, if the smallest meaningful element is a 3-meter room, use 1.5-meter Modules. A room corner then falls in the center of one Module (with 0.75 m of wall on each side of the corner), followed by a full 1.5 m straight-wall Module, then the opposite corner Module. Three Modules = one 3 m wall. This “half-size” Rule ensures that semantic boundaries (corners, junctions, transitions) land in the middle of Modules - exactly where the “exciting middle” principle says they should be - rather than at Faces where they would complicate Rule matching. #### Modules contain parts, not wholes Never design a Module that represents an entire semantic object - a whole room, a whole door, a whole furniture piece. Always put a **part** of the object inside the Module, so that parts of neighboring objects are also present. The Solver works by connecting parts across boundaries; if a Module already contains a complete object, the connections become trivial and the result loses combinatorial variety. Exception: objects that are significantly smaller than everything else in the system can be placed as whole Modules. #### Design systems separately, connect through interfaces When the design involves multiple independent spatial systems (e.g. a road network and surrounding landscaping), design each system’s Modules and Rules independently. Then create a small number of **interface Modules** that bridge the two systems. For example: road Modules connect only to other road Modules; greenery Modules connect only to other greenery Modules; a single “road edge” Module connects road sides to greenery boundaries. This keeps complexity manageable - each system can be developed and tested independently. See [1.8 System-based Module design](workflows.html#strategy-systembased) for a full walkthrough. #### Multiple variants of the same purpose The same functional role often needs multiple Module variants with different names and Rules. The most common case: `empty-interior`, `empty-exterior`, and `empty-boundary`. Each participates in different Rules even though the geometry may be identical or absent. Without distinct identities, you cannot control which “kind of empty” appears where. #### Same geometry, different identity Multiple Modules can share identical geometry but carry different names and participate in different Rules. The identity of a Module is in its **name and Rule participation**, not its geometry. This is a feature, not a workaround - use it freely whenever the same shape should behave differently in different contexts. #### Don’t embed extra data in Modules Keep associated data (materials, structural properties, cost, metadata) separate from Modules. Distribute it alongside geometry during the Materialize phase. Trying to pack additional data into Module geometry leads to Face matching problems and unmanageable Rule sets. #### Two workflows for wall/boundary design When Modules must represent boundaries between different zones, there are two valid approaches demonstrated in architectural examples: - **Wall-centered:** The wall sits in the center of the Module (per the “exciting middle” principle). Both sides of the wall carry context from their respective zones. This is self-contained and works well with Detect Rules from Geometry or Detect Rules from Voxels, but requires designing a Module for every combination of zone types on either side of the wall. - **Boundary-split:** The wall is split at the Module boundary - half the wall thickness in one Module, half in the next. Zone identity is controlled through Rules rather than Module design. This requires more Rules but fewer Modules, and works better when there are many zone types. See also workflows and FAQs: [2.1 Bare minimum](workflows.html#bare-minimum), [2.9 Working with multiple Modules](workflows.html#multiple-modules), [2.30 Restricting Modules to specific regions](workflows.html#region-restriction), [1.2 Bottom-up: Modules first](workflows.html#strategy-bottomup), [1.8 System-based Module design](workflows.html#strategy-systembased), [1.45 Can Module geometry protrude outside the Cell?](#faq-44), [1.40 What is the “coast principle” for Module design?](#faq-39). ### 1.6 What makes a good Envelope? - Start with a **Homogeneous Grid** for simplicity. Use Heterogeneous grids only when the design requires variable cell sizes. - Add a **boundary layer** of Slots filled with boundary Modules. The Solver needs to know what happens at the edges. - For complex shapes, use Cells from Geometry to convert Rhino geometry into Cells. - Don't make the Envelope too large initially. Start with 5×5×5 and scale up once the Rules work. See also workflows and FAQs: [2.13 Boundary handling](workflows.html#boundary), [2.7 Rotating the envelope base plane](workflows.html#rotated-envelope), [1.1 Top-down: envelope first](workflows.html#strategy-topdown). ### 1.7 How do I handle boundaries? Monoceros 3 has no implicit boundary layer. You must define boundaries explicitly: 1. Use Add Boundary Layer to generate Cells around your Envelope. 1. Create Slots from those Cells, pre-loaded with your boundary Module(s). 1. Define Rules connecting boundary Module Faces to interior Module Faces. This gives you full control: you can use different boundary Modules on different Faces, multiple boundary layers, or no boundary at all. See also workflows and FAQs: [2.13 Boundary handling](workflows.html#boundary), [4.2.3 Add Boundary Layer](index.html#comp-addbound). ### 1.8 How do I create an “empty” Module? Monoceros 3 has no built-in Empty Module. Simply create a Module with no geometry: 1. Use Construct Module with a name like `empty` and a Cell matching your grid cell size. 1. Do not connect any geometry to the Geometry input. 1. Define Rules connecting the empty Module's Faces to other Modules as needed. You can have multiple empty Modules with different names for different purposes (e.g. `empty-interior`, `empty-boundary`). See also workflows and FAQs: [2.6 Empty Module](workflows.html#empty-module). ### 1.9 Why does my Module show red in the viewport? A red display box and red name label indicate the Module is **invalid**. Common causes: - The Module Name is empty or contains a reserved character (newline, `:`, `->`, `@`, or `#`). - A Cell is degenerate. - The Cells of a multi-cell Module do not form one face-connected block; edge or corner contact is insufficient. Use Deconstruct Module to inspect the Module's properties and identify the issue. Note: a rotated base plane is **not** an error - the Module display box rotates with the plane. See also workflows and FAQs: [2.21 Audit-driven debugging](workflows.html#audit), [1.5 Debugging strategy](workflows.html#strategy-debugging). ### 1.10 How do I control how often a Module appears? Use the **Weights** input on Construct Slot. Higher Weight means higher probability of being selected during Observation. A Module with Weight 5.0 will appear roughly five times as often as one with Weight 1.0 in an unconstrained scenario. Weights are soft preferences, not guarantees - the Solver still respects Rules, so actual counts depend on the constraint structure. See example [2.12 Weighted Module placement](./workflows.html#weighted-slots). ### 1.11 Why does a zero-weight Module still appear in the result? It no longer does. A Weight of 0 or less on Construct Slot removes the Module from that Slot entirely, turning the Weight into a hard constraint: the Solver never considers the Module there, and a Remark reports how many Modules were removed. (In earlier Monoceros versions a zero-Weight Module stayed in the allowed set and Propagation could still force it into the result.) Removing the Module from the Slot's `ModuleNames` list is equivalent - use whichever is more convenient on the canvas. A near-zero *positive* Weight (e.g. `0.001`) keeps the old soft behavior: the Module is almost never chosen during Observation but remains available when Propagation eliminates every other option. See also workflows and FAQs: [2.30 Restricting Modules to specific regions](workflows.html#region-restriction), [FAQ tip 2.17 Exclude a Module from a zone](faq.html#tip-17). ### 1.12 How do I get different results each time? Change the **Random Seed** input on the WFC Solver. Each Seed produces a Deterministic but different result. Use a Number Slider to browse variations quickly. See example [2.20 Solver settings](./workflows.html#solver-settings). ### 1.13 How do I get reproducible results? Keep the Random Seed fixed. The same Seed with the same Modules, Rules, Slots, and Solver settings will always produce the identical result, on any computer, on any operating system. If the result changes unexpectedly, something in the setup has changed - check Module Names, Rule lists, Slot Weights, and data tree structures. See also workflows and FAQs: [2.20 Solver settings](workflows.html#solver-settings). ### 1.14 Why does the same seed give different results after I change something? The Seed controls the pseudo-random sequence, but any change to the input - adding a Module, modifying a Rule, changing a Weight, resizing the Envelope - changes the sequence of decisions the Solver makes. Even a small change can cascade into a completely different result. This is expected behaviour. If you need to preserve a result while modifying the setup, note both the Seed and the exact input configuration. See also workflows and FAQs: [1.3 Iterative refinement](workflows.html#strategy-iterative). ### 1.15 Why do some Modules never appear in the result? A Module may be valid but effectively excluded by the constraint structure. Common causes: - The Module has no Rules on one or more Faces - the Solver cannot place it because neighbouring Slots would have no valid connection. - The Module's Rules are too restrictive - its required neighbours conflict with the rest of the Rule set. - Weights are too low relative to other Modules - it is statistically unlikely to be chosen. - The Module is not listed in the Slot's allowed Module Names - check that all Slots include this Module Name. - Disallowed Rules removed all Rules for the Module (including rotated variants). When Construct Assembly's Disallowed Rules input references a rotatable Module, all rotation-variant combinations are automatically removed. If this leaves authored Faces without Rules or Connectors, Construct Assembly warns that they are uncovered and marks the Assembly invalid. A Slot with no allowed Module is also a build problem; the Solver refuses an invalid Assembly. **Key point about Weights here:** raising a Weight does *not* help if the Module was already removed by Propagation. When a session starts the Solver first runs Propagation (the same step as **Clean up**), which deletes any Module that cannot legally sit in a Slot given its neighbours — and Propagation ignores Weights completely. Weighted Observation only chooses among the survivors. So a heavily-weighted Module that never appears was almost certainly deleted before its Weight could matter. Enable **Clean up** on Construct Assembly and read the Slot's allowed list (Deconstruct Slot / Slot to Module): if the Module is already gone there, the fix is in the Rules or the shape of the allowed region, not the Weight. **Shape matters for path-like Modules.** If a Module family connects through a Face role that only pairs with itself (a "through" Connector - see [1.33 clustering](#faq-32)), each such Module can only sit next to another of its kind or run off the grid edge. In a thin or ragged region of allowing Slots there may be no legal arrangement for it anywhere except at the boundary, so Propagation empties every interior Slot of it - even at high Weight. Widening the region (so each cell has more matching neighbours, and it reaches the grid edge or can close a loop) is what lets the family survive. A classic symptom is a heavily-weighted "lane" Module that only ever shows up touching the envelope edge, or not at all. Use `Audit` to identify orphaned Faces, and **Occurrence Count** to verify the Module appears at all. See also workflows and FAQs: [2.21 Audit-driven debugging](workflows.html#audit), [2.31 Occurrence Count analysis](workflows.html#occurrence-count). ### 1.16 Can Monoceros work in 2D? Yes. Set the grid count to 1 in the unused dimension (e.g. Z count = 1 for a floor plan in XY). Design Modules as flat tiles and define Rules for the four in-plane Directions. The two out-of-plane Faces need no Rules, Connectors, or Terminators: a flat Axis is the front and back of a 2D tiling, not an envelope edge that needs closing. Require Terminators caps only the in-plane rim and skips the flat Direction entirely, so no Terminator capability is required for the out-of-plane top and bottom Faces. See [2.26 2D grids](./workflows.html#2d-grids). ### 1.17 What geometry types are supported? Modules accept the following Rhino geometry types: - Point - Curve (Line, Arc, Circle, Polyline, NurbsCurve, etc.) - Surface (including Sphere, Cylinder, etc.) - Brep / Polysurface (including Box, Extrusion, etc.) - Mesh **Not supported:** Block instances, groups, text dots, hatches, annotations. If you need block instances, use the Materialize transforms to create them after solving (see example [2.19 Materializing results](./workflows.html#materializing)). See also workflows and FAQs: [2.14 Constructing Slots from geometry](workflows.html#constructing-slots). ### 1.18 Can I use curved geometry as Modules? Yes. Module geometry can be any shape - it is purely decorative and does not affect the solving process. The Solver only considers the Module's Cell and Face Rules. Curved geometry can extend beyond the Cell, be smaller than the Cell, or sit anywhere within it. The only requirement is that Faces on matching Faces produce compatible geometric results when placed side by side. See also workflows and FAQs: [2.14 Constructing Slots from geometry](workflows.html#constructing-slots). ### 1.19 How do I create L-shaped or T-shaped Modules? A Module occupies one grid cell per Cell it is given. For a shape spanning several cells, give Construct Module a list of adjacent Cells: the result is one multi-cell Module that the Solver places as a single rigid body, with the cells held together by lock rules the engine synthesizes during the assembly build. It supports auto-rotation via a Rotational Freedom value (0 None, 1 X, 2 Y, 3 Z, 4 Full), and the whole body rotates together. See example [2.18 Multi-cell Modules](./workflows.html#megamodules). Alternatively, design your Module geometry to visually suggest an L or T shape within a single cell (e.g. a partition wall that runs along two Faces of the cell). ### 1.20 What is the "interior geometry" design principle? Design Module geometry so that all content stays inside the Cell boundary. When two Modules sit side by side, their shared Face must match geometrically - edges meeting at the Face should align. The Solver has no geometry awareness: it cannot detect visual overlaps or gaps between adjacent Modules. Geometry that protrudes beyond the Cell will visually intersect neighbouring Modules without triggering any Solver error. Geometry that stops short of the Face will leave visible gaps. For transitions (e.g. a wall ending at a Face), design the geometry to stop exactly at the Cell Face, and ensure the neighbouring Module starts at the same Face with a matching profile. See also workflows and FAQs: [2.1 Bare minimum](workflows.html#bare-minimum). See also workflows and FAQs: [FAQ 1.45 Can Module geometry protrude outside the Cell?](faq.html#faq-44). ### 1.21 Are block instances supported? Materialize Assembly outputs geometry and transforms. Baking it creates a block definition for each unique placed Module and a block instance for each placement. You can also use the transforms for custom block placement in Rhino. See example [2.19 Materializing results](./workflows.html#materializing). ### 1.22 Can I use Monoceros with other plug-ins? Yes. Monoceros is a standard Grasshopper plug-in and its inputs and outputs are standard Grasshopper data types (Points, Breps, Meshes, numbers, strings). You can combine Monoceros with any other plug-in: - **Kangaroo** - Apply physics simulation to the materialized result (e.g. relaxation, collision detection). - **Human UI** - Build custom control panels to expose WFC parameters (Seed, Weights, Module toggles) as interactive sliders and buttons. - **Karamba3D** - Structural analysis of WFC assemblies. - **Pufferfish** - Advanced mesh and surface operations on materialized geometry. - **Lunchbox** - Additional paneling and pattern tools. - **Elefront** - Bake with attributes, layers, and user data. See also workflows and FAQs: [2.32 Exporting results for fabrication](workflows.html#export-fabrication), [1.6 Fabrication-oriented workflow](workflows.html#strategy-fabrication), [How Monoceros compares to Galapagos, Anemone and Kangaroo](/guides/grasshopper-plugins-generative-design.html). ### 1.23 How do I ensure watertight results? WFC does not guarantee geometric continuity - it guarantees Rule satisfaction. To get watertight results: - Design Module geometry so that Face boundaries are geometrically continuous when placed side by side. Edges at Faces should align perfectly. - Use the [proto-results workflow](./workflows.html#proto-results) to join geometry after solving. - Use Detect Rules from Geometry to ensure only Modules with matching edge geometry are paired. ### 1.24 What is the free-tier run limit? Monoceros 3 Free includes full functionality on Envelopes of any size. The **WFC Solver** and **Growth Solver** components share a **limited number of runs per fixed time window** (windows are aligned to UTC clock boundaries); additional runs require a paid license (Annual, Edu, or Lifetime). A run uses the budget only when **Run** is `true`, the Assembly is valid, and the engine setup succeeds. It counts once regardless of **Max Attempts** on the WFC Solver or **Steps** on the Growth Solver. Both Solver components have a boolean **Run** input (default `false`). Each Solver only executes when Run is explicitly set to True, which prevents upstream slider changes from accidentally using the run budget. Connect a Boolean Toggle or a Button component. The component footer on each Solver shows how many runs remain and when the window resets. After a completed run, each Solver reports how many runs remain in a Remark. When the cap is hit, the component message says `Limit reached`, and the runtime Error reports the reset time. Separately, the Solver has a hard limit of **16,370 solver Modules** on every tier. Each rotation variant and each Cell of a multi-cell Module counts toward it. Full Rotational Freedom generates up to 24 orientations per Module, allowing roughly 682 single-cell designs at that maximum; X only, Y only, or Z only generates four, allowing roughly 4,092. Construct Assembly reports a build problem and marks the Assembly invalid if the limit is exceeded. See also workflows and FAQs: [1.4 Multi-scale approach](workflows.html#strategy-multiscale), [FAQ tip 2.1 Start small, scale up](faq.html#tip-1). ### 1.25 How do I count the Slots in my Envelope? Connect the Slot list to a Grasshopper **List Length** component. The output is the total Slot count, including any boundary Slots you have added. There is no free-tier cap on Envelope size - only the Solver run rate is capped (see [1.24 What is the free-tier run limit?](#faq-23)). See also workflows and FAQs: [2.31 Occurrence Count analysis](workflows.html#occurrence-count). ### 1.26 Why does the solver fail only with larger Envelopes? A small Envelope is more forgiving because there are fewer Slots to fill and fewer constraint Propagation paths. As the Envelope grows: - Constraint chains become longer and more likely to create contradictions. - Random choices early in solving have a larger cascading effect. - Rule sets that are “almost Contradictory” on a small Envelope become fully Contradictory on a larger one. Solutions: add more flexibility (more Rules, empty Modules, more Module types), reduce constraint density, increase Max Attempts, or consider a [multi-scale approach](./workflows.html#strategy-multiscale). ### 1.27 Why does the solver fail with small Envelopes? Small Envelopes can fail for different reasons than large ones. The most common: the Module set or boundary conditions force a pattern that requires more space than available. A Module that needs a specific sequence of neighbours may need five or more cells in a row; a 3×3 Envelope cannot accommodate it. Boundary Modules also create additional constraints on every edge Slot, which consumes a disproportionate fraction of a small Envelope. Solutions: verify that every Module can sit directly adjacent to your boundary Module; check that no Module type implicitly requires a run longer than the shortest dimension of your Envelope; temporarily disable Modules one at a time to isolate which one causes the failure. See also workflows and FAQs: [1.2 Bottom-up: Modules first](workflows.html#strategy-bottomup), [1.5 Debugging strategy](workflows.html#strategy-debugging), [2.6 Empty Module](workflows.html#empty-module). ### 1.28 How do I guarantee exactly one of a specific Module in the result? WFC cannot enforce global uniqueness through Rules or Weights alone. Three approaches are available: 1. **Pre-fixing** - Select the specific Cell where the unique element (e.g. `control_station`) should appear. Use Construct Slot with `ModuleNames = [only that module]`. Exclude the Module from all other Slots. Deterministic, but the location must be known upfront. 1. **Anemone loop** - Solve freely, count occurrences with **Occurrence Count**. If count > 1, remove the Module from all non-preferred Slots and re-solve. Loop via the Anemone GH plugin until count = 1. 1. **Two-pass carving** - Run the WFC Solver (pass 1) with the Module allowed everywhere. Identify redundant placements. Reset those Slots and several surrounding layers to the full possibility set minus that Module, keeping the rest of the pass 1 result as Deterministic boundary conditions. Run a second Solver on just the carved region. No extra plugins required. See also workflows and FAQs: [2.15 Fixing Modules in the Envelope](workflows.html#fixing-modules), [2.31 Occurrence Count analysis](workflows.html#occurrence-count). ### 1.29 What is All Modules Count and why does it matter? `AllModulesCount` is a property of every Slot that records the number of distinct Module Names referenced by any Slot in the Assembly. It controls how Entropy is displayed as a normalised percentage in the Grasshopper parameter preview — it does not affect solving. You never set it by hand. Every Slot leaves **Construct Slot** with `AllModulesCount = 0` — the total is not yet known, so the Slot preview shows only the allowed-Module count (one number in the corner, no `/ total`). **Construct Assembly** then stamps every Slot it processes with the number of distinct Module Names referenced by any Slot, and the preview switches to the familiar `allowed / total` form. Every Slot in the Assembly agrees on the same denominator automatically. **Allow-all Slots.** If you leave the Allowed Module Names input unconnected, the Slot becomes an allow-all Slot. Construct Assembly fills its Module list with authored Module Names whose Cells fit its Cell size, then stamps the count with the number of distinct Module Names referenced by any Slot. See also workflows and FAQs: [2.1 Bare minimum](workflows.html#bare-minimum). ### 1.30 Can I use Module Rotations instead of defining every directional variant? Yes - and this is the recommended approach. Define one base Module (e.g. `path-straight` with a path on ±X and void on ±Y) and apply Module Rotations with Rotational Freedom = 3 (Z only) and Cull Duplicates = true. The component generates all valid orientations. All variants share the same Module Name, and Construct Assembly merges them into one Module whose allowed orientations are the variants that reached it, so Rules defined for the base Module apply automatically to all rotations. Filter the output to restrict the orientations, but keep the unrotated variant (the first one). Alternatively, set Rotational Freedom on Construct Module to let Construct Assembly generate the rotation variants without a separate Module Rotations component. This avoids manually defining `path-straight-x` and `path-straight-y` as separate Modules, and similarly for corners (1 base → 4 rotated corners), T-junctions (1 base → 4 variants), and directional entry Modules (1 base → 4 entry-facing variants). Note: Module Rotations generates only rotational variants. If you need a mirrored Module, model it separately from scratch. See [FAQ 1.52](faq.html#faq-mirror) for why. See also workflows and FAQs: [2.10 Automatic Module rotations](workflows.html#module-rotations), [4.5.4 Module Rotations](index.html#comp-modulerot), [FAQ tip 2.16 Use Module Rotations for isotropic geometry](faq.html#tip-16). ### 1.31 How do I make a Module type go mostly straight instead of winding? Use a combination of Weight biasing and Rule filtering: - Give straight Modules a high Weight (e.g. 4.0–5.0) and corner / junction Modules a low Weight (e.g. 0.3–0.5). - Optionally connect Rules to the **Disallowed Rules** input of Construct Assembly to remove corner-to-corner adjacency, preventing back-to-back turns. - Enforce a transition zone: remove any direct straight-Module ↔ void or straight-Module ↔ other-type Rules so a transition Module is mandatory between them. This creates structured, mostly straight runs rather than winding paths. See also workflows and FAQs: [2.16 Disallowing Rules](workflows.html#disallowing), [2.17 Rule Exclusivity](workflows.html#rule-exclusivity), [1.6 Fabrication-oriented workflow](workflows.html#strategy-fabrication). ### 1.32 How do I design tile Modules for a 2D layout? Set Z Count = 1. Design one base Module per tile role. Apply Module Rotations with Rotational Freedom = 3 (Z only) and Cull Duplicates = true to derive all directional variants - every variant keeps the tile's name and Construct Assembly merges them into one Module with those orientations, so Rules are defined once against the unrotated tile using the methods in section 1.5. Assign Face roles to each Face and set all ±Z Faces to the boundary role. Use Face grouping ( Construct Rules From Faces with same-role lists as both source and target) to create all valid pairings efficiently. Add a boundary layer and pre-fix any unique elements. See also workflows and FAQs: [2.26 2D grids (floor layouts and panel systems)](workflows.html#2d-grids), [2.10 Automatic Module rotations](workflows.html#module-rotations). ### 1.33 How do I make tiles of the same type cluster together? Use two mechanisms together: - **Topological isolation:** Design cluster Modules so their horizontal Faces use a dedicated role (e.g. `fluid`) that connects only to other cluster tiles or edge / transition tiles. This means cluster tiles cannot be directly adjacent to non-cluster tiles. - **Attractor-based Weight gradient:** Compute each Slot's distance to a target location, remap to Weights (high near attractor, exactly 0.0 far away), and assign these per-Slot Weights for the cluster Module. Setting Weight to **0.0** or less removes the Module from those Slots entirely; a near-zero positive Weight remains a soft preference. See also workflows and FAQs: [2.12 Weighted Module placement](workflows.html#weighted-slots), [2.24 Rules from Face Groups](workflows.html#shared-face-groups), [2.30 Restricting Modules to specific regions](workflows.html#region-restriction). ### 1.34 How do I create a mandatory buffer zone between two Module types? Do not create Rules between type-A Faces and type-B Faces directly. Only create: type-A ↔ type-A, type-A ↔ buffer, and buffer ↔ type-B. This makes the buffer zone the mandatory intermediate layer: type-A → buffer → type-B. The WFC Solver cannot place a type-A Module directly next to a type-B Module because no Rule exists for that adjacency. Every type-A tile will always be separated from type-B tiles by at least one buffer tile. See also workflows and FAQs: [2.17 Rule Exclusivity](workflows.html#rule-exclusivity), [2.16 Disallowing Rules](workflows.html#disallowing). ### 1.35 Can I use a hexagonal grid? No. Monoceros only supports rectilinear (Axis-aligned rectangular box) grids. Hexagonal, triangular, diagonal, and polar grids are not supported. Adjacency is always Face-to-Face between rectangular cells - 6 neighbours only. **There is no workable hex workaround for layouts that require Face-to-Face geometric continuity between tiles.** The common suggestion - “stagger every other row after baking” - fails for two reasons: 1. **Rectangular tiles produce a brick-wall bond, not hexagonal geometry.** Shifting rows moves geometry but the tile shapes remain rectangular; seams are still straight horizontal and vertical lines. 1. **Staggering breaks physical connections.** Modules are designed to connect Face-to-Face at rectilinear grid positions. Shifting a row moves the geometry away from those positions. Any Module set requiring Face-to-Face continuity becomes non-functional after staggering. **Recommendation: abandon the hex grid idea.** Design directly for the rectilinear grid. Results can read as organic, directional, and non-uniform through Weight gradients, attractor clustering, and varied Module geometry - without any hexagonal topology. See also workflows and FAQs: [2.26 2D grids (floor layouts and panel systems)](workflows.html#2d-grids). ### 1.36 What is entropy and how does it affect solving? The Solver uses the count of allowed Modules as Entropy when all Weights are 1.0, or weighted Shannon entropy when any Weight differs from 1.0. It observes a Slot in the highest Slot Priority band, then chooses the lowest Entropy within that band. Deconstruct Slot and the Slot preview instead display the allowed-to-total Module count as a ratio from 0.0 to 1.0. This heuristic reduces the chance of Contradiction by making the most constrained decisions first. In practice, Entropy affects the visual character of results: Slots near already- solved regions tend to have lower Entropy and get solved next, producing a “growing” pattern outward from the first Observation. See also workflows and FAQs: [2.20 Solver settings](workflows.html#solver-settings). ### 1.37 How does randomness affect solving? Is the result entirely random? Monoceros is Deterministic for a given Seed and input configuration. During Observation, the Seed breaks ties between equally ranked Slots and chooses a Module for the selected Slot. Module selection uses the Slot Weights when authored, or a uniform choice otherwise. Constraint Propagation after those choices is Deterministic. The Solver uses Slot Priority and Entropy to choose a Slot before randomly choosing its Module. Changing any input (Module list, Rule set, Slot Weights, Envelope size) changes the tie-breaking sequence and can produce a completely different result even with the same Seed. See also workflows and FAQs: [2.20 Solver settings](workflows.html#solver-settings). ### 1.38 Why does a contradiction appear in a Slot far from where I made changes? WFC uses constraint Propagation: each time a Module is placed in a Slot, the Solver removes incompatible Modules from all neighbouring Slots, then propagates outward through the Rule graph. A change near Slot A can cascade through a chain of Propagation steps and create a Contradiction in a distant Slot Z. This is expected WFC behaviour. To debug: use the Audit Assembly component to identify which Slot contradicts, then trace backward - examine which Rule connections reach that Slot and which recent placement could have triggered the chain. Adding a void Module with general Rules usually breaks the Propagation chain. See also workflows and FAQs: [2.21 Audit-driven debugging](workflows.html#audit), [1.5 Debugging strategy](workflows.html#strategy-debugging). ### 1.39 Are weights a property of a Module or of a Slot? Weights are a property of a **Slot**, not of a Module. The same Module can have Weight 5.0 in one Slot and Weight 0.0 in another. A Module has no Weight property of its own - the Weight is always specified on the Construct Slot component, as a value paired with the Module Name for that specific Slot. This is what makes attractor-based gradients work: you compute a spatial value (distance to a point, Z height, noise) for each Slot's centre, remap it to a Weight range, and supply those per-Slot values as the Weights input. The result is a Module that appears frequently near the attractor and rarely or never away from it - all from a single Module definition. See also workflows and FAQs: [2.12 Weighted Module placement](workflows.html#weighted-slots), [2.30 Restricting Modules to specific regions](workflows.html#region-restriction), [FAQ tip 2.5 Attractor-based weight gradients](faq.html#tip-5). ### 1.40 What is the “coast principle” for Module design? The most generative Modules are those that contain **transitions** rather than uniform content. The analogy: a coastline tile containing part land and part sea generates richer adjacency possibilities than a purely-land or purely-sea tile. Applied to design: a Module that is half one content type and half another generates richer adjacency possibilities than a Module that is entirely one type. Design your Modules to live at the boundary between adjacent content types rather than fully representing only one. See also workflows and FAQs: [1.2 Bottom-up: Modules first](workflows.html#strategy-bottomup), [2.8 Defining Rules from Faces](workflows.html#defining-rules). ### 1.41 Why do Slot Cells in the viewport look smaller than the actual cell size? This is intentional. Adjacent Slots share Faces: if every Slot Cell were drawn at full cell size, the shared edges of neighbouring Slots would overlap exactly in the viewport, making it impossible to visually distinguish individual cells. Monoceros shrinks each display box inward by a small fraction so there is always a visible gap between neighbours, regardless of how large or densely packed the Envelope is. The display box is a **display-only** artifact. The solving geometry, Rule matching, and Face placement all use the true, full-size cell box. Baked geometry fills the full box. See also workflows and FAQs: [2.1 Bare minimum](workflows.html#bare-minimum). ### 1.42 Why does the result always look the same regardless of the seed? Changing the Seed changes which Slot the Solver observes first and how it breaks ties, but it cannot create variety that the Rule set does not allow. If every Face that is covered by explicit Rules appears in exactly one Rule, then each Face of each Module has only one valid neighbour. Once the first Module is placed, the entire grid is forced to follow a single valid chain - the Solver has no choices to make, and the result is structurally identical every time. Changing the Seed shifts the starting point but the same pattern tiles out in all Directions. For variety to occur, at least some Faces must participate in more than one Rule. When a Face has two or more valid neighbours, the Solver has a genuine choice at that Face; different Seeds resolve those choices differently and produce different results. The more Faces have multiple valid partners, the richer and more varied the output space. The Audit Assembly component reports which Faces are referenced in only a single Rule (the *Singly Constrained Faces* output) and which are effectively Indifferent even though explicit Rules exist for them. If all Rule-covered Faces are singly constrained, Seed variation will produce no structural variety. See also workflows and FAQs: [1.5 Debugging strategy](workflows.html#strategy-debugging), [2.20 Solver settings](workflows.html#solver-settings), [2.17 Rule Exclusivity](workflows.html#rule-exclusivity), [FAQ tip 2.15 Compare results with Changed Slots](faq.html#tip-15). ### 1.43 Why do Construct Assembly and Materialize Assembly report Module dimension mismatches? The WFC Solver assigns Modules to Slots by name only - it has no knowledge of box dimensions. A Module is removed from a Slot's candidate list by constraint Propagation (Rules), not by geometry checks. This means the Solver can successfully complete a solve where a Slot is assigned a Module whose box dimensions do not match the Slot's dimensions. When Materialize Assembly later tries to place the geometry, it compares Module and Slot dimensions. If no variant fits, placement is skipped and Materialize Assembly emits an Error listing the affected Slots. Construct Assembly warns about manually provided Modules whose dimensions match no Slot where they are allowed. Use Audit Assembly's **Slots Without Fitting Modules** output for a per-Slot check before solving. See also workflows and FAQs: [2.11 Heterogeneous grid](workflows.html#heterogeneous), [2.28 Rectangular (non-square) Modules](workflows.html#rectangular-modules). ### 1.44 Is there a good or bad random seed? No. All Seeds are equivalent from the Solver's perspective. Whether a given Seed finds a solution depends entirely on the Rule set, Envelope, and Weights - not on the Seed value itself. A Seed that fails with one setup may solve with another; a Seed that solves is not lucky, it simply encountered constraints in an order that avoided a dead end. Changing the Seed is just another way of exploring the same solution space from a different starting point. The only thing that makes a Seed “special” is that once a successful Seed is known, pinning it to the input guarantees the same result every time - because the Solver is fully Deterministic for a given Seed and setup. See also [1.14 Why does the same seed give different results after I change something?](#faq-13) ### 1.45 Can Module geometry protrude outside the Cell? Yes, and Monoceros fully supports this. Module geometry is never clipped or constrained to the Cell boundary - it can extend freely in any Direction. The Solver only uses the Cell and Faces to determine adjacency; the geometry itself plays no role in solving. Two intentional uses of protruding geometry: - **Physical Faces** - bolts, screws, tabs, or latching hooks that physically join adjacent Modules can be modelled as geometry protruding into the neighbouring cell. Design the matching receiving feature (hole, socket, recess) into the neighbouring Module’s geometry. The Solver will not detect or flag the protrusion - only the Rules determine what may be placed next to what. - **Artistic overreach** - fins, overhangs, tendrils, or any decorative element that intentionally crosses a cell boundary. Results can appear seamlessly continuous across multiple cells even though each Module is geometrically independent. Monoceros has no geometry awareness: it cannot detect or warn about clashes between protruding geometry and the content of neighbouring Modules. Verifying that protrusions fit correctly together is a design responsibility. See also workflows and FAQs: [FAQ 1.20 What is the “interior geometry” design principle?](faq.html#faq-19), [1.6 Fabrication-oriented workflow](workflows.html#strategy-fabrication). ### 1.46 Can I use an AI assistant (ChatGPT, Claude, etc.) to help me with Monoceros? Yes. Monoceros provides an official machine-readable documentation file specifically designed for AI assistants. You can feed it to any LLM - ChatGPT, Claude, Gemini, Copilot, or any other - to get accurate, context-aware help with your Monoceros projects. - **[llms.txt](../llms.txt)** - the complete documentation in a single text file: all data types, every component with inputs and outputs, the workflow strategies, every example walkthrough, all FAQ answers, and the tips. It is generated from this manual, so it always matches the current release. To use it: paste the contents into your AI chat, upload the file as an attachment, or give the AI the URL directly. Then ask your question as you normally would. For detailed step-by-step instructions, see the [AI-assisted help](../ai-help/) page. ### 1.47 How many Modules can the solver handle? The Solver supports at most **16,370 solver Modules**. Each rotation variant and each Cell of a multi-cell Module counts. Full Rotational Freedom can produce **24 orientations** per single-cell Module, allowing roughly **682 unique designs** at that maximum. X only, Y only, or Z only produces four variants, allowing roughly **4,092 designs**. Construct Assembly reports `too many distinct modules` as a build problem when the limit is exceeded. The Solver automatically selects a compact internal representation based on the actual Module count. Problems with fewer Modules use less memory and run faster - there is no penalty for the higher limit. ### 1.48 Common Module design mistakes These are the mistakes that come up most often when people start working with Monoceros. Each one stems from a reasonable intuition. WFC and Monoceros, however, often happen to be counterintuitive. #### Making one room = one Module Architects and designers think in terms of assembling whole semantic objects: one Module for a bedroom, one for a bathroom, one for a corridor. This is the most counterintuitive aspect of working with Monoceros. A room is not a Module - a Module contains a **part** of a room, together with parts of whatever is next to it. A 3-meter room built from 1.5-meter Modules consists of a corner Module, a straight-wall Module, and another corner Module. The room only exists as an emergent result of the Solver connecting these parts. If you make an entire room a single Module, the Solver has nothing to combine - it just places pre-made rooms side by side, losing the combinatorial variety that WFC is designed to produce. #### Expecting empty space to appear automatically The WFC Solver fills **every Slot** in the Envelope. It does not leave cells empty or skip areas where no Module fits well. If you want empty space in the result, you must include an explicit empty Module - a Module with no geometry, just a name and a Cell - and define Rules for it. Without an empty Module, the Solver is forced to place visible geometry everywhere, which usually leads to either contradictions or visually cluttered results. The Growth Solver can leave Slots empty. #### Confusing Monoceros with growth algorithms The WFC Solver fills a predefined Envelope by assigning Modules to Slots. The Growth Solver places Modules one Slot at a time. Both operate within a fixed Envelope. The Growth Solver starts from a randomly chosen eligible Slot and extends along the frontier of placed Modules, with constraint Propagation after every placement. See also workflows and FAQs: [1.5 What makes a good Module?](#faq-4), [1.8 How do I create an "empty" Module?](#faq-7), [1.2 Does the solver always fill every cell in the Envelope?](#faq-fills-grid). ### 1.49 What are the Monoceros license tiers? Monoceros 3 offers Free, Annual, Edu, Lifetime, and Enterprise licenses. **Free** includes full functionality on Envelopes of any size, with the WFC Solver and Growth Solver sharing a run limit per fixed time window and no purchase required. **Annual** is a yearly subscription that unlocks unlimited Solver runs. **Edu** is a discounted annual license for students and educators. **Lifetime** is a one-time purchase that unlocks unlimited Solver runs permanently. **Enterprise** is a separate edition for volume licensing with unlimited Solver runs. Free, Annual, Edu, and Lifetime use the retail plugin; Enterprise uses a separate edition build. Right-click any Monoceros component and select **Log in to Monoceros…** to open **Monoceros 3 — Registration**. Claim Monoceros 3 Free or buy a license on Gumroad, enter your Gumroad email, and click Verify. Annual and Edu licenses are cached for 7 days - a period during which it works also offline. Lifetime licenses are cached for 1 year. After that they need to go online to renew automatically. The server can require registration before any component runs. Accept the License Agreement before using the components. See also workflows and FAQs: [1.24 What is the free-tier run limit?](#faq-23). ### 1.50 What happens when my subscription ends? If your Annual or Edu subscription ends (cancelled, expired, or not renewed), the plugin automatically falls back to **Free** mode (WFC Solver and Growth Solver runs share a rate limit per fixed time window). Your definitions and settings are preserved - only the run-rate cap is re-applied. Lifetime licenses never expire. They are a one-time purchase that unlocks unlimited Solver runs permanently. See also workflows and FAQs: [1.49 What are the Monoceros license tiers?](#faq-license-tiers). ### 1.51 Why does Monoceros ask me to register? The server can require registration before any Monoceros component runs. When required, unregistered components report `REGISTRATION required. Right-click → Log in to Monoceros.` Right-click a Monoceros component, select **Log in to Monoceros…**, then claim Monoceros 3 Free or buy a license on Gumroad and verify your Gumroad email in the **Monoceros 3 — Registration** dialog. You must also accept the License Agreement. See also workflows and FAQs: [1.49 What are the Monoceros license tiers?](#faq-license-tiers), [1.24 What is the free-tier run limit?](#faq-23). ### 1.52 Why is there no automatic mirror option on the Module? Rotations and mirrors are fundamentally different. If a Module represents a real-world object, every rotation of that object is still the same object - a chair rotated 90° is still the same chair. All 24 rotational variants are guaranteed to be valid representations of the original design. Mirroring does not have this property. A mirrored version of an object is not always the same object. A left shoe is not a right shoe. A staircase that spirals clockwise becomes one that spirals counter-clockwise. Text becomes unreadable. A door with hinges on the left becomes a door with hinges on the right. Automatically generating mirrored variants would silently introduce Modules that may not represent valid real-world objects, leading to results that look plausible at first glance but are physically wrong. For this reason, Monoceros does not offer automatic mirroring. If your design genuinely needs a mirrored variant (e.g. a left-hand and right-hand corner), model it separately from scratch as its own Module and define its Rules independently. This forces an explicit design decision for each mirrored variant, ensuring every Module in the system is intentional. See also workflows and FAQs: [1.30 Can I use Module Rotations instead of defining every directional variant?](#faq-29), [2.16 Use Module Rotations for isotropic geometry](#tip-16), [4.5.4 Module Rotations](index.html#comp-modulerot). ### 1.53 What are Connectors and how do they differ from Rules? A **Rule** is an explicit permission for two specific Module Faces to be adjacent. A **Connector** is a higher-level abstraction that combines interface identity (name + symmetry) with placement (Module + Face + rotation) in a single object. Think of a Connector like a USB socket on a device - it has a specific type and Orientation, and only mates with a compatible counterpart. All Connectors sharing the same name are the same type. Each Connector carries four symmetry flags (0°, 90°, 180°, 270°) that declare which rotations are considered identical. You declare which Connector names can connect via **Connector Pairs**. Monoceros then generates the corresponding Rules automatically. This is especially powerful when Modules have many rotation variants - the Connector system handles all rotational bookkeeping for you. There is no separate type-definition step: every Connector already carries its type identity and its placement. See also workflows and FAQs: [2.4 Using Connectors for rotation-aware rule generation](workflows.html#connectors-workflow), [1.55 How does Connector rotation compatibility work?](#faq-connector-rotation). ### 1.54 How does the Assembly workflow work? **Construct Assembly** takes Modules, Slots, explicit Rules, Connectors, and Connector Pairs. It expands rotation variants, generates Connector-based Rules, applies indifference, merges all Rule sources, deduplicates, audits, and packages everything into a single **Discrete Assembly**. The **WFC Solver** takes this Assembly as its sole data input. After solving, use **Materialize Assembly** to extract placed geometry, or **Deconstruct Assembly** to access the Modules, Slots, and Rules. Use **Audit Assembly** on the input Assembly to diagnose setup errors before solving, and on the output Assembly to inspect the solution. See also workflows and FAQs: [The Assembly workflow](workflows.html#assembly-workflow), [1.53 What are Connectors and how do they differ from Rules?](#faq-connectors). ### 1.55 How does Connector rotation compatibility work? Each Connector has four symmetry flags corresponding to 0°, 90°, 180°, and 270° rotations around the Face normal. When a flag is `true`, the Connector at that rotation is considered identical to the Connector at 0°. A round pipe end that looks the same from every angle has all four flags set to `true`. A door frame that can be placed upright or upside-down but not sideways has only the 0° and 180° flags set. An interface with no rotational symmetry at all has only the 0° flag set. When two adjacent Module Faces carry compatible Connector names, the Solver checks whether the relative rotation between them is covered by the symmetry flags. If the rotation is not marked as symmetric, the pairing is not allowed. See also workflows and FAQs: [2.4 Using Connectors for rotation-aware rule generation](workflows.html#connectors-workflow), [1.53 What are Connectors and how do they differ from Rules?](#faq-connectors). ### 1.56 Can I mix Connectors with explicit Rules? Yes. **Construct Assembly** accepts both explicit Rules and Connector-based inputs (Connectors + Connector Pairs). It merges Connector-generated Rules with explicit Rules into a single set. Duplicates are harmless - the Solver ignores them. Use Connectors for the bulk of adjacency logic and add explicit Rules for special cases that Connectors cannot express - for example, a one-off connection between two specific Module Faces that does not fit any Connector name. Connect any unwanted Rules to the **Disallowed Rules** input of Construct Assembly, or connect unwanted Connector Pairs to the **Disallowed Connector Pairs** input. See also workflows and FAQs: [2.5 Combining Connectors with explicit Rules](workflows.html#connectors-manual-rules), [1.53 What are Connectors and how do they differ from Rules?](#faq-connectors). ### 1.57 Why does Construct Assembly generate so many indifference rules? Indifference Rule count grows **quadratically** with the number of Modules that have Unused Faces. For *M* Modules with Unused Faces, indifference generates up to **3 × M²** Rules - *M* positive × *M* negative Faces on each of 3 Axes. With rotation variants enabled, *M* can be up to 24× the number of original Modules (one variant per cube rotation). The growth is dramatic: | Original Modules | Variants (all rotations) | Max indifference Rules | | --- | --- | --- | | 2 | 48 | 6,912 | | 5 | 120 | 43,200 | | 10 | 240 | 172,800 | | 20 | 480 | 691,200 | | 42 | 1,008 | ≈ 3,000,000 (Solver ceiling) | These numbers assume fully asymmetric cubic Modules where all 24 rotations produce geometrically distinct variants and every Face is unused (no explicit Rules or Connectors). In practice the count is often lower because: - Non-cubic Modules produce fewer fitting variants (some rotations change dimensions and are filtered out by Slot size). - Every Face covered by an explicit Rule or Connector is excluded from the Indifferent set, which reduces *M* in the formula. - Require Terminators restricts the allowed Modules on exposed Slot Faces without generating Rules. A Face with only Terminator coverage still receives indifference Rules and is included in *M*. **How to reduce Rule explosion:** add explicit Rules or Connectors to cover more Faces. Even one Connector per Module Face can dramatically reduce the Indifferent set. Construct Assembly warns when there are no explicit Rules or Connectors and the result is fully randomized. See also workflows and FAQs: [1.47 How many Modules can the solver handle?](#faq-46), [2.8 Defining Rules from Faces](workflows.html#defining-rules). ### 1.58 Can I create a Slot that allows all Modules? Yes. Leave the **Allowed Module Names** input on **Construct Slot** unconnected. The Slot is then created in an “allow all” state with an empty Module list and a Total Modules Count of zero, and the component emits a Remark confirming it. When Construct Assembly runs, it fills the Slot with names from authored Modules whose Cells match its dimensions, assigns Weight `1.0` to each, and sets the Total Modules Count accordingly. Rotation variants are orientations of those Modules, not separate names. If none fits, Construct Assembly emits an Error. This is the shortest path for the common case where every Envelope cell accepts every Module - no List Length, no wiring between Module Names and Slots. Allow-all Slots preview as white Cells with the label `All`, and Deconstruct Slot reports Entropy of `1.0` for them. Weights connected alongside an empty Allowed Module Names input are ignored - the Assembly decides the final values. ### 1.59 How do I create a Connector by clicking on a Module face? Use the **Connector from Point** component. Wire Modules and a point on a Module Face. Set Connector Name, Point Tag, Face Rotation, and the Symmetry 0°, 90°, 180°, and 270° inputs. The component returns one Connector per matching Face. It combines Faces from Point with Construct Connector; an empty Connector Name is generated automatically. If the Point matches no Face, Connector from Point emits a Warning and produces no Connectors. If the Point matches several Faces (for example, where Modules overlap), a Remark reports the count and one Connector per match is produced. ### 1.60 What is a Terminator and when should I use it? A **Terminator** is a boundary-facing marker placed on a specific Module Face. It declares that the marked Face is allowed to sit against the outer boundary of the Envelope when **Require Terminators** is enabled on Construct Assembly. Use Terminators when you want explicit, per-Face control over which Module Faces may appear at the Envelope edge — rather than letting any Face land there. When Require Terminators is on, Construct Assembly caps the occupied Envelope frontier directly: every exposed Slot Face must be filled by a Module carrying a Terminator on the matching Face, and any Module Face without a Terminator is forbidden from the outer edge. If no allowed Module can cap an exposed Face on a non-flat axis, Construct Assembly reports a build problem and emits an invalid Assembly, which the WFC Solver refuses. No boundary cells are added — the grid stays exactly the cells you authored. Terminators carry no name, rotation, or symmetry — they declare only that a given Module Face is boundary-compatible. Create them with **Construct Terminator** (from a Face or Module) or **Terminator from Point** (from a viewport click). See also: [4.1.5 Construct Terminator](index.html#comp-terminatorconstruct), [4.1.7 Terminator from Point](index.html#comp-terminatorfrompoint), [4.4.1 Construct Assembly](index.html#comp-assembly). ### 1.61 What happens when Require Terminators is on but some face directions have no Terminator? Construct Assembly emits an Assembly marked invalid and reports a build problem when Require Terminators has no Terminators or an exposed Slot Face has no allowed Module that can cap it. The WFC Solver refuses that Assembly. Audit Assembly lists missing Directions in **Terminator Faces Missing** and its Report; it does not raise a runtime Warning for them. To prevent this, supply Terminators for the exposed Face Directions on non-flat axes. You can wire a single Module directly into Construct Terminator to create Terminators for all six of its Faces. **Flat Axes:** when the grid has extent = 1 in one or more dimensions (e.g. a 10×10×1 grid), those Axes have no neighbours, so Construct Assembly needs no adjacency Rules or self-adjacency for the collapsed dimension. Require Terminators also skips the flat Direction: the out-of-plane top and bottom Faces are the front and back of a 2D tiling, not envelope edges, so no Terminator is required for them. Supply Terminators only for the non-flat Face Directions (the in-plane rim). Check the **Terminator Faces Covered** and **Terminator Faces Missing** outputs on Audit Assembly to verify coverage before solving. See also: [4.4.6 Audit Assembly](index.html#comp-audit), [4.1.5 Construct Terminator](index.html#comp-terminatorconstruct). ### 1.62 How does Require Terminators differ from manually adding boundary Slots? Both approaches enforce that specific Module Faces appear at the Envelope boundary, but they work at different levels of control. **Manual boundary Slots** (using Add Boundary Layer, creating an “Out” Module, and defining Rules to interior Modules) gives you full authoring control: you design what the boundary layer contains, what geometry it carries, and which Rules connect it to your interior Modules. **Require Terminators** is a shortcut: Construct Assembly caps the occupied Envelope frontier directly from your Terminator declarations, with no extra boundary cells or boundary Module — the grid stays exactly the cells you authored, and an exposed Face that no allowed Module can cap makes Construct Assembly report a build problem and emit an invalid Assembly. Use Require Terminators when you simply want boundary enforcement without designing explicit boundary Module types. Use manual boundary Slots when you need specific geometry or custom Rule logic at the Envelope edge. See also: [4.4.1 Construct Assembly](index.html#comp-assembly), [1.7 How do I handle boundaries?](#faq-6). ### 1.63 How do I create Terminators for all faces of a Module at once? Wire the Module directly into the **Face or Module** input of **Construct Terminator**. When a Module is wired instead of individual Faces, the component expands it into all six of its Faces and produces one Terminator per Face. A Remark reports how many Terminators were produced from the expansion. This is the fastest way to mark an entire Module as fully boundary-compatible on all six sides. You can also use **Terminator from Point** and place six point tags — one on each Face — or extract individual Faces with Get Module Faces and wire each one into Construct Terminator separately. See also: [4.1.5 Construct Terminator](index.html#comp-terminatorconstruct), [4.1.7 Terminator from Point](index.html#comp-terminatorfrompoint). ### 1.64 What does “authored face(s) have no rules and no connectors” mean? Construct Assembly checks authored Module Faces on non-flat axes for Rule or Connector coverage. If Faces remain uncovered with Indifference off, it reports `1 authored face(s) have no rules and no connectors. Enable Indifference or add rules/connectors for these faces. Use Audit Assembly to see which faces are uncovered.` when one Face is uncovered, and marks the Assembly invalid. The WFC Solver refuses the invalid Assembly and reports `WFC SOLVER DID NOT RUN`. With Indifference on, uncovered Faces receive indifference Rules. Rotation variants can also have uncovered Faces after expansion. Construct Assembly warns that the Solver may eliminate those variants in directions without coverage. Use Audit Assembly to inspect the uncovered Faces, then add Rules or Connectors, or enable Indifference. See also: [4.4.5 WFC Solver](index.html#comp-solver), [4.4.6 Audit Assembly](index.html#comp-audit), [1.61 What happens when Require Terminators is on but some face directions have no Terminator?](#faq-terminator-require). ### 1.65 Can I use my Monoceros 1 or 2 projects in Monoceros 3? Yes. Monoceros 1 and 2 each ship a **Convert to Monoceros 3** component on the legacy plug-in's *Monoceros 1* or *Monoceros 2* tab, in the *Convert* panel. Drop it onto your existing definition, wire the legacy **Slots**, **Modules**, and **Rules** into it, and route its outputs into v3's **Construct Assembly**. The original definition stays untouched; the converter is one explicit node you can delete to fall back to the legacy Solver. v3 itself does not need to know about v1 or v2 — it just receives v3 goo. See also: [2.36 Converting Monoceros 1 or 2 projects to Monoceros 3](workflows.html#convert-from-v1-or-v2). ### 1.66 What do I need to install to convert a Monoceros 1 or 2 project? Install **Monoceros 3** alongside the legacy plug-in from the Yak Package Manager or [monoceros.tools](https://www.monoceros.tools/). All three plug-ins are independent `.gha` files and load side-by-side in the same Rhino. You also need the legacy plug-in updated to a release that includes **Convert to Monoceros 3** in its *Convert* panel — older builds of Monoceros 1 and 2 do not contain the converter. Restart Rhino so Grasshopper picks up both plug-ins. ### 1.67 What is different when converting from Monoceros 1 versus Monoceros 2? The converter inputs are the same in both legacy plug-ins (Slots, Modules, Rules) but the outputs differ: - **From v1**: five outputs — Slots, Modules, Rules, Connectors, Connector Pairs. v1 had typed Rules; the converter reuses the v1 Connector-type names to emit v3 Connectors and one ConnectorPair per unique type so any Face tagged `T` connects to any other Face tagged `T`, preserving v1 semantics. - **From v2**: three outputs — Slots, Modules, Rules. v2 has no typed Rules and no Connector concept, so no Connector or Connector Pair outputs are emitted. v1 megamodules become one single-cell v3 Module per part, named with a part index and count suffix such as `_p0/3`. The converter adds internal Rules between touching parts. Only part 0 materializes the shared geometry. v2 Modules are 1:1 single-cell and need no conversion. ### 1.68 The converter shows a Remark about indifference. What should I do? v1 and v3 model indifference differently: - **v1**: explicit typed Rules tagged `"indifferent"` on selected Faces. Faces without such Rules cause contradictions. - **v3**: a flag on Construct Assembly (**Indifference**, default on) that auto-pairs any unused Face with all opposing Unused Faces on the same Axis. The converter forwards v1's explicit Indifferent Rules as v3 Connectors and ConnectorPairs. It also emits the Remark so you can decide whether to keep v3's **Indifference** flag on: - *Strict v1 parity* — turn the v3 Indifference flag **off**. Only Faces explicitly marked Indifferent in v1 will accept any neighbour. - *Modern v3 behaviour* — leave the flag **on**. v3's auto-indifference covers any unused Face in addition to the converted ones, more permissive but matching the idiomatic v3 style. ### 1.69 Why does every Monoceros 1 or 2 component show a "Monoceros 3 is installed" warning? When the legacy plug-in detects Monoceros 3 in the same Rhino session, every Monoceros 1 or 2 component and floating parameter raises a Warning recommending you migrate the definition with the **Convert to Monoceros 3** component. This is intentional: the warning is the discovery path for the converter and disappears as soon as you replace the legacy pipeline with the converted one (since the converter component itself is exempt). If you intentionally keep a legacy definition running for archive or comparison, ignore the Warnings — they do not affect the solve. ### 1.70 What does the converter NOT produce? The converter emits the *ingredients* — Slots, Modules, Rules, optionally Connectors and Connector Pairs — and leaves Assembly construction to v3's **Construct Assembly**. It does not produce a `GH_DiscreteAssembly` directly. This is deliberate: new v3 features (Terminators, indifference, exclusive Rules, Audit, rotation expansion) flow into the converted definition automatically as v3 evolves, without needing to re-author the converter. It also means converted definitions get all the v3 authoring controls (the 15 inputs of Construct Assembly) so you can layer rotation variants, Terminators, or stricter boundary handling on top after migration. See also: [2.36 Converting Monoceros 1 or 2 projects to Monoceros 3](workflows.html#convert-from-v1-or-v2), [2.3 The Assembly workflow](workflows.html#assembly-workflow). ### 1.71 Can I place several Connectors on one Face? Yes. One Face may carry as many Connectors as you want. The Face then accepts every neighbour any of them is paired with, so a Face given both a `door` and a `glazing` Connector mates with everything either pairing allows. This is ordinary authoring and passes Audit without comment. Construct Assembly drops only assignments that say nothing: the identical Connector assigned twice to one Face, and a per-ordinal Connector whose whole-side twin already covers it (e.g. `meeting:+X` alongside `meeting:+X2`). See also workflows and FAQs: [1.53 Connectors](#faq-connectors), [2.3 The Assembly workflow](workflows.html#assembly-workflow). ### 1.72 What does the Clean up toggle on Construct Assembly do? Enable the **Clean up** toggle on Construct Assembly to canonicalize and prune the Assembly immediately after it is built. Canonicalization runs constraint Propagation (the same step the Solver performs automatically before its first Observation) and narrows the engine's internal Slot-allowed lists. Pruning then removes Modules and Rules that are no longer reachable. The Solver performs this step automatically on every session creation, so enabling **Clean up** does not change solve results. It is purely a diagnostic and pre-validation tool. **When to use it:** - **Debugging over-constrained assemblies.** If constraint Propagation forces one or more Slots to zero allowed Modules (a Contradiction), the component emits a Warning with the count. The Rules are contradictory before solving even begins. - **Visualizing constraint tightness.** Use Deconstruct Slot or Slot to Module on the output to see which Slots are forced to a single Module by the Rules alone. - **Pre-narrowing for downstream logic.** The cleaned-up Assembly serializes the narrowed engine state; downstream components that re-use the serialized Assembly skip redundant work. Clean-up is deterministic: the same Assembly always produces the same narrowed result regardless of Seed. See also workflows and FAQs: [1.15 Why do some Modules never appear?](#faq-14), [2.21 Audit-driven debugging](workflows.html#audit). ### 1.73 What happens when Construct Assembly encounters a build problem? Construct Assembly emits an invalid Assembly for recoverable build problems, including missing required Terminators, an exposed Slot Face that cannot be capped, no Rules after construction, a Slot with no allowed Module, too many distinct Modules, duplicate Slot positions, an out-of-range Face ordinal, or a disconnected multi-cell footprint. It reports each problem as a Warning. A wired Modules or Slots input with no valid contents also produces an invalid Assembly. If either input is unwired, the component reports `Failed to collect Modules.` or `Failed to collect Slots.` and emits no Assembly. The WFC Solver and Growth Solver refuse any Assembly whose `IsValid` flag is false. The Growth Solver reports `Assembly is invalid: `. The problem is therefore caught one component downstream, with the cause already named on the canvas, instead of the Assembly vanishing silently at construction time. To clear the invalid state, fix every Warning the component reports. See also workflows and FAQs: [1.3 Contradictory state](#faq-2), [2.21 Audit-driven debugging](workflows.html#audit). ### 1.74 How do I steer which Slots the Solver resolves first? Set **Slot Priorities** on Construct Assembly as a number list parallel to Slots (default 1.0). Values are truncated to integers. A shorter list repeats its last value; more priorities than Slots is an Error. The WFC Solver selects the lowest-Entropy Slot within the highest-priority band; the Growth Solver picks at random within that band from its frontier. **WFC Solver (band-then-Entropy):** among non-Deterministic Slots, restrict to the highest-priority band, then apply min-Entropy within that band, random tie-break unchanged. Forcing a high-priority-but-high-Entropy Slot first raises Contradiction probability because it bypasses the min-Entropy heuristic that normally keeps Contradictions rare. Use Priority to steer order, not to replace Entropy. **Growth Solver (frontier-then-band):** each step collects unresolved Slots with a Module candidate that share a Face with a Slot holding a placed Module. If this frontier is empty, it collects all unresolved Slots with a Module candidate. It keeps the highest-priority band in that set and picks one Slot uniformly at random. Priority does not make growth jump to a Slot outside the frontier. A Slot that allows only one Module is not placed first unless its priority and eligibility make it the chosen Slot. **Worked example:** fix two Slot regions at opposite ends of a grid. Set the Slots along the desired connection corridor to Priority 10 and all others to 0. The WFC Solver resolves the corridor first, so it connects the two fixed regions before the surrounding lower-priority area fills in and over-constrains. The Growth Solver starts in the corridor and grows along it while corridor Slots remain on its frontier. Slot Priorities are authored on Construct Assembly and carried through the Assembly. There is no live mid-solve mutation; to change Priority, rebuild the Assembly. ### 1.75 Can I define a Rule that spans more than one Slot? No. Rules define only immediate adjacency -- the relationship between a Slot and its 6 direct neighbours. You cannot create a Rule that says "Module A must be exactly 3 Slots away from Module B." WFC operates locally; global spatial relationships must emerge from local Rules, not be specified directly. **Workaround for 1D/2D designs:** use extra dimensions as "state channels." For example, in a 2D floorplan (X-Y), use the Z dimension to encode additional constraints through multi-cell Modules that span Z levels. See also workflows and FAQs: [1.15 Why do some Modules never appear?](#faq-14), [1.31 How do I make a Module type go mostly straight?](#faq-30). ### 1.76 How does Cells from Geometry handle cell boundaries? **Cells from Geometry** generates Cells covering input geometry. **Surface Wrap** marks a Cell when a Mesh, Surface, or Brep overlaps its box; **Fill Volume** marks a Cell when its centre lies inside a closed volume. **Populate Method** selects either mode or both. Open shapes use surface coverage only and produce a Remark. Curves and Points are sampled; with **Interior Only**, curve samples on a Cell face are skipped. Snapping to Cell boundaries uses the document tolerance, capped at 2 % of a Cell. The component reports a Remark when that cap applies. Use the generated Cells as inputs to Construct Slot or Construct Module. See also workflows and FAQs: [1.5 What makes a good Module?](#faq-4). ### 1.77 What is the Growth Solver? Monoceros runs the same native WFC kernel behind two solve modes. | Solver | Respects adjacency | Drives to targets | You author | Read the result from | | --- | --- | --- | --- | --- | | **WFC Solver** | yes | no -- any valid arrangement | Modules, Rules | the solved Slots | | **Growth Solver** | yes, between placed Modules | no | Modules, Rules, optional supply caps | the grown Assembly and the Placed, Empty and Settled outputs | WFC yields any valid tiling by filling the entire Envelope at once. The Growth Solver places Modules step by step along a growth frontier (Slots adjacent to already-placed Modules). It can leave Slots empty or stop with Slots unresolved. In Grasshopper, it runs up to **Steps** automatic steps per run; no component places a chosen Module in a chosen Slot. See [4.4.2 Growth Solver](index.html#comp-growth-solver). The Growth Solver's **Supply Names** use authored Module names. Each cap counts Module instances across every rotation variant; the first part of a multi-cell Module reserves an instance, and its remaining parts can still be placed after the cap is reached. A negative cap applies as zero. See the [Growth Solver component](index.html#comp-growth-solver) for its inputs and outputs. See also workflows and FAQs: [1.13 Reproducible results](#faq-12). ### 1.78 How does Group Faces by Voxels work? The **Group Faces by Voxels** component clusters Module Faces that share the same geometric profile, so you can author one Rule per group instead of inspecting every Face by hand. It is the inspection counterpart of the voxel-based Face matching used by Detect Rules from Voxels and Detect Connectors from Voxels. **Inputs:** Modules (M); Voxel Dimension (VD, default `{16,16,16}`); Precision (P, default 48 rays per cell); Inner Depth (ID, default 0.05) and Outer Depth (OD, default 0.05), both as fractions of the Module depth along the Face normal. **Output:** Face Groups (FG) -- a data tree where each branch holds the Face IDs that share one voxel fingerprint. **How it works:** 1. Each Module's geometry is merged into a single mesh. 1. For each of the six Faces, the two axes spanning the Face set the scan resolution from Voxel Dimension; the third axis sets the scan depth. 1. Rays are cast on that grid from Inner Depth inside the Cell to Outer Depth outside it, at Precision rays per cell. A voxel bin is marked where the mesh surface crosses a ray (surface-only marking, not solid fill). 1. The marked bins form a boolean 3D fingerprint for the Face. Faces with no occupied voxels are skipped. 1. Fingerprints are compared by structural equality; Faces with identical fingerprints land in the same output branch, drawn in a distinct colour in the viewport. **Tuning:** - Raise **Precision** or **Voxel Dimension** to separate Faces that differ only in small features; lower them for speed. - Raise **Inner/Outer Depth** if Faces come back ungrouped because the geometry sits away from the Face plane (the component warns when no occupied voxels are found). - Grouping is geometric, not semantic: Faces that look identical to the scan are grouped even if you intended them to connect differently. Where precision matters, author explicit Rules instead (see [1.48 Common Module design mistakes](#faq-module-mistakes)). See also workflows and FAQs: [2.24 Rules from Face Groups](workflows.html#shared-face-groups), [2.8 Defining Rules from Faces](workflows.html#defining-rules). ## 2. Tips & Tricks ### 2.1 Start small, scale up Always prototype on a small Envelope (3×3×3 or 5×5×5) first. Verify that Modules, Rules, and boundary handling work correctly before scaling to production-size Envelopes. Debugging a 20×20×20 Envelope (8,000 Slots) is orders of magnitude harder than debugging a 3×3×3 Envelope (27 Slots). See also workflows and FAQs: [2.1 Bare minimum](workflows.html#bare-minimum), [1.5 Debugging strategy](workflows.html#strategy-debugging). ### 2.2 Always run Audit before Solve The Audit Assembly component catches most setup errors instantly: orphaned Faces, invalid Modules, dimension mismatches, unknown Module Names in Slots. Running Audit Assembly first saves you from chasing down mysterious Solver failures. Wire Audit Assembly in parallel with the WFC Solver and check its Report output before investigating Solver results. See also workflows and FAQs: [2.21 Audit-driven debugging](workflows.html#audit), [1.5 Debugging strategy](workflows.html#strategy-debugging), [4.4.6 Audit Assembly component](index.html#comp-audit). ### 2.3 Name Modules descriptively Use names like `frame-corner`, `panel-solid`, `void-interior` instead of `m1`, `m2`, `m3`. Descriptive names make Rules readable, Audit reports understandable, and occurrence counts meaningful. Remember that names auto-convert to lowercase. See also workflows and FAQs: [2.9 Working with multiple Modules](workflows.html#multiple-modules). ### 2.4 Use multiple void Module types Instead of a single `empty` Module, create purpose-specific void types: `void-interior` (for empty space inside the design), `void-boundary` (for the outer shell), `void-clearance` (for access clearance zones within the design). Each can have different Rules, giving you finer control over where empty space appears. More void types also give the Solver more flexibility, reducing Contradiction frequency. See also workflows and FAQs: [2.6 Empty Module](workflows.html#empty-module). ### 2.5 Use attractor-based weight gradients Instead of uniform Weights, compute Weights per Slot based on spatial criteria: - **Distance from a point** - Use Grasshopper's **Distance** component and remap the result to a Weight range. - **Distance from a curve** - Use **Curve Closest Point** to create a gradient along a path. - **Z height** - Use the Z coordinate of each Slot's centre point to create a vertical gradient. - **Noise** - Add Perlin noise or random jitter to Weights for organic variation. Remap the gradient to a reasonable Weight range (e.g. 0.1 to 5.0). Extremely high Weights can force contradictions. To completely exclude a Module from a Slot, remove it from that Slot's `ModuleNames` list or set its Weight to 0 or less. A near-zero positive Weight remains a soft preference. See also workflows and FAQs: [2.12 Weighted Module placement](workflows.html#weighted-slots), [2.30 Restricting Modules to specific regions](workflows.html#region-restriction). ### 2.6 Bulk Rule creation Construct Rules From Faces has a built-in **Cross Match** toggle (default: true) that generates all Source × Target combinations automatically. There is no need for an external Cross Reference component. See also workflows and FAQs: [2.8 Defining Rules from Faces](workflows.html#defining-rules). ### 2.7 Test Rules visually before solving Before running the Solver, use Preview Rule and Preview Rule in Slots to inspect your Rules visually. Preview every Rule as a pair of positioned Modules - this immediately reveals Orientation errors, wrong Face assignments, and missing pairings that would otherwise cause mysterious Solver failures. See also workflows and FAQs: [2.22 Visualizing Rules and Faces](workflows.html#visualizing), [4.6.8 Preview Rule](index.html#comp-rulepreview). ### 2.8 Bake as block instances for smaller files The Materialize component creates block instances when baked - each unique Module becomes a block definition and placements are lightweight references. This produces dramatically smaller .3dm files compared to baking individual geometry copies. For an Envelope with 1,000 Slots and 5 Module types, you get 5 block definitions instead of 1,000 geometry copies. See also workflows and FAQs: [2.19 Materializing results](workflows.html#materializing), [2.32 Exporting results for fabrication](workflows.html#export-fabrication). ### 2.9 Use Data Recorder to save multiple results Connect the Materialize Geometry output to a **Data Recorder** component. Step through different Seeds, and the Data Recorder captures each result. You can then browse all recorded results without re-solving, making Seed surfing much faster. See also workflows and FAQs: [2.20 Solver settings](workflows.html#solver-settings). ### 2.10 Use Add Boundary Layer for irregular Envelopes When constructing irregular Envelopes (L-shapes, courtyards, organic shapes), use Add Boundary Layer to ensure proper adjacency around irregular Envelope edges. The component takes Cells and outputs **Boundary Layer Cells**. Use Construct Slot to turn those Cells into boundary Slots. Require Terminators on Construct Assembly enforces boundary Faces without adding Cells. See also workflows and FAQs: [2.14 Constructing Slots from geometry](workflows.html#constructing-slots), [2.27 Sparse grids with intentional gaps](workflows.html#sparse-grids), [4.2.3 Add Boundary Layer](index.html#comp-addbound). ### 2.11 Understand the Materialize output tree Materialize Assembly outputs Geometry, Transforms, and Modules as trees with branch paths `{assembly iteration; slot index}`. To work with specific Module types after materialization: - Filter by the Modules output name (for example, Deconstruct Module → Module Name) to select placements of a specific Module. - Use **Flatten** to get all geometry in a single list. - Use **Graft** if you need per-Slot isolation. See also workflows and FAQs: [2.19 Materializing results](workflows.html#materializing). ### 2.12 Disable the Solver while editing The WFC Solver has a **Run** input (default False). Set Run to False while editing Modules, Rules, or the Envelope; it reports `Idle` and does not solve. Set Run to True when you are ready to see results. While Run is True, input changes trigger a new solve. See also workflows and FAQs: [1.3 Iterative refinement](workflows.html#strategy-iterative). ### 2.13 Proto-results save computation time If your final Module geometry is complex (dense meshes, heavy Breps), use lightweight placeholder geometry (simple boxes or wireframes) during design iteration. Replace with the final geometry only for the last Materialize step. This dramatically speeds up the preview cycle. See also workflows and FAQs: [2.25 Proto-results workflow](workflows.html#proto-results). ### 2.14 Use Sample Geometry to verify Module content Sample Geometry samples input Geometry against Sampling Cells to create Module candidates and matching Slots. It deduplicates identical pieces at Voxel Resolution and outputs Modules, Module Count, Slots, Box to Module Index, and Box to Slot Index. Use Box to Module Index to check which Cells produced Module geometry. See also workflows and FAQs: [2.14 Constructing Slots from geometry](workflows.html#constructing-slots), [2.1 Bare minimum](workflows.html#bare-minimum), [4.4.8 Sample Geometry](index.html#comp-sample). ### 2.15 Compare results with Changed Slots The Changed Slots component compares two solved Envelopes and identifies which Slots changed. Use it to understand the effect of Rule modifications, Weight changes, or Seed differences. This is especially useful during iterative refinement - you can see exactly which parts of the result were affected by your last change. See also workflows and FAQs: [2.20 Solver settings](workflows.html#solver-settings), [4.7.3 Changed Slots](index.html#comp-changedslots). ### 2.16 Use Module Rotations for isotropic geometry Define one base Module for each tile type, then apply Module Rotations with Rotational Freedom = 3 (Z only) and Cull Duplicates = true. Alternatively, set Rotational Freedom on Construct Module to let Construct Assembly generate the rotation variants. All variants share the Module Name, so a single Rule set covers all orientations. This is significantly less error-prone than manually defining directional variants as separate Modules and ensuring their Face Directions are correct. Note: Module Rotations generates only rotational variants. If your design requires a mirrored Module (e.g. a left-hand and right-hand corner), model the mirrored version separately from scratch and define its Rules independently. See [FAQ 1.52](faq.html#faq-mirror) for why automatic mirroring is not supported. See also workflows and FAQs: [2.10 Automatic Module rotations](workflows.html#module-rotations), [4.5.4 Module Rotations](index.html#comp-modulerot). ### 2.17 To exclude a Module from a zone, remove it from allowed names To prevent a Module from appearing in a Slot, remove it from that Slot's `ModuleNames` list when constructing the Slot, or set its Weight to 0 or less - both are hard exclusions. Construct Slot removes a zero-or-negative-Weight Module from the Slot entirely, so the Solver cannot place it there under any circumstances. See also workflows and FAQs: [2.30 Restricting Modules to specific regions](workflows.html#region-restriction), [2.15 Fixing Modules in the Envelope](workflows.html#fixing-modules). ### 2.18 Plan Face roles before modelling geometry Write a table: for each Module, list what role each Face Index plays (e.g. path, transition, void, boundary). Roles are a design-planning convention - there is no type or role field on the FaceId itself. You implement them by extracting the right index outputs from Get Module Faces and organising them into named lists on the canvas. Verify that every role appears as both source and target somewhere in the system - a role that is always a source but never a target (or vice versa) will cause contradictions. Do this planning before any geometry work - it is much easier to fix a role table than to re-model Modules. See also workflows and FAQs: [2.8 Defining Rules from Faces](workflows.html#defining-rules), [1.2 Bottom-up: Modules first](workflows.html#strategy-bottomup). ### 2.19 Let Construct Assembly stamp AllModulesCount Construct Slot no longer takes an All Modules Count input. Every Slot is created with `AllModulesCount = 0` and **Construct Assembly** stamps every Slot it processes with the number of distinct Module Names referenced by any Slot — so every Slot in the Assembly agrees on the same denominator automatically. No List Length wiring or cross-component synchronisation is needed. The Slot preview reflects the two phases: pre-Assembly it shows only the allowed count (one number in the corner, no `/ total`); post-Assembly it shows the familiar `allowed / total` form. See also workflows and FAQs: [2.1 Bare minimum](workflows.html#bare-minimum). ### 2.20 How to allow all indifferent Faces on the boundary When using **Require Terminators** on Construct Assembly, every Module Face that may sit against the Envelope boundary must have a Terminator. If your setup has many Modules with Faces that carry no explicit Rules or Connectors (Indifferent Faces), marking each one by hand is tedious and error-prone. The **Used Faces** component automates this. Wire your Modules, Rules and Connectors into it — the **Unused Faces** output gives you exactly the Faces that have no Rule or Connector coverage. Feed that output directly into **Construct Terminator** and the resulting Terminators into Construct Assembly. Every Indifferent Face is now marked as boundary-safe, so Require Terminators will not reject them. #### Step by step 1. Place a **Used Faces** component. 1. Wire your **Modules** into the M input. 1. Wire your **Rules** into the R input (and Connectors into C if you use the Connector workflow). 1. Connect the **Unused Faces** (UF) output to a **Construct Terminator** component. 1. Merge these Terminators with any Terminators you already have. 1. Wire the merged Terminators into Construct Assembly’s Terminators input with Require Terminators enabled. This pattern is safe because Indifferent Faces already connect to every opposing Indifferent Face — marking them as Terminators simply declares that they may also sit against the boundary, which is the expected behaviour. See also workflows and FAQs: [2.35 Marking unused Faces as Terminators](workflows.html#unused-faces-terminators), [4.3.6 Used Faces](index.html#comp-usedfaces). ## Vocabulary All terms used in Monoceros 3 documentation and component tooltips. **Attempt** — A single WFC solve run, from Canonicalization through cycles of Observation and Propagation. Additional Attempts with incrementing Seeds run when the first Attempt does not fully collapse, or when Return first is off. **Axis** — One of the three coordinate axes: X, Y, or Z. Each Face direction belongs to one axis (positive or negative face). **Canonical** — The tightest consistent starting state of the Envelope, reached after the initial Propagation pass (Canonicalization). Every Slot contains only Modules that are actually achievable given the Rules. **Canonicalization** — The initial constraint-propagation pass run before the first Observation. It eliminates already-impossible Module assignments from every Slot so the solver starts from the tightest consistent state. **Cell** — The basic spatial unit in a Monoceros grid — a box-shaped region that defines the size and position of one cell. **Changed Slots** — Compares two sets of Slots element-wise and returns the indices of Slots whose allowed Module list has changed. Useful for tracking which parts of the Envelope were affected by a Rule or weight change. **Connector** — A named, rotation-aware interface placed on a specific Module face. Combines type identity (name + symmetry flags) and placement (module + face + rotation) in one object. All Connectors sharing a name are the same type. Symmetry flags define which in-plane rotations are self-identical. **Connector Pair** — Declares that two Connector types (by name) can connect across opposing faces. Bidirectional: A → B also allows B → A. **Contradictory** — A Slot state where no Module can legally occupy it, making the current solve attempt impossible. The solver retries with a different seed up to the configured attempt limit. **Deterministic** — A Slot state where exactly one Module is allowed. A fully deterministic Envelope means the solve succeeded and can be materialized. **Direction** — One of the six face orientations: +X, −X, +Y, −Y, +Z, −Z. Combines an Axis and an Orientation. Direction is not a direct Grasshopper parameter — it is expressed through Face Index names. **Discrete Assembly** — Contains authored inputs and the expanded Modules, Slots, Rules, and audit results used by the WFC Solver. **Entropy** — A measure of how many Module candidates remain in a Slot. Low entropy means few options remain; a Slot with all Modules allowed has maximum entropy. Within the highest Slot Priority, the WFC Solver observes the Slot with the lowest entropy first. **Envelope** — A set of Slots arranged in a valid grid, forming the spatial volume to be filled by the WFC solver. A grid of Cells becomes an Envelope once the Cells are converted to Slots. **Face** — One face of a Module, identified by a FaceId (e.g. module_name:+X). Rules specify which Face pairs may touch. External Faces of multi-cell Modules carry a cell ordinal. **Face Index** — An integer 0–5 identifying one face direction of a Module or Cell: +X = 0, +Y = 1, +Z = 2, -X = 3, -Y = 4, -Z = 5. **Growth Solver** — Places Modules one Slot at a time from a Discrete Assembly, extending along the frontier of placed Modules. Slots can settle empty; it returns the grown Assembly, Report, Settled, Placed, and Empty. Supply caps on authored Module names cover their rotation variants and parts. **Heterogeneous Grid** — A grid where cells can have different sizes along each axis. Each row along X can have a different width, each column along Y a different depth, and each layer along Z a different height. **Homogeneous Grid** — A grid where every cell has the same X, Y, and Z dimensions. The most common starting point for a WFC setup. **Indifferent Face** — A Face that has no explicit Rule. When indifference is enabled on Construct Assembly, indifferent Faces automatically connect to any other indifferent Face facing the opposite direction on the same axis. **Module** — A named design element with one or more Cells and optional geometry. Each Cell has six Faces. During solving each Slot starts with its allowed Modules as candidates. **Module Name** — A lowercase string identifying an authored Module. Modules may share a name only when they are Module Rotations variants of one source Module; other repeated names are an Error. **Module Rotations** — Uses Rotational Freedom (None, X only, Y only, Z only, Full) to generate up to 24 orientations, with Cull Duplicates and Voxel Resolution options. **Multi-cell Module** — A Module that spans several adjacent Cells and is placed as one rigid body. Give Construct Module more than one box; the geometry attaches once and materializes once per placed instance. Faces are numbered per direction (+X0, +X1, ...). Supports auto-rotation — the whole body rotates together. Previously a separate Megamodule type. **Non-deterministic** — A Slot state where multiple Modules are still possible. Non-deterministic Slots have not yet been resolved by the solver. **Observation** — The WFC step where the Slot with the lowest entropy is selected within the highest Slot Priority and assigned a Module using the Slot weights. **Occurrence Count** — Counts how many Slots in the envelope contain a given Module Name. By default (Only Resolved = true) only Slots where that Module is the sole allowed option are counted; set Only Resolved to false to also include Slots that still allow the target Module alongside others. Useful for bill-of-materials and distribution analysis after solving. **Orientation** — Positive or negative along an axis. Combined with an axis (X, Y, or Z), orientation defines one of the six faces of a Cell or Module. **Propagation** — The WFC step that cascades the consequences of an Observation through the Envelope. After a Slot is assigned a Module, neighboring Slots that can no longer legally host certain Modules have those candidates removed. This cascade continues until the Envelope stabilises. **Random Seed** — An integer that initialises the pseudo-random number generator used by the WFC Solver and the Growth Solver. The same seed with the same input always produces an identical result. Changing the seed explores a different solution path without altering any rules or weights. **Rule** — An allowed adjacency between two Module Faces facing opposite directions. Rules are bidirectional: each adjacency only needs to be defined once. **Slot** — A cell in the Envelope that holds a list of allowed Module candidates and their weights. Before solving, Slots allow multiple Modules; after solving, each holds exactly one Module (deterministic) or none (contradictory). **Wave Function Collapse (WFC)** — An algorithm that fills a spatial envelope by alternating Observation and Propagation steps until all Slots are deterministic or a contradictory state is reached. **Weight** — A per-Module probability value stored in each Slot. Higher weight increases the chance of that Module being chosen during Observation. Default weight is 1.0; a weight of zero or less removes the Module from the Slot. **WFC Solver** — The main Monoceros 3 component. Given a Discrete Assembly it runs the Wave Function Collapse algorithm and returns an Assemblies list with fully solved or partial results, or one contradictory Assembly if all Attempts contradict, plus Report, Deterministic, Contradictory, Seeds, Observations, and Attempts.