// Copyright (C) Stichting Deltares 2019. All rights reserved. // // This file is part of the Dam Engine. // // The Dam Engine is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . // // All names, logos, and references to "Deltares" are registered trademarks of // Stichting Deltares and remain full property of Stichting Deltares at all times. // All rights reserved. using System; using System.Collections.Generic; using System.Linq; namespace Deltares.DamEngine.Data.Geometry { /// /// Dataclass for the geometryloop. /// /// public class GeometryLoop : GeometryPointString { private readonly List curveList = new List(); /// /// Gets the List of all Curves. /// public List CurveList { get { return curveList; } } /// /// List of points that describe the physical surface line or surface. /// /// This property is not serialized. If you want to add point definitions, /// do so by adding/inserting a new element to . public override List CalcPoints { get { // explicit use of protected field to prevent stack overflow due to recursive call if (calcPoints.Count == 0) { PopulateLoopPointList(); SyncPoints(); } return calcPoints; } } /// /// Determines whether this instance is loop. /// /// public bool IsLoop() { if (CurveList.Count <= 2) { return false; } GeometryCurve beginCurve = curveList[0]; GeometryCurve endCurve = curveList[curveList.Count - 1]; if (beginCurve.HeadPoint == endCurve.HeadPoint || beginCurve.HeadPoint == endCurve.EndPoint || beginCurve.EndPoint == endCurve.HeadPoint || beginCurve.EndPoint == endCurve.EndPoint) { return true; } return false; } /// /// Determines whether this instance has area. /// /// public bool HasArea() { if (CurveList.Count < 3) { return false; } var points = new List(); points.AddRange(Points); points.Add(points[0]); // Calculate area of polygon using Shoelace algorithm: var sum = 0.0; for (int i = 1; i < points.Count; i++) { sum += points[i - 1].X * points[i].Z - points[i - 1].Z * points[i].X; } return Math.Abs(sum) > 1e-6; } /// /// Determines whether [is clock wise]. /// /// /// /// Cannot determine if loop is clockwise if checked location forms a straight line with its neighboring vectors. public bool IsClockWise() { var polyGon = GetLocalPoint2DList(); var isClockWise = Routines2D.IsClockWise(polyGon); if (isClockWise == Clockwise.NotEnoughUniquePoints) { throw new NotEnoughUniquePointsException(); } if (isClockWise == Clockwise.PointsOnLine) { throw new InvalidOperationException("Cannot determine if loop is clockwise if checked location forms a straight line with its neighboring vectors."); } return isClockWise == Clockwise.IsClockwise; } /// /// See if a point lies in a closed surface /// /// /// public bool IsPointInLoopArea(Point2D aPoint) { return Routines2D.CheckIfPointIsInPolygon(this, aPoint.X, aPoint.Z) != PointInPolygon.OutsidePolygon; } /// /// Gets the local 2D Points List. /// /// private List GetLocalPoint2DList() { return CalcPoints;//Points.Select(loopPoint => new Point2D(loopPoint.X, loopPoint.Z)).ToList(); } /// /// Populates the loop's GeometryPoint list. /// private void PopulateLoopPointList() { // explicit use of protected field to prevent stack overflow due to recursive call if (calcPoints.Count > 0) { return; } for (int index = 0; index < curveList.Count; index++) { if (index == 0) { calcPoints.Add(curveList[index].HeadPoint); calcPoints.Add(curveList[index].EndPoint); } else { // TODO why not compare by value instead of reference if (curveList[index].HeadPoint == calcPoints[calcPoints.Count - 1]) { calcPoints.Add(curveList[index].EndPoint); } else if (curveList[index].EndPoint == calcPoints[calcPoints.Count - 1]) { calcPoints.Add(curveList[index].HeadPoint); } else { if (calcPoints.Count == 2) { calcPoints.Reverse(); } if (curveList[index].HeadPoint == calcPoints[calcPoints.Count - 1]) { calcPoints.Add(curveList[index].EndPoint); } else if (curveList[index].EndPoint == calcPoints[calcPoints.Count - 1]) { calcPoints.Add(curveList[index].HeadPoint); } } } } if (calcPoints.Count > 0) { if (calcPoints[0] == calcPoints[calcPoints.Count - 1]) { calcPoints.RemoveAt(calcPoints.Count - 1); } } } /// /// Helper class NotEnoughUniquePointsException /// public class NotEnoughUniquePointsException : InvalidOperationException { /// /// Initializes a new instance of the class. /// public NotEnoughUniquePointsException() : base("At least 3 unique points are required to determine if the loop is running clockwise.") { } } } }