Trying to figure out how to script MeshPatch in Python/RC

As per the title, trying to get a result similar to what MeshPatch does when you give it just points - it essentially just does a Delaunay mesh. The only function I found in RhinoCommon was Mesh.CreatePatch, but I cannot get any kind of results that make sense. But I’m probably using it wrong…

In the file below, there are some points and a boundary curve (which should not be needed), running the script below does not give me the results I want. Also included in the file are the results of MeshPatch and GH Delaunay.

I guess I can just import the GH component and use that (have to remember how) but shouldn’t there be a way to do this without GH?


ScriptedMeshPatch.3dm (3.1 MB)

import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino

"""
Mesh.CreatePatch input arguments
Polyline outerBoundary (or None)
double angleToleranceRadians
Surface pullbackSurface (or None)
IEnumerable<Curve> innerBoundaryCurves (or None)
IEnumerable<Curve> innerBothSideCurves (or None)
IEnumerable<Point3d> innerPoints
bool trimback (only used when outer boundary is not provided)
int divisions (only used when outer boundary is not provided)
"""

def TestMeshFromPts(pts,b_pl):    
    atol=sc.doc.ModelAngleToleranceRadians
    mesh=Rhino.Geometry.Mesh.CreatePatch(b_pl,atol,None,None,None,pts,True,2)
    return mesh

def TestFunction():
    pts=rs.GetObjects("Select points",1,preselect=True)
    if not pts: return
    
    b_crv_id=rs.GetObject("Select outer boundary curve or Enter for None",4)
    
    pts=rs.coerce3dpointlist(pts)
    if b_crv_id:
        b_crv=rs.coercecurve(b_crv_id)
        b_pl=b_crv.ToPolyline()
    else:
        b_pl=None
    mesh=TestMeshFromPts(pts,b_pl)
    if mesh:
        sc.doc.Objects.AddMesh(mesh)
        sc.doc.Views.Redraw()
TestFunction()

If I remember right you need to specify the pullbackSurface parameter, which you’ve currently set to None! You should be successfully with simply passing a boundary surface of the the boundary polyline.

Nope, can’t get it to work correctly with a surface either, either with or without the outer perimeter in addition…

Just to be clear, what I am looking for is a 3D mesh that goes through the points in the file above, not a flat mesh that corresponds to the outline. Like what the Rhino command MeshPatch produces, or the GH Delaunay component does with the points as input.

Oh, in that case you probably need to implement a constrained Delauney triangulation algorithm.
Since, Delaunay triangulations usually work in 2D, you’d project the points to a 2D plane first, perform the constrained triangulation, and replace the vertices with the initial points to get back to 3D.
The triangulation needs to be constrained, because your points boundary is concave and Delaunay can only produce convex ones, like in your example above. You’d constrain it with your points boundary.

Here’s a C++ example:

Well, yes, I can simply use the GH Delaunay component in my script via Node in Code; worst case I could even script the Rhino command MeshPatch with rs.Command(). However I was just surprised to see that I couldn’t get something working via Mesh.CreatePatch() - or something else that maybe I missed - without having to resort to either of the two above ‘workarounds’.

As far as constraining the resulting Delaunay mesh to just faces within the convex boundary, I think can just get each face center from the mesh face list and eliminate the faces whose centers fall outside the boundary (projected to a plane of course).

In Rhino 7, on PC, the mesh patch seems to work by providing the points as a point3dList. This worked in a ghpy component, (innerPts set to “List Access”). UPDATING as inital post was incomplete.

1 Like

Doesn’t work for me here, it creates a flat mesh. I am not working with Python in GH, but rather Python directly in Rhino. Right now I am just using GH node in Code and the GH Delaunay component.

Try this, seems to work here. The meshpatch method is looking for a point3dlist.

import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino

"""
Mesh.CreatePatch input arguments
Polyline outerBoundary (or None)
double angleToleranceRadians
Surface pullbackSurface (or None)
IEnumerable<Curve> innerBoundaryCurves (or None)
IEnumerable<Curve> innerBothSideCurves (or None)
IEnumerable<Point3d> innerPoints
bool trimback (only used when outer boundary is not provided)
int divisions (only used when outer boundary is not provided)
"""

def TestMeshFromPts(pts,b_pl):    
    atol=sc.doc.ModelAngleToleranceRadians
    mesh=Rhino.Geometry.Mesh.CreatePatch(b_pl,atol,None,None,None,pts,True,2)
    return mesh

def TestFunction():
    pts=rs.GetObjects("Select points",1,preselect=True)
    ptcoerced = rs.coerce3dpointlist(pts)  #need to create an enumerable point3dlist
    PtList = Rhino.Collections.Point3dList(ptcoerced)  #this is what the the method is looking for
    if not pts: return
    
    b_crv_id=rs.GetObject("Select outer boundary curve or Enter for None",4)
    
    if b_crv_id:
        b_crv=rs.coercecurve(b_crv_id)
        b_pl=b_crv.ToPolyline()
    else:
        b_pl=None
    mesh=TestMeshFromPts(PtList,b_pl)
    if mesh:
        sc.doc.Objects.AddMesh(mesh)
        sc.doc.Views.Redraw()
TestFunction()
2 Likes

If you get a flat mesh but a vertex count that corresponds to your input points, you can always substitute the vertices for the initial points, and this should turn your two-dimensional mesh into a 3D one. This has some limitations but should work fine in your case.

Yes, however chances are that the Delaunay triangulation will draw some faces that don’t overlap the boundary with a mesh edge, but rather intersect it. In that case, this procedure would cull a face, partially relevant to the mesh.

Yep, but that does seem like a lot of extra work… Rhino’s native MeshPatch and the GH Delaunay algorithm have this integrated I guess. As I said, I would have expected to be able to do this with Mesh.CreatePatch() directly.

Good point, this is probably not a good strategy for the general case. In my particular case however, the point collections I’m working on are actually derived from original meshes who all have edges along the containment boundary. The boundary curve is actually derived from the mesh. Therefore for these particular objects, I think it will work OK. Once I get the rest of the stuff in the script working (long way to go yet) I will be testing more extensively. If there do end up being cases where the newly generated mesh triangles cross the containment boundary, I will need to find some other way/workaround.

1 Like