// 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;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using Deltares.DamEngine.Calculators.Properties;
using Deltares.DamEngine.Data.General;
using Deltares.DamEngine.Data.General.PlLines;
using Deltares.DamEngine.Data.Geometry;
using Deltares.DamEngine.Data.Geotechnics;
using Deltares.DamEngine.Data.Standard;
namespace Deltares.DamEngine.Calculators.KernelWrappers.MacroStabilityCommon;
public static class PlLinesToWaternetConverter
{
private enum LayerType
{
BottomAquifer,
TopLayerInBetweenAquiferCluster,
BottomLayerInBetweenAquiferCluster
}
private const double epsilon = 1e-9;
private const string waternetLine1Name = "Waternet line phreatic line";
private const string waternetLine2Name = "Penetration zone lower aquifer";
private const string waternetLine3Name = "Waternet line lower aquifer";
private const string waternetLine4Name = "Waternet area in-between aquifer";
private const string headLine1Name = "Phreatic line (PL 1)";
private const string headLine2Name = "Head line 2 (PL 2)";
private const string headLine3Name = "Head line 3 (PL 3)";
private const string headLine4Name = "Head line 4 (PL 4)";
///
/// Creates a based on a 1D profile by assigning the to waternet lines.
///
/// The pl lines.
/// The 1D soil profile.
/// Length of the penetration.
/// Left side of the 2D profile.
/// Right side of the 2D profile.
///
public static Waternet CreateWaternetBasedOnPlLines(PlLines plLines, SoilProfile1D soilProfile1D, double penetrationLength, double xLeft, double xRight)
{
ThrowWhenPlLinesIsNull(plLines);
ThrowWhenSoilProfileIsNull(soilProfile1D);
var waternet = new Waternet();
PlLine plLine = plLines.Lines[PlLineType.Pl1];
var headLine = CreateLine(plLine, headLine1Name);
if (plLine != null && !IsBelowSoilProfile(soilProfile1D, plLine))
{
waternet.PhreaticLine = CreateLine(plLine, headLine1Name);
double level = soilProfile1D.GetLayerAt(headLine.GetMinZ()).BottomLevel;
WaternetLine waternetLine = CreateWaternetLine(level, xLeft, xRight);
waternetLine.Name = waternetLine1Name;
waternetLine.HeadLine = headLine;
waternetLine.HeadLine.SyncCalcPoints();
waternet.WaternetLineList.Add(waternetLine);
}
plLine = plLines.Lines[PlLineType.Pl2];
headLine = CreateLine(plLine, headLine2Name);
if (headLine != null && !IsBelowSoilProfile(soilProfile1D, plLine))
{
waternet.HeadLineList.Add(headLine);
if (soilProfile1D.BottomAquiferLayer != null)
{
double level = soilProfile1D.BottomAquiferLayer.TopLevel + penetrationLength;
WaternetLine waternetLine = CreateWaternetLine(level, xLeft, xRight);
waternetLine.Name = waternetLine2Name;
waternetLine.HeadLine = headLine;
waternet.WaternetLineList.Add(waternetLine);
}
}
plLine = plLines.Lines[PlLineType.Pl3];
headLine = CreateLine(plLine, headLine3Name);
if (headLine != null && !IsBelowSoilProfile(soilProfile1D, plLine))
{
waternet.HeadLineList.Add(headLine);
if (soilProfile1D.BottomAquiferLayer != null)
{
double level = soilProfile1D.BottomAquiferLayer.TopLevel;
WaternetLine waternetLine = CreateWaternetLine(level, xLeft, xRight);
waternetLine.Name = waternetLine3Name;
waternetLine.HeadLine = headLine;
waternet.WaternetLineList.Add(waternetLine);
}
}
CreateWaternetLinesForInBetweenAquifers(plLines, soilProfile1D, xLeft, xRight, waternet);
AdjustWaternetLineOfPhreaticLineWhenCoincidingWithOtherWaternetLines(waternet);
return waternet;
}
///
/// Creates a based on a 2D profile by assigning the to waternet lines.
///
/// The to convert.
/// The to convert the with.
/// The penetration length.
/// A .
/// Thrown when or
/// is null.
public static Waternet CreateWaternetBasedOnPlLines(PlLines plLines, SoilProfile2D soilProfile, double penetrationLength)
{
ThrowWhenPlLinesIsNull(plLines);
ThrowWhenSoilProfileIsNull(soilProfile);
double[] xCoordinates = DetermineAllXCoordinatesOfSoilProfile(soilProfile);
Point2D[] bottomAquiferCoordinates = DetermineLayerBoundaryCoordinates(LayerType.BottomAquifer, xCoordinates, soilProfile).ToArray();
var waternet = new Waternet();
PlLine plLine = plLines.Lines[PlLineType.Pl1];
if (plLine != null)
{
var headLine = CreateLine(plLine, headLine1Name);
waternet.PhreaticLine = CreateLine(plLine, headLine1Name);
WaternetLine waternetLine = CreateWaternetLineForPhreaticLine(soilProfile, plLine);
waternetLine.Name = waternetLine1Name;
waternetLine.HeadLine = headLine;
waternet.WaternetLineList.Add(waternetLine);
}
plLine = plLines.Lines[PlLineType.Pl2];
if (plLine != null)
{
var headLine = CreateLine(plLine, headLine2Name);
waternet.HeadLineList.Add(headLine);
if (bottomAquiferCoordinates.Any())
{
WaternetLine waternetLine = CreateWaternetLine(bottomAquiferCoordinates, penetrationLength);
waternetLine.Name = waternetLine2Name;
waternetLine.HeadLine = headLine;
waternet.WaternetLineList.Add(waternetLine);
}
}
plLine = plLines.Lines[PlLineType.Pl3];
if (plLine != null)
{
var headLine = CreateLine(plLine, headLine3Name);
waternet.HeadLineList.Add(headLine);
if (bottomAquiferCoordinates.Any())
{
WaternetLine waternetLine = CreateWaternetLine(bottomAquiferCoordinates);
waternetLine.HeadLine = headLine;
waternetLine.Name = waternetLine3Name;
waternet.WaternetLineList.Add(waternetLine);
}
}
CreateWaternetLinesForInBetweenAquifers(waternet, soilProfile, plLines.Lines[PlLineType.Pl4]);
AdjustWaternetLineOfPhreaticLineWhenCoincidingWithOtherWaternetLines(waternet);
return waternet;
}
///
/// Determine all the xCoordinates to make cross-sections.
///
/// The soil profile 2D.
/// All the xCoordinates of the soil profile 2D.
private static double[] DetermineAllXCoordinatesOfSoilProfile(SoilProfile2D soilProfile)
{
IEnumerable points = soilProfile.Surfaces.SelectMany(surf => surf.GeometrySurface.OuterLoop.CalcPoints);
double[] xCoordinates = points.Select(point => point.X).OrderBy(x => x).Distinct().ToArray();
return xCoordinates;
}
private static void CreateWaternetLinesForInBetweenAquifers(PlLines plLines, SoilProfile1D soilProfile1D, double xLeft, double xRight, Waternet waternet)
{
PlLine plLine = plLines.Lines[PlLineType.Pl4];
var headLine = CreateLine(plLine, headLine4Name);
if (headLine != null && !IsBelowSoilProfile(soilProfile1D, plLine))
{
waternet.HeadLineList.Add(headLine);
if (soilProfile1D.InBetweenAquiferClusters != null)
{
foreach ((SoilLayer1D, SoilLayer1D) inBetweenAquiferCluster in soilProfile1D.InBetweenAquiferClusters.Where(layer => layer is { Item1: not null, Item2: not null }))
{
double levelTop = inBetweenAquiferCluster.Item1.TopLevel;
double levelBottom = inBetweenAquiferCluster.Item2.BottomLevel;
WaternetLine waternetLine = CreateWaternetLine(levelTop, levelBottom, xLeft, xRight);
waternetLine.Name = waternetLine4Name;
waternetLine.HeadLine = headLine;
waternet.WaternetLineList.Add(waternetLine);
}
}
}
}
private static void CreateWaternetLinesForInBetweenAquifers(Waternet waternet, SoilProfile2D soilProfile, PlLine plLine)
{
if (plLine == null)
{
return;
}
var headLine = CreateLine(plLine, headLine4Name);
waternet.HeadLineList.Add(headLine);
double[] xCoordinates = DetermineAllXCoordinatesOfSoilProfile(soilProfile);
int inBetweenAquiferCount = DetermineInBetweenAquiferClusterCount(soilProfile, xCoordinates);
if (inBetweenAquiferCount == 0)
{
return;
}
for (var i = 0; i < inBetweenAquiferCount; i++)
{
Point2D[] inBetweenAquiferUpperCoordinates = DetermineLayerBoundaryCoordinates(LayerType.TopLayerInBetweenAquiferCluster, xCoordinates, soilProfile, i).ToArray();
Point2D[] inBetweenAquiferLowerCoordinates = DetermineLayerBoundaryCoordinates(LayerType.BottomLayerInBetweenAquiferCluster, xCoordinates, soilProfile, i).ToArray();
if (inBetweenAquiferUpperCoordinates.Any() && inBetweenAquiferLowerCoordinates.Any())
{
WaternetLine waternetLine = CreateWaternetLine(inBetweenAquiferUpperCoordinates, inBetweenAquiferLowerCoordinates);
waternetLine.HeadLine = headLine;
waternetLine.Name = waternetLine4Name;
waternet.WaternetLineList.Add(waternetLine);
}
}
}
private static int DetermineInBetweenAquiferClusterCount(SoilProfile2D soilProfile, double[] xCoordinates)
{
var currentCount = 0;
var previousCount = 0;
for (var i = 0; i < xCoordinates.Length; i++)
{
SoilProfile1D crossSection = soilProfile.GetSoilProfile1D(xCoordinates[i]);
currentCount = crossSection.InBetweenAquiferClusters?.Count ?? 0;
if (i > 0 && currentCount != previousCount)
{
return 0;
}
previousCount = currentCount;
}
return currentCount;
}
private static TLineType CreateLine(PlLine plLine, string name)
where TLineType : GeometryPointString, new()
{
if (plLine == null || !plLine.Points.Any())
{
return null;
}
var line = new TLineType();
line.Points.AddRange(plLine.Points);
line.SyncCalcPoints();
line.Name = name;
return line;
}
internal static WaternetLine CreateWaternetLine(double level, double xLeft, double xRight)
{
var waternetLine = new WaternetLine();
waternetLine.Points.Add(new GeometryPoint(xLeft, level));
waternetLine.Points.Add(new GeometryPoint(xRight, level));
waternetLine.SyncCalcPoints();
return waternetLine;
}
private static WaternetLine CreateWaternetLine(double levelTop, double levelBottom, double xLeft, double xRight)
{
var waternetLine = new WaternetLine();
waternetLine.Points.Add(new GeometryPoint(xLeft, levelTop));
waternetLine.Points.Add(new GeometryPoint(xRight, levelTop));
waternetLine.Points.Add(new GeometryPoint(xRight, levelBottom));
waternetLine.Points.Add(new GeometryPoint(xLeft, levelBottom));
waternetLine.Points.Add(new GeometryPoint(xLeft, levelTop));
waternetLine.SyncCalcPoints();
return waternetLine;
}
private static WaternetLine CreateWaternetLine(IEnumerable coordinates)
{
return CreateWaternetLine(coordinates, 0);
}
private static WaternetLine CreateWaternetLine(IEnumerable coordinates, double zOffSet)
{
var line = new WaternetLine();
foreach (Point2D coordinate in coordinates)
{
var point = new GeometryPoint(coordinate.X, coordinate.Z + zOffSet);
line.Points.Add(point);
}
line.SyncCalcPoints();
LineHelper.RemoveDuplicatedPoints(line.CalcPoints, epsilon);
line.RemoveUnnecessaryPoints();
line.SyncPoints();
return line;
}
private static WaternetLine CreateWaternetLine(IEnumerable upperCoordinates, IEnumerable lowerCoordinates)
{
var line = new WaternetLine();
List upperCoordList = upperCoordinates.ToList();
upperCoordList.Sort();
GeometryPoint point;
foreach (Point2D upperCoord in upperCoordList)
{
point = new GeometryPoint(upperCoord.X, upperCoord.Z);
line.Points.Add(point);
}
List lowerCoordList = lowerCoordinates.ToList();
lowerCoordList.Sort();
for (int i = lowerCoordList.Count - 1; i >= 0; i--)
{
point = new GeometryPoint(lowerCoordList[i].X, lowerCoordList[i].Z);
line.Points.Add(point);
}
point = new GeometryPoint(upperCoordList[0].X, upperCoordList[0].Z);
line.Points.Add(point);
line.SyncCalcPoints();
line.RemoveUnnecessaryPoints();
line.SyncPoints();
return line;
}
private static IEnumerable DetermineLayerBoundaryCoordinates(LayerType layerType, double[] xCoordinates, SoilProfile2D soilProfile, int indexInBetweenAquifer = -1)
{
SoilLayer1D previousAquiferLayer = null;
var coordinates = new List();
var xCoordinatesAll = new List();
foreach (double xCoordinate in xCoordinates)
{
if (HasAquiferAVerticalPartAtGivenX(layerType, xCoordinate, soilProfile, indexInBetweenAquifer))
{
xCoordinatesAll.Add(xCoordinate - epsilon);
xCoordinatesAll.Add(xCoordinate + epsilon);
}
else
{
xCoordinatesAll.Add(xCoordinate);
}
}
foreach (double xCoordinate in xCoordinatesAll)
{
SoilProfile1D crossSection = soilProfile.GetSoilProfile1D(xCoordinate);
// Determine if the layer is in range of the previous layer
// If not, return empty coordinates, because the layer is interrupted
SoilLayer1D currentAquifer = GetSoilLayer1D(layerType, crossSection, indexInBetweenAquifer);
if (previousAquiferLayer != null && currentAquifer != null
&& !AreHorizontallyConnected(previousAquiferLayer, currentAquifer))
{
return Enumerable.Empty();
}
if (currentAquifer != null)
{
previousAquiferLayer = currentAquifer;
coordinates.Add(layerType is LayerType.BottomAquifer or LayerType.TopLayerInBetweenAquiferCluster ? new Point2D(xCoordinate, currentAquifer.TopLevel) : new Point2D(xCoordinate, currentAquifer.BottomLevel));
}
}
// Perform a short validation that the coordinates are fully defined from the beginning to the end
// of the profile. If not, the layer is not continuous.
if (!IsLayerBoundaryContinuous(coordinates, xCoordinates.First(), xCoordinates.Last()))
{
return Enumerable.Empty();
}
return coordinates;
}
private static bool IsLayerBoundaryContinuous(List boundaryPoints, double leftGeometryBoundary, double rightGeometryBoundary)
{
return boundaryPoints.Any() && (Math.Abs(boundaryPoints.First().X - leftGeometryBoundary) < epsilon)
&& Math.Abs(boundaryPoints.Last().X - rightGeometryBoundary) < epsilon;
}
private static SoilLayer1D GetSoilLayer1D(LayerType layerType, SoilProfile1D soilProfile1D, int indexInBetweenAquifer)
{
switch (layerType)
{
case LayerType.BottomAquifer:
return soilProfile1D.BottomAquiferLayer;
case LayerType.TopLayerInBetweenAquiferCluster:
return soilProfile1D.InBetweenAquiferClusters[indexInBetweenAquifer].Item1;
case LayerType.BottomLayerInBetweenAquiferCluster:
return soilProfile1D.InBetweenAquiferClusters[indexInBetweenAquifer].Item2;
}
return null;
}
///
/// Create the waternet line associated to the phreatic line (PL1).
/// For the waternet type "Dam Standard", this line lies on the bottom of the soil layers “in which the phreatic plane lies”.
/// Where PL1 lies above the ground level, the line lies on the ground level.
/// As a consequence, the line can contain vertical "jumps" when layers have vertical parts.
///
///
///
///
private static WaternetLine CreateWaternetLineForPhreaticLine(SoilProfile2D soilProfile2D, PlLine plLine)
{
var waternetLine = new WaternetLine();
var curves = new List();
foreach (SoilLayer2D surface in soilProfile2D.Surfaces)
{
GeometryLoop outerLoop = surface.GeometrySurface.OuterLoop;
// Add the first point to get a closed GeometryPointString
outerLoop.CalcPoints.Add(new Point2D(outerLoop.CalcPoints[0].X, outerLoop.CalcPoints[0].Z));
var plLineInGeometryPoint = new GeometryPointString();
foreach (PlLinePoint point in plLine.Points)
{
plLineInGeometryPoint.Points.Add(new GeometryPoint(point.X, point.Z));
plLineInGeometryPoint.SyncCalcPoints();
}
List intersectionPoints = outerLoop.IntersectionXzPointsWithGeometryString(plLineInGeometryPoint);
// Only the layers that are below and do not intersect the phreatic line are kept.
if ((intersectionPoints.Count == 0) && (outerLoop[0].Z < plLineInGeometryPoint.GetZatX(outerLoop[0].X)))
{
var geometrySurface = new GeometrySurface(outerLoop);
GeometryPointString top = geometrySurface.DetermineTopGeometrySurface();
top.SyncPoints();
top.SortPointsByXAscending();
curves.Add(top);
}
}
// The waternet line is the polyline formed with the highest lines connected to each other.
List lines = DivideCurvesIntoLines(curves);
IEnumerable allPoints = curves.SelectMany(curve => curve.CalcPoints);
double[] xCoordinates = allPoints.Select(point => point.X).OrderBy(x => x).Distinct().ToArray();
List splitLines = SplitLinesAtXCoordinates(xCoordinates, lines);
for (var i = 0; i < xCoordinates.Length - 1; i++)
{
List relevantLines = splitLines.FindAll(line => Math.Abs(line.BeginPoint.X - xCoordinates[i]) < epsilon && Math.Abs(line.BeginPoint.X - line.EndPoint.X) > 0);
double max = relevantLines.Select(line => line.BeginPoint.Z).Max();
Line relevantLine = relevantLines.Find(line => Math.Abs(line.BeginPoint.Z - max) < epsilon);
waternetLine.Points.Add(new GeometryPoint(relevantLine.BeginPoint.X, relevantLine.BeginPoint.Z));
waternetLine.Points.Add(new GeometryPoint(relevantLine.EndPoint.X, relevantLine.EndPoint.Z));
}
waternetLine.SyncCalcPoints();
LineHelper.RemoveDuplicatedPoints(waternetLine.CalcPoints, epsilon);
waternetLine.RemoveUnnecessaryPoints();
waternetLine.SyncPoints();
return waternetLine;
}
private static bool HasAquiferAVerticalPartAtGivenX(LayerType layerType, double xCoordinate, SoilProfile2D soilProfile, int indexInBetweenAquifer)
{
SoilProfile1D crossSectionLeft = soilProfile.GetSoilProfile1D(xCoordinate - epsilon);
SoilProfile1D crossSectionRight = soilProfile.GetSoilProfile1D(xCoordinate + epsilon);
if (crossSectionLeft != null && crossSectionRight != null)
{
SoilLayer1D currentAquiferLeft = GetSoilLayer1D(layerType, crossSectionLeft, indexInBetweenAquifer);
SoilLayer1D currentAquiferRight = GetSoilLayer1D(layerType, crossSectionRight, indexInBetweenAquifer);
if (currentAquiferLeft != null && currentAquiferRight != null)
{
if (layerType is LayerType.BottomAquifer or LayerType.TopLayerInBetweenAquiferCluster)
{
return Math.Abs(currentAquiferLeft.TopLevel - currentAquiferRight.TopLevel) > GeometryConstants.Accuracy;
}
return Math.Abs(currentAquiferLeft.BottomLevel - currentAquiferRight.BottomLevel) > GeometryConstants.Accuracy;
}
}
return false;
}
private static bool AreHorizontallyConnected(SoilLayer1D leftSoilLayer, SoilLayer1D rightSoilLayer)
{
// The layers are identical
if (ReferenceEquals(leftSoilLayer, rightSoilLayer))
{
return true;
}
// Left soil layer envelopes whole right soil layer
if (leftSoilLayer.BottomLevel <= rightSoilLayer.BottomLevel
&& leftSoilLayer.TopLevel >= rightSoilLayer.TopLevel)
{
return true;
}
// Right soil layer envelopes whole left soil layer
if (rightSoilLayer.BottomLevel <= leftSoilLayer.BottomLevel
&& rightSoilLayer.TopLevel >= leftSoilLayer.TopLevel)
{
return true;
}
return (rightSoilLayer.TopLevel <= leftSoilLayer.TopLevel && rightSoilLayer.TopLevel >= leftSoilLayer.BottomLevel) // Top level lies inbetween the left soil layer
|| (rightSoilLayer.BottomLevel >= leftSoilLayer.BottomLevel && rightSoilLayer.BottomLevel <= leftSoilLayer.TopLevel); // Bottom level lies inbetween the left soil layer
}
private static bool IsBelowSoilProfile(SoilProfile1D soilProfile, PlLine line)
{
double bottomSoilProfileLevel = soilProfile.BottomLevel;
return line.Points.Any(point => point.Z <= bottomSoilProfileLevel);
}
///
/// Throws when the soil profile is not assigned.
///
/// The soil profile.
///
private static void ThrowWhenSoilProfileIsNull(SoilProfile soilProfile)
{
if (soilProfile == null)
{
throw new NoNullAllowedException(Resources.PlLinesToWaternetConverter_NoSoilProfileDefined);
}
}
///
/// Throws when the pl lines object is not assigned.
///
/// The pl lines.
///
private static void ThrowWhenPlLinesIsNull(PlLines plLines)
{
if (plLines == null)
{
throw new NoNullAllowedException(Resources.PlLinesToWaternetConverter_NoPlLinesDefined);
}
}
///
/// Waternet lines can't coincide otherwise the program does not know which headline to use.
/// That's why when the waternet line of the phreatic line coincides with the waternet line of either PL2, PL3 or PL4,
/// an adjustment of the waternet line of the phreatic line is performed by moving it 1 mm upwards.
///
/// The waternet.
private static void AdjustWaternetLineOfPhreaticLineWhenCoincidingWithOtherWaternetLines(Waternet waternet)
{
const double minimumDistance = 0.001;
WaternetLine waternetLine1 = waternet.WaternetLineList.Find(w => w.Name == waternetLine1Name);
foreach (GeometryPoint waternetLine1Point
in from waternetLineOther in waternet.WaternetLineList.FindAll(w => w.Name != waternetLine1Name)
from waternetLine1Point in waternetLine1.Points
where waternetLineOther.Points.Any(point => Math.Abs(point.Z - waternetLine1Point.Z) < epsilon)
select waternetLine1Point)
{
waternetLine1Point.Z += minimumDistance;
}
}
private static List SplitLinesAtXCoordinates(double[] xCoordinates, List lines)
{
var splitLines = new List();
foreach (Line line in lines)
{
List xCoordinatesToBeAdded = xCoordinates.Where(xCoordinate => line.BeginPoint.X.IsLessThan(xCoordinate) && xCoordinate.IsLessThan(line.EndPoint.X)).ToList();
if (xCoordinatesToBeAdded.Count > 0)
{
SplitLineAtXCoordinate(xCoordinatesToBeAdded, line, splitLines);
}
else
{
splitLines.Add(line);
}
}
return splitLines;
}
private static void SplitLineAtXCoordinate(List xCoordinatesToBeAdded, Line line, List splitLines)
{
for (var i = 0; i < xCoordinatesToBeAdded.Count; i++)
{
var point1 = new Point2D(xCoordinatesToBeAdded[i], Math.Max(line.BeginPoint.Z, line.EndPoint.Z) + 1);
var point2 = new Point2D(xCoordinatesToBeAdded[i], Math.Min(line.BeginPoint.Z, line.EndPoint.Z) - 1);
double zCoordinate = line.GetIntersectPointXz(new Line(point1, point2)).Z;
var middlePoint = new Point2D(xCoordinatesToBeAdded[i], zCoordinate);
Point2D beginPoint = i == 0 ? line.BeginPoint : middlePoint;
Point2D endPoint = i == xCoordinatesToBeAdded.Count - 1 ? line.EndPoint : middlePoint;
splitLines.Add(new Line(beginPoint, middlePoint));
splitLines.Add(new Line(middlePoint, endPoint));
}
}
private static List DivideCurvesIntoLines(List curves)
{
var lines = new List();
foreach (GeometryPointString curve in curves)
{
for (var i = 0; i < curve.Points.Count - 1; i++)
{
var line = new Line
{
BeginPoint = new Point2D(curve.Points[i].X, curve.Points[i].Z),
EndPoint = new Point2D(curve.Points[i + 1].X, curve.Points[i + 1].Z)
};
lines.Add(line);
}
}
return lines;
}
}