// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "PowderCalibrationWidget.h" #include #include #include #include "../../common/JFJochMath.h" #include "../image_analysis/geom_refinement/AssignSpotsToRings.h" #include "../image_analysis/geom_refinement/Calibrants.h" PowderCalibrationWidget::PowderCalibrationWidget(QWidget *parent) : QWidget(parent) { auto layout = new QVBoxLayout(this); calibrantCombo = new QComboBox(this); updateCalibrantList(); // How a whole-dataset run measures the rings. The two buttons below always work from the spots of // the image on screen, so this only takes effect for "Analyze dataset". methodCombo = new QComboBox(this); methodCombo->addItem("Rings (summed profile)", static_cast(CalibrationMethod::Rings)); methodCombo->addItem("Spots (pooled spot lists)", static_cast(CalibrationMethod::Spots)); methodCombo->setToolTip("How \"Analyze dataset\" measures the powder rings: rings sums the " "(q x azimuth) azimuthal profile over every image and fits the ring arcs in it; " "spots pools the found spots and fits those."); auto findBeamCenterButton = new QPushButton("Guess detector calibration", this); connect(findBeamCenterButton, &QPushButton::clicked,this, &PowderCalibrationWidget::findBeamCenterClicked); auto optimizeBeamCenterButton = new QPushButton("Refine detector calibration", this); connect(optimizeBeamCenterButton, &QPushButton::clicked,this, &PowderCalibrationWidget::optimizeBeamCenterClicked); auto calibrantRingsButton = new QPushButton("Display calibrant rings", this); connect(calibrantRingsButton, &QPushButton::clicked, this, [this]() { QVector d_rings; for (float q : GetCalibrantRings()) d_rings.append(2 * PI / q); emit ringsFromCalibration(d_rings); }); auto refine_row = new QGridLayout(); refine_row->setSpacing(12); refine_row->addWidget(new QLabel("Calibrant:"),0,0); refine_row->addWidget(calibrantCombo,0,1); refine_row->addWidget(findBeamCenterButton,1, 0); refine_row->addWidget(optimizeBeamCenterButton,1, 1); refine_row->addWidget(calibrantRingsButton,2, 0); refine_row->addWidget(new QLabel("Dataset method:"),3,0); refine_row->addWidget(methodCombo,3,1); auto hint = new QLabel("The two buttons fit the image on screen; \"Analyze dataset\" fits the whole " "run and writes a .poni.", this); hint->setWordWrap(true); hint->setStyleSheet("color: gray;"); layout->addLayout(refine_row); layout->addWidget(hint); setLayout(layout); } std::vector PowderCalibrationWidget::GetCalibrantRings() const { // The combo lists the shared calibrant table (in its order), then the current sample's own cell, // which is not a powder standard but is what a single-crystal image's spots actually sit on. const int idx = calibrantCombo->currentIndex(); const auto &table = Calibrants(); if (idx >= 0 && idx < static_cast(table.size())) return CalibrantRings(table[idx].name); if (sample_cell) return CalculateXtalRings(sample_cell.value()); return CalibrantRings(table.front().name); } CalibrationSelection PowderCalibrationWidget::Selection() const { return {calibrantCombo->currentText(), GetCalibrantRings(), static_cast(methodCombo->currentData().toInt())}; } void PowderCalibrationWidget::updateCalibrantList() { calibrantCombo->clear(); for (const auto &c : Calibrants()) calibrantCombo->addItem(QString::fromStdString(c.name)); if (sample_cell) calibrantCombo->addItem(QString("Current sample (%1 %2 %3 %4 %5 %6)") .arg(QString::number(sample_cell->a, 'f', 1)) .arg(QString::number(sample_cell->b, 'f', 1)) .arg(QString::number(sample_cell->c, 'f', 1)) .arg(QString::number(sample_cell->alpha, 'f', 1)) .arg(QString::number(sample_cell->beta, 'f', 1)) .arg(QString::number(sample_cell->gamma, 'f', 1))); calibrantCombo->setCurrentIndex(0); } void PowderCalibrationWidget::loadImage(std::shared_ptr image) { if (image) sample_cell = image->Dataset().experiment.GetUnitCell(); updateCalibrantList(); } void PowderCalibrationWidget::findBeamCenterClicked() { const std::vector rings = GetCalibrantRings(); emit findBeamCenter(QVector(rings.begin(), rings.end()), true); } void PowderCalibrationWidget::optimizeBeamCenterClicked() { const std::vector rings = GetCalibrantRings(); emit findBeamCenter(QVector(rings.begin(), rings.end()), false); }