Copying objects in Rhino memory issue


I would like to ask if it possible use more intelligent method than this.

What I am doing is basically taking one object guid.
Duplicate (copying it) it and transform it (scale, rotate and plane to plane transformation)

for (int i = 0; i < planes.Count; i++)
int c = i%G.Count;
Guid temp = Rhino.RhinoDoc.ActiveDoc.Objects.Duplicate(G[c]);
Rhino.RhinoDoc.ActiveDoc.Objects.Transform(temp, xform[i], true);

This is good for a few thousand objects, like above boxes.

But then I get memory issue, when copying much larger numbers of objects or objects are higher res.
It is O(n) operation and rhino file size increases from few mb to hundreads of megabytes, while I am just copying one single element with several transformations.

I noticed that copying blocks (I do not know how to do it using rhinocommon) does not increase Rhino file size as it is referencing to the same object and allows some basic transformation too. Maybe this would be method, I do not know.

What would be the best solution to have “light geometry” when copying once instance hundreads of times?
Could you please give a hand how to do it using rhinocommon?

This is a good idea - have you tried?

I do not know how to do it.

Could you give me an example?

In Rhino:

1.) Create a box
2.) Run the Block command and create a block form the box
3.) From Grasshopper, pick the block.
4.) You do the rest…

Without calling the command is it possible to block Guid using something from Rhino.RhinoDoc.ActiveDoc.Objects ?

I have made a quick script illustrating how you could create a Block definition, update it if it already exists, and instantiate it as many times as needed. (7.0 KB)

The script

using Rhino;
using Rhino.Geometry;
using Rhino.DocObjects;
using Rhino.Collections;

using GH_IO;
using GH_IO.Serialization;
using Grasshopper;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Data;
using Grasshopper.Kernel.Types;

using System;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using System.Linq;
using System.Data;
using System.Drawing;
using System.Reflection;
using System.Collections;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Runtime.InteropServices;

/// <summary>
/// This class will be instantiated on demand by the Script component.
/// </summary>
public class Script_Instance : GH_ScriptInstance
#region Utility functions
  /// <summary>Print a String to the [Out] Parameter of the Script component.</summary>
  /// <param name="text">String to print.</param>
  private void Print(string text) { /* Implementation hidden. */ }
  /// <summary>Print a formatted String to the [Out] Parameter of the Script component.</summary>
  /// <param name="format">String format.</param>
  /// <param name="args">Formatting parameters.</param>
  private void Print(string format, params object[] args) { /* Implementation hidden. */ }
  /// <summary>Print useful information about an object instance to the [Out] Parameter of the Script component. </summary>
  /// <param name="obj">Object instance to parse.</param>
  private void Reflect(object obj) { /* Implementation hidden. */ }
  /// <summary>Print the signatures of all the overloads of a specific method to the [Out] Parameter of the Script component. </summary>
  /// <param name="obj">Object instance to parse.</param>
  private void Reflect(object obj, string method_name) { /* Implementation hidden. */ }

#region Members
  /// <summary>Gets the current Rhino document.</summary>
  private readonly RhinoDoc RhinoDocument;
  /// <summary>Gets the Grasshopper document that owns this script.</summary>
  private readonly GH_Document GrasshopperDocument;
  /// <summary>Gets the Grasshopper script component that owns this script.</summary>
  private readonly IGH_Component Component;
  /// <summary>
  /// Gets the current iteration count. The first call to RunScript() is associated with Iteration==0.
  /// Any subsequent call within the same solution will increment the Iteration count.
  /// </summary>
  private readonly int Iteration;

  /// <summary>
  /// This procedure contains the user code. Input parameters are provided as regular arguments,
  /// Output parameters as ref arguments. You don't have to assign output parameters,
  /// they will have a default value.
  /// </summary>
  private void RunScript(object obj, object blockname, object count, object x, object y, object z)
    var rand = new System.Random(13);
    var og = (System.Guid) obj;
    var bsname = (string) blockname;
    var o = Rhino.RhinoDoc.ActiveDoc.Objects.Find(og);
    var _count = Convert.ToInt32(count);
    var _x = Convert.ToDouble(x);
    var _y = Convert.ToDouble(y);
    var _z = Convert.ToDouble(z);

    var c = o.Geometry.GetBoundingBox(true).Center;

    var geom = new System.Collections.Generic.List<Rhino.Geometry.GeometryBase>();
    var attr = new System.Collections.Generic.List<Rhino.DocObjects.ObjectAttributes>();


    var mydef = Rhino.RhinoDoc.ActiveDoc.InstanceDefinitions.Find(bsname);

    var theidx = -1;

    if(mydef == null) {
      var idx = Rhino.RhinoDoc.ActiveDoc.InstanceDefinitions.Add(bsname, "MyGhDef", c, geom, attr);
      if(idx > -1) {
        theidx = idx;
    } else {
      var oidx = Rhino.RhinoDoc.ActiveDoc.InstanceDefinitions.InstanceDefinitionIndex(mydef.Id, true);
      var oinst = mydef.GetReferences(1);
      foreach(Rhino.DocObjects.InstanceObject r in oinst) {
        Rhino.RhinoDoc.ActiveDoc.Objects.Delete(r.Id, true);
      if(oidx > -1) {
        Rhino.RhinoDoc.ActiveDoc.InstanceDefinitions.ModifyGeometry(oidx, geom, attr);
        theidx = oidx;

    for(int i = 0; i < _count; i++) {
      var v = new Rhino.Geometry.Vector3d(rand.NextDouble() * _x, rand.NextDouble() * _y, rand.NextDouble() * _z);
      var t = Rhino.Geometry.Transform.Translation(v);
      Rhino.RhinoDoc.ActiveDoc.Objects.AddInstanceObject(theidx, t);

  // <Custom additional code> 

  // </Custom additional code> 

I hope that gives you some idea how to go about creating your work :slight_smile:


1 Like

Thank you very much, it was what I was looking for:)


I stuck on this :

This property does not exist in current rhinocommon, what could be an alternative?

Ah sorry, I always forget I am working with Rhino WIP. I’ll see if I can find out how to do this in Rhino 5 (unless a friendly soul beats me to it). I am currently on the phone, but I’ll check in a few hours.


Initialise an int to 0 and loop over the InstanceDefinitions. Increase int if hit wasn’t with the current item. I’m still on the phone, so next snippet isn’t tested, but you should get the idea:

int idx = 0;
foreach(var idef in InstanceDefinitions) {
    if(idef.Id==mydef.Id) break; // match found, we know our idx now,break early out of loop
    idx++; // no match found, do next

Note that this bit if code assumes mydef can be found in the table.

Does this do what you need?


Wow duplicated stuff and file size with block is much faster.thanks

Is it possible to get an instance from Guid rather than string?

//This one always produce result
Rhino.DocObjects.InstanceDefinition mydef = Rhino.RhinoDoc.ActiveDoc.InstanceDefinitions.Find(“myblockName”, true);

//But this one always always produce null, when I reference guid of the block instead of its name.
Rhino.DocObjects.InstanceDefinition mydef2 = Rhino.RhinoDoc.ActiveDoc.InstanceDefinitions.Find(Guid, true);

I see that there is constructor overloading when using second one, but I always get null, even constructor requires either block name as string or guid.

By the way InstanceDefinition already has property Index, so there is no need to loop:)

The Id of a block (instance) will be different from the InstanceDefinition Id. You should cast your object to InstanceObject and query InstanceDefinition property

Does that help?