Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Structure_Engine: Add engine methods for foundation classes #3138

Merged
merged 35 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
65fc797
Initial commit for Create methods and empty GrahamScan
peterjamesnugent Aug 12, 2023
572dbdc
Update PileGroup.cs
peterjamesnugent Aug 12, 2023
404392c
Update GrahamScan.cs
Maria1129 Aug 14, 2023
12c1da4
Update engine methods to follow changes to PadFoundation object
peterjamesnugent Aug 15, 2023
e9baa08
Add required methods for IElement2D as per https://github.com/BHoM/do…
peterjamesnugent Aug 15, 2023
c487da3
Update OutlineElements1D.cs
peterjamesnugent Aug 15, 2023
476824a
Add VolumetricMaterialTakeOff for PileFoundation
peterjamesnugent Aug 15, 2023
6a6ad9e
Add IsNull methods for foundation objects
peterjamesnugent Aug 15, 2023
6e624bb
Update descriptions and use of GrahamScan for PileFoundation
peterjamesnugent Aug 15, 2023
9bf1b32
Update create methods for PadFoundation and PileFoundation
peterjamesnugent Aug 15, 2023
6553fc2
Add additional checks for GrahamScan method and update descriptions
peterjamesnugent Aug 15, 2023
0f299e5
Update descriptions for GrahamScan
peterjamesnugent Aug 22, 2023
81de384
Response to @pawelbaran 's comments
peterjamesnugent Aug 22, 2023
6bd6c59
Update to reflect changes to oM, adding Pile object and removing Pile…
peterjamesnugent Aug 25, 2023
41517bb
Updates to GrahamScan based on comments
peterjamesnugent Aug 25, 2023
c7eadef
Add required and optional methods for IElement1D and Pile
peterjamesnugent Aug 25, 2023
c1ab7cc
Add VolumetricMaterialTakeOff for new IElementM types
peterjamesnugent Aug 25, 2023
72cef8f
Changes for MaterialTakeOff
peterjamesnugent Aug 29, 2023
8100ffc
Remove duplicate point in GrahamScan
peterjamesnugent Aug 29, 2023
c354a10
Compliance fixes
peterjamesnugent Aug 29, 2023
be7b41f
Documentation-compliance fix
peterjamesnugent Aug 29, 2023
08b354a
Add default values
peterjamesnugent Aug 29, 2023
858456a
Update GrahamScan to allow points in any plane to be analysed
peterjamesnugent Aug 31, 2023
192e39c
Add null handling and update descriptions
peterjamesnugent Aug 31, 2023
81aa95a
Response to @pawelbaran's comments
peterjamesnugent Sep 1, 2023
ec67857
Remove surface/section property comparers and remove HasMergeAbleProp…
peterjamesnugent Sep 1, 2023
c19bde2
Add transform methods and local orientation method for PadFoundation …
peterjamesnugent Sep 1, 2023
c3755f2
Remove VolumetricMaterialTakeOff and add lower level methods for Soli…
peterjamesnugent Sep 1, 2023
241a8a8
Fix documentation-compliance fail
peterjamesnugent Sep 4, 2023
85c1993
Update SetOutlineElements1D.cs
peterjamesnugent Sep 4, 2023
1aea6f3
Formatting
peterjamesnugent Sep 5, 2023
397e47d
Update Transform.cs
peterjamesnugent Sep 5, 2023
52f9ddf
Merge branch 'develop' into Structure_oM-#1543-AddFoundationClasses
peterjamesnugent Sep 5, 2023
fca5811
Unit tests for GrahamScan method
peterjamesnugent Sep 8, 2023
09b4b0b
Fix copyright-compliance failure
peterjamesnugent Sep 8, 2023
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
1 change: 1 addition & 0 deletions .ci/Datasets/Geometry_Engine/Compute/GrahamScan.json

Large diffs are not rendered by default.

181 changes: 181 additions & 0 deletions Geometry_Engine/Compute/GrahamScan.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2023, the respective contributors. All rights reserved.
*
* Each contributor holds copyright over their respective contributions.
* The project versioning (Git) records all such contribution source information.
*
*
* The BHoM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* The BHoM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using BH.oM.Geometry;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
using BH.oM.Base.Attributes;
using BH.oM.Geometry.CoordinateSystem;
using BH.oM.Quantities.Attributes;
using BH.Engine.Base;
using BH.Engine.Geometry;

namespace BH.Engine.Geometry
{
public static partial class Compute
{
/***************************************************/
/**** Public Methods ****/
/***************************************************/

[Description("Implements the GrahamScan algorithm to determine the convex hull of a list of points contained within a single Plane.")]
[Input("pts", "The points to determine the convex hull contained within a single Plane.")]
[Input("tolerance", "Geometrical tolerance to be used in the method.", typeof(Length))]
[Output("c", "The convex hull of the point list, no repeat points are returned.")]
public static List<Point> GrahamScan(List<Point> pts, double tolerance = Tolerance.Distance)
{
if (pts.IsNullOrEmpty())
{
Base.Compute.RecordError("The point list is either null or empty.");
return pts;
}

pts.CullDuplicates(tolerance);

if (pts.IsNullOrEmpty())
{
Base.Compute.RecordError("The point list is null or empty.");
return pts;
}

if (pts.Count < 3)
peterjamesnugent marked this conversation as resolved.
Show resolved Hide resolved
{
Base.Compute.RecordError("The point list (excluding duplicates) must be greater than three to determine the Convex Hull.");
return pts;
}

// Check that the points are all wihtin the same plane
Plane plane = pts.FitPlane(tolerance);
if (!Query.IsInPlane(pts, plane, tolerance))
{
pts.Select(x => x.Project(plane));
Base.Compute.RecordWarning("The points needs to be within a single Plane. They have been projected to the plane that fits the points.");
}

Cartesian origin = new Cartesian();
Cartesian cartesian = null;

bool outOfPlane = 1 - Math.Abs(plane.Normal.DotProduct(Vector.ZAxis)) > tolerance;

// Check if the points are located in the XY plane, otherwise map them to XY
if (outOfPlane)
{
Vector locY = pts.FitLine(tolerance).Direction();
cartesian = Create.CartesianCoordinateSystem(pts.Average(), locY.CrossProduct(plane.Normal), locY);
pts = pts.Select(x => x.Orient(cartesian, origin)).ToList();
}

// Find the point with the lowest Y coordinate
IOrderedEnumerable<Point> orderedPts = pts.OrderBy(pt => pt.Y);

pts = orderedPts.ToList();
Point p = pts[0];

// Check if there is more than one point with the lowest Y
if (Math.Abs(p.Y - pts[1].Y) < tolerance)
{
// Get points with lowest Y coordinate and select the point with the lowest X
pts = orderedPts.ThenBy(x => x.X).ToList();
p = pts.First();
}

// Remove the P from the list of points
pts.Remove(p);

// Calculate the angles between p and each pt
List<double> angles = pts.Select(pt => pt - p).Select(v => v.DotProduct(Vector.XAxis) / v.Length()).ToList();
peterjamesnugent marked this conversation as resolved.
Show resolved Hide resolved

// Combine both lists, sort by angle and then extract the points
pts = pts.Zip(angles, (a, b) => new { pt = a, angle = b }).OrderBy(c => c.angle).Select(x => x.pt).Reverse().ToList();

// Group by angle between P and the points
IEnumerable<IGrouping<double, Point>> groupedPts = pts.GroupBy(pt => Create.Vector(p, pt).DotProduct(Vector.XAxis) / Create.Vector(p, pt).Length());

// Check for points that have the same angle
if (groupedPts.Where(grp => grp.Count() > 1).Any())
{
// For each group, sort by distance from P and select the furthest point
pts = groupedPts.Select(g => g.OrderByDescending(i => i.SquareDistance(p)).First()).ToList();
}

// Add to the start of selPts as it has been removed from pts
List<Point> selPts = new List<Point>() { p };

// Iterate through the algorithim
while (pts.Count > 0)
{
GrahamScanSolver(pts, selPts);
};

// Remap the points to their original orientation
if (outOfPlane)
{
selPts = selPts.Select(x => x.Orient(origin, cartesian)).ToList();
}

return selPts;
}

[Description("Take the first point from pts (p), and the last two points from selPts to determine if the three points form an anticlockwise turn. If the turn is anticlockwise, p is added to selPts, otherwise the " +
"last selPts is removed. This method is used iteratively to determine the convex hull of a point list.")]
private static void GrahamScanSolver(List<Point> pts, List<Point> selPts)
{
if (pts.Count > 0)
{
// Update the reference as the method moves along the curve
var p = pts[0];

// Add the next point along, needs a minimum of two points to begin (the third coming from p)
if (selPts.Count <= 1)
{
selPts.Add(p);
pts.RemoveAt(0);
}
else
{
Point pt1 = selPts[selPts.Count - 1];
Point pt2 = selPts[selPts.Count - 2];
Vector dir1 = pt1 - pt2;
Vector dir2 = p - pt1;

if (Query.CrossProduct(dir1, dir2).Z < 0)
{
// Less than zero, therefore clockwise and the point is within the boundary
selPts.RemoveAt(selPts.Count - 1);
}
else
{
// Greater than zero, therefore anticlockwise - so point is not within the boundary
selPts.Add(p);
pts.RemoveAt(0);
}
}
}
}
}
}




2 changes: 1 addition & 1 deletion Structure_Engine/Convert/PanelToFEMesh.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public static FEMesh PanelToFEMesh(this Panel panel)
}
foreach (Edge edge in edges)
{
ICurve curve = Analytical.Query.Geometry(edge);
ICurve curve = edge.Curve;
points.AddRange(Geometry.Convert.IToPolyline(curve).ControlPoints);
}
int count = points.Distinct().Count();
Expand Down
80 changes: 80 additions & 0 deletions Structure_Engine/Create/Elements/PadFoundation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2023, the respective contributors. All rights reserved.
*
* Each contributor holds copyright over their respective contributions.
* The project versioning (Git) records all such contribution source information.
*
*
* The BHoM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* The BHoM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using BH.oM.Base.Attributes;
using BH.oM.Geometry;
using BH.oM.Geometry.CoordinateSystem;
using BH.oM.Spatial.ShapeProfiles;
using BH.oM.Structure.Elements;
using BH.oM.Structure.MaterialFragments;
using BH.oM.Structure.SurfaceProperties;
using BH.Engine.Base;
using BH.Engine.Geometry;
using BH.Engine.Spatial;


namespace BH.Engine.Structure
{
public static partial class Create
peterjamesnugent marked this conversation as resolved.
Show resolved Hide resolved
{
/***************************************************/
/**** Public Methods ****/
/***************************************************/

[Description("Creates a PadFoundation from an outline, property and orientation angle.")]
[Input("topOutline", "The outer edge of the pad. All section constants are derived based on the dimensions of this.")]
[InputFromProperty("thickness")]
[Input("orientationAngle", "The rotation to be applied the local X of the PadFoundation about the normal of the PadFoundation. This does not affect the geometry but can be used to define prinicpal directions for reinforcement.")]
[Output("padFoundation", "The created PadFoundation with the property and orientation applied.")]
public static PadFoundation PadFoundation(PolyCurve topOutline, ISurfaceProperty thickness = null, double orientationAngle = 0)
{
return topOutline.IsNull() ? null : new PadFoundation() { TopOutline = topOutline, Property = thickness, OrientationAngle = orientationAngle };
}

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

[Description("Creates a rectangular PadFoundation and orients it to the coordinate system provided.")]
[Input("width", "The width of the PadFoundation aligned with Global X.")]
[Input("length", "The length of the PadFoundation aligned with Global Y.")]
[InputFromProperty("thickness")]
[Input("coordinateSystem", "The Cartesian coordinate system to control the position and orientation of the PadFoundation to which the PadFoundation is mapped to.")]
[Input("orientationAngle", "The rotation to be applied the local X of the PadFoundation about the normal of the PadFoundation. This does not affect the geometry but can be used to define prinicpal directions for reinforcement.")]
[Output("padFoundation", "The created PadFoundation with a rectangular outline mapped to the coordinate system provided.")]
public static PadFoundation PadFoundation(double width, double length, ConstantThickness thickness = null, Cartesian coordinateSystem = null, double orientationAngle = 0)
{
PolyCurve topOutline = Spatial.Create.RectangleProfile(length, width).Edges.ToList().IJoin()[0];

if (coordinateSystem == null)
coordinateSystem = new Cartesian();
else
topOutline.Orient(new Cartesian(), coordinateSystem);

return PadFoundation(topOutline, thickness, orientationAngle);
}
}
}
69 changes: 69 additions & 0 deletions Structure_Engine/Create/Elements/Pile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2023, the respective contributors. All rights reserved.
*
* Each contributor holds copyright over their respective contributions.
* The project versioning (Git) records all such contribution source information.
*
*
* The BHoM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* The BHoM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using BH.oM.Base.Attributes;
using BH.oM.Geometry;
using BH.oM.Spatial.Layouts;
using BH.oM.Structure.Elements;
using BH.oM.Structure.SectionProperties;

namespace BH.Engine.Structure
{
public static partial class Create
{
/***************************************************/
/**** Public Methods ****/
/***************************************************/

[Description("Creates a structural Pile. This object can be used with a PileFoundation or as a standalone foundation.")]
[Input("topNode", "The node at the top of the pile.")]
[Input("bottomNode", "The node at the bottom of the pile.")]
[InputFromProperty("pileSection")]
[InputFromProperty("orientationAngle")]
[Output("pile", "The created Pile with a centreline defined by the provided nodes.")]
public static Pile Pile(Node topNode, Node bottomNode, ISectionProperty pileSection = null, double orientationAngle = 0)
{
return topNode.IsNull() || bottomNode.IsNull() ? null : new Pile() { TopNode = topNode, BottomNode = bottomNode, Section = pileSection, OrientationAngle = orientationAngle };
}

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

[Description("Creates a structural Pile. This object can be used with a PileFoundation or as a standalone foundation.")]
[Input("line", "The definining geometry for the pile.")]
[InputFromProperty("pileSection")]
[InputFromProperty("orientationAngle")]
[Output("pile", "The created Pile with a centreline matching the provided geometrical Line.")]
public static Pile Pile(Line line, ISectionProperty pileSection = null, double orientationAngle = 0)
{
return Pile((Node)line.Start, (Node)line.End, pileSection, orientationAngle);
}

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

}
}
Loading