Monday, June 27, 2011

Model Security! Take it Seriously!

I just returned from a trip to Huntington Beach, CA where I attended the inaugural Revit Technology Conference of North America. I have to say that it was one of the best conferences I've ever attended and that's saying a lot for someone that spent three blurry years living in Las Vegas.

One of the recurring themes that came up over and over was model security. Just what exactly is it and what are you doing to make sure that your models contain adequate security? There are many methods and techniques for you to achieve model security, and they were all debated heavily and often throughout the conference.

I proposed my idea on model security and what I'm doing to make sure that all of my models are at their optimum security performance. I prefer a non human approach to model security. Something with some sort of motion sensor that doesn't take shit off of nobody, but still has a sense of humor and doesn't eat too much. Turrets are a great selection for such a medial task and are 40% reliable at least 60% of the time. These turrets are also very cheap to replicate throughout your models.


The turrets shown here are rigged with parametric motion sensing lasers to provide a fully adjustable motion sensing distance. Smaller spaces will require a shorter range while larger spaces may require longer motion sensing distances. These wonderful model security devices can to both all while costing very little to keep operable!!


You can download this model security device here for a limited time. The license to use these devices come with a cost however. You will be required to leave a comment describing your model security needs and how many you intend to place within your model to achieve the level of security that you require. These devices have not been tested with government installations and are not suggested for any form of government or even commercial use whatsoever, mostly due to the fact that they are not entirely reliable all of the time.

Take good care of your turret(s) and enjoy the videos below demonstrating their capabilities as I find them a very valuable means for model security.







Wednesday, May 25, 2011

Revit Technology Conference USA 2011

Well, RTC AUS 2011 is going on as we read (speak), and RTC USA 2011 is just around the corner (28 days)! I can't wait to work hard at the beach!

I've been working on my dataset for the class that I will be presenting at the inaugural RTC North America 2011 in Huntington Beach, California entitled "Building a Dynamic Facility Management Web Application with Revit Using APIs."

Here's a sneak peak of my interiors model:

Tuesday, May 3, 2011

Stretchable Grips for Type Parameter Lengths

Have you ever noticed that you do not get the stretchable grips on parametric families that have their element lengths bound to type parameters? Did you know there is a way around this?


DISCLAIMER!!!  Before we get started, I have to warn you... this may not be a good idea depending on what your application and goals are as this method will result in the possible loss of integrity for family type sizes!!!


This method comes in handy for block and stacking of masses for the purpose of laying out relationships of departments in a large facility... so... that being said, let's get started:

Most people like the convenience of stretchable grips. But it is a good practice to use type parameters to control the widths and lengths of elements that need to resize together. Here's how you have have the best of both worlds and it is silly how simple it is:

Draw an extrusion and place a dimension along the width that you want to control. Add a label to this dimension and name it InstanceWidth.


Then create a new length parameter and name it TypeWidth.


Set the formula for this parameter to = InstanceWidth.


Now you get the convenience of grips, but the power and honesty of a type parameter.


Tell your friends or better yet... the cute girl sitting at the end of your favorite bar, you can thank me later.

Monday, May 2, 2011

Revit Tip, Ceiling Displays Messed up by Tall Furniture

Have you ever had an issue where your ceilings do not display correctly when you have elements that you need to show in your ceiling plans that are taller than your cut-plane? This is a common issue worth discussing, so here's one of many ways to get your displays fixed...

Issue: Tall cabinets that will obstruct lighting design cause unwanted breaks in the ceiling patterns. You do not want to raise the cut plane because it makes your door openings display incorrectly.

In this case, the ceiling is placed at 9'-6", the furniture causing the display issue is 8'-0" tall and the RCP view cut plane needs to be set at 7'-6". The image below shows how the grid pattern is broken incorrectly by the tall furniture element:


Solution: Use a series of "Plan Regions" and set their View Range to the necessary height so that the ceiling above these items displays correctly. The image below shows the correct plan display for the ceiling pattern:


This issue was resolved by placing a Plan Region over the tall furniture item and setting the cut plane to 8'-0"


Draw the region around the necessary area and set the cut plane's height to the height that you require.

Wednesday, April 27, 2011

Revit 2011 Worksharing User Name

Do you have a bunch of public workstations that your users use to access workshared Revit 2011 models? Have you ever noticed that the name that displays in the worksharing monitor does not match the actual user's name?... Well this is an issue that has been resolved in Revit 2012 but is till annoying in Revit 2011.


So if you're still wondering how to change this name (sigh), you can adjust it to read anything you like... just be careful that you do not have two names in any two machines that are the same and have the same model loaded at the same time (this really jacks up how Revit 2011 worksharing works and will cause some dumb stuff...)



So if you have a bunch (or just one) public walk-up or training machines that people use to access workshared Revit 2011 models, enter a name in the user name field that is descriptive as to the machine's use or location:

  • San Francisco Giants 2010 World Series Champion Test Machine #1
  • Training #1
  • 197th Floor Spaceship Walk-up
  • Etc.

Thursday, April 21, 2011

Navisworks Controls on 64 Bit

If you are running a 64 bit development environment like the rest of the world and have attempted to utilize the Navisworks ViewControl you have probably run into some rather annoying issues. This control will not work in a 64 bit environment at design time in your Visual Studio project, but behold... there is a workaround.

You will need a 32 bit OS where you can install the 32 bit version of Navisworks so you can steal the API dll references that you can use on your 64 bit OS for development. Or if you're super duper lazy, you can just download the one that I'm using and adjust all your Autodesk.Navisworks.Controls references to this 32 bit flavor of the dll for use at design time.

Download the 32 bit control DLL here. Hopefully Autodesk doesn't try to kick my ass for offering this file for download. But after all you can install a 30 day trial to keep this file forever, so no big whoop I guess.

When compiled, the control will work just fine on a 32 bit and 64 bit machine (just like it should otherwise).

Monday, April 4, 2011

Revit 2012 Addin Templates for Visual Studio 2010

Well, if you have not yet begun using Visual Studio 2010 for your Addin development with Revit 2011... you don't have much of a choice with Revit 2012.

If you want the antiquated Revit 2011 templates, go here.

If you haven't heard, the API for Revit 2012 has been updated to support .NET 4.0. Your .NET 3.5 and Revit 2011 Add-ins will still function with Revit 2012 (with some key edits to your .addin manifest files) but you will no longer be able to debug .NET 3.5 Add-Ins with Revit 2012.

I like to maintain separate templates for each flavor of Revit that I develop for since the paths to the API references are different for each product.


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 2012") 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.


For you lazy folks... here are the links to the preconfigured templates:

Thursday, March 31, 2011

Kickin it with Excel - Part 1

So you love and Cherish Revit... and are fond of Excel... you want to share model data between Excel and your Revit models... here's step one... exporting data to Excel from a Revit model using the API

Please keep in mind that I can't give away ALL of my secrets, so this one is provided as a means to get you started...

This post assumes that you have 64 Bit Revit and 32 bit Microsoft Excel 2010 installed and have mad skills with a Bow Staff.

First set a reference to the following:



Now all you need is a handy class for opening an Excel document that you can write stuff to. This really doesn't need much explanation and should get you all greased up and ready for Part 2 (TBD):


Imports System.Windows.Forms
Imports System.IO
Imports Microsoft.Office.Interop

Public Class clsUtilityExcel

    Private m_ExcelSession As Excel.Application = New Excel.ApplicationClass

    ''' <summary>
    ''' Bind data to Excel
    ''' </summary>
    ''' <param name="path"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function BindToExcel(ByVal path As String) As Excel.Application
        Dim m_Excel As Excel.Application
        Dim m_WB As Excel.Workbook
        If path.Trim() = "" Then
            Try
                m_WB = m_ExcelSession.Workbooks.Add(Type.Missing)
                While m_ExcelSession.Worksheets.Count > 1
                    Dim m_WS As Excel.Worksheet
                    m_WS = TryCast(m_ExcelSession.Worksheets(1), Excel.Worksheet)
                    m_WS.Delete()
                End While
                m_Excel = m_WB.Application
                m_Excel.Visible = True
                Return m_Excel
            Catch exception As Exception
                MessageBox.Show("Unable to start Excel session with file. " & _
                                vbCr & vbCr & _
                                "System Error Message: " & vbCr & _
                                exception.Message, "Error")
                Return Nothing
            End Try
        Else
            If Not File.Exists(path) Then
                MessageBox.Show("Unable to locate file: " & path & ".", "Error")
                Return Nothing
            End If
            Try
                m_WB = DirectCast(System.Runtime.InteropServices.Marshal.BindToMoniker(path), Excel.Workbook)
                If m_WB.Application.ActiveWorkbook Is Nothing Then
                    Dim m_FileName As String
                    m_FileName = path.Substring(path.LastIndexOf("\") + 1)
                    m_WB.Application.Windows(m_FileName).Visible = True
                End If
                m_Excel = m_WB.Application
                m_Excel.Visible = True
                Return m_Excel
            Catch exception As Exception
                MessageBox.Show("Unable to find or start Excel with file: " & path & ". " & _
                                vbLf & vbLf & "System Error Message: " & vbLf & _
                                exception.Message, "Error")
                Return Nothing
            End Try
        End If
    End Function

    ''' <summary>
    ''' Launch an Excel Session
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function LaunchExcel() As Excel.Application
        Dim m_Worksheet As Excel.Worksheet
        Dim m_Workbook As Excel.Workbook = m_ExcelSession.Workbooks.Add(Type.Missing)
        Try
            If m_ExcelSession Is Nothing Then
                Return Nothing
            End If
            While m_ExcelSession.Worksheets.Count > 1
                m_Worksheet = TryCast(m_ExcelSession.Worksheets(1), Excel.Worksheet)
                m_Worksheet.Delete()
            End While
            Return m_ExcelSession
        Catch ex As Exception
            MessageBox.Show(ex.Message)
            Return Nothing
        End Try
    End Function

End Class

I guess you'll have to stay tuned on how the magic is done to get the data from the elements and into the Excel worksheet...

Monday, March 28, 2011

Care to Request a Topic?

Do you have a burning desire to make an API post request?

Send your request as a mention on Twitter to AYBABTM

Wednesday, March 23, 2011

Come Book an Appointment to Master Revit

Well... it's already that time of year again... The Autodesk annual software releasals event that you've all been waiting for (patiently I hope). We have a new book out this year and while the name isn't a total representation of how creative we are at our day jobs, is a very creative and enlightening perspective on how to master Autodesk Revit Architecture 2012!

Be sure to check out the best chapter in the whole book, chapter 25!!
"Getting Acquainted with the API"

Buy your copy today (...using the link to the right please ;)

You may want to buy one for every room in your house... your friends will be absolutely envious!! They also make great gifts to relatives and even better gifts for anniversaries and graduations!! Collect 'em all!!!

And if you bring your copy to Revit Technology Conference USA 2011 in Huntington Beach, CA... we (the main authors... and then me) might even autograph them for you!!... Registration is now open!!

Wednesday, March 9, 2011

Winning Visual Studio Stretchy Form Design 101

I get this question a bunch: "How Don, how do you build those awesome winning forms all the time that stretch all sweet like?"... The answer is simple and I'll share how it's done right here in this very post!

First off, if you're using the form resize events to calculate the widths and heights of your controls based on the size of the form, you're on the wrong path... there's a far easier way to do it.

If you want to follow along, create a new form and layout some controls like the one shown here in this tricky example (datagrid on the left, grouped listbox in upper right, two buttons on the lower right):




Here is how it looks as it stretches (in design view too!!!).... notice how the controls morph in a logical way? It's magic... and logic (I know, I get the two confused sometimes too...)



Here's how I got it to do this... the trick is in each control's anchor settings!... Select a control and click on the Anchor properties. You can set any combination of four directions for anchoring for a single control.



Setting the anchoring of opposite directions will result in the control stretching as the form is stretched in that same direction. Setting all four directions will result in the control stretching in all directions as the form is stretched in both directions from the corner.

Setting the anchor to upper and right will simply justify the control in the form in that direction as the form is stretched in any direction...

When working with groups, the controls inside the groups are anchored within the group boundary only. So if you want a group to stretch, you must set the anchor settings for the group... you should then set the anchoring for the control within the group boundary separately.

See you guys at RTC 2011!!

Wednesday, March 2, 2011

Thou Shall Read Thy Dialog Box!!

This may appear to be a ridiculous post to most... but you would be amazed as to how many people call me to fix something with their model when they get a popup dialog that they have not seen before. They could solve their own problems 99% of the time if they would just simply READ the dialog box.

It really gets me though when people never read any warnings or dialogs at all and just close them no matter what. These are the people that tend to destroy stuff and don't even know it!

So please... if you're new to Revit or have been doing this stuff for 20 years... please... PLEASE read the dialogs and warning popups all the time!


...or the code ninjas will get you!!! I'm planning to start placing random jokes in my dialogs to urge people to want to start reading dialog boxes... I wish Autodesk could do the same

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 &lt; 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):