|
| 1 | +#include <vector> |
1 | 2 | #include "test-paintbuffer.h" |
| 3 | +#include <painting/viewport-offset.h> |
2 | 4 |
|
3 | 5 | void TestPaintBuffer::init() |
4 | 6 | { |
@@ -388,3 +390,110 @@ void TestPaintBuffer::skipRepaint_bufferNotReuploadedOnPan() |
388 | 390 | // because draw() was NOT called (mRenderedRange not updated) |
389 | 391 | QVERIFY(!mainLayer->pixelOffset().isNull()); |
390 | 392 | } |
| 393 | + |
| 394 | +void TestPaintBuffer::colormap2_panDirtiesLayerBuffer() |
| 395 | +{ |
| 396 | + // A ViewportDependent colormap resamples asynchronously on pan. It must |
| 397 | + // still dirty its layer on every viewport change so the existing image |
| 398 | + // redraws repositioned to the current axes — otherwise the spectrogram |
| 399 | + // freezes in place while the axes scroll until the resample lands. |
| 400 | + auto* cm = new QCPColorMap2(mPlot->xAxis, mPlot->yAxis); |
| 401 | + mPlot->xAxis->setRange(0, 10); |
| 402 | + mPlot->yAxis->setRange(0, 100); |
| 403 | + std::vector<double> x{0, 5, 10}, y{0, 50, 100}; |
| 404 | + std::vector<double> z(x.size() * y.size(), 1.0); |
| 405 | + cm->setData(std::move(x), std::move(y), std::move(z)); |
| 406 | + |
| 407 | + mPlot->replot(QCustomPlot::rpImmediateRefresh); |
| 408 | + |
| 409 | + auto buf = cm->layer()->mPaintBuffer.toStrongRef(); |
| 410 | + QVERIFY(buf); |
| 411 | + buf->setContentDirty(false); // clean slate (ignore any queued async redraw) |
| 412 | + |
| 413 | + mPlot->xAxis->setRange(2, 12); // pure pan |
| 414 | + QVERIFY2(buf->contentDirty(), |
| 415 | + "colormap2 pan did not dirty its layer — spectrogram frozen during drag"); |
| 416 | +} |
| 417 | + |
| 418 | +void TestPaintBuffer::colormap2_panDirtiesLayerBufferLogY() |
| 419 | +{ |
| 420 | + // Same, with a logarithmic value axis (energy spectrogram): a pure pan in |
| 421 | + // log space must still dirty the layer (regression for the reported stuck |
| 422 | + // log-Y spectrogram). |
| 423 | + auto* cm = new QCPColorMap2(mPlot->xAxis, mPlot->yAxis); |
| 424 | + mPlot->xAxis->setRange(0, 10); |
| 425 | + mPlot->yAxis->setScaleType(QCPAxis::stLogarithmic); |
| 426 | + mPlot->yAxis->setRange(1, 1000); |
| 427 | + std::vector<double> x{0, 5, 10}, y{1, 10, 100, 1000}; |
| 428 | + std::vector<double> z(x.size() * y.size(), 1.0); |
| 429 | + cm->setData(std::move(x), std::move(y), std::move(z)); |
| 430 | + |
| 431 | + mPlot->replot(QCustomPlot::rpImmediateRefresh); |
| 432 | + |
| 433 | + auto buf = cm->layer()->mPaintBuffer.toStrongRef(); |
| 434 | + QVERIFY(buf); |
| 435 | + buf->setContentDirty(false); |
| 436 | + |
| 437 | + mPlot->yAxis->setRange(2, 2000); // pure pan in log space (×2) |
| 438 | + QVERIFY2(buf->contentDirty(), |
| 439 | + "colormap2 log-Y pan did not dirty its layer — spectrogram frozen during drag"); |
| 440 | +} |
| 441 | + |
| 442 | +void TestPaintBuffer::colormap2_stallOffsetOnPan() |
| 443 | +{ |
| 444 | + // Skip-on-translate: after a pan the colormap reports a pixel offset so the |
| 445 | + // compositor shifts the existing texture instead of repainting+re-uploading. |
| 446 | + auto* cm = new QCPColorMap2(mPlot->xAxis, mPlot->yAxis); |
| 447 | + mPlot->xAxis->setRange(0, 10); |
| 448 | + mPlot->yAxis->setRange(0, 100); |
| 449 | + std::vector<double> x{0, 5, 10}, y{0, 50, 100}; |
| 450 | + std::vector<double> z(x.size() * y.size(), 1.0); |
| 451 | + cm->setData(std::move(x), std::move(y), std::move(z)); |
| 452 | + (void)mPlot->toPixmap(400, 300); // synchronous draw establishes the rendered range |
| 453 | + QVERIFY(cm->hasRenderedRange()); |
| 454 | + QCOMPARE(cm->stallPixelOffset(), QPointF(0, 0)); // no pan yet |
| 455 | + |
| 456 | + const QCPRange oldKey(0, 10), oldVal(0, 100); |
| 457 | + mPlot->xAxis->setRange(2, 12); // pure pan |
| 458 | + const QPointF expected = |
| 459 | + qcp::computeViewportOffset(mPlot->xAxis, mPlot->yAxis, oldKey, oldVal); |
| 460 | + QVERIFY(!expected.isNull()); |
| 461 | + QCOMPARE(cm->stallPixelOffset(), expected); |
| 462 | + QCOMPARE(mPlot->layer("main")->pixelOffset(), expected); // layer aggregates it |
| 463 | +} |
| 464 | + |
| 465 | +void TestPaintBuffer::colormap2_stallOffsetOnLogYPan() |
| 466 | +{ |
| 467 | + // A pure pan on a log Y axis must still translate (regression for the stuck |
| 468 | + // log-Y energy spectrogram): the zoom check is scale-aware. |
| 469 | + auto* cm = new QCPColorMap2(mPlot->xAxis, mPlot->yAxis); |
| 470 | + mPlot->xAxis->setRange(0, 10); |
| 471 | + mPlot->yAxis->setScaleType(QCPAxis::stLogarithmic); |
| 472 | + mPlot->yAxis->setRange(1, 1000); |
| 473 | + std::vector<double> x{0, 5, 10}, y{1, 10, 100, 1000}; |
| 474 | + std::vector<double> z(x.size() * y.size(), 1.0); |
| 475 | + cm->setData(std::move(x), std::move(y), std::move(z)); |
| 476 | + (void)mPlot->toPixmap(400, 300); |
| 477 | + QVERIFY(cm->hasRenderedRange()); |
| 478 | + |
| 479 | + mPlot->yAxis->setRange(2, 2000); // pure pan in log space (x2) |
| 480 | + QVERIFY2(!cm->stallPixelOffset().isNull(), |
| 481 | + "log-Y pan misread as zoom — colormap would repaint+reupload every frame"); |
| 482 | +} |
| 483 | + |
| 484 | +void TestPaintBuffer::colormap2_stallOffsetNullOnZoom() |
| 485 | +{ |
| 486 | + // A genuine zoom must NOT translate (texture would be wrongly scaled); fall |
| 487 | + // back to a real repaint. |
| 488 | + auto* cm = new QCPColorMap2(mPlot->xAxis, mPlot->yAxis); |
| 489 | + mPlot->xAxis->setRange(0, 10); |
| 490 | + mPlot->yAxis->setRange(0, 100); |
| 491 | + std::vector<double> x{0, 5, 10}, y{0, 50, 100}; |
| 492 | + std::vector<double> z(x.size() * y.size(), 1.0); |
| 493 | + cm->setData(std::move(x), std::move(y), std::move(z)); |
| 494 | + (void)mPlot->toPixmap(400, 300); |
| 495 | + QVERIFY(cm->hasRenderedRange()); |
| 496 | + |
| 497 | + mPlot->xAxis->setRange(0, 20); // zoom out 2x |
| 498 | + QVERIFY(cm->stallPixelOffset().isNull()); |
| 499 | +} |
0 commit comments