Skip to content
Snippets Groups Projects
circle_array.py 5.25 KiB
# gpl author: Antonis Karvelas

# -*- coding: utf-8 -*-

bl_info = {
    "name": "Circle Array",
    "author": "Antonis Karvelas",
    "version": (1, 0),
    "blender": (2, 6, 7),
    "location": "View3D > Object > Circle_Array",
    "description": "Uses an existing array and creates an empty, "
                   "rotates it properly and makes a Circle Array",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Mesh"
    }


import bpy
from bpy.types import Operator
from math import radians


class Circle_Array(Operator):
    bl_label = "Circle Array"
    bl_idname = "objects.circle_array_operator"
    bl_description = ("Creates an Array Modifier with offset empty object\n"
                      "Works with Mesh, Curve, Text & Surface")

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def check_empty_name(self, context):
        new_name, def_name = "", "EMPTY_C_Array"
        suffix = 1
        try:
            # first slap a simple linear count + 1 for numeric suffix, if it fails
            # harvest for the rightmost numbers and append the max value
            list_obj = []
            obj_all = context.scene.objects
            list_obj = [obj.name for obj in obj_all if obj.name.startswith(def_name)]
            new_name = "{}_{}".format(def_name, len(list_obj) + 1)

            if new_name in list_obj:
                from re import findall
                test_num = [findall("\d+", words) for words in list_obj]
                suffix += max([int(l[-1]) for l in test_num])
                new_name = "{}_{}".format(def_name, suffix)
            return new_name
        except:
            return None

    def execute(self, context):
        try:
            allowed_obj = ['MESH', 'CURVE', 'SURFACE', 'FONT']
            if context.active_object.type not in allowed_obj:
                self.report(
                    {"WARNING"},
                    "Operation Cancelled. The active object is not of "
                    "Mesh, Curve, Surface or Font type"
                    )
                return {'CANCELLED'}

            default_name = self.check_empty_name(context) or "EMPTY_C_Array"
            bpy.ops.object.modifier_add(type='ARRAY')

            if len(bpy.context.selected_objects) == 2:
                list = bpy.context.selected_objects
                active = list[0]
                active.modifiers[0].use_object_offset = True
                active.modifiers[0].use_relative_offset = False
                active.select = False
                bpy.context.scene.objects.active = list[0]
                bpy.ops.view3d.snap_cursor_to_selected()
                if active.modifiers[0].offset_object is None:
                    bpy.ops.object.add(type='EMPTY')
                    empty_name = bpy.context.active_object
                    empty_name.name = default_name
                    active.modifiers[0].offset_object = empty_name
                else:
                    empty_name = active.modifiers[0].offset_object

                bpy.context.scene.objects.active = active
                num = active.modifiers["Array"].count
                rotate_num = 360 / num
                active.select = True
                bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
                empty_name.rotation_euler = (0, 0, radians(rotate_num))
                empty_name.select = False
                active.select = True
                bpy.ops.object.origin_set(type="ORIGIN_CURSOR")

                return {'FINISHED'}
            else:
                active = context.active_object
                active.modifiers[0].use_object_offset = True
                active.modifiers[0].use_relative_offset = False
                bpy.ops.view3d.snap_cursor_to_selected()
                if active.modifiers[0].offset_object is None:
                    bpy.ops.object.add(type='EMPTY')
                    empty_name = bpy.context.active_object
                    empty_name.name = default_name
                    active.modifiers[0].offset_object = empty_name
                else:
                    empty_name = active.modifiers[0].offset_object

                bpy.context.scene.objects.active = active
                num = active.modifiers["Array"].count
                rotate_num = 360 / num
                active.select = True
                bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
                empty_name.rotation_euler = (0, 0, radians(rotate_num))
                empty_name.select = False
                active.select = True

                return {'FINISHED'}
        except Exception as e:
            self.report({'WARNING'},
                        "Circle Array operator could not be executed (See the console for more info)")
            print("\n[objects.circle_array_operator]\nError: {}\n".format(e))

            return {'CANCELLED'}


# Register
def circle_array_menu(self, context):
    self.layout.operator(Circle_Array.bl_idname, text="Circle_Array")


def register():
    bpy.utils.register_class(Circle_Array)
    bpy.types.VIEW3D_MT_object.append(circle_array_menu)


def unregister():
    bpy.utils.unregister_class(Circle_Array)
    bpy.types.VIEW3D_MT_object.remove(circle_array_menu)


if __name__ == "__main__":
    register()