28bool finite(
float value)
noexcept
30 return std::isfinite(
static_cast<double>(value));
37bool finitePositive(
float value)
noexcept
39 return finite(value) && value > 0.0f;
50 throw std::invalid_argument(message);
62 const std::vector<PotentialPoint2D> &vertices)
noexcept
65 const std::size_t n = vertices.size();
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);
73 const bool crosses = ((yi > y) != (yj > y));
75 const double xCross = (xj - xi) * (y - yi) / (yj - yi) + xi;
100 if (!finitePositive(
dx) || !finitePositive(
dy)) {
101 throw std::invalid_argument(
"PotentialField2D: dx and dy must be finite and positive");
119 std::fill(data_.begin(), data_.end(), value);
125 return data_.empty();
154 std::size_t y)
const noexcept
156 return data_[index(x, y)];
166 data_[index(x, y)] = value;
178 requireFinite(scale,
"PotentialField2D::accumulate: scale must be finite");
181 throw std::invalid_argument(
"PotentialField2D::accumulate: grid mismatch");
184 for (std::size_t i = 0; i <
data_.size(); ++i) {
197 return width_ == field.width() &&
198 height_ == field.height() &&
208 return *std::min_element(
data_.begin(),
data_.end());
216 return *std::max_element(
data_.begin(),
data_.end());
229 const double centre = 0.5 *
static_cast<double>(width_ - 1u);
230 return (
static_cast<double>(x) - centre) *
static_cast<double>(dx_);
238 const double centre = 0.5 *
static_cast<double>(height_ - 1u);
239 return (
static_cast<double>(y) - centre) *
static_cast<double>(dy_);
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");
255 const double halfW = 0.5 *
static_cast<double>(
width);
256 const double halfH = 0.5 *
static_cast<double>(
height);
258 for (std::size_t y = 0; y <
height_; ++y) {
260 if (std::abs(py -
static_cast<double>(centerY)) > halfH) {
263 for (std::size_t x = 0; x <
width_; ++x) {
265 if (std::abs(px -
static_cast<double>(centerX)) <= halfW) {
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");
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);
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");
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;
324 for (std::size_t y = 0; y <
height_; ++y) {
326 for (std::size_t x = 0; x <
width_; ++x) {
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));
338 const double dx = px - cx;
339 const double dy = py - cy;
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");
356 if (!finite(p.x) || !finite(p.y)) {
357 throw std::invalid_argument(
"PotentialField2D::addPolygon: vertices must be finite");
361 for (std::size_t y = 0; y <
height_; ++y) {
363 for (std::size_t x = 0; x <
width_; ++x) {
365 if (pointInPolygon(px, py, vertices)) {
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");
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) {
390 const bool inOpening = std::abs(py -
static_cast<double>(slitCenterY)) <= halfSlit;
394 for (std::size_t x = 0; x <
width_; ++x) {
396 if (std::abs(px -
static_cast<double>(centerX)) <= halfW) {
407 float slitSeparation,
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");
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;
424 for (std::size_t y = 0; y <
height_; ++y) {
426 const bool inOpening = std::abs(py - slit1) <= halfSlit ||
427 std::abs(py - slit2) <= halfSlit;
431 for (std::size_t x = 0; x <
width_; ++x) {
433 if (std::abs(px -
static_cast<double>(centerX)) <= halfW) {
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");
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);
462 static_cast<double>(amplitude) * std::exp(-(
dx *
dx * inv2sx2 +
dy *
dy * inv2sy2)));
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");
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);
485 static_cast<double>(offset) +
486 0.5 *
static_cast<double>(curvatureX) *
dx *
dx +
487 0.5 *
static_cast<double>(curvatureY) *
dy *
dy);
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");
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);
508 static_cast<double>(q1) * (
dx *
dx -
dy *
dy) +
509 2.0 *
static_cast<double>(q2) *
dx *
dy);
519 if (!finitePositive(period)) {
520 throw std::invalid_argument(
"PotentialField2D::addStandingWaveX: period must be finite and positive");
522 requireFinite(depth,
"PotentialField2D::addStandingWaveX: depth must be finite");
523 requireFinite(phaseDegrees,
"PotentialField2D::addStandingWaveX: phase must be finite");
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) {
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");
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);
556 static_cast<double>(offset) +
557 static_cast<double>(slopeX) *
dx +
558 static_cast<double>(slopeY) *
dy);
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");
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);
582 static_cast<double>(coupling) / std::sqrt(
dx *
dx +
dy *
dy + eps2));
590 return y * width_ + x;
596 throw std::out_of_range(
"PotentialField2D: coordinates outside field");
611 float straightLength,
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");
622 const double halfStraight = 0.5 *
static_cast<double>(straightLength);
623 const double r =
static_cast<double>(radius);
624 const double r2 = r * r;
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);
631 if (std::abs(lx) <= halfStraight) {
632 inside = std::abs(ly) <= r;
634 const double capX = lx > 0.0 ? halfStraight : -halfStraight;
635 const double dx = lx - capX;
636 inside =
dx *
dx + ly * ly <= r2;
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.