implot
implot copied to clipboard
Backends branch: log scale does not render properly
Log axis scale does not render properly using the OpenGL backend:
With Y axis log scale on:
With X axis log scale on:
With both of them on:
I see nothing wrong in the image. I honestly think this is how it should look like.
As a side note though, please note that the leftmost column of pixels represent X=0, and that point is a singularity in log scale: in logarithmic scale, there's an infinite distance between 0 and any other non-zero, finite point. This will usually result in extremelly stretched-out plots like the first two images. But that's not a bug, that is how logarithmic scale works :)
ImPlot corrects for when the lower bound is 0, it also still renders that way with higher values for it. And comparing with the master branch makes it very clear that this render is not intended behaviour.
On Wed, 6 Apr 2022, 08:33 Marc, @.***> wrote:
I see nothing wrong in the image. I honestly think this is how it should look like.
As a side note though, please note that the leftmost column of pixels represent X=0, and that point is a singularity in log scale: in logarithmic scale, there's an infinite distance between 0 and any other non-zero, finite point. This will usually result in extremelly stretched-out plots like the first two images. But that's not a bug, that is how logarithmic scale works :)
— Reply to this email directly, view it on GitHub https://github.com/epezent/implot/issues/349#issuecomment-1089874936, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABSS3MIZZLXTCN753F22MDTVDUV4VANCNFSM5SUXYSAQ . You are receiving this because you authored the thread.Message ID: @.***>
I disagree. This correction for X=0 is done on the CPU in the master
branch and on the GPU in the backends
flag. Both branches differ because the implementations differ, and as I explained, you simply cannot plot X=0. Including 0 in the scale simply makes the plot result undefined. And because it is undefined, both versions might render differently, and that does not mean that this is unintended behaviour. It simply means that you're pushing a feature off-bounds and implementation details start to show up.
This issue was already discussed and clarified (with the same exact example program) when I implemented this feature. Quote:
The LogY does this because the heatmp starts at 0 and it basically stretches it to -inf. Setting the minimum Y position to something like 0.001 does the trick and works fine. The older implementation didn't have this issue since it rendered a bunch of small quads from the center point, and thus when it rendered the bottom one, the Y coordinate (or X for that matter) was always < 0.0 and was treated accordingly. In the GPU implementation this doesn't happen since I'm rendering the entire quad at once and so the point (X,0) goes to infinity. I think I can patch that one easily so that both heatmaps work the same and there's no need to change anything.
So simply replacing the 0 to something like 0.001 will do the trick.
Huh... I might not be running into the same issue then, because I'm still getting the same output if I set it to like X=20
On Wed, 6 Apr 2022, 13:56 Marc, @.***> wrote:
I disagree. This correction for X=0 is done on the CPU in the master branch and on the GPU in the backends flag. Both branches differ because the implementations differ, and as I explained, you simply cannot plot X=0. Including 0 in the scale simply makes the plot result undefined. And because it is undefined, both versions might render differently, and that does not mean that this is unintended behaviour. It simply means that you're pushing a feature off-bounds and implementation details start to show up.
This issue was already discussed https://github.com/epezent/implot/pull/254#issuecomment-868156329 and clarified https://github.com/epezent/implot/pull/254#issuecomment-868317828 (with the same exact example program) when I implemented this feature. Quote:
The LogY does this because the heatmp starts at 0 and it basically stretches it to -inf. Setting the minimum Y position to something like 0.001 does the trick and works fine. The older implementation didn't have this issue since it rendered a bunch of small quads from the center point, and thus when it rendered the bottom one, the Y coordinate (or X for that matter) was always < 0.0 and was treated accordingly. In the GPU implementation this doesn't happen since I'm rendering the entire quad at once and so the point (X,0) goes to infinity. I think I can patch that one easily so that both heatmaps work the same and there's no need to change anything.
So simply replacing the 0 to something like 0.001 will do the trick.
— Reply to this email directly, view it on GitHub https://github.com/epezent/implot/issues/349#issuecomment-1090182855, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABSS3MJGFAGQ6WTVXKISTJDVDV3WRANCNFSM5SUXYSAQ . You are receiving this because you authored the thread.Message ID: @.***>
Okay I got it to display something by inverting x to 1-x in the fragment shader. I found it out completely by accident when I set xmin to something extremely low like 1e-10 and it started displaying things. The scale is still wrong but at least it's displaying something...
The issue is not present for horizontal axes, so I think it's related to the fact that vertical texture coords start from 1 at the top
The issue is not present for horizontal axes, so I think it's related to the fact that vertical texture coords start from 1 at the top
Yeah, I'm also testing it and I found out the same thing. 100% agree, there seems to be a bug related to the Y axis.
I can't find where the bug is. Inverting the input value into the fragment shader does seem to fix the issue, but inverting the UV coordinates (which should be the same) does not. Furthermore, any other sample application does work as expected... I'm starting to think there is something wrong with this example.
I managed to fix it by completely redoing the way log scale transforms are done. It's slightly more costly on the CPU side but it also allows to use other scales. I did this on my fork where I added support for alternate frequency scales but I'll make a PR upstream at some point
But more or less, in ImPlotAxis::UpdateTransformCache()
:
LogM = IsLog() ? ImLog10(Range.Min) : 0;
LogR = IsLog() ? ImLog10(Range.Max) - LogM : 0;
In PixelsToPlot()
and PlotToPixels()
:
if (IsLog()) {
double t = (plt - Range.Min) / Range.Size();
plt = ImPow(10, t * LogR + LogM);
}
if (IsLog()) {
plt = plt <= 0.0 ? IMPLOT_LOG_ZERO : plt;
double t = (ImLog10(plt) - LogM) / LogR;
plt = ImLerp(Range.Min, Range.Max, (float)t);
}
And similar in TransformerLog, and it renders as expected.
It uncovered another bug though, where the very last row of the heatmap isn't rendered properly:
Wow, awesome! So now you basically do the transformation on the CPU side and then you render the heatmap on the GPU without applying any transformation? Is that correct? I'm just curious, as this seems like a really good solution. And according to the framerate shown on the images, it looks like the performance is unchanged. Good job! 😄
It uncovered another bug though, where the very last row of the heatmap isn't rendered properly
This looks like it is wrapping around (which it should, since it is an OpenGL texture after all...). I might take a look at it in the coming days and open a PR if I find out what's wrong. Thanks for reporting!
So now you basically do the transformation on the CPU side and then you render the heatmap on the GPU without applying any transformation? Is that correct?
Nah, it's pretty much the same process as before, transform from linear on [min,max] to log on the CPU side, transform from log to linear on [0,1] on the GPU side.
The "only" thing I changed was the way that the CPU-side log transform was being done. The quotient method seemed to have too many tricky edge cases.
You can look at the code on the fork if you want, though it does also have extra code to include other scales than log.
The general formula that I used is that for any scale transformation function Tr, the forward transform from [min,max] to [0,1] is (Tr(x) - Tr(min)) / (Tr(max) - Tr(min))
and the inverse transform from [0,1] to [min,max] is InvTr(x * (Tr(max) - Tr(min)) + Tr(min))
The "only" thing I changed was the way that the CPU-side log transform was being done. The quotient method seemed to have too many tricky edge cases.
Oh! That explains why I didn't find the bug in the shader 😂 Thanks for sharing the fork though, will definitely take a look at it :)
Could this log scaling be extended to include negative values? (as described in J Beau W Webber 2013 Meas. Sci. Technol. 24 027001 https://doi.org/10.1088/0957-0233/24/2/027001)