# 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 - thanks a lot!

``````using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

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>
{
}

/// <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>
{
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++)
{
}

TrajectoryPtCloud cloud = new TrajectoryPtCloud(Value.markerName, pts, Value.frames);
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);
}

TrajectoryPtCloud cloud = new TrajectoryPtCloud(Value.markerName, pts, Value.frames);
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);
}

TrajectoryPtCloud cloud = new TrajectoryPtCloud(Value.markerName, pts, Value.frames);
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)
{"ft_lang_label":"__label__en","ft_lang_prob":0.50150853,"math_prob":0.54364127,"size":8198,"snap":"2020-45-2020-50","text_gpt3_token_len":1986,"char_repetition_ratio":0.16219185,"word_repetition_ratio":0.12990654,"special_character_ratio":0.25311053,"punctuation_ratio":0.19213313,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.9567548,"pos_list":[0,1,2,3,4],"im_url_duplicate_count":[null,null,null,null,null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2020-11-23T22:59:40Z\",\"WARC-Record-ID\":\"<urn:uuid:a12d9e28-40a9-4bcf-8198-6c89c31d0e3c>\",\"Content-Length\":\"23795\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:566be7f7-1dd9-4736-bcf4-6611d16f2aaa>\",\"WARC-Concurrent-To\":\"<urn:uuid:7a890dca-411f-4950-9d58-601a3a4f7065>\",\"WARC-IP-Address\":\"64.62.250.111\",\"WARC-Target-URI\":\"https://discourse.mcneel.com/t/implementing-new-custom-parameter-with-custom-geometry-preview/111614\",\"WARC-Payload-Digest\":\"sha1:FC6RBMSXKH2S6FUZWBCZHLWTJMQIOZDL\",\"WARC-Block-Digest\":\"sha1:R4FLVWEOFV5NLPCOMOXHK3QJ3DAIOVME\",\"WARC-Identified-Payload-Type\":\"text/html\",\"warc_filename\":\"/cc_download/warc_2020/CC-MAIN-2020-50/CC-MAIN-2020-50_segments_1606141168074.3_warc_CC-MAIN-20201123211528-20201124001528-00466.warc.gz\"}"}