This commit is contained in:
GukSang.Jin
2026-05-14 17:05:53 +08:00
parent 23cefe1378
commit a8859bfc3e
5 changed files with 81 additions and 31 deletions

View File

@@ -280,22 +280,7 @@ public sealed class RealtimeForceChart : FrameworkElement
private static int ResolveXAxisDivisions(double plotWidth)
{
if (plotWidth >= 700)
{
return 10;
}
if (plotWidth >= 520)
{
return 8;
}
if (plotWidth >= 360)
{
return 6;
}
return 4;
return Math.Clamp((int)Math.Ceiling(plotWidth / 110), 4, 240);
}
private void DrawCenteredText(

View File

@@ -251,21 +251,31 @@
<RowDefinition Height="300" />
</Grid.RowDefinitions>
<controls:RealtimeForceChart x:Name="RealtimeForceChart"
Grid.Row="0"
Margin="0,0,0,8"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Samples="{Binding RealtimeForceSamples}"
PeakLineSamples="{Binding RealtimePeakLineSamples}"
AverageLineSamples="{Binding RealtimeAverageLineSamples}"
CurrentPointSamples="{Binding RealtimeCurrentPointSamples}"
XMax="{Binding RealtimeChartXMax}"
YMax="{Binding RealtimeChartYMax}"
TravelMm="{Binding Recipe.TravelMm}"
SledMassGrams="{Binding Recipe.SledMassGrams}"
Loaded="RealtimeForceChart_Loaded"
SizeChanged="RealtimeForceChart_SizeChanged" />
<ScrollViewer x:Name="RealtimeChartScrollViewer"
Grid.Row="0"
Margin="0,0,0,8"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Disabled"
CanContentScroll="False"
Loaded="RealtimeChartScrollViewer_Loaded"
ScrollChanged="RealtimeChartScrollViewer_ScrollChanged">
<controls:RealtimeForceChart x:Name="RealtimeForceChart"
Width="{Binding RealtimeChartCanvasWidth}"
MinWidth="{Binding ViewportWidth, ElementName=RealtimeChartScrollViewer}"
Height="{Binding ViewportHeight, ElementName=RealtimeChartScrollViewer}"
HorizontalAlignment="Left"
VerticalAlignment="Stretch"
Samples="{Binding RealtimeForceSamples}"
PeakLineSamples="{Binding RealtimePeakLineSamples}"
AverageLineSamples="{Binding RealtimeAverageLineSamples}"
CurrentPointSamples="{Binding RealtimeCurrentPointSamples}"
XMax="{Binding RealtimeChartXMax}"
YMax="{Binding RealtimeChartYMax}"
TravelMm="{Binding Recipe.TravelMm}"
SledMassGrams="{Binding Recipe.SledMassGrams}"
Loaded="RealtimeForceChart_Loaded"
SizeChanged="RealtimeForceChart_SizeChanged" />
</ScrollViewer>
<Border Grid.Row="1" Style="{StaticResource PanelBorderStyle}" Padding="10" Margin="0,0,0,8">
<Grid>

View File

@@ -15,6 +15,7 @@ public partial class MainWindow : Window
private readonly MainViewModel _viewModel = new();
private readonly DispatcherTimer _realtimeChartRenderTimer;
private bool _isRealtimeChartLayoutRefreshQueued;
private bool _isRealtimeChartAutoScrollPinned = true;
private DateTime _realtimeChartRenderUntilUtc = DateTime.MinValue;
public MainWindow()
@@ -73,6 +74,30 @@ public partial class MainWindow : Window
RequestRealtimeChartRender(RealtimeChartStartupRenderPulseDuration);
}
private void RealtimeChartScrollViewer_Loaded(object sender, RoutedEventArgs e)
{
_isRealtimeChartAutoScrollPinned = true;
RequestRealtimeChartRender(RealtimeChartStartupRenderPulseDuration);
}
private void RealtimeChartScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (e.ExtentWidthChange < -1)
{
_isRealtimeChartAutoScrollPinned = true;
ScrollRealtimeChartToEndIfPinned();
return;
}
if (Math.Abs(e.ExtentWidthChange) > 0 || Math.Abs(e.ViewportWidthChange) > 0)
{
ScrollRealtimeChartToEndIfPinned();
return;
}
_isRealtimeChartAutoScrollPinned = IsRealtimeChartScrolledToEnd();
}
private void RealtimeForceChart_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (e.NewSize.Width <= 0 || e.NewSize.Height <= 0)
@@ -196,5 +221,29 @@ public partial class MainWindow : Window
RealtimeForceChart.InvalidateArrange();
RealtimeForceChart.InvalidateVisual();
RealtimeForceChart.UpdateLayout();
RealtimeChartScrollViewer.UpdateLayout();
ScrollRealtimeChartToEndIfPinned();
}
private bool IsRealtimeChartScrolledToEnd()
{
const double tolerance = 8;
return RealtimeChartScrollViewer.HorizontalOffset >= RealtimeChartScrollViewer.ScrollableWidth - tolerance;
}
private void ScrollRealtimeChartToEndIfPinned()
{
if (!_isRealtimeChartAutoScrollPinned)
{
return;
}
if (RealtimeChartScrollViewer.ScrollableWidth <= 0)
{
RealtimeChartScrollViewer.ScrollToHorizontalOffset(0);
return;
}
RealtimeChartScrollViewer.ScrollToHorizontalOffset(RealtimeChartScrollViewer.ScrollableWidth);
}
}

View File

@@ -56,6 +56,8 @@ public sealed class MainViewModel : ObservableObject, IDisposable
private static readonly TimeSpan RealtimeChartRefreshInterval = TimeSpan.FromMilliseconds(250);
private const double RealtimeChartTimeStepSeconds = 0.05;
private const double RealtimeChartXAxisPaddingSeconds = 1;
private const double RealtimeChartMinimumCanvasWidth = 900;
private const double RealtimeChartPixelsPerSecond = 18;
private const double MinimumEmptyChartXAxisMaxLimit = 1;
private const double EmptyChartYAxisMaxLimit = 0.5;
private const double EmptyChartPointForceN = 0.001;
@@ -308,6 +310,8 @@ public sealed class MainViewModel : ObservableObject, IDisposable
public double RealtimeChartXMax => _lastForceXAxisMaxLimit;
public double RealtimeChartCanvasWidth => Math.Max(RealtimeChartMinimumCanvasWidth, _lastForceXAxisMaxLimit * RealtimeChartPixelsPerSecond);
public double RealtimeChartYMax => _lastForceYAxisMaxLimit;
public RelayCommand StartCommand => _startCommand;
@@ -1306,6 +1310,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
OnPropertyChanged(nameof(ForceXAxes));
OnPropertyChanged(nameof(ForceYAxes));
OnPropertyChanged(nameof(RealtimeChartXMax));
OnPropertyChanged(nameof(RealtimeChartCanvasWidth));
OnPropertyChanged(nameof(RealtimeChartYMax));
}
}
@@ -1319,6 +1324,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
OnPropertyChanged(nameof(ForceXAxes));
OnPropertyChanged(nameof(ForceYAxes));
OnPropertyChanged(nameof(RealtimeChartXMax));
OnPropertyChanged(nameof(RealtimeChartCanvasWidth));
OnPropertyChanged(nameof(RealtimeChartYMax));
NotifyRealtimeChartChanged();
}

BIN
COFTester/摩擦仪.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB