UV, Emergence, Fuzz and the get_ Prefix

Here are some recent topics of interest that came up in the Revit API discussion forum and elsewhere:

Switch Metric + Imperial Units

The Revit Idea Station wish list entry to change project units between imperial and metric with one button click has already been addressed by providing the UnitMigration add-in solution and source code. However, since some people lack the possibility to compile it, I shared updated compiled .NET assemblies for Revit 2021 and Revit 2023 that can be downloaded and installed right out of the box.

What is UV?

Angelo Mastroberardino commented on Planes, Projections and Picking Points and the revitapidocs ComputeDerivatives method asking:

Question: What is the point UV? What is its reference frame and origin? Are these UV the same (x,y) coordinates of any XYZ points of the Face.EdgeLoops without the z-coordinate?

Answer: The best place to start understanding UV coordinates is Scott Conover's 2010 AU class on Analysing Building Geometry, spread out over ten or so blog posts.

Read that and all will be clear. Drilling down into further detail and practical application, here is an article explaining the relationship between 2D UV and 3D XYZ coordinates.

To round off, here is a nice debugging explanation showing how you can use AVF to document and label the UV coordinates.

What is Fuzz?

Confusion around comparison of floating point values persists, and came up again in the question on analytical node vs analytical member coordinate accuracy:

Question: I came across a puzzling inconsistency with the new Analytical Members vs Analytical Nodes. It seems that somewhere in the guts of Revit, the geometry curve end point coordinates that get reported get mysteriously rounded, but the ReferencePoint coordinates do not. In my case, the AnalyticalNode ReferencePoint X Coordinate is 100.000002115674, but the AnalyticalMember Line.EndPoint(0) sees it's X coordinate as 100.000000000. I'm no expert, but that seems bad to me; even though it's very small numbers, any operation looking for A==B is not going to have any success unless someone knew ahead of time that they needed to go on and round down to 5 decimal places before doing a comparison.

Answer: Yes. In fact, anyone experienced in the comparison of real floating-point numbers does already know that you need to perform some rounding operation when comparing any and all such numbers on a digital computer. That is standard. The Building Coder quite regularly repeats the need for fuzz.

In the case of Revit, matters are even worse than in some other areas, since the Revit database represents property values and dimensions such as length using float instead of double. Hence, the need to think big in Revit and ignore every deviation below a certain (quite large) tolerance as irrelevant to the BIM, any "length below about 0.004 feet, i.e. ca. 0.05 inches or 1.2 millimetres".

Personally, when I retrieve vertex or coordinate data from Revit, I simply round it to the closest millimetre while retrieving it. I add every vertex or coordinate data item as a key to a dictionary. Every new item is looked up in the dictionary and considered equal to the existing item if it lies within a millimetre of it.

So, in my case, A==B if A-B is smaller than 1 mm.

What is get_Parameter and get_Geometry?

For a final FAQ reiteration, we address what's the deal with get_Parameter:

Question: This is more of a request for a history lesson here about the Revit API, but what's the deal with the method get_Parameter? I've seen it used in past forum posts for use in Revit versions before 2022, but I don't see any references to it in the revitapidocs. Does anyone know where it stems from (the API, another API, the enchanted jungle of BIM?) and if still a valid method to use today in lieu of the GetParameter method (seems far easier to just use get_Parameter with a GUID vs GetParameter.

Answer: The GetParameter method is only available in the family definition environment, working with an RFA. The get_ prefix is automatically generated by .NET to enable a C# client to pass in arguments to a property call, making the Element.Parameter property with its various overloads taking a GUID, a parameter id or a parameter definition look like a method instead of a property.

In C#, the same prefix is also added to the Geometry property, as explained by Maxim architect.bim Stepannikov in ImportInstance Geometry vs get_Geometry:

Question: I am working thru some sample code to get the geometry from an AutoCAD import instance and I have a question about the get_Geometry call. My understanding is that the code gets an ImportInstance element and then calls get_Geometry to get the geometry. However, when I look at the Revit API docs for this object, I see only a Geometry property, not a get_Geometry property or method. Shouldn't the code call the Geometry property, since that is what the API says is available? What am I missing?

Also, when I am working in the Revit Python Shell app, the autocomplete shows that the Geometry property is available for use, but not get_Geometry. If I try to use the Geometry property, I get an error, but if I try get_Geometry, things work. So, why is get_Geometry not listed? And why does Geometry return an error, TypeError: indexer# is not callable?

Answer: I you look into the documentation, you can see that Geometry is actually an indexer. To get its value, you need to specify the index in square brackets:

# Python sample
geometry = import.Geometry[Options()]

But also any property, including the indexer, can be replaced by a method with a get prefix. In this case, the value is passed in parentheses, not square brackets. This is not mentioned in the Revit API documentation, as it is the basic syntax of IronPython and C# languages:

# Python sample
geometry = import.get_Geometry(Options())

Response: Thank you for the response and for including the link to Indexers. So, after reading the material on Indexers, I see that it is the word "this" (and maybe the square brackets) in the Revit API that indicates that ImportInstance.Geometry is an indexer. Since I don't know C# and barely remember the original C language, it was certainly something that flew over my head.

public GeometryElement this[
  Options options
] { get; }

Is it correct to say, the statement y = ImportInstance.Geometry[Options()] returns a value based on the index presented by Options()? Put another way, if I had bucket of misc fruit with an indexer attached to it and wrote y = FruitBucket["apple"], the indexer would return an object associated with index of "apple" – that is, an Apple object?

Also, if there is a get_ prefix, would there also be a set_ prefix in IronPython?

Finally, would anyone know of a good book, or online link, that would discuss this _get prefix in more detail?

Answer: Here is a nice little booklet on the topic .net get_ property prefix for you to print at your leisure...   :-)

Default Localised Workset Names

Julian Wandzilak raised a thread on doc.EnableWorksharing & language versions asking for all the different language-specific default names of the two predefined default worksets. Unfortunately, these are not provided by the Revit API, so he went ahead and implemented his own, which I also added to The Building Coder samples:

  1. /// <summary>
  2. /// Return default workset names
  3. /// for all languages supported by Revit
  4. /// </summary>
  5. /// <param name="sLanguage">`app.Language.ToString()`</param>
  6. /// <returns>`false` if no valid language input argument provided, else `true`</returns>
  7. bool GetDefaultWorksetNames(
  8.     string sLanguage,
  9.     out string wsnLevelsAndGrids,
  10.     out string wsnWorkset1 )
  11. {
  12.     wsnLevelsAndGrids = string.Empty;
  13.     wsnWorkset1 = string.Empty;
  14.  
  15.     switch (sLanguage)
  16.     {
  17.         case "Unknown":
  18.             wsnLevelsAndGrids = "Shared Levels and Grids";
  19.             wsnWorkset1 = "Workset1";
  20.             break;
  21.         case "English_USA":
  22.             wsnLevelsAndGrids = "Shared Levels and Grids";
  23.             wsnWorkset1 = "Workset1";
  24.             break;
  25.         case "German":
  26.             wsnLevelsAndGrids = "Gemeinsam genutzte Ebenen und Raster";
  27.             wsnWorkset1 = "Bearbeitungsbereich1";
  28.             break;
  29.         case "Spanish":
  30.             wsnLevelsAndGrids = "Niveles y rejillas compartidos";
  31.             wsnWorkset1 = "Subproyecto1";
  32.             break;
  33.         case "French":
  34.             wsnLevelsAndGrids = "Quadrillages et niveaux partagés";
  35.             wsnWorkset1 = "Sous-projet 1";
  36.             break;
  37.         case "Italian":
  38.             wsnLevelsAndGrids = "Griglie e livelli condivisi";
  39.             wsnWorkset1 = "Workset1";
  40.             break;
  41.         //case "Dutch":
  42.         //  wsnLevelsAndGrids = "Shared Levels and Grids";
  43.         //  wsnWorkset1 = "Workset1";
  44.         //  break;
  45.         case "Chinese_Simplified":
  46.             wsnLevelsAndGrids = "共享标高和轴网";
  47.             wsnWorkset1 = "工作集1";
  48.             break;
  49.         case "Chinese_Traditional":
  50.             wsnLevelsAndGrids = "共用的樓層和網格";
  51.             wsnWorkset1 = "工作集 1";
  52.             break;
  53.         case "Japanese":
  54.             wsnLevelsAndGrids = "共有レベルと通芯";
  55.             wsnWorkset1 = "ワークセット1";
  56.             break;
  57.         case "Korean":
  58.             wsnLevelsAndGrids = "공유 레벨 및 그리드";
  59.             wsnWorkset1 = "작업세트1";
  60.             break;
  61.         case "Russian":
  62.             wsnLevelsAndGrids = "Общие уровни и сетки";
  63.             wsnWorkset1 = "Рабочий набор 1";
  64.             break;
  65.         case "Czech":
  66.             wsnLevelsAndGrids = "Sdílená podlaží a osnovy";
  67.             wsnWorkset1 = "Pracovní sada1";
  68.             break;
  69.         case "Polish":
  70.             wsnLevelsAndGrids = "Współdzielone poziomy i osie";
  71.             wsnWorkset1 = "Zadanie1";
  72.             break;
  73.         //case "Hungarian":
  74.         //  wsnLevelsAndGrids = "Shared Levels and Grids";
  75.         //  wsnWorkset1 = "Workset1";
  76.         //  break;
  77.         case "Brazilian_Portuguese":
  78.             wsnLevelsAndGrids = "Níveis e eixos compartilhados";
  79.             wsnWorkset1 = "Workset1";
  80.             break;
  81.         case "English_GB":
  82.             wsnLevelsAndGrids = "Shared Levels and Grids";
  83.             wsnWorkset1 = "Workset1";
  84.             break;
  85.         default:
  86.             wsnLevelsAndGrids = "Shared Levels and Grids";
  87.             wsnWorkset1 = "Workset1";
  88.             break;
  89.     }
  90.     return 0 < wsnLevelsAndGrids.Length;
  91. }

Additional comments on this by Julian: I decided to implement a default language checker returns names for the two default worksets. It was a rather tedious job, so please find it above. I haven't double-checked all of them, so please report if something is wrong. There are some interesting findings in it.

There is an "Unknown type" – I didn't know if I should add it (default in a switch would cover it). In the end, I left it in the code. What should we do to get this type from Revit API?

We have two language types in the Revit API, Hungarian and Dutch, that are not listed in the language help. I assume that those language versions are being developed, so, for now, I commented them out in the code.

Also, it is interesting that some translators decided to keep Workset1 as default – we have it not only in English, but also in Italian and Brazilian Portuguese. I like this approach because, in my opinion, the Polish translation Zadanie1 is rather bad and confusing; it means something closer to "task". Because of that, I have seen some projects where worksets were named "Tasks of Adam", "Tasks of Kate", etc.

The other interesting thing is lack of consistency regarding the number 1. In most languages, there is no extra space between Workset and 1, but in Russian, French and traditional Chinese there is. Chinese is double interesting, because in simplified Chinese, we don't have the empty space.

It made me wonder about how Revit is treating languages – I knew before that if for example you load a family (with family types in text file) from different language libraries you will get an error (because of a translation of default parameters like height/width/ length). But you can open a project in a correct language, then load families into project and after opening again in your previous language you will be fine most of the time.

Is it possible to get parameter name in a currently used language? That would be super useful for translations etc.

Also, I started to look around and decided to test the levels – I was slightly disappointed to learn that Level.Create(doc, elevation) adds a level with a name similar to the last created or last renamed (whatever happened last).

It works this way even if you decide to delete all the levels in a project – it is not easy, but I managed to delete them manually. After doing it, you might get into a problem of creating new levels. And here it starts to be interesting. If you add a level using Revit API, Revit still keeps the same way of naming the levels – for example, you will get Level 3 (after deleting Level 1 and 2). But if you go to View/plan views/ you will get a pop-out which gives you a default Level 0 / Poziom 0 (I tested it also in Polish).

That would mean that Revit saves somewhere the name of the next level to create, and at the same time somewhere keeps the default level values. Enough nerding for today.

Many thanks to Julian for taking on this laborious task and sharing his interesting thoughts and results!

Bing Chat Python and Dynamo Tutor

Looking at some recent AI and LLM developments, Eric Boehlke shares some of his interesting experiences programming Python and Dynamo with Bing chat AI as helper.

Claude on Slack Summarises and Answers Questions

Claude, now in Slack is an app that can summarise threads, answer questions, and more, with a focus on reliability, albeit including a caveat...

Sparks of Artificial General Intelligence

My most interesting read last week was the 154-page (101 + appendix) paper by Microsoft Research presenting some fascinating capability assessments and discussing the open question of emergence:

Sparks of Artificial General Intelligence: Early experiments with GPT-4

Artificial intelligence (AI) researchers have been developing and refining large language models (LLMs) that exhibit remarkable capabilities across a variety of domains and tasks, challenging our understanding of learning and cognition. The latest model developed by OpenAI, GPT-4, was trained using an unprecedented scale of compute and data. In this paper, we report on our investigation of an early version of GPT-4, when it was still in active development by OpenAI. We contend that (this early version of) GPT-4 is part of a new cohort of LLMs (along with ChatGPT and Google's PaLM for example) that exhibit more general intelligence than previous AI models. We discuss the rising capabilities and implications of these models. We demonstrate that, beyond its mastery of language, GPT-4 can solve novel and difficult tasks that span mathematics, coding, vision, medicine, law, psychology and more, without needing any special prompting. Moreover, in all of these tasks, GPT-4's performance is strikingly close to human-level performance, and often vastly surpasses prior models such as ChatGPT. Given the breadth and depth of GPT-4's capabilities, we believe that it could reasonably be viewed as an early (yet still incomplete) version of an artificial general intelligence (AGI) system. In our exploration of GPT-4, we put special emphasis on discovering its limitations, and we discuss the challenges ahead for advancing towards deeper and more comprehensive versions of AGI, including the possible need for pursuing a new paradigm that moves beyond next-word prediction. We conclude with reflections on societal influences of the recent technological leap and future research directions.

One aspect that especially caught my attention was the topic of emergence that is currently being observed (surprisingly) in the LLMs:

emergence occurs when an entity is observed to have properties its parts do not have on their own, properties or behaviours that emerge only when the parts interact in a wider whole.

Emergence in snowflakes