# How to find if geometry linked to an edge is coplanar

I have this mesh: 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:
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: 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:
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:
faces.append(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 for x in normals)

print("Coplanar: ", coplanarity)


First selection: 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)>
Normal:  <Vector (0.0000, -0.0000, 1.0000)>
Normal copy:  <Vector (0.0000, -0.0000, 1.0000)>
Coplanar:  True


Second selection: 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

• 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
• 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

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).angle(x) < 0.0001 for x in normals)


would IMO be a better test than normals == x.