I have a very huge model with hundreds of rooms. I want to collect elements that DO NOT belong to (or inside) any room. I have tried extracting room solids and check with each element using ElementIntersectsSolidFilter. I know this is extremely slow process. Here is the code;
public async Task<List<ElementId>> CollectElementsOutsideAsync(Phase phase, IProgress<double> progress)
{
Debris.Clear();
// Step 1: Collect valid rooms (Main Thread)
List<Room> validRooms = new FilteredElementCollector(doc)
.WhereElementIsNotElementType()
.OfCategory(BuiltInCategory.OST_Rooms)
.Cast<Room>()
.Where(r => r.Area > 0 && r.Location != null)
.ToList();
List<Solid> roomSolids = new List<Solid>();
SpatialElementBoundaryOptions sebOptions = new SpatialElementBoundaryOptions
{
SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Center,
StoreFreeBoundaryFaces = true
};
for (int i = 0; i < validRooms.Count; i++)
{
Room room = validRooms[i];
Debug.Print($"Processing Room {i + 1} of {validRooms.Count}");
try
{
SpatialElementGeometryCalculator cal = new SpatialElementGeometryCalculator(doc, sebOptions);
SpatialElementGeometryResults results = cal.CalculateSpatialElementGeometry(room);
Solid roomSolid = results.GetGeometry();
roomSolids.Add(roomSolid);
}
catch
{
try
{
sebOptions.SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Finish;
SpatialElementGeometryCalculator cal = new SpatialElementGeometryCalculator(doc, sebOptions);
SpatialElementGeometryResults results = cal.CalculateSpatialElementGeometry(room);
Solid roomSolid = results.GetGeometry();
roomSolids.Add(roomSolid);
}
catch (Exception e)
{
Debug.Print($"...No solid found!!");
}
}
await Task.Delay(50);
Debug.Print($"...Done");
progress.Report((i + 1) / (double)validRooms.Count * 50); // Report progress for room processing
await Task.Yield(); // Ensure the UI updates the progress bar during the loop
}
Debug.Print($"...Done");
List<ElementId> all3DElements = doc.GetAll3DElements().Select(e => e.Id).ToList();
List<ElementId> elementsInsideRooms = new List<ElementId>();
int totalSolids = roomSolids.Count;
foreach (Solid roomSolid in roomSolids)
{
ElementIntersectsSolidFilter elementIntersectsSolidFilter = new ElementIntersectsSolidFilter(roomSolid);
FilteredElementCollector collector = new FilteredElementCollector(doc, all3DElements);
ICollection<ElementId> elementsInThisRoom = collector.WherePasses(elementIntersectsSolidFilter).ToElementIds();
elementsInsideRooms.AddRange(elementsInThisRoom);
await Task.Delay(50);
progress.Report(75 + (roomSolids.IndexOf(roomSolid) / (double)totalSolids * 25)); // Report progress for filtering elements
await Task.Yield();
}
progress.Report(100); // Report progress for completion
return all3DElements.ToList().Where(e => elementsInsideRooms.Contains(e) == false).ToList();
}
The line;
ElementIntersectsSolidFilter elementIntersectsSolidFilter = new ElementIntersectsSolidFilter(roomSolid);
throws error saying;
System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'
Another question; Is there any better approach than to make Revit do this heavy lift?