ChartsOrg / Charts

Beautiful charts for iOS/tvOS/OSX! The Apple side of the crossplatform MPAndroidChart.
Apache License 2.0
27.52k stars 5.99k forks source link

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

Open dsaliberti opened 3 years ago

dsaliberti commented 3 years ago

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 commented 3 years ago

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

Arestronaut commented 3 years ago

@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.

dsaliberti commented 3 years ago

@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. 👍

Arestronaut commented 3 years ago

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

drewster99 commented 2 years ago

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

yes please!

drewster99 commented 2 years ago

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)

forafontov98 commented 2 years ago

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

iDevPro commented 2 years ago

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 ))