// Copyright (C) Stichting Deltares 2017. 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.Language;
using Deltares.DamEngine.Data.Standard.Validation;
namespace Deltares.DamEngine.Data.Geometry
{
///
/// Class containing the geometry data
///
///
public class GeometryData : GeometryObject
{
private readonly List curveDataList = new List();
private readonly List loopDataList = new List();
private readonly List pointDataList = new List();
private readonly List surfaceDataList = new List();
private readonly GeometryPointString surfaceLine = new GeometryPointString();
private double bottom = GeometryConstants.DefaultBottomLimitGeometry;
private double left = GeometryConstants.DefaultLeftLimitGeometry;
private double right = GeometryConstants.DefaultRightLimitGeometry;
private bool updatingSurfaceLine;
#region properties
///
/// Gets the points.
///
///
/// The points.
///
[Validate]
public IList Points
{
get
{
return pointDataList;
}
}
///
/// gets the Curve data list.
///
///
/// The curves.
///
public List Curves
{
get
{
return curveDataList;
}
}
///
/// gets the Loop data list.
///
///
/// The loops.
///
public List Loops
{
get
{
return loopDataList;
}
}
///
/// gets the Surface data list.
///
public List Surfaces
{
get
{
return surfaceDataList;
}
}
///
/// 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
{
return left;
}
set
{
left = value;
}
}
///
/// Gets or sets the right.
///
///
/// The right.
///
public double Right
{
get
{
return right;
}
set
{
right = value;
}
}
///
/// Gets or sets the bottom.
///
///
/// The bottom.
///
public double Bottom
{
get
{
return bottom;
}
set
{
bottom = value;
}
}
///
/// Gets all points on the Left boundary.
///
///
private List GetLeftPoints()
{
var geometryPoints = pointDataList.Where(gp => Math.Abs(gp.X - Left) < GeometryConstants.Accuracy).ToList();
return geometryPoints;
}
///
/// Gets all points on the Right boundary.
///
///
private List GetRightPoints()
{
var geometryPoints = pointDataList.Where(point => Math.Abs(point.X - Right) < GeometryConstants.Accuracy).ToList();
return geometryPoints;
}
///
/// 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()
{
double xMin = double.MaxValue;
double xMax = double.MinValue;
double zMin = double.MaxValue;
double zMax = double.MinValue;
foreach (var point in pointDataList)
{
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()
{
pointDataList.Clear();
curveDataList.Clear();
surfaceDataList.Clear();
// newlyEffectedPoints.Clear();
// newlyEffectedCurves.Clear();
}
///
/// deletes all the Loop from IGeometryLoop.
///
private void DeleteAllLoops()
{
Loops.Clear();
}
#endregion
#region other functions
#region calculation function
///
/// CheckIfIntersectStricktly
/// Determines if two lines intersect each other stricktly (so no extrapolated points).
///
/// Line 1 GeometryPoint 1
/// Line 1 GeometryPoint 2
/// Line 2 GeometryPoint 1
/// Line 2 GeometryPoint 2
/// Intersection coordinates
/// True if lines intersect each other
private static bool CheckIfIntersectStricktly(Point2D beginPoint1, Point2D endPoint1,
Point2D beginPoint2, Point2D endPoint2,
ref Point2D intersect)
{
Point2D ip;
var res = Routines2D.DetermineIf2DLinesIntersectStrickly(beginPoint1.X, beginPoint1.Z, endPoint1.X, endPoint1.Z, beginPoint2.X,
beginPoint2.Z, endPoint2.X, endPoint2.Z, out ip);
if (ip != null)
{
intersect = ip;
}
return res == LineIntersection.Intersects;
}
///
/// CheckIfIntersectStricktly
/// Determines if two lines intersect each other stricktly (so no extrapolated points).
///
/// Line 1 GeometryPoint 1
/// Line 1 GeometryPoint 2
/// Line 2 GeometryPoint 1
/// Line 2 GeometryPoint 2
/// Intersection coordinates
/// True if lines intersect each other
public static 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();
var 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 (int 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 (int surfaceIndexLocal = 0; surfaceIndexLocal < Surfaces.Count; surfaceIndexLocal++)
{
var outerLoopCurveList = Surfaces[surfaceIndexLocal].OuterLoop.CurveList;
for (int 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) == false)
{
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 (var 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 (var loop in loops)
{
foreach (var curve in loop.CurveList)
{
if (curves.Contains(curve))
{
// Second appearance, remove
curves.Remove(curve);
}
else
{
curves.Add(curve);
}
}
}
return curves;
}
#endregion
#endregion
#endregion
///
/// Ordered list of all geometry points at the surface
///
public virtual GeometryPointString SurfaceLine
{
get
{
if (surfaceLine.CalcPoints.Count == 0 && pointDataList.Count > 0)
{
UpdateSurfaceLine();
}
return surfaceLine;
}
}
///
/// Checks geometry for loose curves and AutoRegeneration
///
///
[Validate]
public ValidationResult[] ValidateGeometry()
{
var validationList = new List();
{
foreach (var point in Points)
{
foreach (var point1 in Points)
{
if (point != point1)
{
bool isValidated = false;
foreach (var 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();
}
///
/// Updates the line at the top of the geometry
///
private void UpdateSurfaceLine()
{
if (updatingSurfaceLine)
{
return;
}
updatingSurfaceLine = true;
var bCurves = GetBoundaryCurves();
if (bCurves.Count == 0)
{
surfaceLine.CalcPoints.Clear();
}
var curvesCopy = GetCurvesCopy(bCurves);
var curves = GetTopCurves(curvesCopy);
CreateSurfaceLinePointString(curves);
updatingSurfaceLine = false;
}
///
/// 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)
{
var minX = geometry.GetMinX();
var maxX = geometry.GetMaxX();
var minZ = geometry.GetMinZ();
foreach (var 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 (var 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 (var geometryCurve in boundaryCurves)
{
if (geometryCurve != curve && !excludedCurves.Contains(geometryCurve))
{
if (AreConnected(curve, geometryCurve))
{
return geometryCurve;
}
}
}
return null;
}
///
/// Returns a that represents this instance.
///
///
/// A that represents this instance.
///
public override string ToString()
{
return LocalizationManager.GetTranslatedText(this, "GeometryData");
}
///
/// Synchronizes the loops.
///
public void SynchronizeLoops()
{
DeleteAllLoops();
foreach (var 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())
{
loopDataList.Add(surface.OuterLoop);
}
}
}
///
/// 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 (var 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 (var 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);
var 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);
}
}
}