// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "DetectorOrientation.h" #include "JFJochException.h" DetectorOrientation::DetectorOrientation(bool mirror_y, int64_t quarter_turns) { MirrorY(mirror_y); QuarterTurns(quarter_turns); } DetectorOrientation &DetectorOrientation::MirrorY(bool input) { mirror_y = input; return *this; } DetectorOrientation &DetectorOrientation::QuarterTurns(int64_t input) { if ((input < 0) || (input > 3)) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Quarter turns must be 0, 1, 2 or 3"); quarter_turns = input; return *this; } bool DetectorOrientation::IsMirrorY() const { return mirror_y; } int64_t DetectorOrientation::GetQuarterTurns() const { return quarter_turns; } bool DetectorOrientation::IsIdentity() const { return !mirror_y && (quarter_turns == 0); } RotMatrix DetectorOrientation::Matrix() const { // Columns of Rz(k*90 deg), in the internal frame (x = column, y = row downward, z = beam). y // points down, so a positive right-handed turn about +z takes +x to +y - clockwise on screen. const float c[4] = {1, 0, -1, 0}; const float s[4] = {0, 1, 0, -1}; const Coord rz_x = {c[quarter_turns], s[quarter_turns], 0}; const Coord rz_y = {-s[quarter_turns], c[quarter_turns], 0}; // Rz * diag(1,-1,1): the mirror negates the second column. return {rz_x, mirror_y ? -rz_y : rz_y, {0, 0, 1}}; } std::optional DetectorOrientation::Match(const Coord &fast, const Coord &slow) { for (int64_t quarter_turns = 0; quarter_turns < 4; quarter_turns++) { for (const bool mirror_y: {false, true}) { const DetectorOrientation orientation(mirror_y, quarter_turns); const RotMatrix m = orientation.Matrix(); if (((m.Column(0) - fast).Length() < 1e-3f) && ((m.Column(1) - slow).Length() < 1e-3f)) return orientation; } } return {}; } bool DetectorOrientation::operator==(const DetectorOrientation &other) const { return (mirror_y == other.mirror_y) && (quarter_turns == other.quarter_turns); }