Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zooming LineChart x axis on 32bit processor doesn't work as expected #1679

Closed
Fahrenheit-sp opened this issue Oct 19, 2016 · 10 comments
Closed

Comments

@Fahrenheit-sp
Copy link

Fahrenheit-sp commented Oct 19, 2016

I'm using iOS-Charts in my project. There are date values on xAxis in UNIX timestamp formatted to show hours and minutes .

Here is my ValueFormatter Code

@objc(ChartFormatter)
public class ChartFormatter: NSObject, IAxisValueFormatter{

public func stringForValue(_ value: Double, axis: AxisBase?) -> String{
    let dateFormatter = DateFormatter()
    dateFormatter.timeZone = TimeZone(abbreviation: "GMT")
    dateFormatter.dateFormat = "HH:mm"
    let date = Date(timeIntervalSince1970: TimeInterval(value))
    let xValue = dateFormatter.string(from: date)
    return xValue
}
}

When I try to zoom on 64bit processor devices - everything works just well, but when I try to zoom on 32bit device - I get such kind of result 32bit device zooming Labels are intersected, and I can't zoom more.

I tried to use granularity, forceLabelsEnabled and setLabelCount on xAxis - but nothing helps.

Thanks for any help. And thanks for such awesome library! You saved me so many times :)

@Fahrenheit-sp
Copy link
Author

I've figured out, that this may happen cause of CGFloat is float on 32bit devices, and double on 64bit. Is it possible to somehow force CGFloat to be double even on 32bit device?

@liuxuan30
Copy link
Member

It should not matter whether it's double or float with your data, though I don't fully understand why you say it.

@liuxuan30
Copy link
Member

reopen if you give more information and details.

@Fahrenheit-sp
Copy link
Author

I've placed print("e1.x: \(e1.x), e1.y: \(e1.y * phaseY)") in line 404 in LineChartRenderer, right in front of applying transform to entry point. And to print the actual point on chart after applying transform print("point: \(pt)") in line 409.
That's a part of logs what I got on 32bit device:
e1.x: 1477047261.0, e1.y: 18063.0
point: (251.571, 184.381)
e1.x: 1477047293.0, e1.y: 18058.0
point: (251.571, 239.22)

As you can see, the xValues of two points are different, but their x coordinates are the same, which is obviously not correct. And some logs for 64bit device:
e1.x: 1477047638.0, e1.y: 1.08811
point: (259.413942337036, 236.467363886797)
e1.x: 1477047639.0, e1.y: 1.08809
point: (259.638811230659, 239.2275390625)

Here you can see, that xValue of the first point is only 1 lower, than the second one, and their x coordinates on chart are different.

And speaking about labels positions. I've put print("position before applying: \(position)") in line 255 in XAxisRenderer. Right between
position.y = 0.0 position = position.applying(valueToPixelMatrix)

and print("position after applying: \(position)") in line 255, right before drawLabel calling.
Here are the logs for 32bit device:
position before applying: (1.47705e+09, 0.0)
position after applying: (128.611, 221622.0)
position before applying: (1.47705e+09, 0.0)
position after applying: (167.408, 221622.0)
position before applying: (1.47705e+09, 0.0)
position after applying: (167.408, 221622.0)
position before applying: (1.47705e+09, 0.0)
position after applying: (206.206, 221622.0)

As you can see - position for two labels is the same. Here is a screenshot.
screenshot32bit
Here is my properties for chartView:
` chartView.delegate = self

    chartView.chartDescription?.text = ""
    chartView.legend.enabled = false
    chartView.leftAxis.enabled = false        
    chartView.xAxis.labelPosition = .bottom
    chartView.xAxis.labelTextColor = UIColor.white       
    chartView.highlightPerTapEnabled = false
    chartView.highlightPerDragEnabled = false        
    chartView.rightAxis.labelTextColor = UIColor.white`

and properties for dataSet:
` set = LineChartDataSet(values: dataEntries, label: quote)
set.fillAlpha = 0.3
set.fill = Fill.fillWithLinearGradient(gradient!, angle: 90.0)
set.drawFilledEnabled = true

    //disable values and circles
    set.drawCirclesEnabled = false
    set.drawCircleHoleEnabled = false
    set.drawValuesEnabled = false
    set.lineWidth = 2.0
    set.setColor(UIColor.white)
    set.highlightColor = UIColor.white`

Nothing unusual. Thanks again for any help, please tell me if some more information needed.

@danielgindi
Copy link
Collaborator

@Fahrenheit-sp a few points:

  1. CGFloat is the type used for CoreGraphics, which is the rendering engine. Calculations in Charts are done on Doubles, but we can't help but use CGFloat for rendering.
  2. It shouldn't really matter - as you will need a VERY deep zoom to hit the limit of a 32-bit processor. It's unlikely that a user will use such a zoom. If someone will - it will only be to test how far you'll allow him before the app crashes.
  3. In the rare cases that your values are very high / very low / very extremely fractioned to begin with - maybe you can think about normalizing them, to allow a 32-bit processor to cope.
  4. I am considering moving to Skia as the rendering engine, to eliminate other problems - I think that it is float-based too - but it is open source so we can change that. Maybe we'll take a subset of their code if the license allows.
  5. Who in the goddamn hell still uses 32-bit processors? iPhone 5 is the next iPhone 4... Will disappear soon. On Android it's more problematic due to the many many devices available, but companies like Samsung were faster to bring in the newer hardware like 64bit processors.

@danielgindi danielgindi reopened this Oct 22, 2016
@liuxuan30
Copy link
Member

liuxuan30 commented Oct 26, 2016

position before applying: (1.47705e+09, 0.0)
position after applying: (128.611, 221622.0)
position before applying: (1.47705e+09, 0.0)
position after applying: (167.408, 221622.0)
position before applying: (1.47705e+09, 0.0)
position after applying: (167.408, 221622.0)
position before applying: (1.47705e+09, 0.0)
position after applying: (206.206, 221622.0)

Is this the value where the overlap happens in your screenshot? If so, it's strange - the value is less than float max, and should work, also, the x values are all 1.47705e+09, but they are converted to different values. I wonder there would be a mistake.

Can you try debug into the converting, to print out valueToPixelMatrix and see if it changes for each convert?

@Fahrenheit-sp
Copy link
Author

Fahrenheit-sp commented Oct 26, 2016

@liuxuan30 seems like you're right. So prints in lines 409/410 in LineChartRenderer (and in line 404 to see initial e1.x and e1.y). (both logs taken without any zooming. So seems like problem is not just in zooming.)
Here is logs from 64bit device:

e1.x: 1477481340.0, e1.y: 1.02663004398346
pt: (205.683577865362, 214.413730418863)
valueToPixelmatrix: CGAffineTransform(a: 0.108713098810008, b: 0.0, c: -0.0, d:     -248096.917399965, tx: -160621369.221785, ty: 254918.162952906)
e1.x: 1477481390.0, e1.y: 1.02667
pt: (211.119232803583, 204.500765883568)
valueToPixelmatrix: CGAffineTransform(a: 0.108713098810008, b: 0.0, c: -0.0, d: -248096.917399965, tx: -160621369.221785, ty: 254918.162952906)
e1.x: 1477481393.0, e1.y: 1.02665
pt: (211.445372104645, 209.462704231555)
valueToPixelmatrix: CGAffineTransform(a: 0.108713098810008, b: 0.0, c: -0.0, d: -248096.917399965, tx: -160621369.221785, ty: 254918.162952906)

And from the 32bit:

e1.x: 1477481340.0, e1.y: 1.09144997596741
pt: (201.842, 205.805)
valueToPixelmatrix: CGAffineTransform(a: 0.110597, b: 0.0, c: -0.0, d: -257299.0, tx: -1.63405e+08, ty: 281035.0)
e1.x: 1477481400.0, e1.y: 1.09142005443573
pt: (201.842, 213.504)
valueToPixelmatrix: CGAffineTransform(a: 0.110597, b: 0.0, c: -0.0, d: -257299.0, tx: -1.63405e+08, ty: 281035.0)
e1.x: 1477481460.0, e1.y: 1.09139001369476
pt: (215.998, 221.233)
valueToPixelmatrix: CGAffineTransform(a: 0.110597, b: 0.0, c: -0.0, d: -257299.0, tx: -1.63405e+08, ty: 281035.0)

@liuxuan30
Copy link
Member

liuxuan30 commented Oct 27, 2016

it's subtle then. You see on 64bit, matrix values are more precise than 32 bit; that's the problem I guess. With these matrix values, the results could be different. But not that far. Your original values are closer, and the screen dots will be much closer for sure.

It might an issue with CoreGraphics or ours, but looks like it can't be solved in a short time. I never met this before, and I suggest don't spend time on something you couldn't change easily.

Also, double check your screenshot - your x axis label is 10:55, 10:56, 10:58(?), then 11:01, 11:03, 11:05. The interval is not equal, which means your had problems with x axis labels. Have you changed x axis code? Normally, x axis will calculate the labels, it shouldn't calculate something very close or not equal interval. If you think there is a bug, please help using ChartsDemo to reproduce, so we could have a look and fix it.

@Fahrenheit-sp
Copy link
Author

Fahrenheit-sp commented Oct 27, 2016

@liuxuan30 actually, resolved it a little bit by subtracting starting date in unix timestamp format from the current one, so actual values on axis is near 100.000 and then adding it back, when formatting TimeInterval to date, and then date to string. I know, that not great solution, bit it obviously better than no solution at all.
(well, the coordinates on 32bit are not as precise, as on 64bit, but it doesn't matter - looks good anyway).

@liuxuan30
Copy link
Member

great :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants