Is It Possible to Select Faces (in Edit Mode) Based on the Normals of the Faces Connected to It?

I understand how weirdly specific this is, but is there no way to select faces based on the normals they're facing AND the adjacent faces' normals?

What I want is a little complicated, but if I were to have a subdivided cube with perfectly flat faces, would I be able to select every face on the X positive normal but make an exception for any face that's connected to a different face with a Z positive normal?

I've tried putting specific selections into different vertex groups, but I'ven't been getting much luck using that work around. Could I not use python to make a custom search and dot in my own parameters?

[EDIT 1]

Alright, thinking a little better this time, I realised that what I was asking was odd because of the examples I used. So, I'm editing this to better clarify, using a model that correlates much better to the issue I'm having.

I suppose there'd be 2 options to what I'm asking: "select adjacent faces of normal based on the faces' normals" or "limit selection of faces based on the connected faces' normals." Though in all fairness, I'd only profit from the 2nd case. Giving examples now.

In the first case, "select adjacent faces of normal based on faces' normals," I've set up an example. In this case, I asked to select all the Z+ normals AND select adjacent faces that have the Y- normal. As you can see, only the faces that are making contact with the Z+ faces are selected.

In the second case, "limit selection of faces based on the connected faces' normals," I've another example. Again, I ask to select all Z+ faces, but this time, avoid any faces that are on the Y- normal.

Is there any way to do this? Not a big deal if there isn't, I just spent the better part of my day trying to do something like the earlier 2nd example with only commands and couldn't.

• Hi, thanks for the post. This question was in the review queue. I think it's a good question and I'm pretty sure I understand what you mean, but can you be more specific? Can you include more examples of success cases for this type of selection? Maybe even failure? For example, what should happen at higher levels of subdivision? If a mesh has an odd number of faces such that one normal could be considered to be both Z- and Z+, what should happen? May 7, 2021 at 20:23
• There is Select -> Select Similar -> Normal but that is only part of what you asked. May 7, 2021 at 20:25
• @AllenSimpson I already racked my mind immensely trying to convey my issue, I'm not so sure I can do any better, but I can still try. Imagine a staircase, and imagine that I select a tread (google stair anatomy) somewhere in the middle of the staircase. What if I wanted to select the riser that's directly below it? If I do Select -> Select More, I'd get the riser above the tread and the riser below the tread when I only want one. This example makes no sense since, in theory, every riser has the same normals; imagine that it's a SINGLE step instead of a staircase, with only 2 risers and 1 tread May 7, 2021 at 20:41
• Man, if I knew that pressing enter immediately uploaded the comment, I sure as hell wouldn't've pressed it. May 7, 2021 at 20:47
• Your first example selects Y- adjacent to Z+, but only downwards. That's the tricky bit. If you wanted up and down, that would be easier. May 7, 2021 at 22:19

Select +Z faces and neighboring -Y faces if they go down:

import bpy, bmesh
from mathutils import Vector

up = Vector((0, 0, 1))
side = Vector((0, -1, 0))

def main():
me = bpy.context.active_object.data
bm = bmesh.from_edit_mesh(me)
for face in bm.faces:
if face.normal == up:
face.select = True
conditional_select_neighbors(face)
bmesh.update_edit_mesh(me)

def conditional_select_neighbors(face):
for loop in face.loops:  # each edge has a 'loop' associated with it for each face
is_side = loop2.face.normal == side  # as R. Betts spotted, this is not enough
is_lower = loop.vert.co.z > loop3.vert.co.z
if is_side and is_lower:
loop2.face.select = True

if __name__ == "__main__":
main()


Select +Z faces without -Y neighbors:

import bpy, bmesh
from mathutils import Vector

up = Vector((0, 0, 1))
side = Vector((0, -1, 0))

def main():
me = bpy.context.active_object.data
bm = bmesh.from_edit_mesh(me)
for face in bm.faces:
if face.normal == up:
conditional_select(face)
bmesh.update_edit_mesh(me)

def conditional_select(face):
face.select = True
for loop in face.loops:  # each edge has a 'loop' associated with it for each face