// 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.Collections.Generic;
using System.Linq;
using Deltares.DamEngine.Data.Geometry;
using Deltares.DamEngine.Data.Standard;
namespace Deltares.DamEngine.Data.Geotechnics;
///
///When publisherEventArgs vertical is drawn in the Geometry, this reports the intersection layers.
///
/// Step 1: To Determine the possibility of Surface and Vertical intersection, Min/Max of each surface
/// is checked with Vertical's X coordinate.
/// Step 2: Intersection Points of each surface with the vertical are determined and added to pointlist
/// in sorted order (greatest Y at first).
/// Step 3: For forming the Intersection Curves, the consecutive points in the pointlist are checked if
/// their mid-point lies in the surface and not in the innerloop. They are stored in the sorted order.
///
public class LayerDetector
{
private readonly List intersectionSurfacesList = new List();
private readonly List pointList = new List();
private readonly IList soilSurfaces;
private double verticalXCoordinate;
///
/// Initializes a new instance of the class.
///
/// The soil surfaces.
public LayerDetector(IList soilSurfaces)
{
this.soilSurfaces = soilSurfaces;
}
///
/// Gets the layer list.
///
///
/// The layer list.
///
public List LayerList { get; } = new List();
///
/// Determines the materials.
///
/// A vertical x coordinate.
public void DetermineMaterials(double aVerticalXCoordinate)
{
verticalXCoordinate = aVerticalXCoordinate;
// The detection process
StartDetection();
}
private void StartDetection()
{
ClearLists();
GetIntersectionPointList();
DeleteDuplicateCurve();
}
private void FillCurveListBasedOnPoints(GeometryLoop loop)
{
var tel = 0;
var curve = new GeometryCurve();
foreach (Point2D loopPoint in loop.Points)
{
if (tel == 0) //(tel % 2 == 0)
{
curve.HeadPoint = loopPoint;
}
else
{
curve.EndPoint = loopPoint;
loop.CurveList.Add(curve);
curve = new GeometryCurve();
curve.HeadPoint = loopPoint;
}
tel++;
}
//curve = new GeometryCurve(loop.CurveList.Last().EndPoint, loop.CurveList.First().HeadPoint);
curve.EndPoint = loop.CurveList.First().HeadPoint;
loop.CurveList.Add(curve);
}
private void GetIntersectionPointList()
{
foreach (SoilLayer2D surface in soilSurfaces)
{
if (!intersectionSurfacesList.Contains(surface))
{
if (surface.GeometrySurface.OuterLoop.CurveList.Count == 0)
{
FillCurveListBasedOnPoints(surface.GeometrySurface.OuterLoop);
}
foreach (GeometryCurve curve in surface.GeometrySurface.OuterLoop.CurveList)
{
FindIntersections(curve.HeadPoint, curve.EndPoint, surface);
}
// check if it has inner loops
if (surface.GeometrySurface.InnerLoops.Count > 0)
{
List loopList = surface.GeometrySurface.InnerLoops;
foreach (GeometryLoop loop in loopList)
{
foreach (GeometryCurve curve in loop.CurveList)
{
FindIntersections(curve.HeadPoint, curve.EndPoint, surface);
}
}
}
//create intersection curves
if (pointList.Count > 0)
{
GetLayerList();
pointList.Clear();
}
}
}
}
// finds intersections with regard to publisherEventArgs curve
private void FindIntersections(Point2D aHeadpoint, Point2D aEndPoint, SoilLayer2D aSurfaceRefKey)
{
const double maxvalue = 999999999.0;
const double minvalue = -999999999.0;
var verticalHeadPoint = new Point2D(verticalXCoordinate, maxvalue);
var verticalEndPoint = new Point2D(verticalXCoordinate, minvalue);
LineIntersection intersectionResult = Routines2D.DetermineIf2DLinesIntersectStrickly(verticalHeadPoint,
verticalEndPoint, aHeadpoint, aEndPoint, out Point2D intersectionPoint);
if (intersectionResult == LineIntersection.Intersects)
{
AddPointToList(new LayerIntersectionPoint(intersectionPoint, aSurfaceRefKey));
}
else if (intersectionResult == LineIntersection.NoIntersection || intersectionResult == LineIntersection.Parallel)
{
const double cEpsilon = 1.0e-7;
if ((Routines2D.DoesPointExistInLine(verticalHeadPoint, verticalEndPoint,
aHeadpoint, cEpsilon)) && (Routines2D.DoesPointExistInLine(verticalHeadPoint,
verticalEndPoint, aEndPoint, cEpsilon)))
{
AddPointToList(new LayerIntersectionPoint(aHeadpoint, aSurfaceRefKey));
AddPointToList(new LayerIntersectionPoint(aEndPoint, aSurfaceRefKey));
}
}
}
private void GetLayerList()
{
int pointCount = pointList.Count;
if (pointCount > 1)
{
object point1 = null;
for (var index = 0; index < pointCount; index++)
{
if (point1 == null)
{
point1 = pointList[index];
}
else
{
//point not same condition
bool notSameCondition = ((pointList[index].IntersectionPoint.X.IsNearEqual((
(LayerIntersectionPoint) point1).IntersectionPoint.X)) && (pointList[index].IntersectionPoint.Z.IsNearEqual((
(LayerIntersectionPoint) point1).IntersectionPoint.Z)));
if (!notSameCondition)
{
object point2 = pointList[index];
FindLayer((LayerIntersectionPoint) point1, (LayerIntersectionPoint) point2);
point1 = pointList[index];
}
}
}
}
}
private void ClearLists()
{
pointList.Clear();
LayerList.Clear();
intersectionSurfacesList.Clear();
}
// To make it top to bottom
private void CheckTopToBottom(ref Point2D aHeadPoint, ref Point2D aEndPoint)
{
if (aHeadPoint.Z.IsLessThan(aEndPoint.Z))
{
Point2D temp = aHeadPoint;
aHeadPoint = aEndPoint;
aEndPoint = temp;
}
}
// Finds what kind of passage does this curve make with regard to the surface
private void FindLayer(LayerIntersectionPoint aPoint1, LayerIntersectionPoint aPoint2)
{
var midPoint = new GeometryPoint
{
X = (aPoint1.IntersectionPoint.X + aPoint2.IntersectionPoint.X) / 2,
Z = (aPoint1.IntersectionPoint.Z + aPoint2.IntersectionPoint.Z) / 2
};
// check where the mid point lies
PointInPolygon polygonResult = Routines2D.CheckIfPointIsInPolygon(aPoint1.SurfaceRefKey.GeometrySurface.OuterLoop,
midPoint.X, midPoint.Z);
Point2D startPoint = aPoint1.IntersectionPoint;
Point2D endPoint = aPoint2.IntersectionPoint;
Soil soil = aPoint1.SurfaceRefKey.Soil;
bool isAquifer = aPoint1.SurfaceRefKey.IsAquifer;
if (polygonResult == PointInPolygon.InsidePolygon || polygonResult == PointInPolygon.OnPolygonEdge)
{
var isPresentInInnerLoop = false;
List innerLoopList = aPoint1.SurfaceRefKey.GeometrySurface.InnerLoops;
foreach (GeometryLoop innerLoop in innerLoopList)
{
PointInPolygon innerPolygonResult = Routines2D.CheckIfPointIsInPolygon(innerLoop, midPoint.X, midPoint.Z);
if (innerPolygonResult != PointInPolygon.OutsidePolygon)
{
isPresentInInnerLoop = true;
}
}
if (!isPresentInInnerLoop)
{
var layerCurve = new Layer(startPoint, endPoint, soil)
{
IsAquifer = isAquifer
};
AddLayerToList(layerCurve);
}
}
}
// Adds the LayerIntersectionCurve in publisherEventArgs sorted order - Top to bottom
private void AddLayerToList(Layer aLayer)
{
int count = LayerList.Count;
var isAdded = false;
Point2D startPoint = aLayer.StartPoint;
Point2D endPoint = aLayer.EndPoint;
CheckTopToBottom(ref startPoint, ref endPoint);
aLayer.StartPoint = startPoint;
aLayer.EndPoint = endPoint;
if (count == 0)
{
LayerList.Add(aLayer);
}
else
{
for (var index = 0; index < count; index++)
{
if (aLayer.StartPoint.Z.IsGreaterThanOrEqualTo(LayerList[index].StartPoint.Z))
{
LayerList.Insert(index, aLayer);
isAdded = true;
break;
}
}
if (!isAdded)
{
LayerList.Add(aLayer);
}
}
}
// Adds the LayerIntersectionPoints (ignores duplicates when found within same surface)
private void AddPointToList(LayerIntersectionPoint aPoint)
{
if (!intersectionSurfacesList.Contains(aPoint.SurfaceRefKey))
{
intersectionSurfacesList.Add(aPoint.SurfaceRefKey);
}
int count = pointList.Count;
var isPointFound = false;
if (count > 0)
{
for (var index = 0; index < count; index++) // to find duplicate
{
if ((aPoint.IntersectionPoint.X.IsNearEqual(pointList[index].IntersectionPoint.X))
&& (aPoint.IntersectionPoint.Z.IsNearEqual(pointList[index].IntersectionPoint.Z))
&& (aPoint.SurfaceRefKey == pointList[index].SurfaceRefKey))
{
isPointFound = true;
break;
}
}
if (!isPointFound) // to be added in the sorted order Z greater at the top
{
var isAdded = false;
for (var index = 0; index < count; index++)
{
if (aPoint.IntersectionPoint.Z.IsGreaterThanOrEqualTo(pointList[index].IntersectionPoint.Z))
{
pointList.Insert(index, aPoint);
isAdded = true;
break;
}
}
if (!isAdded)
{
pointList.Add(aPoint);
}
}
}
else
{
pointList.Add(aPoint);
}
}
///
/// if two surfaces share the same curve choose any one surface
///
private void DeleteDuplicateCurve()
{
var duplicateList = new List();
int curveCount = LayerList.Count;
object curve1 = null;
for (var index = 0; index < curveCount; index++)
{
if (curve1 == null)
{
curve1 = LayerList[index];
}
else
{
Layer curve2 = LayerList[index];
// check if there is overlapping
var curveOne = (Layer) curve1;
Layer curveTwo = curve2;
if (curveOne.StartPoint.Z.IsGreaterThanOrEqualTo(curveTwo.StartPoint.Z) && curveOne.EndPoint.Z.IsLessThanOrEqualTo(curveTwo.EndPoint.Z))
{
duplicateList.Add(LayerList[index]);
}
// Case - No space between two consecutive curves
else
{
curve1 = LayerList[index];
}
}
}
for (var index = 0; index < duplicateList.Count; index++)
{
if (LayerList.Contains(duplicateList[index]))
{
LayerList.Remove(duplicateList[index]);
}
}
}
}