Here are our topics for today:
ReferenceIntersector
in linked filesA new quick, high-level, non-technical overview of Autodesk Forge is now available on YouTube, the 90-second What is Forge introduction video. This short introduction showcases the endless possibilities and some innovative solutions and workflows built with it. Featured footage includes demos from Moicon, CADshare, Xinaps, Project Frog, and InsiteVR.
Question: How can I open an RVT file managed by BIM360 locally using the Revit API, e.g., using OpenAndActivateDocument
?
Answer: What exactly do you mean by RVT file managed by BIM 360 locally?
Is the file stored in %localappdata%\autodesk\revit\CollboarationCache, coming from RCM?
Response: I am using BIM360 to manage the RVT file. I would now like to add some local processing using full Revit plus my custom add-in. Of course, I could use the Forge Data Management API to download it, but I hope there is some way to achieve this directly using the Revit API.
Answer: Currently, Revit only supports opening RCM-based cloud models. Revit doesn’t yet support opening files directly uploaded to BIM360.
Response: What if the file was uploaded to BIM360 via RCW?
Does the Revit API provide access to it then?
Answer: If the file was uploaded to BIM360 via RCW, officially termed as Initiate, and only accessible through the Revit UI and API, then yes, it can be opened via OpenAndActivateDocument
.
Here is a documentation on opening a BIM360 file in Design Automation; note that this file is an eTransmitted Workshared file.
If DA is not an option here, the right overload of to use in the Revit API is the UIApplication
method OpenAndActivateDocument
taking the arguments ModelPath
, OpenOptions
, Boolean
and IOpenFromCloudCallback
.
You will also have to call the ModelPathUtils
method ConvertCloudGUIDsToCloudPath
taking String
, Guid
, Guid
first.
The two guids are the project id and model id used by RCM to identify the project and model.
For an example, please refer to the discussion
on how to get project Guid and model Guid from PathName
.
Question: I would like to program using the Robobat API.
The documentation in RSA 2020 suggests using Visual Studio 2008. The current version of Visual Studio is 2020.
Can you confirm what version of Visual Studio I can use for programming the Robobat API?
Answer: The Robot SDK can be installed from any version the standard Robot Structural Analysis installer.
Select Tools and Utilities followed by Autodesk Robot Structural Analysis Professional SDK.
The SDK includes the document Getting Started Guide Robot API.pdf with information about Visual Studio.
The statement about VS 2008 should be more precise and should in fact suggest VS2008 and all later versions.
A few other things worth knowing:
Diving in a bit deeper into the Revit API,
Ilia Ivanov shares a nice example
of using ReferenceIntersector
in linked files:
I faced the problem of how to use a ReferenceIntersector
with RevitLinkInstance
elements.
I achieved a solution using filters etc. that works rather well and I would like to share with the community.
My task was to add opening family instances to each wall intersected by a crossing pipe.
I decided to implement that using IUpdater
.
My solution worked well with non-linked walls, using filters to make the ReferenceIntersector
find only walls.
However, when I started preparing the solution for linked walls, I faced a problem: references from the list ReferenceWithContext
contain the id of a RevitLinkInstance
and not the id of the target wall, so I couldn't gather the linked walls.
I looked through the post
on using ReferenceIntersector
in linked files and
found that we can't get the references from a linked file.
Debugging my code further, I realized a solution.
The Reference
class has a property LinkedElementId
.
Given a RevitLinkInstance
and the property of the element in the linked file we can retrieve the element in the linked file.
But, when my pipe crosses the linked wall, the ReferenceWithContext
list contains some amount of elements with the same ids (LinkedElementId, Id), maybe because it also gathers geometry like faces etc.
To distinguish the values in this list, I had to create a custom EqualityCompare
.
Finally, the code worked perfectly.
Below is the method to gather count of intersected walls.
Note that you need to run it in a 3D view without section boxes achieve good results.
public void GetWalls( UIDocument uidoc ) { Document doc = uidoc.Document; Reference pipeRef = uidoc.Selection.PickObject( ObjectType.Element ); Element pipeElem = doc.GetElement( pipeRef ); LocationCurve lc = pipeElem.Location as LocationCurve; Curve curve = lc.Curve; ReferenceComparer reference1 = new ReferenceComparer(); ElementFilter filter = new ElementCategoryFilter( BuiltInCategory.OST_Walls ); FilteredElementCollector collector = new FilteredElementCollector( doc ); Func<View3D, bool> isNotTemplate = v3 => !(v3.IsTemplate); View3D view3D = collector .OfClass( typeof( View3D ) ) .Cast<View3D>() .First<View3D>( isNotTemplate ); ReferenceIntersector refIntersector = new ReferenceIntersector( filter, FindReferenceTarget.Element, view3D ); refIntersector.FindReferencesInRevitLinks = true; IList<ReferenceWithContext> referenceWithContext = refIntersector.Find( curve.GetEndPoint( 0 ), (curve as Line).Direction ); IList<Reference> references = referenceWithContext .Select( p => p.GetReference() ) .Distinct( reference1 ) .Where( p => p.GlobalPoint.DistanceTo( curve.GetEndPoint( 0 ) ) < curve.Length ) .ToList(); IList<Element> walls = new List<Element>(); foreach( Reference reference in references ) { RevitLinkInstance instance = doc.GetElement( reference ) as RevitLinkInstance; Document linkDoc = instance.GetLinkDocument(); Element element = linkDoc.GetElement( reference.LinkedElementId ); walls.Add( element ); } TaskDialog.Show( "Count of wall", walls.Count.ToString() ); } public class ReferenceComparer : IEqualityComparer<Reference> { public bool Equals( Reference x, Reference y ) { if( x.ElementId == y.ElementId ) { if( x.LinkedElementId == y.LinkedElementId ) { return true; } return false; } return false; } public int GetHashCode( Reference obj ) { int hashName = obj.ElementId.GetHashCode(); int hashId = obj.LinkedElementId.GetHashCode(); return hashId ^ hashId; } }
Unfortunately, no time to add comments to the code.
Hope it may help somebody.
Here is reference_intersector_in_linked_files.zip containing a sample project with a macro to test.
It includes two Revit files:
The latter hosts the former as a linked file and contains a macro module named CountOfIntersectedWalls
defining the method GetWalls
listed above:
In case of need, here is also a code snippet to get a StableRepresentation
for a linked wall's exterior face:
public string GetFaceRefRepresentation( Wall wall, Document doc, RevitLinkInstance instance ) { Reference faceRef = HostObjectUtils.GetSideFaces( wall, ShellLayerType.Exterior ).FirstOrDefault(); Reference stRef = faceRef.CreateLinkReference( instance ); string stable = stRef.ConvertToStableRepresentation( doc ); return stable; }
Many thanks to Ilia for this nice solution and very helpful explanation!