Implementing new custom parameter with custom geometry preview

Hey all,
I am trying to implement a custom datatype and parameter in the context of some plug-in development (C#).
Main problem is to handle the preview of geometry for the parameter. I have custom class holding some points and some colors as geometric objects. Further i implemented a Data_class driven from GH_GeometricGoo and implemented IGH_PreviewData interface. So far working quite well - when put my custom type inside a Geo-Param i get the correct geometry preview. The i implemented a Param class driven from GH_Param<Data_class> and tried to implement IGH_PreviewObject and i can make it work so far that i don’t get amy compiler errors but i can’t get why my parameter isn’t able to create a preview and didn’t even seem to be capable of previewing data (No hide-option in the component-menu of the parameter).

Here is the code of my classes. I hope anybody can help out :slight_smile: - thanks a lot!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Rhino.Geometry;
using System.Drawing;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;

namespace MCSculpting
{
    /// <summary>
    /// TrajectoryPtCloud - Object representing the trajectory of a marker as pointcloud.
    /// </summary>
    public class TrajectoryPtCloud
    {
        /// <summary>
        /// Variable storing the name of the trajoctory/ marker.
        /// </summary>
        public string markerName { get; private set; }
        /// <summary>
        /// List storing all points describing the trajectory.
        /// </summary>
        public List<Point3d> points { get; private set; }
        /// <summary>
        /// A list containg all frame-numbers of the captured data.
        /// </summary>
        public List<int> frames { get; private set; }
        /// <summary>
        /// An optional list of colors assigned to the pointcloud. 
        /// </summary>
        public List<Color> colors { get; private set; }

        #region Constructors
        /// <summary>
        /// Empty Constructor.
        /// </summary>
        public TrajectoryPtCloud() 
        {
            this.markerName = "unknown";
            this.points = new List<Point3d>();
            this.frames = new List<int>();
            this.colors = new List<Color>();
        }

        /// <summary>
        /// Constructor from marker name and lsit of points.
        /// </summary>
        /// <param name="marker"> The name of the marker or label of the trajectory.</param>
        /// <param name="pts">A list of points describing the trajectory - order matters and is respected.</param>
        public TrajectoryPtCloud( string marker, List<Point3d> pts, List<int> frames)
        {
            if(pts.Count == frames.Count)
            {
                this.markerName = marker;
                this.points = pts;
                this.frames = frames;
                this.colors = new List<Color>();
            }
            else
            {
                throw new IndexOutOfRangeException("The number of frames dosen't match the number of points");
            }
            
        }
        #endregion

        /// <summary>
        /// Add a point at the end of the current pointlist describing thr trajectory.
        /// </summary>
        /// <param name="pt"> The point to add at the end of the trajectory.</param>
        public void addPoint(Point3d pt)
        {
            this.points.Add(pt);
        }

        /// <summary>
        /// Assigning a list of colors to the trajectory in order to map results on the pointcloud.
        /// </summary>
        /// <param name="colors">List of colors to assign. Must match the number of points in the pointcloud.</param>
        public void addColors(List<Color> colors)
        {
            if(this.points.Count == colors.Count) 
            {
                this.colors = colors;
            }
            else
            {
                throw new IndexOutOfRangeException("The number of colors dosen't match the number of points");
            }
        }

    }

    /// <summary>
    /// Implementation of GH_Data und GH_Param in order to use MCData as custom datatype in grasshopper.
    /// </summary>
    #region Implement as GH_Parameter
    public class Data_TrajectoryPtCloud : GH_GeometricGoo<TrajectoryPtCloud>, IGH_PreviewData
    {
        #region Constructors
        public Data_TrajectoryPtCloud() : base() { }
        public Data_TrajectoryPtCloud(TrajectoryPtCloud cloud)
        {
            this.Value = cloud;
        }

        public override Grasshopper.Kernel.Types.IGH_Goo Duplicate()
        {
            //TODO: implement proper duplication...
            //ProcessStep copy = null; //Value.DuplicateTopology();
            //return new ProcessStep(copy);
            return null;
        }
        #endregion

        #region IGH_Goo Members
        public override bool IsValid
        {
            //TODO: implement proper validation...
            get { return true; }
        }
        public override string TypeName
        {
            get { return "Trajectory Pointcloud"; }
        }
        public override string TypeDescription
        {
            get { return "Holds the trajectory of a given marker as pointcloud"; }
        }
        public override string ToString()
        {
            return "Trajectory: " + Value.markerName + "; [Points:" + Value.points.Count.ToString() + "]";
        }

        public override IGH_GeometricGoo DuplicateGeometry()
        {
            //ToDo: Implement proper Duplicate function
            return new Data_TrajectoryPtCloud();
        }

        public override BoundingBox Boundingbox
        {
            get
            {
                BoundingBox box = new BoundingBox(Value.points);
                return box;
            }
        }

        public override IGH_GeometricGoo Morph(SpaceMorph xmorph)
        {
            List<Point3d> pts = new List<Point3d>();
            for(var i = 0; i < Value.points.Count; i++)
            {
                pts.Add(xmorph.MorphPoint(Value.points[i]));
            }

            TrajectoryPtCloud cloud = new TrajectoryPtCloud(Value.markerName, pts, Value.frames);
            cloud.addColors(Value.colors);
            return new Data_TrajectoryPtCloud(cloud);
        }

        public override IGH_GeometricGoo Transform(Transform xform)
        {
            List<Point3d> pts = new List<Point3d>();
            for (var i = 0; i < Value.points.Count; i++)
            {
                Value.points[i].Transform(xform);
                pts.Add(Value.points[i]);
            }

            TrajectoryPtCloud cloud = new TrajectoryPtCloud(Value.markerName, pts, Value.frames);
            cloud.addColors(Value.colors);
            return new Data_TrajectoryPtCloud(cloud);
        }
        public override BoundingBox GetBoundingBox(Transform xform)
        {
            List<Point3d> pts = new List<Point3d>();
            for (var i = 0; i < Value.points.Count; i++)
            {
                Value.points[i].Transform(xform);
                pts.Add(Value.points[i]);
            }

            TrajectoryPtCloud cloud = new TrajectoryPtCloud(Value.markerName, pts, Value.frames);
            cloud.addColors(Value.colors);
            BoundingBox box = new BoundingBox(cloud.points);
            return box;
        }


        // This function is called when Grasshopper needs to convert other data into ProcessSteps.
        public override bool CastFrom(object source)
        {
            //Abort immediately on bogus data.
            if (source == null) { return false; }

            // subclasses need to be converted to baseclass
            if (source is TrajectoryPtCloud trajectoryPtCloud)
            {
                this.Value = trajectoryPtCloud;
                return true;
            }

            //We've exhausted the possible conversions, it seems that source cannot be converted into a ProcessStep after all.
            return false;
        }

        // This function is called when Grasshopper needs to convert this
        // instance of ProcessStep into some other type Q.
        public override bool CastTo<Q>(ref Q target)
        {
            //Q is String
            if (typeof(Q).IsAssignableFrom(typeof(TrajectoryPtCloud)))
            {
                object ptr = this.Value;
                target = (Q)ptr;
                return true;
            }

            /*

            //Then, see if Q is similar to the GH_Integer type.
            if (typeof(Q).IsAssignableFrom(typeof(GH_Integer)))
            {
                object ptr = new GH_Integer(this.Value);
                target = (Q)ptr;
                return true;
            }
            */

            //We could choose to also handle casts to Boolean, GH_Boolean,
            //Double and GH_Number, but this is left as an exercise for the reader.
            return false;
        }

        #endregion

        #region IGH_PreviewData
        public BoundingBox ClippingBox
        {
            get { return this.Boundingbox; }
        }

        public void DrawViewportWires(GH_PreviewWireArgs args)
        {
            for (var i = 0; i < Value.points.Count; i++)
            {

                if (Value.colors.Count != 0)
                {
                    args.Pipeline.DrawPoint(Value.points[i], Rhino.Display.PointStyle.Triangle,5, Value.colors[i]);
                }
                else
                {
                    args.Pipeline.DrawPoint(Value.points[i], Color.White);
                }

            }
        }
        public void DrawViewportMeshes(GH_PreviewMeshArgs args) { }
        #endregion
    }

    public class Param_TrajectoryPtCloud : GH_Param<Data_TrajectoryPtCloud>, IGH_PreviewObject
    {
        public Param_TrajectoryPtCloud()
          : base(new GH_InstanceDescription("trj", "Trajectory Pointcloud", "Holds the trajectory of a given marker as pointcloud.", "MCSculpting", "Params"))
        {
        }

        #region IGH_PreviewObject
        bool IGH_PreviewObject.Hidden { get; set; }

        bool IGH_PreviewObject.IsPreviewCapable { get; }

        BoundingBox IGH_PreviewObject.ClippingBox { get; }

        void IGH_PreviewObject.DrawViewportWires(IGH_PreviewArgs args){}

        void IGH_PreviewObject.DrawViewportMeshes(IGH_PreviewArgs args){}
        #endregion

        public override Guid ComponentGuid
        {
            get { return new Guid("a08e1c20-1e45-40e7-a323-e6f0a56a882e"); }
        }

        public override GH_Exposure Exposure
        {
            get
            {
                return GH_Exposure.primary;
            }
        }


    }
    #endregion
}

TrajectoryPtCloud.cs (9.9 KB)