@@ -544,6 +544,7 @@ void SimulationCanvasWidget::setDensityOverlay(
544544 std::isfinite (scaleMaxPeoplePerSquareMeter) && scaleMaxPeoplePerSquareMeter > 0.0
545545 ? scaleMaxPeoplePerSquareMeter
546546 : kDefaultDensityScaleMaxPeoplePerSquareMeter ;
547+ invalidateOverlayCache ();
547548 update ();
548549}
549550
@@ -555,6 +556,7 @@ void SimulationCanvasWidget::setPressureOverlay(
555556 std::isfinite (scaleMaxPressureScore) && scaleMaxPressureScore > 0.0
556557 ? scaleMaxPressureScore
557558 : kDefaultPressureScaleMaxScore ;
559+ invalidateOverlayCache ();
558560 update ();
559561}
560562
@@ -563,6 +565,7 @@ void SimulationCanvasWidget::setHotspotOverlay(std::vector<safecrowd::domain::Sc
563565 if (focusedHotspotIndex_.has_value () && *focusedHotspotIndex_ >= hotspotOverlay_.size ()) {
564566 focusedHotspotIndex_.reset ();
565567 }
568+ invalidateOverlayCache ();
566569 update ();
567570}
568571
@@ -571,11 +574,16 @@ void SimulationCanvasWidget::setBottleneckOverlay(std::vector<safecrowd::domain:
571574 if (focusedBottleneckIndex_.has_value () && *focusedBottleneckIndex_ >= bottleneckOverlay_.size ()) {
572575 focusedBottleneckIndex_.reset ();
573576 }
577+ invalidateOverlayCache ();
574578 update ();
575579}
576580
577581void SimulationCanvasWidget::setResultOverlayMode (ResultOverlayMode mode) {
582+ if (overlayMode_ == mode) {
583+ return ;
584+ }
578585 overlayMode_ = mode;
586+ invalidateOverlayCache ();
579587 update ();
580588}
581589
@@ -589,6 +597,7 @@ void SimulationCanvasWidget::focusHotspot(std::size_t index) {
589597 }
590598 focusedHotspotIndex_ = index;
591599 focusedBottleneckIndex_.reset ();
600+ invalidateOverlayCache ();
592601 focusWorldPoint (hotspotOverlay_[index].center , std::max (camera_.zoom (), kHotspotFocusZoom ));
593602}
594603
@@ -603,6 +612,7 @@ void SimulationCanvasWidget::focusBottleneck(std::size_t index) {
603612 }
604613 focusedBottleneckIndex_ = index;
605614 focusedHotspotIndex_.reset ();
615+ invalidateOverlayCache ();
606616 focusWorldPoint (
607617 {.x = (passage.start .x + passage.end .x ) / 2.0 , .y = (passage.start .y + passage.end .y ) / 2.0 },
608618 std::max (camera_.zoom (), kBottleneckFocusZoom ));
@@ -640,6 +650,7 @@ void SimulationCanvasWidget::mouseDoubleClickEvent(QMouseEvent* event) {
640650 if (event->button () == Qt::LeftButton) {
641651 camera_.reset ();
642652 layoutCacheValid_ = false ;
653+ invalidateOverlayCache ();
643654 update ();
644655 event->accept ();
645656 return ;
@@ -658,6 +669,7 @@ void SimulationCanvasWidget::mouseMoveEvent(QMouseEvent* event) {
658669 QToolTip::hideText ();
659670 }
660671 layoutCacheValid_ = false ;
672+ invalidateOverlayCache ();
661673 update ();
662674 return ;
663675 }
@@ -807,14 +819,9 @@ void SimulationCanvasWidget::paintEvent(QPaintEvent* event) {
807819 painter.drawPixmap (0 , 0 , layoutCache_);
808820
809821 const auto transform = currentTransform (*bounds);
810- if (overlayMode_ == ResultOverlayMode::Density) {
811- drawDensityOverlay (painter, transform);
812- } else if (overlayMode_ == ResultOverlayMode::Pressure) {
813- drawPressureOverlay (painter, transform);
814- } else if (overlayMode_ == ResultOverlayMode::Hotspots) {
815- drawHotspotOverlay (painter, transform);
816- } else if (overlayMode_ == ResultOverlayMode::Bottlenecks) {
817- drawBottleneckOverlay (painter, transform);
822+ refreshOverlayCache (*bounds);
823+ if (!overlayCache_.isNull ()) {
824+ painter.drawPixmap (0 , 0 , overlayCache_);
818825 }
819826 drawEnvironmentHazardOverlay (painter, transform);
820827 drawConnectionBlockOverlay (painter, transform);
@@ -847,6 +854,7 @@ void SimulationCanvasWidget::wheelEvent(QWheelEvent* event) {
847854 }
848855 if (camera_.zoomAt (event, *bounds, previewViewport ())) {
849856 layoutCacheValid_ = false ;
857+ invalidateOverlayCache ();
850858 update ();
851859 return ;
852860 }
@@ -901,6 +909,69 @@ void SimulationCanvasWidget::refreshLayoutCache(const LayoutCanvasBounds& bounds
901909 layoutCacheValid_ = true ;
902910}
903911
912+ void SimulationCanvasWidget::refreshOverlayCache (const LayoutCanvasBounds& bounds) {
913+ if (overlayMode_ == ResultOverlayMode::None) {
914+ overlayCache_ = QPixmap ();
915+ overlayCacheValid_ = true ;
916+ overlayCacheMode_ = overlayMode_;
917+ overlayCacheFloorId_ = currentFloorId_;
918+ return ;
919+ }
920+
921+ const auto currentSize = size ();
922+ if (currentSize.isEmpty ()) {
923+ overlayCache_ = QPixmap ();
924+ overlayCacheSize_ = currentSize;
925+ overlayCacheDevicePixelRatio_ = 0.0 ;
926+ overlayCacheValid_ = false ;
927+ return ;
928+ }
929+
930+ const auto devicePixelRatio = devicePixelRatioF ();
931+ if (overlayCacheValid_
932+ && overlayCacheSize_ == currentSize
933+ && overlayCacheDevicePixelRatio_ == devicePixelRatio
934+ && overlayCacheZoom_ == camera_.zoom ()
935+ && overlayCachePan_ == camera_.panOffset ()
936+ && overlayCacheMode_ == overlayMode_
937+ && overlayCacheFloorId_ == currentFloorId_) {
938+ return ;
939+ }
940+
941+ const QSize physicalSize{
942+ std::max (1 , static_cast <int >(std::ceil (currentSize.width () * devicePixelRatio))),
943+ std::max (1 , static_cast <int >(std::ceil (currentSize.height () * devicePixelRatio))),
944+ };
945+ overlayCache_ = QPixmap (physicalSize);
946+ overlayCache_.setDevicePixelRatio (devicePixelRatio);
947+ overlayCache_.fill (Qt::transparent);
948+
949+ QPainter painter (&overlayCache_);
950+ painter.setRenderHint (QPainter::Antialiasing, true );
951+ const auto transform = currentTransform (bounds);
952+ if (overlayMode_ == ResultOverlayMode::Density) {
953+ drawDensityOverlay (painter, transform);
954+ } else if (overlayMode_ == ResultOverlayMode::Pressure) {
955+ drawPressureOverlay (painter, transform);
956+ } else if (overlayMode_ == ResultOverlayMode::Hotspots) {
957+ drawHotspotOverlay (painter, transform);
958+ } else if (overlayMode_ == ResultOverlayMode::Bottlenecks) {
959+ drawBottleneckOverlay (painter, transform);
960+ }
961+
962+ overlayCacheSize_ = currentSize;
963+ overlayCacheZoom_ = camera_.zoom ();
964+ overlayCachePan_ = camera_.panOffset ();
965+ overlayCacheDevicePixelRatio_ = devicePixelRatio;
966+ overlayCacheMode_ = overlayMode_;
967+ overlayCacheFloorId_ = currentFloorId_;
968+ overlayCacheValid_ = true ;
969+ }
970+
971+ void SimulationCanvasWidget::invalidateOverlayCache () {
972+ overlayCacheValid_ = false ;
973+ }
974+
904975QRectF SimulationCanvasWidget::previewViewport () const {
905976 return QRectF (rect ()).adjusted (kViewportPadding , kViewportPadding , -kViewportPadding , -kViewportPadding );
906977}
@@ -922,6 +993,7 @@ void SimulationCanvasWidget::focusWorldPoint(const safecrowd::domain::Point2D& p
922993 const LayoutCanvasTransform transform (*bounds, viewport, camera_.zoom (), {});
923994 camera_.setPanOffset (viewport.center () - transform.map (point));
924995 layoutCacheValid_ = false ;
996+ invalidateOverlayCache ();
925997 update ();
926998}
927999
@@ -1404,6 +1476,7 @@ void SimulationCanvasWidget::setCurrentFloorId(std::string floorId, bool manualS
14041476 manualFloorSelection_ = manualSelection;
14051477 layoutBounds_ = collectLayoutCanvasBounds (layout_, currentFloorId_);
14061478 layoutCacheValid_ = false ;
1479+ invalidateOverlayCache ();
14071480 camera_.reset ();
14081481
14091482 if (floorComboBox_ != nullptr ) {
0 commit comments