Skip to content

Commit 4f273f5

Browse files
authored
Issues with pulling from links fixed (#1486)
2 parents 3bb620d + 11760a2 commit 4f273f5

File tree

4 files changed

+198
-115
lines changed

4 files changed

+198
-115
lines changed

Revit_Core_Adapter/CRUD/Read.cs

Lines changed: 98 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
using BH.Engine.Adapters.Revit;
2525
using BH.Engine.Base;
2626
using BH.Engine.Geometry;
27-
using BH.Engine.Graphics;
2827
using BH.oM.Adapter;
2928
using BH.oM.Adapters.Revit;
3029
using 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

Comments
 (0)