Hi all.
This is the UV Curve of cylinder, which has a 60 mm in length.
In this example, there are three united groups inside the bounding boxes.
These groups need to be distributed at the same distance from each other.
In manual I make calculations as follows:
I use the addition operation, length of each BB group together (10.84 + 13.19 + 20.97 = 45), then the resulting value subtract from the length of UV curve (60 - 45 = 15).
The resulting difference (15) is divided into the number of objects or groups (3) 15/3 = 5
This value (5) will be the same distance between objects.
One of the distances (5) divide into 2. 5/2 = 2.5. The resulting value (2.5) will be the distance from the left and right sides of UV curve.
Don’t forget to calculate the height of the text too, to keep the same aspect ratio, to preserve the design’s look. To cram 19 capital letters into 60mm of length, I’m guessing each must be 2-3mm high, for the result to be legible (from close up).
It’s well worth checking nothing that does this already exists. It’s straightforward to produce a simple script. The padding calculation itself is only 1 line.
Will the text direction always be aligned with the x-axis?
Are the text objects always going to neatly start from the left edge?
Is the user going to select the text objects and containing box, or can the script work on all the text boxes and containers it finds?
It all depends how you’d like to run it too, e.g. from Rhinoscript or Grasshopper.
Hi James -
This is a small part of the jewelry ring, just the area of ​​the words.
As a rule, I make UV Crv. And I use flat elements in the group or separate curves.
It can be letters, markup for precious stones or seamless ornament.
Usually, the UV Crv is located the long side along the X axis.
I’m not interested in vertical distances, it is always handmade.
In the script I would like any objects that are grouped to be perceived as a single object.
There is no difference for me Rhinoscript or Python. But wouldn’t want from Grasshopper.
Maybe this pseudo code helps (not guaranteed error free):
#pseudo code:
words = [<objects>]
total_length = <float>
word_lengths = [<float>]
words_length =0
for wl in word_lengths:
words_length += wl
remainder = total_length-words_length
am = len(words)
increment = remainder / am
pos_x = increment/2
for i, word in enumerate(words):
if i = am-1:
break
word.xpos = pos_x
pox_x += increment
pos_x += word_lengths[i]
Hi Kevin
This is exactly what I’m looking for.
Thank you very much for your support!
Is there a way not to move the elements inside the groups?
The first thing that come to mind is convert groups to blocks and run script.
Is there a way not to violate the order of elements, sort from left to right and align in order?
If there are many elements, it will be very difficult to chose for each individually.
Using blocks, It may not be the best idea.
But I did not find another way to freeze the groups.
It works. I got this code from Mitch. https://discourse.mcneel.com/t/group-to-block
It remains to align in order.
import rhinoscriptsyntax as rs
def onegroup_filt(rhino_object, geometry, component_index):
return rhino_object.GroupCount==1
def ConvertGroupsToBlocks():
msg="Select group of objects to convert to block"
objs=rs.GetObjects(msg,group=True,preselect=True,custom_filter=onegroup_filt)
if not objs: return
group_set=set()
for obj in objs:
obj_g_names=rs.ObjectGroups(obj)
for g_name in obj_g_names:
group_set.add(g_name)
all_obj_groups=list(group_set)
#all_obj_groups should now contain unique list of groups
for group in all_obj_groups:
g_objs=rs.ObjectsByGroup(group)
origin=rs.coerce3dpoint([0,0,0])
block=rs.AddBlock(g_objs,origin,group,delete_input=True)
rs.InsertBlock(block,origin)
ConvertGroupsToBlocks()
def AlignObjs():
frame = rs.GetObjects("Select frame")
frame_bbox = rs.BoundingBox(frame)
frame_left = frame_bbox[0][0]
frame_width = abs(frame_bbox[0][0] - frame_bbox[1][0])
objects = rs.GetObjects("Select objects to align")
bboxes = [rs.BoundingBox(obj) for obj in objects]
widths = [abs(bbox[0][0] - bbox[1][0]) for bbox in bboxes]
x_positions = [bbox[0][0] for bbox in bboxes]
width_sum = sum(widths)
gap = (frame_width - width_sum) / len(objects)
current_x = frame_left + (gap/2)
for i, obj in enumerate(objects):
transform = [current_x - x_positions[i] ,0 ,0]
rs.MoveObject(obj, transform)
current_x += widths[i] + gap
rs.UnselectAllObjects()
AlignObjs()
Hi Mitch! @Helvetosaur
I’ve returned to this script, but I still haven’t been able to complete it on my own.
Would you be able to help if you have some spare time?
There are several groups of objects arranged horizontally in order, and there’s a frame within which they are placed.
Currently, when the script runs, and the groups are selected simultaneously, if equal spacing is found and a new distribution is applied, the groups end up changing their original order.
Could you help assign an ID to each group from left to right, for example, relative to the coordinate axis? The group closest to zero should get ID1, the next one ID2, and so on. Then use these IDs to distribute the groups in order along the given frame.
As for the frame itself, it should be sorted from groups along the longest distance or in a larger area, assigned a separate ID, and used as the path along which the group distribution will take place.
import rhinoscriptsyntax as rs
def onegroup_filt(rhino_object, geometry, component_index):
return rhino_object.GroupCount==1
def ConvertGroupsToBlocks():
msg="Select group of objects to convert to block"
objs=rs.GetObjects(msg,group=True,preselect=True,custom_filter=onegroup_filt)
if not objs: return
group_set=set()
for obj in objs:
obj_g_names=rs.ObjectGroups(obj)
for g_name in obj_g_names:
group_set.add(g_name)
all_obj_groups=list(group_set)
#all_obj_groups should now contain unique list of groups
for group in all_obj_groups:
g_objs=rs.ObjectsByGroup(group)
origin=rs.coerce3dpoint([0,0,0])
block=rs.AddBlock(g_objs,origin,group,delete_input=True)
rs.InsertBlock(block,origin)
ConvertGroupsToBlocks()
def AlignObjs():
objects = rs.GetObjects("Select objects to align")
bboxes = [rs.BoundingBox(obj) for obj in objects]
frame = rs.GetObjects("Select frame")
frame_bbox = rs.BoundingBox(frame)
frame_left = frame_bbox[0][0]
frame_width = abs(frame_bbox[0][0] - frame_bbox[1][0])
widths = [abs(bbox[0][0] - bbox[1][0]) for bbox in bboxes]
x_positions = [bbox[0][0] for bbox in bboxes]
width_sum = sum(widths)
gap = (frame_width - width_sum) / len(objects)
current_x = frame_left + (gap/2)
for i, obj in enumerate(objects):
transform = [current_x - x_positions[i] ,0 ,0]
rs.MoveObject(obj, transform)
current_x += widths[i] + gap
rs.UnselectAllObjects()
AlignObjs()
import scriptcontext as sc
import rhinoscriptsyntax as rs
import Rhino
def AlignObjs():
#SelectOneByOne
msg = "Select cures to sort"
obj_ids = rs.GetObjects(msg, 4096)
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)
for i, rh_obj in enumerate(rh_objs):
bbox = rh_obj.Geometry.GetBoundingBox(True)
rs.SelectObject(rh_obj)
rs.Sleep(500)
#AlignObjs
objects = rs.GetObjects("Select objects to align", preselect=True)
bboxes = [rs.BoundingBox(obj) for obj in objects]
frame = rs.GetObjects("Select frame")
frame_bbox = rs.BoundingBox(frame)
frame_left = frame_bbox[0][0]
frame_width = abs(frame_bbox[0][0] - frame_bbox[1][0])
widths = [abs(bbox[0][0] - bbox[1][0]) for bbox in bboxes]
x_positions = [bbox[0][0] for bbox in bboxes]
width_sum = sum(widths)
gap = (frame_width - width_sum) / len(objects)
current_x = frame_left + (gap/2)
for i, obj in enumerate(objects):
transform = [current_x - x_positions[i] ,0 ,0]
BlockAligng=rs.MoveObject(obj, transform)
current_x += widths[i] + gap
rs.SelectObjects(BlockAligng)
#BlocksToGroups
ids = rs.GetObjects("Select block instances to convert", 4096, preselect=True)
if not ids: return
for id in ids:
rs.UnselectAllObjects()
rs.SelectObject(id)
rs.Command("_ExplodeBlock")
rs.Command("_Group")
rs.UnselectAllObjects()
AlignObjs()
import scriptcontext as sc
import rhinoscriptsyntax as rs
import Rhino
def AlignObjs():
objects = rs.GetObjects("Select objects to align", preselect=True)
bboxes = [rs.BoundingBox(obj) for obj in objects]
frame = rs.GetObjects("Select frame")
frame_bbox = rs.BoundingBox(frame)
frame_left = frame_bbox[0][0]
frame_width = abs(frame_bbox[0][0] - frame_bbox[1][0])
widths = [abs(bbox[0][0] - bbox[1][0]) for bbox in bboxes]
x_positions = [bbox[0][0] for bbox in bboxes]
width_sum = sum(widths)
gap = (frame_width - width_sum) / len(objects)
current_x = frame_left + (gap/2)
for i, obj in enumerate(objects):
transform = [current_x - x_positions[i] ,0 ,0]
BlockAligng=rs.MoveObject(obj, transform)
current_x += widths[i] + gap
rs.SelectObjects(BlockAligng)
#BlocksToGroups
ids = rs.GetObjects("Select block instances to convert", 4096, preselect=True)
if not ids: return
for id in ids:
rs.UnselectAllObjects()
rs.SelectObject(id)
rs.Command("_ExplodeBlock")
rs.Command("_Group")
rs.UnselectAllObjects()
AlignObjs()
The selection works fine – it goes from left to right, one by one.
But when it comes to alignment, things get messed up, like all the objects are being selected at once.
If I select them manually with the mouse one by one, the alignment works just right.
Maybe I’m misunderstanding but are you simply trying to space these objects equally along a curve from the groups/text’s bounding rectangle center (B)?
Alternatively, are you trying to ensure that the space between the groups is what is equal? (A)
This can definitely be solved in Python as it’s just evaluating bounding rectangles and/or text length compared to a curve length or domain. Seems like it’s getting further from your initial concept but I may be misunderstanding the intent of the result
A quick GH mockup below for purpose of asking the questions above…
Hi Michael-
Most of the work has been resolved in Python, but there’s still an issue with object distribution when selecting them with a single mouse click. The objects change positions and fall out of order — for example, the first one might end up in the third spot, and the fourth could move to the first. I’m trying to figure out a solution, but nothing has come to mind yet.
At the moment, I don’t have access to your Grasshopper definition — I’ll be able to take a look tomorrow.
Hi Michael-
I was a bit shy to ask because I wanted to fix it myself, but unfortunately, I still haven’t figured out why your definition isn’t working for me.
When I open the file, I get an error in the “Deconstruct” component: Object reference not set to an instance of an object.
If I replace the “Deconstruct” component with a new one, I get an error right after in the “Curve” componen: Object reference not set to an instance of an object.
Sorry about that, it’s been a LONG time since I’ve actually opened up that component that is attempting to create data trees from groups in the Rhino Model. I actually don’t think I created that but forget who did. I’ll try and take a look
Stupid question but, do you still get the error if you have at least 1 group in your Rhino Model that has a name?
Likely I just need to handle null values in that component and had some oversight on that.
EDIT:
Actually its possible something is broken here… looks like it’s casting oddly or not handling Curves because I can get an error like so:
@kike do see you see this casting failure on your end with the Query Model Objects component output connected to Curve nodes when no Curves are present in the Rhino Model?