2
$\begingroup$

I need to set a face's direction from a Python script using bpy modules.
I tried the below code, but it does not work correctly.

obj = bpy.context.active_object
bm = bmesh.from_edit_mesh(obj.data)
faces = []
for f in bm.faces:
    if f.select:
        faces.append(f)
    if len(faces) >= 2:
        break
c = faces[0].normal.dot(faces[1].normal) / (faces[0].normal.magnitude * faces[1].normal.magnitude)
c = min(1, max(-1, c))
angle = acos(c)

cv = []
for l0 in faces[0].loops:
    for l1 in faces[1].loops:
        if l0.vert.index == l1.vert.index:
            cv.append(l0.vert)

ov = []
for l1 in faces[1].loops:
    for c in cv:
        if c.index == l1.vert.index:
            break
    else:
        ov.append(l1.vert)

axis = cv[1].co - cv[0].co
axis.normalize()

mat = Matrix.Rotation(-angle, 4, axis)

for o in ov:
    o.co = mat * o.co

I don't get it why this code doesn't correctly.
Is there any method to arrange face's direction from python scripts?
Here, I show what I want to do.

Before:

enter image description here

After:

enter image description here

Any idea?

$\endgroup$
  • 1
    $\begingroup$ Could you define more clearly what you mean by arranging a faces direction ? $\endgroup$ – ideasman42 Feb 10 '16 at 4:45
  • $\begingroup$ Arranging a face's direction means that normal vector of two adjacent faces make same without changing common edge. On above image, left rectangle will be transformed in order to have same normal as right rectangle. $\endgroup$ – nutti Feb 10 '16 at 6:56
  • 1
    $\begingroup$ from what I can tell this isn't common/conventional terminology. Note (faces[0].normal.magnitude * faces[1].normal.magnitude) will always be 1. $\endgroup$ – ideasman42 Feb 10 '16 at 7:08
  • $\begingroup$ Sorry for unclear title, and thanks to change title. $\endgroup$ – nutti Feb 10 '16 at 7:25
4
$\begingroup$

Here's a slightly different approach that worked for me. It rotates the active face to align it with the other selected face (that is not active).

enter image description here

It uses the common edge as the pivot point for the rotation. It doesn't have any tests for making sure only two faces are selected etc, so will only work with two faces selected in edit mode.

import bpy, bmesh

o  = bpy.context.object
bm = bmesh.from_edit_mesh( o.data )

active   = bm.select_history.active
selected = list( # Find non-active face using set boolean difference
    set( bm.select_history ).difference( 
        set( [ bm.select_history.active ] ) 
    ) 
)[0]

# Calculate the rotation difference between the selected and active faces' normals
rot = active.normal.rotation_difference( selected.normal ).to_matrix()

# The pivot is the common edge's center point
# Intersect both faces' edge sets to find the common edge
pivot = list(
    set( active.edges[:] ).intersection( 
        set( selected.edges ) 
    ) 
)[0]

pivot_coos = [ o.matrix_world * v.co for v in pivot.verts ]
pivot_vec  = ( pivot_coos[0] + pivot_coos[1] ) / 2 # Calculate edge center

bmesh.ops.rotate(
    bm,                      # BMESH object
    cent   = pivot_vec,      # Rotation pivot point (edge center)
    matrix = rot,            # Rotation value
    verts  = active.verts,   # What verts to rotate (active face's verts)
    space  = o.matrix_world  # Rotate in object world space
)
$\endgroup$
  • 1
    $\begingroup$ Thank you!!! I may not come up with this method if you tell me. It's wonderful! I could do my work with your code. $\endgroup$ – nutti Feb 11 '16 at 5:48

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.