I’ve got around 400 objects i need to send to the CNC - Each part is different.
As the CNC program we use can read layer names i would like to set each object to a different layer name. For example in Rhino I will have a main layer folder named - “Slats” then sub folders inside this labelled with the item numbers - “S001,S002,S003” ETC. That way our CNC guys can auto nest parts and then the layer name can also be placed on the part automatically.
I found a script which would label each part up, but only by object name not by layer.
You’re kind of already explaining what you need to do.
So in the script you’ll need to check for the object later instead of the object name. Have a look at objectlayer in rhinoscriptsyntax.
I know little to nothing about rhino python (I would love to learn however)
The below was the script i copied to set object names
import rhinoscriptsyntax as rs
AutoLabel Subroutine
def AutoLabel():
# Select objects
arrObjects = rs.GetObjects(“Select objects to name”)
if not arrObjects: return
# Prompt for a prefix to add to the labels
name = rs.GetString("Prefix for labels, press Enter for none")
if name is None: name = ""
# Prompt for a suffix starting number
suffix = rs.GetInteger("Starting base number to increment",0)
if suffix is None: return
# Prompt for direction
dirPoint1 = rs.GetPoint("Base point for sort direction")
if not dirPoint1: return
dirPoint2 = rs.GetPoint("Pick point for sort direction", dirPoint1)
if not dirPoint2: return
# Initialize collection
collection = []
# Process each seleted object
for obj in arrObjects:
# Process curves
if rs.IsCurve(obj):
# Get the curve starting point
point = rs.CurveStartPoint(obj)
collection.append( (point, obj) )
# Process surfaces
if rs.IsSurface(obj):
# Get the Surface center point
point, error = rs.SurfaceAreaCentroid(obj)
collection.append( (point, obj) )
# Process points
if rs.IsPoint(obj):
# Get the Point Corrdinates point
point = rs.PointCoordinates(obj)
collection.append( (point, obj) )
# TODO: add support for additional object types here
# Determine Direction of sort for each axis
sortDir = dirPoint2 - dirPoint1
# compare function for sorting
def sortcompare(a, b):
pointa, pointb = a[0], b[0]
rc = cmp(pointa.X, pointb.X)
if sortDir.X<0: rc = -1*rc
if rc==0:
rc = cmp(pointa.Y, pointb.Y)
if sortDir.Y<0: rc = -1*rc
if rc==0:
rc = cmp(pointa.Z, pointb.Z)
if sortDir.Z<0: rc = -1*rc
return rc
# sort the collection
collection = sorted(collection, sortcompare)
# Process each item in the collection
for point, item in collection:
#Curves need to have the textdot at thier Midpoint
if rs.IsCurve(item): point = rs.CurveMidPoint(item)
# Add a text dot at the point location
dot = rs.AddTextDot(name + str(suffix), point)
# Set the dot name to the originating object
rs.ObjectName(dot, item)
rs.ObjectName(item, name+str(suffix))
rs.SetUserText(item, "AutoCount::DotUuid", dot)
suffix += 1
selection_to_layers.py (825 Bytes)
This example will ask for the objects and place them in layers starting with ‘S’ (LAYER_PREFIX= ‘S’) and a 3 digit serial like so:
S001
S002
S003
import rhinoscriptsyntax as rs
def objects_to_layers():
LAYER_PREFIX = 'S'
all_ids = rs.GetObjects('select objects to layerize')
if not(all_ids): return
#test for existing layernames
existing = []
for i in range(len(all_ids)):
layer_name = '{}{:03d}'.format(LAYER_PREFIX,i+1)
if rs.IsLayer(layer_name):
existing.append(layer_name)
if existing:
msg = 'STOPPING:\nLayer conflict with existing layers:\n'
msg += '\n'.join(existing)
rs.MessageBox(msg)
return
for i,id in enumerate(all_ids):
layer_name = '{}{:03d}'.format(LAYER_PREFIX,i+1)
rs.AddLayer(layer_name)
rs.ObjectLayer(id, layer_name)
objects_to_layers()
The only issue I’m having is that when i select all 400 Objects it looks like it is assigning the layer name based on the Objects ID. So when i run it they’re not necessarily in the order that i want. I could individually click on each object in the order i want and that seems to work, but that would mean clicking 400 times.
import rhinoscriptsyntax as rs
def proximitysort(ids, seed_id):
class BoxSorter(object):
def __init__(self, id):
self.id = id
bbox= rs.BoundingBox(id)
self.cpt = 0.5 * (bbox[0]+bbox[6])
self.distances = {}
def distance_to(self,other):
return rs.Distance(self.cpt, other.cpt)
#create classed containing id and boundingbox center
box_sorters = [BoxSorter(id) for id in ids]
#store distances in dictionaries
for bs1 in box_sorters:
for bs2 in box_sorters:
if bs1 == bs2 : continue
if bs1.id in bs2.distances: continue
distance = bs1.distance_to(bs2)
bs1.distances[bs2.id] = distance
bs2.distances[bs1.id] = distance
#get seed_box and others
seed_box = [bs for bs in box_sorters if bs.id == seed_id][0]
sorted_boxes = [seed_box.id]
other_boxes = [bs for bs in box_sorters if not bs == seed_box]
while other_boxes:
other_boxes.sort(key= lambda bs : bs.distances[sorted_boxes[-1]])
sorted_boxes.append(other_boxes[0].id)
other_boxes= other_boxes[1:]
return sorted_boxes
def objects_to_layers():
LAYER_PREFIX = 'S'
all_ids = rs.GetObjects('select objects to layerize')
if not(all_ids): return
seed_id = rs.GetObject('Select first object')
if not seed_id: return
if not seed_id in all_ids:
rs.MessageBox('ERROR: first object not in previous selection')
return
all_ids = proximitysort(all_ids, seed_id)
#test for existing layernames
existing = []
for i in range(len(all_ids)):
layer_name = '{}{:03d}'.format(LAYER_PREFIX,i+1)
if rs.IsLayer(layer_name):
existing.append(layer_name)
if existing:
msg = 'STOPPING:\nLayer conflict with existing layers:\n'
msg += '\n'.join(existing)
rs.MessageBox(msg)
return
for i,id in enumerate(all_ids):
layer_name = '{}{:03d}'.format(LAYER_PREFIX,i+1)
rs.AddLayer(layer_name)
rs.ObjectLayer(id, layer_name)
objects_to_layers()
I know I’m probably asking too much and i appreciate people don’t have spare time just to find/create scripts for me.
But just wanted to see if it was possible to find a way to write a text label on the part. (I know this seems backwards as I’ve already got the item name in the layer)
So from having a part on its correct layer, say for example Part No. 40, would i be able to somehow click the part to pull the layer name as text on the part?