diff --git a/.gitignore b/.gitignore index d596b37..95c0a05 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *\bin *\packages *\.vs +**\publish diff --git a/App.xaml b/App.xaml deleted file mode 100644 index 6c920ed..0000000 --- a/App.xaml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - diff --git a/App.xaml.cs b/App.xaml.cs deleted file mode 100644 index a696ce9..0000000 --- a/App.xaml.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Windows; - -namespace StructureHelper -{ - /// - /// Логика взаимодействия для App.xaml - /// - public partial class App : Application - { - } -} diff --git a/FieldVisualizer/Entities/ColorMaps/ColorMap.cs b/FieldVisualizer/Entities/ColorMaps/ColorMap.cs new file mode 100644 index 0000000..faf1a80 --- /dev/null +++ b/FieldVisualizer/Entities/ColorMaps/ColorMap.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Windows.Media; +using System.Text; + +namespace FieldVisualizer.Entities.ColorMaps +{ + public class ColorMap : IColorMap + { + public string Name { get; set; } + public List Colors { get; set; } + } +} diff --git a/FieldVisualizer/Entities/ColorMaps/Factories/ColorMapFactory.cs b/FieldVisualizer/Entities/ColorMaps/Factories/ColorMapFactory.cs new file mode 100644 index 0000000..b5c0010 --- /dev/null +++ b/FieldVisualizer/Entities/ColorMaps/Factories/ColorMapFactory.cs @@ -0,0 +1,89 @@ +using FieldVisualizer.InfraStructures.Enums; +using FieldVisualizer.InfraStructures.Exceptions; +using FieldVisualizer.InfraStructures.Strings; +using System; +using System.Collections.Generic; +using System.Windows.Media; + +namespace FieldVisualizer.Entities.ColorMaps.Factories +{ + /// + /// Factory for creating of different color maps + /// + public static class ColorMapFactory + { + public static IColorMap GetColorMap(ColorMapsTypes mapsTypes) + { + if (mapsTypes == ColorMapsTypes.FullSpectrum) { return GetFullSpectrum(); } + if (mapsTypes == ColorMapsTypes.RedToWhite) { return GetRedToWhite(); } + if (mapsTypes == ColorMapsTypes.RedToBlue) { return GetRedToBlue(); } + if (mapsTypes == ColorMapsTypes.BlueToWhite) { return GetBlueToWhite(); } + else { throw new FieldVisulizerException(ErrorStrings.ColorMapTypeIsUnknown); } + } + + private static IColorMap GetFullSpectrum() + { + ColorMap colorMap = new ColorMap(); + colorMap.Name = "FullSpectrumColorMap"; + List colors = new List(); + byte Alpha = 0xff; + colors.AddRange(new Color[]{ + Color.FromArgb(Alpha, 0xFF, 0x80, 0x80) ,// + Color.FromArgb(Alpha, 0xFF, 0, 0x80) ,// + Color.FromArgb(Alpha, 0xFF, 0, 0) ,//Red + Color.FromArgb(Alpha, 0xFF, 0x45, 0) ,//Orange Red + Color.FromArgb(Alpha, 0xFF, 0xD7, 0) ,//Gold + Color.FromArgb(Alpha, 0xFF, 0xFF, 0) ,//Yellow + Color.FromArgb(Alpha, 0x9A, 0xCD, 0x32) ,//Yellow Green + Color.FromArgb(Alpha, 0, 0x80, 0) ,//Green + Color.FromArgb(Alpha, 0, 0x64, 0) ,//Dark Green + Color.FromArgb(Alpha, 0x2F, 0x4F, 0x4F) ,//Dark Slate Gray + Color.FromArgb(Alpha, 0, 0, 0xFF) ,//Blue + }); + colorMap.Colors = colors; + return colorMap; + } + + private static IColorMap GetRedToWhite() + { + ColorMap colorMap = new ColorMap(); + colorMap.Name = "FullSpectrumColorMap"; + List colors = new List(); + byte Alpha = 0xff; + colors.AddRange(new Color[]{ + Color.FromArgb(Alpha, 0xFF, 0, 0) ,//Red + Color.FromArgb(Alpha, 0xFF, 0xFF, 0xFF) ,//White + }); + colorMap.Colors = colors; + return colorMap; + } + + private static IColorMap GetRedToBlue() + { + ColorMap colorMap = new ColorMap(); + colorMap.Name = "FullSpectrumColorMap"; + List colors = new List(); + byte Alpha = 0xff; + colors.AddRange(new Color[]{ + Color.FromArgb(Alpha, 0xFF, 0, 0) ,//Red + Color.FromArgb(Alpha, 0, 0, 0xFF) ,//Blue + }); + colorMap.Colors = colors; + return colorMap; + } + + private static IColorMap GetBlueToWhite() + { + ColorMap colorMap = new ColorMap(); + colorMap.Name = "FullSpectrumColorMap"; + List colors = new List(); + byte Alpha = 0xff; + colors.AddRange(new Color[]{ + Color.FromArgb(Alpha, 0, 0, 0xFF) ,//Blue + Color.FromArgb(Alpha, 0xFF, 0xFF, 0xFF) ,//White + }); + colorMap.Colors = colors; + return colorMap; + } + } +} diff --git a/FieldVisualizer/Entities/ColorMaps/IColorMap.cs b/FieldVisualizer/Entities/ColorMaps/IColorMap.cs new file mode 100644 index 0000000..0d77ae3 --- /dev/null +++ b/FieldVisualizer/Entities/ColorMaps/IColorMap.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Windows.Media; +using System.Text; + +namespace FieldVisualizer.Entities.ColorMaps +{ + public interface IColorMap + { + string Name { get;} + List Colors { get; } + } +} diff --git a/FieldVisualizer/Entities/ColorMaps/IValueColorRange.cs b/FieldVisualizer/Entities/ColorMaps/IValueColorRange.cs new file mode 100644 index 0000000..d4da2bc --- /dev/null +++ b/FieldVisualizer/Entities/ColorMaps/IValueColorRange.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Media; + +namespace FieldVisualizer.Entities.ColorMaps +{ + public interface IValueColorRange + { + bool IsActive { get; set; } + double BottomValue { get; set; } + double AverageValue { get; set; } + double TopValue {get;set;} + Color BottomColor { get; set; } + Color TopColor { get; set; } + } +} diff --git a/FieldVisualizer/Entities/ColorMaps/ValueColorRange.cs b/FieldVisualizer/Entities/ColorMaps/ValueColorRange.cs new file mode 100644 index 0000000..92eaff5 --- /dev/null +++ b/FieldVisualizer/Entities/ColorMaps/ValueColorRange.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Media; + +namespace FieldVisualizer.Entities.ColorMaps +{ + public class ValueColorRange : IValueColorRange + { + public bool IsActive { get; set; } + public double BottomValue { get; set; } + public double AverageValue { get; set; } + public double TopValue { get; set; } + public Color BottomColor { get; set; } + public Color TopColor { get; set; } + } +} diff --git a/FieldVisualizer/Entities/Values/IValueRange.cs b/FieldVisualizer/Entities/Values/IValueRange.cs new file mode 100644 index 0000000..976e436 --- /dev/null +++ b/FieldVisualizer/Entities/Values/IValueRange.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace FieldVisualizer.Entities.Values +{ + public interface IValueRange + { + double TopValue { get; set; } + double BottomValue { get; set; } + } +} diff --git a/FieldVisualizer/Entities/Values/Primitives/CirclePrimitive.cs b/FieldVisualizer/Entities/Values/Primitives/CirclePrimitive.cs new file mode 100644 index 0000000..b44a723 --- /dev/null +++ b/FieldVisualizer/Entities/Values/Primitives/CirclePrimitive.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FieldVisualizer.Entities.Values.Primitives +{ + public class CirclePrimitive : ICirclePrimitive + { + public double Diameter { get; set; } + public double Value { get; set; } + public double CenterX { get; set; } + public double CenterY { get; set; } + public double Area => Math.PI * Math.Pow(Diameter, 2) / 4; + } +} diff --git a/FieldVisualizer/Entities/Values/Primitives/ICenter.cs b/FieldVisualizer/Entities/Values/Primitives/ICenter.cs new file mode 100644 index 0000000..fcaa228 --- /dev/null +++ b/FieldVisualizer/Entities/Values/Primitives/ICenter.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FieldVisualizer.Entities.Values.Primitives +{ + public interface ICenter + { + double CenterX { get;} + double CenterY { get;} + } +} diff --git a/FieldVisualizer/Entities/Values/Primitives/ICirclePrimitive.cs b/FieldVisualizer/Entities/Values/Primitives/ICirclePrimitive.cs new file mode 100644 index 0000000..af9d2d5 --- /dev/null +++ b/FieldVisualizer/Entities/Values/Primitives/ICirclePrimitive.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FieldVisualizer.Entities.Values.Primitives +{ + /// + /// Represent circle primitive + /// + public interface ICirclePrimitive : IValuePrimitive + { + double Diameter { get; set; } + } +} diff --git a/FieldVisualizer/Entities/Values/Primitives/IPrimitiveSet.cs b/FieldVisualizer/Entities/Values/Primitives/IPrimitiveSet.cs new file mode 100644 index 0000000..8cefb5f --- /dev/null +++ b/FieldVisualizer/Entities/Values/Primitives/IPrimitiveSet.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FieldVisualizer.Entities.Values.Primitives +{ + public interface IPrimitiveSet + { + string Name { get; } + IEnumerable ValuePrimitives { get; } + + } +} diff --git a/FieldVisualizer/Entities/Values/Primitives/IRectanglePrimitive.cs b/FieldVisualizer/Entities/Values/Primitives/IRectanglePrimitive.cs new file mode 100644 index 0000000..6d4db3f --- /dev/null +++ b/FieldVisualizer/Entities/Values/Primitives/IRectanglePrimitive.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FieldVisualizer.Entities.Values.Primitives +{ + internal interface IRectanglePrimitive : IValuePrimitive + { + double Height { get; set; } + double Width { get; set; } + } +} diff --git a/FieldVisualizer/Entities/Values/Primitives/IValuePrimitive.cs b/FieldVisualizer/Entities/Values/Primitives/IValuePrimitive.cs new file mode 100644 index 0000000..57cbda4 --- /dev/null +++ b/FieldVisualizer/Entities/Values/Primitives/IValuePrimitive.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FieldVisualizer.Entities.Values.Primitives +{ + public interface IValuePrimitive + { + double Value { get; } + double CenterX { get; } + double CenterY { get; } + double Area { get; } + } +} diff --git a/FieldVisualizer/Entities/Values/Primitives/PrimitiveSet.cs b/FieldVisualizer/Entities/Values/Primitives/PrimitiveSet.cs new file mode 100644 index 0000000..732a515 --- /dev/null +++ b/FieldVisualizer/Entities/Values/Primitives/PrimitiveSet.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FieldVisualizer.Entities.Values.Primitives +{ + public class PrimitiveSet : IPrimitiveSet + { + public string Name { get; set; } + public IEnumerable ValuePrimitives { get; set;} + + public PrimitiveSet() + { + Name = "New set of primitives"; + ValuePrimitives = new List(); + } + } +} diff --git a/FieldVisualizer/Entities/Values/Primitives/RectanglePrimitive.cs b/FieldVisualizer/Entities/Values/Primitives/RectanglePrimitive.cs new file mode 100644 index 0000000..d4d2ae5 --- /dev/null +++ b/FieldVisualizer/Entities/Values/Primitives/RectanglePrimitive.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FieldVisualizer.Entities.Values.Primitives +{ + public class RectanglePrimitive : IRectanglePrimitive + { + public double Height { get; set; } + public double Width { get; set; } + public double Value { get; set; } + public double CenterX { get; set; } + public double CenterY { get; set; } + public double Area => Height * Width; + } +} diff --git a/FieldVisualizer/Entities/Values/ValueRange.cs b/FieldVisualizer/Entities/Values/ValueRange.cs new file mode 100644 index 0000000..bc0c379 --- /dev/null +++ b/FieldVisualizer/Entities/Values/ValueRange.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace FieldVisualizer.Entities.Values +{ + public class ValueRange : IValueRange + { + public double TopValue { get; set; } + public double BottomValue { get; set; } + } +} diff --git a/FieldVisualizer/FieldVisualizer.csproj b/FieldVisualizer/FieldVisualizer.csproj new file mode 100644 index 0000000..0a3b622 --- /dev/null +++ b/FieldVisualizer/FieldVisualizer.csproj @@ -0,0 +1,11 @@ + + + + net6.0-windows7.0 + enable + true + disable + 7.0 + + + diff --git a/FieldVisualizer/FieldVisualizer.csproj.user b/FieldVisualizer/FieldVisualizer.csproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/FieldVisualizer/FieldVisualizer.csproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/FieldVisualizer/InfraStructures/Comands/RelayCommand.cs b/FieldVisualizer/InfraStructures/Comands/RelayCommand.cs new file mode 100644 index 0000000..130de8f --- /dev/null +++ b/FieldVisualizer/InfraStructures/Comands/RelayCommand.cs @@ -0,0 +1,27 @@ +using System; +using System.Windows.Input; + +namespace FieldVisualizer.Infrastructure.Commands +{ + public class RelayCommand : ICommand + { + private Action execute; + private Func canExecute; + + public event EventHandler CanExecuteChanged + { + add => CommandManager.RequerySuggested += value; + remove => CommandManager.RequerySuggested -= value; + } + + public RelayCommand(Action execute, Func canExecute = null) + { + this.execute = execute; + this.canExecute = canExecute; + } + + public bool CanExecute(object parameter) => canExecute == null || canExecute(parameter); + + public void Execute(object parameter) => execute(parameter); + } +} diff --git a/FieldVisualizer/InfraStructures/Enums/ColorMapsTypes.cs b/FieldVisualizer/InfraStructures/Enums/ColorMapsTypes.cs new file mode 100644 index 0000000..5be9d8c --- /dev/null +++ b/FieldVisualizer/InfraStructures/Enums/ColorMapsTypes.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace FieldVisualizer.InfraStructures.Enums +{ + public enum ColorMapsTypes + { + FullSpectrum = 0, + RedToWhite = 1, + RedToBlue = 2, + BlueToWhite = 3 + } +} diff --git a/FieldVisualizer/InfraStructures/Exceptions/FieldVisulizerException.cs b/FieldVisualizer/InfraStructures/Exceptions/FieldVisulizerException.cs new file mode 100644 index 0000000..2cf8f61 --- /dev/null +++ b/FieldVisualizer/InfraStructures/Exceptions/FieldVisulizerException.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace FieldVisualizer.InfraStructures.Exceptions +{ + public class FieldVisulizerException : Exception + { + public FieldVisulizerException(string errorString) : base(errorString) + { + } + } +} diff --git a/FieldVisualizer/InfraStructures/Strings/ErrorStrings.cs b/FieldVisualizer/InfraStructures/Strings/ErrorStrings.cs new file mode 100644 index 0000000..dd2e0da --- /dev/null +++ b/FieldVisualizer/InfraStructures/Strings/ErrorStrings.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace FieldVisualizer.InfraStructures.Strings +{ + public static class ErrorStrings + { + public static string ColorMapTypeIsUnknown => "#0001: ColorMap type is unknown"; + public static string PrimitiveTypeIsUnknown => "#0002: Type of primitive is unknown"; + } +} diff --git a/FieldVisualizer/Services/ColorServices/ColorOperations.cs b/FieldVisualizer/Services/ColorServices/ColorOperations.cs new file mode 100644 index 0000000..dd4c7c7 --- /dev/null +++ b/FieldVisualizer/Services/ColorServices/ColorOperations.cs @@ -0,0 +1,63 @@ +using FieldVisualizer.Entities.ColorMaps; +using FieldVisualizer.Entities.Values; +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows.Media; + +namespace FieldVisualizer.Services.ColorServices +{ + public static class ColorOperations + { + const byte Alpha = 0xff; + public static Color GetColorByValue(IValueRange range, IColorMap map, double val) + { + if (range.TopValue == range.BottomValue || map.Colors.Count == 0) { return map.Colors[0]; } + double minVal = range.BottomValue - 1e-15d*(Math.Abs(range.BottomValue)); + double maxVal = range.TopValue + 1e-15d * (Math.Abs(range.TopValue)); + if (val > maxVal || val < minVal) { return Colors.Gray; } + if (val == minVal) { return map.Colors[0]; } + if (val == maxVal) { return map.Colors[map.Colors.Count - 1]; } + + double valPerc = (val - minVal) / (maxVal - minVal);// value% + if (valPerc >= 1d) + { return map.Colors[map.Colors.Count - 1]; } + double colorPerc = 1d / (map.Colors.Count - 1d); // % of each block of color. the last is the "100% Color" + double blockOfColor = valPerc / colorPerc;// the integer part repersents how many block to skip + int blockIdx = (int)Math.Truncate(blockOfColor);// Idx of + double valPercResidual = valPerc - (blockIdx * colorPerc);//remove the part represented of block + double percOfColor = valPercResidual / colorPerc;// % of color of this block that will be filled + + Color cTarget = map.Colors[blockIdx]; + Color cNext = map.Colors[blockIdx + 1]; + + var deltaR = cNext.R - cTarget.R; + var deltaG = cNext.G - cTarget.G; + var deltaB = cNext.B - cTarget.B; + + var R = cTarget.R + (deltaR * percOfColor); + var G = cTarget.G + (deltaG * percOfColor); + var B = cTarget.B + (deltaB * percOfColor); + + Color c = map.Colors[0]; + c = Color.FromArgb(Alpha, (byte)R, (byte)G, (byte)B); + return c; + } + public static IEnumerable GetValueColorRanges(IValueRange fullRange, IEnumerable valueRanges, IColorMap colorMap) + { + var colorRanges = new List(); + foreach (var valueRange in valueRanges) + { + IValueColorRange valueColorRange = new ValueColorRange(); + valueColorRange.IsActive = true; + valueColorRange.BottomValue = valueRange.BottomValue; + valueColorRange.AverageValue = (valueRange.BottomValue + valueRange.TopValue) / 2; + valueColorRange.TopValue = valueRange.TopValue; + valueColorRange.BottomColor = GetColorByValue(fullRange, colorMap, valueColorRange.BottomValue); + valueColorRange.TopColor = GetColorByValue(fullRange, colorMap, valueColorRange.TopValue); + colorRanges.Add(valueColorRange); + } + return colorRanges; + } + } +} diff --git a/FieldVisualizer/Services/PrimitiveServices/PrimitiveOperations.cs b/FieldVisualizer/Services/PrimitiveServices/PrimitiveOperations.cs new file mode 100644 index 0000000..c043a8c --- /dev/null +++ b/FieldVisualizer/Services/PrimitiveServices/PrimitiveOperations.cs @@ -0,0 +1,95 @@ +using FieldVisualizer.Entities.Values; +using FieldVisualizer.Entities.Values.Primitives; +using FieldVisualizer.InfraStructures.Exceptions; +using FieldVisualizer.InfraStructures.Strings; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + +namespace FieldVisualizer.Services.PrimitiveServices +{ + public static class PrimitiveOperations + { + public static IValueRange GetValueRange(IEnumerable valuePrimitives) + { + double minVal =0d, maxVal = 0d; + foreach (var primitive in valuePrimitives) + { + minVal = Math.Min(minVal, primitive.Value); + maxVal = Math.Max(maxVal, primitive.Value); + } + return new ValueRange() { BottomValue = minVal, TopValue = maxVal }; + } + + public static double[] GetMinMaxX(IEnumerable valuePrimitives) + { + List coords = GetXs(valuePrimitives); + return new double[] { coords.Min(), coords.Max() }; + } + + public static double GetSizeX(IEnumerable valuePrimitives) + { + double[] coords = GetMinMaxX(valuePrimitives); + return coords[1] - coords[0]; + } + + public static double[] GetMinMaxY(IEnumerable valuePrimitives) + { + List coords = GetYs(valuePrimitives); + return new double[] { coords.Min(), coords.Max() }; + } + + public static double GetSizeY(IEnumerable valuePrimitives) + { + double[] coords = GetMinMaxY(valuePrimitives); + return coords[1] - coords[0]; + } + + public static List GetXs(IEnumerable valuePrimitives) + { + List coords = new List(); + foreach (var primitive in valuePrimitives) + { + if (primitive is IRectanglePrimitive) + { + IRectanglePrimitive rectanglePrimitive = primitive as IRectanglePrimitive; + coords.Add(rectanglePrimitive.CenterX + rectanglePrimitive.Width / 2); + coords.Add(rectanglePrimitive.CenterX - rectanglePrimitive.Width / 2); + } + else if (primitive is ICirclePrimitive) + { + ICirclePrimitive circlePrimitive = primitive as ICirclePrimitive; + coords.Add(circlePrimitive.CenterX + circlePrimitive.Diameter / 2); + coords.Add(circlePrimitive.CenterX - circlePrimitive.Diameter / 2); + } + else { throw new FieldVisulizerException(ErrorStrings.PrimitiveTypeIsUnknown);} + } + return coords; + } + + public static List GetYs(IEnumerable valuePrimitives) + { + List coords = new List(); + foreach (var primitive in valuePrimitives) + { + if (primitive is IRectanglePrimitive) + { + IRectanglePrimitive rectanglePrimitive = primitive as IRectanglePrimitive; + coords.Add(rectanglePrimitive.CenterY + rectanglePrimitive.Height / 2); + coords.Add(rectanglePrimitive.CenterY - rectanglePrimitive.Height / 2); + } + else if (primitive is ICirclePrimitive) + { + ICirclePrimitive circlePrimitive = primitive as ICirclePrimitive; + coords.Add(circlePrimitive.CenterY + circlePrimitive.Diameter / 2); + coords.Add(circlePrimitive.CenterY - circlePrimitive.Diameter / 2); + } + else { throw new FieldVisulizerException(ErrorStrings.PrimitiveTypeIsUnknown); } + } + return coords; + } + } +} diff --git a/FieldVisualizer/Services/PrimitiveServices/TestPrimitivesOperation.cs b/FieldVisualizer/Services/PrimitiveServices/TestPrimitivesOperation.cs new file mode 100644 index 0000000..2ebd1a1 --- /dev/null +++ b/FieldVisualizer/Services/PrimitiveServices/TestPrimitivesOperation.cs @@ -0,0 +1,49 @@ +using FieldVisualizer.Entities.Values.Primitives; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FieldVisualizer.Services.PrimitiveServices +{ + public static class TestPrimitivesOperation + { + public static List AddTestPrimitives() + { + List primitiveSets = new List(); + int imax = 100; + int jmax = 100; + PrimitiveSet primitiveSet = new PrimitiveSet(); + primitiveSets.Add(primitiveSet); + List primitives = new List(); + primitiveSet.ValuePrimitives = primitives; + IValuePrimitive primitive; + for (int i = 0; i < imax; i++) + { + for (int j = 0; j < jmax; j++) + { + primitive = new RectanglePrimitive() { Height = 10, Width = 20, CenterX = 20 * i, CenterY = 10 * j, Value = -(i + j) }; + primitives.Add(primitive); + } + } + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 2; j++) + { + primitive = new CirclePrimitive() { Diameter = 150, CenterX = -200 * i, CenterY = -100 * j, Value = i * 100 + j * 200 }; + primitives.Add(primitive); + } + } + primitiveSet = new PrimitiveSet(); + primitives = new List(); + primitive = new RectanglePrimitive() { Height = 100, Width = 200, CenterX = 0, CenterY = 0, Value = 100 }; + primitives.Add(primitive); + primitive = new CirclePrimitive() { Diameter = 50, CenterX = 0, CenterY = 0, Value = -100 }; + primitives.Add(primitive); + primitiveSet.ValuePrimitives = primitives; + primitiveSets.Add(primitiveSet); + return primitiveSets; + } + } +} diff --git a/FieldVisualizer/Services/ValueRanges/ValueRangeOperations.cs b/FieldVisualizer/Services/ValueRanges/ValueRangeOperations.cs new file mode 100644 index 0000000..bdd6adb --- /dev/null +++ b/FieldVisualizer/Services/ValueRanges/ValueRangeOperations.cs @@ -0,0 +1,35 @@ +using FieldVisualizer.Entities.Values; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FieldVisualizer.Services.ValueRanges +{ + public static class ValueRangeOperations + { + public static IEnumerable DivideValueRange (IValueRange valueRange, int divisionNumber) + { + List valueRanges = new List(); + if (valueRange.BottomValue == valueRange.TopValue) + { + var newRange = new ValueRange() { BottomValue = valueRange.BottomValue, TopValue = valueRange.TopValue }; + valueRanges.Add(newRange); + } + else + { + double dVal = (valueRange.TopValue - valueRange.BottomValue) / divisionNumber; + double startBottom = valueRange.BottomValue; + for (int i = 0; i < divisionNumber; i++ ) + { + + double currentBottom = startBottom + i * dVal; + var newRange = new ValueRange() { BottomValue = currentBottom, TopValue = currentBottom + dVal }; + valueRanges.Add(newRange); + } + } + return valueRanges; + } + } +} diff --git a/FieldVisualizer/ViewModels/FieldViewerViewModels/FieldViewerViewModel.cs b/FieldVisualizer/ViewModels/FieldViewerViewModels/FieldViewerViewModel.cs new file mode 100644 index 0000000..4f16ce6 --- /dev/null +++ b/FieldVisualizer/ViewModels/FieldViewerViewModels/FieldViewerViewModel.cs @@ -0,0 +1,377 @@ +using FieldVisualizer.Entities.ColorMaps.Factories; +using FieldVisualizer.Entities.ColorMaps; +using FieldVisualizer.Entities.Values.Primitives; +using FieldVisualizer.Entities.Values; +using FieldVisualizer.Infrastructure.Commands; +using FieldVisualizer.InfraStructures.Enums; +using FieldVisualizer.InfraStructures.Exceptions; +using FieldVisualizer.InfraStructures.Strings; +using FieldVisualizer.Services.ColorServices; +using FieldVisualizer.Services.PrimitiveServices; +using FieldVisualizer.Services.ValueRanges; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Shapes; +using FieldVisualizer.Windows.UserControls; +using System.ComponentModel; +using System.Xml.Serialization; + +namespace FieldVisualizer.ViewModels.FieldViewerViewModels +{ + public class FieldViewerViewModel : ViewModelBase, IDataErrorInfo + { + public ICommand RebuildCommand { get; } + public ICommand ZoomInCommand { get; } + public ICommand ZoomOutCommand { get; } + public ICommand ChangeColorMapCommand { get; } + public ICommand SetUserColorsCommand { get; } + public ICommand SetCrossLineCommand + { + get + { + if (setCrossLineCommand == null) + { + setCrossLineCommand = new RelayCommand(SetCrossLine); + } + + return setCrossLineCommand; + } + } + + public IPrimitiveSet PrimitiveSet + { get + { + return primitiveSet; + } + set + { + primitiveSet = value; + OnPropertyChanged(nameof(PrimitiveSet)); + AreaTotal = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Sum(x => x.Area); + OnPropertyChanged(nameof(AreaTotal)); + AreaNeg = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Where(x => x.Value < 0d).Sum(x => x.Area); + OnPropertyChanged(nameof(AreaNeg)); + AreaZero = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Where(x => x.Value == 0d).Sum(x => x.Area); + OnPropertyChanged(nameof(AreaZero)); + AreaPos = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Where(x => x.Value > 0d).Sum(x => x.Area); + OnPropertyChanged(nameof(AreaPos)); + SumTotal = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Sum(x => x.Value); + OnPropertyChanged(nameof(SumTotal)); + SumNeg = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Where(x => x.Value < 0d).Sum(x => x.Value); + OnPropertyChanged(nameof(SumNeg)); + SumPos = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Where(x => x.Value > 0d).Sum(x => x.Value); + OnPropertyChanged(nameof(SumPos)); + + } + } + public IValueRange UserValueRange { get; set; } + public bool SetMinValue + { + get + { + return setMinValue; + } + set + { + setMinValue = value; + OnPropertyChanged(nameof(SetMinValue)); + //OnPropertyChanged(nameof(UserMinValue)); + } + } + public bool SetMaxValue + { + get + { + return setMaxValue; + } + set + { + setMaxValue = value; + OnPropertyChanged(nameof(SetMaxValue)); + //OnPropertyChanged(nameof(UserMaxValue)); + } + } + public double UserMinValue + { + get + { + return UserValueRange.BottomValue; + } + set + { + UserValueRange.BottomValue = value; + OnPropertyChanged(nameof(UserMinValue)); + } + } + public double UserMaxValue + { + get + { + return UserValueRange.TopValue; + } + set + { + double tmpVal = UserValueRange.TopValue; + try + { + UserValueRange.TopValue = value; + OnPropertyChanged(nameof(UserMaxValue)); + } + catch (Exception ex) + { + UserValueRange.TopValue = tmpVal; + } + } + } + public double AreaTotal { get; private set; } + public double AreaNeg { get; private set; } + public double AreaZero { get; private set; } + public double AreaPos { get; private set; } + public double SumTotal { get; private set;} + public double SumNeg { get; private set; } + public double SumPos { get; private set; } + public Viewbox WorkPlaneBox { get; set; } + public VerticalLegend Legend { get; set; } + public Canvas WorkPlaneCanvas { get; set; } + public double ScrolWidth { get; set; } + public double ScrolHeight { get; set; } + public double CrossLineX { get => crossLineX; set => SetProperty(ref crossLineX, value); } + public double CrossLineY { get => crossLineY; set => SetProperty(ref crossLineY, value); } + + public double SumAboveLine { get => sumAboveLine; set => SetProperty(ref sumAboveLine, value); } + public double SumUnderLine { get => sumUnderLine; set => SetProperty(ref sumUnderLine, value); } + + public string Error { get; } + public string this[string columnName] + { + get + { + string error = String.Empty; + return error; + } + } + + private ICommand setCrossLineCommand; + private IPrimitiveSet primitiveSet; + private double dX, dY; + private ColorMapsTypes _ColorMapType; + private IColorMap _ColorMap; + private IValueRange valueRange; + private IEnumerable _ValueRanges; + private IEnumerable _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.FullSpectrum; + 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()); + SetUserColorsCommand = new RelayCommand(o => ColorRefresh(), o => (SetMinValue || SetMaxValue)); + UserValueRange = new ValueRange() { BottomValue = 0, TopValue = 0 }; + SetMinValue = false; + SetMaxValue = false; + } + public void ColorRefresh() + { + if (PrimitiveValidation() == false) { return; } + _ColorMap = ColorMapFactory.GetColorMap(_ColorMapType); + SetColor(); + if ((PrimitiveSet is null) == false) + { + ProcessPrimitives(); + Legend.ValueColorRanges = _ValueColorRanges; + Legend.Refresh(); + } + } + public void Refresh() + { + SetMinValue = false; + SetMaxValue = false; + ColorRefresh(); + } + private void ProcessPrimitives() + { + WorkPlaneCanvas.Children.Clear(); + double sizeX = PrimitiveOperations.GetSizeX(PrimitiveSet.ValuePrimitives); + double sizeY = PrimitiveOperations.GetSizeY(PrimitiveSet.ValuePrimitives); + 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) + { + if (primitive is IRectanglePrimitive) + { + IRectanglePrimitive rectanglePrimitive = primitive as IRectanglePrimitive; + Rectangle rectangle = ProcessRectanglePrimitive(rectanglePrimitive); + WorkPlaneCanvas.Children.Add(rectangle); + } + else if (primitive is ICirclePrimitive) + { + ICirclePrimitive circlePrimitive = primitive as ICirclePrimitive; + Ellipse ellipse = ProcessCirclePrimitive(circlePrimitive); + WorkPlaneCanvas.Children.Add(ellipse); + } + else { throw new FieldVisulizerException(ErrorStrings.PrimitiveTypeIsUnknown); } + } + } + 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); + 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); + return ellipse; + } + private void ProcessShape(Shape shape, IValuePrimitive valuePrimitive, double addX, double addY) + { + SolidColorBrush brush = new SolidColorBrush(); + brush.Color = ColorOperations.GetColorByValue(valueRange, _ColorMap, valuePrimitive.Value); + foreach (var valueRange in _ValueColorRanges) + { + if (valuePrimitive.Value >= valueRange.BottomValue & valuePrimitive.Value <= valueRange.TopValue & (!valueRange.IsActive)) + { + brush.Color = Colors.Gray; + } + } + shape.ToolTip = valuePrimitive.Value; + shape.Tag = valuePrimitive; + shape.Fill = brush; + Canvas.SetLeft(shape, valuePrimitive.CenterX - addX - dX); + Canvas.SetTop(shape, -valuePrimitive.CenterY - addY + dY); + } + private void Zoom(double coefficient) + { + WorkPlaneBox.Width *= coefficient; + WorkPlaneBox.Height *= coefficient; + } + private void ChangeColorMap() + { + //Iterate all available color maps one by one + try + { + _ColorMapType++; + IColorMap colorMap = ColorMapFactory.GetColorMap(_ColorMapType); + } + catch (Exception ex) { _ColorMapType = 0; } + ColorRefresh(); + } + private bool PrimitiveValidation() + { + if (PrimitiveSet == null || PrimitiveSet.ValuePrimitives.Count() == 0) { return false; } + else return true; + } + private void SetColor() + { + valueRange = PrimitiveOperations.GetValueRange(PrimitiveSet.ValuePrimitives); + //if bottom value is greater than top value + if (SetMinValue + & SetMaxValue + & (UserValueRange.BottomValue > UserValueRange.TopValue)) + { + UserValueRange.TopValue = UserValueRange.BottomValue; + } + if (SetMinValue) { valueRange.BottomValue = UserValueRange.BottomValue; } else { UserValueRange.BottomValue = valueRange.BottomValue; } + if (SetMaxValue) { valueRange.TopValue = UserValueRange.TopValue; } else { UserValueRange.TopValue = valueRange.TopValue; } + _ValueRanges = ValueRangeOperations.DivideValueRange(valueRange, RangeNumber); + _ValueColorRanges = ColorOperations.GetValueColorRanges(valueRange, _ValueRanges, _ColorMap); + } + private void SetCrossLine(object commandParameter) + { + AddCrossLine(); + AddSummaryInfoCrossLine(); + } + private void AddCrossLine() + { + double width = WorkPlaneCanvas.ActualWidth; + double heigth = WorkPlaneCanvas.ActualHeight; + Line line = new Line(); + if (crossLineX == 0d) + { + line.X1 = - width / 2d - dX; + line.Y1 = - crossLineY + dY; + line.X2 = width / 2d - dX; + line.Y2 = - crossLineY + dY; + } + else if (crossLineY == 0d) + { + line.X1 = crossLineX - dX; + line.Y1 = heigth / 2 + dY; + line.X2 = crossLineX - dX; + line.Y2 = -heigth / 2 + dY; + } + else + { + line.X1 = - width / 2d - dX; + line.Y1 = -(crossLineY / crossLineX * width / 2 + crossLineY) - dY; + line.X2 = width / 2d - dX; + line.Y2 = -(-crossLineY / crossLineX * width / 2 + crossLineY) - dY ; + } + SolidColorBrush brush = new SolidColorBrush(); + brush.Color = Colors.Red; + line.Fill = brush; + line.Stroke = brush; + line.StrokeThickness = (width + heigth) / 100; + if (previosLine != null) + { + try { WorkPlaneCanvas.Children.Remove(previosLine);} + catch (Exception) {} + } + previosLine = line; + WorkPlaneCanvas.Children.Add(line); + } + private double GetPointOfCrossLine(double x) + { + double y; + if (crossLineX == 0d) + { + y = crossLineY; + } + else + { + y = -crossLineY / crossLineX - crossLineY; + } + return y; + } + private void AddSummaryInfoCrossLine() + { + SumAboveLine = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Where(x=>x.CenterY >= GetPointOfCrossLine(x.CenterX)).Sum(x => x.Value); + SumUnderLine = primitiveSet is null ? 0 : primitiveSet.ValuePrimitives.Where(x => x.CenterY <= GetPointOfCrossLine(x.CenterX)).Sum(x => x.Value); + } + } +} diff --git a/FieldVisualizer/ViewModels/ViewModelBase.cs b/FieldVisualizer/ViewModels/ViewModelBase.cs new file mode 100644 index 0000000..d15e32d --- /dev/null +++ b/FieldVisualizer/ViewModels/ViewModelBase.cs @@ -0,0 +1,36 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace FieldVisualizer.ViewModels +{ + public class ViewModelBase : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void OnPropertyChanged(T value, T prop, [CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + protected virtual void OnPropertyChanged(T value, ref T prop, [CallerMemberName] string propertyName = null) + { + prop = value; + OnPropertyChanged(propertyName); + } + + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + + protected bool SetProperty(ref T field, T newValue, [CallerMemberName] string propertyName = null) + { + if (!Equals(field, newValue)) + { + field = newValue; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + return true; + } + + return false; + } + } +} diff --git a/FieldVisualizer/Windows/UserControls/FieldViewer.xaml b/FieldVisualizer/Windows/UserControls/FieldViewer.xaml new file mode 100644 index 0000000..0fbb914 --- /dev/null +++ b/FieldVisualizer/Windows/UserControls/FieldViewer.xaml @@ -0,0 +1,100 @@ + + + + + + + + + + + + +