Warning, file /education/kstars/kstars/ekos/focus/aberrationinspectorplot.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2023 John Evans <john.e.evans.email@googlemail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "aberrationinspectorplot.h"
0008 #include "curvefit.h"
0009 #include "klocalizedstring.h"
0010 
0011 namespace Ekos
0012 {
0013 
0014 #define DEFAULT_BASIC_FONT_SIZE 10
0015 
0016 AberrationInspectorPlot::AberrationInspectorPlot(QWidget *parent) : QCustomPlot (parent)
0017 {
0018     setBackground(QBrush(Qt::black));
0019 
0020     xAxis->setBasePen(QPen(Qt::white, 1));
0021     yAxis->setBasePen(QPen(Qt::white, 1));
0022 
0023     xAxis->setTickPen(QPen(Qt::white, 1));
0024     yAxis->setTickPen(QPen(Qt::white, 1));
0025 
0026     xAxis->setSubTickPen(QPen(Qt::white, 1));
0027     yAxis->setSubTickPen(QPen(Qt::white, 1));
0028 
0029     xAxis->setTickLabelColor(Qt::white);
0030     yAxis->setTickLabelColor(Qt::white);
0031 
0032     xAxis->setLabelColor(Qt::white);
0033     yAxis->setLabelColor(Qt::white);
0034 
0035     xAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
0036     yAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
0037     xAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
0038     yAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
0039     xAxis->grid()->setZeroLinePen(Qt::NoPen);
0040     yAxis->grid()->setZeroLinePen(Qt::NoPen);
0041 
0042     setInteractions(QCP::iRangeZoom);
0043     setInteraction(QCP::iRangeDrag, true);
0044 
0045     QVector<QCPScatterStyle::ScatterShape> shapes;
0046     shapes << QCPScatterStyle::ssCross;
0047     shapes << QCPScatterStyle::ssPlus;
0048     shapes << QCPScatterStyle::ssCircle;
0049     shapes << QCPScatterStyle::ssSquare;
0050     shapes << QCPScatterStyle::ssDisc;
0051     shapes << QCPScatterStyle::ssDiamond;
0052     shapes << QCPScatterStyle::ssStar;
0053     shapes << QCPScatterStyle::ssTriangle;
0054     shapes << QCPScatterStyle::ssTriangleInverted;
0055 
0056     this->setAutoAddPlottableToLegend(true);
0057     for (int i = 0; i < NUM_TILES; i++)
0058     {
0059         m_graph[i] = addGraph();
0060         m_graph[i]->setLineStyle(QCPGraph::lsLine);
0061         m_graph[i]->setPen(QPen(QColor(TILE_COLOUR[i]), 2, Qt::SolidLine));
0062         m_graph[i]->setScatterStyle(shapes[i]);
0063         m_graph[i]->setName(TILE_NAME[i]);
0064 
0065         // Store the auto generated legends array for later manipulation
0066         m_legendItems[i] = this->legend->item(i);
0067     }
0068 
0069     this->setAutoAddPlottableToLegend(false);
0070     for (int i = 0; i < NUM_TILES; i++)
0071     {
0072         // Add focus point graphs
0073         focusPoint[i] = addGraph();
0074         focusPoint[i]->setLineStyle(QCPGraph::lsImpulse);
0075         focusPoint[i]->setPen(QPen(QColor(TILE_COLOUR[i]), 2, Qt::SolidLine));
0076         focusPoint[i]->setScatterStyle(QCPScatterStyle(shapes[i], TILE_COLOUR[i], TILE_COLOUR[i], 10));
0077     }
0078 
0079     // determine font size
0080     if (parent != nullptr)
0081         setBasicFontSize(parent->font().pointSize());
0082     else
0083         setBasicFontSize(DEFAULT_BASIC_FONT_SIZE);
0084 
0085     connect(this, &QCustomPlot::mouseMove, [this](QMouseEvent * event)
0086     {
0087         double key = xAxis->pixelToCoord(event->localPos().x());
0088         if (xAxis->range().contains(key))
0089         {
0090             QCPGraph *graph = qobject_cast<QCPGraph *>(plottableAt(event->pos(), false));
0091 
0092             if (graph)
0093             {
0094                 for (int i = 0; i < NUM_TILES; i++)
0095                 {
0096                     if (graph == focusPoint[i])
0097                     {
0098                         if (focusPoint[i]->visible())
0099                         {
0100                             int positionKey = focusPoint[i]->findBegin(key);
0101                             double focusPosition = focusPoint[i]->dataMainKey(positionKey);
0102                             double focusMeasure = focusPoint[i]->dataMainValue(positionKey);
0103                             QToolTip::showText(
0104                                 event->globalPos(),
0105                                 i18nc("Graphics tooltip; %2 is tile code; %3 is tile name, %4 is Focus Position; %5 is Focus Measure;",
0106                                       "<style>table { background-color: white;}</style>"
0107                                       "<font color='%1'><table>"
0108                                       "<tr><td>Tile: </td><td>%2 (%3)</td></tr>"
0109                                       "<tr><td>Pos:  </td><td>%4</td></tr>"
0110                                       "<tr><td>Val:  </td><td>%5</td></tr>"
0111                                       "</table></font>",
0112                                       TILE_COLOUR[i],
0113                                       TILE_NAME[i], TILE_LONGNAME[i],
0114                                       QString::number(focusPosition, 'f', 0),
0115                                       QString::number(focusMeasure, 'g', 3)));
0116                         }
0117                         break;
0118                     }
0119                 }
0120             }
0121         }
0122     });
0123 }
0124 
0125 void AberrationInspectorPlot::init(QString yAxisLabel, double starUnits, bool useWeights, bool showLabels, bool showCFZ)
0126 {
0127     Q_UNUSED(useWeights);
0128 
0129     yAxis->setLabel(yAxisLabel);
0130     m_starUnits = starUnits;
0131     m_showLabels = showLabels;
0132     m_showCFZ = showCFZ;
0133     for (int i = 0; i < NUM_TILES; i++)
0134     {
0135         m_graph[i]->data()->clear();
0136         focusPoint[i]->data().clear();
0137         focusPoint[i]->setData(QVector<double> {}, QVector<double> {});
0138     }
0139     // Displat the legend
0140     this->legend->setVisible(true);
0141 }
0142 
0143 void AberrationInspectorPlot::setAxes(const int tile)
0144 {
0145     if (tile == -1)
0146     {
0147         // Recalculate axes based on current setting of member variables
0148         const double xborder = (m_maxPosition - m_minPosition) / 10.0;
0149         xAxis->setRange(m_minPosition - xborder, m_maxPosition + xborder);
0150 
0151         const double yborder = (m_maxMeasure - m_minMeasure) / 10.0;
0152         yAxis->setRange(m_minMeasure - yborder, m_maxMeasure + yborder);
0153         return;
0154     }
0155     // x-range - since its the same for each curve only do it once
0156     if (tile == 0)
0157     {
0158         if (m_positions.empty())
0159             return;
0160 
0161         m_minPosition = *std::min_element(m_positions.constBegin(), m_positions.constEnd());
0162         m_maxPosition = *std::max_element(m_positions.constBegin(), m_positions.constEnd());
0163         const double border = (m_maxPosition - m_minPosition) / 10.0;
0164         xAxis->setRange(m_minPosition - border, m_maxPosition + border);
0165     }
0166 
0167     // y range
0168     if (m_measures[tile].empty())
0169         return;
0170 
0171     if (tile == 0)
0172     {
0173         m_minMeasure = *std::min_element(m_measures[tile].constBegin(), m_measures[tile].constEnd());
0174         m_maxMeasure = *std::max_element(m_measures[tile].constBegin(), m_measures[tile].constEnd());
0175     }
0176     else
0177     {
0178         m_minMeasure = std::min(m_minMeasure, *std::min_element(m_measures[tile].constBegin(), m_measures[tile].constEnd()));
0179         m_maxMeasure = std::max(m_maxMeasure, *std::max_element(m_measures[tile].constBegin(), m_measures[tile].constEnd()));
0180     }
0181     const double border = (m_maxMeasure - m_minMeasure) / 10.0;
0182     yAxis->setRange(m_minMeasure - border, m_maxMeasure + border);
0183 }
0184 
0185 void AberrationInspectorPlot::addData(QVector<int> positions, QVector<double> measures, QVector<double> weights,
0186                                       QVector<bool> outliers)
0187 {
0188     Q_UNUSED(weights);
0189     Q_UNUSED(outliers);
0190     if (m_positions.count() == 0)
0191         m_positions.append(positions);
0192 
0193     // Convert the incoming measures (e.g. pixels) to display measures (e.g. arc-secs)
0194     QVector<double> displayMeasures;
0195     for (int i = 0; i < measures.count(); i++)
0196         displayMeasures.push_back(getDisplayMeasure(measures[i]));
0197     m_measures.append(displayMeasures);
0198 
0199     setAxes(m_measures.count() - 1);
0200 }
0201 
0202 void AberrationInspectorPlot::drawMaxMin(int tile, double solutionPosition, double solutionMeasure)
0203 {
0204     // Do nothing for invalid positions
0205     if (solutionPosition <= 0)
0206         return;
0207 
0208     double displayMeasure = getDisplayMeasure(solutionMeasure);
0209     m_minMeasure = std::min(m_minMeasure, displayMeasure);
0210     m_maxMeasure = std::max(m_maxMeasure, displayMeasure);
0211 
0212     focusPoint[tile]->addData(solutionPosition, displayMeasure);
0213     m_labelItems[tile] = new QCPItemText(this);
0214     m_labelItems[tile]->setPositionAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
0215     m_labelItems[tile]->setColor(TILE_COLOUR[tile]);
0216     m_labelItems[tile]->setBrush(Qt::white);
0217     m_labelItems[tile]->setPen(Qt::NoPen);
0218     m_labelItems[tile]->setFont(QFont(font().family(), (int) std::round(0.8 * basicFontSize())));
0219     m_labelItems[tile]->position->setType(QCPItemPosition::ptPlotCoords);
0220     m_labelItems[tile]->setText(QString::number(solutionPosition, 'f', 0));
0221     m_labelItems[tile]->position->setCoords(solutionPosition, displayMeasure * 0.8);
0222     m_labelItems[tile]->setVisible(m_showLabels);
0223 }
0224 
0225 void AberrationInspectorPlot::drawCFZ(double solutionPosition, double solutionMeasure, int cfzSteps)
0226 {
0227     // Do nothing for invalid positions
0228     if (solutionPosition <= 0 || solutionMeasure <= 0)
0229         return;
0230 
0231     if (!m_CFZ)
0232         m_CFZ = new QCPItemBracket(this);
0233 
0234     m_CFZ->left->setType(QCPItemPosition::ptPlotCoords);
0235     m_CFZ->right->setType(QCPItemPosition::ptPlotCoords);
0236 
0237     double y = m_minMeasure * 0.95;
0238 
0239     m_CFZ->left->setCoords(solutionPosition + cfzSteps / 2.0, y);
0240     m_CFZ->right->setCoords(solutionPosition - cfzSteps / 2.0, y);
0241     m_CFZ->setLength(15);
0242     m_CFZ->setAntialiased(false);
0243     m_CFZ->setPen(QPen(QColor(Qt::yellow)));
0244     m_CFZ->setVisible(m_showCFZ);
0245 }
0246 
0247 void AberrationInspectorPlot::setShowCFZ(bool setting)
0248 {
0249     m_showCFZ = setting;
0250     if (m_CFZ)
0251         m_CFZ->setVisible(setting);
0252 }
0253 
0254 void AberrationInspectorPlot::setShowLabels(bool setting)
0255 {
0256     m_showLabels = setting;
0257     for (int tile = 0; tile < NUM_TILES; tile++)
0258     {
0259         if (m_labelItems[tile])
0260             m_labelItems[tile]->setVisible(setting);
0261     }
0262 }
0263 
0264 void AberrationInspectorPlot::drawCurve(int tile, Ekos::CurveFitting *curveFit, int maxmin, double measure, bool fit,
0265                                         double R2)
0266 {
0267     Q_UNUSED(fit);
0268     Q_UNUSED(R2);
0269 
0270     if (curveFit == nullptr)
0271         return;
0272 
0273     if (!fit)
0274         return;
0275 
0276     // Extend max/mins if appropriate
0277     m_minPosition = std::min(m_minPosition, maxmin);
0278     m_maxPosition = std::max(m_maxPosition, maxmin);
0279     m_minMeasure = std::min(m_minMeasure, measure);
0280     m_maxMeasure = std::max(m_maxMeasure, measure);
0281     setAxes(-1);
0282 
0283     if (m_graph[tile] != nullptr)
0284     {
0285         m_graph[tile]->data()->clear();
0286         QCPRange range = xAxis->range();
0287         double interval = range.size() / 40.0;
0288 
0289         for(double x = range.lower; x < range.upper; x += interval)
0290         {
0291             double y = getDisplayMeasure(curveFit->f(x));
0292             m_graph[tile]->addData(x, y);
0293         }
0294     }
0295 }
0296 
0297 // Show only the graph elements relevant to the passed in useTile array
0298 void AberrationInspectorPlot::redrawCurve(bool useTile[NUM_TILES])
0299 {
0300     for (int i = 0; i < NUM_TILES; i++)
0301     {
0302         // Show / hide the appropriate curves
0303         if (m_graph[i])
0304             m_graph[i]->setVisible(useTile[i]);
0305 
0306         // Only display legend entries for displayed graphs
0307         this->legend->item(i)->setVisible(useTile[i]);
0308 
0309         // Show / hide the focus point and text labels appopriate to the selected curves
0310         if (focusPoint[i])
0311             focusPoint[i]->setVisible(useTile[i]);
0312         if (m_labelItems[i])
0313             m_labelItems[i]->setVisible(useTile[i] && m_showLabels);
0314     }
0315 }
0316 
0317 void AberrationInspectorPlot::setBasicFontSize(int basicFontSize)
0318 {
0319     m_basicFontSize = basicFontSize;
0320 
0321     // Axis Labels Settings
0322     yAxis->setLabelFont(QFont(font().family(), basicFontSize));
0323     xAxis->setTickLabelFont(QFont(font().family(), (int) std::round(0.9 * basicFontSize)));
0324     yAxis->setTickLabelFont(QFont(font().family(), (int) std::round(0.9 * basicFontSize)));
0325 }
0326 
0327 // Internally calculations are done in units of pixels for HFR and FWHM
0328 // If user preference is arcsecs then convert measures for display purposes.
0329 double AberrationInspectorPlot::getDisplayMeasure(const double measure)
0330 {
0331     return measure * m_starUnits;
0332 }
0333 
0334 }