Quantum Phenomena Explorer 7.9.8
Interactive quantum-phenomena Explorer Tutor — source documentation
Loading...
Searching...
No Matches
potentialfield2d.cpp
Go to the documentation of this file.
1/**
2 * @file potentialfield2d.cpp
3 * @brief Implements the potentialfield2d physics/numerical component used by QPE experiments.
4 * @ingroup qpe_quantum
5 *
6 * Source: @c core/quantum/potentialfield2d.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
12#include "potentialfield2d.h"
13
15
16#include <algorithm>
17#include <cmath>
18#include <limits>
19#include <stdexcept>
20
21namespace qpe {
22namespace {
23
24/**
25 * @brief Performs finite.
26 * @param value Value to assign or evaluate.
27 */
28bool finite(float value) noexcept
29{
30 return std::isfinite(static_cast<double>(value));
31}
32
33/**
34 * @brief Performs finite positive.
35 * @param value Value to assign or evaluate.
36 */
37bool finitePositive(float value) noexcept
38{
39 return finite(value) && value > 0.0f;
40}
41
42/**
43 * @brief Performs require finite.
44 * @param value Value to assign or evaluate.
45 * @param message The message value used by the operation.
46 */
47void requireFinite(float value, const char *message)
48{
49 if (!finite(value)) {
50 throw std::invalid_argument(message);
51 }
52}
53
54/**
55 * @brief Performs point in polygon.
56 * @param x X coordinate/value.
57 * @param y Y coordinate/value.
58 * @param vertices The vertices value used by the operation.
59 */
60bool pointInPolygon(double x,
61 double y,
62 const std::vector<PotentialPoint2D> &vertices) noexcept
63{
64 bool inside = false;
65 const std::size_t n = vertices.size();
66
67 for (std::size_t i = 0, j = n - 1; i < n; j = i++) {
68 const double xi = static_cast<double>(vertices[i].x);
69 const double yi = static_cast<double>(vertices[i].y);
70 const double xj = static_cast<double>(vertices[j].x);
71 const double yj = static_cast<double>(vertices[j].y);
72
73 const bool crosses = ((yi > y) != (yj > y));
74 if (crosses) {
75 const double xCross = (xj - xi) * (y - yi) / (yj - yi) + xi;
76 if (x < xCross) {
77 inside = !inside;
78 }
79 }
80 }
81
82 return inside;
83}
84
85} // namespace
86
88 std::size_t height,
89 float dx,
90 float dy)
91{
93}
94
95void PotentialField2D::resize(std::size_t width,
96 std::size_t height,
97 float dx,
98 float dy)
99{
100 if (!finitePositive(dx) || !finitePositive(dy)) {
101 throw std::invalid_argument("PotentialField2D: dx and dy must be finite and positive");
102 }
103
104 width_ = width;
105 height_ = height;
106 dx_ = dx;
107 dy_ = dy;
108 data_.assign(width_ * height_, 0.0f);
109 touch();
110}
111
113{
114 fill(0.0f);
115}
116
117void PotentialField2D::fill(Value value) noexcept
118{
119 std::fill(data_.begin(), data_.end(), value);
120 touch();
121}
122
123bool PotentialField2D::empty() const noexcept
124{
125 return data_.empty();
126}
127
128std::size_t PotentialField2D::width() const noexcept
129{
130 return width_;
131}
132
133std::size_t PotentialField2D::height() const noexcept
134{
135 return height_;
136}
137
138std::size_t PotentialField2D::size() const noexcept
139{
140 return data_.size();
141}
142
143float PotentialField2D::dx() const noexcept
144{
145 return dx_;
146}
147
148float PotentialField2D::dy() const noexcept
149{
150 return dy_;
151}
152
154 std::size_t y) const noexcept
155{
156 return data_[index(x, y)];
157}
158
159PotentialField2D::Value PotentialField2D::at(std::size_t x, std::size_t y) const
160{
161 return data_[checkedIndex(x, y)];
162}
163
164void PotentialField2D::set(std::size_t x, std::size_t y, Value value) noexcept
165{
166 data_[index(x, y)] = value;
167 touch();
168}
169
170void PotentialField2D::setAt(std::size_t x, std::size_t y, Value value)
171{
172 data_[checkedIndex(x, y)] = value;
173 touch();
174}
175
177{
178 requireFinite(scale, "PotentialField2D::accumulate: scale must be finite");
179 if (width_ != other.width_ || height_ != other.height_ ||
180 dx_ != other.dx_ || dy_ != other.dy_) {
181 throw std::invalid_argument("PotentialField2D::accumulate: grid mismatch");
182 }
183
184 for (std::size_t i = 0; i < data_.size(); ++i) {
185 data_[i] += other.data_[i] * scale;
186 }
187 touch();
188}
189
191{
192 return data_.data();
193}
194
195bool PotentialField2D::matchesGrid(const ComplexField2D &field) const noexcept
196{
197 return width_ == field.width() &&
198 height_ == field.height() &&
199 dx_ == field.dx() &&
200 dy_ == field.dy();
201}
202
204{
205 if (data_.empty()) {
206 return 0.0f;
207 }
208 return *std::min_element(data_.begin(), data_.end());
209}
210
212{
213 if (data_.empty()) {
214 return 0.0f;
215 }
216 return *std::max_element(data_.begin(), data_.end());
217}
218
219std::uint64_t PotentialField2D::revision() const noexcept
220{
221 return revision_;
222}
223
224double PotentialField2D::xCoordinate(std::size_t x) const noexcept
225{
226 if (width_ == 0) {
227 return 0.0;
228 }
229 const double centre = 0.5 * static_cast<double>(width_ - 1u);
230 return (static_cast<double>(x) - centre) * static_cast<double>(dx_);
231}
232
233double PotentialField2D::yCoordinate(std::size_t y) const noexcept
234{
235 if (height_ == 0) {
236 return 0.0;
237 }
238 const double centre = 0.5 * static_cast<double>(height_ - 1u);
239 return (static_cast<double>(y) - centre) * static_cast<double>(dy_);
240}
241
243 float centerY,
244 float width,
245 float height,
246 Value value)
247{
248 requireFinite(centerX, "PotentialField2D::addRectangle: centerX must be finite");
249 requireFinite(centerY, "PotentialField2D::addRectangle: centerY must be finite");
250 requireFinite(value, "PotentialField2D::addRectangle: value must be finite");
251 if (!finitePositive(width) || !finitePositive(height)) {
252 throw std::invalid_argument("PotentialField2D::addRectangle: width and height must be finite and positive");
253 }
254
255 const double halfW = 0.5 * static_cast<double>(width);
256 const double halfH = 0.5 * static_cast<double>(height);
257
258 for (std::size_t y = 0; y < height_; ++y) {
259 const double py = yCoordinate(y);
260 if (std::abs(py - static_cast<double>(centerY)) > halfH) {
261 continue;
262 }
263 for (std::size_t x = 0; x < width_; ++x) {
264 const double px = xCoordinate(x);
265 if (std::abs(px - static_cast<double>(centerX)) <= halfW) {
266 data_[index(x, y)] = value;
267 }
268 }
269 }
270 touch();
271}
272
274 float centerY,
275 float radius,
276 Value value)
277{
278 requireFinite(centerX, "PotentialField2D::addCircle: centerX must be finite");
279 requireFinite(centerY, "PotentialField2D::addCircle: centerY must be finite");
280 requireFinite(value, "PotentialField2D::addCircle: value must be finite");
281 if (!finitePositive(radius)) {
282 throw std::invalid_argument("PotentialField2D::addCircle: radius must be finite and positive");
283 }
284
285 const double r2 = static_cast<double>(radius) * static_cast<double>(radius);
286 for (std::size_t y = 0; y < height_; ++y) {
287 const double dy = yCoordinate(y) - static_cast<double>(centerY);
288 for (std::size_t x = 0; x < width_; ++x) {
289 const double dx = xCoordinate(x) - static_cast<double>(centerX);
290 if (dx * dx + dy * dy <= r2) {
291 data_[index(x, y)] = value;
292 }
293 }
294 }
295 touch();
296}
297
299 float y1,
300 float x2,
301 float y2,
302 float thickness,
303 Value value)
304{
305 requireFinite(x1, "PotentialField2D::addBarrier: x1 must be finite");
306 requireFinite(y1, "PotentialField2D::addBarrier: y1 must be finite");
307 requireFinite(x2, "PotentialField2D::addBarrier: x2 must be finite");
308 requireFinite(y2, "PotentialField2D::addBarrier: y2 must be finite");
309 requireFinite(value, "PotentialField2D::addBarrier: value must be finite");
310 if (!finitePositive(thickness)) {
311 throw std::invalid_argument("PotentialField2D::addBarrier: thickness must be finite and positive");
312 }
313
314 const double ax = static_cast<double>(x1);
315 const double ay = static_cast<double>(y1);
316 const double bx = static_cast<double>(x2);
317 const double by = static_cast<double>(y2);
318 const double vx = bx - ax;
319 const double vy = by - ay;
320 const double length2 = vx * vx + vy * vy;
321 const double halfT = 0.5 * static_cast<double>(thickness);
322 const double limit2 = halfT * halfT;
323
324 for (std::size_t y = 0; y < height_; ++y) {
325 const double py = yCoordinate(y);
326 for (std::size_t x = 0; x < width_; ++x) {
327 const double px = xCoordinate(x);
328
329 double cx = ax;
330 double cy = ay;
331 if (length2 > std::numeric_limits<double>::epsilon()) {
332 double t = ((px - ax) * vx + (py - ay) * vy) / length2;
333 t = std::max(0.0, std::min(1.0, t));
334 cx = ax + t * vx;
335 cy = ay + t * vy;
336 }
337
338 const double dx = px - cx;
339 const double dy = py - cy;
340 if (dx * dx + dy * dy <= limit2) {
341 data_[index(x, y)] = value;
342 }
343 }
344 }
345 touch();
346}
347
348void PotentialField2D::addPolygon(const std::vector<PotentialPoint2D> &vertices,
349 Value value)
350{
351 requireFinite(value, "PotentialField2D::addPolygon: value must be finite");
352 if (vertices.size() < 3) {
353 throw std::invalid_argument("PotentialField2D::addPolygon: at least three vertices are required");
354 }
355 for (const PotentialPoint2D &p : vertices) {
356 if (!finite(p.x) || !finite(p.y)) {
357 throw std::invalid_argument("PotentialField2D::addPolygon: vertices must be finite");
358 }
359 }
360
361 for (std::size_t y = 0; y < height_; ++y) {
362 const double py = yCoordinate(y);
363 for (std::size_t x = 0; x < width_; ++x) {
364 const double px = xCoordinate(x);
365 if (pointInPolygon(px, py, vertices)) {
366 data_[index(x, y)] = value;
367 }
368 }
369 }
370 touch();
371}
372
374 float barrierWidth,
375 float slitCenterY,
376 float slitHeight,
377 Value value)
378{
379 requireFinite(centerX, "PotentialField2D::addSingleSlit: centerX must be finite");
380 requireFinite(slitCenterY, "PotentialField2D::addSingleSlit: slitCenterY must be finite");
381 requireFinite(value, "PotentialField2D::addSingleSlit: value must be finite");
382 if (!finitePositive(barrierWidth) || !finitePositive(slitHeight)) {
383 throw std::invalid_argument("PotentialField2D::addSingleSlit: dimensions must be finite and positive");
384 }
385
386 const double halfW = 0.5 * static_cast<double>(barrierWidth);
387 const double halfSlit = 0.5 * static_cast<double>(slitHeight);
388 for (std::size_t y = 0; y < height_; ++y) {
389 const double py = yCoordinate(y);
390 const bool inOpening = std::abs(py - static_cast<double>(slitCenterY)) <= halfSlit;
391 if (inOpening) {
392 continue;
393 }
394 for (std::size_t x = 0; x < width_; ++x) {
395 const double px = xCoordinate(x);
396 if (std::abs(px - static_cast<double>(centerX)) <= halfW) {
397 data_[index(x, y)] = value;
398 }
399 }
400 }
401 touch();
402}
403
405 float barrierWidth,
406 float slitCenterY,
407 float slitSeparation,
408 float slitHeight,
409 Value value)
410{
411 requireFinite(centerX, "PotentialField2D::addDoubleSlit: centerX must be finite");
412 requireFinite(slitCenterY, "PotentialField2D::addDoubleSlit: slitCenterY must be finite");
413 requireFinite(value, "PotentialField2D::addDoubleSlit: value must be finite");
414 if (!finitePositive(barrierWidth) || !finitePositive(slitSeparation) || !finitePositive(slitHeight)) {
415 throw std::invalid_argument("PotentialField2D::addDoubleSlit: dimensions must be finite and positive");
416 }
417
418 const double halfW = 0.5 * static_cast<double>(barrierWidth);
419 const double halfSlit = 0.5 * static_cast<double>(slitHeight);
420 const double halfSep = 0.5 * static_cast<double>(slitSeparation);
421 const double slit1 = static_cast<double>(slitCenterY) - halfSep;
422 const double slit2 = static_cast<double>(slitCenterY) + halfSep;
423
424 for (std::size_t y = 0; y < height_; ++y) {
425 const double py = yCoordinate(y);
426 const bool inOpening = std::abs(py - slit1) <= halfSlit ||
427 std::abs(py - slit2) <= halfSlit;
428 if (inOpening) {
429 continue;
430 }
431 for (std::size_t x = 0; x < width_; ++x) {
432 const double px = xCoordinate(x);
433 if (std::abs(px - static_cast<double>(centerX)) <= halfW) {
434 data_[index(x, y)] = value;
435 }
436 }
437 }
438 touch();
439}
440
441
443 float centerY,
444 float sigmaX,
445 float sigmaY,
446 Value amplitude)
447{
448 requireFinite(centerX, "PotentialField2D::addGaussian: centerX must be finite");
449 requireFinite(centerY, "PotentialField2D::addGaussian: centerY must be finite");
450 requireFinite(amplitude, "PotentialField2D::addGaussian: amplitude must be finite");
451 if (!finitePositive(sigmaX) || !finitePositive(sigmaY)) {
452 throw std::invalid_argument("PotentialField2D::addGaussian: sigmas must be finite and positive");
453 }
454
455 const double inv2sx2 = 1.0 / (2.0 * static_cast<double>(sigmaX) * static_cast<double>(sigmaX));
456 const double inv2sy2 = 1.0 / (2.0 * static_cast<double>(sigmaY) * static_cast<double>(sigmaY));
457 for (std::size_t y = 0; y < height_; ++y) {
458 const double dy = yCoordinate(y) - static_cast<double>(centerY);
459 for (std::size_t x = 0; x < width_; ++x) {
460 const double dx = xCoordinate(x) - static_cast<double>(centerX);
461 data_[index(x, y)] += static_cast<Value>(
462 static_cast<double>(amplitude) * std::exp(-(dx * dx * inv2sx2 + dy * dy * inv2sy2)));
463 }
464 }
465 touch();
466}
467
469 float centerY,
470 Value offset,
471 float curvatureX,
472 float curvatureY)
473{
474 requireFinite(centerX, "PotentialField2D::addHarmonic: centerX must be finite");
475 requireFinite(centerY, "PotentialField2D::addHarmonic: centerY must be finite");
476 requireFinite(offset, "PotentialField2D::addHarmonic: offset must be finite");
477 requireFinite(curvatureX, "PotentialField2D::addHarmonic: curvatureX must be finite");
478 requireFinite(curvatureY, "PotentialField2D::addHarmonic: curvatureY must be finite");
479
480 for (std::size_t y = 0; y < height_; ++y) {
481 const double dy = yCoordinate(y) - static_cast<double>(centerY);
482 for (std::size_t x = 0; x < width_; ++x) {
483 const double dx = xCoordinate(x) - static_cast<double>(centerX);
484 data_[index(x, y)] += static_cast<Value>(
485 static_cast<double>(offset) +
486 0.5 * static_cast<double>(curvatureX) * dx * dx +
487 0.5 * static_cast<double>(curvatureY) * dy * dy);
488 }
489 }
490 touch();
491}
492
494 float centerY,
495 Value q1,
496 Value q2)
497{
498 requireFinite(centerX, "PotentialField2D::addQuadrupole: centerX must be finite");
499 requireFinite(centerY, "PotentialField2D::addQuadrupole: centerY must be finite");
500 requireFinite(q1, "PotentialField2D::addQuadrupole: q1 must be finite");
501 requireFinite(q2, "PotentialField2D::addQuadrupole: q2 must be finite");
502
503 for (std::size_t y = 0; y < height_; ++y) {
504 const double dy = yCoordinate(y) - static_cast<double>(centerY);
505 for (std::size_t x = 0; x < width_; ++x) {
506 const double dx = xCoordinate(x) - static_cast<double>(centerX);
507 data_[index(x, y)] += static_cast<Value>(
508 static_cast<double>(q1) * (dx * dx - dy * dy) +
509 2.0 * static_cast<double>(q2) * dx * dy);
510 }
511 }
512 touch();
513}
514
516 Value depth,
517 float phaseDegrees)
518{
519 if (!finitePositive(period)) {
520 throw std::invalid_argument("PotentialField2D::addStandingWaveX: period must be finite and positive");
521 }
522 requireFinite(depth, "PotentialField2D::addStandingWaveX: depth must be finite");
523 requireFinite(phaseDegrees, "PotentialField2D::addStandingWaveX: phase must be finite");
524
525 const double pi = 3.141592653589793238462643383279502884;
526 const double halfPhase = 0.5 * static_cast<double>(phaseDegrees) * pi / 180.0;
527 const double wave = pi / static_cast<double>(period);
528 for (std::size_t x = 0; x < width_; ++x) {
529 const double argument = wave * xCoordinate(x) + halfPhase;
530 const double c = std::cos(argument);
531 const Value v = static_cast<Value>(static_cast<double>(depth) * c * c);
532 for (std::size_t y = 0; y < height_; ++y) {
533 data_[index(x, y)] += v;
534 }
535 }
536 touch();
537}
538
540 float centerY,
541 Value offset,
542 float slopeX,
543 float slopeY)
544{
545 requireFinite(centerX, "PotentialField2D::addLinear: centerX must be finite");
546 requireFinite(centerY, "PotentialField2D::addLinear: centerY must be finite");
547 requireFinite(offset, "PotentialField2D::addLinear: offset must be finite");
548 requireFinite(slopeX, "PotentialField2D::addLinear: slopeX must be finite");
549 requireFinite(slopeY, "PotentialField2D::addLinear: slopeY must be finite");
550
551 for (std::size_t y = 0; y < height_; ++y) {
552 const double dy = yCoordinate(y) - static_cast<double>(centerY);
553 for (std::size_t x = 0; x < width_; ++x) {
554 const double dx = xCoordinate(x) - static_cast<double>(centerX);
555 data_[index(x, y)] += static_cast<Value>(
556 static_cast<double>(offset) +
557 static_cast<double>(slopeX) * dx +
558 static_cast<double>(slopeY) * dy);
559 }
560 }
561 touch();
562}
563
565 float centerY,
566 Value coupling,
567 float softening)
568{
569 requireFinite(centerX, "PotentialField2D::addSoftenedRadial: centerX must be finite");
570 requireFinite(centerY, "PotentialField2D::addSoftenedRadial: centerY must be finite");
571 requireFinite(coupling, "PotentialField2D::addSoftenedRadial: coupling must be finite");
572 if (!finitePositive(softening)) {
573 throw std::invalid_argument("PotentialField2D::addSoftenedRadial: softening must be finite and positive");
574 }
575
576 const double eps2 = static_cast<double>(softening) * static_cast<double>(softening);
577 for (std::size_t y = 0; y < height_; ++y) {
578 const double dy = yCoordinate(y) - static_cast<double>(centerY);
579 for (std::size_t x = 0; x < width_; ++x) {
580 const double dx = xCoordinate(x) - static_cast<double>(centerX);
581 data_[index(x, y)] += static_cast<Value>(
582 static_cast<double>(coupling) / std::sqrt(dx * dx + dy * dy + eps2));
583 }
584 }
585 touch();
586}
587
588std::size_t PotentialField2D::index(std::size_t x, std::size_t y) const noexcept
589{
590 return y * width_ + x;
591}
592
593std::size_t PotentialField2D::checkedIndex(std::size_t x, std::size_t y) const
594{
595 if (x >= width_ || y >= height_) {
596 throw std::out_of_range("PotentialField2D: coordinates outside field");
597 }
598 return index(x, y);
599}
600
602{
603 ++revision_;
604 if (revision_ == 0) {
605 revision_ = 1;
606 }
607}
608
610 float centerY,
611 float straightLength,
612 float radius,
613 Value value)
614{
615 requireFinite(centerX, "PotentialField2D::addStadiumExterior: centerX must be finite");
616 requireFinite(centerY, "PotentialField2D::addStadiumExterior: centerY must be finite");
617 requireFinite(value, "PotentialField2D::addStadiumExterior: value must be finite");
618 if (!finitePositive(straightLength) || !finitePositive(radius)) {
619 throw std::invalid_argument("PotentialField2D::addStadiumExterior: dimensions must be finite and positive");
620 }
621
622 const double halfStraight = 0.5 * static_cast<double>(straightLength);
623 const double r = static_cast<double>(radius);
624 const double r2 = r * r;
625
626 for (std::size_t y = 0; y < height_; ++y) {
627 const double ly = yCoordinate(y) - static_cast<double>(centerY);
628 for (std::size_t x = 0; x < width_; ++x) {
629 const double lx = xCoordinate(x) - static_cast<double>(centerX);
630 bool inside = false;
631 if (std::abs(lx) <= halfStraight) {
632 inside = std::abs(ly) <= r;
633 } else {
634 const double capX = lx > 0.0 ? halfStraight : -halfStraight;
635 const double dx = lx - capX;
636 inside = dx * dx + ly * ly <= r2;
637 }
638 if (!inside) {
639 data_[index(x, y)] = value;
640 }
641 }
642 }
643 touch();
644}
645
646} // namespace qpe
Owning Cartesian grid of complex scalar samples.
Implements the Potential Field2D component.
Value at(std::size_t x, std::size_t y) const
Performs at.
void addHarmonic(float centerX, float centerY, Value offset, float curvatureX, float curvatureY)
Adds harmonic.
std::size_t height() const noexcept
Returns height.
void addLinear(float centerX, float centerY, Value offset, float slopeX, float slopeY)
Adds linear.
void clear() noexcept
Performs clear.
void fill(Value value) noexcept
Performs fill.
std::uint64_t revision() const noexcept
Performs revision.
void resize(std::size_t width, std::size_t height, float dx=1.0f, float dy=1.0f)
Performs resize.
void addPolygon(const std::vector< PotentialPoint2D > &vertices, Value value)
Adds polygon.
void addRectangle(float centerX, float centerY, float width, float height, Value value)
Adds rectangle.
double xCoordinate(std::size_t x) const noexcept
Performs x coordinate.
std::size_t checkedIndex(std::size_t x, std::size_t y) const
Checks ed index.
Value minimum() const noexcept
Performs minimum.
std::size_t size() const noexcept
Returns size.
float dy() const noexcept
Performs dy.
void touch() noexcept
Performs touch.
bool matchesGrid(const ComplexField2D &field) const noexcept
Performs matches grid.
const Value * data() const noexcept
Performs data.
void setAt(std::size_t x, std::size_t y, Value value)
Sets at.
Value operator()(std::size_t x, std::size_t y) const noexcept
Returns the potential sample at a grid coordinate without bounds checking.
void addCircle(float centerX, float centerY, float radius, Value value)
Adds circle.
bool empty() const noexcept
Performs empty.
std::size_t index(std::size_t x, std::size_t y) const noexcept
Returns index.
void addGaussian(float centerX, float centerY, float sigmaX, float sigmaY, Value amplitude)
Adds gaussian.
std::size_t width() const noexcept
Returns width.
std::vector< Value > data_
void addStandingWaveX(float period, Value depth, float phaseDegrees=0.0f)
Adds standing wave x.
double yCoordinate(std::size_t y) const noexcept
Performs y coordinate.
float dx() const noexcept
Performs dx.
PotentialField2D()=default
Performs potential field2 d.
void addQuadrupole(float centerX, float centerY, Value q1, Value q2)
Adds quadrupole.
void addBarrier(float x1, float y1, float x2, float y2, float thickness, Value value)
Adds barrier.
void set(std::size_t x, std::size_t y, Value value) noexcept
Performs set.
void addStadiumExterior(float centerX, float centerY, float straightLength, float radius, Value value)
Adds stadium exterior.
void addSoftenedRadial(float centerX, float centerY, Value coupling, float softening)
Adds softened radial.
void accumulate(const PotentialField2D &other, Value scale=1.0f)
Performs accumulate.
void addDoubleSlit(float centerX, float barrierWidth, float slitCenterY, float slitSeparation, float slitHeight, Value value)
Adds double slit.
Value maximum() const noexcept
Performs maximum.
void addSingleSlit(float centerX, float barrierWidth, float slitCenterY, float slitHeight, Value value)
Adds single slit.
Declares the complex scalar field container representing two-dimensional wave functions.
bool pointInPolygon(double x, double y, const std::vector< PotentialPoint2D > &vertices) noexcept
Performs point in polygon.
void requireFinite(float value, const char *message)
Performs require finite.
Declares the potentialfield2d physics/numerical component used by QPE experiments.
Data structure used by the Potential Point2D component.