Quantum Phenomena Explorer 7.9.8
Interactive quantum-phenomena Explorer Tutor — source documentation
Loading...
Searching...
No Matches
quantumboundstateplotwidget.cpp
Go to the documentation of this file.
1/**
2 * @file quantumboundstateplotwidget.cpp
3 * @brief Implements the quantumboundstateplotwidget visualization component used by QPE.
4 * @ingroup qpe_graphics
5 *
6 * Source: @c graphics/renderer/quantumboundstateplotwidget.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 <QPaintEvent>
15#include <QPainter>
16#include <QPainterPath>
17
18#include <algorithm>
19#include <cmath>
20
22 : QWidget(parent)
23{
24 setMinimumHeight(170);
25 setAutoFillBackground(false);
26}
27
29 int selectedStateIndex)
30{
31 spectrum_ = spectrum;
32 selectedStateIndex_ = selectedStateIndex;
33 update();
34}
35
42
44{
45 return QSize(480, 170);
46}
47
49{
50 Q_UNUSED(event);
51
52 QPainter painter(this);
53 painter.setRenderHint(QPainter::Antialiasing, true);
54 painter.setRenderHint(QPainter::TextAntialiasing, true);
55 painter.fillRect(rect(), QColor(15, 18, 23));
56
57 const QRectF plotRect(62.0, 26.0,
58 std::max(10.0, width() - 84.0),
59 std::max(10.0, height() - 64.0));
60
61 painter.setPen(QPen(QColor(78, 86, 96), 1.0));
62 for (int i = 0; i <= 4; ++i) {
63 const double u = static_cast<double>(i) / 4.0;
64 painter.drawLine(QPointF(plotRect.left() + u * plotRect.width(), plotRect.top()),
65 QPointF(plotRect.left() + u * plotRect.width(), plotRect.bottom()));
66 painter.drawLine(QPointF(plotRect.left(), plotRect.top() + u * plotRect.height()),
67 QPointF(plotRect.right(), plotRect.top() + u * plotRect.height()));
68 }
69 painter.setPen(QPen(QColor(170, 180, 190), 1.0));
70 painter.drawRect(plotRect);
71
72 if (!spectrum_.valid || spectrum_.x.size() < 2u || spectrum_.states.empty()) {
73 painter.setPen(QColor(190, 195, 205));
74 painter.drawText(plotRect, Qt::AlignCenter,
75 QStringLiteral("Compute bound states for a Y-invariant confining potential"));
76 return;
77 }
78
79 const double xMin = spectrum_.x.front();
80 const double xMax = spectrum_.x.back();
81 double yMin = *std::min_element(spectrum_.potential.begin(), spectrum_.potential.end());
82 double yMax = *std::max_element(spectrum_.potential.begin(), spectrum_.potential.end());
83 for (const qpe::BoundState1D &state : spectrum_.states) {
84 yMin = std::min(yMin, state.energy);
85 yMax = std::max(yMax, state.energy);
86 }
87 yMax = std::max(yMax, spectrum_.continuumThreshold);
88 const double yPad = 0.08 * std::max(1.0e-9, yMax - yMin);
89 yMin -= yPad;
90 yMax += yPad;
91
92 const auto mapX = [plotRect, xMin, xMax](double value) {
93 const double u = (value - xMin) / std::max(1.0e-30, xMax - xMin);
94 return plotRect.left() + u * plotRect.width();
95 };
96 const auto mapY = [plotRect, yMin, yMax](double value) {
97 const double u = (value - yMin) / std::max(1.0e-30, yMax - yMin);
98 return plotRect.bottom() - u * plotRect.height();
99 };
100
101 // Potential profile.
102 QPainterPath potentialPath;
103 for (std::size_t i = 0u; i < spectrum_.x.size(); ++i) {
104 const QPointF p(mapX(spectrum_.x[i]), mapY(spectrum_.potential[i]));
105 if (i == 0u) potentialPath.moveTo(p); else potentialPath.lineTo(p);
106 }
107 painter.setPen(QPen(QColor(185, 190, 198), 2.0));
108 painter.drawPath(potentialPath);
109
110 // Continuum/escape threshold.
111 const double thresholdY = mapY(spectrum_.continuumThreshold);
112 painter.setPen(QPen(QColor(130, 140, 150), 1.0, Qt::DashLine));
113 painter.drawLine(QPointF(plotRect.left(), thresholdY), QPointF(plotRect.right(), thresholdY));
114 painter.setPen(QColor(145, 152, 162));
115 painter.drawText(QRectF(plotRect.right() - 150.0, thresholdY - 18.0, 146.0, 18.0),
116 Qt::AlignRight | Qt::AlignVCenter,
117 QStringLiteral("continuum %1").arg(spectrum_.continuumThreshold, 0, 'g', 5));
118
119 // Energy ladder. Selected state is highlighted.
120 for (std::size_t i = 0u; i < spectrum_.states.size(); ++i) {
121 const qpe::BoundState1D &state = spectrum_.states[i];
122 const bool selected = static_cast<int>(i) == selectedStateIndex_;
123 painter.setPen(QPen(selected ? QColor(255, 184, 70) : QColor(82, 190, 235),
124 selected ? 2.0 : 1.2,
125 selected ? Qt::SolidLine : Qt::DashLine));
126 const double y = mapY(state.energy);
127 painter.drawLine(QPointF(plotRect.left(), y), QPointF(plotRect.right(), y));
128 painter.setPen(selected ? QColor(255, 205, 115) : QColor(130, 205, 235));
129 painter.drawText(QRectF(plotRect.left() + 5.0, y - 18.0, 130.0, 18.0),
130 Qt::AlignLeft | Qt::AlignVCenter,
131 QStringLiteral("n=%1 E=%2")
132 .arg(state.quantumNumber)
133 .arg(state.energy, 0, 'g', 5));
134 }
135
136 if (selectedStateIndex_ >= 0 &&
137 selectedStateIndex_ < static_cast<int>(spectrum_.states.size())) {
138 const qpe::BoundState1D &state = spectrum_.states[static_cast<std::size_t>(selectedStateIndex_)];
139 float maxAbs = 0.0f;
140 for (float value : state.amplitude) {
141 maxAbs = std::max(maxAbs, std::abs(value));
142 }
143 if (maxAbs > 0.0f) {
144 // Classic textbook view: draw phi_n vertically around its energy line.
145 const double energySpan = std::max(1.0e-9, yMax - yMin);
146 const double amplitudeEnergyScale = 0.075 * energySpan / static_cast<double>(maxAbs);
147 QPainterPath statePath;
148 for (std::size_t i = 0u; i < spectrum_.x.size(); ++i) {
149 const double displayEnergy = state.energy +
150 amplitudeEnergyScale * static_cast<double>(state.amplitude[i]);
151 const QPointF p(mapX(spectrum_.x[i]), mapY(displayEnergy));
152 if (i == 0u) statePath.moveTo(p); else statePath.lineTo(p);
153 }
154 painter.setPen(QPen(QColor(255, 220, 120), 2.2));
155 painter.drawPath(statePath);
156
157 // Mark zero crossings of the selected real eigenfunction.
158 painter.setBrush(QColor(255, 115, 125));
159 painter.setPen(Qt::NoPen);
160 const float nodeThreshold = 0.015f * maxAbs;
161 for (std::size_t i = 1u; i < state.amplitude.size(); ++i) {
162 const float a = state.amplitude[i - 1u];
163 const float b = state.amplitude[i];
164 if (std::max(std::abs(a), std::abs(b)) >= nodeThreshold &&
165 ((a < 0.0f && b > 0.0f) || (a > 0.0f && b < 0.0f))) {
166 const double t = std::abs(static_cast<double>(a)) /
167 std::max(1.0e-30, std::abs(static_cast<double>(a)) +
168 std::abs(static_cast<double>(b)));
169 const double x = spectrum_.x[i - 1u] +
170 t * (spectrum_.x[i] - spectrum_.x[i - 1u]);
171 painter.drawEllipse(QPointF(mapX(x), mapY(state.energy)), 3.2, 3.2);
172 }
173 }
174 }
175 }
176
177 painter.setPen(QColor(210, 215, 222));
178 painter.drawText(QRectF(plotRect.left(), 2.0, plotRect.width(), 20.0),
179 Qt::AlignCenter,
180 QStringLiteral("Bound-state ladder: V(x), E_n and selected eigenfunction"));
181 painter.setPen(QColor(160, 168, 178));
182 painter.drawText(QRectF(plotRect.left(), plotRect.bottom() + 5.0, plotRect.width(), 22.0),
183 Qt::AlignCenter, QStringLiteral("x"));
184 painter.drawText(QRectF(2.0, plotRect.top() - 6.0, 55.0, 18.0),
185 Qt::AlignRight | Qt::AlignVCenter,
186 QString::number(yMax, 'g', 4));
187 painter.drawText(QRectF(2.0, plotRect.bottom() - 10.0, 55.0, 18.0),
188 Qt::AlignRight | Qt::AlignVCenter,
189 QString::number(yMin, 'g', 4));
190}
void paintEvent(QPaintEvent *event) override
Paints event.
void setSpectrum(const qpe::BoundStateSpectrum1D &spectrum, int selectedStateIndex)
Sets spectrum.
QSize minimumSizeHint() const override
Performs minimum size hint.
QuantumBoundStatePlotWidget(QWidget *parent=nullptr)
Performs quantum bound state plot widget.
Declares the quantumboundstateplotwidget visualization component used by QPE.
Data structure used by the Bound State1D component.
std::vector< float > amplitude
Data structure used by the Bound State Spectrum1D component.
std::vector< double > x
std::vector< BoundState1D > states
std::vector< double > potential