Thursday, 26 February 2009

Virtual Desktops

I had a pint with a friend last week who keeps extolling the virtues of the Macintosh. One of his big plus points was the multiple desktops offered by the Unix OS.

Multiple desktops are indeed great for the busy multi-tasking programmer, but using a Mac clearly isn't an option for us Revit lot. So I dabbled with a couple of  'virtual desktop' apps for my XP machine and have found VirtuaWin to be pretty cool.

Now I can separate comms, development, support etc. A lot neater!

Monday, 23 February 2009

Looping through selected elements

Fred emailed and asked about how to iterate through a selection set and make a modification to each element in that set, in particular regarding adding a leader to selected text notes.

The API provides the ElementSetIterator class. I used its method MoveNext in a While loop, and this takes us through each selected element, on which you can then test for type and act accordingly.

Public Sub LoopSelectedElements()

Dim activeDoc As Document = revitApp.ActiveDocument

Dim selectionIterator As Autodesk.Revit.ElementSetIterator
selectionIterator = revitApp.ActiveDocument.Selection.Elements.ForwardIterator

'loop through iterator
While selectionIterator.MoveNext

Dim currElement As Autodesk.Revit.Element = selectionIterator.Current

'see what elements we've found

'check for type and make mods
If TypeOf currElement Is Autodesk.Revit.Elements.TextNote Then
Dim textNote As Autodesk.Revit.Elements.TextNote = currElement
End If

End While

End Sub

I didn't really test the textNote.AddLeader part. Fred reports that it works but the new leader only becomes visible when you move the textnote. Something to look into when I've got a spare five mins :)

Friday, 20 February 2009

Simple menu and toolbar in VB

The focus on C# in the Revit SDK code samples can be a hindrance to those of us who prefer VB. Yesterday I had a request from a reader (or is it a viewer? subscriber? follower? what do you call someone that visits your blog?) asking for a sample of VB code that creates a simple menu and toolbar. Well, here's some that should do the trick:

Imports System
Imports System.Collections.Generic
Imports System.Text

Imports Autodesk.Revit

Public Class EdsToolbar
Implements Autodesk.Revit.IExternalApplication

Public Function OnShutdown(ByVal application As Autodesk.Revit.ControlledApplication) As Autodesk.Revit.IExternalApplication.Result Implements Autodesk.Revit.IExternalApplication.OnShutdown

Return Autodesk.Revit.IExternalApplication.Result.Succeeded

End Function

Public Function OnStartup(ByVal application As Autodesk.Revit.ControlledApplication) As _
Autodesk.Revit.IExternalApplication.Result Implements Autodesk.Revit.IExternalApplication.OnStartup


'custom tool bar with buttons
Dim toolBar As Autodesk.Revit.Toolbar = application.CreateToolbar()
toolBar.Name = "Ed's Tools"

'image for toolbar, set to nothing by default
Dim imagePath As String = ""
toolBar.Image = imagePath

Dim menuItem As MenuItem = application.CreateTopMenu("Ed's Tools")
Dim menuItem1 As MenuItem = menuItem.Append(menuItem.MenuType.BasicMenu, "Open Working Directory", "C:\Program Files\Revit Architecture 2009\Program\EdPittOpenWrkDir.dll", "EdPittOpenWrkDir.OpenWD")

Dim item As ToolbarItem = toolBar.AddItem("C:\Program Files\Revit Architecture 2009\Program\OpenWorkingDirectory.dll", "OpenWorkingDirectory.OpenWorkingDirectory")
item.ItemType = ToolbarItem.ToolbarItemType.BtnRText
item.StatusbarTip = "Open Working Directory"
item.ToolTip = "Open Working Directory"
item.ItemText = "Open Working Directory"

Catch ex As Exception
Return IExternalApplication.Result.Failed
End Try

Return IExternalApplication.Result.Succeeded

End Function

End Class

Create a dll from this, and then in your .ini file drop in the following:

EAAssembly1=C:\Program Files\Revit Architecture 2009\Program\EdsTools.dll

Obviously if you already have some External Applications you need to modify this to suit.

Monday, 16 February 2009

Creating a new drafting view

I saw a query on the discussion groups about creating a draft view and setting the title on sheet, so I thought I'd have a stab at it and make a little post-ette on t'blog.

Dim newView As ViewDrafting = revitApp.ActiveDocument.Create.NewViewDrafting

newView.ViewName = viewName
newView.Scale = viewScale
newView.Parameter("Title on Sheet").Set(sheetName)

And of course if you want to create other types of view these are the available methods:

Document.NewView3D - Creates a new 3D view.
Document.NewViewDrafting - Creates a new drafting view.
Document.NewViewPlan - Creates a plan view based on the specified level.
Document.NewViewSection - Creates a new section view.
Document.NewViewSheet - Creates a new sheet view.

Thursday, 5 February 2009

Calculating building height - again

My last post was an attempt to calculate building height, but I quickly realised after posting (and luckily before being rumbled by some smart alec) that what I was doing was calculating the height at which the uppermost level was placed. Any thing on the uppermost level was being ignored.

So, here's another idea, and it assumes that your building has a roof at the top, which I'm sure in most cases is a fair assumption(?). It uses a category filter to look for roofs and then it queries the Max Z value of the BoundingBox of the roof:

Dim element As Autodesk.Revit.Element
Dim zValue As String

'active view used later for boundingbox
Dim activeView = revitApp.ActiveDocument.ActiveView

'get all elements in the Roof category
Dim CatFilter As Autodesk.Revit.Filter
CatFilter = revitApp.Create.Filter.NewCategoryFilter(Autodesk.Revit.BuiltInCategory.OST_Roofs)

Dim result As New List(Of Autodesk.Revit.Element)
Dim NumLevels As String = revitApp.ActiveDocument.Elements(CatFilter, result)

For Each element In result

'change this to footprintRoof etc if you wish            
If TypeOf element Is Elements.ExtrusionRoof Then

If element.BoundingBox(activeView).Max.Z > zValue Then
    zValue = element.BoundingBox(activeView).Max.Z
End If

End If


You could of course remove the 'If TypeOf' conditional and replace it with a try catch which would be a cheeky way of looking for the Max.Z in every element that the roof category filter returns, catching those that don't support it, and returning values for those that do. This would then return the Max Z value for all types of roof in your model.

In fact that's given me an idea. How about this, this will scan through every element in your model and return the largest Z value it can find. Is this the height of your building? :

Dim elementIterator As Autodesk.Revit.ElementIterator
elementIterator = revitApp.ActiveDocument.Elements

Dim topElement As Autodesk.Revit.Element
Dim element As Autodesk.Revit.Element
Dim zValue As Double
zValue = 0
Dim activeView = revitApp.ActiveDocument.ActiveView

While (elementIterator.MoveNext())

 element = elementIterator.Current

  If element.BoundingBox(activeView).Max.Z > zValue Then
   zValue = element.BoundingBox(activeView).Max.Z
   topElement = element
  End If
 Catch ex As Exception
 End Try

End While

WriteOutput("The highest Z value is: " & zValue)
WriteOutput("The element is: " & topElement.Name)
WriteOutput("The element ID is: " & topElement.Id.Value)

The answer is no, I just tried this last idea on the Revit training sample c_Condo_Complex.rvt and of course it takes a long time to run, but it gave me the value for the 3D view. If you look for all elements, they won't all be part of your building!

So stick with the first idea above. I bet there'll be a comment in soon telling me there's a really easy way to do this, like building.height :)

Wednesday, 4 February 2009

Calculating building height

I saw a query on the Revit API discussion groups about how to calculate the height of a building, so I thought I'd have a quick go. Admittedley they wanted it in C#, but I like my VB.

Loop through your elements looking for a level type, and when you find one use the level.ProjectElevation property to get its height - in decimal feet of course. At first I added these up to get total height, but then realised that ProjectElevation returns the elevation of the level relative to the project origin, so all we want is the highest figure we can find of all our levels.

Dim elementIterator As Autodesk.Revit.ElementIterator
elementIterator = revitApp.ActiveDocument.Elements
Dim height As Double

While (elementIterator.MoveNext())

'look for level types
If TypeOf elementIterator.Current Is Autodesk.Revit.Elements.Level Then

Dim level As Elements.Level = elementIterator.Current

'is current value higher than previous values?
If level.ProjectElevation > height Then
    height = level.ProjectElevation
End If

End If

End While

MessageBox.Show("Height: " & height)

UPDATE: This only retrieves the elevation of the uppermost level, which isn't the height of the building - see my next post for a different approach!