Fix LFP Viewer assertion when channels hang off the channel bitmap - #707
Conversation
Since the channel bitmap became a viewport-sized window, pxPaint() and pxPaintHistory() measure each channel from channelBitmapYOrigin rather than from the top of the whole channel stack. The two clamps that bound the channel span were left as they were, and each of them only moves one end of that span: jfrom is raised to 0 and jto is lowered to the last row of the bitmap. A channel sitting above or below the bitmap window therefore ends up with jto < jfrom. LfpDisplay::refresh paints every channel that overlaps the bitmap window at all, so the channels straddling its top and bottom edges are painted in exactly that state on every refresh. The yellow playhead fillRect and drawEventOverlay then receive a negative height, which trips the jassertquiet in juce_GraphicsContext.cpp and floods the console of a Debug build. A negative-height rectangle is already a no-op in JUCE, so skipping the draw when the clipped span is empty leaves rendering unchanged and only removes the assertion. Also corrects the LfpDisplay doc comment, which still described the bitmap as spanning the sum of all channel heights. Adds a regression test that scrolls a tall channel stack past the viewport and asserts that no JUCE assertion reaches stderr. Fixes open-ephys#705
|
Hi @adityasingh2400, Thanks for putting together this PR! I managed to replicate the #705 issue reported by @ckemere after trying to adjust the channel height while the LFP Viewer was busy plotting. Initially, I was only testing with 16 channel data, which perfectly fit within the entire bitmap in my setup, so I couldn’t reproduce it. However, after increasing the channel height, it hit the edge case you mentioned when plotting the event overlay rectangle with a negative height. The fix and test seem reasonable (and thanks for disclosing the use of AI!). Once all the checks are passed, I’ll go ahead and merge it. Since |
|
Thanks for taking the time to actually reproduce it, and for the release reasoning. Increasing the channel height is exactly the shortcut I ended up using too, since it is the quickest way to push a channel span past the bitmap edge without needing a large probe. Your read on the impact matches mine. Checks are at 4 of 5 green right now, with Integration Tests still running and nothing failing. I will keep an eye on it and sort out anything that comes back red. |
|
Following up on my note above, all five checks came back green: build-ubuntu, build-windows, build-osx, Unit Tests, and Integration Tests. The branch is mergeable with no conflicts, so nothing is outstanding on my side whenever you are ready. |
|
Just gave it a final local test, and it's looking good. Thanks again for the PR! |
|
Thanks all! |
Fixes #705.
Since commit
afe1d71made the channel bitmap a viewport-sized window,pxPaintandpxPaintHistorymeasure each channel fromchannelBitmapYOriginrather than from the top of the whole channel stack. The two clamps that bound the channel span were left as they were, and each of them only moves one end of that span:jfromis raised to 0 andjtois lowered to the last row of the bitmap. A channel sitting above or below the bitmap window therefore ends up withjto < jfrom.LfpDisplay::refreshpaints every channel that overlaps the bitmap window at all, so the channels straddling its top and bottom edges are painted in exactly that state on every refresh. The yellow playheadfillRectanddrawEventOverlaythen receive a negative height, which trips thejassertquietinjuce_GraphicsContext.cpp:94and floods the console of a Debug build. That matches the reporter's observation that it prints a few times when the chain is instantiated and then continuously during streaming.Component height is
channelHeight + channelOverlap * overlapFactorwithchannelOverlap = channelHeight * 2, so the affected band at the top edge is roughly a full channel wide, which is why it is easy to hit.A negative-height rectangle is already a no-op in JUCE, since
Rectangle::isEmpty()covers it, so skipping the draw when the clipped span is empty leaves rendering unchanged and only removes the assertion. The fix is three guards totalling 18 lines, skipping the playheadfillRectin both paint paths and early-returning fromdrawEventOverlay.I also corrected the
LfpDisplaydoc comment, which still described the bitmap as spanning the sum of all channel heights. That stopped being true withafe1d71and now contradicts the code.Added a regression test that scrolls a 16 channel stack at 120 px per channel past a 400 px viewport and captures stderr, where
Logger::outputDebugStringsends the assertion. Ondevelopmentit fails with eight assertions readingJUCE Assertion failure in juce_GraphicsContext.cpp:94, which is character for character the message in the report. With this change the full LfpViewer suite is green at 9 of 9 on a Debug macOS build, with zero JUCE assertions anywhere in the run.git clang-format --diffreports no modifications.Disclosure: this change was prepared with AI assistance. I have reviewed and tested it.