Wednesday, December 28, 2011

To and From Room Query Tool

I posted earlier on the weird behavior of FamilyInstance "To Room" and "From Room" data. I just thought I'd go ahead and share the simple tool that I used to query the elements.

The sample project consists of a command entry class, a selection manager class and one form.

The first class shown below is used to manage the selection of elements or faces in a model. This class is taken from the SDK with some minor adjustments.

Imports Autodesk.Revit.DB
Imports Autodesk.Revit.UI
Imports Autodesk.Revit.UI.Selection

''' <summary>
''' Selection type.
''' </summary>
Public Enum SelectionType
    Element
    Face
    Edge
    Point
End Enum

''' <summary>
''' A class for object selection and storage.
''' </summary>
Public Class clsSelectionManager

    Private m_commandData As ExternalCommandData
    Private m_application As UIApplication
    Private m_document As UIDocument
    Private m_CreationBase As Autodesk.Revit.Creation.ItemFactoryBase
    Private m_elemPickedPoint As XYZ
    Private m_selectionType As SelectionType = SelectionType.Element
    Private m_selectedPoint As XYZ
    Private m_selectedElement As Element

    ''' <summary>
    ''' constructor of SelectionManager
    ''' </summary>
    ''' <param name="commandData"></param>
    Public Sub New(commandData As ExternalCommandData)

        ' Widen Scope
        m_commandData = commandData
        m_application = m_commandData.Application
        m_document = m_application.ActiveUIDocument

        ' Support for Family and Project Environment
        If m_document.Document.IsFamilyDocument Then
            m_CreationBase = m_document.Document.FamilyCreate
        Else
            m_CreationBase = m_document.Document.Create
        End If

    End Sub

    ''' <summary>
    ''' For specific selection type.
    ''' </summary>
    Public Property SelectionType() As SelectionType
        Get
            Return m_selectionType
        End Get
        Set(value As SelectionType)
            m_selectionType = value
        End Set
    End Property

    ''' <summary>
    ''' Store the selected element.
    ''' </summary>
    Public Property SelectedElement() As Element
        Get
            Return m_selectedElement
        End Get
        Set(value As Element)
            m_selectedElement = value
        End Set
    End Property

    ''' <summary>
    ''' Store the selected point. 
    ''' When the point is picked, move the element to the point.
    ''' </summary>
    Public Property SelectedPoint() As XYZ
        Get
            Return m_selectedPoint
        End Get
        Set(value As XYZ)
            m_selectedPoint = value
            If m_selectedElement IsNot Nothing AndAlso m_selectedPoint IsNot Nothing Then
                MoveElement(m_selectedElement, m_selectedPoint)
            End If
        End Set
    End Property

    ''' <summary>
    ''' Select objects according to the selection type.
    ''' </summary>
    Public Sub SelectObjects()
        Select Case m_selectionType
            Case SelectionType.Element
                PickElement()
                ' pick element
                Exit Select
            Case SelectionType.Face
                Exit Select
            Case SelectionType.Edge
                Exit Select
            Case SelectionType.Point
                PickPoint()
                ' pick point
                Exit Select
        End Select
    End Sub

    ''' <summary>
    ''' Pick the element from UI.
    ''' </summary>
    Friend Sub PickElement()
        Try
            ' Pick an element.
            Dim eRef As Reference = m_document.Selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Element, "Please pick an element.")
            If eRef IsNot Nothing AndAlso eRef.ElementId <> ElementId.InvalidElementId Then
                SelectedElement = m_document.Document.GetElement(eRef)
                m_elemPickedPoint = eRef.GlobalPoint
            End If
        Catch generatedExceptionName As Autodesk.Revit.Exceptions.OperationCanceledException
            ' Element selection cancelled.
            SelectedElement = Nothing
        End Try
    End Sub

    ''' <summary>
    ''' Pick the point from UI.
    ''' </summary>
    Friend Sub PickPoint()
        Try
            ' Pick a point.
            Dim targetPoint As XYZ = m_document.Selection.PickPoint("Please pick a point.")
            SelectedPoint = targetPoint
        Catch generatedExceptionName As Autodesk.Revit.Exceptions.OperationCanceledException
            ' Point selection cancelled.
            SelectedPoint = Nothing
        End Try
    End Sub

    ''' <summary>
    ''' Move an element to the point.
    ''' </summary>
    ''' <param name="elem">The element to be moved.</param>
    ''' <param name="targetPoint">The location element to be moved.</param>
    Friend Sub MoveElement(elem As Element, targetPoint As XYZ)
        Dim vecToMove As XYZ = targetPoint - m_elemPickedPoint
        m_elemPickedPoint = targetPoint
        ElementTransformUtils.MoveElement(m_document.Document, elem.Id, vecToMove)
    End Sub

End Class

The code for the form used to display the to and from data is shown here as well after the image showing the basic layout of the controls:


Public Class Form_ToFrom

    ''' <summary>
    ''' Constructor
    ''' </summary>
    ''' <param name="p_e"></param>
    ''' <remarks></remarks>
    Public Sub New(p_e As FamilyInstance)
        InitializeComponent()

        Try ' To Room
            If Not p_e.ToRoom Is Nothing Then
                Me.LabelToName.Text = p_e.ToRoom.Name
                Me.LabelToNumber.Text = p_e.ToRoom.Number
            Else
                Me.LabelToName.Text = "n/a"
                Me.LabelToNumber.Text = "n/a"
            End If
        Catch ex As Exception
            Me.LabelToName.Text = "n/a"
            Me.LabelToNumber.Text = "n/a"
        End Try
        Try ' From Room
            If Not p_e.FromRoom Is Nothing Then
                Me.LabelFromName.Text = p_e.FromRoom.Name
                Me.LabelFromNumber.Text = p_e.FromRoom.Number
            Else
                Me.LabelFromName.Text = "n/a"
                Me.LabelFromNumber.Text = "n/a"
            End If
        Catch ex As Exception
            Me.LabelFromName.Text = "n/a"
            Me.LabelFromNumber.Text = "n/a"
        End Try
    End Sub

    ''' <summary>
    ''' Close the Form
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub ButtonOk_Click(sender As System.Object, e As System.EventArgs) Handles ButtonOk.Click
        Me.Close()
    End Sub

End Class

Finally, here's the basic command entry and execution class used to launch the Add-In:


Imports Autodesk.Revit.ApplicationServices
Imports Autodesk.Revit.Attributes
Imports Autodesk.Revit.DB
Imports Autodesk.Revit.UI
Imports Autodesk.Revit.UI.Selection

<Transaction(TransactionMode.Automatic)>
Public Class Commands
    Implements IExternalCommand

    ''' <summary>
    ''' Entry
    ''' </summary>
    ''' <param name="commandData"></param>
    ''' <param name="message"></param>
    ''' <param name="elements"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function Execute(ByVal commandData As ExternalCommandData,
                            ByRef message As String,
                            ByVal elements As ElementSet) As Result Implements IExternalCommand.Execute
        Try
            ' Selection Manager
            Dim m_mgr As New clsSelectionManager(commandData)

            ' Select an Element
            m_mgr.SelectionType = SelectionType.Element

            ' Pick an Element
            m_mgr.PickElement()

            ' Is it a FamilyInstance?
            If TypeOf m_mgr.SelectedElement Is FamilyInstance Then

                ' Analyze It in the Form
                Using m_d As New Form_ToFrom(m_mgr.SelectedElement)

                    ' Display the Results
                    m_d.ShowDialog()

                End Using

            End If

            ' Return Success
            Return Result.Succeeded

        Catch ex As Exception

            ' Failure
            Return Result.Failed

        End Try

    End Function
End Class

Text

Strange To Room and From Room Data on Elements

If you have relied on the "To Room" or "From Room" data on an element and didn't always get what you were expecting, I'd be willing to bet that it is due to one of two things. No room elements in the newest phase of your model or refer* to the first reason.

* recursion humor

Room data for the "To Room" and "From Room" parameters on FamilyInstance elements can only read from rooms in the youngest phase in time. This means that any room you place in a previous phase in your model will be ignored in terms of "To Room" and "From Room" no matter what. Confused yet?

Let's say that your model has three phases:

  • Existing
  • Main Construction
  • Future
If you perform most of your modeling and place your rooms in the "Main Construction" phase none of your elements will return a "To Room" or "From Room" element at all unless these rooms are placed in the "Future" phase. Something even a bit more confusing is that your existing doors will report the "To Room" and "From Room" data for the room that exists in the same location in the newest phase.

The image below shows the room data that results from a door selected in a view that is set to the existing phase. There are rooms on either side both places in the existing phase yet the data reported by the door is showing from the future. If the rooms placed in the future phase were deleted, this door would not display any room data even though there are clearly two existing rooms on either side of the door.



The two phases are shown below to help you understand how this door data is coming through the element.



The "To Room" and "From Room" data comes from the rooms placed in the newest phase of your model only so be careful when developing tools that rely on these room settings. You can see below what happens when an element is not adjacent to any rooms in the last phase of your model... you get nothing:


Thursday, December 15, 2011

How To Uninstall or Remove a Revit Add-In

I've been getting this question quite a bit recently:
"How do I remove an Add-In from Revit?"



This post will not focus on what an Add-In is nor what an Add-In manifest is... hopefully you already have an idea or can find out about those things here.

The uninstall solution depends on the version of Revit that you are running. The method that I will discuss in this post is suitable for Revit 2011 and newer. If you are using Revit 2010 then you just need to go ahead and upgrade your shiz.

There are two locations that Add-In manifests can be placed to load into Revit. The paths shown below are for Windows7. If you are still running Windows XP than it may be time to update your other shiz.

Machine Wide Location
The first is available to all users that log into the machine and requires administrative privileges to add files to.

  • C:\ProgramData\Autodesk\Revit\Addins\
User Profile Location
The user profile location will only load for the current user. This location does not require administrative privileges and therefor anyone can install Revit Add-Ins into their environment if they use this location.
  • %USERPROFILE%\AppData\Roaming\Autodesk\Revit\Addins\

Preventing the Add-In from Loading
There's a bunch of ways to prevent an Add-In from loading into Revit.

  • Removing the associated .addin file from either of the locations mentioned above will prevent the Add-In from loading into Revit. You can leave the .dll and all other files there if you want and they will not load without the associated .addin file instructing Revit to do so.
  • Another trick is to simply rename the extension of the .addin file to something like .addin.notloaded or something to keep it from loading.
  • Revit 2011 and 2012 do not load .addin files that are placed in sub-directories of these Add-In manifest directories either, so you could just simply create a sub-directory for the Add-Ins that you don't want to be loaded and place them in that sub directory.

Sunday, December 4, 2011

Free Revit Add-Ins from Your Friends @ Case Design, Inc.


It has been a while since I've posted anything so I’d like this to be one for the users out there. We’ve been working on lots of cool stuff recently including the beginnings of what we will be offering as a suite of free tools for Revit 2012 and beyond.

For those of you that made it out to Autodesk University 2011 in Las Vegas and attended our first annual AU Tweetup (free beers for our twitter friends), you may have heard about a few of these tools. We released three fully functional tools that you can install from our apps site by registering your valid email address. Once you’ve registered for the tools, the links to install will be revealed in the page.

The first three tools of the Case Free Tool Suite include:


Once installed, the tools will be accessible from a new Tab on your Revit Ribbon named "Case Design, Inc." These are are intended to be simple to use everyday use tools to help out. If you ever have a need for anything more complicated or heavily customized give me a shout and I'd be happy to work with you on a solution.

We are planning to add others in the future to our free suite of tools, so stay tuned to announcements from this blog as well as twitter from any of the following accounts:
@case_inc
@AYBABTM
@davidfano
@fedenegro

Tuesday, November 1, 2011

Free Revit Add-Ins?

As some of you may have been hearing, Case is in the process of developing some free Revit Add-Ins for general consumption. I'd like to offer up a chance for you to suggest a tool or utility that you would like to see make it as part of this free offering.



Leave a comment here explaining a Revit tool that you'd like to see added to the free Case toolset and you just might get your wish... Who knows, you might even win something.

Saturday, October 1, 2011

How to Build a ClickOnce Installer for Revit Add-Ins

Have you ever wanted to deploy an installation package that did not required Admin privileges for the distribution of your Revit Add-ins? Do you also need a simple means to deploy frequent updates? ClickOnce installers can do just that for you.


Microsoft unveiled the ClickOnce technology way back in 2003 and is great so long as your application is simple and does not require any modifications during install that may require administrative access. ClickOnce works by copying an entire isolated application to the user's profile on install. There is a small issue in that ClickOnce is not available for class library projects (Revit Add-Ins are class library projects).

Here is a workaround that you can use to deploy a ClickOnce application to install your Revit Add-Ins.

From inside your current Revit Add-In Visual Studio solution, add a new Windows Console project named the same as your target project but with a ".Updater" suffix added to the end. Save this new project alongside your current project. This new console project will only be used to copy the Revit Add-In program files into the user's Revit Add-Ins directory under their Windows user profile.



This new project will allow you to generate the published installer that a standard class will not. Open the project settings and click on the "Publish" tab along the left (VB.NET, C# will be different). You can enter a version setting as well as an installation URL where youy will post your updates.


Notice the highlighted path above in the URL section? You need to also enter this in the "Updates" section. Click on the "Updates" button to access the optional updates settings.


You can set the project and publisher settings from the "Options" button shown here.


The next thing that you need to do is add a reference to your main project so that the resulting dll files can be included as part of the installation of this console project. Add a reference to your console project and choose your project from the Projects tab.


Now that you have a reference to this project, you can add the output dll to your ClickOnce installation process. From the Publish tab, click the "Application Files" button and make sure that everything required by your Revit-Add-In is also available within this dialog. Set each file to "Include" that you will require in your main installation.


You may have noticed that the ".addin" file is included in the above list. Be sure to add your addin file to the console project so that it can be included in this list as it is required in order for the Revit Add-In to launch in a Revit session.

You can also add an icon to be used by your installer if you like by setting an icon in the "Application" tab of the console project.

Now that you've got the basic format of the publish project configured, you'll need some code to copy the files into the user's Revit Add-Ins directory. Enter the following code in the default "Module1" that was created automatically in your new console project to do just that:


Imports System.IO
Imports System.Reflection

Module mod1

    Private m_sourcePath As String = Path.GetDirectoryName(Assembly.GetExecutingAssembly.Location)
    Private m_w7 As String = Environment.GetEnvironmentVariable("UserProfile") & "\AppData\Roaming\"
    Private m_xp As String = Environment.GetEnvironmentVariable("UserProfile") & "\Application Data\"

    ''' <summary>
    ''' The Main Function
    ''' </summary>
    ''' <remarks></remarks>
    Sub Main()
        ' Test for Win7
        If Directory.Exists(m_w7) Then
            DoCopy(m_w7)
            Exit Sub
        End If
        ' Test for XP
        If Directory.Exists(m_xp) Then
            DoCopy(m_xp)
            Exit Sub
        End If
        ' Warn on Failure
        MsgBox("Your Operating System was not Properly Detected", MsgBoxStyle.Exclamation, "Installation Failed")
    End Sub

    ''' <summary>
    ''' Copy the Files
    ''' </summary>
    ''' <param name="p_destination"></param>
    ''' <remarks></remarks>
    Private Sub DoCopy(p_destination As String)
        ' Addin path
        Dim m_PathAddin As String = p_destination & "Autodesk\Revit\Addins\2012"
        ' Get Files
        Dim m_di As New DirectoryInfo(m_sourcePath)
        Dim m_FilesAddin As FileInfo() = m_di.GetFiles("*.addin")
        Dim m_FilesDll As FileInfo() = m_di.GetFiles("*.dll")
        For Each x In m_FilesAddin
            Try
                x.CopyTo(m_PathAddin & "\" & x.Name, True)
            Catch ex As Exception
                ' Quiet Fail
            End Try
        Next
        For Each x In m_FilesDll
            Try
                x.CopyTo(m_PathAddin & "\" & x.Name, True)
            Catch ex As Exception
                ' Quiet Fail
            End Try
        Next
    End Sub

End Module

After you've made all the necessary adjustments, you can click the "Publish Now" button in the Publish tab to publish the project. A new Publish directory will be created beneath the directory where your new console project was saved. This is your ClickOnce installer? Copy the entire set of files to either a location on your network or to an FTP on the web where you will host the installations. The ".application" file is the installer. Setup.exe can be used for browsers other than IE.


Each subsequent time that you update your code, click the "Publish Now" button to create a new publish installer. Each sequence will be saved in the same location. Copy the updated files to the location where you are hosting your updates and will become available to your users by executing the "updater" program that was originally installed on your client's machines (Executing the tool from inside Revit will NOT automatically update the tools in this configuration).

Be sure and use Internet Explorer to run the "*.application" ClickOnce installer as this is the only browser that will support this technology so far.

Tuesday, August 30, 2011

Relax Dataset Constraints for TableAdapters in .NET

This is a very common issue that people run into as they begin to use complex dataset objects in their .NET development work. This applies to ASP.NET as well as Winforms development work... even WPF.

If you've ever put together a complex join query in one of your TableAdapter objects and then in your code attempted to apply it to a DataTable object, you may have seen this warning and had no idea what to do about it:

The solution is simple. Open your XSD in design view and select on the background area (do not select a query or a TableAdapter).

Then in the properties pallet you will see a property named "EnforceConstraints". Set this property to False.


This option gives you the ability to provide dataset level constraints which are usually not necessary if your database backend has its own constraints.

Wednesday, August 24, 2011

Using the Find Items Feature in Navisworks 2012 for Revit Rooms

I've heard some frustrations from people on how to use the Find Items feature in Navisworks 2012 to find Revit rooms within the model.

The first thing that you need to make sure is that the "Export room geometry" option is checked prior to exporting the Revit model to Navisworks 2012.


Then in the "Find Items" dialog accessible from the View tab of the ribbon under the Windows drop-down set the following line in your search:

  • Category = Category
  • Property = Name
  • Condition = =
  • Value = Rooms
This will find all Revit rooms in your Navisworks model where you can then save this as a search set for future use without ever having to update your selections.


Monday, August 15, 2011

Check a String for Valid File Naming Characters

Here's a super simple solution to a surprisingly common problem for validating file naming strings using .NET.

If you have ever required a user to enter a file name and had to validate the characters they entered, I'm sure you have some sort of function that can do this. Surprisingly though, I see a bunch of code where people build their own file name character validation functions.

There is a built-in .NET function for validating file naming characters available from the System.IO namespace. The snippet below shows a very basic use of this command accepting an input of a string to validate and returns the same string back out if valid and an empty string if invalid file naming characters are detected.

    ''' <summary>
    ''' Make sure the path does not contain any invalid file naming characters
    ''' </summary>
    ''' <param name="fileName">The Filename to check</param>
    ''' <returns>A string</returns>
    ''' <remarks></remarks>
    Private Function CheckValidFileName(ByVal fileName As String) As String
        For Each c In Path.GetInvalidFileNameChars()
            If fileName.Contains(c) Then
                ' Invalid filename characters detected...
                ' Could either replace characters or return empty
                Return ""
            End If
        Next
        Return fileName
    End Function
You can call this function like:

If CheckValidFileName(m_string) = "" Then Throw New Exception
If an empty string is returned, you've got invalid characters in the file name...

Tuesday, August 2, 2011

Revit 2011 Categories that Do NOT Have Types

I've taken the liberty of listing each Revit 2011 category that does not contain types for anyone looking to impress their spouse with some pointless trivia (more for my own future reference than anything else).

Can anyone name the one category that only has types and no instances??....


AREAS
AREA LOADS
DUCT SYSTEMS
ELECTRICAL CIRCUITS
EXTERIOR
GBXML SURFACE
LEVELS
HVAC ZONES
INTERIOR
INTERNAL AREA LOADS
INTERNAL LINE LOADS
INTERNAL POINT LOADS
LINE LOADS
MASS EXTERIOR WALL
MASS FLOOR
MASS GLAZING
MASS INTERIOR WALL
MASS OPENING
MASS ROOF
MASS SKYLIGHT
MASS ZONE
MATERIALS
OPENING
PADS
PIPING SYSTEMS
POINT LOADS
PROJECT INFORMATION
ROADS
ROOMS
SHADE
SHAFT OPENINGS
SHEETS
SPACES
SWITCH SYSTEM
UNDERGROUND
VIEWS

FACIAS is the category that only contains types and no instances...

Monday, August 1, 2011

Finding the Centroid of a Room Boundary

It's been a while since my last post and I'm sure most of you were like... "Where the hell is Don!".... it's ok! I'm still around. I've been busy working on stuff I can't talk about. Don't worry though, I'm not a good secret keeper.

So this post is going to explain something that a bunch of folks have issues with that involves finding the actual centroid of a polygon, or in this case a Room element. Now let's be careful not to confuse a centroid with a pair of midpoints taken from the furthest X and Y planes... a centroid is more closely described as the center of mass within a polygon.


Now this is done by taking a series of 2D points and running some tricky math on them and dividing the points by 6x the area of the polygon. So to make it simple for you guys, I've taken the liberty of sharing a couple of functions that makes this all possible. The samples here are in Revit 2011 format.

First you'll need a function that iterates through the boundary segments of a Room Element and builds up a series of 2D points taken from either endpoints of each segment (no need to worry about curved segments since they usually wont effect the centroid too much, or you can add the midpoint of the curve arc to get it closer).

This little Function will return a list of 2D PointF from a boundary of a Room element.


''' <summary>
    ''' Extract a List of 2D Points from a Room's Boundary
    ''' </summary>
    ''' <param name="p_room"></param>
    ''' <remarks></remarks>
    Private Sub ExtractBoundaryPointsFromRoom(p_room As Room)
        ' The Points List
        Dim m_pts As New List(Of PointF)
        ' The Z Height
        Dim m_z As Double = 0
        ' Work with the Boundary
        Dim m_bsaa As Autodesk.Revit.DB.Architecture.BoundarySegmentArrayArray = m_room.Boundary
        ' Segment Array at Floor Level
        For Each bsa As Autodesk.Revit.DB.Architecture.BoundarySegmentArray In m_bsaa
            Try
                For Each bs As Autodesk.Revit.DB.Architecture.BoundarySegment In bsa
                    Dim m_c As Curve = bs.Curve
                    ' First Endpoint
                    Dim m_EndPoint1 As XYZ = m_c.EndPoint(0)
                    Dim m_PointF1 As New PointF(m_EndPoint1(0), m_EndPoint1(1))
                    m_pts.Add(m_PointF1)
                    ' Second Endpoint
                    Dim m_EndPoint2 As XYZ = m_c.EndPoint(1)
                    Dim m_PointF2 As New PointF(m_EndPoint2(0), m_EndPoint2(1))
                    m_pts.Add(m_PointF2)
                    ' The Height
                    m_z = m_EndPoint1(2)
                Next
            Catch ex As Exception

            End Try
        Next
        ' Return the 2D Centroid
        Dim m_2Dcentroid As PointF = FindCentroid(m_pts.ToArray, m_room.Area)
        ' Add the Floor Level of Boundary for Z Elevation
        InsertionPoint = New XYZ(m_2Dcentroid.X, m_2Dcentroid.Y, m_z)
    End Sub

The Function below will take a list of points (first gathered from the segments array of a room) and convert them to a real life centroid in 2D format. The Z elevation is pretty easy to figure out for a room and what ever you're using this for is typically going to use 0 or a preset elevation for the result anyway.


''' <summary>
    ''' Find 2D Centroid
    ''' </summary>
    ''' <param name="pts">Collection of Points Describing the Polygon</param>
    ''' <param name="p_rmArea">The Area of the Polygon</param>
    ''' <returns>2D Point (Pointf)</returns>
    ''' <remarks>This Function Kicks Ass</remarks>
    Private Function FindCentroid(ByVal pts() As PointF, p_rmArea As Single) As PointF
        ' Add the First PT to the End of the Array (full circulation)
        ReDim Preserve pts(pts.Length)
        pts(pts.Length - 1) = New PointF(pts(0).X, pts(0).Y)
        ' Get the Centroid
        Dim X As Single = 0
        Dim Y As Single = 0
        Dim m_sf As Single
        ' This is Where the Magic Happens
        For i As Integer = 0 To pts.Length - 2
            m_sf = pts(i).X * pts(i + 1).Y - pts(i + 1).X * pts(i).Y
            X += (pts(i).X + pts(i + 1).X) * m_sf
            Y += (pts(i).Y + pts(i + 1).Y) * m_sf
        Next i
        ' Divide by 6X the Are of the Polygon
        X /= (6 * p_rmArea)
        Y /= (6 * p_rmArea)
        ' This is the Final Result
        Return New PointF(X, Y)
    End Function

That's all until next time...

Tuesday, July 5, 2011

Che-che-che- Changes....

It's been a very busy year so far and some rumors have surfaced. I have indeed officially moved on into a new roll with a new company that some may refer to as a start up. I prefer to think of us as an industry changing leader of the AEC community with very deep roots.

Who are we, you ask? Here's a clue:
I will be serving this fine group of super powered individuals as their new CTO and look forward to working with you all in one form or another before it's all said and done...

Be sure and contact me if you have any needs for AEC development, no matter how large or small. We will be focusing on Revit and Navisworks mostly. You should also keep an eye out for a few game changing mobile apps from us to hit the streets soon as well!

This concludes today's public service announcement!

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