soft array
zeffii opened this issue · comments
Dealga McArdle commented
sometimes a modifier is too much, this has linear and radial array
import bpy
import bmesh
import math
import mathutils
from mathutils import Vector
def main(self, context):
if not self.duplicates > 1:
return
obj = bpy.context.edit_object
me = obj.data
bm = bmesh.from_edit_mesh(me)
copy_geom_faces = [f for f in bm.faces if f.select and not f.hide]
num_faces = len(copy_geom_faces)
def offset_face(face):
for i in range(1, self.duplicates):
new_verts = []
for v in face.verts:
plus_vector = Vector((self.f_x * i, self.f_y * i, self.f_z * i))
nv = bm.verts.new(v.co + plus_vector)
new_verts.append(nv)
bm.faces.new(new_verts)
def rotate_face(face):
axis = mathutils.Vector(self.plane[:]).normalized()
theta = math.radians(self.radial_degrees) / self.duplicates
view = context.space_data
for i in range(1, self.duplicates):
new_verts = []
mat_rot = mathutils.Matrix.Rotation(theta * i, 4, axis)
if not self.use_cursor:
for v in face.verts:
vec = mat_rot * v.co
nv = bm.verts.new(vec)
new_verts.append(nv)
else:
for v in face.verts:
vec = mat_rot * (v.co + obj.location - view.cursor_location)
nv = bm.verts.new(vec + view.cursor_location - obj.location)
new_verts.append(nv)
bm.faces.new(new_verts)
if num_faces > 0:
if not self.radial_array:
for face in copy_geom_faces:
offset_face(face)
else:
for face in copy_geom_faces:
rotate_face(face)
if self.recalc:
# could also operate on faces=new_faces ... bm.faces might be overkill
bmesh.ops.recalc_face_normals(bm, faces=bm.faces)
bmesh.update_edit_mesh(me, True, destructive=True)
class SimpleOperator(bpy.types.Operator):
"""Tooltip"""
bl_idname = "object.array_operator"
bl_label = "Simple Array Operator"
bl_options = {'REGISTER', 'UNDO'}
bl_icon = 'TEXT'
f_x = bpy.props.FloatProperty()
f_y = bpy.props.FloatProperty()
f_z = bpy.props.FloatProperty()
plane = bpy.props.FloatVectorProperty(default=(0,0,1), subtype='XYZ')
duplicates = bpy.props.IntProperty(min=0)
recalc = bpy.props.BoolProperty()
radial_array = bpy.props.BoolProperty()
radial_degrees = bpy.props.FloatProperty(default=360)
use_cursor = bpy.props.BoolProperty()
def draw(self, context):
l = self.layout
c = l.column()
icon = ['MOD_ARRAY', 'FORCE_FORCE'][self.radial_array]
c.prop(self, 'radial_array', toggle=True, icon=icon)
if not self.radial_array:
c.prop(self, 'f_x')
c.prop(self, 'f_y')
c.prop(self, 'f_z')
else:
view = context.space_data
c.prop(self, 'use_cursor', text='pivot around cursor')
if self.use_cursor:
c.prop(view, "cursor_location", text="Location")
c.prop(self, 'plane')
c.prop(self, 'radial_degrees')
c.prop(self, 'duplicates')
c.prop(self, 'recalc')
@classmethod
def poll(cls, context):
return context.active_object is not None
def execute(self, context):
main(self, context)
return {'FINISHED'}
def register():
bpy.utils.register_class(SimpleOperator)
def unregister():
bpy.utils.unregister_class(SimpleOperator)
if __name__ == "__main__":
register()