From 2cb82526bcd7077fe3ce37a0cb98b780b7521bdc Mon Sep 17 00:00:00 2001 From: monitor1394 Date: Thu, 30 Oct 2025 22:48:36 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96`Candlestick`=E5=AF=B9?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E8=BD=B4=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Documentation~/zh/changelog.md | 1 + Runtime/Component/Axis/Axis.cs | 1 + Runtime/Component/Axis/AxisHandler.cs | 2 +- Runtime/Internal/BaseChart.cs | 2 +- .../Serie/Candlestick/CandlestickHandler.cs | 4 +- Runtime/Serie/SerieData.cs | 10 +- Runtime/Serie/SeriesHelper.cs | 6 +- Runtime/Utilities/DateTimeUtil.cs | 152 +++++++++++------- 8 files changed, 108 insertions(+), 70 deletions(-) diff --git a/Documentation~/zh/changelog.md b/Documentation~/zh/changelog.md index 533dd576..4634ea0d 100644 --- a/Documentation~/zh/changelog.md +++ b/Documentation~/zh/changelog.md @@ -80,6 +80,7 @@ slug: /changelog ## master +* (2025.10.30) 优化`Candlestick`对时间轴的支持 * (2025.10.30) 增加`Scatter`的`ignore`支持设置忽略数据 * (2025.10.24) 优化`Sankey`的线条绘制排序 * (2025.10.22) 增加`Pie`的`pieType`支持实心饼图和线框柄图 (#349) diff --git a/Runtime/Component/Axis/Axis.cs b/Runtime/Component/Axis/Axis.cs index 915a9d4f..527e6917 100644 --- a/Runtime/Component/Axis/Axis.cs +++ b/Runtime/Component/Axis/Axis.cs @@ -485,6 +485,7 @@ namespace XCharts.Runtime context.maxValue = 0; context.destMinValue = 0; context.destMaxValue = 0; + context.labelValueList.Clear(); } public Axis Clone() diff --git a/Runtime/Component/Axis/AxisHandler.cs b/Runtime/Component/Axis/AxisHandler.cs index 1b576b95..d1d4432c 100644 --- a/Runtime/Component/Axis/AxisHandler.cs +++ b/Runtime/Component/Axis/AxisHandler.cs @@ -364,7 +364,7 @@ namespace XCharts { var lastCount = axis.context.labelValueList.Count; axis.context.tickValue = DateTimeUtil.UpdateTimeAxisDateTimeList(axis.context.labelValueList, - axis.context.minValue, axis.context.maxValue, axis.splitNumber); + axis.context.minValue, axis.context.maxValue, axis.splitNumber, axis.ceilRate); if (axis.context.labelValueList.Count != lastCount) axis.SetAllDirty(); diff --git a/Runtime/Internal/BaseChart.cs b/Runtime/Internal/BaseChart.cs index 3c8caab6..9b325278 100644 --- a/Runtime/Internal/BaseChart.cs +++ b/Runtime/Internal/BaseChart.cs @@ -204,7 +204,7 @@ namespace XCharts.Runtime protected override void OnValidate() { base.OnValidate(); - foreach (var handler in m_SerieHandlers) handler.ForceUpdateSerieContext(); + ResetChartStatus(); } #endif diff --git a/Runtime/Serie/Candlestick/CandlestickHandler.cs b/Runtime/Serie/Candlestick/CandlestickHandler.cs index b72be4de..b17cc585 100644 --- a/Runtime/Serie/Candlestick/CandlestickHandler.cs +++ b/Runtime/Serie/Candlestick/CandlestickHandler.cs @@ -157,13 +157,13 @@ namespace XCharts.Runtime (itemStyle.borderWidth == 0 ? theme.serie.candlestickBorderWidth : itemStyle.borderWidth); if (serieData.IsDataChanged()) dataChanging = true; - float pX = grid.context.x + i * categoryWidth; + float pX = xAxis.IsCategory() ? grid.context.x + i * categoryWidth : AxisHelper.GetAxisValuePosition(grid, xAxis, categoryWidth, serieData.GetData(0)); float zeroY = grid.context.y + yAxis.context.offset; if (!xAxis.boundaryGap) pX -= categoryWidth / 2; float pY = zeroY; var barHig = 0f; double valueTotal = yMaxValue - yMinValue; - var minCut = (yMinValue > 0 ? yMinValue : 0); + var minCut = yMinValue > 0 ? yMinValue : 0; if (valueTotal != 0) { barHig = (float)((close - open) / valueTotal * grid.context.height); diff --git a/Runtime/Serie/SerieData.cs b/Runtime/Serie/SerieData.cs index d6104093..fc01844d 100644 --- a/Runtime/Serie/SerieData.cs +++ b/Runtime/Serie/SerieData.cs @@ -645,11 +645,12 @@ namespace XCharts.Runtime /// the maxinum value. /// ||最大值。 /// - public double GetMaxData(bool inverse = false) + public double GetMaxData(bool inverse = false, int startDimensionIndex = 0) { if (m_Data.Count == 0) return 0; var temp = double.MinValue; - for (int i = 0; i < m_Data.Count; i++) + if (startDimensionIndex < 0) startDimensionIndex = 0; + for (int i = startDimensionIndex; i < m_Data.Count; i++) { var value = GetData(i, inverse); if (value > temp) temp = value; @@ -661,11 +662,12 @@ namespace XCharts.Runtime /// the mininum value. /// ||最小值。 /// - public double GetMinData(bool inverse = false) + public double GetMinData(bool inverse = false, int startDimensionIndex = 0) { if (m_Data.Count == 0) return 0; var temp = double.MaxValue; - for (int i = 0; i < m_Data.Count; i++) + if (startDimensionIndex < 0) startDimensionIndex = 0; + for (int i = startDimensionIndex; i < m_Data.Count; i++) { var value = GetData(i, inverse); if (value < temp) temp = value; diff --git a/Runtime/Serie/SeriesHelper.cs b/Runtime/Serie/SeriesHelper.cs index ed6a0ab7..84837696 100644 --- a/Runtime/Serie/SeriesHelper.cs +++ b/Runtime/Serie/SeriesHelper.cs @@ -384,7 +384,7 @@ namespace XCharts.Runtime else { var showData = serie.GetDataList(filterByDataZoom ? chart.GetXDataZoomOfSerie(serie) : null); - if (serie is Candlestick || serie is SimplifiedCandlestick) + if (dimension > 0 && (serie is Candlestick || serie is SimplifiedCandlestick)) { foreach (var data in showData) { @@ -441,9 +441,9 @@ namespace XCharts.Runtime if (!_serieTotalValueForMinMax.ContainsKey(j)) _serieTotalValueForMinMax[j] = 0; double currData = 0; - if (serie is Candlestick) + if (serie is Candlestick || serie is SimplifiedCandlestick) { - currData = showData[j].GetMaxData(false); + currData = showData[j].GetMaxData(false, dimension); } else { diff --git a/Runtime/Utilities/DateTimeUtil.cs b/Runtime/Utilities/DateTimeUtil.cs index 26a064c7..33936825 100644 --- a/Runtime/Utilities/DateTimeUtil.cs +++ b/Runtime/Utilities/DateTimeUtil.cs @@ -83,15 +83,16 @@ namespace XCharts.Runtime } } - public static DateTime GetDateTime(double timestamp, bool local = true) + public static DateTime GetDateTime(double timestamp, bool local = false) { - return local ? k_LocalDateTime1970.AddSeconds(timestamp) : k_DateTime1970.AddSeconds(timestamp); + var dateTime = local ? k_LocalDateTime1970.AddSeconds(timestamp) : k_DateTime1970.AddSeconds(timestamp); + return dateTime; } - public static string GetDefaultDateTimeString(double timestamp, double range = 0) + public static string GetDefaultDateTimeString(double timestamp, double range = 0, bool local = false) { var dateString = String.Empty; - var dateTime = GetDateTime(timestamp); + var dateTime = GetDateTime(timestamp, local); if (range <= 0 || range >= DateTimeUtil.ONE_DAY) { dateString = dateTime.ToString("yyyy-MM-dd"); @@ -144,7 +145,7 @@ namespace XCharts.Runtime /// /// /// - internal static float UpdateTimeAxisDateTimeList(List list, double minTimestamp, double maxTimestamp, int splitNumber) + internal static float UpdateTimeAxisDateTimeList(List list, double minTimestamp, double maxTimestamp, int splitNumber, double ceilRate) { var range = maxTimestamp - minTimestamp; if (range <= 0) @@ -152,61 +153,15 @@ namespace XCharts.Runtime list.Clear(); return 0; } - var dtMin = DateTimeUtil.GetDateTime(minTimestamp); - var dtMax = DateTimeUtil.GetDateTime(maxTimestamp); - int tick = 0; - if (range >= ONE_YEAR * MIN_TIME_SPLIT_NUMBER) + var dtMin = GetDateTime(minTimestamp); + var dtMax = GetDateTime(maxTimestamp); + int tick; + if (ceilRate != 0) { - var num = splitNumber <= 0 ? GetSplitNumber(range, ONE_YEAR) : (int)Math.Max(range / (splitNumber * ONE_YEAR), 1); - var dtStart = GetDateTime(GetFirstMaxValue(list, minTimestamp)); - dtStart = new DateTime(dtStart.Year, dtStart.Month, 1); - while (dtStart > dtMin) - { - dtStart = dtStart.AddYears(-num); - } - if (dtStart < dtMin) - { - dtStart = dtStart.AddYears(num); - } - tick = num * 365 * 24 * 3600; - list.Clear(); - while (dtStart.Ticks < dtMax.Ticks) - { - list.Add(DateTimeUtil.GetTimestamp(dtStart)); - dtStart = dtStart.AddYears(num); - } - } - else if (range >= ONE_MONTH * MIN_TIME_SPLIT_NUMBER) - { - var num = splitNumber <= 0 ? GetSplitNumber(range, ONE_MONTH) : (int)Math.Max(range / (splitNumber * ONE_MONTH), 1); - var dtStart = GetDateTime(GetFirstMaxValue(list, minTimestamp)); - dtStart = new DateTime(dtStart.Year, dtStart.Month, 1); - while (dtStart > dtMin) - { - dtStart = dtStart.AddMonths(-num); - } - if (dtStart < dtMin) - { - dtStart = dtStart.AddMonths(num); - } - tick = num * 30 * 24 * 3600; - list.Clear(); - while (dtStart.Ticks < dtMax.Ticks) - { - list.Add(DateTimeUtil.GetTimestamp(dtStart)); - dtStart = dtStart.AddMonths(num); - } - } - else - { - int tickSecond; - if (range >= ONE_DAY * MIN_TIME_SPLIT_NUMBER) tickSecond = ONE_DAY; - else if (range >= ONE_HOUR * MIN_TIME_SPLIT_NUMBER) tickSecond = ONE_HOUR; - else if (range >= ONE_MINUTE * MIN_TIME_SPLIT_NUMBER) tickSecond = ONE_MINUTE; - else tickSecond = ONE_SECOND; - tick = GetTickSecond(range, splitNumber, tickSecond); - var let = minTimestamp % tick; - var defaultTimestamp = let == 0 ? minTimestamp : minTimestamp - let + tick; + var tickSecond = (int)ceilRate; + tick = GetTickSecond(range, 0, tickSecond); + var let = minTimestamp % tickSecond; + var defaultTimestamp = let == 0 ? minTimestamp : minTimestamp - let + tickSecond; var startTimestamp = (int)GetFirstMaxValue(list, minTimestamp, defaultTimestamp); while (startTimestamp > minTimestamp) { @@ -219,6 +174,85 @@ namespace XCharts.Runtime list.Clear(); AddTickTimestamp(list, startTimestamp, maxTimestamp, tick); } + else + { + if (range >= ONE_YEAR * MIN_TIME_SPLIT_NUMBER) + { + var num = splitNumber <= 0 ? GetSplitNumber(range, ONE_YEAR) : (int)Math.Max(range / (splitNumber * ONE_YEAR), 1); + var dtStart = GetDateTime(GetFirstMaxValue(list, minTimestamp)); + dtStart = new DateTime(dtStart.Year, dtStart.Month, 1); + while (dtStart > dtMin) + { + dtStart = dtStart.AddYears(-num); + } + if (dtStart < dtMin) + { + dtStart = dtStart.AddYears(num); + } + tick = num * 365 * 24 * 3600; + list.Clear(); + while (dtStart.Ticks < dtMax.Ticks) + { + list.Add(DateTimeUtil.GetTimestamp(dtStart)); + dtStart = dtStart.AddYears(num); + } + } + else if (range >= ONE_MONTH * MIN_TIME_SPLIT_NUMBER) + { + var num = splitNumber <= 0 ? GetSplitNumber(range, ONE_MONTH) : (int)Math.Max(range / (splitNumber * ONE_MONTH), 1); + var dtStart = GetDateTime(GetFirstMaxValue(list, minTimestamp)); + dtStart = new DateTime(dtStart.Year, dtStart.Month, 1); + while (dtStart > dtMin) + { + dtStart = dtStart.AddMonths(-num); + } + if (dtStart < dtMin) + { + dtStart = dtStart.AddMonths(num); + } + tick = num * 30 * 24 * 3600; + list.Clear(); + while (dtStart.Ticks < dtMax.Ticks) + { + list.Add(DateTimeUtil.GetTimestamp(dtStart)); + dtStart = dtStart.AddMonths(num); + } + } + else + { + int tickSecond; + if (range >= ONE_DAY * MIN_TIME_SPLIT_NUMBER) + { + tickSecond = ONE_DAY; + } + else if (range >= ONE_HOUR * MIN_TIME_SPLIT_NUMBER) + { + tickSecond = ONE_HOUR; + } + else if (range >= ONE_MINUTE * MIN_TIME_SPLIT_NUMBER) + { + tickSecond = ONE_MINUTE; + } + else + { + tickSecond = ONE_SECOND; + } + tick = GetTickSecond(range, splitNumber, tickSecond); + var let = minTimestamp % tickSecond; + var defaultTimestamp = let == 0 ? minTimestamp : minTimestamp - let + tickSecond; + var startTimestamp = (int)GetFirstMaxValue(list, minTimestamp, defaultTimestamp); + while (startTimestamp > minTimestamp) + { + startTimestamp -= tick; + } + if (startTimestamp < minTimestamp) + { + startTimestamp += tick; + } + list.Clear(); + AddTickTimestamp(list, startTimestamp, maxTimestamp, tick); + } + } return tick; }