Quantum Phenomena Explorer 7.9.8
Interactive quantum-phenomena Explorer Tutor — source documentation
Loading...
Searching...
No Matches
wavefunctionimagerenderer.cpp
Go to the documentation of this file.
1/**
2 * @file wavefunctionimagerenderer.cpp
3 * @brief Implements the wavefunctionimagerenderer visualization component used by QPE.
4 * @ingroup qpe_graphics
5 *
6 * Source: @c graphics/renderer/wavefunctionimagerenderer.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
16
17#include <QColor>
18
19#include <algorithm>
20#include <cmath>
21#include <stdexcept>
22
23namespace qpe {
24namespace {
25
26constexpr double kPi = 3.1415926535897932384626433832795;
27
28/**
29 * @brief Performs to byte.
30 * @param v The v value used by the operation.
31 */
32int toByte(double v) noexcept
33{
34 v = std::max(0.0, std::min(1.0, v));
35 return static_cast<int>(std::lround(v * 255.0));
36}
37
38/**
39 * @brief Applies gamma.
40 * @param normalizedValue The normalized value value used by the operation.
41 * @param gamma The gamma value used by the operation.
42 */
43double applyGamma(double normalizedValue, double gamma) noexcept
44{
45 return std::pow(std::max(0.0, std::min(1.0, normalizedValue)), gamma);
46}
47
48/**
49 * @brief Performs signed color.
50 * @param value Value to assign or evaluate.
51 * @param peakAbs The peak abs value used by the operation.
52 * @param gamma The gamma value used by the operation.
53 */
54QRgb signedColor(double value, double peakAbs, double gamma) noexcept
55{
56 if (!(peakAbs > 0.0)) {
57 return qRgb(0, 0, 0);
58 }
59
60 const double magnitude = applyGamma(std::abs(value) / peakAbs, gamma);
61 const int c = toByte(magnitude);
62
63 if (value > 0.0) {
64 return qRgb(c, 0, 0); // positive -> red
65 }
66 if (value < 0.0) {
67 return qRgb(0, 0, c); // negative -> blue
68 }
69 return qRgb(0, 0, 0);
70}
71
72/**
73 * @brief Performs phase color.
74 * @param v The v value used by the operation.
75 * @param peakAmplitude The peak amplitude value used by the operation.
76 * @param gamma The gamma value used by the operation.
77 */
79 double peakAmplitude,
80 double gamma) noexcept
81{
82 const double re = static_cast<double>(v.real());
83 const double im = static_cast<double>(v.imag());
84 const double amplitude = std::hypot(re, im);
85
86 if (!(peakAmplitude > 0.0) || !(amplitude > 0.0)) {
87 return qRgb(0, 0, 0);
88 }
89
90 // Hue carries phase; brightness is only an amplitude mask so that phase is
91 // not shown in regions where the wavefunction is effectively absent.
92 double hue = (std::atan2(im, re) + kPi) / (2.0 * kPi);
93 hue += 0.5; // phase 0 -> red, positive phase rotates through the hue wheel
94 hue -= std::floor(hue);
95
96 const double value = applyGamma(amplitude / peakAmplitude, gamma);
97 return QColor::fromHsvF(hue, 1.0, value).rgb();
98}
99
100/**
101 * @brief Validates options.
102 * @param options The options value used by the operation.
103 */
105{
106 if (!(options.gamma > 0.0f) || !std::isfinite(static_cast<double>(options.gamma))) {
107 throw std::invalid_argument("WaveFunctionImageRenderer: gamma must be finite and positive");
108 }
109}
110
111} // namespace
112
114{
115 double peak = 0.0;
116 for (std::size_t i = 0; i < field.size(); ++i) {
117 const auto &v = field.data()[i];
118 peak = std::max(peak,
119 std::hypot(static_cast<double>(v.real()),
120 static_cast<double>(v.imag())));
121 }
122 return peak;
123}
124
126{
127 double peak = 0.0;
128 for (std::size_t i = 0; i < field.size(); ++i) {
129 peak = std::max(peak, std::abs(static_cast<double>(field.data()[i].real())));
130 }
131 return peak;
132}
133
135{
136 double peak = 0.0;
137 for (std::size_t i = 0; i < field.size(); ++i) {
138 peak = std::max(peak, std::abs(static_cast<double>(field.data()[i].imag())));
139 }
140 return peak;
141}
142
144 WaveFunctionView view,
145 const WaveFunctionRenderOptions &options)
146{
147 if (field.empty()) {
148 return QImage();
149 }
150
151 validateOptions(options);
152
153 if (view == WaveFunctionView::Probability) {
154 ProbabilityRenderOptions probabilityOptions;
155 probabilityOptions.normalizeToPeak = true;
156 probabilityOptions.gamma = options.gamma;
157 return ProbabilityImageRenderer::render(field, probabilityOptions);
158 }
159
160 const double peakAmp = peakAmplitude(field);
161 const double peakReal = peakAbsReal(field);
162 const double peakImag = peakAbsImaginary(field);
163 const double gamma = static_cast<double>(options.gamma);
164
165 QImage image(static_cast<int>(field.width()),
166 static_cast<int>(field.height()),
167 QImage::Format_RGB32);
168
169 for (std::size_t y = 0; y < field.height(); ++y) {
170 const int imageY = static_cast<int>(field.height() - 1u - y);
171 QRgb *scanLine = reinterpret_cast<QRgb *>(image.scanLine(imageY));
172
173 for (std::size_t x = 0; x < field.width(); ++x) {
174 const auto &v = field(x, y);
175
176 switch (view) {
178 scanLine[x] = phaseColor(v, peakAmp, gamma);
179 break;
181 scanLine[x] = signedColor(static_cast<double>(v.real()), peakReal, gamma);
182 break;
184 scanLine[x] = signedColor(static_cast<double>(v.imag()), peakImag, gamma);
185 break;
187 // Handled above.
188 scanLine[x] = qRgb(0, 0, 0);
189 break;
190 }
191 }
192 }
193
194 return image;
195}
196
197} // namespace qpe
Owning Cartesian grid of complex scalar samples.
bool empty() const noexcept
Returns whether the field contains no samples.
std::size_t height() const noexcept
Returns the number of samples along Y.
std::complex< float > Value
std::size_t width() const noexcept
Returns the number of samples along X.
static QImage render(const ComplexField2D &field, const ProbabilityRenderOptions &options={})
Renders the component state.
static double peakAbsReal(const ComplexField2D &field) noexcept
Carries out peak abs real.
static double peakAbsImaginary(const ComplexField2D &field) noexcept
Carries out peak abs imaginary.
static double peakAmplitude(const ComplexField2D &field) noexcept
Carries out peak amplitude.
static QImage render(const ComplexField2D &field, WaveFunctionView view, const WaveFunctionRenderOptions &options={})
Renders the component state.
Declares the complex scalar field container representing two-dimensional wave functions.
double applyGamma(double normalizedValue, double gamma) noexcept
Applies gamma.
QRgb phaseColor(const ComplexField2D::Value &v, double peakAmplitude, double gamma) noexcept
Performs phase color.
QRgb signedColor(double value, double peakAbs, double gamma) noexcept
Performs signed color.
void validateOptions(const WaveFunctionRenderOptions &options)
Validates options.
WaveFunctionView
Enumerates the supported Wave Function View values.
Declares the probabilityimagerenderer visualization component used by QPE.
Configuration values for Probability Render Options.
Configuration values for Wave Function Render Options.
Declares the wavefunctionimagerenderer visualization component used by QPE.