new skill rhino3d-scripts (#1705)

* resolve readme validate

* Apply suggestions from code review

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: John Haugabook <johnhaugabook@gmail.com>

* rhino3d-scripts: rm reference to deprecated VBScript

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
John Haugabook
2026-05-14 21:08:46 -04:00
committed by GitHub
parent 9c54e98353
commit b50617e33e
6 changed files with 572 additions and 0 deletions
@@ -0,0 +1,95 @@
# Macros, Loading, and Running Scripts
## Command Macros (no script needed)
A macro is a string of command-line input. Anywhere Rhino accepts a command (alias, toolbar button, `_ReadCommandFile`), you can place a macro.
### Syntax rules
| Token | Meaning |
|---|---|
| `!` | **Cancel** any currently running command before this one starts. Always start macros with `!`. |
| `_` | Use the **English** (invariant) command name, so the macro works in any locale. |
| `-` | Run the command in **script mode** — suppress dialogs, accept input from the macro string. |
| `_Enter` | Press Enter at the current prompt. |
| `Pause` | Stop and wait for the user to supply this input interactively. |
| `;` | Comment to end of line. |
| Newline | Same as a space — a separator between tokens, not a command terminator. |
### Example macros
```text
! _-Line 0,0,0 10,0,0
! _-Circle 0,0,0 5 _Enter
! _SelAll _Delete
! _-Properties _Object _Name "MyObject" _EnterEnd _Enter
! _-RunPythonScript "MyScript.py"
```
## Running Saved Scripts
### Python (`.py`)
```text
_-RunPythonScript "C:\Users\example\Scripts\MyScript.py"
```
Or, with the script folder on the search path:
```text
_-RunPythonScript "MyScript.py"
```
`_EditPythonScript` opens the legacy editor; `_ScriptEditor` (Rhino 8) opens the unified editor with Python 3, VB, and C#.
### RhinoScript (`.rvb`, `.vbs`)
Two steps: **load** the file (registers its subs/functions), then **run** a named sub.
```text
_-LoadScript "MyScript.rvb"
_-RunScript MyMainSub
```
A single `.rvb` can hold many subs; `_RunScript` chooses which to invoke.
### Search Paths
`Options → Files → Search paths` — folders listed here are scanned when you reference a script by bare filename. Without this, you must give a full path.
### Startup Scripts
`Options → RhinoScript → Startup` (and `Options → Python → Startup`) — files in these lists run once when Rhino opens. Useful for registering custom commands or aliases.
**Guard against missing document** in startup code:
```python
import scriptcontext as sc
def startup():
if sc.doc is None:
return
startup()
```
## Toolbar Buttons & Aliases
A toolbar buttons **Command** field is just a macro. To make a button that runs your script:
```text
! _-RunPythonScript "MyScript.py"
```
Set the **Tooltip** to a short description; set the icon via the button editor.
To create an alias: `Options → Aliases → New`. The alias name becomes a typed command; its value is the macro.
## Invoking Macros From a Script
```python
import rhinoscriptsyntax as rs
rs.Command("! _-Line 0,0,0 10,0,0", echo=False)
```
`echo=False` suppresses command-history output but does **not** suppress prompts — always use `-` and complete every prompt within the macro string.
@@ -0,0 +1,90 @@
# RhinoCommon Namespace Map
Use this to decide which `Rhino.*` namespace to import for a task. All of these are accessible from Python with `import Rhino`.
## Document & Object Model
| Need | Namespace | Key types |
|---|---|---|
| The active document, units, tolerances, undo | `Rhino` | `RhinoDoc`, `RhinoApp` |
| Reading/writing objects in the doc | `Rhino.DocObjects` | `RhinoObject`, `ObjectAttributes`, `Layer`, `ObjectType` |
| Tables (layers, materials, blocks, dim styles) | `Rhino.DocObjects.Tables` | `LayerTable`, `InstanceDefinitionTable`, `MaterialTable` |
| Custom per-object user data | `Rhino.DocObjects.Custom` | `UserData` |
| File I/O (.3dm, import/export) | `Rhino.FileIO` | `File3dm`, `File3dmObject` |
## Geometry
| Need | Namespace | Key types |
|---|---|---|
| Points, vectors, transforms | `Rhino.Geometry` | `Point3d`, `Vector3d`, `Transform`, `Plane`, `BoundingBox` |
| Curves | `Rhino.Geometry` | `Curve`, `NurbsCurve`, `PolylineCurve`, `LineCurve`, `ArcCurve` |
| Surfaces & breps | `Rhino.Geometry` | `Surface`, `NurbsSurface`, `Brep`, `BrepFace`, `Extrusion` |
| Meshes | `Rhino.Geometry` | `Mesh`, `MeshFace`, `MeshNgon` |
| SubD | `Rhino.Geometry` | `SubD`, `SubDFace`, `SubDEdge` |
| Geometry collections (vertices, edges, faces lists) | `Rhino.Geometry.Collections` | `MeshVertexList`, `BrepEdgeList` |
| Intersections | `Rhino.Geometry.Intersect` | `Intersection`, `CurveIntersections` |
| Mesh refinement | `Rhino.Geometry.MeshRefinements` | |
| Space morphs (bend, twist, etc.) | `Rhino.Geometry.Morphs` | `BendSpaceMorph`, `TwistSpaceMorph` |
## User Interaction
| Need | Namespace | Key types |
|---|---|---|
| Prompts, getters, command results | `Rhino.Input` / `Rhino.Input.Custom` | `RhinoGet`, `GetObject`, `GetPoint`, `GetOption` |
| Commands (when building a plugin) | `Rhino.Commands` | `Command`, `Result` |
| Forms, dialogs, panels | `Rhino.UI` | `Dialog`, `Panels`, `RhinoEtoExtensions` |
| UI controls / data sources | `Rhino.UI.Controls` | |
| Gumball | `Rhino.UI.Gumball` | |
## Display & Rendering
| Need | Namespace | Key types |
|---|---|---|
| Viewports, display conduits, draw overlays | `Rhino.Display` | `DisplayPipeline`, `DisplayConduit`, `RhinoViewport` |
| Render content (materials, environments) | `Rhino.Render` | `RenderContent`, `RenderMaterial`, `RenderTexture` |
| Render mesh customization | `Rhino.Render.CustomRenderMeshes` | |
| Post effects | `Rhino.Render.PostEffects` | |
## Runtime & Plugins
| Need | Namespace | Key types |
|---|---|---|
| Plugin lifecycle, settings | `Rhino.PlugIns` | `PlugIn`, `FileImportPlugIn`, `FileExportPlugIn` |
| In-process Rhino (run Rhino from external .NET) | `Rhino.Runtime.InProcess` | `RhinoCore` |
| Native interop (pointers, marshalling) | `Rhino.Runtime.InteropWrappers` | |
| User notifications (toasts) | `Rhino.Runtime.Notifications` | |
## Common Patterns
```python
import Rhino
import scriptcontext as sc
import System.Drawing
doc = sc.doc # Rhino.RhinoDoc
tol = doc.ModelAbsoluteTolerance # float
view = doc.Views.ActiveView # Rhino.Display.RhinoView
layer_index = doc.Layers.Add("MyLayer", System.Drawing.Color.Red)
# Find a Rhino object from a rhinoscriptsyntax GUID
rhobj = doc.Objects.Find(guid) # Rhino.DocObjects.RhinoObject
geom = rhobj.Geometry # Rhino.Geometry.GeometryBase
# Add geometry with attributes
attrs = Rhino.DocObjects.ObjectAttributes()
attrs.LayerIndex = layer_index
attrs.Name = "example"
new_id = doc.Objects.AddCurve(curve, attrs)
```
## Undo
```python
undo_serial = doc.BeginUndoRecord("Batch Op")
try:
# ... many doc.Objects.* calls ...
pass
finally:
doc.EndUndoRecord(undo_serial)
doc.Views.Redraw()
```
@@ -0,0 +1,108 @@
# `rhinoscriptsyntax` Cheatsheet
```python
import rhinoscriptsyntax as rs
```
## User Input
| Function | Returns |
|---|---|
| `rs.GetObject(message, filter, preselect, select)` | GUID or `None` |
| `rs.GetObjects(message, filter)` | list of GUIDs |
| `rs.GetPoint(message, base_point)` | `Point3d` or `None` |
| `rs.GetString(message, default, strings)` | str |
| `rs.GetInteger`, `rs.GetReal` | int / float |
| `rs.GetBoolean(message, items, defaults)` | list of bools |
Common `rs.filter.*` flags (OR them together):
```
point=1, point_cloud=2, curve=4, surface=8, polysurface=16,
mesh=32, light=256, annotation=512, instance_reference=4096,
text_dot=8192, grip=16384, detail=32768, hatch=65536,
morph_control=131072, sub_d=262144
```
## Creating Geometry
| Function | Notes |
|---|---|
| `rs.AddPoint(point)` | |
| `rs.AddLine(start, end)` | |
| `rs.AddPolyline(points)` | `points` is a list of 3-tuples |
| `rs.AddCircle(plane_or_center, radius)` | |
| `rs.AddArc(plane, radius, angle_deg)` | angle in **degrees** |
| `rs.AddCurve(points, degree=3)` | NURBS through control points |
| `rs.AddInterpCurve(points, degree=3)` | NURBS through points |
| `rs.AddSphere(center, radius)` | |
| `rs.AddBox(corners)` | `corners` = 8 points |
| `rs.AddPlanarSrf(curves)` | returns list |
| `rs.AddLoftSrf(curves, ...)` | returns list of GUIDs |
| `rs.AddExtrusion(profile, path)` / `rs.ExtrudeCurveStraight` | |
## Object Properties
| Function | Purpose |
|---|---|
| `rs.ObjectLayer(id [, layer])` | get/set |
| `rs.ObjectColor(id [, color])` | RGB tuple |
| `rs.ObjectName(id [, name])` | |
| `rs.ObjectType(id)` | int matching `rs.filter.*` |
| `rs.IsCurve / IsSurface / IsBrep / IsMesh / IsPoint(id)` | |
| `rs.DeleteObject(id)` / `rs.DeleteObjects(ids)` | |
| `rs.CopyObject(id, translation)` | |
| `rs.MoveObject(id, translation)` | |
| `rs.RotateObject(id, center, angle, axis=None, copy=False)` | angle in degrees |
| `rs.ScaleObject(id, origin, scale)` | scale is a 3-tuple |
## Curves
| Function | |
|---|---|
| `rs.CurveLength(id)` | |
| `rs.CurveDomain(id)` | `(t0, t1)` |
| `rs.EvaluateCurve(id, t)` | `Point3d` |
| `rs.CurveStartPoint / CurveEndPoint(id)` | |
| `rs.CurveClosestPoint(id, point)` | parameter `t` |
| `rs.DivideCurve(id, segments, create_points=False, return_points=True)` | |
| `rs.IsCurveClosed / IsCurvePlanar(id)` | |
## Layers
| Function | |
|---|---|
| `rs.AddLayer(name, color=None, visible=True, locked=False, parent=None)` | |
| `rs.CurrentLayer([layer])` | |
| `rs.LayerNames()` | list |
| `rs.LayerVisible(name [, visible])` | |
| `rs.DeleteLayer(name)` | |
| `rs.ObjectsByLayer(name)` | list of GUIDs |
## Document & View
| Function | |
|---|---|
| `rs.UnitAbsoluteTolerance()` | |
| `rs.UnitSystem()` | int (`rs.unit_system_*`) |
| `rs.EnableRedraw(enable)` | **toggle this around bulk ops** |
| `rs.Redraw()` | force one redraw |
| `rs.ViewNames()` / `rs.CurrentView([name])` | |
| `rs.ZoomExtents(view=None, all=False)` | |
## Selection
| Function | |
|---|---|
| `rs.SelectedObjects()` | list |
| `rs.SelectObject(id)` / `rs.SelectObjects(ids)` | |
| `rs.UnselectAllObjects()` | |
| `rs.InvertSelectedObjects()` | |
## Macros from Script
`rs.Command(command_string, echo=True)` runs a macro exactly as if typed at the command line. Always prefix `!` (cancel) and `-` (no dialog):
```python
rs.Command("! _-Line 0,0,0 10,0,0", echo=False)
```
@@ -0,0 +1,112 @@
# VBScript Quirks for RhinoScript
Things that bite when writing `.rvb` / `.vbs` files and arent obvious to anyone whose mental model is C-family or Python.
## Always Start With `Option Explicit`
Without it, mistyping a variable name silently creates a new `Variant` set to `Empty`. Every `.rvb` file should begin:
```vbscript
Option Explicit
```
## No Block Scope
All `Dim` declarations inside a `Sub`/`Function` are hoisted to the top. A `Dim` inside an `If` block is visible after the `If` ends. Loop counters survive the loop.
## `Nothing` vs `Empty` vs `Null`
| Sentinel | Test with | Meaning |
|---|---|---|
| `Empty` | `IsEmpty(x)` | `Dim`d but never assigned |
| `Null` | `IsNull(x)` | Explicit “no value” — what `Rhino.GetObject` returns on cancel |
| `Nothing` | `x Is Nothing` | An **object** reference that points to nothing — only for `Set` variables |
Wrong sentinel → silently false.
## Parentheses Change Semantics
```vbscript
Foo a, b ' Call a Sub or Function (return value discarded)
Call Foo(a, b) ' Call a Sub or Function (return value discarded)
x = Foo(a, b) ' Call a Function and capture the return
Foo(a, b) ' SYNTAX ERROR for multi-arg subs
Foo(x) ' Calls Foo passing x BY VALUE, even if Foo declares ByRef
Foo x ' Honors Foo's ByRef declaration
```
If a Sub modifies its argument and your change isnt taking effect — you wrapped the argument in parentheses.
## `ByRef` Is the Default
Unlike most languages, VBScript passes arguments **by reference by default**. Functions can mutate their callers variables. Be explicit:
```vbscript
Sub Increment(ByRef n)
n = n + 1
End Sub
```
## Arrays Are 0-Based But Have `UBound`, Not `Length`
```vbscript
Dim arr(2) ' Three elements: arr(0), arr(1), arr(2)
For i = 0 To UBound(arr)
arr(i) = i * 10
Next
```
`Dim arr(n)` creates `n+1` elements. `ReDim Preserve arr(newSize)` resizes (only the last dimension of a multi-dim array).
## `Set` Is Required for Object Assignment
```vbscript
Set fso = CreateObject("Scripting.FileSystemObject") ' correct
fso = CreateObject("Scripting.FileSystemObject") ' RUNTIME ERROR
```
Any time the right-hand side is an object (COM object, RegExp, Dictionary), you must use `Set`.
## Error Handling Is Manual
```vbscript
On Error Resume Next
Rhino.AddCircle Array(0,0,0), -1
If Err.Number <> 0 Then
Rhino.Print "Failed: " & Err.Description
Err.Clear
End If
On Error GoTo 0 ' Restore normal error behavior
```
`On Error Resume Next` suppresses **all** errors until `On Error GoTo 0`. Forgetting to restore is a common bug.
## Points Are 3-Element Arrays
Rhino expects `Array(x, y, z)`. A 2-element array (`Array(x, y)`) raises a type-mismatch error.
```vbscript
Dim pt
pt = Array(1.0, 2.0, 3.0)
Rhino.AddPoint pt
```
## String Concatenation Uses `&`, Not `+`
`+` on strings works only if **both** sides are strings. If one side is a number, `+` does numeric addition and throws a type-mismatch. Always use `&`:
```vbscript
Rhino.Print "Count: " & n
```
## `For Each` Needs a `Variant`
```vbscript
Dim item
For Each item In someCollection
' ...
Next
```
The loop variable must be `Variant`. You cannot `Dim item As Long` (VBScript has no typed `Dim`).