I was presented with an opportunity earlier this year to contribute to a book project that a coworker of mine in our New York office was working on by the name of James Vandezande, maybe you've heard of him?... He was looking for some help in getting an API chapter put together for his book entitled "Mastering Autodesk Revit Architecture 2011"
It sounded like a pretty cool opportunity, so I whipped up a quick 40 or so page chapter complete with a basic example for batch exporting Revit families... The chapter that I contributed is Chapter 24 "Under the Hood of Revit"...
The 1,122 page paperback book is out now and is available on Amazon!
Sunday, July 25, 2010
Friday, July 16, 2010
External Images to Individual Drafting Views
Have you ever had the need to generate hundreds or possibly thousands of drafting views to host photograph images for the purpose of tagging snapshot location and direction on a plan drawing???... Say maybe for a CA purpose?... or maybe for a facility assessment?... Well I was presented with such a problem and this is what I did to solve it since importing the several hundred photos into my model and generating the drafting views to go with them would have taken an eternity.
The way this app breaks down is that the user will be presented with a dialog that they can enter an override for the image width and/or height if they choose. There is a browse button that they will use to select multiple image files for import. After the use selects their images, Revit will generate a new drafting view named the same as the image file and then import the image into it, repeating until complete. The updates made to the Revit API in the 2011 products make this process capable of importing over 500 images in less than a minute (somehow)...
This code and any code I ever provide is of course provided as-is... use at your own risk.
This example utility utilizes one helper class to access and describe the parameter objects, one user form, and a main command class. First lets get the familiar main command entry point class out of the way...
The parameter helper class used to save data to the parameter when we find is provided below.
The user form is very simple and only contains a few basic controls. Follow the steps below to setup the form. I've added two buttons. One for browsing to the images and a cancel button. Two textboxes are placed to accept the user's optional dimension overrides for the imported images. A progress bar is then added along the bottom for process posting...
The form code is fairly simple as well. There are only a few basic subs and a few key form events to prevent the user from entering invalid characters into our textbox inputs (TextBoxHeight_KeyPress and TextBoxWidth_KeyPress).
We will utilize a method to retrieve the last element in the database in our image import loop so that we can easily retrieve our new image element for setting its width and or height overrides.
The way this app breaks down is that the user will be presented with a dialog that they can enter an override for the image width and/or height if they choose. There is a browse button that they will use to select multiple image files for import. After the use selects their images, Revit will generate a new drafting view named the same as the image file and then import the image into it, repeating until complete. The updates made to the Revit API in the 2011 products make this process capable of importing over 500 images in less than a minute (somehow)...
This code and any code I ever provide is of course provided as-is... use at your own risk.
This example utility utilizes one helper class to access and describe the parameter objects, one user form, and a main command class. First lets get the familiar main command entry point class out of the way...
Imports Autodesk.Revit.DB
Imports Autodesk.Revit.UI
Imports Autodesk.Revit.Attributes
''' <summary>
''' Import Images to Drafting Views
''' </summary>
<Transaction(TransactionMode.Automatic)> _
<Regeneration(RegenerationOption.Manual)> _
Public Class cmdDraftingViews
' Implement the RevitAPI entry
Implements IExternalCommand
' Post version date to title of form objects
Public Const appVer As String = "V2011.07.15"
' Main Command Entry
Public Function Execute(ByVal commandData As ExternalCommandData, _
ByRef message As String, _
ByVal elements As ElementSet) As Result _
Implements IExternalCommand.Execute
Try
Dim dlg As New formMain(commandData, appVer)
dlg.ShowDialog()
Return Result.Succeeded
Catch ex As Exception
Return Result.Failed
End Try
End Function
End Class
Imports Autodesk.Revit
Imports Autodesk.Revit.DB
''' Helper class to define a parameter
Public Class clsPara
Private m_parameter As DB.Parameter
Public Sub New(ByVal parameter As DB.Parameter)
m_parameter = parameter
End Sub
Public Property Value() As String
Get
Try
Return GetParameterValue(m_parameter)
Catch
Return Nothing
End Try
End Get
Set(ByVal value As String)
Try
SetParameterValue(m_parameter, value)
Catch
End Try
End Set
End Property
Public Shared Function GetParameterValue(ByVal parameter As Parameter) As String
Select Case parameter.StorageType
Case StorageType.[Double]
Return parameter.AsDouble
Case StorageType.ElementId
Return parameter.AsElementId.ToString
Case StorageType.[Integer]
Return parameter.AsInteger
Case StorageType.None
Return parameter.AsValueString()
Case StorageType.[String]
Return parameter.AsString()
Case Else
Return ""
End Select
End Function
Public Shared Sub SetParameterValue(ByVal parameter As Parameter, ByVal value As Object)
If parameter.IsReadOnly Then
Exit Sub
End If
Select Case parameter.StorageType
Case StorageType.[Double]
parameter.SetValueString(TryCast(value, String))
Exit Select
Case StorageType.ElementId
Dim myElementId As DB.ElementId = DirectCast((value), DB.ElementId)
parameter.[Set](myElementId)
Exit Select
Case StorageType.[Integer]
parameter.SetValueString(TryCast(value, String))
Exit Select
Case StorageType.None
parameter.SetValueString(TryCast(value, String))
Exit Select
Case StorageType.[String]
parameter.[Set](TryCast(value, String))
Exit Select
Case Else
Exit Select
End Select
End Sub
End Class
The user form is very simple and only contains a few basic controls. Follow the steps below to setup the form. I've added two buttons. One for browsing to the images and a cancel button. Two textboxes are placed to accept the user's optional dimension overrides for the imported images. A progress bar is then added along the bottom for process posting...
The form code is fairly simple as well. There are only a few basic subs and a few key form events to prevent the user from entering invalid characters into our textbox inputs (TextBoxHeight_KeyPress and TextBoxWidth_KeyPress).
We will utilize a method to retrieve the last element in the database in our image import loop so that we can easily retrieve our new image element for setting its width and or height overrides.
Imports Autodesk.Revit
Imports Autodesk.Revit.DB
Imports Autodesk.Revit.UI
Imports System.IO
Public Class formMain
Private m_SelectedJPG() As String
Private m_RvtDoc As DB.Document
Private m_width As Double
Private m_height As Double
Public Sub New(ByVal cmd As ExternalCommandData, ByVal apv As String)
InitializeComponent()
m_RvtDoc = cmd.Application.ActiveUIDocument.Document
Me.ProgressBar1.Visible = False
Me.Text = "Generate Drafting Views for Image Files - " & apv
End Sub
Private Function GetElementCount() As Integer
Return GetElements.ToElements().Count
End Function
Private Function GetElements() As DB.FilteredElementCollector
Dim collector As New DB.FilteredElementCollector(m_RvtDoc)
Return collector.WhereElementIsNotElementType()
End Function
' Return all database elements after the given number n.
Private Function GetElementsAfter(ByVal eInt As Integer, ByVal rvtDoc As Document) As List(Of DB.Element)
Dim elems As New List(Of DB.Element)
Dim fec As DB.FilteredElementCollector = GetElements()
Dim i As Integer = 0
For Each e As DB.Element In fec
i += 1
If eInt < i Then
elems.Add(e)
End If
Next
Return elems
End Function
Private Sub ImportImages()
' Collect the list of drafting views
Dim ViewsElements As New List(Of DB.Element)
Dim CollectorSheets As New DB.FilteredElementCollector(m_RvtDoc)
CollectorSheets.OfCategory(DB.BuiltInCategory.OST_Views)
ViewsElements = CollectorSheets.ToElements
' Start adding the views and importing the images
For i = 0 To UBound(m_SelectedJPG)
Dim imgFileName As String = Path.GetFileNameWithoutExtension(m_SelectedJPG(i))
' Only create the view if it does not already exist
For Each el As DB.Element In ViewsElements
If el.Name.ToUpper = imgFileName.ToUpper Then GoTo prepNextView
Next
' Only continue here if the view does not exist
Dim dv As DB.ViewDrafting = m_RvtDoc.Create.NewViewDrafting
dv.Name = imgFileName
' Link the image
ImportJPG(m_SelectedJPG(i), dv)
prepNextView:
Me.ProgressBar1.Increment(1)
Next
Me.Close()
End Sub
Public Function ImportJPG(ByVal m_importFileFullName As String, ByVal m_View As DB.ViewDrafting) As Boolean
' Get the last elementID
Dim eCnt As Integer = GetElementCount()
' Setup the image import options
Dim options As New ImageImportOptions
options.Placement = DB.BoxPlacement.Center
options.View = m_View
' Import the image
Dim element As DB.Element = Nothing
Dim imported As Boolean = m_RvtDoc.Import(m_importFileFullName, options, element)
' Test if user requires the width or height overriden
If m_height > 0 Or m_width > 0 Then
' Get the imported image element
Dim myElements As New List(Of DB.Element)
myElements = GetElementsAfter(eCnt, m_RvtDoc)
' Get the newest element
For Each e As DB.Element In myElements
' Test to see if it is a rasterimage
Try
If e.Category.Name.ToUpper = "RASTER IMAGES" Then
' Set the Width
Dim wParam As DB.Parameter = e.Parameter("Width")
Dim myWPara As New clsPara(wParam)
If m_width > 0 Then
myWPara.Value = m_width
End If
' Set the Height
Dim hParam As DB.Parameter = e.Parameter("Height")
Dim myEPara As New clsPara(hParam)
If m_height > 0 Then
myEPara.Value = m_height
End If
End If
Catch ex As Exception
End Try
Next
End If
Return imported
End Function
Private Sub ButtonSelectImages_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonSelectImages.Click
' Select the files
Me.OpenFileDialogJPG.ShowDialog()
If Me.OpenFileDialogJPG.FileNames.Count = 0 Then
MsgBox("Nothing to Process!", MsgBoxStyle.Critical, "Terminating")
Me.Close()
End If
m_SelectedJPG = Me.OpenFileDialogJPG.FileNames
With Me.ProgressBar1
.Minimum = 0
.Value = 0
.Maximum = Me.OpenFileDialogJPG.FileNames.Count
.Visible = True
End With
If Me.TextBoxWidth.Text <> "" Then
m_width = (Me.TextBoxWidth.Text / 12)
Else
m_width = 0
End If
If Me.TextBoxHeight.Text <> "" Then
m_height = (Me.TextBoxHeight.Text / 12)
Else
m_height = 0
End If
ImportImages()
End Sub
Private Sub ButtonCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonCancel.Click
Me.Close()
End Sub
Private Sub TextBoxHeight_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBoxHeight.KeyPress
Dim allowedChars As String = "0123456789."
If allowedChars.IndexOf(e.KeyChar) = -1 Then
' Invalid Character
e.Handled = True
End If
End Sub
Private Sub TextBoxWidth_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBoxWidth.KeyPress
Dim allowedChars As String = "0123456789."
If allowedChars.IndexOf(e.KeyChar) = -1 Then
' Invalid Character
e.Handled = True
End If
End Sub
End Class
Thursday, July 15, 2010
Embedded Schedules in Revit 2011
Some of you expert Revit Architects might be wondering just what in the heck I'm talking about when I mention "Embedded Schedules" in Revit. This feature is currently only available in Revit MEP 2011.
It is possible however to generate a nested schedule in Revit MEP for use in the Revit Architecture product... you just wont be able to modify the nested schedule unless you open the model in Revit MEP 2011...
Revit MEP 2011 supports nesting schedules beneath Rooms, Spaces, and MEP Systems. This basically allows the user to report elements in the model grouped by Room, Space, or System while showing whatever data they choose for the parent as well as the child elements... confused yet?
First start by creating a new Room schedule in Revit MEP 2011. Select the parameters that you want to see for rooms and then select the "Embedded Schedule" tab...
It is possible however to generate a nested schedule in Revit MEP for use in the Revit Architecture product... you just wont be able to modify the nested schedule unless you open the model in Revit MEP 2011...
Revit MEP 2011 supports nesting schedules beneath Rooms, Spaces, and MEP Systems. This basically allows the user to report elements in the model grouped by Room, Space, or System while showing whatever data they choose for the parent as well as the child elements... confused yet?
First start by creating a new Room schedule in Revit MEP 2011. Select the parameters that you want to see for rooms and then select the "Embedded Schedule" tab...
To really see how this feature works, set the "Number" parameter as "Group By" and select "Header" and "Blank Line"
Then in the "Embedded Schedule" tab, check both the "Embedded Schedule" and "Show categories from all disciplines" checkboxes. Now you can choose the category that you want to schedule within rooms. Click the "Embedded Schedule Properties" button to select the parameters and general schedule settings that you want to use for your embedded schedule.
And finally... here's what we end up with...
Subscribe to:
Posts (Atom)