Hi all.
I need to populate a mesh with points in a c# script.
(just on the surface, not the volume)
There is a way to access the “Populate geometry” function from a script?
Thanks in advance
Riccardo
Hi all.
I need to populate a mesh with points in a c# script.
(just on the surface, not the volume)
There is a way to access the “Populate geometry” function from a script?
Thanks in advance
Riccardo
Sorry, that code is internal to the Vector GHa lib, but it relies mostly on classes available in Grasshopper itself.
Here’s the relevant method which slowly builds up a population over time:
Public Function Populate(count As Int32, existing As Point3dList) As Point3dList
If (existing Is Nothing) Then existing = New Point3dList()
Dim box As BoundingBox = Me.BoundingBox
box.Union(existing.BoundingBox)
Dim tree As New Node3d(Of Point3d)(AddressOf TreeDelegates.Point3dCoordinates, box, 150)
tree.AddRange(existing)
Dim population As New Point3dList()
Dim distanceThreshold As Double = Double.MaxValue
For i As Int32 = 1 To count
If (population.Count = 0) AndAlso (existing.Count = 0) Then
Dim pt As Point3d = NextPoint()
tree.Add(pt)
population.Add(pt)
Else
Dim attempts As Int32 = Math.Max(50, Convert.ToInt32(Math.Sqrt(i + existing.Count)))
Dim maxd As Double = Double.MinValue
Dim maxp As Point3d
For k As Int32 = 1 To attempts
If (k > 100) AndAlso (maxd > distanceThreshold * 0.9) Then
Exit For
ElseIf (k > 50) AndAlso (maxd > distanceThreshold * 0.92) Then
Exit For
ElseIf (k > 25) AndAlso (maxd > distanceThreshold * 0.95) Then
Exit For
End If
Dim pt As Point3d = NextPoint()
Dim index As Index3d(Of Point3d) = tree.NearestItem(pt, 0.0, Double.MaxValue)
Dim d As Double = pt.DistanceTo(index.Item)
If (d = Double.MaxValue) Then Exit For
If (d > maxd) Then
maxd = d
maxp = pt
If (maxd > distanceThreshold * 1.25) Then Exit For
End If
Next
distanceThreshold = maxd
tree.Add(maxp)
population.Add(maxp)
End If
Next
Return population
End Function
To populate a mesh you could
Make an array with the sum of faces area
S[I] = S[I-1] +area of face i
Then make random number between 0 and S[number if faces -1]
this random number will allow you to choose a face.
Then you place a random point on the face.
If needed I could make the script
Yes, that’s the code that goes into the NextPoint()
call in my code. The rest of the logic is to avoid you place two random points too close together.
Thank you guys for the tips.
But still I didn’t understood… both the code supplied by David and the formula from Laurent…
I’m working on a finite element method to simulate magnetic field and their interactions.
I was looking around for a way to uniformly populate a geometry as otherwise results are less precise.
I need a Volume populating method, not surface as I said in first post… but I was thinking of populate multiple shells done via offset…
Bad idea; now i’m thinking of some fast iteration of sphere collide after a raw populating.
I did a c# method to populate the surface of a mesh.
This is 10 times faster (or more) than “Populate Geometry” component in grasshopper, the result is less uniform in small portions, but more uniform in the overall geometry (a ready-er situation to start some loop with sphere collide).
(“Populate Geometry” seems, to me, to be influenced by vertices density in the geometry; I’ts good, indeed, way better than mine. Just… I needed a code version and this is a step to volume populating, so speed is better than local density uniformity for now).
The logic is to iterate through each face adding random points in proportion to its area… just that.
private void RunScript(Mesh M, int N, ref object A)
{
A = RawPopulate(M, N);
}
// <Custom additional code>
public List<Rhino.Geometry.Point3d> RawPopulate(Mesh M, int N){
Rhino.Geometry.Collections.MeshFaceList faces = M.Faces;
faces.ConvertQuadsToTriangles();
Rhino.Geometry.Collections.MeshVertexList vertices = M.Vertices;
// Calculating area for each face
// code by David Rutten from https://www.grasshopper3d.com/forum/topics/what-is-the-most-efficient-way-to-convert-mesh-faces-into-breps
List<double> areas = new List<double>(faces.Count);
for (int i = 0; i < faces.Count; i++){
Rhino.Geometry.MeshFace face = faces[i];
double area = 0;
if (face.IsTriangle){
Point3d p0 = vertices[face.A];
Point3d p1 = vertices[face.B];
Point3d p2 = vertices[face.C];
Vector3d v0 = p0 - p1;
Vector3d v1 = p2 - p1;
Vector3d cp = Vector3d.CrossProduct(v0, v1);
area = 0.5 * cp.Length;
}
else{
if(face.IsQuad){
Point3d p0 = vertices[face.A];
Point3d p1 = vertices[face.B];
Point3d p2 = vertices[face.C];
Point3d p3 = vertices[face.D];
Vector3d v0 = p0 - p1;
Vector3d v1 = p2 - p1;
Vector3d v2 = p0 - p3;
Vector3d v3 = p2 - p3;
Vector3d cp0 = Vector3d.CrossProduct(v0, v1);
Vector3d cp1 = Vector3d.CrossProduct(v2, v3);
area = 0.5 * cp0.Length + 0.5 * cp1.Length;
}
}
areas.Add(area);
}
Random rnd = new Random();
List<Rhino.Geometry.Point3d> pts = new List<Rhino.Geometry.Point3d>(N);
double totalarea = 0.999999999 * (Rhino.Geometry.AreaMassProperties.Compute(M, true, false, false, false)).Area;
double fraction = totalarea / N;
double resto = 0;
for(int i = 0;i < faces.Count;i++){
double nn = Math.Floor((areas[i] + resto) / fraction);
for(int j = 0;j < nn;j++){
double par0 = Math.Pow(rnd.NextDouble(), 1.5);
double par1 = Math.Pow(rnd.NextDouble(), 1.5);
double par2 = Math.Pow(rnd.NextDouble(), 1.5);
double tot = par0 + par1 + par2;
par0 = par0 / tot;
par1 = par1 / tot;
par2 = par2 / tot;
Rhino.Geometry.Point3d p = M.PointAt(i, par0, par1, par2, 0.0);
pts.Add(p);
}
resto = (areas[i] + resto) - nn * fraction;
}
return pts;
}
Thanks again.
I’ll keep this updated.
Hello
it is normal that David’s component is slower as it searches to place points not too near from each others. It is like a Poisson Sampling. So recursion is involved and distance calculations which is long with many points.
Your approach is very good. I made an example with 10100 points, with a mesh with a face with a surface of 10000 and the other 100. So one must have 10 000 points and the other 100. Your code gives the exact number, mine is fluctuating between the good number. David’s script gives too much points on the little surface surely because it pushes points on the edges. No approach is perfect !
Your method is not good for the placement of point on triangle.
You must use something like that
Point3d p0 = M.Vertices[M.Faces[lastIndexFace].A];
Point3d p1 = M.Vertices[M.Faces[lastIndexFace].B];
Point3d p2 = M.Vertices[M.Faces[lastIndexFace].C];
double r1 = rnd.NextDouble();
double r2 = rnd.NextDouble();
if ((r1 + r2) > 1)
{
r1 = 1.0 - r1;
r2 = 1.0 - r2;
}
pts.Add(p0 * (1 - r1 - r2) + p1 * r1 + p2 * r2);
See this :
http://mathworld.wolfram.com/TrianglePointPicking.html
Here your placement
RandomPopulateMesh_LD.gh (18.9 KB)
If you’re actually distributing points in a volume, here’s a way to do it with sphere packing:
populate_volume.gh (11.8 KB)
and another quick way to fill a volume with points, making sure none are closer than some distance apart is to use a random distribution to start with, then cull duplicates with a distance tolerance:
populate_volume2.gh (22.1 KB)
i made an class for populate geometry it just work for PolyLine and all of the Curves you can custome it for all of geometry easily
PolylinePopulate .cs (2.5 KB)
i hope it help to who client want to make an RhinoPlugin
keywords :
C# populate geometry
C# populate generic