3
$\begingroup$

Using the GUI you can Set Transform Orientation to Normal select a face in edit mode then hit G button then Z button to move the face in the direction of the normal.

I am trying to do this with blender python.

import bpy

ob=bpy.context.view_layer.objects.active

for i in range(0,len(ob.data.polygons)):

    # deselect everything first
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.select_all(action='DESELECT')

    # it seems you can only select face during object mode
    bpy.ops.object.mode_set(mode='OBJECT')
    face=ob.data.polygons[i]
    face.select=True

    bpy.ops.object.mode_set(mode='EDIT')
    face_normal=face.normal

    matrix=((0, -1, 0), (face_normal[2], 0, -face_normal[0]), (face_normal[0], 0, face_normal[2]))

    bpy.ops.transform.translate(value=(0, 0, 0.1),
        orient_type='NORMAL', 
        orient_matrix=matrix, 
        orient_matrix_type='NORMAL',
        constraint_axis=(False, False, True))

This sample code should iterate through all the faces on the selected object and translate each face individually along the face normal.

This is just a test snippet - I intend to use filters to select specific faces later but just iterating through all faces now to verify code works.

If you create an object this code works to scale the object on the Z axis but not oriented toward the face normal.

I suspect the problem is something with the way I'm assigning the orient_matrix matrix variable. I can't find any examples on how to assign the the orient_matrix for translate operations. there are lots of examples for rotations but not for translating individual faces.

$\endgroup$
1

1 Answer 1

9
$\begingroup$

I would recommend bmesh for this.

No face selection required, no toggling mode, no bpy.ops.mesh... operators.

Because a recent answer used an edit mode bmesh, here is an edit mode version

Translates all vertices 0.1 locally in the direction of their normal for each face.

Example scripts, move all faces along their normals.

import bpy

context = bpy.context

import bmesh

ob = context.object
me = ob.data
bm = bmesh.from_edit_mesh(me)
for f in bm.faces:
    bmesh.ops.translate(bm,
            verts=f.verts,
            vec=0.1 * f.normal)

bmesh.update_edit_mesh(me)

Or in object mode

import bpy
import bmesh

context = bpy.context

ob = context.object
me = ob.data
bm = bmesh.new()
bm.from_mesh(me)
for f in bm.faces:
    bmesh.ops.translate(bm,
            verts=f.verts,
            vec=0.1 * f.normal)

bm.to_mesh(me)
me.update()

Could have also simply

for f in bm.faces:
    vec = 0.1 * f.normal
    for v in bm.verts:
        v.co += vec

Notice this is going to give dreadful results, as each vertex can be a member of multiple faces. Running scripts above on a uv sphere will spike the poles. On the cone make the apex grow (appear to scale more in Z)

A good indicator of what we are trying to do here when using all faces would be to split the edges, so each face is free to move on its normal without affecting the normals of adjacent faces. Here is an example using default cone

enter image description here

bmesh.ops.split_edges(bm,
        edges=bm.edges,
        )
for f in bm.faces:
    bmesh.ops.translate(bm,
            verts=f.verts,
            vec=0.1 * f.normal)

This shows where each face would be if we extruded individual faces along their normal.

Instead we could move all the vertices along their normals. This will give the impression of a 0.1 local scale on convex objects like the spheres and cube, but will "fatten" Suzanne.

import bpy
import bmesh

context = bpy.context

ob = context.object
me = ob.data
bm = bmesh.new()
bm.from_mesh(me)


for v in bm.verts:
    v.co += 0.1 * v.normal

bm.to_mesh(me)
me.update()

Putting it together.

enter image description here

Here we move each selected face along its normal, by finding all the verts that belong to selected faces, then for each vert move along average of the normals of connected and selected faces

Run in edit mode with faces selected.

import bpy
import bmesh
from mathutils import Vector

context = bpy.context

ob = context.object
me = ob.data
bm = bmesh.from_edit_mesh(me)

verts = set(v for f in bm.faces if f.select for v in f.verts)
for v in verts:
    norms  = [f.normal for f in v.link_faces if f.select]
    n = sum(norms, Vector()) / len(norms)
    v.co += 0.1 * n

bmesh.update_edit_mesh(me)
$\endgroup$
2
  • 1
    $\begingroup$ batFINGER: really detailed and thorough reply. I appreciate you taking the time to do this. Very helpful. Thank you. $\endgroup$
    – stackzz
    Commented Feb 18, 2020 at 21:56
  • $\begingroup$ I have a question in edit mode why do you use sometimes just me.update() (like there blender.stackexchange.com/questions/66894/…) and others bmesh.update_edit_mesh(me) $\endgroup$
    – FkNWO
    Commented Nov 28, 2021 at 17:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .