13
$\begingroup$

"Copy to selected" copies the complete location X, Y and Z to all the selected objects. I wish to copy only Z location. Please see the images bellow to get idea of what I intend to achieve.

Before
before transforms

After
after transforms

$\endgroup$
16
$\begingroup$

You can also try another way:

  • Switch pivot point type to Active Element, toggle Manipulate Center Points;
  • Select objects, keep one active, then SZ0.

enter image description here

UPDATE:

Thanks to Bastien Montagne (aka mont29)'s commit, you can find this feature in latest version, now only for Location, but also Rotation, Scale, Dimensions, etc:

enter image description here

$\endgroup$
  • $\begingroup$ "toggle Manipulate Center Points" is the key step, and i was not ware of this feature. Thanks $\endgroup$ – ashwin Feb 17 '15 at 6:16
9
$\begingroup$

The position of the object is defined by it's origin point, but you can't always assume that the origin is centered when selecting multiple and more complex objects than a cube.

enter image description here

That the origins are prefectly centered is a special case, because of the different dimensions or diffenrent kinds of geometry. In this case the result of the test scene above would look like:

enter image description here

One way to get around this is to calculate the object position differences of the meshes and move the objects by adding or subtracting this differences in the axis from the current locations of the objects.

For this case I wrote a small addon to align multiple objects to the geometry of the active one. The script calculates the dimensions of every object that is selected and move the objects to the right spot, independent of the origin's position.

enter image description here
Align objects to the second suzanne

bl_info = {
    "name": "Align Geometry",
    "description": "",
    "author": "poor",
    "version": (0, 0, 1),
    "blender": (2, 73, 0),
    "location": "Tool Shelf > Tools",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "3D View"
}

import bpy
from bpy.props import (EnumProperty,
                       PointerProperty,
                       )
from bpy.types import (Panel,
                       Operator,
                       AddonPreferences,
                       PropertyGroup,
                       )

# ------------------------------------------------------------------------
#    logic
# ------------------------------------------------------------------------

# get x,y,z min or max of geometry
def get_geo_min_max(obj, axis):
    axis_coords = []
    # iterate through all vertices
    for face in obj.data.polygons:
        verts_in_face = face.vertices[:]
        for vert in verts_in_face:
            local_point = obj.data.vertices[vert].co
            world_point = obj.matrix_world * local_point
            axis_coords.append(world_point[axis])
    # return the geometry minimum and maximum        
    return min(axis_coords), max(axis_coords)


# align objects to min or max of the reference object
def align_objects_to_min_or_max_of_reference_object(ob_reference, mesh_ob_transform_list, align_axis, extreme_point):
    for i in mesh_ob_transform_list:
        # get the difference of obj maximums
        if extreme_point == "MAX":
            diff = abs(get_geo_min_max(ob_reference, align_axis)[1] - get_geo_min_max(i, align_axis)[1])
        # get the difference of obj minimums
        if extreme_point == "MIN":
            diff = abs(get_geo_min_max(ob_reference, align_axis)[0] - get_geo_min_max(i, align_axis)[0])
        # if current position is higher the reference
        if get_geo_min_max(ob_reference, align_axis)[0] > get_geo_min_max(i, align_axis)[0]:
            i.location[align_axis] += diff
        # if current position is same the reference
        if get_geo_min_max(ob_reference, align_axis)[0] == get_geo_min_max(i, align_axis)[0]:
            i.location[align_axis] -= diff
        # if current position is below the reference
        if get_geo_min_max(ob_reference, align_axis)[0] < get_geo_min_max(i, align_axis)[0]:
            i.location[align_axis] -= diff

# align objects to the center of the reference object
def align_objects_to_center_of_reference_object(ob_reference, mesh_ob_transform_list, align_axis):
    # get extreme points of reference
    ob_ref_extremes = get_geo_min_max(ob_reference, align_axis)
    # calculate axis center
    ob_ref_axis_center = ob_ref_extremes[0] + (0.5 * (ob_ref_extremes[1] - ob_ref_extremes[0]))
    for i in mesh_ob_transform_list:
        # get extreme points of i
        i_extremes = get_geo_min_max(i, align_axis)
        # calculate center
        i_axis_center = i_extremes[0] + (0.5 * (i_extremes[1] - i_extremes[0]))
        # calculate difference
        diff = abs(ob_ref_axis_center - i_axis_center)
        # if current position is above the reference
        if ob_ref_axis_center > i_axis_center:
            i.location[align_axis] += diff
        # if current position is same as reference
        if ob_ref_axis_center == i_axis_center:
            i.location[align_axis] += diff
        # if current position is below as reference
        if ob_ref_axis_center < i_axis_center:
            i.location[align_axis] -= diff

# ------------------------------------------------------------------------
#    ui
# ------------------------------------------------------------------------

def axisStringToInt(axis_str):
    axes = {
        'X': 0,
        'Y': 1,
        'Z': 2
    }
    return axes[axis_str]

class GeometryAlignSettings(PropertyGroup):
    axes = [
        ('X', "X Axis", ""),
        ('Y', "Y Axis", ""),
        ('Z', "Z Axis", ""),
    ]
    transform = EnumProperty(
        name="Transformation Axes:",
        description="Apply transformation to",
        items=axes
        )

class OBJECT_PT_geometry_align_panel(Panel):
    bl_idname = "OBJECT_PT_geometry_align_panel"
    bl_label = "Align Geometry"
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"
    bl_category = "Tools"
    bl_context = "objectmode"

    def draw(self, context):
        layout = self.layout
        scene = context.scene
        active_obj = context.active_object
        col = layout.column(align=True)
        col.prop(scene.geometry_align, "transform", text="")
        row = col.row(align=True)
        row.operator("geometryalign.to_max_geo", icon="ZOOMIN")
        row.operator("geometryalign.to_min_geo", icon="ZOOMOUT")
        row = col.row(align=True)
        row.operator("geometryalign.to_center_geo", icon="CURSOR")

        if active_obj != None:
            if active_obj.type == "MESH" and active_obj != None:
                layout.label( "Snap to " + active_obj.name )
            else:
                layout.label( "No active mesh" )


class OBJECT_OT_AlignMaxButton(bpy.types.Operator):
    bl_idname = "geometryalign.to_max_geo"
    bl_label = "Maximum"
    bl_description = "Align objects to maximum of active object"
    #bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        scn = context.scene # get the scene
        objs = bpy.context.selected_objects  # get selected objects
        mesh_objs = [m for m in objs if m.type == "MESH"] # get all mesh objects in selection
        active_obj = scn.objects.active # get active object

        if active_obj != None: # error handling
            if active_obj.type == "MESH":
                axis = axisStringToInt(scn.geometry_align.transform)  # set transform axis: x=0, y=1, z=2
                align_objects_to_min_or_max_of_reference_object (active_obj, mesh_objs, axis, 'MAX')
            else:
                self.report({'INFO'}, "%s is not a mesh object" % (active_obj.name))
        return{'FINISHED'}


class OBJECT_OT_AlignMinButton(bpy.types.Operator):
    bl_idname = "geometryalign.to_min_geo"
    bl_label = "Minimum"
    bl_description = "Align objects to minimum of active object"
    #bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        scn = context.scene # get the scene
        objs = bpy.context.selected_objects  # get selected objects
        mesh_objs = [m for m in objs if m.type == "MESH"] # get all mesh objects in selection
        active_obj = scn.objects.active # get active object

        if active_obj != None: # error handling
            if active_obj.type == "MESH":
                axis = axisStringToInt(scn.geometry_align.transform)  # set transform axis: x=0, y=1, z=2
                align_objects_to_min_or_max_of_reference_object (active_obj, mesh_objs, axis, 'MIN')
            else:
                self.report({'INFO'}, "%s is not a mesh object" % (active_obj.name))
        return{'FINISHED'}

class OBJECT_OT_AlignCenterButton(bpy.types.Operator):
    bl_idname = "geometryalign.to_center_geo"
    bl_label = "Center"
    bl_description = "Align objects to center of active object"
    #bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        scn = context.scene # get the scene
        objs = bpy.context.selected_objects  # get selected objects
        mesh_objs = [m for m in objs if m.type == "MESH"] # get all mesh objects in selection
        active_obj = scn.objects.active # get active object

        if active_obj != None: # error handling
            if active_obj.type == "MESH":
                axis = axisStringToInt(scn.geometry_align.transform)  # set transform axis: x=0, y=1, z=2
                align_objects_to_center_of_reference_object (active_obj, mesh_objs, axis)
            else:
                self.report({'INFO'}, "%s is not a mesh object" % (active_obj.name))
        return{'FINISHED'}


# ------------------------------------------------------------------------
#    register and unregister functions
# ------------------------------------------------------------------------

def register():
    bpy.utils.register_module(__name__)
    bpy.types.Scene.geometry_align = PointerProperty(type=GeometryAlignSettings)

def unregister():
    bpy.utils.unregister_module(__name__)
    del bpy.types.Scene.geometry_align

if __name__ == "__main__":
    register()
$\endgroup$
  • $\begingroup$ You can center the origin of all the selected objects with Ctrl Shift Alt C > Origin to geometry (or whatever centering method you like). $\endgroup$ – gandalf3 Feb 10 '15 at 5:31
  • $\begingroup$ Oops, I meant that I don't think it's necessary to use python, but perhaps I missed something. You can center the origins of multiple objects at once without python.. $\endgroup$ – gandalf3 Feb 10 '15 at 5:49
  • $\begingroup$ @gandalf3 If the origins of all objects are not in the center, it becomes difficult, right? Maybe there is a clever way without it, but I haven't a better idea. $\endgroup$ – p2or Feb 10 '15 at 5:59
  • 1
    $\begingroup$ @poor thanks, this script is bang on and will help me learn script based manupelation $\endgroup$ – ashwin Feb 17 '15 at 6:20
  • 1
    $\begingroup$ @poor Wow more than expected. This creates new possibilities. $\endgroup$ – ashwin Mar 8 '15 at 23:29

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.