Skip to content

Commit 310dcf8

Browse files
committed
Smooth result hotspot heatmap
1 parent 4708739 commit 310dcf8

1 file changed

Lines changed: 33 additions & 9 deletions

File tree

src/application/SimulationCanvasWidget.cpp

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
#include "application/SimulationCanvasWidget.h"
22

33
#include <algorithm>
4+
#include <cmath>
45
#include <utility>
56

67
#include <QCoreApplication>
78
#include <QEvent>
89
#include <QKeyEvent>
910
#include <QMouseEvent>
1011
#include <QPainter>
12+
#include <QRadialGradient>
1113
#include <QWheelEvent>
1214

1315
namespace safecrowd::application {
@@ -16,8 +18,9 @@ namespace {
1618
constexpr double kViewportPadding = 32.0;
1719
constexpr double kVelocityIndicatorSeconds = 0.75;
1820
constexpr double kAgentMarkerRadius = 5.0;
19-
constexpr int kHotspotMinAlpha = 42;
20-
constexpr int kHotspotMaxAlpha = 156;
21+
constexpr double kDefaultHotspotCellSize = 1.5;
22+
constexpr int kHotspotMinCoreAlpha = 72;
23+
constexpr int kHotspotMaxCoreAlpha = 190;
2124

2225
} // namespace
2326

@@ -205,18 +208,39 @@ void SimulationCanvasWidget::drawHotspotOverlay(QPainter& painter, const LayoutC
205208
painter.save();
206209
painter.setPen(Qt::NoPen);
207210
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
211+
QPainterPath walkableClip;
212+
for (const auto& zone : layout_.zones) {
213+
walkableClip.addPath(layoutCanvasPolygonPath(zone.area, transform));
214+
}
215+
if (!walkableClip.isEmpty()) {
216+
painter.setClipPath(walkableClip);
217+
}
218+
208219
for (const auto& hotspot : hotspotOverlay_) {
209220
if (hotspot.agentCount == 0) {
210221
continue;
211222
}
212-
const auto topLeft = transform.map({.x = hotspot.cellMin.x, .y = hotspot.cellMax.y});
213-
const auto bottomRight = transform.map({.x = hotspot.cellMax.x, .y = hotspot.cellMin.y});
214-
const QRectF cellRect(topLeft, bottomRight);
215223
const auto intensity = static_cast<double>(hotspot.agentCount) / static_cast<double>(maxAgentCount);
216-
const auto alpha = kHotspotMinAlpha
217-
+ static_cast<int>((kHotspotMaxAlpha - kHotspotMinAlpha) * intensity);
218-
painter.setBrush(QColor(220, 38, 38, std::clamp(alpha, kHotspotMinAlpha, kHotspotMaxAlpha)));
219-
painter.drawRoundedRect(cellRect.normalized(), 6.0, 6.0);
224+
const auto center = transform.map(hotspot.center);
225+
const auto cellWidth = hotspot.cellMax.x > hotspot.cellMin.x
226+
? hotspot.cellMax.x - hotspot.cellMin.x
227+
: kDefaultHotspotCellSize;
228+
const auto cellHeight = hotspot.cellMax.y > hotspot.cellMin.y
229+
? hotspot.cellMax.y - hotspot.cellMin.y
230+
: kDefaultHotspotCellSize;
231+
const auto sourceRadiusWorld = std::max(cellWidth, cellHeight) * (1.2 + (0.85 * std::sqrt(intensity)));
232+
const auto radiusAnchor = transform.map({.x = hotspot.center.x + sourceRadiusWorld, .y = hotspot.center.y});
233+
const auto radius = std::max(12.0, std::hypot(radiusAnchor.x() - center.x(), radiusAnchor.y() - center.y()));
234+
const auto coreAlpha = kHotspotMinCoreAlpha
235+
+ static_cast<int>((kHotspotMaxCoreAlpha - kHotspotMinCoreAlpha) * intensity);
236+
237+
QRadialGradient gradient(center, radius);
238+
gradient.setColorAt(0.0, QColor(185, 28, 28, std::clamp(coreAlpha, kHotspotMinCoreAlpha, kHotspotMaxCoreAlpha)));
239+
gradient.setColorAt(0.28, QColor(220, 38, 38, static_cast<int>(coreAlpha * 0.62)));
240+
gradient.setColorAt(0.58, QColor(249, 115, 22, static_cast<int>(coreAlpha * 0.28)));
241+
gradient.setColorAt(1.0, QColor(249, 115, 22, 0));
242+
painter.setBrush(gradient);
243+
painter.drawEllipse(center, radius, radius);
220244
}
221245
painter.restore();
222246
}

0 commit comments

Comments
 (0)