Read Transform Matrix of block Instance in FILE3DM

unhandled

#1

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?


(Lando Schumpich) #2

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


#3

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


(Dale Fugier) #4

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


(Lando Schumpich) #5

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.


#6

Thank you both!


(Dale Fugier) #7

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


#8

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);
                }
            }
        }

(Dale Fugier) #9

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


#10

Hi @dale

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


(Dale Fugier) #11

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


#12

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.)


(Dale Fugier) #13

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


#14

@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!


(Dale Fugier) #15

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

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


#16

@dale, thank you, I opted for a github library that I could bend to make binary stl’s, works well enough. (we produce roughly 15000 parts per eannum, each part with up to 6 components saved as stls, ascii was not an option).

Question if i may, I noticed a bug in my output regarding extracting volume from the block instance, in all cases the volume of any iteration of a block instance was always the same, the volume of the original instance (idef)?, not the iteration.(iref)?

Am I correct to assume that given I can already extract the volume of the original(idef), and knowing the scaling factors I can deduce a fairly accurate volume by multiplying out the scale factors, or is there a way to extract the volume of the scaled geometry in (iref)?the iterated instance I am working with at runtime, I cannot see a way to do so, a duplicate and volumecompute of the iref jsut gives the original (idef) volume again.

Many thanks!