Skip to content
223 changes: 223 additions & 0 deletions Revit_Core_Engine/Compute/GeneratePadFoundation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2026, 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 <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using BH.Engine.Adapters.Revit;
using BH.oM.Adapters.Revit.Settings;
using BH.oM.Base.Attributes;
using BH.oM.Geometry;
using BH.oM.Physical.Elements;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

namespace BH.Revit.Engine.Core
{
public static partial class Compute
{
/***************************************************/
/**** Public methods ****/
/***************************************************/

[Description("Generates a Revit family type (and its parent family, if not loaded yet) that represents the profile of a given BHoM pad foundation." +
"\nThe profile is created based on the template families stored in C:\\ProgramData\\Resources\\Revit.")]
[Input("padFoundation", "BHoM pad foundation to generate the Revit profile for.")]
[Input("document", "Revit document, in which the family type will be created.")]
[Input("settings", "Settings to be used when generating the family type.")]
[Output("symbol", "Created Revit family type that represents the profile of the input BHoM pad foundation.")]
public static FamilySymbol GeneratePadFoundation(this PadFoundation padFoundation, Document document, RevitSettings settings = null)
{
if (padFoundation == null)
{
BH.Engine.Base.Compute.RecordError($"The BHoM pad foundation is null. BHoM_Guid: {padFoundation?.BHoM_Guid}");
return null;
}

string familyName = padFoundation.PadFoundationFamilyName();
if (string.IsNullOrWhiteSpace(familyName))
{
BH.Engine.Base.Compute.RecordError($"Creation of a Revit pad foundation family failed because the BHoM pad foundation does not have a name. BHoM_Guid: {padFoundation.BHoM_Guid}");
return null;
}

settings = settings.DefaultIfNull();

Family family = new FilteredElementCollector(document).OfClass(typeof(Family)).FirstOrDefault(x => x.Name == familyName) as Family;
if (family != null)
{
List<FamilySymbol> symbols = family.GetFamilySymbolIds().Select(x => document.GetElement(x) as FamilySymbol).Where(x => x != null).ToList();
FamilySymbol result = symbols.FirstOrDefault(x => x?.Name == padFoundation.Name);
if (result == null && symbols.Count != 0)
{
result = symbols[0].Duplicate(padFoundation.Name) as FamilySymbol;
result.Activate();
padFoundation.ICopyFoundationDimensions(result, settings);
}

return result;
}
else
{
family = document.GenerateFamilyFromTemplate(padFoundation, familyName, settings);
if (family == null)
{
family = document.GenerateFreeformPadFoundation(padFoundation, familyName, settings);
if (family == null)
return null;
}

FamilySymbol result = document.GetElement(family.GetFamilySymbolIds().FirstOrDefault()) as FamilySymbol;
if (result == null)
{
BH.Engine.Base.Compute.RecordWarning($"Generation of a Revit family representing the BHoM pad foundation failed due to an internal error. BHoM_Guid: {padFoundation.BHoM_Guid}");
return null;
}

result.Activate();
result.Name = padFoundation.PadFoundationTypeName() + " " + padFoundation.PadFoundationFamilyName();
return result;
}
}


/***************************************************/
/**** Private methods ****/
/***************************************************/

private static Family GenerateFamilyFromTemplate(this Document document, PadFoundation padFoundation, string familyName, RevitSettings settings = null)
{
string templateFamilyName = padFoundation.PadFoundationFamilyName();
if (string.IsNullOrWhiteSpace(templateFamilyName))
return null;

string path = Path.Combine(m_FamilyDirectory, $"{templateFamilyName}.rfa");

Family result = null;
UIDocument uidoc = new UIDocument(document);
Document familyDocument = uidoc.Application.Application.OpenDocumentFile(path);

try
{
result = SaveAndLoadFamily(document, familyDocument, familyName);
}
catch (Exception ex)
{
BH.Engine.Base.Compute.RecordError($"Creation of a freeform Revit profile geometry failed with the following error: {ex.Message}");
}
finally
{
familyDocument.Close(false);
}

if (result != null)
{
FamilySymbol symbol = document.GetElement(result?.GetFamilySymbolIds().FirstOrDefault()) as FamilySymbol;
if (symbol != null)
padFoundation.ICopyFoundationDimensions(symbol, settings);
}

return result;
}

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

private static Family GenerateFreeformPadFoundation(this Document document, PadFoundation padFoundation, string familyName, RevitSettings settings = null)
{
throw new NotImplementedException("GenerateFreeformPadFoundation is not implemented.");
}

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


private static string PadFoundationFamilyName(this PadFoundation padFoundation)
{
string name = padFoundation?.Name;
if (string.IsNullOrWhiteSpace(name))
return null;

if (name.Contains(':'))
return name.Split(':')[0].Trim();
else
{
Regex pattern = new Regex(@"\d([\d\.\/\-xX ])*\d");
return $"BHE_StructuralFoundations_{pattern.Replace(name, "").Replace(" ", " ").Trim()}";
}
}

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

private static string PadFoundationTypeName(this PadFoundation padFoundation)
{
string name = padFoundation?.Name;
if (string.IsNullOrWhiteSpace(name))
return null;

if (name.Contains(':'))
return name.Split(':')[1].Trim();
else
return name;
}

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

private static void ICopyFoundationDimensions(this PadFoundation padFoundation, FamilySymbol targetSymbol, RevitSettings settings = null)
{
CopyFoundationDimensions(padFoundation as dynamic, targetSymbol, settings);
}

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

private static void CopyFoundationDimensions(this PadFoundation padFoundation, FamilySymbol targetSymbol, RevitSettings settings = null)
{
Polyline outline = padFoundation.ExtractBoundary();
if (outline == null)
{
BH.Engine.Base.Compute.RecordError($"PadFoundation outline extraction failed. BHoM_Guid: {padFoundation.BHoM_Guid}");
return;
}

var (width, length) = outline.GetRectangleDimensions();
double depth = padFoundation.GetThicknessFromConstr();

Parameter widthParam = targetSymbol.LookupParameter("BHE_Width");
if (widthParam != null)
widthParam.Set(width);

Parameter lengthParam = targetSymbol.LookupParameter("BHE_Length");
if (lengthParam != null)
lengthParam.Set(length);

Parameter depthParam = targetSymbol.LookupParameter("BHE_Depth");
if (depthParam != null)
depthParam.Set(depth);
}

/***************************************************/
}
}



9 changes: 5 additions & 4 deletions Revit_Core_Engine/Compute/GenerateProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
using BH.Engine.Geometry;
using BH.Engine.Spatial;
using BH.oM.Adapters.Revit.Settings;
using BH.oM.Base;
using BH.oM.Base.Attributes;
using BH.oM.Physical.Elements;
using BH.oM.Physical.FramingProperties;
Expand Down Expand Up @@ -116,7 +117,7 @@ public static FamilySymbol GenerateProfile(this IFramingElement element, Documen
/**** Private methods ****/
/***************************************************/

private static Family SaveAndLoadFamily(Document document, Document familyDocument, string familyName, IFramingElement element, RevitSettings settings)
private static Family SaveAndLoadFamily(Document document, Document templateDocument, string familyName)
{
Family result = null;
string tempFolder = Path.GetTempPath();
Expand All @@ -129,7 +130,7 @@ private static Family SaveAndLoadFamily(Document document, Document familyDocume

SaveAsOptions saveOptions = new SaveAsOptions();
saveOptions.OverwriteExistingFile = true;
familyDocument.SaveAs(tempLocation, saveOptions);
templateDocument.SaveAs(tempLocation, saveOptions);
}
catch (Exception ex)
{
Expand Down Expand Up @@ -174,7 +175,7 @@ private static Family GenerateFamilyFromTemplate(this Document document, IFramin
t.Commit();
}

result = SaveAndLoadFamily(document, familyDocument, familyName, element, settings);
result = SaveAndLoadFamily(document, familyDocument, familyName);
}
catch (Exception ex)
{
Expand Down Expand Up @@ -273,7 +274,7 @@ private static Family GenerateFreeformFamily(this Document document, IFramingEle
t.Commit();
}

result = SaveAndLoadFamily(document, familyDocument, familyName, element, settings);
result = SaveAndLoadFamily(document, familyDocument, familyName);
}
catch (Exception ex)
{
Expand Down
119 changes: 119 additions & 0 deletions Revit_Core_Engine/Convert/Physical/FromRevit/PadFoundation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2026, 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 <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using Autodesk.Revit.DB;
using BH.Engine.Adapters.Revit;
using BH.Engine.Geometry;
using BH.oM.Adapters.Revit;
using BH.oM.Adapters.Revit.Settings;
using BH.oM.Base;
using BH.oM.Geometry;
using BH.oM.Physical.Elements;
using BH.oM.Base.Attributes;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using ISurface = BH.oM.Geometry.ISurface;
using Point = BH.oM.Geometry.Point;

namespace BH.Revit.Engine.Core
{
public static partial class Convert
{
/***************************************************/
/**** Public Methods ****/
/***************************************************/

[Description("Converts a Revit FamilyInstance to BH.oM.Physical.Elements.PadFoundation.")]
[Input("familyInstance", "Revit 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("padFoundation", "BH.oM.Physical.Elements.PadFoundation resulting from converting the input Revit FamilyInstance.")]
public static PadFoundation PadFoundationFromRevit(this FamilyInstance familyInstance, RevitSettings settings = null, Dictionary<string, List<IBHoMObject>> refObjects = null)
{
settings = settings.DefaultIfNull();

PadFoundation padFoundation = refObjects.GetValue<PadFoundation>(familyInstance.Id);
if (padFoundation != null)
return padFoundation;

oM.Geometry.ICurve locationCurve = familyInstance.LocationCurvePadFoundation(settings);
if (locationCurve == null)
{
BH.Engine.Base.Compute.RecordError($"Failed to extract geometry from pad foundation. ElementId: {familyInstance.Id.Value()}");
return null;
}

oM.Geometry.PlanarSurface planarSurface = null;

if (locationCurve is oM.Geometry.PlanarSurface ps)
{
planarSurface = ps;
}
else if (locationCurve is oM.Geometry.Polyline polyline)
{
planarSurface = BH.Engine.Geometry.Create.PlanarSurface(polyline);
}
else
{
BH.Engine.Base.Compute.RecordError($"Unsupported geometry type for PadFoundation: {locationCurve.GetType().Name}");
return null;
}

FamilySymbol familySymbol = familyInstance.Document.GetElement(familyInstance.GetTypeId()) as FamilySymbol;
oM.Physical.Constructions.Construction construction = familySymbol?.ConstructionFromRevit(settings, refObjects);

BoundingBoxXYZ bbox = familyInstance.get_BoundingBox(null);
double thickness = (bbox.Max.Z - bbox.Min.Z).ToSI(SpecTypeId.Length);

BH.oM.Physical.Materials.Material material = familyInstance.FramingMaterial(settings, refObjects);
construction.Layers.Add(new oM.Physical.Constructions.Layer { Name = construction.Name, Material = material, Thickness = thickness });

padFoundation = BH.Engine.Physical.Create.PadFoundation(planarSurface, construction, familyInstance.FamilyTypeFullName());

if (thickness > 0)
{
List<Point> topPoints = planarSurface.ExternalBoundary.IControlPoints();
if (topPoints[0].Distance(topPoints[topPoints.Count - 1]) < settings.DistanceTolerance)
topPoints.RemoveAt(topPoints.Count - 1);

List<Point> bottomPoints = topPoints.Select(p => new Point { X = p.X, Y = p.Y, Z = p.Z - thickness }).ToList();
if (bottomPoints[0].Distance(bottomPoints[bottomPoints.Count - 1]) > settings.DistanceTolerance)
bottomPoints.Add(bottomPoints[0]);

PlanarSurface bottomSurface = BH.Engine.Geometry.Create.PlanarSurface(new Polyline { ControlPoints = bottomPoints });
RevitGeometry geometryFragment = new RevitGeometry(null, new List<ISurface> { planarSurface, bottomSurface }, null);
padFoundation.Fragments.AddOrReplace(geometryFragment);
}
//Set identifiers, parameters & custom data
padFoundation.SetIdentifiers(familyInstance);
padFoundation.CopyParameters(familyInstance, settings.MappingSettings);
padFoundation.SetProperties(familyInstance, settings.MappingSettings);

refObjects.AddOrReplace(familyInstance.Id, padFoundation);
return padFoundation;
}

/***************************************************/
}
}
Loading