Quantum Phenomena Explorer 7.9.8
Interactive quantum-phenomena Explorer Tutor — source documentation
Loading...
Searching...
No Matches
quantumbilliardplotwidget.cpp
Go to the documentation of this file.
1/**
2 * @file quantumbilliardplotwidget.cpp
3 * @brief Implements the quantumbilliardplotwidget visualization component used by QPE.
4 * @ingroup qpe_graphics
5 *
6 * Source: @c graphics/renderer/quantumbilliardplotwidget.cpp. This file is part of Quantum Phenomena Explorer (QPE),
7 * maintained by Officina Turini. See LICENSE_IT.txt / LICENSE.txt for the
8 * QPE freeware source-available terms and THIRD_PARTY_NOTICES.txt for
9 * third-party components.
10 */
11
13
14#include <QImage>
15#include <QPaintEvent>
16#include <QPainter>
17#include <QPainterPath>
18
19#include <algorithm>
20#include <cmath>
21
22namespace {
23
24/**
25 * @brief Draws frame.
26 * @param painter The painter value used by the operation.
27 * @param rect The rect value used by the operation.
28 */
29void drawFrame(QPainter &painter, const QRectF &rect)
30{
31 painter.setPen(QPen(QColor(72, 80, 90), 1.0));
32 painter.drawRect(rect);
33}
34
35/**
36 * @brief Performs density colour.
37 * @param intensity The intensity value used by the operation.
38 */
39QRgb densityColour(double intensity)
40{
41 const double v = std::max(0.0, std::min(1.0, intensity));
42 const int r = static_cast<int>(255.0 * std::pow(v, 1.7));
43 const int g = static_cast<int>(255.0 * std::pow(v, 0.72));
44 const int b = static_cast<int>(255.0 * std::min(1.0, 0.12 + 1.25 * std::pow(v, 0.9)));
45 return qRgb(r, g, b);
46}
47
48} // namespace
49
51 : QWidget(parent)
52{
53 setMinimumHeight(360);
54 setAutoFillBackground(false);
55}
56
58 const qpe::QuantumBilliardGeometry2D &geometry)
59{
60 history_ = &history;
61 geometry_ = geometry;
62 update();
63}
64
71
73{
74 return QSize(600, 380);
75}
76
78{
79 Q_UNUSED(event);
80
81 QPainter painter(this);
82 painter.setRenderHint(QPainter::Antialiasing, true);
83 painter.setRenderHint(QPainter::TextAntialiasing, true);
84 painter.fillRect(rect(), QColor(14, 17, 22));
85
86 const double marginLeft = 62.0;
87 const double marginRight = 24.0;
88 const double top = 30.0;
89 const double bottom = 38.0;
90 const double gap = 36.0;
91 const double available = std::max(120.0, height() - top - bottom - gap);
92 const double mapHeight = available * 0.63;
93 const QRectF mapRect(marginLeft, top,
94 std::max(40.0, width() - marginLeft - marginRight), mapHeight);
95 const QRectF curveRect(marginLeft, mapRect.bottom() + gap,
96 mapRect.width(), available - mapHeight);
97
98 drawFrame(painter, mapRect);
99 drawFrame(painter, curveRect);
100
101 painter.setPen(QColor(214, 220, 228));
102 painter.drawText(QRectF(mapRect.left(), 4.0, mapRect.width(), 20.0), Qt::AlignCenter,
103 QStringLiteral("Time-averaged probability density inside the billiard"));
104 painter.drawText(QRectF(curveRect.left(), mapRect.bottom() + 8.0,
105 curveRect.width(), 20.0), Qt::AlignCenter,
106 QStringLiteral("Spatial filling / recurrence / scatterer encounter"));
107
108 if (!geometry_.valid || history_ == nullptr || history_->empty() ||
109 history_->gridWidth() < 2u || history_->gridHeight() < 2u) {
110 painter.setPen(QColor(220, 228, 238));
111 painter.drawText(mapRect, Qt::AlignCenter,
112 QStringLiteral("Run a Quantum Billiard preset to accumulate the occupation map"));
113 painter.drawText(curveRect, Qt::AlignCenter,
114 QStringLiteral("Four box walls are required; Sinai adds one circular scatterer"));
115 return;
116 }
117
118 const std::vector<float> &mean = history_->meanProbabilityDensity();
119 const std::size_t gw = history_->gridWidth();
120 const std::size_t gh = history_->gridHeight();
121 const double dx = history_->dx();
122 const double dy = history_->dy();
123 const double x0 = geometry_.centerX - 0.5 * geometry_.innerWidth;
124 const double x1 = geometry_.centerX + 0.5 * geometry_.innerWidth;
125 const double y0 = geometry_.centerY - 0.5 * geometry_.innerHeight;
126 const double y1 = geometry_.centerY + 0.5 * geometry_.innerHeight;
127 const double gxCentre = 0.5 * static_cast<double>(gw - 1u);
128 const double gyCentre = 0.5 * static_cast<double>(gh - 1u);
129 int ix0 = static_cast<int>(std::ceil(x0 / dx + gxCentre));
130 int ix1 = static_cast<int>(std::floor(x1 / dx + gxCentre));
131 int iy0 = static_cast<int>(std::ceil(y0 / dy + gyCentre));
132 int iy1 = static_cast<int>(std::floor(y1 / dy + gyCentre));
133 ix0 = std::max(0, std::min(ix0, static_cast<int>(gw) - 1));
134 ix1 = std::max(ix0, std::min(ix1, static_cast<int>(gw) - 1));
135 iy0 = std::max(0, std::min(iy0, static_cast<int>(gh) - 1));
136 iy1 = std::max(iy0, std::min(iy1, static_cast<int>(gh) - 1));
137
138 const int columns = ix1 - ix0 + 1;
139 const int rows = iy1 - iy0 + 1;
140 double maxDensity = 0.0;
141 for (int y = iy0; y <= iy1; ++y) {
142 for (int x = ix0; x <= ix1; ++x) {
143 const std::size_t index = static_cast<std::size_t>(y) * gw + static_cast<std::size_t>(x);
144 if (index < mean.size()) maxDensity = std::max(maxDensity, static_cast<double>(mean[index]));
145 }
146 }
147 maxDensity = std::max(maxDensity, 1.0e-30);
148
149 if (columns > 0 && rows > 0 && mean.size() == gw * gh) {
150 QImage image(columns, rows, QImage::Format_RGB32);
151 for (int yy = 0; yy < rows; ++yy) {
152 const int gy = iy1 - yy; // physical +Y upward in the display
153 for (int xx = 0; xx < columns; ++xx) {
154 const int gx = ix0 + xx;
155 const std::size_t index = static_cast<std::size_t>(gy) * gw + static_cast<std::size_t>(gx);
156 const double normalized = static_cast<double>(mean[index]) / maxDensity;
157 image.setPixel(xx, yy, densityColour(std::sqrt(std::max(0.0, normalized))));
158 }
159 }
160 painter.setRenderHint(QPainter::SmoothPixmapTransform, false);
161 painter.drawImage(mapRect, image, QRectF(image.rect()));
162 painter.setRenderHint(QPainter::Antialiasing, true);
163 }
164
166 const double cx = mapRect.left() +
167 (geometry_.obstacleCenterX - x0) / std::max(1.0e-12, x1 - x0) * mapRect.width();
168 const double cy = mapRect.bottom() -
169 (geometry_.obstacleCenterY - y0) / std::max(1.0e-12, y1 - y0) * mapRect.height();
170 const double rx = geometry_.obstacleRadius / (x1 - x0) * mapRect.width();
171 const double ry = geometry_.obstacleRadius / (y1 - y0) * mapRect.height();
172 painter.setBrush(QColor(15, 18, 22, 230));
173 painter.setPen(QPen(QColor(235, 238, 242), 1.3));
174 painter.drawEllipse(QPointF(cx, cy), rx, ry);
175 painter.setBrush(Qt::NoBrush);
176 }
177
178 const std::vector<qpe::QuantumBilliardSample2D> &samples = history_->samples();
179 const double tMin = samples.front().time;
180 const double tMax = samples.back().time;
181 const double span = std::max(1.0e-12, tMax - tMin);
182 const auto mapX = [curveRect, tMin, span](double t) {
183 return curveRect.left() + (t - tMin) / span * curveRect.width();
184 };
185 const auto mapY = [curveRect](double v) {
186 const double clamped = std::max(0.0, std::min(1.0, v));
187 return curveRect.bottom() - clamped * curveRect.height();
188 };
189
190 painter.setPen(QPen(QColor(72, 80, 90), 1.0));
191 for (int i = 1; i < 4; ++i) {
192 const double y = curveRect.top() + i * curveRect.height() / 4.0;
193 painter.drawLine(QPointF(curveRect.left(), y), QPointF(curveRect.right(), y));
194 }
195
196 QPainterPath occupiedPath;
197 QPainterPath fidelityPath;
198 QPainterPath obstaclePath;
199 bool started = false;
200 for (const qpe::QuantumBilliardSample2D &sample : samples) {
201 const double x = mapX(sample.time);
202 const QPointF pOccupied(x, mapY(sample.occupiedFraction));
203 const QPointF pFidelity(x, mapY(sample.fidelity));
204 const QPointF pObstacle(x, mapY(std::min(1.0, 4.0 * sample.obstacleShellProbability)));
205 if (!started) {
206 occupiedPath.moveTo(pOccupied);
207 fidelityPath.moveTo(pFidelity);
208 obstaclePath.moveTo(pObstacle);
209 started = true;
210 } else {
211 occupiedPath.lineTo(pOccupied);
212 fidelityPath.lineTo(pFidelity);
213 obstaclePath.lineTo(pObstacle);
214 }
215 }
216
217 painter.setPen(QPen(QColor(80, 220, 255), 2.2));
218 painter.drawPath(occupiedPath);
219 painter.setPen(QPen(QColor(255, 205, 92), 1.7));
220 painter.drawPath(fidelityPath);
222 painter.setPen(QPen(QColor(255, 112, 160), 1.5));
223 painter.drawPath(obstaclePath);
224 }
225
226 painter.setPen(QColor(215, 224, 234));
227 painter.drawText(QRectF(5.0, curveRect.top() - 9.0, 50.0, 18.0),
228 Qt::AlignRight | Qt::AlignVCenter, QStringLiteral("1.0"));
229 painter.drawText(QRectF(5.0, curveRect.bottom() - 10.0, 50.0, 18.0),
230 Qt::AlignRight | Qt::AlignVCenter, QStringLiteral("0.0"));
231 painter.drawText(QRectF(curveRect.left(), curveRect.bottom() + 5.0, 90.0, 20.0),
232 Qt::AlignLeft, QString::number(tMin, 'f', 1));
233 painter.drawText(QRectF(curveRect.right() - 90.0, curveRect.bottom() + 5.0, 90.0, 20.0),
234 Qt::AlignRight, QString::number(tMax, 'f', 1));
235 painter.drawText(QRectF(curveRect.left(), curveRect.bottom() + 4.0,
236 curveRect.width(), 24.0), Qt::AlignCenter,
237 QStringLiteral("simulation time"));
238
239 const double legendY = mapRect.bottom() + 7.0;
240 painter.setPen(QColor(80, 220, 255));
241 painter.drawText(QRectF(mapRect.left(), legendY, 160.0, 20.0), Qt::AlignLeft,
242 QStringLiteral("cyan: occupied fraction"));
243 painter.setPen(QColor(255, 205, 92));
244 painter.drawText(QRectF(mapRect.left() + 165.0, legendY, 150.0, 20.0), Qt::AlignLeft,
245 QStringLiteral("yellow: fidelity"));
247 painter.setPen(QColor(255, 112, 160));
248 painter.drawText(QRectF(mapRect.left() + 315.0, legendY, 240.0, 20.0), Qt::AlignLeft,
249 QStringLiteral("pink: 4x obstacle-shell probability"));
250 }
251}
void setData(const qpe::QuantumBilliardHistory2D &history, const qpe::QuantumBilliardGeometry2D &geometry)
Sets data.
QSize minimumSizeHint() const override
Performs minimum size hint.
QuantumBilliardPlotWidget(QWidget *parent=nullptr)
Performs quantum billiard plot widget.
void paintEvent(QPaintEvent *event) override
Paints event.
qpe::QuantumBilliardGeometry2D geometry_
const qpe::QuantumBilliardHistory2D * history_
Implements the Quantum Billiard History2D component.
bool empty() const noexcept
Returns whether the object contains no data.
std::size_t gridWidth() const noexcept
Carries out grid width.
const std::vector< QuantumBilliardSample2D > & samples() const noexcept
Samples s.
const std::vector< float > & meanProbabilityDensity() const noexcept
Carries out mean probability density.
std::size_t gridHeight() const noexcept
Carries out grid height.
float dy() const noexcept
Returns the Y-axis grid spacing.
float dx() const noexcept
Returns the X-axis grid spacing.
QRgb densityColour(double intensity)
Performs density colour.
void drawFrame(QPainter &painter, const QRectF &rect)
Draws frame.
Declares the quantumbilliardplotwidget visualization component used by QPE.
Data structure used by the Quantum Billiard Geometry2D component.
Data structure used by the Quantum Billiard Sample2D component.