Monday, February 28, 2011

The Rest of... Convert Rooms to 3D Masses

I got a little lazy and preoccupied yesterday so cut the post short on you, but you have to admit the suspense was pretty exciting! As I promised in yesterday's post on Convert Rooms to 3D Masses, this post will be the main meat of the whole utility.

My Test model contains five rooms boundarized by most of the common conditions that exist in a real design model. Some are partially surrounded by room separation lines, one entirely surrounded by room separation lines, one with a curved wall, etc.


Text first thing we need is a complete list collection of the rooms in our model. The code below does this for us and puts the list of room elements into m_Rooms:


' Get the list of rooms
Dim m_Collector As New FilteredElementCollector(m_Doc)
m_Collector.OfCategory(BuiltInCategory.OST_Rooms)
Dim m_Rooms As New List(Of Element)
m_Rooms = m_Collector.ToElements

Now we can start iterating our collection of rooms in a For Each loop with the beginning of that shown here:


' Iterate the list and gather a list of boundaries
        For Each x As Architecture.Room In m_Rooms

This next group of code shows how we will avoid working on unplaced rooms by making sure the Area is larger than 1. The second portion shows how we start the stop watch object used to quantify the time that it takes to generate a mass from each room. Then the path to our specialty equipment family template and the saveas function is called to give the family a name. Make sure that the directory being called already exists for this sample to work.


' Avoid unplaced rooms
            If x.Area < 1 Then Continue For

            ' Stopwatcher - This comes FIRST
            Dim m_StopWatch As New Stopwatch
            m_StopWatch.Reset()
            m_StopWatch.Start()

            ' This is the path to our family template
            m_FamDoc = m_App.NewFamilyDocument("C:\Documents and Settings\All Users\Application Data\" & _
                                               "Autodesk\RAC 2011\Imperial Templates\Specialty Equipment.rft")

            ' The "C:\My Families\" directory needs to exist first obviously
            m_FamDoc.SaveAs("C:\My Families\" & x.UniqueId.ToString & ".rfa")

The next snip shows where we create a transaction for both the main model as well as the family document


' Start a new Model Transaction
            Dim m_Trans As New Transaction(m_Doc, "My Rooms to Masses - By Boundary")
            m_Trans.Start()

            ' Start a new Family Transaction
            Dim m_TransFam As New Transaction(m_FamDoc, "Transaction in Family Document")
            m_TransFam.Start()

Now in this next portion, we will create a new subcategory named using the department of the room and material named equally that we will apply to the extrusion that we build the room with. This is handy to display the masses in color related to their department assignment.


' Get the department name
            Dim m_para As New clsPara(x.Parameter("Department"))
            Dim m_SubCatName As String = "My_Rooms"
            If m_para.Value <> "" Then m_SubCatName = "My_Rooms_" & m_para.Value
            ' Subcategory named by Department
            Dim m_Subcategory As Category = Nothing
            Try
                ' Try to create the subcategory if it does not exist
                m_Subcategory = m_FamDoc.Settings.Categories.NewSubcategory(m_SECategory, m_SubCatName)
            Catch ex As Exception
                ' Get the subcategory object since it exists already
                Dim m_NameMap As CategoryNameMap = m_SECategory.SubCategories
                For Each x1 As Category In m_NameMap
                    If x1.Name = m_SubCatName Then
                        m_Subcategory = x1
                        Exit For
                    End If
                Next
            End Try
            ' Material named by Department
            Dim m_Material As Material = Nothing
            Try
                ' Try to create the material if it does not exist
                m_Material = m_FamDoc.Settings.Materials.AddWood(m_SubCatName)
            Catch ex As Exception
                ' Get the material object since it exists already
                m_Material = m_FamDoc.Settings.Materials.Item(m_SubCatName)
            End Try
            ' Apply the material to the subcategory
            m_Subcategory.Material = m_Material

Now for the part where we get the room boundary into a curve array that we will eventually use to extrude as a form representing a room element. The snip below iterates through the room boundary objects returning a CurveArray which just so happens to be the second to last process to get us to out required argument that we need to generate the 3D form, CurveArrArray!


' Get the room boundary
            Dim m_Boundary As Architecture.BoundarySegmentArrayArray = x.Boundary
            If m_Boundary Is Nothing Then Continue For
            ' The array of boundary curves
            Dim m_CurveArray As New CurveArray
            ' Iterate to gather the curve objects
            For i = 0 To m_Boundary.Size - 1
                ' Boundary segments array
                Dim m_SegAray As Architecture.BoundarySegmentArray = m_Boundary.Item(i)
                ' Segments Array
                For ii = 0 To m_SegAray.Size - 1
                    Dim m_Seg As Architecture.BoundarySegment = m_SegAray.Item(ii)
                    ' Add the segment curve to the array
                    m_CurveArray.Append(m_Seg.Curve)
                Next
            Next

The snip below shows how we build up the workplane that is required to extrude the form from in our family template. We then append the CurveArray to the final argument required to extrude our form... CurveArrArray:


' Simple insertion point
            Dim pt1 As New XYZ(0, 0, 0)
            ' Our normal point that points the extrusion directly up
            Dim ptNormal As New XYZ(0, 0, 1)
            ' The plane to extrude the mass from
            Dim m_Plane As Plane = m_AppCreate.NewPlane(ptNormal, pt1)
            Dim m_SketchPlane As SketchPlane = m_FamDoc.FamilyCreate.NewSketchPlane(m_Plane)
            ' Need to add the CurveArray to the final requirement to generate the form
            Dim m_CurveArArray As New CurveArrArray
            m_CurveArArray.Append(m_CurveArray)

Now we can generate the form and add the extrusion to out subcategory representing the department:


' Extrude the form
            Dim m_Extrusion As Extrusion = m_FamDoc.FamilyCreate.NewExtrusion(True, m_CurveArArray, m_SketchPlane, 8)
            Try
                m_Extrusion.Subcategory = m_Subcategory
            Catch ex As Exception

            End Try

The next snip shows how we load the family into the project and place it into the model at 0,0,0 so our coordinates used to generate the form will line up right where the room needs to be:


' Commit the Family Transaction
            m_TransFam.Commit()
            ' Load the Family into the Model
            Dim m_NewFamily As Family = m_FamDoc.LoadFamily(m_Doc)
            ' Create a reference to the latest family (we just created it)
            Dim m_FamilySymbolSetIterator As FamilySymbolSetIterator = m_NewFamily.Symbols.ForwardIterator()
            m_FamilySymbolSetIterator.MoveNext()
            Dim m_FamSymbol As FamilySymbol = TryCast(m_FamilySymbolSetIterator.Current, FamilySymbol)
            ' Place the Family at 0,0,0 since we used the same coordinates as the rooms to generate
            Dim m_FamilyInstance As FamilyInstance = m_Doc.Create.NewFamilyInstance(New XYZ(0, 0, 0), m_FamSymbol, [Structure].StructuralType.NonStructural)

So that's about it.... now we just need to cleanup and close the stopwatch objects and report the timing to the user:


' Commit the Model Transaction
            m_Trans.Commit()
            ' Elapsed Time Per Element
            m_StopWatch.Stop()
            ' Report the elapsed time
            MsgBox(m_StopWatch.Elapsed.TotalSeconds.ToString & " Seconds!", MsgBoxStyle.Information, "Elapsed Time!")

Now check out the results....!!! A fully 3D schedulable room mass (as a specialty equipment form)...

Sunday, February 27, 2011

Convert Rooms to 3D Masses

Well, it has been quite a while since I shared something pertinent to anytime of code or .NET. I've been busy with less exciting things and haven't really had the time, but that's about to change!

This post will touch on how to solve a common problem where designers want to see how a room looks in full 3D while in the programming (Room and Area Programming) stages to help figure out the relationships from one room to others. This is difficult to do with traditional rooms that Revit creates, mainly because they aren't real physical objects (and you cannot see the damn things in 3D).

Here is what my test model looks like with five simple rooms placed in. One surrounded entirely by room separation lines, one with curved walls, and some others for verification and proof that the tool will actually work in most conditions.


Create a new .NET 3.5 class project in Visual Studio 2010. You'll need to build in the Command class on your own this time... Add a form object and name it Form_Main. Add the following referenced to your project and import their name spaces as shown below:


Imports System
Imports System.Collections.Generic
Imports System.Diagnostics
Imports Autodesk.Revit.DB
Imports Autodesk.Revit.UI
Imports Autodesk.Revit.ApplicationServices
Imports Autodesk.Revit.Creation

Add two buttons to your form named "ButtonGenerateMasses" and "ButtonCancel"... their suggested placements are shown here:



Now we need to focus on what all variables we need to expose to our form class and how they will be used. Add the following private variables to this form class just beneath the class declaration:


Private m_CmdData As ExternalCommandData
    Private m_Doc As Autodesk.Revit.DB.Document
    Private m_FamDoc As Autodesk.Revit.DB.Document
    Private m_App As Autodesk.Revit.ApplicationServices.Application
    Private m_SECategory As Category
    Private m_AppCreate As Autodesk.Revit.Creation.Application

As you can see in the listing above, our variable requirements are actually quite simple. We're mainly concerned with document and application objects that are required to generate families in Revit.

The next item we'll code in is the base class constructor. Now keep in mind that one argument is required to generate an instance of this class, IExternalCommand. This argument is the same as the one required in the IExternalCommand's Execute function. The Constructor is shown here:


''' <summary>
    ''' General Class Constructor
    ''' </summary>
    ''' <param name="settings"></param>
    ''' <remarks></remarks>
    Public Sub New(ByVal settings As ExternalCommandData)
        ' Always call this when using a constructor for a form object
        InitializeComponent()
        ' Settings reference to UI and DB objects
        m_CmdData = settings
        ' Application and Document References
        m_App = m_CmdData.Application.Application
        m_Doc = m_CmdData.Application.ActiveUIDocument.Document
        ' Mass Category
        m_SECategory = m_Doc.Settings.Categories.Item(BuiltInCategory.OST_SpecialityEquipment)
        ' App creator
        m_AppCreate = m_App.Create
        ' Set the form title
        Me.Text = "Rooms to Masses"""
    End Sub

Now that we have all of the main framework ready to go, we can dig down into how the families are built using data from the room elements in the model. Create a new subroutine named GenerateMasses. This subroutine is where all the magic happens.


''' <summary>
    ''' Generate 3D Specialty Equipment Extrusions for Rooms
    ''' Specialty Equipment is Scheduleable
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub GenerateMasses()

    End Sub

Before we get back into filling in the functionality in this subroutine, let's get the button assignments in our form out of the way. Double click each of the buttons in the form designer to autogenerate their click event functions and add the following very simple code to these functions:


''' <summary>
    ''' Close the App
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub ButtonCancel_Click(ByVal sender As System.Object, _
                                   ByVal e As System.EventArgs) _
                               Handles ButtonCancel.Click
        Me.Close()
    End Sub

    ''' <summary>
    ''' Launch GenerateMasses
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub ButtonGenerateMasses_Click(ByVal sender As System.Object, _
                                           ByVal e As System.EventArgs) _
                                       Handles ButtonGenerateMasses.Click
        GenerateMasses()
    End Sub

Now let's focus the rest of this post on what's missing!.... haha I know, you'll have to wait until tomorrow to see the ending. So stay tuned and come back tomorrow for the meat of this idea finalized into true usable code! Tomorrow's post will be on the completion of the GenerateMasses subroutine.

Friday, February 18, 2011

Top 10 Reasons Chuck Norris Would be a Great BIM Manager

I realize that I haven't posted any API stuff lately. I've been tied up with the BETA's as well as writing the API chapter for the next installment of "Mastering Autodesk Revit Architecture 2012"... Look for an essential post later this month on converting rooms to masses without tessellation (super fast).

It's amazing how much of a BIM Manager's time is taken up daily to cater to the occasional user's laziness or lack of fear. If BIM Manager's were more like Chuck Norris, our jobs would be amazingly more efficient and productive [wink]... So here are the top ten reasons on why I think Chuck Norris would be an excellent BIM Manager:


10.) User's would never miss a deadline for fear that they might have to become victim to his wrath

9.) Plotter's would never fail or run out of paper where Chuck Norris is BIM Manager

8.) Getting user's to pay attention in training sessions would never be easier

7.) Why?... Chuck Norris, that's why!

6.) Chuck Norris doesn't miss deadlines because they work only within HIS schedule!

5.) Chuck Norris could improve the functionality of Revit simply by starring it down

4.) All constraints would always be satisfied or die a painful round-house death

3.) No Revit model under Chuck Norris' responsibility would ever think of containing an error or perform slowly

2.) Chuck Norris would tell families to build themselves, and they would

1.) Not even Revit would dare give warnings to Chuck Norris

Tuesday, February 15, 2011

Best Idea for a Building... EVER

This post requires very little explanation, Pie day is next month!

Now this is Architectin'

Sunday, January 30, 2011

Pientines Day!

I know most people relate falling in love with February 14th. ...being the geek at heart that I am, I think this holiday should be relocated to March 14...
That's right, 3/14.. or 3.14!! It's only one month exactly from the ever so improperly commercialized love holiday of 2.14!!!



Write your senators and let's get this taken care of once and for all!!! We all owe our livelihoods to simple arithmetic whether we know it or not and should celebrate our love of understanding it!!

And ladies... if you feel the same way, let's talk!!

Tuesday, January 25, 2011

Top 10 Excuses All BIM Managers Hate

Do you have any users that refuse to follow the modernized BIM way of doing things and insist on using their outdated "proven" 2D garbage ways of doing things, yet complain about how the whole BIM craze is oversold and not worth their trouble? Do you remember the excuses they offer?... Do you think they are as funny as I do?...

Hopefully after reading this post you will stop wanting to stuff your head in a garbage disposal and dumping a gallon of boiling bleach in your wounds:


10. ) "If Revit can't handle an exploded 3D AutoCAD mesh of my custom imported roofing structure, then we need to find a better software to help us get our $hit done!!!" [slamming fists on table and storming out of the room]

9. ) "What is wrong with having a pile of elements off to the side of my building on each of the 63 levels of my model? It makes it easier for me to grab and place them where I need them in my design.... I didn't know that would upset the construction bid consultant"

8. ) "I turn my model background black so I can see my yellow walls better..."
7. ) Consultant: "I couldn't print my sheets because I didn't know where your firm's standard pen table was..."

6. ) "Drafting elevations in AutoCAD is so much easier... how are you supposed to reference in the plan view so you know where everything lines up in elevation?... I would have been done days ago in AutoCAD, sorry."

5. ) "I don't Sync to Central because every time I do, all this other crap pops up in my drawing that get in the way..."

4. ) "I was cleaning the rooms out of my schedule to display the rooms only on level 4 and now they are all gone in the model... I didn't know that would delete them from the model"
3. ) "If I knew you wanted to use Navisworks on this project, I would have not built all 127 lighting fixture families as 2D... DAMMIT... tell me next time!!!!"



2. ) "Drafted my door schedule in lines and text because I didn't know how else to get my drafted doors to show up in the schedule..."



And the number one excuse I hear that never fails to cheer me up....

1. ) "If this were anything like AutoCAD, I'd be done with my plan views and ready to start manually drafting my sections, elevations, and details... this crap is SLOW!"

Wednesday, January 19, 2011

Batch Family Fun for Everyone!!! Part 1

I bet you've been waking up recently in the middle of the night dreaming about how you could jockey the Revit API to batch process families, haven't you?... Well you're in luck because that's exactly what this post is for.

There's obviously a whole load of topics to dive into on batch processing families, so to keep it short and sweet I'll touch on how to open a family, add a shared parameter with a set value and close the family with the saved values. Later posts will build on even cooler family API processing ideas.

The first thing you will need to accomplish is get a reference to the local Revit application and document using a class that implements the IExternalCommand interface. The code belows shows the beginning of such a class...


Public Class ScribblesForBlog

    Private m_Manager As FamilyManager = Nothing
    Private m_App As UIApplication
    Private m_Doc As Document = Nothing
    Private m_SharedParamFile As DefinitionFile

    ''' <summary>
    ''' General Class Constructor
    ''' </summary>
    ''' <param name="CmdData"></param>
    ''' <remarks></remarks>
    Public Sub New(ByVal CmdData As ExternalCommandData)
        m_Doc = CmdData.Application.ActiveUIDocument.Document
        m_App = CmdData.Application
        If m_Doc.IsFamilyDocument Then m_Manager = m_Doc.FamilyManager
        ' Get a reference to the active shared parameter file if one exists
        Try
            m_SharedParamFile = m_App.Application.OpenSharedParameterFile
        Catch ex As Exception
            ' It is possible to have an empty value here (No Active Shared Parameter File)
        End Try
    End Sub

End Class

The next thing we need is a quick and simple means of opening and closing external family files by calling their file paths as an argument to open the file. The functions below will open and close the active family respectively:


''' <summary>
    ''' Open an external family file and set the active doc reference to this family
    ''' </summary>
    ''' <param name="m_FileName">Full path to a valid rfa file</param>
    ''' <returns>Set the document object to this family on success</returns>
    ''' <remarks>www.RevitNet.blogspot.com RULES</remarks>
    Public Function OpenExternalFamily(ByVal m_FileName As String) As Boolean
        Try
            ' Open the family and set the active document object to the family
            m_Doc = m_App.Application.OpenDocumentFile(m_FileName)

            ' Verify the active document is the correct family (paranoia)
            If m_Doc.IsFamilyDocument And m_Doc.PathName.ToUpper = m_FileName.ToUpper Then
                ' Update the reference to the active family manager
                m_Manager = m_Doc.FamilyManager
                ' We're done, success
                Return True
            End If
            ' If we got to this line then something failed
            m_Manager = Nothing
            Return False
        Catch ex As Exception
            Return False
        End Try
    End Function

    ''' <summary>
    ''' Close the current family file
    ''' </summary>
    ''' <param name="p_SaveFile">Set to True will save the file on close</param>
    ''' <returns>Optional save, default is YES</returns>
    ''' <remarks></remarks>
    Public Function CloseActiveFamily(Optional ByVal p_SaveFile As Boolean = True) As Boolean
        Try
            ' This should only run on family files
            If m_Doc.IsFamilyDocument Then
                ' True means to save the file 
                m_Doc.Close(p_SaveFile)
            End If
            ' We're done, success
            Return True
        Catch ex As Exception
            Return False
        End Try
    End Function

Now that we can open and close the families... lets add a shared parameter! That would be exciting, right? We can open a shared parameter using:


''' <summary>
    ''' Open as set the shared parameter file
    ''' </summary>
    ''' <param name="p_FileName">Full path to the shared parameter file</param>
    ''' <remarks></remarks>
    Public Sub OpenSharedParameterFile(ByVal p_FileName As String)
        If IO.File.Exists(p_FileName) Then
            Try
                m_App.Application.SharedParametersFilename = p_FileName
                m_SharedParamFile = m_App.Application.OpenSharedParameterFile
            Catch ex As Exception
                ' Should never fail... maybe a poorly formatted shared parameter file?
            End Try
        End If
    End Sub

And now for some real excitement! The code below will search your active shared parameter file for a specific parameter by name and then add it to the family and set a value to it!:


''' <summary>
    ''' Add a shared parameter to the current family and use a string
    ''' to set its value. A double formatted parameter can use 9'-4" (architectural) format
    ''' </summary>
    ''' <param name="SharedParameterName">The Name of the parameter to find and add</param>
    ''' <returns>True on Success</returns>
    ''' <remarks></remarks>
    Public Function AddSharedParameter(ByVal SharedParameterName As String, _
                                       ByVal SharedParameterValue As String, _
                                       ByVal isInstParameter As Boolean) As Boolean
        Try
            ' Make sure we are working with a valid family document
            If m_Doc.IsFamilyDocument Then
                ' make sure we have a valid shared parameter file
                If m_SharedParamFile IsNot Nothing Then
                    ' Iterate the Parameter Groups
                    For Each group As DefinitionGroup In m_SharedParamFile.Groups
                        ' Iterate the Parameters in the Group
                        For Each def As ExternalDefinition In group.Definitions
                            ' Do we have a match?
                            If def.Name.ToUpper <> SharedParameterName.ToUpper Then Continue For
                            ' We found the parameter that we're after, 
                            ' does it already exist in the family?
                            Dim param As FamilyParameter = m_Manager.Parameter(def.Name)
                            ' If we have a valid parameter object,
                            ' then it exists in the family (no need to add it)
                            If param IsNot Nothing Then
                                ' Start a new Family transaction
                                Dim m_TransFam As New Transaction(m_Doc, "Family Transaction")
                                m_TransFam.Start()
                                Try
                                    ' Set the value from string...
                                    '9'-6" can set to a double or a string
                                    m_Manager.SetValueString(param, SharedParameterValue)
                                    m_TransFam.Commit()
                                Catch ex As Exception
                                    ' Roll back on failure
                                    m_TransFam.RollBack()
                                End Try

                            Else ' Need to add the parameter...
                                ' Start a new Family transaction
                                Dim m_TransFam As New Transaction(m_Doc, "Family Transaction")
                                ' Add the parameter as type or instance
                                If param.IsInstance = isInstParameter Then
                                    m_TransFam.Start()
                                    Try ' First add the parameter
                                        m_Manager.AddParameter(def, def.ParameterGroup, isInstParameter)
                                        Dim fParam As FamilyParameter = m_Manager.Parameter(def.Name)
                                        If fParam IsNot Nothing Then
                                            ' Set the value from string...
                                            '9'-6" can set to a double or a string
                                            m_Manager.SetValueString(param, SharedParameterValue)
                                        End If
                                        m_TransFam.Commit()
                                    Catch ex As Exception
                                        ' Roll back on failure
                                        m_TransFam.RollBack()
                                    End Try

                                End If
                            End If
                        Next
                    Next
                End If
            End If
            Return True
        Catch ex As Exception
            ' Something did not go according to plan if we make it here
            Return False
        End Try
    End Function

So that's the basics! You now know how to open a family and add a shared parameter to it... Look for more batch family capabilities in future posts!

Tuesday, January 4, 2011

Visual Studio 2010 Revit 2011 Addin Templates

Have you begun using Visual Studio 2010 for your Addin development yet? Well if you have, then this post is for you! Just don't forget to always set your .NET Framework to 3.5

As you begin to develop with the Revit API, it can be annoying to have to duplicate your addin setup code and references to the Revit API. Did you know that you can create custom addin templates for your projects with all references and boiler plate code prepopulated? Just think of all the women that you can impress at the bar with a story about how you can do this!!!

Create a project with no special functionality but with all required references to the Revit API along with a basic command class setup the way you like and an application class as well. This will help make it easier to use the same template for both commands and applications. Links to the prebuilt template files that I use are at the bottom of this post.

You should maintain separate templates for each flavor of Revit that you develop for since the paths to the API references are different.


The provided "Command" class


'.NET common used namespaces
Imports System
Imports System.Windows.Forms
Imports System.Collections.Generic

'Revit.NET common used namespaces
Imports Autodesk.Revit.ApplicationServices
Imports Autodesk.Revit.Attributes
Imports Autodesk.Revit.DB
Imports Autodesk.Revit.UI
Imports Autodesk.Revit.UI.Selection

&lt;Transaction(TransactionMode.Automatic)> _
&lt;Regeneration(RegenerationOption.Manual)> _
Public Class Commands
    Implements IExternalCommand

    ''' &lt;summary>
    ''' Main entry point for every external command.
    ''' &lt;/summary>
    ''' &lt;param name="commandData">Provides access to the Revit app and docs&lt;/param>
    ''' &lt;param name="message">Return message&lt;/param>
    ''' &lt;param name="elements">elements&lt;/param>
    ''' &lt;returns>Cancelled, Failed or Succeeded Result code.&lt;/returns>
    Public Function Execute(ByVal commandData As ExternalCommandData, _
                            ByRef message As String, _
                            ByVal elements As ElementSet) As Result Implements IExternalCommand.Execute
        Try
            ' Add your code here


            Return Result.Succeeded
        Catch ex As Exception
            ' Add failure handling here

            Return Result.Failed
        End Try

    End Function
End Class


The provided "Application" class


'.NET common used namespaces
Imports System.Windows.Forms
Imports System.Collections.Generic

'Revit.NET common used namespaces
Imports Autodesk.Revit.ApplicationServices
Imports Autodesk.Revit.Attributes
Imports Autodesk.Revit.DB
Imports Autodesk.Revit.UI
Imports Autodesk.Revit.UI.Selection

&lt;Transaction(TransactionMode.Automatic)> _
 &lt;Regeneration(RegenerationOption.Manual)> _
Class Application
    Implements IExternalApplication
    ''' &lt;summary>
    ''' Implement the external application when Revit starts
    ''' before a file or default template is actually loaded.
    ''' &lt;/summary>
    ''' &lt;param name="application">Contains the controlled application.&lt;/param>
    ''' &lt;returns>Return the status &lt;/returns>
    Public Function OnStartup(ByVal application As UIControlledApplication) _
                    As Result Implements IExternalApplication.OnStartup
        ' Add your code here


        ' Return Success
        Return Result.Succeeded
    End Function

    ''' &lt;summary>
    ''' Implement the external application when Revit is about to exit.
    ''' Any documents must have been closed before this method is called.
    ''' &lt;/summary>
    ''' &lt;param name="application">Contains the controlled application.&lt;/param>
    ''' &lt;returns>Return the status&lt;/returns>
    Public Function OnShutdown(ByVal application As UIControlledApplication) _
                    As Result Implements IExternalApplication.OnShutdown

        ' Add your code here


        ' Return Success
        Return Result.Succeeded
    End Function
End Class

All you have to do is export a project as a template (the result will be a zip file) and place them under your current user's profile at:
"%USERPROFILE%\Documents\Visual Studio 2010\Templates\ProjectTemplates\Visual Basic".

It is important to place your templates into a sub directory (we'll name ours "Revit 2011") and to NOT unzip the template files. Leave the zip files intact and this will work just fine. Your directory structure should look something like the image below.


Links to the above mentioned templates can be found here (for those that do not want or know how to create their own):

Sunday, December 26, 2010

Supplemental BIM Database Porn Part 2, Reporting Multiple Categories Together

This is part 2 of the highly anticipated and exciting mini series on supplemental BIM database design!

This post will focus on how data from two entirely different tables can be UNION queried together to aggregate counts and cost SUM's in a single report. The really cool part about how this works is that the fields don't have to match names or even data types between the two tables... I know, SUPER EXCITING~!!
The examples in this mini series have been built in MS Access 2007 in case you haven't noticed but could very easily be used with minor adjustments on SQL or my new favorite PostgreSQL.

A quick snapshot showing the 5 tables that we'll be working with is shown below. The type properties are in the category tables prefixed with "type" and the instance properties in the tables prefixed with "inst"...


UNION queries can only be built in MS Access 2007 using pure SQL syntax. This shouldn't be any big deal at all to a seasoned SQL DB Admin. To create a UNION query in MS Access 2007, click the "UNION" button in the query Design tab of the ribbon.


Union queries will show up in your MS Access 2007 Object Browser with a Union icon as shown below.


It is important to note that while the data types and names of the fields in each table can be entirely different when used in a UNION query, the quantity selected between each table must be equal. Select 5 fields in one table, then you better select 5 in the other. There is no limit to the amount of UNION queries you can run together, just remember to select the same quantity of fields in each of your table selections.


Another super important key is the order of the fields in each of your selection queries. That is to say that the first field in the first selection will list directly with the first field of the second selection query and so on, make sense? Great!


So what would the query look like? The example below shows the selection of all "Furniture" and "Furniture Systems" elements from four tables named "inst Furniture", "type Furniture", "inst Furniture Systems" and "type Furniture Systems." Each category holds a relationship between two tables for instance and type properties respectively. Yet another relationship to complicate matters further but also show the flexibility of such a query is the "ProjectInfo" table that holds the project information useful in populating report headers and footers.



The resulting query results (partial) looks like this (Quantity and TotalCost are aggregated across the two UNION queries in totally separate table groups):



Thursday, December 23, 2010

Supplemental BIM Database Porn Part 1, Introduction and Table Schema

Now that I've got your attention...

Have you ever wanted to know a little more about databases in the context of your everyday BIM related tasks but weren't quite sure where you should turn? Then this post is for you and if you're by yourself, nobody will be there to laugh at you! This is part 1 of what will more than likely be an entire action packed mini super series on how databases can help the BIM process and lead to your ability to conquer the world.

It is fairly common to manage design specifications for repetitive yet varying design elements for us in the AEC industry. Some example categories that are typically handled in such a way are Furniture, Furniture Systems, Door Hardware, Light Fixtures, and Casework to name a few. These items are commonly only tagged with a type value in the model and then synced with an external database where they can be further elaborated on.

So what's a good schema to use for a database design to solve such a task when your BIM application is say... Revit? Well, Revit stores data for it's elements in two basic forms that are basically entirely separate. Type data is stored in a "Family Symbol" where all of an element's "Type Parameters" live. The rest of the data describing an element in the model is stored as "Instance Parameters" within each instance of a placed symbol. So to mirror this in the database world, it makes sense to have one table to store an element's type properties and another related table to store the instance properties. It also makes good sense to have a matching pair of these tables for each Revit category that your elements belong to that you are interested in synchronizing.


A consistent naming strategy for these category based tables will also help you succeed. I like to use a simple prefix of "inst" for the instance properties and a prefix of "type" for the type property tables. I then just name each table with a suffix matching the Revit category name.

You may have also noticed in the image up above that the same "inst" and "type" prefixes are used for the primary keys of each table. This also helps with the relationships between the database and Revit. I prefer to use the "UniqueID" property for all elements as their primary key when synchronizing with a database. These values are pretty much guaranteed to never duplicate even across project models. The value returned by UniqueID is a hugungous GUID. I then use the UniqueID from the type element as a foreign key relationship to the instance tables. This is possible since the type element (family symbol) and the instances of these symbols as they are placed throughout the model are all unique! That is to say that the type element is entirely a separate object behind the scenes in the Revit API thus making a handy way to join them in a database schema.


I also generally use the name of the Revit parameter or property to name the corresponding database field name. String based parameters and properties should have a string formatted database field. Double formatted Revit parameters or properties should have their database fields formatted as decimal. Be careful with Revit parameters that are named using what are referred to as "Reserved Names" as these may result in strange unwanted features during synchronization. A list of these so called reserved words can be found at http://support.microsoft.com/kb/286335.

Stay tuned for future episodes of this action packed and exciting topic!!! We'll also get into how these tables and databases can be setup programmatic!!! Enormously exciting and guaranteed to help you pick up chicks at a bar!!

Monday, December 20, 2010

Parametric Holiday Tree from Charles Lee

Well, working for an Architectural firm definitely brings me closer to some strange and interesting people. One such interesting person I get to work with regularly is Mr. Charles Lee.


Charles was named this year's "Young Architect of the Year 2010" here in San Francisco and really loves the BIM stuff. Check out his biosarch blog based on BIO Design!

Here's Charlie's Revit model of a Holiday Tree... Thanks Charles! Maybe next time we can set this up as a shared model where everyone in the office could model their own ornaments to help decorate it! Next time we'll have to get that setup for our users...

Download

Friday, December 17, 2010

Wait for IT....

I know this is the worst blog post you will ever read in your human existence... but I just wanted you to know that I have a post on its way that is going to really bring the house down!!! Just waiting on the OK to post it!... Trust me... it's HUGE!

If I can't post it for some reason this week... it will have to wait until March (I wonder why)...

Tuesday, December 14, 2010

10 Pick up Lines All BIM Managers Must Know

If you lack a sense of humor or are easily offended then click an ad to the right and avoid reading this post! There will be other posts later that actually have a point!

As you can imagine, I have a lot of friends (pause) that poke fun at my line of work (I'm from Austin Texas). So I got into a bit of a challenge in comparing how my profession can just as easily be compared to theirs in terms of pick up lines to use at a bar. So here's the top ten pick up lines that ALL BIM managers need to know:

10.) These hands aren't just for constraining forms

9.) Have you ever been with a BIM Manager?

8.) All my models make it to stardom!

7.) Your forms are constrained quite efficiently

6.) I'm an expert at starting complex families...

5.) You have eyes that shine like the most elaborate curtainwall on the most perfect summer day

4.) You're more beautiful than any building I've ever modeled

3.) Speaking of Models, you're one of the best I've ever managed

2.) Has anyone ever 3D modeled you before?

1.) Wanna come back to my place and check out some sweet renderings?

Thursday, December 9, 2010

Schedule Accumulated Road Centerline Lengths in Revit

Another NON API Post!!! Don't worry, I haven't converted...

Have you ever needed to schedule some sort of element's accumulated linear distance?... Say like a whole bunch of road centerlines for curb costing?... Here's a quick tip on how you can do just that?...

First create a new wall type and name it something like "Roads_CenterLine" and set it's construction width to something very skinny... say 0.05mm
Set the "Type Mark" for this new wall type to "RoadCenterline"... we'll use this to filter our schedule later.

Next draw or trace all of your road centerlines using this new wall type. Make sure that all of the walls that you draw using this new wall type are no taller than your cut plane in your view (very short). A wall height of 1mm works great.

Create a view filter based on the name of this wall and apply it to your current view. We'll use this to override the display of the walls so that when plotted they will look like a real centerline.
Apply the filter to your view and override the filter's display to whatever you want. My example modifies the linetype to a centerline...

Your centerline walls should look something like this one now:
So you obviously need to schedule the accumulated lengths of these centerlines, so create a new wall schedule and add the "Length" and "Family Type" fields.
Filter walls by "Type Mark" using contains "RoadCenterline" as the filter means.

Set the Grand Totals checkbox in the "Sorting/Grouping" tab so you get the totals.

Now hide the "Type Mark" field since you're only using it as a quantification filter.
Now check the "Calculate totals" box for the "Length" field... this is how we calculate the totals.
You're done! Your schedule should resemble something like this one:
BIM is easy...

Wednesday, December 8, 2010

Revit MEP Reuseable System Layouts Across Multiple Projects

Before anyone boo's me... this is a NON API post...

This one's for you HVAC guys out there struggling with the speed of Revit MEP. I know it can be a little painful at times, but boy is it nice to have an intelligent model as a finished product that you can really make use of throughout the CA and construction phase to help avoid change orders and SNAFU's in the field.

I'll have to warn you first about the performance hits that you may encounter if you fail to "UNGROUP" these systems after you place them into your model... PLEASE remember to ungroup all placements after they dropped into your model as these groups will really leave a mark in terms of performance if left as live groups.

OK... let's get started...

Have you ever worked on a building that uses repeatable VAV layouts or has numerous zones that for the most part are all basically the same in terms of elements placed to design it? This method can be used for just about anything you can imagine that utilizes system families, tags, text, and well... you get the idea.

I'm going to show you a way that you can build a warehouse of reusable system layouts complete with equipment, ductwork, and air terminals that you can reuse on all of your projects.

First let's start by drawing a full system complete with a VAV unit, all necessary ductwork, terminals and tags (you really could add anything you want to this).
Then let's select the items and create a group. Notice how you get two options for naming the groups? Tags will be placed into a nested group that you can name independently from the model group.

Now that you've got the group created, verify that your project browser displays the model group with a nested detail group beneath it like the image below.

Now to test if this thing works, right-click the model group from the above mentioned model group in the project browser and chose "Create Instance" and place one in your model (anywhere). It should look like the one below (without the tags... don't worry...).

Did you notice in the image above that you have an option button named "Attached Detail Group" when you select the model group that you just placed? Pick it and select the nested detail group name in the resulting dialog box.

Now your group should look a little more complete like the one below.

Now you can export your preconfigured system layout groups to a server where they can be shared across projects...


Use the "Load as Group" button in the "Insert" tab of the ribbon to add an externally saved group to a new project...