

The following code might be further optimized by vectorization or intercepting GH’s SetDataList
procedure.
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Rhino.Geometry;
using System.Reflection;
using System.Reflection.Emit;
namespace PancakeAlgo.Misc
{
public class DenseGridOptimized : GH_Component
{
/// <summary>
/// Initializes a new instance of the DenseGridBaseline class.
/// </summary>
public DenseGridOptimized()
: base("DenseGridOptimized", "DenseGridOptimized",
"Description",
"Pancake", "Algo")
{
}
/// <summary>
/// Registers all the input parameters for this component.
/// </summary>
protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
{
double defaultDim = 5;
Box box = new Box(Plane.WorldXY, new Interval(-defaultDim, defaultDim), new Interval(-defaultDim, defaultDim), new Interval(-defaultDim, defaultDim));
pManager.AddPointParameter("Distance Object", "D", "Distance Object", GH_ParamAccess.item);
pManager.AddBoxParameter("Box", "B", "Bounding Box", GH_ParamAccess.item, box);
pManager.AddIntegerParameter("Number X", "NX", "Number of points in X", GH_ParamAccess.item, 50);
pManager.AddIntegerParameter("Number Y", "NY", "Number of points in Y", GH_ParamAccess.item, 50);
pManager.AddIntegerParameter("Number Z", "NZ", "Number of points in Z", GH_ParamAccess.item, 50);
}
/// <summary>
/// Registers all the output parameters for this component.
/// </summary>
protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
{
pManager.AddNumberParameter("Values", "V", "Distance values", GH_ParamAccess.list);
pManager.AddPointParameter("Points", "P", "Query points for which the distance was evaluated", GH_ParamAccess.list);
}
/// <summary>
/// This is the method that actually does the work.
/// </summary>
/// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param>
protected override void SolveInstance(IGH_DataAccess DA)
{
Point3d go = new Point3d(0, 0, 0);
int nX = 10;
int nY = 10;
int nZ = 10;
Box bx = new Box();
DA.GetData(0, ref go);
DA.GetData(1, ref bx);
if (!DA.GetData(2, ref nX)) return;
if (!DA.GetData(3, ref nY)) return;
if (!DA.GetData(4, ref nZ)) return;
BoundingBox box = bx.BoundingBox;
double x1 = box.Min.X;
double y1 = box.Min.Y;
double z1 = box.Min.Z;
double x2 = box.Max.X;
double y2 = box.Max.Y;
double z2 = box.Max.Z;
double dimX = (x2 - x1) / (nX - 1);
double dimY = (y2 - y1) / (nY - 1);
double dimZ = (z2 - z1) / (nZ - 1);
var _values = new List<GH_Number>(nX * nY * nZ);
var _points = new List<GH_Point>(nX * nY * nZ);
_values.SetListLength(nX * nY * nZ);
_points.SetListLength(nX * nY * nZ);
var values = _values.GetInternalArray();
var points = _points.GetInternalArray();
Parallel.For(0, nX, x =>
{
var position = x * nY * nZ;
double cx = x * dimX + x1;
double cy = y1;
var sqX = (cx - go.X) * (cx - go.X);
for (int y = 0; y < nY; y++)
{
double cz = z1;
var sqY = (cy - go.Y) * (cy - go.Y) + sqX;
for (int z = 0; z < nZ; z++)
{
var sqZ = (cz - go.Z) * (cz - go.Z) + sqY;
var dist = Math.Sqrt(sqZ);
Point3d newPt = new Point3d(cx, cy, cz);
values[position] = new GH_Number(dist);
points[position] = new GH_Point(newPt);
++position;
cz += dimZ;
}
cy += dimY;
}
});
DA.SetDataList(0, _values);
DA.SetDataList(1, _points);
}
/// <summary>
/// Provides an Icon for the component.
/// </summary>
protected override System.Drawing.Bitmap Icon => null;
/// <summary>
/// Gets the unique ID for this component. Do not change this ID after release.
/// </summary>
public override Guid ComponentGuid => new Guid("6317B759-68A1-4395-955D-931D8C7A95E6");
}
/// <summary>
/// some code is from https://stackoverflow.com/questions/4972951/listt-to-t-without-copying
/// CC-BY-SA 3.0
/// </summary>
public static class ListExtensions
{
internal static class ArrayAccessor<T>
{
internal delegate void SetListLength(List<T> list, int size);
internal static Func<List<T>, T[]> Getter;
internal static SetListLength CountSetter;
private static void Method1()
{
var dm = new DynamicMethod("get",
MethodAttributes.Static | MethodAttributes.Public,
CallingConventions.Standard,
typeof(T[]),
new Type[] { typeof(List<T>) },
typeof(ArrayAccessor<T>),
true);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // Load List<T> argument
il.Emit(OpCodes.Ldfld, typeof(List<T>).GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance)); // Replace argument by field
il.Emit(OpCodes.Ret); // Return field
Getter = (Func<List<T>, T[]>)dm.CreateDelegate(typeof(Func<List<T>, T[]>));
}
private static void Method2()
{
var dm = new DynamicMethod("set",
MethodAttributes.Static | MethodAttributes.Public,
CallingConventions.Standard,
null,
new Type[] { typeof(List<T>), typeof(int) },
typeof(ArrayAccessor<T>),
true);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, typeof(List<T>).GetField("_size", BindingFlags.NonPublic | BindingFlags.Instance)); // Replace argument by field
il.Emit(OpCodes.Ret); // Return field
CountSetter = (SetListLength)dm.CreateDelegate(typeof(SetListLength));
}
static ArrayAccessor()
{
Method1();
Method2();
}
}
public static T[] GetInternalArray<T>(this List<T> list)
{
return ArrayAccessor<T>.Getter(list);
}
public static void SetListLength<T>(this List<T> list, int newLength)
{
ArrayAccessor<T>.CountSetter(list, newLength);
}
}
}