diff --git a/Revit_Core_Engine/Compute/GeneratePadFoundationType.cs b/Revit_Core_Engine/Compute/GeneratePadFoundationType.cs index 0ecbad7b5..4a68d2674 100644 --- a/Revit_Core_Engine/Compute/GeneratePadFoundationType.cs +++ b/Revit_Core_Engine/Compute/GeneratePadFoundationType.cs @@ -188,7 +188,7 @@ private static Family GenerateFreeFormPadFamilyFromTemplate(this Document docume try { - if (!ReplaceFreeFormExtrusion(familyDocument, orientedOutline, thickness)) + if (ReplaceFreeFormExtrusion(familyDocument, orientedOutline, thickness) == null) return null; return SaveAndLoadFamily(document, familyDocument, $"{Path.GetFileNameWithoutExtension(templatePath)}_{index}"); @@ -206,28 +206,34 @@ private static Family GenerateFreeFormPadFamilyFromTemplate(this Document docume /***************************************************/ - private static bool ReplaceFreeFormExtrusion(Document familyDocument, Polyline orientedOutline, double thickness) + private static Extrusion ReplaceFreeFormExtrusion(Document familyDocument, Polyline orientedOutline, double thickness) { + Extrusion result = null; try { Extrusion extrusion = new FilteredElementCollector(familyDocument).OfClass(typeof(Extrusion)).FirstOrDefault() as Extrusion; CurveArrArray profile = new CurveArrArray(); profile.Append(orientedOutline.ToRevitCurveArray()); + Category subcategory = extrusion.Subcategory; using (Transaction t = new Transaction(familyDocument, "Update Freeform Pad Foundation Footprint")) { t.Start(); - familyDocument.FamilyCreate.NewExtrusion(true, profile, extrusion.Sketch.SketchPlane, -FreeformExtrusionDepth(thickness)); + result = familyDocument.FamilyCreate.NewExtrusion(true, profile, extrusion.Sketch.SketchPlane, -thickness.FromSI(SpecTypeId.Length)); + + if (subcategory != null) + result.Subcategory = subcategory; + familyDocument.Delete(extrusion.Id); t.Commit(); } } catch { - return false; + return null; } - return true; + return result; } /***************************************************/ diff --git a/Revit_Core_Engine/Compute/GeneratePileFoundationType.cs b/Revit_Core_Engine/Compute/GeneratePileFoundationType.cs index 7978ffd2b..6df758381 100644 --- a/Revit_Core_Engine/Compute/GeneratePileFoundationType.cs +++ b/Revit_Core_Engine/Compute/GeneratePileFoundationType.cs @@ -130,12 +130,12 @@ private static FamilySymbol GenerateFreeformType(PileFoundation pileFoundation, List freeformFamilies = new FilteredElementCollector(document).OfClass(typeof(Family)).Cast() .Where(x => Regex.IsMatch(x.Name, $"{prefix}\\d+$")).ToList(); - Family family = freeformFamilies.FirstOrDefault(x => x.IsMatchingOutlineAndLayout(orientedOutline, layout, diameter, settings)); + Family family = freeformFamilies.FirstOrDefault(x => x.IsMatchingOutlineAndLayout(orientedOutline, layout, settings)); if (family == null) { List takenIndices = freeformFamilies.Select(x => Regex.Match(x.Name, $"{Regex.Escape(prefix)}(\\d+)$")).Select(x => int.Parse(x.Groups[1].Value)).ToList(); int newIndex = takenIndices.Count > 0 ? takenIndices.Max() + 1 : 1; - family = GenerateFreeFormPileFoundationFamilyFromTemplate(document, orientedOutline, layout, diameter, thickness, newIndex); + family = GenerateFreeFormPileFoundationFamilyFromTemplate(document, orientedOutline, layout, newIndex); } if (family == null) @@ -146,7 +146,7 @@ private static FamilySymbol GenerateFreeformType(PileFoundation pileFoundation, /***************************************************/ - private static Family GenerateFreeFormPileFoundationFamilyFromTemplate(this Document document, Polyline orientedOutline, ExplicitLayout layout, double diameter, double thickness, int index) + private static Family GenerateFreeFormPileFoundationFamilyFromTemplate(this Document document, Polyline orientedOutline, ExplicitLayout layout, int index) { string templateFamilyName = "StructuralFoundations_PileFoundation-Freeform"; string templatePath = Directory.GetFiles(m_FamilyDirectory, $"*{templateFamilyName}.rfa").FirstOrDefault(); @@ -162,10 +162,10 @@ private static Family GenerateFreeFormPileFoundationFamilyFromTemplate(this Docu try { - if (!ReplaceFreeFormExtrusionWithAssociate(familyDocument, orientedOutline, thickness)) + if (!ReplaceFreeFormExtrusionWithDepthBinding(familyDocument, orientedOutline)) return null; - if (!PlaceNestedPiles(familyDocument, layout, diameter)) + if (!SetPileLayout(familyDocument, layout)) return null; return SaveAndLoadFamily(document, familyDocument, $"{Path.GetFileNameWithoutExtension(templatePath)}_{index}"); @@ -183,61 +183,85 @@ private static Family GenerateFreeFormPileFoundationFamilyFromTemplate(this Docu /***************************************************/ - private static double FreeformExtrusionDepth(double thickness) + private static bool ReplaceFreeFormExtrusionWithDepthBinding(Document familyDocument, Polyline orientedOutline) { - double h = double.IsNaN(thickness) ? double.NaN : thickness.FromSI(SpecTypeId.Length); - if (double.IsNaN(h) || h <= 1e-6) - h = 0.5.FromSI(SpecTypeId.Length); - return h; + try + { + Extrusion extrusion = ReplaceFreeFormExtrusion(familyDocument, orientedOutline, 1); + if (extrusion == null) + return false; + + extrusion.BindThicknessWithDimension(); + return true; + } + catch + { + return false; + } } /***************************************************/ - private static bool ReplaceFreeFormExtrusionWithAssociate(Document familyDocument, Polyline orientedOutline, double thickness) + private static Dimension BindThicknessWithDimension(this Extrusion extrusion) { - try + Document doc = extrusion.Document; + Options options = new Options { - if (!ReplaceFreeFormExtrusion(familyDocument, orientedOutline, thickness)) - return false; + ComputeReferences = true + }; - using (Transaction t = new Transaction(familyDocument, "Update Freeform Pile Foundation Footprint")) - { - Extrusion extrusion = new FilteredElementCollector(familyDocument).OfClass(typeof(Extrusion)).Cast().FirstOrDefault(); - if (extrusion == null) - return false; + PlanarFace topFace = null; + PlanarFace bottomFace = null; - t.Start(); + List faces = extrusion.Faces(options); + foreach (Autodesk.Revit.DB.Face face in faces) + { + if (!(face is PlanarFace planarFace)) + continue; - FamilyManager familyManager = familyDocument.FamilyManager; - Parameter oldStartParam = extrusion.get_Parameter(BuiltInParameter.EXTRUSION_START_PARAM); - FamilyParameter associatedStartParameter = oldStartParam != null - ? familyManager.GetAssociatedFamilyParameter(oldStartParam) - : null; + if (planarFace.FaceNormal.IsAlmostEqualTo(XYZ.BasisZ)) + topFace = planarFace; - Parameter endParam = extrusion.get_Parameter(BuiltInParameter.EXTRUSION_END_PARAM); - if (endParam != null && !endParam.IsReadOnly) - endParam.Set(0.0); + if (planarFace.FaceNormal.IsAlmostEqualTo(-XYZ.BasisZ)) + bottomFace = planarFace; + } - if (associatedStartParameter != null) - { - Parameter newStartParam = extrusion.get_Parameter(BuiltInParameter.EXTRUSION_START_PARAM); - familyManager.AssociateElementParameterToFamilyParameter(newStartParam, associatedStartParameter); - } + if (topFace == null || bottomFace == null) + { + BH.Engine.Base.Compute.RecordError("Failed to find top and bottom faces of the extrusion."); + return null; + } - t.Commit(); - } + ReferenceArray references = new ReferenceArray(); + references.Append(bottomFace.Reference); + references.Append(topFace.Reference); - return true; - } - catch + double z1 = bottomFace.Origin.Z; + double z2 = topFace.Origin.Z; + + // Vertical dimension line through (0,0) + Autodesk.Revit.DB.Line dimensionLine = Autodesk.Revit.DB.Line.CreateBound(new XYZ(0, 0, z1), new XYZ(0, 0, z2)); + + using (Transaction t = new Transaction(doc, "Create Thickness Dimension")) { - return false; + t.Start(); + + View view = new FilteredElementCollector(doc).OfClass(typeof(ViewSection)).FirstOrDefault() as View; + Dimension dimension = doc.FamilyCreate.NewDimension(view, dimensionLine, references); + FamilyManager fm = doc.FamilyManager; + + FamilyParameter thicknessParameter = fm.get_Parameter(BuiltInParameter.STRUCTURAL_FOUNDATION_THICKNESS); + if (thicknessParameter != null) + dimension.FamilyLabel = thicknessParameter; + + t.Commit(); + return dimension; } } /***************************************************/ - private static bool PlaceNestedPiles(Document familyDocument, ExplicitLayout layout, double diameter) + private static bool SetPileLayout(Document familyDocument, ExplicitLayout layout) { List templatePiles = new FilteredElementCollector(familyDocument).OfClass(typeof(FamilyInstance)).Cast().ToList(); if (templatePiles.Count == 0) @@ -253,10 +277,10 @@ private static bool PlaceNestedPiles(Document familyDocument, ExplicitLayout lay BoundingBoxXYZ bbox = e.get_BoundingBox(null); if (bbox == null) return double.MaxValue; + XYZ c = (bbox.Min + bbox.Max) / 2.0; return c.DistanceTo(new XYZ(pileOrigin.X, pileOrigin.Y, c.Z)); - }) - .FirstOrDefault(); + }).FirstOrDefault(); try { @@ -264,13 +288,6 @@ private static bool PlaceNestedPiles(Document familyDocument, ExplicitLayout lay { t.Start(); - FamilySymbol nestType = NestedPileSymbol(templatePile.Symbol, diameter); - if (nestType == null) - { - t.RollBack(); - return false; - } - if (templatePiles.Count > 1) familyDocument.Delete(templatePiles.Skip(1).Select(x => x.Id).ToList()); @@ -305,17 +322,6 @@ private static bool PlaceNestedPiles(Document familyDocument, ExplicitLayout lay if (templateVoidExtrusion != null) familyDocument.Delete(templateVoidExtrusion.Id); - foreach (FamilyInstance pile in placedPiles) - { - if (pile.Symbol.Id != nestType.Id && pile.IsValidType(nestType.Id)) - pile.ChangeTypeId(nestType.Id); - } - - FamilyManager fm = familyDocument.FamilyManager; - FamilyParameter radiusParam = fm.get_Parameter("Radius"); - if (radiusParam != null) - fm.Set(radiusParam, (diameter / 2.0).FromSI(SpecTypeId.Length)); - Extrusion cap = new FilteredElementCollector(familyDocument).OfClass(typeof(Extrusion)).Cast().FirstOrDefault(e => e.IsSolid); if (cap != null) { @@ -343,30 +349,6 @@ private static bool PlaceNestedPiles(Document familyDocument, ExplicitLayout lay /***************************************************/ - private static FamilySymbol NestedPileSymbol(FamilySymbol pileSymbol, double diameter) - { - if (pileSymbol == null || diameter <= 0) - return pileSymbol; - - string typeName = $"Ø{(long)Math.Round(diameter * 1000.0)}"; - FamilySymbol nestSymbol = pileSymbol.Family.GetFamilySymbolIds().Select(id => pileSymbol.Document.GetElement(id) as FamilySymbol).FirstOrDefault(s => s != null && s.Name.Equals(typeName, StringComparison.OrdinalIgnoreCase)); - - if (nestSymbol == null) - nestSymbol = pileSymbol.Duplicate(typeName) as FamilySymbol; - - if (nestSymbol == null) - return null; - - nestSymbol.SetParameter("Radius", diameter / 2.0); - - if (!nestSymbol.IsActive) - nestSymbol.Activate(); - - return nestSymbol; - } - - /***************************************************/ - private static FamilySymbol FindOrCreateTypeWithDimensions(this Family family, double thickness, double diameter) { List symbols = family.GetFamilySymbolIds().Select(id => family.Document.GetElement(id) as FamilySymbol).Where(s => s != null).ToList(); @@ -383,7 +365,26 @@ private static FamilySymbol FindOrCreateTypeWithDimensions(this Family family, d else result = symbols[0].Duplicate(typeName) as FamilySymbol; - result.SetParameter("Depth", thickness); + Parameter depthParam = result.Parameters.Cast().FirstOrDefault(x => !x.IsReadOnly && x.Definition.Name.EndsWith("Depth") && !x.Definition.Name.Contains("Pile")); + if (depthParam != null) + depthParam.Set(thickness.FromSI(depthParam.Definition.GetDataType())); + else + BH.Engine.Base.Compute.RecordError($"Pile foundation thickness could not be set. {family.Name} : {typeName}"); + + Parameter diameterParam = result.Parameters.Cast().FirstOrDefault(x => !x.IsReadOnly && x.Definition.Name.EndsWith("Diameter")); + if (diameterParam != null) + diameterParam.Set(diameter.FromSI(diameterParam.Definition.GetDataType())); + else + BH.Engine.Base.Compute.RecordError($"Pile diameter could not be set. {family.Name} : {typeName}"); + + Parameter pileTypeParam = result.Parameters.Cast().FirstOrDefault(x => !x.IsReadOnly && x.Definition.Name.EndsWith("Pile Type")); + ElementId currentPileTypeId = pileTypeParam?.AsElementId() ?? ElementId.InvalidElementId; + Family pileFamily = (family.Document.GetElement(currentPileTypeId) as FamilySymbol)?.Family; + FamilySymbol pileType = NestedPileSymbol(pileFamily, diameter); + if (pileType != null) + pileTypeParam.Set(pileType.Id); + else + BH.Engine.Base.Compute.RecordError($"Pile type could not be set. {family.Name} : {typeName}"); } result?.Activate(); @@ -391,5 +392,32 @@ private static FamilySymbol FindOrCreateTypeWithDimensions(this Family family, d } /***************************************************/ + + private static FamilySymbol NestedPileSymbol(Family pileFamily, double diameter) + { + if (pileFamily == null || diameter <= 0) + return null; + + string typeName = $"Ø{(long)Math.Round(diameter * 1000.0)}"; + FamilySymbol result = pileFamily.GetFamilySymbolIds().Select(id => pileFamily.Document.GetElement(id) as FamilySymbol).FirstOrDefault(s => s != null && s.Name.Equals(typeName, StringComparison.OrdinalIgnoreCase)); + + if (result == null) + { + result = (pileFamily.Document.GetElement(pileFamily.GetFamilySymbolIds().FirstOrDefault()) as FamilySymbol)?.Duplicate(typeName) as FamilySymbol; + if (result == null) + return null; + + Parameter radiusParam = result.Parameters.Cast().FirstOrDefault(x => !x.IsReadOnly && x.Definition.Name.EndsWith("Radius")); + if (radiusParam != null) + radiusParam.Set((diameter / 2.0).FromSI(radiusParam.Definition.GetDataType())); + } + + if (!result.IsActive) + result.Activate(); + + return result; + } + + /***************************************************/ } } diff --git a/Revit_Core_Engine/Compute/GenerateProfile.cs b/Revit_Core_Engine/Compute/GenerateProfile.cs index 19fafa165..31a10f579 100644 --- a/Revit_Core_Engine/Compute/GenerateProfile.cs +++ b/Revit_Core_Engine/Compute/GenerateProfile.cs @@ -321,12 +321,13 @@ private static bool SetMaterialSubcategory(this Document familyDocument, BH.oM.P else return false; - Category subCategory = familyDocument.OwnerFamily.FamilyCategory.SubCategories.Cast().FirstOrDefault(x => x.Name.EndsWith(keyword)); + Category subCategory = familyDocument.OwnerFamily.FamilyCategory.SubCategories.Cast().Where(x => x.Name.EndsWith(keyword)).OrderBy(x => x.Name).FirstOrDefault(); if (subCategory != null) { foreach (GenericForm gf in new FilteredElementCollector(familyDocument).OfClass(typeof(GenericForm))) { - gf.Subcategory = subCategory; + if (gf.IsSolid) + gf.Subcategory = subCategory; } return true; diff --git a/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs b/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs index e699e07fd..6001a4b31 100644 --- a/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs +++ b/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs @@ -30,6 +30,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Linq; namespace BH.Revit.Engine.Core { @@ -312,7 +313,11 @@ public static FamilyInstance ToRevitFamilyInstance(this PileFoundation pileFound double pileDepth = pileFoundation.PileFoundationDepth(settings); if (!double.IsNaN(pileDepth)) - familyInstance.SetParameter("Pile Depth", pileDepth); + { + Parameter pileDepthParam = familyInstance.Parameters.Cast().FirstOrDefault(x => !x.IsReadOnly && x.Definition.Name.Contains("Pile") && x.Definition.Name.Contains("Depth")); + if (pileDepthParam != null) + pileDepthParam.Set(pileDepth.FromSI(pileDepthParam.Definition.GetDataType())); + } familyInstance.SetLocation(pileFoundation, settings); refObjects.AddOrReplace(pileFoundation, familyInstance); diff --git a/Revit_Core_Engine/Files/Families/StructuralFoundations_PileFoundation-Freeform.rfa b/Revit_Core_Engine/Files/Families/StructuralFoundations_PileFoundation-Freeform.rfa index 8f06370fc..d5f86bdc3 100644 Binary files a/Revit_Core_Engine/Files/Families/StructuralFoundations_PileFoundation-Freeform.rfa and b/Revit_Core_Engine/Files/Families/StructuralFoundations_PileFoundation-Freeform.rfa differ diff --git a/Revit_Core_Engine/Modify/SetLocation.cs b/Revit_Core_Engine/Modify/SetLocation.cs index 87c9c1473..964853454 100644 --- a/Revit_Core_Engine/Modify/SetLocation.cs +++ b/Revit_Core_Engine/Modify/SetLocation.cs @@ -317,15 +317,11 @@ public static bool SetLocation(this FamilyInstance element, Pile pile, RevitSett verticalDimensionSet = false; // Try setting depth parameter - Parameter depthParam = element.LookupParameter("Pile Depth"); + Parameter depthParam = element.Parameters.Cast().FirstOrDefault(x => x.StorageType == StorageType.Double && !x.IsReadOnly && x.Definition.Name.Contains("Pile Depth")); if (depthParam == null) - { - depthParam = element.Parameters.Cast().FirstOrDefault(x => x.StorageType == StorageType.Double && !x.IsReadOnly && x.Definition.Name.Contains("Depth")); - if (depthParam == null) - BH.Engine.Base.Compute.RecordWarning($"Could not find a suitable parameter to set pile depth on the element."); - else - BH.Engine.Base.Compute.RecordWarning($"Parameter 'Pile Depth' could not be found on the element, therefore an attempt to set vertical constraint via '{depthParam.Definition.Name}' is made."); - } + BH.Engine.Base.Compute.RecordWarning($"Could not find a suitable parameter to set pile depth on the element."); + else + BH.Engine.Base.Compute.RecordWarning($"Parameter 'Pile Depth' could not be found on the element, therefore an attempt to set vertical constraint via '{depthParam.Definition.Name}' is made."); if (depthParam != null && !depthParam.IsReadOnly) verticalDimensionSet &= depthParam.Set(pileDepth.FromSI(SpecTypeId.Length)); diff --git a/Revit_Core_Engine/Modify/Update.cs b/Revit_Core_Engine/Modify/Update.cs index cb99a2718..a727d77d2 100644 --- a/Revit_Core_Engine/Modify/Update.cs +++ b/Revit_Core_Engine/Modify/Update.cs @@ -187,7 +187,11 @@ public static bool Update(this FamilyInstance element, PileFoundation bHoMObject double pileDepth = bHoMObject.PileFoundationDepth(settings); if (!double.IsNaN(pileDepth)) - element.SetParameter("Pile Depth", pileDepth); + { + Parameter pileDepthParam = element.Parameters.Cast().FirstOrDefault(x => !x.IsReadOnly && x.Definition.Name.Contains("Pile") && x.Definition.Name.Contains("Depth")); + if (pileDepthParam != null) + pileDepthParam.Set(pileDepth.FromSI(pileDepthParam.Definition.GetDataType())); + } return result; } diff --git a/Revit_Core_Engine/Query/IsMatchingOutlineAndLayout.cs b/Revit_Core_Engine/Query/IsMatchingOutlineAndLayout.cs index 221778669..330e55ee3 100644 --- a/Revit_Core_Engine/Query/IsMatchingOutlineAndLayout.cs +++ b/Revit_Core_Engine/Query/IsMatchingOutlineAndLayout.cs @@ -27,7 +27,6 @@ using BH.oM.Base.Attributes; using BH.oM.Geometry; using BH.oM.Spatial.Layouts; -using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; @@ -40,16 +39,16 @@ public static partial class Query /**** Public methods ****/ /***************************************************/ + [PreviousVersion("10.0", "BH.Revit.Engine.Core.Query.IsMatchingOutlineAndLayout(Autodesk.Revit.DB.Family, BH.oM.Geometry.Polyline, BH.oM.Spatial.Layouts.ExplicitLayout, System.Double, BH.oM.Adapters.Revit.Settings.RevitSettings)")] [Description("Checks whether a pile foundation family matches the pile-cap outline, pile layout and pile diameter.")] [Input("family", "Revit pile foundation family to compare.")] [Input("orientedOutline", "Oriented pile-cap outline.")] [Input("layout", "Explicit pile layout points.")] - [Input("diameter", "Pile diameter to match against the family.")] [Input("settings", "Revit adapter settings.")] [Output("matches", "True if outline, layout and diameter all match.")] - public static bool IsMatchingOutlineAndLayout(this Family family, Polyline orientedOutline, ExplicitLayout layout, double diameter, RevitSettings settings) + public static bool IsMatchingOutlineAndLayout(this Family family, Polyline orientedOutline, ExplicitLayout layout, RevitSettings settings) { - if (family == null || orientedOutline == null || layout?.Points == null || diameter <= 0) + if (family == null || orientedOutline == null || layout?.Points == null) return false; settings = settings.DefaultIfNull(); @@ -61,9 +60,6 @@ public static bool IsMatchingOutlineAndLayout(this Family family, Polyline orien if (!family.IsMatchingPileLayout(layout, tol)) return false; - if (!family.IsMatchingPileDiameter(diameter, tol)) - return false; - return true; } @@ -89,27 +85,6 @@ private static bool IsMatchingPileLayout(this Family family, ExplicitLayout layo /***************************************************/ - private static bool IsMatchingPileDiameter(this Family family, double diameter, double tol) - { - Document doc = family.Document; - FamilyInstance host = new FilteredElementCollector(doc).OfClass(typeof(FamilyInstance)).Cast().FirstOrDefault(fi => fi.Symbol?.Family?.Id == family.Id); - if (host == null) - return false; - FamilyInstance nest = host.GetSubComponentIds().Select(id => doc.GetElement(id) as FamilyInstance).FirstOrDefault(fi => fi != null); - FamilySymbol nestSymbol = nest?.Symbol; - if (nestSymbol == null) - return false; - double radius = nestSymbol.LookupParameterDouble("Radius"); - if (!double.IsNaN(radius)) - return Math.Abs(radius * 2.0 - diameter) <= tol; - double nestDiameter = nestSymbol.LookupParameterDouble("Diameter"); - if (!double.IsNaN(nestDiameter)) - return Math.Abs(nestDiameter - diameter) <= tol; - return false; - } - - /***************************************************/ - private static List PileLayoutPoints(this Family family) { Document doc = family.Document;