0
$\begingroup$

I have this mesh:

enter image description here

I need to find all faces linked to the selected edges. How can I find that using BMesh? I know this way:

for e in edges:
    for f in e.link_faces:
        print("Link Faces:", f)
        normals.append(f.normal.copy())

but for some reason this isn't good for me. Is there any other? Thank you.

EDIT: I need to be more specific, so to highlight the problem. The object I need to check:

enter image description here

Now I need to know if the edges from a selection lies on the same plane. So I check for those edges face normals. The script:

me = ob.data

if me.is_editmode:
    # Gain direct access to the mesh
    bm = bmesh.from_edit_mesh(me)
else:
    # Create a bmesh from mesh
    # (won't affect mesh, unless explicitly written back)
    bm = bmesh.new()
    bm.from_mesh(me)

# get all selected edges
edges = [e for e in bm.edges if e.select]


# check if all connected face normals are
# on the same plane. the check is strict
faces=[]
normals=[]
for e in edges:
    for f in e.link_faces:
        faces.append(f)
        print("Link Faces:", f)
        print("Normal: ",f.normal)
        print("Normal copy: ",f.normal.copy())
        normals.append(f.normal.copy())

# check they're all the same vector
coplanarity = all(x==normals[0] for x in normals)

print("Coplanar: ", coplanarity)

First selection:

enter image description here

results:

Link Faces: <BMFace(0x11f892818), index=311, totverts=4>
Normal:  <Vector (0.0000, -0.0000, 1.0000)>
Normal copy:  <Vector (0.0000, -0.0000, 1.0000)>
Link Faces: <BMFace(0x11f8927e0), index=310, totverts=4>
Normal:  <Vector (0.0000, -0.0000, 1.0000)>
Normal copy:  <Vector (0.0000, -0.0000, 1.0000)>
Coplanar:  True

Second selection:

enter image description here

Results:

Link Faces: <BMFace(0x109d47b80), index=226, totverts=4>
Normal:  <Vector (0.0000, 0.0000, 0.0000)>
Normal copy:  <Vector (0.0000, 0.0000, 0.0000)>
Coplanar:  True

The two edges are selected to be on the same face; as you can see, the normals result being different while they're obviously the same. Looks like a non-manifold kind-of bug. So I need to take another route...

EDIT2:

No it's not a bug. I did more tests, there's st. I don't get here...

the blend

$\endgroup$
  • 3
    $\begingroup$ what do you mean this isn't good. Is that not working or is there some reason you do not want to use the .link_faces method? $\endgroup$ – patmo141 Apr 28 '18 at 17:35
  • $\begingroup$ calling it to do f.normal.copy yields always a zero vector. But that face has a normal; so it must be s.t. related to the way the data is accessed. Hence the search for a different path $\endgroup$ – Kabu Apr 28 '18 at 19:45
  • 1
    $\begingroup$ Bmesh is cross-referenced, no matter how you access a particular vert/edge/face it's going to point to the same instance in memory. Remember that when you modify data you might need to re-calculate normals before accessing them. $\endgroup$ – kheetor Apr 28 '18 at 20:36
  • $\begingroup$ I think you are on the correct path. I use link_faces for many many calculations. Also your image above looks like a wireframe $\endgroup$ – patmo141 Apr 28 '18 at 23:30
  • $\begingroup$ Got the question more clear to show what the problem really is. $\endgroup$ – Kabu Apr 29 '18 at 10:54
3
$\begingroup$

Checking if all selected geometry is coplanar.

Similarly to this answer I find using the convex hull bmesh operator, and calculate volume on result a quick and dirty (and somewhat bulletproof) way to calculate if all geometry lies on the same plane. Doesn't need to calculate face normals at a all.

Here is a test script to run in edit mode, that will report if all selected edges are coplanar.

import bpy
import bmesh
from mathutils import Vector

def coplanar(geom, TOL=0.0001):
    verts = []
    for g in geom:
        if hasattr(g, "verts"):
            verts.extend(v.co for v in g.verts)
        else:
            verts.append(g.co)
    if len(verts) < 3:
        return True
    o = sum(verts, Vector((0,0,0))) / len(verts)
    bm2 = bmesh.new()    
    for v in verts:
        bm2.verts.new(v - o)    
    bmesh.ops.convex_hull(bm2, input=bm2.verts)    
    vol = bm2.calc_volume() 
    bm2.free()
    return vol <= TOL

context = bpy.context
ob = context.edit_object
me = ob.data
bm = bmesh.from_edit_mesh(me)
edges = [e for e in bm.edges if e.select]

print("Coplanar:", coplanar(edges))

As mentioned by @kheetor if you are seeing zero normals, you most likely need to update them bm.normal_update() or on a per face basis with face.normal_update().

Also suggest using a tolerance rather than equality when comparing float values. For example in your script above.

# check they're all the same vector
coplanarity = all((normals[0]).angle(x) < 0.0001 for x in normals)

would IMO be a better test than normals[0] == x.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.