Can patch mesh be created with Rhino Common?

Currently I use:

rs.Command(‘NoEcho -MeshPatch EnterEnd’)

to create a patch mesh from selected points. Is there a way to do this using Rhino Common calls that do not require that I add visible points to the document, that is , that use only the geometry of the points to create the patch mesh?

Regards,
Terry.

I tried using the Rhino call:

patch_mesh = Mesh()
patch_mesh.CreatePatch(None,0.01,None,None,None,pts,False,0)

where pts is a list of Point3D objects, but this fails with the message:

Message: Specified cast is not valid.

The same list of pts (when added to the document) was successfully used to create a patch mesh with:

rs.Command('NoEcho -MeshPatch EnterEnd')

The arguments for CreatePatch are:

Mesh.CreatePatch( Polyline outerBoundary, double angleToleranceRadians, Surface pullbackSurface, IEnumerable<Curve> innerBoundaryCurves, IEnumerable<Curve> innerBothSideCurves, IEnumerable<Point3d> innerPoints, bool trimback, int divisions )

but I am a little lost as to how to specify the ones I am not using. I have read the documentation at:
http://developer.rhino3d.com/api/RhinoCommon/html/M_Rhino_Geometry_Mesh_CreatePatch.htm
but I am not understanding its details.

Any help, especially a working example, will be very much appreciated.

Regards, Terry.

Hi Terry,

This seems to work here:

import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino

pts=[your list of 3dpoints]
tol=sc.doc.ModelAngleToleranceRadians
patch=Rhino.Geometry.Mesh.CreatePatch(None,tol,None,None,None,pts,True,32)
sc.doc.Objects.AddMesh(patch)
sc.doc.Views.Redraw()

I just don’t know what integer to set the last value to, how or if it affects the mesh patch creation. I just threw a random one in there, didn’t seem to change anything no matter what I set it at…

Edit - important - don’t use 0 as the last value!!! Instant Rhino crash!
https://mcneel.myjetbrains.com/youtrack/issue/RH-45528

Edit #2 - OK, a bit more testing reveals that the integer does affect patch creation, setting it to 1 seems to create a “convex hull” type of effect at the outer boundary, 2, 3 or 4 some different outer border configs, above 4 seems not to change any more - but this was just with a small group of points… Would be interesting to know exactly what this does…

–Mitch

The last number is divisions:

http://developer.rhino3d.com/api/RhinoCommon/html/M_Rhino_Geometry_Mesh_CreatePatch.htm

Yeah, I got that, but no explanations of exactly how changing that value that affects the patch when it is composed of points and no outer border…

–Mitch

Ah, @dale is this something you can tell more about?

@Helvetosaur, @nathanletwory,

I could not get your example to work right off the bat but the error message led me to a fix: I need to use a .NET compatible IEnumerable list to talk to CreatePatch and not just a standard Python list.

	# I had to add this to my imports:
	from System.Collections.Generic import List
	#
	# Then this worked:
	#
	# Create pts list in .NET compatible IEnumerable format.
	pts = List[Point3d]()
	# Add Point3d's to pts list.
	for ptg in Xvertices[i][j]: pts.Add(ptg)
	# Create boundary polyline from boundary curve entered by user.
	boundary = Polyline(boundary_geo)
	tol=doc.ModelAngleToleranceRadians
	# Create patch mesh that stays inside boundary polyline using pts list in .NET IEnumerable format.
	patchGeo = Mesh.CreatePatch(boundary,tol,None,None,None,pts,False,10)
	patch_mesh = doc.Objects.AddMesh(patchGeo)

The impact of moving from using rs.Command(‘NoEcho -MeshPatch EnterEnd’) to Mesh.CreatePatch on my Python script is profound, dropping the time from 35 sec to only 16 sec. This is because (1) I no longer have to remove mesh faces outside the boundary curve. MeshPatch will not allow concave sections in the boundary curve, it fills them with very long faces while CreatePatch does not let any faces be created outside the boundary curve. And (2) I got to eliminate the creation of 600,000 visible points in the document needed to drive MeshPatch.

Joy, joy, joy. So happy.

Regards,
Terry.

@Helvetosaur, @nathanletwory,

The good news keeps coming. When I finished up the code to use CreatePatch to make the base of the patch_mesh and then joined the two meshes, the resulting mesh is now closed. I could never get this to work with MeshPatch meshes. So now I am getting what seem to be accurate estimates of the volume enclosed by the two meshes.

Thank you so much for helping me to get CreatePatch working.

Regards,
Terry.

Just in case you didn’t know MeshPatch has a “bug” where it doesn’t remove duplicate points, and that makes the patch take a whole lot longer to calculate. So adding a way to remove duplicate points should be done.

@dale could you add a remove duplicates to meshpatch?

@Holo, @Helvetosaur, @nathanletwory,

Thanks for letting me know. I am no longer using MeshPatch but does this also impact CreatePatch?

I checked my points list using rs.CullDuplicates and found no duplicates. I did find that CullDuplicates runs very slowly, taking as much time as my script for a 24,878 vertice patch mesh. So using it to save time in creating a mesh with MeshPatch may not be the way to go.

When I tried CullDuplicates on my large test case with 660,000 vertices, it took 2451 sec. After moving to CreatePatch, this test case runs in only 16 sec so I am not inclined to use CullDuplicates. Maybe there is a more efficient function? CullDuplicates seems to run in n^2 time. Maybe this can be reduced to n x log n time with a better algorithm. I had the same run time problem with my script. When I first started out, it took over an hour to do what it now does in 16 sec. Most of the time reduction came from eliminating 95% of the calls to:

curve_geo.Contains(pt, XYplane, 0.01) == PointContainment.Inside

which runs very slowing for a mesh with 2.2 million points. I fixed this by pre-binning all the mesh vertices into 5’ x 5’ bins for the 340’ x 400’ area of the 3D mesh model. This took 4 sec but now its very quick to look for mesh points inside the boundary curve by just inspecting bins that overlap the xmin,ymin by xmax,ymax limits of the boundary curve. Bins that fall completely inside the boundary curve are not inspected at all; all their points are just swept into my list of points inside. In the old days this was not popular as it uses many MB of data for the bins. But today, with computers full of GB of data, it is a cheap way to go fast. If I remember correctly, the combination of these improvements sped up the script about 50X (3600 sec down to 72 sec). The rest of the improvements came from using more Rhino calls vs rs. calls, using CreatePatch instead of MeshPatch and, so far in one case, tasks.Parallel.ForEach.

My next big challenge is getting tasks.Parallel.ForEach to live up to its potential for a 5X to 10X speed improvement on my 18 core CPU. I have many functions that can be executed in parallel with no interaction. I then collect the sub-lists from the outputs and combine them at the end. One example is gradient coloring of my mesh where it takes a couple dozen simple calculations to compute the color for each of the 2.2 million vertices in the 3D mesh model. The calculation for each vertice can be independent of all the others so I divide the points into 36 sequential groups which gives each thread 61,000 colors to calculate before it returns its list of 61,000 colors. But right now, the code runs more slowly using tasks.Parallel.ForEach no matter how many threads are used. Using 1 thread gives the fastest result and it just gradually slows down as more threads are added. Obviously nothing is being computed in parallel. So far I have not gotten help that improves this result.

Regards,
Terry.

1 Like

The speed is strange because I just made a test and wrote a simple script to extract every fourth vertice from a 8M vertice mesh.
And running SelDup on these 2.132.975 points took just five seconds. (it found 38K dups) (Rhino6)

The rest of the script is quite slow since Rhino thinks a lot when there are so many objects and just selecting the mesh takes almost a second.

But figuring out which of the points that are similar to all those 2M other points are very fast.

(Just run ExtractPt on a partyally unwelded mesh and you will get lots of points where some are duplicates. Then run SelDup to see how long it takes)

How do I run SelDup inside my Python script? For CullDuplicates I use:

tolerance = Rhino.RhinoMath.ZeroTolerance
dups = list(Point3d.CullDuplicates(pts, tolerance))

Regards,
Terry.

I might use a somewhat larger tolerance, depending on how close one point can be to another and be considered the same - I guess that will be scale-dependent…

Hi @Helvetosaur,

Thanks for catching this. I’ve have made a fix for SR5.

– Dale

RH-84670 is fixed in Rhino 8 Service Release 14 Release Candidate