| name | rhino3d-scripts |
|---|---|
| description | Authoring and debugging scripts for Rhinoceros 3D (Rhino 8 and later). Use when asked to write RhinoScript (VBScript / .rvb / .vbs), RhinoPython, or RhinoCommon-based scripts; automate Rhino modeling tasks; build command macros; manipulate Rhino geometry, layers, blocks, or document objects; pick objects from the viewport; control redraw and undo; or load and run scripts from the Rhino Script Editor. Covers `rhinoscriptsyntax`, `scriptcontext`, the `Rhino.*` RhinoCommon namespaces (`Rhino.Geometry`, `Rhino.DocObjects`, `Rhino.Input`, `Rhino.UI`, `Rhino.Display`, `Rhino.FileIO`), and the Rhino 8 unified Script Editor. |
Write production-quality scripts for Rhinoceros 3D. Covers the three scripting surfaces (RhinoScript/VBScript, RhinoPython, direct RhinoCommon .NET) and the Rhino 8+ Script Editor.
- User asks to write, edit, or debug a
.rvb,.vbs, or.pyRhino script - User wants a Rhino command macro or wants to automate a sequence of Rhino commands
- User wants to manipulate geometry, layers, blocks, materials, viewports, or annotations from code
- User mentions
rhinoscriptsyntax,scriptcontext,RhinoCommon,Rhino.Geometry,RhinoDoc, or the Script Editor - User wants to pick objects, prompt for input, or build a small UI inside Rhino
- User asks how to load, run, or distribute a script (startup scripts, aliases, toolbar buttons)
Pick the surface based on the task, not preference. Recommend Python by default for new work.
| Surface | When to choose | File ext |
|---|---|---|
RhinoPython (rhinoscriptsyntax + RhinoCommon) |
Default for new scripts. Best ecosystem, readable, full RhinoCommon access. | .py |
| RhinoScript (VBScript) | Maintaining legacy .rvb/.vbs files; integrating with VBA/COM. |
.rvb, .vbs |
| RhinoCommon (C#/.NET) via Script Editor | Performance-critical loops, complex geometry, leveraging .NET libraries. | .cs |
| Command macro | Pure sequence of existing Rhino commands; no logic. | toolbar/alias |
A macro is not a script — it is a string of command-line input (e.g. ! _-Line 0,0,0 10,0,0 _Enter). Use a script the moment you need a variable, loop, or conditional.
- Rhino 7 or later (Rhino 8 strongly recommended — unified Script Editor supports Python 3, VB, and C# in one window).
- Script Editor: type
_ScriptEditor(Rhino 8) or_EditPythonScript/_EditScript(older). - Run a saved file from the command line with
_-RunPythonScriptor_LoadScript+_RunScript.
import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino
def main():
obj_id = rs.GetObject("Select a curve", filter=rs.filter.curve, preselect=True)
if not obj_id:
return
length = rs.CurveLength(obj_id)
print("Length: {0:.4f}".format(length))
if __name__ == "__main__":
main()import Rhino
import scriptcontext as sc
doc = sc.doc # Rhino.RhinoDoc.ActiveDoc
tol = doc.ModelAbsoluteTolerance
circle = Rhino.Geometry.Circle(Rhino.Geometry.Point3d(0, 0, 0), 5.0)
curve_id = doc.Objects.AddCircle(circle)
doc.Views.Redraw()Option Explicit
Call Main()
Sub Main()
Dim strObject
strObject = Rhino.GetObject("Select a curve", 4) ' 4 = curve filter
If IsNull(strObject) Then Exit Sub
Rhino.Print "Length: " & Rhino.CurveLength(strObject)
End Subimport Rhino
import scriptcontext as sc
go = Rhino.Input.Custom.GetObject()
go.SetCommandPrompt("Select breps")
go.GeometryFilter = Rhino.DocObjects.ObjectType.Brep
go.SubObjectSelect = False
go.GetMultiple(1, 0)
if go.CommandResult() != Rhino.Commands.Result.Success:
pass
else:
ids = [go.Object(i).ObjectId for i in range(go.ObjectCount)]- Disable redraw:
rs.EnableRedraw(False). - Wrap mutations in a single undo record:
undo = doc.BeginUndoRecord("My Op")…doc.EndUndoRecord(undo). - Use RhinoCommon directly inside the loop (skip
rhinoscriptsyntaxoverhead). - Re-enable redraw and call
doc.Views.Redraw()in atry/finallyso a crash never leaves the viewport frozen.
- Save the
.py/.rvbsomewhere on disk. - Add the folder to
Options → Files → Search pathsso Rhino can find it by name. - Create a toolbar button or alias whose macro is:
- Python:
! _-RunPythonScript "MyScript.py" - RhinoScript:
! _-LoadScript "MyScript.rvb" _-RunScript MySubName
- Python:
- The leading
!cancels any running command;-runs the command in script (no-dialog) mode.
- Place a
.rvb/.pyin a search path. Tools → Options → RhinoScript(orPython) → add to Startup list. The file executes once per session.
rhinoscriptsyntaxreturns GUIDs, RhinoCommon returns objects. Mixing them is fine, butdoc.Objects.Find(guid)is the bridge from ars.*id to aRhinoObject.- Coordinates differ by surface. Python uses
(x, y, z)tuples orRhino.Geometry.Point3d; VBScript uses 3-elementArray(x, y, z). Never pass a Python list to a VBScript helper through COM. Option Explicitis off by default in VBScript. Typos silently create new variables. Always addOption Explicitat the top of.rvbfiles.- VBScript has no block scope. All
Dims inside aSubare hoisted to the top of the procedure. Loop counters leak. Nothing,Empty, andNullare different in VBScript. UseIsNullforRhino.GetObjectfailure,IsEmptyfor uninitializedVariant,Is Nothingfor object refs.- Parentheses change calling semantics in VBScript.
Call Foo(a, b)andFoo a, bare valid;Foo(a, b)(noCall, with parens) is not a call to a Sub — it’s a syntax error for multi-arg subs and a forcedByValfor single-arg. - Tolerance is per-document. Always read
doc.ModelAbsoluteTolerancerather than hardcoding0.001; users work in mm, m, inches, etc. - Long loops should poll
Rhino.RhinoApp.EscapeKeyPressedso the user can cancel. Otherwise Rhino appears frozen. - GUID strings vs
System.Guid.rhinoscriptsyntaxaccepts either; RhinoCommon wantsSystem.Guid. Convert withSystem.Guid(str_id)if needed. - Don’t call
doc.Views.Redraw()inside a tight loop. Toggle redraw once outside the loop. .rvbis just.vbsrenamed with a Rhino-specific extension so Rhino’sLoadScriptrecognizes it. Same VBScript engine.
| Symptom | Fix |
|---|---|
rs.GetObject returns None immediately |
The user pressed Escape, or your filter excludes everything. Re-check rs.filter.* flags. |
| “Unable to find script” when running by name | The folder isn’t in Options → Files → Search paths. |
VBScript Type mismatch on coordinates |
You passed a 2-element array. Rhino requires 3-element Array(x, y, z). |
Python ImportError: No module named Rhino |
You’re running CPython outside Rhino. RhinoCommon is only available in Rhino’s embedded Python (or via rhino3dm for read-only file work). |
| Created geometry doesn’t appear | You forgot doc.Views.Redraw(), or rs.EnableRedraw(False) was never re-enabled. |
| Undo undoes only the last object of a batch | Wrap the batch in BeginUndoRecord / EndUndoRecord. |
| Script works alone but fails as a startup script | Startup runs before any document is open — return early or skip document-dependent work when sc.doc is None. |
rs.Command("...") returns False |
The macro string is malformed. Prefix with ! and -, end every prompt with _Enter or a value. |
- references/rhinoscriptsyntax-cheatsheet.md — most-used
rs.*functions by category. - references/rhinocommon-map.md — which namespace to import for which task.
- references/macros-and-loading.md — command-line macro syntax,
LoadScript/RunScript, search paths. - references/vbscript-quirks.md — VBScript-only traps relevant to RhinoScript.
- RhinoScript landing: https://docs.mcneel.com/rhino/8/help/en-us/information/rhinoscripting.htm
- Developer hub: https://developer.rhino3d.com/
- RhinoCommon API index: https://mcneel.github.io/rhinocommon-api-docs/api/RhinoCommon/html/R_Project_RhinoCommon.htm
- Example scripts repo: https://github.com/mcneel/rhino-developer-samples/tree/8/rhinoscript