Canvas organisation and workflow tools for Grasshopper. Bedrock adds keyboard-driven commands, smart snapping, custom wire shapes, wire display management, and an anti-freeze system — all accessible from a single menu in the Grasshopper toolbar.
- Build and install the plugin (see Building below).
- Open Grasshopper — the Bedrock menu appears in the toolbar.
- All features work out of the box with sensible defaults. Snapping is on; wire display and AntiFreeze are off until you enable them.
All keyboard shortcuts are defined in ShortcutRegistry.cs and can be changed in one place.
| Command | Shortcut | Description |
|---|---|---|
| Add Param Left | Ctrl+Alt+Left |
Insert parameters upstream of selection |
| Add Param Right | Ctrl+Alt+Right |
Insert parameters downstream of selection |
| Remove Param | Ctrl+Alt+Down |
Remove standalone parameters and splice wires through |
| Layout Selection | Ctrl+Alt+L |
Auto-layout selected components by data flow |
| Merge Inputs | Ctrl+Alt+X |
Insert Merge components on multi-source inputs |
| Duplicate | Alt+D |
Duplicate selection directly below |
| Rename From Source | Alt+S |
Rename parameter to match its source |
| Rename From Recipient | Alt+R |
Rename parameter to match its recipient |
| Replace Relay → Param | Alt+Shift+R |
Convert relays to typed parameters |
| Select Connected | Alt+Click |
Select object and all direct neighbours |
| Delete Wire | Alt+Click on wire |
Delete a wire without selecting the parameter |
| Quick Group 1–5 | Alt+1 – Alt+5 |
Wrap selection in a coloured group preset |
| Open Test Dashboard | Ctrl+Shift+T |
Open the testing dashboard window |
| Run All Tests | Ctrl+Alt+T |
Trigger a re-solve to run all assertion components |
Additional toggles and submenus in the Bedrock menu:
| Setting | Default |
|---|---|
| Enable Snapping | ✅ On |
| Snap to Edges | ✅ On |
| Snap to Centers | ✅ On |
| Snap to Wires | ✅ On |
| Wire Display by Length | Off |
| AntiFreeze | Off |
| Wire Shape | Default |
Inserts new parameters upstream (to the left) of your selection, intercepting all existing input connections.
How it works:
- Select one or more components or standalone parameters.
- Press
Ctrl+Alt+Left. - For each input on a selected component (or for a selected standalone parameter), a new
Param_GenericObjectis created to the left. - All existing sources are moved from the target input to the new parameter, and the new parameter is wired into the target.
- The new parameter inherits its nickname from the first source (or from the target if there are no sources).
Use case: Quickly intercept incoming data with relay-like parameters for readability or debugging.
Inserts new parameters downstream (to the right) of your selection, intercepting all existing output connections.
How it works:
- Select one or more components or standalone parameters.
- Press
Ctrl+Alt+Right. - For each output on a selected component (or for a selected standalone parameter), a new
Param_GenericObjectis created to the right. - The target is wired into the new parameter, and all existing recipients are rewired to read from the new parameter instead.
- The new parameter inherits the target's nickname.
Use case: Fan out outputs through named parameters before they reach downstream consumers.
Removes standalone parameters from the canvas and splices their connections through — sources are wired directly to recipients.
Requirements:
- The selected parameter must be standalone (not an input/output slot on a component).
- It must have both sources and recipients (otherwise there is nothing to splice).
How it works:
- For each recipient of the parameter, all of the parameter's sources are connected directly.
- The parameter is then removed from the document.
Use case: Clean up intermediate relay parameters that are no longer needed.
Automatically arranges selected components in a left-to-right layout following data-flow order, with wire-crossing minimisation and vertical alignment.
How it works:
- Topological sort — Components are assigned to columns based on their depth in the data-flow graph (rightmost outputs = level 0, walking backwards).
- Crossing minimisation — A median heuristic sweeps back and forth to reorder nodes within each column, reducing wire crossings.
- Vertical positioning — A Brandes–Köpf algorithm runs four alignment passes and takes the median, producing balanced vertical coordinates with a minimum of 20 px padding between nodes.
- Connection alignment — Standalone parameters are aligned to the inputs/outputs they connect to.
The layout preserves the vertical centre of your original selection.
Use case: Tidy up a messy patch — select the components you want to reorganise and press Ctrl+Alt+L.
For every component input that has two or more sources, a native Grasshopper Merge component is inserted automatically.
How it works:
- Each original source is wired into a separate input of the new Merge component.
- The Merge output is connected to the target input.
- Input parameters on the Merge are named after their source.
- The Merge is positioned to the left of the target component.
Use case: Consolidate multi-source inputs into explicit Merge nodes for clarity.
Duplicates the current selection (components and groups) directly below the originals.
How it works:
- The bounding box of the selection is computed, and copies are placed below with 10 px padding.
- Groups and their members are duplicated together.
- External connections on the copies are severed — only internal wiring within the duplicated set is kept.
- Full undo support.
Use case: Quickly create a variant of a sub-graph to experiment with.
Sets the nickname of each selected parameter to match its first upstream source's nickname.
Sets the nickname of each selected parameter to match its first downstream recipient's nickname.
Both commands support undo/redo.
Use case: Keep relay-style parameters labelled consistently with the data they carry.
Converts Grasshopper relay objects into properly typed parameters that match the data flowing through them.
How it works:
- If relays are selected, only those are converted. Otherwise, all relays in the document are converted.
- For each relay, Bedrock inspects the source output type and creates the matching parameter type from
Grasshopper.Kernel.Parameters(e.g. a relay carrying curves becomes aParam_Curve). - If the type cannot be resolved, a
Param_GenericObjectis used as a fallback. - The new parameter inherits: position, display mode, nickname (from the source), all connections, and locked state.
Use case: Replace anonymous relays with typed parameters that show meaningful icons and enable type checking.
Selects the clicked object and all its direct neighbours (one hop in either direction via wires).
Use case: Quickly grab a component and everything wired to it for moving or inspecting.
Deletes the wire under the cursor directly, without needing to select the target parameter first. Works with both default and custom wire shapes.
Bedrock adds a snap-guide system that helps you align objects as you drag them on the canvas. Visual guide lines appear when an object's edge or centre lines up with another object.
Toggle from the menu:
| Option | Default | What it does |
|---|---|---|
| Enable Snapping | On | Master toggle for the entire snapping system |
| Snap to Edges | On | Snap to the top, bottom, left, and right edges of other objects |
| Snap to Centers | On | Snap to the centre (pivot) of other objects |
| Snap to Wires | On | Snap to wire grip points so parameters line up with their connections |
Visual indicators:
- Green solid line — edge alignment
- Magenta dashed line — centre alignment
- Red dashed line — wire grip alignment
Behaviour details:
- Snapping activates during drag-move and panel-resize operations.
- Only objects within a 600-unit radius are considered.
- Alt+Drag (copy mode) is supported — snapping uses absolute positions instead of relative deltas.
When enabled, this feature automatically adjusts wire visibility based on how long wires are on the canvas. Short wires remain fully visible, medium-length wires become faint, and very long wires are hidden entirely.
Enable: Check Wire Display by Length in the Bedrock menu.
| Threshold | Default | Effect |
|---|---|---|
| Faint threshold | 500 px | Wires longer than this become faint |
| Hidden threshold | 1000 px | Wires longer than this are hidden |
Wire display modes update automatically when:
- A solution completes
- Objects are added or removed
- You finish dragging components
When you disable the feature, all wires return to their original display mode.
Use case: Reduce visual clutter in large definitions where long wires cross the entire canvas.
Bedrock can replace Grasshopper's default Bézier wire curves with alternative routing styles. Choose a wire shape from the Wire Shape submenu:
| Shape | Description |
|---|---|
| Default | Grasshopper's native Bézier curves — no modification |
| Line | Right-angle routes with rounded corners |
| Polyline | Three-segment paths with an adjustable bend point |
| Electric | Dual-radius corners for a kinked, circuit-board look |
Each shape has parameters (extend distance, corner radius, pitch multiplier) that control its geometry. These are stored in Grasshopper's settings and persist across sessions.
Wire shapes are applied globally via Harmony patches on Grasshopper's paint system — all wires in every document use the selected shape.
AntiFreeze prevents Grasshopper from locking up during long-running solutions. When enabled, it aborts any solution that exceeds the configured timeout.
Configure from the menu: Bedrock → AntiFreeze
| Option | Effect |
|---|---|
| Off | AntiFreeze disabled |
| 2 seconds | Abort solutions running longer than 2 s |
| 5 seconds | Abort solutions running longer than 5 s |
When a solution is aborted:
- All volatile data is cleared to prevent the UI from freezing on partial results.
- A red warning banner fades in at the top-left of the canvas: "AntiFreeze: solution aborted after Ns timeout" (visible for ~3 seconds).
- A message is logged to the Rhino console.
Use case: Protect against accidentally triggering expensive computations (e.g. connecting a large data set to a heavy solver).
A canvas widget that draws floating component name labels above each object. This gives you an at-a-glance overview of what each component does without hovering.
Toggle: Click the OverSight button in the widget toolbar at the bottom of the Grasshopper canvas.
Settings (persisted in Grasshopper settings):
| Setting | Default | Description |
|---|---|---|
| Enabled | Off | Master toggle |
| Label Colour | Black | Colour of the floating labels |
| Exceptions | Scribble, Panel, Sketch, Button, Boolean Toggle, Number Slider | Components excluded from labelling |
Labels automatically hide when zoomed out past 50% of the default zoom level.
Wrap selected objects in a coloured group with a single keypress. Five preset slots are available, each with a configurable name and colour.
| Shortcut | Action |
|---|---|
Alt+1 |
Apply Quick Group slot 1 |
Alt+2 |
Apply Quick Group slot 2 |
Alt+3 |
Apply Quick Group slot 3 |
Alt+4 |
Apply Quick Group slot 4 |
Alt+5 |
Apply Quick Group slot 5 |
Configure slot names and colours via Bedrock → Quick Groups → Configure… in the menu.
When you rename a standalone parameter, the new nickname is automatically propagated through all connected standalone parameters in the chain. Propagation stops at component boundaries (i.e. it only flows through parameter-to-parameter connections).
This feature is always active and requires no configuration. All changes are batched into a single undo record.
Use case: Rename one parameter in a relay chain and have all linked parameters update instantly.
Automatically replaces missing-plugin proxy objects (placeholder components that appear when a plugin is not installed) with native Grasshopper parameters on document load.
Built-in rules:
| Missing Component | Replacement |
|---|---|
| AutoGraph "Data Description" | Param_GenericObject |
The replacement preserves the original component's name, position, and all wire connections.
Extensible: Add new swap rules by subclassing ProxyReplacementRule and registering in ProxyReplacementConfig.Rules. Each rule has an independent enable/disable flag.
Note: This feature is disabled by default and is currently toggled in code via ProxyReplacementConfig.Enabled.
Bedrock includes a full set of assertion components for testing Grasshopper definitions. All components appear under the Bedrock tab in the Testing subcategory. Each assertion outputs a TestResult object and a Passed boolean, with a dynamic icon that turns green (pass), red (fail), or orange (error) after each solve.
| Component | Nickname | Inputs | Description |
|---|---|---|---|
| Assert Equal | AEq | Actual, Expected, Tolerance | Value equality with numeric tolerance (supports numbers, Point3d, Vector3d, strings) |
| Assert Not Equal | ANEq | Actual, Expected, Tolerance | Asserts two values are not equal |
| Assert True | ATru | Value (bool) | Asserts a boolean value is true |
| Assert False | AFls | Value (bool) | Asserts a boolean value is false |
| Assert Null | ANul | Value | Asserts a value is null |
| Assert Not Null | ANNl | Value | Asserts a value is not null |
| Assert In Range | ARng | Value, Min, Max | Asserts a number falls within an inclusive range |
| Component | Nickname | Inputs | Description |
|---|---|---|---|
| Assert Identical | AId | N tree inputs (variable) | Asserts inputs have identical structure and content |
| Assert Structure | AStr | N tree inputs (variable) | Asserts inputs share the same tree paths |
| Assert Structure and Length | AStL | N tree inputs (variable) | Asserts same tree paths and per-branch item counts |
| Assert List Length | ALen | Data (tree), Count | Asserts a tree has the expected total item count |
| Assert Contains | ACnt | Data (tree), Item | Asserts a data tree contains a specific item |
| Component | Nickname | Inputs | Description |
|---|---|---|---|
| Assert Geometry Valid | AGeo | Geometry | Asserts geometry is non-null and valid (accepts Box, Brep, Curve, Mesh, etc.) |
| Assert Point Equal | APtEq | Actual, Expected, Tolerance | Asserts two points are equal within a per-coordinate tolerance |
| Component | Nickname | Inputs | Description |
|---|---|---|---|
| Assert No Message | ANoMsg | Component, Level | Asserts component(s) have no runtime messages at or above a threshold (Blank/Remark/Warning/Error) |
| Assert Has Message | AHMsg | Component, Text | Asserts component(s) produce a runtime message containing specific text |
All assertion inputs include a Name (N) parameter for labelling the test. The variable-input tree assertions support adding/removing inputs via the ZUI (zoom-able user interface).
A floating Eto Forms window that displays all assertion results in the current document at a glance.
Open: Ctrl+Shift+T or Bedrock → Testing → Open Dashboard
The dashboard shows a tree grid with four columns:
| Column | Content |
|---|---|
| Icon | Pass (✓), Fail (✗), or Error (!) indicator |
| Test | Assertion name, grouped by containing GH_Group |
| Status | Pass / Fail / Error / Not Run |
| Message | Failure reason or error details |
A summary bar at the top shows the total pass/fail counts. The dashboard updates automatically whenever the document finishes solving and stays on top of the Grasshopper window.
Interactions:
- Double-click a row to zoom the canvas to that assertion component.
- Export results as JSON, JUnit XML, or Markdown via the Export button.
- Re-run triggers a document re-solve to refresh all results.
Components for collecting assertion results into reports and serializing them to standard formats. All appear under Bedrock → Testing in the component panel.
| Component | Nickname | Inputs | Outputs | Description |
|---|---|---|---|---|
| Collect Report | TRpt | Results, Source | Report | Aggregates multiple TestResult objects into a single TestReport |
| Report Viewer | RVw | Report | All Passed (bool) | Displays a pass/fail summary as a component message with dynamic icon |
| Component | Nickname | Inputs | Outputs | Description |
|---|---|---|---|---|
| Report to JUnit | RJU | Report | XML (string) | Serializes a report to JUnit XML format for CI integration |
| Report to JSON | RJs | Report | JSON (string) | Serializes a report to JSON format |
| Report to Markdown | RMd | Report | Markdown (string) | Serializes a report to a Markdown table |
| Component | Nickname | Inputs | Outputs | Description |
|---|---|---|---|---|
| Test Runner | TRun | Source, Server, API Key, Run | Reports, Summary, All Passed | Solves .gh files via Rhino.Compute and collects test results |
The Test Runner sends Grasshopper definitions to a running Rhino.Compute instance, collects assertion results, and outputs a list of TestReport objects. It runs asynchronously — the component shows progress messages while files are being solved.
Bedrock includes a standalone command-line tool (Bedrock.Testing.CLI.exe) for running Grasshopper test definitions via Rhino.Compute. It is included in the Yak package and can also be built separately.
Bedrock.Testing.CLI run <path> [options]
| Option | Default | Description |
|---|---|---|
<path> |
(required) | Path to a .gh file or folder |
--server |
http://localhost:6500 |
Rhino.Compute server URL |
--apikey |
— | API key for Rhino.Compute |
--format |
text |
Output format: text, junit, tap, json, markdown |
--output |
— | Write report to file instead of stdout |
--recurse / --no-recurse |
--recurse |
Recurse into subfolders |
--timeout |
60 |
Per-file solve timeout in seconds |
--parallel |
1 |
Number of files to solve in parallel |
--verbose |
off | Show individual results for passing files |
| Code | Meaning |
|---|---|
| 0 | All tests passed |
| 1 | One or more tests failed |
| 2 | Error (connection failure, invalid path, etc.) |
Bedrock.Testing.CLI run ./tests --format junit --output results.xml --parallel 4cd Bedrock/Testing.CLI
dotnet build
The CLI targets net7.0 and depends on Bedrock.Testing.Goo (the shared data library).
cd Bedrock
dotnet build
This produces two builds, each with a .gha file (Grasshopper's plugin format):
bin/Debug/net48/Bedrock.gha— for .NET Framework runtimebin/Debug/net7.0-windows/Bedrock.gha— for .NET Core runtime
- Rhino 8 (with Grasshopper)
- .NET SDK 7.0+ (for building)
- .NET Framework 4.8 targeting pack (installed with Visual Studio or separately)
Set the RHINO_PACKAGE_DIRS environment variable to point to the build output folder, then launch Rhino. Grasshopper will discover and load the plugin automatically.
PowerShell (one-time):
$env:RHINO_PACKAGE_DIRS = "C:\Users\<you>\path\to\Bedrock\bin\Debug\net7.0-windows\"
& "C:\Program Files\Rhino 8\System\Rhino.exe" /netcore /runscript="_Grasshopper"Copy the built Bedrock.gha (and its dependencies) into Grasshopper's component folder:
%APPDATA%\Grasshopper\Libraries\
Then restart Rhino/Grasshopper.
The repository includes Publish-Yak.ps1 to build and optionally publish a .yak package for the Rhino package manager.
- Rhino 8 installed (the script uses the Yak CLI bundled at
C:\Program Files\Rhino 8\System\Yak.exe) - .NET SDK 7.0+
.\Publish-Yak.ps1 -SkipPushThis will:
- Run
dotnet buildin Release mode targetingnet7.0-windows. - Publish
Bedrock.Testing.CLIas a single-file executable. - Stage
Bedrock.gha, companion DLLs,Bedrock.Testing.CLI.exe,manifest.yml, and the icon into a temporaryyak-stagingdirectory. - Run
yak buildto produce the.yakfile. - Copy the resulting
.yakto the repository root.
.\Publish-Yak.ps1You will be prompted to authenticate via yak login if not already logged in.
.\Publish-Yak.ps1 -TestServer| Flag | Effect |
|---|---|
-Configuration Debug |
Build in Debug instead of Release |
-SkipBuild |
Skip dotnet build and use existing build output |
-SkipPush |
Create the .yak file without pushing it |
-TestServer |
Push to https://test.yak.rhino3d.com instead of production |
The following Grasshopper plugins were used as reference and inspiration during the development of Bedrock: