Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issues with pulling from links fixed #1486

Merged
merged 6 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 98 additions & 70 deletions Revit_Core_Adapter/CRUD/Read.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
using BH.Engine.Adapters.Revit;
using BH.Engine.Base;
using BH.Engine.Geometry;
using BH.Engine.Graphics;
using BH.oM.Adapter;
using BH.oM.Adapters.Revit;
using BH.oM.Adapters.Revit.Enums;
Expand Down Expand Up @@ -87,7 +86,8 @@ protected override IEnumerable<IBHoMObject> Read(IRequest request, ActionConfig
}
}

Dictionary<Document, IRequest> requestsByLinks = request.SplitRequestTreeByLinks(this.Document);
// Split the request into separate requests per each link model
Dictionary<ElementId, IRequest> requestsByLinks = request.SplitRequestTreeByLinks(this.Document);
if (requestsByLinks == null)
{
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.");
Expand All @@ -96,12 +96,49 @@ protected override IEnumerable<IBHoMObject> Read(IRequest request, ActionConfig

RevitSettings settings = RevitSettings.DefaultIfNull();

// Group links that hold the same document and have same transform
// Addresses the case when there is a nested link being loaded via more than one parent link
// Same document linked in multiple locations is being pulled per each location
// Performance is not affected by multiple converts of same elements thanks to refObjects
Dictionary<(Document, Transform), List<IRequest>> requestsByDocumentAndTransform = new Dictionary<(Document, Transform), List<IRequest>>();
foreach (KeyValuePair<ElementId, IRequest> requestByLink in requestsByLinks)
{
Document doc;
Transform transform = Transform.Identity;
if (requestByLink.Key.IntegerValue == -1)
doc = this.Document;
else
{
var linkInstance = this.Document.GetElement(requestByLink.Key) as RevitLinkInstance;
doc = linkInstance.GetLinkDocument();

Transform linkTransform = linkInstance.GetTotalTransform();
if (!linkTransform.IsIdentity)
transform = linkTransform;
}

(Document doc, Transform transform) tuple;
if (requestsByDocumentAndTransform.Keys.All(x => x.Item1.Title != doc.Title || !x.Item2.AlmostEqual(transform)))
{
tuple = (doc, transform);
requestsByDocumentAndTransform.Add(tuple, new List<IRequest>());
}
else
tuple = requestsByDocumentAndTransform.Keys.First(x => x.Item1.Title == doc.Title && x.Item2.AlmostEqual(transform));

requestsByDocumentAndTransform[tuple].Add(requestByLink.Value);
}

// Global refObjects help sharing the refObjects when pulling from same document linked in a few different locations (e.g. copy-pasted link)
// Thanks to sharing refObjects, an element is processed only once even if FromRevit is called against it multiple times
Dictionary<string, Dictionary<string, List<IBHoMObject>>> globalRefObjects = new Dictionary<string, Dictionary<string, List<IBHoMObject>>>();
List<IBHoMObject> result = new List<IBHoMObject>();
foreach (KeyValuePair<Document, IRequest> requestByLink in requestsByLinks)
foreach (var kvp in requestsByDocumentAndTransform)
{
result.AddRange(Read(requestByLink.Key, requestByLink.Value, pullConfig, settings));
result.AddRange(Read(kvp.Key.Item1, kvp.Key.Item2, kvp.Value, pullConfig, settings, globalRefObjects));
}

// Restore selection
this.UIDocument.Selection.SetElementIds(selected);

return result;
Expand All @@ -112,31 +149,26 @@ protected override IEnumerable<IBHoMObject> Read(IRequest request, ActionConfig
/**** Public Methods ****/
/***************************************************/

public static List<IBHoMObject> Read(Document document, IRequest request, RevitPullConfig pullConfig = null, RevitSettings settings = null)
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)
{
if (document == null)
{
BH.Engine.Base.Compute.RecordError("BHoM objects could not be read because provided Revit document is null.");
return new List<IBHoMObject>();
}

if (request == null)
{
BH.Engine.Base.Compute.RecordError("BHoM objects could not be read because provided IRequest is null.");
return new List<IBHoMObject>();
}

pullConfig = pullConfig.DefaultIfNull();
settings = settings.DefaultIfNull();

// Prefilter only elements from open worksets if requested
IEnumerable<ElementId> worksetPrefilter = null;
if (!pullConfig.IncludeClosedWorksets)
worksetPrefilter = document.OpenWorksetsPrefilter();

List<ElementId> elementIds = request.IElementIds(document, pullConfig.Discipline, settings, worksetPrefilter).RemoveGridSegmentIds(document)?.ToList();
if (elementIds == null)
return new List<IBHoMObject>();
// Get elementIds from all requests
List<ElementId> elementIds = new LogicalOrRequest { Requests = requests }.ElementIds(document, pullConfig.Discipline, settings, worksetPrefilter).RemoveGridSegmentIds(document).ToList();

// Get elementIds of nested elements if requested
if (pullConfig.IncludeNestedElements)
{
List<ElementId> elemIds = new List<ElementId>();
Expand All @@ -153,12 +185,12 @@ public static List<IBHoMObject> Read(Document document, IRequest request, RevitP
elementIds.AddRange(elemIds);
}

return Read(document, elementIds, pullConfig, settings);
return Read(document, transform, elementIds.ToList(), pullConfig, settings, globalRefObjects);
}

/***************************************************/

public static List<IBHoMObject> Read(Document document, List<ElementId> elementIds, RevitPullConfig pullConfig = null, RevitSettings settings = null)
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)
{
if (document == null)
{
Expand All @@ -175,129 +207,128 @@ public static List<IBHoMObject> Read(Document document, List<ElementId> elementI
pullConfig = pullConfig.DefaultIfNull();
settings = settings.DefaultIfNull();

PullGeometryConfig geometryConfig = pullConfig.GeometryConfig;
if (geometryConfig == null)
geometryConfig = new PullGeometryConfig();

PullRepresentationConfig representationConfig = pullConfig.RepresentationConfig;
if (representationConfig == null)
representationConfig = new PullRepresentationConfig();

Discipline discipline = pullConfig.Discipline;
if (discipline == Discipline.Undefined)
{
BH.Engine.Base.Compute.RecordNote($"Conversion discipline has not been specified, default {Discipline.Physical} will be used.");
discipline = Discipline.Physical;
}

Options geometryOptions = BH.Revit.Engine.Core.Create.Options(ViewDetailLevel.Fine, geometryConfig.IncludeNonVisible, false);
Options meshOptions = BH.Revit.Engine.Core.Create.Options(geometryConfig.MeshDetailLevel.ViewDetailLevel(), geometryConfig.IncludeNonVisible, false);
Options renderMeshOptions = BH.Revit.Engine.Core.Create.Options(representationConfig.DetailLevel.ViewDetailLevel(), representationConfig.IncludeNonVisible, false);
// Set up refObjects
if (globalRefObjects == null)
globalRefObjects = new Dictionary<string, Dictionary<string, List<IBHoMObject>>>();

if (!globalRefObjects.ContainsKey(document.Title))
globalRefObjects.Add(document.Title, new Dictionary<string, List<IBHoMObject>>());

Dictionary<string, List<IBHoMObject>> refObjects = globalRefObjects[document.Title];

Transform linkTransform = null;
TransformMatrix bHoMTransform = null;
if (document.IsLinked)
// Get the elements already processed for a given document
// Only relevant in case of same document linked in multiple locations
// Helps avoid getting same element processed multiple times
List<IBHoMObject> result = new List<IBHoMObject>();
List<ElementId> remainingElementIds = new List<ElementId>();
foreach (ElementId id in elementIds)
{
linkTransform = document.LinkTransform();
if (linkTransform?.IsIdentity == false)
bHoMTransform = linkTransform.FromRevit();
var existing = refObjects.GetValues<IBHoMObject>(id);
if (existing != null)
result.AddRange(existing);
else
remainingElementIds.Add(id);
}

Dictionary<string, List<IBHoMObject>> refObjects = new Dictionary<string, List<IBHoMObject>>();

// 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)
if (!document.IsLinked)
document.CachePanelGeometry(elementIds, discipline, settings, refObjects);

List<IBHoMObject> result = new List<IBHoMObject>();
foreach (ElementId id in elementIds)
document.CachePanelGeometry(remainingElementIds, discipline, settings, refObjects);

// Set up all geometry/representation configs
PullGeometryConfig geometryConfig = pullConfig.GeometryConfig;
if (geometryConfig == null)
geometryConfig = new PullGeometryConfig();

PullRepresentationConfig representationConfig = pullConfig.RepresentationConfig;
if (representationConfig == null)
representationConfig = new PullRepresentationConfig();

Options geometryOptions = BH.Revit.Engine.Core.Create.Options(ViewDetailLevel.Fine, geometryConfig.IncludeNonVisible, false);
Options meshOptions = BH.Revit.Engine.Core.Create.Options(geometryConfig.MeshDetailLevel.ViewDetailLevel(), geometryConfig.IncludeNonVisible, false);
Options renderMeshOptions = BH.Revit.Engine.Core.Create.Options(representationConfig.DetailLevel.ViewDetailLevel(), representationConfig.IncludeNonVisible, false);

// Convert each element in coordinate system of the document that owns it
// Transformation from that document's coordinate system to the coordinate system of host document done further downstream
foreach (ElementId id in remainingElementIds)
{
Element element = document.GetElement(id);
if (element == null)
continue;

IEnumerable<IBHoMObject> iBHoMObjects = Read(element, discipline, linkTransform, settings, refObjects);

if (iBHoMObjects != null && iBHoMObjects.Any())
IEnumerable<IBHoMObject> converted = Read(element, discipline, settings, refObjects);
if (converted != null)
{
if (pullConfig.PullMaterialTakeOff)
{
foreach (IBHoMObject iBHoMObject in iBHoMObjects)
foreach (IBHoMObject obj in converted)
{
oM.Physical.Materials.VolumetricMaterialTakeoff takeoff = element.VolumetricMaterialTakeoff(settings, refObjects);
if (takeoff != null)
iBHoMObject.Fragments.AddOrReplace(takeoff);
obj.Fragments.AddOrReplace(takeoff);
}
}

List<ICurve> edges = null;
if (geometryConfig.PullEdges)
{
edges = element.Curves(geometryOptions, settings, true).FromRevit();
if (bHoMTransform != null)
edges = edges.Select(x => x?.ITransform(bHoMTransform)).ToList();
}

List<ISurface> surfaces = null;
if (geometryConfig.PullSurfaces)
{
surfaces = element.Faces(geometryOptions, settings).Select(x => x.IFromRevit()).ToList();
if (bHoMTransform != null)
surfaces = surfaces.Select(x => x?.ITransform(bHoMTransform)).ToList();
}

List<oM.Geometry.Mesh> meshes = null;
if (geometryConfig.PullMeshes)
{
meshes = element.MeshedGeometry(meshOptions, settings);
if (bHoMTransform != null)
meshes = meshes.Select(x => x?.Transform(bHoMTransform)).ToList();
}

if (geometryConfig.PullEdges || geometryConfig.PullSurfaces || geometryConfig.PullMeshes)
{
RevitGeometry geometry = new RevitGeometry(edges, surfaces, meshes);
foreach (IBHoMObject iBHoMObject in iBHoMObjects)
foreach (IBHoMObject obj in converted)
{
iBHoMObject.Fragments.AddOrReplace(geometry);
obj.Fragments.AddOrReplace(geometry);
}
}

if (representationConfig.PullRenderMesh)
{
List<RenderMesh> renderMeshes = element.RenderMeshes(renderMeshOptions, settings);
if (bHoMTransform != null)
renderMeshes = renderMeshes.Select(x => x?.Transform(bHoMTransform)).ToList();

RevitRepresentation representation = new RevitRepresentation(renderMeshes);
foreach (IBHoMObject iBHoMObject in iBHoMObjects)
foreach (IBHoMObject obj in converted)
{
iBHoMObject.Fragments.AddOrReplace(representation);
obj.Fragments.AddOrReplace(representation);
}
}

result.AddRange(iBHoMObjects);
result.AddRange(converted);
}
}

bool[] activePulls = new bool[] { geometryConfig.PullEdges, geometryConfig.PullSurfaces, geometryConfig.PullMeshes, representationConfig.PullRenderMesh };
if (activePulls.Count(x => x) > 1)
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.");

return result;
// Postprocess clones the output and transforms it to the coordinate system of the host model
return result.Select(x => x.IPostprocess(transform, settings)).Where(x => x != null).ToList();
}

/***************************************************/

public static List<IBHoMObject> Read(Element element, Discipline discipline, Transform transform, RevitSettings settings = null, Dictionary<string, List<IBHoMObject>> refObjects = null)
public static List<IBHoMObject> Read(Element element, Discipline discipline, RevitSettings settings = null, Dictionary<string, List<IBHoMObject>> refObjects = null)
{
if (element == null || !element.IsValidObject)
return new List<IBHoMObject>();

List<IBHoMObject> result = null;
try
{
result = element.IFromRevit(discipline, transform, settings, refObjects);
result = element.IFromRevit(discipline, settings, refObjects);
}
catch (Exception exception)
{
Expand All @@ -323,6 +354,3 @@ public static List<IBHoMObject> Read(Element element, Discipline discipline, Tra
/***************************************************/
}
}



Loading