Index: DamEngine/trunk/src/Deltares.DamEngine.Data/Geometry/GeometryPointString.cs =================================================================== diff -u -r4000 -r4052 --- DamEngine/trunk/src/Deltares.DamEngine.Data/Geometry/GeometryPointString.cs (.../GeometryPointString.cs) (revision 4000) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/Geometry/GeometryPointString.cs (.../GeometryPointString.cs) (revision 4052) @@ -24,970 +24,969 @@ using System.Linq; using Deltares.DamEngine.Data.Standard; -namespace Deltares.DamEngine.Data.Geometry +namespace Deltares.DamEngine.Data.Geometry; + +/// +/// Classifications of an objected with regards to a geometric line in the XZ-plane. +/// +public enum RelativeXzPosition { /// - /// Classifications of an objected with regards to a geometric line in the XZ-plane. + /// Indicates that the object is considered 'on' the geometric line. /// - public enum RelativeXzPosition - { - /// - /// Indicates that the object is considered 'on' the geometric line. - /// - OnGeometricLine, + OnGeometricLine, - /// - /// Indicates that the object is considered 'above' the geometric line (object Z - /// coordinate higher than that of the line). - /// - AboveGeometricLine, + /// + /// Indicates that the object is considered 'above' the geometric line (object Z + /// coordinate higher than that of the line). + /// + AboveGeometricLine, - /// - /// Indicates that the object is considered 'below' the geometric line (object Z - /// coordinates lower than that of the line). - /// - BelowGeometricLine, + /// + /// Indicates that the object is considered 'below' the geometric line (object Z + /// coordinates lower than that of the line). + /// + BelowGeometricLine, - /// - /// Indicates that the object is considered 'outside the scope' of the geometric line. - /// - BeyondGeometricLine - } + /// + /// Indicates that the object is considered 'outside the scope' of the geometric line. + /// + BeyondGeometricLine +} +/// +/// Type of extrapolation +/// +public enum ExtraPolationMode +{ /// - /// Type of extrapolation + /// No extrapolation should be used. /// - public enum ExtraPolationMode - { - /// - /// No extrapolation should be used. - /// - Beyond, + Beyond, - /// - /// Used constant (0th order) extrapolation at the extremes. - /// - Horizontal - } + /// + /// Used constant (0th order) extrapolation at the extremes. + /// + Horizontal +} +/// +/// Collection of points (X,Z) in world coordinates used for describing Surface lines +/// +public class GeometryPointString : GeometryObject +{ /// - /// Collection of points (X,Z) in world coordinates used for describing Surface lines + /// Matching distance where a point within this range is considered on the same point. /// - public class GeometryPointString : GeometryObject - { - /// - /// Matching distance where a point within this range is considered on the same point. - /// - private const double epsilon = GeometryConstants.Accuracy; + private const double epsilon = GeometryConstants.Accuracy; - /// - /// The calculate points as protected field (to be able to prevent recursive calls to CalcPoints) - /// - protected readonly List calcPoints = new List(); + /// + /// The calculate points as protected field (to be able to prevent recursive calls to CalcPoints) + /// + protected readonly List calcPoints = new List(); - private readonly List points = new List(); - private bool isFrozen; - private bool hasNaNx; - private double frozenMaxZ = double.NaN; + private readonly List points = new List(); + private bool isFrozen; + private bool hasNaNx; + private double frozenMaxZ = double.NaN; - // sortedPoints must never be used outside this class. Either the GPS concerned must have sorted points but then they already are - // (eg. surfaceline, headline) or they may be unsorted in which case using the sorted list in other classes leads to errors (eg. - // geometrysurfaces, waternetlines) - private List sortedPoints; + // sortedPoints must never be used outside this class. Either the GPS concerned must have sorted points but then they already are + // (eg. surfaceline, headline) or they may be unsorted in which case using the sorted list in other classes leads to errors (eg. + // geometrysurfaces, waternetlines) + private List sortedPoints; - /// - /// Gets the at the specified index. - /// - /// - /// The . - /// - /// The index. - /// When less - /// than zero or is greater or equals to . - public Point2D this[int index] + /// + /// Gets the at the specified index. + /// + /// + /// The . + /// + /// The index. + /// When less + /// than zero or is greater or equals to . + public Point2D this[int index] + { + get { - get - { - return calcPoints[index]; - } + return calcPoints[index]; } + } - /// - /// The calculate points (to be used in calcualtion instead of Points for better performance) - /// - public virtual List CalcPoints + /// + /// The calculate points (to be used in calcualtion instead of Points for better performance) + /// + public virtual List CalcPoints + { + get { - get - { - return calcPoints; - } + return calcPoints; } + } - /// - /// List of points that describe the physical surface line or surface. - /// - /// - /// The points. - /// - public virtual IList Points + /// + /// List of points that describe the physical surface line or surface. + /// + /// + /// The points. + /// + public virtual IList Points + { + get { - get - { - return points; - } + return points; } + } - /// - /// Gets the count of the points. - /// - /// - /// The count. - /// - public int Count + /// + /// Gets the count of the points. + /// + /// + /// The count. + /// + public int Count + { + get { - get - { - return calcPoints.Count; - } + return calcPoints.Count; } + } - /// - /// Freezes this instance. - /// - public void Freeze() + /// + /// Freezes this instance. + /// + public void Freeze() + { + if (!isFrozen) { - if (!isFrozen) + sortedPoints = new List(calcPoints.Count); + foreach (Point2D point in calcPoints) { - sortedPoints = new List(calcPoints.Count); - foreach (Point2D point in calcPoints) - { - sortedPoints.Add(point); - hasNaNx = hasNaNx || double.IsNaN(point.X); - frozenMaxZ = Math.Max(frozenMaxZ, point.Z); - } - - sortedPoints.Sort(); + sortedPoints.Add(point); + hasNaNx = hasNaNx || double.IsNaN(point.X); + frozenMaxZ = Math.Max(frozenMaxZ, point.Z); } - isFrozen = true; + sortedPoints.Sort(); } - /// - /// Clones this object except the points - /// - /// - public virtual GeometryPointString Clone() - { - var clone = new GeometryPointString(); - this.CloneProperties(clone); // exludes the points ! - clone.Points.Clear(); - foreach (GeometryPoint point in Points) - { - clone.Points.Add(new GeometryPoint(point)); - } + isFrozen = true; + } - clone.SyncCalcPoints(); - return clone; + /// + /// Clones this object except the points + /// + /// + public virtual GeometryPointString Clone() + { + var clone = new GeometryPointString(); + this.CloneProperties(clone); // exludes the points ! + clone.Points.Clear(); + foreach (GeometryPoint point in Points) + { + clone.Points.Add(new GeometryPoint(point)); } - /// - /// Gets the minimum z. - /// Note: CalcPoints must be used instead of calcPoints as otherwise unclear behaviour of Linq spoils the result - /// Change back to calcPoints and Benchmark4_01l in the WaternetCreatorTests will fail! - /// - /// - public double GetMinZ() + clone.SyncCalcPoints(); + return clone; + } + + /// + /// Gets the minimum z. + /// Note: CalcPoints must be used instead of calcPoints as otherwise unclear behaviour of Linq spoils the result + /// Change back to calcPoints and Benchmark4_01l in the WaternetCreatorTests will fail! + /// + /// + public double GetMinZ() + { + return CalcPoints.Any() + ? CalcPoints.Min(p => p.Z) + : double.NaN; + } + + /// + /// Gets the maximum z. + /// Note: CalcPoints must be used instead of calcPoints as otherwise unclear behaviour of Linq spoils the result + /// Change back to calcPoints and Benchmark4_01l in the WaternetCreatorTests will fail! + /// + /// + public double GetMaxZ() + { + if (isFrozen) { - return CalcPoints.Any() - ? CalcPoints.Min(p => p.Z) - : double.NaN; + return frozenMaxZ; } - /// - /// Gets the maximum z. - /// Note: CalcPoints must be used instead of calcPoints as otherwise unclear behaviour of Linq spoils the result - /// Change back to calcPoints and Benchmark4_01l in the WaternetCreatorTests will fail! - /// - /// - public double GetMaxZ() + return CalcPoints.Any() + ? CalcPoints.Max(p => p.Z) + : double.NaN; + } + + /// + /// The minimal X value among . + /// Note: CalcPoints must be used instead of calcPoints as otherwise unclear behaviour of Linq spoils the result + /// Change back to calcPoints and Benchmark4_01l in the WaternetCreatorTests will fail! + /// + /// The minimal X value or in case there are no points. + public double GetMinX() + { + if (isFrozen && !hasNaNx) { - if (isFrozen) - { - return frozenMaxZ; - } + return sortedPoints[0].X; + } - return CalcPoints.Any() - ? CalcPoints.Max(p => p.Z) - : double.NaN; + return CalcPoints.Any(p => !double.IsNaN(p.X)) + ? CalcPoints.Where(p => !double.IsNaN(p.X)).Min(p => p.X) + : double.NaN; + } + + /// + /// Gets the maximum x. + /// Note: CalcPoints must be used instead of calcPoints as otherwise unclear behaviour of Linq spoils the result + /// Change back to calcPoints and Benchmark4_01l in the WaternetCreatorTests will fail! + /// + /// + public double GetMaxX() + { + if (isFrozen && !hasNaNx) + { + return sortedPoints[sortedPoints.Count - 1].X; } - /// - /// The minimal X value among . - /// Note: CalcPoints must be used instead of calcPoints as otherwise unclear behaviour of Linq spoils the result - /// Change back to calcPoints and Benchmark4_01l in the WaternetCreatorTests will fail! - /// - /// The minimal X value or in case there are no points. - public double GetMinX() + return CalcPoints.Any(p => !double.IsNaN(p.X)) + ? CalcPoints.Where(p => !double.IsNaN(p.X)).Max(p => p.X) + : double.NaN; + } + + /// + /// Finds all vertical XZ intersections with this geometric point string, returning + /// the highest value among the intersections. + /// + /// X coordinate + /// + /// The z value determined by or + /// when is empty. + /// + /// + /// Uses constant extrapolation and linear interpolation. + /// + public double GetZAtUnsortedX(double x) + { + if (calcPoints.Count > 0) { - if (isFrozen && !hasNaNx) + var verticalLineAtX = new Line(new Point2D + { + X = x, + Z = GetMaxZ() + }, + new Point2D + { + X = x, + Z = GetMinZ() + }); + if (Math.Abs(verticalLineAtX.BeginPoint.Z - verticalLineAtX.EndPoint.Z) < GeometryConstants.Accuracy) { - return sortedPoints[0].X; + verticalLineAtX.BeginPoint.Z += 1.0; + verticalLineAtX.EndPoint.Z -= 1.0; } - return CalcPoints.Any(p => !double.IsNaN(p.X)) - ? CalcPoints.Where(p => !double.IsNaN(p.X)).Min(p => p.X) - : double.NaN; + IList intersectionPoints = IntersectionPointsXzWithLineXz(verticalLineAtX); + if (intersectionPoints.Count != 0) + { + return intersectionPoints.Max(gp => gp.Z); + } + + // Remain consistent with GetZAtX, do constant extrapolation: + double zAtX; + if (DoConstantExtrapolationXz(x, out zAtX)) + { + return zAtX; + } } - /// - /// Gets the maximum x. - /// Note: CalcPoints must be used instead of calcPoints as otherwise unclear behaviour of Linq spoils the result - /// Change back to calcPoints and Benchmark4_01l in the WaternetCreatorTests will fail! - /// - /// - public double GetMaxX() + return double.NaN; + } + + /// + /// Retrieves the Z value for the given x. + /// + /// X coordinate + /// + /// The z value determined by or + /// when is empty. + /// + /// + /// Uses constant extrapolation and linear interpolation. + /// + public virtual double GetZatX(double x) + { + if (calcPoints.Any()) { - if (isFrozen && !hasNaNx) + double zAtX; + if (DoConstantExtrapolationXz(x, out zAtX)) { - return sortedPoints[sortedPoints.Count - 1].X; + return zAtX; } - - return CalcPoints.Any(p => !double.IsNaN(p.X)) - ? CalcPoints.Where(p => !double.IsNaN(p.X)).Max(p => p.X) - : double.NaN; } - /// - /// Finds all vertical XZ intersections with this geometric point string, returning - /// the highest value among the intersections. - /// - /// X coordinate - /// - /// The z value determined by or - /// when is empty. - /// - /// - /// Uses constant extrapolation and linear interpolation. - /// - public double GetZAtUnsortedX(double x) + for (var i = 0; i < calcPoints.Count - 1; i++) { - if (calcPoints.Count > 0) - { - var verticalLineAtX = new Line(new Point2D - { - X = x, - Z = GetMaxZ() - }, - new Point2D - { - X = x, - Z = GetMinZ() - }); - if (Math.Abs(verticalLineAtX.BeginPoint.Z - verticalLineAtX.EndPoint.Z) < GeometryConstants.Accuracy) - { - verticalLineAtX.BeginPoint.Z += 1.0; - verticalLineAtX.EndPoint.Z -= 1.0; - } + Point2D current = calcPoints[i]; + Point2D next = calcPoints[i + 1]; - IList intersectionPoints = IntersectionPointsXzWithLineXz(verticalLineAtX); - if (intersectionPoints.Count != 0) - { - return intersectionPoints.Max(gp => gp.Z); - } + double leftOffset = x - current.X; + double rightOffset = next.X - x; - // Remain consistent with GetZAtX, do constant extrapolation: - double zAtX; - if (DoConstantExtrapolationXz(x, out zAtX)) - { - return zAtX; - } + if (Math.Abs(leftOffset) < epsilon) + { + return current.Z; } - return double.NaN; - } - - /// - /// Retrieves the Z value for the given x. - /// - /// X coordinate - /// - /// The z value determined by or - /// when is empty. - /// - /// - /// Uses constant extrapolation and linear interpolation. - /// - public virtual double GetZatX(double x) - { - if (calcPoints.Any()) + if (Math.Abs(rightOffset) < epsilon) { - double zAtX; - if (DoConstantExtrapolationXz(x, out zAtX)) - { - return zAtX; - } + return next.Z; } - for (var i = 0; i < calcPoints.Count - 1; i++) + if (leftOffset >= 0 && rightOffset >= 0) { - Point2D current = calcPoints[i]; - Point2D next = calcPoints[i + 1]; + double fraction = leftOffset / (leftOffset + rightOffset); - double leftOffset = x - current.X; - double rightOffset = next.X - x; + return (1.0 - fraction) * current.Z + fraction * next.Z; + } + } - if (Math.Abs(leftOffset) < epsilon) - { - return current.Z; - } + return Double.NaN; + } - if (Math.Abs(rightOffset) < epsilon) - { - return next.Z; - } + /// + /// Gets the z at x starting from index i in the point list. + /// This is only meant as a fast(er) version for GetZAtX for lists that are sorted by X, so use with care. + /// + /// The x. + /// The i. + /// + public virtual double GetZatX(double x, ref int i) + { + for (; i < calcPoints.Count - 1; i++) + { + Point2D current = calcPoints[i]; + double leftOffset = x - current.X; + Point2D next = calcPoints[i + 1]; + double rightOffset = next.X - x; - if (leftOffset >= 0 && rightOffset >= 0) - { - double fraction = leftOffset / (leftOffset + rightOffset); + if (leftOffset < epsilon) + { + return current.Z; + } - return (1.0 - fraction) * current.Z + fraction * next.Z; - } + if (rightOffset >= epsilon) + { + double fraction = leftOffset / (leftOffset + rightOffset); + return (1.0 - fraction) * current.Z + fraction * next.Z; } - return Double.NaN; + if (i + 1 == calcPoints.Count - 1) + { + return next.Z; + } } - /// - /// Gets the z at x starting from index i in the point list. - /// This is only meant as a fast(er) version for GetZAtX for lists that are sorted by X, so use with care. - /// - /// The x. - /// The i. - /// - public virtual double GetZatX(double x, ref int i) - { - for (; i < calcPoints.Count - 1; i++) - { - Point2D current = calcPoints[i]; - double leftOffset = x - current.X; - Point2D next = calcPoints[i + 1]; - double rightOffset = next.X - x; + return Double.NaN; + } - if (leftOffset < epsilon) - { - return current.Z; - } + /// + /// Gets all z-values at x for a line. + /// + /// The x. + /// + public List GetAllZatXForLine(double x) + { + return GetAllZatX(x, false); + } - if (rightOffset >= epsilon) - { - double fraction = leftOffset / (leftOffset + rightOffset); - return (1.0 - fraction) * current.Z + fraction * next.Z; - } + /// + /// Gets all z values at x for a surface. + /// + /// The x. + /// + public List GetAllZatXForSurface(double x) + { + return GetAllZatX(x, true); + } - if (i + 1 == calcPoints.Count - 1) - { - return next.Z; - } + /// + /// Finds the first intersection of the line with a given horizontal line. + /// + /// The height level of the horizontal line. + /// Optional: Discard intersections whose X value is less then + /// or equal to this value. + /// The first intersection point matching the criteria. + /// + public double GetXatZ(double z, double xmin = Double.MinValue) + { + return GetXatZStartingAt(0, z, xmin); + } + + /// + /// Finds the first intersection of the line with a given horizontal line. + /// + /// Start considering instances from + /// starting from this index. + /// The height level of the horizontal line. + /// Optional: Discard intersections whose X value is less then + /// or equal to this value. + /// The first intersection point matching the criteria. + /// + public double GetXatZStartingAt(int start, double z, double xmin = Double.MinValue) + { + for (int i = start; i < calcPoints.Count - 1; i++) + { + Point2D current = calcPoints[i]; + Point2D next = calcPoints[i + 1]; + + if (IsSegmentNotCrossingZ(z, current, next)) // Performance micro-optimization + { + continue; } - return Double.NaN; + double x = GetXIntersectingZ(z, current, next); + if (x > xmin) + { + return x; + } } - /// - /// Gets all z-values at x for a line. - /// - /// The x. - /// - public List GetAllZatXForLine(double x) + return Double.NaN; + } + + /// + /// Gets the points at x. + /// + /// The x. + /// + public IEnumerable GetPointsAtX(double x) + { + return (from Point2D point in calcPoints + where point.X.AlmostEquals(x, GeometryPoint.Precision) + select point); + } + + /// + /// Gets the Point2D at the specified coordinates. + /// + /// The x. + /// The z. + /// + public Point2D GetPointAt(double x, double z) + { + return calcPoints.FirstOrDefault( + point => point.X.AlmostEquals(x, GeometryPoint.Precision) && + point.Z.AlmostEquals(z, GeometryPoint.Precision)); + } + + /// + /// Gets the GeometryPoint at the specified coordinates. + /// + /// The x. + /// The z. + /// + public GeometryPoint GetGeometryPointAt(double x, double z) + { + return Points.FirstOrDefault( + point => point.X.AlmostEquals(x, GeometryPoint.Precision) && + point.Z.AlmostEquals(z, GeometryPoint.Precision)); + } + + //#Bka: this should never be 1 method! Split it into 2 methods + // It is used for sliplanes only and therefor uses Points instead of calcPoints + /// + /// Adds the or remove list object. + /// + /// a point. + /// if set to true [a to add]. + /// a index. + public virtual void AddOrRemoveListObject(object aPoint, bool aToAdd, int aIndex) + { + var point = aPoint as GeometryPoint; + if (point == null) { - return GetAllZatX(x, false); + return; } - /// - /// Gets all z values at x for a surface. - /// - /// The x. - /// - public List GetAllZatXForSurface(double x) + if (aToAdd) { - return GetAllZatX(x, true); + Points.Insert(aIndex, point); //#bka what happens if index already exists? what if point exists? What if both??? } - - /// - /// Finds the first intersection of the line with a given horizontal line. - /// - /// The height level of the horizontal line. - /// Optional: Discard intersections whose X value is less then - /// or equal to this value. - /// The first intersection point matching the criteria. - /// - public double GetXatZ(double z, double xmin = Double.MinValue) + else { - return GetXatZStartingAt(0, z, xmin); + if (Points.Contains(point)) + { + Points.Remove(point); + } } + } - /// - /// Finds the first intersection of the line with a given horizontal line. - /// - /// Start considering instances from - /// starting from this index. - /// The height level of the horizontal line. - /// Optional: Discard intersections whose X value is less then - /// or equal to this value. - /// The first intersection point matching the criteria. - /// - public double GetXatZStartingAt(int start, double z, double xmin = Double.MinValue) + /// + /// Returns ALL intersection points that are found for a line costructed at level z, + /// including the double ones as the number of points can be relevant! + /// + /// the level at which the line for intersections is constructed. + /// + public IList IntersectionsXAtZ(double z) + { + var intersectionsX = new List(); + + var lineAtZ = new Line { - for (int i = start; i < calcPoints.Count - 1; i++) - { - Point2D current = calcPoints[i]; - Point2D next = calcPoints[i + 1]; + BeginPoint = new Point2D(GetMinX(), z), + EndPoint = new Point2D(GetMaxX(), z) + }; - if (IsSegmentNotCrossingZ(z, current, next)) // Performance micro-optimization - { - continue; - } + intersectionsX.AddRange(IntersectionPointsXzWithLineXzWithAllPoints(lineAtZ).Select(gp => gp.X)); + return intersectionsX; + } - double x = GetXIntersectingZ(z, current, next); - if (x > xmin) - { - return x; - } - } + /// + /// Returns ALL intersection points that are found, including the double ones as the number of points can be relevant! + /// + /// + /// + public IList IntersectionPointsXzWithLineXzWithAllPoints(Line line) + { + return IntersectionPointsWithLineCore(line, true); + } - return Double.NaN; - } + /// + /// Returns the UNIQUE intersectionpoints (so can not be used where number of interscections is relevant) + /// + /// + /// + public IList IntersectionPointsXzWithLineXz(Line line) + { + return IntersectionPointsWithLineCore(line, false); + } - /// - /// Gets the points at x. - /// - /// The x. - /// - public IEnumerable GetPointsAtX(double x) - { - return (from Point2D point in calcPoints - where point.X.AlmostEquals(x, GeometryPoint.Precision) - select point); - } + /// + /// Find intersection (xz-plane) from this surface with another surface + /// or phratic line + /// + /// + /// + /// Considers all in to + /// such that they describe a closed loop. + /// + public IList ClosedGeometryIntersectionXzPointsWithGeometryPointList(IList list) + { + return IntersectWithPointsListCore(list, true); + } - /// - /// Gets the Point2D at the specified coordinates. - /// - /// The x. - /// The z. - /// - public Point2D GetPointAt(double x, double z) - { - return calcPoints.FirstOrDefault( - point => point.X.AlmostEquals(x, GeometryPoint.Precision) && - point.Z.AlmostEquals(z, GeometryPoint.Precision)); - } + /// + /// Finds all intersections in the XZ-plane the given . + /// + /// The geometry point string. + /// + /// + public List IntersectionXzPointsWithGeometryString(GeometryPointString externalSurface) + { + return IntersectionXzPointsWithGeometryPointList(externalSurface.CalcPoints); + } - /// - /// Gets the GeometryPoint at the specified coordinates. - /// - /// The x. - /// The z. - /// - public GeometryPoint GetGeometryPointAt(double x, double z) - { - return Points.FirstOrDefault( - point => point.X.AlmostEquals(x, GeometryPoint.Precision) && - point.Z.AlmostEquals(z, GeometryPoint.Precision)); - } + /// + /// Sorts the points by x ascending (only to be used for Surface lines). + /// + public virtual void SortPointsByXAscending() + { + calcPoints.Sort(); + points.Sort(); + } - //#Bka: this should never be 1 method! Split it into 2 methods - // It is used for sliplanes only and therefor uses Points instead of calcPoints - /// - /// Adds the or remove list object. - /// - /// a point. - /// if set to true [a to add]. - /// a index. - public virtual void AddOrRemoveListObject(object aPoint, bool aToAdd, int aIndex) + /// + /// Removes the non ascending points on surface line. + /// + public void RemoveNonAscendingPointsOnSurfaceLine() + { + for (int i = points.Count - 1; i > 0; i--) { - var point = aPoint as GeometryPoint; - if (point == null) + if (Math.Abs(points[i].X - points[i - 1].X) < GeometryConstants.Accuracy) { - return; + if (Math.Abs(points[i].Z - points[i - 1].Z) > GeometryConstants.Accuracy) + { + points.Remove(points[i]); + } } + } + } - if (aToAdd) + /// + /// Gets a part of the geometry point string defined by a begin and end point + /// + /// + /// + /// + public GeometryPointString GetPart(double begin, double end) + { + var part = new GeometryPointString(); + var filling = false; + var filled = false; + + for (var i = 0; i < calcPoints.Count; i++) + { + if (!filling && !filled) { - Points.Insert(aIndex, point); //#bka what happens if index already exists? what if point exists? What if both??? + filling = calcPoints[i].X >= begin - epsilon; } else { - if (Points.Contains(point)) + filled = calcPoints[i].X >= end - epsilon; + if (filled) { - Points.Remove(point); + filling = false; } } - } - /// - /// Returns ALL intersection points that are found for a line costructed at level z, - /// including the double ones as the number of points can be relevant! - /// - /// the level at which the line for intersections is constructed. - /// - public IList IntersectionsXAtZ(double z) - { - var intersectionsX = new List(); - - var lineAtZ = new Line + if (filling) { - BeginPoint = new Point2D(GetMinX(), z), - EndPoint = new Point2D(GetMaxX(), z) - }; - - intersectionsX.AddRange(IntersectionPointsXzWithLineXzWithAllPoints(lineAtZ).Select(gp => gp.X)); - return intersectionsX; + part.calcPoints.Add(calcPoints[i]); + } } - /// - /// Returns ALL intersection points that are found, including the double ones as the number of points can be relevant! - /// - /// - /// - public IList IntersectionPointsXzWithLineXzWithAllPoints(Line line) - { - return IntersectionPointsWithLineCore(line, true); - } + var beginPoint = new Point2D(begin, GetZatX(begin)); + var endPoint = new Point2D(end, GetZatX(end)); - /// - /// Returns the UNIQUE intersectionpoints (so can not be used where number of interscections is relevant) - /// - /// - /// - public IList IntersectionPointsXzWithLineXz(Line line) + if (part.calcPoints.Count == 0) { - return IntersectionPointsWithLineCore(line, false); + part.calcPoints.Add(beginPoint); + part.calcPoints.Add(endPoint); } - /// - /// Find intersection (xz-plane) from this surface with another surface - /// or phratic line - /// - /// - /// - /// Considers all in to - /// such that they describe a closed loop. - /// - public IList ClosedGeometryIntersectionXzPointsWithGeometryPointList(IList list) + if (!part.calcPoints[0].LocationEquals(beginPoint)) { - return IntersectWithPointsListCore(list, true); + part.calcPoints.Insert(0, beginPoint); } - /// - /// Finds all intersections in the XZ-plane the given . - /// - /// The geometry point string. - /// - /// - public List IntersectionXzPointsWithGeometryString(GeometryPointString externalSurface) + if (!part.calcPoints[part.calcPoints.Count - 1].LocationEquals(endPoint)) { - return IntersectionXzPointsWithGeometryPointList(externalSurface.CalcPoints); + part.calcPoints.Add(endPoint); } - /// - /// Sorts the points by x ascending (only to be used for Surface lines). - /// - public virtual void SortPointsByXAscending() - { - calcPoints.Sort(); - points.Sort(); - } + SyncPoints(); + return part; + } - /// - /// Removes the non ascending points on surface line. - /// - public void RemoveNonAscendingPointsOnSurfaceLine() + /// + /// Removes all double points at a location, if consecutive + /// + public void CondensePoints() + { + for (int i = calcPoints.Count - 1; i > 0; i--) { - for (int i = points.Count - 1; i > 0; i--) + if (calcPoints[i].LocationEquals(calcPoints[i - 1])) { - if (Math.Abs(points[i].X - points[i - 1].X) < GeometryConstants.Accuracy) - { - if (Math.Abs(points[i].Z - points[i - 1].Z) > GeometryConstants.Accuracy) - { - points.Remove(points[i]); - } - } + calcPoints.RemoveAt(i); } } + } - /// - /// Gets a part of the geometry point string defined by a begin and end point - /// - /// - /// - /// - public GeometryPointString GetPart(double begin, double end) - { - var part = new GeometryPointString(); - var filling = false; - var filled = false; + /// + /// Removes all points which don't influence the shape of the line + /// + public void RemoveUnnecessaryPoints() + { + const double slopeTolerance = 1E-10; - for (var i = 0; i < calcPoints.Count; i++) - { - if (!filling && !filled) - { - filling = calcPoints[i].X >= begin - epsilon; - } - else - { - filled = calcPoints[i].X >= end - epsilon; - if (filled) - { - filling = false; - } - } + CondensePoints(); - if (filling) - { - part.calcPoints.Add(calcPoints[i]); - } - } + for (int i = calcPoints.Count - 2; i > 1; i--) + { + // if the slope of the line before the point is equal to the slope after the point, the point can be removed + double slopeBefore = (calcPoints[i].Z - calcPoints[i - 1].Z) / (calcPoints[i].X - calcPoints[i - 1].X); + double slopeAfter = (calcPoints[i + 1].Z - calcPoints[i].Z) / (calcPoints[i + 1].X - calcPoints[i].X); - var beginPoint = new Point2D(begin, GetZatX(begin)); - var endPoint = new Point2D(end, GetZatX(end)); - - if (part.calcPoints.Count == 0) + if (Routines2D.AreEqual(slopeBefore, slopeAfter, slopeTolerance)) { - part.calcPoints.Add(beginPoint); - part.calcPoints.Add(endPoint); + calcPoints.RemoveAt(i); } + } + } - if (!part.calcPoints[0].LocationEquals(beginPoint)) + /// + /// Synchronizes the calculation points. + /// + public void SyncCalcPoints() + { + calcPoints.Clear(); + foreach (GeometryPoint geometryPoint in Points) + { + var p2D = new Point2D { - part.calcPoints.Insert(0, beginPoint); - } + X = geometryPoint.X, + Z = geometryPoint.Z + }; + calcPoints.Add(p2D); + } + } - if (!part.calcPoints[part.calcPoints.Count - 1].LocationEquals(endPoint)) + /// + /// Synchronizes the points. + /// + public void SyncPoints() + { + points.Clear(); + foreach (Point2D p2D in calcPoints) + { + var geometryPoint = new GeometryPoint { - part.calcPoints.Add(endPoint); - } - - SyncPoints(); - return part; + X = p2D.X, + Z = p2D.Z + }; + points.Add(geometryPoint); } + } - /// - /// Removes all double points at a location, if consecutive - /// - public void CondensePoints() + /// + /// Gets the surrounding rectangle around the geometry point string + /// + /// + public override GeometryBounds GetGeometryBounds() + { + if (!Points.Any()) { - for (int i = calcPoints.Count - 1; i > 0; i--) + // Sync with calcPoints + SyncPoints(); + // if still no points, then return null + if (!Points.Any()) { - if (calcPoints[i].LocationEquals(calcPoints[i - 1])) - { - calcPoints.RemoveAt(i); - } + return null; } } - /// - /// Removes all points which don't influence the shape of the line - /// - public void RemoveUnnecessaryPoints() + GeometryBounds bounds = Points[0].GetGeometryBounds(); + for (var i = 1; i < Points.Count; i++) { - const double slopeTolerance = 1E-10; + GeometryPoint point = Points[i]; - CondensePoints(); + bounds.Left = Math.Min(bounds.Left, point.X); + bounds.Right = Math.Max(bounds.Right, point.X); + bounds.Top = Math.Max(bounds.Top, point.Z); + bounds.Bottom = Math.Min(bounds.Bottom, point.Z); + } - for (int i = calcPoints.Count - 2; i > 1; i--) - { - // if the slope of the line before the point is equal to the slope after the point, the point can be removed - double slopeBefore = (calcPoints[i].Z - calcPoints[i - 1].Z) / (calcPoints[i].X - calcPoints[i - 1].X); - double slopeAfter = (calcPoints[i + 1].Z - calcPoints[i].Z) / (calcPoints[i + 1].X - calcPoints[i].X); + return bounds; + } - if (Routines2D.AreEqual(slopeBefore, slopeAfter, slopeTolerance)) - { - calcPoints.RemoveAt(i); - } - } - } + private IList IntersectionPointsXzClosedStringWithLineXz(Line line) + { + IList intersectionPointsWithLine = IntersectionPointsXzWithLineXz(line); - /// - /// Synchronizes the calculation points. - /// - public void SyncCalcPoints() + // close the poly line + if (calcPoints.Count > 0) { - calcPoints.Clear(); - foreach (GeometryPoint geometryPoint in Points) - { - var p2D = new Point2D - { - X = geometryPoint.X, - Z = geometryPoint.Z - }; - calcPoints.Add(p2D); - } + DoIntersectAndAddToCollection(line, calcPoints[calcPoints.Count - 1], calcPoints[0], + intersectionPointsWithLine, false); } - /// - /// Synchronizes the points. - /// - public void SyncPoints() - { - points.Clear(); - foreach (Point2D p2D in calcPoints) - { - var geometryPoint = new GeometryPoint - { - X = p2D.X, - Z = p2D.Z - }; - points.Add(geometryPoint); - } - } + return intersectionPointsWithLine; + } - /// - /// Gets the surrounding rectangle around the geometry point string - /// - /// - public override GeometryBounds GetGeometryBounds() + /// + /// Finds all intersections in the XZ-plane the given list. + /// + /// The list. + /// + /// + /// + private List IntersectionXzPointsWithGeometryPointList(IList list) + { + return IntersectWithPointsListCore(list, false); + } + + /// + /// Checks if constant extrapolation can be applied, and if so set the Z value. + /// + /// The evaluated X coordinate. + /// Output param: Extrapolated Z value, or + /// when no extrapolation is possible. + /// True if extrapolation possible; false otherwise. + private bool DoConstantExtrapolationXz(double x, out double z) + { + if (calcPoints.Count > 0) { - if (!Points.Any()) + Point2D first = calcPoints[0]; + if (x < first.X || Math.Abs(x - first.X) < epsilon) { - // Sync with calcPoints - SyncPoints(); - // if still no points, then return null - if (!Points.Any()) - { - return null; - } + z = first.Z; + return true; } - GeometryBounds bounds = Points[0].GetGeometryBounds(); - for (var i = 1; i < Points.Count; i++) + Point2D last = calcPoints[calcPoints.Count - 1]; + if (x > last.X) { - GeometryPoint point = Points[i]; - - bounds.Left = Math.Min(bounds.Left, point.X); - bounds.Right = Math.Max(bounds.Right, point.X); - bounds.Top = Math.Max(bounds.Top, point.Z); - bounds.Bottom = Math.Min(bounds.Bottom, point.Z); + z = last.Z; + return true; } - - return bounds; } - private IList IntersectionPointsXzClosedStringWithLineXz(Line line) - { - IList intersectionPointsWithLine = IntersectionPointsXzWithLineXz(line); + z = double.NaN; + return false; + } - // close the poly line - if (calcPoints.Count > 0) - { - DoIntersectAndAddToCollection(line, calcPoints[calcPoints.Count - 1], calcPoints[0], - intersectionPointsWithLine, false); - } + /// + /// Can be used for a Line or for a Surface where a surface is supposed to closed. + /// In case of a waternet line it is possible + /// to have more z values at a give x coor + /// Furthermore a z is not needed at al x values + /// + /// + /// + /// + private List GetAllZatX(double x, bool asSurface) + { + var result = new List(); - return intersectionPointsWithLine; + if (calcPoints.Count == 1 && Math.Abs(calcPoints[0].X - x) < epsilon) + { + result.Add(calcPoints[0].Z); } - /// - /// Finds all intersections in the XZ-plane the given list. - /// - /// The list. - /// - /// - /// - private List IntersectionXzPointsWithGeometryPointList(IList list) + int pointsCount = calcPoints.Count - 1; + if (asSurface) { - return IntersectWithPointsListCore(list, false); + pointsCount = calcPoints.Count; } - /// - /// Checks if constant extrapolation can be applied, and if so set the Z value. - /// - /// The evaluated X coordinate. - /// Output param: Extrapolated Z value, or - /// when no extrapolation is possible. - /// True if extrapolation possible; false otherwise. - private bool DoConstantExtrapolationXz(double x, out double z) + for (var i = 0; i < pointsCount; i++) { - if (calcPoints.Count > 0) + Point2D current; + Point2D next; + if (i == calcPoints.Count - 1) { - Point2D first = calcPoints[0]; - if (x < first.X || Math.Abs(x - first.X) < epsilon) - { - z = first.Z; - return true; - } - - Point2D last = calcPoints[calcPoints.Count - 1]; - if (x > last.X) - { - z = last.Z; - return true; - } + current = calcPoints[i]; + next = calcPoints[0]; } + else + { + current = calcPoints[i]; + next = calcPoints[i + 1]; + } - z = double.NaN; - return false; - } + double leftOffset = x - current.X; + double rightOffset = next.X - x; - /// - /// Can be used for a Line or for a Surface where a surface is supposed to closed. - /// In case of a waternet line it is possible - /// to have more z values at a give x coor - /// Furthermore a z is not needed at al x values - /// - /// - /// - /// - private List GetAllZatX(double x, bool asSurface) - { - var result = new List(); - - if (calcPoints.Count == 1 && Math.Abs(calcPoints[0].X - x) < epsilon) + var matchedWithAPoint = false; + if (Math.Abs(leftOffset) < epsilon) { - result.Add(calcPoints[0].Z); + result.Add(current.Z); + matchedWithAPoint = true; } - int pointsCount = calcPoints.Count - 1; - if (asSurface) + if (Math.Abs(rightOffset) < epsilon) { - pointsCount = calcPoints.Count; + result.Add(next.Z); + matchedWithAPoint = true; } - for (var i = 0; i < pointsCount; i++) + if (!matchedWithAPoint) { - Point2D current; - Point2D next; - if (i == calcPoints.Count - 1) + if (leftOffset > 0 && rightOffset > 0) { - current = calcPoints[i]; - next = calcPoints[0]; - } - else - { - current = calcPoints[i]; - next = calcPoints[i + 1]; - } + double fraction = leftOffset / (leftOffset + rightOffset); - double leftOffset = x - current.X; - double rightOffset = next.X - x; - - var matchedWithAPoint = false; - if (Math.Abs(leftOffset) < epsilon) - { - result.Add(current.Z); - matchedWithAPoint = true; + result.Add((1.0 - fraction) * current.Z + fraction * next.Z); } - if (Math.Abs(rightOffset) < epsilon) + // if both ofsets are negative the waterline goes back + if ((leftOffset < 0) && (rightOffset < 0)) { - result.Add(next.Z); - matchedWithAPoint = true; - } + double fraction = rightOffset / (rightOffset + leftOffset); - if (!matchedWithAPoint) - { - if (leftOffset > 0 && rightOffset > 0) - { - double fraction = leftOffset / (leftOffset + rightOffset); - - result.Add((1.0 - fraction) * current.Z + fraction * next.Z); - } - - // if both ofsets are negative the waterline goes back - if ((leftOffset < 0) && (rightOffset < 0)) - { - double fraction = rightOffset / (rightOffset + leftOffset); - - result.Add((1.0 - fraction) * next.Z + fraction * current.Z); - } + result.Add((1.0 - fraction) * next.Z + fraction * current.Z); } } - - return result.Distinct().ToList(); } - private static bool IsSegmentNotCrossingZ(double z, Point2D current, Point2D next) - { - if (double.IsNaN(z)) - { - return true; - } + return result.Distinct().ToList(); + } - double leftOffset = Math.Abs(current.Z - z); - double rightOffset = Math.Abs(next.Z - z); - - int currentSign = leftOffset < epsilon ? 0 : Math.Sign(current.Z - z); - int nextSign = rightOffset < epsilon ? 0 : Math.Sign(next.Z - z); - return currentSign == nextSign && currentSign != 0; + private static bool IsSegmentNotCrossingZ(double z, Point2D current, Point2D next) + { + if (double.IsNaN(z)) + { + return true; } - private static double GetXIntersectingZ(double z, Point2D current, Point2D next) - { - double leftOffset = Math.Abs(current.Z - z); - if (leftOffset < epsilon) - { - return current.X; - } + double leftOffset = Math.Abs(current.Z - z); + double rightOffset = Math.Abs(next.Z - z); - double rightOffset = Math.Abs(next.Z - z); - if (rightOffset < epsilon) - { - return next.X; - } + int currentSign = leftOffset < epsilon ? 0 : Math.Sign(current.Z - z); + int nextSign = rightOffset < epsilon ? 0 : Math.Sign(next.Z - z); + return currentSign == nextSign && currentSign != 0; + } - double fraction = leftOffset / (leftOffset + rightOffset); - return GeneralMathRoutines.LinearInterpolate(current.X, next.X, fraction); + private static double GetXIntersectingZ(double z, Point2D current, Point2D next) + { + double leftOffset = Math.Abs(current.Z - z); + if (leftOffset < epsilon) + { + return current.X; } - private IList IntersectionPointsWithLineCore(Line line, bool allowDuplicates) + double rightOffset = Math.Abs(next.Z - z); + if (rightOffset < epsilon) { - var intersectionPointsWithLine = new List(); + return next.X; + } - for (var pointIndex = 0; pointIndex < calcPoints.Count - 1; pointIndex++) - { - DoIntersectAndAddToCollection(line, calcPoints[pointIndex], calcPoints[pointIndex + 1], - intersectionPointsWithLine, allowDuplicates); - } + double fraction = leftOffset / (leftOffset + rightOffset); + return GeneralMathRoutines.LinearInterpolate(current.X, next.X, fraction); + } - return intersectionPointsWithLine; - } + private IList IntersectionPointsWithLineCore(Line line, bool allowDuplicates) + { + var intersectionPointsWithLine = new List(); - private static void DoIntersectAndAddToCollection(Line line, Point2D begin, Point2D end, ICollection intersectionPointsWithLine, bool allowDuplicates) + for (var pointIndex = 0; pointIndex < calcPoints.Count - 1; pointIndex++) { - var lineInPoly = new Line - { - BeginPoint = begin, - EndPoint = end - }; - Point2D intersectionPoint = lineInPoly.GetIntersectPointXz(line); - if (intersectionPoint != null && (allowDuplicates || NoPointSameXzLocation(intersectionPointsWithLine, intersectionPoint))) - { - intersectionPointsWithLine.Add(intersectionPoint); - } + DoIntersectAndAddToCollection(line, calcPoints[pointIndex], calcPoints[pointIndex + 1], + intersectionPointsWithLine, allowDuplicates); } - private static bool NoPointSameXzLocation(IEnumerable collection, Point2D point) + return intersectionPointsWithLine; + } + + private static void DoIntersectAndAddToCollection(Line line, Point2D begin, Point2D end, ICollection intersectionPointsWithLine, bool allowDuplicates) + { + var lineInPoly = new Line { - return !collection.Any( - p => Math.Abs(p.X - point.X) < GeometryConstants.Accuracy && - Math.Abs(p.Z - point.Z) < GeometryConstants.Accuracy); + BeginPoint = begin, + EndPoint = end + }; + Point2D intersectionPoint = lineInPoly.GetIntersectPointXz(line); + if (intersectionPoint != null && (allowDuplicates || NoPointSameXzLocation(intersectionPointsWithLine, intersectionPoint))) + { + intersectionPointsWithLine.Add(intersectionPoint); } + } - private List IntersectWithPointsListCore(IList list, bool closePointString) + private static bool NoPointSameXzLocation(IEnumerable collection, Point2D point) + { + return !collection.Any( + p => Math.Abs(p.X - point.X) < GeometryConstants.Accuracy && + Math.Abs(p.Z - point.Z) < GeometryConstants.Accuracy); + } + + private List IntersectWithPointsListCore(IList list, bool closePointString) + { + var result = new List(); + var line = new Line(); + for (var externalPointIndex = 0; externalPointIndex < list.Count - 1; externalPointIndex++) { - var result = new List(); - var line = new Line(); - for (var externalPointIndex = 0; externalPointIndex < list.Count - 1; externalPointIndex++) - { - line.BeginPoint = list[externalPointIndex]; - line.EndPoint = list[externalPointIndex + 1]; + line.BeginPoint = list[externalPointIndex]; + line.EndPoint = list[externalPointIndex + 1]; - result.AddRange(!closePointString - ? IntersectionPointsXzWithLineXzWithAllPoints(line) - : IntersectionPointsXzClosedStringWithLineXz(line)); - } - - return result; + result.AddRange(!closePointString + ? IntersectionPointsXzWithLineXzWithAllPoints(line) + : IntersectionPointsXzClosedStringWithLineXz(line)); } + + return result; } } \ No newline at end of file