Weird behaviour when turning on and off preview for custom data type

I’m creating some components that preview voxel data as a point cloud with colours. I’ve got my custom data type set up with the corresponding DrawViewportWires override to draw the bounding box:

public class VoxelGoo : GH_GeometricGoo<VoxelStructure>, IGH_PreviewData
{
  ...

  /// <summary>
  /// Clipping box for drawing.
  /// </summary>
  public BoundingBox ClippingBox => Boundingbox;
  
  public void DrawViewportWires(GH_PreviewWireArgs args)
  {
      args.Pipeline.DrawBox(Boundingbox, Color.DarkOliveGreen);
  }
...
}

Which works fine as seen in the below screenshot when the relevant preview is activated on the canvas:

I’ve also got a custom preview component that renders the voxels as a point cloud, with associated colours for their values:

protected override void SolveInstance(IGH_DataAccess DA)
{
    GH_Structure<IGH_Goo> voxelGoos = new GH_Structure<IGH_Goo>();
    Interval displayRange = new Interval();
    int weight = 2;
    string colourBarTitle = "";

    if (!DA.GetDataTree("Voxel Data", out voxelGoos)) return;
    if (!DA.GetData("Scalar Range", ref displayRange)) return;
    if (!DA.GetData("Point Weight", ref weight)) return;
    if (!DA.GetData("Colourbar Title", ref colourBarTitle)) return;

    _colourBarTitle = colourBarTitle;

    _weight = weight < 1 ? 1 : weight;

    _displayClouds = new List<PointCloud>();

    foreach (var branch in voxelGoos.Branches)
    {
        if (branch != null)
        {
            foreach (var voxelGoo in branch)
            {
                if (voxelGoo == null) continue;
                var display = new VoxelGoo((VoxelGoo)voxelGoo);
                _displayClouds.Add(GeneratePointCloud(display, displayRange.T0, displayRange.T1));
            }
        }
    }
    _interval = displayRange;
    _voxelGoos = voxelGoos;
}

/// <summary>
/// We need this to be able to draw the preview.
/// </summary>
/// <param name="args"></param>
public override void DrawViewportWires(IGH_PreviewArgs args)
{
    base.DrawViewportWires(args);

    if (!Locked)
    {

       ... Colourbar code ...

        if (_displayClouds.Count > 0)
        {
            foreach (var cloud in _displayClouds)
            {
                args.Display.DrawPointCloud(cloud, _weight);
            }
        }
    }
}

If both of these components have their preview enabled, the custom preview component displays the point cloud with a colourbar:

However, if I deactivate the preview on the “SpatialCorrelation” component, stopping the draw call on my custom class, my preview component also deactivates the preview, despite it still being “activated” on the canvas (notice how the colourbar isn’t drawn):

I have tried debugging this in Visual Studio, and when the previously connected component has preview deactivated, the corresponding DrawViewportWires method in the Preview component is not even called, hence not even the colourbar is drawn. Visual studio doesn’t give any information as to why the draw calls are not happening in the custom preview component.

Can anyone suggest why simply deactivating the preview of a previous component affects my upstream preview?

Please let me know if there’s some detail I haven’t included!

Thanks!

Hi @TobyW,

Are you overriding ClippingBox on your component?

Hi Kike,

The ClippingBox is the same bounding box as you can see on the screen, although I check to see if its planar cause that has caused issues in the past. Here’s the code for the custom Goo type:

/// <summary>
/// Clipping box for drawing.
/// </summary>
public BoundingBox ClippingBox => Boundingbox;

/// <summary>
/// Geometric bounding box.
/// </summary>
public override BoundingBox Boundingbox
{
    get
    {
        ((double minx, double miny, double minz), (double maxx, double maxy, double maxz)) = Value.BoundingBox;
        _boundingBox = new BoundingBox(new Point3d(minx, miny, minz), new Point3d(maxx, maxy, maxz));

        // Check if box is planar in any dimension and if so, inflate a little
        if (_boundingBox.Diagonal.X == 0) _boundingBox.Inflate(0, 0, 0.001);
        if (_boundingBox.Diagonal.Y == 0) _boundingBox.Inflate(0, 0, 0.001);
        if (_boundingBox.Diagonal.Z == 0) _boundingBox.Inflate(0, 0, 0.001);

        return _boundingBox;
    }
}

I don’t think the clipping box is the issue though, because if you look at the output when the preview component is turned on there is a colourbar. This isn’t drawn, so the DrawViewportWires method in the Preview component is not even called.

I was asking about the ClippingBox on the component, not on the Goo type.

The code you share look like the Goo type ClippingBox property.

Nope, I’ve not touched ClippingBox in the preview component at all. Would you be able to explain why this might cause the issue? What would prevent the preview component calling the DrawViewportWires method? I can’t seem to see the call stack, visual studio just says “external code”.

A component that implements DrawViewportWires or DrawViewportMeshes should return on ClippingBox the bounding box of the data is going to draw on these two methods. Rhino infers several things from this property but one is the bounding box of the scene it has to display.

If another component is already enabling this volume Rhino will display correctly your preview, this is why enabling the other component makes your able to display.

The thing is the default implementation of GH_Component.ClippingBox use to be ok for most cases, but it relays on the implementation of the input parameters. It returns a union box of all input parameters that implement IsPreviewCapable and its value is true.

What type of parameter are you using for hosting ‘Voxel Data’?
Is it custom?
If it is the case, you should implement ClippingBox there and components that use this type of parameter will work without any additional effort.

To do so implement IGH_PreviewObject on your parameter.
To implement this interface easily there are three helper methods that you may want to use:

  • Preview_ComputeClippingBox
  • Preview_DrawWires
  • Preview_DrawMeshes

Hi Kike,

So I have a VoxelStructure (my backend C# class for number crunching, separate from Grasshopper), and a VoxelGoo class for making it work with Grasshopper, public class VoxelGoo : GH_GeometricGoo<VoxelStructure>, IGH_PreviewData. ClippingBox is correctly implemented in VoxelGoo. I didn’t originally have an explicit parameter, I was just passing VoxelGoo around. I have since created one:

public class VoxelParam : GH_Param<VoxelGoo>, IGH_PreviewObject
{
    public override Guid ComponentGuid => new Guid("5A106831-86DF-4D41-B38F-53D0D54FDEB3");

    public bool IsPreviewCapable => true;

    public BoundingBox ClippingBox
    {
        get
        {
            return Preview_ComputeClippingBox();
        }
    }

    bool _hidden;
    public bool Hidden 
    {
        get
        {
            return _hidden;
        }
        set
        {
            _hidden = value;
        }
    }

    public VoxelParam()
        : base(new GH_InstanceDescription("Voxel Structure", "Voxels", "A voxel structure", "IPMSpatialAnalysis", "Types"))
    { }

    public void DrawViewportMeshes(IGH_PreviewArgs args)
    {
        Preview_DrawMeshes(args);
    }

    public void DrawViewportWires(IGH_PreviewArgs args)
    {
        Preview_DrawWires(args);
    }
}

I am still getting the weird behaviour though, so the Preview component will only display when one of the previous components has display enabled. I am still struggling to see the link. These components don’t even need to be directly connected for this behaviour.

Now you have to use this new type of parameter on the first input of your component.
Instead of using AddGenericParameter you have to do something like AddParameter(new VoxelParam, ...).

And since your Goo implements IGH_GeometricGoo, ideally your param should inherit from GH_PersistentGeometryParam<VoxelGoo>.

Hi Kike,

So that’s all done, I’ve got my public class VoxelParam : GH_PersistentGeometryParam<VoxelGoo>, IGH_PreviewObject set up, and all my components now have pManager.AddParameter(new VoxelParam(), "Voxel Data",...) as the input. I still get this same display behaviour.

Is this something new with Rhino 8? I never saw this issue with Rhino 7, although I have not tested this exact code.

Thanks for your time and patience with this by the way!

All I’m saying here is the same in Rhino 7 and 8.

I see now the issue.
GH_Component just draws the inputs that have internalized data and the outputs.
So the default implementation will not display anything on your component.

This is why you need to override ClippingBox on your component like this.

    public override BoundingBox ClippingBox
    {
      get
      {
        var clippingBox = base.ClippingBox;
        if (_displayClouds != null)
        {
          for each (var cloud in _displayClouds)
            clippingBox.Union(cloud.BoundingBox);
        }
        return clippingBox;
      }
    }
1 Like

Legend, this has fixed it! Really appreciate the help.

1 Like