Quantum Phenomena Explorer 7.9.8
Interactive quantum-phenomena Explorer Tutor — source documentation
Loading...
Searching...
No Matches
potentialcontours2d.cpp
Go to the documentation of this file.
1/**
2 * @file potentialcontours2d.cpp
3 * @brief Implements the potentialcontours2d physics/numerical component used by QPE experiments.
4 * @ingroup qpe_quantum
5 *
6 * Source: @c core/quantum/potentialcontours2d.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
14
15#include <algorithm>
16#include <cmath>
17
18namespace qpe {
19namespace {
20
21struct P2 { float x; float y; };
22
23/**
24 * @brief Performs interpolate.
25 * @param a The a value used by the operation.
26 * @param b The b value used by the operation.
27 * @param va The va value used by the operation.
28 * @param vb The vb value used by the operation.
29 * @param level The level value used by the operation.
30 */
31P2 interpolate(const P2 &a, const P2 &b, float va, float vb, float level)
32{
33 const float dv = vb - va;
34 float t = 0.5f;
35 if (std::fabs(dv) > 1.0e-20f) {
36 t = (level - va) / dv;
37 }
38 t = std::max(0.0f, std::min(1.0f, t));
39 P2 p;
40 p.x = a.x + t * (b.x - a.x);
41 p.y = a.y + t * (b.y - a.y);
42 return p;
43}
44
45/**
46 * @brief Adds segment.
47 * @param out The out value used by the operation.
48 * @param a The a value used by the operation.
49 * @param b The b value used by the operation.
50 * @param level The level value used by the operation.
51 */
52void addSegment(PotentialContours2DResult &out, const P2 &a, const P2 &b, float level)
53{
55 s.x1 = a.x; s.y1 = a.y;
56 s.x2 = b.x; s.y2 = b.y;
57 s.level = level;
58 out.segments.push_back(s);
59}
60
61} // namespace
62
64 std::size_t requestedLevels)
65{
67 if (potential.empty() || potential.width() < 2u || potential.height() < 2u || requestedLevels == 0u) {
68 return out;
69 }
70
71 out.minimum = potential.minimum();
72 out.maximum = potential.maximum();
73 const float range = out.maximum - out.minimum;
74 if (!(range > 1.0e-12f)) {
75 return out;
76 }
77
78 bool onlyExtremes = true;
79 const float eps = std::max(1.0e-7f, std::fabs(range) * 1.0e-6f);
80 for (std::size_t y = 0; y < potential.height() && onlyExtremes; ++y) {
81 for (std::size_t x = 0; x < potential.width(); ++x) {
82 const float v = potential(x, y);
83 if (std::fabs(v - out.minimum) > eps && std::fabs(v - out.maximum) > eps) {
84 onlyExtremes = false;
85 break;
86 }
87 }
88 }
89 out.binaryField = onlyExtremes;
90
91 if (onlyExtremes) {
92 out.levels.push_back(out.minimum + 0.5f * range);
93 } else {
94 const std::size_t count = std::max<std::size_t>(1u, requestedLevels);
95 for (std::size_t i = 0; i < count; ++i) {
96 const float f = static_cast<float>(i + 1u) / static_cast<float>(count + 1u);
97 out.levels.push_back(out.minimum + f * range);
98 }
99 }
100
101 for (std::size_t li = 0; li < out.levels.size(); ++li) {
102 const float level = out.levels[li];
103 for (std::size_t y = 0; y + 1u < potential.height(); ++y) {
104 for (std::size_t x = 0; x + 1u < potential.width(); ++x) {
105 const float v0 = potential(x, y); // bottom-left
106 const float v1 = potential(x + 1u, y); // bottom-right
107 const float v2 = potential(x + 1u, y + 1u); // top-right
108 const float v3 = potential(x, y + 1u); // top-left
109
110 int mask = 0;
111 if (v0 > level) mask |= 1;
112 if (v1 > level) mask |= 2;
113 if (v2 > level) mask |= 4;
114 if (v3 > level) mask |= 8;
115 if (mask == 0 || mask == 15) continue;
116
117 const P2 p0 = { static_cast<float>(potential.xCoordinate(x)),
118 static_cast<float>(potential.yCoordinate(y)) };
119 const P2 p1 = { static_cast<float>(potential.xCoordinate(x + 1u)),
120 static_cast<float>(potential.yCoordinate(y)) };
121 const P2 p2 = { static_cast<float>(potential.xCoordinate(x + 1u)),
122 static_cast<float>(potential.yCoordinate(y + 1u)) };
123 const P2 p3 = { static_cast<float>(potential.xCoordinate(x)),
124 static_cast<float>(potential.yCoordinate(y + 1u)) };
125
126 P2 edge[4];
127 edge[0] = interpolate(p0, p1, v0, v1, level); // bottom
128 edge[1] = interpolate(p1, p2, v1, v2, level); // right
129 edge[2] = interpolate(p2, p3, v2, v3, level); // top
130 edge[3] = interpolate(p3, p0, v3, v0, level); // left
131
132 switch (mask) {
133 case 1: case 14: addSegment(out, edge[3], edge[0], level); break;
134 case 2: case 13: addSegment(out, edge[0], edge[1], level); break;
135 case 3: case 12: addSegment(out, edge[3], edge[1], level); break;
136 case 4: case 11: addSegment(out, edge[1], edge[2], level); break;
137 case 6: case 9: addSegment(out, edge[0], edge[2], level); break;
138 case 7: case 8: addSegment(out, edge[3], edge[2], level); break;
139 case 5: {
140 const float center = 0.25f * (v0 + v1 + v2 + v3);
141 if (center > level) {
142 addSegment(out, edge[3], edge[2], level);
143 addSegment(out, edge[0], edge[1], level);
144 } else {
145 addSegment(out, edge[3], edge[0], level);
146 addSegment(out, edge[2], edge[1], level);
147 }
148 break;
149 }
150 case 10: {
151 const float center = 0.25f * (v0 + v1 + v2 + v3);
152 if (center > level) {
153 addSegment(out, edge[3], edge[0], level);
154 addSegment(out, edge[2], edge[1], level);
155 } else {
156 addSegment(out, edge[3], edge[2], level);
157 addSegment(out, edge[0], edge[1], level);
158 }
159 break;
160 }
161 default: break;
162 }
163 }
164 }
165 }
166
167 return out;
168}
169
170} // namespace qpe
static PotentialContours2DResult compute(const PotentialField2D &potential, std::size_t requestedLevels)
Performs compute.
Implements the Potential Field2D component.
std::size_t height() const noexcept
Returns height.
double xCoordinate(std::size_t x) const noexcept
Performs x coordinate.
Value minimum() const noexcept
Performs minimum.
bool empty() const noexcept
Performs empty.
std::size_t width() const noexcept
Returns width.
double yCoordinate(std::size_t y) const noexcept
Performs y coordinate.
Value maximum() const noexcept
Performs maximum.
P2 interpolate(const P2 &a, const P2 &b, float va, float vb, float level)
Performs interpolate.
void addSegment(PotentialContours2DResult &out, const P2 &a, const P2 &b, float level)
Adds segment.
Declares the potentialcontours2d physics/numerical component used by QPE experiments.
Declares the potentialfield2d physics/numerical component used by QPE experiments.
Data structure used by the Potential Contour Segment2D component.
Data record containing Potential Contours2D Result.
std::vector< PotentialContourSegment2D > segments