Rhino.Scripting
A complete reimplementation of the Rhino-Script-Syntax in F#
Install / Use
/learn @goswinr/Rhino.ScriptingREADME

Rhino.Scripting
<!-- [](https://github.com/goswinr/Rhino.Scripting/actions/workflows/outdatedDotnetTool.yml) -->Rhino.Scripting is a complete re-implementation of the original RhinoScript syntax in and for F# (and C#).<br> Before this repo, the high-level RhinoScript API was only available for VBScript and (Iron-)Python.<br> This repo enables the use of the RhinoScriptSyntax in F# and C#<br> together with all the great coding experience and editor tooling that come with F# and C#, like:<br>
- automatic code completion while typing.<br>
- automatic error checking and highlighting in the background.<br>
- type info on mouse over.<br>
- type safety even without type annotation (= type inference in F#).<br>
What is RhinoScript?
RhinoScript provides application scripting for the Rhino3D CAD app.<br> RhinoScript has more than 900 functions to control all kinds of aspects of automating Rhino3D.<br> It was originally implemented in 2002 in VBScript.<br> Extensive documentation on the original VBScript-based version is available here.
In 2010, all functions from RhinoScript were reimplemented in IronPython (Python running on .NET).<br> This allowed the use of a modern, rich, and dynamically typed programming language with a huge standard library and <br> also access to all functions of the underlying .NET Framework as well as the RhinoCommon SDK.
What is this repo?
This repo has all original RhinoScript functions reimplemented in F#.<br> It is literally a translation of the open-source IronPython rhinoscriptsyntax implementation to F#.<br> You can see all 900+ methods in this repo in the docs.
A few minor bugs from the Python implementation are fixed and a few extra methods and optional parameters were added.<br> I have been using this library extensively for my own professional scripting needs since 2019.<br> If you have problems, questions, or find a bug, please open an issue.
Get started
The recommended scripting use case is via Fesh, the dedicated F# scripting editor for Rhino.<br> However, you can use this library just as well in the new Rhino 8 ScriptEditor with C# or in independently compiled F#, C#, or VB.net projects.
Get started in F#
First reference the assemblies.
#r "nuget: Rhino.Scripting"
The main namespace is Rhino.Scripting.<br>
The main class of this library is called RhinoScriptSyntax it has all ~900 functions as static methods.<br>
In F# you can create an alias like this:
open Rhino.Scripting
type rs = RhinoScriptSyntax
then use any of the RhinoScript functions like you would in Python or VBScript.<br>
The CoerceXXXX functions will help you create types if you are too lazy to fully specify them.
let pl = rs.CoercePlane(0 , 80 , 0) // makes World XY plane at point
rs.AddText("Hello, Fesh", pl, height = 50.)
For F# scripting the Rhino.Scripting.Fsharp provides useful extensions and curried functions for piping and partial application.
Get started in C#
You can use it via the new Rhino 8 ScriptEditor. First reference the assemblies.
#r "nuget: Rhino.Scripting"
The main namespace is Rhino.Scripting.<br>
The main class of this library is called RhinoScriptSyntax it has all ~900 functions as static methods.<br>
In C# you can create an alias like this:
using rs = Rhino.Scripting.RhinoScriptSyntax;
then you can use it like the RhinoScriptSyntax in Python:
var pt = rs.GetObject("Select an Object");
rs.ObjectColor(pt, System.Drawing.Color.Blue);
How about the dynamic types and optional parameters from VBScript and Python?
Many RhinoScript functions take variable types of input parameters.<br> This is implemented with method overloads.<br> Many RhinoScript functions have optional parameters.<br> These are also implemented as optional method parameters.<br> Many RhinoScript functions are getters and setters at the same time.<br> Depending on if an argument is provided or not, the function acts as a getter or setter.<br> This is also implemented with method overloads.
Example
For example rs.ObjectLayer can be called in several ways:
To get the layer of one object, returns a string:<br>
rs.ObjectLayer(guid)
To set the layer of one object (fails if layer does not exist), no return value:<br>
rs.ObjectLayer(guid, string)
To set the layer of one object, and create the layer if it does not exist yet, no return value:<br>
rs.ObjectLayer(guid, string, createLayerIfMissing = true )
To set the layer of several objects (fails if layer does not exist), no return value:<br>
rs.ObjectLayer(list of guids, string)
To set the layer of several objects, and create the layer if it does not exist yet, no return value:<br>
rs.ObjectLayer(list of guids, string, createLayerIfMissing = true )
These are implemented with 3 overloads and Optional and DefaultParameterValue parameters:
///<summary>Returns the full layer name of an object.
/// parent layers are separated by <c>::</c>.</summary>
///<param name="objectId">(Guid) The identifier of the object</param>
///<returns>(string) The object's current layer.</returns>
static member ObjectLayer(objectId:Guid) : string = //GET
let obj = RhinoScriptSyntax.CoerceRhinoObject(objectId)
let index = obj.Attributes.LayerIndex
State.Doc.Layers.[index].FullPath
///<summary>Modifies the layer of an object ,
/// optionally creates layer if it does not exist yet.</summary>
///<param name="objectId">(Guid) The identifier of the object</param>
///<param name="layer">(string) Name of an existing layer</param>
///<param name="createLayerIfMissing">(bool) Optional,
/// Default Value: <c>false</c> Set true to create Layer
/// if it does not exist yet.</param>
///<param name="allowAllUnicode">(bool) Optional,
/// Allow Ambiguous Unicode characters too </param>
///<param name="collapseParents">(bool) Optional,
/// Collapse parent layers in Layer UI </param>
///<returns>(unit) void, nothing.</returns>
static member ObjectLayer( objectId:Guid
, layer:string
,[<OPT;DEF(false)>]createLayerIfMissing:bool
,[<OPT;DEF(false:bool)>]allowAllUnicode:bool
,[<OPT;DEF(false:bool)>]collapseParents:bool) : unit = //SET
let obj = RhinoScriptSyntax.CoerceRhinoObject(objectId)
let layerIndex =
if createLayerIfMissing then
UtilLayer.getOrCreateLayer(
layer,
UtilLayer.randomLayerColor,
UtilLayer.ByParent,
UtilLayer.ByParent,
allowAllUnicode,
collapseParents
).Index
else
RhinoScriptSyntax.CoerceLayer(layer).Index
obj.Attributes.LayerIndex <- layerIndex
obj.CommitChanges() |> ignore
State.Doc.Views.Redraw()
///<summary>Modifies the layer of multiple objects, optionally creates
/// layer if it does not exist yet.</summary>
///<param name="objectIds">(Guid seq) The identifiers of the objects</param>
///<param name="layer">(string) Name of an existing layer</param>
///<param name="createLayerIfMissing">(bool) Optional,
/// Default Value: <c>false</c> Set true to create Layer
/// if it does not exist yet.</param>
///<param name="allowUnicode">(bool) Optional,
/// Allow Ambiguous Unicode characters too </param>
///<param name="collapseParents">(bool) Optional,
/// Collapse parent layers in Layer UI </param>
///<returns>(unit) void, nothing.</returns>
static member ObjectLayer( objectIds:Guid seq
, layer:string
, [<OPT;DEF(false)>]createLayerIfMissing:bool
, [<OPT;DEF(false:bool)>]allowUnicode:bool
, [<OPT;DEF(false:bool)>]collapseParents:bool) : unit = //MULTISET
let layerIndex =
if createLayerIfMissing then
UtilLayer.getOrCreateLayer(
layer,
UtilLayer.randomLayerColor,
