Today, we look at important aspects of geometry handling and other useful stuff:
Benoit Favre, CEO of etudes & automates shares some interesting advice on how to simplify and clean up curve loops in the Revit API discussion forum thread on a boundary segments issue:
Funny to get this very old post alive.
I'd change my answer from the first time and now say:
BoundarySegment
list has gaps, e.g., around windows and at the end of walls ending in the middle of a room.
So, you have to close the gap; in practice, we add another segment to the list.On Wikipedia:
Many thanks to Benoit for the interesting pointers!
The question on how to find centroid of wall in Revit API
provided an opportunity to clarify the meaning of specific settings in the geometry Options
and the use of the NewGeometryOptions
method:
ComputeReferences
is
only needed if you require references to the geometry, e.g., for dimensioning purposes.
Furthermore, it adds computational effort.
Therefore, you should not set it to true unless needed, as explained in the 2010 article
on Geometry Options.
The effect of turning off ComputeReferences
was recently benchmarked in the discussion
on computing the correlation of objects in Revit:
It includes the final code and the benchmark results:
PC specs:
The call to BooleanOperationsUtils.ExecuteBooleanOperation(solidST, solidAR, BooleanOperationsType.Intersect) is triggered 113,696 times, both lists columnsSTR and columnsARC have 336 items each.
ComputeReferences
= true : 10.38 sec, AVG. 91.34 micro-seconds per intersection.ComputeReferences
= false : 9.52 sec, AVG. 83.76 micro-seconds per intersection.IncludeNonVisibleObjects
is only required for certain supplementary graphical elements, e.g.,
for curtain walls.
I am pretty sure that it is not required for such basic element geometry as solids.
So, I would leave both of those settings turned off in this case, set to their default value of false.
Furthermore, I doubt that there is any difference between using new Options
and app.Create.NewGeometryOptions
.
However, specifying a view argument in the options will definitely make a difference, depending on the view you supply.
That can be achieved using both new Options
and app.Create.NewGeometryOptions
.
Jacopo Chiappetti of One Team srl shared a new implementation of JtClicker to programmatically dismiss a UI warning message in the thread on some annotations, schedules, view templates, filters, and views related to analytical elements might be modified or lost during the upgrade process:
Question: In Revit 2023, every time I open a model that contains at least one analytical element, a warning is displayed at the end of the process to Upgrade the Analytical Model: "Some annotations, schedules, view templates, filters, and views related to analytical elements might be modified or lost during the upgrade process."
This error window doesn't seem to be trappable by code (i.e., Application_FailuresProcessing
) and blocks the automatic process of the model (custom code), after it is opened, until it's closed by the user.
Is there a way to eliminate the message or intercept it somehow?
Answer: Yes, definitely. If worst comes to worst, you can use the native Windows API to catch and dismiss this dialogue. Look at the various options listed in The Building Coder topic group on detecting and handling dialogues and failures.
Response: I cannot use Application.FailuresProcessing
because it doesn't trap this warning.
Also, I cannot use ControlledApplication.DialogBoxShowing
, as I use IExternalDBApplication
, not IExternalApplication
.
Hence, I have no access to any UI related functionality at all.
So, the only way seems using your JtClicker, isn't it?
I gave the .NET Dialogue Clicker in VB by Greg Wesner a try and it works with some small mods.
The loading process is slower, but finally I can trap the warning dialog and close it.
I really can't understand why this warning hasn't been included, along with all the others, in the FailuresProcessing
event and no possibility is given to disable it: really disheartening.
Answer: If you are operating in an interactive session of Revit, you should be able to use IExternalApplication. It is great to keep your code split validly between DB and UI levels; it allows the DB code to work with the APS Design Automation API. In that case, you might need a specific small UI Application subscribing to and dismissing just this notice.
Is that feasible for you? Or is the JtClicker approach easier?
Response: For many reasons, I prefer not to use UI, so it's easier to use the "JtClicker approach". Here is my implementation; it's very similar to Greg's version:
Public timer1 As Timer Public timer_interval As Integer = 1000 'millisecondi Public timer_attempts As Integer 'not used Public Const diagTitle As String = "Aggiornamento del modello analitico strutturale" Public Const diagButton As String = "&Chiudi" Public Function EnumWindowsProc(ByVal hwnd As Integer, ByVal lParam As Integer) As Boolean Dim sbTitle As New StringBuilder(256) Dim test As Integer = User32.GetWindowText(hwnd, sbTitle, sbTitle.Capacity) Dim title As String = sbTitle.ToString() If title.Length > 0 AndAlso title = diagTitle Then User32.EnumChildWindows(hwnd, New User32.EnumWindowsProc(AddressOf EnumChildProc), 0) Return False Else Return True End If End Function Public Function EnumChildProc(ByVal hwnd As Integer, ByVal lParam As Integer) As Boolean Dim sbTitle As New StringBuilder(256) User32.GetWindowText(hwnd, sbTitle, sbTitle.Capacity) Dim title As String = sbTitle.ToString() If title.Length > 0 AndAlso title = diagButton Then User32.SendMessage(hwnd, User32.BM_SETSTATE, 1, 0) User32.SendMessage(hwnd, User32.WM_LBUTTONDOWN, 0, 0) User32.SendMessage(hwnd, User32.WM_LBUTTONUP, 0, 0) User32.SendMessage(hwnd, User32.BM_SETSTATE, 1, 0) If Not timer1 Is Nothing Then timer1.Stop() timer1 = Nothing End If Return False Else Return True End If End Function Public Sub timer1_Elapsed(ByVal sender As Object, ByVal e As EventArgs) 'If timer_attempts < 3000 Then User32.EnumWindows(New User32.EnumWindowsProc(AddressOf EnumWindowsProc), 0) 'Else ' timer1.Stop() 'End If 'timer_attempts += 1 'Debug.Print(timer_attempts.ToString()) End Sub Public Sub closeOptionsDialog() timer_attempts = 0 If timer1 Is Nothing Then timer1 = New Timer() End If timer1.Interval = timer_interval AddHandler timer1.Elapsed, New ElapsedEventHandler(AddressOf timer1_Elapsed) timer1.Start() End Sub
The user32
module is identical:
Imports System Imports System.Runtime.InteropServices Imports System.Text Module User32 Delegate Function EnumWindowsProc(ByVal hWnd As Integer, ByVal lParam As Integer) As Boolean <DllImport("user32.dll", CharSet:=CharSet.Unicode)> Function FindWindow(ByVal className As String, ByVal windowName As String) As Integer End Function <DllImport("user32.dll", CharSet:=CharSet.Unicode)> Function EnumWindows(ByVal callbackFunc As EnumWindowsProc, ByVal lParam As Integer) As Integer End Function <DllImport("user32.dll", CharSet:=CharSet.Unicode)> Function EnumChildWindows(ByVal hwnd As Integer, ByVal callbackFunc As EnumWindowsProc, ByVal lParam As Integer) As Integer End Function <DllImport("user32.dll", CharSet:=CharSet.Unicode)> Function GetWindowText(ByVal hwnd As Integer, ByVal buff As StringBuilder, ByVal maxCount As Integer) As Integer End Function <DllImport("user32.dll", CharSet:=CharSet.Unicode)> Function GetLastActivePopup(ByVal hwnd As Integer) As Integer End Function <DllImport("user32.dll", CharSet:=CharSet.Unicode)> Function SendMessage(ByVal hwnd As Integer, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer End Function Public BM_SETSTATE As Integer = 243 Public WM_LBUTTONDOWN As Integer = 513 Public WM_LBUTTONUP As Integer = 514 End Module
Many thanks to Jacopo for sharing his solution and thoughts on this!
By the way, Kev_D added another aspect:
Question: Is there a non API solution to remove this?
Answer: I doubt it. But, of course, there are Windows automation tools that you can use. Standard Windows automation. They can click buttons for you, i.e., simulate the required user interaction to dismiss the dialogue.
Chuong Ho released OpenMEP, an awesome package for MEP and computational design inside Dynamo Revit.
The OpenMEP Package also includes a comprehensive library of MEP components, making it easy to select and incorporate the right components into your design. The library contains a wide range of mechanical, electrical, and plumbing components, including pipes, fittings, valves, ducts, electrical equipment, and more fully automate your design process in design, maintenance, calculation and analysis. I believe that the MEP Package will be a valuable asset to construction professionals looking to streamline the MEP design process and ensure that their projects are completed on time and within budget.
Completely removed from programming and the Revit MEP, I enjoyed this 11-minute analysis of why we all need subtitles now.