Skip to content

Scripting API

Copy on objects

from ArtisanPlugin.Scripting import TransformApi as transform

Scattering a motif - a bead, a leaf, a small setting - over the curved surface of a ring or a bangle, with each copy sitting flat on the metal instead of floating at the original orientation. This is the ArtisanCopyOnObjects command headless: each point in points stands in for one click of the command, which otherwise loops until you press Enter.

Usage

ids = transform.CopyOnObjects(objectIds, targetIds, points)   # -> ids of the copies created
ParameterDefaultMeaning
objectIds-The motif to copy; None or empty falls back to the current viewport selection
targetIds-The objects to place on - surfaces, polysurfaces or meshes. No selection fallback: at least one is required
points-One placement per point, at least one. Points are pulled onto the target surface first

Nothing is moved and nothing is deleted: the source objects stay exactly where they are, and the method only creates copies. The return value is an IReadOnlyList<Guid> of the created copies, in point-major order - all the objects for the first point, then all for the second, and so on - so the list length is normally points × objects. The copies are plain: no history, no parametric link back to the motif or the targets, matching what the command records.

How the frame is derived

The targets are meshed together at render quality into one base mesh, and every point you pass is pulled onto that mesh, reproducing the command’s cursor constraint - so approximate points are fine, but a point that cannot be pulled onto the mesh is silently skipped rather than raising.

Each object is then framed individually. Its bounding-box centre is projected onto the base mesh, and the mesh point plus the face normal there define the origin plane. The same offset the object holds relative to the selection centre is applied at the picked point, that result is projected onto the mesh again, and its point and normal define the destination plane. The object is mapped from one plane to the other.

Two consequences follow. A group of objects keeps its arrangement, because every object is offset from the shared selection centre. And each copy reorients to the local surface normal where it lands, so a motif crossing a curved band follows the curvature. The origin frame is taken from the base mesh too, which means the motif is assumed to be sitting on or near the target surface already; a motif floating far away is framed by whatever part of the mesh happens to be nearest to it.

Objects whose placement transform cannot be built - no geometry, or a projection that fails - are quietly skipped, so a short return list is normal rather than an error.

Validation

points empty or None throws At least one placement point is required. Ids in objectIds are checked one by one: a missing one throws Object <id> does not exist., an empty resolved list throws Nothing selected: select (or pass) at least one object., and a selection with no usable geometry throws The objects have no valid geometry to place.

On the target side, an empty targetIds throws At least one target object is required., a missing id throws Target object <id> does not exist., and targets that mesh to nothing - curves, points, open geometry that yields no faces - throw The target objects produced no surface to place on: pass surfaces, polysurfaces or meshes.

The call mutates the document and ends with a viewport redraw. Wrap it in a Transaction so a run of placements undoes in one step.

from ArtisanPlugin.Scripting import TransformApi as transform, Transaction
from Rhino.Geometry import Point3d

points = [Point3d(0, 8, 2), Point3d(3, 8, 2), Point3d(-3, 8, 2)]
with Transaction.Begin("Scatter motif"):
    ids = transform.CopyOnObjects(motifIds, [shankId], points)