Today, let's talk about the life span of Revit geometry and accessing the IFC GUID of an imported element:
First, I'll just mention that I returned safe and sound from the successful fourth and last day of the Milano WebGL workshop, which we concluded with a group session guiding through the View and Data API tutorial.
I wrote is a short report on that and published it together with a few other current topics on The 3D Web Coder, several of which might be of equal interest to you here as well:
- WebGL workshop report
- Working at Autodesk
- Resolving the link offset error caused by the Snap responsive design menu bar
I took a break in the middle of the train trip from Milano back home to Switzerland to meet up with a friend in Frutigen for a hike up the mountains.
We were fortunate enough to be able to spend one night out in the open, enjoy nature, space, tranquility and the light of the half moon, followed by a beautiful day hiking over the First mountain, providing panorama views over the Bernese alps, overlooking Kandersteg village and the Oeschinensee halfway up (or down) the other side of the valley:
Here is my First mountain panorama view photo album.
Life Span and Cloning of Solids and Faces
Getting back to the Revit API, let's look at this issue raised by Miroslav Schonauer, Solution Architect in Autodesk Consulting.
Question: I’d like to cache a number of Face objects by analysing Wall/Floor Solids in a Transaction and then use these faces afterwards outside the transaction, i.e.:
FaceCollection instantiate -Trans.Start Loop Walls/Floors Geometry (Solids) FaceCollection populate End Loop -Trans.End FaceCollection use (just read-only geom.access to faces)
- Is this a supported scenario?
- If NOT, any suggestions other than having to do ALL my work within the Transaction?
- If YES, would it also work with "Dummy Transaction trick" (i.e., I modify Walls/Floors, regen, get faces from such modified Solids and then Abort the Transaction).
To give some more context, I need to find Wall and Floor faces without certain Generic Void family instances that cut them via InstanceVoidCutUtils – I have their element ids, so I can delete them in a temporary transaction – then potentially relocate or manipulate these void instances based on the 'virgin' wall geometry cached in the dummy transaction.
Answer: First, do you even need a transaction? It is not obvious from the code fragment that you need that.
Secondly, and also briefly, your data will not be valid after rolling back the transaction. That is not limited to faces. If you obtain objects within a transaction, then regenerate and roll back the transaction, the validity of your previously obtained objects is uncertain because they may have been scoped out.
It sounds like this is the scenario where you want to make a temporary change and analyse the results of the temporary change. You would have two options:
- Perform the analysis inside the scope of the transaction, before the rollback (but after regenerating the initial temporary change).
- Copy the results of the change so that they are not removed from scope. For example, we have GeometryElement.GetTransformed, which will make a copy of the input. You could pass it an identity transform and then have a copy of the element geometry which will not be affected by transaction scopes (you still need to ensure that you are keeping references to the parent object to avoid conflicts with garbage collection).
Response: I do need the transaction as I am making dummy-changes, as I explained in the description of the context.
Your answer confirms more or less what I suspected.
The reason why the option 1 is not feasible for me is that during my further actions I may need to perform valid editing transactions, so can’t do that within the dummy-one to be rolled back at the end.
Option 2 is what I really need, but I couldn’t find any way to clone faces into equivalent persistent objects. The hint about getting the clones of GeometryElement instances from which I can later retrieve faces (via drilling down its contained geometry objects) sounds like the way forward!
BTW, shame that there is no GetTransformed method on the GeometryObject itself, as that would simplify the process by directly making the clones of faces (and other primitives for other possible uses outside my requirements).
Answer: A Face is too granular to copy and preserve separately; it really represents the confluence of the mathematical definition of the Surface with surrounding Edges, so all of this context would need to be duplicated in order for you to do anything useful with it.
The smallest unit of Revit 3D geometry which could be preserved and used once the original context is gone would be the Solid. We do also have a Solid duplication routine in Revit 2016: SolidUtils.Clone() – so that could be another option, cf. cloning a solid and GeometryCreationUtilities for moving and copying solids.
Accessing the IFC GUID of an Imported Wall
Question: I have a wall defined in an IFC file with an id value of '3lDzp1LFjDqwXDAihsyNrA' like this:
#615 = IFCWALLSTANDARDCASE( '3lDzp1LFjDqwXDAihsyNrA', #42, '\X2\6A196E9658C1\X0\:(P)PC200:1185289', $, '\X2\6A196E9658C1\X0\:(P)PC200:794115', #587, #613, '1185289' );
How can I retrieve the IFC GUID '3lDzp1LFjDqwXDAihsyNrA' for this wall within the Revit model?
I see it listed in the user interface as the IfcGUID property:
Answer: This data is stored as a normal parameter on the associated element.
You can look it up by name using the standard
GetParameters methods, e.g. like this:
IList<Parameter> ps = elem.GetParameters( "IfcGUID" ); // or Parameter pGuid = elem.LookupParameter( "IfcGUID" ); string ifc_guid = pGuid.AsString();
The former is preferred, because it ensures you take care of multiple similarly named parameters, cf. replacing Element.get_Parameter by Element.GetParameters.
While we are at it, I'll also point out that you should absolutely avoid using the language dependent method of looking up parameters by name. You can use the corresponding built-in parameter enumeration value instead, for built-in parameters, or the GUID, for shared parameters. Looking up a parameter by display name is only necessary for custom family parameters.
In this case, the built-in parameters IFC_GUID and IFC_TYPE_GUID will let you access the values for instances and types, respectively, e.g., like this:
Parameter pGuid = e.get_Parameter( BuiltInParameter.IFC_GUID ); string ifc_guid = pGuid.AsString();