Charts icon indicating copy to clipboard operation
Charts copied to clipboard

[CombinedChart] Line entries disappears when changing `xAxis.spaceMax` or `xAxis.axisMaximum` with positive values (to fix the cropped bars)

Open dsaliberti opened this issue 3 years ago • 8 comments

What did you do?

While using the CombinedChartView with barData and lineData properly set, I realised the first and last bars cropped and after a few searches it's now clear that it's a known issue and when trying the suggested workarounds, I noticed that the line rendering breaks when changing the xAxis.spaceMax or xAxis.axisMaximum which internally ends up in the same code path.

The video below explains the case:

https://user-images.githubusercontent.com/469209/131644473-1da2f0ec-36da-4a50-a08d-41a81b22ae47.mov

What did you expect to happen?

I expected to fix the fist and last cropped bars issue without breaking the line chart.

What happened instead?

The line rendering breaks and disappears, ending up with just the very first entry.

Charts Environment

master but also tried the branch bugfix/xbounds-iterator with no luck Xcode version: 12.5 Swift version: 5.5 Platform(s) running Charts: iOS macOS version running Xcode: Big Sur 11.5.2

Demo Project

ChartExample.zip

Any help on how to workaround it is very appreciated!

Thank you

dsaliberti avatar Sep 01 '21 10:09 dsaliberti

@liuxuan30 would you have any advice on that? (sorry to mark you)

dsaliberti avatar Sep 02 '21 08:09 dsaliberti

@dsaliberti

I recently ran into similar issue. In the course of investigation the problem I also discovered that setting the axisMaximum/axisMinimum results in the same behaviour.

Debugging led me to a possibly faulty behaviour in the BarLineScatterCandleBubbleRenderer.XBounds set(chart: BarLineScatterCandleBubbleChartDataProvider, dataSet: BarLineScatterCandleBubbleChartDataSetProtocol, animator: Animator?) method:

open func set(chart: BarLineScatterCandleBubbleChartDataProvider,
                      dataSet: BarLineScatterCandleBubbleChartDataSetProtocol,
                      animator: Animator?)
        {
            let phaseX = Swift.max(0.0, Swift.min(1.0, animator?.phaseX ?? 1.0))

            // This seems to be broken !
           // When setting `axisMaximum`/`axisMinimum` and/or `spaceMax`/`spaceMin` these values result in `entryFrom`/`entryTo` being nil
            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)

            self.min = entryFrom == nil ? 0 : dataSet.entryIndex(entry: entryFrom!)
            self.max = entryTo == nil ? 0 : dataSet.entryIndex(entry: entryTo!)
            range = Int(Double(self.max - self.min) * phaseX)
        }

Simply changing the low/high calculation to:

let low: Double = Swift.max(chart.lowestVisibleX, dataSet.xMin)
let high: Double = Swift.min(chart.highestVisibleX, dataSet.xMax)

fixes the issue (at least for me). BUT I did not investigate the fix properly so I can't really tell if it breaks the code somewhere else.

Arestronaut avatar Sep 03 '21 08:09 Arestronaut

@Arestronaut Thank you very much! This worked perfectly! Couldn't see any other problem until now. I'm using bar+line and no problems so far. 👍

dsaliberti avatar Sep 06 '21 09:09 dsaliberti

@dsaliberti Great to hear that :) I’ll open up a PR for the fix soon

Arestronaut avatar Sep 06 '21 09:09 Arestronaut

@dsaliberti Great to hear that :) I’ll open up a PR for the fix soon

yes please!

drewster99 avatar Oct 05 '21 18:10 drewster99

This fix works for me as well let low: Double = Swift.max(chart.lowestVisibleX, dataSet.xMin) let high: Double = Swift.min(chart.highestVisibleX, dataSet.xMax)

drewster99 avatar Oct 05 '21 18:10 drewster99

This is no quite a fix. The problem with xAxis.axisMaximum. If set axisMaximum MORE than real maximum of dataSet - data disappears. If set axisMaximum to real max of dataSet - all is good. p.s.: axisMinimum has no problems

forafontov98 avatar Dec 09 '21 18:12 forafontov98

I have trouble when draw group of datasources )

class XBounds`
func set(chart: BarLineScatterCandleBubbleChartDataProvider,
                      dataSet: BarLineScatterCandleBubbleChartDataSetProtocol,
                      animator: Animator?)
entryFrom = dataSet.entryForXValue(low, closestToY: .nan, rounding: .down) // - work perfect (set low in dataSet)
entryTo = dataSet.entryForXValue(high, closestToY: .nan, rounding: .up) //  - can't find upper in passed data set

steps:

let high = chart.highestVisibleX // (set last item in chart by X)
dataSet.entryForXValue(high, closestToY: .nan, rounding: .up) // (take last item in chart by X, and .nan value as closesToY)
let index = entryIndex(x: xValue, closestToY: yValue, rounding: rounding) // (pass high as xValue, pass .nan as yValue)
// closest set to 0 by partitioningIndex { $0.x >= xValue }. As closest less than endIndex we move next step
closest > startIndex // false - and we move next step (without calculation)
switch rounding // .up - and we select branch for down where closest < startIndex... and next finish line :)
if !yValue.isNaN // false - and we move out, without searching :)

but upper value in this range the last item in dataSource, because I wait draw path, that described in dataSource ))

iDevPro avatar Apr 03 '22 13:04 iDevPro