Grasshopper

algorithmic modeling for Rhino

Let's say, (-"-)
in c++:
static ON_3dPoint* MakeSomePoints(ON_Line* line, int* num)
{
*num = total point number
ON_3dPoint* result = new ON_3dPoint[*num]();
get *num points on the line;
}
static void DisposeMyPoints(ON_3dPoint* ptrObject[])
{
if(ptrObject!=NULL)
{
delete [] ptrObject;
ptrObject =NULL;
}
}

Views: 1394

Replies to This Discussion

Two recommendations:

1. use the non-legacy scripting components, so use RhinoCommon types - this means using Point3d instead of ON_3dPoint, and Line instead of ON_Line. 

2. Use the built-in System.Collections types. Rather than going through all the mess above, simply declare  a List<Point3d> and be done with it. 

 

For example:

List<Point3d> MakeSomePoints(Line L, int num){
List<Point3d> results = new List<Point3d>();
for(int i = 0;i <= num;i++){
results.Add(L.PointAt(i / (double) num));
}
return results;
}

I'm trying to marshal some points which are generated by MarchingCube from c++ to c#
//PInvoke in c#:
//static ON_3dPoint* ComputeMeshPoints(int resX, int resY, int resZ, double data[], double iso, ON_3dPoint* minCorner, ON_3dPoint* maxCorner, int* outNum);
[DllImport("SomniumGrasshopper_C.dll", EntryPoint = "MarchingCubeComputeMeshPoints", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr MarchingCubeComputeMeshPoints(int resX, int resY, int resZ, IntPtr data, double iso, ref Point3d minCorner, ref Point3d maxCorner, ref int outNum);
_______________
//Here's some of the code in the component:

double[] dataArray = data.ToArray();
fixed (double* dataPtr = dataArray)
{
Point3d minCorner = box.BoundingBox.Min;
Point3d maxCorner = box.BoundingBox.Max;
int num=0;
IntPtr retrievePtr = SomniumSolver.MarchingCubeComputeMeshPoints(resX, resY, resZ, (IntPtr)dataPtr, isoValue, ref minCorner, ref maxCorner, ref num);
IntPtr cleanUpPtr = retrievePtr;
Mesh mesh = new Mesh();
for (int i = 0; i < num; i++)
{
mesh.Vertices.Add((Point3d)Marshal.PtrToStructure(retrievePtr,typeof(Point3d)));
retrievePtr = (IntPtr)((int)retrievePtr + 0x18);
}

SomniumSolver.MarchingCubeDisposePoints(cleanUpPtr);

for(int i =0;i< mesh.Vertices.Count;i+=3)
{
mesh.Faces.AddFace(i, i + 1, i + 2);
}
if (flag)
{
mesh.Vertices.CombineIdentical(true, true);
mesh.FaceNormals.ComputeFaceNormals();
mesh.Normals.ComputeNormals();
}
DA.SetData(0, mesh);
DA.SetData(1, num);
}
____________________
It involves a lot of copying data into functions so that it's slower than doing all in C#.

Where is this C++ code running from?

--

David Rutten

david@mcneel.com

Poprad, Slovakia

ON_3dPoint* MarchingCubeComputeMeshPoints(int resX, int resY, int resZ, double data[], double iso, ON_3dPoint* minCorner,ON_3dPoint*maxCorner, int* outNum)

{

int resX_1 = resX+1, resY_1 = resY+1, resZ_1 = resZ+1;

/*if((resX_1)*(resY_1)*(resZ_1) != sizeof(data) / sizeof(data[0]))

{

*outNum = 0;

return NULL;

}*/

int sliceXY = (resX_1)*(resY_1);

double intervalX = 1.0/resX;

double intervalY = 1.0/resY;

double intervalZ = 1.0/resZ;

ON_BoundingBox box = ON_BoundingBox(*minCorner,*maxCorner);

ON_Box refBox = ON_Box(box);

//Get corners of a single voxel

ON_3dPoint corners[8];

corners[0] = refBox.PointAt(0.0,0.0,0.0);

corners[1] = refBox.PointAt(intervalX,0.0,0.0);

corners[2] = refBox.PointAt(intervalX,intervalY,0.0);

corners[3] = refBox.PointAt(0.0,intervalY,0.0);

corners[4] = refBox.PointAt(0.0,0.0,intervalZ);

corners[5] = refBox.PointAt(intervalX,0.0,intervalZ);

corners[6] = refBox.PointAt(intervalX,intervalY,intervalZ);

corners[7] = refBox.PointAt(0.0,intervalY,intervalZ);

double d[8];

ON_3dPoint intersectionPoints[16];

int minCornerIndex= 0;

vector<ON_3dPoint> pts;

for (int z = 0; z < resZ; z++)

    {

        for (int y = 0; y < resY; y++)

        {

            for (int x = 0; x < resX; x++)

            {

minCornerIndex = z * sliceXY + y * resX_1 + x;

                d[0] = data[minCornerIndex];

                d[1] = data[minCornerIndex + 1];

                d[2] = data[minCornerIndex + 1 + resX_1];

                d[3] = data[minCornerIndex + resX_1];

                d[4] = data[minCornerIndex + sliceXY];

                d[5] = data[minCornerIndex + 1 + sliceXY];

                d[6] = data[minCornerIndex + 1 + resX_1 + sliceXY];

                d[7] = data[minCornerIndex + resX_1 + sliceXY];

ON_3dVector translate = refBox.PointAt(x * intervalX, y * intervalY, z * intervalZ) - corners[0];

int num = VoxelBox::GetFaces(corners, d, iso, intersectionPoints);

if (num > 0)

                {

                    for (int i = 0; i < num; i += 3)

                    {

                        pts.push_back(ON_3dPoint(intersectionPoints[i].x + translate.x, intersectionPoints[i].y + translate.y, intersectionPoints[i].z + translate.z));

                        pts.push_back(ON_3dPoint(intersectionPoints[i + 1].x + translate.x, intersectionPoints[i + 1].y + translate.y, intersectionPoints[i + 1].z + translate.z));

                        pts.push_back(ON_3dPoint(intersectionPoints[i + 2].x + translate.x, intersectionPoints[i + 2].y + translate.y, intersectionPoints[i + 2].z + translate.z));

                        *outNum += 3;

                    }

                }

}

}

}

ON_3dPoint* result = new ON_3dPoint[pts.size()]();

for(unsigned int i =0;i<pts.size();i++)

{

result[i].Set(pts[i].x,pts[i].y,pts[i].z);

}

return result;

}

I think it's slow in tow places.

1. using vector<> to hold all the points first, then copy them to the array. 

2. in c#, List<double> .ToArray

The first time I tested the code in Grasshopper, a warning "can''t find entry point blahblah..." came up. Then I did some google, used extern "C"__declspec(dllexport) and a .DEF file. If more code is added in, updating the .DEF file manually will be annoying. 

How is RhinoCommon manage all the exported functions?

 

RhinoCommon consists of two parts, rhcommon_c.dll which is a pure C++ library that exposes Rhino SDK features as easily PInvokeable functions and RhinoCommon.dll which is a pure C# library which invokes these methods.

We have a small console application which runs through the rhcommon_c source code and collects all invokable methods, then it creates a giant C# class which exposes all of these in a way that .NET can call.

There are also a bunch of special classes in the Rhino.Runtime.InteropWrappers namespace that help us ferry data from C++ to C# and vice versa.

Things like ON_3dPoint etc. are somewhat special but basically we make sure that the memory layout of Point3d is the same as ON_3dPoint so we can pass a pointer.

Maybe it would be easier if you created arrays of doubles rather than ON_3dPoint? {x0,y0,z0,x1,y1,z1,x2,y2,z2,...}  Then you can just iterate in steps of 3 and build Point3d structs from the coordinates.

--

David Rutten

david@mcneel.com

Poprad, Slovakia

Thanks, David. Marshalling double instead of Point3d struct can close the gap a little bit. //double[] dataArray = data.ToArray(); double[] dataArray = (double[])typeof(List) .GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance) .GetValue(data); I googled a way to convert List to array without copying, but there is no significant improvement. Reflection is not a good approach.

Hi Ian,

Here's some sample code that may clarify how I would go about performing the pInvoke.

https://gist.github.com/3970130

Let me know if you have any questions.

Thanks,

-Steve

Thanks a lot.
Rhino.Runtime.InteropWrappers namespace is very handy!

RSS

About

Translate

Search

Videos

  • Add Videos
  • View All

© 2024   Created by Scott Davidson.   Powered by

Badges  |  Report an Issue  |  Terms of Service