0

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

Kabu
  • 788
  • 8
  • 19
  • 3
    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? – patmo141 Apr 28 '18 at 17:35
  • 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 – Kabu Apr 28 '18 at 19:45
  • 1
    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. – kheetor Apr 28 '18 at 20:36
  • I think you are on the correct path. I use link_faces for many many calculations. Also your image above looks like a wireframe – patmo141 Apr 28 '18 at 23:30
  • Got the question more clear to show what the problem really is. – Kabu Apr 29 '18 at 10:54
  • My mistake. Forgot the math. What I need is calc_face_angle, but because that won't work with one face only edges, I think calc_edge_angle on vertices is more appropriate – Kabu Apr 29 '18 at 11:39

1 Answers1

3

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.

batFINGER
  • 84,216
  • 10
  • 108
  • 233