We held a DevLab in Gothenburg yesterday with lots of interesting things to discuss with our Scandinavian developer guests. Today we are in Munich, Germany. So is Kean :-)
One of the topics that came up yesterday is a thing I have been wanting to write about for over a year now and never gotten around to, the use of the AVF or Analysis Visualisation Framework to display temporary geometry to the user.
I used it myself to display a live webcam image on a building element, and Saikat used it in the AVF part of his structural AVF and DMU sample, although I only documented the DMU or Dynamic Model Update aspect of it in detail.
This kind of use of the AVF is also demonstrated by the Revit SDK sample GeometryCreation_BooleanOperation. It creates geometry solids using the GeometryCreationUtils and BooleanOperationUtils classes and displays them in a view using AVF.
Transient AVF visualisation was used very impressively in the presentation of new geometry functionality at the developer conferences last year. You may and should be aware of the FindColumns example of using the FindReferencesByDirection method to determine collisions between walls and columns in a situation like this:
As I mentioned in the DevDays 2010 Online with Revit 2012 API news, it was converted to use the exact wall and column geometry instead of the original approximate ray tracing approach, and the results are displayed exactly by hiding the wall and column categories in the view and displaying their intersection solids using AVF instead.
It defines two commands:
Here is the original 3D view of the situation:
Running HighlightIntersectionsCommand performs the following steps:
Here is the list of the resulting intersections:
This is what the intersection geometry looks like in AVF:
As said, you can call RestoreViewCommand afterwards to restore the view by resetting the wall and column category visibility in the view.
For your reference, here is Geometry2012.zip containing the C# sample code and Visual Studio solution together the suitable sample model to run it in.
So all of that is really important stuff from last year that I have been dying to publish for such a long time and never gotten around to. Finally!
I passed the geometry sample on to Betina Mette Zimmermann of NTI CAD Center A/S in Denmark, and she converted it to VB and modified it to highlight the room geometry returned by the Room.ClosedShell method instead of wall and column intersections within half an hour:
#Region "Namespaces" Imports System Imports System.Collections.Generic Imports Autodesk.Revit.ApplicationServices Imports Autodesk.Revit.Attributes Imports Autodesk.Revit.DB Imports Autodesk.Revit.DB.Analysis Imports Autodesk.Revit.DB.Architecture Imports Autodesk.Revit.UI Imports Autodesk.Revit.UI.Selection #End Region <Transaction(TransactionMode.Manual)> Public Class Command Implements IExternalCommand Private schemaId As Integer = -1 Public Function Execute( ByVal commandData As ExternalCommandData, ByRef message As String, ByVal elements As ElementSet) _ As Result Implements IExternalCommand.Execute Dim uidoc As UIDocument = commandData.Application.ActiveUIDocument Dim doc As Document = uidoc.Document Dim transaction As Transaction = New Transaction(doc) Try Dim col As FilteredElementCollector _ = New FilteredElementCollector(uidoc.Document) col.WhereElementIsNotElementType.ToElements() col.OfCategory(BuiltInCategory.OST_Rooms) Dim value As Double = 1.0 For Each room As Room In col Dim geo As GeometryElement = room.ClosedShell() Dim solid As Solid = GetGeometry(geo) If Not solid Is Nothing Then PaintSolid(doc, solid, value) End If value = value + 1 Next transaction.Start("Hide Walls") Dim categories As Categories _ = uidoc.Document.Settings.Categories SetCategoryInvisible( _ categories, BuiltInCategory.OST_Walls, uidoc.ActiveView) transaction.Commit() Return Result.Succeeded Catch ex As Exception message = ex.Message Return Result.Failed End Try End Function Public Shared Sub SetCategoryInvisible( _ ByVal categories As Categories, _ ByVal bic As BuiltInCategory, _ ByVal view As View) SetCategoryVisibility(categories, bic, view, False) End Sub Private Shared Sub SetCategoryVisibility( _ ByVal categories As Categories, _ ByVal bic As BuiltInCategory, _ ByVal view As View, _ ByVal visible As Boolean) Dim category As Category = categories.Item(bic) category.Visible(view) = visible End Sub Public Function GetGeometry( _ ByVal geomElem As GeometryElement) As Solid For Each geomObj As GeometryObject In geomElem.Objects ' Walls and some columns will have a solid ' directly in its geometry If TypeOf geomObj Is Solid Then Dim solid As Solid = DirectCast(geomObj, Solid) If solid.Volume > 0 Then Return solid End If End If ' Some columns will have a instance ' pointing to symbol geometry If TypeOf geomObj Is GeometryInstance Then Dim geomInst As GeometryInstance _ = DirectCast(geomObj, GeometryInstance) ' Instance geometry is obtained so that the ' intersection works as expected without ' requiring transformation Dim instElem As GeometryElement _ = geomInst.GetInstanceGeometry() For Each instObj As GeometryObject In instElem.Objects If TypeOf instObj Is Solid Then Dim solid As Solid = DirectCast(instObj, Solid) If solid.Volume > 0 Then Return solid End If End If Next End If Next Return Nothing End Function Private Sub PaintSolid( _ ByVal doc As Document, _ ByVal s As Solid, _ ByVal value As Double) Dim app As Application = doc.Application Dim view As View = doc.ActiveView If view.AnalysisDisplayStyleId _ = ElementId.InvalidElementId Then CreateAVFDisplayStyle(doc, view) End If Dim sfm As SpatialFieldManager _ = SpatialFieldManager.GetSpatialFieldManager(view) If sfm Is Nothing Then sfm = SpatialFieldManager _ .CreateSpatialFieldManager(view, 1) End If If schemaId <> -1 Then Dim results As IList(Of Integer) _ = sfm.GetRegisteredResults() If Not results.Contains(schemaId) Then schemaId = -1 End If End If If schemaId = -1 Then Dim resultSchema1 As New AnalysisResultSchema( _ "PaintedSolid", "Description") schemaId = sfm.RegisterResult(resultSchema1) End If Dim faces As FaceArray = s.Faces Dim trf As Transform = Transform.Identity For Each face As Face In faces Dim idx As Integer _ = sfm.AddSpatialFieldPrimitive(face, trf) Dim uvPts As IList(Of UV) = New List(Of UV)() Dim doubleList As New List(Of Double)() Dim valList As IList(Of ValueAtPoint) _ = New List(Of ValueAtPoint)() Dim bb As BoundingBoxUV = face.GetBoundingBox() uvPts.Add(bb.Min) doubleList.Add(value) valList.Add(New ValueAtPoint(doubleList)) Dim pnts As New FieldDomainPointsByUV(uvPts) Dim vals As New FieldValues(valList) sfm.UpdateSpatialFieldPrimitive( _ idx, pnts, vals, schemaId) Next End Sub Private Sub CreateAVFDisplayStyle( _ ByVal doc As Document, _ ByVal view As View) Dim t As New Transaction(doc) t.Start("Create AVF Style") Dim coloredSurfaceSettings As _ New AnalysisDisplayColoredSurfaceSettings() coloredSurfaceSettings.ShowGridLines = True Dim colorSettings As _ New AnalysisDisplayColorSettings() Dim legendSettings As _ New AnalysisDisplayLegendSettings() legendSettings.ShowLegend = False Dim analysisDisplayStyle As AnalysisDisplayStyle _ = analysisDisplayStyle.CreateAnalysisDisplayStyle( _ doc, "Paint Solid", coloredSurfaceSettings, _ colorSettings, legendSettings) view.AnalysisDisplayStyleId = analysisDisplayStyle.Id t.Commit() End Sub End Class
To test this, I created the following Revit model of a cow hotel, implemented according to a design suggested by Jim Quanci in the Gothenburg airport:
Here are the resulting highlighted rooms using AVF:
Here is HighlightRooms.zip containing the VB code and complete Visual Studio solution for this command.
Many thanks to Betina for implementing and sharing this!