// 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; using Deltares.DamEngine.Data.Standard.Validation; namespace Deltares.DamEngine.Data.Geometry; /// /// Class containing the geometry data /// /// public class GeometryData : GeometryObject { private readonly GeometryPointString surfaceLine = new GeometryPointString(); 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 virtual GeometryPointString SurfaceLine { get { if (surfaceLine.CalcPoints.Count == 0 && Points.Count > 0) { UpdateSurfaceLine(); } return surfaceLine; } } /// /// Checks geometry for loose curves and AutoRegeneration /// /// [Validate] public ValidationResult[] ValidateGeometry() { var validationList = new List(); { foreach (Point2D point in Points) { foreach (Point2D point1 in Points) { if (point != point1) { var isValidated = false; foreach (ValidationResult validatedItem in validationList) { if (validatedItem.Subject == point) { isValidated = true; } } if (!isValidated) { if (Math.Abs(point.X - point1.X) < GeometryConstants.Accuracy && Math.Abs(point.Z - point1.Z) < GeometryConstants.Accuracy) { validationList.Add(new ValidationResult(ValidationResultType.Error, point + " and " + point1 + " values are same.", point1)); } } } } } } if (Surfaces.Count < 1) { validationList.Add(new ValidationResult(ValidationResultType.Error, "No soil surface available.", this)); } return validationList.ToArray(); } /// /// 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 (this.GetDependentCurveCount(geometryPoint) == 0) this.Remove((IGeometryObject) geometryPoint, true); } } /// /// Deletes all the loose curves. /// Returns true when the loose curve is inside publisherEventArgs surface. /// Calls Regeneration if the funtion returns true. /// public bool DeleteLooseCurves() { SynchronizeLoops(); 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(); } return regenerateGeometry; } /// /// Deletes the curve if the aValidate is true. /// /// The curve to delete /// Indocates whether the validation was successful /// True if delete successful public bool DeleteCurve(GeometryCurve geometryCurve, bool validate) { GeometryCurve curve = geometryCurve; if (validate) { if (GetDependentCurveCount(curve.HeadPoint) <= 1 && Points.Contains(curve.HeadPoint)) { Remove(curve.HeadPoint, false); } if (GetDependentCurveCount(curve.EndPoint) <= 1 && Points.Contains(curve.EndPoint)) { Remove(curve.EndPoint, false); } Remove(geometryCurve, false); if (geometryCurve.SurfaceAtLeft != null || geometryCurve.SurfaceAtRight != null) { return true; } } else { Remove(geometryCurve, false); } return false; } /// /// Synchronizes the loops. /// public void SynchronizeLoops() { DeleteAllLoops(); foreach (GeometrySurface surface in Surfaces) { // #Bka: 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 tolerance. /// The point at the location; if not found returns null. public Point2D GetPointAtLocation(Point2D point2D, double tolerance) { for (var i = 0; i < Points.Count; i++) { if (Routines2D.DetermineIfPointsCoincide(point2D.X, point2D.Z, Points[i].X, Points[i].Z, tolerance)) { return Points[i]; } } return null; } /// /// Gets the list of curves which contain the given point. /// /// The point /// The curves containg the point public void GetCurvesCoincidingWithPoint(Point2D point2D, ref List curveList) { curveList.Clear(); int curveCount = Curves.Count; // loop through the list of curves, check if point on line for (var index = 0; index < curveCount; index++) { GeometryCurve curve = Curves[index]; // does the input point exist in this line (within aValue1 tolerance) if (Routines2D.DoesPointExistInLine(curve.HeadPoint, curve.EndPoint, point2D, GeometryConstants.Accuracy)) { curveList.Add(curve); // add to list } } } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { return LocalizationManager.GetTranslatedText(this, "GeometryData"); } /// /// 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; } /// /// Gets next connected top curve in list of curves /// /// /// /// /// private 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; } foreach (GeometryCurve geometryCurve in boundaryCurves) { if (geometryCurve != curve && !excludedCurves.Contains(geometryCurve)) { if (AreConnected(curve, geometryCurve)) { return geometryCurve; } } } return null; } /// /// 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 bool AreConnected(GeometryCurve curve1, GeometryCurve curve2) { return (curve1.HeadPoint == curve2.HeadPoint || curve1.HeadPoint == curve2.EndPoint || curve1.EndPoint == curve2.HeadPoint || curve1.EndPoint == curve2.EndPoint); } /// /// Updates the line at the top of the geometry /// private 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; surfaceLine.SyncPoints(); } #region properties /// /// Gets the points. /// /// /// The points. /// [Validate] public List Points { get; } = new List(); /// /// Gets the newly effected points. /// /// /// The newly effected points. /// public List NewlyEffectedPoints { get; } = new List(); /// /// gets the Curve data list. /// /// /// The curves. /// public List Curves { get; } = new List(); /// /// Gets the newly effected curves. /// /// /// The newly effected curves. /// public List NewlyEffectedCurves { get; } = new List(); /// /// gets the Loop data list. /// /// /// The loops. /// public List Loops { get; } = new List(); /// /// gets the Surface data list. /// public List Surfaces { get; } = new List(); 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() { var pdel = new List(); Point2D[] par = NewlyEffectedPoints.ToArray(); for (var i = 0; i < par.Length; i++) { for (int j = i; j < par.Length; j++) { if (i != j && par[i].LocationEquals(par[j])) { if (!pdel.Contains(par[j])) { pdel.Add(par[j]); } } } } foreach (Point2D point in pdel) { NewlyEffectedPoints.Remove(point); } var cdel = new List(); GeometryCurve[] car = NewlyEffectedCurves.ToArray(); // First remove all "illegal" newlyeffected curves for (var i = 0; i < car.Length; i++) { if (car[i].HeadPoint == null || car[i].EndPoint == null || car[i].HeadPoint.LocationEquals(car[i].EndPoint)) { cdel.Add(car[i]); } } foreach (GeometryCurve curve in cdel) { NewlyEffectedCurves.Remove(curve); } // then remove all real doubles GeometryCurve[] car2 = NewlyEffectedCurves.ToArray(); cdel.Clear(); for (var i = 0; i < car2.Length; i++) { for (int j = i; j < car2.Length; j++) { if (i != j && car2[i].LocationEquals(car2[j])) { if (!cdel.Contains(car2[j])) { cdel.Add(car2[j]); } } } } foreach (GeometryCurve curve in cdel) { NewlyEffectedCurves.Remove(curve); } } /// /// 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(); NewlyEffectedPoints.Clear(); NewlyEffectedCurves.Clear(); } /// /// Removes the given data object /// /// The IGeometryObject to remove /// If set to true [a validate]. /// public bool Remove(IGeometryObject geometryObject, bool validate) { var removeFromList = false; var objectlist = new List { geometryObject }; if (geometryObject == null) { return false; } if (geometryObject.GetType() == typeof(Point2D)) { var point = (Point2D) geometryObject; if (Points.Remove(point)) { // TODO: MWDAM-2132, check if code below is still needed // if (aValidate) // { // HandleDelete(objectlist); // } // // DataEventPublisher.DataListModified(pointDataList, point); removeFromList = true; } } else if (geometryObject.GetType() == typeof(GeometryCurve)) { var geometryCurve = (GeometryCurve) geometryObject; if (Curves.IndexOf(geometryCurve) > -1) { // TODO: MWDAM-2132, check if code below is still needed // if (aValidate) // { // HandleDelete(objectlist); // } if (Curves.Remove(geometryCurve)) { RemoveDeletedCurveFromIsUsedCurveLists(geometryCurve); removeFromList = true; // TODO: MWDAM-2132, check if code below is still needed // DataEventPublisher.DataListModified(curveDataList, geometryCurve); } } } else if (geometryObject.GetType() == typeof(GeometryLoop)) { var geometryLoop = (GeometryLoop) geometryObject; if (Loops.Remove(geometryLoop)) { // TODO: MWDAM-2132, check if code below is still needed // DataEventPublisher.DataListModified(loopDataList, geometryLoop); // // if (aValidate) // { // HandleDelete(objectlist); // } removeFromList = true; } } else if (geometryObject.GetType() == typeof(GeometrySurface)) { var geometrySurface = (GeometrySurface) geometryObject; if (Surfaces.Remove(geometrySurface)) { removeFromList = true; // TODO: MWDAM-2132, check if code below is still needed // DataEventPublisher.DataListModified(surfaceDataList, geometrySurface); } } // TODO: MWDAM-2132, check if code below is still needed // DataEventPublisher.AfterChange(this); return removeFromList; } /// /// 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 #region calculation function private int GetDependentCurveCount(Point2D aPoint) { int curveCount = Curves.Count; var curvePointDependency = 0; if (curveCount > 0) { for (var index = 0; index < curveCount; index++) { if (Curves[index].HeadPoint == aPoint || Curves[index].EndPoint == aPoint) { curvePointDependency++; } } } return curvePointDependency; } /// /// CheckIfIntersectStricktly /// Determines if two lines intersect each other stricktly (so no extrapolated points). /// /// Line 1 Point2D 1 /// Line 1 Point2D 2 /// Line 2 Point2D 1 /// Line 2 Point2D 2 /// Intersection coordinates /// True if lines intersect each other private bool CheckIfIntersectStricktly(Point2D beginPoint1, Point2D endPoint1, Point2D beginPoint2, Point2D endPoint2, ref Point2D intersect) { Point2D ip; LineIntersection res = Routines2D.DetermineIf2DLinesIntersectStrickly(beginPoint1, endPoint1, beginPoint2, endPoint2, out ip); if (ip != null) { intersect = ip; } return res == LineIntersection.Intersects; } /// /// CheckIfIntersect /// Determines if two lines intersect each other stricktly (so no extrapolated points). /// /// Line 1 Point2D 1 /// Line 1 Point2D 2 /// Line 2 Point2D 1 /// Line 2 Point2D 2 /// Intersection coordinates /// True if lines intersect each other public bool CheckIfIntersect(double[] aL1P1, double[] aL1P2, double[] aL2P1, double[] aL2P2, ref double[] aIntersect) { var p1 = new Point2D(aL1P1[0], aL1P1[1]); var p2 = new Point2D(aL1P2[0], aL1P2[1]); var p3 = new Point2D(aL2P1[0], aL2P1[1]); var p4 = new Point2D(aL2P2[0], aL2P2[1]); var ip = new Point2D(); bool res = CheckIfIntersectStricktly(p1, p2, p3, p4, ref ip); if (res) { aIntersect[0] = ip.X; aIntersect[1] = ip.Z; } return res; } /// /// Gets the height of the surface(s) intersected at the given x. /// /// The x. /// public double GetSurfaceHeight(double x) { double surfaceHeight = -Double.MaxValue; double[] intersectionPoints = IntersectLayers(x, -9999); for (var i = 0; i < intersectionPoints.Length; i++) { if (intersectionPoints[i] > surfaceHeight) { surfaceHeight = intersectionPoints[i]; } } return surfaceHeight; } /// /// All the Intersection of layers in respect with a given vertical are detemined here. /// /// Startingpoint of the Vertical (X) /// Startingpoint of the Vertical (Y) /// List of Z intersection coordinates private double[] IntersectLayers(double aXCoord, double aZCoord) { if (Surfaces == null) { throw new Exception("Empty Surfaces in IntersectLayers"); } var beginPoint2 = new Point2D { X = aXCoord, Z = aZCoord }; var endPoint2 = new Point2D { X = aXCoord, Z = 99999 }; var referencePoint = new Point2D(); var intersections = new List(); for (var surfaceIndexLocal = 0; surfaceIndexLocal < Surfaces.Count; surfaceIndexLocal++) { List outerLoopCurveList = Surfaces[surfaceIndexLocal].OuterLoop.CurveList; for (var curveIndexLocal = 0; curveIndexLocal < outerLoopCurveList.Count; curveIndexLocal++) { //Check for each curve if it intersects with x coordinate Point2D beginPoint1 = outerLoopCurveList[curveIndexLocal].GetHeadPoint(CurveDirection.Forward); Point2D endPoint1 = outerLoopCurveList[curveIndexLocal].GetEndPoint(CurveDirection.Forward); if (Math.Max(beginPoint1.X, endPoint1.X) >= aXCoord && Math.Min(beginPoint1.X, endPoint1.X) <= aXCoord) { if (CheckIfIntersectStricktly(beginPoint1, endPoint1, beginPoint2, endPoint2, ref referencePoint)) { if (referencePoint.Z > aZCoord && !intersections.Contains(referencePoint.Z)) { intersections.Add(referencePoint.Z); } } } } } return intersections.ToArray(); } /// /// 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 public Point2D CreatePoint(Point2D requestedPoint, bool snapToExistingPoint) { Point2D newPoint = DetermineNewPointAndFlags(requestedPoint, snapToExistingPoint, out bool flag1, out bool flag2); if (!flag2 && snapToExistingPoint) { int count = Curves.Count; for (int index = 0; index < count; ++index) { GeometryCurve curve = Curves[index]; if (Routines2D.DoesPointExistInLine(curve.HeadPoint, curve.EndPoint, newPoint, 0.25)) { Routines2D.GetPointOnLineClosestTo(newPoint.X, newPoint.Z, curve.HeadPoint.X, curve.HeadPoint.Z, curve.EndPoint.X, curve.EndPoint.Z, out Point2D aResultPoint); newPoint.X = aResultPoint.X; newPoint.Z = aResultPoint.Z; break; } } } if (!NewlyEffectedPoints.Contains(newPoint) && !flag1) NewlyEffectedPoints.Add(newPoint); return newPoint; } private Point2D DetermineNewPointAndFlags(Point2D requestedPoint, bool snapToExistingPoint, out bool flag1, out bool flag2) { Point2D newPoint = null; flag1 = false; flag2 = false; if (snapToExistingPoint) newPoint = GetPoint(requestedPoint, 0.001); if (newPoint != null) { requestedPoint.X = newPoint.X; requestedPoint.Z = newPoint.Z; if (!Points.Contains(requestedPoint)) newPoint = null; else flag1 = true; } if (newPoint == null) { newPoint = new Point2D(requestedPoint.X, requestedPoint.Z); Create((IGeometryObject) newPoint); } else flag2 = true; return newPoint; } public IGeometryObject Create(IGeometryObject aData) { if (aData == null) return (IGeometryObject) null; if (aData.GetType() == typeof (Point2D)) { Point2D point = (Point2D) aData; Points.Add(point); } else if (aData.GetType() == typeof (GeometryCurve)) { GeometryCurve geometryCurve = (GeometryCurve) aData; Curves.Add(geometryCurve); } else if (aData.GetType() == typeof (GeometryLoop)) Loops.Add((GeometryLoop) aData); else if (aData.GetType() == typeof (GeometrySurface)) Surfaces.Add((GeometrySurface) aData); return aData; } public Point2D GetPoint(Point2D point2D, double snapDistance) { for (int index = 0; index < Points.Count; ++index) { if (Routines2D.DetermineIfPointsCoincide(point2D.X, point2D.Z, Points[index].X, Points[index].Z, snapDistance)) return new Point2D(Points[index].X, Points[index].Z); } return null; } public bool DeletePoint(Point2D aPoint) { bool flag = false; List source = new List(); if (Curves.Count > 0) { source.AddRange(Curves.Where((Func) (c => c.HeadPoint == aPoint || c.EndPoint == aPoint))); if (source.Count == 2) { CheckForCurvesWherePointIsOnBothConnectedCurvesInLineWithEachOther(aPoint, source); } Remove(aPoint, false); if (source.Exists((curve => curve.SurfaceAtLeft != null || curve.SurfaceAtRight != null))) flag = true; foreach (GeometryCurve aCurve in source) DeleteCurve(aCurve, true); return flag; } Remove(aPoint, false); return false; } private void CheckForCurvesWherePointIsOnBothConnectedCurvesInLineWithEachOther(Point2D aPoint, List source) { Point2D line1Point1 = new Point2D(); Point2D line2Point1 = new Point2D(); line1Point1.Init(source[0].HeadPoint != aPoint ? source[0].HeadPoint : source[0].EndPoint); line2Point1.Init(source[1].HeadPoint != aPoint ? source[1].HeadPoint : source[1].EndPoint); double angle = Routines2D.FindAngle(line1Point1, aPoint, line2Point1, aPoint); if (angle.IsGreaterThanOrEqualTo(179.0) && angle.IsLessThanOrEqualTo(181.0)) { FixCurvesWherePointIsOnBothConnectedCurvesInLineWithEachOther(aPoint, source); } } private void FixCurvesWherePointIsOnBothConnectedCurvesInLineWithEachOther(Point2D aPoint, List source) { for (int index = 0; index < Curves.Count - 1; ++index) { if (Curves[index] == source[0]) { if (Curves[index].EndPoint != source[1].EndPoint && aPoint == source[0].HeadPoint && aPoint == source[1].HeadPoint) Curves[index].HeadPoint = source[1].EndPoint; else if (Curves[index].EndPoint != source[1].EndPoint && aPoint == source[1].HeadPoint) Curves[index].EndPoint = source[1].EndPoint; else if (Curves[index].HeadPoint != source[1].HeadPoint && aPoint == Curves[index].EndPoint) Curves[index].EndPoint = source[1].HeadPoint; else if (Curves[index].HeadPoint == source[1].EndPoint) Curves[index].HeadPoint = source[1].HeadPoint; Remove(aPoint, false); Remove(source[1], false); } } } public GeometryCurve CreateCurve(List points, Point2D aPoint1, Point2D aPoint2) { if (HandleEmptyPointsGeometryCurve(points, aPoint1, aPoint2, out GeometryCurve geometryCurve1)) { return geometryCurve1; } foreach (GeometryCurve curve in this.Curves) { if (curve.HeadPoint == aPoint1 && curve.EndPoint == aPoint2 || curve.HeadPoint == aPoint2 && curve.EndPoint == aPoint1) { return curve; } } GeometryCurve curve1 = new GeometryCurve(); geometryGenerator.SetIsUsed(curve1, CurveDirection.Forward, false); geometryGenerator.SetIsUsed(curve1, CurveDirection.Reverse, false); curve1.HeadPoint = aPoint1; curve1.EndPoint = aPoint2; Create((IGeometryObject) curve1); NewlyEffectedCurves.Add(curve1); return curve1; } private bool HandleEmptyPointsGeometryCurve(List points, Point2D aPoint1, Point2D aPoint2, out GeometryCurve geometryCurve1) { if (aPoint1 == null && aPoint2 == null) { if (points.Count < 1) { geometryCurve1 = null; return true; } Point2D geometryPoint1 = null; if (points.Count == 1) { Point2D point = points[0]; Point2D aPoint = new Point2D(point.X, point.Z); List aCurveList = new List(); GetCurvesCoincidingInputPoint(aPoint, ref aCurveList); if (aCurveList.Count <= 0) { geometryCurve1 = null; return true; } geometryGenerator.SplitCurvesAtCoincidentPoint(CreatePoint(point, true), aCurveList); } else { HandleEmptyPointsGeometryCurveNewWithMoreThanOnePointMethod(points, geometryPoint1); } // #Bka: this is a direct call instead of a delayed invoke. Can lead to problems (endless spinning) // Possibly check the isgeneratinggeometry flag RegenerateGeometry(); geometryCurve1 = Curves.Count > 0 ? Curves[0] : (GeometryCurve) null; return true; } geometryCurve1 = null; return false; } private void HandleEmptyPointsGeometryCurveNewWithMoreThanOnePointMethod(List points, Point2D geometryPoint1) { for (int index = 0; index < points.Count - 1; ++index) { Point2D geometryPoint2 = index != 0 ? geometryPoint1 : CreatePoint(points[index], true); geometryPoint1 = CreatePoint(points[index + 1], true); bool flag = false; foreach (GeometryCurve curve in Curves) { if (curve.HeadPoint == geometryPoint2 && curve.EndPoint == geometryPoint1 || curve.HeadPoint == geometryPoint1 && curve.EndPoint == geometryPoint2) { flag = true; } } if (!flag) { GeometryCurve geometryCurve = new GeometryCurve(); geometryGenerator.SetIsUsed(geometryCurve, CurveDirection.Forward, false); geometryGenerator.SetIsUsed(geometryCurve, CurveDirection.Reverse, false); geometryCurve.HeadPoint = geometryPoint2; geometryCurve.EndPoint = geometryPoint1; Create((IGeometryObject) geometryCurve); NewlyEffectedCurves.Add(geometryCurve); } } } /// /// Returns a list of curves that contain the input point. /// /// /// public void GetCurvesCoincidingInputPoint(Point2D aPoint, ref List aCurveList) { aCurveList.Clear(); int count = Curves.Count; for (int index = 0; index < count; ++index) { GeometryCurve curve = Curves[index]; if (Routines2D.DoesPointExistInLine(curve.HeadPoint, curve.EndPoint, aPoint, 0.001)) aCurveList.Add(curve); } } }