Search code examples
c#.netrevit-api

Is there a way to get the minimum BoundingBox of all elements in a View using Revit API?


namespace CropboxUtil
{
    [Transaction(TransactionMode.Manual)]
    internal static class ViewUtility
    {
        public static void CropToBoundingBox(this View view, short clearance = 5)
        {
            Document document = view.Document;
            

            using (var transaction = new Transaction(document))
            {
                transaction.Start("Visual Coordinate Grid: CropToBoundingBox");

                BoundingBoxXYZ elementsBoundingBox = view.GetBoundingBoxOfElements();

                if (elementsBoundingBox != null)
                {
                    view.CropBoxActive = true;
                    view.CropBoxVisible = true;

                    view.CropBox = elementsBoundingBox;
                    transaction.Commit();
                }
                else
                {
                    TaskDialog.Show("Error!", "Não foi possível definir os limites dos elementos!");
                    transaction.RollBack();
                }
            }        
        }

        public static BoundingBoxXYZ GetBoundingBoxOfElements(this View view)
        {
            List<Element> elements = view.GetElements();

            try
            {
                double maxX = elements.Max(e => e.get_BoundingBox(view).Max.X);
                double maxY = elements.Max(e => e.get_BoundingBox(view).Max.Y);
                double maxZ = elements.Max(e => e.get_BoundingBox(view).Max.Z);

                double minX = elements.Min(e => e.get_BoundingBox(view).Min.X);
                double minY = elements.Min(e => e.get_BoundingBox(view).Min.Y);
                double minZ = elements.Min(e => e.get_BoundingBox(view).Min.Z);

                XYZ max = new XYZ(maxX, maxY, maxZ);
                XYZ min = new XYZ(minX, minY, minZ);

                return new BoundingBoxXYZ
                {
                    Max = max,
                    Min = min,
                };
            }
            catch (Autodesk.Revit.Exceptions.ApplicationException)
            {
                throw;
            }
        }

        public static List<Element> GetElements(this View view)
        {
            Document document = view.Document;
            Category sectionCategory = Category.GetCategory(document, BuiltInCategory.OST_Sections);

            return new FilteredElementCollector(document, view.Id)
                                                .WhereElementIsNotElementType()
                                                .WhereElementIsViewIndependent()
                                                .Where(e => e.CanBeHidden(view))
                                                .Where(e => e.Category != sectionCategory)
                                                .Cast<Element>()
                                                .ToList();
        }
    }
}

When I run the code above calling the someViewInstanceObject.CropToBoundingBox() If the crop box is smaller than the view's objects limits it is adjusted properly. But, if the crop box is bigger than view's objects limits it remains unchanged. But it should But it should be shrunken to the smaller possible that contains objects limits. Any thoughts? PS.: The clearance parameter is for future use.


Solution

  • return new FilteredElementCollector(document, view.Id)                                               
               .WhereElementIsNotElementType()                                            
               .WhereElementIsViewIndependent()
               .Where(e => e.CanBeHidden(view))
               .Where(e => e.Category != null) //Avoid null exceptions
               .Where(e => e.Category.CategoryType is CategoryType.Model) //this is the trick
               .Cast<Element>()
               .ToList();
    

    Found that the GetElements method was the one not working properly. The filters I've applied via LINQ was not removing some annotation elements from FilteredElementCollector. So I've tried this new approach: use the CategoryType for filtering only the CategoryType.Model elements. It worked fine. Maybe only this filter alone could've done the job anyway.

    PS.: It's still working funny when called from a 3D View, maybe it needs a Transformation for working properly on 3D Views.