3
$\begingroup$

Is there a way in Python to get a list of faces that are coplanar to a particular face similar to the way the command "Select Linked Flat Faces" works, but without actually requiring me to change the selection.

I think it might be possible to use Python to call the aforementioned command to select coplanar faces, and then store the selected faces in a list, but this seems really inefficient as I will be iterating over a lot of faces, so I'm wondering if there is a command in the Python API that I can send a face index and angle tolerance and it will just return a list of coplanar faces.

$\endgroup$
  • 1
    $\begingroup$ there is a function that gives the angle between 2 vectors, you get the face normal then loop through the faces list and calculate the angle difference with their normals and use your threshold to decide which go in $\endgroup$ – Chebhou May 29 '16 at 7:19
3
$\begingroup$

There is also built in bpy.ops.mesh.select_similar(type='COPLANAR', threshold=0)

$\endgroup$
4
$\begingroup$

Using bmesh

As an alternative to bpy.ops.mesh.select_similar(type='COPLANAR')

4 points are coplanar if the volume created by the points is 0. If any three points determine a plane then additional points can be checked for coplanarity by measuring the distance of the points from the plane, if the distance is 0 then the point is coplanar

Select all faces with normals within angle tolerance of face.normal, then check if the convex hull, made from the verts of each of these faces with those of selected face, volume is zero (or within some tolerance). If so it must be on same plane.

Quick script to test. Runs thru all mesh objects in the scene each face in the objects mesh and the coplanar list of the indices of faces coplanar to that face.

"Volume" of face

import bpy
import bmesh
from mathutils import Vector

def coplanar_list(bm, index, TOL=0.001):
    bm.faces.ensure_lookup_table()
    face = bm.faces[index]
    coplanar = []
    # narrow down selection based on normals
    faces = [f for f in bm.faces if f.normal.angle(face.normal) < TOL]
    # list of faces with same normal
    bm2 = bmesh.new()
    for f in faces:
        verts = [v.co for v in face.verts]
        if f.index != face.index:
            verts.extend(v.co for v in f.verts)
        # make a dummy bmesh with verts of each face
        o = sum(verts, Vector((0,0,0))) / len(verts)
        for v in verts:
            bm2.verts.new(v - o)
        #calculate a convex hull
        bmesh.ops.convex_hull(bm2, input=bm2.verts)
        # if the convex hull has no volume its on same plane.
        volume = bm2.calc_volume()
        if volume < 0.001:
            coplanar.append(f.index)
        bm2.clear()
    bm2.free()
    return coplanar

context = bpy.context
scene = context.scene
meshobjs = [o for o in scene.objects if o.type == 'MESH']
print("-" * 72)
for o in meshobjs:
    print("Object", o.name)
    if o == context.edit_object:
        bm = bmesh.from_edit_mesh(o.data)
    else:
        bm = bmesh.new()
        bm.from_mesh(o.data)
    for f in bm.faces:
        print("\tface %3d " % (f.index), coplanar_list(bm, f.index))

Or using distance point to plane for 4th and higher verts of plane defined by first 3 verts. Note this script merely lists faces whose verts are coplanar by way of example, not those that are coplanar to active face.

import bpy
import bmesh

from mathutils.geometry import distance_point_to_plane as dp2p

TOL = 1e-6

context = bpy.context
ob = context.edit_object #  run in edit mode
me = ob.data

bm = bmesh.from_edit_mesh(me)

coplanar = []
for f in bm.faces:
    if len(f.verts) == 3: #  tri
        coplanar.append(f)
        continue
    v1 = f.verts[1].co - f.verts[0].co
    v2 = f.verts[2].co - f.verts[0].co
    if all(abs(dp2p(v.co, f.verts[0].co, v1.cross(v2))) < TOL for v in f.verts[3:]) :
        coplanar.append(f)

print([f.index for f in coplanar])
$\endgroup$
0
$\begingroup$

In edit mode, Having your face of comparison selected, you can use Shift+G >> Co-planar. You must be in Face selection mode for this to work though, otherwise you will get a different context for verts/edges respectively.

This equates to the same answer given by JuhaW from the Python perspective.

bpy.ops.mesh.select_similar(type='COPLANAR', threshold=0.01)

The limitations to this is that everything that you are looking for must be in one object reference.

To overcome this limitation, I would recommend (with Object Mode active):

  1. Create a full copy of your current scene.

Create a copy of the scene

  1. Within the copied scene, Select all your mesh objects, and join them into ONE object by hitting Ctrl+J.

  2. Delete the other objects that are not important for this operation.

  3. Go into edit mode.

  4. run your python function: bpy.ops.mesh.select_similar(type='COPLANAR', threshold=0.01)

$\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.