// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "DiffractionSpot.h" #include "DiffractionGeometry.h" #include "RawToConvertedGeometry.h" DiffractionSpot::DiffractionSpot(uint32_t col, uint32_t line, int64_t in_photons) { if (in_photons < 0) in_photons = 0; x = static_cast(col) * in_photons; y = static_cast(line) * in_photons; pixel_count = 1; photons = in_photons; max_photons = in_photons; } DiffractionSpot& DiffractionSpot::operator+=(const DiffractionSpot &other) { this->x += other.x; this->y += other.y; this->photons += other.photons; this->max_photons = std::max(this->max_photons, other.max_photons); this->pixel_count += other.pixel_count; return *this; } DiffractionSpot::DiffractionSpot(int64_t x_sum, int64_t y_sum, int64_t in_pixel_count, int64_t in_photons, int64_t in_max_photons) : x(x_sum), y(y_sum), pixel_count(in_pixel_count), photons(in_photons), max_photons(in_max_photons) {} int64_t DiffractionSpot::Count() const { return photons; } int64_t DiffractionSpot::MaxCount() const { return max_photons; } Coord DiffractionSpot::RawCoord() const { if (photons == 0) return {0, 0, 0}; // In double: the sums run past what a float mantissa holds on a bright spot, and the centroid is // wanted to better than the last pixel bit. return {static_cast(static_cast(x) / static_cast(photons)), static_cast(static_cast(y) / static_cast(photons)), 0}; } int64_t DiffractionSpot::PixelCount() const { return pixel_count; } void DiffractionSpot::AddPixel(uint32_t col, uint32_t line, int64_t photons) { this->x += static_cast(col) * photons; this->y += static_cast(line) * photons; this->photons += photons; this->max_photons = std::max(this->max_photons, photons); this->pixel_count += 1; } void DiffractionSpot::ConvertToImageCoordinates(const DiffractionExperiment &experiment, uint16_t module_number) { // The raw -> image map is a signed axis swap (module axes are +/-1 along X or Y, never rotated - // see DetectorGeometryModular::GetDirection) plus an integer translation: the module's origin in // the assembled image, and the two-pixel multipixel gaps, which depend on where the centroid // falls. Applied to the SUMS rather than to the centroid, it therefore leaves them exact // integers; converting the centroid and multiplying it back by the photon count would put the // rounding this class avoids straight back in. const Coord centroid = RawCoord(); const Coord fast = experiment.GetModuleFastDirection(module_number); const Coord slow = experiment.GetModuleSlowDirection(module_number); // Everything the map adds on top of the axis swap - origin plus gap corrections, an integer. const Coord shift = RawToConvertedCoordinate(experiment, module_number, centroid) - fast * centroid.x - slow * centroid.y; const int64_t x_raw = x, y_raw = y; x = std::lround(shift.x) * photons + std::lround(fast.x) * x_raw + std::lround(slow.x) * y_raw; y = std::lround(shift.y) * photons + std::lround(fast.y) * x_raw + std::lround(slow.y) * y_raw; } std::optional DiffractionSpot::Export(const DiffractionGeometry &geometry, int64_t image_num) const { if (photons == 0) return std::nullopt; const Coord centroid = RawCoord(); auto d = geometry.PxlToRes(centroid.x, centroid.y); float phi = 0.0f; if (geometry.GetRotation()) { // Rotation angle is considered as increment + half wedge. // It ignores the starting angle. phi = geometry.GetRotation()->GetAngle_deg(image_num) + geometry.GetRotation()->GetWedge_deg() / 2.0f; } return SpotToSave{ .x = centroid.x, .y = centroid.y, .phi = phi, .intensity = static_cast(photons), .maxc = max_photons, .lattice = -1, .image = image_num, .d_A = d, .ice_ring = false, .indexed = false }; }