MeshFace.Set -- how does it work?

Greetings.
For example, if I need to change in a mesh quad face one vertex to another by swaping their indeces?
I’m trying to use this method MeshFace.Set Method (Int32, Int32, Int32, Int32)
Heres my code:

Mesh_Faces_list = My_Mesh_Ref.Faces
    
this_face = Mesh_Faces_list.GetFace(1)
print("Before:")
print(this_face)

print("Trying:")
this_face.Set(1, 2, 3, 4)
print(this_face)

print("After:")
Mesh_Faces_list = My_Mesh_Ref.Faces
this_face = Mesh_Faces_list.GetFace(1)
print(this_face)

But I get this result:

Before:
Q(5, 3, 2, 4)
Trying:
Q(1, 2, 3, 4)
After:
Q(5, 3, 2, 4)

Theres also Item method which seems may do the same

print("Trying:")
this_face.Item[1] = 11
print(this_face)

But the result is the same

Hi,

You’re currently copying the mesh face at index 1 into a variable this_face and changing it, which only changes the copy.

You can either set the relevant mesh face to the copy, after changing the copy:

Mesh_Faces_list = My_Mesh_Ref.Faces

print My_Mesh_Ref.Faces[1]

this_face = Mesh_Faces_list.GetFace(1)
this_face.Set(1, 2, 3, 4)

My_Mesh_Ref.Faces[1] = this_face

print My_Mesh_Ref.Faces[1]

Or you can change it directly for the mesh:

import Rhino

print My_Mesh_Ref.Faces[1]

My_Mesh_Ref.Faces[1] = Rhino.Geometry.MeshFace(1, 2, 3, 4)

print My_Mesh_Ref.Faces[1]

Thanks, it works… of course

diff-arch, just to make this topic quite clear to me, can you please explain such a behaviour:
If I place for example this MeshFase.Set modification function to the imported module,
and I pass mesh Guid to this module from the main script, so I get the mesh object inside this module like that:

My_Mesh_Ref = sc.doc.Objects.FindGeometry(self.My_Mesh_Guid)

So, when I check for this Mesh changes while calling inside module - they are done fine.
But when the same chek is done from the main script body, after calling module function of course,
theres no changes in Mesh geometry… why is that?

It’s probably the same issue as above. When you pass the mesh into your function, you need to pass it by reference, which can be tricky in Python. Otherwise, it gets copied when it’s passed in and everything you do is performed on the copy. Meanwhile, the original mesh remains the same.

Hi, this was really helpful. I tried setting individual corner indices (A , B , C D properties) one by one and it didn’t work. For whatever reason, all done in one worked fine.

Is there a way you should be able to set the properties one by one? Am I wrong to think this?

e.g.

meshfaces.Item[selected_faces[0]].A = 14
meshfaces.Item[selected_faces[0]].B = 3
meshfaces.Item[selected_faces[0]].C = 16
meshfaces.Item[selected_faces[0]].D = 2

Thanks!

That should work, but it’s hard to judge from your sample code what’s the issue.
Do you get a specific error?

Nothing specific no; I just used a breakpoint after setting and the changes on a property by property basis weren’t reflected in the debugger.

@dale, could it be that it’s not properly implemented for Python use?

No.

@Jonathan_Hutchinson - please provide a code sample - one that we can run here - that isn’t working for you.

– Dale

Hi Jonathan, you can try this:

this_face = MyMesh.Faces.GetFace(some_face_index)

this_face[0] = 435
this_face[1] = 223
this_face[2] = 999

MyMesh.Faces[some_face_index] = this_face

Hi Dale,

One thing I am wondering, is why I can’t pass a list of 4 integers to create a mesh face?

e.g.

face = [10,1,3,5]
Rhino.Geometry.MeshFace( face )

Message: CreateInstance() takes at most 4 arguments (1 given)

The script I’m working on can be found at Spin Edge for Rhino SubD Tools - #2 by theoutside , just conscious of not derailing an old thread.

Try:

Rhino.Geometry.MeshFace(10,1,3,5)

More:

https://developer.rhino3d.com/api/RhinoCommon/html/Overload_Rhino_Geometry_MeshFace__ctor.htm

– Dale

Is there not a way to do this programmatically? Or only with hard typed values? my current solution I just needed to hard type the item from the list

e.g.
rg.MeshFace(new_face_0[0],new_face_0[1],new_face_0[2], new_face_0[3])

Script below, lines 177 to 186. The items at my determined positions don’t get changed.


# SPIN EDGE V1

import Rhino
import Rhino.Geometry as rg
import rhinoscriptsyntax as rs
import scriptcontext as sc

def SpinEdge():
    
    go = Rhino.Input.Custom.GetObject()
    go.SetCommandPrompt("Select edge to orient")
    go.GeometryAttributeFilter = Rhino.Input.Custom.GeometryAttributeFilter.InnerLoop
    go.GeometryFilter = Rhino.DocObjects.ObjectType.EdgeFilter
    #go.GeometryFilter = Rhino.DocObjects.ObjectType.MeshEdge
    #go.GeometryAttributeFilter = Rhino.Input.Custom.GeometryAttributeFilter.EdgeCurve
    go.Get()
    objref = go.Object(0)
    subd = rs.coercegeometry(objref.ObjectId) #coerce guid
    geo = objref.Geometry()
    picked_edge_id = objref.GeometryComponentIndex.Index
    
    picked_subd_edge = subd.Edges.Find(picked_edge_id)
    if picked_subd_edge.FaceCount == 2:
        face1 = picked_subd_edge.FaceAt(0)
        face2 = picked_subd_edge.FaceAt(1)
    else:
        print "Cannot spin Naked Edge."
        return
    
    #cant handle triangles yet
    #also appears to be an issue when two edges are a straight line
    #this might be during subd creation or mesh?
    # Manually Loop through subd quad faces (assuming they're quads not tris)
    
    subd_verts = [picked_subd_edge.VertexFrom.Id, picked_subd_edge.VertexTo.Id]
    face1, face2 = subd.Faces.Find(face1.Id), subd.Faces.Find(face2.Id)
    
    mesh = rg.Mesh.CreateFromSubDControlNet(subd)
    meshfaces, meshverts = mesh.Faces, mesh.Vertices
    meshverts.UseDoublePrecisionVertices = True
    p3d = meshverts.ToPoint3dArray()
    
    picked_mesh_edge_indices = []
    face_verts = []
    
    # Get mesh vertices from subd vertices
    for index, i in enumerate(p3d):
        if i.DistanceTo(subd.Vertices.Find(subd_verts [0]).ControlNetPoint) == 0:
            picked_mesh_edge_indices.append(index)
    
    for index, i in enumerate(p3d):
        if i.DistanceTo(subd.Vertices.Find(subd_verts [1]).ControlNetPoint) == 0:
            picked_mesh_edge_indices.append(index)
    
    # Determine the adjacent Mesh face indices and hence Corner Indices
    all_faces =  [ meshverts.GetVertexFaces(i) for i in picked_mesh_edge_indices ]
    for i in all_faces:
        for vert in i:
            face_verts.append(vert)
    
    face_verts_set = set(face_verts)
    selected_faces = []
    for i in face_verts_set:
        if face_verts.count(i) > 1:
            selected_faces.append(i)
    
    vert0, vert1 = picked_mesh_edge_indices[0], picked_mesh_edge_indices[1]
    face1_corners_list = [ vert for vert in meshfaces.Item[selected_faces[0]] ]
    face2_corners_list = [ vert for vert in meshfaces.Item[selected_faces[1]] ]
    print "Adjacent Mesh Face Indices are", selected_faces
    print "Mesh Face 1 Corner Indices:", meshfaces.Item[selected_faces[0]]
    print "Mesh Face 2 Corner Indices:", meshfaces.Item[selected_faces[1]]
    print "Edge Indices:", vert0, ";", vert1
    
    #Worked through example:
    # [14,30,16,2] [18,30,14,3] 14/30:30/14 relation, rplace second/last occurence
    # becomes >>>
    # [14,3,16,2] [18,30,16,3]
    # if both lists order with 14,30 then 14,30; may be an issue with normals if adjacent (cw/ccw)
    #clockwise next vert after edge verts, from 30 on face 1 >> 16
    #clockwise next vert after edge verts, from 14 on face 2 >> 3
    #in each face, the last edge vert is replaced by the next cw vert
    #eg 30 would be replaced with 3; 14 would be replaced with 16
    # get list index of edge vert index 1, get list index of edge vert index 2; see which is bigger
    #in both lists, get the index of where each value is from those selected
    #store as dict eg 14:0 30:1, 30:1 14:2
    #if index position difference is greater than one, theyre at ends, use the smallest
    
    def FunctionifyTbd(picked_mesh_edge_indices, face1_corners_list):
        for i in picked_mesh_indices:
            if i in face1_corners_list:
                mydict1[i] = face1_corners_list.index(i)
        
        dictionary = {}
        
        if abs(dictionary.get(vert0) - dictionary.get(vert1)) >1:
            #if difference between dict values is > 1, use index of 0
            # i.e. [0*,1,2,3*] <<< difference = 3, Tris [0*,1,2*]
            face_index_to_edit = 0
        else:
            if dictionary.get(vert0) > dictionary.get(vert1):
                face_index_to_edit = dictionary.get(vert0)
            else:
                face_index_to_edit = dictionary.get(vert1)
        
        next_index = face_index_to_edit+1
        if next_index == 4:
            next_index = 0


    mydict1 = {}
    mydict2 = {}
    
    #generate index locations of the picked vertices
    #in respect to each faces makeup in terms of vertices
    for i in picked_mesh_edge_indices:
        if i in face1_corners_list:
            mydict1[i] = face1_corners_list.index(i)
        if i in face2_corners_list:
            mydict2[i] = face2_corners_list.index(i)
    if mydict1.get(vert0) == mydict1.get(vert0) | mydict2.get(vert0) == mydict2.get:
        print "Hmm, something's up"
    
    print "Dictionaries for Edge Vertex Location in Lists:", mydict1, mydict2
    
    # checking difference, if the edges are at list ends, if difference is >1
    if abs(mydict1.get(vert0) - mydict1.get(vert1)) >1:
        #if difference between dict values is > 1, use index of 0
        # i.e. [0*,1,2,3*] <<< difference = 3, Tris [0*,1,2*]
        edit_face0_index = 0
    else:
        if mydict1.get(vert0) > mydict1.get(vert1):
            edit_face0_index = mydict1.get(vert0)
        else:
            edit_face0_index = mydict1.get(vert1)
    
    if abs(mydict2.get(vert0) - mydict2.get(vert1)) >1:
        edit_face1_index = 0
    else:
        if mydict2.get(vert0) > mydict2.get(vert1):
            edit_face1_index = mydict2.get(vert0)
        else:
            edit_face1_index = mydict2.get(vert1)
    
    print "Indices to edit:", "face0:", edit_face0_index, "face1:", edit_face1_index
    
    next_index_0 = edit_face0_index+1 #need to not let it be 4, ie 3+1, if tris then 2+1
    next_index_1 = edit_face1_index+1
    if next_index_0 == 4:
        next_index_0 = 0
    if next_index_1 == 4:
        next_index_1 = 0
    
    """
    if next_index_0 == 3:
        next_index_0 = 0
    if next_index_1 == 3:
        next_index_1 = 0
    """
    
    new_face_0, new_face_1 = face1_corners_list, face2_corners_list
    list0_extracted_value = face1_corners_list[ next_index_0 ]
    list1_extracted_value = face2_corners_list[ next_index_1 ]
    
    #then swap
    new_face_0[edit_face0_index] = list1_extracted_value
    new_face_1[edit_face1_index] = list0_extracted_value
    
    print "face 1:", new_face_0, "face 2:", new_face_1
    
    # if the biggest dict value is 3, 0 index is the one to be swapped
    # else dict value +1 is what is taken and used in other list
    
    #meshfaces.Item[selected_faces[0]] = rg.MeshFace(new_face_0[0],new_face_0[1],new_face_0[2], new_face_0[3])
    #meshfaces.Item[selected_faces[1]] = rg.MeshFace(new_face_1[0],new_face_1[1],new_face_1[2], new_face_1[3])
    
    print "face0 index to edit:", edit_face0_index, "face1 index to edit:", edit_face1_index
    print "value in face0 to replace with:", list1_extracted_value, "value in face1 to replace with:", list0_extracted_value
    print meshfaces.Item[selected_faces[0]]
    print meshfaces.Item[selected_faces[1]]
    
    meshfaces.Item[selected_faces[0]].Item[edit_face0_index] = list1_extracted_value
    meshfaces.Item[selected_faces[1]].Item[edit_face1_index] = list0_extracted_value
    
    print meshfaces.Item[selected_faces[0]]
    print meshfaces.Item[selected_faces[1]]
    
    #Add SubD
    newsubd = rg.SubD.CreateFromMesh( mesh )
    sc.doc.Objects.Replace(objref.ObjectId, newsubd) #use guid to replace

if __name__ == "__main__":
    SpinEdge()

Hi @Jonathan_Hutchinson,

One think you are missing, I think, is that MeshFace is a .NET struct, not a .NET class. So when you do something like this:

face = mesh.Faces[index]

face is a copy of the mesh face at index, not a reference to the mesh face at index. Making changes to the copy doesn’t not effect the object it was copied from.

So to modify the face at index, you can do something like this:

# make a copy
face = mesh.Faces[index]
# update the copy
face.A = 14
face.B = 3
face.C = 16
face.D = 2
# replace the original with the copy
mesh.Faces[index] = face

or this:

# just make a new one
mesh.Faces[index] = Rhino.Geometry.MeshFace(14, 3, 16, 2)

Does this help?

– Dale