2
$\begingroup$

I rather hastily wrote a short python script which orients the local rotation of an object so the Y goes along the longest axis, without affecting the apparent global orientation.

import bpy
import math


def set_orientation(ob, target_rotation):
    bpy.context.scene.objects.active = ob
    #ob: object to operate on

    #this assumes you are starting at 0 rotaton, so you pobably want to apply rotation before doing anything:
    bpy.ops.object.transform_apply(location=False, rotation=True, scale=False) #apply rotation
    #get inverse of rotation we want:
    t_rot = (math.radians(target_rotation[0]*-1),
             math.radians(target_rotation[1]*-1), 
             math.radians(target_rotation[2]*-1))
    #we need to apply the rotations in reverse order, so unsure that we are in zyx:
    ob.rotation_mode = 'ZYX'
    ob.rotation_euler = (t_rot) #set rotation to inverse
    bpy.ops.object.transform_apply(location=False, rotation=True, scale=False) #apply rotation

    t_rot = (math.radians(target_rotation[0]),
             math.radians(target_rotation[1]), 
             math.radians(target_rotation[2]))
    ob.rotation_mode = 'XYZ' #set rotation mode back (was zyx, so we want xyz)
    ob.rotation_euler = (t_rot) #set rotation


def fix_orientation(ob):
    if ob.type != 'MESH':
        return

    d = ob.dimensions
    correction = (0,0,0)

    if d[0] > d[1] and d[0] > d[2]:
        correction = (0,0,90)

    if d[1] > d[0] and d[0] > d[2]:
        correction = (0,0,0)

    if d[2] > d[0] and d[2] > d[1]:
        correction = (90,0,0)

    # hack for fixing rotation on boards at 45 degree angles:
    if abs(d[0] - d[1]) < .1:
        correction = (0,0,45)

    if abs(d[0] - d[2]) < .1:
        correction = (0,45,0)

    if abs(d[1] - d[2]) < .1:
        correction = (45,0,0)

    set_orientation(ob, correction)



for ob in bpy.data.objects:  
    # doesn't work as expected
    fix_orientation(ob)

# works as expected:
#fix_orientation(bpy.context.object)

However when I run the function fix_orientation() from within the loop, it changes the actual object rotation in global space. This doesn't happen if I just call it once on the active object.

Why is it only working as intended when called once?
What am I doing wrong (aside from the rather uncleanly implementation)?

$\endgroup$
  • $\begingroup$ Interesting. Another way would be to manipulate the mesh vertex coords v.co = v.co.yxz would swap x and y for instance, would probably naff some of the normals tho. $\endgroup$ – batFINGER Nov 18 '15 at 15:26
4
$\begingroup$

The loop is not the problem. The dimensions are based on the selection, which is why running the loop did not work.

All you need is to add in selection and deselection to your script.

Notice on line 6 ob.select = True and on line 25 ob.select = False. That selects the object the loop is working on, then deselects it when that iteration of the loop is done.
The reason you need to select and deselect each object is because ob.dimensions is the bounding box of the selection, not just one object.
Which is why it seamed to work for only one object at a time.

Entire script:

import bpy
import math

def set_orientation(ob, target_rotation):
    bpy.context.scene.objects.active = ob
    ob.select = True
    #ob: object to operate on

    #this assumes you are starting at 0 rotaton, so you pobably want to apply rotation before doing anything:
    bpy.ops.object.transform_apply(location=False, rotation=True, scale=False) #apply rotation
    #get inverse of rotation we want:
    t_rot = (math.radians(target_rotation[0]*-1),
             math.radians(target_rotation[1]*-1), 
             math.radians(target_rotation[2]*-1))
    #we need to apply the rotations in reverse order, so unsure that we are in zyx:
    ob.rotation_mode = 'ZYX'
    ob.rotation_euler = (t_rot) #set rotation to inverse
    bpy.ops.object.transform_apply(location=False, rotation=True, scale=False) #apply rotation

    t_rot = (math.radians(target_rotation[0]),
             math.radians(target_rotation[1]), 
             math.radians(target_rotation[2]))
    ob.rotation_mode = 'XYZ' #set rotation mode back (was zyx, so we want xyz)
    ob.rotation_euler = (t_rot) #set rotation
    ob.select = False

def fix_orientation(ob):
    if ob.type != 'MESH':
        return

    d = ob.dimensions
    correction = (0,0,0)

    if d[0] > d[1] and d[0] > d[2]:
        correction = (0,0,90)

    if d[1] > d[0] and d[0] > d[2]:
        correction = (0,0,0)

    if d[2] > d[0] and d[2] > d[1]:
        correction = (90,0,0)

    # hack for fixing rotation on boards at 45 degree angles:
    if abs(d[0] - d[1]) < .1:
        correction = (0,0,45)

    if abs(d[0] - d[2]) < .1:
        correction = (0,45,0)

    if abs(d[1] - d[2]) < .1:
        correction = (45,0,0)

    set_orientation(ob, correction)

for ob in bpy.data.objects:
    fix_orientation(ob)
$\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.