Read Transform Matrix of block Instance in FILE3DM

Hey All,

I need to read the transform matrix of block instances with specific names in up to 26000 rhino files…
I have successfully manage to read transform matrix previously, is a DOC… but… read as a FILE3DM its not working…

While I can access the InstanceDefintions which returns a format InstanceDefintionGeometry, which oddly I can scale, transform and translate, however I cannot actually read the current transform information back from an existing file…

Any leads, I really don’t want to sit opening 26000 files :wink:

QUASICODE:

GetDirectory
new List(all files that end in .3dm)

iterate List {
AA = File3DM.read(next file)
var instancelists = AA.Instancedefinitions

iterate each instance{
if instance.name = “somename” then
Transform XXX = instance.blockinstanceXform

^^ this is where i fall down. Any help?

The instancedefinition is just some data but not an actual object in your file, what you are looking for are all Instancereferences

Notably the InstanceObject.InstanceXform attribute

Yes, I know, thats the question… it does not exist in the list of things that can be read, without actually opening the file in rhino into a DOC… I can do this fine with a file open in rhino, Im needing to do it without actually having to load 26000 files one at a time into rhino.

@dale , what am I missing here, is there a way to read the transform matrix without loading the file into rhino?

image

Hi @ChristopherBotha,

An instance object’s transformation is stored on the instance reference geometry itself, not the instance definition.

using (var file = File3dm.Read(filename))
{
  foreach (var obj in file.Objects)
  {
    if (obj.Geometry is InstanceReferenceGeometry iref)
    {
      var xform = iref.Xform;
      // todo...
    }
  }
}

– Dale

Beat me to it …

Basically there is no table for all instanceReferences, even if you were to open the file, so the only way is to iterate over all objects and look if they are instances of your wanted instanceDefinition.

Thank you both!

For those wanting the Python equivalent, here you go:

import Rhino
import clr

filename = ...

with Rhino.FileIO.File3dm.Read(filename) as f:
    for rhobj in f.Objects:
        if type(rhobj.Geometry) is Rhino.Geometry.InstanceReferenceGeometry:
            iref = clr.Convert(rhobj.Geometry, Rhino.Geometry.InstanceReferenceGeometry)
            xform = iref.Xform
            print xform

– Dale

3 Likes

Hi @dale,

Using below I am 99% of where i need to be, what I seem to be unable to extract is the name of the block instance, inside of the objectloop (I can extract the scale info no problem, but iref NAME returns the object name, not the name of the blockinstance?

A secondary goal (id like but not critical) would be to extract the volume of the geometry inside the blockinstance, in my case always only one polysrf.)

(thank you for the post of how to deconvolute a transform array, super helpful!)

private void Scrubber()
        {
            using (var GetFolder = new FolderBrowserDialog())
            {
                string ConcatString = "";
                DialogResult result = GetFolder.ShowDialog();
                if (result == DialogResult.OK)
                {
                    DirectoryInfo d = new DirectoryInfo(GetFolder.SelectedPath);
                    Files = d.GetFiles("*.3dm").ToList();
                    foreach (FileInfo file in Files)
                    {
                        using (var CurrentFile = File3dm.Read(file.FullName))
                        {
                            ConcatString += file.Name + ", ";                            
                            foreach (var obj in CurrentFile.Objects)
                            {                               
                                if (obj.Name.Contains("Ring Rail")){ ConcatString += obj.Name + ", ";}
                                //retrieving instance name here retrives the high level name, not the blockinstance name itself
                                //ie: Object Name = Brick
                                //actiual instance name inside that object is SquareBrick, I need SquareBrick.
                                if (obj.Geometry is InstanceReferenceGeometry iref)
                                {
                                    var xform = iref.Xform;
                                    //we only need XScale for now, leaving rest in for next coder "just in case"
                                    // rest at https://github.com/mcneel/rhino-developer-samples/blob/master/rhinoscript/DecomposeXform.rvb
                                    var XScale = Math.Sqrt((Math.Pow(xform.M00,2.0)) + (Math.Pow(xform.M01, 2.0)) + (Math.Pow(xform.M02, 2.0)));
                                    var YScale = Math.Sqrt((Math.Pow(xform.M10, 2.0)) + (Math.Pow(xform.M11, 2.0)) + (Math.Pow(xform.M12, 2.0)));
                                    var ZScale = Math.Sqrt((Math.Pow(xform.M20, 2.0)) + (Math.Pow(xform.M21, 2.0)) + (Math.Pow(xform.M22, 2.0)));
                                    var XLocation = xform.M03; var YLocation = xform.M03; var ZLocation = xform.M03;
                                    var XRotate = Math.Atan2(-xform.M21, xform.M22);
                                    var YRotate = Math.Asin(xform.M20);
                                    var ZRotate = Math.Atan2(xform.M10, xform.M00);

                                    //stuck at retrieving the instance name in this loop. 

   
                                    ConcatString += XScale.ToString("0.##") + "mm, ";

                                }
                        }
                        ConcatString += Environment.NewLine;
                    }
                    RhinoApp.WriteLine(ConcatString);
                }
            }
        }

This should do the trick:

using (var file = File3dm.Read(filename))
{
  foreach (var obj in file.Objects)
  {
    if (obj.Geometry is InstanceReferenceGeometry iref)
    {
      var xform = iref.Xform;

      string idef_name;
      var idef = file.AllInstanceDefinitions.FindId(iref.ParentIdefId);
      if (null != idef)
      {
        idef_name = idef.Name;
      }
    }
  }
}

– Dale

Hi @dale

Im in rhino 5 and i get "does not contain .AllInstanceDefintions ", I tried Instancedefintions instead, but it again has no .FindId.

For Rhino 5, you can enumerate the File3dm.InstanceDefinitions list. Or use LINQ:

using System.Linq;
...
var idef = file.InstanceDefinitions.Single(i => i.Id == iref.ParentIdefId);

– D

Perfect, that did the trick! Thanks again.

If you are at a loose end, see my second question re extraction the volume of that polysurface in the block instance, its only of academic interest as I already know the volume before it was scaled, and ergo can formulate the volume multiplied by the scaling , however would be interesting to see the code to extract it directly from the polysrf itself in that instance (in my case its a rectangle so formula would be right, but if it was a non linear object, im guessing no formula for multiplying scale on a volume would be accurate.)

After you get the InstanceDefinitionGeometry object, you can get the ids of the definition geometry using InstanceDefinitionGeometry.GetObjectIds. Lookup these objects in the object table, or File3dm.Objects. When found, make a copy of the geometry and then transform it with the Transform you obtained prior. Then make your mass properties calculation.

– Dale

1 Like

@dale, thank you that worked, the gem scraper is done collecting 15000 files already!

another question if I may. this is one half of the thing I’m building, my problem now is after I complete stages below, I am left with nice clean, sealed, booleaned meshes in a
List(Mesh), (without affecting anything in the document, just in memory) and I need to save out each List(Mesh) as its own STL file.Pointers would be most appreciated!

Hi @ChristopherBotha,

RhinoCommon doesn’t have a function or class to write STL files.

One option is to just script the Export command. Here is an example:

Export stl files with C# component in GH - #2 by dsonntag

The STL file format is pretty simple. It doesn’t take much to write your own file write. Here is an example:

https://github.com/mcneel/rhino-developer-samples/blob/6/rhinocommon/cs/SampleCsCommands/SampleCsWriteStl.cs

– Dale

1 Like

A post was split to a new topic: Calculating volueme of instance reference