1
$\begingroup$

So what I have is 2 sets of objects distributed roughly into 2 grids, 1 in front of the other. I want to select each of the objects in the back (cubes) and merge them with the object roughly in front of them (spheres). enter image description here

The objects in these arrays all have different geometries (The spheres and cubes are just an example) and their origins are not aligned but the one consistent thing is that their origins are always contained in a 15x15x15 area. e.g the bottom, middle sphere is always between (-7.5, -7.5, -7.5) and (7.5, 7.5, 7.5). I already got a scrip to detect if an object is contained between those coordinates from another unrelated question:

import bpy
from mathutils import Vector

def IsInBoundingVectors(vector_check, vector1, vector2):
    # if vector_check is either bigger or smaller than both other, it does not lie between them
    # in that case it won't be inside the bounding box; hence return false
    for i in range(0, 3):
        if (vector_check[i] < vector1[i] and vector_check[i] < vector2[i]
            or vector_check[i] > vector1[i] and vector_check[i] > vector2[i]):
            return False
    return True

So what I need it to do is some kind of cycle where it selects the first object (bottom, left) in the back and merges it with the object in front (which has the same rough coordinates except the Y is +15 in both cases), and then moves to the next one. When it's done with one row it jumps to the start of the next one.

I assume this is possible if I manually declare 4 variables for the starting set of coordinates and the width and height of the grid, i.e '(-22.5, -22.5, -7.5)', '(-7.5, -7.5, 7.5)', '3' and '3' in this case. But I'm pretty new to python/programing so I have no any idea how to declare the variables and add to them or use them to make the cycle.

Any pointers on how to do this?

I would also want to restrict the script to only the objects on the grids since there will be others in the file. I can put them all in a specifically names collection if it's needed for that or start with them selected

$\endgroup$

1 Answer 1

1
$\begingroup$

You could write some double loop starting from a corner and check which object falls into the current segment in space, but there is a shorter way by snapping the object's locations to the grid layout. Try this script, either choose a collection or select all objects you want to process (swap the # at the objects = ... line):

import bpy

# grid layout in space
cell_offset = (-7.5, -30, -7.5)
cell_size = (15, 60, 15)

# little helper to snap values to a grid
def snap(val, step):
    return (val//step) * step

objects = bpy.data.collections["Collection"].objects # take from a collection
#objects = bpy.context.selected_objects  # or take selected objects

cells = {}
for obj in objects:
    # snap the object's location to the next lower cell corner
    key = (
        snap(obj.location.x - cell_offset[0], cell_size[0]),
        snap(obj.location.y - cell_offset[1], cell_size[1]),
        snap(obj.location.z - cell_offset[2], cell_size[2])
    )
    # group objects with the same cell
    if key not in cells:
        cells[key] = [obj]
    else:
        cells[key].append(obj)

# select all objects in one cell and join them
for value in cells.values():
    bpy.ops.object.select_all(action='DESELECT')
    
    if len(value) > 1: # joining makes sense
        bpy.context.view_layer.objects.active = value[0]
        for obj in value:
            obj.select_set(True)

        bpy.ops.object.join()

Visualization attempt of the script:

enter image description here

With cell_size you define, well, the size of the cells and with cell_offset you can translate the cell grid in space until it fits your assumptions. If you're curious about the y-values $-30$ and $60$, i just made the cells sufficiently long in the y-direction to safely include corresponding objects because from your description this did not seem to be necessary to be partitioned too. The script has the advantage that the grid is infinite and that it can join any amount of objects which belong to the same cell. And you don't need to calculate bounding boxes ;)

$\endgroup$
3
  • $\begingroup$ Thanks for the help. It is merging them properly with the objects in line but the result is that the spheres merge to the cubes instead of the other way around. Is there a way to do the opposite? Putting the cubes in front of the spheres on the Y direction doesn't seem to work and neither does renaming them to something else, so I assume it's doing it based on which object was spawned later (the cubes). I can't really get around that, the cubes grid will always be imported afterwards. $\endgroup$
    – Cornivius
    Commented Sep 11, 2023 at 11:19
  • 1
    $\begingroup$ @Cornivius then you need to change the active object to the one you want before joining. If there are only 2 objects within each cell, it would be just bpy.context.view_layer.objects.active = value[1]. If there are more, you would need to find a way to get the correct one, by comparison with the name or something. $\endgroup$
    – taiyo
    Commented Sep 11, 2023 at 11:25
  • 1
    $\begingroup$ Yeah, there's only ever 2 objects. Works perfectly. Thanks a lot $\endgroup$
    – Cornivius
    Commented Sep 11, 2023 at 11:29

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .