From cfd7e8e208457ee4cbcb84be0b4873fd91152674 Mon Sep 17 00:00:00 2001 From: fumoboy007 Date: Sat, 11 Jun 2022 18:41:44 -0700 Subject: [PATCH] Fix bug in `XBounds` calculation of minimum/maximum visible entry index. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `XBounds` looks for data entries on the boundary of or outside of the visible range. However, there may not be any such entries for a particular data set. Imagine a chart with two data sets, one of which has a bigger range of x-values than the other. The chart’s zoomed-out visible range would be the range of the wider data set. When the renderer tries to render the narrower data set, it would fail to correctly calculate the starting and ending entry indices because the starting/ending entries are strictly inside of the visible range. The fix is to add fallback logic to look for data entries inside of the visible range if the initial algorithm does not find anything. The `XBounds` bug fix also requires a bug fix in `ChartDataSet`’s binary search algorithm. --- .../Data/Implementations/Standard/ChartDataSet.swift | 2 +- .../BarLineScatterCandleBubbleRenderer.swift | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Source/Charts/Data/Implementations/Standard/ChartDataSet.swift b/Source/Charts/Data/Implementations/Standard/ChartDataSet.swift index 70d2da5582..8deb9cee6f 100644 --- a/Source/Charts/Data/Implementations/Standard/ChartDataSet.swift +++ b/Source/Charts/Data/Implementations/Standard/ChartDataSet.swift @@ -217,7 +217,7 @@ open class ChartDataSet: ChartBaseDataSet rounding: ChartDataSetRounding) -> Int { var closest = partitioningIndex { $0.x >= xValue } - guard closest < endIndex else { return rounding == .closest ? (endIndex-1) : -1 } + guard closest < endIndex else { return [.down, .closest].contains(rounding) ? (endIndex-1) : -1 } var closestXValue = self[closest].x diff --git a/Source/Charts/Renderers/BarLineScatterCandleBubbleRenderer.swift b/Source/Charts/Renderers/BarLineScatterCandleBubbleRenderer.swift index 2f4751d1aa..4862f1c2a8 100644 --- a/Source/Charts/Renderers/BarLineScatterCandleBubbleRenderer.swift +++ b/Source/Charts/Renderers/BarLineScatterCandleBubbleRenderer.swift @@ -102,9 +102,15 @@ open class BarLineScatterCandleBubbleRenderer: NSObject, DataRenderer let low = chart.lowestVisibleX let high = chart.highestVisibleX - - let entryFrom = dataSet.entryForXValue(low, closestToY: .nan, rounding: .down) - let entryTo = dataSet.entryForXValue(high, closestToY: .nan, rounding: .up) + + // First, try to find entries on the boundary of or outside of the visible range. Then, if there are none, try to find entries + // inside of the visible range. + // + // We want to allow and prioritize entries outside of the visible range because renderers may draw graphics in between entries. + // For example, a zoomed-in line graph should still show a line connecting the entry outside of the visible range with the entry + // inside of the visible range even if the line is only partially shown. + let entryFrom = dataSet.entryForXValue(low, closestToY: .nan, rounding: .down) ?? dataSet.entryForXValue(low, closestToY: .nan, rounding: .up) + let entryTo = dataSet.entryForXValue(high, closestToY: .nan, rounding: .up) ?? dataSet.entryForXValue(high, closestToY: .nan, rounding: .down) self.min = entryFrom == nil ? 0 : dataSet.entryIndex(entry: entryFrom!) self.max = entryTo == nil ? 0 : dataSet.entryIndex(entry: entryTo!)