For those still looking for it, python code for 3d view from active view, also works for sections.
#### coding: utf8
__title__ = "STR_3D depuis la vue"
__author__ = "Stéphane ROSPARS DUPIN"
__version__ = "Version: 1.0"
__doc__ = """Version = 1.0
Date = 2024.03.26
_____________________________________________________________________
Description:
Passe la vue 3D utilisateur avec une zone de coupe alignée sur la vue active.
_____________________________________________________________________
Mode d'emploi:
_____________________________________________________________________
Last update:
- [024.03.26] - 1.0 Création
_____________________________________________________________________
Contact et support:
- Stéphane ROSPARS DUPIN - AIA ANGERS
_____________________________________________________________________
To-Do:
-> Ø
_____________________________________________________________________"""
#####################################################################
#📚📚📚📚---------------------------------------------BIBLIOTHEQUES:
#####################################################################
from Autodesk.Revit.DB import *
from pyrevit import revit, HOST_APP, script, forms
from Autodesk.Revit.DB import BoundingBoxIntersectsFilter, Outline, FilteredElementCollector
#####################################################################
#*️⃣0️⃣1️⃣2️⃣------------------------------------------------VARIABLES:
#####################################################################
uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document #type:Document
app = __revit__.Application
rvt_year = int(app.VersionNumber)
curview = revit.active_view
selection = revit.get_selection()
#📺----------------------------------------------------------Sorties:
output = script.get_output()
output.close_others() #Ferme la fenêtre de script précédente
#####################################################################
#🧬🧬🧬🧬------------------------------------------------FONCTIONS:
#####################################################################
def is_supported_view(view):
return view.ViewType in [ViewType.FloorPlan, ViewType.EngineeringPlan,ViewType.CeilingPlan, ViewType.Section]
def is_viewplan(view):
return view.ViewType in [ViewType.FloorPlan, ViewType.EngineeringPlan,ViewType.CeilingPlan]
def zoom_to_bbox(view, bbox):
"""Fait un zoom étendu sur une BoundingBox donnée dans une vue donnée."""
outline = Outline(bbox.Min, bbox.Max)
filter_bbox = BoundingBoxIntersectsFilter(outline)
ids_in_box = FilteredElementCollector(doc, view.Id).WherePasses(filter_bbox).ToElementIds()
if ids_in_box:
revit.uidoc.ShowElements(ids_in_box)
def get_crop_box_from_view_range(view):
"""Construit une bounding box orientée à partir de la plage de vue (plans) ou fallback sur la bounding box brute."""
# Récupération de la bounding box actuelle
bbox_view = view.CropBox if view.CropBoxActive else view.get_BoundingBox(None)
if not bbox_view:
return None
transform = bbox_view.Transform # Important pour l'orientation !
# X/Y depuis la bbox (fallback plus stable que CurveLoop)
minX = bbox_view.Min.X
maxX = bbox_view.Max.X
minY = bbox_view.Min.Y
maxY = bbox_view.Max.Y
# Lecture de la plage de vue
view_range = view.GetViewRange()
bottom_id = view_range.GetLevelId(PlanViewPlane.BottomClipPlane)
top_id = view_range.GetLevelId(PlanViewPlane.TopClipPlane)
bottom_level = revit.doc.GetElement(bottom_id) if bottom_id != ElementId.InvalidElementId else None
top_level = revit.doc.GetElement(top_id) if top_id != ElementId.InvalidElementId else None
bottom_offset = view_range.GetOffset(PlanViewPlane.BottomClipPlane)
top_offset = view_range.GetOffset(PlanViewPlane.TopClipPlane)
bottom_elev = bottom_level.ProjectElevation + bottom_offset if bottom_level else bbox_view.Min.Z
top_elev = top_level.ProjectElevation + top_offset if top_level else bbox_view.Max.Z
# Création de la nouvelle bounding box
bbox = BoundingBoxXYZ()
bbox.Min = XYZ(minX, minY, bottom_elev)
bbox.Max = XYZ(maxX, maxY, top_elev)
bbox.Transform = transform
return bbox
# ------------------------------------------
# EXPAND / REORIENT BOUNDINGBOX
# ------------------------------------------
def reorient_bbox_to_world(bbox):
"""Transforme une BoundingBox orientée dans le repère local vers le repère monde."""
local_min = bbox.Min
local_max = bbox.Max
corners_local = [XYZ(x, y, z) for x in [local_min.X, local_max.X]
for y in [local_min.Y, local_max.Y]
for z in [local_min.Z, local_max.Z]]
corners_world = [bbox.Transform.OfPoint(p) for p in corners_local]
min_x = min(p.X for p in corners_world)
min_y = min(p.Y for p in corners_world)
min_z = min(p.Z for p in corners_world)
max_x = max(p.X for p in corners_world)
max_y = max(p.Y for p in corners_world)
max_z = max(p.Z for p in corners_world)
new_bbox = BoundingBoxXYZ()
new_bbox.Min = XYZ(min_x, min_y, min_z)
new_bbox.Max = XYZ(max_x, max_y, max_z)
new_bbox.Transform = Transform.Identity
return new_bbox
def apply_cropbox_to_user3d(cropbox):
user = str(HOST_APP.username)
user_view_name = "{3D - " + user[0] + "." + user[1:] + "}"
collector = FilteredElementCollector(revit.doc).OfCategory(BuiltInCategory.OST_Views)
target_view = next((v for v in collector if v.Name == user_view_name and isinstance(v, View3D)), None)
if not target_view:
user_view_name="{3D}"
target_view=next((v for v in collector if v.Name == user_view_name and isinstance(v, View3D)), None)
elif not target_view:
#🚩🚧🚩-Alerte:
forms.alert("❌ Vue 3D non trouvée❌",
title="Erreur vue utilisateur",
sub_msg="**{}** non trouvée".format(user_view_name),
ok=True,
yes=False,
no=False,
exitscript=True,
warn_icon=False)
return
with Transaction(revit.doc, "Appliquer CropBox") as t:
t.Start()
target_view.SetSectionBox(cropbox)
if target_view.ViewTemplateId != ElementId.InvalidElementId:
target_view.ViewTemplateId = ElementId.InvalidElementId
target_view.SetCategoryHidden(ElementId(BuiltInCategory.OST_Levels), True) # Cache les niveaux dans la vue
target_view.SetCategoryHidden(ElementId(BuiltInCategory.OST_VolumeOfInterest), True) # Cache les zones de définition dans la vue
t.Commit()
revit.uidoc.ActiveView = target_view
revit.uidoc.RefreshActiveView()
def get_crop_box_transformed(view):
"""Retourne la bounding box transformée pour une coupe."""
bbox = view.CropBox
if not bbox or not bbox.Enabled:
return None
# Appliquer la transformation au repère global
transform = bbox.Transform
min_pt = transform.OfPoint(bbox.Min)
max_pt = transform.OfPoint(bbox.Max)
transformed_box = BoundingBoxXYZ()
transformed_box.Min = XYZ(
min(min_pt.X, max_pt.X),
min(min_pt.Y, max_pt.Y),
min(min_pt.Z, max_pt.Z)
)
transformed_box.Max = XYZ(
max(min_pt.X, max_pt.X),
max(min_pt.Y, max_pt.Y),
max(min_pt.Z, max_pt.Z)
)
return transformed_box
#####################################################################
#🚀🚀🚀🚀🚀----------------------------------------------LANCEMENT:
#####################################################################
def main():
view = revit.active_view
if not is_supported_view(view):
#🚩🚧🚩-Alerte:
forms.alert("Type de vue non supportée",
title="Erreur de format",
sub_msg="lancer la commande dans un plan d'étage, de plafond, une vue en plan ou une coupe",
ok=True,
yes=False,
no=False,
exitscript=True,
warn_icon=False)
return
if is_viewplan(view):
bbox = get_crop_box_from_view_range(view)
else:
bbox = get_crop_box_transformed(view)
apply_cropbox_to_user3d(bbox)
main()