2424using BH . Engine . Adapters . Revit ;
2525using BH . Engine . Base ;
2626using BH . Engine . Geometry ;
27- using BH . Engine . Graphics ;
2827using BH . oM . Adapter ;
2928using BH . oM . Adapters . Revit ;
3029using BH . oM . Adapters . Revit . Enums ;
@@ -87,7 +86,8 @@ protected override IEnumerable<IBHoMObject> Read(IRequest request, ActionConfig
8786 }
8887 }
8988
90- Dictionary < Document , IRequest > requestsByLinks = request . SplitRequestTreeByLinks ( this . Document ) ;
89+ // Split the request into separate requests per each link model
90+ Dictionary < ElementId , IRequest > requestsByLinks = request . SplitRequestTreeByLinks ( this . Document ) ;
9191 if ( requestsByLinks == null )
9292 {
9393 BH . Engine . Base . Compute . RecordError ( $ "Pull failed due to issues with the request containing { nameof ( FilterByLink ) } . Please try to restructure the used Request and try again.") ;
@@ -96,12 +96,49 @@ protected override IEnumerable<IBHoMObject> Read(IRequest request, ActionConfig
9696
9797 RevitSettings settings = RevitSettings . DefaultIfNull ( ) ;
9898
99+ // Group links that hold the same document and have same transform
100+ // Addresses the case when there is a nested link being loaded via more than one parent link
101+ // Same document linked in multiple locations is being pulled per each location
102+ // Performance is not affected by multiple converts of same elements thanks to refObjects
103+ Dictionary < ( Document , Transform ) , List < IRequest > > requestsByDocumentAndTransform = new Dictionary < ( Document , Transform ) , List < IRequest > > ( ) ;
104+ foreach ( KeyValuePair < ElementId , IRequest > requestByLink in requestsByLinks )
105+ {
106+ Document doc ;
107+ Transform transform = Transform . Identity ;
108+ if ( requestByLink . Key . IntegerValue == - 1 )
109+ doc = this . Document ;
110+ else
111+ {
112+ var linkInstance = this . Document . GetElement ( requestByLink . Key ) as RevitLinkInstance ;
113+ doc = linkInstance . GetLinkDocument ( ) ;
114+
115+ Transform linkTransform = linkInstance . GetTotalTransform ( ) ;
116+ if ( ! linkTransform . IsIdentity )
117+ transform = linkTransform ;
118+ }
119+
120+ ( Document doc , Transform transform ) tuple ;
121+ if ( requestsByDocumentAndTransform . Keys . All ( x => x . Item1 . Title != doc . Title || ! x . Item2 . AlmostEqual ( transform ) ) )
122+ {
123+ tuple = ( doc , transform ) ;
124+ requestsByDocumentAndTransform . Add ( tuple , new List < IRequest > ( ) ) ;
125+ }
126+ else
127+ tuple = requestsByDocumentAndTransform . Keys . First ( x => x . Item1 . Title == doc . Title && x . Item2 . AlmostEqual ( transform ) ) ;
128+
129+ requestsByDocumentAndTransform [ tuple ] . Add ( requestByLink . Value ) ;
130+ }
131+
132+ // Global refObjects help sharing the refObjects when pulling from same document linked in a few different locations (e.g. copy-pasted link)
133+ // Thanks to sharing refObjects, an element is processed only once even if FromRevit is called against it multiple times
134+ Dictionary < string , Dictionary < string , List < IBHoMObject > > > globalRefObjects = new Dictionary < string , Dictionary < string , List < IBHoMObject > > > ( ) ;
99135 List < IBHoMObject > result = new List < IBHoMObject > ( ) ;
100- foreach ( KeyValuePair < Document , IRequest > requestByLink in requestsByLinks )
136+ foreach ( var kvp in requestsByDocumentAndTransform )
101137 {
102- result . AddRange ( Read ( requestByLink . Key , requestByLink . Value , pullConfig , settings ) ) ;
138+ result . AddRange ( Read ( kvp . Key . Item1 , kvp . Key . Item2 , kvp . Value , pullConfig , settings , globalRefObjects ) ) ;
103139 }
104140
141+ // Restore selection
105142 this . UIDocument . Selection . SetElementIds ( selected ) ;
106143
107144 return result ;
@@ -112,31 +149,26 @@ protected override IEnumerable<IBHoMObject> Read(IRequest request, ActionConfig
112149 /**** Public Methods ****/
113150 /***************************************************/
114151
115- public static List < IBHoMObject > Read ( Document document , IRequest request , RevitPullConfig pullConfig = null , RevitSettings settings = null )
152+ public static List < IBHoMObject > Read ( Document document , Transform transform , List < IRequest > requests , RevitPullConfig pullConfig = null , RevitSettings settings = null , Dictionary < string , Dictionary < string , List < IBHoMObject > > > globalRefObjects = null )
116153 {
117154 if ( document == null )
118155 {
119156 BH . Engine . Base . Compute . RecordError ( "BHoM objects could not be read because provided Revit document is null." ) ;
120157 return new List < IBHoMObject > ( ) ;
121158 }
122159
123- if ( request == null )
124- {
125- BH . Engine . Base . Compute . RecordError ( "BHoM objects could not be read because provided IRequest is null." ) ;
126- return new List < IBHoMObject > ( ) ;
127- }
128-
129160 pullConfig = pullConfig . DefaultIfNull ( ) ;
130161 settings = settings . DefaultIfNull ( ) ;
131162
163+ // Prefilter only elements from open worksets if requested
132164 IEnumerable < ElementId > worksetPrefilter = null ;
133165 if ( ! pullConfig . IncludeClosedWorksets )
134166 worksetPrefilter = document . OpenWorksetsPrefilter ( ) ;
135167
136- List < ElementId > elementIds = request . IElementIds ( document , pullConfig . Discipline , settings , worksetPrefilter ) . RemoveGridSegmentIds ( document ) ? . ToList ( ) ;
137- if ( elementIds == null )
138- return new List < IBHoMObject > ( ) ;
168+ // Get elementIds from all requests
169+ List < ElementId > elementIds = new LogicalOrRequest { Requests = requests } . ElementIds ( document , pullConfig . Discipline , settings , worksetPrefilter ) . RemoveGridSegmentIds ( document ) . ToList ( ) ;
139170
171+ // Get elementIds of nested elements if requested
140172 if ( pullConfig . IncludeNestedElements )
141173 {
142174 List < ElementId > elemIds = new List < ElementId > ( ) ;
@@ -153,12 +185,12 @@ public static List<IBHoMObject> Read(Document document, IRequest request, RevitP
153185 elementIds . AddRange ( elemIds ) ;
154186 }
155187
156- return Read ( document , elementIds , pullConfig , settings ) ;
188+ return Read ( document , transform , elementIds . ToList ( ) , pullConfig , settings , globalRefObjects ) ;
157189 }
158190
159191 /***************************************************/
160192
161- public static List < IBHoMObject > Read ( Document document , List < ElementId > elementIds , RevitPullConfig pullConfig = null , RevitSettings settings = null )
193+ public static List < IBHoMObject > Read ( Document document , Transform transform , List < ElementId > elementIds , RevitPullConfig pullConfig = null , RevitSettings settings = null , Dictionary < string , Dictionary < string , List < IBHoMObject > > > globalRefObjects = null )
162194 {
163195 if ( document == null )
164196 {
@@ -175,129 +207,128 @@ public static List<IBHoMObject> Read(Document document, List<ElementId> elementI
175207 pullConfig = pullConfig . DefaultIfNull ( ) ;
176208 settings = settings . DefaultIfNull ( ) ;
177209
178- PullGeometryConfig geometryConfig = pullConfig . GeometryConfig ;
179- if ( geometryConfig == null )
180- geometryConfig = new PullGeometryConfig ( ) ;
181-
182- PullRepresentationConfig representationConfig = pullConfig . RepresentationConfig ;
183- if ( representationConfig == null )
184- representationConfig = new PullRepresentationConfig ( ) ;
185-
186210 Discipline discipline = pullConfig . Discipline ;
187211 if ( discipline == Discipline . Undefined )
188212 {
189213 BH . Engine . Base . Compute . RecordNote ( $ "Conversion discipline has not been specified, default { Discipline . Physical } will be used.") ;
190214 discipline = Discipline . Physical ;
191215 }
192216
193- Options geometryOptions = BH . Revit . Engine . Core . Create . Options ( ViewDetailLevel . Fine , geometryConfig . IncludeNonVisible , false ) ;
194- Options meshOptions = BH . Revit . Engine . Core . Create . Options ( geometryConfig . MeshDetailLevel . ViewDetailLevel ( ) , geometryConfig . IncludeNonVisible , false ) ;
195- Options renderMeshOptions = BH . Revit . Engine . Core . Create . Options ( representationConfig . DetailLevel . ViewDetailLevel ( ) , representationConfig . IncludeNonVisible , false ) ;
217+ // Set up refObjects
218+ if ( globalRefObjects == null )
219+ globalRefObjects = new Dictionary < string , Dictionary < string , List < IBHoMObject > > > ( ) ;
220+
221+ if ( ! globalRefObjects . ContainsKey ( document . Title ) )
222+ globalRefObjects . Add ( document . Title , new Dictionary < string , List < IBHoMObject > > ( ) ) ;
223+
224+ Dictionary < string , List < IBHoMObject > > refObjects = globalRefObjects [ document . Title ] ;
196225
197- Transform linkTransform = null ;
198- TransformMatrix bHoMTransform = null ;
199- if ( document . IsLinked )
226+ // Get the elements already processed for a given document
227+ // Only relevant in case of same document linked in multiple locations
228+ // Helps avoid getting same element processed multiple times
229+ List < IBHoMObject > result = new List < IBHoMObject > ( ) ;
230+ List < ElementId > remainingElementIds = new List < ElementId > ( ) ;
231+ foreach ( ElementId id in elementIds )
200232 {
201- linkTransform = document . LinkTransform ( ) ;
202- if ( linkTransform ? . IsIdentity == false )
203- bHoMTransform = linkTransform . FromRevit ( ) ;
233+ var existing = refObjects . GetValues < IBHoMObject > ( id ) ;
234+ if ( existing != null )
235+ result . AddRange ( existing ) ;
236+ else
237+ remainingElementIds . Add ( id ) ;
204238 }
205239
206- Dictionary < string , List < IBHoMObject > > refObjects = new Dictionary < string , List < IBHoMObject > > ( ) ;
207-
208240 // Extract panel geometry of walls, floors, slabs and roofs prior to running the converts (this is an optimisation aimed to reduce the number of view regenerations)
209241 if ( ! document . IsLinked )
210- document . CachePanelGeometry ( elementIds , discipline , settings , refObjects ) ;
211-
212- List < IBHoMObject > result = new List < IBHoMObject > ( ) ;
213- foreach ( ElementId id in elementIds )
242+ document . CachePanelGeometry ( remainingElementIds , discipline , settings , refObjects ) ;
243+
244+ // Set up all geometry/representation configs
245+ PullGeometryConfig geometryConfig = pullConfig . GeometryConfig ;
246+ if ( geometryConfig == null )
247+ geometryConfig = new PullGeometryConfig ( ) ;
248+
249+ PullRepresentationConfig representationConfig = pullConfig . RepresentationConfig ;
250+ if ( representationConfig == null )
251+ representationConfig = new PullRepresentationConfig ( ) ;
252+
253+ Options geometryOptions = BH . Revit . Engine . Core . Create . Options ( ViewDetailLevel . Fine , geometryConfig . IncludeNonVisible , false ) ;
254+ Options meshOptions = BH . Revit . Engine . Core . Create . Options ( geometryConfig . MeshDetailLevel . ViewDetailLevel ( ) , geometryConfig . IncludeNonVisible , false ) ;
255+ Options renderMeshOptions = BH . Revit . Engine . Core . Create . Options ( representationConfig . DetailLevel . ViewDetailLevel ( ) , representationConfig . IncludeNonVisible , false ) ;
256+
257+ // Convert each element in coordinate system of the document that owns it
258+ // Transformation from that document's coordinate system to the coordinate system of host document done further downstream
259+ foreach ( ElementId id in remainingElementIds )
214260 {
215261 Element element = document . GetElement ( id ) ;
216262 if ( element == null )
217263 continue ;
218264
219- IEnumerable < IBHoMObject > iBHoMObjects = Read ( element , discipline , linkTransform , settings , refObjects ) ;
220-
221- if ( iBHoMObjects != null && iBHoMObjects . Any ( ) )
265+ IEnumerable < IBHoMObject > converted = Read ( element , discipline , settings , refObjects ) ;
266+ if ( converted != null )
222267 {
223268 if ( pullConfig . PullMaterialTakeOff )
224269 {
225- foreach ( IBHoMObject iBHoMObject in iBHoMObjects )
270+ foreach ( IBHoMObject obj in converted )
226271 {
227272 oM . Physical . Materials . VolumetricMaterialTakeoff takeoff = element . VolumetricMaterialTakeoff ( settings , refObjects ) ;
228273 if ( takeoff != null )
229- iBHoMObject . Fragments . AddOrReplace ( takeoff ) ;
274+ obj . Fragments . AddOrReplace ( takeoff ) ;
230275 }
231276 }
232277
233278 List < ICurve > edges = null ;
234279 if ( geometryConfig . PullEdges )
235- {
236280 edges = element . Curves ( geometryOptions , settings , true ) . FromRevit ( ) ;
237- if ( bHoMTransform != null )
238- edges = edges . Select ( x => x ? . ITransform ( bHoMTransform ) ) . ToList ( ) ;
239- }
240281
241282 List < ISurface > surfaces = null ;
242283 if ( geometryConfig . PullSurfaces )
243- {
244284 surfaces = element . Faces ( geometryOptions , settings ) . Select ( x => x . IFromRevit ( ) ) . ToList ( ) ;
245- if ( bHoMTransform != null )
246- surfaces = surfaces . Select ( x => x ? . ITransform ( bHoMTransform ) ) . ToList ( ) ;
247- }
248285
249286 List < oM . Geometry . Mesh > meshes = null ;
250287 if ( geometryConfig . PullMeshes )
251- {
252288 meshes = element . MeshedGeometry ( meshOptions , settings ) ;
253- if ( bHoMTransform != null )
254- meshes = meshes . Select ( x => x ? . Transform ( bHoMTransform ) ) . ToList ( ) ;
255- }
256289
257290 if ( geometryConfig . PullEdges || geometryConfig . PullSurfaces || geometryConfig . PullMeshes )
258291 {
259292 RevitGeometry geometry = new RevitGeometry ( edges , surfaces , meshes ) ;
260- foreach ( IBHoMObject iBHoMObject in iBHoMObjects )
293+ foreach ( IBHoMObject obj in converted )
261294 {
262- iBHoMObject . Fragments . AddOrReplace ( geometry ) ;
295+ obj . Fragments . AddOrReplace ( geometry ) ;
263296 }
264297 }
265298
266299 if ( representationConfig . PullRenderMesh )
267300 {
268301 List < RenderMesh > renderMeshes = element . RenderMeshes ( renderMeshOptions , settings ) ;
269- if ( bHoMTransform != null )
270- renderMeshes = renderMeshes . Select ( x => x ? . Transform ( bHoMTransform ) ) . ToList ( ) ;
271-
272302 RevitRepresentation representation = new RevitRepresentation ( renderMeshes ) ;
273- foreach ( IBHoMObject iBHoMObject in iBHoMObjects )
303+ foreach ( IBHoMObject obj in converted )
274304 {
275- iBHoMObject . Fragments . AddOrReplace ( representation ) ;
305+ obj . Fragments . AddOrReplace ( representation ) ;
276306 }
277307 }
278308
279- result . AddRange ( iBHoMObjects ) ;
309+ result . AddRange ( converted ) ;
280310 }
281311 }
282312
283313 bool [ ] activePulls = new bool [ ] { geometryConfig . PullEdges , geometryConfig . PullSurfaces , geometryConfig . PullMeshes , representationConfig . PullRenderMesh } ;
284314 if ( activePulls . Count ( x => x ) > 1 )
285315 BH . Engine . Base . Compute . RecordWarning ( "Pull of more than one geometry/representation type has been specified in RevitPullConfig. Please consider this can be time consuming due to the amount of conversions." ) ;
286316
287- return result ;
317+ // Postprocess clones the output and transforms it to the coordinate system of the host model
318+ return result . Select ( x => x . IPostprocess ( transform , settings ) ) . Where ( x => x != null ) . ToList ( ) ;
288319 }
289320
290321 /***************************************************/
291322
292- public static List < IBHoMObject > Read ( Element element , Discipline discipline , Transform transform , RevitSettings settings = null , Dictionary < string , List < IBHoMObject > > refObjects = null )
323+ public static List < IBHoMObject > Read ( Element element , Discipline discipline , RevitSettings settings = null , Dictionary < string , List < IBHoMObject > > refObjects = null )
293324 {
294325 if ( element == null || ! element . IsValidObject )
295326 return new List < IBHoMObject > ( ) ;
296327
297328 List < IBHoMObject > result = null ;
298329 try
299330 {
300- result = element . IFromRevit ( discipline , transform , settings , refObjects ) ;
331+ result = element . IFromRevit ( discipline , settings , refObjects ) ;
301332 }
302333 catch ( Exception exception )
303334 {
@@ -323,6 +354,3 @@ public static List<IBHoMObject> Read(Element element, Discipline discipline, Tra
323354 /***************************************************/
324355 }
325356}
326-
327-
328-
0 commit comments