From 534d579a180ee33b3f0f3e7977feb50c5d7d4bf6 Mon Sep 17 00:00:00 2001 From: wojciech buda Date: Thu, 12 Feb 2026 10:52:35 +0100 Subject: [PATCH 01/10] initial commit --- .../Convert/Physical/ToRevit/FamilyInstance.cs | 13 +++++++++++++ Revit_Core_Engine/Convert/ToRevit.cs | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs b/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs index e876bdb4f..e509645ad 100644 --- a/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs +++ b/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs @@ -223,6 +223,19 @@ public static FamilyInstance ToRevitFamilyInstance(this Pile framingElement, Doc /***************************************************/ + [Description("Converts BH.oM.Physical.Elements.PadFoundation to a Revit FamilyInstance.")] + [Input("framingElement", "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 framingElement, Document document, RevitSettings settings = null, Dictionary> refObjects = null) + { + return null; + } + + /***************************************************/ + [Description("Converts BH.oM.Physical.Elements.IFramingElement to a Revit FamilyInstance.")] [Input("framingElement", "BH.oM.Physical.Elements.IFramingElement to be converted.")] [Input("document", "Revit document, in which the output of the convert will be created.")] diff --git a/Revit_Core_Engine/Convert/ToRevit.cs b/Revit_Core_Engine/Convert/ToRevit.cs index cd0357341..d46d343b5 100644 --- a/Revit_Core_Engine/Convert/ToRevit.cs +++ b/Revit_Core_Engine/Convert/ToRevit.cs @@ -273,6 +273,19 @@ public static Element ToRevit(this BH.oM.MEP.System.Pipe pipe, Document document /***************************************************/ + [Description("Converts BH.oM.Physical.Elements.PadFoundation to a Revit FamilyInstance.")] + [Input("foundation", "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 Element ToRevit(this BH.oM.Physical.Elements.PadFoundation foundation, Document document, RevitSettings settings = null, Dictionary> refObjects = null) + { + return foundation.ToRevitFamilyInstance(document, settings, refObjects); + } + + /***************************************************/ + [Description("Converts BH.oM.MEP.System.MaterialFragments.PipeMaterial to a Revit PipeSegment.")] [Input("pipeMaterial", "BH.oM.MEP.System.MaterialFragments.PipeMaterial to be converted.")] [Input("document", "Revit document, in which the output of the convert will be created.")] From 4763c638cc70434f0ac5498544334e1e642105f5 Mon Sep 17 00:00:00 2001 From: wojciech buda Date: Thu, 12 Feb 2026 15:29:03 +0100 Subject: [PATCH 02/10] Update FamilyInstance --- .../Physical/ToRevit/FamilyInstance.cs | 134 +++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs b/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs index e509645ad..7ffa69e29 100644 --- a/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs +++ b/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs @@ -231,7 +231,139 @@ public static FamilyInstance ToRevitFamilyInstance(this Pile framingElement, Doc [Output("instance", "Revit FamilyInstance resulting from converting the input BH.oM.Physical.Elements.PadFoundation.")] public static FamilyInstance ToRevitFamilyInstance(this PadFoundation framingElement, Document document, RevitSettings settings = null, Dictionary> refObjects = null) { - return null; + if (framingElement == null || document == null) + return null; + + FamilyInstance familyInstance = refObjects.GetValue(document, framingElement.BHoM_Guid); + if (familyInstance != null) + return familyInstance; + + settings = settings.DefaultIfNull(); + + List boundary = ExtractBoundary(framingElement); + if (boundary == null || boundary.Count == 0) + { + BH.Engine.Base.Compute.RecordError($"Failed to extract boundary from PadFoundation. BHoM_Guid: {framingElement.BHoM_Guid}"); + return null; + } + + if (!IsRectangular(boundary)) + { + BH.Engine.Base.Compute.RecordError($"PadFoundation must be rectangular. BHoM_Guid: {framingElement.BHoM_Guid}"); + return null; + } + + BH.oM.Geometry.Point centerPoint = CalculateBoundaryCenter(boundary); + + BH.oM.Geometry.CoordinateSystem.Cartesian localCS = new BH.oM.Geometry.CoordinateSystem.Cartesian( + centerPoint, + BH.oM.Geometry.Vector.XAxis, + BH.oM.Geometry.Vector.YAxis, + BH.oM.Geometry.Vector.ZAxis + ); + + FamilySymbol familySymbol = framingElement.ElementType(document, settings) as FamilySymbol; + if (familySymbol == null) + { + Compute.ElementTypeNotFoundWarning(framingElement); + return null; + } + + BH.oM.Geometry.TransformMatrix orientationMatrix = BH.Engine.Geometry.Create.OrientationMatrixGlobalToLocal(localCS); + Transform transform = orientationMatrix.ToRevit().TryFixIfNonConformal(); + + XYZ origin = transform.Origin; + Level level = document.LevelBelow(origin, settings); + if (level == null) + return null; + + familyInstance = document.Create.NewFamilyInstance(origin, familySymbol, level, StructuralType.Footing); + document.Regenerate(); + + familyInstance.CheckIfNullPush(framingElement); + if (familyInstance == null) + return null; + + familyInstance.CopyParameters(framingElement, settings); + familyInstance.SetLocation(framingElement as IFramingElement, settings); + + refObjects.AddOrReplace(framingElement, familyInstance); + return familyInstance; + } + + private static List ExtractBoundary(PadFoundation element) + { + if (element.Location == null) + return null; + + List boundary = new List(); + + if (element.Location is BH.oM.Geometry.PlanarSurface surface && surface.ExternalBoundary != null) + { + boundary.AddRange( + surface.ExternalBoundary + .SelectMany(curve => curve is BH.oM.Geometry.PolyCurve polyCurve ? polyCurve.Curves : new[] { curve }) + .OfType() + ); + } + + return boundary.Count > 0 ? boundary : null; + } + + private static bool IsRectangular(List edges) + { + if (edges == null || edges.Count != 4) + return false; + + double[] lengths = new double[4]; + for (int i = 0; i < 4; i++) + lengths[i] = edges[i].Length(); + + double tolerance = 0.001; + if (Math.Abs(lengths[0] - lengths[2]) > tolerance || Math.Abs(lengths[1] - lengths[3]) > tolerance) + return false; + + BH.oM.Geometry.Point p1 = edges[0].Start; + BH.oM.Geometry.Point p2 = edges[0].End; + BH.oM.Geometry.Point p3 = edges[1].End; + BH.oM.Geometry.Point p4 = edges[2].End; + + var d1 = (p1 - p3).Length(); + var d2 = (p2 - p4).Length(); + + return Math.Abs(d1 - d2) <= tolerance; + } + + private static BH.oM.Geometry.Point CalculateBoundaryCenter(List boundary) + { + if (boundary == null || boundary.Count == 0) + return oM.Geometry.Point.Origin; + + List boundaryPoints = new List(); + foreach (var line in boundary) + { + boundaryPoints.Add(line.Start); + boundaryPoints.Add(line.End); + } + + if (boundaryPoints.Count > 0) + { + double sumX = 0, sumY = 0, sumZ = 0; + foreach (var point in boundaryPoints) + { + sumX += point.X; + sumY += point.Y; + sumZ += point.Z; + } + + return new oM.Geometry.Point( + sumX / boundaryPoints.Count, + sumY / boundaryPoints.Count, + sumZ / boundaryPoints.Count + ); + } + + return BH.oM.Geometry.Point.Origin; } /***************************************************/ From 5eaa792290458988522fd3d8a30e0b8122a46af6 Mon Sep 17 00:00:00 2001 From: wojciech buda Date: Tue, 17 Feb 2026 17:26:37 +0100 Subject: [PATCH 03/10] FoundationGeometry setup --- .../Physical/ToRevit/FamilyInstance.cs | 122 +-------- Revit_Core_Engine/Query/FoundationGeometry.cs | 241 ++++++++++++++++++ 2 files changed, 255 insertions(+), 108 deletions(-) create mode 100644 Revit_Core_Engine/Query/FoundationGeometry.cs diff --git a/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs b/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs index 7ffa69e29..51f673166 100644 --- a/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs +++ b/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs @@ -229,143 +229,49 @@ public static FamilyInstance ToRevitFamilyInstance(this Pile framingElement, Doc [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 framingElement, Document document, RevitSettings settings = null, Dictionary> refObjects = null) + public static FamilyInstance ToRevitFamilyInstance(this PadFoundation padFoundation, Document document, RevitSettings settings = null, Dictionary> refObjects = null) { - if (framingElement == null || document == null) + if (padFoundation == null || document == null) return null; - FamilyInstance familyInstance = refObjects.GetValue(document, framingElement.BHoM_Guid); + FamilyInstance familyInstance = refObjects.GetValue(document, padFoundation.BHoM_Guid); if (familyInstance != null) return familyInstance; settings = settings.DefaultIfNull(); - List boundary = ExtractBoundary(framingElement); - if (boundary == null || boundary.Count == 0) + XYZ origin = padFoundation.GetFoundationOrigin(); + if (origin == null) { - BH.Engine.Base.Compute.RecordError($"Failed to extract boundary from PadFoundation. BHoM_Guid: {framingElement.BHoM_Guid}"); + BH.Engine.Base.Compute.RecordError($"PadFoundation boundary extraction failed or foundation is not rectangular. BHoM_Guid: {padFoundation.BHoM_Guid}"); return null; } - if (!IsRectangular(boundary)) - { - BH.Engine.Base.Compute.RecordError($"PadFoundation must be rectangular. BHoM_Guid: {framingElement.BHoM_Guid}"); + Level level = document.LevelBelow(origin, settings); //LevelAbove? + if (level == null) return null; - } - - BH.oM.Geometry.Point centerPoint = CalculateBoundaryCenter(boundary); - BH.oM.Geometry.CoordinateSystem.Cartesian localCS = new BH.oM.Geometry.CoordinateSystem.Cartesian( - centerPoint, - BH.oM.Geometry.Vector.XAxis, - BH.oM.Geometry.Vector.YAxis, - BH.oM.Geometry.Vector.ZAxis - ); - - FamilySymbol familySymbol = framingElement.ElementType(document, settings) as FamilySymbol; + FamilySymbol familySymbol = padFoundation.ElementType(document, settings) as FamilySymbol; if (familySymbol == null) { - Compute.ElementTypeNotFoundWarning(framingElement); + Compute.ElementTypeNotFoundWarning(padFoundation); return null; } - BH.oM.Geometry.TransformMatrix orientationMatrix = BH.Engine.Geometry.Create.OrientationMatrixGlobalToLocal(localCS); - Transform transform = orientationMatrix.ToRevit().TryFixIfNonConformal(); - - XYZ origin = transform.Origin; - Level level = document.LevelBelow(origin, settings); - if (level == null) - return null; - familyInstance = document.Create.NewFamilyInstance(origin, familySymbol, level, StructuralType.Footing); document.Regenerate(); - familyInstance.CheckIfNullPush(framingElement); + familyInstance.CheckIfNullPush(padFoundation); if (familyInstance == null) return null; - familyInstance.CopyParameters(framingElement, settings); - familyInstance.SetLocation(framingElement as IFramingElement, settings); + familyInstance.CopyParameters(padFoundation, settings); + familyInstance.SetLocation(padFoundation, settings); //setlocation padfoundation - refObjects.AddOrReplace(framingElement, familyInstance); + refObjects.AddOrReplace(padFoundation, familyInstance); return familyInstance; } - private static List ExtractBoundary(PadFoundation element) - { - if (element.Location == null) - return null; - - List boundary = new List(); - - if (element.Location is BH.oM.Geometry.PlanarSurface surface && surface.ExternalBoundary != null) - { - boundary.AddRange( - surface.ExternalBoundary - .SelectMany(curve => curve is BH.oM.Geometry.PolyCurve polyCurve ? polyCurve.Curves : new[] { curve }) - .OfType() - ); - } - - return boundary.Count > 0 ? boundary : null; - } - - private static bool IsRectangular(List edges) - { - if (edges == null || edges.Count != 4) - return false; - - double[] lengths = new double[4]; - for (int i = 0; i < 4; i++) - lengths[i] = edges[i].Length(); - - double tolerance = 0.001; - if (Math.Abs(lengths[0] - lengths[2]) > tolerance || Math.Abs(lengths[1] - lengths[3]) > tolerance) - return false; - - BH.oM.Geometry.Point p1 = edges[0].Start; - BH.oM.Geometry.Point p2 = edges[0].End; - BH.oM.Geometry.Point p3 = edges[1].End; - BH.oM.Geometry.Point p4 = edges[2].End; - - var d1 = (p1 - p3).Length(); - var d2 = (p2 - p4).Length(); - - return Math.Abs(d1 - d2) <= tolerance; - } - - private static BH.oM.Geometry.Point CalculateBoundaryCenter(List boundary) - { - if (boundary == null || boundary.Count == 0) - return oM.Geometry.Point.Origin; - - List boundaryPoints = new List(); - foreach (var line in boundary) - { - boundaryPoints.Add(line.Start); - boundaryPoints.Add(line.End); - } - - if (boundaryPoints.Count > 0) - { - double sumX = 0, sumY = 0, sumZ = 0; - foreach (var point in boundaryPoints) - { - sumX += point.X; - sumY += point.Y; - sumZ += point.Z; - } - - return new oM.Geometry.Point( - sumX / boundaryPoints.Count, - sumY / boundaryPoints.Count, - sumZ / boundaryPoints.Count - ); - } - - return BH.oM.Geometry.Point.Origin; - } - /***************************************************/ [Description("Converts BH.oM.Physical.Elements.IFramingElement to a Revit FamilyInstance.")] diff --git a/Revit_Core_Engine/Query/FoundationGeometry.cs b/Revit_Core_Engine/Query/FoundationGeometry.cs new file mode 100644 index 000000000..73c649f12 --- /dev/null +++ b/Revit_Core_Engine/Query/FoundationGeometry.cs @@ -0,0 +1,241 @@ +/* + * 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 . + */ + +using Autodesk.Revit.DB; +using BH.Engine.Adapters.Revit; +using BH.Engine.Geometry; +using BH.oM.Adapters.Revit.Settings; +using BH.oM.Physical.Elements; +using System; +using System.Collections.Generic; + +namespace BH.Revit.Engine.Core +{ + public static partial class Query + { + + /***************************************************/ + /**** Public methods ****/ + /***************************************************/ + + public static List ExtractBoundary(PadFoundation element) + { + if (element.Location == null) + return null; + + List boundary = new List(); + + if (element.Location is BH.oM.Geometry.PlanarSurface surface && surface.ExternalBoundary != null) + { + var externalCurve = surface.ExternalBoundary; + + if (externalCurve is BH.oM.Geometry.PolyCurve polyCurve) + { + foreach (var curve in polyCurve.Curves) + { + if (curve is BH.oM.Geometry.Line line) + { + boundary.Add(line); + } + } + } + else if (externalCurve is BH.oM.Geometry.Line line) + { + boundary.Add(line); + } + } + + if (boundary.Count == 0) + return null; + + if (!IsRectangular(boundary)) + return null; + + return boundary; + } + + /***************************************************/ + + public static bool IsRectangular(List edges) + { + if (edges == null || edges.Count != 4) + return false; + + double[] lengths = new double[4]; + for (int i = 0; i < 4; i++) + lengths[i] = edges[i].Length(); + + double tolerance = 0.001; + if (Math.Abs(lengths[0] - lengths[2]) > tolerance || Math.Abs(lengths[1] - lengths[3]) > tolerance) + return false; + + BH.oM.Geometry.Point p1 = edges[0].Start; + BH.oM.Geometry.Point p2 = edges[0].End; + BH.oM.Geometry.Point p3 = edges[1].End; + BH.oM.Geometry.Point p4 = edges[2].End; + + var d1 = (p1 - p3).Length(); + var d2 = (p2 - p4).Length(); + + return Math.Abs(d1 - d2) <= tolerance; + } + + /***************************************************/ + + public static double ComputeTransformAngle(BH.oM.Geometry.CoordinateSystem.Cartesian localCS) + { + //basisXY? + if (localCS == null) + return 0.0; + + BH.oM.Geometry.Vector globalXAxis = BH.oM.Geometry.Vector.XAxis; + BH.oM.Geometry.Vector localXAxis = localCS.X; + + BH.oM.Geometry.Vector globalXAxisXY = new BH.oM.Geometry.Vector + { + X = globalXAxis.X, + Y = globalXAxis.Y, + Z = 0.0 + }.Normalise(); + + BH.oM.Geometry.Vector localXAxisXY = new BH.oM.Geometry.Vector + { + X = localXAxis.X, + Y = localXAxis.Y, + Z = 0.0 + }.Normalise(); + + double dot = globalXAxisXY.DotProduct(localXAxisXY); + double cross = globalXAxisXY.CrossProduct(localXAxisXY).Z; + + return Math.Atan2(cross, dot); + } + + /***************************************************/ + + public static double GetThicknessFromConstr(PadFoundation element) + { + BH.oM.Physical.Constructions.Construction constr = element.Construction as oM.Physical.Constructions.Construction; + + if (constr == null || constr.Layers == null || constr.Layers.Count == 0) + return 0.5; + + double totalDepth = 0.0; + foreach (var layer in constr.Layers) + { + totalDepth += layer.Thickness; + } + return totalDepth; + } + + /***************************************************/ + + public static (double width, double length) GetRectangleDimensions(List boundary) + { + if (boundary == null || boundary.Count != 4) + return (0, 0); + + double[] lengths = new double[4]; + for (int i = 0; i < 4; i++) + lengths[i] = boundary[i].Length(); + + double side1 = lengths[0]; + double side2 = lengths[1]; + + double width = Math.Min(side1, side2); + double length = Math.Max(side1, side2); + + return (width, length); + } + + /***************************************************/ + + public static FamilySymbol LoadPadRectangleTemplate(Document document, RevitSettings settings) + { + string familyName = "StructuralFoundations_RectangleProfile"; + string typeName = "Rectangle Foundation"; + + return settings.FamilyLoadSettings.LoadFamilySymbol(document, "Structural Foundations", familyName, typeName); + } + + /***************************************************/ + + public static FamilySymbol GenerateFoundationType(this PadFoundation element, Document document, RevitSettings settings = null) + { + settings = settings.DefaultIfNull(); + + FamilySymbol baseSymbol = LoadPadRectangleTemplate(document, settings); + if (baseSymbol == null) + { + BH.Engine.Base.Compute.RecordError("Could not load rectangular foundation template."); + return null; + } + + string newTypeName = $"PadFoundation_{Guid.NewGuid().ToString().Substring(0, 8)}"; + FamilySymbol foundationSymbol = baseSymbol.Duplicate(newTypeName) as FamilySymbol; + if (foundationSymbol == null) + { + BH.Engine.Base.Compute.RecordError("Could not duplicate foundation family symbol."); + return null; + } + + foundationSymbol.Activate(); + + List boundary = ExtractBoundary(element); + var (width, length) = GetRectangleDimensions(boundary); + double depth = GetThicknessFromConstr(element); + + Parameter widthParam = foundationSymbol.LookupParameter("Width"); + widthParam?.Set(width); + + Parameter lengthParam = foundationSymbol.LookupParameter("Length"); + lengthParam?.Set(length); + + Parameter depthParam = foundationSymbol.LookupParameter("Depth"); + depthParam?.Set(depth); + + return foundationSymbol; + } + + /***************************************************/ + + public static XYZ GetFoundationOrigin(this PadFoundation element) + { + List boundary = ExtractBoundary(element); + if (boundary == null) return null; + + BH.oM.Geometry.Point centerPoint = boundary.Centroid(); + BH.oM.Geometry.CoordinateSystem.Cartesian localCS = new BH.oM.Geometry.CoordinateSystem.Cartesian(centerPoint, BH.oM.Geometry.Vector.XAxis, BH.oM.Geometry.Vector.YAxis, BH.oM.Geometry.Vector.ZAxis); + BH.oM.Geometry.TransformMatrix orientationMatrix = BH.Engine.Geometry.Create.OrientationMatrixGlobalToLocal(localCS); + + Transform transform = orientationMatrix.ToRevit().TryFixIfNonConformal(); + return transform.Origin; + } + } +} + + + + + + + From 245256864388643a962bfe0ceaa53999b7815b13 Mon Sep 17 00:00:00 2001 From: wojciech buda Date: Wed, 18 Feb 2026 12:58:40 +0100 Subject: [PATCH 04/10] Basis support in ComputeTransformAngle added --- .../Physical/ToRevit/FamilyInstance.cs | 4 +-- Revit_Core_Engine/Modify/SetLocation.cs | 7 ++++ Revit_Core_Engine/Query/FoundationGeometry.cs | 33 +++++++------------ 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs b/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs index 51f673166..a0a25a450 100644 --- a/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs +++ b/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs @@ -247,7 +247,7 @@ public static FamilyInstance ToRevitFamilyInstance(this PadFoundation padFoundat return null; } - Level level = document.LevelBelow(origin, settings); //LevelAbove? + Level level = document.LevelAbove(origin, settings); //LevelAbove? if (level == null) return null; @@ -266,7 +266,7 @@ public static FamilyInstance ToRevitFamilyInstance(this PadFoundation padFoundat return null; familyInstance.CopyParameters(padFoundation, settings); - familyInstance.SetLocation(padFoundation, settings); //setlocation padfoundation + familyInstance.SetLocation(padFoundation, settings); //setlocation -> padFoundation added - to check refObjects.AddOrReplace(padFoundation, familyInstance); return familyInstance; diff --git a/Revit_Core_Engine/Modify/SetLocation.cs b/Revit_Core_Engine/Modify/SetLocation.cs index 1fa10dc84..061402d9e 100644 --- a/Revit_Core_Engine/Modify/SetLocation.cs +++ b/Revit_Core_Engine/Modify/SetLocation.cs @@ -343,6 +343,13 @@ public static bool SetLocation(this FamilyInstance element, Pile pile, RevitSett /***************************************************/ + public static bool SetLocation(this FamilyInstance element, PadFoundation padFoundation, RevitSettings settings) + { + return false; + } + + /***************************************************/ + [Description("Sets the location of a given Revit Space based on a given BHoM Space.")] [Input("revitSpace", "Revit Space to be modified.")] [Input("bHoMSpace", "BHoM Space acting as a source of information about the new location.")] diff --git a/Revit_Core_Engine/Query/FoundationGeometry.cs b/Revit_Core_Engine/Query/FoundationGeometry.cs index 73c649f12..b63fdef61 100644 --- a/Revit_Core_Engine/Query/FoundationGeometry.cs +++ b/Revit_Core_Engine/Query/FoundationGeometry.cs @@ -24,6 +24,8 @@ using BH.Engine.Adapters.Revit; using BH.Engine.Geometry; using BH.oM.Adapters.Revit.Settings; +using BH.oM.Geometry; +using BH.oM.Geometry.CoordinateSystem; using BH.oM.Physical.Elements; using System; using System.Collections.Generic; @@ -101,33 +103,20 @@ public static bool IsRectangular(List edges) /***************************************************/ - public static double ComputeTransformAngle(BH.oM.Geometry.CoordinateSystem.Cartesian localCS) + public static double ComputeTransformAngle(Cartesian localCS) { - //basisXY? if (localCS == null) return 0.0; - BH.oM.Geometry.Vector globalXAxis = BH.oM.Geometry.Vector.XAxis; - BH.oM.Geometry.Vector localXAxis = localCS.X; + Basis basis = (Basis)localCS; - BH.oM.Geometry.Vector globalXAxisXY = new BH.oM.Geometry.Vector - { - X = globalXAxis.X, - Y = globalXAxis.Y, - Z = 0.0 - }.Normalise(); - - BH.oM.Geometry.Vector localXAxisXY = new BH.oM.Geometry.Vector - { - X = localXAxis.X, - Y = localXAxis.Y, - Z = 0.0 - }.Normalise(); - - double dot = globalXAxisXY.DotProduct(localXAxisXY); - double cross = globalXAxisXY.CrossProduct(localXAxisXY).Z; - - return Math.Atan2(cross, dot); + var gx = BH.oM.Geometry.Vector.XAxis; // Global X Axis + var lx = basis.X; // Local X Axis + + var global = new BH.oM.Geometry.Vector { X = gx.X, Y = gx.Y, Z = 0 }.Normalise(); + var local = new BH.oM.Geometry.Vector { X = lx.X, Y = lx.Y, Z = 0 }.Normalise(); + + return Math.Atan2(global.CrossProduct(local).Z, global.DotProduct(local)); } /***************************************************/ From f07fcaa92799f5ec99ab33325431ae936d860d55 Mon Sep 17 00:00:00 2001 From: wojciech buda Date: Wed, 18 Feb 2026 17:03:47 +0100 Subject: [PATCH 05/10] SetLocation update --- .../Convert/Physical/ToRevit/FamilyInstance.cs | 4 ++-- Revit_Core_Engine/Modify/SetLocation.cs | 16 +++++++++++++++- Revit_Core_Engine/Query/FoundationGeometry.cs | 15 +++++++++------ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs b/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs index a0a25a450..440fa5c52 100644 --- a/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs +++ b/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs @@ -247,11 +247,11 @@ public static FamilyInstance ToRevitFamilyInstance(this PadFoundation padFoundat return null; } - Level level = document.LevelAbove(origin, settings); //LevelAbove? + Level level = document.LevelAbove(origin.Z, settings); //LevelAbove? if (level == null) return null; - FamilySymbol familySymbol = padFoundation.ElementType(document, settings) as FamilySymbol; + FamilySymbol familySymbol = padFoundation.GenerateFoundationType(document, settings); if (familySymbol == null) { Compute.ElementTypeNotFoundWarning(padFoundation); diff --git a/Revit_Core_Engine/Modify/SetLocation.cs b/Revit_Core_Engine/Modify/SetLocation.cs index 061402d9e..773d1089b 100644 --- a/Revit_Core_Engine/Modify/SetLocation.cs +++ b/Revit_Core_Engine/Modify/SetLocation.cs @@ -28,6 +28,7 @@ using BH.oM.Base.Attributes; using BH.oM.Environment.Elements; using BH.oM.Geometry; +using BH.oM.Geometry.CoordinateSystem; using BH.oM.Physical.Elements; using BH.oM.Physical.FramingProperties; using System; @@ -345,7 +346,20 @@ public static bool SetLocation(this FamilyInstance element, Pile pile, RevitSett public static bool SetLocation(this FamilyInstance element, PadFoundation padFoundation, RevitSettings settings) { - return false; + if (element == null || padFoundation == null) + return false; + + Cartesian coordinateSystem = padFoundation.CartesianCoordinateSystem(); + + if (coordinateSystem == null) + { + BH.Engine.Base.Compute.RecordWarning("CoordinateSystem is invalid."); + return false; + } + + double rotationAngle = coordinateSystem.ComputeRotationAngle(); + + return true; } /***************************************************/ diff --git a/Revit_Core_Engine/Query/FoundationGeometry.cs b/Revit_Core_Engine/Query/FoundationGeometry.cs index b63fdef61..24f157d0f 100644 --- a/Revit_Core_Engine/Query/FoundationGeometry.cs +++ b/Revit_Core_Engine/Query/FoundationGeometry.cs @@ -103,7 +103,7 @@ public static bool IsRectangular(List edges) /***************************************************/ - public static double ComputeTransformAngle(Cartesian localCS) + public static double ComputeRotationAngle(this Cartesian localCS) { if (localCS == null) return 0.0; @@ -126,7 +126,10 @@ public static double GetThicknessFromConstr(PadFoundation element) BH.oM.Physical.Constructions.Construction constr = element.Construction as oM.Physical.Constructions.Construction; if (constr == null || constr.Layers == null || constr.Layers.Count == 0) - return 0.5; + { + BH.Engine.Base.Compute.RecordError("Invalid input"); + return double.NaN; + } double totalDepth = 0.0; foreach (var layer in constr.Layers) @@ -209,12 +212,12 @@ public static FamilySymbol GenerateFoundationType(this PadFoundation element, Do public static XYZ GetFoundationOrigin(this PadFoundation element) { - List boundary = ExtractBoundary(element); + List boundary = ExtractBoundary(element); if (boundary == null) return null; - BH.oM.Geometry.Point centerPoint = boundary.Centroid(); - BH.oM.Geometry.CoordinateSystem.Cartesian localCS = new BH.oM.Geometry.CoordinateSystem.Cartesian(centerPoint, BH.oM.Geometry.Vector.XAxis, BH.oM.Geometry.Vector.YAxis, BH.oM.Geometry.Vector.ZAxis); - BH.oM.Geometry.TransformMatrix orientationMatrix = BH.Engine.Geometry.Create.OrientationMatrixGlobalToLocal(localCS); + oM.Geometry.Point centerPoint = boundary.Centroid(); + Cartesian localCS = new(centerPoint, Vector.XAxis, Vector.YAxis, Vector.ZAxis); + TransformMatrix orientationMatrix = BH.Engine.Geometry.Create.OrientationMatrixGlobalToLocal(localCS); Transform transform = orientationMatrix.ToRevit().TryFixIfNonConformal(); return transform.Origin; From 84cdb2c4c0dfe45e480b34e5b4c2a1187b880e3c Mon Sep 17 00:00:00 2001 From: wojciech buda Date: Mon, 23 Feb 2026 11:59:10 +0100 Subject: [PATCH 06/10] Improve SetLocation and foundation angle calc --- .../Physical/ToRevit/FamilyInstance.cs | 2 +- Revit_Core_Engine/Modify/SetLocation.cs | 68 ++++++++++++++++++- Revit_Core_Engine/Query/FoundationGeometry.cs | 5 ++ 3 files changed, 71 insertions(+), 4 deletions(-) diff --git a/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs b/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs index 440fa5c52..93a7c7c28 100644 --- a/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs +++ b/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs @@ -247,7 +247,7 @@ public static FamilyInstance ToRevitFamilyInstance(this PadFoundation padFoundat return null; } - Level level = document.LevelAbove(origin.Z, settings); //LevelAbove? + Level level = document.LevelAbove(origin.Z, settings); if (level == null) return null; diff --git a/Revit_Core_Engine/Modify/SetLocation.cs b/Revit_Core_Engine/Modify/SetLocation.cs index 773d1089b..b87f8adff 100644 --- a/Revit_Core_Engine/Modify/SetLocation.cs +++ b/Revit_Core_Engine/Modify/SetLocation.cs @@ -22,6 +22,7 @@ using Autodesk.Revit.DB; using BH.Engine.Geometry; +using BH.Engine.Spatial; using BH.oM.Adapters.Revit.Elements; using BH.oM.Adapters.Revit.Settings; using BH.oM.Base; @@ -343,21 +344,82 @@ public static bool SetLocation(this FamilyInstance element, Pile pile, RevitSett } /***************************************************/ - + + //TO DO + //Teset script with rectangles (create & update) for push & pull public static bool SetLocation(this FamilyInstance element, PadFoundation padFoundation, RevitSettings settings) { if (element == null || padFoundation == null) return false; - Cartesian coordinateSystem = padFoundation.CartesianCoordinateSystem(); - + //CS + oM.Geometry.Point centerPoint = padFoundation.Centroid(); + Cartesian coordinateSystem = BH.Engine.Geometry.Create.CartesianCoordinateSystem(centerPoint, Vector.XAxis, Vector.YAxis); if (coordinateSystem == null) { BH.Engine.Base.Compute.RecordWarning("CoordinateSystem is invalid."); return false; } + // 1) Compare padFoundation vs element + LocationPoint elementLocation = element.Location as LocationPoint; + if (elementLocation == null) + { + BH.Engine.Base.Compute.RecordError($"Element does not have a valid LocationPoint. ElementId: {element.Id.Value()}"); + return false; + } + + XYZ currentLocation = elementLocation.Point; + XYZ targetLocation = coordinateSystem.Origin.ToRevit(); + + //oM.Geometry.Point currentPoint = elementLocation.Point.PointFromRevit(); + //oM.Geometry.Point targetPoint = coordinateSystem.Origin; + // 2) If locations !=, -> MoveElement() + XYZ translation = targetLocation.Subtract(currentLocation); + //oM.Geometry.Vector translationVector = targetPoint - currentPoint; + //XYZ translation = translationVector.ToRevit(); + if (translation.GetLength() > settings.DistanceTolerance) + { + using (Transaction transaction = new Transaction(element.Document, "Move Element")) + { + transaction.Start(); + ElementTransformUtils.MoveElement(element.Document, element.Id, translation); + transaction.Commit(); + } + } + + // 3) ElementTransformUtils.RotateElement(); double rotationAngle = coordinateSystem.ComputeRotationAngle(); + if (Math.Abs(rotationAngle) > settings.AngleTolerance) + { + using (Transaction transaction = new Transaction(element.Document, "Rotate Element")) + { + transaction.Start(); + Autodesk.Revit.DB.Line rotationAxis = Autodesk.Revit.DB.Line.CreateBound( + targetLocation, targetLocation.Add(XYZ.BasisZ)); // BH.Engine.Geometry.Create.Line() - check if it can be used instead + /* + oM.Geometry.Line rotationAxisBhom = BH.Engine.Geometry.Create.Line(targetPoint, targetPoint + oM.Geometry.Vector.ZAxis); + Autodesk.Revit.DB.Line rotationAxis = rotationAxisBhom.ToRevit(); + */ + ElementTransformUtils.RotateElement(element.Document, element.Id, rotationAxis, rotationAngle); + transaction.Commit(); + } + } + + // 4) Handle reference level, offset + Level level = element.Document.GetElement(element.LevelId) as Level; + if (level != null) + { + double levelElevation = level.ProjectElevation.ToSI(SpecTypeId.Length); + double targetZ = coordinateSystem.Origin.Z; + double offset = targetZ - levelElevation; + + Parameter baseOffsetParam = element.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM); + if (baseOffsetParam != null && !baseOffsetParam.IsReadOnly) + { + baseOffsetParam.Set(offset.FromSI(SpecTypeId.Length)); + } + } return true; } diff --git a/Revit_Core_Engine/Query/FoundationGeometry.cs b/Revit_Core_Engine/Query/FoundationGeometry.cs index 24f157d0f..dcbc5fc27 100644 --- a/Revit_Core_Engine/Query/FoundationGeometry.cs +++ b/Revit_Core_Engine/Query/FoundationGeometry.cs @@ -112,10 +112,13 @@ public static double ComputeRotationAngle(this Cartesian localCS) var gx = BH.oM.Geometry.Vector.XAxis; // Global X Axis var lx = basis.X; // Local X Axis + //if (1 - Math.Abs(gx.DotProduct(lx)) <= Tolerance.Angle) + // gx = Vector.YAxis; var global = new BH.oM.Geometry.Vector { X = gx.X, Y = gx.Y, Z = 0 }.Normalise(); var local = new BH.oM.Geometry.Vector { X = lx.X, Y = lx.Y, Z = 0 }.Normalise(); + //return Math.Atan2(Vector.ZAxis, global.DotProduct(local)); return Math.Atan2(global.CrossProduct(local).Z, global.DotProduct(local)); } @@ -216,6 +219,8 @@ public static XYZ GetFoundationOrigin(this PadFoundation element) if (boundary == null) return null; oM.Geometry.Point centerPoint = boundary.Centroid(); + return centerPoint.ToRevit(); //? + Cartesian localCS = new(centerPoint, Vector.XAxis, Vector.YAxis, Vector.ZAxis); TransformMatrix orientationMatrix = BH.Engine.Geometry.Create.OrientationMatrixGlobalToLocal(localCS); From e54574ee7dfdf768f6f1aa9d2690eab1fb7c1050 Mon Sep 17 00:00:00 2001 From: wojciech buda Date: Wed, 25 Feb 2026 10:23:04 +0100 Subject: [PATCH 07/10] Generate PadFoundation families --- .../Compute/GeneratePadFoundation.cs | 275 ++++++++++++++++++ .../Physical/ToRevit/FamilyInstance.cs | 6 +- Revit_Core_Engine/Modify/SetLocation.cs | 68 ++++- Revit_Core_Engine/Query/BuiltInCategories.cs | 6 + Revit_Core_Engine/Query/ElementType.cs | 19 ++ Revit_Core_Engine/Query/FoundationGeometry.cs | 14 +- 6 files changed, 366 insertions(+), 22 deletions(-) create mode 100644 Revit_Core_Engine/Compute/GeneratePadFoundation.cs diff --git a/Revit_Core_Engine/Compute/GeneratePadFoundation.cs b/Revit_Core_Engine/Compute/GeneratePadFoundation.cs new file mode 100644 index 000000000..52b5159d6 --- /dev/null +++ b/Revit_Core_Engine/Compute/GeneratePadFoundation.cs @@ -0,0 +1,275 @@ +/* + * 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 . + */ + +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.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 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 SaveAndLoadFamily(Document document, Document familyDocument, string familyName, PadFoundation padFoundation, RevitSettings settings) //clean duplicate IBhom ob ject etc. + { + Family result = null; + string tempFolder = Path.GetTempPath(); + string tempLocation = SanitizePathPadFoundation($"{tempFolder}\\{familyName}.rfa"); + + try + { + if (!Directory.Exists(tempFolder)) + Directory.CreateDirectory(tempFolder); + + SaveAsOptions saveOptions = new SaveAsOptions(); + saveOptions.OverwriteExistingFile = true; + familyDocument.SaveAs(tempLocation, saveOptions); + } + catch (Exception ex) + { + BH.Engine.Base.Compute.RecordError($"Creation of a freeform Revit profile geometry failed because the family could not be temporarily saved in {tempFolder}. Please make sure the folder exists and you have access to it, then try to empty it in case the issue persists."); + return null; + } + + document.LoadFamily(tempLocation, out result); + + try + { + File.Delete(tempLocation); + } + catch (Exception ex) + { + BH.Engine.Base.Compute.RecordNote($"File {tempLocation} could not be deleted."); + } + + return result; + } + + /***************************************************/ + + private static string SanitizePathPadFoundation(string inputPath) //clean duplicate + { + char[] invalidPathChars = Path.GetInvalidPathChars(); + char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); + char[] allInvalidChars = invalidPathChars.Concat(invalidFileNameChars).Where(x => x != '\\' && x != ':').Distinct().ToArray(); + + foreach (char c in allInvalidChars) + { + inputPath = inputPath.Replace(c.ToString(), ""); + } + + return inputPath; + } + + /***************************************************/ + 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, padFoundation, settings); + } + 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 + { + // Add pad foundation name with StructuralFoundations prefix + Regex pattern = new Regex(@"\d([\d\.\/\-xX ])*\d"); + return $"StructuralFoundations_{pattern.Replace(name, "").Replace(" ", " ").Trim()}"; + } + } + + /***************************************************/ + + private static string PadFoundationTypeName(this PadFoundation padFoundation) //clean up duplicate + { + 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) + { + List boundary = padFoundation.ExtractBoundary(); + if (boundary == null) + { + BH.Engine.Base.Compute.RecordError($"PadFoundation boundary extraction failed. BHoM_Guid: {padFoundation.BHoM_Guid}"); + return; + } + + var (width, length) = boundary.GetRectangleDimensions(); + double depth = padFoundation.GetThicknessFromConstr(); + + Parameter widthParam = targetSymbol.LookupParameter("Width"); + if (widthParam != null) + widthParam.Set(width); + + Parameter lengthParam = targetSymbol.LookupParameter("Length"); + if (lengthParam != null) + lengthParam.Set(length); + + Parameter depthParam = targetSymbol.LookupParameter("Depth"); + if (depthParam != null) + depthParam.Set(depth); + } + + /***************************************************/ + } +} + + + diff --git a/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs b/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs index 93a7c7c28..13304512e 100644 --- a/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs +++ b/Revit_Core_Engine/Convert/Physical/ToRevit/FamilyInstance.cs @@ -224,7 +224,7 @@ public static FamilyInstance ToRevitFamilyInstance(this Pile framingElement, Doc /***************************************************/ [Description("Converts BH.oM.Physical.Elements.PadFoundation to a Revit FamilyInstance.")] - [Input("framingElement", "BH.oM.Physical.Elements.PadFoundation to be converted.")] + [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.")] @@ -251,7 +251,7 @@ public static FamilyInstance ToRevitFamilyInstance(this PadFoundation padFoundat if (level == null) return null; - FamilySymbol familySymbol = padFoundation.GenerateFoundationType(document, settings); + FamilySymbol familySymbol = padFoundation.ElementType(document, settings); if (familySymbol == null) { Compute.ElementTypeNotFoundWarning(padFoundation); @@ -266,7 +266,7 @@ public static FamilyInstance ToRevitFamilyInstance(this PadFoundation padFoundat return null; familyInstance.CopyParameters(padFoundation, settings); - familyInstance.SetLocation(padFoundation, settings); //setlocation -> padFoundation added - to check + familyInstance.SetLocation(padFoundation, settings); refObjects.AddOrReplace(padFoundation, familyInstance); return familyInstance; diff --git a/Revit_Core_Engine/Modify/SetLocation.cs b/Revit_Core_Engine/Modify/SetLocation.cs index b87f8adff..90420c780 100644 --- a/Revit_Core_Engine/Modify/SetLocation.cs +++ b/Revit_Core_Engine/Modify/SetLocation.cs @@ -352,10 +352,59 @@ public static bool SetLocation(this FamilyInstance element, PadFoundation padFou if (element == null || padFoundation == null) return false; - //CS + //CS oM.Geometry.Point centerPoint = padFoundation.Centroid(); - Cartesian coordinateSystem = BH.Engine.Geometry.Create.CartesianCoordinateSystem(centerPoint, Vector.XAxis, Vector.YAxis); - if (coordinateSystem == null) + + Cartesian coordinateLocalSystem = null; + try + { + List boundary = padFoundation.ExtractBoundary(); + if (boundary != null && boundary.Count >= 4) + { + // Find the longest edge that is perpendicular to edges + oM.Geometry.Line longestPerpendicularEdge = null; + double maxLength = 0; + + for (int i = 0; i < boundary.Count; i++) + { + oM.Geometry.Line currentEdge = boundary[i]; + double currentLength = currentEdge.Length(); + + oM.Geometry.Line prevEdge = boundary[(i - 1 + boundary.Count) % boundary.Count]; + oM.Geometry.Line nextEdge = boundary[(i + 1) % boundary.Count]; + + Vector prevDirection = prevEdge.Direction(); + Vector nextDirection = nextEdge.Direction(); + Vector currentDirection = currentEdge.Direction(); + + double prevDot = Math.Abs(prevDirection.DotProduct(currentDirection)); + double nextDot = Math.Abs(nextDirection.DotProduct(currentDirection)); + + bool isPerpendicular = prevDot < 0.1 && nextDot < 0.1; + + if (isPerpendicular && currentLength > maxLength) + { + longestPerpendicularEdge = currentEdge; + maxLength = currentLength; + } + } + + if (longestPerpendicularEdge != null) + { + Vector xAxis = longestPerpendicularEdge.Direction().Normalise(); + Vector zAxis = Vector.ZAxis; + Vector yAxis = zAxis.CrossProduct(xAxis).Normalise(); + + coordinateLocalSystem = BH.Engine.Geometry.Create.CartesianCoordinateSystem(centerPoint, xAxis, yAxis); + } + } + } + catch (Exception ex) + { + BH.Engine.Base.Compute.RecordError($"Failed to create local coordinate system for PadFoundation."); + } + + if (coordinateLocalSystem == null) { BH.Engine.Base.Compute.RecordWarning("CoordinateSystem is invalid."); return false; @@ -370,10 +419,10 @@ public static bool SetLocation(this FamilyInstance element, PadFoundation padFou } XYZ currentLocation = elementLocation.Point; - XYZ targetLocation = coordinateSystem.Origin.ToRevit(); + XYZ targetLocation = coordinateLocalSystem.Origin.ToRevit(); //oM.Geometry.Point currentPoint = elementLocation.Point.PointFromRevit(); - //oM.Geometry.Point targetPoint = coordinateSystem.Origin; + //oM.Geometry.Point targetPoint = coordinateLocalSystem.Origin; // 2) If locations !=, -> MoveElement() XYZ translation = targetLocation.Subtract(currentLocation); //oM.Geometry.Vector translationVector = targetPoint - currentPoint; @@ -389,8 +438,9 @@ public static bool SetLocation(this FamilyInstance element, PadFoundation padFou } // 3) ElementTransformUtils.RotateElement(); - double rotationAngle = coordinateSystem.ComputeRotationAngle(); - if (Math.Abs(rotationAngle) > settings.AngleTolerance) + double rotationAngleNew = coordinateLocalSystem.ComputeRotationAngle(); + double rotationAngleOld = element.CoordinateSystem().ComputeRotationAngle(); + if (Math.Abs(rotationAngleNew) > settings.AngleTolerance) { using (Transaction transaction = new Transaction(element.Document, "Rotate Element")) { @@ -401,7 +451,7 @@ public static bool SetLocation(this FamilyInstance element, PadFoundation padFou oM.Geometry.Line rotationAxisBhom = BH.Engine.Geometry.Create.Line(targetPoint, targetPoint + oM.Geometry.Vector.ZAxis); Autodesk.Revit.DB.Line rotationAxis = rotationAxisBhom.ToRevit(); */ - ElementTransformUtils.RotateElement(element.Document, element.Id, rotationAxis, rotationAngle); + ElementTransformUtils.RotateElement(element.Document, element.Id, rotationAxis, rotationAngleNew); transaction.Commit(); } } @@ -411,7 +461,7 @@ public static bool SetLocation(this FamilyInstance element, PadFoundation padFou if (level != null) { double levelElevation = level.ProjectElevation.ToSI(SpecTypeId.Length); - double targetZ = coordinateSystem.Origin.Z; + double targetZ = coordinateLocalSystem.Origin.Z; double offset = targetZ - levelElevation; Parameter baseOffsetParam = element.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM); diff --git a/Revit_Core_Engine/Query/BuiltInCategories.cs b/Revit_Core_Engine/Query/BuiltInCategories.cs index 53f726c67..8386e7406 100644 --- a/Revit_Core_Engine/Query/BuiltInCategories.cs +++ b/Revit_Core_Engine/Query/BuiltInCategories.cs @@ -118,6 +118,12 @@ public static HashSet BuiltInCategories(this Type bHoMType) Autodesk.Revit.DB.BuiltInCategory.OST_StructuralFoundation } }, + { + typeof (BH.oM.Physical.Elements.PadFoundation), new BuiltInCategory[] + { + Autodesk.Revit.DB.BuiltInCategory.OST_StructuralFoundation, + } + }, { typeof (Column), new BuiltInCategory[] { diff --git a/Revit_Core_Engine/Query/ElementType.cs b/Revit_Core_Engine/Query/ElementType.cs index 636da66b8..6baade4a0 100644 --- a/Revit_Core_Engine/Query/ElementType.cs +++ b/Revit_Core_Engine/Query/ElementType.cs @@ -78,6 +78,25 @@ public static FamilySymbol ElementType(this BH.oM.Physical.Elements.IFramingElem /***************************************************/ + [Description("Returns the Revit element type to be used when converting a given BHoM pad foundation to Revit.")] + [Input("padFoundation", "BHoM pad foundation to find a correspondent Revit element type for.")] + [Input("document", "Revit document to parse in search for the element type.")] + [Input("settings", "Revit adapter settings to be used while performing the query.")] + [Output("padFoundationType", "Revit element type to be used when converting the input BHoM pad foundation to Revit.")] + public static FamilySymbol ElementType(this BH.oM.Physical.Elements.PadFoundation padFoundation, Document document, RevitSettings settings = null) + { + HashSet categories = padFoundation.BuiltInCategories(); + + FamilySymbol result = padFoundation.ElementTypeInclProperty(null, document, categories, settings) as FamilySymbol; + + if (result == null) + result = padFoundation.GeneratePadFoundation(document, settings); + + return result; + } + + /***************************************************/ + [Description("Returns the Revit element type to be used when converting a given BHoM IInstance to Revit.")] [Input("instance", "BHoM IInstance to find a correspondent Revit element type for.")] [Input("document", "Revit document to parse in search for the element type.")] diff --git a/Revit_Core_Engine/Query/FoundationGeometry.cs b/Revit_Core_Engine/Query/FoundationGeometry.cs index dcbc5fc27..f4cb41bd8 100644 --- a/Revit_Core_Engine/Query/FoundationGeometry.cs +++ b/Revit_Core_Engine/Query/FoundationGeometry.cs @@ -39,7 +39,7 @@ public static partial class Query /**** Public methods ****/ /***************************************************/ - public static List ExtractBoundary(PadFoundation element) + public static List ExtractBoundary(this PadFoundation element) { if (element.Location == null) return null; @@ -124,7 +124,7 @@ public static double ComputeRotationAngle(this Cartesian localCS) /***************************************************/ - public static double GetThicknessFromConstr(PadFoundation element) + public static double GetThicknessFromConstr(this PadFoundation element) { BH.oM.Physical.Constructions.Construction constr = element.Construction as oM.Physical.Constructions.Construction; @@ -144,7 +144,7 @@ public static double GetThicknessFromConstr(PadFoundation element) /***************************************************/ - public static (double width, double length) GetRectangleDimensions(List boundary) + public static (double width, double length) GetRectangleDimensions(this List boundary) { if (boundary == null || boundary.Count != 4) return (0, 0); @@ -219,13 +219,7 @@ public static XYZ GetFoundationOrigin(this PadFoundation element) if (boundary == null) return null; oM.Geometry.Point centerPoint = boundary.Centroid(); - return centerPoint.ToRevit(); //? - - Cartesian localCS = new(centerPoint, Vector.XAxis, Vector.YAxis, Vector.ZAxis); - TransformMatrix orientationMatrix = BH.Engine.Geometry.Create.OrientationMatrixGlobalToLocal(localCS); - - Transform transform = orientationMatrix.ToRevit().TryFixIfNonConformal(); - return transform.Origin; + return centerPoint.ToRevit(); } } } From ed9840262870fcf626c8340cec97a4d0996f87eb Mon Sep 17 00:00:00 2001 From: wojciech buda Date: Thu, 26 Feb 2026 16:51:23 +0100 Subject: [PATCH 08/10] PadFoundation switch to Polyline --- .../Compute/GeneratePadFoundation.cs | 74 ++------- Revit_Core_Engine/Compute/GenerateProfile.cs | 3 +- Revit_Core_Engine/Modify/SetLocation.cs | 95 ++++++------ Revit_Core_Engine/Query/FoundationGeometry.cs | 145 +++++++++++++----- 4 files changed, 164 insertions(+), 153 deletions(-) diff --git a/Revit_Core_Engine/Compute/GeneratePadFoundation.cs b/Revit_Core_Engine/Compute/GeneratePadFoundation.cs index 52b5159d6..af097f86c 100644 --- a/Revit_Core_Engine/Compute/GeneratePadFoundation.cs +++ b/Revit_Core_Engine/Compute/GeneratePadFoundation.cs @@ -25,6 +25,7 @@ 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; @@ -106,58 +107,6 @@ public static FamilySymbol GeneratePadFoundation(this PadFoundation padFoundatio /**** Private methods ****/ /***************************************************/ - private static Family SaveAndLoadFamily(Document document, Document familyDocument, string familyName, PadFoundation padFoundation, RevitSettings settings) //clean duplicate IBhom ob ject etc. - { - Family result = null; - string tempFolder = Path.GetTempPath(); - string tempLocation = SanitizePathPadFoundation($"{tempFolder}\\{familyName}.rfa"); - - try - { - if (!Directory.Exists(tempFolder)) - Directory.CreateDirectory(tempFolder); - - SaveAsOptions saveOptions = new SaveAsOptions(); - saveOptions.OverwriteExistingFile = true; - familyDocument.SaveAs(tempLocation, saveOptions); - } - catch (Exception ex) - { - BH.Engine.Base.Compute.RecordError($"Creation of a freeform Revit profile geometry failed because the family could not be temporarily saved in {tempFolder}. Please make sure the folder exists and you have access to it, then try to empty it in case the issue persists."); - return null; - } - - document.LoadFamily(tempLocation, out result); - - try - { - File.Delete(tempLocation); - } - catch (Exception ex) - { - BH.Engine.Base.Compute.RecordNote($"File {tempLocation} could not be deleted."); - } - - return result; - } - - /***************************************************/ - - private static string SanitizePathPadFoundation(string inputPath) //clean duplicate - { - char[] invalidPathChars = Path.GetInvalidPathChars(); - char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); - char[] allInvalidChars = invalidPathChars.Concat(invalidFileNameChars).Where(x => x != '\\' && x != ':').Distinct().ToArray(); - - foreach (char c in allInvalidChars) - { - inputPath = inputPath.Replace(c.ToString(), ""); - } - - return inputPath; - } - - /***************************************************/ private static Family GenerateFamilyFromTemplate(this Document document, PadFoundation padFoundation, string familyName, RevitSettings settings = null) { string templateFamilyName = padFoundation.PadFoundationFamilyName(); @@ -172,7 +121,7 @@ private static Family GenerateFamilyFromTemplate(this Document document, PadFoun try { - result = SaveAndLoadFamily(document, familyDocument, familyName, padFoundation, settings); + result = SaveAndLoadFamily(document, familyDocument, familyName, padFoundation, settings);// in GenereateProfile.cs in SaveAndLoadFamily - IFraming changed to IBHoMObject } catch (Exception ex) { @@ -213,15 +162,14 @@ private static string PadFoundationFamilyName(this PadFoundation padFoundation) return name.Split(':')[0].Trim(); else { - // Add pad foundation name with StructuralFoundations prefix Regex pattern = new Regex(@"\d([\d\.\/\-xX ])*\d"); - return $"StructuralFoundations_{pattern.Replace(name, "").Replace(" ", " ").Trim()}"; + return $"BHE_StructuralFoundations_{pattern.Replace(name, "").Replace(" ", " ").Trim()}"; } } /***************************************************/ - private static string PadFoundationTypeName(this PadFoundation padFoundation) //clean up duplicate + private static string PadFoundationTypeName(this PadFoundation padFoundation) { string name = padFoundation?.Name; if (string.IsNullOrWhiteSpace(name)) @@ -244,25 +192,25 @@ private static void ICopyFoundationDimensions(this PadFoundation padFoundation, private static void CopyFoundationDimensions(this PadFoundation padFoundation, FamilySymbol targetSymbol, RevitSettings settings = null) { - List boundary = padFoundation.ExtractBoundary(); - if (boundary == null) + Polyline outline = padFoundation.ExtractBoundary(); + if (outline == null) { - BH.Engine.Base.Compute.RecordError($"PadFoundation boundary extraction failed. BHoM_Guid: {padFoundation.BHoM_Guid}"); + BH.Engine.Base.Compute.RecordError($"PadFoundation outline extraction failed. BHoM_Guid: {padFoundation.BHoM_Guid}"); return; } - var (width, length) = boundary.GetRectangleDimensions(); + var (width, length) = outline.GetRectangleDimensions(); double depth = padFoundation.GetThicknessFromConstr(); - Parameter widthParam = targetSymbol.LookupParameter("Width"); + Parameter widthParam = targetSymbol.LookupParameter("BHE_Width"); if (widthParam != null) widthParam.Set(width); - Parameter lengthParam = targetSymbol.LookupParameter("Length"); + Parameter lengthParam = targetSymbol.LookupParameter("BHE_Length"); if (lengthParam != null) lengthParam.Set(length); - Parameter depthParam = targetSymbol.LookupParameter("Depth"); + Parameter depthParam = targetSymbol.LookupParameter("BHE_Depth"); if (depthParam != null) depthParam.Set(depth); } diff --git a/Revit_Core_Engine/Compute/GenerateProfile.cs b/Revit_Core_Engine/Compute/GenerateProfile.cs index d9948f03d..a87a9d83c 100644 --- a/Revit_Core_Engine/Compute/GenerateProfile.cs +++ b/Revit_Core_Engine/Compute/GenerateProfile.cs @@ -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; @@ -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 familyDocument, string familyName, IBHoMObject bHoMObject, RevitSettings settings) { Family result = null; string tempFolder = Path.GetTempPath(); diff --git a/Revit_Core_Engine/Modify/SetLocation.cs b/Revit_Core_Engine/Modify/SetLocation.cs index 90420c780..9ef50c8e7 100644 --- a/Revit_Core_Engine/Modify/SetLocation.cs +++ b/Revit_Core_Engine/Modify/SetLocation.cs @@ -353,60 +353,30 @@ public static bool SetLocation(this FamilyInstance element, PadFoundation padFou return false; //CS - oM.Geometry.Point centerPoint = padFoundation.Centroid(); + oM.Geometry.Point centerPoint = padFoundation.Centroid(); //error check ! (create.cs)/ physical engine + if (centerPoint == null) + { + BH.Engine.Base.Compute.RecordError($"Failed to calculate centroid for PadFoundation. BHoM_Guid: {padFoundation.BHoM_Guid}"); + return false; + } Cartesian coordinateLocalSystem = null; try { - List boundary = padFoundation.ExtractBoundary(); - if (boundary != null && boundary.Count >= 4) + Polyline boundary = padFoundation.ExtractBoundary(); + if (boundary != null) { - // Find the longest edge that is perpendicular to edges - oM.Geometry.Line longestPerpendicularEdge = null; - double maxLength = 0; - - for (int i = 0; i < boundary.Count; i++) - { - oM.Geometry.Line currentEdge = boundary[i]; - double currentLength = currentEdge.Length(); - - oM.Geometry.Line prevEdge = boundary[(i - 1 + boundary.Count) % boundary.Count]; - oM.Geometry.Line nextEdge = boundary[(i + 1) % boundary.Count]; - - Vector prevDirection = prevEdge.Direction(); - Vector nextDirection = nextEdge.Direction(); - Vector currentDirection = currentEdge.Direction(); - - double prevDot = Math.Abs(prevDirection.DotProduct(currentDirection)); - double nextDot = Math.Abs(nextDirection.DotProduct(currentDirection)); - - bool isPerpendicular = prevDot < 0.1 && nextDot < 0.1; - - if (isPerpendicular && currentLength > maxLength) - { - longestPerpendicularEdge = currentEdge; - maxLength = currentLength; - } - } - - if (longestPerpendicularEdge != null) - { - Vector xAxis = longestPerpendicularEdge.Direction().Normalise(); - Vector zAxis = Vector.ZAxis; - Vector yAxis = zAxis.CrossProduct(xAxis).Normalise(); - - coordinateLocalSystem = BH.Engine.Geometry.Create.CartesianCoordinateSystem(centerPoint, xAxis, yAxis); - } + coordinateLocalSystem = boundary.LocalCs(); } } catch (Exception ex) { - BH.Engine.Base.Compute.RecordError($"Failed to create local coordinate system for PadFoundation."); + BH.Engine.Base.Compute.RecordError($"Failed to create local coordinate system for PadFoundation. BHoM_Guid: {padFoundation.BHoM_Guid}"); } - if (coordinateLocalSystem == null) + if (coordinateLocalSystem == null || coordinateLocalSystem.Origin == null) { - BH.Engine.Base.Compute.RecordWarning("CoordinateSystem is invalid."); + BH.Engine.Base.Compute.RecordWarning($"CoordinateSystem is invalid. BHoM_Guid: {padFoundation.BHoM_Guid}"); return false; } @@ -418,7 +388,7 @@ public static bool SetLocation(this FamilyInstance element, PadFoundation padFou return false; } - XYZ currentLocation = elementLocation.Point; + XYZ currentLocation = elementLocation.Point; XYZ targetLocation = coordinateLocalSystem.Origin.ToRevit(); //oM.Geometry.Point currentPoint = elementLocation.Point.PointFromRevit(); @@ -447,10 +417,7 @@ public static bool SetLocation(this FamilyInstance element, PadFoundation padFou transaction.Start(); Autodesk.Revit.DB.Line rotationAxis = Autodesk.Revit.DB.Line.CreateBound( targetLocation, targetLocation.Add(XYZ.BasisZ)); // BH.Engine.Geometry.Create.Line() - check if it can be used instead - /* - oM.Geometry.Line rotationAxisBhom = BH.Engine.Geometry.Create.Line(targetPoint, targetPoint + oM.Geometry.Vector.ZAxis); - Autodesk.Revit.DB.Line rotationAxis = rotationAxisBhom.ToRevit(); - */ + ElementTransformUtils.RotateElement(element.Document, element.Id, rotationAxis, rotationAngleNew); transaction.Commit(); } @@ -474,6 +441,40 @@ public static bool SetLocation(this FamilyInstance element, PadFoundation padFou return true; } + //check + private static Cartesian LocalCs(this Polyline outline) + { + if (outline == null) + return null; + + var subParts = outline.SubParts(); + if (subParts == null) + return null; + + Dictionary lenByDir = new Dictionary(); + + foreach (var ln in subParts) + { + var len = ln.Length(); + var dir = ln.Direction(); + + var key = lenByDir.Keys.FirstOrDefault(l => Math.Abs(Math.Abs(l.DotProduct(dir)) - 1) <= Tolerance.Angle); + + if (key != null) + lenByDir[key] += len; + else + lenByDir[dir] = len; + } + + if (lenByDir.Count == 0) + return null; + + var x = lenByDir.OrderByDescending(kvp => kvp.Value).First().Key; + var y = new Vector { X = -x.Y, Y = x.X }; + + return BH.Engine.Geometry.Create.CartesianCoordinateSystem(outline.Centroid(), x, y); + } + /***************************************************/ [Description("Sets the location of a given Revit Space based on a given BHoM Space.")] diff --git a/Revit_Core_Engine/Query/FoundationGeometry.cs b/Revit_Core_Engine/Query/FoundationGeometry.cs index f4cb41bd8..ddb8620e6 100644 --- a/Revit_Core_Engine/Query/FoundationGeometry.cs +++ b/Revit_Core_Engine/Query/FoundationGeometry.cs @@ -23,12 +23,14 @@ 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.Geometry; using BH.oM.Geometry.CoordinateSystem; using BH.oM.Physical.Elements; using System; using System.Collections.Generic; +using System.Linq; namespace BH.Revit.Engine.Core { @@ -39,49 +41,45 @@ public static partial class Query /**** Public methods ****/ /***************************************************/ - public static List ExtractBoundary(this PadFoundation element) + public static Polyline ExtractBoundary(this PadFoundation element) { if (element.Location == null) return null; - List boundary = new List(); - - if (element.Location is BH.oM.Geometry.PlanarSurface surface && surface.ExternalBoundary != null) + if (element.Location.InternalBoundaries.Count != 0) { - var externalCurve = surface.ExternalBoundary; - - if (externalCurve is BH.oM.Geometry.PolyCurve polyCurve) - { - foreach (var curve in polyCurve.Curves) - { - if (curve is BH.oM.Geometry.Line line) - { - boundary.Add(line); - } - } - } - else if (externalCurve is BH.oM.Geometry.Line line) - { - boundary.Add(line); - } + BH.Engine.Base.Compute.RecordError("InternalBoundaries error"); } - if (boundary.Count == 0) - return null; - - if (!IsRectangular(boundary)) - return null; + List segments = element.Location.ExternalBoundary.ISubParts().ToList(); + if (segments.Any(x => !(x is BH.oM.Geometry.Line))) + { + BH.Engine.Base.Compute.RecordError(""); + } - return boundary; + return BH.Engine.Geometry.Create.Polyline(segments.Cast().ToList()); } /***************************************************/ - public static bool IsRectangular(List edges) + public static bool IsRectangular(Polyline polyline) { - if (edges == null || edges.Count != 4) + if (polyline == null || polyline.ControlPoints == null) return false; + var points = polyline.ControlPoints; + int pointCount = points.Count; + + //Reecrtangle check closing point + if (pointCount != 5) + return false; + + List edges = new List(); + for (int i = 0; i < 4; i++) + { + edges.Add(new BH.oM.Geometry.Line { Start = points[i], End = points[(i + 1) % pointCount] }); + } + double[] lengths = new double[4]; for (int i = 0; i < 4; i++) lengths[i] = edges[i].Length(); @@ -144,14 +142,27 @@ public static double GetThicknessFromConstr(this PadFoundation element) /***************************************************/ - public static (double width, double length) GetRectangleDimensions(this List boundary) + public static (double width, double length) GetRectangleDimensions(this Polyline polyline) { - if (boundary == null || boundary.Count != 4) - return (0, 0); + if (polyline == null || polyline.ControlPoints == null) + { + BH.Engine.Base.Compute.RecordWarning("Cannot get rectangle dimensions: polyline or control points are null."); + } + + var points = polyline.ControlPoints; + int pointCount = points.Count; + + if (pointCount != 5) + { + BH.Engine.Base.Compute.RecordWarning("Cannot get rectangle dimensions: polyline or control points are null."); + } double[] lengths = new double[4]; for (int i = 0; i < 4; i++) - lengths[i] = boundary[i].Length(); + { + var line = new BH.oM.Geometry.Line { Start = points[i], End = points[(i + 1) % pointCount] }; + lengths[i] = line.Length(); + } double side1 = lengths[0]; double side2 = lengths[1]; @@ -166,8 +177,8 @@ public static (double width, double length) GetRectangleDimensions(this List boundary = ExtractBoundary(element); - var (width, length) = GetRectangleDimensions(boundary); + Polyline outline = ExtractBoundary(element); + if (outline == null || !IsRectangular(outline)) + { + BH.Engine.Base.Compute.RecordError("Foundation outline must be rectangular."); + return null; + } + + var (width, length) = GetRectangleDimensions(outline); double depth = GetThicknessFromConstr(element); - Parameter widthParam = foundationSymbol.LookupParameter("Width"); + Parameter widthParam = foundationSymbol.LookupParameter("BHE_Width"); widthParam?.Set(width); - Parameter lengthParam = foundationSymbol.LookupParameter("Length"); + Parameter lengthParam = foundationSymbol.LookupParameter("BHE_Length"); lengthParam?.Set(length); - Parameter depthParam = foundationSymbol.LookupParameter("Depth"); + Parameter depthParam = foundationSymbol.LookupParameter("BHE_Depth"); depthParam?.Set(depth); return foundationSymbol; @@ -215,10 +232,54 @@ public static FamilySymbol GenerateFoundationType(this PadFoundation element, Do public static XYZ GetFoundationOrigin(this PadFoundation element) { - List boundary = ExtractBoundary(element); - if (boundary == null) return null; + Polyline outline = ExtractBoundary(element); + if (outline == null) + return null; + + // Validate the outline shape + if (outline.ControlPoints == null) + return null; + + var points = outline.ControlPoints; + int pointCount = points.Count; + + if (pointCount != 5) + { + BH.Engine.Base.Compute.RecordError($"Foundation outline should have exactly 5 points for a rectangle, but has {pointCount}."); + return null; + } + + // Check if the shape is closed + if (points[0].Distance(points[4]) > BH.oM.Geometry.Tolerance.Distance) + { + BH.Engine.Base.Compute.RecordError("Foundation outline is not properly closed."); + return null; + } + + List outlineLines = new List(); + for (int i = 0; i < 4; i++) + { + outlineLines.Add(new oM.Geometry.Line { Start = points[i], End = points[i + 1] }); + } + + //;check + oM.Geometry.Point centerPoint = new oM.Geometry.Point(); + + try + { + centerPoint = outlineLines.Centroid(); + } + catch (Exception ex) + { + BH.Engine.Base.Compute.RecordError("Error msg:" + ex.Message); + } + //oM.Geometry.Point centerPoint = outlineLines.Centroid(); + if (centerPoint == null) + { + BH.Engine.Base.Compute.RecordError("Could not calculate centroid of foundation outline."); + return null; + } - oM.Geometry.Point centerPoint = boundary.Centroid(); return centerPoint.ToRevit(); } } From b701af09e1f9eecba8a876b701313be36cd85366 Mon Sep 17 00:00:00 2001 From: wojciech buda Date: Thu, 26 Feb 2026 17:56:27 +0100 Subject: [PATCH 09/10] Centroid and SaveAndLoadFamily fixed --- .../Compute/GeneratePadFoundation.cs | 2 +- Revit_Core_Engine/Compute/GenerateProfile.cs | 8 +++--- Revit_Core_Engine/Modify/SetLocation.cs | 7 +---- Revit_Core_Engine/Query/FoundationGeometry.cs | 27 +++---------------- 4 files changed, 10 insertions(+), 34 deletions(-) diff --git a/Revit_Core_Engine/Compute/GeneratePadFoundation.cs b/Revit_Core_Engine/Compute/GeneratePadFoundation.cs index af097f86c..182e78247 100644 --- a/Revit_Core_Engine/Compute/GeneratePadFoundation.cs +++ b/Revit_Core_Engine/Compute/GeneratePadFoundation.cs @@ -121,7 +121,7 @@ private static Family GenerateFamilyFromTemplate(this Document document, PadFoun try { - result = SaveAndLoadFamily(document, familyDocument, familyName, padFoundation, settings);// in GenereateProfile.cs in SaveAndLoadFamily - IFraming changed to IBHoMObject + result = SaveAndLoadFamily(document, familyDocument, familyName); } catch (Exception ex) { diff --git a/Revit_Core_Engine/Compute/GenerateProfile.cs b/Revit_Core_Engine/Compute/GenerateProfile.cs index a87a9d83c..38d623b90 100644 --- a/Revit_Core_Engine/Compute/GenerateProfile.cs +++ b/Revit_Core_Engine/Compute/GenerateProfile.cs @@ -117,7 +117,7 @@ public static FamilySymbol GenerateProfile(this IFramingElement element, Documen /**** Private methods ****/ /***************************************************/ - private static Family SaveAndLoadFamily(Document document, Document familyDocument, string familyName, IBHoMObject bHoMObject, RevitSettings settings) + private static Family SaveAndLoadFamily(Document document, Document templateDocument, string familyName) { Family result = null; string tempFolder = Path.GetTempPath(); @@ -130,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) { @@ -175,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) { @@ -274,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) { diff --git a/Revit_Core_Engine/Modify/SetLocation.cs b/Revit_Core_Engine/Modify/SetLocation.cs index 9ef50c8e7..82b16824d 100644 --- a/Revit_Core_Engine/Modify/SetLocation.cs +++ b/Revit_Core_Engine/Modify/SetLocation.cs @@ -353,7 +353,7 @@ public static bool SetLocation(this FamilyInstance element, PadFoundation padFou return false; //CS - oM.Geometry.Point centerPoint = padFoundation.Centroid(); //error check ! (create.cs)/ physical engine + oM.Geometry.Point centerPoint = padFoundation.Centroid(); if (centerPoint == null) { BH.Engine.Base.Compute.RecordError($"Failed to calculate centroid for PadFoundation. BHoM_Guid: {padFoundation.BHoM_Guid}"); @@ -391,12 +391,7 @@ public static bool SetLocation(this FamilyInstance element, PadFoundation padFou XYZ currentLocation = elementLocation.Point; XYZ targetLocation = coordinateLocalSystem.Origin.ToRevit(); - //oM.Geometry.Point currentPoint = elementLocation.Point.PointFromRevit(); - //oM.Geometry.Point targetPoint = coordinateLocalSystem.Origin; - // 2) If locations !=, -> MoveElement() XYZ translation = targetLocation.Subtract(currentLocation); - //oM.Geometry.Vector translationVector = targetPoint - currentPoint; - //XYZ translation = translationVector.ToRevit(); if (translation.GetLength() > settings.DistanceTolerance) { using (Transaction transaction = new Transaction(element.Document, "Move Element")) diff --git a/Revit_Core_Engine/Query/FoundationGeometry.cs b/Revit_Core_Engine/Query/FoundationGeometry.cs index ddb8620e6..8db760fa9 100644 --- a/Revit_Core_Engine/Query/FoundationGeometry.cs +++ b/Revit_Core_Engine/Query/FoundationGeometry.cs @@ -233,11 +233,8 @@ public static FamilySymbol GenerateFoundationType(this PadFoundation element, Do public static XYZ GetFoundationOrigin(this PadFoundation element) { Polyline outline = ExtractBoundary(element); - if (outline == null) - return null; - // Validate the outline shape - if (outline.ControlPoints == null) + if (outline?.ControlPoints == null) return null; var points = outline.ControlPoints; @@ -250,30 +247,14 @@ public static XYZ GetFoundationOrigin(this PadFoundation element) } // Check if the shape is closed - if (points[0].Distance(points[4]) > BH.oM.Geometry.Tolerance.Distance) + if (!outline.IsClosed( BH.oM.Geometry.Tolerance.Distance)) { BH.Engine.Base.Compute.RecordError("Foundation outline is not properly closed."); return null; } - List outlineLines = new List(); - for (int i = 0; i < 4; i++) - { - outlineLines.Add(new oM.Geometry.Line { Start = points[i], End = points[i + 1] }); - } - - //;check - oM.Geometry.Point centerPoint = new oM.Geometry.Point(); - - try - { - centerPoint = outlineLines.Centroid(); - } - catch (Exception ex) - { - BH.Engine.Base.Compute.RecordError("Error msg:" + ex.Message); - } - //oM.Geometry.Point centerPoint = outlineLines.Centroid(); + var centerPoint = outline.Centroid(); + if (centerPoint == null) { BH.Engine.Base.Compute.RecordError("Could not calculate centroid of foundation outline."); From e598b42fb2a9f3373c760b44a5a5b93e31b08b78 Mon Sep 17 00:00:00 2001 From: wojciech buda Date: Fri, 27 Feb 2026 16:56:28 +0100 Subject: [PATCH 10/10] From Revit init --- .../Physical/FromRevit/PadFoundation.cs | 77 +++++++++++++++++++ Revit_Core_Engine/Query/BHoMType.cs | 3 +- Revit_Core_Engine/Query/FoundationGeometry.cs | 1 - Revit_Core_Engine/Query/LocationCurve.cs | 21 +++++ 4 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 Revit_Core_Engine/Convert/Physical/FromRevit/PadFoundation.cs diff --git a/Revit_Core_Engine/Convert/Physical/FromRevit/PadFoundation.cs b/Revit_Core_Engine/Convert/Physical/FromRevit/PadFoundation.cs new file mode 100644 index 000000000..7ae9caa1b --- /dev/null +++ b/Revit_Core_Engine/Convert/Physical/FromRevit/PadFoundation.cs @@ -0,0 +1,77 @@ +/* + * 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 . + */ + +using Autodesk.Revit.DB; +using BH.Engine.Adapters.Revit; +using BH.oM.Adapters.Revit.Settings; +using BH.oM.Base; +using BH.oM.Physical.Elements; +using BH.oM.Physical.FramingProperties; +using BH.oM.Base.Attributes; +using System.Collections.Generic; +using System.ComponentModel; + +namespace BH.Revit.Engine.Core +{ + public static partial class Convert + { + /***************************************************/ + /**** Public Methods ****/ + /***************************************************/ + + [Description("Converts a Revit FamilyInstance to BH.oM.Physical.Elements..")] + [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.Pile resulting from converting the input Revit FamilyInstance.")] + public static PadFoundation PadFoundationFromRevit(this FamilyInstance familyInstance, RevitSettings settings = null, Dictionary> refObjects = null) + { + settings = settings.DefaultIfNull(); + + PadFoundation padFoundation = refObjects.GetValue(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; + } + + BoundingBoxXYZ bbox = familyInstance.get_BoundingBox(null); + double depth = bbox.Max.Z - bbox.Min.Z; + + padFoundation = BH.Engine.Physical.Create.PadFoundation((oM.Geometry.PlanarSurface)locationCurve, null, familyInstance.FamilyTypeFullName()); + + //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; + } + + /***************************************************/ + } +} diff --git a/Revit_Core_Engine/Query/BHoMType.cs b/Revit_Core_Engine/Query/BHoMType.cs index 9f28b534e..c42b3298e 100644 --- a/Revit_Core_Engine/Query/BHoMType.cs +++ b/Revit_Core_Engine/Query/BHoMType.cs @@ -96,8 +96,7 @@ public static Type BHoMType(this FamilyInstance familyInstance, Discipline disci double width = Math.Max(bbox.Max.X - bbox.Min.X, bbox.Max.Y - bbox.Min.Y); double height = bbox.Max.Z - bbox.Min.Z; if (height < width) - //return typeof(BH.oM.Physical.Elements.PadFoundation); - return null; // Temporarily disabled until PadFoundation is properly implemented in the Revit adapter. + return typeof(BH.oM.Physical.Elements.PadFoundation); else if (familyInstance.GetSubComponentIds().Count != 0) //return typeof(BH.oM.Physical.Elements.PileFoundation); return null; // Temporarily disabled until PileFoundation is properly implemented in the Revit adapter. diff --git a/Revit_Core_Engine/Query/FoundationGeometry.cs b/Revit_Core_Engine/Query/FoundationGeometry.cs index 8db760fa9..b7a64b865 100644 --- a/Revit_Core_Engine/Query/FoundationGeometry.cs +++ b/Revit_Core_Engine/Query/FoundationGeometry.cs @@ -23,7 +23,6 @@ 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.Geometry; using BH.oM.Geometry.CoordinateSystem; diff --git a/Revit_Core_Engine/Query/LocationCurve.cs b/Revit_Core_Engine/Query/LocationCurve.cs index 0023b7504..71571344f 100644 --- a/Revit_Core_Engine/Query/LocationCurve.cs +++ b/Revit_Core_Engine/Query/LocationCurve.cs @@ -246,6 +246,27 @@ public static BH.oM.Geometry.Line LocationCurvePile(this FamilyInstance familyIn } /***************************************************/ + + [Description("Queries a FamilyInstance to find its top face boundary curve, intended usage for Pad Foundations.")] + [Input("familyInstance", "Revit FamilyInstance to be queried.")] + [Input("settings", "Optional, the RevitSettings to be used.")] + [Output("locationCurvePadFoundation", "BHoM curve representing the top boundary of the pad foundation.")] + public static BH.oM.Geometry.ICurve LocationCurvePadFoundation(this FamilyInstance familyInstance, RevitSettings settings = null) + { + settings = settings.DefaultIfNull(); + + BoundingBoxXYZ bbox = familyInstance.get_BoundingBox(null); + if (bbox == null) return null; + + oM.Geometry.Point p1 = new XYZ(bbox.Min.X, bbox.Min.Y, bbox.Max.Z).PointFromRevit(); + oM.Geometry.Point p2 = new XYZ(bbox.Max.X, bbox.Min.Y, bbox.Max.Z).PointFromRevit(); + oM.Geometry.Point p3 = new XYZ(bbox.Max.X, bbox.Max.Y, bbox.Max.Z).PointFromRevit(); + oM.Geometry.Point p4 = new XYZ(bbox.Min.X, bbox.Max.Y, bbox.Max.Z).PointFromRevit(); + + return new BH.oM.Geometry.Polyline { ControlPoints = new List { p1, p2, p3, p4, p1 } }; + } + + /***************************************************/ } }