diff --git a/Revit_Core_Engine/Convert/Facade/FromRevit/CurtainWall.cs b/Revit_Core_Engine/Convert/Facade/FromRevit/CurtainWall.cs index 2689bc991..70f167b87 100644 --- a/Revit_Core_Engine/Convert/Facade/FromRevit/CurtainWall.cs +++ b/Revit_Core_Engine/Convert/Facade/FromRevit/CurtainWall.cs @@ -70,15 +70,9 @@ public static CurtainWall CurtainWallFromRevit(this Wall wall, RevitSettings set 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 extEdges = new List(); - List cwEdgeCrvs = curtainPanels.ExternalEdges(); - foreach (ICurve crv in cwEdgeCrvs) - { - FrameEdge frameEdge = new FrameEdge { Curve = crv }; - extEdges.Add(frameEdge); - } + 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 }; @@ -95,6 +89,45 @@ public static CurtainWall CurtainWallFromRevit(this Wall wall, RevitSettings set } /***************************************************/ + + [Description("Converts a Revit Wall to BH.oM.Facade.Elements.CurtainSystem.")] + [Input("system", "Revit CurtainSystem 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("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 null; + + settings = settings.DefaultIfNull(); + + CurtainWall bHoMCurtainWall = refObjects.GetValue(system.Id); + if (bHoMCurtainWall != null) + return bHoMCurtainWall; + + IEnumerable curtainPanels = system.ICurtainGrids().SelectMany(x => x.FacadeCurtainPanels(system.Document, settings, refObjects)).ToList(); + + 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)); + + // 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() }; + + //Set identifiers, parameters & custom data + bHoMCurtainWall.SetIdentifiers(system); + bHoMCurtainWall.CopyParameters(system, settings.MappingSettings); + bHoMCurtainWall.SetProperties(system, settings.MappingSettings); + + refObjects.AddOrReplace(system.Id, bHoMCurtainWall); + + return bHoMCurtainWall; + } + + /***************************************************/ } } diff --git a/Revit_Core_Engine/Convert/Facade/FromRevit/Opening.cs b/Revit_Core_Engine/Convert/Facade/FromRevit/Opening.cs index e240f5f3a..43652567f 100644 --- a/Revit_Core_Engine/Convert/Facade/FromRevit/Opening.cs +++ b/Revit_Core_Engine/Convert/Facade/FromRevit/Opening.cs @@ -72,7 +72,10 @@ public static oM.Facade.Elements.Opening FacadeOpeningFromRevit(this FamilyInsta if (opening != null) return opening; - FrameEdgeProperty frameEdgeProperty = familyInstance.FrameEdgeProperty(settings, refObjects); + // 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(); diff --git a/Revit_Core_Engine/Convert/Structure/FromRevit/Profile.cs b/Revit_Core_Engine/Convert/Structure/FromRevit/Profile.cs index 43a7e39a5..a1062fca9 100644 --- a/Revit_Core_Engine/Convert/Structure/FromRevit/Profile.cs +++ b/Revit_Core_Engine/Convert/Structure/FromRevit/Profile.cs @@ -44,7 +44,7 @@ public static partial class Convert /***************************************************/ /**** Public Methods ****/ /***************************************************/ - + [Description("Converts a Revit FamilySymbol to BH.oM.Spatial.ShapeProfiles.IProfile.")] [Input("familySymbol", "Revit FamilySymbol to be converted.")] [Input("settings", "Revit adapter settings to be used while performing the convert.")] @@ -131,6 +131,92 @@ public static IProfile ProfileFromRevit(this FamilySymbol familySymbol, RevitSet return profile; } + /***************************************************/ + + [Description("Converts a Revit MullionType to BH.oM.Spatial.ShapeProfiles.IProfile.")] + [Input("mullionType", "Revit MullionType 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("profile", "BH.oM.Spatial.ShapeProfiles.IProfile resulting from converting the input Revit MullionType.")] + public static IProfile ProfileFromRevit(this MullionType mullionType, RevitSettings settings = null, Dictionary> refObjects = null) + { + settings = settings.DefaultIfNull(); + + IProfile profile = refObjects.GetValue(mullionType.Id); + if (profile != null) + return profile; + + // The algorithm below is so convoluted because mullion type does not have a profile + // Instead, one needs to extract the geometry of one of the instances + // The instance to be queried needs to have its start (bottom) face exposed + // In simple words, it needs to be the first mullion in a chain of mullions of same type + + // First, take arbitrary mullion of a given type + Document doc = mullionType.Document; + Mullion representativeMullion = new FilteredElementCollector(doc) + .OfClass(typeof(FamilyInstance)) + .Where(x => x is Mullion) + .Cast() + .FirstOrDefault(x => x.MullionType.Id.IntegerValue == mullionType.Id.IntegerValue && x.MullionLine() != null); + + if (representativeMullion == null) + { + BH.Engine.Base.Compute.RecordError($"Profile could not be extracted from a mullion. ElementId: {mullionType.Id.IntegerValue}"); + return null; + } + + // Find the first mullion in the chain the arbitrary one belongs to + List allMullionsInWall = new FilteredElementCollector(doc) + .OfClass(typeof(FamilyInstance)) + .Where(x => x is Mullion) + .Cast() + .Where(x => x.Host.Id.IntegerValue == representativeMullion.Host.Id.IntegerValue + && x.MullionType.Id.IntegerValue == mullionType.Id.IntegerValue) + .ToList(); + + BH.oM.Geometry.Line line = representativeMullion.MullionLine(); + BH.oM.Geometry.Vector direction = line.Direction(); + foreach (Mullion candidateMullion in allMullionsInWall) + { + // Skip mullions that are not collinear with the representative mullion + BH.oM.Geometry.Line candidateLine = candidateMullion.MullionLine(); + if (candidateLine == null || !candidateLine.IsCollinear(line)) + continue; + + // Update the representative mullion if the candidate: + // - is collinear (check above) + // - has the same direction + // - has start before the current representative mullion, counting along the mullion direction + if ((candidateLine.Start - line.Start).DotProduct(direction) > 0) + { + representativeMullion = candidateMullion; + line = candidateLine; + } + } + + // Find the instance geometry and extract profile from it + Options options = new Options(); + options.DetailLevel = ViewDetailLevel.Fine; + options.IncludeNonVisibleObjects = false; + GeometryInstance instance = representativeMullion.get_Geometry(options).FirstOrDefault(x => x is GeometryInstance) as GeometryInstance; + MullionType instanceSymbol = instance.Symbol as MullionType; + if (instanceSymbol != null) + profile = instanceSymbol.FreeFormProfileFromRevit(settings); + + if (profile == null) + return null; + + //Set identifiers, parameters & custom data + profile.SetIdentifiers(mullionType); + profile.CopyParameters(mullionType, settings.MappingSettings); + profile.SetProperties(mullionType, settings.MappingSettings); + + profile.Name = mullionType.Name; + refObjects.AddOrReplace(mullionType.Id, profile); + + return profile; + } + /***************************************************/ /**** Private Methods ****/ @@ -610,7 +696,7 @@ private static FreeFormProfile FreeFormProfileFromRevit(this FamilySymbol family XYZ direction; if (familySymbol.Family.FamilyPlacementType == FamilyPlacementType.CurveDrivenStructural) direction = XYZ.BasisX; - else if (familySymbol.Family.FamilyPlacementType == FamilyPlacementType.TwoLevelsBased) + else if (familySymbol.Family.FamilyPlacementType == FamilyPlacementType.TwoLevelsBased || familySymbol is MullionType) direction = XYZ.BasisZ; else { @@ -714,6 +800,13 @@ private static FreeFormProfile FreeFormProfileFromRevit(this FamilySymbol family if (adjustment.Length() > settings.DistanceTolerance) profileCurves = profileCurves.Select(x => x.ITranslate(adjustment)).ToList(); + // If Mullion, rotate 90 degrees to match orientation of Mullions on CurtainWalls per BHoM Standard of -X is Exterior, +X is Interior + if (familySymbol is MullionType) + { + double angle = -Math.PI * 0.5; + profileCurves = profileCurves.Select(x => x.IRotate(oM.Geometry.Point.Origin, Vector.ZAxis, angle)).ToList(); + } + // Check if the curves are in the horizontal plane, if not then align them. if (familySymbol.Family.FamilyPlacementType == FamilyPlacementType.CurveDrivenStructural) { @@ -753,6 +846,17 @@ private static Solid SingleSolid(this GeometryElement geometryElement) return solid; } + /***************************************************/ + + private static BH.oM.Geometry.Line MullionLine(this Mullion mullion) + { + Curve curve = mullion?.LocationCurve; + if (curve == null) + return null; + else + return new oM.Geometry.Line { Start = curve.GetEndPoint(0).PointFromRevit(), End = curve.GetEndPoint(1).PointFromRevit() }; + } + /***************************************************/ /**** Private helpers ****/ diff --git a/Revit_Core_Engine/Query/BHoMType.cs b/Revit_Core_Engine/Query/BHoMType.cs index 99b05b5ae..30d89278d 100644 --- a/Revit_Core_Engine/Query/BHoMType.cs +++ b/Revit_Core_Engine/Query/BHoMType.cs @@ -254,6 +254,26 @@ public static Type BHoMType(this RoofBase roofBase, Discipline discipline, Revit /***************************************************/ + [Description("Finds a suitable BHoM type to convert the given Revit CurtainSystem to, based on the requested engineering discipline and adapter settings.")] + [Input("system", "Revit CurtainSystem to find a correspondent BHoM type.")] + [Input("discipline", "Engineering discipline based on the BHoM discipline classification.")] + [Input("settings", "Revit adapter settings to be used while performing the search for the correspondent type.")] + [Output("bHoMType", "A suitable BHoM type to convert the given Revit CurtainSystem to.")] + public static Type BHoMType(this CurtainSystem system, Discipline discipline, RevitSettings settings = null) + { + switch (discipline) + { + case Discipline.Facade: + case Discipline.Architecture: + case Discipline.Physical: + return typeof(BH.oM.Facade.Elements.CurtainWall); + } + + return null; + } + + /***************************************************/ + [Description("Finds a suitable BHoM type to convert the given Revit HostObjAttributes to, based on the requested engineering discipline and adapter settings.")] [Input("hostObjAttributes", "Revit HostObjAttributes to find a correspondent BHoM type.")] [Input("discipline", "Engineering discipline based on the BHoM discipline classification.")] diff --git a/Revit_Core_Engine/Query/CurtainWallMullions.cs b/Revit_Core_Engine/Query/CurtainWallMullions.cs index 152cd4f98..933f3b385 100644 --- a/Revit_Core_Engine/Query/CurtainWallMullions.cs +++ b/Revit_Core_Engine/Query/CurtainWallMullions.cs @@ -51,7 +51,7 @@ public static List CurtainWallMullions(this CurtainGrid curtainGrid, List result = new List(); List mullions = curtainGrid.GetMullionIds().Select(x => document.GetElement(x)).ToList(); - foreach (Mullion mullion in mullions) + foreach (Mullion mullion in mullions.Where(x => x.get_BoundingBox(null) != null)) { result.Add(mullion.FrameEdgeFromRevit(settings, refObjects)); } diff --git a/Revit_Core_Engine/Query/FacadeCurtainPanels.cs b/Revit_Core_Engine/Query/FacadeCurtainPanels.cs index 8ce90c417..8118e71e9 100644 --- a/Revit_Core_Engine/Query/FacadeCurtainPanels.cs +++ b/Revit_Core_Engine/Query/FacadeCurtainPanels.cs @@ -32,6 +32,10 @@ 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; namespace BH.Revit.Engine.Core { @@ -58,52 +62,67 @@ public static partial class Query if (panels.Count != cells.Count) return null; - List cwMullions = curtainGrid.CurtainWallMullions(document, settings, refObjects).Select(x => x as IElement1D).ToList(); + 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; - List pcs = new List(); if (panel == null) continue; + + List outlines = new List(); try - { - pcs = cells[i].CurveLoops.FromRevit(); + { + // 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 // This catches when CurveLoops throws an exception due to the cell having no loops, meaning in Revit it exists in the database but is no longer on the CurtainWall with any corresponding Curves + catch { continue; } - foreach (PolyCurve pc in pcs) + foreach (PolyCurve outline in outlines) { - BH.oM.Facade.Elements.Opening bHoMOpening = null; - // If panel is a basic wall, the panel is not the actual element, it is an empty panel that hosts the - // actual element, so we assign it a null construction and separately return the wall - if (panel is Autodesk.Revit.DB.Panel && document.GetElement((panel as Autodesk.Revit.DB.Panel).FindHostPanel()) is Wall) + 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) { - Wall hostElement = document.GetElement((panel as Autodesk.Revit.DB.Panel).FindHostPanel()) as Wall; - BH.oM.Facade.Elements.Panel bHoMCWPanel = hostElement.FacadePanelFromRevit(settings, refObjects); - bHoMOpening = bHoMCWPanel.FacadePanelAsOpening(hostElement.Id.ToString(), refObjects); + // 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); } - else - bHoMOpening = panel.FacadeOpeningFromRevit(settings, refObjects); - if (bHoMOpening == null) - continue; + bHoMOpening.Name = panel.Name; - List edges = bHoMOpening.Edges; - foreach (FrameEdge edge in edges) - { - List adjEdges = edge.AdjacentElements(cwMullions).OfType().ToList() ; - if (adjEdges.Count > 0) - edge.FrameEdgeProperty = adjEdges[0].FrameEdgeProperty; - else - edge.FrameEdgeProperty = null; - } - bHoMOpening.Edges = edges; + //Set identifiers, parameters & custom data + bHoMOpening.SetIdentifiers(panel); + bHoMOpening.CopyParameters(panel, settings.MappingSettings); + bHoMOpening.SetProperties(panel, settings.MappingSettings); result.Add(bHoMOpening); } @@ -117,18 +136,39 @@ public static partial class Query /**** Private methods ****/ /***************************************************/ - private static oM.Facade.Elements.Opening FacadePanelAsOpening(this oM.Facade.Elements.Panel panel, string refId = "", Dictionary> refObjects = null) + private static BH.oM.Physical.Constructions.Construction Construction(this FamilyInstance panel, RevitSettings settings, Dictionary> refObjects) { - if (panel == null) - return null; - ; - oM.Facade.Elements.Opening opening = refObjects.GetValue(refId); - if (opening != null) - return opening; + 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(); + } + } - opening = new oM.Facade.Elements.Opening { Name = panel.Name, Edges = panel.ExternalEdges, Fragments = panel.Fragments, OpeningConstruction = panel.Construction, Tags = panel.Tags, CustomData = panel.CustomData, Type = oM.Facade.Elements.OpeningType.Undefined }; + /***************************************************/ - return opening; + 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; } /***************************************************/ diff --git a/Revit_Core_Engine/Query/FrameEdgeProperty.cs b/Revit_Core_Engine/Query/FrameEdgeProperty.cs deleted file mode 100644 index f8209182e..000000000 --- a/Revit_Core_Engine/Query/FrameEdgeProperty.cs +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2023, 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.Facade.SectionProperties; -using System; -using System.Collections.Generic; -using System.Linq; -using BH.oM.Geometry; -using BH.Engine.Geometry; -using System.ComponentModel; -using BH.oM.Base.Attributes; - -namespace BH.Revit.Engine.Core -{ - public static partial class Query - { - /***************************************************/ - /**** Public methods ****/ - /***************************************************/ - - [Description("Extracts the frame edge property from a Revit FamilyInstance.")] - [Input("familyInstance", "Revit FamilyInstance to be queried.")] - [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("property", "BHoM frame edge property extracted from the input Revit FamilyInstance.")] - public static FrameEdgeProperty FrameEdgeProperty(this FamilyInstance familyInstance, RevitSettings settings, Dictionary> refObjects = null) - { - if (familyInstance == null ) - return null; - - // Create FrameEdgeProperties, currently only using default - BH.Engine.Base.Compute.RecordWarning(String.Format("Revit specific FrameEdgeProperty conversion for this element is not currently supported, and a default FrameEdgeProperty has been assigned. Revit ElementId: {0}", familyInstance.Id.IntegerValue)); - List frameEdgeSectionProps = new List(); - oM.Physical.Materials.Material alumMullion = new oM.Physical.Materials.Material { Name = "Aluminum" }; - BH.oM.Spatial.ShapeProfiles.RectangleProfile rect = BH.Engine.Spatial.Create.RectangleProfile(0.1, 0.2); - - Vector offsetVector = new Vector { X = 0.1 }; - List mullionCrvs = new List(); - foreach (ICurve crv in rect.Edges) - { - mullionCrvs.Add(crv.ITranslate(offsetVector)); - } - - oM.Spatial.ShapeProfiles.FreeFormProfile edgeProf = BH.Engine.Spatial.Create.FreeFormProfile(mullionCrvs, false); - oM.Physical.FramingProperties.ConstantFramingProperty frameEdgeProp = new oM.Physical.FramingProperties.ConstantFramingProperty { Name = "Default Frame Edge Section Prop", Material = alumMullion, Profile = edgeProf }; - frameEdgeSectionProps.Add(frameEdgeProp); - FrameEdgeProperty defaultEdgeProp = new FrameEdgeProperty { Name = "Default Edge Property", SectionProperties = frameEdgeSectionProps }; - - return defaultEdgeProp; - } - - /***************************************************/ - } -} - diff --git a/Revit_Core_Engine/Query/FramingElementProperty.cs b/Revit_Core_Engine/Query/FramingElementProperty.cs index bab5350ce..eea019235 100644 --- a/Revit_Core_Engine/Query/FramingElementProperty.cs +++ b/Revit_Core_Engine/Query/FramingElementProperty.cs @@ -54,30 +54,15 @@ public static IFramingElementProperty FramingElementProperty(this FamilyInstance if (framingProperty != null) return framingProperty; - // Convert the material to BHoM. - ElementId structuralMaterialId = familyInstance.StructuralMaterialId; - if (structuralMaterialId.IntegerValue < 0) - structuralMaterialId = familyInstance.Symbol.LookupParameterElementId(BuiltInParameter.STRUCTURAL_MATERIAL_PARAM); - - Material revitMaterial = familyInstance.Document.GetElement(structuralMaterialId) as Material; - if (revitMaterial == null) - revitMaterial = familyInstance.Category.Material; - - string materialGrade = familyInstance.MaterialGrade(settings); - BH.oM.Physical.Materials.Material material = revitMaterial.MaterialFromRevit(materialGrade, settings, refObjects); - - // If Revit material is null, rename the BHoM material based on material type of framing family. - if (material != null && revitMaterial == null) - { - material.Name = String.Format("Unknown {0} Material", familyInstance.StructuralMaterialType); - material.Properties.Add(familyInstance.StructuralMaterialType.EmptyMaterialFragment(materialGrade)); - } + // Convert the material to BHoM + BH.oM.Physical.Materials.Material material = familyInstance.FramingMaterial(settings, refObjects); + // Convert the profile to BHoM and mirror it if needed IProfile profile = familyInstance.Symbol.ProfileFromRevit(settings, refObjects); if (profile == null) familyInstance.Symbol.NotConvertedWarning(); - if (familyInstance.Mirrored) + if (profile != null && familyInstance.Mirrored) { if (profile is FreeFormProfile) { @@ -95,7 +80,8 @@ public static IFramingElementProperty FramingElementProperty(this FamilyInstance profile = BH.Engine.Spatial.Create.ChannelProfile(channel.Height, channel.FlangeWidth, channel.WebThickness, channel.FlangeThickness, channel.RootRadius, channel.ToeRadius, true); } } - + + // Get rotation double rotation = familyInstance.OrientationAngle(settings); framingProperty = BH.Engine.Physical.Create.ConstantFramingProperty(profile, material, rotation, familyInstance.Symbol.Name); diff --git a/Revit_Core_Engine/Query/FramingMaterial.cs b/Revit_Core_Engine/Query/FramingMaterial.cs new file mode 100644 index 000000000..63d07f5a3 --- /dev/null +++ b/Revit_Core_Engine/Query/FramingMaterial.cs @@ -0,0 +1,89 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2023, 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.Engine.Adapters.Revit; +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 a BHoM representation of material from Revit FamilyInstance representing a framing element.")] + [Input("familyInstance", "Revit FamilyInstance to be queried.")] + [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("material", "BHoM representation of material extracted from Revit FamilyInstance representing a framing element.")] + public static BH.oM.Physical.Materials.Material FramingMaterial(this FamilyInstance familyInstance, RevitSettings settings, Dictionary> refObjects = null) + { + settings = settings.DefaultIfNull(); + + // Look up the material in structural material parameters + ElementId structuralMaterialId = familyInstance.StructuralMaterialId; + if (structuralMaterialId.IntegerValue < 0) + structuralMaterialId = familyInstance.Symbol.LookupParameterElementId(BuiltInParameter.STRUCTURAL_MATERIAL_PARAM); + + // If not found under structural material parameters, check if the solid representation has a consistent material assigned - if so, use it + if (structuralMaterialId.IntegerValue < 0) + { + Options options = new Options(); + options.DetailLevel = Autodesk.Revit.DB.ViewDetailLevel.Coarse; + List faces = familyInstance.Faces(options); + if (faces != null) + { + IEnumerable materialIdsFromFaces = faces.Where(x => x.MaterialElementId != null).Select(x => x.MaterialElementId); + List uniqueMaterialIds = materialIdsFromFaces.Distinct().ToList(); + if (uniqueMaterialIds.Count == 1) + structuralMaterialId = uniqueMaterialIds[0]; + } + } + + // Convert the Revit material to BHoM + Material revitMaterial = familyInstance.Document.GetElement(structuralMaterialId) as Material; + if (revitMaterial == null) + revitMaterial = familyInstance.Category.Material; + + string materialGrade = familyInstance.MaterialGrade(settings); + BH.oM.Physical.Materials.Material material = revitMaterial.MaterialFromRevit(materialGrade, settings, refObjects); + + // If Revit material is null, rename the BHoM material based on material type of framing family. + if (material != null && revitMaterial == null) + { + material.Name = $"Unknown {familyInstance.StructuralMaterialType} Material"; + material.Properties.Add(familyInstance.StructuralMaterialType.EmptyMaterialFragment(materialGrade)); + } + + return material; + } + + /***************************************************/ + } +} diff --git a/Revit_Core_Engine/Query/MullionElementProperty.cs b/Revit_Core_Engine/Query/MullionElementProperty.cs index 5a8548ea3..1ba2cb1c5 100644 --- a/Revit_Core_Engine/Query/MullionElementProperty.cs +++ b/Revit_Core_Engine/Query/MullionElementProperty.cs @@ -29,6 +29,7 @@ using BH.oM.Spatial.ShapeProfiles; using System.Collections.Generic; using System.ComponentModel; +using System.Linq; namespace BH.Revit.Engine.Core { @@ -38,33 +39,39 @@ public static partial class Query /**** Public methods ****/ /***************************************************/ - [Description("Extracts the mullion element property from a Revit FamilyInstance.")] - [Input("familyInstance", "Revit FamilyInstance to be queried.")] + [PreviousVersion("6.3", "BH.Revit.Engine.Core.Query.MullionElementProperty(Autodesk.Revit.DB.FamilyInstance, BH.oM.Adapters.Revit.Settings.RevitSettings, System.Collections.Generic.Dictionary>)")] + [Description("Extracts the mullion element property from a Revit Mullion.")] + [Input("mullion", "Revit Mullion to be queried.")] [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("property", "BHoM mullion element property extracted from the input Revit FamilyInstance.")] - public static FrameEdgeProperty MullionElementProperty(this FamilyInstance familyInstance, RevitSettings settings, Dictionary> refObjects = null) + [Output("property", "BHoM mullion element property extracted from the input Revit Mullion.")] + public static FrameEdgeProperty MullionElementProperty(this Mullion mullion, RevitSettings settings, Dictionary> refObjects = null) { - if (familyInstance == null || familyInstance.Symbol == null) + if (mullion?.Symbol == null) return null; - FrameEdgeProperty frameEdgeProperty = refObjects.GetValue(familyInstance.Id); + FrameEdgeProperty frameEdgeProperty = refObjects.GetValue(mullion.Id); if (frameEdgeProperty != null) return frameEdgeProperty; - // Profile and material extraction not yet implemented - IProfile profile = null; - BH.oM.Physical.Materials.Material material = null; + // Convert the material to BHoM + BH.oM.Physical.Materials.Material material = mullion.FramingMaterial(settings, refObjects); - List sectionProperties = new List { BH.Engine.Physical.Create.ConstantFramingProperty(profile, material, 0, familyInstance.Symbol.Name) }; - frameEdgeProperty = new FrameEdgeProperty { Name = familyInstance.Symbol.Name, SectionProperties = sectionProperties }; + // Convert the profile to BHoM + IProfile profile = mullion.MullionType.ProfileFromRevit(settings, refObjects); + + 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) }; + frameEdgeProperty = new FrameEdgeProperty { Name = mullion.Symbol.Name, SectionProperties = sectionProperties }; //Set identifiers, parameters & custom data - frameEdgeProperty.SetIdentifiers(familyInstance.Symbol); - frameEdgeProperty.CopyParameters(familyInstance.Symbol, settings.MappingSettings); - frameEdgeProperty.SetProperties(familyInstance.Symbol, settings.MappingSettings); + frameEdgeProperty.SetIdentifiers(mullion.Symbol); + frameEdgeProperty.CopyParameters(mullion.Symbol, settings.MappingSettings); + frameEdgeProperty.SetProperties(mullion.Symbol, settings.MappingSettings); - refObjects.AddOrReplace(familyInstance.Id, frameEdgeProperty); + refObjects.AddOrReplace(mullion.Id, frameEdgeProperty); return frameEdgeProperty; } diff --git a/Revit_Core_Engine/Versioning_63.json b/Revit_Core_Engine/Versioning_63.json new file mode 100644 index 000000000..f0c533228 --- /dev/null +++ b/Revit_Core_Engine/Versioning_63.json @@ -0,0 +1,5 @@ +{ + "MessageForDeleted": { + "BH.Revit.Engine.Core.Query.FrameEdgeProperty(Autodesk.Revit.DB.FamilyInstance, BH.oM.Adapters.Revit.Settings.RevitSettings, System.Collections.Generic.Dictionary>)": "FrameEdgeProperty extraction mechanism had changed and this method became obsolete." + } +} \ No newline at end of file