Grasshopper Component Display with Ngons

Hi,

I would like to display meshes with ngons with black edges as ngon outlines.

I have already done this in the following simple code as component (Code of component and screenshot below).

My question: I have a bunch of components that are working with ngons in meshes.
Is there any way to apply this procedure to all of them, without copying this type of code in all the components? I.E. inhereit from some main class, a preview of ngons meshes? If yes how to do this?

Once I did this my creating custom parameter for another grasshopper add-on. But this requires a bit of work to do. I am wondering if there is other way than creating a custom parameter just to solve this tiny display preference of mine.

using System;
using System.Collections.Generic;
using Grasshopper;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using NGonsCore;
using Rhino.Geometry;

namespace SubD.NGons {
    public class NGon : GH_Component {


        Mesh mesh = new Mesh();
        Line[] lines = new Line[0];
        BoundingBox bbox = BoundingBox.Empty;

        public override BoundingBox ClippingBox => bbox;

        public override void DrawViewportMeshes(IGH_PreviewArgs args) {
            
            args.Display.DrawMeshShaded(mesh, new Rhino.Display.DisplayMaterial(System.Drawing.Color.White));
        }

        public override void DrawViewportWires(IGH_PreviewArgs args) {
            args.Display.DrawLines(lines, System.Drawing.Color.Black);
        }

        public NGon()
          : base("NGon", "NGon",  "NGon",    "SubD", "NGons") {

        }

        protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) {
            pManager.AddMeshParameter("Mesh", "M", "Mesh", GH_ParamAccess.tree);
            pManager.HideParameter(0);
        }


        protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) {
            pManager.AddMeshParameter("Mesh", "M", "Mesh", GH_ParamAccess.tree);

            pManager.HideParameter(0);
        }


        protected override void SolveInstance(IGH_DataAccess DA) {

            mesh = new Mesh();
            lines = new Line[0];

            Grasshopper.Kernel.Data.GH_Structure<GH_Mesh> tree = new Grasshopper.Kernel.Data.GH_Structure<GH_Mesh>();
            DA.GetDataTree(0, out tree);

            foreach (GH_Mesh m in tree) {
                mesh.Append(m.Value);
            }

            bbox = mesh.GetBoundingBox(false);
            lines = mesh.GetAllNGonEdgesLines(mesh.GetAllNGonEdges(mesh.GetNGonsTopoBoundaries()));

            DA.SetDataTree(0, tree);

        }


        protected override System.Drawing.Bitmap Icon {
            get {
                return Properties.Resources.preview;
            }
        }

        public override Guid ComponentGuid {
            get { return new Guid("47c23c43-efa7-4d73-ac4b-2e729e76ecb7"); }
        }

    }
}
1 Like

Create a class that inherits IGH_PreviewObject and implements your visualization, and inherit this class in the components.
When I am going to create many components in the same project, I create a class that inherits GH_Component simply for the constructor to take 3 parameters (name, nickname and description) and thus inherit the category and subcategory in all my components. You could also do this with the display, as GH_Component inherits IGH_PreviewObject.

Would it be possible to get example to understand how this main class that will be inherited looks like?

Since I can already override prieview methods and clipping box.
I will probably need some additional method in that main class that passes meshes used in the solve instance to global parameters (meshes, lines) which will be input for preview override.

Not tested, but…

 public abstract class NGonBaseComponent : GH_Component
{
    public NGonBaseComponent(string Name, string Nick, string Desc) :base (Name, Nick, Desc,"Category", "Subcategory"){ }

    protected Mesh mesh;
    protected Line[] lines;
    protected BoundingBox bbox;
     
    public override bool IsPreviewCapable => mesh.IsValid;

    public override BoundingBox ClippingBox => bbox;
    
    public override void DrawViewportMeshes(IGH_PreviewArgs args)
    {
        if (this.Hidden || this.Locked) return;
        args.Display.DrawMeshShaded(mesh, new Rhino.Display.DisplayMaterial(System.Drawing.Color.White));
    }

    public override void DrawViewportWires(IGH_PreviewArgs args)
    {
        if (this.Hidden || this.Locked) return;
        args.Display.DrawLines(lines, System.Drawing.Color.Black);
    }

    public virtual void PreparePreview(Mesh msh) {
        mesh = msh;
        bbox = mesh.GetBoundingBox(false);
        lines = mesh.GetAllNGonEdgesLines(mesh.GetAllNGonEdges(mesh.GetNGonsTopoBoundaries()));
    }
}

and in solveInstance of components that inherit this you call PreparePreview().

Thanks I will try to make this structure.

And for the rest of components I should do inheritance instead of this?

public class NGon : GH_Component

to this:

public class NGon : NGonBaseComponent

Yes :slight_smile:

Works like a charm.

I have 2 other question regarding this preview thing.

Do I need to hide all outputs in the base class like this, or they will be automatically overriden and not displayed?

    protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) {

        for(int i = 0; i < pManager.ParamCount; i++)
            pManager.HideParameter(0);
    }

    protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) {
        for (int i = 0; i < pManager.ParamCount; i++)
            pManager.HideParameter(0);
    }

When I have output as item in the component, the component displays only first or last item.
Does it mean that for this kind of preview the only option is gather all data as datatree instead of items?

  1. I’m not sure, I guess you have to hide them.

  2. Try with this:

     public void PreparePreview(Mesh msh, IGH_DataAccess DA) {
             if (DA.Iteration == 0)
                 mesh = msh;
             else 
                 mesh.Append(msh);
             bbox = mesh.GetBoundingBox(false);
             lines = mesh.GetAllNGonEdgesLines(mesh.GetAllNGonEdges(mesh.GetNGonsTopoBoundaries()));
         }

Nice trick thank you alot:)