Geometry Options and Clean Simple Curves

Today, we look at important aspects of geometry handling and other useful stuff:

Curve Loop Simplify and Clean Up

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:

On Wikipedia:

Many thanks to Benoit for the interesting pointers!

Geometry Options Clarification

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.

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.

JtClicker 2023

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?

JtClicker handles it

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.

OpenMEP by Chuong Ho

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.

Unintelligible Film Dialogue

Completely removed from programming and the Revit MEP, I enjoyed this 11-minute analysis of why we all need subtitles now.