epezent / implot

Immediate Mode Plotting
MIT License
4.64k stars 516 forks source link

Lock zero/origin of multiple Y axes #416

Open bzroom opened 1 year ago

bzroom commented 1 year ago

I'm using AutoFit on multiple Y axes. This generates different 0 positions for each axis. image

Is it possible to keep AutoFit + RangeFit and get their zeros to stay aligned?

If not, is there some workaround to get autofit + aligned zeros?

epezent commented 1 year ago

There isn't a built in way to do this. Off the top of my head, a contrived way would be to submit some invisible dummy data to each axis so that when the plot is double clicked, all the 0s line up. If you know what expected range of each axis will be, you can hard code the dummy points. Otherwise you could something more sophisticated by looking at the min/max of data on each axis, and adding dummy data to two of the three axes so that their new min/max are proportionally equidistant from 0 to the other axis.

BenBE commented 1 year ago

Couldn't you, based on the auto-fit, let the plot determine min/max ranges for each axis and the best scaling for each one? After that you need to ensure that the minimum and maximum are the same distance when calculating value / scaling, setting each axis to have the same (scaled) value for their minimum and maximum points of the display range.

So in the above example: A1: -5/5/1 A2: -7/5/1 A3: -0.4/0.8/0.1

Start with A1 as the reference: Min = -5/1, Max = 5/1

For A2 you get -7/1 .. 5/1, as that's out of the range for A1, update the target range to -7 .. 5.

For A3 you get -0.4/0.1=4 .. 0.8/0.1=8, thus you update the max value to get the target range -7..8.

Now you work backwards: A1: -7 1 -> -7 … 8 1 -> 8 A2: -7 1 -> -7 … 8 1 -> 8 A3: -7 0.1 = -0.7 … 8 0.1 -> 0.8

Now set those as the new axis ranges and things should be aligned.

Instead of the idealised scaling you could even use the units/pixel of each axis, which would even allow for different (non-rounded) scaling for each axis.

HTH.

bzroom commented 1 year ago

Thank you both for the suggestions!

I 50% understood Ben's suggestion and implemented something lazier because I can currently accept the 0 being in the center:

// adjust plot axes before endplot, to keep zeros aligned
ImPlotPlot& plot = *GImPlot->CurrentPlot;
for (int s = ImAxis_Y1; s <= scales.scaleId; ++s)
{
    ImPlotAxis& axis = plot.Axes[s];
    auto v = fabs(axis.FitExtents.Min);
    auto v2 = fabs(axis.FitExtents.Max);
    if (v2 > v) {
        v = v2;
    }
    axis.FitExtents.Min = -v;
    axis.FitExtents.Max = v;
}

ImPlot::EndPlot();

image

This should do for me for now. It's easy enough to play around with and post process the extents before end plot that I should be able to get what I need out of this without anything changed. Feel free to close this if you want, unless you want to make it an official feature.

Thank you for the great lib.