Python Sorted by X and after by Y

Hello,
I don’t understand the sort method in python, and i need today to sort geometry with the X value and the Y value.. for example i select circles, and i need that python sort them like this:


i wrote this:
sorted_circles=sorted([(rs.BoundingBox(circle)[0][0],circle) for circle in circles])

but it’s only on X value…

I don’t find how i have to think about it… :exploding_head:

thank you!

Hi @onlyforpeace,

you can sort by multiple arguments either with a comparison function or using a lambda like below. Note that sort does sort an interable in place while sorted would create a sorted copy:

import Rhino
import scriptcontext
import rhinoscriptsyntax as rs

tolerance = scriptcontext.doc.ModelAbsoluteTolerance

def circle_filter(rhino_object, geometry, component_index):
    rc, circle = geometry.TryGetCircle(tolerance)
    return rc

def DoSomething():
    
    msg = "Select circles to sort"
    obj_ids = rs.GetObjects(msg, 4, True, False, False, None, 2, 0, circle_filter)
    if not obj_ids: return
    
    rh_objs = [rs.coercerhinoobject(obj_id, True, True) for obj_id in obj_ids]
    rh_objs.sort(key=lambda c: (c.Geometry.Arc.Center.Y, c.Geometry.Arc.Center.X))
    
    for i, rh_obj in enumerate(rh_objs):
        rs.AddTextDot(i, rh_obj.Geometry.Arc.Center)
    
DoSomething()

Since the origin is at the lower left in Rhino, you would negate the first item in the sort tuple to get what you want, it has the origin in the upper left:

rh_objs.sort(key=lambda c: (-c.Geometry.Arc.Center.Y, c.Geometry.Arc.Center.X))

Above sorts by the x and y coordinate of the circle center. Does this help ?

_
c.

1 Like

Yes, thank you!! that it’s!

but I took circle for example, in fact it can be square, or rectangle, why not spline… so i need to sort by the less x an y value of boundingbox.

so i wrote this:
rh_objs.sort(key=lambda c: (-c.Geometry.PointAtStart.Y, c.Geometry.PointAtStart.X))
but it isn’t the good value.

Hi @onlyforpeace,

using curves instead of circles, you can provide a custom SortFunction to your sort as the key argument like this:

import Rhino
import scriptcontext
import rhinoscriptsyntax as rs

def DoSomething():
    
    msg = "Select cures to sort"
    obj_ids = rs.GetObjects(msg, 4, True, False, False, None, 2)
    if not obj_ids: return
    
    rh_objs = [rs.coercerhinoobject(obj_id, True, True) for obj_id in obj_ids]
    
    def SortFunction(rh_obj):
        bbox = rh_obj.Geometry.GetBoundingBox(True)
        return (-bbox.Center.Y, bbox.Center.X)
    
    rh_objs.sort(key=SortFunction)
    
    for i, rh_obj in enumerate(rh_objs):
        bbox = rh_obj.Geometry.GetBoundingBox(True)
        rs.AddTextDot(i, bbox.Center)
    
DoSomething()

This will sort by the BoundingBox.Center in the WorldXY plane. Note that the SortFunction also returns a tuple where the first item is the negative Y axis and the second item is the positive X axis, so your origin is at the upper left.

To use the lower left of the bounding box you would just change the SortFunction to look like this:

def SortFunction(rh_obj):
    bbox = rh_obj.Geometry.GetBoundingBox(True)
    return (-bbox.Min.Y, bbox.Min.X)

_
c.

1 Like

merci beaucoup!!
it’s what i need!!!

Hi Clement-
Is there a way to select all the sorted objects one by one using this script?
I’ve got a bunch of groups packed into blocks, and I’d like to select them one at a time from left to right, based on their X position – kind of like I’m clicking on each one of them with the mouse, one by one..

import scriptcontext as sc
import rhinoscriptsyntax as rs
import Rhino



def DoSomething():
    
    msg = "Select cures to sort"
    obj_ids = rs.GetObjects(msg, 4096, True, False, False, None, 2)
    if not obj_ids: return
    
    rh_objs = [rs.coercerhinoobject(obj_id, True, True) for obj_id in obj_ids]
    
    def SortFunction(rh_obj):
        bbox = rh_obj.Geometry.GetBoundingBox(True)
        return (-bbox.Center.Y, bbox.Center.X)
    
    rh_objs.sort(key=SortFunction)
    
    for i, rh_obj in enumerate(rh_objs):
        bbox = rh_obj.Geometry.GetBoundingBox(True)
        rs.AddTextDot(i, bbox.Center)
        
        rs.SelectObject(rh_objs) #Yeah, sorta like that#
DoSomething()

Hi @leex, if rh_obj is your sorted object, you could use rs.SelectObject(rh_obj) in that loop. Maybe add a rs.Sleep(500) too in the next line so you can see how it selects eg:

    for i, rh_obj in enumerate(rh_objs):
        bbox = rh_obj.Geometry.GetBoundingBox(True)
        rs.AddTextDot(i, bbox.Center)
        rs.SelectObject(rh_obj)
        rs.Sleep(500)

_
c.

1 Like

Sweet! Big thanks!

Clement, would you mind helping me out with something on a different topic? I’d rather not bring it up here, just to keep the thread clean.