diff --git a/Geometry_Engine/Geometry_Engine.csproj b/Geometry_Engine/Geometry_Engine.csproj index c15e2aba9..12d259093 100644 --- a/Geometry_Engine/Geometry_Engine.csproj +++ b/Geometry_Engine/Geometry_Engine.csproj @@ -150,6 +150,7 @@ + diff --git a/Geometry_Engine/Query/IsOrthogonal.cs b/Geometry_Engine/Query/IsOrthogonal.cs new file mode 100644 index 000000000..250cc2ee1 --- /dev/null +++ b/Geometry_Engine/Query/IsOrthogonal.cs @@ -0,0 +1,54 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2021, 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 BH.oM.Geometry; +using BH.oM.Reflection.Attributes; +using System; +using System.ComponentModel; + +namespace BH.Engine.Geometry +{ + public static partial class Query + { + /***************************************************/ + /**** Public Methods - Vectors ****/ + /***************************************************/ + + [Description("Queries whether a vector is orthogonal in relation to global X, Y or Z axis.")] + [Input("vector", "The vector to evaluate.")] + [Input("angleTolerance", "Optional, the angle discrepancy in radians from the global axis to consider as tolerance.")] + [Output("isOrthogonal", "The boolean value of whether the vector is orthogonal or not.")] + public static bool IsOrthogonal(this Vector vector, double angleTolerance = Tolerance.Angle) + { + if (vector == null || angleTolerance == null) + { + BH.Engine.Reflection.Compute.RecordError("One or more of the inputs is empty or null."); + return false; + } + + return (vector.IsParallel(Vector.XAxis, angleTolerance) != 0 || vector.IsParallel(Vector.YAxis, angleTolerance) != 0 || vector.IsParallel(Vector.ZAxis, angleTolerance) != 0); + } + + /***************************************************/ + + } +} diff --git a/Spatial_Engine/Query/DominantVector.cs b/Spatial_Engine/Query/DominantVector.cs new file mode 100644 index 000000000..45e16f6e8 --- /dev/null +++ b/Spatial_Engine/Query/DominantVector.cs @@ -0,0 +1,139 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2021, 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 BH.Engine.Geometry; +using BH.oM.Geometry; +using BH.oM.Dimensional; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using BH.oM.Reflection.Attributes; + +namespace BH.Engine.Spatial +{ + public static partial class Query + { + /******************************************/ + /**** IElement1D ****/ + /******************************************/ + + [Description("Gets the the dominant vector (orientation) of an Element1D based on its lines lengths.")] + [Input("element1D", "Element1D to evaluate.")] + [Input("orthogonalPriority", "Optional, if true gives priority to curves that are on the orthogonal axis (X, Y or Z vectors).")] + [Input("orthogonalLengthFactor", "Optional, tests the orthogonal vector length's in relation to the actual non-orthogonal dominant vector. For example if the dominant vector is 10 in length but the orthogonal is only 5 in length, then this number should be 0.5 for it to pass the test.")] + [Input("angleTolerance", "Optional, angle in radians that vectors will be considered similar.")] + [Output("dominantVector", "The dominant vector of an Element1D.")] + public static BH.oM.Geometry.Vector DominantVector(this IElement1D element1D, bool orthogonalPriority = true, double orthogonalLengthFactor = 0.5, double angleTolerance = BH.oM.Geometry.Tolerance.Angle) + { + if (element1D == null || orthogonalPriority == null || orthogonalLengthFactor == null || angleTolerance == null) + { + BH.Engine.Reflection.Compute.RecordError("One or more of the inputs is empty or null."); + return null; + } + + List curves = element1D.IGeometry().ISubParts().ToList(); + + if(!curves.Any(x=> x.IIsLinear())) + BH.Engine.Reflection.Compute.RecordWarning("Non-linear curves are using an approximate vector between its start and end."); + + List vectors = curves.Select(x => (x.IStartPoint() -x.IEndPoint())).ToList(); + + //group vectors by direction whilst comparing angle for tolerance + List> groupByNormal = GroupSimilarVectorsWithTolerance(vectors, angleTolerance); + groupByNormal = groupByNormal.OrderByDescending(x => x.Sum(y => y.Length())).ToList(); + List largestGlobal = groupByNormal[0]; + + Vector dominantVector = largestGlobal[0].Normalise(); + if (!orthogonalPriority) + return dominantVector; + + List largestOrthogonal = groupByNormal.FirstOrDefault(x => (x.First().IsOrthogonal(angleTolerance))); + if (largestOrthogonal != null) + { + if (largestGlobal.Sum(x => x.Length()) * orthogonalLengthFactor > largestOrthogonal.Sum(x => x.Length())) + BH.Engine.Reflection.Compute.RecordWarning("Orthogonal vector was found but didn't pass the length tolerance in relation to the actual non-orthogonal dominant vector. The actual dominant vector is the output."); + else + dominantVector = largestOrthogonal[0].Normalise(); + } + + return dominantVector; + } + + /******************************************/ + /**** IElement2D ****/ + /******************************************/ + + [Description("Gets the the dominant vector (orientation) of an Element2D based on its lines lengths.")] + [Input("element2D", "Element2D to evaluate.")] + [Input("orthogonalPriority", "Optional, if true gives priority to curves that are on the orthogonal axis (X, Y or Z vectors.")] + [Input("orthogonalLengthFactor", "Optional, tests the orthogonal vector length's in relation to the actual non-orthogonal dominant vector. For example if the dominant vector is 10 in length but the orthogonal is only 5 in length, then this number should be 0.5 for it to pass the test.")] + [Input("angleTolerance", "Optional, angle in radians that vectors will be considered similar.")] + [Output("dominantVector", "The dominant vector of an Element2D.")] + public static BH.oM.Geometry.Vector DominantVector(this IElement2D element2D, bool orthogonalPriority = true, double orthogonalLengthFactor = 0.5, double angleTolerance = BH.oM.Geometry.Tolerance.Angle) + { + if (element2D == null || orthogonalPriority == null || orthogonalLengthFactor == null || angleTolerance == null) + { + BH.Engine.Reflection.Compute.RecordError("One or more of the inputs is empty or null."); + return null; + } + + IElement1D outline = BH.Engine.Geometry.Create.PolyCurve(element2D.IOutlineElements1D().Select(x =>x.IGeometry())); + return DominantVector(outline, orthogonalPriority, orthogonalLengthFactor, angleTolerance); + } + + /******************************************/ + /**** Private ****/ + /******************************************/ + + [Description("Groups vectors by direction whilst allowing for an angle discrepancy tolerance.")] + [Input("vectors", "Vectors to evaluate.")] + [Input("angleTolerance", "The angle in radians to compare vectors with each other for tolerance when grouping.")] + [Output("GroupSimilarVectorsWithTolerance", "The grouped vectors.")] + private static List> GroupSimilarVectorsWithTolerance(List vectors, double angleTolerance) + { + List> result = new List>(); + List orderByLength = vectors.OrderByDescending(x => x.Length()).ToList(); + + while (orderByLength.Count != 0) + { + List sublist = new List(); + sublist.Add(orderByLength[0]); + + for (int i = orderByLength.Count - 1; i > 0; i--) + { + if (orderByLength[0].IsParallel(orderByLength[i],angleTolerance) != 0) + { + sublist.Add(orderByLength[i]); + orderByLength.RemoveAt(i); + } + } + orderByLength.RemoveAt(0); + result.Add(sublist); + } + + return result; + } + + /******************************************/ + } +} diff --git a/Spatial_Engine/Spatial_Engine.csproj b/Spatial_Engine/Spatial_Engine.csproj index fa089a8af..24ec8848b 100644 --- a/Spatial_Engine/Spatial_Engine.csproj +++ b/Spatial_Engine/Spatial_Engine.csproj @@ -100,6 +100,7 @@ +