DisplayConduit - DrawForeground - Exploded Block - Z-Fighting?

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