KoalaPlot / koalaplot-core

Koala Plot is a Compose Multiplatform based charting and plotting library written in Kotlin
https://koalaplot.github.io/
MIT License
317 stars 14 forks source link

Automatic axis scaling for incoming data #63

Open SPC-code opened 1 month ago

SPC-code commented 1 month ago

Currently it is not possible to automatically re-scale axis to match data change in case of dynamic data. It would be nice to have this option in Axis API.

gsteckman commented 1 month ago

I am not sure what you mean, this is already possible. I added a sample that will rescale an axis with changing data.

Is there a more efficient way to do this?

SPC-code commented 1 month ago

Yes, I had something like this in mind. I've implemented automatic data scaling for time axis here: https://git.sciprog.center/kscience/controls-kt/src/branch/dev/controls-visualisation-compose/src/commonMain/kotlin/koalaPlots.kt

SPC-code commented 1 month ago

The possible solution is to pass range as a callback.

gsteckman commented 4 weeks ago

Maybe I misinterpreted your initial ask. I thought it was about getting the plot to rescale the axes when the axis range changes. But this works using the normal Compose way of doing things, e.g. with MutableState. Are you instead asking for algorithms to be implemented to calculate what the min..max range should be based on provided data? In this case I don't see the relevance of a callback function.

I haven't implemented autorange scaling because I haven't found a reliable algorithm to do it. If anyone can point me to one, I don't expect it to be difficult to implement.

SPC-code commented 4 weeks ago

The callback is needed to avoid re-creating axis object on each data update.

Automatic range by itself is quite simple - you just find min and max value in the dataset. In case of your API, it is a bit more complicated because axes are created before the data is known. So, in order to do autorange, you need to pass data to axes builder as well.

One of possible solution is to introduce a concept of DataSet, which includes a number of components (in Plotly they are called Trace. Each component includes data (possibly observable), name and some metadata. Then you pass this dataset to a XYGraph so it automatically knows all ranges it needs and inside LinePlot you just reference datasets by name. As a bonus, you get automatic legend generation. It does not require to change current API, but the work is not so small.

gsteckman commented 4 weeks ago

A callback won't cause recomposition if the scale changes but the data doesn't change. I'd need to be convinced that creating an axis object is a performance bottleneck to move away from an approach that is aligned with the rest of the Compose APIs (I can't think of a Compose API that uses a callback to access data).

Autorange isn't necessarily quite so simple, because people may want to round the range up/down to the nearest "major" interval, like a factor of 10. For example, if your data min/max is -3 and 92, would you want an axis exactly starting and stopping at -3 and 92, or starting at -10 and stopping at 100? This is possible to do, and the FloatLinearAxisModel has some code like this to generate the major ticks based on divisions of powers of 10. An axis builder with a configurable auto ranger would be a good thing for someone to contribute because it doesn't get into the inner workings of the plotting code.

SPC-code commented 4 weeks ago

Re-creating axis should not be that expensive, though it could not be "remembered" is it does change. I think it is more like a question of design. Should there be recomposition of plot frame on data change or not. It seems like both ways are valid.