DisplayConduit - Cannot Hide Block Instance Definition?

Hello,

I’m working on a Display Conduit script in Python 3 that works for block instances as well.

My issue is that I can get the Block nested geometry to show in the conduit but it is not drawing in the foreground like the rest of my object types.

You can see that Meshes (M), Breps (B), and Curves (C) are all showing correctly but the BI (exploded breps, curves, etc.) are having z.fighting issues with the parent/non-exploded block.

Thank you all for any guidance you can provide!

Here’s the code, it’s a hybrid of the C# @menno provided here and the explode block sample code that @dale provided [here] adapted for Python 3(RhinoCommon nested instances - #2 by dale):

Rhino Test File:
test_file.3dm (3.4 MB)

Python Code:

import Rhino
import scriptcontext as sc
import rhinoscriptsyntax as rs
from System.Drawing import Color

class PreviewAttributes:
    def __init__(self):
        self.ObjectColor = Color.Black
        self.ColorSource = Rhino.DocObjects.ObjectColorSource.ColorFromObject
        self.WireDensity = 1
        self.LayerIndex = -1
        self.CurveThickness = 5
        self.PointStyle = Rhino.Display.PointStyle.ControlPoint
        self.PointPixels = 3
        self.TextColor = Color.White

    @staticmethod
    def Selected():
        attr = PreviewAttributes()
        attr.ObjectColor = Color.BlueViolet  # Default is Color.Yellow
        attr.ColorSource = Rhino.DocObjects.ObjectColorSource.ColorFromObject
        attr.TextColor = Color.Black
        return attr

    @staticmethod
    def New():
        attr = PreviewAttributes()
        attr.ColorSource = Rhino.DocObjects.ObjectColorSource.ColorFromLayer
        attr.LayerIndex = sc.doc.Layers.CurrentLayer.LayerIndex
        return attr

    @staticmethod
    def Warning():
        attr = PreviewAttributes()
        attr.ObjectColor = Color.Red
        attr.ColorSource = Rhino.DocObjects.ObjectColorSource.ColorFromObject
        attr.TextColor = Color.White
        return attr


class PreviewDisplayConduit(Rhino.Display.DisplayConduit):
    def __init__(self):
        super(PreviewDisplayConduit, self).__init__()
        self._objects = {}
        self._exploded_geometries = []

    def AddObject(self, obj, attributes):
        if obj:
            self._objects[obj] = attributes

    def ClearObjects(self):
        self._objects.clear()
        self._exploded_geometries.clear()

    def DrawForeground(self, e):
        self._exploded_geometries.clear()
        
        for obj, attr in self._objects.items():
            color = attr.ObjectColor
            material = Rhino.Display.DisplayMaterial(color)
            if attr.ColorSource == Rhino.DocObjects.ObjectColorSource.ColorFromLayer:
                layer = sc.doc.Layers[attr.LayerIndex]
                color = layer.Color

            if isinstance(obj, Rhino.Geometry.Curve):
                e.Display.DrawCurve(obj, color, attr.CurveThickness)
            elif isinstance(obj, Rhino.Geometry.Brep):
                e.Display.DrawBrepShaded(obj, material)
                # e.Display.DrawBrepWires(obj, color, attr.WireDensity)
            elif isinstance(obj, Rhino.Geometry.Point):
                e.Display.DrawPoint(obj.Location, attr.PointStyle, attr.PointPixels, color)
            elif isinstance(obj, Rhino.Geometry.Mesh):
                e.Display.DrawMeshWires(obj, color)
            elif isinstance(obj, Rhino.Geometry.TextDot):
                e.Display.DrawDot(obj.Point, obj.Text, color, attr.TextColor)
            elif isinstance(obj, Rhino.Geometry.InstanceReferenceGeometry):
                # Explode Block Geometry Here
                self.ExplodeBlockHelper(e, obj, attr, Rhino.Geometry.Transform.Identity)
            else:
                Rhino.RhinoApp.WriteLine("Obj Type Is: " + str(obj))
        
        # Draw exploded geometries after processing all objects
        for geom, attr in self._exploded_geometries:
            color = attr.ObjectColor
            material = Rhino.Display.DisplayMaterial(color)
            if attr.ColorSource == Rhino.DocObjects.ObjectColorSource.ColorFromLayer:
                layer = sc.doc.Layers[attr.LayerIndex]
                color = layer.Color

            if isinstance(geom, Rhino.Geometry.Curve):
                e.Display.DrawCurve(geom, color, attr.CurveThickness)
            elif isinstance(geom, Rhino.Geometry.Brep):
                e.Display.DrawBrepShaded(geom, material)
            elif isinstance(geom, Rhino.Geometry.Point):
                e.Display.DrawPoint(geom.Location, attr.PointStyle, attr.PointPixels, color)
            elif isinstance(geom, Rhino.Geometry.Mesh):
                e.Display.DrawMeshWires(geom, color)
            elif isinstance(geom, Rhino.Geometry.TextDot):
                e.Display.DrawDot(geom.Point, geom.Text, color, attr.TextColor)

    def ExplodeBlockHelper(self, e, iref, attr, xform):
        idef = sc.doc.InstanceDefinitions.FindId(iref.ParentIdefId)
        if idef is None:
            return False

        xform = xform * iref.Xform
        do_xform = xform.IsValid and not xform.Equals(Rhino.Geometry.Transform.Identity)

        objects = idef.GetObjects()
        for obj in objects:
            if obj is None:
                continue

            if isinstance(obj, Rhino.DocObjects.InstanceObject):
                # Explode any nested instances...
                self.ExplodeBlockHelper(e, obj.Geometry, attr, xform)
                continue

            geom = obj.Geometry.Duplicate()
            if do_xform:
                if xform.SimilarityType == Rhino.Geometry.TransformSimilarityType.NotSimilarity:
                    if not geom.MakeDeformable() and isinstance(geom, Rhino.Geometry.Curve):
                        geom = geom.ToNurbsCurve()

                if not geom.Transform(xform):
                    continue

                if xform.SimilarityType == Rhino.Geometry.TransformSimilarityType.OrientationReversing:
                    if isinstance(geom, Rhino.Geometry.Brep):
                        geom.Flip()
                    elif isinstance(geom, Rhino.Geometry.Mesh):
                        geom.Flip(True, True, True)

            self._exploded_geometries.append((geom, attr))

    def CalculateBoundingBox(self, e):
        bbox = Rhino.Geometry.BoundingBox()
        for obj in self._objects.keys():
            bbox.Union(obj.GetBoundingBox(True))
        e.IncludeBoundingBox(bbox)


def PreviewObjects():
    selected_objects = rs.GetObjects("Select objects to preview")
    if not selected_objects:
        return

    conduit = PreviewDisplayConduit()
    attr = PreviewAttributes.Selected()

    for obj_id in selected_objects:
        rhino_obj = rs.coercegeometry(obj_id)
        conduit.AddObject(rhino_obj, attr)

    conduit.Enabled = True
    sc.doc.Views.Redraw()

    rs.GetString("Press Enter to end preview")
    conduit.Enabled = False
    conduit.ClearObjects()
    sc.doc.Views.Redraw()


if __name__ == "__main__":
    PreviewObjects()

Kindly bumping this again as a month later I am still stuck and I learned the issue is that I cannot seem to hide the geometry of the original Block Instance being added to the conduit.

For all object types except InstanceDefinitions, it works fine since I can directly call hide on the object but the HideObjects and Objects.Hide methods in Rhinoscriptsyntax and Scriptcontext.doc respectively do not hide the objects despite being provided with the GUID of the objects to be hidden.

Can someone please help me understand how to hide the geometry in the block or can I simply hide the entire original block? I would think yes, but I could not get that working.

@stevebaer am I overlooking a simple function to be able to hide the InstanceDefinition/Reference?

Hide Function Excerpt:

    def HideOriginalInstance(self, iref):
        idef = sc.doc.InstanceDefinitions.FindId(iref.ParentIdefId)
        if idef is None:
            return False

        objs_to_hide = []

        for obj in idef.GetObjects():
            sc.doc.Objects.Hide(obj.Id, False)
            objs_to_hide.append(obj.Id)

        Rhino.RhinoApp.WriteLine(f"Hide Objs: {objs_to_hide}")
        # rs.HideObjects(objs_to_hide)

Full Working Code (Python 3):

import Rhino
import scriptcontext as sc
import rhinoscriptsyntax as rs
from System.Drawing import Color

class PreviewAttributes:
    def __init__(self):
        self.ObjectColor = Color.Black
        self.ColorSource = Rhino.DocObjects.ObjectColorSource.ColorFromObject
        self.WireDensity = 1
        self.LayerIndex = -1
        self.CurveThickness = 5
        self.PointStyle = Rhino.Display.PointStyle.ControlPoint
        self.PointPixels = 3
        self.TextColor = Color.White

    @staticmethod
    def Selected():
        attr = PreviewAttributes()
        attr.ObjectColor = Color.BlueViolet  # Default is Color.Yellow
        attr.ColorSource = Rhino.DocObjects.ObjectColorSource.ColorFromObject
        attr.TextColor = Color.Black
        return attr

    @staticmethod
    def New():
        attr = PreviewAttributes()
        attr.ColorSource = Rhino.DocObjects.ObjectColorSource.ColorFromLayer
        attr.LayerIndex = sc.doc.Layers.CurrentLayer.LayerIndex
        return attr

    @staticmethod
    def Warning():
        attr = PreviewAttributes()
        attr.ObjectColor = Color.Red
        attr.ColorSource = Rhino.DocObjects.ObjectColorSource.ColorFromObject
        attr.TextColor = Color.White
        return attr


class PreviewDisplayConduit(Rhino.Display.DisplayConduit):
    def __init__(self):
        super(PreviewDisplayConduit, self).__init__()
        self._objects = {}
        self._hidden_objects = []
        self._exploded_geometries = []

    def AddObject(self, obj, attributes):
        if obj:
            self._objects[obj] = attributes

            # Check if the object is an instance reference (block instance)
            if isinstance(obj, Rhino.Geometry.InstanceReferenceGeometry):
                # Find the corresponding InstanceObject in the document
                parent_id = obj.ParentIdefId
                Rhino.RhinoApp.WriteLine(f"Hide Id: {parent_id}")
                if parent_id is not None:
                    self.HideOriginalInstance(obj)

    def ClearObjects(self):
        self._objects.clear()
        self._exploded_geometries.clear()
        self.ShowHiddenObjects()

    def ShowHiddenObjects(self):
        for obj_id in self._hidden_objects:
            if rs.IsObjectHidden(obj_id):
                rs.ShowObject(obj_id)
        self._hidden_objects.clear()

    def DrawForeground(self, e):
        self._exploded_geometries.clear()
        
        for obj, attr in self._objects.items():
            color = attr.ObjectColor
            material = Rhino.Display.DisplayMaterial(color)
            if attr.ColorSource == Rhino.DocObjects.ObjectColorSource.ColorFromLayer:
                layer = sc.doc.Layers[attr.LayerIndex]
                color = layer.Color

            if isinstance(obj, Rhino.Geometry.Curve):
                e.Display.DrawCurve(obj, color, attr.CurveThickness)
            elif isinstance(obj, Rhino.Geometry.Brep):
                e.Display.DrawBrepShaded(obj, material)
            elif isinstance(obj, Rhino.Geometry.Point):
                e.Display.DrawPoint(obj.Location, attr.PointStyle, attr.PointPixels, color)
            elif isinstance(obj, Rhino.Geometry.Mesh):
                e.Display.DrawMeshWires(obj, color)
            elif isinstance(obj, Rhino.Geometry.TextDot):
                e.Display.DrawDot(obj.Point, obj.Text, color, attr.TextColor)
            elif isinstance(obj, Rhino.Geometry.InstanceReferenceGeometry):
                # Explode Block Geometry Here
                self.ExplodeBlockHelper(e, obj, attr, Rhino.Geometry.Transform.Identity)
            else:
                Rhino.RhinoApp.WriteLine("Obj Type Is: " + str(obj))
        
        # Draw exploded geometries after processing all objects
        for geom, attr in self._exploded_geometries:
            color = attr.ObjectColor
            material = Rhino.Display.DisplayMaterial(color)
            if attr.ColorSource == Rhino.DocObjects.ObjectColorSource.ColorFromLayer:
                layer = sc.doc.Layers[attr.LayerIndex]
                color = layer.Color

            if isinstance(geom, Rhino.Geometry.Curve):
                e.Display.DrawCurve(geom, color, attr.CurveThickness)
            elif isinstance(geom, Rhino.Geometry.Brep):
                e.Display.DrawBrepShaded(geom, material)
            elif isinstance(geom, Rhino.Geometry.Point):
                e.Display.DrawPoint(geom.Location, attr.PointStyle, attr.PointPixels, color)
            elif isinstance(geom, Rhino.Geometry.Mesh):
                e.Display.DrawMeshWires(geom, color)
            elif isinstance(geom, Rhino.Geometry.TextDot):
                e.Display.DrawDot(geom.Point, geom.Text, color, attr.TextColor)

    def ExplodeBlockHelper(self, e, iref, attr, xform):
        idef = sc.doc.InstanceDefinitions.FindId(iref.ParentIdefId)
        if idef is None:
            return False

        xform = xform * iref.Xform
        do_xform = xform.IsValid and not xform.Equals(Rhino.Geometry.Transform.Identity)

        objects = idef.GetObjects()
        for obj in objects:
            if obj is None:
                continue

            if isinstance(obj, Rhino.DocObjects.InstanceObject):
                # Explode any nested instances...
                self.ExplodeBlockHelper(e, obj.Geometry, attr, xform)
                continue

            geom = obj.Geometry.Duplicate()
            if do_xform:
                if xform.SimilarityType == Rhino.Geometry.TransformSimilarityType.NotSimilarity:
                    if not geom.MakeDeformable() and isinstance(geom, Rhino.Geometry.Curve):
                        geom = geom.ToNurbsCurve()

                if not geom.Transform(xform):
                    continue

                if xform.SimilarityType == Rhino.Geometry.TransformSimilarityType.OrientationReversing:
                    if isinstance(geom, Rhino.Geometry.Brep):
                        geom.Flip()
                    elif isinstance(geom, Rhino.Geometry.Mesh):
                        geom.Flip(True, True, True)

            self._exploded_geometries.append((geom, attr))

    def HideOriginalInstance(self, iref):
        idef = sc.doc.InstanceDefinitions.FindId(iref.ParentIdefId)
        if idef is None:
            return False

        objs_to_hide = []

        for obj in idef.GetObjects():
            sc.doc.Objects.Hide(obj.Id, False)
            objs_to_hide.append(obj.Id)

        Rhino.RhinoApp.WriteLine(f"Hide Objs: {objs_to_hide}")
        # rs.HideObjects(objs_to_hide)

    def CalculateBoundingBox(self, e):
        bbox = Rhino.Geometry.BoundingBox()
        for obj in self._objects.keys():
            bbox.Union(obj.GetBoundingBox(True))
        e.IncludeBoundingBox(bbox)


def PreviewObjects():
    selected_objects = rs.GetObjects("Select objects to preview")
    if not selected_objects:
        return

    conduit = PreviewDisplayConduit()
    attr = PreviewAttributes.Selected()

    for obj_id in selected_objects:
        rhino_obj = rs.coercegeometry(obj_id)
        conduit.AddObject(rhino_obj, attr)

    conduit.Enabled = True
    sc.doc.Views.Redraw()

    rs.GetString("Press Enter to end preview")
    conduit.Enabled = False
    conduit.ClearObjects()
    sc.doc.Views.Redraw()


if __name__ == "__main__":
    PreviewObjects()

Thank you all for the help!

EDIT:

Alright, after reevaluating the code I was able to get it working simply by handling it within the PreviewObjects function:

def PreviewObjects():
    selected_objects = rs.GetObjects("Select objects to preview")
    if not selected_objects:
        return

    conduit = PreviewDisplayConduit()
    attr = PreviewAttributes.Selected()

    hide_objs = []

    for obj_id in selected_objects:
        rhino_obj = rs.coercegeometry(obj_id)
        hide_objs.append(obj_id)
        rs.HideObjects(hide_objs)
        conduit.AddObject(rhino_obj, attr)

    conduit.Enabled = True
    sc.doc.Views.Redraw()

    rs.GetString("Press Enter to end preview")
    conduit.Enabled = False
    conduit.ClearObjects()
    rs.ShowObjects(hide_objs)
    sc.doc.Views.Redraw()

Simply using the IDs from the selected_objects instead of trying to fetch the IDs from within the InstanceDefinitions…

Go figure, I was overcomplicating it :pensive:

2 Likes