using System; using System.Collections.Generic; using System.ComponentModel; using System.Xml.Serialization; using Deltares.Geometry; using Deltares.Geotechnics; using Deltares.Geotechnics.Soils; using Deltares.Geotechnics.SurfaceLines; using Deltares.Mathematics; using Deltares.Standard; using Deltares.Standard.Attributes; using Deltares.Standard.EventPublisher; using Deltares.Standard.Units; using Deltares.Standard.Validation; namespace Deltares.Stability { public class TangentLine : IComparable { private double height; public TangentLine() {} public TangentLine(double height) { this.height = height; } [Format("F3")] [Unit(UnitType.Depth)] public double Height { get { return height; } set { DataEventPublisher.BeforeChange(this, "Height"); height = value; DataEventPublisher.AfterChange(this, "Height"); } } public override string ToString() { return Height.ToString("F3"); } public int CompareTo(TangentLine other) { return - Height.CompareTo(other.Height); } } public class SlipCircleTangentLine : GeometryObject, IVisibleEnabled, IDisposable { public const double TangentLineOffset = 7.5; private bool automaticAtBoundaries = false; private List boundaryHeights = new List(); private bool boundaryHeightsDirty = true; private double maxSpacingBetweenBoundaries = 10; private StabilityModel stabilityModel; private int tangentLineNumber = 0; private double tangentLineZBottom = 0.0; private double tangentLineZTop = 0.0; private ITangentLinesBoundaries tangentLinesBoundaries; public SlipCircleTangentLine() { DataEventPublisher.OnAfterChange += DataEventPublisher_OnAfterChange; } [Browsable(false)] [XmlIgnore] public List BoundaryHeights { get { if (boundaryHeightsDirty) { boundaryHeightsDirty = false; UpdateBoundaries(); } return boundaryHeights; } } /// /// Gets and Sets the Top Z Value of the Tangent Line /// [Unit(UnitType.Length)] [Minimum(-1.000e+06)] [Maximum(100000)] [PropertyOrder(1)] [Format("F3")] public double TangentLineZTop { get { return tangentLineZTop; } set { DataEventPublisher.BeforeChange(this, "TangentLineZTop"); tangentLineZTop = value; DataEventPublisher.AfterChange(this, "TangentLineZTop"); } } /// /// Gets and sets the Number in TangentLine /// [Unit(UnitType.None)] [Minimum(0)] [Maximum(50)] [PropertyOrder(3)] [Format("F0")] public int TangentLineNumber { get { return tangentLineNumber; } set { DataEventPublisher.BeforeChange(this, "TangentLineNumber"); tangentLineNumber = value; DataEventPublisher.AfterChange(this, "TangentLineNumber"); } } /// /// Gets and sets the Bottom Z Value of the TangentLine /// [Unit(UnitType.Length)] [Minimum(-1.000e+06)] [Maximum(100000)] [PropertyOrder(2)] [Format("F3")] public double TangentLineZBottom { get { return tangentLineZBottom; } set { DataEventPublisher.BeforeChange(this, "TangentLineZBottom"); tangentLineZBottom = value; DataEventPublisher.AfterChange(this, "TangentLineZBottom"); } } [Browsable(false)] [XmlIgnore] public double TangentLineXLeft { get { return tangentLinesBoundaries != null ? tangentLinesBoundaries.XLeft : 0; } } [Browsable(false)] [XmlIgnore] public double TangentLineXRight { get { return tangentLinesBoundaries != null ? tangentLinesBoundaries.XRight : 0; } } public bool AutomaticAtBoundaries { get { return automaticAtBoundaries; } set { if (value != automaticAtBoundaries) { DataEventPublisher.BeforeChange(this, "AutomaticAtBoundaries"); automaticAtBoundaries = value; DataEventPublisher.AfterChange(this, "AutomaticAtBoundaries"); } } } [Browsable(false)] [XmlIgnore] public ITangentLinesBoundaries TangentLinesBoundaries { get { return tangentLinesBoundaries; } set { tangentLinesBoundaries = value; } } [Browsable(false)] [XmlIgnore] public StabilityModel StabilityModel { get { return stabilityModel; } set { stabilityModel = value; } } [Unit(UnitType.Length)] [Format("F3")] [Minimum(0.01)] public double MaxSpacingBetweenBoundaries { get { return maxSpacingBetweenBoundaries; } set { DataEventPublisher.BeforeChange(this, "MaxSpacingBetweenBoundaries"); maxSpacingBetweenBoundaries = value; DataEventPublisher.AfterChange(this, "MaxSpacingBetweenBoundaries"); } } public bool IsAutomaticAtBoundaries() { return IsAutomaticBoundariesApplicable() && AutomaticAtBoundaries; } public void UpdateBoundaries() { boundaryHeights.Clear(); if (stabilityModel != null) { if (AutomaticAtBoundaries && IsAutomaticBoundariesApplicable()) { double xCoordinate; if (stabilityModel.SurfaceLine2.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtPolder) != null) { xCoordinate = stabilityModel.SurfaceLine2.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtPolder).X; } else { xCoordinate = 0.5*(TangentLineXLeft + TangentLineXRight); } var layerDetector = new LayerDetector(stabilityModel.SoilProfile.Surfaces); layerDetector.DetermineMaterials(xCoordinate); if (layerDetector.LayerList.Count > 0) { boundaryHeights.Add(new TangentLine(stabilityModel.GeometryData.GetLocalCoordinate(layerDetector.LayerList[0].StartPoint).Y)); for (int index = 0; index < layerDetector.LayerList.Count; index++) { boundaryHeights.Add(new TangentLine(stabilityModel.GeometryData.GetLocalCoordinate(layerDetector.LayerList[index].EndPoint).Y)); } } // then add extra tangent lines if the spacing is big var additionalTangentLines = new List(); double maxSpacing = Math.Max(0.01, MaxSpacingBetweenBoundaries); for (int i = 0; i < boundaryHeights.Count - 1; i++) { if (Math.Abs(boundaryHeights[i].Height - boundaryHeights[i + 1].Height) > maxSpacing) { double topLevel = boundaryHeights[i].Height; double bottomLevel = boundaryHeights[i + 1].Height; double numberOfIntervals = (int) Math.Max(Math.Truncate((topLevel - bottomLevel)/(maxSpacing/2)), 2); for (int j = 1; j < numberOfIntervals; j++) { double interval = Math.Abs(bottomLevel - topLevel)/numberOfIntervals; additionalTangentLines.Add(new TangentLine(bottomLevel + j*interval)); } } } boundaryHeights.AddRange(additionalTangentLines); } else { if (tangentLineNumber <= 1) { boundaryHeights.Add(new TangentLine(TangentLineZTop)); } else { double deltaZ = (TangentLineZTop - TangentLineZBottom)/(TangentLineNumber - 1); for (int i = 0; i < TangentLineNumber; i++) { boundaryHeights.Add(new TangentLine(TangentLineZTop - i*deltaZ)); } } } boundaryHeights.Sort(); } } public override GeometryBounds GetGeometryBounds() { double top = TangentLineZTop; double bottom = TangentLineZBottom; if (IsAutomaticAtBoundaries() && BoundaryHeights.Count > 0) { top = BoundaryHeights[0].Height; bottom = BoundaryHeights[BoundaryHeights.Count - 1].Height; } return new GeometryBounds(TangentLineXLeft, TangentLineXRight, bottom, top); } public override bool ContainsPoint(Point3D point, double tolerance) { return GetGeometryBounds().ContainsPoint(point.X, point.Z); } public void Dispose() { //stabilityModel = null; DataEventPublisher.OnAfterChange -= DataEventPublisher_OnAfterChange; } public bool IsVisible(string property) { switch (property) { case "Name": return false; case "TangentLineZBottom": return !AutomaticAtBoundaries; case "TangentLineZTop": return !AutomaticAtBoundaries; case "TangentLineNumber": return !AutomaticAtBoundaries && StabilityModel != null && StabilityModel.SearchAlgorithm == SearchAlgorithm.Grid; case "AutomaticAtBoundaries": return IsAutomaticBoundariesApplicable(); case "MaxSpacingBetweenBoundaries": return IsAutomaticBoundariesApplicable() && IsAutomaticAtBoundaries(); default: return true; } } public bool IsEnabled(string property) { switch (property) { case "TangentLineZTop": return !IsAutomaticAtBoundaries(); case "TangentLineZBottom": return !IsAutomaticAtBoundaries(); case "TangentLineXLeft": return !IsAutomaticAtBoundaries(); case "TangentLineXRight": return !IsAutomaticAtBoundaries(); case "TangentLineNumber": return !IsAutomaticAtBoundaries(); default: return true; } } private void DataEventPublisher_OnAfterChange(object sender, PublishEventArgs e) { bool update = false; if (stabilityModel != null) { if (sender is SoilLayer2D && stabilityModel.SoilProfile.Surfaces.Contains((SoilLayer2D) sender)) { update = true; } else if (sender is GeometryCurve && stabilityModel.GeometryData.Curves.Contains((GeometryCurve) sender)) { update = true; } else if (sender is GeometryPoint && stabilityModel.GeometryData.Points.Contains((GeometryPoint) sender)) { update = true; } else if (sender == stabilityModel.SlipCircle.SlipCircleGrid) { update = true; } else if (sender == stabilityModel.SlipPlaneUpliftVan) { update = true; } else if (sender == this) { update = true; } if (update) { Delayed.Invoke(UpdateBoundaries); } } } private bool IsAutomaticBoundariesApplicable() { return stabilityModel != null && (stabilityModel.ModelOption == ModelOptions.UpliftVan || stabilityModel.ModelOption == ModelOptions.UpliftSpencer); } } }