Offset View CropBox of Selected Viewports


# ╦╔╦╗╔═╗╔═╗╦═╗╔╦╗╔═╗
# ║║║║╠═╝║ ║╠╦╝ ║ ╚═╗
# ╩╩ ╩╩  ╚═╝╩╚═ ╩ ╚═╝ IMPORTS
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
from Autodesk.Revit.DB import  *

# pyRevit IMPORTS
from pyrevit.forms import alert

# ╦  ╦╔═╗╦═╗╦╔═╗╔╗ ╦  ╔═╗╔═╗
# ╚╗╔╝╠═╣╠╦╝║╠═╣╠╩╗║  ║╣ ╚═╗
#  ╚╝ ╩ ╩╩╚═╩╩ ╩╚═╝╩═╝╚═╝╚═╝ VARIABLES
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
uidoc   = __revit__.ActiveUIDocument
app     = __revit__.Application
doc     = __revit__.ActiveUIDocument.Document
rvt_year = int(app.VersionNumber)

# ╔═╗╦ ╦╔╗╔╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
# ╠╣ ║ ║║║║║   ║ ║║ ║║║║╚═╗
# ╚  ╚═╝╝╚╝╚═╝ ╩ ╩╚═╝╝╚╝╚═╝ Functions
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
def convert_cm_to_internal(length):
    """Function to convert cm to internal units."""
    # RVT >= 2022
    if rvt_year < 2022:
        from Autodesk.Revit.DB import DisplayUnitType
        return UnitUtils.Convert(length,
                                DisplayUnitType.DUT_CENTIMETERS,
                                DisplayUnitType.DUT_DECIMAL_FEET)
    # RVT >= 2022
    else:
        from Autodesk.Revit.DB import UnitTypeId
        return UnitUtils.ConvertToInternalUnits(length, UnitTypeId.Centimeters)

def get_selected_elements():
    """Property that retrieves selected views or promt user to select some from the dialog box."""
    # GET SELECTED ELEMENTS IN UI
    selected_elements = [doc.GetElement(el_id) for el_id in uidoc.Selection.GetElementIds()]
    return selected_elements

# ╔═╗╔═╗╔╗╔╔╦╗╦═╗╔═╗╦  ╔═╗
# ║  ║ ║║║║ ║ ╠╦╝║ ║║  ╚═╗
# ╚═╝╚═╝╝╚╝ ╩ ╩╚═╚═╝╩═╝╚═╝ CONTROLS
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
__controls__ = """
ADJUST THESE VALUES AS YOU WANT. 
You can use Positive, Negative and Zero values"""
TOP    = convert_cm_to_internal(0)
BOTTOM = convert_cm_to_internal(-10)
RIGHT  = convert_cm_to_internal(0)
LEFT   = convert_cm_to_internal(0)


# ╔╦╗╔═╗╦╔╗╔
# ║║║╠═╣║║║║
# ╩ ╩╩ ╩╩╝╚╝ MAIN
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
if __name__ == '__main__':
    # GET SELECTED VIEWPORTS
    selected           = get_selected_elements()
    selected_viewports = [i for i in selected if type(i) == Viewport]

    # VERIFY THAT VIEWPORTS WERE SELECTED
    if not selected_viewports:
        alert("No ViewPorts were selected.\nPlease, try again.", exitscript=True)


    # START TRANSACTION
    with Transaction(doc,__title__) as t:
        t.Start()

        # LOOP THROUGH SELECTED VIEWPORTS
        for vp in selected_viewports:
            view.CropBoxActive = True #FIXME This might give an error if View has ScopeBox

            # GET VIEW CROPBOX
            view_id = vp.ViewId
            view    = doc.GetElement(view_id)
            view_bb = view.CropBox

            # CREATE NEW BOUNDING BOX
            BB = BoundingBoxXYZ()
            BB.Min = XYZ(view_bb.Min.X + LEFT   , view_bb.Min.Y + BOTTOM   , view_bb.Min.Z)
            BB.Max = XYZ(view_bb.Max.X + RIGHT  , view_bb.Max.Y + TOP      , view_bb.Max.Z)

            # APPLY NEW BOUNDING BOX
            view.CropBox = BB

        t.Commit()

⌨️ Happy Coding!
Erik Frits