Scripting API
The gem handle
from ArtisanPlugin.Scripting import GemApi as gem, Transaction
Everything GemApi.Create, Find, All, Selected, ByLayer and ByMaterial hand back is an IGem. A handle is not a copy of the stone: it holds nothing but the object’s Guid and re-reads the document every time you touch it, so it always tells you the truth about the gem as it stands right now.
Usage
g = gem.Selected()[0]
print(g.Shape, g.Material, g.CaratWeight, g.SizeX, g.LayerName)
with Transaction.Begin("Enlarge the centre stone"):
g.SetCaratWeight(1.25)
g.Rotate(45)
Read-only properties
No licence check, no undo record, no transaction needed.
| Member | Meaning |
|---|---|
Id | The Rhino object Guid backing the handle |
Shape | Cut name, e.g. ROUND, PRINCESS, MARQUISE |
Material | Compound name, e.g. Diamond, Ruby |
CaratWeight | Weight in carats |
SizeX / SizeY / SizeZ | Dimensions in mm |
Position | Origin of the gem’s plane, on the girdle |
Plane | The gem’s full placement plane - origin and orientation |
LayerName | Full path of the layer the gem sits on |
Position and Plane report the gem’s live plane, kept in the object’s user dictionary and updated on every transform. Only if that entry is missing do they fall back to the plane captured when the gem was created.
Mutations
Each one calls LicenseGate.RequireValid() before touching the document and ends with a viewport redraw. All of them belong inside a Transaction if you want the script to undo as a single step.
| Member | Meaning |
|---|---|
SetMaterial(materialName) | Change the compound; the display material is reapplied |
SetCaratWeight(caratWeight) | Resize by carat, keeping shape, material, plane and layer |
SetSize(sizeX, sizeY, sizeZ) | Resize to explicit mm dimensions |
Scale(factor) | Uniform scale of the current size (factor > 0) |
Move(translation) | Translate by a Vector3d |
SetPlane(plane) | Re-place the gem: move and orient so its plane coincides with plane |
Rotate(degrees) | Spin around the gem’s own Z axis through its plane origin; positive is counter-clockwise |
Flip() | Turn upside down - 180 degrees around its own X axis, in place |
Copy() / Copy(translation) | Duplicate in place or displaced; returns the new gem’s handle |
Delete() | Remove the gem from the document |
Select(on) toggles the gem’s selection in the viewport and redraws. It is not a document mutation: no undo record, no licence gate. Use it to highlight what a script produced.
Handle semantics
Guids are stable across every mutation but Copy. Move, SetPlane, Rotate and Flip transform the object in place with deleteOriginal: true, which preserves the Guid. SetMaterial only edits attributes. The three resizers - SetCaratWeight, SetSize and Scale - regenerate the gem mesh at the new proportions and swap it in with Replace rather than add-and-delete, deliberately so the object keeps its Guid and its attributes (layer, material, name). A handle you took before a resize is still valid afterwards. Only Copy produces a new object, and it hands you a fresh handle to it; the source handle is untouched.
Handles are live, never cached. Neither the Rhino object nor the underlying gem is held on to: every property read and every mutation resolves the Guid against the active document first. Two handles to the same gem therefore always agree, and a handle picks up changes made by other scripts, by commands or by the user in between calls.
Touching a deleted gem throws. Once the object is gone - or was never an Artisan gemstone - resolving fails with an InvalidOperationException:
Gem 4a1f… no longer exists in the document.
That applies to reads as much as to mutations, so a stale handle fails loudly rather than silently doing nothing. Delete() does not invalidate the variable in Python; it invalidates the object, and the next access raises.
Copy keeps the source’s layer; Create does not. A copy is rebuilt through the same generator as a new gem, then explicitly moved onto the source gem’s layer, so duplicating a stone that lives on a custom layer keeps it there. GemApi.Create, by contrast, always drops new gems on the primary gems layer. Copy also carries over shape, material, size and orientation, and reapplies the gemstone material.
Validation happens before anything is written. Arguments are checked first, so a rejected call leaves the document untouched:
| Call | Rejected when | Message |
|---|---|---|
SetMaterial(name) | Empty or whitespace | Material name is required. |
SetMaterial(name) | Not a known compound | Unknown gem material ‘xxx’. |
SetCaratWeight(ct) | ct <= 0 | Carat weight must be > 0. |
SetSize(x, y, z) | Any dimension <= 0 | Gem dimensions must be > 0. |
Scale(factor) | factor <= 0 | Scale factor must be > 0. |
SetPlane(plane) | Plane is not valid | Target plane is not valid. |
Material names are normalised before parsing: trimmed, with dashes and spaces turned into underscores, matched case-insensitively. "pink sapphire", "Pink-Sapphire" and "PINK_SAPPHIRE" all reach the same compound. Use GemApi.Materials() when in doubt.
Two further failures come from the document rather than from your arguments: No active document. when there is no open RhinoDoc, and Failed to replace gem … while resizing. if the rebuild cannot be swapped in.
The complete interface, member by member, is on the IGem page.