You've already forked stocker_helper
Migrated from Stocker project
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,44 @@
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
def calculate_global_bounds(selected_objects):
|
||||
if not selected_objects:
|
||||
print("No objects selected.")
|
||||
return None
|
||||
|
||||
min_x = float('inf')
|
||||
min_y = float('inf')
|
||||
min_z = float('inf')
|
||||
max_x = float('-inf')
|
||||
max_y = float('-inf')
|
||||
max_z = float('-inf')
|
||||
|
||||
for obj in selected_objects:
|
||||
if obj.type != 'MESH':
|
||||
continue
|
||||
|
||||
# Transform local bounding box corners to world space
|
||||
matrix = obj.matrix_all_world = obj.matrix_world
|
||||
for corner in obj.bound_box:
|
||||
world_corner = matrix @ mathutils.Vector(corner)
|
||||
|
||||
min_x = min(min_x, world_corner.x)
|
||||
min_y = min(min_y, world_corner.y)
|
||||
min_z = min(min_z, world_corner.z)
|
||||
max_x = max(max_x, world_corner.x)
|
||||
max_y = max(max_y, world_corner.y)
|
||||
max_z = max(max_z, world_corner.z)
|
||||
|
||||
if min_x == float('inf'):
|
||||
print("No valid mesh objects found in selection.")
|
||||
return None
|
||||
|
||||
return {
|
||||
'min': (min_x, min_y, min_z),
|
||||
'max': (max_x, max_y, max_z),
|
||||
'dimensions': (max_x - min_x, max_y - min_y, max_z - min_z),
|
||||
'target_origin': (max_x, min_y, min_z)
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Phase 1: Logic verification script loaded.")
|
||||
@@ -0,0 +1,71 @@
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
def calculate_global_bounds(selected_objects):
|
||||
if not selected_objects:
|
||||
return None
|
||||
|
||||
min_x, min_y, min_z = float('inf'), float('inf'), float('inf')
|
||||
max_x, max_y, max_z = float('-inf'), float('-inf'), float('-inf')
|
||||
|
||||
found_any_mesh = False
|
||||
for obj in selected_objects:
|
||||
if obj.type != 'MESH':
|
||||
continue
|
||||
|
||||
found_any_mesh = True
|
||||
matrix = obj.matrix_world
|
||||
for corner in obj.bound_box:
|
||||
world_corner = matrix @ mathutils.Vector(corner)
|
||||
|
||||
min_x = min(min_x, world_corner.x)
|
||||
min_y = min(min_y, world_corner.y)
|
||||
min_z = min(min_z, world_corner.z)
|
||||
max_x = max(max_x, world_corner.x)
|
||||
max_y = max(max_y, world_corner.y)
|
||||
max_z = max(max_z, world_corner.z)
|
||||
|
||||
if not found_any_mesh:
|
||||
return None
|
||||
|
||||
return {
|
||||
'min': mathutils.Vector((min_x, min_y, min_z)),
|
||||
'max': mathutils.Vector((max_x, max_y, max_z)),
|
||||
'size': mathutils.Vector((max_x - min_x, max_y - min_y, max_z - min_z))
|
||||
}
|
||||
|
||||
def create_aligned_boundary_cube(bounds):
|
||||
if not bounds:
|
||||
return None
|
||||
|
||||
min_v = bounds['min']
|
||||
size = bounds['size']
|
||||
|
||||
mesh = bpy.data.meshes.new("Stocker_Boundary")
|
||||
obj = bpy.data.objects.new("Stocker_Boundary", mesh)
|
||||
bpy.context.collection.objects.link(obj)
|
||||
|
||||
verts = [
|
||||
(0, 0, 0),
|
||||
(size.x, 0, 0),
|
||||
(size.x, -size.y, 0),
|
||||
(0, -size.y, 0),
|
||||
(0, 0, size.z),
|
||||
(size.x, 0, size.z),
|
||||
(size.x, -size.y, size.z),
|
||||
(0, -size.y, size.z)
|
||||
]
|
||||
faces = [
|
||||
(0, 1, 2, 3), (4, 5, 6, 7), (0, 1, 5, 4),
|
||||
(1, 2, 6, 5), (2, 3, 7, 6), (3, 0, 4, 7)
|
||||
]
|
||||
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
mesh.update()
|
||||
|
||||
# Place origin at Back-Left (Min X, Max Y, Min Z)
|
||||
obj.location = (bounds['min'].x, bounds['max'].y, bounds['min'].z)
|
||||
return obj
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Phase 2: Geometry (Origin Alignment) logic loaded.")
|
||||
@@ -0,0 +1,59 @@
|
||||
import bpy
|
||||
|
||||
def setup_stocker_operator(logic_module):
|
||||
"""
|
||||
Wraps the phase 1 & 2 logic into a Blender Operator.
|
||||
"""
|
||||
class STOCKER_OT_setup(bpy.types.Operator):
|
||||
bl_idname = "object.stocker_setup"
|
||||
bl_label = "Stocker Helper"
|
||||
bl_description = "Generate a boundary cube for the selected objects"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
mode: bpy.props.EnumProperty(
|
||||
name="Packing Mode",
|
||||
description="Select the geometry nodes template to apply",
|
||||
items=[
|
||||
('GRID', "Grid Pack", "Use the Grid Pack template"),
|
||||
('CIRCLE', "Circle Pack", "Use the Circle Pack template"),
|
||||
# Placeholder for future modes
|
||||
],
|
||||
default='GRID'
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
# 1. Get selected objects
|
||||
selected_objs = context.selected_objects
|
||||
|
||||
# 2. Run logic from previous phases
|
||||
bounds = logic_module.calculate_global_bounds(selected_objs)
|
||||
if not bounds:
|
||||
self.report({'ERROR'}, "No valid mesh objects selected.")
|
||||
return {'CANCELLED'}
|
||||
|
||||
# 3. Create the cube
|
||||
new_obj = logic_module.create_aligned_boundary_cube(bounds)
|
||||
if not new_obj:
|
||||
self.report({'ERROR'}, "Failed to create boundary cube.")
|
||||
return {'CANCELLED'}
|
||||
|
||||
# 4. Modifier Injection
|
||||
# Search for the node group
|
||||
group_name = "grid_pack" if self.mode == 'GRID' else "Stocker_Circle"
|
||||
node_group = bpy.data.node_groups.get(group_name)
|
||||
|
||||
if not node_group:
|
||||
self.report({'WARNING'}, f"Node Group '{group_name}' not found. Boundary created without modifier.")
|
||||
return {'FINISHED'}
|
||||
|
||||
# Add Geometry Nodes modifier
|
||||
mod = new_obj.modifiers.new(name="circle_pack", type='NODES')
|
||||
mod.node_group = node_group
|
||||
|
||||
self.report({'INFO'}, f"Stocker setup complete using {self.mode} mode.")
|
||||
return {'FINISHED'}
|
||||
|
||||
return STOCKER_OT_setup
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Phase 3: Integration (The Operator) logic loaded.")
|
||||
Reference in New Issue
Block a user