using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using Ringtoets.Piping.Data; using Ringtoets.Piping.IO.Calculation; using Ringtoets.Piping.IO.Properties; namespace Ringtoets.Piping.IO.Builders { /// /// This class represents objects which were imported from a DSoilModel database. Instances of this class are transient and are not to be used /// once the DSoilModel database has been imported. /// internal class SoilLayer2D { /// /// Creates a new instance of . /// public SoilLayer2D() { InnerLoops = new Collection>(); } /// /// Gets the outer loop of the as a of . /// internal HashSet OuterLoop { get; set; } /// /// Gets the of inner loops (as of ) of the . /// internal Collection> InnerLoops { get; private set; } /// /// Constructs a (1D) based on the and set for the . /// /// The point from which to take a 1D profile. /// The bottom level of the . /// internal IEnumerable AsPipingSoilLayers(double atX, out double bottom) { bottom = Double.MaxValue; var result = new Collection(); if (OuterLoop != null) { Collection outerLoopIntersectionHeights = GetLoopIntersectionHeights(OuterLoop, atX); if (outerLoopIntersectionHeights.Count > 0) { IEnumerable> innerLoopsIntersectionHeights = InnerLoops.Select(loop => GetLoopIntersectionHeights(loop, atX)); IEnumerable> innerLoopIntersectionHeightPairs = GetOrderedStartAndEndPairsIn1D(innerLoopsIntersectionHeights).ToList(); IEnumerable> outerLoopIntersectionHeightPairs = GetOrderedStartAndEndPairsIn1D(outerLoopIntersectionHeights).ToList(); var currentBottom = outerLoopIntersectionHeightPairs.First().Item1; var heights = new List(); heights.AddRange(innerLoopIntersectionHeightPairs.Where(p => p.Item1 >= currentBottom).Select(p => p.Item1)); heights.AddRange(outerLoopIntersectionHeightPairs.Select(p => p.Item2)); foreach (var height in heights.Where(height => !innerLoopIntersectionHeightPairs.Any(tuple => HeightInInnerLoop(tuple, height)))) { result.Add(new PipingSoilLayer(height)); } bottom = EnsureBottomOutsideInnerLoop(innerLoopIntersectionHeightPairs, currentBottom); } } return result; } private double EnsureBottomOutsideInnerLoop(IEnumerable> innerLoopIntersectionHeightPairs, double bottom) { var newBottom = bottom; var heigthPairArray = innerLoopIntersectionHeightPairs.ToList(); var overlappingInnerLoop = heigthPairArray.FirstOrDefault(t => BottomInInnerLoop(t, newBottom)); while (overlappingInnerLoop != null) { newBottom = overlappingInnerLoop.Item2; overlappingInnerLoop = heigthPairArray.FirstOrDefault(t => BottomInInnerLoop(t, newBottom)); } return newBottom; } private bool HeightInInnerLoop(Tuple tuple, double height) { return height <= tuple.Item2 && height > tuple.Item1; } private bool BottomInInnerLoop(Tuple tuple, double height) { return height < tuple.Item2 && height >= tuple.Item1; } private IEnumerable> GetOrderedStartAndEndPairsIn1D(IEnumerable> innerLoopsIntersectionPoints) { Collection> result = new Collection>(); foreach (var innerLoopIntersectionPoints in innerLoopsIntersectionPoints) { foreach (var tuple in GetOrderedStartAndEndPairsIn1D(innerLoopIntersectionPoints)) { result.Add(tuple); } } return result; } private static Collection> GetOrderedStartAndEndPairsIn1D(IEnumerable innerLoopIntersectionPoints) { var result = new Collection>(); var orderedHeights = innerLoopIntersectionPoints.OrderBy(v => v).ToList(); for (int i = 0; i < orderedHeights.Count; i = i+2) { var first = orderedHeights[i]; var second = orderedHeights[i+1]; result.Add(new Tuple(first, second)); } return result; } private Collection GetLoopIntersectionHeights(HashSet loop, double atX) { Collection intersectionPointY = new Collection(); ; for (int segmentIndex = 0; segmentIndex < loop.Count; segmentIndex++) { var intersectionPoint = GetSegmentIntersectionAtX(loop, segmentIndex, atX); if (intersectionPoint.Length > 0) { intersectionPointY.Add(intersectionPoint[1]); } else if (IsVerticalAtX(GetSegmentWithStartAtIndex(loop, segmentIndex), atX)) { throw new SoilLayer2DConversionException(String.Format(Resources.Error_CanNotDetermine1DProfileWithVerticalSegmentsAtX, atX)); } } return intersectionPointY; } private bool IsVerticalAtX(Point3D[] segment, double atX) { return Math.Abs(segment[0].X - atX) + Math.Abs(segment[1].X - atX) < Math2D.EpsilonForComparisons; } private static Point3D[] GetSegmentWithStartAtIndex(HashSet loop, int i) { var current = loop.ElementAt(i); var next = loop.ElementAt((i + 1) % loop.Count); return new[] { current, next }; } private static double[] GetSegmentIntersectionAtX(HashSet loop, int segmentIndex, double atX) { Point3D[] segment = GetSegmentWithStartAtIndex(loop, segmentIndex); return Math2D.LineSegmentIntersectionWithLine(new[] { segment[0].X, segment[1].X, atX, atX }, new[] { segment[0].Z, segment[1].Z, 0, 1 }); } } }