preview: draw azimuthal ROIs

AddROI now renders azimuthal ROIs in addition to box and circle ROIs:
inner/outer arcs at the Q (d) bounds and, for a sector, the two radial
edges at the phi bounds. The per-pixel phi comes from the same
DiffractionGeometry::Phi_rad used to assign ROIs, so the overlay matches
the ROI footprint exactly (including wrap-around sectors). Full-ring
azimuthal ROIs draw as two concentric arcs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 09:44:57 +02:00
co-authored by Claude Opus 4.8
parent 03814b1425
commit 2bd276ae1b
+44
View File
@@ -36,6 +36,20 @@ constexpr const static rgb plotly[] = {{0x1f, 0x77, 0xb4},
constexpr const static rgb gray = {.r = 0xbe, .g = 0xbe, .b = 0xbe};
// Smallest difference between two angles in degrees, accounting for wrap-around.
static float AngularDistance_deg(float a, float b) {
float d = std::fabs(a - b);
if (d > 180.0f)
d = 360.0f - d;
return d;
}
static bool InPhiSector(float phi_deg, float phi_min, float phi_max) {
if (phi_min <= phi_max)
return phi_deg >= phi_min && phi_deg <= phi_max;
return phi_deg >= phi_min || phi_deg <= phi_max; // sector wraps across 0
}
void PreviewImage::color_pixel(std::vector<rgb> &ret, int64_t in_xpixel, int64_t in_ypixel,const rgb &color) const {
if ((in_xpixel >= 0) && (in_xpixel < xpixel) && (in_ypixel >= 0) && (in_ypixel < ypixel))
ret[(in_ypixel * xpixel + in_xpixel)] = color;
@@ -198,6 +212,36 @@ void PreviewImage::AddROI(std::vector<rgb> &rgb_image) const {
}
roi_counter++;
}
DiffractionGeometry geom = experiment.GetDiffractionGeometry();
for (const auto &az: experiment.ROI().GetROIDefinition().azimuthal) {
const int width = 5;
const float r_inner = geom.ResToPxl(az.GetDMax_A()); // larger d -> smaller radius
const float r_outer = geom.ResToPxl(az.GetDMin_A());
const bool has_phi = az.HasPhi();
for (int64_t y = 0; y <= ypixel; y++) {
for (int64_t x = 0; x <= xpixel; x++) {
const float dx = x - beam_x;
const float dy = y - beam_y;
const float dist = sqrtf(dx * dx + dy * dy);
const float phi_deg = geom.Phi_rad(x, y) * 180.0f / static_cast<float>(PI);
const bool in_sector = !has_phi || InPhiSector(phi_deg, az.GetPhiMin_deg(), az.GetPhiMax_deg());
const bool on_arc = (std::fabs(dist - r_inner) < width) || (std::fabs(dist - r_outer) < width);
// Radial edges of a sector: angular tolerance scaled to keep ~constant pixel width.
const float tol_deg = (dist > 1.0f) ? width / dist * 180.0f / static_cast<float>(PI) : 180.0f;
const bool on_edge = has_phi && (dist >= r_inner) && (dist <= r_outer)
&& (AngularDistance_deg(phi_deg, az.GetPhiMin_deg()) < tol_deg
|| AngularDistance_deg(phi_deg, az.GetPhiMax_deg()) < tol_deg);
if ((on_arc && in_sector) || on_edge)
roi(rgb_image, x, y, roi_counter);
}
}
roi_counter++;
}
}
void PreviewImage::AddResolutionRing(std::vector<rgb> &rgb_image, float d) const {