Insert a block: It seems the only way is with RhinoScript?

Basically I want to mimic some behavior I created in AutoCAD with AutoLISP: Basically just a script to insert blocks and control their scale and rotation (by, for example, selecting a second point). After a bit of a battle with RhinoCommon I noticed that it’s quite an easy task in RhinoScript. The only issue there is that I have to feel my way around in the dark as I don’t know RhinoScript very well. And I’ll probably eventually need to call the RhinoScript from RhinoCommon/C#.

Note that I’ve searched the heck out of the forums and sample code. Learnt lots but what I specifically need isn’t there. RhinoCommon in general, feels cleaner than say, AutoCAD’s .NET API. But as far as working with block references go… I hope I’m looking over a few obvious things but I have to admit, I am struggling.

I’m pondering this solution (which involves RhinoScript):

To wrap my head around the warnings I would really need an example. I think that what I’m trying to achieve will avoid any of the issues but I’m not quite sure. I might have to store the object itself as a reference. I can work with RhinoScript and have successfully created a few tools in the past, but it’s really not something I want to get into. I feel like I’m maxed-out as far as learning new syntax goes.

Ironically, creating a block definition is, while not easy, quite well documented. Inserting that block with an “AddBlock”/“AddInstance” type function (as we have with lines, circles, etc…) eludes me. I’m hoping I’ve just missed something.

Hi @keithscadservices,

To insert a block using RhinoCommon, use ObjectTable.AddInstanceObject.

– Dale

1 Like

Thanks Dale! I played around with that but I couldn’t get anywhere. It returns a Guid. And my inexperience will show because I don’t know what to do with that afterwards.

I looked up the ObjectTable.FindId method which returns a Rhino object. I’m going to play around with that. If the object exists in the table.

I’ve investigated the instanceDefinitionIndex as well as the Transform. The is quite simple. The transform however; perhaps because it’s a block the transform is quite simple but I’m failing to establish it’s purpose and why it isn’t just automatically created with the method. It’s easy enough to create the variable at least (I think!!).

If I’m able to use that FindId method to get the object, then set the rotation and all that stuff after creating the instance I think I’m in business.

It feels like a very involving process especially compared to the RhinoScript InsertBlock method as well as AutoCAD. It caught me off guard I guess because many things are way quicker in RhinoCommon than ACAD’s .NET API. At least once the code works it doesn’t really matter how hard it was to learn. And the samples contain much of the error trapping already.

A cheap Python sample:

import Rhino
import scriptcontext as sc

def test_insert_block():
    idef = sc.doc.InstanceDefinitions.Find('Block 01')
    if not idef:
        return
    
    origin = Rhino.Geometry.Point3d.Origin
    normal = Rhino.Geometry.Vector3d.ZAxis
    
    dir = Rhino.Geometry.Vector3d(5, 5, 0)
    radians = Rhino.RhinoMath.ToRadians(20)
    
    t = Rhino.Geometry.Transform.Translation(dir)
    s = Rhino.Geometry.Transform.Scale(origin, 1.0)
    r = Rhino.Geometry.Transform.Rotation(radians, normal, origin)
    xform = t * s * r
    
    sc.doc.Objects.AddInstanceObject(idef.Index, xform )
    sc.doc.Views.Redraw()
    
if __name__ == "__main__":
    test_insert_block()

– Dale

2 Likes

Thanks again Dale!

I think things are coming together in my head. I had extracted the values of the transform from an existing block (or at least I thought) as that HAD to be the place where the block’s location and rotation was stored, but all it had in it was 1’s and 0’s.

I just have to figure out attributes and vectors. I will have two versions: One that just inserts the block at a base point and one that inserts the block with a rotation angle included. And hopefully I can automatically set up vectors as well.

The easier stuff turns out to be pretty challenging. I felt like I had to learn the “harder” stuff first. But the reward is that I will be able to dynamically create custom blocks and integrate that into a workflow of some kind (which was doable but a bit of a pain in ACAD).

Appreciate the help!!

Make sure you use the InstanceObject.InstanceXform Property. It is available for InstanceObjects. For instance this to print the xforms for all blocks in the document.

import scriptcontext as sc
import Rhino.DocObjects

for o in sc.doc.Objects:
    if isinstance(o, Rhino.DocObjects.InstanceObject):
        print o.InstanceXform
1 Like

Thanks Nathan! Whatever I pulled it wasn’t from the xForm property.

Before getting into C# I only programmed in AutoLISP. I tried a bit of VBA and hated it. One thing that blows my mind is how easy it is to translate the Python into C#. Python being as popular as it is, I realized that if I drop the ‘C#’ from my searches and just look for the solution (regardless of language).

I’ve already got the attribute(s) working (that took less time to learn than it did for me in AutoLISP). Now I just need to learn about what a vector is and sort out the rotation part.

I got things working… kind of.

The issue I’m having is first related to issues rotating the object, and thereafter, transformations not being applied to the object.

If I rotate the object when I first create it (that is, making a Transformation with both a transform and a rotate) it seems to be rotating the object about the world origin… kind of (you’ll see what I’m trying to do in my code).
I’ve conjured up a few work-arounds for my knowledge gap. One was to separate the rotation and transform; that is just place the object at 0,0 and rotate it there, then move it. This will work fine for what I’m trying to do at the moment, but will place a ceiling on future projects. Also, I anticipate that whatever I’m doing wrong here will come back to bite me later.

            int i = blockDefinition.Index; 

            var origin = Rhino.Geometry.Point3d.Origin;
            var dir = new Rhino.Geometry.Vector3d(pt0);
            var normal = new Rhino.Geometry.Vector3d(0, 0, 1);

                      
            Transform s = Rhino.Geometry.Transform.Scale(origin, blockScale);
            Transform r = Rhino.Geometry.Transform.Rotation(a, normal, origin);
            Transform t = Rhino.Geometry.Transform.Translation(0, 0, 0);


            Transform xForm = t * s * r;

            Guid instanceGuid = doc.Objects.AddInstanceObject(i, xForm);

            Rhino.DocObjects.RhinoObject newInstanceObject = doc.Objects.FindId(instanceGuid);

            Rhino.DocObjects.ObjectAttributes objectAttributes = new Rhino.DocObjects.ObjectAttributes();

            objectAttributes.Name = "Test";

            newInstanceObject.Attributes.SetUserString("Test", blockAttributeText);

            t = Rhino.Geometry.Transform.Translation(dir);

            bool tTest = newInstanceObject.Geometry.Translate(dir); // returns "true" but does nothing

            doc.Objects.Transform(instanceGuid, t, false); // works but might create issues later on

            RhinoApp.WriteLine(tTest.ToString() );

            return Result.Success;

I can post the entire code if anyone wants it’s just a draft/messy