Files
Jungfraujoch/common/ROIAzimuthal.cpp
T
leonarski_fandClaude Opus 4.8 9e57219b31 common: add azimuthal-angle (phi) sectors to ROIAzimuthal
ROIAzimuthal can now be restricted to an azimuthal-angle sector in
addition to its Q/d range, enabling STXM-style directional ROIs. phi
bounds are optional (both-or-neither); absent means a full 360 ring, so
existing Q-only azimuthal ROIs are unchanged.

- ROIElement::CheckROI gains a phi_deg argument; box/circle ignore it.
- MarkROI gains an optional phi_map; ROIMap builds it from Phi_rad
  alongside the resolution map only when azimuthal ROIs are present.
- phi bounds are normalized to [0,360) and wrap-around sectors are
  supported (phi_min > phi_max).
- ROIConfigAzim carries phi_min/phi_max (kept trivially copyable for the
  union; phi_min == phi_max means full ring).

No phi crosses the wire yet (CBOR/API wiring follows separately).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 09:21:39 +02:00

88 lines
2.5 KiB
C++

// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include <cmath>
#include "JFJochMath.h"
#include "ROIAzimuthal.h"
#include "JFJochException.h"
static float NormalizePhi_deg(float phi) {
phi = std::fmod(phi, 360.0f);
if (phi < 0)
phi += 360.0f;
return phi;
}
ROIAzimuthal::ROIAzimuthal(const std::string &in_name, float in_d_min_A, float in_d_max_A,
std::optional<float> in_phi_min_deg, std::optional<float> in_phi_max_deg)
: ROIElement(in_name) {
if ((in_d_min_A <= 0) || (in_d_max_A <= 0))
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Resolution cannot be zero or negative");
if (in_d_min_A > in_d_max_A) {
d_max_A = in_d_min_A;
d_min_A = in_d_max_A;
} else {
d_max_A = in_d_max_A;
d_min_A = in_d_min_A;
}
if (in_phi_min_deg && in_phi_max_deg) {
has_phi = true;
phi_min_deg = NormalizePhi_deg(in_phi_min_deg.value());
phi_max_deg = NormalizePhi_deg(in_phi_max_deg.value());
}
}
float ROIAzimuthal::GetDMin_A() const {
return d_min_A;
}
float ROIAzimuthal::GetDMax_A() const {
return d_max_A;
}
bool ROIAzimuthal::HasPhi() const {
return has_phi;
}
float ROIAzimuthal::GetPhiMin_deg() const {
return phi_min_deg;
}
float ROIAzimuthal::GetPhiMax_deg() const {
return phi_max_deg;
}
bool ROIAzimuthal::CheckROI(int64_t x, int64_t y, float resolution, float phi_deg) const {
if (resolution < d_min_A || resolution > d_max_A)
return false;
if (!has_phi)
return true;
// A sector with phi_min > phi_max wraps across 0 degrees.
if (phi_min_deg <= phi_max_deg)
return (phi_deg >= phi_min_deg) && (phi_deg <= phi_max_deg);
else
return (phi_deg >= phi_min_deg) || (phi_deg <= phi_max_deg);
}
float ROIAzimuthal::GetQMax_recipA() const {
return 2.0f * PI / d_min_A;
}
float ROIAzimuthal::GetQMin_recipA() const {
return 2.0f * PI / d_max_A;
}
ROIConfig ROIAzimuthal::ExportMetadata() const {
double qmin = GetQMin_recipA();
double qmax = GetQMax_recipA();
return ROIConfig{
.type = ROIConfig::ROIType::Azim,
.name = name,
.azim = ROIConfigAzim{.qmin = qmin, .qmax = qmax,
.phi_min = has_phi ? phi_min_deg : 0.0f,
.phi_max = has_phi ? phi_max_deg : 0.0f}
};
}