Change field viewer

This commit is contained in:
Evgeny Redikultsev
2025-12-13 20:13:45 +05:00
parent 681ab17781
commit f937b9f373
31 changed files with 730 additions and 246 deletions

View File

@@ -4,26 +4,24 @@ using StructureHelperCommon.Models.Calculators;
using StructureHelperCommon.Models.Forces;
using StructureHelperLogics.Models.CrossSections;
using StructureHelperLogics.NdmCalculations.Primitives;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DataAccess.DTOs
{
public class CrossSectionRepositoryDTO : ICrossSectionRepository
{
private IRepositoryOperationsLogic operations;
[JsonProperty("Id")]
public Guid Id { get; set; }
[JsonProperty("HeadMaterials")]
public List<IHeadMaterial> HeadMaterials { get; } = new();
public List<IHeadMaterial> HeadMaterials { get; } = [];
[JsonProperty("ForceActions")]
public List<IForceAction> ForceActions { get; } = new();
public List<IForceAction> ForceActions { get; } = [];
[JsonProperty("Primitives")]
public List<INdmPrimitive> Primitives { get; } = new();
public List<INdmPrimitive> Primitives { get; } = [];
[JsonProperty("Calculators")]
public List<ICalculator> Calculators { get; } = new();
public List<ICalculator> Calculators { get; } = [];
[JsonIgnore]
public IRepositoryOperationsLogic Operations => operations ??= new RepositoryOperationsLogic(this);
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace FieldVisualizer.Entities.Values
{
public interface IValueLabel
{
bool IsActive { get; set; }
string Value { get; set; }
double X { get; set; }
double Y { get; set; }
}
}

View File

@@ -0,0 +1,184 @@
using FieldVisualizer.Entities.ColorMaps;
using FieldVisualizer.InfraStructures.Exceptions;
using FieldVisualizer.InfraStructures.Strings;
using FieldVisualizer.Services.ColorServices;
using FieldVisualizer.Windows.UserControls;
using StructureHelperCommon.Services;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
namespace FieldVisualizer.Entities.Values.Primitives
{
internal class AddPrimitivesToCanvasLogic
{
private const int zoomFactor = 1000;
private IMathRoundLogic roundLogic = new SmartRoundLogic() { DigitQuant = 3 };
public Canvas WorkPlaneCanvas { get; set; }
public IValueRange ValueRange { get; set; }
public IColorMap ColorMap { get; set; }
public IEnumerable<IValueColorRange> ValueColorRanges { get; set; }
public double DX { get; set; }
public double DY { get; set; }
public void ProcessPrimitives(IEnumerable<IValuePrimitive> valuePrimitives)
{
foreach (var primitive in valuePrimitives)
{
if (primitive is IRectanglePrimitive rectanglePrimitive)
{
Rectangle rectangle = ProcessRectanglePrimitive(rectanglePrimitive);
WorkPlaneCanvas.Children.Add(rectangle);
}
else if (primitive is ICirclePrimitive circlePrimitive)
{
Ellipse ellipse = ProcessCirclePrimitive(circlePrimitive);
WorkPlaneCanvas.Children.Add(ellipse);
}
else if (primitive is ITrianglePrimitive triangle)
{
Path path = ProcessTrianglePrimitive(triangle);
WorkPlaneCanvas.Children.Add(path);
}
else { throw new FieldVisulizerException(ErrorStrings.PrimitiveTypeIsUnknown); }
}
}
private Path ProcessTrianglePrimitive(ITrianglePrimitive triangle)
{
// Create the PathFigure using triangle vertices.
var figure = new PathFigure
{
StartPoint = new Point(triangle.Point1.X * zoomFactor, -triangle.Point1.Y * zoomFactor),
IsClosed = true,
IsFilled = true
};
// Add the remaining vertices as LineSegments
var segments = new PathSegmentCollection
{
new LineSegment(new Point(triangle.Point2.X * zoomFactor, - triangle.Point2.Y * zoomFactor), true),
new LineSegment(new Point(triangle.Point3.X * zoomFactor, - triangle.Point3.Y * zoomFactor), true)
// Closing is handled by IsClosed = true, so we don't need to add a segment back to Point1
};
figure.Segments = segments;
// Create geometry and path
var geometry = new PathGeometry();
geometry.Figures.Add(figure);
var path = new Path
{
Data = geometry,
};
ProcessShape(path, triangle, 0, 0, false);
path.MouseLeftButtonDown += OnPathClicked;
return path;
}
private Rectangle ProcessRectanglePrimitive(IRectanglePrimitive rectanglePrimitive)
{
Rectangle rectangle = new Rectangle
{
Height = rectanglePrimitive.Height * zoomFactor,
Width = rectanglePrimitive.Width * zoomFactor
};
double addX = rectanglePrimitive.Width / 2 * zoomFactor;
double addY = rectanglePrimitive.Height / 2 * zoomFactor;
ProcessShape(rectangle, rectanglePrimitive, addX, addY, true);
rectangle.MouseLeftButtonDown += OnRectangleClicked;
return rectangle;
}
private Ellipse ProcessCirclePrimitive(ICirclePrimitive circlePrimitive)
{
Ellipse ellipse = new Ellipse
{
Height = circlePrimitive.Diameter * zoomFactor,
Width = circlePrimitive.Diameter * zoomFactor
};
double addX = circlePrimitive.Diameter / 2 * zoomFactor;
double addY = circlePrimitive.Diameter / 2 * zoomFactor;
ProcessShape(ellipse, circlePrimitive, addX, addY, true);
ellipse.MouseLeftButtonDown += OnEllipseClicked;
return ellipse;
}
private void ProcessShape(Shape shape, IValuePrimitive valuePrimitive, double addX, double addY, bool addCenter)
{
SolidColorBrush brush = new SolidColorBrush();
brush.Color = ColorOperations.GetColorByValue(ValueRange, ColorMap, valuePrimitive.Value);
foreach (var valueRange in ValueColorRanges)
{
if (valuePrimitive.Value >= valueRange.ExactValues.BottomValue & valuePrimitive.Value <= valueRange.ExactValues.TopValue & (!valueRange.IsActive))
{
brush.Color = Colors.Gray;
}
}
shape.ToolTip = roundLogic.RoundValue(valuePrimitive.Value);
shape.Tag = valuePrimitive;
shape.Fill = brush;
shape.StrokeThickness = 0.0;
double addLeft = -addX - DX * zoomFactor;
double addTop = -addY + DY * zoomFactor;
if (addCenter == true)
{
addLeft += valuePrimitive.CenterX * zoomFactor;
addTop -= valuePrimitive.CenterY * zoomFactor;
}
Canvas.SetLeft(shape, addLeft);
Canvas.SetTop(shape, addTop);
}
private void OnRectangleClicked(object sender, MouseButtonEventArgs e)
{
if (sender is not Rectangle rectangle)
return;
if (rectangle.Tag is not IRectanglePrimitive primitive)
return;
AddLabel(primitive);
e.Handled = true;
}
private void OnEllipseClicked(object sender, MouseButtonEventArgs e)
{
if (sender is not Ellipse ellipse)
return;
if (ellipse.Tag is not ICirclePrimitive primitive)
return;
AddLabel(primitive);
e.Handled = true;
}
private void AddLabel(IValuePrimitive primitive)
{
var value = roundLogic.RoundValue(primitive.Value);
var label = new ValueLabelControl()
{
Value = Convert.ToString(value),
Scale = WorkPlaneCanvas.Height / 600,
X = (primitive.CenterX - DX) * zoomFactor,
Y = (- primitive.CenterY + DY) * zoomFactor,
};
WorkPlaneCanvas.Children.Add(label);
}
private void OnPathClicked(object sender, MouseButtonEventArgs e)
{
if (sender is not Path path)
return;
if (path.Tag is not ITrianglePrimitive primitive)
return;
AddLabel(primitive);
e.Handled = true;
}
}
}

View File

@@ -1,14 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace FieldVisualizer.Entities.Values.Primitives
{
public interface IPrimitiveSet
{
string Name { get; }
string Name { get; set; }
string SubTitle { get; set; }
IEnumerable<IValuePrimitive> ValuePrimitives { get; }
}

View File

@@ -8,12 +8,13 @@ namespace FieldVisualizer.Entities.Values.Primitives
{
public class PrimitiveSet : IPrimitiveSet
{
public string Name { get; set; }
public string Name { get; set; } = string.Empty;
public string SubTitle { get; set; } = string.Empty;
public IEnumerable<IValuePrimitive> ValuePrimitives { get; set;}
public PrimitiveSet()
{
Name = "New set of primitives";
ValuePrimitives = new List<IValuePrimitive>();
}
}

View File

@@ -14,8 +14,8 @@ namespace FieldVisualizer.Entities.Values.Primitives
// --- Computed properties ---
// Centroid (geometric center)
public double CenterX => (Point1.X + Point2.X + Point3.X) / 3.0;
public double CenterY => (Point1.Y + Point2.Y + Point3.Y) / 3.0;
public double CenterX => (Point1.X + Point2.X + Point3.X) / 3d;
public double CenterY => (Point1.Y + Point2.Y + Point3.Y) / 3d;
// Triangle area using determinant formula
public double Area =>

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace FieldVisualizer.Entities.Values
{
public class ValueLabel : IValueLabel
{
public bool IsActive { get; set; } = true;
public string Value { get; set; } = string.Empty;
public double X { get; set; }
public double Y { get; set; }
}
}

View File

@@ -1,4 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
<ItemGroup>
<Compile Update="Windows\UserControls\ValueLabelControl.xaml.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
</Project>

View File

@@ -3,20 +3,16 @@ using FieldVisualizer.Entities.ColorMaps.Factories;
using FieldVisualizer.Entities.Values;
using FieldVisualizer.Entities.Values.Primitives;
using FieldVisualizer.Infrastructure.Commands;
using FieldVisualizer.InfraStructures.Exceptions;
using FieldVisualizer.InfraStructures.Strings;
using FieldVisualizer.Services.ColorServices;
using FieldVisualizer.Services.PrimitiveServices;
using FieldVisualizer.Services.ValueRanges;
using FieldVisualizer.Windows.UserControls;
using StructureHelperCommon.Services;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
@@ -25,8 +21,26 @@ namespace FieldVisualizer.ViewModels.FieldViewerViewModels
{
public class FieldViewerViewModel : ViewModelBase, IDataErrorInfo
{
private IMathRoundLogic roundLogic = new SmartRoundLogic() { DigitQuant = 3 };
public ICommand RebuildCommand { get; }
private ICommand setCrossLineCommand;
private IPrimitiveSet primitiveSet;
private double dX, dY;
private ColorMapsTypes _ColorMapType = ColorMapsTypes.LiraSpectrum;
private IColorMap _ColorMap;
private IValueRange valueRange;
private IEnumerable<IValueRange> valueRanges;
private IEnumerable<IValueColorRange> valueColorRanges;
private bool setMinValue;
private bool setMaxValue;
private double crossLineX;
private double crossLineY;
private double sumAboveLine;
private double sumUnderLine;
private Line previosLine;
const int RangeNumber = 16;
private const int zoomFactor = 1000;
private RelayCommand rebuildCommand;
public ICommand RebuildCommand => rebuildCommand ??= new RelayCommand(o => ProcessPrimitives(), o => PrimitiveValidation());
public ICommand ZoomInCommand { get; }
public ICommand ZoomOutCommand { get; }
public ICommand ChangeColorMapCommand { get; }
@@ -148,6 +162,9 @@ namespace FieldVisualizer.ViewModels.FieldViewerViewModels
public double SumUnderLine { get => sumUnderLine; set => SetProperty(ref sumUnderLine, value); }
public string Error { get; }
public string Title { get; set; }
public string SubTitle { get; set; }
public string this[string columnName]
{
get
@@ -157,27 +174,10 @@ namespace FieldVisualizer.ViewModels.FieldViewerViewModels
}
}
private ICommand setCrossLineCommand;
private IPrimitiveSet primitiveSet;
private double dX, dY;
private ColorMapsTypes _ColorMapType;
private IColorMap _ColorMap;
private IValueRange valueRange;
private IEnumerable<IValueRange> valueRanges;
private IEnumerable<IValueColorRange> valueColorRanges;
private bool setMinValue;
private bool setMaxValue;
private double crossLineX;
private double crossLineY;
private double sumAboveLine;
private double sumUnderLine;
private Line previosLine;
const int RangeNumber = 16;
public FieldViewerViewModel()
{
_ColorMapType = ColorMapsTypes.LiraSpectrum;
RebuildCommand = new RelayCommand(o => ProcessPrimitives(), o => PrimitiveValidation());
ZoomInCommand = new RelayCommand(o => Zoom(1.2), o => PrimitiveValidation());
ZoomOutCommand = new RelayCommand(o => Zoom(0.8), o => PrimitiveValidation());
ChangeColorMapCommand = new RelayCommand(o => ChangeColorMap(), o => PrimitiveValidation());
@@ -207,114 +207,29 @@ namespace FieldVisualizer.ViewModels.FieldViewerViewModels
private void ProcessPrimitives()
{
WorkPlaneCanvas.Children.Clear();
double sizeX = PrimitiveOperations.GetSizeX(PrimitiveSet.ValuePrimitives);
double sizeY = PrimitiveOperations.GetSizeY(PrimitiveSet.ValuePrimitives);
Title = PrimitiveSet.Name;
OnPropertyChanged(nameof(Title));
SubTitle = PrimitiveSet.SubTitle;
OnPropertyChanged(nameof(SubTitle));
double sizeX = PrimitiveOperations.GetSizeX(PrimitiveSet.ValuePrimitives) * zoomFactor;
double sizeY = PrimitiveOperations.GetSizeY(PrimitiveSet.ValuePrimitives) * zoomFactor;
dX = PrimitiveOperations.GetMinMaxX(PrimitiveSet.ValuePrimitives)[0];
dY = PrimitiveOperations.GetMinMaxY(PrimitiveSet.ValuePrimitives)[1];
WorkPlaneCanvas.Width = Math.Abs(sizeX);
WorkPlaneCanvas.Height = Math.Abs(sizeY);
WorkPlaneBox.Width = ScrolWidth - 50;
WorkPlaneBox.Height = ScrolHeight - 50;
foreach (var primitive in PrimitiveSet.ValuePrimitives)
WorkPlaneBox.Width = (ScrolWidth - 50);
WorkPlaneBox.Height = (ScrolHeight - 50);
var logic = new AddPrimitivesToCanvasLogic()
{
if (primitive is IRectanglePrimitive rectanglePrimitive)
{
Rectangle rectangle = ProcessRectanglePrimitive(rectanglePrimitive);
WorkPlaneCanvas.Children.Add(rectangle);
}
else if (primitive is ICirclePrimitive circlePrimitive)
{
Ellipse ellipse = ProcessCirclePrimitive(circlePrimitive);
WorkPlaneCanvas.Children.Add(ellipse);
}
else if (primitive is ITrianglePrimitive triangle)
{
Path path = ProcessTrianglePrimitive(triangle);
WorkPlaneCanvas.Children.Add(path);
}
else { throw new FieldVisulizerException(ErrorStrings.PrimitiveTypeIsUnknown); }
}
}
private Path ProcessTrianglePrimitive(ITrianglePrimitive triangle)
{
// Create the PathFigure using triangle vertices.
var figure = new PathFigure
{
StartPoint = new Point(triangle.Point1.X, - triangle.Point1.Y),
IsClosed = true,
IsFilled = true
WorkPlaneCanvas = WorkPlaneCanvas,
DX = dX,
DY = dY,
ValueRange = valueRange,
ColorMap = _ColorMap,
ValueColorRanges = valueColorRanges,
};
logic.ProcessPrimitives(PrimitiveSet.ValuePrimitives);
// Add the remaining vertices as LineSegments
var segments = new PathSegmentCollection
{
new LineSegment(new Point(triangle.Point2.X, - triangle.Point2.Y), true),
new LineSegment(new Point(triangle.Point3.X, - triangle.Point3.Y), true)
// Closing is handled by IsClosed = true, so we don't need to add a segment back to Point1
};
figure.Segments = segments;
// Create geometry and path
var geometry = new PathGeometry();
geometry.Figures.Add(figure);
var path = new Path
{
Data = geometry,
};
ProcessShape(path, triangle, 0, 0, false);
return path;
}
private Rectangle ProcessRectanglePrimitive(IRectanglePrimitive rectanglePrimitive)
{
Rectangle rectangle = new Rectangle
{
Height = rectanglePrimitive.Height,
Width = rectanglePrimitive.Width
};
double addX = rectanglePrimitive.Width / 2;
double addY = rectanglePrimitive.Height / 2;
ProcessShape(rectangle, rectanglePrimitive, addX, addY, true);
return rectangle;
}
private Ellipse ProcessCirclePrimitive(ICirclePrimitive circlePrimitive)
{
Ellipse ellipse = new Ellipse
{
Height = circlePrimitive.Diameter,
Width = circlePrimitive.Diameter
};
double addX = circlePrimitive.Diameter / 2;
double addY = circlePrimitive.Diameter / 2;
ProcessShape(ellipse, circlePrimitive, addX, addY, true);
return ellipse;
}
private void ProcessShape(Shape shape, IValuePrimitive valuePrimitive, double addX, double addY, bool addCenter)
{
SolidColorBrush brush = new SolidColorBrush();
brush.Color = ColorOperations.GetColorByValue(valueRange, _ColorMap, valuePrimitive.Value);
foreach (var valueRange in valueColorRanges)
{
if (valuePrimitive.Value >= valueRange.ExactValues.BottomValue & valuePrimitive.Value <= valueRange.ExactValues.TopValue & (!valueRange.IsActive))
{
brush.Color = Colors.Gray;
}
}
shape.ToolTip = roundLogic.RoundValue(valuePrimitive.Value);
shape.Tag = valuePrimitive;
shape.Fill = brush;
double addLeft = - addX - dX;
double addTop = - addY + dY;
if (addCenter == true)
{
addLeft += valuePrimitive.CenterX;
addTop -= valuePrimitive.CenterY;
}
Canvas.SetLeft(shape, addLeft);
Canvas.SetTop(shape, addTop);
}
private void Zoom(double coefficient)
{

View File

@@ -24,7 +24,12 @@
<Button x:Name="ZoomOutButton" Content="ZoomOut" Command="{Binding ZoomOutCommand}"/>
<Button x:Name="ChangeColorMapButton" Content="ColorMap" Command="{Binding ChangeColorMapCommand}"/>
</StackPanel>
<ScrollViewer Name="WorkPlaneViewer" Grid.Row="1" HorizontalScrollBarVisibility="Visible" SizeChanged="WorkPlaneViewer_SizeChanged">
<Grid Grid.Row="1">
<StackPanel Panel.ZIndex="1">
<TextBlock Text="{Binding Title}" FontWeight="Bold"/>
<TextBlock Text="{Binding SubTitle}"/>
</StackPanel>
<ScrollViewer Name="WorkPlaneViewer" HorizontalScrollBarVisibility="Visible" SizeChanged="WorkPlaneViewer_SizeChanged">
<ScrollViewer.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black"/>
@@ -33,11 +38,16 @@
</LinearGradientBrush>
</ScrollViewer.Background>
<Viewbox Name="WorkPlaneBox" Margin="10">
<Canvas Name="WorkPlaneCanvas">
<Grid>
<Canvas x:Name="LabelCanvas"/>
<Canvas x:Name="WorkPlaneCanvas">
</Canvas>
</Grid>
</Viewbox>
</ScrollViewer>
</Grid>
</Grid>
<ScrollViewer Grid.Column="2">
<StackPanel>
<Expander Header="Color legend" IsExpanded="True">

View File

@@ -0,0 +1,27 @@
<UserControl x:Class="FieldVisualizer.Windows.UserControls.ValueLabelControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FieldVisualizer.Windows.UserControls"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="100"
Name="ThisControl">
<Grid DataContext="{Binding ElementName=ThisControl}">
<Canvas>
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="{Binding Scale}" ScaleY="{Binding Scale}"/>
<TranslateTransform X="{Binding X}" Y="{Binding Y}"/>
</TransformGroup>
</Canvas.RenderTransform>
<Path Data="M 0 0 l 20 -25 v 16 z" Fill="AntiqueWhite"/>
<Canvas>
<Canvas.RenderTransform>
<TranslateTransform X="20" Y="-25"/>
</Canvas.RenderTransform>
<TextBlock MinWidth="25" Text="{Binding Value}" Background="AntiqueWhite"/>
</Canvas>
</Canvas>
</Grid>
</UserControl>

View File

@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace FieldVisualizer.Windows.UserControls
{
/// <summary>
/// Interaction logic for ValueLabelControl.xaml
/// </summary>
public partial class ValueLabelControl : UserControl
{
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
// Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(nameof(Value), typeof(string), typeof(ValueLabelControl), new PropertyMetadata("Label value"));
public double X
{
get { return (double)GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
// Using a DependencyProperty as the backing store for X. This enables animation, styling, binding, etc...
public static readonly DependencyProperty XProperty =
DependencyProperty.Register(nameof(X), typeof(double), typeof(ValueLabelControl), new PropertyMetadata(0.0));
public double Y
{
get { return (double)GetValue(YProperty); }
set { SetValue(YProperty, value); }
}
// Using a DependencyProperty as the backing store for Y. This enables animation, styling, binding, etc...
public static readonly DependencyProperty YProperty =
DependencyProperty.Register(nameof(Y), typeof(double), typeof(ValueLabelControl), new PropertyMetadata(0.0));
public double Scale
{
get { return (double)GetValue(ScaleProperty); }
set { SetValue(ScaleProperty, value); }
}
// Using a DependencyProperty as the backing store for Scale. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ScaleProperty =
DependencyProperty.Register(nameof(Scale), typeof(double), typeof(ValueLabelControl), new PropertyMetadata(0.0));
public ValueLabelControl()
{
InitializeComponent();
}
}
}

View File

@@ -30,7 +30,7 @@ namespace StructureHelper.Infrastructure
OnPropertyChanged(nameof(Camera));
}
}
public System.Windows.Media.Color AmbientLightColor
public Color AmbientLightColor
{
get => ambientLightColor;
set
@@ -39,7 +39,7 @@ namespace StructureHelper.Infrastructure
OnPropertyChanged(nameof(AmbientLightColor));
}
}
public System.Windows.Media.Color DirectionalLightColor { get; set; }
public Color DirectionalLightColor { get; set; }
public Vector3D DirectionalLightDirection
{
get => directionalLightDirection;
@@ -87,6 +87,7 @@ namespace StructureHelper.Infrastructure
OnPropertyChanged(nameof(Title));
}
}
public string SubTitle
{
get => subTitle;

View File

@@ -17,6 +17,7 @@ namespace StructureHelper.Services.ResultViewers
static IUnit unitStress = UnitLogic.GetUnit(UnitTypes.Stress);
static IUnit unitLength = UnitLogic.GetUnit(UnitTypes.Length, "mm");
static IUnit unitStrain = UnitLogic.GetUnit(UnitTypes.Strain);
public static List<CrackResultFunc> GetResultFuncs()
{
@@ -69,28 +70,28 @@ namespace StructureHelper.Services.ResultViewers
Name = "Long rebar strain",
ResultFunction = (IRebarCrackResult rebar) => rebar.LongTermResult.RebarStressResult.RebarStrain,
UnitFactor = 1d,
UnitName = string.Empty
UnitName = unitStrain.Name
},
new()
{
Name = "Short rebar strain",
ResultFunction = (IRebarCrackResult rebar) => rebar.ShortTermResult.RebarStressResult.RebarStrain,
UnitFactor = 1d,
UnitName = string.Empty
UnitName = unitStrain.Name
},
new()
{
Name = "Long concrete strain",
ResultFunction = (IRebarCrackResult rebar) => rebar.LongTermResult.RebarStressResult.ConcreteStrain,
UnitFactor = 1d,
UnitName = string.Empty
UnitName = unitStrain.Name
},
new()
{
Name = "Short concrete strain",
ResultFunction = (IRebarCrackResult rebar) => rebar.ShortTermResult.RebarStressResult.ConcreteStrain,
UnitFactor = 1d,
UnitName = string.Empty
UnitName = unitStrain.Name
}
};
return results;

View File

@@ -1,13 +1,8 @@
using LoaderCalculator.Logics;
using StructureHelper.Infrastructure.UI.Converters.Units;
using StructureHelperCommon.Infrastructures.Enums;
using StructureHelperCommon.Infrastructures.Exceptions;
using StructureHelperCommon.Services.Units;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StructureHelper.Services.ResultViewers
{
@@ -25,6 +20,7 @@ namespace StructureHelper.Services.ResultViewers
static IUnit unitStress = unitLogic.GetUnit(UnitTypes.Stress);
static IUnit unitMoment = unitLogic.GetUnit(UnitTypes.Moment);
static IUnit unitCurvature = unitLogic.GetUnit(UnitTypes.Curvature);
static IUnit unitStrain = unitLogic.GetUnit(UnitTypes.Strain);
static readonly IStressLogic stressLogic = new StressLogic();
public static List<ForceResultFunc> GetResultFuncs(FuncsTypes funcsType = FuncsTypes.Full)
@@ -57,12 +53,12 @@ namespace StructureHelper.Services.ResultViewers
private static List<ForceResultFunc> GetStrainResultFuncs()
{
List<ForceResultFunc> resultFuncs = [];
resultFuncs.Add(new ForceResultFunc() { Name = "Section Strain", ResultFunction = stressLogic.GetSectionStrain });
resultFuncs.Add(new ForceResultFunc() { Name = "Total Strain", ResultFunction = stressLogic.GetTotalStrain });
resultFuncs.Add(new ForceResultFunc() { Name = "Prestrain", ResultFunction = stressLogic.GetPrestrain });
resultFuncs.Add(new ForceResultFunc() { Name = "Elastic Strain", ResultFunction = stressLogic.GetElasticStrain });
resultFuncs.Add(new ForceResultFunc() { Name = "Plastic Strain", ResultFunction = stressLogic.GetPlasticStrain });
resultFuncs.Add(new ForceResultFunc() { Name = "Limit Strain Ratio", ResultFunction = stressLogic.GetLimitStrainRatio });
resultFuncs.Add(new ForceResultFunc() { Name = "Section Strain", UnitName = unitStrain.Name, ResultFunction = stressLogic.GetSectionStrain });
resultFuncs.Add(new ForceResultFunc() { Name = "Total Strain", UnitName = unitStrain.Name, ResultFunction = stressLogic.GetTotalStrain });
resultFuncs.Add(new ForceResultFunc() { Name = "Prestrain", UnitName = unitStrain.Name, ResultFunction = stressLogic.GetPrestrain });
resultFuncs.Add(new ForceResultFunc() { Name = "Elastic Strain", UnitName = unitStrain.Name, ResultFunction = stressLogic.GetElasticStrain });
resultFuncs.Add(new ForceResultFunc() { Name = "Plastic Strain", UnitName = unitStrain.Name, ResultFunction = stressLogic.GetPlasticStrain });
resultFuncs.Add(new ForceResultFunc() { Name = "Limit Strain Ratio", UnitName = unitStrain.Name, ResultFunction = stressLogic.GetLimitStrainRatio });
return resultFuncs;
}
private static List<ForceResultFunc> GetStressResultFuncs()

View File

@@ -18,7 +18,6 @@ namespace StructureHelper.Services.ResultViewers
primitiveSet = GetBaseDevelopmentLength(strainMatrix, limitState, calcTerm, ndmPrimitives);
primitiveSets.Add(primitiveSet);
primitiveSet = GetDevelopmentLength(strainMatrix, limitState, calcTerm, ndmPrimitives, true);
primitiveSet.Name = "Development length full strength";
primitiveSets.Add(primitiveSet);
primitiveSet = GetDevelopmentLength(strainMatrix, limitState, calcTerm, ndmPrimitives,false);
primitiveSet.Name = "Development length actual stress";
@@ -69,7 +68,11 @@ namespace StructureHelper.Services.ResultViewers
private static PrimitiveSet GetBaseDevelopmentLength(IStrainMatrix strainMatrix, LimitStates limitState, CalcTerms calcTerm, IEnumerable<INdmPrimitive> ndmPrimitives)
{
PrimitiveSet primitiveSet = new PrimitiveSet() { Name = "Base Development Length"};
PrimitiveSet primitiveSet = new PrimitiveSet()
{
Name = "Base Development Length",
SubTitle = "mm"
};
List<IValuePrimitive> primitives = new List<IValuePrimitive>();
foreach (var item in ndmPrimitives)
{
@@ -88,7 +91,11 @@ namespace StructureHelper.Services.ResultViewers
}
private static PrimitiveSet GetDevelopmentLength(IStrainMatrix strainMatrix, LimitStates limitState, CalcTerms calcTerm, IEnumerable<INdmPrimitive> ndmPrimitives, bool fullStrength)
{
PrimitiveSet primitiveSet = new PrimitiveSet();
PrimitiveSet primitiveSet = new PrimitiveSet()
{
Name = "Development length full strength",
SubTitle = "mm"
};
List<IValuePrimitive> primitives = new List<IValuePrimitive>();
foreach (var item in ndmPrimitives)
{

View File

@@ -28,7 +28,11 @@ namespace StructureHelper.Services.ResultViewers
List<IPrimitiveSet> primitiveSets = new List<IPrimitiveSet>();
foreach (var valDelegate in resultFuncs)
{
PrimitiveSet primitiveSet = new PrimitiveSet() { Name = valDelegate.Name };
PrimitiveSet primitiveSet = new PrimitiveSet()
{
Name = valDelegate.Name,
SubTitle = valDelegate.UnitName
};
List<IValuePrimitive> primitives = new List<IValuePrimitive>();
foreach (INdm ndm in ndms)
{
@@ -45,7 +49,11 @@ namespace StructureHelper.Services.ResultViewers
List<IPrimitiveSet> primitiveSets = new List<IPrimitiveSet>();
foreach (var valDelegate in resultFuncs)
{
PrimitiveSet primitiveSet = new PrimitiveSet() { Name = valDelegate.Name };
PrimitiveSet primitiveSet = new PrimitiveSet()
{
Name = valDelegate.Name,
SubTitle = valDelegate.UnitName
};
List<IValuePrimitive> primitives = new List<IValuePrimitive>();
foreach (var rebarResult in rebarResults)
{

View File

@@ -5,9 +5,6 @@ using StructureHelper.Windows.ViewModels.Errors;
using StructureHelperLogics.NdmCalculations.Cracking;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StructureHelper.Windows.CalculationWindows.CalculatorsViews
{

View File

@@ -53,6 +53,7 @@ namespace StructureHelper.Windows.CalculationWindows.CalculatorsViews.ForceCalcu
{
selectedPrimitiveSet = value;
ViewportViewModel.Title = selectedPrimitiveSet.Name;
ViewportViewModel.SubTitle = selectedPrimitiveSet.SubTitle;
OnPropertyChanged(nameof(SelectedPrimitiveSet));
RebuildPrimitives();
}

View File

@@ -3,11 +3,11 @@ using StructureHelper.Infrastructure.Enums;
using StructureHelper.Infrastructure.UI.DataContexts;
using StructureHelper.Windows.PrimitivePropertiesWindow;
using StructureHelper.Windows.Services;
using StructureHelper.Windows.ViewModels.Errors;
using StructureHelperCommon.Infrastructures.Exceptions;
using StructureHelperCommon.Models.Calculators;
using StructureHelperCommon.Models.Shapes;
using StructureHelperLogics.Models.CrossSections;
using StructureHelperLogics.Models.Primitives;
using StructureHelperLogics.NdmCalculations.Analyses.ByForces;
using StructureHelperLogics.NdmCalculations.Analyses.Curvatures;
using StructureHelperLogics.NdmCalculations.Analyses.ValueDiagrams;
@@ -77,7 +77,7 @@ namespace StructureHelper.Windows.ViewModels.NdmCrossSections
var dialogResult = MessageBox.Show("Delete all primitives?", "Please, confirm deleting", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (dialogResult == DialogResult.Yes)
{
ClearRepository();
SafetyProcessor.RunSafeProcess(ClearRepository, "Error of deleting primitives");
Refresh();
OnPropertyChanged(nameof(Items));
OnPropertyChanged(nameof(PrimitivesCount));
@@ -86,30 +86,7 @@ namespace StructureHelper.Windows.ViewModels.NdmCrossSections
private void ClearRepository()
{
repository.Primitives.Clear();
foreach (var calculator in repository.Calculators)
{
if (calculator is IForceCalculator forceCalculator)
{
forceCalculator.InputData.Primitives.Clear();
}
else if (calculator is ICrackCalculator crackCalculator)
{
crackCalculator.InputData.Primitives.Clear();
}
else if (calculator is IValueDiagramCalculator valueDiagramCalculator)
{
valueDiagramCalculator.InputData.Primitives.Clear();
}
else if (calculator is ICurvatureCalculator curvatureCalculator)
{
curvatureCalculator.InputData.Primitives.Clear();
}
else
{
// skip
}
}
repository.Operations.Primitives.RemoveAll();
Items.Clear();
}
@@ -139,29 +116,6 @@ namespace StructureHelper.Windows.ViewModels.NdmCrossSections
private void RemoveFromRepository(PrimitiveBase item)
{
repository.Primitives.Remove(item.NdmPrimitive);
foreach (var calculator in repository.Calculators)
{
if (calculator is IForceCalculator forceCalculator)
{
forceCalculator.InputData.Primitives.Remove(item.NdmPrimitive);
}
else if (calculator is ICrackCalculator crackCalculator)
{
crackCalculator.InputData.Primitives.Remove(item.NdmPrimitive);
}
else if (calculator is IValueDiagramCalculator valueDiagramCalculator)
{
valueDiagramCalculator.InputData.Primitives.Remove(item.NdmPrimitive);
}
else if (calculator is ICurvatureCalculator curvatureCalculator)
{
curvatureCalculator.InputData.Primitives.Remove(item.NdmPrimitive);
}
else
{
// skip
}
}
Items.Remove(item);
}

View File

@@ -8,6 +8,7 @@
Force,
Moment,
Curvature,
DistributedLoad
DistributedLoad,
Strain
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace StructureHelperCommon.Infrastructures.Interfaces
{
public interface IRepositoryOperation<T, V>
{
void RemoveAll();
void Remove(V entity);
void Remove(IEnumerable<V> entities);
}
}

View File

@@ -25,6 +25,7 @@ namespace StructureHelperCommon.Services.Units
{ UnitTypes.Stress, "MPa"},
{ UnitTypes.Curvature, "1/m"},
{ UnitTypes.DistributedLoad, "kN/m" },
{ UnitTypes.Strain, "Dimensionless" },
};
}

View File

@@ -42,6 +42,8 @@ namespace StructureHelperCommon.Services.Units
units.Add(new Unit() { UnitType = type, Name = "N/m", Multiplyer = 1d });
units.Add(new Unit() { UnitType = type, Name = "kN/m", Multiplyer = 1e-3d });
units.Add(new Unit() { UnitType = type, Name = "MN/m", Multiplyer = 1e-6d });
type = UnitTypes.Strain;
units.Add(new Unit() { UnitType = type, Name = "Dimensionless", Multiplyer = 1d });
return units;
}
}

View File

@@ -7,12 +7,16 @@ namespace StructureHelperLogics.Models.CrossSections
{
public class CrossSectionRepository : ICrossSectionRepository
{
private RepositoryOperationsLogic operations;
public Guid Id { get; }
public List<IForceAction> ForceActions { get; private set; } = new();
public List<IHeadMaterial> HeadMaterials { get; private set; } = new();
public List<INdmPrimitive> Primitives { get; } = new();
public List<ICalculator> Calculators { get; private set; } = new();
public IRepositoryOperationsLogic Operations => operations ??= new RepositoryOperationsLogic(this);
public CrossSectionRepository(Guid id)
{
Id = id;

View File

@@ -1,13 +1,15 @@
using StructureHelperCommon.Infrastructures.Interfaces;
using StructureHelperCommon.Models.Calculators;
using StructureHelperCommon.Models.Forces;
using StructureHelperLogics.Models.Materials;
using StructureHelperLogics.NdmCalculations.Primitives;
namespace StructureHelperLogics.Models.CrossSections
{
/// <summary>
/// Repository of members of cross-section
/// </summary>
public interface ICrossSectionRepository : ISaveable, IHasHeadMaterials, IHasForcesAndPrimitives, IHasCalculators
{
IRepositoryOperationsLogic Operations { get; }
}
}

View File

@@ -0,0 +1,13 @@
using StructureHelperCommon.Infrastructures.Interfaces;
using StructureHelperLogics.NdmCalculations.Primitives;
using System;
using System.Collections.Generic;
using System.Text;
namespace StructureHelperLogics.Models.CrossSections
{
public interface IRepositoryOperationsLogic
{
IRepositoryOperation<ICrossSectionRepository, INdmPrimitive> Primitives { get; }
}
}

View File

@@ -0,0 +1,18 @@
using StructureHelperCommon.Infrastructures.Interfaces;
using StructureHelperLogics.NdmCalculations.Primitives;
namespace StructureHelperLogics.Models.CrossSections
{
public class RepositoryOperationsLogic : IRepositoryOperationsLogic
{
private ICrossSectionRepository repository;
private RepositoryPrimitiveOperation primitiveLogic;
public RepositoryOperationsLogic(ICrossSectionRepository repository)
{
this.repository = repository;
}
public IRepositoryOperation<ICrossSectionRepository, INdmPrimitive> Primitives => primitiveLogic ??= new RepositoryPrimitiveOperation(repository);
}
}

View File

@@ -1,18 +1,13 @@
using LoaderCalculator.Data.Matrix;
using LoaderCalculator;
using LoaderCalculator;
using LoaderCalculator.Data.Matrix;
using LoaderCalculator.Data.Ndms;
using LoaderCalculator.Data.ResultData;
using LoaderCalculator.Data.SourceData;
using LoaderCalculator.Logics;
using StructureHelperCommon.Models;
using StructureHelperCommon.Models.Calculators;
using StructureHelperCommon.Models.Loggers;
using StructureHelperLogics.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LoaderCalculator.Logics;
using LoaderCalculator.Data.Ndms;
namespace StructureHelperLogics.NdmCalculations.Analyses.ByForces
{

View File

@@ -0,0 +1,92 @@
using StructureHelperCommon.Infrastructures.Exceptions;
using StructureHelperCommon.Infrastructures.Interfaces;
using StructureHelperLogics.Models.CrossSections;
using StructureHelperLogics.NdmCalculations.Analyses.ByForces;
using StructureHelperLogics.NdmCalculations.Analyses.Curvatures;
using StructureHelperLogics.NdmCalculations.Analyses.ValueDiagrams;
using StructureHelperLogics.NdmCalculations.Cracking;
namespace StructureHelperLogics.NdmCalculations.Primitives
{
public class RepositoryPrimitiveOperation : IRepositoryOperation<ICrossSectionRepository, INdmPrimitive>
{
private ICrossSectionRepository repository;
public RepositoryPrimitiveOperation(ICrossSectionRepository repository)
{
this.repository = repository;
}
public void RemoveAll()
{
foreach (var calculator in repository.Calculators)
{
if (calculator is IForceCalculator forceCalculator)
{
forceCalculator.InputData.Primitives.Clear();
}
else if (calculator is ICrackCalculator crackCalculator)
{
crackCalculator.InputData.Primitives.Clear();
}
else if (calculator is IValueDiagramCalculator valueDiagramCalculator)
{
valueDiagramCalculator.InputData.Primitives.Clear();
}
else if (calculator is ICurvatureCalculator curvatureCalculator)
{
curvatureCalculator.InputData.Primitives.Clear();
}
else if (calculator is ILimitCurvesCalculator limitCurve)
{
// skip
}
else
{
throw new StructureHelperException(ErrorStrings.ObjectTypeIsUnknownObj(calculator) + $": Unsupported calculator type: { calculator.GetType().FullName }");
}
}
repository.Primitives.Clear();
}
public void Remove(IEnumerable<INdmPrimitive> entities)
{
foreach (var item in entities)
{
Remove(item);
}
}
public void Remove(INdmPrimitive entity)
{
foreach (var calculator in repository.Calculators)
{
if (calculator is IForceCalculator forceCalculator)
{
forceCalculator.InputData.Primitives.Remove(entity);
}
else if (calculator is ICrackCalculator crackCalculator)
{
crackCalculator.InputData.Primitives.Remove(entity);
}
else if (calculator is IValueDiagramCalculator valueDiagramCalculator)
{
valueDiagramCalculator.InputData.Primitives.Remove(entity);
}
else if (calculator is ICurvatureCalculator curvatureCalculator)
{
curvatureCalculator.InputData.Primitives.Remove(entity);
}
else if (calculator is ILimitCurvesCalculator limitCurve)
{
// skip, nothing to do
}
else
{
throw new StructureHelperException(ErrorStrings.ObjectTypeIsUnknownObj(calculator) + $": Unsupported calculator type: {calculator.GetType().FullName}");
}
}
repository.Primitives.Remove(entity);
}
}
}

View File

@@ -0,0 +1,137 @@
using Moq;
using NUnit.Framework;
using StructureHelperCommon.Infrastructures.Exceptions;
using StructureHelperCommon.Models.Calculators;
using StructureHelperLogics.Models.CrossSections;
using StructureHelperLogics.NdmCalculations.Analyses.ByForces;
using StructureHelperLogics.NdmCalculations.Analyses.Curvatures;
using StructureHelperLogics.NdmCalculations.Analyses.ValueDiagrams;
using StructureHelperLogics.NdmCalculations.Cracking;
using StructureHelperLogics.NdmCalculations.Primitives;
using System;
using System.Collections.Generic;
namespace StructureHelperTests.UnitTests.CrossSections
{
[TestFixture]
public class RepositoryPrimitiveOperationTests
{
private Mock<ICrossSectionRepository> repositoryMock;
private List<INdmPrimitive> repositoryPrimitives;
private Mock<IForceCalculator> forceCalculatorMock;
private Mock<ICrackCalculator> crackCalculatorMock;
private Mock<IValueDiagramCalculator> valueDiagramCalculatorMock;
private Mock<ICurvatureCalculator> curvatureCalculatorMock;
private Mock<ILimitCurvesCalculator> limitCurvesCalculatorMock;
private List<INdmPrimitive> forcePrimitives;
private List<INdmPrimitive> crackPrimitives;
private List<INdmPrimitive> valueDiagramPrimitives;
private List<INdmPrimitive> curvaturePrimitives;
private RepositoryPrimitiveOperation operation;
[SetUp]
public void SetUp()
{
repositoryPrimitives = new List<INdmPrimitive>
{
Mock.Of<INdmPrimitive>(),
Mock.Of<INdmPrimitive>()
};
forcePrimitives = new List<INdmPrimitive>(repositoryPrimitives);
crackPrimitives = new List<INdmPrimitive>(repositoryPrimitives);
valueDiagramPrimitives = new List<INdmPrimitive>(repositoryPrimitives);
curvaturePrimitives = new List<INdmPrimitive>(repositoryPrimitives);
forceCalculatorMock = new Mock<IForceCalculator>();
crackCalculatorMock = new Mock<ICrackCalculator>();
valueDiagramCalculatorMock = new Mock<IValueDiagramCalculator>();
curvatureCalculatorMock = new Mock<ICurvatureCalculator>();
limitCurvesCalculatorMock = new Mock<ILimitCurvesCalculator>();
repositoryMock = new Mock<ICrossSectionRepository>();
repositoryMock.Setup(r => r.Primitives).Returns(repositoryPrimitives);
repositoryMock.Setup(r => r.Calculators).Returns(new List<ICalculator>
{
forceCalculatorMock.Object,
crackCalculatorMock.Object,
valueDiagramCalculatorMock.Object,
curvatureCalculatorMock.Object,
limitCurvesCalculatorMock.Object
});
forceCalculatorMock.Setup(x => x.InputData.Primitives).Returns(forcePrimitives);
crackCalculatorMock.Setup(x => x.InputData.Primitives).Returns(crackPrimitives);
valueDiagramCalculatorMock.Setup(x => x.InputData.Primitives).Returns(valueDiagramPrimitives);
curvatureCalculatorMock.Setup(x => x.InputData.Primitives).Returns(curvaturePrimitives);
operation = new RepositoryPrimitiveOperation(repositoryMock.Object);
}
[Test]
public void RemoveAll_ClearsPrimitivesInAllSupportedCalculators_AndRepository()
{
// Act
operation.RemoveAll();
// Assert
Assert.That(forcePrimitives, Is.Empty);
Assert.That(crackPrimitives, Is.Empty);
Assert.That(valueDiagramPrimitives, Is.Empty);
Assert.That(curvaturePrimitives, Is.Empty);
Assert.That(repositoryPrimitives, Is.Empty);
}
[Test]
public void Remove_RemovesPrimitiveFromAllCalculators_AndRepository()
{
// Arrange
var primitive = repositoryPrimitives[0];
// Act
operation.Remove(primitive);
// Assert
Assert.That(forcePrimitives, Does.Not.Contain(primitive));
Assert.That(crackPrimitives, Does.Not.Contain(primitive));
Assert.That(valueDiagramPrimitives, Does.Not.Contain(primitive));
Assert.That(curvaturePrimitives, Does.Not.Contain(primitive));
Assert.That(repositoryPrimitives, Does.Not.Contain(primitive));
}
[Test]
public void Remove_MultipleEntities_RemovesAll()
{
// Arrange
var toRemove = new List<INdmPrimitive>(repositoryPrimitives);
// Act
operation.Remove(toRemove);
// Assert
Assert.That(forcePrimitives, Is.Empty);
Assert.That(crackPrimitives, Is.Empty);
Assert.That(valueDiagramPrimitives, Is.Empty);
Assert.That(curvaturePrimitives, Is.Empty);
Assert.That(repositoryPrimitives, Is.Empty);
}
[Test]
public void RemoveAll_UnsupportedCalculatorType_ThrowsStructureHelperException()
{
// Arrange
Mock<ICalculator> calculator = new Mock<ICalculator>();
repositoryMock.Setup(r => r.Calculators)
.Returns(new List<ICalculator>() { calculator.Object});
// Act + Assert
Assert.Throws<StructureHelperException>(() => operation.RemoveAll());
}
}
}