Skip to content

Commit

Permalink
feat: add square_array operator and create arrays sub-menu
Browse files Browse the repository at this point in the history
  • Loading branch information
tristan-hm committed Apr 9, 2022
1 parent efadb47 commit 14ea44e
Show file tree
Hide file tree
Showing 6 changed files with 310 additions and 2 deletions.
6 changes: 5 additions & 1 deletion interface/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from . import boolean_menu
from . import bevel_menu
from . import extrude_menu
from . import array_menu


def reload():
Expand All @@ -16,6 +17,7 @@ def reload():
importlib.reload(boolean_menu)
importlib.reload(bevel_menu)
importlib.reload(extrude_menu)
importlib.reload(array_menu)


def register():
Expand All @@ -26,6 +28,7 @@ def register():
boolean_menu.register()
bevel_menu.register()
extrude_menu.register()
array_menu.register()


def unregister():
Expand All @@ -35,4 +38,5 @@ def unregister():
utils_menu.unregister()
boolean_menu.unregister()
bevel_menu.unregister()
extrude_menu.unregister()
extrude_menu.unregister()
array_menu.unregister()
29 changes: 29 additions & 0 deletions interface/array_menu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import bpy
from .. import bl_info


class ND_MT_array_menu(bpy.types.Menu):
bl_label = "Arrays"
bl_idname = "ND_MT_array_menu"


def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_DEFAULT'
layout.operator("nd.circular_array", icon='DRIVER_ROTATIONAL_DIFFERENCE')
layout.operator("nd.square_array", icon='LIGHTPROBE_GRID')


def draw_item(self, context):
layout = self.layout
layout.menu(ND_MT_array_menu.bl_idname)


def register():
bpy.utils.register_class(ND_MT_array_menu)
bpy.types.INFO_HT_header.append(draw_item)


def unregister():
bpy.utils.unregister_class(ND_MT_array_menu)
bpy.types.INFO_HT_header.remove(draw_item)
2 changes: 1 addition & 1 deletion interface/main_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ def draw(self, context):
layout.menu("ND_MT_boolean_menu", icon='MOD_BOOLEAN')
layout.menu("ND_MT_bevel_menu", icon='MOD_BEVEL')
layout.menu("ND_MT_extrude_menu", icon='MOD_SOLIDIFY')
layout.menu("ND_MT_array_menu", icon='MOD_ARRAY')
layout.operator("nd.mirror", icon='MOD_MIRROR')
layout.operator("nd.circular_array", icon='DRIVER_ROTATIONAL_DIFFERENCE')
layout.separator()
layout.operator("nd.recon_poly", icon='SURFACE_NCURVE')
layout.operator("nd.screw_head", icon='CANCEL')
Expand Down
4 changes: 4 additions & 0 deletions interface/main_ui_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ def draw(self, context):
row = column.row(align=True)
row.scale_y = 1.2
row.operator("nd.circular_array", icon='DRIVER_ROTATIONAL_DIFFERENCE')

row = column.row(align=True)
row.scale_y = 1.2
row.operator("nd.square_array", icon='LIGHTPROBE_GRID')

box = layout.box()
box.label(text="Generators", icon='GHOST_ENABLED')
Expand Down
4 changes: 4 additions & 0 deletions power_mods/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from . import mirror
from . import profile_extrude
from . import circular_array
from . import square_array
from . import bevel


Expand All @@ -17,6 +18,7 @@ def reload():
importlib.reload(mirror)
importlib.reload(profile_extrude)
importlib.reload(circular_array)
importlib.reload(square_array)
importlib.reload(bevel)


Expand All @@ -28,6 +30,7 @@ def register():
mirror.register()
profile_extrude.register()
circular_array.register()
square_array.register()
bevel.register()


Expand All @@ -39,6 +42,7 @@ def unregister():
mirror.unregister()
profile_extrude.unregister()
circular_array.unregister()
square_array.unregister()
bevel.unregister()


267 changes: 267 additions & 0 deletions power_mods/square_array.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
import bpy
import bmesh
from math import radians
from mathutils import Euler
from .. lib.overlay import update_overlay, init_overlay, toggle_pin_overlay, toggle_operator_passthrough, register_draw_handler, unregister_draw_handler, draw_header, draw_property
from .. lib.events import capture_modifier_keys


mod_first_array = "Square Array A — ND"
mod_second_array = "Square Array B — ND"
mod_summon_list = [mod_first_array, mod_second_array]


class ND_OT_square_array(bpy.types.Operator):
bl_idname = "nd.square_array"
bl_label = "Square Array"
bl_description = "Array an object in a square/grid like fashion"
bl_options = {'UNDO'}


def modal(self, context, event):
capture_modifier_keys(self, event)

offset_factor = (self.base_offset_factor / 10.0) if self.key_shift else self.base_offset_factor

if self.key_toggle_operator_passthrough:
toggle_operator_passthrough(self)

elif self.key_toggle_pin_overlay:
toggle_pin_overlay(self, event)

elif self.operator_passthrough:
update_overlay(self, context, event)

return {'PASS_THROUGH'}

elif self.key_cancel:
self.revert(context)

return {'CANCELLED'}

elif self.key_increase_factor:
if self.key_ctrl or self.key_ctrl_alt:
self.base_offset_factor = min(1, self.base_offset_factor * 10.0)

elif self.key_decrease_factor:
if self.key_ctrl or self.key_ctrl_alt:
self.base_offset_factor = max(0.001, self.base_offset_factor / 10.0)

elif self.key_step_up:
if self.key_alt:
if self.key_shift:
self.second_axis = (self.second_axis + 1) % 3
else:
self.first_axis = (self.first_axis + 1) % 3
elif self.key_ctrl:
self.first_offset += offset_factor
elif self.key_ctrl_alt:
self.second_offset += offset_factor
else:
if self.key_shift:
self.second_count = self.second_count + 1
else:
self.first_count = self.first_count + 1

self.dirty = True

elif self.key_step_down:
if self.key_alt:
if self.key_shift:
self.second_axis = (self.second_axis - 1) % 3
else:
self.first_axis = (self.first_axis - 1) % 3
elif self.key_ctrl:
self.first_offset -= offset_factor
elif self.key_ctrl_alt:
self.second_offset -= offset_factor
else:
if self.key_shift:
self.second_count = max(2, self.second_count - 1)
else:
self.first_count = max(2, self.first_count - 1)

self.dirty = True

elif self.key_confirm:
self.finish(context)

return {'FINISHED'}

elif self.key_movement_passthrough:
return {'PASS_THROUGH'}

if self.dirty:
self.operate(context)

update_overlay(self, context, event)

return {'RUNNING_MODAL'}


def invoke(self, context, event):
self.dirty = False
self.base_offset_factor = 0.01

mods = context.active_object.modifiers
mod_names = list(map(lambda x: x.name, mods))
previous_op = all(m in mod_names for m in mod_summon_list)

if previous_op:
self.summon_old_operator(context, mods)
else:
self.prepare_new_operator(context)

self.operate(context)

capture_modifier_keys(self)

init_overlay(self, event)
register_draw_handler(self, draw_text_callback)

context.window_manager.modal_handler_add(self)

return {'RUNNING_MODAL'}


@classmethod
def poll(cls, context):
if context.mode == 'OBJECT':
return len(context.selected_objects) == 1


def prepare_new_operator(self, context):
self.summoned = False

self.first_axis = 0
self.second_axis = 1
self.first_count = 2
self.second_count = 2
self.first_offset = 2
self.second_offset = 2

self.add_first_array_modifier(context)
self.add_second_array_modifier(context)


def summon_old_operator(self, context, mods):
self.summoned = True

self.first_array = mods[mod_first_array]
self.second_array = mods[mod_second_array]

self.first_count_prev = self.first_count = self.first_array.count
self.second_count_prev = self.second_count = self.second_array.count

first_axis = 0
first_offset = 0
for i in range(3):
offset = self.first_array.relative_offset_displace[i]
if offset != 0:
first_axis = i
first_offset = offset
break

self.first_axis_prev = self.first_axis = first_axis
self.first_offset_prev = self.first_offset = first_offset

second_axis = 0
second_offset = 0
for i in range(3):
offset = self.second_array.relative_offset_displace[i]
if offset != 0:
second_axis = i
second_offset = offset
break

self.second_axis_prev = self.second_axis = second_axis
self.second_offset_prev = self.second_offset = second_offset



def add_first_array_modifier(self, context):
array = context.object.modifiers.new('Square Array A — ND', 'ARRAY')
array.use_relative_offset = True

self.first_array = array


def add_second_array_modifier(self, context):
array = context.object.modifiers.new('Square Array B — ND', 'ARRAY')
array.use_relative_offset = True

self.second_array = array


def operate(self, context):
self.first_array.count = self.first_count
self.first_array.relative_offset_displace = [self.first_offset if i == self.first_axis else 0 for i in range(3)]
self.second_array.count = self.second_count
self.second_array.relative_offset_displace = [self.second_offset if i == self.second_axis else 0 for i in range(3)]

self.dirty = False


def finish(self, context):
unregister_draw_handler()


def revert(self, context):
if not self.summoned:
bpy.ops.object.modifier_remove(modifier=self.first_array.name)
bpy.ops.object.modifier_remove(modifier=self.second_array.name)

if self.summoned:
self.first_array.count = self.first_count_prev
self.first_array.relative_offset_displace = [self.first_offset_prev if i == self.first_axis_prev else 0 for i in range(3)]
self.second_array.count = self.second_count_prev
self.second_array.relative_offset_displace = [self.second_offset_prev if i == self.second_axis_prev else 0 for i in range(3)]

unregister_draw_handler()


def draw_text_callback(self):
draw_header(self)

draw_property(
self,
"A Count: {0} / B Count: {1}".format(self.first_count, self.second_count),
"(±1)",
active=self.key_no_modifiers,
alt_mode=self.key_shift_no_modifiers)

draw_property(
self,
"A Axis: {0} / B Axis: {1}".format(['X', 'Y', 'Z'][self.first_axis], ['X', 'Y', 'Z'][self.second_axis]),
"Alt (X, Y, Z)",
active=self.key_alt,
alt_mode=self.key_shift_alt)

draw_property(
self,
"A Offset: {0:.3f}".format(self.first_offset),
"Ctrl (±{0:.1f}) | Shift + Ctrl (±{1:.1f})".format(self.base_offset_factor * 1000, (self.base_offset_factor / 10) * 1000),
active=self.key_ctrl,
alt_mode=self.key_shift_ctrl)

draw_property(
self,
"B Offset: {0:.3f}".format(self.second_offset),
"Ctrl + Alt (±{0:.1f}) | Shift + Ctrl + Alt (±{1:.1f})".format(self.base_offset_factor * 1000, (self.base_offset_factor / 10) * 1000),
active=self.key_ctrl_alt,
alt_mode=self.key_shift_ctrl_alt)


def menu_func(self, context):
self.layout.operator(ND_OT_square_array.bl_idname, text=ND_OT_square_array.bl_label)


def register():
bpy.utils.register_class(ND_OT_square_array)
bpy.types.VIEW3D_MT_object.append(menu_func)


def unregister():
bpy.utils.unregister_class(ND_OT_square_array)
bpy.types.VIEW3D_MT_object.remove(menu_func)
unregister_draw_handler()

0 comments on commit 14ea44e

Please sign in to comment.