diff --git a/XP.Common/Controls/ImageHistogram/ChartRenderer.cs b/XP.Common/Controls/ImageHistogram/ChartRenderer.cs index bf242a4..b02d5fc 100644 --- a/XP.Common/Controls/ImageHistogram/ChartRenderer.cs +++ b/XP.Common/Controls/ImageHistogram/ChartRenderer.cs @@ -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; /// @@ -25,12 +25,12 @@ namespace XP.Common.Controls.ImageHistogram /// 构造函数,接收 RadCartesianChart 实例 | Constructor, receives RadCartesianChart instance /// /// 图表控件实例 | Chart control instance - /// 柱状图系列 | Bar series + /// 面积图系列 | Area series /// X 轴 | X axis - 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(); @@ -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 /// 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 collection) + if (_areaSeries.ItemsSource is ICollection collection) return collection.Count; - if (_barSeries.ItemsSource is IEnumerable enumerable) + if (_areaSeries.ItemsSource is IEnumerable 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); + } + } + + /// + /// 将步长取整为"好看"的数值(1, 2, 5 的倍数 × 10^n)| Round step to "nice" value (multiples of 1, 2, 5 × 10^n) + /// 例如:123456 → 100000,350000 → 500000,780000 → 1000000 + /// + 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); } /// diff --git a/XP.Common/Controls/ImageHistogram/FrequencyLabelConverter.cs b/XP.Common/Controls/ImageHistogram/FrequencyLabelConverter.cs new file mode 100644 index 0000000..76a76c8 --- /dev/null +++ b/XP.Common/Controls/ImageHistogram/FrequencyLabelConverter.cs @@ -0,0 +1,49 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace XP.Common.Controls.ImageHistogram +{ + /// + /// 频次标签转换器:将大数值转为 K/M 缩写格式 | Frequency label converter: converts large values to K/M abbreviation format + /// 例如:500000 → "500K",1500000 → "1.5M",800 → "800" + /// + internal sealed class FrequencyLabelConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null) return "0"; + + double num; + if (value is double d) + num = d; + else if (value is decimal dec) + num = (double)dec; + else if (!double.TryParse(value.ToString(), out num)) + return value.ToString() ?? "0"; + + if (num >= 1_000_000) + { + double mValue = num / 1_000_000.0; + return mValue == Math.Floor(mValue) + ? $"{(int)mValue}M" + : $"{mValue:0.#}M"; + } + + if (num >= 1_000) + { + double kValue = num / 1_000.0; + return kValue == Math.Floor(kValue) + ? $"{(int)kValue}K" + : $"{kValue:0.#}K"; + } + + return $"{(int)num}"; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/XP.Common/Controls/ImageHistogram/ImageHistogramControl.xaml b/XP.Common/Controls/ImageHistogram/ImageHistogramControl.xaml index 7d1f974..f274cf1 100644 --- a/XP.Common/Controls/ImageHistogram/ImageHistogramControl.xaml +++ b/XP.Common/Controls/ImageHistogram/ImageHistogramControl.xaml @@ -3,39 +3,58 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation" xmlns:loc="clr-namespace:XP.Common.Localization.Extensions" + xmlns:local="clr-namespace:XP.Common.Controls.ImageHistogram" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="200" d:DesignWidth="400"> + + + - + - + + MajorStep="64" + FontSize="9"> + + + + + + - + + FontSize="9"> + + + + + + - + - + @@ -44,7 +63,7 @@ Text="{loc:Localization Histogram_NoData}" HorizontalAlignment="Center" VerticalAlignment="Center" - FontSize="14" + FontSize="12" Foreground="#9E9E9E" Visibility="Visible"/>