更新2026
This commit is contained in:
@@ -7,8 +7,27 @@ using LiveChartsCore.Defaults;
|
|||||||
|
|
||||||
namespace COFTester.Controls;
|
namespace COFTester.Controls;
|
||||||
|
|
||||||
|
public enum RealtimeForceChartPart
|
||||||
|
{
|
||||||
|
Full,
|
||||||
|
LeftAxis,
|
||||||
|
Plot,
|
||||||
|
RightAxis
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class RealtimeForceChart : FrameworkElement
|
public sealed class RealtimeForceChart : FrameworkElement
|
||||||
{
|
{
|
||||||
|
private const double PlotTop = 36;
|
||||||
|
private const double PlotBottomMargin = 68;
|
||||||
|
private const double FullChartLeftMargin = 78;
|
||||||
|
private const double FullChartRightMargin = 142;
|
||||||
|
|
||||||
|
public static readonly DependencyProperty ChartPartProperty = DependencyProperty.Register(
|
||||||
|
nameof(ChartPart),
|
||||||
|
typeof(RealtimeForceChartPart),
|
||||||
|
typeof(RealtimeForceChart),
|
||||||
|
new FrameworkPropertyMetadata(RealtimeForceChartPart.Full, FrameworkPropertyMetadataOptions.AffectsRender));
|
||||||
|
|
||||||
public static readonly DependencyProperty SamplesProperty = DependencyProperty.Register(
|
public static readonly DependencyProperty SamplesProperty = DependencyProperty.Register(
|
||||||
nameof(Samples),
|
nameof(Samples),
|
||||||
typeof(IEnumerable),
|
typeof(IEnumerable),
|
||||||
@@ -72,6 +91,12 @@ public sealed class RealtimeForceChart : FrameworkElement
|
|||||||
private readonly Brush _pointBrush = new SolidColorBrush(Color.FromRgb(18, 22, 25));
|
private readonly Brush _pointBrush = new SolidColorBrush(Color.FromRgb(18, 22, 25));
|
||||||
private readonly Pen _pointPen = new(new SolidColorBrush(Color.FromRgb(247, 249, 251)), 1.8);
|
private readonly Pen _pointPen = new(new SolidColorBrush(Color.FromRgb(247, 249, 251)), 1.8);
|
||||||
|
|
||||||
|
public RealtimeForceChartPart ChartPart
|
||||||
|
{
|
||||||
|
get => (RealtimeForceChartPart)GetValue(ChartPartProperty);
|
||||||
|
set => SetValue(ChartPartProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable? Samples
|
public IEnumerable? Samples
|
||||||
{
|
{
|
||||||
get => (IEnumerable?)GetValue(SamplesProperty);
|
get => (IEnumerable?)GetValue(SamplesProperty);
|
||||||
@@ -126,28 +151,80 @@ public sealed class RealtimeForceChart : FrameworkElement
|
|||||||
|
|
||||||
var width = ActualWidth;
|
var width = ActualWidth;
|
||||||
var height = ActualHeight;
|
var height = ActualHeight;
|
||||||
if (width < 120 || height < 90)
|
var minimumWidth = ChartPart == RealtimeForceChartPart.Plot ? 1 : 120;
|
||||||
|
if (width < minimumWidth || height < 90)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var plot = new Rect(78, 36, Math.Max(1, width - 220), Math.Max(1, height - 104));
|
|
||||||
var samples = ReadPoints(Samples);
|
var samples = ReadPoints(Samples);
|
||||||
var currentPoint = ReadPoints(CurrentPointSamples).LastOrDefault();
|
|
||||||
var xMax = ResolveAxisMax(XMax, TravelMm, samples.Select(point => point.X));
|
var xMax = ResolveAxisMax(XMax, TravelMm, samples.Select(point => point.X));
|
||||||
var yMax = ResolveAxisMax(YMax, 0.5, samples.Select(point => point.Y));
|
var yMax = ResolveAxisMax(YMax, 0.5, samples.Select(point => point.Y));
|
||||||
|
|
||||||
drawingContext.DrawRectangle(_chartBackgroundBrush, _chartBorderPen, new Rect(0.5, 0.5, Math.Max(0, width - 1), Math.Max(0, height - 1)));
|
drawingContext.DrawRectangle(_chartBackgroundBrush, _chartBorderPen, new Rect(0.5, 0.5, Math.Max(0, width - 1), Math.Max(0, height - 1)));
|
||||||
DrawGridAndAxes(drawingContext, plot, xMax, yMax);
|
|
||||||
|
switch (ChartPart)
|
||||||
|
{
|
||||||
|
case RealtimeForceChartPart.LeftAxis:
|
||||||
|
DrawLeftAxis(drawingContext, width, height, yMax);
|
||||||
|
break;
|
||||||
|
case RealtimeForceChartPart.Plot:
|
||||||
|
DrawPlot(drawingContext, width, height, xMax, yMax, samples);
|
||||||
|
break;
|
||||||
|
case RealtimeForceChartPart.RightAxis:
|
||||||
|
DrawRightAxis(drawingContext, width, height, yMax);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DrawFullChart(drawingContext, width, height, xMax, yMax, samples);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawFullChart(DrawingContext drawingContext, double width, double height, double xMax, double yMax, IReadOnlyList<ChartPoint> samples)
|
||||||
|
{
|
||||||
|
var plot = new Rect(
|
||||||
|
FullChartLeftMargin,
|
||||||
|
PlotTop,
|
||||||
|
Math.Max(1, width - FullChartLeftMargin - FullChartRightMargin),
|
||||||
|
Math.Max(1, height - PlotTop - PlotBottomMargin));
|
||||||
|
var currentPoint = ReadPoints(CurrentPointSamples).LastOrDefault();
|
||||||
|
|
||||||
|
DrawPlotGridAndXAxis(drawingContext, plot, xMax, yMax);
|
||||||
|
DrawYAxisLabels(drawingContext, plot, yMax, leftAxis: true, labelX: plot.Left - 10);
|
||||||
|
DrawYAxisLabels(drawingContext, plot, yMax, leftAxis: false, labelX: plot.Right + 8);
|
||||||
DrawReferenceLine(drawingContext, plot, xMax, yMax, ReadLineValue(PeakLineSamples), _peakPen);
|
DrawReferenceLine(drawingContext, plot, xMax, yMax, ReadLineValue(PeakLineSamples), _peakPen);
|
||||||
DrawReferenceLine(drawingContext, plot, xMax, yMax, ReadLineValue(AverageLineSamples), _averagePen);
|
DrawReferenceLine(drawingContext, plot, xMax, yMax, ReadLineValue(AverageLineSamples), _averagePen);
|
||||||
DrawCurve(drawingContext, plot, xMax, yMax, samples);
|
DrawCurve(drawingContext, plot, xMax, yMax, samples);
|
||||||
|
DrawCurrentPoint(drawingContext, plot, xMax, yMax, currentPoint);
|
||||||
if (currentPoint is not null)
|
DrawChartTitles(drawingContext, plot);
|
||||||
{
|
|
||||||
var point = ToScreen(currentPoint, plot, xMax, yMax);
|
|
||||||
drawingContext.DrawEllipse(_pointBrush, _pointPen, point, 5.5, 5.5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawPlot(DrawingContext drawingContext, double width, double height, double xMax, double yMax, IReadOnlyList<ChartPoint> samples)
|
||||||
|
{
|
||||||
|
var plot = CreateSplitPlotRect(width, height);
|
||||||
|
var currentPoint = ReadPoints(CurrentPointSamples).LastOrDefault();
|
||||||
|
|
||||||
|
DrawPlotGridAndXAxis(drawingContext, plot, xMax, yMax);
|
||||||
|
DrawReferenceLine(drawingContext, plot, xMax, yMax, ReadLineValue(PeakLineSamples), _peakPen);
|
||||||
|
DrawReferenceLine(drawingContext, plot, xMax, yMax, ReadLineValue(AverageLineSamples), _averagePen);
|
||||||
|
DrawCurve(drawingContext, plot, xMax, yMax, samples);
|
||||||
|
DrawCurrentPoint(drawingContext, plot, xMax, yMax, currentPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawLeftAxis(DrawingContext drawingContext, double width, double height, double yMax)
|
||||||
|
{
|
||||||
|
var plot = CreateAxisPlotRect(width, height);
|
||||||
|
DrawYAxisLabels(drawingContext, plot, yMax, leftAxis: true, labelX: width - 10);
|
||||||
|
DrawText(drawingContext, "Friction force [N]", Math.Max(8, width - 116), plot.Top - 25, 12, _titleBrush);
|
||||||
|
drawingContext.DrawLine(_axisPen, new Point(width - 0.5, plot.Top), new Point(width - 0.5, plot.Bottom));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawRightAxis(DrawingContext drawingContext, double width, double height, double yMax)
|
||||||
|
{
|
||||||
|
var plot = CreateAxisPlotRect(width, height);
|
||||||
|
DrawYAxisLabels(drawingContext, plot, yMax, leftAxis: false, labelX: 10);
|
||||||
|
DrawText(drawingContext, "Coefficient of friction", 10, plot.Top - 25, 11, _titleBrush);
|
||||||
|
drawingContext.DrawLine(_axisPen, new Point(0.5, plot.Top), new Point(0.5, plot.Bottom));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnCollectionSourceChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
|
private static void OnCollectionSourceChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
|
||||||
@@ -171,7 +248,7 @@ public sealed class RealtimeForceChart : FrameworkElement
|
|||||||
InvalidateVisual();
|
InvalidateVisual();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawGridAndAxes(DrawingContext drawingContext, Rect plot, double xMax, double yMax)
|
private void DrawPlotGridAndXAxis(DrawingContext drawingContext, Rect plot, double xMax, double yMax)
|
||||||
{
|
{
|
||||||
drawingContext.DrawRectangle(null, _axisPen, plot);
|
drawingContext.DrawRectangle(null, _axisPen, plot);
|
||||||
|
|
||||||
@@ -203,7 +280,6 @@ public sealed class RealtimeForceChart : FrameworkElement
|
|||||||
|
|
||||||
const int yDivisions = 10;
|
const int yDivisions = 10;
|
||||||
const int yMinorDivisions = 3;
|
const int yMinorDivisions = 3;
|
||||||
var normalForceN = ResolveNormalForceN();
|
|
||||||
for (var index = 0; index <= yDivisions; index++)
|
for (var index = 0; index <= yDivisions; index++)
|
||||||
{
|
{
|
||||||
var y = plot.Bottom - plot.Height * index / yDivisions;
|
var y = plot.Bottom - plot.Height * index / yDivisions;
|
||||||
@@ -216,30 +292,78 @@ public sealed class RealtimeForceChart : FrameworkElement
|
|||||||
drawingContext.DrawLine(_minorGridPen, new Point(plot.Left, minorY), new Point(plot.Right, minorY));
|
drawingContext.DrawLine(_minorGridPen, new Point(plot.Left, minorY), new Point(plot.Right, minorY));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawRightAlignedText(
|
|
||||||
drawingContext,
|
|
||||||
FormatForceNewtonLabel(yMax * index / yDivisions),
|
|
||||||
plot.Left - 10,
|
|
||||||
y - 8,
|
|
||||||
11,
|
|
||||||
_labelBrush,
|
|
||||||
4);
|
|
||||||
DrawText(
|
|
||||||
drawingContext,
|
|
||||||
FormatCoefficientLabel(yMax * index / yDivisions, normalForceN),
|
|
||||||
plot.Right + 8,
|
|
||||||
y - 8,
|
|
||||||
11,
|
|
||||||
_labelBrush);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var xAxisTitleY = Math.Min(plot.Bottom + 30, ActualHeight - 18);
|
var xAxisTitleY = Math.Min(plot.Bottom + 30, ActualHeight - 18);
|
||||||
DrawText(drawingContext, "Time [sec]", plot.Right - 58, xAxisTitleY, 12, _titleBrush);
|
DrawText(drawingContext, "Time [sec]", plot.Right - 58, xAxisTitleY, 12, _titleBrush);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawYAxisLabels(DrawingContext drawingContext, Rect plot, double yMax, bool leftAxis, double labelX)
|
||||||
|
{
|
||||||
|
const int yDivisions = 10;
|
||||||
|
var normalForceN = ResolveNormalForceN();
|
||||||
|
for (var index = 0; index <= yDivisions; index++)
|
||||||
|
{
|
||||||
|
var y = plot.Bottom - plot.Height * index / yDivisions;
|
||||||
|
var value = yMax * index / yDivisions;
|
||||||
|
if (leftAxis)
|
||||||
|
{
|
||||||
|
DrawRightAlignedText(
|
||||||
|
drawingContext,
|
||||||
|
FormatForceNewtonLabel(value),
|
||||||
|
labelX,
|
||||||
|
y - 8,
|
||||||
|
11,
|
||||||
|
_labelBrush,
|
||||||
|
4);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawText(
|
||||||
|
drawingContext,
|
||||||
|
FormatCoefficientLabel(value, normalForceN),
|
||||||
|
labelX,
|
||||||
|
y - 8,
|
||||||
|
11,
|
||||||
|
_labelBrush);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawChartTitles(DrawingContext drawingContext, Rect plot)
|
||||||
|
{
|
||||||
DrawText(drawingContext, "Friction force [N]", plot.Left, plot.Top - 25, 12, _titleBrush);
|
DrawText(drawingContext, "Friction force [N]", plot.Left, plot.Top - 25, 12, _titleBrush);
|
||||||
DrawText(drawingContext, "Coefficient of friction", plot.Right - 8, plot.Top - 25, 11, _titleBrush);
|
DrawText(drawingContext, "Coefficient of friction", plot.Right - 8, plot.Top - 25, 11, _titleBrush);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawCurrentPoint(DrawingContext drawingContext, Rect plot, double xMax, double yMax, ChartPoint? currentPoint)
|
||||||
|
{
|
||||||
|
if (currentPoint is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var point = ToScreen(currentPoint, plot, xMax, yMax);
|
||||||
|
drawingContext.DrawEllipse(_pointBrush, _pointPen, point, 5.5, 5.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Rect CreateSplitPlotRect(double width, double height)
|
||||||
|
{
|
||||||
|
return new Rect(
|
||||||
|
0,
|
||||||
|
PlotTop,
|
||||||
|
Math.Max(1, width),
|
||||||
|
Math.Max(1, height - PlotTop - PlotBottomMargin));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Rect CreateAxisPlotRect(double width, double height)
|
||||||
|
{
|
||||||
|
return new Rect(
|
||||||
|
0,
|
||||||
|
PlotTop,
|
||||||
|
Math.Max(1, width),
|
||||||
|
Math.Max(1, height - PlotTop - PlotBottomMargin));
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawReferenceLine(DrawingContext drawingContext, Rect plot, double xMax, double yMax, double? value, Pen pen)
|
private void DrawReferenceLine(DrawingContext drawingContext, Rect plot, double xMax, double yMax, double? value, Pen pen)
|
||||||
{
|
{
|
||||||
if (value is null || value <= 0 || !IsFinite(value.Value))
|
if (value is null || value <= 0 || !IsFinite(value.Value))
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
@@ -58,8 +59,11 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
private static readonly TimeSpan RealtimeChartRefreshInterval = TimeSpan.FromMilliseconds(250);
|
private static readonly TimeSpan RealtimeChartRefreshInterval = TimeSpan.FromMilliseconds(250);
|
||||||
private const double RealtimeChartTimeStepSeconds = 0.05;
|
private const double RealtimeChartTimeStepSeconds = 0.05;
|
||||||
private const double RealtimeChartXAxisPaddingSeconds = 1;
|
private const double RealtimeChartXAxisPaddingSeconds = 1;
|
||||||
private const double RealtimeChartMinimumCanvasWidth = 900;
|
private const double RealtimeChartMinimumCanvasWidth = 1;
|
||||||
private const double RealtimeChartPixelsPerSecond = 18;
|
private const double RealtimeChartPixelsPerSecond = 18;
|
||||||
|
private const double RealtimeChartMinimumZoomScale = 0.000001;
|
||||||
|
private const double RealtimeChartMaximumZoomScale = 4;
|
||||||
|
private const double RealtimeChartZoomStep = 1.25;
|
||||||
private const double MinimumEmptyChartXAxisMaxLimit = 1;
|
private const double MinimumEmptyChartXAxisMaxLimit = 1;
|
||||||
private const double EmptyChartYAxisMaxLimit = 0.5;
|
private const double EmptyChartYAxisMaxLimit = 0.5;
|
||||||
private const double EmptyChartPointForceN = 0.001;
|
private const double EmptyChartPointForceN = 0.001;
|
||||||
@@ -83,6 +87,9 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
private readonly RelayCommand _completeCalibrationCommand;
|
private readonly RelayCommand _completeCalibrationCommand;
|
||||||
private readonly RelayCommand _exportReportCommand;
|
private readonly RelayCommand _exportReportCommand;
|
||||||
private readonly RelayCommand _clearHistoryCommand;
|
private readonly RelayCommand _clearHistoryCommand;
|
||||||
|
private readonly RelayCommand _realtimeChartZoomInCommand;
|
||||||
|
private readonly RelayCommand _realtimeChartZoomOutCommand;
|
||||||
|
private readonly RelayCommand _realtimeChartResetZoomCommand;
|
||||||
private readonly TestDataRepository _dataRepository;
|
private readonly TestDataRepository _dataRepository;
|
||||||
private readonly RunExportService _runExportService;
|
private readonly RunExportService _runExportService;
|
||||||
private readonly ObservableCollection<ObservablePoint> _forceSamples = [];
|
private readonly ObservableCollection<ObservablePoint> _forceSamples = [];
|
||||||
@@ -155,6 +162,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
private int _nextReciprocatingRecordIndex = 1;
|
private int _nextReciprocatingRecordIndex = 1;
|
||||||
private double _lastForceXAxisMaxLimit;
|
private double _lastForceXAxisMaxLimit;
|
||||||
private double _lastForceYAxisMaxLimit;
|
private double _lastForceYAxisMaxLimit;
|
||||||
|
private double _realtimeChartZoomScale = 1;
|
||||||
|
|
||||||
public MainViewModel()
|
public MainViewModel()
|
||||||
{
|
{
|
||||||
@@ -189,6 +197,9 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
_completeCalibrationCommand = new RelayCommand(CompleteCalibration, () => SelectedCalibrationItem is not null);
|
_completeCalibrationCommand = new RelayCommand(CompleteCalibration, () => SelectedCalibrationItem is not null);
|
||||||
_exportReportCommand = new RelayCommand(ExportHistoricalComparisonReport, CanExportHistoricalReport);
|
_exportReportCommand = new RelayCommand(ExportHistoricalComparisonReport, CanExportHistoricalReport);
|
||||||
_clearHistoryCommand = new RelayCommand(ClearHistoryData, CanClearHistoryStable);
|
_clearHistoryCommand = new RelayCommand(ClearHistoryData, CanClearHistoryStable);
|
||||||
|
_realtimeChartZoomInCommand = new RelayCommand(ZoomInRealtimeChart, () => _realtimeChartZoomScale < RealtimeChartMaximumZoomScale);
|
||||||
|
_realtimeChartZoomOutCommand = new RelayCommand(ZoomOutRealtimeChart);
|
||||||
|
_realtimeChartResetZoomCommand = new RelayCommand(ResetRealtimeChartZoom, () => Math.Abs(_realtimeChartZoomScale - 1) > 0.001);
|
||||||
|
|
||||||
_timer = new DispatcherTimer
|
_timer = new DispatcherTimer
|
||||||
{
|
{
|
||||||
@@ -312,10 +323,14 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
|
|
||||||
public double RealtimeChartXMax => _lastForceXAxisMaxLimit;
|
public double RealtimeChartXMax => _lastForceXAxisMaxLimit;
|
||||||
|
|
||||||
public double RealtimeChartCanvasWidth => Math.Max(RealtimeChartMinimumCanvasWidth, _lastForceXAxisMaxLimit * RealtimeChartPixelsPerSecond);
|
public double RealtimeChartCanvasWidth => Math.Max(
|
||||||
|
RealtimeChartMinimumCanvasWidth,
|
||||||
|
_lastForceXAxisMaxLimit * RealtimeChartPixelsPerSecond * _realtimeChartZoomScale);
|
||||||
|
|
||||||
public double RealtimeChartYMax => _lastForceYAxisMaxLimit;
|
public double RealtimeChartYMax => _lastForceYAxisMaxLimit;
|
||||||
|
|
||||||
|
public string RealtimeChartZoomText => FormatRealtimeChartZoomText(_realtimeChartZoomScale);
|
||||||
|
|
||||||
public RelayCommand StartCommand => _startCommand;
|
public RelayCommand StartCommand => _startCommand;
|
||||||
|
|
||||||
public RelayCommand PauseCommand => _pauseCommand;
|
public RelayCommand PauseCommand => _pauseCommand;
|
||||||
@@ -342,6 +357,26 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
|
|
||||||
public RelayCommand ClearHistoryCommand => _clearHistoryCommand;
|
public RelayCommand ClearHistoryCommand => _clearHistoryCommand;
|
||||||
|
|
||||||
|
public RelayCommand RealtimeChartZoomInCommand => _realtimeChartZoomInCommand;
|
||||||
|
|
||||||
|
public RelayCommand RealtimeChartZoomOutCommand => _realtimeChartZoomOutCommand;
|
||||||
|
|
||||||
|
public RelayCommand RealtimeChartResetZoomCommand => _realtimeChartResetZoomCommand;
|
||||||
|
|
||||||
|
public void ZoomRealtimeChartFromWheel(int wheelDelta)
|
||||||
|
{
|
||||||
|
if (wheelDelta > 0)
|
||||||
|
{
|
||||||
|
ZoomInRealtimeChart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wheelDelta < 0)
|
||||||
|
{
|
||||||
|
ZoomOutRealtimeChart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void BeginTableMotion(string direction)
|
public void BeginTableMotion(string direction)
|
||||||
{
|
{
|
||||||
if (!TryGetTableMotionCoil(direction, out var actionName, out var coilAddress))
|
if (!TryGetTableMotionCoil(direction, out var actionName, out var coilAddress))
|
||||||
@@ -1463,6 +1498,49 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ZoomInRealtimeChart()
|
||||||
|
{
|
||||||
|
SetRealtimeChartZoom(_realtimeChartZoomScale * RealtimeChartZoomStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ZoomOutRealtimeChart()
|
||||||
|
{
|
||||||
|
SetRealtimeChartZoom(_realtimeChartZoomScale / RealtimeChartZoomStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetRealtimeChartZoom()
|
||||||
|
{
|
||||||
|
SetRealtimeChartZoom(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetRealtimeChartZoom(double zoomScale)
|
||||||
|
{
|
||||||
|
var nextZoomScale = Math.Clamp(zoomScale, RealtimeChartMinimumZoomScale, RealtimeChartMaximumZoomScale);
|
||||||
|
var tolerance = Math.Max(0.000000000001, Math.Abs(_realtimeChartZoomScale) * 0.000000001);
|
||||||
|
if (Math.Abs(nextZoomScale - _realtimeChartZoomScale) <= tolerance)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_realtimeChartZoomScale = nextZoomScale;
|
||||||
|
OnPropertyChanged(nameof(RealtimeChartCanvasWidth));
|
||||||
|
OnPropertyChanged(nameof(RealtimeChartZoomText));
|
||||||
|
_realtimeChartZoomInCommand.RaiseCanExecuteChanged();
|
||||||
|
_realtimeChartZoomOutCommand.RaiseCanExecuteChanged();
|
||||||
|
_realtimeChartResetZoomCommand.RaiseCanExecuteChanged();
|
||||||
|
NotifyRealtimeChartChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatRealtimeChartZoomText(double zoomScale)
|
||||||
|
{
|
||||||
|
return zoomScale switch
|
||||||
|
{
|
||||||
|
>= 0.1 => zoomScale.ToString("P0", CultureInfo.CurrentCulture),
|
||||||
|
>= 0.01 => zoomScale.ToString("P1", CultureInfo.CurrentCulture),
|
||||||
|
_ => zoomScale.ToString("P2", CultureInfo.CurrentCulture)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateReferenceLine(ObservableCollection<ObservablePoint> target, double y)
|
private void UpdateReferenceLine(ObservableCollection<ObservablePoint> target, double y)
|
||||||
{
|
{
|
||||||
if (y <= 0)
|
if (y <= 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user