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?
nope, you have read that wrong, Shear creates exactly what Distributeunwantingly does.
do i babble too much in my posts or are you just getting old 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.
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.
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()
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()