# 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 optimally occupying an Envelope with discrete Modules, where the spatial relationship between those Modules can be constrained by a set of user-defined Rules. By using the Wave Function Collapse algorithm, it provides an innovative and fast solution to the emerging architectural problem of discrete aggregation for purposes of design, architecture and urban planning. 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** - the Module Rotations component generates up to 24 orientations per Module, with voxel-based deduplication to remove geometrically identical results. - **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** - multiple Attempts run simultaneously on available CPU cores. - **Megamodule support** - multi-cell Modules with auto-generated internal Rules, enabling larger design elements that span several Slots. - **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, until every Slot holds exactly one Module - and every pair of neighboring Modules is permitted by the Rules. WFC is not a growth algorithm. It does not grow outward from a Seed or leave gaps behind. It always produces a result that fills the entire Envelope: either a complete, valid aggregate in which every Slot is occupied, or no solution at all. Because the algorithm operates simultaneously across the whole grid rather than expanding incrementally, the results are non-hierarchical - like a rhizome rather than a tree. There is no center, no branching structure, no preferred Direction of growth, no overlapping, and no untreated areas. 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/). #### 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 Slot with the lowest *Entropy* - the fewest remaining allowed Modules, weighted by Module Weights - is selected and assigned a single Module chosen randomly according to those Weights, making it Deterministic. 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). On Contradiction the Solver retries with a different Random Seed, up to the configured Attempt limit. ### 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** - all distinct rotations of a Module are generated automatically with voxel-based deduplication. In Monoceros 1, every rotated variant had to be defined by hand - up to 24 separate Modules for a fully rotatable element. - **Multi-solution parallel solving** - the Solver can run multiple Attempts simultaneously across all CPU cores and return a separate branch per solution. Exploring alternatives no longer requires re-running the definition manually. - **Voxel engine for Rule suggestion** - Suggest Rules from Voxels matches Face layers by voxel pattern rather than raw geometry, making automatic Rule suggestion reliable even for complex or irregular Module Faces. - **Full Grasshopper type integration** - all Monoceros types cast to and from standard Grasshopper geometry. A Slot casts to a Grid Box, 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 Grid Boxes. A Grid Box carries only geometry - its size and position. A collection of Grid Boxes forms a grid. It is important to understand that Grid Boxes 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 Grid Boxes must be carefully verified. 1. **Construct Modules** - Define each Module with a name, a Grid Box (defining its cell size and pivot), and optional geometry. Generate rotation variants with the Module Rotations component if needed. 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 Grid Boxes, optionally specifying which Modules are allowed in each Slot and their relative Weights. This step converts the Grid Boxes into an Envelope: Grid Boxes 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 will resolve it to the full final Module set - useful for the common case where every Slot accepts every Module. 1. **Add Boundary** - Optionally add boundary Grid Boxes around the Envelope, convert them to boundary-specific Slots, and merge them with the interior Slots. 1. **Construct Assembly** - Feed Modules, Slots, Rules, Connectors, and Connector Pairs into Construct Assembly. This expands rotation variants, generates Connector-based Rules, applies indifference, deduplicates, audits, and packages everything into a Discrete Assembly. 1. **Solve** - Connect the Assembly to the WFC Solver. Assembly is the sole data input. The Solver returns an output Assembly with solved Slot states. 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, WFC Solver, Materialize Assembly, Audit Assembly - **Connector** - Connector construction, identification, pairing, suggestion, preview - **Grid** - Grid creation, boundary, topology, geometry slicing - **Module** - Module construction, deconstruction, rotations, deduplication, Megamodules - **Face** - Face extraction, analysis, comparison, suggestion, preview - **Rule** - Rule construction, deconstruction, filtering, suggestion, preview - **Slot** - Slot construction, deconstruction, pattern finding ## 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 Grid Box A **Grid Box** is the basic spatial cell unit of a Monoceros grid - a box-shaped region that defines the size and position of one cell. Multiple Grid Boxes form a grid; when converted to Slots via the Construct Slot component, they form an Envelope. Grid Boxes are created by the Homogeneous Grid or Heterogeneous Grid components and can also be cast from standard Rhino Boxes, Rectangles, or Breps. A Grid Box exposes its Box geometry, from which center, dimensions, and Orientation can be derived. #### Validity A Grid Box is valid when its Box geometry is non-degenerate - all three dimensions greater than zero. Invalid Grid Boxes are rejected by all Monoceros components with a warning. #### Viewport preview Draws a wireframe representation of the Grid Box with short colored lines indicating the `+X`, `+Y`, and `+Z` Directions at the center. #### Casting | From / To | Type | Notes | | --- | --- | --- | | Cast from | `Box` | A Rhino Box is wrapped directly into a Grid Box. | | Cast from | `BoundingBox` | A BoundingBox is wrapped directly into a Grid Box. | | Cast from | `Rectangle` | A Rectangle is extruded by one unit in Z to form a Grid Box. | | Cast from | `Brep` | The bounding box of the Brep is used as the Grid Box. | | Cast to | `Box` | Returns the Grid Box as Rhino Box geometry. | | Cast to | `Brep` | Returns the Grid Box as a closed Brep surface. | | Cast to | `Point` | Returns the center point of the Grid Box. | | Cast to | `Plane` | Returns the center plane of the Grid Box with its Orientation. | | Cast to | `Vector` | Returns the diagonal vector (X, Y, Z dimensions) of the Grid Box. | #### String representation Displays as a formatted string showing the box origin and dimensions, for example: `Grid Box at (0,0,0) 1×1×1`. #### Baking Grid Boxes bake as Rhino Box geometry. ### 3.2 Module #### 3.2.1 Module A **Module** is the fundamental design element in Monoceros 3. It carries a name, the geometry of a discrete building element, and a Grid Box that defines its cell size and pivot. Each Module has exactly six Faces - one per Face, identified by Axis and Orientation: `+X`, `-X`, `+Y`, `-Y`, `+Z`, `-Z`. 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 Grid Box - 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 lowercased automatically on input. Multiple Modules sharing the same name are treated as variants (invariants) of the same Module type - useful in Heterogeneous Grids where the same Module Name must cover Slots of different sizes. - **Grid Box** - The bounding box defining the Module’s cell size and pivot. - **Geometry** - Optional Rhino geometry (curves, surfaces, Breps, meshes). Can exceed the bounding box. - **Faces** - Six Faces, one per Face. Each is identified by its Direction (`+X`…`-Z`), which encodes the Face’s Axis (X, Y, or Z) and Orientation (positive or negative). Any Face that should participate in adjacency decisions must appear in at least one Rule. Faces without a Rule are treated as Indifferent when indifference is enabled on Construct Assembly. - **Rotation flags** - Record whether the Module was transformed. Construct Assembly expands flagged Modules into all implied rotational variants, remapping Face Directions accordingly. The expanded variants are available via Deconstruct Assembly. #### 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 wireframe outline of the bounding Grid Box (white if valid, red if invalid). - **Face dots** at each Face center, colored by Axis: X = red, Y = green, Z = blue. Dot text is white for positive Directions (`+X`, `+Y`, `+Z`) and black for negative (`-X`, `-Y`, `-Z`). - The Module **name** as a text label (with rotation suffixes when applicable). Like all Monoceros viewport text, the label only draws once the Module cage spans at least 450 pixels on screen; each label is gated individually, so text on far-away Modules switches off regardless of what else is near the camera. #### Validity A Module is valid when its name is non-empty and its Grid Box is valid. Modules with no geometry are allowed - they produce no output during materialization but participate fully in solving. #### 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 | `Grid Box` | Extracts the bounding Grid Box of the Module. | | Cast to | `Point` | Returns the center point of the Module's Grid Box. | | Cast to | `Plane` | Returns the center plane of the Module's Grid Box with its Orientation. | | Cast to | `Vector` | Returns the diagonal vector (X, Y, Z) of the Module's Grid Box. | #### Baking Modules bake as a group containing the geometry, Face rectangles, and Face text dots. #### 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 are the unit of identity for the Solver. Two Modules with the same name are treated as variants (called **invariants**) of the same Module type. The Solver decides which Module “wins” a Slot by name; Materialize then picks the invariant whose dimensions match the Slot’s Grid Box. This is the mechanism used in Heterogeneous Grid setups: define several Modules sharing the same name but with different bounding-box sizes, and the Solver treats them as interchangeable - Materialize picks the one that physically fits. **Rules apply to all invariants simultaneously.** A Rule defined for a name covers every size variant of that name. You write the Rule once; all invariants obey it. **Variant design contract.** Sharing a name is a commitment that all size variants are functionally equivalent from the Solver’s point of view. If two size variants would need different connection Rules - for example, a long variant connects to a long neighbour but a short variant does not connect to a short neighbour - they must not share a name. Give them independent names and define separate Rules. See [example 1.8 - The invariant design contract](workflows.html#invariant-design-contract) 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 lowercased and used as the Module Name. | | Cast from | `Module` | Extracts the Module's name. | | Cast from | `Integer` | The integer is converted to a string and used as the Module Name. | | Cast from | `Number` | The number is converted to a string and used as the Module Name. | | Cast from | `Data Path` | The Data Path is converted to a string and used as the Module Name. | | 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** is an integer 0–5 that identifies one of the six Face Directions of a Module or Grid Box. Each index maps to a named Direction combining an Axis (X, Y, or Z) and an Orientation (positive or negative). Opposite Faces always have indices that sum to 5 (`+X` 0 ↔ `-X` 3, etc.), which is how the Solver checks that neighboring Faces Face each other correctly. Face Indices appear as input to Rule-construction components such as Get Module Faces and Construct Rules From Faces, and as part of FaceId strings. When building Rules manually, the named form (`+X`, `-Y`, etc.) is preferred over the numeric form for readability. | 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"`) and named (`"+X"`, `"-Y"`) formats. | | Cast to | `Integer` | Returns the numeric index (0–5). | #### Validity Valid indices are integers 0 through 5. Strings in named form (`+X`, `-Y`, etc.) are also accepted as input. #### 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`). #### String representation Format: `modulename:+X`. 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 and its Face Index is in the range 0–5. #### Casting | From / To | Type | Notes | | --- | --- | --- | | Cast from | `String` | Parses the `"modulename:faceindex"` format. | | 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 Grid Box it was created from). - **Allowed Module Names** - The list of Module Names still allowed in this Slot. - **Module Weights** - One Number per allowed Module. Higher Weight increases the probability of that Module being chosen during Observation. Default Weight is `1.0`. A Weight covers the Module as a whole, including all of its rotated variants, so authored ratios (e.g. 4:1) hold regardless of how many rotation variants each Module has. Per-Slot Weights override per-Module Weights where both are set. - **Total Modules Count** - Optional. The total number of distinct Module types in the system, used to compute the Entropy color gradient. When omitted (or zero), the gradient and Entropy label are not shown, but the Slot is fully valid for solving. - **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; it is unresolved and will be filled with the full final Module set by Construct Assembly. #### 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 full final Module set. Previews with the label `All`. | #### Viewport preview Draws a colored wireframe box. The wireframe is intentionally drawn slightly smaller than the actual cell box. Adjacent Slots share Faces, so if the boxes 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 box. The occupancy label (allowed count, Entropy percentage) is drawn on three faces (XY, XZ, YZ) once the Slot spans at least 450 pixels on screen, and switches off below that. Each label is gated individually, so labels on far-away Slots turn off regardless of what else is near the camera. **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 position, for example: `Slot [3 allowed] at (1,0,0)`, `Slot [deterministic: wall] at (2,1,0)`, or `Slot [All allowed] at (1,0,0)` for allow-all Slots. #### Casting Slots automatically cast to Grid Box when passed to components that accept Grid Boxes (such as Add Boundary Layer). This means solved Slots can be used directly wherever Grid Boxes are expected. | From / To | Type | Notes | | --- | --- | --- | | Cast from | (none) | Use the Construct Slot component. | | Cast to | `Grid Box` | Returns the cell's Grid Box, enabling use wherever Grid Boxes 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 the `"module:index -> module:index"` format. | | Cast to | (none) | | #### 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 `@`. Spaces are allowed. - **Symmetry 0°** - Self-identical at 0° rotation. Default: `true`. - **Symmetry 90°** - Self-identical at 90° rotation. Default: `true`. - **Symmetry 180°** - Self-identical at 180° rotation. Default: `true`. - **Symmetry 270°** - Self-identical at 270° rotation. Default: `true`. - **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`. Deconstructs into Connector name, rotation, Module Name, and Face id. 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 the `"connectorName#rot-A@moduleName:faceIndex"` format. | | Cast to | (none) | | #### Preview & Baking The Connector data type does not support viewport preview or baking on its own. The preview is provided by the producing component - Construct Connector, Match Connectors by Geometry, and Match Connectors by Voxels each draw Connector labels as stickers on Module Faces when Modules are connected to their inputs. #### 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**) is an opaque bundle that packages Modules, Slots, Rules, and an Audit report into a single container ready for the Solver. The Construct Assembly component creates an Assembly by expanding rotation variants, transforming Connectors, generating Rules from Connectors and Connector Pairs, applying indifference, merging and deduplicating all Rule sources, and running a full Audit. The result replaces the manual Module Rotations → Rule merging → Audit wiring pattern with a single component. The Solver accepts an Assembly as an optional input. When connected, the Solver uses the Assembly's Modules, Slots, and Rules directly, skipping its own rotation expansion and indifference steps (which are assumed already applied). The Solver also produces an output Assembly containing the solved Slot states. #### 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 valid when its Modules, Slots, Rules, and Audit Result are all non-null. #### Casting | From / To | Type | Notes | | --- | --- | --- | | Cast from | (none) | Use the Construct Assembly component or the Solver's Assembly output. | | Cast to | (none) | | #### Baking Assemblies do not bake directly. Use the Deconstruct Assembly component to extract Modules, Slots, and Rules, or connect the Assembly to the Materialize Assembly component 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 [Connector from Face](#comp-connectorconstruct). 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, so the offending point is obvious at a glance instead of only named in a warning bubble. Viewport preview draws the Connector sticker on every matched Face, identically to Connector From Face. **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. 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: true. | | Symmetry 180° | `S180` | Boolean | Item | Self-identical at 180° rotation. Default: true. | | Symmetry 270° | `S270` | Boolean | Item | Self-identical at 270° rotation. Default: true. | **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 Validates the Connector name (must be non-empty, no reserved characters), the symmetry flags (at least one must be true), the Module Name, Face Direction, and Rotation. The name is lowercased automatically. 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 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: true. | | Symmetry 180° | `S180` | Boolean | Item | Self-identical at 180° rotation. Default: true. | | Symmetry 270° | `S270` | Boolean | Item | Self-identical at 270° rotation. Default: true. | | All Modules | `M` | Module | List | All Modules (optional). Modules for viewport preview. When connected, the connector is displayed on the target face. | **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 single items: Source Connector (item) + Target Connector (item) produces one Connector Pair (item). Compatibility is bidirectional - declaring A → B also allows B → A. Use Grasshopper’s cross-reference component to make multiple pairs from lists. #### Behavior Takes one Source Connector and one Target Connector, extracts their names, and produces a single Connector Pair object. To generate multiple pairs, use Grasshopper’s Cross Reference component on the inputs. **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, Module Name, and Face id. The inverse of [Connector from Face](#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. When it is, Construct Assembly caps the occupied Envelope frontier directly: every exposed Slot Face must be filled by a Module that carries a Terminator on the matching Face. Module Faces without a Terminator cannot sit at the outer edge of the Envelope; if no allowed Module can cap an exposed Face, the Assembly will contradict. No extra boundary cells are added — the grid stays exactly the cells you authored. The optional All 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 the viewport. #### String representation Format: `Terminator@moduleName:faceIndex`. Example: `Terminator@pipe:+X`. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Face or Module | `F` | Face ID | List | Face where the Terminator is placed, obtained from the 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. | | All Modules | `M` | Module | List | All Modules (optional). Modules for viewport preview. When connected, the terminator badge is displayed on the target face. | **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 (e.g. `vox_0`, `vox_1`, etc.) with the detected rotation offset. 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 random colour per Connector group. 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 | All Module objects to voxelize and analyse. 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 cage 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 cage 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 every Face in every Module, checking whether the tagged point falls within the Face’s geometry. A Face matches if the point is within its boundary. 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. 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 Face geometry for every Module Face, transforms to a normalized base plane, and compares against the exemplar Connector's Face. For each match, the component tests all four 90° rotations of the geometry pattern to detect the rotation offset. The output is a list of Connectors covering all matching Faces (including the exemplar itself), each with the correct rotation. The exemplar Connector's name and symmetry flags are inherited by all output Connectors. The component draws a viewport preview showing all detected Connectors as stickers on Module Faces (name label and symmetry arrows in the Connector’s type color, with degree labels). **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Exemplar | `E` | Connector | Item | The reference Connector whose face geometry defines what to match. | | Modules | `M` | Module | List | All modules to search. | **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. The resulting 3D boolean fingerprint is compared at all four 90° rotations to find matching geometry with the correct rotation offset. The prototype Connector’s name and symmetry flags are inherited by all output Connectors. The prototype Face shows solid coloured voxels; candidate matches show wireframe voxel outlines with Axis colour coding. The prototype Module does not need to appear in the candidate Modules list. **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 | `PM` | 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. 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 cage 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 cage 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. When Connector Pairs are wired, the pairs are resolved to Rules internally and a bezier curve is drawn per Rule, using the same colour coding as Preview Rule. The bezier curves can be baked; Connector arrow/label geometry is not bakeable. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | All Modules | `M` | Module | List | All Modules that the Connectors reference. | | 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 Grid Boxes, 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, so each cell starts exactly where the previous one ends. The resulting Grid Boxes can have different sizes in one, two, or all three dimensions. **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, Module invariants 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, both sharing the same Module Name. Rules are defined once per name and apply to every dimensional invariant of that name. **Variant design contract.** Sharing a Module Name across different sizes asserts that all size variants are functionally equivalent: the Solver will allow any invariant in any position where the name fits, regardless of which size ends up there. If the connection geometry or logic differs between sizes, the two variants should have separate names and separate Rules. See [example 1.8 - The invariant design contract](../monoceros-v3-manual/workflows.html#invariant-design-contract) for a detailed worked example. The output is a flat list of Grid Boxes. Flatten the output when passing it 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 | | --- | --- | --- | --- | --- | | Grid Boxes | `B` | Grid Box | Tree | Heterogeneous grid GridBox 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 Grid Boxes, 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 output is a data tree of Grid Boxes, flattened by the component, in row-major order (X changes fastest, then Y, then Z). 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 | | --- | --- | --- | --- | --- | | Grid Boxes | `B` | Grid Box | Tree | Homogeneous grid GridBox 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 Grid Boxes. 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 Grid Boxes 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 Grid Boxes 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. You can restrict which Faces of the Envelope get a boundary layer using the six directional toggles. For example, disable `Z+` to leave the top of the Envelope open. When working with non-rectangular (masked) Envelopes, the boundary grows the actual shape outward along its real Face - a masked sphere thickens into a shell - rather than filling out to the bounding box, so each new layer can extend past the Envelope's original extent in every enabled direction. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Grid Boxes | `B` | Grid Box | List | All GridBox objects to surround with boundary layers. | | Diagonal Neighbors | `D` | Boolean | Item | Include diagonal Grid Box 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 Boxes | `B` | Grid Box | List | New grid boxes forming the boundary layers; ready for use as Slot envelopes. | #### 4.2.4 Are Grid Boxes Boundary *Envelope · Nickname: `AreBoxesBound` · GUID: `04B1608C-1672-4FA8-8865-965BAF21CA85`* Identify which Grid Boxes in an Envelope sit on its outer boundary. Returns a boolean per input box - `true` if the box is within the specified number of layers from the outer edge. Use this to separate boundary cells from interior cells to treat them differently - assign dedicated boundary Modules, restrict allowed candidates, or simply visualize the distinction. #### Behavior The component scans the Envelope from the outside inward. Any Grid Box that has fewer occupied neighbors than expected in the enabled Axes is considered a boundary box. With **Layers** set to `1`, only the outermost ring is flagged. With `2`, the two outermost rings are flagged, and so on. 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 Grid Boxes (or their resulting Slots) into boundary and interior groups, then apply different constraints to each. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Grid Boxes | `B` | Grid Box | List | All GridBox 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 Grid Box is on the boundary of the grid envelope. | #### 4.2.5 Deconstruct Grid Box *Envelope · Nickname: `DeconGridBox` · GUID: `6C600E1F-A033-4D65-88A8-D8D1EC438924`* Extract the center plane, diagonal dimensions and Axis intervals from a Grid Box. 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 Grid Box in the input tree into its geometric components. The output tree structure mirrors the input tree. Invalid Grid Boxes produce null outputs in their branch positions (with a component warning), preserving index alignment with the input. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Grid Box | `B` | Grid Box | Tree | A GridBox to deconstruct into its geometric components. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Center Plane | `P` | Plane | Tree | The plane at the center of the GridBox with the box's orientation. | | Diagonal | `D` | Vector | Tree | Vector representing the X, Y, and Z dimensions of the GridBox. | | 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 Grid Box, the component reports its integer position in the grid and which other boxes 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 Grid Box’s integer (column, row, layer) position within the grid, expressed as a Point. These are not world-space coordinates - they are grid indices. Index `(0, 0, 0)` is always the first box. The **Topology** output is a tree where branch `N` contains the indices of all Grid Boxes adjacent to box `N`. This is analogous to the Grasshopper **Proximity 3D** component, but works in discrete grid-step distances rather than Euclidean distance, and respects the actual cell connectivity of the Envelope rather than raw point clouds. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Grid Boxes | `B` | Grid Box | Tree | List of GridBox 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 GridBox, useful for visualization and indexing. | | Topology | `T` | Integer | Tree | Tree of integer neighbor indices: for each grid box (by index) the connected neighbor indices are listed. | #### 4.2.7 Neighbor Grid Boxes *Envelope · Nickname: `Neighbors` · GUID: `8BEADDF2-96B1-4D59-BD68-B8ED5F7377F3`* Find the Grid Boxes that are adjacent to a given set of focus cells. Given a list of focus indices, the component returns the indices of all Grid Boxes within the specified search distance (in grid steps) that are not themselves focus boxes. Useful for selecting “everything around” a particular sub-region of the Envelope. #### Behavior The search follows Grid Topology (not Euclidean distance), so a distance of `1` returns only directly adjacent cells, `2` includes those plus their neighbors, and so on. The focus cells themselves are excluded from the output. A typical workflow is to use the **Are Grid Boxes Boundary** component to get boundary indices, then feed those into Neighbor Grid Boxes 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 | | --- | --- | --- | --- | --- | | Grid Boxes | `B` | Grid Box | List | List of GridBox objects representing the grid to analyze. Provide a flat list (flattened) of boxes. | | Grid Box Indices | `I` | Integer | List | Focus Indices. Indices of grid boxes for which neighbor indices should be computed. Provide indices relative to the supplied Grid Boxes list. | | Layers | `L` | Integer | Item | Maximum search distance (in grid steps) to consider when collecting neighbor boxes. 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 grid boxes found for the supplied focus indices. | #### 4.2.8 Grid Boxes from Geometry *Envelope · Nickname: `GeoGrid` · GUID: `9B1DF19E-BB90-4AB6-A092-3B416ABCCB28`* Generate a set of Grid Boxes 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 component samples the input geometry at grid intersection points and creates a Grid Box for each cell that the geometry passes through or contains. The **Fill Method** controls whether only the surface skin is covered, only the interior volume, or both. The sampling works by testing candidate grid positions against the geometry. When a test point lies exactly on a geometric feature (such as on a surface edge or curve endpoint), the result can be ambiguous and that position may be skipped. The component reports these cases with a remark. If Grid Boxes are missing in expected positions, try slightly offsetting, scaling, or simplifying the geometry near those gaps. **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 grid boxes. Supported types: Points, Curves, (untrimmed) Surfaces, Breps, Meshes. | | Base Plane | `B` | Plane | Item | Grid space base plane. Defines orientation of the grid. | | Grid Box Diagonal | `D` | Vector | Item | Vector specifying the grid-box 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, Grid Boxes where the geometry only touches a face boundary without penetrating inside are excluded from the result. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Grid Boxes | `B` | Grid Box | List | Generated GridBox 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 6 individual Faces, one per output. #### Behavior Reads the Module’s internal Face array and outputs each Direction as a separate FaceId parameter. Returns error if Module is null or invalid. Also previews each Face directly on the input Module in the viewport, using the same rectangle/arrow/Direction-label glyph as Preview Faces, so the six outputs can be told apart by eye without wiring a separate preview component. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Module | `M` | Module | Item | A Module from which individual faces will be extracted. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | {GH_FaceIndex.XPositive} Face | `{GH_FaceIndex.XPositive}` | Face ID | Item | Positive X Face. Face ID representing the module face that faces the positive X direction. | | {GH_FaceIndex.XNegative} Face | `{GH_FaceIndex.XNegative}` | Face ID | Item | Negative X Face. Face ID representing the module face that faces the negative X direction. | | {GH_FaceIndex.YPositive} Face | `{GH_FaceIndex.YPositive}` | Face ID | Item | Positive Y Face. Face ID representing the module face that faces the positive Y direction. | | {GH_FaceIndex.YNegative} Face | `{GH_FaceIndex.YNegative}` | Face ID | Item | Negative Y Face. Face ID representing the module face that faces the negative Y direction. | | {GH_FaceIndex.ZPositive} Face | `{GH_FaceIndex.ZPositive}` | Face ID | Item | Positive Z Face. Face ID representing the module face that faces the positive Z direction. | | {GH_FaceIndex.ZNegative} Face | `{GH_FaceIndex.ZNegative}` | Face ID | Item | Negative Z Face. Face ID representing the module face that faces 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. Builds output trees parallel to the input Face tree. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Face or Module | `F` | Face ID | Tree | Face to analyze, obtained from the Module Faces component or other face-producing components. A Module wired here instead is expanded into all six of its faces. | | Modules | `M` | Module | Tree | Optional list of Module objects used to resolve face geometry and planes. If omitted, some geometric outputs will be unavailable. | | 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 Compares Module Name, Face Index and Direction. Same Axis = same or opposite; different Axis = skew. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Face | `F` | Face ID | Item | First FaceId to compare. Obtain faces from the 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 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 point containment against each Face of each Module. Warns if point matches zero Faces; remarks if point matches more than one. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | All available Module objects to sample when detecting faces. 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 and Connector coverage. Partitions every Module’s six Faces into used (referenced by at least one Rule or Connector) 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) and Connectors (Module + Face Index) into a set, then tests each of the six Faces of every input Module against that set. Both inputs are optional — when neither is provided all Faces are reported as unused. Output trees mirror the input Module tree structure with one grafted branch per Module. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | Tree | Modules whose 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 For each branch of the Modules input tree, builds a voxel fingerprint for every Face via bidirectional ray scanning. Fingerprints are grouped using structural equality on the voxel array. Faces with no occupied voxels are skipped. Output extends the input tree by one layer: each sub-branch contains Face IDs sharing the same geometry pattern. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | All Module objects to analyse. 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 cage 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 cage 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 rectangles at Face planes with Direction arrows colored by Axis (X = red, Y = green, Z = blue). Supports both viewport preview and baking to Rhino with grouped geometry. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Faces or Modules | `F` | Face ID | Tree | Faces to preview, obtained from the Module Faces component or other face-producing components. A Module wired here instead is expanded into all six of its faces. | | Modules | `M` | Module | Tree | Available Module objects 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 Modules and Rules, runs a full Audit, and packages everything into an Assembly consumed by the Solver. #### Behavior Construct Assembly replaces the manual wiring pattern of Module Rotations → Rule merging → Audit with a single component. It performs rotation expansion on all Modules with rotation flags set, 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 everything, optionally generates Indifferent Rules for uncovered Faces, and runs the same Audit logic as the standalone Audit component. 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 AllowIndifferent flag is now handled here instead of on the Solver. **Allowed Connector Pair deduplication.** Construct Assembly removes exact-duplicate Allowed Connector Pairs from the input list before generating any Rules. Cross-referencing Connector lists on the canvas easily produces exact duplicates, and the Rule generator is O(Modules² × Axes) per pair - deduplicating up front saves substantial computation on larger definitions. A single Remark reports how many duplicates were dropped. Note that `A→B` and `B→A` are kept as separate entries because the downstream Rule generator treats Direction as meaningful. **Allow-all Slot resolution.** Construct Assembly also resolves any allow-all Slots (Slots created without explicit Module Names) it receives. After rotation expansion has produced the final Module set, every allow-all Slot is filled with the deduplicated list of Module Names, assigned uniform Weights of `1.0`, and its Total Modules Count is set to match. Modules whose box dimensions do not match the Slot are filtered out, so mixed- dimension setups still produce dimension-consistent Slots. A single summary Remark reports how many Slots were resolved and to how many Modules - one Remark per Slot would flood the log for large allow-all Envelopes. **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 (directly actionable) and one for rotation variant Modules (informational - the Solver simply prunes a variant in the Directions it cannot cover). The coverage verdict is computed by the engine from the final Rule set, so enabling Indifference (which injects covering Rules) clears it. In addition, the output Discrete Assembly marks every such Face directly in the viewport with a red circle-and-cross badge on the Module preview, so the offending Faces can be found at a glance (most visible with Indifference disabled). **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 (the standalone Preview Rule component still colors arbitrary Rule lists by Face Axis). Authored Rules are drawn exactly as wired - including Disallowed Rules on a collapsed Envelope axis - because you made them; the Clean up input below is what removes the ones that can never apply. Authored per-mesh display colors of the source geometry are carried into the exported document as per-mesh materials, so a Module assembled from differently colored meshes keeps each color. **Clean up.** With the Clean up input enabled, Construct Assembly drops Rules that can never apply to the Slot layout and Modules left unplaceable by that pruning. A Rule is dead 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 neighbor 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, since the Solver can never place it. Clean up also runs constraint propagation, narrowing each Slot to the Modules that can actually occupy it. Everything is gated on the input: left off, every Rule and Module you wired is preserved. **Partial and invalid Assemblies.** Construct Assembly never suppresses its output. A recoverable build problem - required Terminators requested but none provided, or a literally empty Envelope (no Modules or no Slots) - no longer aborts the component. Instead it builds the most complete Assembly it can, reports each problem as its own Warning, and marks the Assembly invalid. An empty Envelope still emits an Assembly that carries whatever Modules and Slots were authored. The downstream WFC Solver refuses any Assembly whose IsValid flag is false, so an invalid Assembly is caught one component later with the problems already named on the canvas, rather than vanishing silently at construction time. **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 when enabled. 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 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 - the data exactly as supplied on Construct Assembly before rotation expansion and Rule generation. Rotation variants generated from Module symmetry flags are not included; use **Dissolve Assembly** to access the expanded Solver-ready state. 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 original Module Names list so order and Weights are preserved. All Audit outputs have moved to the Audit Assembly component. **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 Symmetry 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.3 Dissolve Assembly *Main · Nickname: `DisAssembly` · GUID: `AA68962C-3C09-4BF8-8E70-8CB0E4E129FD`* Extract the fully expanded solver-ready state from a Discrete Assembly: expanded modules (with rotation variants), expanded slots, and explicit + indifferent rules. 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, their rotation variants, and - when Require Terminators was enabled - the synthesized boundary module. | | Slots | `S` | Slot | List | All slots the solver runs on: authored slots and - when Require Terminators was enabled - the synthesized boundary slots wrapping the envelope. | | Explicit Rules | `R` | Rule | List | Manual, connector-generated, terminator-generated, and boundary self-adjacency rules (everything except indifference). | | Indifferent Rules | `IR` | Rule | List | Rules auto-generated for faces not covered by explicit rules. | #### 4.4.4 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 all inputs and checks whether the Envelope is already Deterministic (fully solved) or already Contradictory (cannot be solved at all). If so, it stops immediately without running any solve steps. The Solver then runs one single-threaded Attempt using the chosen Seed. This is a fast, cheap check: if the problem is easy it often finds a solution or reaches a Contradiction right away, with no extra overhead. The Solver spawns parallel workers on the remaining CPU cores - each with an incremented Seed, searching simultaneously - when that first Attempt does not produce a definitive result, or when *Return First* is off and every Attempt's solution is wanted (so an easy Envelope that solves on the first try still yields one Assembly per successful Seed). 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 continues until either every Slot has exactly one candidate (Deterministic - success) or some Slot has no candidates left (Contradictory - failure). A Contradictory result is retried with a different Seed. The Solver log (**Report** output) shows a summary of preprocessing, the number of Rules used (including any auto-generated Indifferent Rules), and the outcome of each Attempt. #### 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 Every Module has six Faces - one per Face. 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. The Solver generates these implicit pairings internally at solve time. 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. The **Slots Suitable for Solver** output remains `true` even when Faces are uncovered - it only turns `false` for hard errors such as an invalid grid, unknown Module Names, or Contradictory Slots. When Indifferent pairings are generated, the output log shows the total Rule count as *N (M Indifferent)*, so you can see exactly how many Rules were auto-generated versus explicitly authored. #### Limits The free tier caps the WFC Solver at a **limited number of runs per fixed time window** (windows are aligned to UTC clock boundaries); paid tiers (Annual, Edu, Lifetime) are unlimited. Each Solver invocation that produces a result counts as one run regardless of the *Max Attempts* setting. The component footer shows a live counter indicating how many runs remain and when the window resets. Regardless of tier, the Solver supports a maximum of **16,370 distinct Module Names** across all Slots. Each rotation variant of a Module counts as a separate name - a single Module design can produce up to 24 Orientation invariants (from 90° turns around X, Y and Z Axes), so the limit accommodates roughly 682 fully-expanded unique designs, or more if not every Module uses all three rotation Axes. Exceeding this Module-name limit causes the Solver to report an error and stop without producing results. #### 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. **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 | `A` | 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 | `A` | Integer | Item | Total attempts spent. | #### 4.4.5 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 groups findings into Grid, Slots, Modules, Rules and warning sections. 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. **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 Grid Boxes 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 Grid Box 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 Grid Box 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 Solver would crash. | | 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 Face Preview 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 Construct Assembly wrapped the envelope in a synthesized boundary layer. False means no boundary layer was added and 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. | #### 4.4.6 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 the same dimensions, all matching Variants are placed into the corresponding Slot. This is typically a configuration mistake and the **Modules With Identical Names and Dimensions** output of the **Audit Assembly** component will flag it. **Megamodules materialize once per placed instance.** Placed Megamodule sub-parts are grouped into instances and the Megamodule geometry is emitted exactly once per instance, sourced from the surviving part with the lowest part index (not hard-wired to `_p0`). Instances cut by an open Envelope boundary whose `_p0` cell lies outside the Envelope therefore still materialize from any surviving part, with the geometry overhanging the Envelope where the missing parts would sit. A Remark reports how many instances materialized from a non-primary part. The output tree path of a Megamodule instance is the representative part’s Slot index (unchanged for fully interior instances: that is `_p0`’s Slot). **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.7 Sample Geometry *Main · Nickname: `SampleGeo` · GUID: `13C40CD2-DBF4-4221-8AF9-6E15AD72AFD3`* Create Module candidates by sampling input geometry into GridBox 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 GridBox, chops geometry (curve–brep intersection, mesh splitting, brep trimming) in parallel. Voxelizes each Module and deduplicates by comparing voxel patterns and box dimensions (epsilon comparison). Creates a Slot for each box with the assigned Module Name and default Weight. **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 grid box (cage). Monoceros 3 considers two modules identical when their voxelized geometry and module cage 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 Boxes | `B` | Grid Box | List | Boxes used to chop and sample geometry when creating module candidates. Provide one box 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 a name, GridBox and optional geometry. Modules are the building blocks placed into Slots during WFC solving. Each Module has 6 Faces (one per Face). #### Behavior Validates name and GridBox. Removes invalid geometry items with warnings. Tracks geometry GUIDs. Reads the Rotational Freedom value (0 None, 1 X, 2 Y, 3 Z, 4 Full) and stores it on the Module. Construct Assembly expands this into actual rotational variants, remapping Rules and Face Indices accordingly. The expanded variants are available via Deconstruct Assembly. Warns if geometry extends significantly outside the Module's Grid Box. **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) | | Module Box | `B` | Grid Box | Item | Box that contains the Module geometry. | | Geometry | `G` | Geometry | List | Geometry used to materialize the Solver result. The Module geometry does not have to fit into the module cage 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, GridBox, geometry, validity flag and all Faces. #### Behavior Retrieves Module properties and outputs them as separate parameters. Generates a flat list of FaceId items for all 6 Directions. Warns if the Module is invalid. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Module | `M` | Module | Item | A Module to deconstruct. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Module Name | `MN` | Module Name | List | Module name (converted to lowercase). | | Module Box | `B` | Grid Box | List | Box containing the Module geometry. | | 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. | #### 4.5.3 Construct Megamodule *Module · Nickname: `Megamodule` · GUID: `8E932072-4093-4A34-8C86-C8E6DBF5CD3F`* Create a multi-cell Module from a name and a list of adjacent GridBoxes. Automatically creates one sub-Module per box (named `{name}_p0/N`, `{name}_p1/N`, etc. where N is the total part count), generates exclusive internal Rules, and identifies external (boundary) Faces. #### Behavior Every sub-Module receives the full Megamodule geometry so that Face suggestion components can analyze all external Faces. Materialize Assembly emits the geometry exactly once per placed instance, sourced from the surviving part with the lowest part index (for fully interior instances that is `_p0`), to avoid duplication. Display colors, render materials, layers, linetypes, lineweights, and print colors are captured from the referenced source objects, so the geometry previews and bakes with its original appearance. Each sub-Module previews its sibling Grid Boxes as dashed ghost outlines, making the full Megamodule footprint visible even when viewing a single part in isolation. Sub-Modules other than the geometry-carrying part draw no geometry of their own in the viewport - every part carries the full, world-coincident Megamodule geometry, so drawing it per part would overdraw the realistic preview N times. The cage, name, and dashed peer boxes still locate these parts. The Rotational Freedom value (0 None, 1 X, 2 Y, 3 Z, 4 Full) propagates to every sub-Module. When non-zero, the Solver automatically generates rotated variants of the entire Megamodule and remaps both internal and external Rules accordingly. Detects adjacency when Faces Face opposite Directions and anchor plane origins coincide (within epsilon). Creates internal Rules and tracks which Faces are used. Warns if multiple boxes provided but no adjacencies found. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Megamodule Name | `MN` | Module Name | Item | Base name for the megamodule. Sub-modules will be named "{name}_p0/N", "{name}_p1/N", etc. where N is the total part count. If not provided, a deterministic name is auto-generated from the component instance. (Optional) | | Grid Boxes | `B` | Grid Box | List | List of adjacent Grid Boxes that define the megamodule footprint. Each box becomes one sub-module. | | Geometry | `G` | Geometry | List | Geometry (optional). Geometry spanning the full megamodule. All geometry is given to every sub-module (for face analysis and preview); Materialize Assembly places it exactly once per placed megamodule instance. | | Rotational Freedom | `R` | Integer | Item | How much the Solver may rotate this Megamodule's sub-modules. 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 | | --- | --- | --- | --- | --- | | Modules | `M` | Module | Tree | Sub-module Module instances, one per input Grid Box. Each sub-module is in its own tree branch {i}. | | Internal Rules | `IR` | Rule | List | Exclusive adjacency Rules connecting adjacent sub-module faces. Feed these directly into the Solver along with your other rules. | | External Faces | `EF` | Face ID | Tree | Face IDs on the outer boundary of the megamodule, available for user-defined rules with other modules. Each sub-module's faces are in their own tree branch {i}. | #### 4.5.4 Deconstruct Megamodule *Module · Nickname: `DeconMegamodule` · GUID: `C5FE81D3-0821-46DA-BCC1-C1397AD58156`* Extract GridBoxes, internal Rules, and external Faces from a set of Megamodule sub-Modules. Inspection companion to Construct Megamodule. #### Behavior Reconstructs internal Rules by comparing all Module pairs for opposite-Direction Faces with coinciding anchor planes. Remaining Faces (not in internal Rules) are output as external Faces. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | Sub-module Module list as produced by Construct Megamodule. | **Outputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Grid Boxes | `B` | Grid Box | List | One Grid Box per sub-module, defining the megamodule footprint. | | Internal Rules | `IR` | Rule | List | Exclusive adjacency Rules connecting adjacent sub-module faces. | #### 4.5.5 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. Tracks index mapping and valence. If Rules provided, remaps source/target Module Names for culled Modules, creating a cartesian product of mapped names. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | Module variants to identify and remove duplicates. | | Voxel Resolution | `V` | Vector | Item | Number of voxels in each direction of the module grid box (cage). Monoceros 3 considers two modules identical when voxelized geometry and module cage 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.6 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. Creates rotated geometry in parallel. If deduplication is enabled, voxelizes the original Module once, then maps those voxels through each rotation's coordinate transform to produce a comparable voxel block per variant; compares patterns and box dimensions to keep only unique Modules. Variants are named with a canonical rotation suffix (e.g. `_x90`, `_z180`, `_x90y90`). **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Module | `M` | Module | Item | Module to rotate. A Module whose geometry and structure will be rotated. | | 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 grid box (cage). Monoceros 3 considers two modules identical when voxelized geometry and module cage 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. Creates a Rule for each valid pair. Deduplicates automatically and reports duplicate count. 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 Module Faces component. | | Target Faces | `TF` | Face ID | List | Target-side Faces for rule construction, obtained from the 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). **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 | All Module objects to analyse for face geometry matches. 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 all Module Faces. Uses Face-local bidirectional ray scanning to build voxel fingerprints, compares fingerprints across every Face pair 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. For every Face pair with compatible dimensions, compares voxel fingerprints; there is no opposing-direction filter, and non-opposing matches are connector-symmetry hints expanded by Construct Assembly. Outputs Rules for all pairs whose patterns are identical. 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, using the same colour coding as Preview Rule. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | All Module objects to voxelize and analyse 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 cage 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 cage 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 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 forgiving plane-distance tolerance, so they are picked up reliably anywhere within the Face area. Previews matched curves colored by Axis. Reports skipped curves. **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 Module objects to search for faces. 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. Iterates branches pairwise (branch N of sources with branch N of targets). For each branch pair, resolves point locations to Faces, then cross-references the resulting face lists. Every Face pair produces a Rule; non-opposing pairs are connector-symmetry hints that Construct Assembly expands into opposing-face Rules through rotation expansion. Points are matched with a forgiving plane-distance tolerance, so they are picked up reliably anywhere within the Face area. Warns about points that do not land on any face. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Modules | `M` | Module | List | All available Module objects to sample when detecting faces. 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 lines 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. Draws lines between Face anchor plane origins, colored by Direction Axis (X = red, Y = green, Z = blue). Supports viewport preview and baking to Rhino. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Rules | `R` | Rule | Tree | Rules to preview. | | Modules | `M` | Module | Tree | All existing Modules to display with rules. | ### 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 GridBox 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 will be resolved to the full final Module set by Construct Assembly. #### Behavior Validates branch matching between GridBoxes, 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 Grid Box 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 previews as white (“All allowed”) in the viewport 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 preview reflects that: the cage shows only the allowed-Module count — one number in the corner, no `/ total`. Once the Slots pass through Construct Assembly, `AllModulesCount` is stamped with the size of the final Module set and the preview switches to the familiar `allowed / total` form. No cross-component wiring is required to keep Entropy percentages consistent. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | Grid Box | `B` | Grid Box | Tree | Grid Box (grafted). Box that will become a Slot. Provide one box 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 Grid Box and allowed module names/weights. | #### 4.7.2 Deconstruct Slot *Slot · Nickname: `DeconSlot` · GUID: `9DC6F6DB-1209-4B30-ABC2-FC3BC8F6C929`* Extract a Slot’s components: GridBox, allowed Module Names, Weights, determinism flag, Contradiction flag and Entropy. #### Behavior Iterates through all Slots in the input tree. Calculates Entropy as `AllowedModulesCount / AllModulesCount`. Skips invalid Slots with an error message. 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 size of the final Module set 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 | | --- | --- | --- | --- | --- | | Grid Box | `B` | Grid Box | Tree | The grid box 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. The component runs each branch comparison in parallel. 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. Run after solving to get placement frequencies for fabrication estimates, bill-of-materials, or distribution analysis. #### 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. To get a full bill of materials, run Occurrence Count once per Module Name (e.g. via a loop or by grafting the Module Names list). The output is a single integer per invocation. **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 solved Slots output of the WFC Solver. | | 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. Iterates through all Envelope positions testing if the pattern fits (bounds, dimension matching, Module Name inclusion). Returns indices for each match found. **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 — takes a flat list of Modules and a tree of solved Slots as separate inputs. #### 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. Baking produces shared Rhino block definitions — one per unique Module — with one instance per Slot placement. **Inputs** | Name | Nickname | Type | Access | Description | | --- | --- | --- | --- | --- | | All Modules | `M` | Module | List | All Modules (flat list). Complete list of all Modules, including rotation variants generated by the solver. Connect the All Modules output from the WFC Solver directly to this input. | | Solved Slots | `S` | Slot | Tree | Slots resolved by the solver. Each branch contains one complete solution when Return First is false. Connect the Solver's Slots output directly here. | | 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`* Generate Slots by applying a Rule to Module placements. Useful for creating assemblies that follow explicit Face constraints and for visualizing Rule effects. #### Behavior Finds source and target Modules matching the Rule Faces. Matches compatible pairs by comparing Face dimensions (within epsilon). Creates Deterministic Slots and placed Modules by transforming Module boxes to align with the pivot plane. 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 to position the source and target faces referenced by the Rule. Defaults to World XY. | | Modules | `M` | Module | List | Collection of Module objects used to find compatible module pairings for the Rule. | **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 Megamodule construction](#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) 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 Grid Boxes From Geometry to convert it into Grid Boxes. 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 the Solver's output Assembly (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 Grid Box: 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 the result to the relevant Module’s Weight input on Construct Slot. 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 distinct Module Names** across all Slots. Each rotation variant counts as a separate name, so the limit applies to the expanded set after applying the Module Rotations component. #### Concept Every Monoceros definition needs five things before it can run: - A **Module** - the design element the Solver places, carrying optional geometry and six Faces (+X, −X, +Y, −Y, +Z, −Z). - **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 Grid Boxes with a grid component and then convert each Grid Box 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 and returns solved Slots. The Solver assigns exactly one Module to every Slot in the Envelope. It does not leave cells unoccupied or skip areas where no Module fits well: it either fills everything completely, or it reports a Contradiction and returns no result. - 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 Grid Boxes and Envelope Slots always have identical dimensions, which the Solver requires. 1. **Create a Module Grid Box** - 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 Grid Box. 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, 0.5, 0.5}` to `{-9, 0.5, 0.5}`. 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 Grid Box 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 Grid Box here is identical in size to the Module's Grid Box. 1. **Construct Slots** - Use Construct Slot with all Grid Boxes 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. Enable Indifference if you want uncovered Faces to pair freely. The component packages everything into a Discrete Assembly. 1. **Solve** - Connect the Assembly to the WFC Solver. The Solver runs Wave Function Collapse and returns an output Assembly with solved Slot states. 1. **Materialize** - Connect the Solver's **Assembly** 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 enable rotations around whichever Axes make sense. The component produces counter-named variants (`pipe-0`, `pipe-1`, `pipe-2`, … and `L-pipe-0`, `L-pipe-1`, …) and already includes the identity rotation, so you replace the original Module output with the rotation output everywhere downstream. 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. Remember to apply the disallow Rule to all rotation variants, not just the base Module. 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 a practical escape hatch when the Solver cannot find any solution at all. 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 reports a Contradiction or returns no result. Adding an empty Module loosens the constraint space - where a forced adjacency would otherwise be impossible, the Solver can now place a void instead. Many setups that appear stuck become solvable as soon as an empty Module is included. The trade-off is explicit: the resulting layout will contain gaps, so the solution is structurally different from what a fully-packed arrangement would produce. Whether that is acceptable depends on the design intent, but it is often preferable to no solution at all. #### 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. One effective solution for the pipe example: add a surrounding layer of boundary Slots and fill it with empty Modules, but allow those empty Modules only to connect to the non-pipe-end Faces of the pipe Modules - the “sides” that carry the Indifferent or neutral Face type, not the pipe-opening Face. Because no pipe-end Face is allowed to Face the boundary layer, the Solver is forced to route every pipe opening inward. The result is a self-contained loop topology: every pipe end must connect to another pipe end within the Envelope, and nothing leaks out. 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. 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**. The Solver reads Modules, Slots, and Rules from the Assembly directly. 1. After solving, use **Materialize Assembly** to extract placed geometry from the solved Assembly. Alternatively, use **Deconstruct Assembly** to extract Modules, Slots, and Rules for further processing. The WFC Solver takes a Discrete Assembly as its sole 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 **Connector From Face** (formerly Construct Connector). Give each Connector a name (e.g. `"pipe_end"`, `"flat_wall"`), set its symmetry flags, and specify the Module and Face it belongs to. 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 automatically by comparing Module Face geometry. These components take one Connector as a prototype and assign matching Connectors to all Modules with matching Face content. 1. Declare which Connector names can connect using **Construct Connector Pair**. It takes single items: Source Connector + Target Connector produces one Connector Pair. Use Grasshopper’s Cross Reference to make multiple pairs. `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 **Deconstruct Assembly** to extract the merged Rule set, or **Audit Assembly** to inspect diagnostic outputs and verify that both Connector-generated and explicit Rules are present. - 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 Grid Box alone, so the Module is spatially valid with no geometry. 1. Add `empty` to the Allowed Modules list for Construct Slot, alongside all other Module Names that Slot may contain. Every Slot in the Envelope must list every Module permitted to occupy it. 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, remove `empty` from the Allowed Modules 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 Grid Box, 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 Grid Box. 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 Grid Box 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 Grid Boxes 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 Grid Box 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 Grid Box 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 Grid Box 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 Grid Box 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. You can scope the search by providing specific Faces to the **Source Faces** and **Target Faces** inputs. When left empty, all Faces in the Module list are compared against each other. Limiting the inputs is useful when you have a large Module set and only want to test compatibility between a known subset of Faces. 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), extracts a layer of voxels at the Face up to a configurable **Depth** (default 1 voxel layer), and groups Faces whose voxel fingerprints match. Higher resolution gives finer discrimination but increases computation time; increasing depth captures more of the Module body behind each Face, which helps distinguish Faces that look identical at the Face plane alone. 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 operates 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 Solver does not care about the order in which Modules appear in the list. Every Slot must explicitly list which Modules it allows - there is no global “allow all” shortcut, so you need to supply the full Module list (or the equivalent Module Name strings) to every Slot that should accept any Module. #### 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. Supply the merged Module list to the Allowed Modules input of Construct Slot so every Slot accepts every Module. Modules cast to Module Names automatically, so you can wire the Module list directly into Construct Slot without extracting names first - or, if you need the name strings elsewhere, use Deconstruct Module to get them explicitly. 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, Grid Boxes, 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. Pass the flattened Module list directly to Construct Slot (Modules cast to Module Names automatically), or extract the name strings with Deconstruct Module if you need them separately. 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. Enable rotations around the Axes that make sense for your design. For the pipe example, all three Axes produce valid variants. 1. The output is a flat list of Modules. Module Rotations assigns each variant a counter suffix: `pipe-0` (identity, 0°), `pipe-1`, `pipe-2`, `pipe-3`, and so on. Because the identity rotation is already included as `pipe-0`, this list replaces the original Module everywhere downstream - do not include both. 1. Enable **Cull Duplicates** to remove variants that are geometrically identical after rotation (for example, a symmetric straight pipe produces only two unique orientations along each Axis rather than four). #### Generating Rules for all variants Once Module Rotations produces its output, the variants are fully independent Modules. `pipe-0`, `pipe-1`, `pipe-2` and the rest have no shared identity in the Solver - they are treated exactly like any other unrelated Module in the library. The Solver has no knowledge that they were derived from the same base design; it only knows the Rules you supply. This means Rules must be defined between the individual variant names, not against the original `pipe` base name, and must also cover pairs *between* different variants (so the Solver can transition from one Orientation to another in the result). Use any of the methods in [2.8](#defining-rules) to define Rules for the variant list. The Module Rotations output is a flat list of ordinary Modules, so every workflow in [2.8](#defining-rules) applies without modification. #### Wiring the solver 1. Pass the full Module Rotations output list (all variants) to the Modules input of Construct Assembly. 1. Pass the same list to Construct Slot as the Allowed Modules input so every Slot permits every variant. The Slot accepts Module objects directly and casts them to names automatically. 1. Feed the Modules, Slots, and Rules into Construct Assembly, then connect the Assembly to the WFC Solver. Connect the Solver's **Assembly** output to Materialize Assembly - the Assembly includes all rotation variants, so Materialize Assembly will place each variant at the correct position and Orientation. #### Common mistake Defining Rules against the original base Module Name (`pipe`) rather than the rotation variant names (`pipe-0`, `pipe-1`, etc.) causes the Solver to contradict immediately: the Slots list variant names but no Rule references them. Always define Rules using the Module Rotations output list, not the original Module. ### 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 Grid Boxes 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. #### Modules with the same name - dimensional invariants Two or more Modules may share the same name as long as they have different bounding-box dimensions. They are called **invariants** (or *dimensional variants*) of the same Module type. The WFC Solver treats them as a single logical type: Rules defined for a name apply to every invariant of that name simultaneously. You define Rules once and all size variants obey them. Materialize then selects the correct invariant at placement time by matching the Slot's box dimensions to the Module's box dimensions. A Slot that is 2 m wide receives the 2 m-wide invariant; a 1 m-wide Slot receives the 1 m-wide invariant. The Solver itself never sees dimensions - only names. #### The invariant design contract Sharing a name asserts that all invariants are **functionally equivalent** from the Solver's perspective. This is a strong commitment: the Solver will allow any invariant wherever the name is allowed, without regard for size. If the equivalence assumption is violated, the Solver produces answers that look valid by name but are physically incorrect. **Example 1 - T-junction on the long variant only.** Suppose you name both a short pipe (1 m) and a long pipe (2 m) the same thing: `pipe`. The long pipe has enough room along its body to accommodate a T-shaped branch, so you define a Rule that allows another pipe to connect to it in the Y Direction. The short pipe has no such branch - its geometry simply does not support a Y connection. But the Solver does not know this. It sees the name `pipe` and applies all Rules for that name to every invariant. It will happily place the short pipe in a Slot that requires a Y-Direction neighbour, producing a result that looks correct in the solution tree but has no physical Face at that Face. The error is invisible until Materialize places the geometry or you inspect the result manually. **Example 2 - mismatched Face profiles across sizes.** Suppose Module A has a long variant (2 m in X) and a short variant (1 m in X), and Module B similarly has long and short variants. You define a Rule that allows A to connect to B on the +X Face. The Solver reads this as: wherever A appears, B may be adjacent in +X - regardless of actual size. If the long A + long B pairing is geometrically correct but the short A + short B pairing is not (because the short variants have different Face profiles that do not align), the Solver will still place short A next to short B. Again, the error is silent. The Rule of thumb: name two Modules the same only when every size combination that the Solver could produce is genuinely valid in your design. If any size variant needs a different Rule set - whether because it has extra Faces, fewer Faces, or different Face geometry - it must have its own name. #### 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 Module variant 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 required Module variants. 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 Modules for **every distinct cell size**. Materialize matches geometry to Slots by comparing box dimensions; any Slot whose assigned Module has no variant with matching dimensions will be left visually empty. The Solver emits a **Warning** before running if it detects name-dimension mismatches, and Materialize emits a Warning for any path where geometry could not be placed. Use the Audit Assembly component’s **Slots Without Fitting Modules** output to identify every affected Slot and resolve all issues before running the Solver. ### 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 2.0 in the same Slot. 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 2.0 reduce a Module's likelihood relative to the others; 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. 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 **Weights** input. 1. Connect a list of floats to **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. This is called a parallel list - two separate lists where position determines the correspondence. For example, if the **Module Names** input receives [`wall`, `corridor`, `room`] (three items, indices 0-2), then the **Weights** input must also carry three items in the same order: index 0 is the Weight for `wall`, index 1 for `corridor`, index 2 for `room`. A Weight list of [`1.0`, `3.0`, `1.0`] makes `corridor` three times more likely than the others at this Slot. The simplest way to build this on the canvas is with a Panel feeding a Construct Slot, with a second Panel next to it for the Weights - one line per entry in each panel, in the same top-to-bottom order. 1. If the Weight list is shorter than the Module list, the last value is repeated for the remaining Modules. This is a convenient shortcut when all remaining Modules should share the same Weight - for example, a list of [`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 the Weights input 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 Grid Box list you would otherwise pass to Construct Slot. 1. For each Grid Box, 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 Grid Box, each branch containing one float per Module in the same order as the Module list for that Slot. 1. Connect that tree to the **Weights** input of 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 Grid Boxes using Homogeneous Grid or Heterogeneous Grid. 1. Use Add Boundary Layer to generate an additional layer of Grid Boxes around the Envelope. Control which Directions get boundary layers (X±, Y±, Z±) and how many layers deep. 1. Define a boundary Module (typically with no geometry, named e.g. `boundary`). 1. Create Slots from the boundary Grid Boxes, 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 Grid Boxes Boundary to identify which Grid Boxes are on which Face. - Create multiple boundary layers with increasing depth. ### 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 **Grid Boxes From Geometry** to derive the Slot positions automatically rather than placing Grid Boxes by hand. The component maps any Rhino geometry onto a regular grid and returns one Grid Box for each occupied cell. Three coverage modes let you match the geometry type: *surface wrap* covers only the cells a surface or curve passes through, which is the right choice for façades, shells, and curve networks; *volume infill* fills only the enclosed interior without an outer skin layer; and *surface wrap and infill* (the default) captures both together, suiting closed solids where you want a complete volumetric Envelope. For full input details and supported geometry types, see [§ 4.2.8 Grid Boxes 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 Grid Boxes 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 (Fill Method 0), 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 Grid Boxes From Geometry with Fill Method 0 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 the new boundary boxes to create the thickened Envelope, or use the combined output directly if Add Boundary Layer returns all boxes. 1. Proceed with Construct Slot on the combined box list. Remember that the extra boundary boxes 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 Fill Method 2 (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 boxes 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 Grid Boxes 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 Grid Box, 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 Grid Boxes where you want to fix a Module. 1. Create Slots from those Grid Boxes, allowing only the desired Module Name. 1. Create Slots for the remaining Grid Boxes, 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 Megamodule at a specific position To place a [Megamodule](#megamodules) at a known position in the Envelope, you only need to fix one of its individual Modules. Restrict a single Slot to that Module Name and leave the surrounding Slots open with all Module Names allowed. The adjacency Rules between the Megamodule's parts force the neighbouring Slots to resolve deterministically - the Solver has no valid alternative for them - so the full Megamodule emerges without explicitly constraining every position it occupies. The only requirement is that the Megamodule 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 parts 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 - **Megamodule integrity** - The Internal Rules produced by Construct Megamodule already define exactly which sub-Module Faces must connect to each other. Feeding them as Exclusive Rules locks those internal Faces against anything else, preventing automatic suggesters from accidentally pairing a sub-Module Face with an unrelated Module. See [2.18 Megamodule construction](#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 Megamodule construction Megamodules group adjacent cells - a 2×1×1 structural bay, a tall atrium, any element too large for a single cell - into a single design unit. Because one Module can only occupy one cell, Construct Megamodule generates a set of coordinated sub-Modules and the Rules that lock them together, maintaining the WFC constraint model. Sub-Modules are named `{name}_p0/N`, `{name}_p1/N`, etc. where N is the total part count. Each sub-Module previews the sibling Grid Boxes as dashed ghost outlines so the full Megamodule footprint is visible even when viewing a single part in isolation. #### Steps 1. Select a group of adjacent Grid Boxes for the Megamodule. 1. Connect them to Construct Megamodule with a name and optional geometry. 1. The output has three parts: - **Modules** - one sub-Module per Grid Box, output as a data tree (one branch per part, flattened by default). Add these to your Module list alongside all other Modules. - **Internal Rules** - exclusive adjacency Rules that lock the sub-Modules together. Add these to your Rule list. - **External Faces** - the outer boundary Faces available for connecting the Megamodule to other Modules, output as a data tree (one branch per part, flattened by default). Use only these when running Detect Rules from Geometry or constructing external Rules. 1. When generating Rules for the rest of your Module set, pass only the **External Faces** to any automatic Rule suggester - never the full Face list of the sub-Modules. Internal Faces must not appear in any Rule other than the Internal Rules. #### Geometry handling Every sub-Module carries the full Megamodule geometry so that Face suggestion components can analyze all external Faces regardless of which sub-Module they belong to. Only the first sub-Module (`_p0`) materializes the geometry - the Materialize component skips geometry output for the remaining parts to avoid duplication. #### Auto-rotation Set **Rotational Freedom** (0 None, 1 X, 2 Y, 3 Z, 4 Full) on the Construct Megamodule component to have the Construct Assembly automatically generate rotated variants of the entire Megamodule. Construct Assembly applies the same rotation to all sub-Modules and remaps both internal and external Rules accordingly. Rotated variants are named with a canonical rotation suffix (e.g. `_z90`, `_z180`, `_x90y90`). #### Protecting internal Rules with Exclusive Rules The Internal Rules are already correct and complete - the component generates them exclusively. The risk is human error: accidentally running a suggester on all sub-Module Faces instead of only the external ones, or merging Rule lists carelessly, can introduce Rules that reference internal Faces and allow sub-Modules to separate from each other. The safest approach is to treat Internal Rules as a hard constraint. Connect them to the **Exclusive Rules** input of Construct Assembly. This removes any other Rule that references an internal Face - regardless of how it got there - and preserves the exact pairings the Megamodule requires. See [2.17 Rule Exclusivity](#rule-exclusivity). ### 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** - The solved Discrete Assembly (from the WFC Solver’s Assembly 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. Both outputs share the same data tree structure: `{solutionBranch; moduleIndex; slotIndex}`. The *solutionBranch* is inherited from the input Slots path (single solutions start at `{0}`); *moduleIndex* is the position of the Module in your input list; *slotIndex* is the Slot's position within that branch. In the common single-solution case, placements of Module 2 would sit at `{0;2;0}`, `{0;2;1}`, and so on. Flattening the tree gives a plain list of all 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 cage. 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. No geometry is placed and an error is reported. Contradictory Slots mean the solve itself was unsuccessful; they should not appear in well-constrained setups. - **Slot is Non-deterministic** - The Slot still holds more than one possible Module (Solver was not run, or Return Mode was set to return partial results). Materialize skips those Slots and reports a warning. - **Module Name in Slot not found in the Module list** - The Solver assigned a Module that is not present in the list you fed to Materialize. The Slot is skipped and an error is reported. This typically means the Module list is incomplete or the wrong list is wired in. - **Multiple Modules with the same name** - This is intentional and supported: Modules with the same name but different dimensions serve as variants (e.g. rectangular versions of the same element). When a Slot is resolved, Materialize tries every variant and places the one whose box dimensions match the Slot. If more than one variant fits, all matching ones are placed and a remark is added. - **Multiple Modules with the same name and the same dimensions** - This is an error. Materialize cannot distinguish between them and stops with an error message identifying the duplicate. - **Module exists but its dimensions do not match the Slot** - If no variant fits the Slot dimensions, the Slot is left empty and a warning is issued. If at least one variant fits but others do not, the non-fitting ones are silently skipped and a remark is added only to flag that variants were evaluated. - **Module has no geometry** - Materialize still records the placement and outputs a Transform, but nothing is added to the Geometry output. A remark notes the empty placement. This is normal for Empty Modules, which intentionally carry no geometry. #### 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 four configuration parameters - Seed, Attempt count, Observation limit, and return mode - tune the trade-off between speed, reliability, and result variation. Adjust them when the Solver is too slow for larger grids, when solutions consistently fail, or when results lack variety. | 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** | varies | 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** | false | If true, stops as soon as the first valid solution is found and returns it. If false, runs all Attempts and returns all successful results. | 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 that produced each successful solution. 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.13](./faq.html#faq-13). There are no good or bad Seeds - all Seeds are equivalent; see [FAQ 1.43](./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 the Solver's output Assembly (from the WFC Solver, after solving) to separate Audit Assembly instances. Comparing the two Audit reports reveals what the Solver changed and where constraints were violated. The **Report** output gives a human-readable summary; the individual data outputs give machine-readable lists you can wire into other components for targeted fixes. 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 (names not in the Module list); Slots whose candidate Modules all have mismatched box dimensions (those Slots will remain empty after Materialize even if the Solver assigns them); and Slots where a Deterministic assignment would produce duplicate geometry because multiple invariants share the same name and dimensions. - **Modules** - reports the full invariant structure per Module name; counts how many Slots each Module is admitted to; flags Module invariants that appear in no Slot, in no Rule, or whose Faces are completely absent from all Rules; lists Modules with and without geometry, mixed-geometry invariant groups, and identical-name-and-dimension duplicates; identifies Modules that only connect to themselves (self-loop-only), which is usually unintentional. - **Rules and Faces** - flags Rules that reference unknown or unused Module Names; reports per-Rule occurrence counts 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. Three viewport tools make Rule connections and Face Directions visible, so misconfigurations are immediately apparent. - Preview Rule - Draws Rule connections in the viewport as lines between Module Faces, with color coding by Axis. - Face Preview - Draws Face geometry, anchor planes, and Direction arrows for selected Modules. - 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 flags preserved. ### 2.23 Grid topology analysis Grid Boxes are geometrically placed in 3D space, but the Solver works with a discrete grid of integer coordinates. Grid Topology and Neighbor Grid Boxes 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 Grid Boxes and outputs two things: - **Relative Coordinates** - one integer-coordinate point per Grid Box, normalised so the minimum corner of the bounding grid is at the origin. Index-matched to the input list: box 0 → coordinate 0. - **Topology** - a data tree of neighbour indices. Branch `{i}` contains the indices of all Grid Boxes adjacent to box `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 Grid Box list, you can use the X, Y, Z components of each coordinate directly as sorting or filtering keys. For example: decompose the coordinates, extract the Z value, remap it to a 0-1 Weight range with Remap Numbers, and wire the result into the Slot Weights to create a vertical density gradient without any geometry intersection. #### Neighbor Grid Boxes Takes Grid Boxes, a set of **Focus Indices** (the boxes you care about), a **Layers** count (search radius in grid steps), and per-Axis toggles (**X / Y / Z**). Returns **Neighbor Indices**: the flat list of all Grid Box indices that are within the specified layer count of any focus box. This is an expand-by-layers operation. Provide any starting indices - the result of a coordinate filter, an explicit list of hand-picked positions, or indices produced by another query - set Layers to the desired expansion radius, and the output is every Grid Box within that many steps of the starting set. Use the result to construct a separate Slot list for that region, with a different Module palette, Weight, or determinism setting. For purely vertical adjacency, disable X and Y and leave only Z enabled. #### Workflow: inward weight gradient 1. Build the Grid Box 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. Wire the remapped values as Slot Weights. Slots near one edge get low Weights; Slots near the other get high Weights. The Solver will favour heavier Slots when placing Modules. #### 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 leaving the rest of the grid locked. 1. After the first solve, take the solved Slots from the WFC Solver output. Each is fully Deterministic: exactly one Module Name is allowed. 1. Use Grid Topology on the original Grid Box list to get Relative Coordinates. Filter by coordinate value to identify the region to reopen - all boxes at a specific Z level, an X-range column, or any other positional criterion. 1. Pass the selected indices into Neighbor Grid Boxes 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. For the expanded selection, construct new open Slots from those Grid Boxes with the full (or broader) allowed Module list - Non-deterministic. 1. For every other Grid Box, keep the solved Slots as-is. Those locked Slots act as fixed constraints during the next solve. 1. Merge the open Slots with the locked Slots and re-solve. The Solver fills the reopened region while treating every locked Slot as a hard constraint. 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 Grid Boxes Boundary provides the boundary indices directly. For most Envelope tasks - capping pipe ends, enforcing wall Faces at the grid edge - Add Boundary Layer (see [2.13](#boundary-layer)) is the more direct tool. ### 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 the six Faces of a Module as separate named outputs (`+X`, `−X`, `+Y`, `−Y`, `+Z`, `−Z`). Merge the equivalent output from each Module to build a role list manually. This approach gives fine control but requires tracking which output index corresponds to which Face for every Module in the library, which becomes error-prone as the Module count grows. Rule From Curve is usually the better starting point. ### 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. Apply **Module Rotations** with RotateZ = true to generate all orientations of each tile automatically, 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 Grid Boxes do not need to fill a convex volume; the Solver handles any contiguous irregular shape. #### From geometry (recommended) Grid Boxes From Geometry generates Grid Boxes that follow an input shape directly. Set the Fill Method to **fill volume** (method 1) or **surface wrap and fill volume** (method 2) for solid shapes such as Breps and meshes. This produces a contiguous, gap-free set of Grid Boxes with no manual filtering needed. #### From a filtered rectangular grid Alternatively, create a full rectangular grid with Homogeneous Grid and remove unwanted boxes using Grasshopper list operations (Cull Pattern, Dispatch, or membership testing against a Brep). This approach gives more direct control over which cells to include. Be aware of one important edge case: if filtering produces two or more disconnected clusters of Grid Boxes and you then add a boundary layer to the whole set, the layer expands each cluster outward. When clusters are adjacent, these expansions can land on the same grid coordinate, creating two Grid Boxes at the same position - an invalid grid that causes the Solver to fail. To avoid this, either keep the filtered set contiguous (one connected region), or add the boundary layer before filtering and adjust your filter to keep only the inner cells you need. #### Boundary handling Removing Grid Boxes 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 Grid Boxes. 1. Create a separate Module grid: a Homogeneous Grid with the same diagonal vector and counts of 1×1×1. Use that single Grid Box 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 usual setups, every Slot is constructed with the same Allowed Modules list - the complete set of all Module Names. 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 different Allowed Modules list when constructing the Slots for each region. The Solver then picks only from the permitted vocabulary for each Slot. #### Steps 1. Create the full Grid Box set. 1. Partition Grid Boxes 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 Grid Boxes 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. Run it once per Module you want to inspect, or loop it across all Module Names. #### Steps 1. Solve the grid and obtain the solved Slots. 1. Connect one Module Name and the solved Slots to **Occurrence Count**. The component outputs a single integer: the number of Slots that resolved to that Module. 1. Repeat for each Module Name you care about, or use a Grasshopper loop to process all names at once. 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. #### 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 the **Audit Assembly** component. Audit Assembly takes a single Assembly input and exposes more than 30 diagnostic outputs covering the Envelope, Slots, Modules, Rules, and Faces. #### 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. 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 box dimensions. These Slots will appear empty after Materialize Assembly even if the Solver fills them. - **Module Never In Slot** - Modules 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 Face Preview 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 some Slots remain visually empty, check **Slots Without Fitting Modules**. ### 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 the Solver's output Assembly to **Deconstruct Assembly**. This gives you the partially solved Slots (some Deterministic, some still with multiple candidates), along with the expanded Modules and Rules. 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, every Module Face that can sit against the Envelope boundary must carry a 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 automatically. #### Workflow 1. **Identify Unused Faces.** Place a **Used Faces** component. Connect your **Modules** to its M input, your **Rules** to R, and (if applicable) your **Connectors** to C. The **Unused Faces** (UF) output lists every Face that has no Rule or Connector 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**. Construct Assembly will now accept every Indifferent Face at the boundary without errors. #### 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.5 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 (megamodule 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** and the rest of the v3 pipeline as you would in any native v3 definition. #### 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. Rotation flags default to off so behaviour is preserved — flip them on individual Modules in v3 if you want rotation variants. v2's display attributes (per-piece colours, layers, linetypes) have no v1/v2 equivalent and are filled with sentinel values; bake uses default appearance. - **Megamodules (v1 only).** Decomposed into v3 *Megamodule* sub-Modules named `_p/`. The first sub-Module materialises geometry; the rest are ghost outlines. The converter also emits exclusive Rules between touching sub-Module pairs so v3's Construct Assembly reproduces the original v1 shape. - **Explicit Rules.** 1:1 cast from both v1 and v2. Megamodule v1 Rule indices are split into `(part, face)` using the Modules input and re-targeted to the matching sub-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.** v1 reserved both `Out` and `Empty`; v3 reserves only `Out`. Modules named `Empty` lose their special meaning and become ordinary Modules. v3 expresses "this Slot is intentionally empty" through allow-all Slots and indifference, which is a different model. #### After converting The converted definition runs in v3 with v1/v2 behaviour. To take advantage of v3 features, layer them on top: - Flip rotation flags on individual Modules to opt into 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 Megamodule construction](#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? Yes. WFC assigns exactly one Module to every Slot before it finishes. There is no concept of a partially solved Envelope or a cell left unassigned: the Solver either produces a complete assignment covering all Slots, or it reports a Contradiction and returns no result. Every cell will be occupied by some Module. This surprises users who expect the Solver 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 “World state 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 Grid Box) 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 Grid Box Module geometry should not touch the Faces of the Grid Box. 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 Suggest Rules, 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.6 Working with multiple Modules](workflows.html#multiple-modules), [2.27 Restricting Modules to specific regions](workflows.html#restrict-modules), [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 Grid Box?](#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 Slice Geometry to convert Rhino geometry into Grid Boxes. - 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.10 Boundary handling](workflows.html#boundary), [2.4 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 Grid Boxes around your Envelope. 1. Create Slots from those Grid Boxes, 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.10 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 Grid Box 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 contains reserved characters (`:`, `->`, newlines). - The Module Box is degenerate (zero volume, negative dimensions). 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.8 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.27 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.9 Design exploration (seed surfing)](./workflows.html#seed-surfing). ### 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.17 Solver settings](workflows.html#solver-settings), [2.17 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 Disallow Rules input references a rotatable Module, all rotation-variant combinations are automatically removed. If this strips every Rule for the Module, the Solver reports “Some Slots have no Solver-compatible Modules” before it can run. **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 on construction**), 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.28 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.23 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.15](./workflows.html#custom-materialization)). See also workflows and FAQs: [2.11 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 Grid Box and Face Rules. Curved geometry can extend beyond the Grid Box, be smaller than the Grid Box, 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.11 Constructing Slots from geometry](workflows.html#constructing-slots). ### 1.19 How do I create L-shaped or T-shaped Modules? Modules occupy exactly one grid cell. For shapes spanning multiple cells, use a Megamodule - a group of adjacent cells that acts as a single design element. The Construct Megamodule component creates coordinated sub-Modules (named `{name}_p0/N`, `{name}_p1/N`, etc.), internal Rules, and external Faces. It also supports auto-rotation via a Rotational Freedom value (0 None, 1 X, 2 Y, 3 Z, 4 Full) so the Solver can generate rotated variants of the entire Megamodule. See example [2.15 Megamodule construction](./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 Grid Box 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 Grid Box 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 Grid Box 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 Grid Box?](faq.html#faq-44). ### 1.21 Are block instances supported? The Materialize component outputs both geometry and transform data. You can use the transforms to place block instances in Rhino instead of duplicating geometry, which is more memory-efficient for large assemblies. See example [2.15 Custom materialization with transforms](./workflows.html#custom-materialization). ### 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.29 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 component** is rate-limited to 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). Each Solver invocation counts as one run regardless of the *Max Attempts* setting - the algorithm may retry or backtrack internally, but it is still one run from the license point of view. The WFC Solver component also has a 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 your run budget. Connect a Boolean Toggle or a Button component. The component footer on the WFC Solver shows a live counter indicating how many runs remain in the current window and when the window resets. When the cap is hit, the footer turns red and the next available run is announced in the component message. Separately, the Solver has a hard limit of **16,370 distinct Module Names** on every tier. Each rotation variant counts as a separate name. With up to 24 Orientation invariants per Module (from 90° turns around X, Y and Z Axes), the practical limit is roughly 682 unique Module designs if every Module uses all three rotation Axes. The Solver reports an error if this 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.28 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 Grid Box 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.12 Fixing Modules in the Envelope](workflows.html#fixing-modules), [2.28 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 size of the final Module set. 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 final total, 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 both the Module list and the count with the final Module set automatically. 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 [RotateZ = true, CullDuplicates = true]. The component generates all valid orientations. All variants share the same Module Name, so Rules defined for the base Module apply automatically to all rotations. 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.49](faq.html#faq-mirror) for why. See also workflows and FAQs: [2.7 Automatic Module rotations](workflows.html#module-rotations), [4.5.6 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 [RotateZ = true, CullDuplicates = true] to derive all directional variants - each variant receives a unique counter-based name (`tile-0`, `tile-1`, …), so Rules must be defined across the full variant list 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.23 2D grids (floor layouts and panel systems)](workflows.html#2d-grids), [2.7 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** - not just low - prevents the Solver from voluntarily choosing the Module in those Slots. See also workflows and FAQs: [2.9 Weighted Module placement](workflows.html#weighted-slots), [2.21 Rules from Face Groups](workflows.html#shared-face-groups), [2.27 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.23 2D grids (floor layouts and panel systems)](workflows.html#2d-grids). ### 1.36 What is entropy and how does it affect solving? Entropy is the number of allowed Modules in a Slot, expressed as a ratio between 0.0 (no Modules, Contradictory) and 1.0 (all Modules allowed). During solving, the WFC algorithm always observes (collapses) the Slot with the **lowest Entropy** first - the Slot with the fewest options. 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.17 Solver settings](workflows.html#solver-settings), [2.17 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. The “random” part is only the tie-breaking during Observation: when multiple Slots have the same (lowest) Entropy, the Solver uses the Seed to choose which one to collapse first. All constraint Propagation after that choice is fully Deterministic. Results are more structured than truly random arrangements - the Solver always makes the most constrained decision first and only uses randomness to break ties. 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.17 Solver settings](workflows.html#solver-settings), [2.17 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.9 Weighted Module placement](workflows.html#weighted-slots), [2.27 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.5 Defining Rules from Faces](workflows.html#defining-rules). ### 1.41 Why do Slot Grid Boxes in the viewport look smaller than the actual cell size? This is intentional. Adjacent Slots share Faces: if every Slot Grid Box 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.17 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 the Solver and Materialize Assembly warn about 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 the Module's box dimensions to the Slot's dimensions. If they differ, placement is skipped and the Slot remains empty in the geometry output - with no indication of which Slots were affected other than the Module count in the warning. To surface this earlier, the Solver now emits a Warning before the solve runs if any Slot allows a Module Name that exists in the Module list but has no variant with dimensions matching the Slot. The warning lists the mismatched Module Names. Materialize Assembly also now emits a Warning (previously only a Remark) when it skips geometry placement due to a dimension mismatch. Use the Audit Assembly component's **Slots Without Fitting Modules** output to identify every affected Slot precisely before the solve. See also workflows and FAQs: [2.8 Heterogeneous grid](workflows.html#heterogeneous), [2.25 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.13 Why does the same seed give different results after I change something?](#faq-13) ### 1.45 Can Module geometry protrude outside the Grid Box? Yes, and Monoceros fully supports this. Module geometry is never clipped or constrained to the Grid Box boundary - it can extend freely in any Direction. The Solver only uses the Grid Box 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 a maximum of **16,370 distinct Module Names**. Every rotation variant counts as a separate Module Name toward this limit. The Module Rotations component can generate up to **24 orientations** per Module (90° turns around the X, Y, and Z Axes). This means the practical limit is roughly **682 unique Module designs** if every Module uses all three rotation Axes, or significantly more if your Modules only rotate around one or two Axes. 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 Grid Box - 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. #### Confusing Monoceros with growth algorithms There are several amazing tools in the Grasshopper ecosystem that add pieces one by one to a growing aggregate, expanding outward from a Seed. Monoceros works differently: it fills a pre-defined Envelope all at once using WFC. The Envelope is fixed before solving starts, and every Slot gets a Module assignment. Different tools, different paradigms, different outputs. Growth-like behavior can be simulated with Monoceros (e.g. progressive solving on expanding partial Envelopes, or using masks to reveal regions incrementally), but the underlying mechanism is always constraint Propagation over a fixed grid, not incremental aggregation. 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 has four tiers. **Free** includes full functionality on Envelopes of any size, with the WFC Solver rate-limited 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. All tiers use the same plugin. The tier is determined by your Gumroad purchase - enter the email you used on Gumroad in the License dialog and the plugin validates it automatically. 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 plugin may optionally ask you to register with your email - this is for product updates only and can be skipped. 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 runs rate-limited 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? Monoceros 3 may show an optional registration dialog inviting you to enter your email for product updates and news. Registration is not required to use the Free tier - you can dismiss the dialog and continue working. If you choose to register, claim the free Monoceros product on Gumroad and enter the same email in the plugin. We do not share your email with third parties. 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.6 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.54 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. - Terminator-to-boundary Rules (generated when *Require Terminators* is on) do **not** count as covering a Face - a Module Face that only has 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 indifference generates more than 10,000 Rules. 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 replaces the empty list with the full deduplicated final Module set (including rotation variants that fit the Slot dimensions), assigns uniform Weights of `1.0` to each Module, and sets the Total Modules Count accordingly. 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 cages 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 your Modules and a Point3d marking a location on a Module Face; the component returns one Connector per matching Face using the provided Name, Rotation and Symmetry flags. It is a shortcut for Faces from Point → Connector from Face, sharing the same matching logic as Faces From Point and the same validation as Connector From Face - one component instead of two. 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, the Assembly will contradict. 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.6 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 will still build and emit an Assembly, but the Audit Assembly component will list the uncovered Directions in the **Terminator Faces Missing** output and flag them as a Warning. During solving, any Slot on the Envelope boundary whose only remaining Module candidate has a Face pointing outward with no Terminator will hit a Contradiction. To prevent this, supply at least one Terminator for every Face Direction (±X, ±Y, ±Z) — six Directions total. The quickest way is to wire a single Module that Faces all six Directions directly into Construct Terminator, which expands it to all six Faces at once. **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.4 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 the Assembly contradict. 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.6 Terminator from Point](index.html#comp-terminatorfrompoint). ### 1.64 What does “Some modules lack adjacency rules on non-flat axes” mean? Before calling the native Solver, the WFC Solver component checks that every Module appears as both a “low” (negative-Direction) and “high” (positive-Direction) neighbour in at least one adjacency Rule on every non-flat Axis. A Module that is missing from one side of the Rules on an Axis can never be placed next to anything in that Direction, which guarantees a Contradiction. The error message lists the specific Modules and the Axis/Direction where coverage is missing (up to 10 entries). Common causes: - A Module was added to the library but no Rules were defined for one or more of its Face Directions. - Require Terminators is on and the grid is flat in one dimension, but the Rules were authored assuming a 3D grid. Because flat Axes (extent = 1) are automatically skipped, Rules on those Axes are inert — but Rules on the remaining non-flat Axes must still provide full coverage. - Module Rotations generated rotation variants but the Rule set only covers the original Orientation. To fix the error, ensure every Module has at least one Rule connecting it on each non-flat Axis in both Directions. The Audit Assembly component’s **Report** output lists orphaned Faces, which often overlap with the gaps reported here. See also: [4.4.3 WFC Solver](index.html#comp-solver), [4.4.4 Audit Assembly](index.html#comp-audit), [1.60 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 v3** component on a new *Monoceros 3* ribbon tab inside the legacy plug-in. Drop it onto your existing definition, wire the legacy **Modules**, **Rules**, and **Slots** 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 the *Monoceros 3* ribbon tab — 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 (Modules, Rules, Slots) but the outputs differ: - **From v1**: five outputs — Modules, Rules, Connectors, Connector Pairs, Slots. 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 — Modules, Rules, Slots. v2 has no typed Rules and no Connector concept, so no Connector or Connector Pair outputs are emitted. v1 megamodules are decomposed into v3 Megamodule sub-Modules with auto-generated exclusive Rules between touching sub-Module pairs — the original shape is preserved without any extra wiring. v2 Modules are 1:1 single-cell and need no decomposition. ### 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 yellow remark recommending you migrate the definition with the **Convert to v3** 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 yellow remarks — they do not affect the solve. ### 1.70 What does the converter NOT produce? The converter emits the *ingredients* — Modules, Rules, Slots, 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 12 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). ## 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.4 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.6 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 - a zero Weight still allows the Solver to use the Module if no other option remains during Propagation. See also workflows and FAQs: [2.9 Weighted Module placement](workflows.html#weighted-slots), [2.27 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.5 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 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.19 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.16 Materializing results](workflows.html#materializing), [2.29 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.17 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 adds a layer of boundary Slots around the Envelope, guaranteeing that every edge cell has correct Face-to-Face adjacency with the boundary. Run it after constructing your Envelope to prevent Solver failures caused by missing or ambiguous neighbors. See also workflows and FAQs: [2.11 Constructing Slots from geometry](workflows.html#constructing-slots), [2.24 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 The Materialize output is a data tree with branch paths `{module index, slot index}`. To work with specific Module types after materialization: - Use **Tree Branch** to extract all placements of a specific Module (by its index in the input Module list). - Use **Flatten** to get all geometry in a single list. - Use **Graft** if you need per-Slot isolation. See also workflows and FAQs: [2.16 Materializing results](workflows.html#materializing). ### 2.12 Disable the Solver while editing The WFC Solver recomputes every time its inputs change. While editing Modules, Rules, or the Envelope, right-click the Solver component and select **Disable** (or use Grasshopper's **Lock Solver**) to prevent unnecessary recomputation. Re-enable when you are ready to see results. 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.22 Proto-results workflow](workflows.html#proto-results). ### 2.14 Use Sample Geometry to verify Module content The Sample Geometry component lets you check what geometry intersects a specific Grid Box. Use it to verify that your geometry lands in the correct cells, especially when Module geometry extends across cell boundaries or when using complex imported geometry. See also workflows and FAQs: [2.11 Constructing Slots from geometry](workflows.html#constructing-slots), [2.1 Bare minimum](workflows.html#bare-minimum), [4.4.7 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.17 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 [RotateZ = true, CullDuplicates = true]. 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.49](faq.html#faq-mirror) for why automatic mirroring is not supported. See also workflows and FAQs: [2.7 Automatic Module rotations](workflows.html#module-rotations), [4.5.6 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.27 Restricting Modules to specific regions](workflows.html#region-restriction), [2.12 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.5 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 size of the final Module set — 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.5 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. Multiple attempts with incrementing seeds run automatically when the first attempt ends in a Contradiction. **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. **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** — An opaque bundle containing expanded Modules, Slots, merged Rules, and audit results. Produced by Construct Assembly and consumed 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. The WFC solver always 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 Grid Boxes becomes an Envelope once the Grid Boxes are converted to Slots. **Face** — One of the six faces of a Module, identified by a FaceId (e.g. module_name:+X). Rules specify which Face pairs may touch. Every Module has exactly six Faces, one in each direction: +X, −X, +Y, −Y, +Z, −Z. **Face Index** — An integer 0–5 identifying one face direction of a Module or Grid Box: ±X = 0/1, ±Y = 2/3, ±Z = 4/5. **Grid Box** — The basic spatial cell unit in a Monoceros grid — a box-shaped region that defines the size and position of one cell. **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. **Megamodule** — A Module that spans multiple adjacent Grid Boxes, treated as a single design element by the WFC solver. Sub-modules are named {name}_p0/N, {name}_p1/N, etc. All parts carry the full geometry for face analysis; the geometry materializes once per placed instance. Supports auto-rotation (Rotate X/Y/Z) — the Solver expands all parts together. **Module** — A named design element that carries geometry and six Faces. During WFC solving each Slot starts with all allowed Modules as candidates. **Module Name** — A lowercase string identifying a Module type. Multiple Modules sharing the same name are treated as Variants of the same type. **Module Rotations** — Generate rotated variants of a Module around each enabled axis (90°, 180°, 270°), producing up to 24 unique orientations. **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 and assigned a single Module, chosen at random weighted by Module 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 Grid Box 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 during WFC Observation. 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. **WFC Solver** — The main Monoceros 3 component. Given a Discrete Assembly it runs the Wave Function Collapse algorithm and returns an output Assembly with solved Slot states, along with diagnostics and statistics.