How to resize crop view region to assembly elements of Assembly 3D view?

Hello all,

I am trying to understand how Revit works or wants to do with 3D views of Assemblies. It is strange sometimes the crop region of 3D views do not fit to the elements of the assembly and I do not understand the reason and I also I do not know how to do it in case the view does not fit to elements.

I tried this python code from @c.poupin but result looks the same crop region size than before but the centre of the crop region of the view looks like moved to other position.

I also noticed in assembly 3D views options appear something not available in other type of views called “Reset Crop boundary to Model” which is what I want but it seems my 3D view is already fit to the model after I select that option because nothing changes, crop view region keeps identical, so I do not have a clue at all of what to do.

Assembly3dview_weird
the only thing I see I can modify manually is the Crop Region Size after activating and cropping the 3D view, should I do something like resize the Crop Region and try to centre the Crop Region to the Model?
image!

import clr
import sys
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

def get_Global_Middle(all_bbx):
	"""
	# get middle point of global BBX
	"""
	minX = min([bbx.Min.X for bbx in all_bbx])
	minY = min([bbx.Min.Y for bbx in all_bbx])
	minZ = min([bbx.Min.Z for bbx in all_bbx])
	#
	maxX = max([bbx.Max.X for bbx in all_bbx])
	maxY = max([bbx.Max.Y for bbx in all_bbx])
	maxZ = max([bbx.Max.Z for bbx in all_bbx])
	return XYZ((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2 )

toList = lambda x : x if hasattr(x, '__iter__') else [x]
#Preparing input from dynamo to revit
lstElems = toList(UnwrapElement(IN[0]))
view = UnwrapElement(IN[1])
margin = 8
lstTfPts = []
lstBBx = []

#Do some action in a Transaction
TransactionManager.Instance.EnsureInTransaction(doc)
tfView = view.CropBox.Transform.Inverse
for elem in lstElems:
	bbxElemB = elem.get_BoundingBox(view)
	lstBBx.append(bbxElemB)
	pt1 = tfView.OfPoint(bbxElemB.Min)
	pt2 = tfView.OfPoint(bbxElemB.Max)
	pt3 = tfView.OfPoint(XYZ(bbxElemB.Min.X, bbxElemB.Max.Y, 0))
	pt4 = tfView.OfPoint(XYZ(bbxElemB.Max.X, bbxElemB.Min.Y, 0))
	lstTfPts.extend([pt1, pt2, pt3, pt4])
	
# get min max coordinate from transform points
minX = min(lstTfPts, key = lambda p : p.X).X
minY = min(lstTfPts, key = lambda p : p.Y).Y
maxX = max(lstTfPts, key = lambda p : p.X).X
maxY = max(lstTfPts, key = lambda p : p.Y).Y

t = Transform.Identity
t.Origin = get_Global_Middle(lstBBx)
t.BasisX = tfView.BasisX
t.BasisY = tfView.BasisY
t.BasisZ = tfView.BasisZ

newBox = BoundingBoxXYZ()
newBox.Enabled = True
newBox.Min =  XYZ(minX - margin, minY - margin, -10000)
newBox.Max =  XYZ(maxX + margin, maxY + margin, -0.10)
newBox.Transform = t
view.CropBox = newBox

TransactionManager.Instance.TransactionTaskDone()

OUT = view

Hi,

try this version

import clr
import sys
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

def get_Global_Middle(all_bbx):
	"""
	# get middle point of global BBX
	"""
	minX = min([bbx.Min.X for bbx in all_bbx])
	minY = min([bbx.Min.Y for bbx in all_bbx])
	minZ = min([bbx.Min.Z for bbx in all_bbx])
	#
	maxX = max([bbx.Max.X for bbx in all_bbx])
	maxY = max([bbx.Max.Y for bbx in all_bbx])
	maxZ = max([bbx.Max.Z for bbx in all_bbx])
	return XYZ((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2 )

toList = lambda x : x if hasattr(x, '__iter__') else [x]
#Preparing input from dynamo to revit
lstElems = toList(UnwrapElement(IN[0]))
view = UnwrapElement(IN[1])
margin = 8
lstTfPts = []
lstBBx = []
# include sub elems	
lstElems = [[elem] + [doc.GetElement(eId) for eId in elem.GetMemberIds()] for elem in lstElems]
lstElems = sum(lstElems, [])

#Do some action in a Transaction
TransactionManager.Instance.EnsureInTransaction(doc)
tfView = view.CropBox.Transform.Inverse
for elem in lstElems:
	bbxElemB = elem.get_BoundingBox(view)
	lstBBx.append(bbxElemB)
	pt1 = tfView.OfPoint(bbxElemB.Min)
	pt2 = tfView.OfPoint(bbxElemB.Max)
	pt3 = tfView.OfPoint(XYZ(bbxElemB.Min.X, bbxElemB.Max.Y, 0))
	pt4 = tfView.OfPoint(XYZ(bbxElemB.Max.X, bbxElemB.Min.Y, 0))
	lstTfPts.extend([pt1, pt2, pt3, pt4])
	
# get min max coordinate from transform points
minX = min(lstTfPts, key = lambda p : p.X).X
minY = min(lstTfPts, key = lambda p : p.Y).Y
maxX = max(lstTfPts, key = lambda p : p.X).X
maxY = max(lstTfPts, key = lambda p : p.Y).Y

t = Transform.Identity
t.Origin = get_Global_Middle(lstBBx)
t.BasisX = tfView.BasisX
t.BasisY = tfView.BasisY
t.BasisZ = tfView.BasisZ

newBox = BoundingBoxXYZ()
newBox.Enabled = True
newBox.Min =  XYZ(minX - margin, minY - margin, -10000)
newBox.Max =  XYZ(maxX + margin, maxY + margin, -0.10)
newBox.Transform = t
view.CropBox = newBox

TransactionManager.Instance.TransactionTaskDone()

OUT = view
1 Like

many thanks I tried but result is identical than the previous code. Here you see the difference after running the script and after reset the 3d view to Model.

I tried to feed the code just with one element of the assembly and crop region gets resized but view does not fit anyway, too much white space in between.
Assembly3dview_weird

I did a test to get all the elements bounding box and convert to cuboid to visualise their sizes and I am surprised one family generates huge bounding box but the geometry is quite small, it is a pipe fitting. I edited the family and reduced the extension of the reference planes because I read in other post in this forum that it affects to the assembly views crop box but nothing changed Crop Region automatically fit visible elements - #2 by MJB-online. Here a screen shot that shows the size of bounding boxes, I would like to know how to fix that because I think it is the only thing to do.


image

my conclusion is that the view fits to the bounding boxes of the elements in the view, I removed from assembly those weird families with big bounding boxes and it works as expected now, but how can I prevent this happen? how do I know if the bounding boxes of the elements are crazy?

Assembly3dview_weird2

Complex shapes such as your fitting will often have a control vertex for the mirrored part, and/or additional control vertexes well outside where you would expect them (such as we are seeing here). Building the bounding box via another means may be your best bet. Try pulling the edges of the geometry itself as a start.

1 Like

how can I know or fix this? I selected the problematic family that generates huge bounding box and I see that the connector symbol with size appears like far away like around the edge of the bounding box of the element, is there any? I do not know what to fix inside the family as I do not see anything strange, what could cause that?
imageimage

try this update version using the bounding box of geometries (instead of elements)

import clr
import sys
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB

clr.AddReference("RevitNodes")
import Revit

# Import ToProtoType, ToRevitType geometry conversion extension methods
clr.ImportExtensions(Revit.GeometryConversion)

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

def get_Global_Middle(all_bbx):
	"""
	# get middle point of global BBX
	"""
	minX = min(all_bbx, key = lambda p : p.Min.X).Min.X
	minY = min(all_bbx, key = lambda p : p.Min.Y).Min.Y
	minZ = min(all_bbx, key = lambda p : p.Min.Z).Min.Z
	#
	maxX = max(all_bbx, key = lambda p : p.Max.X).Min.X
	maxY = max(all_bbx, key = lambda p : p.Max.Y).Min.Y
	maxZ = max(all_bbx, key = lambda p : p.Max.Z).Min.Z
	return XYZ((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2 )

toList = lambda x : x if hasattr(x, '__iter__') else [x]
#Preparing input from dynamo to revit
lstElems = toList(UnwrapElement(IN[0]))
view = UnwrapElement(IN[1])
margin = 4
opt = Options()
opt.View = view
opt.IncludeNonVisibleObjects = False
lstTfPts = []
lstBBx = []
# include sub elems	
lstElems = [[elem] + [doc.GetElement(eId) for eId in elem.GetMemberIds()] if hasattr(elem, "GetMemberIds") else [elem] for elem in lstElems]
lstElems = sum(lstElems, [])

#Do some action in a Transaction
TransactionManager.Instance.EnsureInTransaction(doc)
tfView = view.CropBox.Transform.Inverse
for elem in lstElems:
	#bbxElemB = elem.get_BoundingBox(view)
	geoSet = elem.get_Geometry(opt)
	if geoSet is not None:
		bbxElemB = geoSet.GetBoundingBox()
		lstBBx.append(bbxElemB)
		# get 8 points of bounding box
		pt1 = tfView.OfPoint(bbxElemB.Min)
		pt2 = tfView.OfPoint(bbxElemB.Max)
		#
		pt3 = tfView.OfPoint(XYZ(bbxElemB.Min.X, bbxElemB.Min.Y, bbxElemB.Max.Z))
		pt4 = tfView.OfPoint(XYZ(bbxElemB.Min.X, bbxElemB.Max.Y, bbxElemB.Max.Z))
		pt5 = tfView.OfPoint(XYZ(bbxElemB.Min.X, bbxElemB.Max.Y, bbxElemB.Min.Z))
		#
		pt6 = tfView.OfPoint(XYZ(bbxElemB.Max.X, bbxElemB.Min.Y, bbxElemB.Min.Z))
		pt7 = tfView.OfPoint(XYZ(bbxElemB.Max.X, bbxElemB.Min.Y, bbxElemB.Max.Z))
		pt8 = tfView.OfPoint(XYZ(bbxElemB.Max.X, bbxElemB.Max.Y, bbxElemB.Min.Z))
		#
		lstTfPts.extend([pt1, pt2, pt3, pt4, pt5, pt6, pt7, pt8])
		geoSet.Dispose()
	
# get min max coordinate from transform points
minX = min(lstTfPts, key = lambda p : p.X).X
minY = min(lstTfPts, key = lambda p : p.Y).Y
maxX = max(lstTfPts, key = lambda p : p.X).X
maxY = max(lstTfPts, key = lambda p : p.Y).Y

t = Transform.Identity
t.Origin = get_Global_Middle(lstBBx)
t.BasisX = tfView.BasisX
t.BasisY = tfView.BasisY
t.BasisZ = tfView.BasisZ

newBox = BoundingBoxXYZ()
newBox.Enabled = True
newBox.Min =  XYZ(minX - margin, minY - margin, -10000)
newBox.Max =  XYZ(maxX + margin, maxY + margin, -0.10)
newBox.Transform = t
view.CropBox = newBox

TransactionManager.Instance.TransactionTaskDone()

OUT = view
5 Likes

hello, I tried it, it works everywhere but not in this case, I do not understand how a Pipe Fitting family can generate a massive bounding box, even selecting the geometry of the family and then creating a bounding box from the geometry @c.poupin ?

I see that when selecting the family, the pipe fitting connector appears separated from it far in a leader line that is like the bounding box. I revised all the family and I do not see any geometry on it with that shape, how the hell know what is going on here? I deleted that family instance and placed again and the bounding box appears as it should…

in the screenshot you see the family and its bounding box


actually the geometry of that weird family of pipe fitting are 3 tiny model lines:

could be this issue generated if the object in the assembly is also within a model Group? I do not know what more to think about this…