直方图将柱状图替换为面积图,优化密集数据显示效果,Y轴刻度自动取整支持 K/M 缩写,X 轴根据数据范围自动设置。

This commit is contained in:
QI Mingxuan
2026-05-21 10:37:28 +08:00
parent ef83a7637a
commit d7c027b732
3 changed files with 131 additions and 20 deletions
@@ -13,7 +13,7 @@ namespace XP.Common.Controls.ImageHistogram
internal sealed class ChartRenderer
{
private readonly RadCartesianChart _chart;
private readonly BarSeries _barSeries;
private readonly ScatterAreaSeries _areaSeries;
private readonly LinearAxis _xAxis;
/// <summary>
@@ -25,12 +25,12 @@ namespace XP.Common.Controls.ImageHistogram
/// 构造函数,接收 RadCartesianChart 实例 | Constructor, receives RadCartesianChart instance
/// </summary>
/// <param name="chart">图表控件实例 | Chart control instance</param>
/// <param name="barSeries">柱状图系列 | Bar series</param>
/// <param name="areaSeries">面积图系列 | Area series</param>
/// <param name="xAxis">X 轴 | X axis</param>
public ChartRenderer(RadCartesianChart chart, BarSeries barSeries, LinearAxis xAxis)
public ChartRenderer(RadCartesianChart chart, ScatterAreaSeries areaSeries, LinearAxis xAxis)
{
_chart = chart ?? throw new ArgumentNullException(nameof(chart));
_barSeries = barSeries ?? throw new ArgumentNullException(nameof(barSeries));
_areaSeries = areaSeries ?? throw new ArgumentNullException(nameof(areaSeries));
_xAxis = xAxis ?? throw new ArgumentNullException(nameof(xAxis));
}
@@ -64,6 +64,8 @@ namespace XP.Common.Controls.ImageHistogram
// 设置 X 轴范围 | Set X axis range
_xAxis.Minimum = 0;
_xAxis.Maximum = xAxisMax;
// 根据范围自动设置刻度间隔(保持 4-5 个刻度)| Auto set major step based on range (keep 4-5 ticks)
_xAxis.MajorStep = xAxisMax <= 255 ? 64 : 16384;
// 构建数据点 | Build data points
var dataPoints = new List<HistogramDataPoint>();
@@ -103,7 +105,7 @@ namespace XP.Common.Controls.ImageHistogram
}
// 更新图表数据 | Update chart data
_barSeries.ItemsSource = dataPoints;
_areaSeries.ItemsSource = dataPoints;
// 设置 Y 轴范围 | Set Y axis range
UpdateYAxis(displayData, isLogarithmic);
@@ -114,7 +116,7 @@ namespace XP.Common.Controls.ImageHistogram
/// </summary>
public void Clear()
{
_barSeries.ItemsSource = null;
_areaSeries.ItemsSource = null;
// X 轴范围重置为 0-255 | Reset X axis range to 0-255
_xAxis.Minimum = 0;
@@ -131,9 +133,9 @@ namespace XP.Common.Controls.ImageHistogram
{
get
{
if (_barSeries.ItemsSource is ICollection<HistogramDataPoint> collection)
if (_areaSeries.ItemsSource is ICollection<HistogramDataPoint> collection)
return collection.Count;
if (_barSeries.ItemsSource is IEnumerable<HistogramDataPoint> enumerable)
if (_areaSeries.ItemsSource is IEnumerable<HistogramDataPoint> enumerable)
return enumerable.Count();
return 0;
}
@@ -173,7 +175,48 @@ namespace XP.Common.Controls.ImageHistogram
if (maxValue == 0)
maxValue = 1;
SetYAxisRange(0, maxValue, isLogarithmic);
// 计算取整的 MajorStep(约 4 个刻度,对齐到 K/M 整数倍)| Calculate rounded MajorStep (~4 ticks, aligned to K/M multiples)
if (_chart.VerticalAxis is LinearAxis linearAxis)
{
long rawStep = maxValue / 4;
long step = RoundStepToNice(rawStep);
if (step < 1) step = 1;
linearAxis.MajorStep = step;
// 将最大值向上取整到 step 的整数倍 | Round max up to multiple of step
long roundedMax = ((maxValue / step) + 1) * step;
SetYAxisRange(0, roundedMax, isLogarithmic);
}
else
{
SetYAxisRange(0, maxValue, isLogarithmic);
}
}
/// <summary>
/// 将步长取整为"好看"的数值(1, 2, 5 的倍数 × 10^n| Round step to "nice" value (multiples of 1, 2, 5 × 10^n)
/// 例如:123456 → 100000350000 → 500000780000 → 1000000
/// </summary>
private static long RoundStepToNice(long rawStep)
{
if (rawStep <= 0) return 1;
// 找到数量级 | Find order of magnitude
double magnitude = Math.Pow(10, Math.Floor(Math.Log10(rawStep)));
double normalized = rawStep / magnitude;
// 取整到 1, 2, 5 中最近的 | Round to nearest of 1, 2, 5
double niceNormalized;
if (normalized <= 1.5)
niceNormalized = 1;
else if (normalized <= 3.5)
niceNormalized = 2;
else if (normalized <= 7.5)
niceNormalized = 5;
else
niceNormalized = 10;
return (long)(niceNormalized * magnitude);
}
/// <summary>