From a65cb643f8b9c6808897b199dba10a71dd23a516 Mon Sep 17 00:00:00 2001 From: Viet Le Date: Thu, 21 Mar 2024 14:16:51 +0000 Subject: [PATCH 01/43] Handle open curve loops --- .../Convert/Geometry/FromRevit/Surface.cs | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs b/Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs index 15913c6d1..75ed104e6 100644 --- a/Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs +++ b/Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs @@ -21,7 +21,9 @@ */ using Autodesk.Revit.DB; +using BH.Engine.Geometry; using BH.oM.Base.Attributes; +using BH.oM.Geometry; using System; using System.Collections.Generic; using System.ComponentModel; @@ -44,12 +46,27 @@ public static oM.Geometry.PlanarSurface FromRevit(this PlanarFace face) return null; List crvLoops = face.GetEdgesAsCurveLoops().ToList(); - CurveLoop externalLoop = crvLoops.FirstOrDefault(x => x.IsCounterclockwise(face.FaceNormal)); + CurveLoop externalLoop = crvLoops.FirstOrDefault(x => !x.IsOpen() && x.IsCounterclockwise(face.FaceNormal)); + //The face may violate conventional winding directions, or Revit may see a complex curve loop as 'Open' and refuses to calculate IsCounterclockwise if (externalLoop == null) { - BH.Engine.Base.Compute.RecordError($"Converting a Revit planar face to a BHoM planar surface failed because it has no counter-clockwise boundary loop."); - return null; + //Checking areas is slower but these problematic faces rarely exist, so performance hit isn't bad. + double maxArea = double.MinValue; + + foreach (CurveLoop loop in crvLoops) + { + List controlPoints = new List(); + foreach (Curve curve in loop) + controlPoints.AddRange(curve.Tessellate().Select(x => x.PointFromRevit())); + + double area = new Polyline { ControlPoints = controlPoints }.Area(); + if (area > maxArea) + { + maxArea = area; + externalLoop = loop; + } + } } oM.Geometry.ICurve externalBoundary = externalLoop.FromRevit(); From 9093f2a296452222ff065ea011b96fc328eab859 Mon Sep 17 00:00:00 2001 From: Viet Le Date: Tue, 26 Mar 2024 11:27:29 +0000 Subject: [PATCH 02/43] Return early on crvLoops.Count == 1 --- Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs b/Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs index 75ed104e6..f9c79dad1 100644 --- a/Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs +++ b/Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs @@ -46,6 +46,9 @@ public static oM.Geometry.PlanarSurface FromRevit(this PlanarFace face) return null; List crvLoops = face.GetEdgesAsCurveLoops().ToList(); + if (crvLoops.Count == 1) + return new PlanarSurface(crvLoops[0].FromRevit(), new List()); + CurveLoop externalLoop = crvLoops.FirstOrDefault(x => !x.IsOpen() && x.IsCounterclockwise(face.FaceNormal)); //The face may violate conventional winding directions, or Revit may see a complex curve loop as 'Open' and refuses to calculate IsCounterclockwise From dc976f0e024240f79a6db81028a525753ad9abc6 Mon Sep 17 00:00:00 2001 From: Viet Le Date: Tue, 26 Mar 2024 11:45:00 +0000 Subject: [PATCH 03/43] Cleaning up --- .../Convert/Geometry/FromRevit/Surface.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs b/Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs index f9c79dad1..eab481282 100644 --- a/Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs +++ b/Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs @@ -40,7 +40,7 @@ public static partial class Convert [Description("Converts a Revit PlanarFace to BH.oM.Geometry.PlanarSurface.")] [Input("face", "Revit PlanarFace to be converted.")] [Output("surface", "BH.oM.Geometry.PlanarSurface resulting from converting the input Revit PlanarFace.")] - public static oM.Geometry.PlanarSurface FromRevit(this PlanarFace face) + public static PlanarSurface FromRevit(this PlanarFace face) { if (face == null) return null; @@ -72,10 +72,10 @@ public static oM.Geometry.PlanarSurface FromRevit(this PlanarFace face) } } - oM.Geometry.ICurve externalBoundary = externalLoop.FromRevit(); - List internalBoundaries = crvLoops.Where(x => x != externalLoop).Select(x => x.FromRevit() as oM.Geometry.ICurve).ToList(); + ICurve externalBoundary = externalLoop.FromRevit(); + List internalBoundaries = crvLoops.Where(x => x != externalLoop).Select(x => x.FromRevit() as ICurve).ToList(); - return new oM.Geometry.PlanarSurface(externalBoundary, internalBoundaries); + return new PlanarSurface(externalBoundary, internalBoundaries); } @@ -86,7 +86,7 @@ public static oM.Geometry.PlanarSurface FromRevit(this PlanarFace face) [Description("Converts a Revit Face to BH.oM.Geometry.ISurface.")] [Input("face", "Revit Face to be converted.")] [Output("surface", "BH.oM.Geometry.ISurface resulting from converting the input Revit Face.")] - public static oM.Geometry.ISurface IFromRevit(this Face face) + public static oM.Geometry.ISurface IFromRevit(this Autodesk.Revit.DB.Face face) { return FromRevit(face as dynamic); } @@ -96,7 +96,7 @@ public static oM.Geometry.ISurface IFromRevit(this Face face) /**** Fallback Methods ****/ /***************************************************/ - private static oM.Geometry.ISurface FromRevit(this Face face) + private static oM.Geometry.ISurface FromRevit(this Autodesk.Revit.DB.Face face) { BH.Engine.Base.Compute.RecordError(String.Format("Revit face of type {0} could not be converted to BHoM due to a missing convert method.", face.GetType())); return null; From 2870e7a291e1a4ec797fcf5af1629cdc1ad164b6 Mon Sep 17 00:00:00 2001 From: BHoMBot Date: Fri, 29 Mar 2024 12:49:29 +0000 Subject: [PATCH 04/43] Upgrade AssemblyFileVersion to 7.2.0.0 --- Revit_Adapter/Revit_Adapter.csproj | 2 +- Revit_Core_Adapter/Revit_Core_Adapter.csproj | 2 +- Revit_Core_Engine/Revit_Core_Engine.csproj | 2 +- Revit_Engine/Revit_Engine.csproj | 2 +- Revit_oM/Revit_oM.csproj | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Revit_Adapter/Revit_Adapter.csproj b/Revit_Adapter/Revit_Adapter.csproj index ff319b619..6499f4f61 100644 --- a/Revit_Adapter/Revit_Adapter.csproj +++ b/Revit_Adapter/Revit_Adapter.csproj @@ -8,7 +8,7 @@ BHoM Copyright © https://github.com/BHoM BH.Adapter.Revit - 7.1.0.0 + 7.2.0.0 diff --git a/Revit_Core_Adapter/Revit_Core_Adapter.csproj b/Revit_Core_Adapter/Revit_Core_Adapter.csproj index a1e6c08af..d7e1cbe6f 100644 --- a/Revit_Core_Adapter/Revit_Core_Adapter.csproj +++ b/Revit_Core_Adapter/Revit_Core_Adapter.csproj @@ -7,7 +7,7 @@ BHoM Copyright © https://github.com/BHoM BH.Revit.Adapter.Core - 7.1.0.0 + 7.2.0.0 diff --git a/Revit_Core_Engine/Revit_Core_Engine.csproj b/Revit_Core_Engine/Revit_Core_Engine.csproj index 9c9c59f7b..30348a1b3 100644 --- a/Revit_Core_Engine/Revit_Core_Engine.csproj +++ b/Revit_Core_Engine/Revit_Core_Engine.csproj @@ -6,7 +6,7 @@ BHoM Copyright © https://github.com/BHoM BH.Revit.Engine.Core - 7.1.0.0 + 7.2.0.0 diff --git a/Revit_Engine/Revit_Engine.csproj b/Revit_Engine/Revit_Engine.csproj index 94bf8b435..7fbd5d847 100644 --- a/Revit_Engine/Revit_Engine.csproj +++ b/Revit_Engine/Revit_Engine.csproj @@ -8,7 +8,7 @@ BHoM Copyright © https://github.com/BHoM BH.Engine.Revit - 7.1.0.0 + 7.2.0.0 diff --git a/Revit_oM/Revit_oM.csproj b/Revit_oM/Revit_oM.csproj index 6192f5c6e..a5884c936 100644 --- a/Revit_oM/Revit_oM.csproj +++ b/Revit_oM/Revit_oM.csproj @@ -8,7 +8,7 @@ BHoM Copyright © https://github.com/BHoM BH.oM.Revit - 7.1.0.0 + 7.2.0.0 From 9d41cacc72fa143e78815e3a619f8b39f852b7e8 Mon Sep 17 00:00:00 2001 From: BHoMBot Date: Fri, 29 Mar 2024 15:58:17 +0000 Subject: [PATCH 05/43] Remove 7.1 versioning --- Revit_Core_Engine/Query/DoesIntersect.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Revit_Core_Engine/Query/DoesIntersect.cs b/Revit_Core_Engine/Query/DoesIntersect.cs index 34a7c81cf..17c396b9f 100644 --- a/Revit_Core_Engine/Query/DoesIntersect.cs +++ b/Revit_Core_Engine/Query/DoesIntersect.cs @@ -64,7 +64,6 @@ public static bool DoesIntersect(this Element element1, Element element2) /***************************************************/ - [PreviousVersion("7.1", "BH.Revit.Engine.Core.Query.DoesIntersect(Autodesk.Revit.DB.BoundingBoxXYZ, Autodesk.Revit.DB.Element)")] [Description("Check if bounding box intersects with element.")] [Input("bbox", "Bounding box to check the intersection for.")] [Input("element", "Element to check the intersection for.")] From 60631973051500102111cff795bcb70cc0c34c13 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Mon, 4 Mar 2024 16:19:13 +0100 Subject: [PATCH 06/43] triangulation factor for Medium mesh quality changed from 0.5 to 0.3 --- Revit_Core_Engine/Query/FaceTriangulationFactor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Revit_Core_Engine/Query/FaceTriangulationFactor.cs b/Revit_Core_Engine/Query/FaceTriangulationFactor.cs index 0afe9430e..01919e628 100644 --- a/Revit_Core_Engine/Query/FaceTriangulationFactor.cs +++ b/Revit_Core_Engine/Query/FaceTriangulationFactor.cs @@ -44,7 +44,7 @@ public static double FaceTriangulationFactor(this ViewDetailLevel viewDetailLeve case Autodesk.Revit.DB.ViewDetailLevel.Fine: return 1; default: - return 0.5; + return 0.3; } } From c7f7009bd5615c80ec6a2049ecb9ddaa67422165 Mon Sep 17 00:00:00 2001 From: Viet Le Date: Tue, 2 Apr 2024 10:56:05 +0100 Subject: [PATCH 07/43] Code clean up --- .gitignore | 3 ++- Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs | 6 +----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index aeeacb329..56d0a518f 100644 --- a/.gitignore +++ b/.gitignore @@ -57,4 +57,5 @@ bld/ *.suo *.user .vs/ -.idea/ \ No newline at end of file +.idea/ +**/launchSettings.json \ No newline at end of file diff --git a/Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs b/Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs index eab481282..586fce8e8 100644 --- a/Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs +++ b/Revit_Core_Engine/Convert/Geometry/FromRevit/Surface.cs @@ -46,9 +46,6 @@ public static PlanarSurface FromRevit(this PlanarFace face) return null; List crvLoops = face.GetEdgesAsCurveLoops().ToList(); - if (crvLoops.Count == 1) - return new PlanarSurface(crvLoops[0].FromRevit(), new List()); - CurveLoop externalLoop = crvLoops.FirstOrDefault(x => !x.IsOpen() && x.IsCounterclockwise(face.FaceNormal)); //The face may violate conventional winding directions, or Revit may see a complex curve loop as 'Open' and refuses to calculate IsCounterclockwise @@ -72,10 +69,9 @@ public static PlanarSurface FromRevit(this PlanarFace face) } } - ICurve externalBoundary = externalLoop.FromRevit(); List internalBoundaries = crvLoops.Where(x => x != externalLoop).Select(x => x.FromRevit() as ICurve).ToList(); - return new PlanarSurface(externalBoundary, internalBoundaries); + return new PlanarSurface(externalLoop.FromRevit(), internalBoundaries); } From 8ee47265d0d9ceca59493005d438fd52e8d80891 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Fri, 15 Mar 2024 20:56:47 +0100 Subject: [PATCH 08/43] facade-related queries cleaned up and extended --- Revit_Core_Engine/Query/EdgeLoops.cs | 87 +++++++++++++++++++ .../Query/FacadeOpeningMaterial.cs | 55 ++++++++++++ Revit_Core_Engine/Query/FacadeOpeningType.cs | 54 ++++++++++++ ...truction.cs => FacadePanelConstruction.cs} | 77 ++++++++-------- Revit_Core_Engine/Query/HostCurtainGrid.cs | 43 +++++++++ Revit_Core_Engine/Query/IsTransparent.cs | 49 +++++++++++ 6 files changed, 326 insertions(+), 39 deletions(-) create mode 100644 Revit_Core_Engine/Query/EdgeLoops.cs create mode 100644 Revit_Core_Engine/Query/FacadeOpeningMaterial.cs create mode 100644 Revit_Core_Engine/Query/FacadeOpeningType.cs rename Revit_Core_Engine/Query/{GlazingConstruction.cs => FacadePanelConstruction.cs} (57%) create mode 100644 Revit_Core_Engine/Query/HostCurtainGrid.cs create mode 100644 Revit_Core_Engine/Query/IsTransparent.cs diff --git a/Revit_Core_Engine/Query/EdgeLoops.cs b/Revit_Core_Engine/Query/EdgeLoops.cs new file mode 100644 index 000000000..b7616a0b2 --- /dev/null +++ b/Revit_Core_Engine/Query/EdgeLoops.cs @@ -0,0 +1,87 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using Autodesk.Revit.DB; +using BH.oM.Base.Attributes; +using BH.oM.Geometry; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +namespace BH.Revit.Engine.Core +{ + public static partial class Query + { + /***************************************************/ + /**** Public methods ****/ + /***************************************************/ + + public static List EdgeLoops(this FamilyInstance curtainCell) + { + HostObject curtainHost = curtainCell.Host as HostObject; + if (curtainHost == null) + return null; + + foreach (CurtainGrid cg in curtainHost.ICurtainGrids()) + { + List ids = cg.GetPanelIds().ToList(); + List cells = cg.GetCurtainCells().ToList(); + for (int i = 0; i < ids.Count; i++) + { + if (ids[i].IntegerValue == curtainCell.Id.IntegerValue) + { + try + { + // This catches when PlanarizedCurveLoops throws an exception due to the cell having no loops, meaning in Revit it exists in the database but is no longer a valid CurtainWall cell + CurveArrArray x = cells[i].PlanarizedCurveLoops; + + // Collapse nonlinear edges of a cell to lines - valid because mullions are linear anyways + List outlines = new List(); + foreach (CurveArray array in cells[i].CurveLoops) + { + PolyCurve outline = new PolyCurve(); + foreach (Curve curve in array) + { + outline.Curves.Add(new BH.oM.Geometry.Line { Start = curve.GetEndPoint(0).PointFromRevit(), End = curve.GetEndPoint(1).PointFromRevit() }); + } + + outlines.Add(outline); + } + + return outlines; + } + catch + { + return null; + } + } + } + } + + return new List(); + } + + /***************************************************/ + } +} + + diff --git a/Revit_Core_Engine/Query/FacadeOpeningMaterial.cs b/Revit_Core_Engine/Query/FacadeOpeningMaterial.cs new file mode 100644 index 000000000..f26efb9ad --- /dev/null +++ b/Revit_Core_Engine/Query/FacadeOpeningMaterial.cs @@ -0,0 +1,55 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using Autodesk.Revit.DB; +using System.Collections.Generic; + +namespace BH.Revit.Engine.Core +{ + public static partial class Query + { + /***************************************************/ + /**** Public methods ****/ + /***************************************************/ + + public static Material FacadeOpeningMaterial(this FamilyInstance opening) + { + List solids = opening?.Solids(new Options()); + if (solids == null) + return null; + + Face maxAreaFace = null; + foreach (Solid solid in solids) + { + foreach (Face face in solid.Faces) + { + if (maxAreaFace == null || maxAreaFace.Area < face.Area) + maxAreaFace = face; + } + } + + return maxAreaFace != null ? opening.Document.GetElement(maxAreaFace.MaterialElementId) as Material : null; + } + + /***************************************************/ + } +} diff --git a/Revit_Core_Engine/Query/FacadeOpeningType.cs b/Revit_Core_Engine/Query/FacadeOpeningType.cs new file mode 100644 index 000000000..6de33fc1d --- /dev/null +++ b/Revit_Core_Engine/Query/FacadeOpeningType.cs @@ -0,0 +1,54 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using Autodesk.Revit.DB; +using BH.oM.Facade.Elements; + +namespace BH.Revit.Engine.Core +{ + public static partial class Query + { + /***************************************************/ + /**** Public methods ****/ + /***************************************************/ + + public static OpeningType FacadeOpeningType(this FamilyInstance panel) + { + BuiltInCategory category = (BuiltInCategory)panel.Category.Id.IntegerValue; + if (category == Autodesk.Revit.DB.BuiltInCategory.OST_Windows) + return BH.oM.Facade.Elements.OpeningType.Window; + else if (category == Autodesk.Revit.DB.BuiltInCategory.OST_CurtainWallPanels) + { + if (panel.IsTransparent()) + return BH.oM.Facade.Elements.OpeningType.Window; + else + return BH.oM.Facade.Elements.OpeningType.CurtainWallSpandrel; + } + else if (category == Autodesk.Revit.DB.BuiltInCategory.OST_Doors) + return BH.oM.Facade.Elements.OpeningType.Door; + else + return BH.oM.Facade.Elements.OpeningType.Undefined; + } + + /***************************************************/ + } +} diff --git a/Revit_Core_Engine/Query/GlazingConstruction.cs b/Revit_Core_Engine/Query/FacadePanelConstruction.cs similarity index 57% rename from Revit_Core_Engine/Query/GlazingConstruction.cs rename to Revit_Core_Engine/Query/FacadePanelConstruction.cs index 41a766277..a90f46950 100644 --- a/Revit_Core_Engine/Query/GlazingConstruction.cs +++ b/Revit_Core_Engine/Query/FacadePanelConstruction.cs @@ -22,12 +22,11 @@ using Autodesk.Revit.DB; using BH.oM.Adapters.Revit.Settings; -using BH.oM.Physical.Constructions; +using BH.oM.Base; using BH.oM.Base.Attributes; -using System; +using BH.oM.Physical.Constructions; using System.Collections.Generic; using System.ComponentModel; -using System.Linq; namespace BH.Revit.Engine.Core { @@ -41,7 +40,34 @@ public static partial class Query [Input("familyInstance", "Revit FamilyInstance to be queried for its glazing property.")] [Input("settings", "Revit adapter settings to be used while performing the query.")] [Output("construction", "Glazing property of the input Revit FamilyInstance, in a form of Physical.Constructions.Construction.")] - public static oM.Physical.Constructions.Construction GlazingConstruction(this FamilyInstance familyInstance, RevitSettings settings = null) + public static BH.oM.Physical.Constructions.Construction FacadePanelConstruction(this FamilyInstance panel, RevitSettings settings, Dictionary> refObjects) + { + if ((panel as Panel)?.FindHostPanel() is ElementId hostId && panel.Document.GetElement(hostId) is Wall wall) + { + HostObjAttributes hostObjAttributes = wall.Document.GetElement(wall.GetTypeId()) as HostObjAttributes; + string materialGrade = wall.MaterialGrade(settings); + return hostObjAttributes.ConstructionFromRevit(materialGrade, settings, refObjects); + } + else + { + int category = panel.Category.Id.IntegerValue; + if (category == (int)Autodesk.Revit.DB.BuiltInCategory.OST_Walls) + { + HostObjAttributes hostObjAttributes = panel.Document.GetElement(panel.GetTypeId()) as HostObjAttributes; + string materialGrade = panel.MaterialGrade(settings); + return hostObjAttributes.ConstructionFromRevit(materialGrade, settings, refObjects); + } + else + return panel.OpeningConstruction(); + } + } + + + /***************************************************/ + /**** Private methods ****/ + /***************************************************/ + + private static oM.Physical.Constructions.Construction OpeningConstruction(this FamilyInstance familyInstance, RevitSettings settings = null) { if (familyInstance == null) return null; @@ -49,51 +75,24 @@ public static oM.Physical.Constructions.Construction GlazingConstruction(this Fa BH.oM.Physical.Materials.Material bhomMat = null; string constName = ""; - //Try to get glazing material name from parameters - ElementType elementType = familyInstance.Document.GetElement(familyInstance.GetTypeId()) as ElementType; - List materialParams = new List(); - - // Try to find the glazing material based on params that may contain it. Expected param to contain glazing material varies - // depending on if it is a system panel with a single material, or any other type with potentially many materials applied. - if (familyInstance.Symbol.Family.Name.Contains("System")) + Material glazingMaterial = familyInstance.FacadeOpeningMaterial(); + if (glazingMaterial != null) { - materialParams = new List { "Material" }; + bhomMat = glazingMaterial.MaterialFromRevit(settings); + constName = bhomMat.Name; } else { - materialParams = new List { "Glass", "Glazing" }; - } - - foreach (Parameter p in elementType.Parameters) - { - if (materialParams.Any(p.Definition.Name.Contains) && p.StorageType == StorageType.ElementId && familyInstance.Document.GetElement(p.AsElementId()) is Material) - { - Material materialElem = familyInstance.Document.GetElement(p.AsElementId()) as Material; - bhomMat = materialElem.MaterialFromRevit(settings); - constName = bhomMat.Name; - break; - } - } - - if (bhomMat == null) - { - BH.Engine.Base.Compute.RecordWarning(String.Format("The Construction of this Opening could not be found, and a default construction has been used. Revit ElementId: {0}", familyInstance.Id.IntegerValue)); - constName = "Default Glazing Construction"; - bhomMat = new oM.Physical.Materials.Material { Name = "Default Glazing Material" }; + BH.Engine.Base.Compute.RecordWarning($"Construction of Opening could not be found, therefore default construction has been used. Revit ElementId: {familyInstance.Id.IntegerValue}"); + constName = "Default Opening Construction"; + bhomMat = new oM.Physical.Materials.Material { Name = "Default Opening Material" }; } List bhomLayers = new List { new Layer { Name = constName, Material = bhomMat, Thickness = 0 } }; - - BH.oM.Physical.Constructions.Construction glazingConstruction = new oM.Physical.Constructions.Construction { Name = constName, Layers = bhomLayers }; - - return glazingConstruction; + return new oM.Physical.Constructions.Construction { Name = constName, Layers = bhomLayers }; } /***************************************************/ } } - - - - diff --git a/Revit_Core_Engine/Query/HostCurtainGrid.cs b/Revit_Core_Engine/Query/HostCurtainGrid.cs new file mode 100644 index 000000000..6dec2506d --- /dev/null +++ b/Revit_Core_Engine/Query/HostCurtainGrid.cs @@ -0,0 +1,43 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using Autodesk.Revit.DB; +using System.Collections.Generic; +using System.Linq; + +namespace BH.Revit.Engine.Core +{ + public static partial class Query + { + /***************************************************/ + /**** Public methods ****/ + /***************************************************/ + + public static CurtainGrid HostCurtainGrid(this FamilyInstance familyInstance) + { + List curtainGrids = (familyInstance?.Host as HostObject)?.ICurtainGrids(); + return curtainGrids?.FirstOrDefault(x => x.GetPanelIds().Any(y => y == familyInstance.Id)); + } + + /***************************************************/ + } +} diff --git a/Revit_Core_Engine/Query/IsTransparent.cs b/Revit_Core_Engine/Query/IsTransparent.cs new file mode 100644 index 000000000..ecd60218e --- /dev/null +++ b/Revit_Core_Engine/Query/IsTransparent.cs @@ -0,0 +1,49 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using Autodesk.Revit.DB; +using System.Collections.Generic; +using System.Linq; + +namespace BH.Revit.Engine.Core +{ + public static partial class Query + { + /***************************************************/ + /**** Public methods ****/ + /***************************************************/ + + public static bool IsTransparent(this FamilyInstance panel) + { + return panel?.FacadeOpeningMaterial()?.IsTransparent() == true; + } + + /***************************************************/ + + public static bool IsTransparent(this Material material) + { + return material != null && material.Transparency >= 50; + } + + /***************************************************/ + } +} From 87f55fea7884f24e871acd9ac41573d2d68fd9e4 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Fri, 15 Mar 2024 20:58:34 +0100 Subject: [PATCH 09/43] complex facade queries refactored to leverage newly added queries --- .../Query/FacadeCurtainPanels.cs | 133 +----------------- Revit_Core_Engine/Query/OpeningSurface.cs | 35 +---- 2 files changed, 5 insertions(+), 163 deletions(-) diff --git a/Revit_Core_Engine/Query/FacadeCurtainPanels.cs b/Revit_Core_Engine/Query/FacadeCurtainPanels.cs index 52a98b163..efb72645f 100644 --- a/Revit_Core_Engine/Query/FacadeCurtainPanels.cs +++ b/Revit_Core_Engine/Query/FacadeCurtainPanels.cs @@ -23,19 +23,10 @@ using Autodesk.Revit.DB; using BH.oM.Adapters.Revit.Settings; using BH.oM.Base; -using BH.oM.Dimensional; -using BH.oM.Geometry; -using BH.oM.Facade.Elements; -using BH.Engine.Facade; -using System; +using BH.oM.Base.Attributes; using System.Collections.Generic; -using System.Linq; using System.ComponentModel; -using BH.oM.Base.Attributes; -using System.Runtime; -using BH.oM.Revit.Enums; -using BH.Engine.Geometry; -using BH.Engine.Base; +using System.Linq; namespace BH.Revit.Engine.Core { @@ -53,127 +44,9 @@ public static partial class Query [Output("panels", "Panels extracted from the input Revit curtain grid and converted to BHoM facade Openings.")] public static List FacadeCurtainPanels(this CurtainGrid curtainGrid, Document document, RevitSettings settings = null, Dictionary> refObjects = null) { - if (curtainGrid == null) - return null; - - List panels = curtainGrid.GetPanelIds().Select(x => document.GetElement(x)).ToList(); - List cells = curtainGrid.GetCurtainCells().ToList(); - - if (panels.Count != cells.Count) - return null; - - List mullions = curtainGrid.CurtainWallMullions(document, settings, refObjects); - - List result = new List(); - for (int i = 0; i < panels.Count; i++) - { - FamilyInstance panel = panels[i] as FamilyInstance; - if (panel == null) - continue; - - List outlines = new List(); - try - { - // This catches when PlanarizedCurveLoops throws an exception due to the cell having no loops, meaning in Revit it exists in the database but is no longer a valid CurtainWall cell - CurveArrArray x = cells[i].PlanarizedCurveLoops; - - // Collapse nonlinear edges of a cell to lines - valid because mullions are linear anyways - foreach (CurveArray array in cells[i].CurveLoops) - { - PolyCurve outline = new PolyCurve(); - foreach (Curve curve in array) - { - outline.Curves.Add(new BH.oM.Geometry.Line { Start = curve.GetEndPoint(0).PointFromRevit(), End = curve.GetEndPoint(1).PointFromRevit() }); - } - - outlines.Add(outline); - } - } - catch - { - continue; - } - - foreach (PolyCurve outline in outlines) - { - BH.oM.Facade.Elements.Opening bHoMOpening = new oM.Facade.Elements.Opening(); - bHoMOpening.OpeningConstruction = panel.Construction(settings, refObjects); - bHoMOpening.Type = panel.OpeningType(); - - // Add mullion information to the openings - bHoMOpening.Edges = new List(); - foreach (ICurve curve in outline.Curves) - { - // Find the correspondent mullions based on adjacency - BH.oM.Geometry.Point mid = curve.IPointAtParameter(0.5); - FrameEdge mullion = mullions.FirstOrDefault(x => x.Curve != null && mid.IDistance(x.Curve) <= settings.DistanceTolerance).DeepClone(); - if (mullion == null) - { - BH.Engine.Base.Compute.RecordWarning("Mullion information is missing for some panels in the curtain wall."); - mullion = new FrameEdge(); - } - - mullion.Curve = curve; - bHoMOpening.Edges.Add(mullion); - } - - bHoMOpening.Name = panel.Name; - - //Set identifiers, parameters & custom data - bHoMOpening.SetIdentifiers(panel); - bHoMOpening.CopyParameters(panel, settings.MappingSettings); - bHoMOpening.SetProperties(panel, settings.MappingSettings); - - result.Add(bHoMOpening); - } - } - - return result; - } - - - /***************************************************/ - /**** Private methods ****/ - /***************************************************/ - - private static BH.oM.Physical.Constructions.Construction Construction(this FamilyInstance panel, RevitSettings settings, Dictionary> refObjects) - { - if ((panel as Autodesk.Revit.DB.Panel)?.FindHostPanel() is ElementId hostId && panel.Document.GetElement(hostId) is Wall wall) - { - HostObjAttributes hostObjAttributes = wall.Document.GetElement(wall.GetTypeId()) as HostObjAttributes; - string materialGrade = wall.MaterialGrade(settings); - return hostObjAttributes.ConstructionFromRevit(materialGrade, settings, refObjects); - } - else - { - int category = panel.Category.Id.IntegerValue; - if (category == (int)Autodesk.Revit.DB.BuiltInCategory.OST_Walls) - { - HostObjAttributes hostObjAttributes = panel.Document.GetElement(panel.GetTypeId()) as HostObjAttributes; - string materialGrade = panel.MaterialGrade(settings); - return hostObjAttributes.ConstructionFromRevit(materialGrade, settings, refObjects); - } - else - return panel.GlazingConstruction(); - } - } - - /***************************************************/ - - private static BH.oM.Facade.Elements.OpeningType OpeningType(this FamilyInstance panel) - { - BuiltInCategory category = (BuiltInCategory)panel.Category.Id.IntegerValue; - if (category == Autodesk.Revit.DB.BuiltInCategory.OST_Windows || category == Autodesk.Revit.DB.BuiltInCategory.OST_CurtainWallPanels) - return BH.oM.Facade.Elements.OpeningType.Window; - else if (category == Autodesk.Revit.DB.BuiltInCategory.OST_Doors) - return BH.oM.Facade.Elements.OpeningType.Door; - else - return BH.oM.Facade.Elements.OpeningType.Undefined; + return curtainGrid?.GetPanelIds().Select(x => document.GetElement(x)).OfType().Select(x => x.FacadeOpeningFromRevit(settings, refObjects)).ToList(); } /***************************************************/ } } - - - diff --git a/Revit_Core_Engine/Query/OpeningSurface.cs b/Revit_Core_Engine/Query/OpeningSurface.cs index 40ee8a850..6ac640cf4 100644 --- a/Revit_Core_Engine/Query/OpeningSurface.cs +++ b/Revit_Core_Engine/Query/OpeningSurface.cs @@ -203,39 +203,8 @@ private static List OpeningSurfaces_LinkDocument(this FamilyInstance f private static List OpeningSurfaces_Curtain(this FamilyInstance familyInstance) { - List surfaces = new List(); - HostObject curtainHost = familyInstance.Host as HostObject; - if (curtainHost == null) - return null; - - List curtainGrids = curtainHost.ICurtainGrids(); - if (curtainGrids.Count != 0) - { - foreach (CurtainGrid cg in curtainGrids) - { - List ids = cg.GetPanelIds().ToList(); - List cells = cg.GetCurtainCells().ToList(); - if (ids.Count != cells.Count) - return null; - - for (int i = 0; i < ids.Count; i++) - { - if (ids[i].IntegerValue == familyInstance.Id.IntegerValue) - { - foreach (PolyCurve curve in cells[i].CurveLoops.FromRevit()) - { - PlanarSurface surface = BH.Engine.Geometry.Create.PlanarSurface(curve, null); - if (surface == null) - return null; - - surfaces.Add(surface); - } - } - } - } - } - - return surfaces; + List loops = familyInstance?.EdgeLoops(); + return loops?.Select(x => BH.Engine.Geometry.Create.PlanarSurface(x, null) as ISurface).Where(x => x != null).ToList(); } /***************************************************/ From 7c8e25080b8eacb3577bd0a0136b3cb0cb5457b3 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Fri, 15 Mar 2024 20:59:01 +0100 Subject: [PATCH 10/43] convert methods tweaked to fully leverage the newly added queries --- .../Convert/Facade/FromRevit/Opening.cs | 87 ++++++++----------- .../Convert/Facade/FromRevit/Panel.cs | 4 +- 2 files changed, 36 insertions(+), 55 deletions(-) diff --git a/Revit_Core_Engine/Convert/Facade/FromRevit/Opening.cs b/Revit_Core_Engine/Convert/Facade/FromRevit/Opening.cs index 021dd5acf..90fe17874 100644 --- a/Revit_Core_Engine/Convert/Facade/FromRevit/Opening.cs +++ b/Revit_Core_Engine/Convert/Facade/FromRevit/Opening.cs @@ -25,11 +25,9 @@ using BH.Engine.Geometry; using BH.oM.Adapters.Revit.Settings; using BH.oM.Base; +using BH.oM.Base.Attributes; using BH.oM.Facade.Elements; -using BH.oM.Facade.SectionProperties; using BH.oM.Geometry; -using BH.oM.Base.Attributes; -using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; @@ -48,76 +46,59 @@ public static partial class Convert [Input("refObjects", "Optional, a collection of objects already processed in the current adapter action, stored to avoid processing the same object more than once.")] [Output("opening", "BH.oM.Facade.Elements.Opening resulting from converting the input Revit FamilyInstance.")] public static oM.Facade.Elements.Opening FacadeOpeningFromRevit(this FamilyInstance familyInstance, RevitSettings settings = null, Dictionary> refObjects = null) - { - return familyInstance.FacadeOpeningFromRevit(null, settings, refObjects); - } - - /***************************************************/ - - [Description("Converts a Revit FamilyInstance to BH.oM.Facade.Elements.Opening.")] - [Input("familyInstance", "Revit FamilyInstance to be converted.")] - [Input("host", "Revit Element hosting the FamilyInstance to be converted.")] - [Input("settings", "Revit adapter settings to be used while performing the convert.")] - [Input("refObjects", "Optional, a collection of objects already processed in the current adapter action, stored to avoid processing the same object more than once.")] - [Output("opening", "BH.oM.Facade.Elements.Opening resulting from converting the input Revit FamilyInstance.")] - public static oM.Facade.Elements.Opening FacadeOpeningFromRevit(this FamilyInstance familyInstance, HostObject host = null, RevitSettings settings = null, Dictionary> refObjects = null) { if (familyInstance == null) return null; settings = settings.DefaultIfNull(); - string refId = familyInstance.Id.ReferenceIdentifier(host); + string refId = familyInstance.Id.ReferenceIdentifier(familyInstance.Host); oM.Facade.Elements.Opening opening = refObjects.GetValue(refId); if (opening != null) return opening; - // Extraction of frame edge property from Revit FamilyInstance is not implemented yet - BH.Engine.Base.Compute.RecordWarning($"Extraction of frame edge property from a Revit opening is currently not supported, property set to null. ElementId: {familyInstance.Id.IntegerValue}"); - FrameEdgeProperty frameEdgeProperty = null; - - BH.oM.Geometry.ISurface location = familyInstance.OpeningSurface(host, settings); - List edges = new List(); - if (location == null) + CurtainGrid hostGrid = familyInstance.HostCurtainGrid(); + if (hostGrid != null) { - if (host == null) + List edgeLoops = familyInstance.EdgeLoops(); + + if (edgeLoops != null && edgeLoops.Count != 0) { - BH.Engine.Base.Compute.RecordWarning(String.Format("Location of the opening could not be retrieved from the model (possibly it has zero area or lies on a non-planar face). An opening object without location has been returned. Revit ElementId: {0}", familyInstance.Id.IntegerValue)); + if (edgeLoops.Count != 1) + BH.Engine.Base.Compute.RecordWarning($"Opening has more than one closed outline. Revit ElementId: {familyInstance.Id.IntegerValue}"); + + List mullions = hostGrid.CurtainWallMullions(familyInstance.Document, settings, refObjects); + foreach (var curve in edgeLoops.SelectMany(x => x.SubParts())) + { + BH.oM.Geometry.Point mid = curve.IPointAtParameter(0.5); + FrameEdge mullion = mullions.FirstOrDefault(x => x.Curve != null && mid.IDistance(x.Curve) <= settings.DistanceTolerance); + + if (mullion == null) + BH.Engine.Base.Compute.RecordWarning($"Mullion information is missing for some edges of a curtain Opening. Revit ElementId: {{familyInstance.Id.IntegerValue}}\""); + + edges.Add(new FrameEdge { Curve = curve, FrameEdgeProperty = mullion?.FrameEdgeProperty }); + } } else - { - BH.Engine.Base.Compute.RecordError(String.Format("Location of the opening could not be retrieved from the model (possibly it has zero area or lies on a non-planar face), the opening has been skipped. Revit ElementId: {0}", familyInstance.Id.IntegerValue)); - return null; - } + BH.Engine.Base.Compute.RecordWarning($"Edge curves of the opening could not be retrieved from the model (possibly it has zero area). An opening object without frame edges has been returned. Revit ElementId: {familyInstance.Id.IntegerValue}"); } else { - List extCrvs = location.IExternalEdges().SelectMany(x => x.ISubParts()).ToList(); - edges = extCrvs.Select( x => new FrameEdge { Curve = x, FrameEdgeProperty = frameEdgeProperty }).ToList(); - } - - int category = familyInstance.Category.Id.IntegerValue; - oM.Physical.Constructions.Construction glazingConstruction = null; - if (category == (int)BuiltInCategory.OST_Walls) - { - HostObjAttributes hostObjAttributes = familyInstance.Document.GetElement(familyInstance.GetTypeId()) as HostObjAttributes; - string materialGrade = familyInstance.MaterialGrade(settings); - glazingConstruction = hostObjAttributes.ConstructionFromRevit(materialGrade, settings, refObjects); - } - else - { - glazingConstruction = familyInstance.GlazingConstruction(); + ISurface openingSurface = familyInstance.OpeningSurface(familyInstance.Host as HostObject, settings); + if (openingSurface != null) + { + List extCrvs = openingSurface.IExternalEdges().SelectMany(x => x.ISubParts()).ToList(); + edges.AddRange(extCrvs.Select(x => new FrameEdge { Curve = x, FrameEdgeProperty = null })); + BH.Engine.Base.Compute.RecordWarning($"Frame edge properties of the opening could not be retrieved from the model. Revit ElementId: {familyInstance.Id.IntegerValue}"); + } + else + BH.Engine.Base.Compute.RecordWarning($"Edge curves of the opening could not be retrieved from the model (possibly it has zero area or complex geometry). An opening object without frame edges has been returned. Revit ElementId: {familyInstance.Id.IntegerValue}"); } - opening = new BH.oM.Facade.Elements.Opening { Name = familyInstance.FamilyTypeFullName(), Edges = edges, OpeningConstruction = glazingConstruction }; - - if (category == (int)BuiltInCategory.OST_Windows || category == (int)BuiltInCategory.OST_CurtainWallPanels) - opening.Type = OpeningType.Window; - else if (category == (int)BuiltInCategory.OST_Doors) - opening.Type = OpeningType.Door; - else if (category == (int)BuiltInCategory.OST_Walls) - opening.Type = OpeningType.Undefined; + oM.Physical.Constructions.Construction construction = familyInstance.FacadePanelConstruction(settings, refObjects); + OpeningType openingType = familyInstance.FacadeOpeningType(); + opening = new BH.oM.Facade.Elements.Opening { Name = familyInstance.Name, Edges = edges, OpeningConstruction = construction, Type = openingType }; //Set identifiers, parameters & custom data opening.SetIdentifiers(familyInstance); diff --git a/Revit_Core_Engine/Convert/Facade/FromRevit/Panel.cs b/Revit_Core_Engine/Convert/Facade/FromRevit/Panel.cs index aded45917..ae907e1ec 100644 --- a/Revit_Core_Engine/Convert/Facade/FromRevit/Panel.cs +++ b/Revit_Core_Engine/Convert/Facade/FromRevit/Panel.cs @@ -115,14 +115,14 @@ public static oM.Facade.Elements.Panel FacadePanelFromRevit(this Wall wall, Revi foreach (FamilyInstance window in inserts.Where(x => x.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Windows)) { - BH.oM.Facade.Elements.Opening bHoMWindow = window.FacadeOpeningFromRevit(wall, settings, refObjects); + BH.oM.Facade.Elements.Opening bHoMWindow = window.FacadeOpeningFromRevit(settings, refObjects); if (bHoMWindow != null) openings.Add(bHoMWindow); } foreach (FamilyInstance door in inserts.Where(x => x.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Doors)) { - BH.oM.Facade.Elements.Opening bHoMDoor = door.FacadeOpeningFromRevit(wall, settings, refObjects); + BH.oM.Facade.Elements.Opening bHoMDoor = door.FacadeOpeningFromRevit(settings, refObjects); if (bHoMDoor != null) openings.Add(bHoMDoor); } From 353d509cb64f4c6109b3209b7b2ebb4fe69ac294 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Fri, 15 Mar 2024 21:18:03 +0100 Subject: [PATCH 11/43] concept of refObjects introduced to mullion and panel queries, renaming and alignment to improve readibility --- .../Convert/Facade/FromRevit/CurtainWall.cs | 4 +- .../Convert/Facade/FromRevit/Opening.cs | 8 +-- .../Convert/Physical/FromRevit/Roof.cs | 2 +- .../Convert/Physical/FromRevit/Wall.cs | 2 +- Revit_Core_Engine/Query/CurtainPanels.cs | 32 +++++++++++- .../Query/CurtainWallMullions.cs | 17 +++--- .../Query/FacadeCurtainPanels.cs | 52 ------------------- 7 files changed, 49 insertions(+), 68 deletions(-) delete mode 100644 Revit_Core_Engine/Query/FacadeCurtainPanels.cs diff --git a/Revit_Core_Engine/Convert/Facade/FromRevit/CurtainWall.cs b/Revit_Core_Engine/Convert/Facade/FromRevit/CurtainWall.cs index 58b2fc7f4..bbc808c51 100644 --- a/Revit_Core_Engine/Convert/Facade/FromRevit/CurtainWall.cs +++ b/Revit_Core_Engine/Convert/Facade/FromRevit/CurtainWall.cs @@ -65,7 +65,7 @@ public static CurtainWall CurtainWallFromRevit(this Wall wall, RevitSettings set if (wall.StackedWallOwnerId != null && wall.StackedWallOwnerId != ElementId.InvalidElementId) return null; - IEnumerable curtainPanels = wall.CurtainGrid.FacadeCurtainPanels(wall.Document, settings, refObjects); + IEnumerable curtainPanels = wall.FacadeCurtainPanels(settings, refObjects); if (curtainPanels == null || !curtainPanels.Any()) BH.Engine.Base.Compute.RecordError(String.Format("Processing of panels of Revit curtain wall failed. BHoM curtain wall without location has been returned. Revit ElementId: {0}", wall.Id.IntegerValue)); @@ -106,7 +106,7 @@ public static CurtainWall CurtainWallFromRevit(this CurtainSystem system, RevitS if (bHoMCurtainWall != null) return bHoMCurtainWall; - IEnumerable curtainPanels = system.ICurtainGrids().SelectMany(x => x.FacadeCurtainPanels(system.Document, settings, refObjects)).ToList(); + IEnumerable curtainPanels = system.FacadeCurtainPanels(settings, refObjects); if (curtainPanels == null || !curtainPanels.Any()) BH.Engine.Base.Compute.RecordError(String.Format("Processing of panels of Revit curtain wall failed. BHoM curtain wall without location has been returned. Revit ElementId: {0}", system.Id.IntegerValue)); diff --git a/Revit_Core_Engine/Convert/Facade/FromRevit/Opening.cs b/Revit_Core_Engine/Convert/Facade/FromRevit/Opening.cs index 90fe17874..e7f1c3758 100644 --- a/Revit_Core_Engine/Convert/Facade/FromRevit/Opening.cs +++ b/Revit_Core_Engine/Convert/Facade/FromRevit/Opening.cs @@ -58,8 +58,8 @@ public static oM.Facade.Elements.Opening FacadeOpeningFromRevit(this FamilyInsta return opening; List edges = new List(); - CurtainGrid hostGrid = familyInstance.HostCurtainGrid(); - if (hostGrid != null) + HostObject host = familyInstance.Host as HostObject; + if (host?.ICurtainGrids()?.Count > 0) { List edgeLoops = familyInstance.EdgeLoops(); @@ -68,7 +68,7 @@ public static oM.Facade.Elements.Opening FacadeOpeningFromRevit(this FamilyInsta if (edgeLoops.Count != 1) BH.Engine.Base.Compute.RecordWarning($"Opening has more than one closed outline. Revit ElementId: {familyInstance.Id.IntegerValue}"); - List mullions = hostGrid.CurtainWallMullions(familyInstance.Document, settings, refObjects); + List mullions = host.CurtainWallMullions(settings, refObjects); foreach (var curve in edgeLoops.SelectMany(x => x.SubParts())) { BH.oM.Geometry.Point mid = curve.IPointAtParameter(0.5); @@ -85,7 +85,7 @@ public static oM.Facade.Elements.Opening FacadeOpeningFromRevit(this FamilyInsta } else { - ISurface openingSurface = familyInstance.OpeningSurface(familyInstance.Host as HostObject, settings); + ISurface openingSurface = familyInstance.OpeningSurface(host, settings); if (openingSurface != null) { List extCrvs = openingSurface.IExternalEdges().SelectMany(x => x.ISubParts()).ToList(); diff --git a/Revit_Core_Engine/Convert/Physical/FromRevit/Roof.cs b/Revit_Core_Engine/Convert/Physical/FromRevit/Roof.cs index c6cc12c1c..36e270fb7 100644 --- a/Revit_Core_Engine/Convert/Physical/FromRevit/Roof.cs +++ b/Revit_Core_Engine/Convert/Physical/FromRevit/Roof.cs @@ -160,7 +160,7 @@ private static oM.Physical.Elements.Roof CurtainRoofFromRevit(this RoofBase roof bool partFailed = false; foreach (CurtainGrid cg in curtainGrids) { - List curtainPanels = cg.CurtainPanels(roof.Document, settings, refObjects); + List curtainPanels = cg.PhysicalCurtainPanels(roof.Document, settings, refObjects); if (curtainPanels == null) partFailed = true; else diff --git a/Revit_Core_Engine/Convert/Physical/FromRevit/Wall.cs b/Revit_Core_Engine/Convert/Physical/FromRevit/Wall.cs index 0297a6226..a313fd803 100644 --- a/Revit_Core_Engine/Convert/Physical/FromRevit/Wall.cs +++ b/Revit_Core_Engine/Convert/Physical/FromRevit/Wall.cs @@ -156,7 +156,7 @@ private static oM.Physical.Elements.Wall WallFromRevit_Solid(this Wall wall, Rev [Output("wall", "BH.oM.Physical.Elements.Wall resulting from converting the input Revit Wall.")] private static oM.Physical.Elements.Wall WallFromRevit_Curtain(this Wall wall, RevitSettings settings = null, Dictionary> refObjects = null) { - List curtainPanels = wall.CurtainGrid.CurtainPanels(wall.Document, settings, refObjects); + List curtainPanels = wall.CurtainGrid.PhysicalCurtainPanels(wall.Document, settings, refObjects); ISurface location = null; if (curtainPanels == null || curtainPanels.Count == 0) diff --git a/Revit_Core_Engine/Query/CurtainPanels.cs b/Revit_Core_Engine/Query/CurtainPanels.cs index 8fccff58f..6144c9f88 100644 --- a/Revit_Core_Engine/Query/CurtainPanels.cs +++ b/Revit_Core_Engine/Query/CurtainPanels.cs @@ -30,6 +30,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; +using BH.oM.Facade.Elements; namespace BH.Revit.Engine.Core { @@ -45,7 +46,7 @@ public static partial class Query [Input("settings", "Revit adapter settings to be used while performing the query.")] [Input("refObjects", "Optional, a collection of objects already processed in the current adapter action, stored to avoid processing the same object more than once.")] [Output("panels", "Panels extracted from the input Revit curtain grid and converted to BHoM IOpenings.")] - public static List CurtainPanels(this CurtainGrid curtainGrid, Document document, RevitSettings settings = null, Dictionary> refObjects = null) + public static List PhysicalCurtainPanels(this CurtainGrid curtainGrid, Document document, RevitSettings settings = null, Dictionary> refObjects = null) { if (curtainGrid == null) return null; @@ -64,7 +65,7 @@ public static List CurtainPanels(this CurtainGrid curtainGrid, Documen if (panel.get_BoundingBox(null) == null) { - ElementId hostPanelId = (panel as Panel)?.FindHostPanel(); + ElementId hostPanelId = (panel as Autodesk.Revit.DB.Panel)?.FindHostPanel(); if (hostPanelId == null || document.GetElement(hostPanelId)?.get_BoundingBox(null) == null) continue; } @@ -82,6 +83,33 @@ public static List CurtainPanels(this CurtainGrid curtainGrid, Documen } /***************************************************/ + + [Description("Extracts the panels from a Revit curtain grid and returns them in a form of BHoM facade Openings.")] + [Input("curtainGrid", "Revit curtain grid to extract the panels from.")] + [Input("document", "Revit document, to which the curtain grid belongs.")] + [Input("settings", "Revit adapter settings to be used while performing the query.")] + [Input("refObjects", "Optional, a collection of objects already processed in the current adapter action, stored to avoid processing the same object more than once.")] + [Output("panels", "Panels extracted from the input Revit curtain grid and converted to BHoM facade Openings.")] + public static List FacadeCurtainPanels(this HostObject element, RevitSettings settings = null, Dictionary> refObjects = null) + { + if (element == null) + return null; + + List openings = refObjects.GetValues(element.Id); + if (openings != null) + return openings; + + openings = new List(); + foreach (CurtainGrid grid in element.ICurtainGrids()) + { + openings.AddRange(grid.GetPanelIds().Select(x => element.Document.GetElement(x)).OfType().Select(x => x.FacadeOpeningFromRevit(settings, refObjects))); + } + + refObjects.AddOrReplace(element.Id, openings); + return openings; + } + + /***************************************************/ } } diff --git a/Revit_Core_Engine/Query/CurtainWallMullions.cs b/Revit_Core_Engine/Query/CurtainWallMullions.cs index 1f7d83564..1df879ddb 100644 --- a/Revit_Core_Engine/Query/CurtainWallMullions.cs +++ b/Revit_Core_Engine/Query/CurtainWallMullions.cs @@ -28,6 +28,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; +using BH.oM.Physical.Elements; namespace BH.Revit.Engine.Core { @@ -43,20 +44,24 @@ public static partial class Query [Input("settings", "Revit adapter settings to be used while performing the query.")] [Input("refObjects", "Optional, a collection of objects already processed in the current adapter action, stored to avoid processing the same object more than once.")] [Output("mullions", "Mullions extracted from the input Revit curtain grid and converted to BHoM FrameEdges.")] - public static List CurtainWallMullions(this CurtainGrid curtainGrid, Document document, RevitSettings settings = null, Dictionary> refObjects = null) + public static List CurtainWallMullions(this HostObject element, RevitSettings settings = null, Dictionary> refObjects = null) { - if (curtainGrid == null) + if (element == null) return null; - List result = new List(); - List mullions = curtainGrid.GetMullionIds().Select(x => document.GetElement(x)).ToList(); + List edges = refObjects.GetValues(element.Id); + if (edges != null) + return edges; + edges = new List(); + List mullions = element.ICurtainGrids().SelectMany(x => x.GetMullionIds()).Select(x => element.Document.GetElement(x)).ToList(); foreach (Mullion mullion in mullions.Where(x => x.get_BoundingBox(null) != null)) { - result.Add(mullion.FrameEdgeFromRevit(settings, refObjects)); + edges.Add(mullion.FrameEdgeFromRevit(settings, refObjects)); } - return result; + refObjects.AddOrReplace(element.Id, edges); + return edges; } /***************************************************/ diff --git a/Revit_Core_Engine/Query/FacadeCurtainPanels.cs b/Revit_Core_Engine/Query/FacadeCurtainPanels.cs deleted file mode 100644 index efb72645f..000000000 --- a/Revit_Core_Engine/Query/FacadeCurtainPanels.cs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using Autodesk.Revit.DB; -using BH.oM.Adapters.Revit.Settings; -using BH.oM.Base; -using BH.oM.Base.Attributes; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; - -namespace BH.Revit.Engine.Core -{ - public static partial class Query - { - /***************************************************/ - /**** Public methods ****/ - /***************************************************/ - - [Description("Extracts the panels from a Revit curtain grid and returns them in a form of BHoM facade Openings.")] - [Input("curtainGrid", "Revit curtain grid to extract the panels from.")] - [Input("document", "Revit document, to which the curtain grid belongs.")] - [Input("settings", "Revit adapter settings to be used while performing the query.")] - [Input("refObjects", "Optional, a collection of objects already processed in the current adapter action, stored to avoid processing the same object more than once.")] - [Output("panels", "Panels extracted from the input Revit curtain grid and converted to BHoM facade Openings.")] - public static List FacadeCurtainPanels(this CurtainGrid curtainGrid, Document document, RevitSettings settings = null, Dictionary> refObjects = null) - { - return curtainGrid?.GetPanelIds().Select(x => document.GetElement(x)).OfType().Select(x => x.FacadeOpeningFromRevit(settings, refObjects)).ToList(); - } - - /***************************************************/ - } -} From 7c2abde5e00096470e83d607006c6a1c10ba59d8 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Fri, 15 Mar 2024 21:21:02 +0100 Subject: [PATCH 12/43] further code unification --- .../Convert/Facade/FromRevit/CurtainWall.cs | 66 ++++++------------- 1 file changed, 20 insertions(+), 46 deletions(-) diff --git a/Revit_Core_Engine/Convert/Facade/FromRevit/CurtainWall.cs b/Revit_Core_Engine/Convert/Facade/FromRevit/CurtainWall.cs index bbc808c51..df09ea9bb 100644 --- a/Revit_Core_Engine/Convert/Facade/FromRevit/CurtainWall.cs +++ b/Revit_Core_Engine/Convert/Facade/FromRevit/CurtainWall.cs @@ -53,39 +53,7 @@ public static partial class Convert [Output("curtainWall", "BH.oM.Facade.Elements.CurtainWall resulting from converting the input Revit Wall.")] public static CurtainWall CurtainWallFromRevit(this Wall wall, RevitSettings settings = null, Dictionary> refObjects = null) { - if (wall == null) - return null; - - settings = settings.DefaultIfNull(); - - CurtainWall bHoMCurtainWall = refObjects.GetValue(wall.Id); - if (bHoMCurtainWall != null) - return bHoMCurtainWall; - - if (wall.StackedWallOwnerId != null && wall.StackedWallOwnerId != ElementId.InvalidElementId) - return null; - - IEnumerable curtainPanels = wall.FacadeCurtainPanels(settings, refObjects); - - if (curtainPanels == null || !curtainPanels.Any()) - BH.Engine.Base.Compute.RecordError(String.Format("Processing of panels of Revit curtain wall failed. BHoM curtain wall without location has been returned. Revit ElementId: {0}", wall.Id.IntegerValue)); - - // Get external edges of whole curtain wall - List allEdges = curtainPanels.SelectMany(x => x.Edges).ToList(); - List extEdges = allEdges.Distinct().Where(x => allEdges.Count(y => x.ElementId() == y.ElementId()) == 1).ToList(); - - bHoMCurtainWall = new CurtainWall { ExternalEdges = extEdges, Openings = curtainPanels.ToList(), Name = wall.WallType.Name }; - - bHoMCurtainWall.Name = wall.FamilyTypeFullName(); - - //Set identifiers, parameters & custom data - bHoMCurtainWall.SetIdentifiers(wall); - bHoMCurtainWall.CopyParameters(wall, settings.MappingSettings); - bHoMCurtainWall.SetProperties(wall, settings.MappingSettings); - - refObjects.AddOrReplace(wall.Id, bHoMCurtainWall); - - return bHoMCurtainWall; + return ((HostObject)wall).CurtainWallFromRevit(settings, refObjects); } /***************************************************/ @@ -97,32 +65,42 @@ public static CurtainWall CurtainWallFromRevit(this Wall wall, RevitSettings set [Output("curtainWall", "BH.oM.Facade.Elements.CurtainWall resulting from converting the input Revit CurtainSystem.")] public static CurtainWall CurtainWallFromRevit(this CurtainSystem system, RevitSettings settings = null, Dictionary> refObjects = null) { - if (system == null) + return ((HostObject)system).CurtainWallFromRevit(settings, refObjects); + } + + + /***************************************************/ + /**** Private Methods ****/ + /***************************************************/ + + private static CurtainWall CurtainWallFromRevit(this HostObject element, RevitSettings settings = null, Dictionary> refObjects = null) + { + if (element == null) return null; settings = settings.DefaultIfNull(); - CurtainWall bHoMCurtainWall = refObjects.GetValue(system.Id); + CurtainWall bHoMCurtainWall = refObjects.GetValue(element.Id); if (bHoMCurtainWall != null) return bHoMCurtainWall; - IEnumerable curtainPanels = system.FacadeCurtainPanels(settings, refObjects); + IEnumerable curtainPanels = element.FacadeCurtainPanels(settings, refObjects); if (curtainPanels == null || !curtainPanels.Any()) - BH.Engine.Base.Compute.RecordError(String.Format("Processing of panels of Revit curtain wall failed. BHoM curtain wall without location has been returned. Revit ElementId: {0}", system.Id.IntegerValue)); + BH.Engine.Base.Compute.RecordError($"Processing of panels of a Revit curtain wall failed. BHoM curtain wall without location has been returned. Revit ElementId: {element.Id.IntegerValue}"); // Get external edges of whole curtain wall List allEdges = curtainPanels.SelectMany(x => x.Edges).ToList(); List extEdges = allEdges.Distinct().Where(x => allEdges.Count(y => x == y) == 1).ToList(); - bHoMCurtainWall = new CurtainWall { ExternalEdges = extEdges, Openings = curtainPanels.ToList(), Name = system.FamilyTypeFullName() }; + bHoMCurtainWall = new CurtainWall { ExternalEdges = extEdges, Openings = curtainPanels.ToList(), Name = element.FamilyTypeFullName() }; //Set identifiers, parameters & custom data - bHoMCurtainWall.SetIdentifiers(system); - bHoMCurtainWall.CopyParameters(system, settings.MappingSettings); - bHoMCurtainWall.SetProperties(system, settings.MappingSettings); + bHoMCurtainWall.SetIdentifiers(element); + bHoMCurtainWall.CopyParameters(element, settings.MappingSettings); + bHoMCurtainWall.SetProperties(element, settings.MappingSettings); - refObjects.AddOrReplace(system.Id, bHoMCurtainWall); + refObjects.AddOrReplace(element.Id, bHoMCurtainWall); return bHoMCurtainWall; } @@ -130,7 +108,3 @@ public static CurtainWall CurtainWallFromRevit(this CurtainSystem system, RevitS /***************************************************/ } } - - - - From 40867bc7469df3996c8713741cc0dbb86fdb2432 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Fri, 15 Mar 2024 21:48:54 +0100 Subject: [PATCH 13/43] bug introduced to refObjects fixed --- Revit_Core_Engine/Query/CurtainPanels.cs | 5 +++-- Revit_Core_Engine/Query/CurtainWallMullions.cs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Revit_Core_Engine/Query/CurtainPanels.cs b/Revit_Core_Engine/Query/CurtainPanels.cs index 6144c9f88..541b85d01 100644 --- a/Revit_Core_Engine/Query/CurtainPanels.cs +++ b/Revit_Core_Engine/Query/CurtainPanels.cs @@ -95,7 +95,8 @@ public static List PhysicalCurtainPanels(this CurtainGrid curtainGrid, if (element == null) return null; - List openings = refObjects.GetValues(element.Id); + string refId = $"{element.Id}_CurtainPanels"; + List openings = refObjects.GetValues(refId); if (openings != null) return openings; @@ -105,7 +106,7 @@ public static List PhysicalCurtainPanels(this CurtainGrid curtainGrid, openings.AddRange(grid.GetPanelIds().Select(x => element.Document.GetElement(x)).OfType().Select(x => x.FacadeOpeningFromRevit(settings, refObjects))); } - refObjects.AddOrReplace(element.Id, openings); + refObjects.AddOrReplace(refId, openings); return openings; } diff --git a/Revit_Core_Engine/Query/CurtainWallMullions.cs b/Revit_Core_Engine/Query/CurtainWallMullions.cs index 1df879ddb..f6d47d773 100644 --- a/Revit_Core_Engine/Query/CurtainWallMullions.cs +++ b/Revit_Core_Engine/Query/CurtainWallMullions.cs @@ -49,7 +49,8 @@ public static List CurtainWallMullions(this HostObject element, Revit if (element == null) return null; - List edges = refObjects.GetValues(element.Id); + string refId = $"{element.Id}_Mullions"; + List edges = refObjects.GetValues(refId); if (edges != null) return edges; @@ -60,7 +61,7 @@ public static List CurtainWallMullions(this HostObject element, Revit edges.Add(mullion.FrameEdgeFromRevit(settings, refObjects)); } - refObjects.AddOrReplace(element.Id, edges); + refObjects.AddOrReplace(refId, edges); return edges; } From 0c4483068f6b3a7010cbcea83405f7b4ca434132 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Fri, 15 Mar 2024 22:35:33 +0100 Subject: [PATCH 14/43] edges to be returned as unified objects --- .../Convert/Facade/FromRevit/CurtainWall.cs | 18 +++++++ Revit_Core_Engine/Query/CurtainPanels.cs | 17 +++++-- Revit_Core_Engine/Query/EdgeLoops.cs | 31 +++++------- Revit_Core_Engine/Query/HasValidLocation.cs | 48 +++++++++++++++++++ 4 files changed, 90 insertions(+), 24 deletions(-) create mode 100644 Revit_Core_Engine/Query/HasValidLocation.cs diff --git a/Revit_Core_Engine/Convert/Facade/FromRevit/CurtainWall.cs b/Revit_Core_Engine/Convert/Facade/FromRevit/CurtainWall.cs index df09ea9bb..34c627443 100644 --- a/Revit_Core_Engine/Convert/Facade/FromRevit/CurtainWall.cs +++ b/Revit_Core_Engine/Convert/Facade/FromRevit/CurtainWall.cs @@ -89,6 +89,24 @@ private static CurtainWall CurtainWallFromRevit(this HostObject element, RevitSe if (curtainPanels == null || !curtainPanels.Any()) BH.Engine.Base.Compute.RecordError($"Processing of panels of a Revit curtain wall failed. BHoM curtain wall without location has been returned. Revit ElementId: {element.Id.IntegerValue}"); + // Unify edges + double sqTol = settings.DistanceTolerance * settings.DistanceTolerance; + List<(FrameEdge, BH.oM.Geometry.Point)> edges = new List<(FrameEdge, BH.oM.Geometry.Point)>(); + foreach (oM.Facade.Elements.Opening panel in curtainPanels) + { + for (int i = 0; i < panel.Edges.Count; i++) + { + FrameEdge edge = panel.Edges[i]; + BH.oM.Geometry.Point midPoint = edge.Curve.IPointAtParameter(0.5); + + FrameEdge existing = edges.FirstOrDefault(x => x.Item2.SquareDistance(midPoint) <= sqTol).Item1; + if (existing != null) + panel.Edges[i] = existing; + else + edges.Add((edge, midPoint)); + } + } + // Get external edges of whole curtain wall List allEdges = curtainPanels.SelectMany(x => x.Edges).ToList(); List extEdges = allEdges.Distinct().Where(x => allEdges.Count(y => x == y) == 1).ToList(); diff --git a/Revit_Core_Engine/Query/CurtainPanels.cs b/Revit_Core_Engine/Query/CurtainPanels.cs index 541b85d01..fb73410ca 100644 --- a/Revit_Core_Engine/Query/CurtainPanels.cs +++ b/Revit_Core_Engine/Query/CurtainPanels.cs @@ -78,7 +78,7 @@ public static List PhysicalCurtainPanels(this CurtainGrid curtainGrid, result.Add(panel.WindowFromRevit(BH.Engine.Geometry.Create.PlanarSurface(pc), settings, refObjects)); } } - + return result; } @@ -103,7 +103,17 @@ public static List PhysicalCurtainPanels(this CurtainGrid curtainGrid, openings = new List(); foreach (CurtainGrid grid in element.ICurtainGrids()) { - openings.AddRange(grid.GetPanelIds().Select(x => element.Document.GetElement(x)).OfType().Select(x => x.FacadeOpeningFromRevit(settings, refObjects))); + List cells = grid.GetCurtainCells().ToList(); + List panelIds = grid.GetPanelIds().ToList(); + for (int i = 0; i < panelIds.Count; i++) + { + if (cells[i].HasValidLocation()) + { + oM.Facade.Elements.Opening opening = (element.Document.GetElement(panelIds[i]) as FamilyInstance)?.FacadeOpeningFromRevit(settings, refObjects); + if (opening != null) + openings.Add(opening); + } + } } refObjects.AddOrReplace(refId, openings); @@ -113,6 +123,3 @@ public static List PhysicalCurtainPanels(this CurtainGrid curtainGrid, /***************************************************/ } } - - - diff --git a/Revit_Core_Engine/Query/EdgeLoops.cs b/Revit_Core_Engine/Query/EdgeLoops.cs index b7616a0b2..33accc085 100644 --- a/Revit_Core_Engine/Query/EdgeLoops.cs +++ b/Revit_Core_Engine/Query/EdgeLoops.cs @@ -49,30 +49,23 @@ public static List EdgeLoops(this FamilyInstance curtainCell) { if (ids[i].IntegerValue == curtainCell.Id.IntegerValue) { - try - { - // This catches when PlanarizedCurveLoops throws an exception due to the cell having no loops, meaning in Revit it exists in the database but is no longer a valid CurtainWall cell - CurveArrArray x = cells[i].PlanarizedCurveLoops; + if (!cells[i].HasValidLocation()) + return null; - // Collapse nonlinear edges of a cell to lines - valid because mullions are linear anyways - List outlines = new List(); - foreach (CurveArray array in cells[i].CurveLoops) + // Collapse nonlinear edges of a cell to lines - valid because mullions are linear anyways + List outlines = new List(); + foreach (CurveArray array in cells[i].CurveLoops) + { + PolyCurve outline = new PolyCurve(); + foreach (Curve curve in array) { - PolyCurve outline = new PolyCurve(); - foreach (Curve curve in array) - { - outline.Curves.Add(new BH.oM.Geometry.Line { Start = curve.GetEndPoint(0).PointFromRevit(), End = curve.GetEndPoint(1).PointFromRevit() }); - } - - outlines.Add(outline); + outline.Curves.Add(new BH.oM.Geometry.Line { Start = curve.GetEndPoint(0).PointFromRevit(), End = curve.GetEndPoint(1).PointFromRevit() }); } - return outlines; - } - catch - { - return null; + outlines.Add(outline); } + + return outlines; } } } diff --git a/Revit_Core_Engine/Query/HasValidLocation.cs b/Revit_Core_Engine/Query/HasValidLocation.cs new file mode 100644 index 000000000..21dbe8a7a --- /dev/null +++ b/Revit_Core_Engine/Query/HasValidLocation.cs @@ -0,0 +1,48 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using Autodesk.Revit.DB; + +namespace BH.Revit.Engine.Core +{ + public static partial class Query + { + /***************************************************/ + /**** Public methods ****/ + /***************************************************/ + + public static bool HasValidLocation(this CurtainCell curtainCell) + { + // This catches when PlanarizedCurveLoops throws an exception due to the cell having no loops, meaning in Revit it exists in the database but is no longer a valid CurtainWall cell + try + { + return curtainCell.PlanarizedCurveLoops?.IsEmpty == false; + } + catch + { + return false; + } + } + + /***************************************************/ + } +} From b8593098ee58afa58966e2bd3653a577f46b596c Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Tue, 2 Apr 2024 16:56:32 +0200 Subject: [PATCH 15/43] line projection method added --- Revit_Core_Engine/Modify/Project.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Revit_Core_Engine/Modify/Project.cs b/Revit_Core_Engine/Modify/Project.cs index 176a58d8e..f3922f6c6 100644 --- a/Revit_Core_Engine/Modify/Project.cs +++ b/Revit_Core_Engine/Modify/Project.cs @@ -21,6 +21,7 @@ */ using Autodesk.Revit.DB; +using BH.oM.Adapters.Revit; using BH.oM.Base.Attributes; using System.ComponentModel; @@ -47,5 +48,27 @@ public static XYZ Project(this XYZ point, Plane plane) } /***************************************************/ + + [Description("Projects a line on a given plane.")] + [Input("line", "Line to project on the plane.")] + [Input("plane", "Plane to project the line on.")] + [Output("projected", "Line projected on the input plane.")] + public static Line Project(this Line line, Plane plane) + { + if (line == null || plane == null) + return null; + + XYZ newStart = line.GetEndPoint(0).Project(plane); + XYZ newEnd = line.GetEndPoint(1).Project(plane); + if (newStart.DistanceTo(newEnd) > Tolerance.ShortCurve / 0.3048) + return Line.CreateBound(newStart, newEnd); + else + { + BH.Engine.Base.Compute.RecordWarning("Projection of a line on plane resulted in zero length curve."); + return null; + } + } + + /***************************************************/ } } From 929569524ee68de3f9ed0099be726a0dc6a0cdd1 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Tue, 2 Apr 2024 16:57:56 +0200 Subject: [PATCH 16/43] null meshes ruled out of the meshed geometry --- Revit_Core_Engine/Query/MeshedGeometry.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Revit_Core_Engine/Query/MeshedGeometry.cs b/Revit_Core_Engine/Query/MeshedGeometry.cs index cc3bf8e4c..4a2266378 100644 --- a/Revit_Core_Engine/Query/MeshedGeometry.cs +++ b/Revit_Core_Engine/Query/MeshedGeometry.cs @@ -48,7 +48,7 @@ public static partial class Query List result = element.Faces(options, settings).Select(x => x.Triangulate(options.DetailLevel.FaceTriangulationFactor()).MeshFromRevit()).ToList(); result.AddRange(element.GeometryPrimitives(options, settings).Where(x => x is Mesh).Cast().Select(x => x.MeshFromRevit())); result.AddRange(element.Curves(options, settings, false).Select(x => x.MeshFromRevit())); - return result; + return result.Where(x => x != null).ToList(); } /***************************************************/ From fbb807268ccbdf82e876c2d32ed0beb16ffaf0e4 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Tue, 2 Apr 2024 16:59:36 +0200 Subject: [PATCH 17/43] HostIdentifiers method tweaked to work without UIApplication --- Revit_Core_Engine/Query/HostIdentifiers.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Revit_Core_Engine/Query/HostIdentifiers.cs b/Revit_Core_Engine/Query/HostIdentifiers.cs index 2fc44fd30..0d0e354e5 100644 --- a/Revit_Core_Engine/Query/HostIdentifiers.cs +++ b/Revit_Core_Engine/Query/HostIdentifiers.cs @@ -79,11 +79,10 @@ public static RevitHostFragment HostIdentifiers(this FamilyInstance familyInstan hostId = host.Id.IntegerValue; if (host.Document.IsLinked) { - RevitLinkInstance linkInstance = host.Document.LinkInstance(new UIApplication(host.Document.Application).ActiveUIDocument.Document); - if (linkInstance != null) - hostLink = (familyInstance.Document.GetElement(linkInstance.GetTypeId()) as RevitLinkType)?.Name; - else - BH.Engine.Base.Compute.RecordWarning("The Revit element has been identified as hosted on a linked element, but the link document could not be identified."); + if (host.Document.IsDetached) + BH.Engine.Base.Compute.RecordWarning($"Host document name could not be scraped from a detached document."); + + hostLink = host.Document.Title + ".rvt"; } } From 351efd025cd35b46be5aa9e1cd09743a16d301a8 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Tue, 2 Apr 2024 16:59:58 +0200 Subject: [PATCH 18/43] default location of instances changed from origin point to null --- Revit_oM/Elements/DraftingInstance.cs | 2 +- Revit_oM/Elements/ModelInstance.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Revit_oM/Elements/DraftingInstance.cs b/Revit_oM/Elements/DraftingInstance.cs index 65f93b1e4..ba348fce5 100644 --- a/Revit_oM/Elements/DraftingInstance.cs +++ b/Revit_oM/Elements/DraftingInstance.cs @@ -38,7 +38,7 @@ public class DraftingInstance : BHoMObject, IInstance public virtual InstanceProperties Properties { get; set; } = new InstanceProperties(); [Description("Location of the instance in three dimensional space.")] - public virtual IGeometry Location { get; set; } = new Point(); + public virtual IGeometry Location { get; set; } = null; [Description("Orientation of the instance in 2 dimensional space (only X and Y vectors are relevant). Applicable only to point-based DraftingInstances. If null, a default orientation will be applied.")] public virtual Basis Orientation { get; set; } = null; diff --git a/Revit_oM/Elements/ModelInstance.cs b/Revit_oM/Elements/ModelInstance.cs index 9cc61577b..a746f5657 100644 --- a/Revit_oM/Elements/ModelInstance.cs +++ b/Revit_oM/Elements/ModelInstance.cs @@ -38,7 +38,7 @@ public class ModelInstance : BHoMObject, IInstance public virtual InstanceProperties Properties { get; set; } = new InstanceProperties(); [Description("Location of the instance in in three dimensional space.")] - public virtual IGeometry Location { get; set; } = new Point(); + public virtual IGeometry Location { get; set; } = null; [Description("Orientation of the instance in 3 dimensional space. Applicable only to point-based ModelInstances. If null, a default orientation will be applied.")] public virtual Basis Orientation { get; set; } = null; From c9e77f793e6d7dec2b96366f106396614577b4e5 Mon Sep 17 00:00:00 2001 From: Michal Pekacki Date: Wed, 17 Apr 2024 15:32:25 +0200 Subject: [PATCH 19/43] connected elements duplicate fix --- Revit_Core_Engine/Query/ConnectedElements.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Revit_Core_Engine/Query/ConnectedElements.cs b/Revit_Core_Engine/Query/ConnectedElements.cs index 02851ba3a..414b6804e 100644 --- a/Revit_Core_Engine/Query/ConnectedElements.cs +++ b/Revit_Core_Engine/Query/ConnectedElements.cs @@ -34,7 +34,7 @@ public static partial class Query /**** Public methods ****/ /***************************************************/ - [Description("Returns all elements connected to the MEP element. Works for Family Instances and MEPCurve objects.")] + [Description("Returns elements connected directly to the MEP element connectors. Works for Family Instances and MEPCurve objects.")] [Input("element", "MEP element to get the connected elements for.")] [Output("connectedElements", "List of the elements that are connected to the input element.")] public static List ConnectedElements(this Element element) @@ -61,7 +61,7 @@ public static List ConnectedElements(this Element element) return null; } - HashSet connectedElements = new HashSet(); + HashSet connectedElementIds = new HashSet(); foreach (Connector conn in connSet) { ConnectorSet allRefs = conn.AllRefs; @@ -69,11 +69,13 @@ public static List ConnectedElements(this Element element) { Element el = refConn?.Owner; if (el != null) - connectedElements.Add(el); + connectedElementIds.Add(el.Id.IntegerValue); } } - return connectedElements.ToList(); + Document doc = element.Document; + + return connectedElementIds.Select(x => doc.GetElement(new ElementId(x))).ToList(); } /***************************************************/ From 28d47987140c3b255c1d6154e7756cb36ff75666 Mon Sep 17 00:00:00 2001 From: Michal Pekacki Date: Wed, 17 Apr 2024 15:49:25 +0200 Subject: [PATCH 20/43] remove duplicates and host element fix --- Revit_Core_Engine/Query/ConnectedElements.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Revit_Core_Engine/Query/ConnectedElements.cs b/Revit_Core_Engine/Query/ConnectedElements.cs index 414b6804e..addd4b2fa 100644 --- a/Revit_Core_Engine/Query/ConnectedElements.cs +++ b/Revit_Core_Engine/Query/ConnectedElements.cs @@ -61,7 +61,7 @@ public static List ConnectedElements(this Element element) return null; } - HashSet connectedElementIds = new HashSet(); + List connectedElements = new List(); foreach (Connector conn in connSet) { ConnectorSet allRefs = conn.AllRefs; @@ -69,13 +69,14 @@ public static List ConnectedElements(this Element element) { Element el = refConn?.Owner; if (el != null) - connectedElementIds.Add(el.Id.IntegerValue); + connectedElements.Add(el); } } - Document doc = element.Document; + //remove duplicates and host element + connectedElements = connectedElements.Where(x => x.Id.IntegerValue != element.Id.IntegerValue).GroupBy(x => x.Id.IntegerValue).Select(x => x.First()).ToList(); - return connectedElementIds.Select(x => doc.GetElement(new ElementId(x))).ToList(); + return connectedElements; } /***************************************************/ From 725cdf8def0413fbaf9fe599440af84d45a3993f Mon Sep 17 00:00:00 2001 From: Michal Pekacki Date: Fri, 19 Apr 2024 12:37:24 +0200 Subject: [PATCH 21/43] connectors method added --- Revit_Core_Engine/Query/ConnectedElements.cs | 20 +---- Revit_Core_Engine/Query/Connectors.cs | 87 ++++++++++++++++++++ 2 files changed, 90 insertions(+), 17 deletions(-) create mode 100644 Revit_Core_Engine/Query/Connectors.cs diff --git a/Revit_Core_Engine/Query/ConnectedElements.cs b/Revit_Core_Engine/Query/ConnectedElements.cs index addd4b2fa..febdc1c5d 100644 --- a/Revit_Core_Engine/Query/ConnectedElements.cs +++ b/Revit_Core_Engine/Query/ConnectedElements.cs @@ -45,24 +45,10 @@ public static List ConnectedElements(this Element element) return null; } - ConnectorSet connSet = null; - if (element is FamilyInstance) - { - connSet = (element as FamilyInstance).MEPModel?.ConnectorManager?.Connectors; - } - else if (element is MEPCurve) - { - connSet = (element as MEPCurve).ConnectorManager?.Connectors; - } - - if (connSet == null) - { - BH.Engine.Base.Compute.RecordError("Input element is not supported by the method. Check if element is a valid MEP object."); - return null; - } - + List connectors = element.Connectors(); List connectedElements = new List(); - foreach (Connector conn in connSet) + + foreach (Connector conn in connectors) { ConnectorSet allRefs = conn.AllRefs; foreach (Connector refConn in allRefs) diff --git a/Revit_Core_Engine/Query/Connectors.cs b/Revit_Core_Engine/Query/Connectors.cs new file mode 100644 index 000000000..2e76c0471 --- /dev/null +++ b/Revit_Core_Engine/Query/Connectors.cs @@ -0,0 +1,87 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using Autodesk.Revit.DB; +using BH.oM.Base.Attributes; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +namespace BH.Revit.Engine.Core +{ + public static partial class Query + { + /***************************************************/ + /**** Public methods ****/ + /***************************************************/ + + [Description("Returns connectors of the MEP element. MEPCurve connectors are sorted by distance from the startpoint, FamilyInstace connectors by IsPrimary property.")] + [Input("element", "MEP element to get the connectors from.")] + [Output("cconnectors", "MEP Element connectors.")] + public static List Connectors(this Element element) + { + return SortedConnectors(element as dynamic); + } + + /***************************************************/ + + private static List SortedConnectors(this MEPCurve mepCurve) + { + XYZ startPoint = (mepCurve.Location as LocationCurve).Curve.GetEndPoint(0); + ConnectorSet connSet = mepCurve.ConnectorManager?.Connectors; + + List connList = new List(); + foreach (Connector conn in connSet) + connList.Add(conn); + + connList = connList.OrderBy(x => x.Origin.DistanceTo(startPoint)).ToList(); + + return connList; + } + + /***************************************************/ + + private static List SortedConnectors(this FamilyInstance familyInstance) + { + ConnectorSet connSet = familyInstance.MEPModel?.ConnectorManager?.Connectors; + + List connList = new List(); + foreach (Connector conn in connSet) + connList.Add(conn); + + connList = connList.OrderByDescending(x => x.GetMEPConnectorInfo().IsPrimary).ToList(); + + return connList; + } + + /***************************************************/ + + private static List SortedConnectors(this Element element) + { + BH.Engine.Base.Compute.RecordError("Input element is not supported by the method. Check if element is a valid MEP object."); + return null; + } + + /***************************************************/ + } +} + From f92b9a75373036a540be85d2563972dcff5444d2 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Fri, 19 Apr 2024 13:29:53 +0200 Subject: [PATCH 22/43] orientation angle query for mullion added --- .../Query/MullionElementProperty.cs | 5 +++- Revit_Core_Engine/Query/OrientationAngle.cs | 28 +++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/Revit_Core_Engine/Query/MullionElementProperty.cs b/Revit_Core_Engine/Query/MullionElementProperty.cs index e42e282d0..b4803b685 100644 --- a/Revit_Core_Engine/Query/MullionElementProperty.cs +++ b/Revit_Core_Engine/Query/MullionElementProperty.cs @@ -62,7 +62,10 @@ public static FrameEdgeProperty MullionElementProperty(this Mullion mullion, Rev if (profile == null) BH.Engine.Base.Compute.RecordWarning($"Mullion profile could not be extracted. ElementId: {mullion.Id.IntegerValue}"); - List sectionProperties = new List { BH.Engine.Physical.Create.ConstantFramingProperty(profile, material, 0, mullion.Symbol.Name) }; + // Get rotation + double rotation = mullion.OrientationAngle(settings); + + List sectionProperties = new List { BH.Engine.Physical.Create.ConstantFramingProperty(profile, material, rotation, mullion.Symbol.Name) }; frameEdgeProperty = new FrameEdgeProperty { Name = mullion.Symbol.Name, SectionProperties = sectionProperties }; //Set identifiers, parameters & custom data diff --git a/Revit_Core_Engine/Query/OrientationAngle.cs b/Revit_Core_Engine/Query/OrientationAngle.cs index 51c62d423..b8e8a0ea8 100644 --- a/Revit_Core_Engine/Query/OrientationAngle.cs +++ b/Revit_Core_Engine/Query/OrientationAngle.cs @@ -49,9 +49,13 @@ public static double OrientationAngle(this FamilyInstance familyInstance, RevitS settings = settings.DefaultIfNull(); double rotation = double.NaN; - if (typeof(Column).BuiltInCategories().Contains((BuiltInCategory)familyInstance.Category.Id.IntegerValue)) + if (familyInstance is Mullion) + return familyInstance.OrientationAngleMullion(settings); + + BuiltInCategory category = (BuiltInCategory)familyInstance.Category.Id.IntegerValue; + if (typeof(Column).BuiltInCategories().Contains(category)) rotation = familyInstance.OrientationAngleColumn(settings); - else if (typeof(IFramingElement).BuiltInCategories().Contains((BuiltInCategory)familyInstance.Category.Id.IntegerValue)) + else if (familyInstance is Mullion || typeof(IFramingElement).BuiltInCategories().Contains(category)) rotation = familyInstance.OrientationAngleFraming(settings); return rotation; @@ -178,6 +182,26 @@ public static double OrientationAngleFraming(this FamilyInstance familyInstance, } /***************************************************/ + + [Description("Extracts a BHoM-representative mullion orientation angle from a given Revit family instance.")] + [Input("familyInstance", "Revit family instance to extract the orientation angle from.")] + [Input("settings", "Revit adapter settings to be used while performing the query.")] + [Output("angle", "BHoM-representative mullion orientation angle extracted from the input Revit family instance.")] + public static double OrientationAngleMullion(this FamilyInstance familyInstance, RevitSettings settings = null) + { + double rotation; + settings = settings.DefaultIfNull(); + + Transform transform = familyInstance.GetTotalTransform(); + if (1 - Math.Abs(transform.BasisZ.DotProduct(XYZ.BasisZ)) <= settings.AngleTolerance) + rotation = XYZ.BasisY.AngleOnPlaneTo(transform.BasisY, transform.BasisZ); + else + rotation = XYZ.BasisZ.AngleOnPlaneTo(transform.BasisX, transform.BasisZ); + + return rotation.NormalizeAngleDomain(); + } + + /***************************************************/ } } From 8f93dde2adbd429041c339e2342c99302e5b1b7c Mon Sep 17 00:00:00 2001 From: Michal Pekacki Date: Mon, 22 Apr 2024 09:26:28 +0200 Subject: [PATCH 23/43] removed sorting by primary connector --- Revit_Core_Engine/Query/Connectors.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Revit_Core_Engine/Query/Connectors.cs b/Revit_Core_Engine/Query/Connectors.cs index 2e76c0471..441e3f8d3 100644 --- a/Revit_Core_Engine/Query/Connectors.cs +++ b/Revit_Core_Engine/Query/Connectors.cs @@ -68,8 +68,6 @@ private static List SortedConnectors(this FamilyInstance familyInstan foreach (Connector conn in connSet) connList.Add(conn); - connList = connList.OrderByDescending(x => x.GetMEPConnectorInfo().IsPrimary).ToList(); - return connList; } From 97b3dc2e3226eec92c6966d837c9cd7586c6f515 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Mon, 22 Apr 2024 10:23:11 +0200 Subject: [PATCH 24/43] descriptions tweaked --- Revit_Core_Engine/Query/CurtainPanels.cs | 11 +++++------ Revit_Core_Engine/Query/CurtainWallMullions.cs | 7 +++---- Revit_Core_Engine/Query/EdgeLoops.cs | 5 +++-- Revit_Core_Engine/Query/FacadeOpeningMaterial.cs | 6 ++++++ Revit_Core_Engine/Query/FacadeOpeningType.cs | 5 +++++ Revit_Core_Engine/Query/FacadePanelConstruction.cs | 5 +++-- Revit_Core_Engine/Query/HasValidLocation.cs | 5 +++++ Revit_Core_Engine/Query/HostCurtainGrid.cs | 5 +++++ Revit_Core_Engine/Query/IsTransparent.cs | 10 ++++++++-- 9 files changed, 43 insertions(+), 16 deletions(-) diff --git a/Revit_Core_Engine/Query/CurtainPanels.cs b/Revit_Core_Engine/Query/CurtainPanels.cs index fb73410ca..a74bc0224 100644 --- a/Revit_Core_Engine/Query/CurtainPanels.cs +++ b/Revit_Core_Engine/Query/CurtainPanels.cs @@ -40,12 +40,12 @@ public static partial class Query /**** Public methods ****/ /***************************************************/ - [Description("Extracts the panels from a Revit curtain grid and returns them in a form of BHoM IOpenings.")] + [Description("Extracts the panels from a Revit curtain grid and returns them in a form of BHoM physical IOpenings.")] [Input("curtainGrid", "Revit curtain grid to extract the panels from.")] [Input("document", "Revit document, to which the curtain grid belongs.")] [Input("settings", "Revit adapter settings to be used while performing the query.")] [Input("refObjects", "Optional, a collection of objects already processed in the current adapter action, stored to avoid processing the same object more than once.")] - [Output("panels", "Panels extracted from the input Revit curtain grid and converted to BHoM IOpenings.")] + [Output("panels", "Panels extracted from the input Revit curtain grid and converted to BHoM physical IOpenings.")] public static List PhysicalCurtainPanels(this CurtainGrid curtainGrid, Document document, RevitSettings settings = null, Dictionary> refObjects = null) { if (curtainGrid == null) @@ -84,12 +84,11 @@ public static List PhysicalCurtainPanels(this CurtainGrid curtainGrid, /***************************************************/ - [Description("Extracts the panels from a Revit curtain grid and returns them in a form of BHoM facade Openings.")] - [Input("curtainGrid", "Revit curtain grid to extract the panels from.")] - [Input("document", "Revit document, to which the curtain grid belongs.")] + [Description("Extracts the panels from a Revit curtain element and returns them in a form of BHoM facade Openings.")] + [Input("element", "Revit curtain element to extract the panels from.")] [Input("settings", "Revit adapter settings to be used while performing the query.")] [Input("refObjects", "Optional, a collection of objects already processed in the current adapter action, stored to avoid processing the same object more than once.")] - [Output("panels", "Panels extracted from the input Revit curtain grid and converted to BHoM facade Openings.")] + [Output("panels", "Panels extracted from the input Revit curtain element and converted to BHoM facade Openings.")] public static List FacadeCurtainPanels(this HostObject element, RevitSettings settings = null, Dictionary> refObjects = null) { if (element == null) diff --git a/Revit_Core_Engine/Query/CurtainWallMullions.cs b/Revit_Core_Engine/Query/CurtainWallMullions.cs index f6d47d773..b86867adf 100644 --- a/Revit_Core_Engine/Query/CurtainWallMullions.cs +++ b/Revit_Core_Engine/Query/CurtainWallMullions.cs @@ -38,12 +38,11 @@ public static partial class Query /**** Public methods ****/ /***************************************************/ - [Description("Extracts the mullions from a Revit curtain grid and returns them in a form of BHoM FrameEdges.")] - [Input("curtainGrid", "Revit curtain grid to extract the mullions from.")] - [Input("document", "Revit document, to which the curtain grid belongs.")] + [Description("Extracts the mullions from a Revit curtain element and returns them in a form of BHoM FrameEdges.")] + [Input("element", "Revit curtain element to extract the mullions from.")] [Input("settings", "Revit adapter settings to be used while performing the query.")] [Input("refObjects", "Optional, a collection of objects already processed in the current adapter action, stored to avoid processing the same object more than once.")] - [Output("mullions", "Mullions extracted from the input Revit curtain grid and converted to BHoM FrameEdges.")] + [Output("mullions", "Mullions extracted from the input Revit curtain element and converted to BHoM FrameEdges.")] public static List CurtainWallMullions(this HostObject element, RevitSettings settings = null, Dictionary> refObjects = null) { if (element == null) diff --git a/Revit_Core_Engine/Query/EdgeLoops.cs b/Revit_Core_Engine/Query/EdgeLoops.cs index 33accc085..fe240fd06 100644 --- a/Revit_Core_Engine/Query/EdgeLoops.cs +++ b/Revit_Core_Engine/Query/EdgeLoops.cs @@ -35,6 +35,9 @@ public static partial class Query /**** Public methods ****/ /***************************************************/ + [Description("Extracts edge loops of a given curtain cell.")] + [Input("curtainCell", "Curtain cell to query for edge loops.")] + [Output("edgeLoops", "Edge loops extracted from the input curtain cell.")] public static List EdgeLoops(this FamilyInstance curtainCell) { HostObject curtainHost = curtainCell.Host as HostObject; @@ -76,5 +79,3 @@ public static List EdgeLoops(this FamilyInstance curtainCell) /***************************************************/ } } - - diff --git a/Revit_Core_Engine/Query/FacadeOpeningMaterial.cs b/Revit_Core_Engine/Query/FacadeOpeningMaterial.cs index f26efb9ad..33a372b0c 100644 --- a/Revit_Core_Engine/Query/FacadeOpeningMaterial.cs +++ b/Revit_Core_Engine/Query/FacadeOpeningMaterial.cs @@ -21,7 +21,9 @@ */ using Autodesk.Revit.DB; +using BH.oM.Base.Attributes; using System.Collections.Generic; +using System.ComponentModel; namespace BH.Revit.Engine.Core { @@ -31,6 +33,10 @@ public static partial class Query /**** Public methods ****/ /***************************************************/ + [Description("Extracts the material that covers largest area of a given facade opening represented by FamilyInstance." + + "\nLargest area is meant to be glazing or spandrel, but may give inconsistent results for elements with very low glazing to frame area ratio.")] + [Input("opening", "Facade opening to query for dominating material.")] + [Output("material", "Material that covers largest area of the input FamilyInstance representing a facade opening.")] public static Material FacadeOpeningMaterial(this FamilyInstance opening) { List solids = opening?.Solids(new Options()); diff --git a/Revit_Core_Engine/Query/FacadeOpeningType.cs b/Revit_Core_Engine/Query/FacadeOpeningType.cs index 6de33fc1d..4211458e9 100644 --- a/Revit_Core_Engine/Query/FacadeOpeningType.cs +++ b/Revit_Core_Engine/Query/FacadeOpeningType.cs @@ -21,7 +21,9 @@ */ using Autodesk.Revit.DB; +using BH.oM.Base.Attributes; using BH.oM.Facade.Elements; +using System.ComponentModel; namespace BH.Revit.Engine.Core { @@ -31,6 +33,9 @@ public static partial class Query /**** Public methods ****/ /***************************************************/ + [Description("Deducts facade opening type from a FamilyInstance representing a curtain panel.")] + [Input("panel", "FamilyInstance representing a curtain panel.")] + [Output("openingType", "Facade opening type deducted from the input curtain panel represented by a FamilyInstance.")] public static OpeningType FacadeOpeningType(this FamilyInstance panel) { BuiltInCategory category = (BuiltInCategory)panel.Category.Id.IntegerValue; diff --git a/Revit_Core_Engine/Query/FacadePanelConstruction.cs b/Revit_Core_Engine/Query/FacadePanelConstruction.cs index a90f46950..79090f2a5 100644 --- a/Revit_Core_Engine/Query/FacadePanelConstruction.cs +++ b/Revit_Core_Engine/Query/FacadePanelConstruction.cs @@ -36,9 +36,10 @@ public static partial class Query /**** Public methods ****/ /***************************************************/ - [Description("Extracts the glazing property in a form of Physical.Constructions.Construction from the given FamilyInstance.")] - [Input("familyInstance", "Revit FamilyInstance to be queried for its glazing property.")] + [Description("Extracts the glazing property in a form of Physical.Constructions.Construction from the given curtain panel represented by a FamilyInstance.")] + [Input("panel", "Revit FamilyInstance representing the curtain panel to be queried for its glazing property.")] [Input("settings", "Revit adapter settings to be used while performing the query.")] + [Input("refObjects", "Optional, a collection of objects already processed in the current adapter action, stored to avoid processing the same object more than once.")] [Output("construction", "Glazing property of the input Revit FamilyInstance, in a form of Physical.Constructions.Construction.")] public static BH.oM.Physical.Constructions.Construction FacadePanelConstruction(this FamilyInstance panel, RevitSettings settings, Dictionary> refObjects) { diff --git a/Revit_Core_Engine/Query/HasValidLocation.cs b/Revit_Core_Engine/Query/HasValidLocation.cs index 21dbe8a7a..139495e48 100644 --- a/Revit_Core_Engine/Query/HasValidLocation.cs +++ b/Revit_Core_Engine/Query/HasValidLocation.cs @@ -21,6 +21,8 @@ */ using Autodesk.Revit.DB; +using BH.oM.Base.Attributes; +using System.ComponentModel; namespace BH.Revit.Engine.Core { @@ -30,6 +32,9 @@ public static partial class Query /**** Public methods ****/ /***************************************************/ + [Description("Checks whether a given curtain cell has valid location (exists in space).")] + [Input("curtainCell", "Curtain cell to check.")] + [Output("isValid", "True if the input curtain cell has valid location (exists in space), otherwise false.")] public static bool HasValidLocation(this CurtainCell curtainCell) { // This catches when PlanarizedCurveLoops throws an exception due to the cell having no loops, meaning in Revit it exists in the database but is no longer a valid CurtainWall cell diff --git a/Revit_Core_Engine/Query/HostCurtainGrid.cs b/Revit_Core_Engine/Query/HostCurtainGrid.cs index 6dec2506d..09ccf3e68 100644 --- a/Revit_Core_Engine/Query/HostCurtainGrid.cs +++ b/Revit_Core_Engine/Query/HostCurtainGrid.cs @@ -21,7 +21,9 @@ */ using Autodesk.Revit.DB; +using BH.oM.Base.Attributes; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; namespace BH.Revit.Engine.Core @@ -32,6 +34,9 @@ public static partial class Query /**** Public methods ****/ /***************************************************/ + [Description("Finds CurtainGrid that hosts a given facade element.")] + [Input("familyInstance", "Curtain element to query for host CurtainGrid.")] + [Output("host", "Host CurtainGrid of the input facade element. Null if the element is not hosted by any parent element.")] public static CurtainGrid HostCurtainGrid(this FamilyInstance familyInstance) { List curtainGrids = (familyInstance?.Host as HostObject)?.ICurtainGrids(); diff --git a/Revit_Core_Engine/Query/IsTransparent.cs b/Revit_Core_Engine/Query/IsTransparent.cs index ecd60218e..fa2f0e0fb 100644 --- a/Revit_Core_Engine/Query/IsTransparent.cs +++ b/Revit_Core_Engine/Query/IsTransparent.cs @@ -21,8 +21,8 @@ */ using Autodesk.Revit.DB; -using System.Collections.Generic; -using System.Linq; +using BH.oM.Base.Attributes; +using System.ComponentModel; namespace BH.Revit.Engine.Core { @@ -32,6 +32,9 @@ public static partial class Query /**** Public methods ****/ /***************************************************/ + [Description("Checks whether glazing of a given curtain panel is transparent (>50%).")] + [Input("panel", "Curtain panel to check for transparency.")] + [Output("isTransparent", "True if glazing of the input curtain panel is transparent (>50%), otherwise false.")] public static bool IsTransparent(this FamilyInstance panel) { return panel?.FacadeOpeningMaterial()?.IsTransparent() == true; @@ -39,6 +42,9 @@ public static bool IsTransparent(this FamilyInstance panel) /***************************************************/ + [Description("Checks whether a given material is transparent (>50%).")] + [Input("material", "Material to check for transparency.")] + [Output("isTransparent", "True if the input material is transparent (>50%), otherwise false.")] public static bool IsTransparent(this Material material) { return material != null && material.Transparency >= 50; From 55f7de7daf2704d88735ae2db3bd486451da1608 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Mon, 22 Apr 2024 12:00:43 +0200 Subject: [PATCH 25/43] versioning fix --- Revit_Core_Engine/Query/CurtainPanels.cs | 1 + Revit_Core_Engine/Versioning_72.json | 48 ++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 Revit_Core_Engine/Versioning_72.json diff --git a/Revit_Core_Engine/Query/CurtainPanels.cs b/Revit_Core_Engine/Query/CurtainPanels.cs index a74bc0224..d6f0bd5be 100644 --- a/Revit_Core_Engine/Query/CurtainPanels.cs +++ b/Revit_Core_Engine/Query/CurtainPanels.cs @@ -40,6 +40,7 @@ public static partial class Query /**** Public methods ****/ /***************************************************/ + [PreviousVersion("7.2", "BH.Revit.Engine.Core.Query.CurtainPanels(Autodesk.Revit.DB.CurtainGrid, Autodesk.Revit.DB.Document, BH.oM.Adapters.Revit.Settings.RevitSettings, System.Collections.Generic.Dictionary>)")] [Description("Extracts the panels from a Revit curtain grid and returns them in a form of BHoM physical IOpenings.")] [Input("curtainGrid", "Revit curtain grid to extract the panels from.")] [Input("document", "Revit document, to which the curtain grid belongs.")] diff --git a/Revit_Core_Engine/Versioning_72.json b/Revit_Core_Engine/Versioning_72.json new file mode 100644 index 000000000..b249502b1 --- /dev/null +++ b/Revit_Core_Engine/Versioning_72.json @@ -0,0 +1,48 @@ +{ + "Namespace": { + "ToNew": { + + }, + "ToOld": { + + } + }, + "Type": { + "ToNew": { + }, + "ToOld": { + } + }, + "Method": { + "ToNew": { + }, + "ToOld": { + + } + }, + "Property": { + "ToNew": { + }, + "ToOld": { + } + }, + "Dataset": { + "ToNew": { + + }, + "ToOld": { + + }, + "MessageForDeleted": { + + } + }, + "MessageForDeleted": { + "BH.Revit.Engine.Core.Convert.FacadeOpeningFromRevit(Autodesk.Revit.DB.FamilyInstance, Autodesk.Revit.DB.HostObject, BH.oM.Adapters.Revit.Settings.RevitSettings, System.Collections.Generic.Dictionary>)": "That method had been removed as it is covered with an overload that does not take host input. Please contact the developers if you still need it in the original form." + }, + "MessageForNoUpgrade": { + "BH.Revit.Engine.Core.Query.CurtainWallMullions(Autodesk.Revit.DB.CurtainGrid, Autodesk.Revit.DB.Document, BH.oM.Adapters.Revit.Settings.RevitSettings, System.Collections.Generic.Dictionary>)": "That method had been refactored resulting in a change of its signature that cannot be versioned (method takes HostObject now). Please contact the developers if you still need it in the original form.", + "BH.Revit.Engine.Core.Query.GlazingConstruction(Autodesk.Revit.DB.FamilyInstance, BH.oM.Adapters.Revit.Settings.RevitSettings)": "Please use BH.Revit.Engine.Core.Query.FacadePanelConstruction method instead.", + "BH.Revit.Engine.Core.Query.FacadeCurtainPanels(Autodesk.Revit.DB.CurtainGrid, Autodesk.Revit.DB.Document, BH.oM.Adapters.Revit.Settings.RevitSettings, System.Collections.Generic.Dictionary>)": "That method had been refactored resulting in a change of its signature that cannot be versioned (method takes HostObject now). Please contact the developers if you still need it in the original form." + } +} \ No newline at end of file From d23460ecd44ae0ca7a7d11627fa8d1daf197b7fe Mon Sep 17 00:00:00 2001 From: Michal Pekacki Date: Mon, 22 Apr 2024 14:46:23 +0200 Subject: [PATCH 26/43] update after @pawelbaran comments --- Revit_Core_Engine/Query/ConnectedElements.cs | 11 +++++------ Revit_Core_Engine/Query/Connectors.cs | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Revit_Core_Engine/Query/ConnectedElements.cs b/Revit_Core_Engine/Query/ConnectedElements.cs index febdc1c5d..8ecf8a5d6 100644 --- a/Revit_Core_Engine/Query/ConnectedElements.cs +++ b/Revit_Core_Engine/Query/ConnectedElements.cs @@ -46,7 +46,7 @@ public static List ConnectedElements(this Element element) } List connectors = element.Connectors(); - List connectedElements = new List(); + HashSet connectedElements = new HashSet(); foreach (Connector conn in connectors) { @@ -54,15 +54,14 @@ public static List ConnectedElements(this Element element) foreach (Connector refConn in allRefs) { Element el = refConn?.Owner; - if (el != null) - connectedElements.Add(el); + if (el != null && el.Id != element.Id) + connectedElements.Add(el.Id); } } - //remove duplicates and host element - connectedElements = connectedElements.Where(x => x.Id.IntegerValue != element.Id.IntegerValue).GroupBy(x => x.Id.IntegerValue).Select(x => x.First()).ToList(); + Document doc = element.Document; - return connectedElements; + return connectedElements.Select(x => doc.GetElement(x)).ToList(); } /***************************************************/ diff --git a/Revit_Core_Engine/Query/Connectors.cs b/Revit_Core_Engine/Query/Connectors.cs index 441e3f8d3..5283dea1e 100644 --- a/Revit_Core_Engine/Query/Connectors.cs +++ b/Revit_Core_Engine/Query/Connectors.cs @@ -36,20 +36,25 @@ public static partial class Query [Description("Returns connectors of the MEP element. MEPCurve connectors are sorted by distance from the startpoint, FamilyInstace connectors by IsPrimary property.")] [Input("element", "MEP element to get the connectors from.")] - [Output("cconnectors", "MEP Element connectors.")] + [Output("connectors", "MEP Element connectors.")] public static List Connectors(this Element element) { return SortedConnectors(element as dynamic); } + /***************************************************/ + /**** Private methods ****/ /***************************************************/ private static List SortedConnectors(this MEPCurve mepCurve) { XYZ startPoint = (mepCurve.Location as LocationCurve).Curve.GetEndPoint(0); ConnectorSet connSet = mepCurve.ConnectorManager?.Connectors; - List connList = new List(); + + if (connSet == null) + return connList; + foreach (Connector conn in connSet) connList.Add(conn); @@ -63,8 +68,11 @@ private static List SortedConnectors(this MEPCurve mepCurve) private static List SortedConnectors(this FamilyInstance familyInstance) { ConnectorSet connSet = familyInstance.MEPModel?.ConnectorManager?.Connectors; - List connList = new List(); + + if (connSet == null) + return connList; + foreach (Connector conn in connSet) connList.Add(conn); @@ -75,7 +83,7 @@ private static List SortedConnectors(this FamilyInstance familyInstan private static List SortedConnectors(this Element element) { - BH.Engine.Base.Compute.RecordError("Input element is not supported by the method. Check if element is a valid MEP object."); + BH.Engine.Base.Compute.RecordError("Input element is not supported by the connector query method. Check if element is a valid MEP object."); return null; } From 6c2ac735ba479cda16362d9a680861cdcac068db Mon Sep 17 00:00:00 2001 From: Michal Pekacki Date: Thu, 6 Jun 2024 12:29:01 +0200 Subject: [PATCH 27/43] added ElementsInPath --- Revit_Core_Engine/Query/ElementsInPath.cs | 108 ++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 Revit_Core_Engine/Query/ElementsInPath.cs diff --git a/Revit_Core_Engine/Query/ElementsInPath.cs b/Revit_Core_Engine/Query/ElementsInPath.cs new file mode 100644 index 000000000..53b751a84 --- /dev/null +++ b/Revit_Core_Engine/Query/ElementsInPath.cs @@ -0,0 +1,108 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using Autodesk.Revit.DB; +using Autodesk.Revit.DB.Electrical; +using Autodesk.Revit.DB.Mechanical; +using Autodesk.Revit.DB.Plumbing; +using BH.oM.Base.Attributes; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +namespace BH.Revit.Engine.Core +{ + public static partial class Query + { + /***************************************************/ + /**** Public methods ****/ + /***************************************************/ + + [Description("Returns list of elements connected to each other between starting and ending element in MEP network. For elements not connected in the system the result is null")] + [Input("startingElement", "Starting element in the path of the elements.")] + [Input("endingElement", "Ending element in the path of the elements.")] + [Output("elementPath", "Elements in path between starting and ending element.")] + public static List ElementsInPath(this Element startingElement, Element endingElement) + { + if (startingElement == null || endingElement == null) + return null; + + return ElementPath(startingElement, endingElement, new List()); + } + + /***************************************************/ + /**** Private methods ****/ + /***************************************************/ + + private static List ElementPath(this Element element, Element endingElement, List visitedElements) + { + if (element.Id.IntegerValue == endingElement.Id.IntegerValue) + return visitedElements; + + if (!visitedElements.Any()) + visitedElements.Add(element); + + List connectedElements = element.ConnectedNetworkElements(); + List nextElements = connectedElements.Where(x => !visitedElements.Select(y => y.Id.IntegerValue).Contains(x.Id.IntegerValue)).ToList(); + + if (nextElements.Count == 0) + { + return null; + } + else if (nextElements.Count == 1) + { + visitedElements.Add(nextElements[0]); + var result = ElementPath(nextElements[0], endingElement, visitedElements); + + if (result != null) + return result; + } + else + { + foreach (Element el in nextElements) + { + List childNetwork = visitedElements.ToList(); + childNetwork.Add(el); + List result = ElementPath(el, endingElement, childNetwork); + + if (result != null) + return result; + } + } + + return null; + } + + /***************************************************/ + + private static List ConnectedNetworkElements(this Element element) + { + List connectedElemnets = element.ConnectedElements(); + return connectedElemnets.Where(x => x is FamilyInstance || x is Duct || x is FlexDuct || x is Pipe || x is FlexPipe || x is CableTrayConduitBase || x is Wire).ToList(); + } + + /***************************************************/ + } +} + + + From d896fc8ca46dbc04f271b22ef05ac0fdd077cd8c Mon Sep 17 00:00:00 2001 From: Michal Pekacki Date: Fri, 7 Jun 2024 14:18:56 +0200 Subject: [PATCH 28/43] update after @pawelbaran comments --- Revit_Core_Engine/Query/ElementsInPath.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Revit_Core_Engine/Query/ElementsInPath.cs b/Revit_Core_Engine/Query/ElementsInPath.cs index 53b751a84..d9fc3c87e 100644 --- a/Revit_Core_Engine/Query/ElementsInPath.cs +++ b/Revit_Core_Engine/Query/ElementsInPath.cs @@ -37,7 +37,7 @@ public static partial class Query /**** Public methods ****/ /***************************************************/ - [Description("Returns list of elements connected to each other between starting and ending element in MEP network. For elements not connected in the system the result is null")] + [Description("Returns list of elements connected to each other between starting and ending element in MEP network. For elements not connected in the system the result is null.")] [Input("startingElement", "Starting element in the path of the elements.")] [Input("endingElement", "Ending element in the path of the elements.")] [Output("elementPath", "Elements in path between starting and ending element.")] @@ -96,8 +96,8 @@ private static List ElementPath(this Element element, Element endingEle private static List ConnectedNetworkElements(this Element element) { - List connectedElemnets = element.ConnectedElements(); - return connectedElemnets.Where(x => x is FamilyInstance || x is Duct || x is FlexDuct || x is Pipe || x is FlexPipe || x is CableTrayConduitBase || x is Wire).ToList(); + List connectedElements = element.ConnectedElements(); + return connectedElements.Where(x => x is FamilyInstance || x is Duct || x is FlexDuct || x is Pipe || x is FlexPipe || x is CableTrayConduitBase || x is Wire).ToList(); } /***************************************************/ From 2af279980672e68261c9a586c7f17d43ef7dd04d Mon Sep 17 00:00:00 2001 From: Michal Pekacki Date: Mon, 10 Jun 2024 15:08:33 +0200 Subject: [PATCH 29/43] added try/catch --- Revit_Core_Engine/Query/DoesIntersect.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Revit_Core_Engine/Query/DoesIntersect.cs b/Revit_Core_Engine/Query/DoesIntersect.cs index 17c396b9f..6e4f026d3 100644 --- a/Revit_Core_Engine/Query/DoesIntersect.cs +++ b/Revit_Core_Engine/Query/DoesIntersect.cs @@ -190,8 +190,16 @@ public static bool DoesIntersect(this Solid solid1, Solid solid2) return false; } - Solid intersectSolid = BooleanOperationsUtils.ExecuteBooleanOperation(solid1, solid2, BooleanOperationsType.Intersect); - return Math.Abs(intersectSolid.Volume) > Math.Pow(BH.oM.Geometry.Tolerance.Distance, 3); + try + { + Solid intersectSolid = BooleanOperationsUtils.ExecuteBooleanOperation(solid1, solid2, BooleanOperationsType.Intersect); + return Math.Abs(intersectSolid.Volume) > Math.Pow(BH.oM.Geometry.Tolerance.Distance, 3); + } + catch (Exception ex) + { + BH.Engine.Base.Compute.RecordWarning($"Intersection of the solids could not be checked due to following error: '{ex.Message}'."); + return false; + } } /***************************************************/ From f2f832a183986d840a7941e34d76f460f7efd6ff Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Wed, 12 Jun 2024 09:41:30 +0200 Subject: [PATCH 30/43] WIP semi-working solution --- Revit_Core_Adapter/CRUD/Read.cs | 110 +++++++++++++----- .../Compute/SplitRequestTreeByLinks.cs | 54 ++++++--- 2 files changed, 119 insertions(+), 45 deletions(-) diff --git a/Revit_Core_Adapter/CRUD/Read.cs b/Revit_Core_Adapter/CRUD/Read.cs index 52ee6d3fb..b21f3bfcb 100644 --- a/Revit_Core_Adapter/CRUD/Read.cs +++ b/Revit_Core_Adapter/CRUD/Read.cs @@ -87,7 +87,7 @@ protected override IEnumerable Read(IRequest request, ActionConfig } } - Dictionary requestsByLinks = request.SplitRequestTreeByLinks(this.Document); + Dictionary 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."); @@ -96,12 +96,42 @@ protected override IEnumerable Read(IRequest request, ActionConfig RevitSettings settings = RevitSettings.DefaultIfNull(); + Dictionary<(Document, Transform), List> requestsByDocumentAndTransform = new Dictionary<(Document, Transform), List>(); + foreach (KeyValuePair requestByLink in requestsByLinks) + { + Document doc; + Transform transform = null; + if (requestByLink.Key.IntegerValue == -1) + doc = this.Document; + else + { + var rli = this.Document.GetElement(requestByLink.Key) as RevitLinkInstance; + doc = rli.GetLinkDocument(); + + Transform t = rli.GetTotalTransform(); + if (!t.IsIdentity) + transform = t; + } + + var tuple = (doc, transform); + if (!requestsByDocumentAndTransform.ContainsKey(tuple)) + requestsByDocumentAndTransform.Add(tuple, new List()); + + requestsByDocumentAndTransform[tuple].Add(requestByLink.Value); + } + + Dictionary>> globalRefObjects = new Dictionary>>(); List result = new List(); - foreach (KeyValuePair 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)); } + //foreach (KeyValuePair requestByLink in requestsByLinks) + //{ + // result.AddRange(Read(requestByLink.Key, requestByLink.Value, pullConfig, settings)); + //} + this.UIDocument.Selection.SetElementIds(selected); return result; @@ -112,7 +142,7 @@ protected override IEnumerable Read(IRequest request, ActionConfig /**** Public Methods ****/ /***************************************************/ - public static List Read(Document document, IRequest request, RevitPullConfig pullConfig = null, RevitSettings settings = null) + public static List Read(Document document, Transform transform, List requests, RevitPullConfig pullConfig = null, RevitSettings settings = null, Dictionary>> globalRefObjects = null) { if (document == null) { @@ -120,11 +150,13 @@ public static List Read(Document document, IRequest request, RevitP return new List(); } - if (request == null) - { - BH.Engine.Base.Compute.RecordError("BHoM objects could not be read because provided IRequest is null."); - return new List(); - } + //TODO: union requests and if any duplicates then warning!! + + //if (request == null) + //{ + // BH.Engine.Base.Compute.RecordError("BHoM objects could not be read because provided IRequest is null."); + // return new List(); + //} pullConfig = pullConfig.DefaultIfNull(); settings = settings.DefaultIfNull(); @@ -133,9 +165,11 @@ public static List Read(Document document, IRequest request, RevitP if (!pullConfig.IncludeClosedWorksets) worksetPrefilter = document.OpenWorksetsPrefilter(); - List elementIds = request.IElementIds(document, pullConfig.Discipline, settings, worksetPrefilter).RemoveGridSegmentIds(document)?.ToList(); - if (elementIds == null) - return new List(); + HashSet elementIds = new HashSet(); + foreach (IRequest request in requests) + { + elementIds.UnionWith(request.IElementIds(document, pullConfig.Discipline, settings, worksetPrefilter)?.RemoveGridSegmentIds(document) ?? new List()); + } if (pullConfig.IncludeNestedElements) { @@ -150,15 +184,15 @@ public static List Read(Document document, IRequest request, RevitP elemIds.AddRange(nestedElemIds); } } - elementIds.AddRange(elemIds); + elementIds.UnionWith(elemIds); } - return Read(document, elementIds, pullConfig, settings); + return Read(document, transform, elementIds.ToList(), pullConfig, settings, globalRefObjects); } /***************************************************/ - public static List Read(Document document, List elementIds, RevitPullConfig pullConfig = null, RevitSettings settings = null) + public static List Read(Document document, Transform transform, List elementIds, RevitPullConfig pullConfig = null, RevitSettings settings = null, Dictionary>> globalRefObjects = null) { if (document == null) { @@ -194,29 +228,51 @@ public static List Read(Document document, List elementI 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); - Transform linkTransform = null; - TransformMatrix bHoMTransform = null; - if (document.IsLinked) + //TODO: that does not work for multiple links! + // would rather get LinkTransforms(), find unique ones and work with these? + // NOT GOING TO WORK FOR LINKS FILTERED BY ID + //Transform linkTransform = null; + //TransformMatrix bHoMTransform = null; + //if (document.IsLinked) + //{ + // linkTransform = document.LinkTransform(); + // if (linkTransform?.IsIdentity == false) + // bHoMTransform = linkTransform.FromRevit(); + //} + + var bHoMTransform = transform.FromRevit(); + if (globalRefObjects == null) + globalRefObjects = new Dictionary>>(); + + if (!globalRefObjects.ContainsKey(document.Title)) + globalRefObjects.Add(document.Title, new Dictionary>()); + + Dictionary> refObjects = globalRefObjects[document.Title]; + + List result = new List(); + List remainingElementIds = elementIds.ToList(); + for (int i = 0; i < remainingElementIds.Count; i++) { - linkTransform = document.LinkTransform(); - if (linkTransform?.IsIdentity == false) - bHoMTransform = linkTransform.FromRevit(); + ElementId id = remainingElementIds[i]; + var existing = refObjects.GetValues(id); + if (existing != null) + { + result.AddRange(existing.Select(x => x.IPostprocess(transform, settings))); + remainingElementIds.RemoveAt(i); + } } - Dictionary> refObjects = new Dictionary>(); - // 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); + document.CachePanelGeometry(remainingElementIds, discipline, settings, refObjects); - List result = new List(); - foreach (ElementId id in elementIds) + foreach (ElementId id in remainingElementIds) { Element element = document.GetElement(id); if (element == null) continue; - IEnumerable iBHoMObjects = Read(element, discipline, linkTransform, settings, refObjects); + IEnumerable iBHoMObjects = Read(element, discipline, transform, settings, refObjects); if (iBHoMObjects != null && iBHoMObjects.Any()) { diff --git a/Revit_Core_Engine/Compute/SplitRequestTreeByLinks.cs b/Revit_Core_Engine/Compute/SplitRequestTreeByLinks.cs index 205357c7f..ce68fa2bc 100644 --- a/Revit_Core_Engine/Compute/SplitRequestTreeByLinks.cs +++ b/Revit_Core_Engine/Compute/SplitRequestTreeByLinks.cs @@ -43,10 +43,10 @@ public static partial class Compute [Input("request", "An IRequest to be split into a dictionary of Revit documents and the IRequests relevant to them.")] [Input("document", "Host document to be used as the basis of the splitting routine.")] [Output("splitRequests", "A dictionary of Revit documents (both host and linked) and the IRequests relevant to them, which in total represents the same request as the input IRequest.")] - public static Dictionary SplitRequestTreeByLinks(this IRequest request, Document document) + public static Dictionary SplitRequestTreeByLinks(this IRequest request, Document document) { - Dictionary requestsByLinks = new Dictionary(); + Dictionary requestsByLinks = new Dictionary(); List splitPerDoc = request.SplitRequestTreeByType(typeof(FilterByLink)); foreach (IRequest splitRequest in splitPerDoc) { @@ -62,7 +62,7 @@ public static Dictionary SplitRequestTreeByLinks(this IReque /**** Private Methods ****/ /***************************************************/ - private static bool TryOrganizeByLink(this IRequest request, Document document, Dictionary requestsByLinks) + private static bool TryOrganizeByLink(this IRequest request, Document document, Dictionary requestsByLinks) { if (request == null) return false; @@ -78,7 +78,7 @@ private static bool TryOrganizeByLink(this IRequest request, Document document, List linkRequests = request.AllRequestsOfType(typeof(FilterByLink)); if (linkRequests.Count == 0) { - requestsByLinks.AddRequestByLink(request, document); + requestsByLinks.AddRequestByLink(request, new ElementId(-1)); return true; } else if (linkRequests.Count == 1) @@ -86,29 +86,47 @@ private static bool TryOrganizeByLink(this IRequest request, Document document, FilterByLink linkRequest = (FilterByLink)linkRequests[0]; List linkInstanceIds = document.ElementIdsOfLinkInstances(linkRequest.LinkName, linkRequest.CaseSensitive); - if (linkInstanceIds.Count == 1) + if (linkInstanceIds.Count == 0) + { + BH.Engine.Base.Compute.RecordError($"Active Revit document does not contain links with neither name nor path nor ElementId equal to {linkRequest.LinkName}."); + return false; + } + else if (linkInstanceIds.Count > 1) + BH.Engine.Base.Compute.RecordWarning($"There is more than one link document named {linkRequest.LinkName} - elements will be pulled from all unique instances of the links." + + $"\nPlease use full link path or its ElementId instead of link name to pull specifically from a chosen instance."); + + foreach (ElementId linkInstanceId in linkInstanceIds) { request.RemoveSubRequest(linkRequest); request = request.SimplifyRequestTree(); - requestsByLinks.AddRequestByLink(request, ((RevitLinkInstance)document.GetElement(linkInstanceIds[0])).GetLinkDocument()); - return true; + requestsByLinks.AddRequestByLink(request, linkInstanceId); } - else if (linkInstanceIds.Count == 0) - BH.Engine.Base.Compute.RecordError($"Active Revit document does not contain links with neither name nor path nor ElementId equal to {linkRequest.LinkName}."); - else - BH.Engine.Base.Compute.RecordError($"There is more than one link document named {linkRequest.LinkName} - please use full link path or its ElementId instead of link name to pull."); - } - return false; + return true; + + //if (linkInstanceIds.Count == 1) + //{ + // request.RemoveSubRequest(linkRequest); + // request = request.SimplifyRequestTree(); + // requestsByLinks.AddRequestByLink(request, ((RevitLinkInstance)document.GetElement(linkInstanceIds[0])).Id); + // return true; + //} + //else if (linkInstanceIds.Count == 0) + // BH.Engine.Base.Compute.RecordError($"Active Revit document does not contain links with neither name nor path nor ElementId equal to {linkRequest.LinkName}."); + //else + // BH.Engine.Base.Compute.RecordError($"There is more than one link document named {linkRequest.LinkName} - please use full link path or its ElementId instead of link name to pull."); + } + else + return false; } /***************************************************/ - private static void AddRequestByLink(this Dictionary requestsByLinks, IRequest request, Document document) + private static void AddRequestByLink(this Dictionary requestsByLinks, IRequest request, ElementId linkId) { - if (requestsByLinks.ContainsKey(document)) + if (requestsByLinks.ContainsKey(linkId)) { - IRequest requestByLink = requestsByLinks[document]; + IRequest requestByLink = requestsByLinks[linkId]; if (requestByLink is LogicalOrFilter) ((LogicalOrRequest)requestByLink).Requests.Add(request); else @@ -116,11 +134,11 @@ private static void AddRequestByLink(this Dictionary request LogicalOrRequest newHead = new LogicalOrRequest(); newHead.Requests.Add(requestByLink); newHead.Requests.Add(request); - requestsByLinks[document] = newHead; + requestsByLinks[linkId] = newHead; } } else - requestsByLinks[document] = request; + requestsByLinks[linkId] = request; } /***************************************************/ From 198c2f6b5e39dcfaebd884733cf794d907766b0a Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Wed, 12 Jun 2024 13:05:03 +0200 Subject: [PATCH 31/43] #1477 finally fixed --- Revit_Core_Adapter/CRUD/Read.cs | 75 ++++++++----------------- Revit_Core_Engine/Convert/FromRevit.cs | 21 +++---- Revit_Core_Engine/Modify/Postprocess.cs | 72 +++++++++++++++++++++--- 3 files changed, 93 insertions(+), 75 deletions(-) diff --git a/Revit_Core_Adapter/CRUD/Read.cs b/Revit_Core_Adapter/CRUD/Read.cs index b21f3bfcb..9239c8289 100644 --- a/Revit_Core_Adapter/CRUD/Read.cs +++ b/Revit_Core_Adapter/CRUD/Read.cs @@ -224,23 +224,6 @@ public static List Read(Document document, Transform transform, Lis 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); - - //TODO: that does not work for multiple links! - // would rather get LinkTransforms(), find unique ones and work with these? - // NOT GOING TO WORK FOR LINKS FILTERED BY ID - //Transform linkTransform = null; - //TransformMatrix bHoMTransform = null; - //if (document.IsLinked) - //{ - // linkTransform = document.LinkTransform(); - // if (linkTransform?.IsIdentity == false) - // bHoMTransform = linkTransform.FromRevit(); - //} - - var bHoMTransform = transform.FromRevit(); if (globalRefObjects == null) globalRefObjects = new Dictionary>>(); @@ -250,89 +233,75 @@ public static List Read(Document document, Transform transform, Lis Dictionary> refObjects = globalRefObjects[document.Title]; List result = new List(); - List remainingElementIds = elementIds.ToList(); - for (int i = 0; i < remainingElementIds.Count; i++) + List remainingElementIds = new List(); + foreach (ElementId id in elementIds) { - ElementId id = remainingElementIds[i]; var existing = refObjects.GetValues(id); if (existing != null) - { - result.AddRange(existing.Select(x => x.IPostprocess(transform, settings))); - remainingElementIds.RemoveAt(i); - } + result.AddRange(existing); + else + remainingElementIds.Add(id); } // 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(remainingElementIds, discipline, settings, refObjects); - + + 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); + foreach (ElementId id in remainingElementIds) { Element element = document.GetElement(id); if (element == null) continue; - IEnumerable iBHoMObjects = Read(element, discipline, transform, settings, refObjects); - - if (iBHoMObjects != null && iBHoMObjects.Any()) + IEnumerable 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 edges = null; if (geometryConfig.PullEdges) - { edges = element.Curves(geometryOptions, settings, true).FromRevit(); - if (bHoMTransform != null) - edges = edges.Select(x => x?.ITransform(bHoMTransform)).ToList(); - } List 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 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 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); } } @@ -340,12 +309,12 @@ public static List Read(Document document, Transform transform, Lis 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; + return result.Select(x => x.IPostprocess(transform, settings)).Where(x => x != null).ToList(); } /***************************************************/ - public static List Read(Element element, Discipline discipline, Transform transform, RevitSettings settings = null, Dictionary> refObjects = null) + public static List Read(Element element, Discipline discipline, RevitSettings settings = null, Dictionary> refObjects = null) { if (element == null || !element.IsValidObject) return new List(); @@ -353,7 +322,7 @@ public static List Read(Element element, Discipline discipline, Tra List result = null; try { - result = element.IFromRevit(discipline, transform, settings, refObjects); + result = element.IFromRevit(discipline, settings, refObjects); } catch (Exception exception) { diff --git a/Revit_Core_Engine/Convert/FromRevit.cs b/Revit_Core_Engine/Convert/FromRevit.cs index a33531928..66f80fe35 100644 --- a/Revit_Core_Engine/Convert/FromRevit.cs +++ b/Revit_Core_Engine/Convert/FromRevit.cs @@ -44,13 +44,12 @@ public static partial class Convert [Description("Interface method that tries to find a suitable FromRevit convert for any Revit Element.")] [Input("element", "Revit Element to be converted.")] [Input("discipline", "Engineering discipline based on the BHoM discipline classification.")] - [Input("transform", "Optional, a transform to apply to the converted object.")] [Input("settings", "Revit adapter settings to be used while performing the convert.")] [Input("refObjects", "Optional, a collection of objects already processed in the current adapter action, stored to avoid processing the same object more than once.")] [Output("fromRevit", "Resulted BHoM object converted from a Revit Element.")] - public static List IFromRevit(this Element element, Discipline discipline, Transform transform = null, RevitSettings settings = null, Dictionary> refObjects = null) + public static List IFromRevit(this Element element, Discipline discipline, RevitSettings settings = null, Dictionary> refObjects = null) { - return FromRevit(element as dynamic, discipline, transform, settings, refObjects); + return FromRevit(element as dynamic, discipline, settings, refObjects); } /***************************************************/ @@ -74,11 +73,10 @@ public static IGeometry IFromRevit(this Location location) [Description("Converts a Revit EnergyAnalysisDetailModel to a BHoM object based on the requested engineering discipline.")] [Input("energyAnalysisModel", "Revit EnergyAnalysisDetailModel to be converted.")] [Input("discipline", "Engineering discipline based on the BHoM discipline classification.")] - [Input("transform", "Optional, a transform to apply to the converted object.")] [Input("settings", "Revit adapter settings to be used while performing the convert.")] [Input("refObjects", "Optional, a collection of objects already processed in the current adapter action, stored to avoid processing the same object more than once.")] [Output("fromRevit", "Resulted BHoM object converted from a Revit EnergyAnalysisDetailModel.")] - public static List FromRevit(this EnergyAnalysisDetailModel energyAnalysisModel, Discipline discipline, Transform transform = null, RevitSettings settings = null, Dictionary> refObjects = null) + public static List FromRevit(this EnergyAnalysisDetailModel energyAnalysisModel, Discipline discipline, RevitSettings settings = null, Dictionary> refObjects = null) { if (energyAnalysisModel == null) { @@ -100,11 +98,10 @@ public static List FromRevit(this EnergyAnalysisDetailModel energyA [Description("Converts a Revit AssemblyInstance to a BHoM object based on the requested engineering discipline.")] [Input("assemblyInstance", "Revit AssemblyInstance to be converted.")] [Input("discipline", "Engineering discipline based on the BHoM discipline classification.")] - [Input("transform", "Optional, a transform to apply to the converted object. Irrelevant in case of assembly instances.")] [Input("settings", "Revit adapter settings to be used while performing the convert.")] [Input("refObjects", "Optional, a collection of objects already processed in the current adapter action, stored to avoid processing the same object more than once.")] [Output("fromRevit", "Resulted BHoM object converted from a Revit AssemblyInstance.")] - public static List FromRevit(this AssemblyInstance assemblyInstance, Discipline discipline, Transform transform = null, RevitSettings settings = null, Dictionary> refObjects = null) + public static List FromRevit(this AssemblyInstance assemblyInstance, Discipline discipline, RevitSettings settings = null, Dictionary> refObjects = null) { if (assemblyInstance == null) { @@ -114,7 +111,7 @@ public static List FromRevit(this AssemblyInstance assemblyInstance foreach (ElementId memberId in assemblyInstance.GetMemberIds()) { - assemblyInstance.Document.GetElement(memberId).IFromRevit(discipline, transform, settings, refObjects); + assemblyInstance.Document.GetElement(memberId).IFromRevit(discipline, settings, refObjects); } return new List { assemblyInstance.AssemblyFromRevit(settings, refObjects) }; @@ -125,11 +122,10 @@ public static List FromRevit(this AssemblyInstance assemblyInstance [Description("Converts a Revit Element to a BHoM object based on the requested engineering discipline.")] [Input("element", "Revit EnergyAnalysisDetailModel to be converted.")] [Input("discipline", "Engineering discipline based on the BHoM discipline classification.")] - [Input("transform", "Optional, a transform to apply to the converted object.")] [Input("settings", "Revit adapter settings to be used while performing the convert.")] [Input("refObjects", "Optional, a collection of objects already processed in the current adapter action, stored to avoid processing the same object more than once.")] [Output("fromRevit", "Resulted BHoM object converted from a Revit Element.")] - public static List FromRevit(this Element element, Discipline discipline, Transform transform = null, RevitSettings settings = null, Dictionary> refObjects = null) + public static List FromRevit(this Element element, Discipline discipline, RevitSettings settings = null, Dictionary> refObjects = null) { if (element == null) { @@ -159,12 +155,11 @@ public static List FromRevit(this Element element, Discipline disci List result = null; if (converted is IBHoMObject) - result = new List { ((IBHoMObject)converted).IPostprocess(transform, settings) }; + result = new List { ((IBHoMObject)converted) }; else if (converted is IEnumerable) { result = new List(((IEnumerable)converted) - .Where(x => x != null) - .Select(x => x.IPostprocess(transform, settings))); + .Where(x => x != null)); } return result; diff --git a/Revit_Core_Engine/Modify/Postprocess.cs b/Revit_Core_Engine/Modify/Postprocess.cs index 651c6753c..c71b2f12e 100644 --- a/Revit_Core_Engine/Modify/Postprocess.cs +++ b/Revit_Core_Engine/Modify/Postprocess.cs @@ -23,14 +23,19 @@ using Autodesk.Revit.DB; using BH.Engine.Adapters.Revit; using BH.Engine.Base; -using BH.Engine.Spatial; +using BH.Engine.Geometry; +using BH.Engine.Graphics; +using BH.oM.Adapters.Revit; using BH.oM.Adapters.Revit.Elements; using BH.oM.Adapters.Revit.Settings; using BH.oM.Base; +using BH.oM.Base.Attributes; using BH.oM.Dimensional; using BH.oM.Geometry; -using BH.oM.Base.Attributes; +using BH.oM.MEP.Fragments; +using BH.oM.Spatial.SettingOut; using System.ComponentModel; +using System.Linq; namespace BH.Revit.Engine.Core { @@ -71,7 +76,17 @@ public static IElement Postprocess(this IElement element, Transform transform, R settings = settings.DefaultIfNull(); if (transform?.IsIdentity == false) - element = element.ITransform(transform.FromRevit(), settings.DistanceTolerance); + { + TransformMatrix bhomTransform = transform.FromRevit(); + element = BH.Engine.Spatial.Modify.ITransform(element, bhomTransform, settings.DistanceTolerance); + + if (element is IBHoMObject) + { + IBHoMObject obj = (IBHoMObject)element; + obj.TransformGeometry(bhomTransform); + obj.TransformRepresentation(bhomTransform); + } + } return element; } @@ -94,7 +109,12 @@ public static IInstance Postprocess(this ModelInstance instance, Transform trans settings = settings.DefaultIfNull(); if (transform?.IsIdentity == false) - instance = instance.Transform(transform.FromRevit(), settings.DistanceTolerance) as ModelInstance; + { + TransformMatrix bhomTransform = transform.FromRevit(); + instance = instance.Transform(bhomTransform, settings.DistanceTolerance) as ModelInstance; + instance.TransformGeometry(bhomTransform); + instance.TransformRepresentation(bhomTransform); + } return instance; } @@ -118,16 +138,54 @@ public static BH.oM.Spatial.SettingOut.Level Postprocess(this BH.oM.Spatial.Sett { level = level.ShallowClone(); level.Elevation += transform.Origin.Z.ToSI(SpecTypeId.Length); + TransformMatrix bhomTransform = transform.FromRevit(); + level.TransformGeometry(bhomTransform); + level.TransformRepresentation(bhomTransform); } return level; } + /***************************************************/ + /**** Private methods ****/ + /***************************************************/ + + private static void TransformGeometry(this IBHoMObject obj, TransformMatrix transform) + { + RevitGeometry geometryFragment = obj.FindFragment(); + if (geometryFragment != null) + { + obj.Fragments.Remove(geometryFragment); + obj.Fragments.Add(new RevitGeometry + ( + geometryFragment.Edges?.Select(x => x.ITransform(transform)).ToList(), + geometryFragment.Surfaces?.Select(x => x.ITransform(transform)).ToList(), + geometryFragment.Meshes?.Select(x => x.Transform(transform)).ToList() + )); + } + } + + /***************************************************/ + + private static void TransformRepresentation(this IBHoMObject obj, TransformMatrix transform) + { + RevitRepresentation representationFragment = obj.FindFragment(); + if (representationFragment != null) + { + obj.Fragments.Remove(representationFragment); + obj.Fragments.Add(new RevitRepresentation + ( + representationFragment.RenderMeshes?.Select(x => x.Transform(transform)) + )); + } + } + + /***************************************************/ /**** Fallback methods ****/ /***************************************************/ - + private static IObject Postprocess(this IObject obj, Transform transform, RevitSettings settings) { return obj; @@ -136,7 +194,3 @@ private static IObject Postprocess(this IObject obj, Transform transform, RevitS /***************************************************/ } } - - - - From 9ba961a963e4fbf5fd13e63f8cb119fc4e0201c2 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Wed, 12 Jun 2024 13:35:23 +0200 Subject: [PATCH 32/43] comments added, code cleaned up --- Revit_Core_Adapter/CRUD/Read.cs | 79 ++++++++++--------- .../Compute/SplitRequestTreeByLinks.cs | 18 +---- 2 files changed, 42 insertions(+), 55 deletions(-) diff --git a/Revit_Core_Adapter/CRUD/Read.cs b/Revit_Core_Adapter/CRUD/Read.cs index 9239c8289..f0d6ade6c 100644 --- a/Revit_Core_Adapter/CRUD/Read.cs +++ b/Revit_Core_Adapter/CRUD/Read.cs @@ -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; @@ -87,6 +86,7 @@ protected override IEnumerable Read(IRequest request, ActionConfig } } + // Split the request into separate requests per each link model Dictionary requestsByLinks = request.SplitRequestTreeByLinks(this.Document); if (requestsByLinks == null) { @@ -96,6 +96,10 @@ protected override IEnumerable 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> requestsByDocumentAndTransform = new Dictionary<(Document, Transform), List>(); foreach (KeyValuePair requestByLink in requestsByLinks) { @@ -105,21 +109,28 @@ protected override IEnumerable Read(IRequest request, ActionConfig doc = this.Document; else { - var rli = this.Document.GetElement(requestByLink.Key) as RevitLinkInstance; - doc = rli.GetLinkDocument(); + var linkInstance = this.Document.GetElement(requestByLink.Key) as RevitLinkInstance; + doc = linkInstance.GetLinkDocument(); - Transform t = rli.GetTotalTransform(); - if (!t.IsIdentity) - transform = t; + Transform linkTransform = linkInstance.GetTotalTransform(); + if (!linkTransform.IsIdentity) + transform = linkTransform; } - var tuple = (doc, transform); - if (!requestsByDocumentAndTransform.ContainsKey(tuple)) + (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()); + } + 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>> globalRefObjects = new Dictionary>>(); List result = new List(); foreach (var kvp in requestsByDocumentAndTransform) @@ -127,11 +138,7 @@ protected override IEnumerable Read(IRequest request, ActionConfig result.AddRange(Read(kvp.Key.Item1, kvp.Key.Item2, kvp.Value, pullConfig, settings, globalRefObjects)); } - //foreach (KeyValuePair requestByLink in requestsByLinks) - //{ - // result.AddRange(Read(requestByLink.Key, requestByLink.Value, pullConfig, settings)); - //} - + // Restore selection this.UIDocument.Selection.SetElementIds(selected); return result; @@ -150,27 +157,18 @@ public static List Read(Document document, Transform transform, Lis return new List(); } - //TODO: union requests and if any duplicates then warning!! - - //if (request == null) - //{ - // BH.Engine.Base.Compute.RecordError("BHoM objects could not be read because provided IRequest is null."); - // return new List(); - //} - pullConfig = pullConfig.DefaultIfNull(); settings = settings.DefaultIfNull(); + // Prefilter only elements from open worksets if requested IEnumerable worksetPrefilter = null; if (!pullConfig.IncludeClosedWorksets) worksetPrefilter = document.OpenWorksetsPrefilter(); - HashSet elementIds = new HashSet(); - foreach (IRequest request in requests) - { - elementIds.UnionWith(request.IElementIds(document, pullConfig.Discipline, settings, worksetPrefilter)?.RemoveGridSegmentIds(document) ?? new List()); - } + // Get elementIds from all requests + List elementIds = new LogicalOrRequest { Requests = requests }.ElementIds(document, pullConfig.Discipline, settings, worksetPrefilter).ToList(); + // Get elementIds of nested elements if requested if (pullConfig.IncludeNestedElements) { List elemIds = new List(); @@ -184,7 +182,7 @@ public static List Read(Document document, Transform transform, Lis elemIds.AddRange(nestedElemIds); } } - elementIds.UnionWith(elemIds); + elementIds.AddRange(elemIds); } return Read(document, transform, elementIds.ToList(), pullConfig, settings, globalRefObjects); @@ -209,14 +207,6 @@ public static List Read(Document document, Transform transform, Lis 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) { @@ -224,6 +214,7 @@ public static List Read(Document document, Transform transform, Lis discipline = Discipline.Physical; } + // Set up refObjects if (globalRefObjects == null) globalRefObjects = new Dictionary>>(); @@ -232,6 +223,9 @@ public static List Read(Document document, Transform transform, Lis Dictionary> refObjects = globalRefObjects[document.Title]; + // 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 result = new List(); List remainingElementIds = new List(); foreach (ElementId id in elementIds) @@ -247,10 +241,21 @@ public static List Read(Document document, Transform transform, Lis if (!document.IsLinked) 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); @@ -309,6 +314,7 @@ public static List Read(Document document, Transform transform, Lis 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."); + // 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(); } @@ -348,6 +354,3 @@ public static List Read(Element element, Discipline discipline, Rev /***************************************************/ } } - - - diff --git a/Revit_Core_Engine/Compute/SplitRequestTreeByLinks.cs b/Revit_Core_Engine/Compute/SplitRequestTreeByLinks.cs index ce68fa2bc..d025a8c96 100644 --- a/Revit_Core_Engine/Compute/SplitRequestTreeByLinks.cs +++ b/Revit_Core_Engine/Compute/SplitRequestTreeByLinks.cs @@ -42,10 +42,9 @@ public static partial class Compute [Description("Decomposes a tree created by a set of nested ILogicalRequests into a dictionary of Revit documents (both host and linked) and the IRequests relevant to them, which in total represents the same request as the original IRequest.")] [Input("request", "An IRequest to be split into a dictionary of Revit documents and the IRequests relevant to them.")] [Input("document", "Host document to be used as the basis of the splitting routine.")] - [Output("splitRequests", "A dictionary of Revit documents (both host and linked) and the IRequests relevant to them, which in total represents the same request as the input IRequest.")] + [Output("splitRequests", "A dictionary of elementIds representing Revit documents (both host as -1 and linked as link Id) and the IRequests relevant to them, which in total represents the same request as the input IRequest.")] public static Dictionary SplitRequestTreeByLinks(this IRequest request, Document document) { - Dictionary requestsByLinks = new Dictionary(); List splitPerDoc = request.SplitRequestTreeByType(typeof(FilterByLink)); foreach (IRequest splitRequest in splitPerDoc) @@ -103,18 +102,6 @@ private static bool TryOrganizeByLink(this IRequest request, Document document, } return true; - - //if (linkInstanceIds.Count == 1) - //{ - // request.RemoveSubRequest(linkRequest); - // request = request.SimplifyRequestTree(); - // requestsByLinks.AddRequestByLink(request, ((RevitLinkInstance)document.GetElement(linkInstanceIds[0])).Id); - // return true; - //} - //else if (linkInstanceIds.Count == 0) - // BH.Engine.Base.Compute.RecordError($"Active Revit document does not contain links with neither name nor path nor ElementId equal to {linkRequest.LinkName}."); - //else - // BH.Engine.Base.Compute.RecordError($"There is more than one link document named {linkRequest.LinkName} - please use full link path or its ElementId instead of link name to pull."); } else return false; @@ -226,6 +213,3 @@ private static bool IsValidToOrganize(this IRequest request) /***************************************************/ } } - - - From 94bed577bf689a10a1dbc26c8f19b918020a6316 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Wed, 12 Jun 2024 13:44:06 +0200 Subject: [PATCH 33/43] null reference bugfix --- Revit_Core_Adapter/CRUD/Read.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Revit_Core_Adapter/CRUD/Read.cs b/Revit_Core_Adapter/CRUD/Read.cs index f0d6ade6c..b3598afe9 100644 --- a/Revit_Core_Adapter/CRUD/Read.cs +++ b/Revit_Core_Adapter/CRUD/Read.cs @@ -104,7 +104,7 @@ protected override IEnumerable Read(IRequest request, ActionConfig foreach (KeyValuePair requestByLink in requestsByLinks) { Document doc; - Transform transform = null; + Transform transform = Transform.Identity; if (requestByLink.Key.IntegerValue == -1) doc = this.Document; else From b686d63958dbb7fa78d82775f84f4701eee8a66c Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Wed, 12 Jun 2024 15:04:33 +0200 Subject: [PATCH 34/43] attempt to fix #1484 --- Revit_Core_Engine/Query/GeometryPrimitives.cs | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Revit_Core_Engine/Query/GeometryPrimitives.cs b/Revit_Core_Engine/Query/GeometryPrimitives.cs index 9447a9744..e9845d251 100644 --- a/Revit_Core_Engine/Query/GeometryPrimitives.cs +++ b/Revit_Core_Engine/Query/GeometryPrimitives.cs @@ -25,6 +25,7 @@ using Autodesk.Revit.DB.Mechanical; using BH.oM.Adapters.Revit.Settings; using BH.oM.Base.Attributes; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; @@ -57,14 +58,24 @@ public static List GeometryPrimitives(this GeometryElement geome Transform geometryTransform = geometryInstance.Transform; if (transform != null) - { geometryTransform = geometryTransform.Multiply(transform.Inverse); - // This is an edge case fix where the transform origin lies very close to zero, but not exactly at it - // See #1330 for reference - double distanceFromZero = geometryTransform.Origin.DistanceTo(XYZ.Zero); - if (distanceFromZero != 0 && distanceFromZero < 1e-6) - geometryTransform.Origin = XYZ.Zero; + // This is an edge case fix where the transform origin lies very close to zero, but not exactly at it + // See #1330 for reference + double distanceFromZero = geometryTransform.Origin.DistanceTo(XYZ.Zero); + if (distanceFromZero != 0 && distanceFromZero < 1e-6) + geometryTransform.Origin = XYZ.Zero; + + if (!geometryTransform.IsConformal) + { + // This is an edge case related to numerical noise in identity matrix + // See #1484 for reference + XYZ x = geometryTransform.BasisX; + XYZ y = geometryTransform.BasisY; + XYZ z = geometryTransform.BasisZ; + geometryTransform.BasisX = new XYZ(Math.Round(x.X, 6), Math.Round(x.Y, 6), Math.Round(x.Z, 6)); + geometryTransform.BasisY = new XYZ(Math.Round(y.X, 6), Math.Round(y.Y, 6), Math.Round(y.Z, 6)); + geometryTransform.BasisZ = new XYZ(Math.Round(z.X, 6), Math.Round(z.Y, 6), Math.Round(z.Z, 6)); } GeometryElement geomElement = geometryInstance.GetInstanceGeometry(geometryTransform); From 9c554b9069a77be9ad4d6f0a2572c9c34ca55cf7 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Wed, 12 Jun 2024 17:51:07 +0200 Subject: [PATCH 35/43] removal of grid segments brought back --- Revit_Core_Adapter/CRUD/Read.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Revit_Core_Adapter/CRUD/Read.cs b/Revit_Core_Adapter/CRUD/Read.cs index b3598afe9..36cd14797 100644 --- a/Revit_Core_Adapter/CRUD/Read.cs +++ b/Revit_Core_Adapter/CRUD/Read.cs @@ -166,7 +166,7 @@ public static List Read(Document document, Transform transform, Lis worksetPrefilter = document.OpenWorksetsPrefilter(); // Get elementIds from all requests - List elementIds = new LogicalOrRequest { Requests = requests }.ElementIds(document, pullConfig.Discipline, settings, worksetPrefilter).ToList(); + List elementIds = new LogicalOrRequest { Requests = requests }.ElementIds(document, pullConfig.Discipline, settings, worksetPrefilter).RemoveGridSegmentIds(document).ToList(); // Get elementIds of nested elements if requested if (pullConfig.IncludeNestedElements) From 11760a23a1d818c54234bd8404c0281f0cc1e9a7 Mon Sep 17 00:00:00 2001 From: Pawel Baran Date: Thu, 13 Jun 2024 10:28:40 +0200 Subject: [PATCH 36/43] versioning fixed --- Revit_Core_Engine/Convert/FromRevit.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Revit_Core_Engine/Convert/FromRevit.cs b/Revit_Core_Engine/Convert/FromRevit.cs index 66f80fe35..048187f62 100644 --- a/Revit_Core_Engine/Convert/FromRevit.cs +++ b/Revit_Core_Engine/Convert/FromRevit.cs @@ -41,6 +41,7 @@ public static partial class Convert /**** Interface Methods ****/ /***************************************************/ + [PreviousVersion("7.2", "BH.Revit.Engine.Core.Convert.IFromRevit(Autodesk.Revit.DB.Element, BH.oM.Adapters.Revit.Enums.Discipline, Autodesk.Revit.DB.Transform, BH.oM.Adapters.Revit.Settings.RevitSettings, System.Collections.Generic.Dictionary>)")] [Description("Interface method that tries to find a suitable FromRevit convert for any Revit Element.")] [Input("element", "Revit Element to be converted.")] [Input("discipline", "Engineering discipline based on the BHoM discipline classification.")] @@ -70,6 +71,7 @@ public static IGeometry IFromRevit(this Location location) /**** Convert Revit elements to BHoM ****/ /***************************************************/ + [PreviousVersion("7.2", "BH.Revit.Engine.Core.Convert.FromRevit(Autodesk.Revit.DB.Analysis.EnergyAnalysisDetailModel, BH.oM.Adapters.Revit.Enums.Discipline, Autodesk.Revit.DB.Transform, BH.oM.Adapters.Revit.Settings.RevitSettings, System.Collections.Generic.Dictionary>)")] [Description("Converts a Revit EnergyAnalysisDetailModel to a BHoM object based on the requested engineering discipline.")] [Input("energyAnalysisModel", "Revit EnergyAnalysisDetailModel to be converted.")] [Input("discipline", "Engineering discipline based on the BHoM discipline classification.")] @@ -95,6 +97,7 @@ public static List FromRevit(this EnergyAnalysisDetailModel energyA /***************************************************/ + [PreviousVersion("7.2", "BH.Revit.Engine.Core.Convert.FromRevit(Autodesk.Revit.DB.AssemblyInstance, BH.oM.Adapters.Revit.Enums.Discipline, Autodesk.Revit.DB.Transform, BH.oM.Adapters.Revit.Settings.RevitSettings, System.Collections.Generic.Dictionary>)")] [Description("Converts a Revit AssemblyInstance to a BHoM object based on the requested engineering discipline.")] [Input("assemblyInstance", "Revit AssemblyInstance to be converted.")] [Input("discipline", "Engineering discipline based on the BHoM discipline classification.")] @@ -119,6 +122,7 @@ public static List FromRevit(this AssemblyInstance assemblyInstance /***************************************************/ + [PreviousVersion("7.2", "BH.Revit.Engine.Core.Convert.FromRevit(Autodesk.Revit.DB.Element, BH.oM.Adapters.Revit.Enums.Discipline, Autodesk.Revit.DB.Transform, BH.oM.Adapters.Revit.Settings.RevitSettings, System.Collections.Generic.Dictionary>)")] [Description("Converts a Revit Element to a BHoM object based on the requested engineering discipline.")] [Input("element", "Revit EnergyAnalysisDetailModel to be converted.")] [Input("discipline", "Engineering discipline based on the BHoM discipline classification.")] From d49cc7e452870edd3be4ee35428de7fbb173bf9d Mon Sep 17 00:00:00 2001 From: Michal Pekacki Date: Thu, 13 Jun 2024 16:53:44 +0200 Subject: [PATCH 37/43] replace list with hashset --- Revit_Core_Engine/Query/ElementsInPath.cs | 27 +++++++++++++---------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/Revit_Core_Engine/Query/ElementsInPath.cs b/Revit_Core_Engine/Query/ElementsInPath.cs index d9fc3c87e..e164ffa7e 100644 --- a/Revit_Core_Engine/Query/ElementsInPath.cs +++ b/Revit_Core_Engine/Query/ElementsInPath.cs @@ -41,28 +41,31 @@ public static partial class Query [Input("startingElement", "Starting element in the path of the elements.")] [Input("endingElement", "Ending element in the path of the elements.")] [Output("elementPath", "Elements in path between starting and ending element.")] - public static List ElementsInPath(this Element startingElement, Element endingElement) + public static List ElementsInPath(this Element startingElement, Element endingElement) { if (startingElement == null || endingElement == null) return null; - return ElementPath(startingElement, endingElement, new List()); + Document doc = startingElement.Document; + List result = ElementPath(startingElement, endingElement, new HashSet()).Select(x => new ElementId(x)).ToList(); + + return result; } /***************************************************/ /**** Private methods ****/ /***************************************************/ - private static List ElementPath(this Element element, Element endingElement, List visitedElements) + private static HashSet ElementPath(this Element element, Element endingElement, HashSet visitedElementIds) { if (element.Id.IntegerValue == endingElement.Id.IntegerValue) - return visitedElements; + return visitedElementIds; - if (!visitedElements.Any()) - visitedElements.Add(element); + if (!visitedElementIds.Any()) + visitedElementIds.Add(element.Id.IntegerValue); List connectedElements = element.ConnectedNetworkElements(); - List nextElements = connectedElements.Where(x => !visitedElements.Select(y => y.Id.IntegerValue).Contains(x.Id.IntegerValue)).ToList(); + List nextElements = connectedElements.Where(x => !visitedElementIds.Contains(x.Id.IntegerValue)).ToList(); if (nextElements.Count == 0) { @@ -70,8 +73,8 @@ private static List ElementPath(this Element element, Element endingEle } else if (nextElements.Count == 1) { - visitedElements.Add(nextElements[0]); - var result = ElementPath(nextElements[0], endingElement, visitedElements); + visitedElementIds.Add(nextElements[0].Id.IntegerValue); + var result = ElementPath(nextElements[0], endingElement, visitedElementIds); if (result != null) return result; @@ -80,9 +83,9 @@ private static List ElementPath(this Element element, Element endingEle { foreach (Element el in nextElements) { - List childNetwork = visitedElements.ToList(); - childNetwork.Add(el); - List result = ElementPath(el, endingElement, childNetwork); + HashSet childNetwork = visitedElementIds.ToHashSet(); + childNetwork.Add(el.Id.IntegerValue); + HashSet result = ElementPath(el, endingElement, childNetwork); if (result != null) return result; From 808c67df68a17b8e8b8f82baebefcd709e274962 Mon Sep 17 00:00:00 2001 From: Michal Pekacki Date: Thu, 13 Jun 2024 17:09:35 +0200 Subject: [PATCH 38/43] order by location point added --- Revit_Core_Engine/Query/ElementsInPath.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Revit_Core_Engine/Query/ElementsInPath.cs b/Revit_Core_Engine/Query/ElementsInPath.cs index e164ffa7e..4036b19db 100644 --- a/Revit_Core_Engine/Query/ElementsInPath.cs +++ b/Revit_Core_Engine/Query/ElementsInPath.cs @@ -65,7 +65,7 @@ private static HashSet ElementPath(this Element element, Element endingElem visitedElementIds.Add(element.Id.IntegerValue); List connectedElements = element.ConnectedNetworkElements(); - List nextElements = connectedElements.Where(x => !visitedElementIds.Contains(x.Id.IntegerValue)).ToList(); + List nextElements = connectedElements.Where(x => !visitedElementIds.Contains(x.Id.IntegerValue)).OrderBy(x => x.LocationPoint().DistanceTo(endingElement.LocationPoint())).ToList(); if (nextElements.Count == 0) { @@ -103,6 +103,21 @@ private static List ConnectedNetworkElements(this Element element) return connectedElements.Where(x => x is FamilyInstance || x is Duct || x is FlexDuct || x is Pipe || x is FlexPipe || x is CableTrayConduitBase || x is Wire).ToList(); } + + /***************************************************/ + + private static XYZ LocationPoint(this Element element) + { + Location location = element.Location; + + if (location is LocationPoint locationPoint) + return locationPoint.Point; + else if (location is LocationCurve locationCurve) + return locationCurve.Curve.Evaluate(0.5, true); + + return null; + } + /***************************************************/ } } From 53d938202285d62bb14d791e28e88d5b274c4c46 Mon Sep 17 00:00:00 2001 From: Michal Pekacki Date: Thu, 13 Jun 2024 17:15:45 +0200 Subject: [PATCH 39/43] null check added --- Revit_Core_Engine/Query/ElementsInPath.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Revit_Core_Engine/Query/ElementsInPath.cs b/Revit_Core_Engine/Query/ElementsInPath.cs index 4036b19db..bf5bd6bda 100644 --- a/Revit_Core_Engine/Query/ElementsInPath.cs +++ b/Revit_Core_Engine/Query/ElementsInPath.cs @@ -47,7 +47,7 @@ public static List ElementsInPath(this Element startingElement, Eleme return null; Document doc = startingElement.Document; - List result = ElementPath(startingElement, endingElement, new HashSet()).Select(x => new ElementId(x)).ToList(); + List result = ElementPath(startingElement, endingElement, new HashSet())?.Select(x => new ElementId(x)).ToList(); return result; } From ced87451c2994f57087285040dc3669373392d22 Mon Sep 17 00:00:00 2001 From: Michal Pekacki Date: Thu, 13 Jun 2024 17:24:21 +0200 Subject: [PATCH 40/43] description update --- Revit_Core_Engine/Query/ElementsInPath.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Revit_Core_Engine/Query/ElementsInPath.cs b/Revit_Core_Engine/Query/ElementsInPath.cs index bf5bd6bda..721a6dde0 100644 --- a/Revit_Core_Engine/Query/ElementsInPath.cs +++ b/Revit_Core_Engine/Query/ElementsInPath.cs @@ -37,10 +37,10 @@ public static partial class Query /**** Public methods ****/ /***************************************************/ - [Description("Returns list of elements connected to each other between starting and ending element in MEP network. For elements not connected in the system the result is null.")] + [Description("Returns list of element ids connected to each other between starting and ending element in MEP network. For elements not connected in the system the result is null.")] [Input("startingElement", "Starting element in the path of the elements.")] [Input("endingElement", "Ending element in the path of the elements.")] - [Output("elementPath", "Elements in path between starting and ending element.")] + [Output("elementPath", "Element ids in path between starting and ending element.")] public static List ElementsInPath(this Element startingElement, Element endingElement) { if (startingElement == null || endingElement == null) @@ -103,7 +103,6 @@ private static List ConnectedNetworkElements(this Element element) return connectedElements.Where(x => x is FamilyInstance || x is Duct || x is FlexDuct || x is Pipe || x is FlexPipe || x is CableTrayConduitBase || x is Wire).ToList(); } - /***************************************************/ private static XYZ LocationPoint(this Element element) From e9d8b9e09693a56b98d559639883deed833777bc Mon Sep 17 00:00:00 2001 From: Michal Pekacki Date: Fri, 14 Jun 2024 09:05:34 +0200 Subject: [PATCH 41/43] replace hashset with list to keep correct order --- Revit_Core_Engine/Query/ElementsInPath.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/Revit_Core_Engine/Query/ElementsInPath.cs b/Revit_Core_Engine/Query/ElementsInPath.cs index 721a6dde0..e771541f4 100644 --- a/Revit_Core_Engine/Query/ElementsInPath.cs +++ b/Revit_Core_Engine/Query/ElementsInPath.cs @@ -46,26 +46,23 @@ public static List ElementsInPath(this Element startingElement, Eleme if (startingElement == null || endingElement == null) return null; - Document doc = startingElement.Document; - List result = ElementPath(startingElement, endingElement, new HashSet())?.Select(x => new ElementId(x)).ToList(); - - return result; + return ElementPath(startingElement, endingElement, new List()); } /***************************************************/ /**** Private methods ****/ /***************************************************/ - private static HashSet ElementPath(this Element element, Element endingElement, HashSet visitedElementIds) + private static List ElementPath(this Element element, Element endingElement, List visitedElementIds) { if (element.Id.IntegerValue == endingElement.Id.IntegerValue) return visitedElementIds; if (!visitedElementIds.Any()) - visitedElementIds.Add(element.Id.IntegerValue); + visitedElementIds.Add(element.Id); List connectedElements = element.ConnectedNetworkElements(); - List nextElements = connectedElements.Where(x => !visitedElementIds.Contains(x.Id.IntegerValue)).OrderBy(x => x.LocationPoint().DistanceTo(endingElement.LocationPoint())).ToList(); + List nextElements = connectedElements.Where(x => !visitedElementIds.Select(y => y.IntegerValue).Contains(x.Id.IntegerValue)).OrderBy(x => x.LocationPoint().DistanceTo(endingElement.LocationPoint())).ToList(); if (nextElements.Count == 0) { @@ -73,7 +70,7 @@ private static HashSet ElementPath(this Element element, Element endingElem } else if (nextElements.Count == 1) { - visitedElementIds.Add(nextElements[0].Id.IntegerValue); + visitedElementIds.Add(nextElements[0].Id); var result = ElementPath(nextElements[0], endingElement, visitedElementIds); if (result != null) @@ -83,9 +80,9 @@ private static HashSet ElementPath(this Element element, Element endingElem { foreach (Element el in nextElements) { - HashSet childNetwork = visitedElementIds.ToHashSet(); - childNetwork.Add(el.Id.IntegerValue); - HashSet result = ElementPath(el, endingElement, childNetwork); + List childNetwork = visitedElementIds.ToList(); + childNetwork.Add(el.Id); + List result = ElementPath(el, endingElement, childNetwork); if (result != null) return result; From bedd636c446a7881f22fd9f79071bc8fef910ab6 Mon Sep 17 00:00:00 2001 From: Michal Pekacki Date: Fri, 14 Jun 2024 09:40:28 +0200 Subject: [PATCH 42/43] common domains added --- Revit_Core_Engine/Query/ElementsInPath.cs | 40 +++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/Revit_Core_Engine/Query/ElementsInPath.cs b/Revit_Core_Engine/Query/ElementsInPath.cs index e771541f4..40bbc45e9 100644 --- a/Revit_Core_Engine/Query/ElementsInPath.cs +++ b/Revit_Core_Engine/Query/ElementsInPath.cs @@ -33,6 +33,12 @@ namespace BH.Revit.Engine.Core { public static partial class Query { + /***************************************************/ + /**** Private field ****/ + /***************************************************/ + + private static List m_CommonDomains; + /***************************************************/ /**** Public methods ****/ /***************************************************/ @@ -46,6 +52,10 @@ public static List ElementsInPath(this Element startingElement, Eleme if (startingElement == null || endingElement == null) return null; + m_CommonDomains = startingElement.CommonDomains(endingElement); + if (m_CommonDomains == null || !m_CommonDomains.Any()) + return null; + return ElementPath(startingElement, endingElement, new List()); } @@ -62,7 +72,9 @@ private static List ElementPath(this Element element, Element endingE visitedElementIds.Add(element.Id); List connectedElements = element.ConnectedNetworkElements(); - List nextElements = connectedElements.Where(x => !visitedElementIds.Select(y => y.IntegerValue).Contains(x.Id.IntegerValue)).OrderBy(x => x.LocationPoint().DistanceTo(endingElement.LocationPoint())).ToList(); + List nextElements = connectedElements. + Where(x => !visitedElementIds.Select(y => y.IntegerValue).Contains(x.Id.IntegerValue)). + OrderBy(x => x.LocationPoint().DistanceTo(endingElement.LocationPoint())).ToList(); if (nextElements.Count == 0) { @@ -97,7 +109,17 @@ private static List ElementPath(this Element element, Element endingE private static List ConnectedNetworkElements(this Element element) { List connectedElements = element.ConnectedElements(); - return connectedElements.Where(x => x is FamilyInstance || x is Duct || x is FlexDuct || x is Pipe || x is FlexPipe || x is CableTrayConduitBase || x is Wire).ToList(); + connectedElements = connectedElements.Where(x => x is FamilyInstance || x is Duct || x is FlexDuct || x is Pipe || x is FlexPipe || x is CableTrayConduitBase || x is Wire).ToList(); + List sameDomainElements = new List(); + + foreach (Element el in connectedElements) + { + List domains = el.Connectors().Select(x => x.Domain).ToList(); + if (m_CommonDomains.Intersect(domains).Any()) + sameDomainElements.Add(el); + } + + return sameDomainElements; } /***************************************************/ @@ -114,6 +136,20 @@ private static XYZ LocationPoint(this Element element) return null; } + + /***************************************************/ + + private static List CommonDomains(this Element element1, Element element2) + { + List el1Domains = element1.Connectors()?.Select(x => x.Domain).ToList(); + List el2Domains = element2.Connectors()?.Select(x => x.Domain).ToList(); + + if (el1Domains == null || el2Domains == null) + return null; + + return el1Domains.Intersect(el2Domains).ToList(); + } + /***************************************************/ } } From 0b8fce5f8dd1ed5959278e619ac8177159de3b8b Mon Sep 17 00:00:00 2001 From: Michal Pekacki Date: Fri, 14 Jun 2024 11:33:46 +0200 Subject: [PATCH 43/43] typo fixed --- Revit_Core_Engine/Query/ElementsInPath.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Revit_Core_Engine/Query/ElementsInPath.cs b/Revit_Core_Engine/Query/ElementsInPath.cs index 40bbc45e9..baae95c81 100644 --- a/Revit_Core_Engine/Query/ElementsInPath.cs +++ b/Revit_Core_Engine/Query/ElementsInPath.cs @@ -34,7 +34,7 @@ namespace BH.Revit.Engine.Core public static partial class Query { /***************************************************/ - /**** Private field ****/ + /**** Private fields ****/ /***************************************************/ private static List m_CommonDomains;