Scripting API
Twist
from ArtisanPlugin.Scripting import TransformApi as transform
Twists objects around an axis through their common bounding box, exactly like the Twist panel: one end stays put, the other rotates by angleDegrees, and everything in between turns proportionally. This is the rope-shank tool - twisted wires, corded bands, spiral motifs - and it is also how you put a slow quarter-turn into a flat motif before flowing it onto a shank. The rotation can be limited to part of the axis with the break positions, so the twist starts partway up instead of at the very bottom.
Usage
ids = transform.Twist(objectIds, angleDegrees, direction = None, mode = None,
breakOne = 0, breakTwo = 0,
rigidIds = None, keepOriginal = False) # -> new object ids
| Parameter | Default | Meaning |
|---|---|---|
objectIds | — | Objects to twist; None/empty = the current selection |
angleDegrees | — (required) | Total twist in degrees; must be non-zero, sign gives the direction |
direction | None = "ZTop" | Axis through the bounding box: ZTop, ZBottom, YTop, YBottom, XTop, XBottom. Case-insensitive; the named end is the one that rotates |
mode | None = "Uniform" | Uniform (whole axis), OneBreak (only from breakOne to the far end), TwoBreaks (only between the two breaks) |
breakOne | 0 | Normalised 0-1 position along the axis; 0 = the tool default, 0.5 |
breakTwo | 0 | Normalised 0-1 position along the axis; 0 = the tool default, 0.75 |
rigidIds | None | Objects that must not distort - gems, heads - carried along rigidly |
keepOriginal | False | True leaves the originals in the document |
Angles are degrees, lengths millimetres. Note the two different meanings of 0 here, and that neither uses a -1 sentinel: angleDegrees = 0 is rejected outright with ArgumentException("Nothing to twist: the angle is 0 degrees."), while breakOne/breakTwo follow the house rule where 0 means “keep the tool default” (0.5 and 0.75 respectively). A break outside 0-1 throws ArgumentOutOfRangeException with Break positions are normalized: 0 = tool default, otherwise between 0 and 1 along the axis. Breaks are only read in OneBreak and TwoBreaks mode, and in TwoBreaks they are sorted, so their order does not matter. An unknown direction or mode throws naming the valid values, e.g. Unknown direction 'up'. Use one of: ZTop, ZBottom, YTop, YBottom, XTop, XBottom. A failed computation raises InvalidOperationException("Twist computation failed."), or Twist computation failed: <reason>.
What you get back, and what is left in the document
The return value is an IReadOnlyList[Guid]: the twisted objects first, in the order you passed them, then the rigid ones. Nothing is deformed in place. Each object is baked as a new object with the original’s attributes - layer, colour, material, groups - and the originals are then deleted, unless keepOriginal = True, in which case originals and twisted copies both remain. Ids that no longer resolve are skipped silently.
rigidIds objects do not twist. Each is copied and repositioned with a plane-to-plane transform: a frame at its bounding-box centre is pushed through the same morph, and the object is moved and rotated onto the result - so a gem ends up sitting correctly on the twisted metal without its facets shearing. The frame follows the twist axis (WorldXY for the Z directions, WorldZX for Y, WorldYZ for X). One quirk, replicated from the panel on purpose: a rigid object that is an extrusion is fully morphed instead of moved rigidly. Convert such objects to Breps beforehand if you need them to stay rigid. Rigid ids not in the document throw Rigid object <id> not found in the document.
An empty or None objectIds falls back to the objects currently selected in the viewport. If that is empty too you get ArgumentException("Nothing to deform: select (or pass) at least one object."), and any id you pass that is not in the document throws Object <id> not found in the document.
Space morph, so: what survives
This is a Rhino space morph, not a transform. Control points are moved, so a Brep stays a Brep and a curve stays a curve, but analytic faces come back as free-form surfaces, and a shape with few control points twists coarsely - a straight-sided box will shear rather than curve. Refine the input where the twist has to look smooth. Extrusions are converted to Breps before morphing, so an extrusion you twist comes back as a Brep with a new id. Circles get special treatment: a four-point rational circle cannot twist at all, so any curve that reads as a circle is rebuilt to a 10-point degree-3 NURBS curve first, exactly as the command does - the returned curve is that rebuilt one, not a circle. Meshes morph vertex by vertex at their existing density. Document tolerance settings are not consulted; the morph is applied by the tool with its own settings and none of them are exposed to the script.
The whole operation runs inside a single Rhino undo record named “Artisan Twist” and ends with a viewport redraw, so one Ctrl+Z undoes it. Wrap it in a Transaction when you want it grouped with other mutations as one step.