Bug/Wish: Improve Distribute

right now when you try to distribute an array of objects that are organized equaly along one axis and you want to distribute them you go from here

to here, they are in the same plane but arranged diagonally.

idk what that is, it looks like a bug to me.. what i would like is this here below of course

to make this possible i always have to group each row separately which is a huge pain.
any chance that this could be improved or am i doing something wrong?

I might use Shear with two reference lines and the Rigid option…

nope, you have read that wrong, Shear creates exactly what Distribute unwantingly does.
do i babble too much in my posts or are you just getting old :smiley: :wink: well both could be since my posts often are misinterpreted..

Sorry about that, I assumed it was the second image you wanted.

I agree the way it works looks wrong, but when you select all the objects at once, I don’t know how you tell Rhino that you want the objects to stay in the X-oriented columns. All it does really is grab ALL of the objects and determine a sequential Y spacing and apply it to one object after the other in order, it doesn’t understand your organizarion.

For example I ran it on this:

and got this:

The algorithm appears to really only be designed for strict linear distribution, not some two-axis arrangement.

maybe but, an object is actually 2 to 3 dimensional already, so to assess the distances even just for 1d distances the 3 dimensional boundary has to be taken into account already. now if that helps to improve distribute easier idk.

either way, when i have objects already arranged along one axis and i am simply asking distribute to respect that and move each object relative to its input position in 1 dimension nothing else should happen than what i have shown in my 3rd image, definitely not some shameful shearing which makes zero sense and most likely nobody would expect. one could argue if it would make sense to “2d raster” objects according to the selection boundary or just relative to its position additionally.

since Distribute is a real handy tool to quickly rearrange elements without having to break a leg at least in simple cases, i would love to see some fix/improvement for this.

@Gijs if i may tag you, any ideas?

besides the improvement/bug fix here, if arranging objects along 3 axis seems useful to someone additionally, maybe making a new tool instead calling it Arrange.

Not behind a pc right now, I vibe coded this, for distributing the objects in Y direction. I haven’t tested it but reading the code, I think it will work.
It should be not too difficult to extend this to more dimensions.

import rhinoscriptsyntax as rs

def distribute_rows_evenly(objs, tolerance=0.01):
    if not objs: return

    # Get object centers
    data = []
    for obj in objs:
        pt = rs.BoundingBox(obj)
        if not pt: continue
        center = (pt[0] + pt[6]) / 2
        data.append((obj, center))

    # Sort by Y
    data.sort(key=lambda x: x[1].Y)

    # Group into rows (by similar Y values)
    rows = []
    current_row = [data[0]]

    for i in range(1, len(data)):
        prev = data[i-1][1].Y
        curr = data[i][1].Y

        if abs(curr - prev) < tolerance:
            current_row.append(data[i])
        else:
            rows.append(current_row)
            current_row = [data[i]]

    rows.append(current_row)

    # Sort rows by average Y
    rows.sort(key=lambda row: sum(p[1].Y for p in row) / len(row))

    # Get min and max Y
    min_y = min(p[1].Y for p in data)
    max_y = max(p[1].Y for p in data)

    row_count = len(rows)
    if row_count < 2:
        return

    step = (max_y - min_y) / (row_count - 1)

    # Move each row
    for i, row in enumerate(rows):
        target_y = min_y + i * step

        # Current average Y of row
        current_y = sum(p[1].Y for p in row) / len(row)
        delta = target_y - current_y

        for obj, pt in row:
            rs.MoveObject(obj, (0, delta, 0))


def main():
    objs = rs.GetObjects("Select grid objects", preselect=True)
    if not objs: return

    distribute_rows_evenly(objs)

if __name__ == "__main__":
    main()

here’s one that distributes in all directions:

import rhinoscriptsyntax as rs

def distribute_evenly(objs, axis="X", tolerance=0.01):
    if not objs: return

    # Axis index helper
    def get_coord(pt):
        if axis == "X": return pt.X
        if axis == "Y": return pt.Y
        if axis == "Z": return pt.Z

    # Get object centers
    data = []
    for obj in objs:
        bb = rs.BoundingBox(obj)
        if not bb: continue
        center = (bb[0] + bb[6]) / 2
        data.append((obj, center))

    if not data: return

    # Sort along chosen axis
    data.sort(key=lambda x: get_coord(x[1]))

    # Group into bands (rows / columns / layers)
    groups = []
    current_group = [data[0]]

    for i in range(1, len(data)):
        prev = get_coord(data[i-1][1])
        curr = get_coord(data[i][1])

        if abs(curr - prev) < tolerance:
            current_group.append(data[i])
        else:
            groups.append(current_group)
            current_group = [data[i]]

    groups.append(current_group)

    # Sort groups by average axis value
    groups.sort(key=lambda g: sum(get_coord(p[1]) for p in g) / len(g))

    # Get min/max along axis
    min_val = min(get_coord(p[1]) for p in data)
    max_val = max(get_coord(p[1]) for p in data)

    count = len(groups)
    if count < 2:
        return

    step = (max_val - min_val) / (count - 1)

    # Move each group
    for i, group in enumerate(groups):
        target = min_val + i * step
        current = sum(get_coord(p[1]) for p in group) / len(group)
        delta = target - current

        # Move in correct axis direction
        move_vec = {
            "X": (delta, 0, 0),
            "Y": (0, delta, 0),
            "Z": (0, 0, delta)
        }[axis]

        for obj, pt in group:
            rs.MoveObject(obj, move_vec)

def main():
    objs = rs.GetObjects("Select grid objects", preselect=True)
    if not objs: return

    # Passes
    distribute_evenly(objs, "X")  # rows
    distribute_evenly(objs, "Y")  # columns
    distribute_evenly(objs, "Z")  # layers

if __name__ == "__main__":
    main()