Quantum Phenomena Explorer 7.9.8
Interactive quantum-phenomena Explorer Tutor — source documentation
Loading...
Searching...
No Matches
quantummoshinskyplotwidget.cpp
Go to the documentation of this file.
1/**
2 * @file quantummoshinskyplotwidget.cpp
3 * @brief Implements the quantummoshinskyplotwidget visualization component used by QPE.
4 * @ingroup qpe_graphics
5 *
6 * Source: @c graphics/renderer/quantummoshinskyplotwidget.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 <QPainter>
15#include <QPaintEvent>
16#include <QFontMetrics>
17#include <QPainterPath>
18
19#include <algorithm>
20#include <cmath>
21
22namespace {
23
24/**
25 * @brief Performs heat color.
26 * @param value Value to assign or evaluate.
27 */
28QColor heatColor(double value)
29{
30 const double v = std::max(0.0, std::min(1.0, value));
31 // Dark blue -> cyan -> warm white. The non-linear map keeps weak Fresnel
32 // ripples visible without changing any numerical data.
33 if (v < 0.55) {
34 const double u = v / 0.55;
35 return QColor(static_cast<int>(8 + 22 * u),
36 static_cast<int>(18 + 155 * u),
37 static_cast<int>(38 + 190 * u));
38 }
39 const double u = (v - 0.55) / 0.45;
40 return QColor(static_cast<int>(30 + 225 * u),
41 static_cast<int>(173 + 82 * u),
42 static_cast<int>(228 + 27 * u));
43}
44
45} // namespace
46
48 : QWidget(parent)
49{
50 setAutoFillBackground(false);
51}
52
54{
55 history_ = &history;
56 update();
57}
58
60{
61 history_ = nullptr;
62 update();
63}
64
66{
67 return QSize(640, 420);
68}
69
71{
72 Q_UNUSED(event);
73 QPainter p(this);
74 p.setRenderHint(QPainter::Antialiasing, true);
75 p.fillRect(rect(), QColor(11, 16, 22));
76
77 p.setPen(QColor(220, 232, 241));
78 QFont title = font();
79 title.setBold(true);
80 p.setFont(title);
81 p.drawText(QRect(12, 8, width() - 24, 24), Qt::AlignLeft | Qt::AlignVCenter,
82 QStringLiteral("Moshinsky shutter — diffraction in time"));
83
84 if (!history_ || !history_->configured() || history_->empty()) {
85 p.setFont(font());
86 p.setPen(QColor(150, 166, 180));
87 p.drawText(QRect(20, 52, width() - 40, height() - 72),
88 Qt::AlignCenter | Qt::TextWordWrap,
89 QStringLiteral("Load Moshinsky Shutter / Diffraction in Time and press Play"));
90 return;
91 }
92
93 const std::vector<qpe::QuantumMoshinskySample2D> &samples = history_->samples();
96 const int left = 62;
97 const int right = 18;
98 const int top = 40;
99 const int gap = 34;
100 const int traceH = std::max(120, height() / 3);
101 const int heatBottom = height() - traceH - gap;
102 QRect heat(left, top, std::max(10, width() - left - right), std::max(80, heatBottom - top));
103 QRect trace(left, heat.bottom() + gap, heat.width(), height() - (heat.bottom() + gap) - 28);
104
105 p.setPen(QColor(60, 76, 90));
106 p.setBrush(QColor(6, 11, 16));
107 p.drawRect(heat);
108 p.drawRect(trace);
109
110 double maxMarginal = 0.0;
111 double maxSignal = 0.0;
112 for (const qpe::QuantumMoshinskySample2D &s : samples) {
113 maxSignal = std::max(maxSignal, s.detectorSignal);
114 for (float v : s.marginalX) maxMarginal = std::max(maxMarginal, static_cast<double>(v));
115 }
116 maxMarginal = std::max(maxMarginal, 1.0e-12);
117 maxSignal = std::max(maxSignal, 1.0e-12);
118
119 const std::size_t nx = history_->gridWidth();
120 if (nx > 0u) {
121 for (std::size_t si = 0; si < samples.size(); ++si) {
122 const qpe::QuantumMoshinskySample2D &s = samples[si];
123 const int y0 = heat.top() + static_cast<int>(
124 (static_cast<double>(si) / static_cast<double>(samples.size())) * heat.height());
125 const int y1 = heat.top() + static_cast<int>(
126 (static_cast<double>(si + 1u) / static_cast<double>(samples.size())) * heat.height());
127 const int hh = std::max(1, y1 - y0 + 1);
128 for (std::size_t xi = 0; xi < nx && xi < s.marginalX.size(); ++xi) {
129 const int x0 = heat.left() + static_cast<int>(
130 (static_cast<double>(xi) / static_cast<double>(nx)) * heat.width());
131 const int x1 = heat.left() + static_cast<int>(
132 (static_cast<double>(xi + 1u) / static_cast<double>(nx)) * heat.width());
133 const int ww = std::max(1, x1 - x0 + 1);
134 const double intensity = std::sqrt(
135 std::max(0.0, static_cast<double>(s.marginalX[xi])) / maxMarginal);
136 p.fillRect(QRect(x0, y0, ww, hh), heatColor(intensity));
137 }
138 }
139 }
140
141 const double xMin = history_->xCoordinate(0u);
142 const double xMax = history_->xCoordinate(nx > 0u ? nx - 1u : 0u);
143 const auto mapX = [&](double x) {
144 const double u = (x - xMin) / std::max(1.0e-12, xMax - xMin);
145 return heat.left() + u * heat.width();
146 };
147 const double tMax = std::max(config.analysisEndTime, samples.back().time);
148 const auto mapTHeat = [&](double t) {
149 return heat.top() + (t / std::max(1.0e-12, tMax)) * heat.height();
150 };
151
152 p.setPen(QPen(QColor(255, 198, 73), 1.3, Qt::DashLine));
153 p.drawLine(QPointF(mapX(geometry.shutterX), heat.top()),
154 QPointF(mapX(geometry.shutterX), heat.bottom()));
155 p.setPen(QPen(QColor(255, 110, 110), 1.3, Qt::DashLine));
156 p.drawLine(QPointF(mapX(config.detectorX), heat.top()),
157 QPointF(mapX(config.detectorX), heat.bottom()));
158 p.setPen(QPen(QColor(240, 240, 240, 140), 1.0, Qt::DashLine));
159 p.drawLine(QPointF(heat.left(), mapTHeat(config.openTime)),
160 QPointF(heat.right(), mapTHeat(config.openTime)));
161
162 p.setFont(font());
163 p.setPen(QColor(180, 197, 210));
164 p.drawText(QRect(4, heat.top(), left - 10, 20), Qt::AlignRight | Qt::AlignVCenter,
165 QStringLiteral("t=0"));
166 p.drawText(QRect(4, heat.bottom() - 20, left - 10, 20), Qt::AlignRight | Qt::AlignVCenter,
167 QStringLiteral("%1").arg(tMax, 0, 'f', 2));
168 p.drawText(QRect(heat.left(), heat.bottom() + 4, heat.width(), 20), Qt::AlignCenter,
169 QStringLiteral("x | gold=shutter red=detector"));
170
171 const auto mapTraceX = [&](double t) {
172 return trace.left() + (t / std::max(1.0e-12, tMax)) * trace.width();
173 };
174 const auto mapTraceY = [&](double v) {
175 return trace.bottom() - (v / maxSignal) * trace.height();
176 };
177
178 QPainterPath path;
179 for (std::size_t i = 0; i < samples.size(); ++i) {
180 const QPointF pt(mapTraceX(samples[i].time), mapTraceY(samples[i].detectorSignal));
181 if (i == 0u) path.moveTo(pt); else path.lineTo(pt);
182 }
183 p.setPen(QPen(QColor(98, 220, 255), 2.0));
184 p.drawPath(path);
185
186 p.setPen(QPen(QColor(240, 240, 240, 110), 1.0, Qt::DashLine));
187 p.drawLine(QPointF(mapTraceX(config.openTime), trace.top()),
188 QPointF(mapTraceX(config.openTime), trace.bottom()));
189
190 p.setPen(QColor(180, 197, 210));
191 p.drawText(QRect(4, trace.top(), left - 10, 20), Qt::AlignRight | Qt::AlignVCenter,
192 QStringLiteral("Imax"));
193 p.drawText(QRect(trace.left(), trace.bottom() + 4, trace.width(), 20), Qt::AlignCenter,
194 QStringLiteral("time — detector line density I(xd,t)"));
195
196 const qpe::QuantumMoshinskySample2D &last = samples.back();
197 p.setPen(QColor(225, 235, 242));
198 p.drawText(QRect(trace.left() + 8, trace.top() + 6, trace.width() - 16, 20),
199 Qt::AlignLeft | Qt::AlignVCenter,
200 QStringLiteral("samples=%1 t=%2 I_det=%3 P(x>shutter)=%4")
201 .arg(static_cast<qulonglong>(samples.size()))
202 .arg(last.time, 0, 'f', 3)
203 .arg(last.detectorSignal, 0, 'g', 5)
204 .arg(last.frontProbability, 0, 'f', 4));
205}
void setData(const qpe::QuantumMoshinskyHistory2D &history)
Sets data.
const qpe::QuantumMoshinskyHistory2D * history_
void paintEvent(QPaintEvent *event) override
Paints event.
QSize minimumSizeHint() const override
Performs minimum size hint.
QuantumMoshinskyPlotWidget(QWidget *parent=nullptr)
Performs quantum moshinsky plot widget.
Implements the Quantum Moshinsky History2D component.
const QuantumMoshinsky2DConfig & config() const noexcept
Returns config.
double xCoordinate(std::size_t x) const noexcept
Carries out x coordinate.
const std::vector< QuantumMoshinskySample2D > & samples() const noexcept
Samples s.
bool configured() const noexcept
Returns configured.
std::size_t gridWidth() const noexcept
Carries out grid width.
const QuantumMoshinskyGeometry2D & geometry() const noexcept
Carries out geometry.
bool empty() const noexcept
Returns whether the object contains no data.
Declares the quantummoshinskyplotwidget visualization component used by QPE.
Configuration values for Quantum Moshinsky2D Config.
Data structure used by the Quantum Moshinsky Geometry2D component.
Data structure used by the Quantum Moshinsky Sample2D component.