Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 29 additions & 16 deletions src/application/ScenarioBatchResultWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -990,28 +990,12 @@ void ScenarioBatchResultWidget::applyReplayFrameData(const safecrowd::domain::Si
if (results_.empty() || currentResultIndex_ < 0 || currentResultIndex_ >= static_cast<int>(results_.size())) {
return;
}
const auto& result = results_[static_cast<std::size_t>(currentResultIndex_)];
replayFrameIndex_ = replayFrames_.empty()
? 0
: std::clamp(sliderIndex, 0, static_cast<int>(replayFrames_.size()) - 1);

if (canvas_ != nullptr) {
canvas_->setConnectionBlocks(result.scenario.control.connectionBlocks);
canvas_->setEnvironmentHazards(result.scenario.environment.hazards);
canvas_->setFrame(frame);
canvas_->setHotspotOverlay(result.risk.hotspots);
canvas_->setBottleneckOverlay(result.risk.bottlenecks);
canvas_->setDensityOverlay(result.artifacts.densitySummary.peakField.cells.empty()
? result.artifacts.densitySummary.peakCells
: result.artifacts.densitySummary.peakField.cells,
result.artifacts.densitySummary.highDensityThresholdPeoplePerSquareMeter);
canvas_->setPressureOverlay(result.artifacts.pressureSummary.peakField.cells.empty()
? result.artifacts.pressureSummary.peakCells
: result.artifacts.pressureSummary.peakField.cells,
std::max(
result.artifacts.pressureSummary.hotspotScoreThreshold,
result.artifacts.pressureSummary.peakPressureScore));
applyOverlayModeToCanvas();
}
if (replaySlider_ != nullptr && replaySlider_->value() != replayFrameIndex_) {
const QSignalBlocker blocker(replaySlider_);
Expand All @@ -1031,6 +1015,34 @@ void ScenarioBatchResultWidget::applyReplayFrameData(const safecrowd::domain::Si
}
}

void ScenarioBatchResultWidget::applySelectedResultStaticCanvasState() {
if (canvas_ == nullptr
|| results_.empty()
|| currentResultIndex_ < 0
|| currentResultIndex_ >= static_cast<int>(results_.size())) {
return;
}

const auto& result = results_[static_cast<std::size_t>(currentResultIndex_)];
canvas_->setConnectionBlocks(result.scenario.control.connectionBlocks);
canvas_->setEnvironmentHazards(result.scenario.environment.hazards);
canvas_->setHotspotOverlay(result.risk.hotspots);
canvas_->setBottleneckOverlay(result.risk.bottlenecks);
canvas_->setDensityOverlay(
result.artifacts.densitySummary.peakField.cells.empty()
? result.artifacts.densitySummary.peakCells
: result.artifacts.densitySummary.peakField.cells,
result.artifacts.densitySummary.highDensityThresholdPeoplePerSquareMeter);
canvas_->setPressureOverlay(
result.artifacts.pressureSummary.peakField.cells.empty()
? result.artifacts.pressureSummary.peakCells
: result.artifacts.pressureSummary.peakField.cells,
std::max(
result.artifacts.pressureSummary.hotspotScoreThreshold,
result.artifacts.pressureSummary.peakPressureScore));
applyOverlayModeToCanvas();
}

void ScenarioBatchResultWidget::applyOverlayModeToCanvas() {
if (canvas_ == nullptr) {
return;
Expand Down Expand Up @@ -1125,6 +1137,7 @@ void ScenarioBatchResultWidget::loadReplayForSelectedResult() {
replayFrames_.clear();
return;
}
applySelectedResultStaticCanvasState();
replayFrames_ = replayFramesForResult(results_[static_cast<std::size_t>(currentResultIndex_)]);
replayFrameIndex_ = replayFrames_.empty() ? 0 : static_cast<int>(replayFrames_.size()) - 1;
if (replaySlider_ != nullptr) {
Expand Down
1 change: 1 addition & 0 deletions src/application/ScenarioBatchResultWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class ScenarioBatchResultWidget : public QWidget {
void applyReplayFrame(int frameIndex);
void applyReplayFrameData(const safecrowd::domain::SimulationFrame& frame, int sliderIndex);
void applyOverlayModeToCanvas();
void applySelectedResultStaticCanvasState();
void loadReplayForSelectedResult();
int nearestReplayFrameIndex(double seconds) const;
void navigateToAuthoring();
Expand Down
89 changes: 81 additions & 8 deletions src/application/SimulationCanvasWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ void SimulationCanvasWidget::setDensityOverlay(
std::isfinite(scaleMaxPeoplePerSquareMeter) && scaleMaxPeoplePerSquareMeter > 0.0
? scaleMaxPeoplePerSquareMeter
: kDefaultDensityScaleMaxPeoplePerSquareMeter;
invalidateOverlayCache();
update();
}

Expand All @@ -555,6 +556,7 @@ void SimulationCanvasWidget::setPressureOverlay(
std::isfinite(scaleMaxPressureScore) && scaleMaxPressureScore > 0.0
? scaleMaxPressureScore
: kDefaultPressureScaleMaxScore;
invalidateOverlayCache();
update();
}

Expand All @@ -563,6 +565,7 @@ void SimulationCanvasWidget::setHotspotOverlay(std::vector<safecrowd::domain::Sc
if (focusedHotspotIndex_.has_value() && *focusedHotspotIndex_ >= hotspotOverlay_.size()) {
focusedHotspotIndex_.reset();
}
invalidateOverlayCache();
update();
}

Expand All @@ -571,11 +574,16 @@ void SimulationCanvasWidget::setBottleneckOverlay(std::vector<safecrowd::domain:
if (focusedBottleneckIndex_.has_value() && *focusedBottleneckIndex_ >= bottleneckOverlay_.size()) {
focusedBottleneckIndex_.reset();
}
invalidateOverlayCache();
update();
}

void SimulationCanvasWidget::setResultOverlayMode(ResultOverlayMode mode) {
if (overlayMode_ == mode) {
return;
}
overlayMode_ = mode;
invalidateOverlayCache();
update();
}

Expand All @@ -589,6 +597,7 @@ void SimulationCanvasWidget::focusHotspot(std::size_t index) {
}
focusedHotspotIndex_ = index;
focusedBottleneckIndex_.reset();
invalidateOverlayCache();
focusWorldPoint(hotspotOverlay_[index].center, std::max(camera_.zoom(), kHotspotFocusZoom));
}

Expand All @@ -603,6 +612,7 @@ void SimulationCanvasWidget::focusBottleneck(std::size_t index) {
}
focusedBottleneckIndex_ = index;
focusedHotspotIndex_.reset();
invalidateOverlayCache();
focusWorldPoint(
{.x = (passage.start.x + passage.end.x) / 2.0, .y = (passage.start.y + passage.end.y) / 2.0},
std::max(camera_.zoom(), kBottleneckFocusZoom));
Expand Down Expand Up @@ -640,6 +650,7 @@ void SimulationCanvasWidget::mouseDoubleClickEvent(QMouseEvent* event) {
if (event->button() == Qt::LeftButton) {
camera_.reset();
layoutCacheValid_ = false;
invalidateOverlayCache();
update();
event->accept();
return;
Expand All @@ -658,6 +669,7 @@ void SimulationCanvasWidget::mouseMoveEvent(QMouseEvent* event) {
QToolTip::hideText();
}
layoutCacheValid_ = false;
invalidateOverlayCache();
update();
return;
}
Expand Down Expand Up @@ -807,14 +819,9 @@ void SimulationCanvasWidget::paintEvent(QPaintEvent* event) {
painter.drawPixmap(0, 0, layoutCache_);

const auto transform = currentTransform(*bounds);
if (overlayMode_ == ResultOverlayMode::Density) {
drawDensityOverlay(painter, transform);
} else if (overlayMode_ == ResultOverlayMode::Pressure) {
drawPressureOverlay(painter, transform);
} else if (overlayMode_ == ResultOverlayMode::Hotspots) {
drawHotspotOverlay(painter, transform);
} else if (overlayMode_ == ResultOverlayMode::Bottlenecks) {
drawBottleneckOverlay(painter, transform);
refreshOverlayCache(*bounds);
if (!overlayCache_.isNull()) {
painter.drawPixmap(0, 0, overlayCache_);
}
drawEnvironmentHazardOverlay(painter, transform);
drawConnectionBlockOverlay(painter, transform);
Expand Down Expand Up @@ -847,6 +854,7 @@ void SimulationCanvasWidget::wheelEvent(QWheelEvent* event) {
}
if (camera_.zoomAt(event, *bounds, previewViewport())) {
layoutCacheValid_ = false;
invalidateOverlayCache();
update();
return;
}
Expand Down Expand Up @@ -901,6 +909,69 @@ void SimulationCanvasWidget::refreshLayoutCache(const LayoutCanvasBounds& bounds
layoutCacheValid_ = true;
}

void SimulationCanvasWidget::refreshOverlayCache(const LayoutCanvasBounds& bounds) {
if (overlayMode_ == ResultOverlayMode::None) {
overlayCache_ = QPixmap();
overlayCacheValid_ = true;
overlayCacheMode_ = overlayMode_;
overlayCacheFloorId_ = currentFloorId_;
return;
}

const auto currentSize = size();
if (currentSize.isEmpty()) {
overlayCache_ = QPixmap();
overlayCacheSize_ = currentSize;
overlayCacheDevicePixelRatio_ = 0.0;
overlayCacheValid_ = false;
return;
}

const auto devicePixelRatio = devicePixelRatioF();
if (overlayCacheValid_
&& overlayCacheSize_ == currentSize
&& overlayCacheDevicePixelRatio_ == devicePixelRatio
&& overlayCacheZoom_ == camera_.zoom()
&& overlayCachePan_ == camera_.panOffset()
&& overlayCacheMode_ == overlayMode_
&& overlayCacheFloorId_ == currentFloorId_) {
return;
}

const QSize physicalSize{
std::max(1, static_cast<int>(std::ceil(currentSize.width() * devicePixelRatio))),
std::max(1, static_cast<int>(std::ceil(currentSize.height() * devicePixelRatio))),
};
overlayCache_ = QPixmap(physicalSize);
overlayCache_.setDevicePixelRatio(devicePixelRatio);
overlayCache_.fill(Qt::transparent);

QPainter painter(&overlayCache_);
painter.setRenderHint(QPainter::Antialiasing, true);
const auto transform = currentTransform(bounds);
if (overlayMode_ == ResultOverlayMode::Density) {
drawDensityOverlay(painter, transform);
} else if (overlayMode_ == ResultOverlayMode::Pressure) {
drawPressureOverlay(painter, transform);
} else if (overlayMode_ == ResultOverlayMode::Hotspots) {
drawHotspotOverlay(painter, transform);
} else if (overlayMode_ == ResultOverlayMode::Bottlenecks) {
drawBottleneckOverlay(painter, transform);
}

overlayCacheSize_ = currentSize;
overlayCacheZoom_ = camera_.zoom();
overlayCachePan_ = camera_.panOffset();
overlayCacheDevicePixelRatio_ = devicePixelRatio;
overlayCacheMode_ = overlayMode_;
overlayCacheFloorId_ = currentFloorId_;
overlayCacheValid_ = true;
}

void SimulationCanvasWidget::invalidateOverlayCache() {
overlayCacheValid_ = false;
}

QRectF SimulationCanvasWidget::previewViewport() const {
return QRectF(rect()).adjusted(kViewportPadding, kViewportPadding, -kViewportPadding, -kViewportPadding);
}
Expand All @@ -922,6 +993,7 @@ void SimulationCanvasWidget::focusWorldPoint(const safecrowd::domain::Point2D& p
const LayoutCanvasTransform transform(*bounds, viewport, camera_.zoom(), {});
camera_.setPanOffset(viewport.center() - transform.map(point));
layoutCacheValid_ = false;
invalidateOverlayCache();
update();
}

Expand Down Expand Up @@ -1404,6 +1476,7 @@ void SimulationCanvasWidget::setCurrentFloorId(std::string floorId, bool manualS
manualFloorSelection_ = manualSelection;
layoutBounds_ = collectLayoutCanvasBounds(layout_, currentFloorId_);
layoutCacheValid_ = false;
invalidateOverlayCache();
camera_.reset();

if (floorComboBox_ != nullptr) {
Expand Down
10 changes: 10 additions & 0 deletions src/application/SimulationCanvasWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ class SimulationCanvasWidget : public QWidget {
std::optional<LayoutCanvasBounds> collectBounds() const;
LayoutCanvasTransform currentTransform(const LayoutCanvasBounds& bounds) const;
void refreshLayoutCache(const LayoutCanvasBounds& bounds);
void refreshOverlayCache(const LayoutCanvasBounds& bounds);
void invalidateOverlayCache();
QRectF previewViewport() const;
void focusWorldPoint(const safecrowd::domain::Point2D& point, double zoom);
void drawConnectionBlockOverlay(QPainter& painter, const LayoutCanvasTransform& transform) const;
Expand Down Expand Up @@ -113,6 +115,14 @@ class SimulationCanvasWidget : public QWidget {
double layoutCacheZoom_{0.0};
double layoutCacheDevicePixelRatio_{0.0};
bool layoutCacheValid_{false};
QPixmap overlayCache_{};
QSize overlayCacheSize_{};
QPointF overlayCachePan_{};
double overlayCacheZoom_{0.0};
double overlayCacheDevicePixelRatio_{0.0};
ResultOverlayMode overlayCacheMode_{ResultOverlayMode::None};
std::string overlayCacheFloorId_{};
bool overlayCacheValid_{false};

std::string hoveredConnectionBlockId_{};
std::string hoveredEnvironmentHazardId_{};
Expand Down
Loading
Loading