using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Linq.Expressions;
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
{
private readonly Collection innerLoops;
private Segment2D[] outerLoop;
///
/// Creates a new instance of .
///
public SoilLayer2D()
{
innerLoops = new Collection();
}
///
/// Gets or sets a value representing
/// whether the is an aquifer.
///
public double? IsAquifer { get; set; }
///
/// Gets or sets the above phreatic level for the .
///
public double? AbovePhreaticLevel { get; set; }
///
/// Gets or sets the below phreatic level for the .
///
public double? BelowPhreaticLevel { get; set; }
///
/// Gets or sets the dry unit weight for the .
///
public double? DryUnitWeight { get; set; }
///
/// Gets the outer loop of the as a of ,
/// for which each of the segments are connected to the next.
///
/// Thrown when the in
/// do not form a loop.
public IEnumerable OuterLoop
{
get
{
return outerLoop;
}
internal set
{
var loop = value.ToArray();;
CheckValidLoop(loop);
outerLoop = loop;
}
}
///
/// Gets the of inner loops (as of ,
/// for which each of the segments are connected to the next) of the .
///
public IEnumerable InnerLoops
{
get
{
return innerLoops;
}
}
///
/// Adds an inner loop to the geometry.
///
/// The innerloop to add.
/// Thrown when the in
/// do not form a loop.
internal void AddInnerLoop(IEnumerable innerLoop)
{
var loop = innerLoop.ToArray();
CheckValidLoop(loop);
innerLoops.Add(loop);
}
///
/// Constructs a (1D) based on the and set for the .
///
/// The point from which to take a 1D profile.
/// The bottom level of the .
/// A of .
/// Thrown when any of the or
/// contain a vertical line at .
internal IEnumerable AsPipingSoilLayers(double atX, out double bottom)
{
bottom = Double.MaxValue;
var result = new Collection();
if (OuterLoop != null)
{
IEnumerable outerLoopIntersectionHeights = GetLoopIntersectionHeights(outerLoop, atX);
if (outerLoopIntersectionHeights.Any())
{
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)
{
IsAquifer = IsAquifer.HasValue && IsAquifer.Value.Equals(1.0),
BelowPhreaticLevel = BelowPhreaticLevel,
AbovePhreaticLevel = AbovePhreaticLevel,
DryUnitWeight = DryUnitWeight
});
}
bottom = EnsureBottomOutsideInnerLoop(innerLoopIntersectionHeightPairs, currentBottom);
}
}
return result;
}
private static void CheckValidLoop(Segment2D[] innerLoop)
{
if (innerLoop.Length == 1 || !IsLoopConnected(innerLoop))
{
throw new ArgumentException(Resources.SoilLayer2D_Error_Loop_contains_disconnected_segments);
}
}
private static bool IsLoopConnected(Segment2D[] segments)
{
int segmentCount = segments.Length;
if (segmentCount == 2)
{
return segments[0].Equals(segments[1]);
}
for (int i = 0; i < segmentCount; i++)
{
var segmentA = segments[i];
var segmentB = segments[(i + 1)%segmentCount];
if (!segmentA.IsConnected(segmentB))
{
return false;
}
}
return true;
}
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;
}
///
/// Gets a of heights where the intersects the
/// vertical line at .
///
/// The of which together create a loop.
/// The point on the x-axis where the vertical line is constructed do determine intersections with.
/// A of , representing the height at which the
/// intersects the vertical line at .
/// Thrown when a segment is vertical at and thus
/// no deterministic intersection points can be determined.
private IEnumerable GetLoopIntersectionHeights(IEnumerable loop, double atX)
{
var segment2Ds = loop.ToArray();
if(segment2Ds.Any(segment => IsVerticalAtX(segment, atX)))
{
var message = string.Format(Resources.Error_Can_not_determine_1D_profile_with_vertical_segments_at_x, atX);
throw new SoilLayer2DConversionException(message);
}
return Math2D.SegmentsIntersectionWithVerticalLine(segment2Ds, atX).Select(p => p.Y);
}
private static bool IsVerticalAtX(Segment2D segment, double atX)
{
return segment.FirstPoint.X.Equals(atX) && segment.IsVertical();
}
}
}