Automatically create multiple layout from rectangle

Create multiple layouts automatically from selecting multiple rectangles.

good to knows:

1- All the layouts has to be one size (for example A3) but they can vary in scale depending on the size of the selected rectangle (for example if you use A3 and select a rectangle that’s 42000 x 29700 then a layout will be made with a detail and sets it’s scale to 100, you could have varying rectangles for varying scales but not varying layout sizes)

2- works best when model space and layout space units are set to the same unit (tbh I’ve only tried mm and it works, idk if it works for other units but lmk I’ll fix it for you)

3- You should never run this script more than once, so you should prepare everything then run the command, however if you wanted to-- you have to delete all the layouts then re run the script otherwise it doesn’t work right

4- in the 5th line you can change the layout size (this is the only adjustment you need to make) the number before the comma is the width of the layout and the number after is the height, in my example I’ve used millimetre

A3

x, y = 420, 297

A4

x,y = 297, 210

5- by default all the detail views will be locked, if you don’t want that change True from line 7 to False (or you can just copy the single line below and paste it in line 7 of the script)

LOCK = False

6- order of creating the layouts is based on the rectangle’s center point, the order is from left top to right bottom

7- if you want to change vertical to horizontal or vise versa, switch the numbers places seen 4th point

bigger number first = horizontal / landscape

x, y = 420, 297

smaller number first = vertical / portrait

x, y = 297, 420

hope you find it useful and save precious time with it

if you’d like to improve the script you’re very welcome and please start with fixing bug in 3rd point

#! python3
import Rhino as rh
from math import sqrt

#page size
x, y = 420, 297
#lock detail view?
LOCK = True

def get_objects():
    a = rh.Input.Custom.GetObject()
    a.SetCommandPrompt("Select Page Bounding Rectangles")
    a.SubObjectSelect = False
    a.GroupSelect = True
    a.AcceptNothing(False)
    a.EnablePostSelect(True)
    res = a.GetMultiple(1, 0)
    
    if res == rh.Input.GetResult.Object:
        res = [a.Object(i).Object() for i in range(a.ObjectCount)]
        return res
    else:
        print("No objects selected")

def check_rectangle(list_of_objects):
    rec_list = []
    for i in list_of_objects:
        try:
            crv = i.CurveGeometry
            crv = crv.ToPolyline()
            rec = rh.Geometry.Rectangle3d.CreateFromPolyline(crv)
            rec_list.append(rec)
        except Exception:
            print("Can't turn selected objects into rectangle")
    return rec_list

def sort_list(points, items):
    return [item for _, item in sorted(zip(points, items), key=lambda pair: (-pair[0][1], pair[0][0]))]

objects = get_objects()
rectangles = check_rectangle(objects)
center_points = [i.Center for i in rectangles]

cp = []
for i in center_points:
    cp.append((i.X,i.Y,i.Z))
rectangles = sort_list(cp, rectangles)

b = len(rectangles)
cr1, cr2 = rh.Geometry.Point2d(0,0), rh.Geometry.Point2d(x,y) 
A1 = sqrt(x*y)
A2 = [i.Area for i in rectangles]
scale_factor = [round(sqrt(i)/A1, 0) for i in A2]
doc = rh.RhinoDoc.ActiveDoc
layers = doc.Layers
black = rh.Display.Color4f(0,0,0,1)
black = black.AsSystemColor()
new_layer = layers.Add("LN_DetailViews", black)
layers.SetCurrentLayerIndex(new_layer, True)
NUM = len(doc.Views.GetPageViews())

for i in range(b):
    L = doc.Views.AddPageView("page {}".format(i+1+NUM),x,y)
    D = L.AddDetailView("D_{}".format(i+1+NUM), cr1, cr2, rh.Display.DefinedViewportProjection.Top)
    D.IsActive = True
    VP = D.Viewport
    dd = VP.ZoomBoundingBox(rectangles[i].BoundingBox)
    DV = D.DetailGeometry
    DV.SetScale(scale_factor[i], doc.ModelUnitSystem, 1, doc.PageUnitSystem)
    VP.LockedProjection = True
    DV.IsProjectionLocked = LOCK
    D.CommitChanges()
    D.IsActive = False

doc.Views.Redraw()
1 Like