Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7722e70
init commit
WojciechBuda00 Mar 31, 2026
d1fea72
ToRevit PadFoundation added
WojciechBuda00 Mar 31, 2026
058cff8
Rotation and thickness tweaks
WojciechBuda00 Mar 31, 2026
99c2a19
Description added
WojciechBuda00 Apr 1, 2026
a8946d5
Method names tweaks
WojciechBuda00 Apr 2, 2026
deb89be
Naming tweaks
WojciechBuda00 Apr 3, 2026
fa1aae9
Rectangular and non-rectangular foundation tweaks
WojciechBuda00 Apr 14, 2026
74f21be
TriangPad update
WojciechBuda00 Apr 29, 2026
de593b6
Refactor padfoundation create and update
WojciechBuda00 May 14, 2026
1fa59ac
element type generation cleaned up
pawelbaran May 15, 2026
4c75b7b
update cleaned up, SetLocation left
pawelbaran May 15, 2026
d3ac3e9
SetLocation cleaned up
pawelbaran May 15, 2026
8bc34f9
cleanup & comments
pawelbaran May 15, 2026
53ca9ea
Freeform generation and setlocation tweaks
WojciechBuda00 May 20, 2026
d1d13ce
FoundationGeometry and compliance issues tweaks
WojciechBuda00 May 21, 2026
03514b9
LongestEdgeDirection: Vector calc update
WojciechBuda00 May 21, 2026
56cbea4
Clean up
WojciechBuda00 May 22, 2026
a5e1037
PadFoundation placement fixed
WojciechBuda00 May 22, 2026
3e2fc84
Migrated methods clean up
WojciechBuda00 May 22, 2026
b4dae9d
IsRectangle tolerance added
WojciechBuda00 May 22, 2026
4e2b386
Update PadFoundation - tweaks
WojciechBuda00 May 26, 2026
3d69437
Tweaks
WojciechBuda00 May 28, 2026
5374ca7
TransformToOrigin clean up
WojciechBuda00 May 28, 2026
03e54b1
Update type generation and dimension naming
WojciechBuda00 Jun 1, 2026
70331ff
transform to origin fixed
pawelbaran Jun 3, 2026
a8b510b
Rectangular type generation clean-up and freeform extrusion tweak
WojciechBuda00 Jun 8, 2026
e11394e
Rectangular type name rounding: int to long
WojciechBuda00 Jun 8, 2026
31a1ef8
Add unit to rectangular type name
WojciechBuda00 Jun 8, 2026
f16a647
Rectangular pad family clean-up
WojciechBuda00 Jun 9, 2026
b0c914e
Freeform family naming tweaks
WojciechBuda00 Jun 9, 2026
5fa8a47
Generate pad foundation from geometry and name mismatch warning
WojciechBuda00 Jun 9, 2026
f5b4b59
Fix freeform pad family index parsing with regex and ElementType tweak
WojciechBuda00 Jun 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
269 changes: 269 additions & 0 deletions Revit_Core_Engine/Compute/GeneratePadFoundationType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
/*
* 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.Engine.Geometry;
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;
using Extrusion = Autodesk.Revit.DB.Extrusion;

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

[Description("Generates a Revit FamilySymbol for a BHoM PadFoundation by querying existing families in the document or creating a new one from a template file with parametric dimensions.")]
[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 outline of the input BHoM pad foundation.")]
public static FamilySymbol GeneratePadFoundationType(this PadFoundation padFoundation, Document document, RevitSettings settings = null)
{
settings = settings.DefaultIfNull();

bool isRectangle;
Polyline outline = padFoundation?.PadFoundationOutline();
if (outline != null)
isRectangle = outline.IsRectangular(settings.DistanceTolerance);
else
return null;

if (isRectangle)
return GenerateRectangularType(padFoundation, document, settings);
else
return GenerateFreeformType(padFoundation, document, settings);
}

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

private static FamilySymbol GenerateRectangularType(PadFoundation padFoundation, Document document, RevitSettings settings)
{
// Check if family loaded to the document, if not, load it from resources path
string familyName = "StructuralFoundations_Pad-Rectangular";

Family family = new FilteredElementCollector(document).OfClass(typeof(Family)).FirstOrDefault(x => x.Name.EndsWith(familyName)) as Family;
if (family == null)
{
string path = Directory.GetFiles(m_FamilyDirectory, $"*{familyName}.rfa").FirstOrDefault();
if (!File.Exists(path))
return null;

if (!document.LoadFamily(path, out family) || family == null)
return null;
}

// Get dimensions of the pad foundation
(double, double, double) dimensions = padFoundation.RectangularDimensions();
if (double.IsNaN(dimensions.Item1) || double.IsNaN(dimensions.Item2) || double.IsNaN(dimensions.Item3))
return null;
return family.FindOrCreateTypeWithDimensions(dimensions.Item1, dimensions.Item2, dimensions.Item3);
}

/***************************************************/
private static FamilySymbol FindOrCreateTypeWithDimensions(this Family family, double width, double length, double thickness)
{
List<FamilySymbol> symbols = family.GetFamilySymbolIds().Select(id => family.Document.GetElement(id) as FamilySymbol).Where(s => s != null).ToList();

long widthMm = (long)Math.Round(width * 1000.0);
long lengthMm = (long)Math.Round(length * 1000.0);
long depthMm = (long)Math.Round(thickness * 1000.0);
string typeName = $"{widthMm}x{lengthMm}x{depthMm}mm";

FamilySymbol result = symbols.FirstOrDefault(x => x?.Name == typeName);
if (result == null && symbols.Count != 0)
{
result = symbols.FirstOrDefault(x => !(new FilteredElementCollector(family.Document).WherePasses(new FamilyInstanceFilter(family.Document, x.Id)).Any()));
if (result != null)
result.Name = typeName;
else
result = symbols[0].Duplicate(typeName) as FamilySymbol;
result.SetParameter("Width", width);
result.SetParameter("Length", length);
result.SetParameter("Foundation Thickness", thickness);
}
result?.Activate();
return result;
}

/***************************************************/
private static (double, double, double) RectangularDimensions(this PadFoundation padFoundation)
{
Polyline outline = padFoundation.PadFoundationOutline();
double len1 = outline.ControlPoints[0].Distance(outline.ControlPoints[1]);
double len2 = outline.ControlPoints[1].Distance(outline.ControlPoints[2]);
double bhomLength = Math.Max(len1, len2);
double bhomWidth = Math.Min(len1, len2);

return (bhomWidth, bhomLength, padFoundation.PadFoundationThickness());
}

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

private static FamilySymbol GenerateFreeformType(PadFoundation padFoundation, Document document, RevitSettings settings)
{
string prefix = "StructuralFoundations_Pad-Freeform_";

// Get the outline and check if valid
Polyline outline = padFoundation.PadFoundationOutline();
if (outline?.IIsClosed() != true)
{
BH.Engine.Base.Compute.RecordError($"Pad foundation outline is invalid. BHoM_Guid: {padFoundation.BHoM_Guid}");
return null;
}

// Get the thickness and check if valid
double thickness = padFoundation.PadFoundationThickness();
if (double.IsNaN(thickness))
return null;

// Orient the outline to origin
Polyline orientedOutline = outline.OrientToOrigin();
if (orientedOutline == null)
return null;

// Get all BHoM-generated freeform families in the document
List<Family> freeformFamilies = new FilteredElementCollector(document).OfClass(typeof(Family)).Cast<Family>()
.Where(x => Regex.IsMatch(x.Name, $"{prefix}\\d+$")).ToList();

// Try to find a family with matching outline, otherwise create a new one from template
Family family = freeformFamilies.FirstOrDefault(x => x.IsMatchingOutline(orientedOutline, settings));
if (family == null)
{
List<int> 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 = GenerateFreeFormPadFamilyFromTemplate(document, orientedOutline, thickness, newIndex, padFoundation, settings);
}

if (family == null)
return null;

// Find or create the type with matching thickness
return family.FindOrCreateTypeWithThickness(thickness);
}

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

private static Family GenerateFreeFormPadFamilyFromTemplate(this Document document, Polyline orientedOutline, double thickness, int index, PadFoundation padFoundation, RevitSettings settings = null)
{
string templateFamilyName = "StructuralFoundations_Pad-Freeform";
string templatePath = Directory.GetFiles(m_FamilyDirectory, $"*{templateFamilyName}.rfa").FirstOrDefault();

Document familyDocument = new UIDocument(document).Application.Application.OpenDocumentFile(templatePath);
if (familyDocument == null)
return null;

try
{
if (!ReplaceFreeFormExtrusion(familyDocument, orientedOutline, padFoundation))
return null;

return SaveAndLoadFamily(document, familyDocument, $"{Path.GetFileNameWithoutExtension(templatePath)}_{index}");
}
catch (Exception ex)
{
BH.Engine.Base.Compute.RecordError($"Creation of a freeform Revit pad foundation failed with the following error: {ex.Message}");
return null;
}
finally
{
familyDocument.Close(false);
}
}

/***************************************************/
private static double FreeformExtrusionDepth(PadFoundation padFoundation)
{
double depth = padFoundation.PadFoundationThickness();
double h = double.IsNaN(depth) ? double.NaN : depth.FromSI(SpecTypeId.Length);
if (double.IsNaN(h) || h <= 1e-6)
h = 0.5.FromSI(SpecTypeId.Length);
return h;
}

/***************************************************/
private static bool ReplaceFreeFormExtrusion(Document familyDocument, Polyline orientedOutline, PadFoundation padFoundation)
{
try
{
Extrusion extrusion = new FilteredElementCollector(familyDocument).OfClass(typeof(Extrusion)).FirstOrDefault() as Extrusion;
CurveArrArray profile = new CurveArrArray();
profile.Append(orientedOutline.ToRevitCurveArray());

using (Transaction t = new Transaction(familyDocument, "Update Freeform Pad Foundation Footprint"))
{
t.Start();
familyDocument.FamilyCreate.NewExtrusion(true, profile, extrusion.Sketch.SketchPlane, -FreeformExtrusionDepth(padFoundation));
familyDocument.Delete(extrusion.Id);
t.Commit();
}
}
catch
{
return false;
}

return true;
}

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

private static FamilySymbol FindOrCreateTypeWithThickness(this Family family, double thickness)
{
List<FamilySymbol> symbols = family.GetFamilySymbolIds().Select(id => family.Document.GetElement(id) as FamilySymbol).Where(s => s != null).ToList();
if (symbols.Count == 0)
return null;

string typeName = $"{(long)Math.Round(thickness * 1000.0)}mm";
FamilySymbol result = symbols.FirstOrDefault(x => x?.Name == typeName);
if (result == null && symbols.Count != 0)
{
result = symbols.FirstOrDefault(x => !(new FilteredElementCollector(family.Document).WherePasses(new FamilyInstanceFilter(family.Document, x.Id)).Any()));
if (result != null)
result.Name = typeName;
else
result = symbols[0].Duplicate(typeName) as FamilySymbol;

result.SetParameter("Foundation Thickness", thickness);
}

result?.Activate();
return result;

/***************************************************/
}
}
}
9 changes: 4 additions & 5 deletions Revit_Core_Engine/Compute/GenerateProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,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 +129,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 +174,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 +273,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 Expand Up @@ -811,4 +811,3 @@ private static void CopyDimensions(this BH.oM.Spatial.ShapeProfiles.IProfile sou
}



58 changes: 51 additions & 7 deletions Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,56 @@ public static FamilyInstance ToRevitFamilyInstance(this Pile framingElement, Doc
return familyInstance;
}

/***************************************************/
[Description("Converts BH.oM.Physical.Elements.PadFoundation to a Revit FamilyInstance.")]
[Input("padFoundation", "BH.oM.Physical.Elements.PadFoundation to be converted.")]
[Input("document", "Revit document, in which the output of the convert will be created.")]
[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("instance", "Revit FamilyInstance resulting from converting the input BH.oM.Physical.Elements.PadFoundation.")]

public static FamilyInstance ToRevitFamilyInstance(this PadFoundation padFoundation, Document document, RevitSettings settings = null, Dictionary<Guid, List<long>> refObjects = null)
{
if (padFoundation == null || document == null)
return null;

FamilyInstance familyInstance = refObjects.GetValue<FamilyInstance>(document, padFoundation.BHoM_Guid);
if (familyInstance != null)
return familyInstance;

settings = settings.DefaultIfNull();

BH.oM.Geometry.Point origin = padFoundation.PadFoundationCentroid();
if (origin == null)
{
BH.Engine.Base.Compute.RecordError($"PadFoundation boundary extraction failed or foundation is not rectangular. BHoM_Guid: {padFoundation.BHoM_Guid}");
return null;
}

XYZ placementPoint = origin.ToRevit();

Level level = document.LevelAbove(placementPoint.Z, settings);
if (level == null)
return null;

FamilySymbol familySymbol = padFoundation.ElementType(document, settings) as FamilySymbol;
if (familySymbol == null)
{
Compute.ElementTypeNotFoundWarning(padFoundation);
return null;
}

familySymbol.Activate();
familyInstance = document.Create.NewFamilyInstance(placementPoint, familySymbol, level, StructuralType.Footing);
document.Regenerate();

familyInstance.CopyParameters(padFoundation, settings);
familyInstance.SetLocation(padFoundation, settings);

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

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

[Description("Converts BH.oM.Physical.Elements.IFramingElement to a Revit FamilyInstance.")]
Expand Down Expand Up @@ -349,10 +399,4 @@ public static FamilyInstance ToRevitFamilyInstance(this IFramingElement framingE

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






}
Loading