// Copyright (C) Stichting Deltares 2024. 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; using Deltares.DamEngine.Data.Standard; using Deltares.DamEngine.Data.Standard.Language; namespace Deltares.DamEngine.Data.Geometry; /// /// Class containing the geometry data /// /// public class GeometryData : GeometryObject { private readonly GeometryPointString surfaceLine = new(); private GeometryGenerator geometryGenerator; private bool isRegeneratingGeometry; private bool updatingSurfaceLine; /// /// Initializes a new instance of the class. /// public GeometryData() { geometryGenerator = null; } /// /// Ordered list of all geometry points at the surface /// public GeometryPointString SurfaceLine { get { if (surfaceLine.CalcPoints.Count == 0 && Points.Count > 0) { UpdateSurfaceLine(); } return surfaceLine; } } /// /// Deletes the point and the curves it belongs too. /// /// The point. public void DeletePointWithCurves(Point2D point) { var curvesToDelete = new List(); foreach (GeometryCurve curve in Curves) { if (curve.ContainsPoint(point)) { curvesToDelete.Add(curve); } } foreach (GeometryCurve curveToDelete in curvesToDelete) { Curves.Remove(curveToDelete); } Points.Remove(point); } /// /// Deletes all the loose points. /// public void DeleteLoosePoints() { foreach (Point2D geometryPoint in Points.ToArray()) { if (GetDependentCurveCount(geometryPoint) == 0) { Remove(geometryPoint); } } } /// /// Deletes all the loose curves. /// Returns true when the loose curve is inside publisherEventArgs surface. /// Calls Regeneration if the function returns true. /// public void DeleteLooseCurves() { SynchronizeLoops(); if (geometryGenerator == null) { geometryGenerator = new GeometryGenerator(this); } geometryGenerator.SetupCurveSurfaceAssociations(); var regenerateGeometry = false; var curvesToDelete = new List(); foreach (GeometryCurve curve in Curves) { if (curve.SurfaceAtLeft == null && curve.SurfaceAtRight == null) { curvesToDelete.Add(curve); } else if (curve.SurfaceAtLeft != null && curve.SurfaceAtRight != null && (curve.SurfaceAtLeft == curve.SurfaceAtRight)) { regenerateGeometry = true; curvesToDelete.Add(curve); } } foreach (GeometryCurve curve in curvesToDelete) { DeleteCurve(curve, false); } if (regenerateGeometry) { RegenerateGeometry(); } } /// /// Deletes the curve if the aValidate is true. /// /// The curve to delete /// Indicates whether the validation was successful public void DeleteCurve(GeometryCurve geometryCurve, bool validate) { GeometryCurve curve = geometryCurve; if (validate) { if (GetDependentCurveCount(curve.HeadPoint) <= 1 && Points.Contains(curve.HeadPoint)) { Remove(curve.HeadPoint); } if (GetDependentCurveCount(curve.EndPoint) <= 1 && Points.Contains(curve.EndPoint)) { Remove(curve.EndPoint); } Remove(geometryCurve); } else { Remove(geometryCurve); } } /// /// Synchronizes the loops. /// private void SynchronizeLoops() { DeleteAllLoops(); foreach (GeometrySurface surface in Surfaces) { // As real donuts (or holes in geom) are not allowed, there can be no innerloop that // is NOT an outerloop for another surface. So no need to sync innerloops. if (surface.OuterLoop != null && surface.OuterLoop.IsLoop()) { Loops.Add(surface.OuterLoop); } } } /// /// Finds the point at location. /// /// Point location to be found. /// The point at the location; if not found returns null. public Point2D GetPointAtLocation(Point2D point2D) { for (var i = 0; i < Points.Count; i++) { if (Routines2D.DetermineIfPointsCoincide(point2D.X, point2D.Z, Points[i].X, Points[i].Z, GeometryConstants.Accuracy)) { return Points[i]; } } return null; } /// /// Updates the line at the top of the geometry /// public void UpdateSurfaceLine() { if (updatingSurfaceLine) { return; } updatingSurfaceLine = true; List bCurves = GetBoundaryCurves(); if (bCurves.Count == 0) { surfaceLine.CalcPoints.Clear(); } List curvesCopy = GetCurvesCopy(bCurves); List curves = GetTopCurves(curvesCopy); CreateSurfaceLinePointString(curves); updatingSurfaceLine = false; } /// /// Creates a point and adds it to the proper lists when needed. /// /// /// public Point2D CreatePointAndAddItToTheProperListsWhenNeeded(Point2D requestedPoint) { Point2D newPoint = AddRequestedPointAsNewPointWhenNeeded(requestedPoint, out bool isExistingPoint); if (!NewlyEffectedPoints.Contains(newPoint) && !isExistingPoint) { NewlyEffectedPoints.Add(newPoint); } return newPoint; } /// /// Deletes the point and the curves it belongs too when curves are 0------point------0. /// /// public void DeletePointAndPossibleObsoleteCurveItsPartOf(Point2D point) { var source = new List(); if (Curves.Count > 0) { // Check if TWO curves are in line with each other and the point is on both curves. // If so, the point is not needed and can be removed from the curves too. source.AddRange(Curves.Where((Func) (c => c.HeadPoint == point || c.EndPoint == point))); if (source.Count == 2) { CheckForCurvesWherePointIsOnBothConnectedCurvesInLineWithEachOtherAndFixThem(point, source); } Remove(point); foreach (GeometryCurve aCurve in source) { DeleteCurve(aCurve, true); } return; } Remove(point); } /// /// Creates a curve between the two points if no such curve already exists. /// /// /// /// public GeometryCurve CreateCurve(Point2D point1, Point2D point2) { foreach (GeometryCurve curve in Curves) { if (curve.HeadPoint == point1 && curve.EndPoint == point2 || curve.HeadPoint == point2 && curve.EndPoint == point1) { return curve; } } var curve1 = new GeometryCurve(); if (geometryGenerator != null) { geometryGenerator.SetIsUsed(curve1, CurveDirection.Forward, false); geometryGenerator.SetIsUsed(curve1, CurveDirection.Reverse, false); } curve1.HeadPoint = point1; curve1.EndPoint = point2; AddDataItemToProperList(curve1); NewlyEffectedCurves.Add(curve1); return curve1; } /// /// Clones the geometry data. /// /// Returns a clone public GeometryData Clone() { var clonedGeometryData = new GeometryData { Left = Left, Right = Right, Bottom = Bottom }; ClonePoints(clonedGeometryData); CloneNewlyEffectedPoints(clonedGeometryData); CloneCurves(clonedGeometryData); CloneNewlyEffectedCurves(clonedGeometryData); CloneSurfaces(clonedGeometryData); // For the clone, set use geometry generator to null. // This will ensure that a new generator for this geometry using this geometry will be used. clonedGeometryData.geometryGenerator = null; return clonedGeometryData; } private void CloneSurfaces(GeometryData clonedGeometryData) { foreach (GeometrySurface surface in Surfaces) { GeometryLoop clonedLoop = surface.OuterLoop.Clone(); ReplacePointsForLoop(clonedGeometryData, clonedLoop); ReplaceCurvesForLoop(clonedGeometryData, clonedLoop); clonedGeometryData.Loops.Add(clonedLoop); var innerLoops = new List(); innerLoops.AddRange(surface.InnerLoops.Select(loop => loop.Clone()).ToList()); foreach (GeometryLoop innerLoop in innerLoops) { ReplacePointsForLoop(clonedGeometryData, innerLoop); ReplaceCurvesForLoop(clonedGeometryData, innerLoop); } clonedGeometryData.Loops.AddRange(innerLoops); GeometrySurface clonedSurface = surface.Clone(); clonedSurface.OuterLoop = clonedLoop; clonedSurface.InnerLoops.AddRange(innerLoops); clonedGeometryData.Surfaces.Add(clonedSurface); } } private static void ReplacePointsForLoop(GeometryData clonedGeometryData, GeometryLoop clonedLoop) { for (int i = 0; i < clonedLoop.CalcPoints.Count; i++) { clonedLoop.CalcPoints[i] = clonedGeometryData.GetPointAtLocation(clonedLoop.CalcPoints[i]); } } private static void ReplaceCurvesForLoop(GeometryData clonedGeometryData, GeometryLoop clonedLoop) { for (int i = 0; i < clonedLoop.CurveList.Count; i++) { clonedLoop.CurveList[i].HeadPoint = clonedGeometryData.GetPointAtLocation(clonedLoop.CurveList[i].HeadPoint); clonedLoop.CurveList[i].EndPoint = clonedGeometryData.GetPointAtLocation(clonedLoop.CurveList[i].EndPoint); } } private void CloneNewlyEffectedCurves(GeometryData clonedGeometryData) { foreach (GeometryCurve curve in NewlyEffectedCurves) { clonedGeometryData.NewlyEffectedCurves.Add(curve.Clone(clonedGeometryData.NewlyEffectedPoints)); } } private void CloneCurves(GeometryData clonedGeometryData) { foreach (GeometryCurve curve in Curves) { clonedGeometryData.Curves.Add(curve.Clone(clonedGeometryData.Points)); } } private void CloneNewlyEffectedPoints(GeometryData clonedGeometryData) { foreach (Point2D point in NewlyEffectedPoints) { clonedGeometryData.NewlyEffectedPoints.Add(point.Clone()); } } private void ClonePoints(GeometryData clonedGeometryData) { foreach (Point2D point in Points) { clonedGeometryData.Points.Add(point.Clone()); } } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { return LocalizationManager.GetTranslatedText(this, "GeometryData"); } /// /// Gets next connected top curve in list of curves /// /// /// /// /// Curve protected internal GeometryCurve GetNextTopCurve(GeometryCurve curve, List boundaryCurves, List excludedCurves) { // if current curve ends on right limit then that must have been the last one so stop the search if (Math.Abs(curve.HeadPoint.X - Right) < GeometryConstants.Accuracy || Math.Abs(curve.EndPoint.X - Right) < GeometryConstants.Accuracy) { return null; } IEnumerable includedCurves = boundaryCurves.Where(geometryCurve => geometryCurve != curve && !excludedCurves.Contains(geometryCurve)); IEnumerable connectedCurves = includedCurves.Where(geometryCurve => AreConnected(curve, geometryCurve)); IEnumerable connectedCurvesOnRightSide = connectedCurves.Where(geometryCurve => IsCurveOnLeftSideOfOtherCurve(curve, geometryCurve)); GeometryCurve topConnectedCurve = connectedCurvesOnRightSide.MaxBy(c => c.HeadPoint.Z + c.EndPoint.Z); return topConnectedCurve; } /// /// Removes the boundary curves from the given list of curves. /// The boundaries themselves are determined from the given geometry /// /// The curves. /// The geometry (as string). private static void RemoveBoundaryCurves(List curves, GeometryPointString geometry) { double minX = geometry.GetMinX(); double maxX = geometry.GetMaxX(); double minZ = geometry.GetMinZ(); foreach (GeometryCurve curve in curves.ToArray()) { if (IsBoundaryCurve(curve, minX, maxX, minZ)) { curves.Remove(curve); } } } /// /// get all geometrypoints from all geometrycurves /// /// /// private static GeometryPointString GetAllPointsFromCurveList(List curveList) { var result = new GeometryPointString(); foreach (GeometryCurve curve in curveList) { result.CalcPoints.Add(curve.EndPoint); result.CalcPoints.Add(curve.HeadPoint); } return result; } /// /// create a copy of the curves /// /// /// private List GetCurvesCopy(List bCurves) { var outerloopCurvesCopy = new List(bCurves); return outerloopCurvesCopy; } /// /// Create a surface line from points in curves /// Precondition is that the curves start at the left boundary and are connected left to right /// (not neccesarily neat head-end) /// /// /// private void CreateSurfaceLinePointString(List curves) { surfaceLine.CalcPoints.Clear(); if (curves.Count == 0) { return; } var reversed = false; // The headpoint of the first curve must be on the left boundary otherwise the // surface line will be in the wrong order. So make sure. if (!(Math.Abs(curves[0].HeadPoint.X - Left) < GeometryConstants.Accuracy)) { curves[0].Reverse(); reversed = true; } foreach (GeometryCurve curve in curves) { if (!surfaceLine.CalcPoints.Contains(curve.HeadPoint)) { surfaceLine.CalcPoints.Add(curve.HeadPoint); } if (!surfaceLine.CalcPoints.Contains(curve.EndPoint)) { surfaceLine.CalcPoints.Add(curve.EndPoint); } } if (reversed) { curves[0].Reverse(); } } /// /// get curves of the top side of the outerloop, vertical curves are omitted /// /// /// private List GetTopCurves(List curves) { GeometryCurve topCurve; // Remove all curves on the geometry boundary if (GetLeftPoints().Count > 0 && GetRightPoints().Count > 0) { foreach (GeometryCurve curve in curves.ToArray()) { if (IsBoundaryCurve(curve, Left, Right, Bottom)) { curves.Remove(curve); } } // Make sure you start with topcurve = curve at the left top position topCurve = curves.Where(g => Math.Abs(g.HeadPoint.X - Left) < GeometryConstants.Accuracy || Math.Abs(g.EndPoint.X - Left) < GeometryConstants.Accuracy) .OrderByDescending(c => c.HeadPoint.Z) .FirstOrDefault(); } else { GeometryPointString gString = GetAllPointsFromCurveList(curves); RemoveBoundaryCurves(curves, gString); double minX = gString.GetMinX(); // Make sure you start with topcurve = curve at the left top position topCurve = curves.Where(g => Math.Abs(g.HeadPoint.X - minX) < GeometryConstants.Accuracy || Math.Abs(g.EndPoint.X - minX) < GeometryConstants.Accuracy).OrderByDescending(c => c.HeadPoint.Z).FirstOrDefault(); } var topCurvesLocal = new List(); while (topCurve != null) { topCurvesLocal.Add(topCurve); topCurve = GetNextTopCurve(topCurve, curves, topCurvesLocal); } return topCurvesLocal; } /// /// Indicates whether a curve is on the boundary of the geometry /// /// /// /// /// /// private static bool IsBoundaryCurve(GeometryCurve curve, double minX, double maxX, double minZ) { if (Math.Abs(curve.HeadPoint.X - minX) < GeometryConstants.Accuracy && Math.Abs(curve.EndPoint.X - minX) < GeometryConstants.Accuracy) { return true; } if (Math.Abs(curve.HeadPoint.X - maxX) < GeometryConstants.Accuracy && Math.Abs(curve.EndPoint.X - maxX) < GeometryConstants.Accuracy) { return true; } if (Math.Abs(curve.HeadPoint.Z - minZ) < GeometryConstants.Accuracy && Math.Abs(curve.EndPoint.Z - minZ) < GeometryConstants.Accuracy) { return true; } return false; } private static bool AreConnected(GeometryCurve curve1, GeometryCurve curve2) { return curve1.HeadPoint.LocationEquals(curve2.HeadPoint) || curve1.HeadPoint.LocationEquals(curve2.EndPoint) || curve1.EndPoint.LocationEquals(curve2.HeadPoint) || curve1.EndPoint.LocationEquals(curve2.EndPoint); } /// /// Checks if curve1 is on the left side of curve2 /// /// /// /// True when curve1 is on the left side of curve2 private static bool IsCurveOnLeftSideOfOtherCurve(GeometryCurve curve1, GeometryCurve curve2) { return curve1.HeadPoint.X.IsLessThanOrEqualTo(curve2.HeadPoint.X) && curve1.HeadPoint.X.IsLessThanOrEqualTo(curve2.EndPoint.X) && curve1.EndPoint.X.IsLessThanOrEqualTo(curve2.HeadPoint.X) && curve1.EndPoint.X.IsLessThanOrEqualTo(curve2.EndPoint.X); } private Point2D AddRequestedPointAsNewPointWhenNeeded(Point2D requestedPoint, out bool isExistingPoint) { isExistingPoint = false; // try to find the requested point in Points Point2D newPoint = GetPointAtLocation(requestedPoint); if (newPoint != null) { // When found make sure that requested point has the same coordinates the found point requestedPoint.X = newPoint.X; requestedPoint.Z = newPoint.Z; // Check by reference if the point is already in the list of points if (!Points.Contains(requestedPoint)) { // if not, make sure a new points is created and added to the list newPoint = null; } else { isExistingPoint = true; } } if (newPoint == null) { newPoint = new Point2D(requestedPoint.X, requestedPoint.Z); AddDataItemToProperList(newPoint); return newPoint; } return newPoint; } private void AddDataItemToProperList(IGeometryObject data) { if (data.GetType() == typeof(Point2D)) { var point = (Point2D) data; Points.Add(point); } else if (data.GetType() == typeof(GeometryCurve)) { var geometryCurve = (GeometryCurve) data; Curves.Add(geometryCurve); } else if (data.GetType() == typeof(GeometryLoop)) { Loops.Add((GeometryLoop) data); } else if (data.GetType() == typeof(GeometrySurface)) { Surfaces.Add((GeometrySurface) data); } } private void CheckForCurvesWherePointIsOnBothConnectedCurvesInLineWithEachOtherAndFixThem(Point2D point, List source) { var line1Point1 = new Point2D(); var line2Point1 = new Point2D(); const double almost180 = 179.0; const double justOver180 = 181.0; line1Point1.Init(source[0].HeadPoint != point ? source[0].HeadPoint : source[0].EndPoint); line2Point1.Init(source[1].HeadPoint != point ? source[1].HeadPoint : source[1].EndPoint); double angle = Routines2D.FindAngle(line1Point1, point, line2Point1, point); if (angle.IsGreaterThanOrEqualTo(almost180) && angle.IsLessThanOrEqualTo(justOver180)) { FixCurvesWherePointIsOnBothConnectedCurvesInLineWithEachOther(point, source); } } private void FixCurvesWherePointIsOnBothConnectedCurvesInLineWithEachOther(Point2D point, List source) { for (var index = 0; index < Curves.Count - 1; ++index) { if (Curves[index] == source[0]) { if (Curves[index].EndPoint != source[1].EndPoint && point == source[0].HeadPoint && point == source[1].HeadPoint) { Curves[index].HeadPoint = source[1].EndPoint; } else if (Curves[index].EndPoint != source[1].EndPoint && point == source[1].HeadPoint) { Curves[index].EndPoint = source[1].EndPoint; } else if (Curves[index].HeadPoint != source[1].HeadPoint && point == Curves[index].EndPoint) { Curves[index].EndPoint = source[1].HeadPoint; } else if (Curves[index].HeadPoint == source[1].EndPoint) { Curves[index].HeadPoint = source[1].HeadPoint; } Remove(point); Remove(source[1]); } } } #region properties /// /// Gets the points. /// /// /// The points. /// public List Points { get; } = new(); /// /// Gets the newly effected points. /// /// /// The newly effected points. /// public List NewlyEffectedPoints { get; } = []; /// /// gets the Curve data list. /// /// /// The curves. /// public List Curves { get; } = []; /// /// Gets the newly effected curves. /// /// /// The newly effected curves. /// public List NewlyEffectedCurves { get; } = []; /// /// gets the Loop data list. /// /// /// The loops. /// public List Loops { get; } = []; public int InnerLoopsCount { get { var innerLoopsCount = 0; foreach (GeometrySurface surface in Surfaces) { innerLoopsCount += surface.InnerLoops.Count; } return innerLoopsCount; } } /// /// gets the Surface data list. /// public List Surfaces { get; } = []; /// /// (Re-)generates the geometry /// public void RegenerateGeometry() { if (isRegeneratingGeometry) { return; } isRegeneratingGeometry = true; if (geometryGenerator == null) { geometryGenerator = new GeometryGenerator(this); } lock (Surfaces) { SynchronizeLoops(); RemoveDoublesFromNewlyEffectedPointsAndCurves(); Points.AddRange(NewlyEffectedPoints); Curves.AddRange(NewlyEffectedCurves); geometryGenerator.GenerateGeometry(); NewlyEffectedPoints.Clear(); NewlyEffectedCurves.Clear(); UpdateSurfaceLine(); SynchronizeLoops(); } isRegeneratingGeometry = false; } /// /// Gets the minimum geometry points x. /// /// /// The minimum geometry points x. /// public double MinGeometryPointsX { get { return Points.Select(geometryPoint => geometryPoint.X).Concat(new[] { double.MaxValue }).Min(); } } /// /// Gets the minimum geometry points z. /// /// /// The minimum geometry points z. /// public double MinGeometryPointsZ { get { return Points.Select(geometryPoint => geometryPoint.Z).Concat(new[] { double.MaxValue }).Min(); } } /// /// Gets the maximum geometry points x. /// /// /// The maximum geometry points x. /// public double MaxGeometryPointsX { get { return Points.Select(geometryPoint => geometryPoint.X).Concat(new[] { double.MinValue }).Max(); } } /// /// Gets the maximum geometry points z. /// /// /// The maximum geometry points z. /// public double MaxGeometryPointsZ { get { return Points.Select(geometryPoint => geometryPoint.Z).Concat(new[] { double.MinValue }).Max(); } } /// /// Gets or sets the left. /// /// /// The left. /// public double Left { get; set; } = GeometryConstants.DefaultLeftLimitGeometry; /// /// Gets or sets the right. /// /// /// The right. /// public double Right { get; set; } = GeometryConstants.DefaultRightLimitGeometry; /// /// Gets or sets the bottom. /// /// /// The bottom. /// public double Bottom { get; set; } = GeometryConstants.DefaultBottomLimitGeometry; /// /// Removes the doubles from newly effected points and curves. /// public void RemoveDoublesFromNewlyEffectedPointsAndCurves() { RemoveDoublesFromNewlyEffectedPoints(); RemoveDoublesFromNewlyEffectedCurves(); } /// /// Determines whether a curve already exists. /// /// /// public bool DoesCurveExist(GeometryCurve curve) { return Curves.Any(c => c.LocationEquals(curve)); } private void RemoveDoublesFromNewlyEffectedCurves() { RemoveIllegalDoublesFromNewlyEffectedCurves(); RemoveRealDoublesFromNewlyEffectedCurves(); } private void RemoveRealDoublesFromNewlyEffectedCurves() { GeometryCurve[] curvesAsArray = NewlyEffectedCurves.ToArray(); var curvesToDelete = new List(); for (var i = 0; i < curvesAsArray.Length; i++) { for (int j = i; j < curvesAsArray.Length; j++) { if ((i != j && curvesAsArray[i].LocationEquals(curvesAsArray[j])) && !curvesToDelete.Contains(curvesAsArray[j])) { curvesToDelete.Add(curvesAsArray[j]); } } } foreach (GeometryCurve curve in curvesToDelete) { NewlyEffectedCurves.Remove(curve); } } private void RemoveIllegalDoublesFromNewlyEffectedCurves() { var curvesToDelete = new List(); GeometryCurve[] curvesAsArray = NewlyEffectedCurves.ToArray(); for (var i = 0; i < curvesAsArray.Length; i++) { if (curvesAsArray[i].HeadPoint == null || curvesAsArray[i].EndPoint == null || curvesAsArray[i].HeadPoint.LocationEquals(curvesAsArray[i].EndPoint)) { curvesToDelete.Add(curvesAsArray[i]); } } foreach (GeometryCurve curve in curvesToDelete) { NewlyEffectedCurves.Remove(curve); } } private void RemoveDoublesFromNewlyEffectedPoints() { var pointsToDelete = new List(); Point2D[] pointsAsArray = NewlyEffectedPoints.ToArray(); for (var i = 0; i < pointsAsArray.Length; i++) { for (int j = i; j < pointsAsArray.Length; j++) { if (i != j && pointsAsArray[i].LocationEquals(pointsAsArray[j]) && !pointsToDelete.Contains(pointsAsArray[j])) { pointsToDelete.Add(pointsAsArray[j]); } } } foreach (Point2D point in pointsToDelete) { NewlyEffectedPoints.Remove(point); } } /// /// Gets all points on the Left boundary. /// /// The points on the Left boundary public List GetLeftPoints() { List leftPoints = Points.Where(gp => Math.Abs(gp.X - Left) < GeometryConstants.Accuracy).ToList(); return leftPoints; } /// /// Gets all points on the Right boundary. /// /// The points on the Right boundary public List GetRightPoints() { List rightPoints = Points.Where(point => Math.Abs(point.X - Right) < GeometryConstants.Accuracy).ToList(); return rightPoints; } /// /// Gets the left curves, i.e. all curves that are on or connected to the Left boundary. /// /// The left curves public List GetLeftCurves() { var leftCurves = new List(); foreach (GeometryCurve geometryCurve in Curves) { if ((geometryCurve.HeadPoint.X <= Left && geometryCurve.EndPoint.X >= Left) || (geometryCurve.HeadPoint.X >= Left && geometryCurve.EndPoint.X <= Left)) { leftCurves.Add(geometryCurve); } } return leftCurves; } /// /// Gets the right curves, i.e. all curves that are on or connected to the Right boundary. /// /// The right curves public List GetRightCurves() { var rightCurves = new List(); foreach (GeometryCurve curve in Curves) { if ((curve.HeadPoint.X <= Right && curve.EndPoint.X >= Right) || (curve.HeadPoint.X >= Right && curve.EndPoint.X <= Right)) { rightCurves.Add(curve); } } return rightCurves; } /// /// Gets the geometry bounds. /// /// public override GeometryBounds GetGeometryBounds() { return new GeometryBounds(Left, Right, Bottom, Bottom + Math.Min(Right - Left, 20)); } #endregion #region Functions #region create functions /// /// Adjust the Geometry Bottom, Left and Right properties to the currently contained surfaces /// public void Rebox() { var xMin = double.MaxValue; var xMax = double.MinValue; var zMin = double.MaxValue; var zMax = double.MinValue; foreach (Point2D point in Points) { xMin = Math.Min(point.X, xMin); xMax = Math.Max(point.X, xMax); zMin = Math.Min(point.Z, zMin); zMax = Math.Max(point.Z, zMax); } Bottom = zMin; Left = xMin; Right = xMax; } #endregion #region remove functions /// /// Clears this instance. /// public void Clear() { Points.Clear(); Curves.Clear(); Surfaces.Clear(); Loops.Clear(); NewlyEffectedPoints.Clear(); NewlyEffectedCurves.Clear(); } /// /// Removes the given data object /// /// The IGeometryObject to remove /// public void Remove(IGeometryObject geometryObject) { if (geometryObject == null) { return; } if (geometryObject.GetType() == typeof(Point2D)) { var point = (Point2D) geometryObject; Points.Remove(point); } else if (geometryObject.GetType() == typeof(GeometryCurve)) { var geometryCurve = (GeometryCurve) geometryObject; if ((Curves.IndexOf(geometryCurve) > -1) && Curves.Remove(geometryCurve)) { RemoveDeletedCurveFromIsUsedCurveLists(geometryCurve); } } else if (geometryObject.GetType() == typeof(GeometryLoop)) { var geometryLoop = (GeometryLoop) geometryObject; Loops.Remove(geometryLoop); } else if (geometryObject.GetType() == typeof(GeometrySurface)) { var geometrySurface = (GeometrySurface) geometryObject; Surfaces.Remove(geometrySurface); } } /// /// Removes the deleted curve from IsUsedCurve lists. /// /// The curve to delete. private void RemoveDeletedCurveFromIsUsedCurveLists(GeometryCurve geometryCurve) { geometryGenerator.RemoveIsUsedCurve(geometryCurve); } /// /// deletes all the Loop from IGeometryLoop. /// private void DeleteAllLoops() { Loops.Clear(); } #endregion #region other functions /// /// Determines if there is already a surface with the same outer loop. /// /// /// public bool HasSurfaceWithSameOuterLoop(GeometryLoop outerLoop) { foreach (GeometrySurface geometrySurface in Surfaces) { if (geometrySurface.OuterLoop.HasSameCurves(outerLoop)) { return true; } } return false; } /// /// Finds a Surface based on its outerloop /// /// /// the surface or when not found null public GeometrySurface FindSurfaceByItsOuterLoop(GeometryLoop outerLoop) { foreach (GeometrySurface geometrySurface in Surfaces) { if (geometrySurface.OuterLoop.HasSameCurves(outerLoop)) { return geometrySurface; } } return null; } /// /// /// /// /// public GeometryLoop FindLoopThatHasSameLoopCurves(GeometryLoop outerLoop) { foreach (GeometrySurface geometrySurface in Surfaces) { if (geometrySurface.OuterLoop.HasSameCurves(outerLoop)) { return geometrySurface.OuterLoop; } foreach (GeometryLoop innerLoop in geometrySurface.InnerLoops.Where(innerLoop => innerLoop.HasSameCurves(outerLoop))) { return innerLoop; } } return null; } #region calculation function private int GetDependentCurveCount(Point2D point) { int curveCount = Curves.Count; var curvePointDependency = 0; if (curveCount > 0) { for (var index = 0; index < curveCount; index++) { if (Curves[index].HeadPoint == point || Curves[index].EndPoint == point) { curvePointDependency++; } } } return curvePointDependency; } /// /// Returns a list of boundary curves. These are curves which are used in only one surface so they have to be on a boundary (inner or outer) /// /// private List GetBoundaryCurves() { var curves = new List(); var loops = new List(); foreach (GeometrySurface surface in Surfaces) { loops.Add(surface.OuterLoop); // Todo Ask Rob/Tom: when a real "doughnut" type surface (so hole in the center) is permitted, adding the innerloops here will // result in a wrong list of curves (because it will include the inner loop curves defining the hole) for its actual purpose: // the determination of the surfaceline. When there is always a surface defined within the "dougnut" (so no real hole), // this code will work and the innerloop must even be added to prevent finding internal boundaries. So this depends on the specs! loops.AddRange(surface.InnerLoops); } foreach (GeometryLoop loop in loops) { foreach (GeometryCurve curve in loop.CurveList) { if (curves.Contains(curve)) { // Second appearance, remove curves.Remove(curve); } else { curves.Add(curve); } } } return curves; } #endregion #endregion #endregion }