Skip to content

Commit

Permalink
[1] Prevent changing zoom of log chart by single click.
Browse files Browse the repository at this point in the history
[2] Reverse log chart series values and X-axis when needed.
[3] Enable animation of log chart.
  • Loading branch information
hamster620 committed Oct 27, 2024
1 parent df21121 commit 03b188c
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 27 deletions.
93 changes: 69 additions & 24 deletions ULogViewer/Controls/SessionView.axaml.LogChart.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ public LvcSize Measure(Chart<SkiaSharpDrawingContext> chart)


// Tool tip of log chart.
class LogChartToolTip(SessionView view) : IChartTooltip<SkiaSharpDrawingContext>
class LogChartToolTip(SessionView view, bool isXAxisInverted) : IChartTooltip<SkiaSharpDrawingContext>
{
// Fields.
StackPanel<RoundedRectangleGeometry, SkiaSharpDrawingContext>? container;
Expand All @@ -166,6 +166,9 @@ public void Hide(Chart<SkiaSharpDrawingContext> chart)
}
}

// Whether X-axis is inverted or not.
public bool IsXAxisInverted { get; } = isXAxisInverted;

// Show
public void Show(IEnumerable<ChartPoint> foundPoints, Chart<SkiaSharpDrawingContext> chart)
{
Expand Down Expand Up @@ -211,7 +214,7 @@ public void Show(IEnumerable<ChartPoint> foundPoints, Chart<SkiaSharpDrawingCont
it.ClippingMode = ClipMode.None;
it.HorizontalAlignment = Align.Start;
it.Paint = logChart.TooltipTextPaint;
it.Text = sessionView.GetLogChartXToolTipLabel(point);
it.Text = sessionView.GetLogChartXToolTipLabel(point, this.IsXAxisInverted);
it.TextSize = logChart.TooltipTextSize ?? 12.0;
it.VerticalAlignment = Align.Start;
}));
Expand Down Expand Up @@ -275,7 +278,6 @@ public override void InitializeTask(SkiaSharpDrawingContext drawingContext)


// Constants.
const int InitLogChartAnimationDelay = 1000;
const double LogBarChartXCoordinateScaling = 1.3;
const long DurationToDropClickEvent = 500;
const double PointerDistanceToDropClickEvent = 5;
Expand All @@ -284,6 +286,7 @@ public override void InitializeTask(SkiaSharpDrawingContext drawingContext)
const int LogChartXAxisMinValueCount = 10;
const double LogChartXAxisMinMaxReservedRatio = 0.01;
const double LogChartYAxisMinMaxReservedRatio = 0.05;
const int ResumeLogChartAnimationDelay = 500;


// Static fields.
Expand Down Expand Up @@ -333,7 +336,7 @@ public override void InitializeTask(SkiaSharpDrawingContext drawingContext)
bool isSyncingLogChartPanelSize;
readonly CartesianChart logChart;
readonly RowDefinition logChartGridRow;
ChartPoint[] logChartPointerDownData = Array.Empty<ChartPoint>();
ChartPoint[] logChartPointerDownData = [];
Point? logChartPointerDownPosition;
readonly Stopwatch logChartPointerDownWatch = new();
readonly ObservableList<ISeries> logChartSeries = new();
Expand All @@ -345,6 +348,7 @@ public override void InitializeTask(SkiaSharpDrawingContext drawingContext)
{
CrosshairSnapEnabled = true,
};
(double?, double?)? logChartXAxisLimitToKeep;
double logChartXCoordinateScaling = 1.0;
readonly Axis logChartYAxis = new()
{
Expand Down Expand Up @@ -378,9 +382,6 @@ void AttachToLogChart(LogChartViewModel viewModel)
this.areLogChartAxesReady = false;
this.UpdateLogChartAxes();
this.UpdateLogChartPanelVisibility();

// start animation later
this.startLogChartAnimationsAction.Reschedule(InitLogChartAnimationDelay);
}


Expand Down Expand Up @@ -439,6 +440,7 @@ ISeries CreateLogChartSeries(LogChartViewModel viewModel, DisplayableLogChartSer

// create series
var chartType = viewModel.ChartType;
var isInverted = viewModel.IsXAxisInverted;
switch (chartType)
{
case LogChartType.ValueStatisticBars:
Expand All @@ -454,7 +456,7 @@ ISeries CreateLogChartSeries(LogChartViewModel viewModel, DisplayableLogChartSer
Rx = 0,
Ry = 0,
Tag = series,
Values = series.Values,
Values = isInverted ? series.Values.Reverse() : series.Values,
XToolTipLabelFormatter = chartType == LogChartType.ValueStatisticBars ? null : this.GetLogChartXToolTipLabel,
YToolTipLabelFormatter = p => this.GetLogChartYToolTipLabel(series, p, false),
};
Expand Down Expand Up @@ -487,7 +489,7 @@ ISeries CreateLogChartSeries(LogChartViewModel viewModel, DisplayableLogChartSer
IsAntialias = true,
},
Tag = series,
Values = series.Values,
Values = isInverted ? series.Values.Reverse() : series.Values,
XToolTipLabelFormatter = this.GetLogChartXToolTipLabel,
YToolTipLabelFormatter = p => this.GetLogChartYToolTipLabel(series, p, false),
};
Expand All @@ -503,7 +505,7 @@ ISeries CreateLogChartSeries(LogChartViewModel viewModel, DisplayableLogChartSer
Rx = 0,
Ry = 0,
Tag = series,
Values = series.Values,
Values = isInverted ? series.Values.Reverse() : series.Values,
XToolTipLabelFormatter = this.GetLogChartXToolTipLabel,
YToolTipLabelFormatter = p => this.GetLogChartYToolTipLabel(series, p, false),
};
Expand Down Expand Up @@ -560,7 +562,7 @@ or LogChartType.ValueCurvesWithDataPoints
},
},
Tag = series,
Values = series.Values,
Values = isInverted ? series.Values.Reverse() : series.Values,
XToolTipLabelFormatter = this.GetLogChartXToolTipLabel,
YToolTipLabelFormatter = p => this.GetLogChartYToolTipLabel(series, p, false),
};
Expand Down Expand Up @@ -677,14 +679,16 @@ string GetLogChartSeriesDisplayName(DisplayableLogChartSeriesSource source)
// Get X label of tool tip of log chart.
string GetLogChartXToolTipLabel<TVisual>(ChartPoint<DisplayableLogChartSeriesValue?, TVisual, LabelGeometry> point) =>
this.GetLogChartXToolTipLabel(point.Model);
string GetLogChartXToolTipLabel(ChartPoint point)
string GetLogChartXToolTipLabel(ChartPoint point, bool isInverted)
{
if (point.Context.Series is not ICartesianSeries<SkiaSharpDrawingContext> cartesianSeries
|| cartesianSeries.Tag is not DisplayableLogChartSeries series)
{
return "";
}
var index = (int)(point.Coordinate.SecondaryValue / this.logChartXCoordinateScaling + 0.5);
if (isInverted)
index = series.Values.Count - index - 1;
if (index < 0 || index >= series.Values.Count)
return "";
return this.GetLogChartXToolTipLabel(series.Values[index]);
Expand Down Expand Up @@ -908,7 +912,7 @@ void OnLogChartDataClick(ChartPoint[] points)
// Called when pointer moved on log chart.
void OnLogChartPointerMoved(object? sender, PointerEventArgs e)
{
if (this.logChartPointerDownPosition.HasValue && this.logChartPointerDownData.Length > 0)
if (this.logChartPointerDownPosition.HasValue && this.logChartPointerDownData.IsNotEmpty())
{
var downPosition = this.logChartPointerDownPosition.Value;
var position = e.GetPosition(this.logChart);
Expand All @@ -917,7 +921,7 @@ void OnLogChartPointerMoved(object? sender, PointerEventArgs e)
var distance = Math.Sqrt(diffX * diffX + diffY * diffY);
if (distance >= PointerDistanceToDropClickEvent)
{
this.logChartPointerDownData = Array.Empty<ChartPoint>();
this.logChartPointerDownData = [];
this.logChartPointerDownPosition = null;
this.logChartPointerDownWatch.Reset();
}
Expand All @@ -938,6 +942,13 @@ void OnLogChartPointerPressed(object? sender, PointerPressedEventArgs e)
}


// Called when pointer wheel changed on log chart.
void OnLogChartPointerWheelChanged(object? sender, PointerWheelEventArgs e)
{
//
}


// Called when pointer released on log chart.
void OnLogChartPointerReleased(object? sender, PointerReleasedEventArgs e)
{
Expand All @@ -950,18 +961,18 @@ void OnLogChartPointerReleased(object? sender, PointerReleasedEventArgs e)
if (this.isLogChartDoubleTapped)
{
this.isLogChartDoubleTapped = false;
this.logChartPointerDownData = Array.Empty<ChartPoint>();
this.logChartPointerDownPosition = null;
this.logChartPointerDownWatch.Reset();
this.SynchronizationContext.PostDelayed(this.ResetLogChartZoom, 100);
if (this.logChartXAxis.MinLimit.HasValue || this.logChartXAxis.MaxLimit.HasValue)
this.SynchronizationContext.Post(this.ResetLogChartZoom);
return;
}

// single click
if (this.logChartPointerDownData.Length > 0)
if (this.logChartPointerDownData.IsNotEmpty())
{
var data = this.logChartPointerDownData;
this.logChartPointerDownData = Array.Empty<ChartPoint>();
this.logChartPointerDownData = [];
if (this.logChartPointerDownPosition.HasValue)
{
var downPosition = this.logChartPointerDownPosition.Value;
Expand All @@ -972,6 +983,9 @@ void OnLogChartPointerReleased(object? sender, PointerReleasedEventArgs e)
this.logChartPointerDownPosition = null;
if (distance < PointerDistanceToDropClickEvent && this.logChartPointerDownWatch.ElapsedMilliseconds < DurationToDropClickEvent)
this.OnLogChartDataClick(data);
var minLimit = this.logChartXAxis.MinLimit;
var maxLimit = this.logChartXAxis.MaxLimit;
this.logChartXAxisLimitToKeep = (minLimit, maxLimit); // [Workaround] Prevent changing limit by click
}
this.logChartPointerDownWatch.Reset();
}
Expand Down Expand Up @@ -1327,7 +1341,9 @@ TimeSpan SelectLogChartSeriesAnimationSpeed()
return this.SelectLogChartSeriesAnimationSpeed(session.LogChart);
}
TimeSpan SelectLogChartSeriesAnimationSpeed(LogChartViewModel viewModel) =>
default;
this.startLogChartAnimationsAction.IsScheduled
? TimeSpan.Zero
: this.Application.FindResourceOrDefault("TimeSpan/Animation.Fast", TimeSpan.FromMilliseconds(200));


// Select color for series.
Expand Down Expand Up @@ -1550,10 +1566,10 @@ public void ShowLogChartTypeMenu()
this.logChartTypeMenu.DataContext = (this.DataContext as Session)?.LogChart.ChartType;
this.logChartTypeMenu.Open(this.logChartTypeButton);
}


// Start all animations og log chart.
void StartLogChartAnimations()
// Update all animations of log chart.
void UpdateLogChartAnimations()
{
var animationSpeed = this.SelectLogChartSeriesAnimationSpeed();
foreach (var series in this.logChartSeries)
Expand Down Expand Up @@ -1588,7 +1604,7 @@ void UpdateLogChartAxes()
var viewModel = (this.DataContext as Session)?.LogChart;
var chartType = viewModel?.ChartType ?? LogChartType.None;
var axisType = viewModel?.XAxisType ?? LogChartXAxisType.None;
axis.IsInverted = (this.DataContext as Session)?.LogChart.IsXAxisInverted ?? false;
axis.IsInverted = viewModel?.IsXAxisInverted ?? false;
if (axisType != LogChartXAxisType.None && chartType.IsDirectNumberValueSeriesType())
{
var textPaint = textBrush.Let(brush =>
Expand All @@ -1603,6 +1619,12 @@ void UpdateLogChartAxes()
var doubleIndex = (value / this.logChartXCoordinateScaling);
var intIndex = (int)(doubleIndex + 0.5);
var logs = session.Logs;
if (axis.IsInverted)
{
var diff = doubleIndex - intIndex;
intIndex = (logs.Count - intIndex - 1);
doubleIndex = intIndex - diff;
}
if (Math.Abs(intIndex - doubleIndex) < 0.001 && intIndex >= 0 && intIndex < logs.Count)
return this.GetLogChartXToolTipLabel(logs[intIndex], axisType == LogChartXAxisType.SimpleTimestamp, true);
return "";
Expand Down Expand Up @@ -1656,6 +1678,8 @@ void UpdateLogChartAxes()
axis.TextSize = (float)axisFontSize;
axis.ZeroPaint = new SolidColorPaint(textPaint.Color, (float)axisWidth);
});
if ((this.logChart.Tooltip as LogChartToolTip)?.IsXAxisInverted != this.logChartXAxis.IsInverted)
this.logChart.Tooltip = new LogChartToolTip(this, this.logChartXAxis.IsInverted);
this.areLogChartAxesReady = true;
}

Expand Down Expand Up @@ -1741,6 +1765,27 @@ void UpdateLogChartXAxisLimit()
var axis = this.logChartXAxis;
var minLimit = axis.MinLimit ?? double.NaN;
var maxLimit = axis.MaxLimit ?? double.NaN;
if (this.logChartXAxisLimitToKeep.HasValue)
{
var limit = this.logChartXAxisLimitToKeep.Value;
this.logChartXAxisLimitToKeep = null;
if (limit.Item1.HasValue)
{
if (double.IsNaN(minLimit) || Math.Abs(minLimit - limit.Item1.Value) >= 0.001)
axis.MinLimit = limit.Item1.Value;
}
else if (double.IsFinite(minLimit))
axis.MinLimit = null;
if (limit.Item2.HasValue)
{
if (double.IsNaN(maxLimit) || Math.Abs(maxLimit - limit.Item2.Value) >= 0.001)
axis.MaxLimit = limit.Item2.Value;
}
else if (double.IsFinite(maxLimit))
axis.MaxLimit = null;
minLimit = limit.Item1 ?? double.NaN;
maxLimit = limit.Item2 ?? double.NaN;
}
if (!double.IsFinite(minLimit) && !double.IsFinite(maxLimit))
{
this.SetValue(IsLogChartHorizontallyZoomedProperty, false);
Expand Down
20 changes: 17 additions & 3 deletions ULogViewer/Controls/SessionView.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -551,9 +551,9 @@ public SessionView()
it.AddHandler(PointerMovedEvent, this.OnLogChartPointerMoved, RoutingStrategies.Tunnel);
it.AddHandler(PointerPressedEvent, this.OnLogChartPointerPressed, RoutingStrategies.Tunnel);
it.AddHandler(PointerReleasedEvent, this.OnLogChartPointerReleased, RoutingStrategies.Tunnel);
it.AddHandler(PointerWheelChangedEvent, this.OnLogChartPointerWheelChanged, RoutingStrategies.Tunnel);
it.SizeChanged += (_, e) => this.OnLogChartSizeChanged(e);
it.Legend = new LogChartLegend(this);
it.Tooltip = new LogChartToolTip(this);
this.logChartXAxis.PropertyChanged += this.OnLogChartAxisPropertyChanged;
this.logChartYAxis.PropertyChanged += this.OnLogChartAxisPropertyChanged;
});
Expand Down Expand Up @@ -1049,7 +1049,7 @@ public SessionView()
this.scrollToLatestLogAnalysisResultAction.Cancel();
this.ScrollToLatestLogAnalysisResult(true);
});
this.startLogChartAnimationsAction = new(this.StartLogChartAnimations);
this.startLogChartAnimationsAction = new(this.UpdateLogChartAnimations);
this.updateCanEditCurrentScriptLogDataSourceProviderAction = new(() =>
{
var session = this.DataContext as Session;
Expand Down Expand Up @@ -3007,6 +3007,7 @@ protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e
});
}
});
window.SizeChanged += this.OnWindowSizeChanged;
});

// attach to control fonts
Expand Down Expand Up @@ -3160,7 +3161,11 @@ protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs
this.areInitDialogsClosedObserverToken = this.areInitDialogsClosedObserverToken.DisposeAndReturnNull();
this.hasDialogsObserverToken = this.hasDialogsObserverToken.DisposeAndReturnNull();
this.isActiveObserverToken = this.isActiveObserverToken.DisposeAndReturnNull();
this.attachedWindow = null;
if (this.attachedWindow is not null)
{
this.attachedWindow.SizeChanged -= this.OnWindowSizeChanged;
this.attachedWindow = null;
}

// call base
base.OnDetachedFromLogicalTree(e);
Expand Down Expand Up @@ -4619,6 +4624,15 @@ void OnToolBarPointerReleased(object? sender, PointerReleasedEventArgs e)
}


// Called when size of attached window changed.
void OnWindowSizeChanged(object? sender, SizeChangedEventArgs e)
{
// pause animation of log chart temporarily
this.startLogChartAnimationsAction.Reschedule(ResumeLogChartAnimationDelay);
this.UpdateLogChartAnimations();
}


/// <summary>
/// Perform full GC.
/// </summary>
Expand Down
19 changes: 19 additions & 0 deletions ULogViewer/ViewModels/LogChartViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ void ApplyLogChartType(LogChartType chartType)
if (this.filteredLogsSeriesGenerator is not null)
this.filteredLogsSeriesGenerator.LogChartType = chartType;
this.markedLogsSeriesGenerator.LogChartType = chartType;
this.UpdateAxisInversionState(chartType);
}


Expand Down Expand Up @@ -644,6 +645,7 @@ protected override void OnLogProfilePropertyChanged(PropertyChangedEventArgs e)
if (this.filteredLogsSeriesGenerator is not null)
this.filteredLogsSeriesGenerator.SourceLogComparer = logComparer;
this.markedLogsSeriesGenerator.SourceLogComparer = logComparer;
this.UpdateAxisInversionState(this.GetValue(ChartTypeProperty));
});
break;
}
Expand Down Expand Up @@ -1151,6 +1153,23 @@ void ToggleVisibleSeriesSource(DisplayableLogChartSeriesSource? source)
public ICommand ToggleVisibleSeriesSourceCommand { get; }


// Update inversion state of axes.
void UpdateAxisInversionState(LogChartType chartType)
{
if (this.LogProfile is not { } logProfile || logProfile.SortDirection == SortDirection.Ascending)
{
this.ResetValue(IsXAxisInvertedProperty);
return;
}
this.SetValue(IsXAxisInvertedProperty, chartType switch
{
LogChartType.None
or LogChartType.ValueStatisticBars => false,
_ => true,
});
}


// Update whether setting type of log chart is available or not.
void UpdateCanSetChartType()
{
Expand Down

0 comments on commit 03b188c

Please sign in to comment.