# Monoceros 3 -- Complete LLM Reference Monoceros 3 is a discrete assembly plug-in for Grasshopper / Rhino by Ján Pernecký. 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. ================================================================================ 1. KEY CONCEPTS ================================================================================ ## Key Concepts - **Module**: The fundamental design element. Carries a name, optional geometry, and a Grid Box defining its cell size and pivot. Each Module has exactly six Faces (one per face: +X, -X, +Y, -Y, +Z, -Z). Modules are placed into Slots during solving. When geometry comes from referenced Rhino document objects, the Module captures each piece's display color, layer name, and linetype name at construction time. These are preserved through rotations and baking. - **Slot**: One cell in the Envelope. Holds the list of Module Names allowed to occupy it and a per-Module weight for each candidate. States: deterministic (1 allowed Module), non-deterministic (2+ allowed), contradictory (0 allowed). - **Rule**: Defines an allowed adjacency between two Module Faces. Holds a source Face and a target Face facing opposite directions (e.g. wall:+X -> corner:-X). Rules are bidirectional for equality. Each adjacency only needs to be defined once. - **Face**: Uniquely identifies a specific face on a specific Module. Combines a Module Name and a Face Index (0-5 mapping to +X, +Y, +Z, -X, -Y, -Z). Opposite faces always have indices that sum to 5. - **Envelope**: A collection of Slots forming the spatial domain to be solved. Created by converting Grid Boxes into Slots via the Construct Slot component. - **Grid Box**: The basic spatial cell unit -- a box-shaped region defining the size and position of one cell. Multiple Grid Boxes form a grid. Grid Boxes carry only geometry (size and position); Slots additionally carry allowed Module lists and weights. - **Connector**: A named, rotation-aware interface binding for a specific Module Face. Combines an interface type name, four symmetry flags {0°, 90°, 180°, 270°}, a FaceId, and an in-plane Rotation. Emulates physical connectors (USB-A, USB-C, 3.5mm jack). Connectors generate Rules automatically when two opposing Faces carry compatible Connectors with matching rotations. When Modules are rotated inside Construct Assembly, Connectors are automatically transformed (face direction permuted, rotation shifted). String format: `"connectorName#rot-A@moduleName:faceIndex"` (e.g. `"corridor#rot-0@pipe:+X"`). Deconstructs into connector name, rotation, module name, and face id. - **Connector Pair**: Declares that two Connector types (by name) can connect across opposing Faces. Bidirectional: ConnectorPair(A, B) also allows ConnectorPair(B, A). String format: `"connectorname -> connectorname"`. - **Terminator**: A boundary-facing marker placed on a specific Module Face. Declares that the marked face is allowed to sit against the envelope boundary when Require Terminators is enabled on Construct Assembly. Carries no name, rotation, or symmetry -- only which module face is boundary-compatible. Created with Construct Terminator (from a Face or Module) or Terminator from Point (from a viewport click). When Require Terminators is on, Construct Assembly synthesizes a one-cell-thick boundary layer and generates adjacency Rules connecting each terminated face to the boundary; module faces without a Terminator cannot touch the outer edge. String format: `"Terminator@moduleName:faceIndex"` (e.g. `"Terminator@pipe:+X"`). - **Assembly (Discrete Assembly)**: An opaque bundle containing expanded Modules (with rotation variants), Slots, merged Rules (allowed + connector-generated − disallowed − exclusive-filtered + exclusive + indifference), and audit results. Produced by Construct Assembly and consumed by the WFC Solver. Replaces the manual Module Rotations → rule merging → audit wiring pattern. ================================================================================ 2. WORKFLOW ================================================================================ ## Workflow A typical Monoceros 3 workflow follows these steps: ### Assembly workflow (recommended) 1. **Define a Grid** -- Use Homogeneous Grid or Heterogeneous Grid to create Grid Boxes. 2. **Construct Modules** -- Define each Module with a name, Grid Box, and optional geometry. Set rotation flags (Rotate X/Y/Z) for automatic rotation variant expansion. 3. **Define Rules** -- Create Rules manually (from Face pairs, Module pairs, curves, or geometry suggestion). Optionally use Connectors for rotation-aware rule generation. 4. **Define Connectors (optional)** -- Create Connectors with Connector from Face (formerly Construct Connector), Connector from Point (point-tag authoring), or Suggest Connectors from Geometry/Voxels, then declare compatibilities with Construct Connector Pair. 5. **Construct Slots (Envelope)** -- Create Slots from Grid Boxes, optionally with allowed Module Names and weights. Leaving Allowed Module Names unconnected creates allow-all Slots, resolved to the full final Module set by Construct Assembly. 6. **Add Boundary** -- Optionally add boundary Grid Boxes, convert to boundary Slots, and merge. 7. **Construct Assembly** -- Feed Modules, Slots, Rules, Connectors, Connector Pairs, and optionally Terminators into Construct Assembly. This component expands rotation variants, transforms connectors, generates connector-based rules, removes disallowed rules, enforces exclusive rules, applies indifference for uncovered faces, deduplicates, runs audit, and packages everything into a Discrete Assembly. To disallow specific adjacencies, connect Rules or Connector Pairs to the Disallowed inputs. To make certain Faces exclusive (removing all other Rules for those Faces), connect Rules or Connector Pairs to the Exclusive inputs. Enable Require Terminators to enforce boundary coverage -- every Module Face that may touch the outer edge must have a Terminator. 8. **Solve** -- Connect the Assembly to the WFC Solver. Assembly is the first and only data input. Returns an output Assembly with solved Slot states. 9. **Materialize** -- Use Materialize Assembly to extract placed geometry from the solved Assembly, Slot to Module to place Module geometry into solved Slots directly, or use Deconstruct Assembly to access Modules, Slots, and Rules. ================================================================================ ## 3. STRATEGIES ### 3.1 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. - Define the Envelope shape in Rhino (a Brep, Mesh, or Surface). - Use Grid Boxes from Geometry to convert it into Grid Boxes. - Choose cell dimensions that suit the design intent. - Design Modules to fit the cell size. - Define Rules and solve. **Best for:** Site-specific projects, spatial planning, filling existing volumes. ### 3.2 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. - Design Module geometry in Rhino at a specific scale. - Construct Modules with Construct Module. - Generate rotations with Module Rotations. - Suggest Rules with Suggest Rules from Voxels. - Create a simple grid matching the Module dimensions. - Solve and iterate on the Rules. **Best for:** Product design, pattern generation, fabrication-driven projects. ### 3.3 Iterative refinement 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. - Start with a rough Module set and simple Rules. - Solve on a small grid (e.g. 5×5×5). - Analyze the result: are Modules distributed well? Are there undesired adjacencies? - Adjust Rules: add disallowed pairs, modify weights, add boundary constraints. - Scale up the Envelope gradually. **Best for:** Exploratory design, prototyping, complex Rule sets. ### 3.4 Multi-scale approach 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. - Solve a coarse grid first (large cells, few Modules representing zones: assembly, logistics, residential, etc.). - Use the coarse result as input for a finer grid: each coarse Module defines the Module palette for the corresponding fine-grid region. - 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. ### 3.5 Debugging strategy When Audit alone does not reveal the cause of consistent solver failures, isolate the problem by progressively reducing complexity until the failure can be attributed to a specific Rule, Module, or constraint. - **Audit**- Run the Audit Assembly component and read the report. Fix all reported errors. - **Visualize Rules**- Use Preview Rule to check that all intended adjacencies are defined. - **Check Faces**- Use Analyze Face to verify Face directions and use patterns. - **Start small, but not too small**- Reduce the grid, but keep it large enough to accommodate the rigid structures your exclusive Rules imply. A very small envelope (2×2×2 or 3×3×3) can be unsolvable even with perfectly correct Rules, because exclusive Rules may force structures that simply do not fit a tiny space. Shrink until failures become rare but not until the envelope is so constrained that it cannot host the minimum required pattern. - **Add empty**- Add an “empty” Module that connects to everything. If this solves, gradually restrict its Rules to isolate the problem. - **Check exclusivity**- Use the Exclusive Rules input on Construct Assembly to enforce that certain Faces only connect in explicitly defined ways. - **Inspect weights**- Extreme weight ratios can force contradictions. Try uniform weights first. ### 3.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. - **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. - **Face design**- If physical Faces exist (tabs, Slots, magnets), model them as part of the Module geometry. Use Suggest Rules from Geometry or Suggest Rules from Voxels 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. - **Scale and tolerance**- Set cell dimensions to match your real-world unit. Account for material thickness and assembly tolerances. - **Solve and count**- Use**Occurrence Count**to produce a bill of materials. Verify that each Module type is fabricable in the required quantity. - **Export**- See example1.29 Exporting results for fabrication. **Best for:** Pavilion design, discrete furniture, modular construction, prototyping. ### 3.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. ### 3.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 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 - 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. **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. **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 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). ================================================================================ 4. DATA TYPES ================================================================================ ## 4.1 Grid Box A Grid Box is the basic spatial cell unit -- a box-shaped region that defines the size and position of one cell. Multiple Grid Boxes form a grid; when converted to Slots via Construct Slot, they form an Envelope. Properties: - Box geometry (center, dimensions, orientation) Validity: Non-degenerate box (all three dimensions > 0). Casting: From Box -> Grid Box (wrapped directly) From BoundingBox -> Grid Box (wrapped directly) From Rectangle -> Grid Box (extruded by one unit in Z) From Brep -> Grid Box (bounding box of Brep) To Box -> Rhino Box geometry To Brep -> Closed Brep surface To Point -> Center point To Plane -> Center plane with orientation To Vector -> Diagonal vector (X, Y, Z dimensions) String: "Grid Box at (0,0,0) 1x1x1" Baking: Bakes as Rhino Box geometry. -------------------------------------------------------------------------------- ## 4.2 Module The fundamental design element. Carries a name, optional geometry, and a Grid Box defining its cell size and pivot. Each Module has exactly six Faces -- one per face: +X (0), +Y (1), +Z (2), -X (3), -Y (4), -Z (5). Properties: - Module Name: Unique lowercase string. Names lowercased automatically. - Grid Box: Bounding box defining cell size and pivot. - Geometry: Optional Rhino geometry (curves, surfaces, Breps, meshes). - Geometry Colors: Per-geometry-piece display color captured from the source Rhino object at construction time. Empty when geometry comes from Grasshopper (not from a Rhino document object). - Geometry Layer Names: Per-geometry-piece layer name captured from the source Rhino object. Null when not applicable. - Geometry Linetype Names: Per-geometry-piece linetype name captured from the source Rhino object. Null when not applicable. - Faces: Six Faces, one per face. - Rotation flags: Record whether the Module was transformed. Validity: Name is non-empty and Grid Box is valid. Modules with no geometry are valid. Casting: To Module Name -> Extracts name To Slot -> Single-Module Slot at Module's Grid Box position To Grid Box -> Extracts bounding Grid Box To Point -> Center point of Grid Box To Plane -> Center plane of Grid Box To Vector -> Diagonal vector of Grid Box Baking: Group containing geometry, Face rectangles, and Face text dots. Geometry pieces with stored colors are baked with ColorFromObject. Pieces with stored layer names are placed on the corresponding layer (created if absent). Pieces with stored linetype names are baked with the matching linetype. The original Rhino objects do not need to exist at bake time - attributes are captured eagerly at construction. -------------------------------------------------------------------------------- ## 4.3 Module Name A lowercase string identifier for a Module type. Input strings converted to lowercase automatically. Two Modules with the same name are treated as variants (invariants) of the same type -- useful in Heterogeneous Grids. Rules apply to all invariants simultaneously. A Rule defined for a name covers every size variant. Validity: Non-empty, free of reserved characters (:, ->, @, #, newlines). Spaces allowed. Casting: From String -> Lowercased as Module Name From Module -> Extracts name From Integer -> Converted to string From Number -> Converted to string From Data Path -> Converted to string To String -> Returns name string -------------------------------------------------------------------------------- ## 4.4 Face Index Integer 0-5 identifying one of six face directions: 0 = +X (opposite: -X = 3) 1 = +Y (opposite: -Y = 4) 2 = +Z (opposite: -Z = 5) 3 = -X (opposite: +X = 0) 4 = -Y (opposite: +Y = 1) 5 = -Z (opposite: +Z = 2) Opposite faces always sum to 5. Casting: From Integer -> 0-5 map directly From Number -> Truncated to integer 0-5 From String -> Accepts "0" or "+X", "-Y" etc. To Integer -> Returns numeric index -------------------------------------------------------------------------------- ## 4.5 Face (UID) Uniquely identifies a specific face on a specific Module. Combines a Module Name and a Face Index. String format: "modulename:+X" or "modulename:3" In Rules: "wall:+X -> corner:-X" Validity: Module Name non-empty, Face Index 0-5. Casting: From String -> Parses "modulename:faceindex" format To String -> Returns FaceId string -------------------------------------------------------------------------------- ## 4.6 Slot One cell in the Envelope. Holds the list of allowed Module Names and per-Module weights. The WFC solver progressively reduces this list until each Slot holds exactly one Module (deterministic) or none (contradictory). Properties: - Box: Cell bounding box - Allowed Module Names: List of Module Names - Module Weights: One Number per allowed Module (default 1.0) - Total Modules Count: Optional, for entropy color gradient - Allows All: True when the Slot was created without explicit Module Names (empty allowed list AND zero Total Modules Count). Allow-all Slots are unresolved, not contradictory: Construct Assembly replaces the empty list with the full final Module set during assembly construction. States: Contradictory (0 allowed, Total > 0) -> Red preview Deterministic (1 allowed) -> Green preview Non-deterministic (2+ allowed) -> Black-to-white gradient Allow-all (0 allowed, Total = 0) -> White preview with label "All". Unresolved; filled by Construct Assembly. Casting: To Grid Box -> Returns cell's Grid Box To Box -> Rhino Box geometry To Brep -> Closed Brep surface To Point -> Center point To Plane -> Center plane To Vector -> Diagonal vector String: "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. Baking: Colored boxes reflecting state. -------------------------------------------------------------------------------- ## 4.7 Rule Defines an allowed adjacency between two Module Faces. Holds source and target FaceIds facing opposite directions. Rules are bidirectional: "wall:+X -> corner:-X" equals "corner:-X -> wall:+X". Each adjacency only needs to be defined once. Validity: Both FaceIds valid, directions must be opposite, referenced Module Names must exist. String: "modulea:+X -> moduleb:-X" Casting: From String -> Parses "module:index -> module:index" format ## 4.8 Connector A named, rotation-aware interface binding for a specific Module Face. Combines an interface type name, four symmetry flags, a Module Name, a Face Direction, and an in-plane Rotation. Connectors emulate physical connectors with rotational symmetry. References Modules by name, not by pointer. Automatically transformed when Modules are rotated inside Construct Assembly (face direction permuted, rotation shifted). The Connector data type does not support viewport preview or baking on its own - preview is provided by the producing component (Construct Connector, Suggest Connectors from Geometry/Voxels). Properties: - Name: Interface type name (case-insensitive, no :, ->, @, or #; spaces allowed) - Symmetry Flags: Four booleans {0°, 90°, 180°, 270°} indicating self-identical rotations - Module Name: Target Module name - Face Direction: +X, -X, +Y, -Y, +Z, or -Z - Rotation: In-plane rotation (0, 90, 180, or 270 degrees) Examples (symmetry flags): {true, false, false, false} - Asymmetric (like USB-A). Only one orientation works. {true, false, true, false} - Half-turn symmetric (like USB-C). Works at 0° and 180°. {true, true, true, true} - Rotationally invariant (like 3.5mm jack). All orientations work. Validity: Name must not be empty or contain reserved characters (:, ->, @, #, newlines). At least one symmetry flag must be true. Module Name valid, Face Direction valid, Rotation is 0/90/180/270. String: "connectorName#rot-A@moduleName:faceIndex" (e.g. "corridor#rot-0@pipe:+X") Deconstructs into: connector name, rotation, module name, face id. Casting: From String -> Parses connector name, rotation, module name, and face direction ## 4.9 Connector Pair Declares that two Connector types (by name) can connect across opposing Faces. Bidirectional: ConnectorPair(A, B) also allows ConnectorPair(B, A). Stores two name strings: SourceConnectorName and TargetConnectorName. String: "connectorname -> connectorname" (e.g. "corridor -> corridor") Casting: From String -> Parses source and target connector names ## 4.10 Terminator A boundary-facing marker placed on a specific Module Face. Declares that the marked face is allowed to sit against the envelope boundary when Require Terminators is enabled on Construct Assembly. Fields: ModuleName (string), FaceIndex (integer, 0-based, canonical +X/+Y/+Z/-X/-Y/-Z order: 0=+X, 1=+Y, 2=+Z, 3=-X, 4=-Y, 5=-Z). Terminators carry no name, rotation, or symmetry -- they record only that a given module face is boundary-compatible. When Require Terminators is on, Construct Assembly synthesizes a one-cell-thick boundary layer around the envelope and generates adjacency Rules connecting each terminated face to the corresponding boundary module face. Module faces without a Terminator are forbidden from touching the boundary layer. On flat axes (grid extent == 1), Construct Assembly automatically skips boundary wrapping, Terminator Rules, and self-adjacency -- Terminators are not needed for flat-axis faces. Rotation translation: Construct Assembly walks every rotation variant it spawns; each Terminator on an authored Module also emits a Terminator on the variant with the face index mapped through the variant's FacePermutation, so all rotation variants are boundary-compatible wherever their source is. Serialization: FormatVersion = 1. String: "Terminator@moduleName:faceIndex" (e.g. "Terminator@pipe:+X") Casting: From Module -> Produces six Terminators, one per face (wildcard expansion) From Face -> Produces one Terminator for the given FaceId ## 4.11 Assembly (Discrete Assembly) An opaque container holding expanded Modules (with rotation variants), Slots, merged Rules (manual + connector-generated + indifference), and audit results. Produced by Construct Assembly and consumed by the WFC Solver. Users do not construct it manually. Not castable from string. Serialized with all contained Modules, Slots, and Rules for file persistence. ================================================================================ ================================================================================ 5. COMPONENT REFERENCE ================================================================================ ## 5.1 MAIN ### 5.1.1 Audit Assembly Nickname: Audit Category: Main Exposure: quarternary Analyze a Discrete Assembly for consistency and errors. Reports missing Modules, unfitting Modules, unused Faces and other issues. Run before the Solver. Allow-all Slot handling: flags any Slots that are still in the allow-all state as an Error (Construct Assembly should resolve them before Audit runs, so an unresolved Slot indicates the pipeline was bypassed). Slots that were originally allow-all but have since been resolved are reported as an informational Remark of the form "N Slots were originally allow-all and have been resolved to the full Module set." Inputs: Assembly (A) | Assembly | Item | Discrete Assembly to analyze Audit accepts Assemblies that fail IsValid (e.g. those with uncovered faces + indifference off) so broken setups can still be inspected. The Report output prefixes the full diagnostic with a note when IsValid is false. Outputs: Report (R) | Text | Item | Human-readable audit report grouping every finding into Grid/Slots/Modules/Rules/warning sections. Slots Suitable for Solver (SlotsInGrid) | Boolean | Item | True if Slot Grid Boxes form a complete axis-aligned 3D lattice the Solver can consume (uniform spacing, no gaps/overlaps/duplicates). Grid Block Dimensions (GridDim) | Vector | Item | Integer Slot count along each world axis, as a Vector {X, Y, Z}. Is Grid Homogeneous (HomoGrid) | Boolean | Item | True when every Slot has identical box dimensions (heterogeneous grids are still supported). Slots Accommodating All Modules (SlotsAccommodatingAll) | Integer | List | Indices of Slots whose allowed Module name list is a superset of every unique Module Name in the Assembly. Includes original allow-all Slots and Slots manually wired with the full list. Slots Without Geometry (SlotsWithoutGeo) | Integer | List | Indices of Slots whose allowed Module list contains only geometry-less Modules (empty/void placeholders). Slots Accommodating Unknown Modules (SlotsUnknownMod) | Integer | List | Indices of Slots referencing Module names not present in the Assembly (typo or missed merge). Slots Without Fitting Modules (SlotsUnfittingMod) | Integer | List | Indices of Slots with no Module Variant whose box dimensions match; cannot be filled, will contradict. Slots With More Fitting Modules (SlotsMoreMod) | Integer | List | Indices of deterministic Slots with multiple same-name same-dimension Variants (all materialize). Domain Of Module Weights (Weights) | Interval | Item | Interval {min, max} of all per-Module weights across all Slots. Module Names (ModName) | ModuleName | List | Deduplicated, alphabetically sorted Module names; the index ordering used by every per-name tree below. Module Variants (ModVar) | Integer | Tree | Per-name tree of Module Variant indices sharing the same name. Variants are Modules with the same Name but different box dimensions; rotation does NOT produce Variants (rotated Modules get new names). Module Name Count In Slots (ModNamInSlots) | Integer | Tree | Per-name single integer: Slots whose allowed list contains that name. Module Variant Count In Slots (ModInSlots) | Integer | Tree | Per-Variant single integer: Slots in which the Variant is actually allowed (after dimension filtering). Module Variant Never In Slots (ModNotInSlots) | Integer | List | Indices of Module Variants not allowed in any Slot (dead weight, candidates for removal). Module Variant Never In Rules (ModNotInRules) | Integer | List | Indices of Module Variants not referenced by any adjacency Rule (no valid neighbors). Module Variant Face Never In Rules (ModFaceNotInRules) | Integer | List | Indices of Module Variants that have at least one Face unreferenced by any Rule. Modules With Geometry (ModWithGeo) | Integer | Tree | Per-name tree of Variant indices carrying geometry. Modules Without Geometry (ModWithoutGeo) | Integer | Tree | Per-name tree of Variant indices without geometry (valid for voids). Modules With And Without Geometry (ModWithWithoutGeo) | Integer | Tree | Per-name tree non-empty only where some Variants carry geometry and others do not (almost always a mistake). Modules With Identical Names and Dimensions (ModIdentDim) | Integer | Tree | Per-name tree of Variant indices with both same name AND same box dimensions (true duplicates). Modules Allowed in Unfitting Slots (ModInUnfitSlots) | Integer | Tree | Per-name tree of Slot indices that allow the name but have no matching-dimension Variant. Module Faces (ModFace) | Face | Tree | Per-Variant tree: the 6 FaceIds of each Variant in canonical +X, +Y, +Z, -X, -Y, -Z order. Module Faces Use Count (ModFaceUse) | Integer | Tree | Per-Variant parallel integers: number of Rules referencing each face; zero means unreferenced. Rules Referring Unknown Modules (RulUnknownMod) | Integer | List | Indices of Rules whose source/target Module name is not declared in the Assembly. Rules Referring Unused Modules (RulUnusedMod) | Integer | List | Indices of Rules whose Module name is declared but not allowed in any Slot (dead weight). Rule Occurrence Count (RulCount) | Integer | List | Parallel to the Rule list: number of times each Rule occurs before deduplication. Is Rule's First Occurrence (RulFirst) | Boolean | List | Parallel boolean mask: true only for the first occurrence of each distinct Rule (use with Cull Pattern). Modules With Only Self-Connecting Rules (ModSelfOnly) | Integer | List | Indices of Variants whose only Rules pair them with themselves (isolated clusters). Potential Over-Constraint (OverConstrained) | Boolean | Item | True when at least one placed Variant has a Face not referenced by any Rule. With Indifference off this invalidates the Assembly. Faces Not In Rules (FaceNotInRules) | Face | List | Flat list of every Module FaceId on a placed Variant that is not referenced by any Rule. All Faces Indifferent (AllIndiff) | Boolean | Item | True when no placed-Variant Face is referenced by any explicit Rule (random fill). Effectively Indifferent Faces (EffectIndiff) | Face | List | FaceIds that end up paired with every opposing Face on the same axis (explicit Rules redundant). Singly Constrained Faces (SingleCon) | Face | List | FaceIds referenced by exactly one Rule (potential over-constraint hot spots). Require Terminators Active (ReqTerm) | Boolean | Item | True if Construct Assembly synthesized a boundary layer. False means no boundary layer was added and the two terminator face outputs are empty. Terminator Faces Covered (TermCovered) | FaceIndex | List | Face directions (+X,+Y,+Z,-X,-Y,-Z) that have at least one Terminator. Only populated when Require Terminators is Active. A direction absent from this list means no module face on that envelope side is allowed to touch the boundary -- likely to cause contradictions. Terminator Faces Missing (TermMissing) | FaceIndex | 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 directions are covered. -------------------------------------------------------------------------------- ### 5.1.2 WFC Solver Nickname: Solver Category: Main Exposure: tertiary Run the Wave Function Collapse algorithm. Progressively eliminates Module candidates from each Slot until every Slot is deterministic or contradictory. The algorithm: 1. Canonicalization -- Initial propagation pass removing already-impossible Modules. 2. Observation -- Select Slot with lowest entropy, assign single Module by weight. 3. Propagation -- Cascade consequences through grid, removing illegal candidates. 4. Repeat until all deterministic (success) or any Slot reaches 0 (contradiction). Limits: Free tier allows a limited number of WFC Solver runs per fixed time window (aligned to UTC clock boundaries; all envelope sizes). Paid tiers are unlimited. Max 16,370 distinct Module names on all tiers. Each rotation variant of a Module counts as a separate name toward this limit. A single Module design can produce up to 24 orientation variants (from 90° turns around X, Y and Z axes). This means the solver supports roughly 682 unique Module designs at maximum expansion, or more if not every Module uses all three rotation axes. Invalid Assembly handling: the Solver gates every run on Assembly.IsValid. When the Assembly is invalid (e.g. uncovered faces with Indifference off) the Solver refuses to run and still emits every output explicitly: - Report: "WFC SOLVER DID NOT RUN" header, the IsValidWhyNot reason, a short list of suggested fixes (enable Indifference, add rules/connectors, use Audit Assembly), followed by the Construct Assembly audit text embedded from the Assembly's AuditResult when present. - Assemblies, Deterministic, Contradictory, Seeds, Observations: empty / null. - Attempts: 0 (no attempt was made). The component raises an Error on the canvas with the short reason; the full actionable text lives in the Report output. Adjacency pre-flight validation: before calling the native solver, the Solver validates that every Module has at least one adjacency Rule as low-side neighbor (positive direction) and high-side neighbor (negative direction) on each non-flat axis. A gap would produce an empty adjacency mask in the solver, making the world unsolvable. If gaps are found, the Solver raises an Error listing which Modules lack coverage on which axes (up to 10 entries) and does not call the native solver. Fix by adding Rules or Connectors covering the reported faces, or enable Indifference. Inputs: Assembly (A) | Assembly | Item | Discrete Assembly from Construct Assembly. The sole data input. Random Seed (S) | Integer | Item | Starting seed. Default: 42 Max Attempts (A) | Integer | Item | Max attempts. Default: CPU cores + 1 Max Observations (O) | Integer | Item | Max observations per attempt. Default: unlimited Return First (F) | Boolean | Item | Stop on first success. Default: true Outputs: Report (R) | Text | Item | Solver log Assembly (A) | Assembly | Item | Output Assembly with solved Slot states. Deterministic (OK) | Boolean | List | True if solution fully solved Contradictory (C) | Boolean | List | True if attempt ended in contradiction Seeds (S) | Integer | List | Seed used per attempt Observations (O) | Integer | List | Observations per attempt Attempts (A) | Integer | Item | Total attempts run -------------------------------------------------------------------------------- ### 5.1.3 Construct Assembly Nickname: Assembly Category: Main Exposure: primary Expand rotation variants, generate Rules from Connectors, remove disallowed Rules, enforce exclusive Rules, apply indifference for uncovered Faces, deduplicate, run audit, and package everything into a Discrete Assembly consumed by the Solver. Previews explicit Rules as colored lines between original (unrotated) Module face centers in the viewport. Rules involving rotation variants are traced back to the original Module via inverse face permutation, so some lines may connect non-opposing faces - this is intentional and shows "these faces connect when rotated". Indifference Rules are not shown. Lines are colored by axis (same convention as Preview Rule). Supports baking. Processing order: 1. Expand rotation variants from Module rotation flags. For each flagged Module, generate all unique 90-degree rotation transforms (composing Z, Y, X axes; deduplicating by transform equality). Create new Module instances with rotated geometry, re-aligned bounding boxes, and rotation-suffixed names (e.g. _z90, _z180y90). Compute a face permutation for each variant: a 6-element array mapping original face indices to new positions after rotation (see step 1b). 1b. Remap existing Rules for rotation variants. When both sides of a Rule are rotation-expanded, only pair variants with the identical face permutation - the spatial neighborhood rotates as a rigid unit, so both modules must share the same rotation. When only one side is expanded, permute only the rotated side's face index. Extend Slot allowed-module lists with fitting variants (filtered by box dimensions). 1c. Resolve allow-all Slots. Slots that were created without explicit Module Names (empty list, zero Total Modules Count) are filled with the deduplicated list of Module names from the now-final `modules` list, with uniform weights of 1.0 and Total Modules Count set to the list length. Modules whose box dimensions do not match the Slot are filtered out so the downstream dimension-mismatch warning does not fire for allow-all Slots. If any Slots were resolved, Construct Assembly emits a single summary Remark of the form "N allow-all Slots resolved to M Modules each." - one Remark per Slot would flood the log for large allow-all envelopes. Each resolved Slot is tagged WasOriginallyAllowAll so Audit Assembly can still report the original count after the fact. 2. Transform Connectors through the same rotations: remap face index via the permutation, recompute in-plane rotation angle by projecting the connector's up-vector through the rotation transform. 3. Validate Connectors (no duplicate face assignments, all referenced Modules exist). 4. Deduplicate Allowed Connector Pairs by value equality. Cross-referencing Connector lists commonly produces exact-duplicate pairs, and the pair rule generator is O(modules² × axes) per pair - dropping duplicates up front saves substantial work. A single Remark reports how many duplicates were removed. A→B and B→A are kept as separate entries because the rule generator treats direction as meaningful. 5. Generate allowed Rules from Connectors using the rotation compatibility algorithm. Merge with manually provided Allowed Rules. 6. Remove Disallowed Rules: collect explicit Disallowed Rules and generate Rules from Disallowed Connector Pairs; remove matching Rules from the allowed set (unmatched disallowed Rules are silently ignored). 7. Enforce Exclusive Rules: collect explicit Exclusive Rules and generate Rules from Exclusive Connector Pairs; collect all Faces referenced by these Rules; remove all other Rules that reference any of those Faces; add the exclusive Rules to the allowed set. 8. Apply indifference for faces with no connector AND no explicit Rule. Terminator-to-boundary rules (generated in step 8b) are excluded from the "face has rules" check - a face covered only by terminator rules is treated as uncovered and still receives indifference rules for module-to-module adjacency. 8b. Require Terminators boundary synthesis: when enabled, synthesize a one-cell-thick boundary layer and generate adjacency Rules linking each Terminator-marked face to the boundary module. Flat axes (grid extent == 1) are skipped -- no boundary wrapping, Terminator Rules, or self-adjacency on axes with only one cell. 9. Merge all Rules (explicit + connector-generated + indifference). Deduplicate. 10. Validate Modules: reject duplicates with the same name AND same dimensions (true duplicates). Same-name Modules with different box dimensions are valid Variants - these are rare in practice because rotation expansion gives each rotated Module a new name, so Variants only arise when the user manually constructs several same-name Modules with different Grid Box sizes. 11. Run audit (face usage, self-loops, over-constraint, etc.). 12. Package into Assembly. Inputs: Modules (M) | Module | List | Modules with rotation flags Slots (S) | Slot | List | Slot envelopes Allowed Rules (R) | Rule | List | Manually defined allowed rules (optional) Disallowed Rules (DR) | Rule | List | Rules to remove from the allowed set (optional) Exclusive Rules (ER) | Rule | List | Rules whose Faces become exclusive (optional) Connectors (C) | Connector | List | Connector-to-face bindings (optional) Allowed Connector Pairs (CP) | ConnectorPair | List | Connector pair declarations (optional) Disallowed Connector Pairs (DCP) | ConnectorPair | List | Connector pairs to remove (optional) Exclusive Connector Pairs (ECP) | ConnectorPair | List | Connector pairs whose Faces become exclusive (optional) Require Terminators (RT) | Boolean | Item | Enforce boundary coverage via synthesized boundary layer. Default: false Terminators (T) | Terminator | List | Boundary-facing face markers (optional; required when Require Terminators is true) Indifference (I) | Boolean | Item | Enable indifference. Default: true Outputs: Assembly (A) | Assembly | Item | Complete, audited Discrete Assembly -------------------------------------------------------------------------------- ### 5.1.4 Deconstruct Assembly Nickname: DeAssembly Category: Main Exposure: primary Extract all authored inputs from a Discrete Assembly. Returns the authored modules, slots, connectors, connector pairs, rules, indifference flag, terminators, and Require Terminators flag exactly as supplied on Construct Assembly. After solving, the Slots output returns authored slots with narrowed Module Names (rotation variants projected back to source module names; authored order and weights preserved). Rotation variants are not included -- use Dissolve Assembly to access the expanded solver-ready state. Inputs: Assembly (A) | Assembly | Item | Any Assembly from Construct Assembly or Solver Outputs: Modules (M) | Module | List | Authored Modules as supplied on Construct Assembly. Rotation variants not included. Slots (S) | Slot | List | Authored Slots in original order. After solving, Module Names are narrowed to surviving source modules. Connectors (C) | Connector | List | Authored Connectors as supplied. Allowed Connector Pairs (CP) | ConnectorPair | List | Authored allowed Connector Pairs. Disallowed Connector Pairs (DCP) | ConnectorPair | List | Authored disallowed Connector Pairs. Exclusive Connector Pairs (ECP) | ConnectorPair | List | Authored exclusive Connector Pairs. Rules (R) | Rule | List | Authored allowed Rules. Disallowed Rules (DR) | Rule | List | Authored disallowed Rules. Exclusive Rules (ER) | Rule | List | Authored exclusive Rules. 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. -------------------------------------------------------------------------------- ### 5.1.5 Materialize Assembly Nickname: Materialize Category: Main Exposure: quarternary Extract placed geometry from a solved Discrete Assembly. Deterministic Slots produce placed geometry; non-deterministic and contradictory Slots are skipped. When multiple Module variants share a name but have different dimensions, the variant whose box dimensions match the Slot is selected. Baked geometry preserves the display color, layer, and linetype captured at Module construction time. Inputs: Assembly (A) | Assembly | Item | Solved (or partially solved) Assembly Outputs: Geometry (G) | Geometry | Tree | Placed Module geometry Transforms (T) | Transform | Tree | Placement transforms per Slot Module Names (MN) | Text | Tree | Module name placed in each Slot -------------------------------------------------------------------------------- ### 5.1.6 Sample Geometry Nickname: SampleGeo Category: Main Exposure: septenary Create Module candidates by sampling geometry into Grid Box cells. Chops geometry, voxelizes, and deduplicates identical Modules. Inputs: Module Name Base (ModNamBas) | Text | Item | Base string for auto-generated names Voxel Resolution (V) | Vector | Item | Voxels per axis. Default: 16x16x16 Geometry (G) | Geometry | List | Geometry to sample Sampling Boxes (B) | GridBox | List | Boxes to chop and sample Outputs: Modules (M) | Module | List | Unique Module candidates Module Count (MC) | Integer | Item | Number of Modules derived Slots (S) | Slot | List | Slots with Module placements Box to Module Index (B2MI) | Integer | List | Per-box Module index (-1 if none) Box to Slot Index (B2SI) | Integer | List | Per-box Slot index (-1 if none) ================================================================================ ## 5.2 CONNECTOR ### 5.2.1 Connector from Face Nickname: ConnFromFace Category: Connector Create Connectors from a Connector Name, four symmetry flags, FaceIds (from Module Faces component), and in-plane Rotation. Formerly called "Construct Connector". All symmetry flags default to true (rotationally invariant like a round jack). A Connector with symmetry {true, false, true, false} is half-turn symmetric (like USB-C). Optionally connect Modules for viewport preview (connector name and symmetry arrows in the connector's type color, with degree labels 0°/90°/180°/270°). The preview is rendered by the component, not by the Connector data type. 8 inputs total. For point-based placement, use Connector from Point. 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 so results arrive as a single list. Inputs: Connector Name (CN) | ConnName | Item | Connector type name (case-insensitive, no :, ->, @, or #; spaces allowed) Face (F) | FaceId | List | FaceId from Module Faces, or a Module (expanded to all six faces) Face Rotation (R) | Integer | Item | In-plane rotation: 0–3 representing 0°, 90°, 180°, 270°. Also accepts 90, 180, 270 directly. Default: 0 Symmetry 0° (S0) | Boolean | Item | Self-identical at 0°. Default: true Symmetry 90° (S90) | Boolean | Item | Self-identical at 90°. Default: true Symmetry 180° (S180)| Boolean | Item | Self-identical at 180°. Default: true Symmetry 270° (S270)| Boolean | Item | Self-identical at 270°. Default: true All Modules (M) | Module | List | Optional. Modules for viewport preview Outputs: Connector (C) | Connector | List | Constructed Connectors (flattened) -------------------------------------------------------------------------------- ### 5.2.2 Connector from Point Nickname: ConnFromPt Category: Connector Create one or more Connectors by tagging a location on a Module face with a Point3d. Combines Face-from-Point lookup (same point-in-face matching as Faces from Point) with Connector construction (same validation as Connector from Face) so authoring a Connector from a viewport click takes one component instead of two. Returns one Connector per Face whose geometry contains the point; zero matches raise a Warning and produce no Connectors; multiple matches emit an informational Remark and produce one Connector per match. Viewport preview draws the Connector sticker on every matched face, identical to Connector from Face. Exposure: primary. 8 inputs total. Output is flattened by default so matches across tree branches land in a single list. Inputs: Connector Name (CN) | ConnName | Item | Connector type name (case-insensitive, no :, ->, @, or #; spaces allowed) Modules (M) | Module | List | All Modules to search for a Face hit. Flattened. Point Tag (Pt) | Point3d | Item | Point on a Module face. One Connector per matching Face. Face Rotation (R) | Integer | Item | In-plane rotation: 0–3 representing 0°, 90°, 180°, 270°. Also accepts 90, 180, 270 directly. Default: 0 Symmetry 0° (S0) | Boolean | Item | Self-identical at 0°. Default: true Symmetry 90° (S90) | Boolean | Item | Self-identical at 90°. Default: true Symmetry 180° (S180)| Boolean | Item | Self-identical at 180°. Default: true Symmetry 270° (S270)| Boolean | Item | Self-identical at 270°. Default: true Outputs: Connectors (C) | Connector | List | One Connector per matched Face -------------------------------------------------------------------------------- ### 5.2.3 Deconstruct Connector Nickname: DeconConn Category: Connector Exposure: secondary Deconstruct a Connector into its constituent parts: connector name, rotation, module name, and face id. The inverse of Connector from Face. Inputs: Connector (C) | Connector | Item | The Connector to deconstruct Outputs: Connector Name (CN) | ConnName | Item | The connector type name Rotation (R) | Integer | Item | In-plane rotation (0, 90, 180, or 270°) Module Name (MN) | ModuleName | Item | The module this connector is placed on Face (F) | FaceId | Item | Face identifier (module name + face index) -------------------------------------------------------------------------------- ### 5.2.4 Construct Connector Pair Nickname: ConnectorPair Category: Connector Exposure: secondary Declare which Connector types can connect. Takes single items: Source Connector (item) + Target Connector (item) produces one Connector Pair (item). Compatibility is bidirectional - A→B also allows B→A. Use Grasshopper's cross-reference component to make multiple pairs from lists. Inputs: Source Connector (SC) | Connector | Item | Connector on the source side Target Connector (TC) | Connector | Item | Connector on the target side Outputs: Connector Pair (CP) | ConnectorPair | Item | One declared compatible pair -------------------------------------------------------------------------------- ### 5.2.5 Suggest Connectors from Geometry Nickname: SuggestConnGeo Category: Connector Given a Connector as exemplar, find all geometrically matching Faces across all Modules and output Connectors with detected rotation offsets. The component renders connector stickers (white name label and symmetry dots) on all matched Faces in the viewport. Inputs: Connector (C) | Connector | Item | Exemplar Connector (defines type, exemplar face, and rotation) Modules (M) | Module | List | All Modules to search Outputs: Connectors (C) | Connector | List | All matched Connectors with rotations Match Count (MC) | Integer | Item | Number of Faces matched (including exemplar) -------------------------------------------------------------------------------- ### 5.2.6 Suggest Connectors from Voxels Nickname: SuggestConnVox Category: Connector Same as Suggest Connectors from Geometry but uses voxelized geometry for more robust matching on complex surfaces. Adds Voxel Resolution and Scanning Depth parameters. The component renders connector stickers on all matched Faces in the viewport. Inputs: Connector (C) | Connector | Item | Exemplar Connector (defines type, exemplar face, and rotation) Modules (M) | Module | List | All Modules to search Voxel Resolution (V) | Vector | Item | Voxels per axis. Default: 16×16×16 Scanning Depth (D) | Interval | Item | Depth range 0.0-1.0. Default: 0-0 Outputs: Connectors (C) | Connector | List | All matched Connectors Match Count (MC) | Integer | Item | Number of Faces matched -------------------------------------------------------------------------------- ### 5.2.7 Construct Terminator Nickname: ConstructTerm Category: Connector Exposure: primary Create Terminators -- boundary-facing markers placed on module faces. Either a Face or a whole Module may be wired into the Face or Module input; wiring a Module produces one Terminator per face of that Module with a Remark reporting the count. For point-based placement, use Terminator from Point. Terminators do nothing unless Require Terminators is enabled on Construct Assembly. When it is on, Construct Assembly synthesizes a one-cell-thick boundary layer around the envelope and links each Terminator-marked face to it; module faces without a Terminator cannot touch the outer edge. The optional All Modules input provides Modules for viewport preview; when connected, a terminator badge (circle with inner X at 45/135/225/315 degrees) is drawn on each target face. Inputs: Face or Module (F) | FaceId | List | Face where the Terminator is placed, or a Module (expanded to all six faces) All Modules (M) | Module | List | Optional. Modules for viewport preview. Outputs: Terminator (T) | Terminator | List | Constructed Terminators (flattened) -------------------------------------------------------------------------------- ### 5.2.8 Terminator from Point Nickname: TermFromPt Category: Connector Exposure: primary Create Terminators from a point tag. Looks up every Module Face whose geometry contains the provided point and creates a Terminator for each match in one step, combining Face-from-Point lookup with Terminator construction. Useful for authoring Terminators by clicking on Module faces in the Rhino viewport. Zero matches raise a Warning and produce no Terminators. Multiple matches (e.g. overlapping Modules sharing a face location) emit an informational Remark and produce one Terminator per match. Output is flattened. Inputs: Modules (M) | Module | List | All Modules to search for a Face hit. Flattened. Point Tag (Pt) | Point3d | Item | Point marking a location on a Module face. Outputs: Terminators (T) | Terminator | List | One Terminator per matched Face (flattened) ================================================================================ ## 5.3 ENVELOPE ### 5.3.1 Homogeneous Grid Nickname: HomoGrid Category: Envelope Generate a regular grid of uniform cells aligned to a base plane. Inputs: Base Plane (P) | Plane | Item | Origin and orientation. Default: World XY Diagonal (D) | Vector | Item | Cell size (X,Y,Z). Default: (1,1,1) X Count (X) | Integer | Item | Cells along X. Default: 1 Y Count (Y) | Integer | Item | Cells along Y. Default: 1 Z Count (Z) | Integer | Item | Cells along Z. Default: 1 Outputs: Grid Boxes (B) | Grid Box | List | Flattened list of Grid Boxes -------------------------------------------------------------------------------- ### 5.3.2 Heterogeneous Grid Nickname: HeteroGrid Category: Envelope Generate a grid where cells along each axis can have different sizes. Each axis receives an independent list of dimension values. Inputs: Base Plane (P) | Plane | Item | Origin and orientation. Default: World XY X Sizes (X) | Number | List | Width of each column along X Y Sizes (Y) | Number | List | Depth of each row along Y Z Sizes (Z) | Number | List | Height of each layer along Z Outputs: Grid Boxes (B) | Grid Box | List | Variable-sized Grid Boxes -------------------------------------------------------------------------------- ### 5.3.3 Add Boundary Layer Nickname: AddBound Category: Envelope Surround an existing Envelope with additional layers of Grid Boxes. Outputs only the new boundary cells, not the originals. Inputs: Grid Boxes (B) | Grid Box | List | Inner Envelope Grid Boxes Diagonal Neighbors (D) | Boolean | Item | Fill corner-diagonals. Default: false Layers (L) | Integer | Item | Number of boundary layers. Default: 1 Include X+ (X) | Boolean | Item | Add on +X face. Default: true Include Y+ (Y) | Boolean | Item | Add on +Y face. Default: true Include Z+ (Z) | Boolean | Item | Add on +Z face. Default: true Include X- (X) | Boolean | Item | Add on -X face. Default: true Include Y- (Y) | Boolean | Item | Add on -Y face. Default: true Include Z- (Z) | Boolean | Item | Add on -Z face. Default: true Outputs: Boundary Layer Boxes (B) | Grid Box | List | New boundary Grid Boxes only -------------------------------------------------------------------------------- ### 5.3.4 Are Grid Boxes Boundary Nickname: AreBoxesBound Category: Envelope Identify which Grid Boxes sit on the outer boundary. Returns a boolean per input box. Inputs: Grid Boxes (B) | Grid Box | List | Grid Boxes to test Layers (L) | Integer | Item | Outer layers to mark. Default: 1 Include X (X) | Boolean | Item | Check X axis. Default: true Include Y (Y) | Boolean | Item | Check Y axis. Default: true Include Z (Z) | Boolean | Item | Check Z axis. Default: true Outputs: Boolean Pattern (B) | Boolean | List | True for boundary Grid Boxes -------------------------------------------------------------------------------- ### 5.3.5 Deconstruct Grid Box Nickname: DeconGridBox Category: Envelope Extract center plane, diagonal dimensions, and axis intervals from a Grid Box. Inputs: Grid Box (B) | Grid Box | Tree | Grid Boxes to deconstruct Outputs: Center Plane (P) | Plane | Tree | Center plane with orientation Diagonal (D) | Vector | Tree | X, Y, Z dimensions X Interval (X) | Interval | Tree | Interval along X axis Y Interval (Y) | Interval | Tree | Interval along Y axis Z Interval (Z) | Interval | Tree | Interval along Z axis -------------------------------------------------------------------------------- ### 5.3.6 Neighbor Grid Boxes Nickname: Neighbors Category: Envelope Find Grid Boxes adjacent to a given set of focus cells (by grid topology, not Euclidean distance). Focus cells are excluded from output. Inputs: Grid Boxes (B) | Grid Box | List | All Envelope Grid Boxes Grid Box Indices (I) | Integer | List | Focus cell indices Layers (L) | Integer | Item | Search distance in grid steps. Default: 1 Include X (X) | Boolean | Item | Search along X. Default: true Include Y (Y) | Boolean | Item | Search along Y. Default: true Include Z (Z) | Boolean | Item | Search along Z. Default: true Outputs: Neighbor Indices (I) | Integer | List | Unique indices of neighboring Grid Boxes -------------------------------------------------------------------------------- ### 5.3.7 Grid Topology Nickname: Topology Category: Envelope Extract discrete grid coordinates and adjacency map. Inputs: Grid Boxes (B) | Grid Box | List | All Envelope Grid Boxes Diagonal Neighbors (D) | Boolean | Item | Include corner-diagonals. Default: false Bi-Directional Topology (BiDi) | Boolean | Item | Both directions. Default: true Outputs: Relative Coordinates (C) | Point | List | Integer grid coordinates per Grid Box Topology (T) | Integer | Tree | Adjacent neighbor indices per Grid Box -------------------------------------------------------------------------------- ### 5.3.8 Grid Boxes from Geometry Nickname: GeoGrid Category: Envelope Generate Grid Boxes covering the shape of input geometry for non-rectangular Envelopes. Supports Points, Curves, untrimmed Surfaces, Breps, and Meshes. Inputs: Geometry (G) | Geometry | List | Input geometry defining shape Base Plane (B) | Plane | Item | Grid orientation. Default: World XY Grid Box Diagonal (D) | Vector | Item | Cell size. Default: (1,1,1) Fill Method (F) | Integer | Item | 0=surface only, 1=interior only, 2=both (default) Outputs: Grid Boxes (B) | Grid Box | List | Generated Grid Boxes -------------------------------------------------------------------------------- ================================================================================ ## 5.4 MODULE ### 5.4.1 Construct Module Nickname: ConstModule Category: Module Construct a Module from a name, Grid Box, and optional geometry. When geometry references Rhino document objects (not purely Grasshopper-generated), the component captures each piece's display color, layer name, and linetype name. These attributes are stored on the Module and preserved through rotations, serialization, and baking. Geometry from Grasshopper (not referenced) carries no attributes. Inputs: Module Name (MN) | ModuleName | Item | Name (converted to lowercase) Module Box (B) | GridBox | Item | Box containing Module geometry Geometry (G) | Geometry | List | Geometry to materialize. Optional Rotate X (RX) | Boolean | Item | Auto-rotate around X in Solver. Variants appear in the Solver's All Modules output. Default: false Rotate Y (RY) | Boolean | Item | Auto-rotate around Y in Solver. Default: false Rotate Z (RZ) | Boolean | Item | Auto-rotate around Z in Solver. Default: false Outputs: Module (M) | Module | Item | Constructed Module instance -------------------------------------------------------------------------------- ### 5.4.2 Deconstruct Module Nickname: DeconModule Category: Module Extract components of a Module. Inputs: Module (M) | Module | Item | Module to deconstruct Outputs: Module Name (MN) | ModuleName | Item | Name (lowercase) Module Box (B) | GridBox | Item | Bounding box Geometry (G) | Geometry | Item | Contained geometry Is Valid (V) | Boolean | Item | True if valid for Solver Faces (C) | FaceId | List | All 6 Faces -------------------------------------------------------------------------------- ### 5.4.3 Module Rotations Nickname: ModuleRot Category: Module Generate rotated variants (90/180/270 degrees) around enabled axes, producing up to 24 unique orientations. Optionally deduplicates by voxel comparison. Inputs: Module (M) | Module | Item | Module to rotate Rotate X (X) | Boolean | Item | Enable X rotation. Default: true Rotate Y (Y) | Boolean | Item | Enable Y rotation. Default: true Rotate Z (Z) | Boolean | Item | Enable Z rotation. Default: true Cull Duplicates (C) | Boolean | Item | Remove identical variants. Default: true Voxel Resolution (V) | Vector | Item | Voxels per axis. Default: 16x16x16 Outputs: Modules (M) | Module | List | Rotated Module variants -------------------------------------------------------------------------------- ### 5.4.4 Cull Duplicate Modules Nickname: CulDupModule Category: Module Remove duplicate Modules by voxel comparison. Optionally remaps Rules. Inputs: Modules (M) | Module | List | Modules to deduplicate Voxel Resolution (V) | Vector | Item | Voxels per axis. Default: 16x16x16 Rules (R) | Rule | List | Rules to remap. Optional Outputs: Modules (M) | Module | List | Unique Modules Indices (I) | Integer | List | Index map: input -> output Valence (V) | Integer | List | Input count per output Rules (RD) | Rule | List | Remapped Rules -------------------------------------------------------------------------------- ### 5.4.5 Construct Megamodule Nickname: Megamodule Category: Module Create a multi-cell Module from adjacent Grid Boxes. Auto-generates sub-Modules (named {name}_p0/N, {name}_p1/N, etc. where N is the total part count), exclusive internal Rules, and identifies external Faces. Every sub-module carries the full megamodule geometry for face suggestion analysis; only the first part (_p0) materializes it. Each sub-module previews sibling Grid Boxes as dashed ghost outlines. Supports auto-rotation (Rotate X/Y/Z) - the Solver expands all parts together and remaps both internal and external Rules. Inputs: Megamodule Name (MN) | ModuleName | Item | Base name Grid Boxes (B) | GridBox | List | Adjacent Grid Boxes defining footprint Geometry (G) | Geometry | List | Geometry for all sub-modules. Optional Rotate X (RX) | Boolean | Item | Auto-rotate around X. Default: false Rotate Y (RY) | Boolean | Item | Auto-rotate around Y. Default: false Rotate Z (RZ) | Boolean | Item | Auto-rotate around Z. Default: false Outputs: Modules (M) | Module | Tree | Sub-Module instances (branch per part, flattened by default) Internal Rules (IR) | Rule | List | Exclusive internal adjacency Rules External Faces (EF) | FaceId | Tree | Outer boundary Faces (branch per part, flattened by default) -------------------------------------------------------------------------------- ### 5.4.6 Deconstruct Megamodule Nickname: DeconMegamodule Category: Module Extract Grid Boxes, internal Rules, and external Faces from Megamodule sub-Modules. Inputs: Modules (M) | Module | List | Sub-Module list from Construct Megamodule Outputs: Grid Boxes (B) | GridBox | List | One per sub-Module Internal Rules (IR) | Rule | List | Internal adjacency Rules External Faces (EC) | FaceId | List | Outer boundary Faces ================================================================================ ## 5.5 FACE ### 5.5.1 Get Module Faces Nickname: ModFaces Category: Face Extract the 6 individual Faces from a Module, one per output. Inputs: Module (M) | Module | Item | Module to extract Faces from Outputs: +X Face (+X) | FaceId | Item | Face facing positive X -X Face (-X) | FaceId | Item | Face facing negative X +Y Face (+Y) | FaceId | Item | Face facing positive Y -Y Face (-Y) | FaceId | Item | Face facing negative Y +Z Face (+Z) | FaceId | Item | Face facing positive Z -Z Face (-Z) | FaceId | Item | Face facing negative Z -------------------------------------------------------------------------------- ### 5.5.2 Analyze Face Nickname: AnalyzeFace Category: Face Analyze Face properties including geometry, direction, and Rule/Connector usage. Inputs: Face (C) | FaceId | Tree | Faces to analyze Modules (M) | Module | Tree | Modules for geometry resolution. Optional Rules (R) | Rule | Tree | Rules for usage determination. Optional Connectors (C) | Connector | Tree | Connectors for usage determination. Optional Outputs: Module Name (MN) | ModuleName | Item | Associated Module name Face Planes (CP) | Plane | Item | Face planes Face Rectangles (CR) | Rectangle3d | Item | Face rectangle outlines Face Direction (CD) | Vector3d | Item | Face normal unit vector Is X / Is Y / Is Z (X/Y/Z) | Boolean | Item | True for respective positive direction Is -X / Is -Y / Is -Z (-X/-Y/-Z) | Boolean | Item | True for respective negative direction Face Use Pattern (CUP) | Boolean | Item | True when referenced by any Rule or Connector Used Faces (CU) | FaceId | Item | Faces with Rules or Connectors Unused Faces (CUU) | FaceId | Item | Faces without Rules or Connectors -------------------------------------------------------------------------------- ### 5.5.3 Compare Faces Nickname: CompFaces Category: Face Compare two Faces for identity, Module membership, and direction relationship. Inputs: Face (C) | FaceId | Item | First Face Face (C) | FaceId | Item | Second Face Outputs: Identical (I) | Boolean | Item | True if identical Same Module (M) | Boolean | Item | True if same Module Same Direction (D) | Boolean | Item | True if same direction Opposite Direction (O) | Boolean | Item | True if opposite directions Skew Direction (S) | Boolean | Item | True if non-parallel directions -------------------------------------------------------------------------------- ### 5.5.4 Faces from Point Nickname: FacesPoint Category: Face Detect Module Faces at a point location. Inputs: Modules (M) | Module | List | All Modules to sample Point Tag (Pt) | Point3d | Item | Point marking Face location Outputs: Faces (C) | FaceId | List | Faces containing the point -------------------------------------------------------------------------------- ### 5.5.5 Used Faces Nickname: UsedFaces Category: Face 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 when using Require Terminators. Inputs: Modules (M) | Module | Tree | Modules whose Faces will be checked Rules (R) | Rule | Tree (flatten) | Rules to check. Optional Connectors (C) | Connector | Tree (flatten) | Connectors to check. Optional Outputs: Used Faces (F) | FaceId | Tree | Faces referenced by at least one Rule or Connector Unused Faces (UF) | FaceId | Tree | Faces not referenced by any Rule or Connector Output tree structure mirrors the input Module tree with one grafted branch per Module (AppendElement). Both Rules and Connectors are optional - when neither is provided all Faces are reported as unused. -------------------------------------------------------------------------------- ### 5.5.6 Preview Faces Nickname: FacesPreview Category: Face Visualize Faces with geometry, anchor planes, and direction indicators. Display-only component. Inputs: Faces (C) | FaceId | Tree | Faces to preview Modules (M) | Module | Tree | Modules for geometry resolution Outputs: (none -- preview/display only) ================================================================================ ## 5.6 RULE ### 5.6.1 Construct Rules from Faces Nickname: RuleFromFaces Category: Rule Create Rules from all valid opposite-direction combinations between source and target Face lists. Deduplicates automatically. Inputs: Source Faces (SC) | FaceId | List | Source-side Faces Target Faces (TC) | FaceId | List | Target-side Faces Outputs: Rule (R) | Rule | List | Generated Rules -------------------------------------------------------------------------------- ### 5.6.2 Construct Rules from Modules Nickname: RuleFromModules Category: Rule Build Rules from source and target Module Name lists with six directional target inputs. Opposite Faces are inferred automatically. Inputs: Source Module Names (SMN) | ModuleName | List | Source Module Names X+ Target Module Names (X+ TMN) | ModuleName | List | Targets via +X. Optional Y+ Target Module Names (Y+ TMN) | ModuleName | List | Targets via +Y. Optional Z+ Target Module Names (Z+ TMN) | ModuleName | List | Targets via +Z. Optional X- Target Module Names (X- TMN) | ModuleName | List | Targets via -X. Optional Y- Target Module Names (Y- TMN) | ModuleName | List | Targets via -Y. Optional Z- Target Module Names (Z- TMN) | ModuleName | List | Targets via -Z. Optional Outputs: Rule (R) | Rule | List | Generated Rules -------------------------------------------------------------------------------- ### 5.6.3 Deconstruct Rule Nickname: DeconRuleFaces Category: Rule Extract source and target Faces from a Rule. Inputs: Rule (R) | Rule | Item | Rule to deconstruct Outputs: Source Face (SC) | FaceId | Item | Source-side Face Target Face (TC) | FaceId | Item | Target-side Face -------------------------------------------------------------------------------- ### 5.6.4 Suggest Rules from Geometry Nickname: RulesSuggest Category: Rule Analyze Face geometry and output Rules for all geometrically matching pairs. Inputs: Modules (M) | Module | List | All Modules to analyze Outputs: Rules (R) | Rule | List | Suggested Rules from geometry matches -------------------------------------------------------------------------------- ### 5.6.5 Suggest Rules from Voxels Nickname: RulesSuggestVox Category: Rule Voxelize Modules at the specified resolution, scan face voxel layers within the configured depth range, and output Rules for all matching face pairs. Voxel-based equivalent of Suggest Rules from Geometry. Inputs: Modules (M) | Module | List | All Modules to voxelize and analyze Voxel Resolution (V) | Vector | Item | Number of voxels per direction Scanning Depth (D) | Interval | Item | Relative depth range 0–1 of face layers to compare Outputs: Rules (R) | Rule | List | Suggested Rules from voxel-matched Face patterns -------------------------------------------------------------------------------- ### 5.6.6 Are Rules Equal Nickname: AreRulesEq Category: Rule Compare two Rules for equality (bidirectional: A->B equals B->A). Inputs: Rule A (RA) | Rule | Item | First Rule Rule B (RB) | Rule | Item | Second Rule Outputs: Identical (I) | Boolean | Item | True when equivalent -------------------------------------------------------------------------------- ### 5.6.7 Rule from Curve Nickname: RuleFromCurve Category: Rule Create Rules from curves drawn between Module Faces. Endpoints are matched to Faces that contain the point (same logic as Faces from Point). A single curve can produce multiple Rules when endpoints overlap Faces from different Modules. Previews matched curves colored by axis. Inputs: Curves (C) | Curve | List | Lines/curves between Faces Modules (M) | Module | List | All Modules to search Outputs: Rules (R) | Rule | List | Rules from valid endpoint pairings -------------------------------------------------------------------------------- ### 5.6.8 Preview Rule Nickname: RulePreview Category: Rule Display Rules as lines connecting Faces. Display-only component. Inputs: Rules (R) | Rule | Tree | Rules to preview Modules (M) | Module | Tree | Modules to display with Rules Outputs: (none -- preview/display only) ================================================================================ ## 5.7 SLOT ### 5.7.1 Construct Slot Nickname: SlotConstruct Category: Slot Create a Slot from a Grid Box with optional allowed Module Names and optional weights. Duplicate Module Names within a branch are handled automatically: if all duplicates carry the same weight, they are deduplicated with a Remark; if any duplicate has a different weight, the component reports an Error (ambiguous intent). Pre-Assembly Slots (everything coming out of Construct Slot) carry AllModulesCount = 0 - the total Module count is not yet known. The viewport preview reflects that: it shows only the allowed-module count (one number in the cage corner, no "/ total"). Once the Slots pass through Construct Assembly, AllModulesCount is populated with the size of the final Module set and the preview switches to the familiar "allowed / total" form. Allow-all Slots: the Allowed Module Names input is optional. When it is left unconnected, the component emits the Remark "No Module Names provided. Slot allows all Modules. The allowed set will be resolved by Construct Assembly.", builds a Slot with an empty Module list, and relies on Construct Assembly to later fill it with the full final Module set. Any Weights connected alongside an empty Allowed Module Names input are ignored (a Remark is emitted). Allow-all Slots preview as white cages with the label "All", report Entropy of 1.0 explicitly in Deconstruct Slot, and are neither deterministic nor contradictory until resolved. Weights are optional. When Allowed Module Names is connected but Weights is left unconnected or empty, every allowed Module gets a uniform weight of 1.0. Inputs: Grid Box (B) | GridBox | Tree (grafted) | Box for Slot Allowed Module Names (MN) | ModuleName | Tree | Permitted Module Names. Optional - if unconnected, the Slot is created as allow-all. Allowed Modules Weights (W) | Number | Tree | Weights. Optional - if unconnected or empty, every allowed Module gets a uniform weight of 1.0. Ignored when Allowed Module Names is unconnected. Outputs: Slot (S) | Slot | Tree | Constructed Slot instances -------------------------------------------------------------------------------- ### 5.7.2 Deconstruct Slot Nickname: DeconSlot Category: Slot Extract a Slot's components. Inputs: Slot (S) | Slot | Tree (grafted) | Slot to deconstruct Outputs: Grid Box (B) | GridBox | Tree | Spatial extent Allowed Module Names (MN) | ModuleName | Tree | Allowed Modules (empty for allow-all Slots) Allowed Module Weights (MW) | Number | Tree | Weights (empty for allow-all Slots) Is Deterministic (Det) | Boolean | Tree | True if exactly 1 Module allowed. False for allow-all Slots. Is Contradictory (Con) | Boolean | Tree | True if 0 Modules allowed AND the Slot is not allow-all. Allow-all is unresolved, not contradictory. Entropy (E) | Number | Tree | Ratio of allowed to total (0.0-1.0). Pre-Assembly and allow-all Slots report 1.0 explicitly because AllModulesCount is still zero and the usual ratio is undefined. -------------------------------------------------------------------------------- ### 5.7.3 Slot Pattern Nickname: Pattern Category: Slot Find a Slot sub-pattern inside an existing Slot envelope. Template matching for repeated Module patterns. Inputs: Slots (S) | Slot | List | Envelope Slots to search Slot Pattern (P) | Slot | List | Sub-pattern to find Outputs: Pattern Found (F) | Boolean | Item | True when pattern exists Slot Indices (I) | Integer | Tree | Indices of matched Slots -------------------------------------------------------------------------------- ### 5.7.4 Changed Slots Nickname: ChngSlt Category: Slot Identify which Slots changed between an original list and one or more new lists. Compares allowed Module name sets element-wise. Runs branch comparisons in parallel. Inputs: Original Slots (OS) | Slot | List | Baseline Slot list New Slots (NS) | Slot | Tree | One or more lists to compare Outputs: Changed Indices (ChngIdx) | Integer | Tree | Indices of changed Slots per branch -------------------------------------------------------------------------------- ### 5.7.5 Occurrence Count Nickname: OccurrCount Category: Slot 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. By default only deterministic Slots (where the target Module is the sole allowed option) are counted. Set Only Resolved to false to also count non-deterministic Slots where the target Module appears alongside others. Inputs: Module Name (MN) | ModuleName | Item | Module Name to count Slots (S) | Slot | List | Slot list to search (typically solved Slots) Only Resolved (OR) | Boolean | Item | Count only deterministic Slots. Default: true Outputs: Count (C) | Integer | Item | Number of matching Slots -------------------------------------------------------------------------------- ### 5.7.6 Slot to Module Nickname: SlotToMod Category: Slot Place Module geometry into solved Slots. Similar to Materialize Assembly 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. 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. Supports baking to Rhino as shared block definitions. Inputs: All Modules (M) | Module | List (flatten) | All Modules including rotation variants Solved Slots (S) | Slot | Tree | Solved Slots from the Solver Only Deterministic (D) | Boolean | Item | Only deterministic Slots. Default: true Outputs: Geometry (G) | Geometry | Tree | Placed module geometry Transforms (X) | Transform | Tree | Placement transforms per Slot -------------------------------------------------------------------------------- ### 5.7.7 Preview Rule in Slots Nickname: PreviewRule Category: Slot Preview a Rule by placing modules into deterministic Slots with co-located Modules. Useful for visualization and testing Rule effects. Inputs: Rule (R) | Rule | Item | Rule defining Face relationships Face Pivot Plane (P) | Plane | Item | Position plane. Default: World XY Modules (M) | Module | List | Modules for compatible pairings Outputs: Source Slots (SS) | Slot | List | Slots permitting source Module Target Slots (TS) | Slot | List | Slots permitting target Module Source Modules (SM) | Module | List | Source Modules placed at Rule positions. Rotation flags preserved. Target Modules (TM) | Module | List | Target Modules placed at Rule positions. Rotation flags preserved. ================================================================================ ================================================================================ 6. DATA TYPE CASTING SUMMARY ================================================================================ Grid Box casts FROM: Box, BoundingBox, Rectangle, Brep Grid Box casts TO: Box, Brep, Point, Plane, Vector Module casts FROM: (use Construct Module) Module casts TO: Module Name, Slot, Grid Box, Point, Plane, Vector Module Name casts FROM: String, Module, Integer, Number, Data Path Module Name casts TO: String Face Index casts FROM: Integer, Number, String Face Index casts TO: Integer Face (UID) casts FROM: String Face (UID) casts TO: String Slot casts FROM: (use Construct Slot) Slot casts TO: Grid Box, Box, Brep, Point, Plane, Vector Rule casts FROM: String Rule casts TO: (none) ================================================================================ ================================================================================ ## 7. FAQ ### 7.1 What is a good exercise to start? Start with the 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. ### 7.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. 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. ### 7.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 Assembly component to check for orphaned Faces or missing Rules. ### 7.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. ### 7.4b What does "Some modules lack adjacency rules on non-flat axes" mean? The Solver pre-validates that every Module has at least one adjacency Rule as a low-side neighbor and as a high-side neighbor on each non-flat axis. A Module that is never referenced as a neighbor in a given direction on a given axis would leave the solver with an empty adjacency mask, guaranteeing contradiction. The error lists which Modules and axes are missing coverage. Fix by adding Rules or Connectors for the listed faces, or enabling Indifference. ### 7.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. **Exciting middle, boring edges** (most important principle): Design each Module so that interesting content - walls, surfaces, objects - sits in the center, not at the edges. The six Faces should show only cross-sections of things, never complete surfaces. Think of slicing through a wall: the cut surface (the “flesh”) appears at the Face, but the wall's visible surface (the “skin”) stays in the interior. This makes Suggest Rules components work correctly (they match cross-sections, which are predictable) and means WFC handles only the mechanical task of continuing interrupted elements, while you control what happens semantically inside each Module. **Scale heuristic**: Module size should be approximately half the smallest semantic object in the design. Example: for 3-meter rooms, use 1.5-meter Modules. A room corner then falls in the center of one Module (with 0.75m of wall on each side), followed by a full 1.5m straight-wall Module, then the opposite corner Module. Three Modules = one 3m wall. This ensures semantic boundaries (corners, junctions, transitions) land in the middle of Modules. **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, Suggest Rules from Geometry and Suggest Rules from Voxels will incorrectly infer that adjacent Modules should also have geometry at that face. Workaround for cases where geometry must extend (e.g. a bolt): use simplified proxy geometry for Face suggestion and Rule creation, then use detailed geometry only for materialization. Two approaches: (a) use the transform output of Materialize Assembly to place complex geometry, or (b) create two sets of Modules with identical names - one simple set for Rule suggestion, one detailed set for Materialize Assembly. **Modules contain parts, not wholes**: Never design a Module that represents an entire semantic object (a whole room, a whole door). Always put a part of the object inside the Module, so that parts of neighboring objects are also present. Exception: objects significantly smaller than everything else in the system can be whole Modules. **Multiple variants of the same purpose**: The same functional role often needs multiple Module variants with different names and Rules. Most commonly: empty-interior, empty-exterior, and empty-boundary. Each participates in different Rules even though the geometry may be identical or absent. **Same geometry, different identity**: Multiple Modules can share identical geometry but carry different names and participate in different Rules. The identity is in the name and Rule participation, not the geometry. **Design systems separately, connect through interfaces**: When the design involves multiple independent spatial systems (e.g. a road network and landscaping), design each system's Modules and Rules independently. Then create a small number of interface Modules that bridge the two systems. This keeps complexity manageable - each system can be developed and tested independently. See strategy 3.8. **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. **Two workflows for wall/boundary design**: (a) Wall-centered: the wall sits in the center of the Module (per the “exciting middle” principle). Both sides carry context from their respective zones. Self-contained and works well with Suggest Rules, but requires a Module for every combination of zone types on either side. (b) Boundary-split: the wall is split at the Module boundary - half in one Module, half in the next. Zone identity controlled through Rules rather than Module design. Requires more Rules but fewer Modules; works better with many zone types. ### 7.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. ### 7.7 How do I handle boundaries? Monoceros 3 has no implicit boundary layer. You must define boundaries explicitly: - Use Add Boundary Layer to generate Grid Boxes around your Envelope. - Create Slots from those Grid Boxes, pre-loaded with your boundary Module(s). - 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. ### 7.8 How do I create an “empty” Module? Monoceros 3 has no built-in Empty Module. Simply create a Module with no geometry: - Use Construct Module with a name like "empty" and a Grid Box matching your grid cell size. - Do not connect any geometry to the Geometry input. - 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"). ### 7.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. ### 7.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. ### 7.11 Why does a zero-weight Module still appear in the result? Zero weight does not remove a Module from the allowed set during constraint propagation - propagation uses Rules, not weights. A zero-weight Module remains a valid candidate in any Slot that lists it in ModuleNames. The WFC Solver will never voluntarily choose a zero-weight Module during observation, but if constraint propagation eliminates every other option in a Slot, the solver must use the zero-weight Module to avoid a contradiction. To reliably prevent a Module from appearing in a zone, remove it from that Slot’s ModuleNames list rather than setting its weight to zero. ### 7.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. ### 7.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. ### 7.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. ### 7.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. Use Audit to identify orphaned Faces, and Occurrence Count to verify the Module appears at all. ### 7.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. Connect the two out-of-plane faces to a boundary Module or to themselves. ### 7.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). ### 7.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. ### 7.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 supports auto-rotation (Rotate X/Y/Z) so the Solver generates rotated variants of the entire megamodule. 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). ### 7.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. ### 7.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. ### 7.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. ### 7.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 to join geometry after solving. - Use Suggest Rules from Geometry to ensure only Modules with matching edge geometry are paired. ### 7.24 What is the free-tier run limit? Monoceros 3 Free includes full functionality on envelopes of any size, with a rate cap on the WFC Solver component: a limited number of solver runs per fixed time window (windows are aligned to UTC clock boundaries). Each time you click the solver's Run input from False to True and it produces a result (solved or failed), that counts as one run. The internal "Max Attempts" setting does not multiply the count - a single solver invocation is always one run regardless of how many internal backtracks the algorithm performs. Paid tiers (Annual, Edu, Lifetime) are unlimited. The WFC Solver component also has a boolean `Run` input (default `false`) to prevent upstream slider changes from accidentally burning through your run budget: the solver only executes when you explicitly set Run to True. Connect a Boolean Toggle or a Button component. Separately, the solver has a hard limit of 16,370 distinct Module names regardless of license 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. ### 7.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. ### 7.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. ### 7.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. ### 7.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: - 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. - 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. - 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. ### 7.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. Before Construct Assembly runs, AllModulesCount is zero (the total is not yet known) and the Slot preview shows only the allowed-module count. Construct Assembly then stamps every Slot it processes with the size of the final Module set, so post-Assembly every Slot shows the familiar "allowed / total" ratio and every Slot agrees on the same total. No List Length wiring or manual synchronisation is needed. ### 7.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 7.48 for why. ### 7.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 use the Disallowed Rules input on 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. ### 7.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 type 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. ### 7.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. ### 7.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. ### 7.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: - 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. - 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. ### 7.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. ### 7.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. ### 7.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. ### 7.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. ### 7.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. ### 7.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. ### 7.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. ### 7.43 Why do the Solver and Materialize 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 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 also now emits a Warning (previously only a Remark) when it skips geometry placement due to a dimension mismatch. Use the Audit Assembly component’sSlots Without Fitting Modules output to identify every affected Slot precisely before the solve. ### 7.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. ### 7.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. ### 7.46 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. ### 7.47 Common Module design mistakes These are the mistakes that come up most often when people start working with Monoceros. **Making one room = one Module.** Beginners 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. See FAQ 7.8. **Confusing Monoceros with growth algorithms (e.g. WASP).** WASP and similar tools 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. ### 7.48 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. ### What are Connectors and how do they differ from Rules? Connectors are a higher-level abstraction for generating Rules. Instead of manually specifying which Faces can touch, you create named Connectors bound to specific Module Faces and declare which Connector types are compatible via Connector Pairs. Construct Assembly then generates the corresponding Rules automatically, taking rotational symmetry into account. Connectors don't replace Rules - they complement them. You can mix explicit Rules with Connector-generated Rules in the same Assembly. ### When should I use Construct Assembly? Use Construct Assembly for all workflows. It handles Connector-based Rule generation, automatic rotation variant expansion with dimension-aware Slot filtering, integrated audit, and packages everything into a Discrete Assembly. The WFC Solver takes a Discrete Assembly as its sole data input. ### How does Connector rotation compatibility work? Each Connector has symmetry flags defining which rotations look identical (e.g. a USB-C connector looks the same at 0° and 180°). Each Connector has an instance rotation (how the Connector is placed on the Face). Two Connectors are rotationally compatible when their effective rotation sets (symmetry flags shifted by instance rotation) overlap. For example: Connector A at rotation 0° with symmetry {0°} has effective set {0°}. Connector B at rotation 90° with symmetry {0°} has effective set {90°}. These don't overlap - not compatible. But if Connector B has symmetry {0°, 90°}, its effective set is {90°, 180°}, still no overlap with {0°}. If Connector A is at rotation 90° with symmetry {0°}, effective set is {90°}, which overlaps Connector B's {90°, 180°} - compatible. ### Can I mix Connectors with explicit Rules? Yes. Wire explicit Rules to Construct Assembly's Rules input and Connectors + Connector Pairs to the Connector inputs. Construct Assembly merges all rule sources (manual + connector-generated + indifference) into a single deduplicated set. ### 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). Example: 10 modules with all rotations produce 240 variants and up to 172,800 indifference rules. At 42 original modules (1,008 variants), the count reaches ~3 million - the solver's practical ceiling. These numbers assume fully asymmetric cubic modules where all 24 rotations produce geometrically distinct variants and every face is unused. In practice the count is lower because: non-cubic modules produce fewer fitting variants (some rotations change dimensions and are filtered by slot size); every face covered by an explicit Rule or Connector is excluded from the indifferent set. Note: 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 the count above. To reduce rule explosion, add explicit Rules or Connectors to cover more faces. Construct Assembly warns when indifference exceeds 10,000 rules. Note: Monoceros does not currently check module symmetry when generating rotation variants. A module with rotational symmetry (e.g. a cylinder) will still produce all 24 variants even though many are geometrically identical - inflating the variant count and the indifference rule count unnecessarily. Voxel-based deduplication (via Module Rotations or Cull Duplicate Modules) can remove duplicates, but Construct Assembly's built-in rotation expansion does not perform this check. ### What is the Assembly output from the Solver? The Solver outputs a new Assembly with solved Slot states. You can feed this into Materialize Assembly for geometry placement, or into Deconstruct Assembly to inspect the results. The output Assembly contains the same Modules and Rules as the input - only the Slot states change. ================================================================================ ## 8. TIPS & TRICKS ### 8.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). ### 8.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 first saves you from chasing down mysterious solver failures. Wire Audit in parallel with the Solver and check its Report output before investigating solver results. ### 8.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. ### 8.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. ### 8.5 Use attractor-based weight gradients Instead of uniform weights, compute weights per Slot based on spatial criteria: - Distance from a point - Use Grasshopper’sDistancecomponent 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. ### 8.6 Use Cross Reference for bulk Rule creation When you want every Module to connect to every other Module on matching faces, use Grasshopper’sCross Referencecomponent to generate all Module Name pairs. Feed these into RuleFromModules to create Rules for every combination in one step, rather than wiring individual Faces. ### 8.7 Test Rules visually before solving Before running the Solver, use Preview Rule and Preview Rule in Slots to inspect your Rules visually. Preview every Rule as a pair of positioned Modules - this immediately reveals orientation errors, wrong Face assignments, and missing pairings that would otherwise cause mysterious solver failures. ### 8.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. ### 8.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. ### 8.10 Use Add Boundary Layer for irregular Envelopes When constructing irregular Envelopes (L-shapes, courtyards, organic shapes), the Add Boundary Layer component wraps the Envelope in a layer of boundary Grid Boxes, ensuring no isolated cells or diagonal-only connections remain at the edges. The solver requires proper face-to-face adjacency. ### 8.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. ### 8.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’sLock Solver) to prevent unnecessary recomputation. Re-enable when you are ready to see results. ### 8.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. ### 8.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. ### 8.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. ### 8.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 7.48 for why automatic mirroring is not supported. ### 8.17 To exclude a Module from a zone, remove it from allowed names The reliable way to prevent a Module from appearing in a Slot is to remove it from that Slot’s ModuleNames list when constructing the Slot. A zero weight reduces selection probability to zero during observation but the Module can still be placed if constraint propagation leaves it as the only valid option. Removing it from ModuleNames guarantees complete exclusion - the solver cannot place it there under any circumstances. ### 8.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. ### 8.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 fills it in with the size of the final Module set, so every Slot in the Assembly agrees on the same total. No manual List Length wiring or cross-component synchronisation is needed - the Slot preview shows only the allowed count pre-Assembly and the familiar "allowed / total" ratio post-Assembly. ================================================================================ ## 9. EXAMPLE WORKFLOWS ### 9.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 always between two Faces that point in opposite directions on the same axis (for example`+X`and`-X`). 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. 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. - **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. - **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. - **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. - **Construct the Module**- Use Construct Module with name`pipe`, the single Grid Box from step 2, and the line from step 3 as geometry. - **Get the Faces**- Connect the Module to Get Module Faces. It outputs six Faces as separate named parameters:`+X`,`-X`,`+Y`,`-Y`,`+Z`,`-Z`. - **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, see2.5 Defining Rules from Faces. - **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. - **Construct Slots**- Use Construct Slot with all Grid Boxes from the Envelope grid and the Module Name`pipe`(it is possible to use the Module itself as the input as it casts to the Module Name automatically). Every Slot in the Envelope will allow this one Module, giving the solver maximum freedom. - **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. - **Solve**- Connect the Assembly to the WFC Solver. The solver runs Wave Function Collapse and returns an output Assembly with solved Slot states. - **Materialize**- Connect the solved Assembly to Materialize Assembly. For each solved Slot, the component locates the corresponding Module, 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. ### 9.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 inexample 1.1). - **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 Construct Assembly. 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 see2.6 Working with 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. See2.21 Rules from Face Groupsfor the full technique applied to larger Module libraries. #### Generating rotation variants automatically 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. See2.7 Automatic Module rotations. #### 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. See9.13 Disallowing Rules. 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 (see2.5 Defining Rules from Faces) 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. Construct Assembly removes them by set subtraction from the allowed set. 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. See2.3 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. See1.10 Boundary handling. #### 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. See2.4 Rotating the envelope base plane. ### 9.3 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 - 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. - 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. - 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. - 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. ### 9.4 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 - Take any working definition that produces a solved result. Note where the output appears in the viewport. - 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. - Observe the Envelope Grid Boxes in the viewport: they all shift and rotate together, maintaining their spacing and relative orientation. - 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. ### 9.5 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. When indifference is enabled on Construct Assembly, 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. This component does**not**use Grasshopper’s default element-wise data matching. Instead it uses cross-matching: every item in the Source list is tested against every item in the Target list, producing all valid opposite-direction combinations. If you feed three source Faces and four target Faces, you get up to twelve Rules, not three. This is intentional - the common use case is to allow all listed faces to connect to all listed faces. If you want strictly one-to-one pairing (first source with first target, second with second, and so on), graft both inputs before connecting them. Each branch will then contain a single Face, forcing the component to process one pair at a time. #### Method B: From Module Names and directions Use Construct Rules from Modules to create Rules by specifying source Module Names and target Module Names per direction. This is convenient for batch Rule creation when you know which Module types should be adjacent but do not need to select individual faces. #### Method C: 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 D: Suggest from geometry Suggest 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 face in opposite directions, have the same face dimensions, and their sets of local-space points are identical within tolerance (order-independent). The component outputs Rules for all matched pairs directly. 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 D 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 E 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 D 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 E: Suggest from voxels Suggest Rules from Voxels works like Method D but compares voxelised face patterns instead of exact geometry. It rasterises each Module at a configurable**Voxel Resolution**(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 with matching voxel patterns. 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 raster pattern at the chosen resolution, they are matched. This makes Method E more forgiving than Method D 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 D 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 voxel cell are invisible to the matcher, and modules that look compatible at voxel resolution may not fit perfectly at the geometric level. Both Method D and Method E 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. ### 9.6 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.5, 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.5, operating on the flattened Module list or its Faces, then flatten and merge the Rule output before connecting to Construct Assembly. ### 9.7 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 - Construct the base Module with Construct Module as normal. - 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. - 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. - 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 insection 2.5to define Rules for the variant list. The Module Rotations output is a flat list of ordinary Modules, so every workflow insection 2.5applies without modification. #### Wiring the solver - Pass the full Module Rotations output list (all variants) to the Modules input of Construct Assembly. - 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. - Feed the Modules, Slots, and Rules into Construct Assembly, then connect the Assembly to the WFC Solver. Materialize Assembly will place each variant at the correct position and orientation; the geometry of each rotated variant was already baked into the Module by Module Rotations. #### 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. ### 9.8 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 sizesUnique Y sizesUnique Z sizesMax distinct cell sizesTypical scenario1111Uniform grid - one Module size, equivalent to Homogeneous Grid.2112One wider column in an otherwise uniform grid. Two Module sizes needed.2214One wider column*and*one deeper row. Four Module sizes.2228One different value in each axis. Eight Module sizes.33218Three column widths, three row depths, two layer heights. Eighteen Module sizes.44464Four 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 - 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. - 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. - 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. ### 9.9 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. Weights must be zero or positive; negative values are not accepted and will cause an error. A weight of exactly`0.0`is valid and means the Module has the lowest possible probability - it will almost never be chosen during observation but remains in the allowed set. Construct Slot silently clamps it to the smallest representable positive value and issues a Remark. To exclude a Module from a Slot entirely, remove it from the allowed-Module list rather than setting its weight to 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 - Use Construct Slot with the**Weights**input. - 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. - 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. - 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. - Start from the Grid Box list you would otherwise pass to Construct Slot. - 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. - 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. - 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. ### 9.10 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 - Create your main Envelope Grid Boxes using Homogeneous Grid or Heterogeneous Grid. - 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. - Define a boundary Module (typically with no geometry, named e.g.`boundary`). - Create Slots from the boundary Grid Boxes, allowing only the boundary Module. - Define Rules connecting the boundary Module’s Faces to interior Modules. - 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. ### 9.11 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 Geometryin 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). - Run Grid Boxes from Geometry with Fill Method 0 to get the surface-aligned layer. - Connect the output to Add Boundary Layer. Set Layers to 1 or more. Disable the directions you do not want to grow into. - 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. - 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 in2.10 Boundary handling. #### 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 in2.10 Boundary handling. #### 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. ### 9.12 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 - Identify the Grid Boxes where you want to fix a Module. - Create Slots from those Grid Boxes, allowing only the desired Module Name. - Create Slots for the remaining Grid Boxes, allowing all Module Names. - 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 aMegamoduleat 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. ### 9.13 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 - Generate your full Rule set using Suggest Rules from Voxels or Suggest Rules from Geometry. Connect it to the**Allowed Rules**input of Construct Assembly. - 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. - 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. ### 9.14 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 - Connect your allowed Rules and/or Connector Pairs to Construct Assembly as usual. - Identify the Faces that need exclusivity - the faces that must connect in only one specific way. - 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. - 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. See2.15 Megamodule construction. - **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. ### 9.15 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 sibling Grid Boxes as dashed ghost outlines so the full megamodule footprint is visible even when viewing a single part in isolation. #### Steps - Select a group of adjacent Grid Boxes for the Megamodule. - Connect them to Construct Megamodule with a name and optional geometry. - 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 Suggest Rules or constructing external Rules. - 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. Only the first sub-module (_p0) materializes the geometry - the Materialize component skips geometry output for the remaining parts to avoid duplication. #### Auto-rotation Enable Rotate X, Rotate Y, or Rotate Z on the Construct Megamodule component to have the Solver automatically generate rotated variants of the entire megamodule. The Solver applies the same rotation to all sub-modules and remaps both internal and external Rules accordingly. Rotated variants are named with a rotation suffix (e.g. _z90, _z180, _z90y90). #### 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. See9.14 Rule Exclusivity. ### 9.16 Materializing results Materialize converts solved Slots into visible Rhino geometry. It matches each Slot to the Module it holds and orients - translates and rotates - that Module’s geometry to the correct position in space. #### Inputs - **Assembly** - A solved Discrete Assembly (from the WFC Solver 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**- Face 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. ### 9.17 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.ParameterDefaultDescription**Random Seed**42Controls the random choices during observation. Different seeds produce different results. Set to a fixed value for reproducibility.**Max Attempts**variesMaximum 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**unlimitedMaximum number of observation steps per attempt. Limits computation for very large grids. Set to a lower value to get partial results.**Return First**falseIf 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. SeeFAQ 1.13. There are no good or bad seeds - all seeds are equivalent; seeFAQ 1.43. ### 9.18 Audit-driven debugging When the solver fails or produces unexpected results, run Audit before making any changes. It performs a comprehensive static analysis of the entire setup and reports every misconfiguration it finds. 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. ### 9.19 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, previewing how the Rule connects them. Also outputs the placed Modules with rotation flags preserved. ### 9.20 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 - Build the Grid Box list and feed it into both Grid Topology and your Slot constructor. - Decompose the Relative Coordinates; take the X component (or whichever axis represents depth). - Remap that value from its actual min–max range to`0.1 – 1.0`. - 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. - After the first solve, take the solved Slots from the WFC Solver output. Each is fully deterministic: exactly one Module name is allowed. - 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. - 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. - For the expanded selection, construct new open Slots from those Grid Boxes with the full (or broader) allowed Module list - non-deterministic. - For every other Grid Box, keep the solved Slots as-is. Those locked Slots act as fixed constraints during the next solve. - 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 (see1.10) is the more direct tool. ### 9.21 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 opposing Faces in that list and automatically skips any two that point in the same direction. 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. - Place a Rhino point on the Module face you want to identify. - Wire the Module list and the point into Faces from Point. - The output is the`FaceId`for that face. Use it wherever a Face is expected. #### 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. ### 9.22 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 - 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. - Design the Faces so that pipe openings align at Module boundaries - centrelines should start and end at the centre of the Faces. - Solve the grid normally. - Materialize to get placed centrelines at every Slot. - Flatten and join the curves using Grasshopper’s**Join Curves**component. - 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. ### 9.23 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 - 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. - 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. - 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. When Require Terminators is on, Construct Assembly automatically skips boundary wrapping, terminator rules, and self-adjacency on flat axes (extent == 1), so you do not need to supply Terminators for the collapsed dimension. 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 - see2.10 Boundary handling. - 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. ### 9.24 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, see2.10 Boundary handling. ### 9.25 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 - 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. - Use Homogeneous Grid with this diagonal to generate Grid Boxes. - 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. - 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 (see2.8 Heterogeneous Grid). ### 9.26 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 - Run Suggest Rules from Voxels to generate the bulk Rule set automatically. - Create additional explicit Rules for specific pairings the suggestion missed (e.g. a structural Module that must always sit under a floor Module). - Connect any Rules that should not exist to the**Disallowed Rules**input of Construct Assembly (e.g. two incompatible aesthetic Modules). - Connect all Rule lists (automatic + manual) to the**Allowed Rules**input of Construct Assembly. Duplicate Rules are harmless - Construct Assembly deduplicates internally. ### 9.27 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 - Create the full Grid Box set. - 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. - 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]`. - 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`. - 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. ### 9.28 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 - Solve the grid and obtain the solved Slots. - 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. - Repeat for each Module Name you care about, or use a Grasshopper loop to process all names at once. - 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. ### 9.29 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) - Materialize the result to get placed geometry. - If Module geometry is Brep-based, convert to Mesh using Grasshopper’s**Mesh Brep**component. - Join all meshes using**Mesh Join**. - Bake to Rhino and export as STL, OBJ, or 3MF. #### Part list export (fabrication, CNC) - Use**Occurrence Count**to count each Module type. - Use the Materialize**Transforms**output to extract position and rotation per placement. - Export placement data as CSV using Grasshopper’s text writing components: columns for Module Name, X, Y, Z, rotation angle. - Use this CSV for pick-and-place machines, assembly instructions, or BIM software. #### Unfolding (laser cutting, sheet fabrication) - Materialize the result. - Use Grasshopper’s**Unroll Brep**or third-party unfolding plug-ins to flatten Module geometry. - Add Module names or labels to each flattened piece for assembly reference. - Export as DXF or PDF for the laser cutter or CNC router. ### 9.30 Auditing an Assembly Connect the Assembly from Construct Assembly to Audit Assembly in parallel with the WFC Solver. Audit Assembly exposes more than 30 diagnostic outputs: Key outputs to check first: - **Report** - human-readable summary of all findings. - **Potential Over-Constraint** - true when uncovered Faces exist and indifference is disabled; almost always causes solver contradictions. - **Faces Not In Rules** - Faces that become indifferent when indifference is enabled. Connect to Face Preview to visualize them. - **Slots Without Fitting Modules** - Slots whose allowed Modules all have mismatched box dimensions (will appear empty after Materialize Assembly). - **Singly Constrained Faces** - Faces in 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, providing no constraint. Common patterns: - Solver contradicts immediately → check Potential Over-Constraint and Faces Not In Rules. - Results identical across seeds → check Singly Constrained Faces. - Some Slots visually empty → check Slots Without Fitting Modules. ### 9.31 Iterative solving with partial results Solve in stages by limiting observations, then modify and re-solve: 1. **Partial solve** - Set Max Observations on the WFC Solver to a low number (e.g. 20–50% of total Slots). The solver assigns some Slots and leaves the rest non-deterministic. 2. **Deconstruct** - Connect the output Assembly to Deconstruct Assembly to get the partially solved Slots, expanded Modules, and Rules. 3. **Modify** - Edit the Slots: force specific Modules into Slots by replacing their allowed list, remove Modules from regions, adjust weights, or change Rules. 4. **Reassemble** - Feed modified Modules, Slots, and Rules into a second Construct Assembly to produce a new Assembly with partial solution baked in. 5. **Complete solve** - Connect to another WFC Solver with Max Observations set to unlimited. The solver respects deterministic Slots and fills only the remaining non-deterministic ones. 6. **Materialize** - Connect the fully solved Assembly to Materialize Assembly. Tips: - Deterministic Slots (1 allowed Module) are treated as fixed by the solver. - Chain more than two passes for fine-grained control. - Use Audit Assembly after reassembly to verify modifications. - Use Changed Slots to compare Slots before and after each pass. ================================================================================ 10. LICENSING ================================================================================ Monoceros 3 has four license tiers. All tiers use the same plugin -- the tier is determined by your Gumroad purchase email. - **Free** -- No purchase required. Full functionality on any envelope size, with a rate cap on the WFC Solver component: a limited number of solver runs per fixed time window (aligned to UTC clock boundaries). Each solver invocation counts as one run regardless of the Max Attempts setting. Once the cap is hit, the solver shows a "limit reached" message and displays when the next run becomes available. - **Annual** -- Yearly subscription, unlimited solver runs, 7-day offline grace period. Falls back to Free mode when the subscription ends. - **Edu** -- Discounted annual license for students and educators, 7-day offline grace period. Same terms as Annual otherwise. - **Lifetime** -- One-time purchase, unlimited solver runs permanently, 1-year offline grace period. Never expires. All tiers share a hard limit of 16,370 distinct Module names. Each rotation variant counts as a separate name toward this limit. The WFC Solver component also 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 burning through the free tier's run budget. Connect a Boolean Toggle or a Button component. License validation requires an internet connection. On successful validation, licenses are cached locally so the plugin works offline for the grace period (7 days for Annual/Edu, 1 year for Lifetime). After that, the plugin falls back to Free mode until it can reach the server again. On first launch, the plugin may show an optional registration dialog inviting you to enter your email for product updates. Registration is separate from licensing -- it is not required to use the Free tier and can be dismissed. The plugin transmits only two pieces of data to the license server: the email address associated with the Gumroad purchase, and an anonymous device identifier (a random UUID generated on first launch). No project data, geometry, hardware fingerprints, usernames, or computer names are ever transmitted. ================================================================================ 11. ABOUT THE AUTHOR ================================================================================ Monoceros is created by Ján Pernecký (Ing. arch. Mgr. art.) - a computational designer, architect, software developer, and educator based in Bratislava, Slovakia. ## Background Ján holds degrees in architecture (Ing. arch., Slovak University of Technology) and arts (Mgr. art., Academy of Fine Arts and Design, Bratislava), with additional studies at KTH Stockholm, Universität für angewandte Kunst Wien, and PhD research in computational design at UMPRUM Prague (2019–2024) focused on constraint satisfaction programming, evolutionary algorithms, and Wave Function Collapse implementations. ## Professional Experience - **CTO / Co-Founder / Creative Director at Qubu (2022–2025)** - Deep-tech company streamlining the building industry through computational design. Proposed and led development of hierarchical constraint-based design pipelines, multi-objective optimization engines (genetic algorithms, simulated annealing), daylighting evaluation, spatial field methods, and domain-specific constraint solvers. - **CEO / Founder of Subdigital s.r.o. (2016–2022)** - Computational design studio focused on digitally enhanced design, interactive installations, and custom software tools. Clients included HB Reavis, JTRE, and the Slovak Design Center. - **Chairman / Founder of rese arch n.g.o. (2010–2020)** - Non-profit platform for architectural research and education. Produced 150+ hours of educational video content, organized conferences and workshops across Europe, and built an international network of computational design practitioners. - **Lead Developer at ICE Industrial Solutions / Coral (2022)** - Custom 3D printing software pipeline for construction-scale additive manufacturing. - **Product Architect / R&D Lead at Vectary (2016–2017)** - Research and development for web-based 3D modeling SaaS, including subdivision surface algorithms. - **Curator, 13th Venice Architecture Biennale (2012)** - Creator, curator, and producer of the Slovak and Czech Pavilion exhibition "Asking Architecture," featuring 18 artists and collectives. - **Practicing architect (2002–2012)** at firms including BKK-3 (Vienna), GutGut (Bratislava), and as co-founder of ask peprsző. ## Software Projects - **Monoceros (2020–2021)** - Discrete assembly toolkit implementing Wave Function Collapse for 3D spatial design. Open-source Grasshopper plugin (C#). Co-developed with Ján Tóth. 150,000+ downloads. - **Qubu Platform (2022–2025)** - Comprehensive computational design system: hierarchical constraint decomposition, multi-objective optimization, daylighting simulation, and spatial optimization solvers. - **H.U.R.B.A.N. Selector (2019–2020)** - Voxel-based 3D morphing tool using evolutionary algorithms. Developed with the Slovak Design Center. - **Coral - 3D Printing Pipeline (2022)** - Slicer and geometry processor for construction-scale additive manufacturing. - **Boids Library (2014)** - Swarm simulation library for Grasshopper implementing Reynolds' flocking algorithms. ## Areas of Expertise - Constraint satisfaction problems (CSP) and discrete optimization - Wave Function Collapse (WFC) algorithm design and implementation - Evolutionary computation: genetic algorithms, simulated annealing, multi-objective optimization - Hierarchical constraint decomposition for complex spatial problems - Computational geometry and spatial reasoning - Performance-based design optimization (daylighting, energy, spatial efficiency) - Parametric and generative design systems - Agent-based and emergent design approaches - 3D printing pipelines and toolpath generation - CAD/BIM tool development and integration (Grasshopper/Rhino) - Programming languages: C#, Rust, Python ## Teaching and Education Ján has taught computational design at institutions across Europe and beyond, including The Bartlett School of Architecture (UCL), Academy of Architecture Amsterdam, University of Toronto, Universität für angewandte Kunst Wien, IAAC Barcelona, DigitalFUTURES, UMPRUM Prague, FA VUT Brno, IUAV Venice, and multiple universities in Slovakia and Poland. He has supervised master's theses and led intensive workshops on discrete assembly, Wave Function Collapse, parametric design, robotic fabrication, and generative methods. Through rese arch n.g.o., he organized dozens of workshops and produced extensive online courses on Grasshopper, optimization, and environmental simulation. ## Selected Publications - "Exuberance and Evolutionary Method" in Kompaktní město (2012) - "A New Argument for the Digital Paradigm" in A&U: Architecture and Urbanism (2018) - "ADM: Digital Shift" in World Transactions on Engineering and Technology Education (2015) - "ADE: Performance-based Generative Design and Fabrication" in Applied Mechanics and Materials (2015) - Featured in Česká a Slovenská architektúra 1971–2011 (2014) ## Selected Awards - Venice Biennale 2012 - Creator and Curator, Slovak & Czech Pavilion - Cena Dušana Jurkoviča 2010 - Honorable Mention (Holocaust Memorial) - Venice Biennale 2008 - Second place, Slovak Exposition - Shinkenchiku Competition 2003 - Honorable Mention (jury: Steven Holl) ## Contact To get in touch with Ján Pernecký, use the contact form at https://www.monoceros.tools ================================================================================ 12. LINKS ================================================================================ Documentation: https://www.monoceros.tools/monoceros-v3-manual/ Examples: https://www.monoceros.tools/monoceros-v3-manual/workflows.html FAQ: https://www.monoceros.tools/monoceros-v3-manual/faq.html Download: https://www.food4rhino.com/app/monoceros