File indexing completed on 2024-03-24 15:17:02

0001 /*
0002     SPDX-FileCopyrightText: 2017 Robert Lancaster <rlancaste@gmail.com>
0003 
0004     Based on the QT Surface Example https://doc.qt.io/qt-5.9/qtdatavisualization-surface-example.html
0005     and the QT Bars Example https://doc-snapshots.qt.io/qt5-5.9/qtdatavisualization-bars-example.html
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "starprofileviewer.h"
0011 #include <KLocalizedString>
0012 
0013 using namespace QtDataVisualization;
0014 
0015 StarProfileViewer::StarProfileViewer(QWidget *parent) : QDialog(parent)
0016 {
0017 
0018 #ifdef Q_OS_OSX
0019     setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
0020 #endif
0021 
0022     m_graph = new Q3DBars();
0023     m_pixelValueAxis = m_graph->valueAxis();
0024     m_xPixelAxis = m_graph->columnAxis();
0025     m_yPixelAxis = m_graph->rowAxis();
0026 
0027     m_pixelValueAxis->setTitle(i18n("Pixel Values"));
0028     m_pixelValueAxis->setLabelAutoRotation(30.0f);
0029     m_pixelValueAxis->setTitleVisible(true);
0030 
0031     m_xPixelAxis->setTitle(i18n("Horizontal"));
0032     m_xPixelAxis->setLabelAutoRotation(30.0f);
0033     m_xPixelAxis->setTitleVisible(true);
0034     m_yPixelAxis->setTitle(i18n("Vertical"));
0035     m_yPixelAxis->setLabelAutoRotation(30.0f);
0036     m_yPixelAxis->setTitleVisible(true);
0037 
0038     m_3DPixelSeries = new QBar3DSeries;
0039 
0040     m_3DPixelSeries->setMesh(QAbstract3DSeries::MeshBevelBar);
0041     m_graph->addSeries(m_3DPixelSeries);
0042 
0043     m_graph->activeTheme()->setLabelBackgroundEnabled(false);
0044 
0045     QWidget *container = QWidget::createWindowContainer(m_graph);
0046 
0047     if (!m_graph->hasContext()) {
0048         QMessageBox msgBox;
0049         msgBox.setText(i18n("Couldn't initialize the OpenGL context."));
0050         msgBox.exec();
0051         return;
0052     }
0053 
0054     QSize screenSize = m_graph->screen()->size();
0055     container->setMinimumSize(QSize(300, 500));
0056     container->setMaximumSize(screenSize);
0057     container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
0058     container->setFocusPolicy(Qt::StrongFocus);
0059 
0060     this->setWindowTitle(i18nc("@title:window", "View Star Profile"));
0061 
0062     QVBoxLayout *mainLayout = new QVBoxLayout(this);
0063     QHBoxLayout *topLayout = new QHBoxLayout();
0064     QHBoxLayout *controlsLayout = new QHBoxLayout();
0065     QWidget* rightWidget = new QWidget();
0066     rightWidget->setVisible(false);
0067     QVBoxLayout *rightLayout = new QVBoxLayout(rightWidget);
0068     QGridLayout *sliderLayout = new QGridLayout();
0069 
0070     topLayout->addWidget(container, 1);
0071     topLayout->addWidget(rightWidget);
0072     mainLayout->addLayout(topLayout);
0073     mainLayout->addLayout(controlsLayout);
0074     controlsLayout->setAlignment(Qt::AlignLeft);
0075 
0076     maxValue=new QLabel(this);
0077     maxValue->setToolTip(i18n("Maximum Value on the graph"));
0078     cutoffValue=new QLabel(this);
0079     cutoffValue->setToolTip(i18n("Cuttoff Maximum for eliminating hot pixels and bright stars."));
0080 
0081     QCheckBox *toggleEnableCutoff= new QCheckBox(this);
0082     toggleEnableCutoff->setToolTip(i18n("Enable or Disable the Max Value Cutoff"));
0083     toggleEnableCutoff->setText(i18n("Toggle Cutoff"));
0084     toggleEnableCutoff->setChecked(false);
0085 
0086     blackPointSlider=new QSlider( Qt::Vertical, this);
0087     blackPointSlider->setToolTip(i18n("Sets the Minimum Value on the graph"));
0088     sliderLayout->addWidget(blackPointSlider,0,0);
0089     sliderLayout->addWidget(new QLabel(i18n("Min")),1,0);
0090 
0091     whitePointSlider=new QSlider( Qt::Vertical, this);
0092     whitePointSlider->setToolTip(i18n("Sets the Maximum Value on the graph"));
0093     sliderLayout->addWidget(whitePointSlider,0,1);
0094     sliderLayout->addWidget(new QLabel(i18n("Max")),1,1);
0095 
0096     cutoffSlider=new QSlider( Qt::Vertical, this);
0097     cutoffSlider->setToolTip(i18n("Sets the Cuttoff Maximum for eliminating hot pixels and bright stars."));
0098     sliderLayout->addWidget(cutoffSlider,0,2);
0099     sliderLayout->addWidget(new QLabel(i18n("Cut")),1,2);
0100     cutoffSlider->setEnabled(false);
0101 
0102     minValue = new QLabel(this);
0103     minValue->setToolTip(i18n("Minimum Value on the graph"));
0104 
0105     autoScale = new QCheckBox(this);
0106     autoScale->setText(i18n("AutoScale"));
0107     autoScale->setToolTip(i18n("Automatically scales the sliders for the subFrame.\nUncheck to leave them unchanged when you pan around."));
0108     autoScale->setChecked(true);
0109 
0110     showScaling = new QPushButton(this);
0111     showScaling->setIcon(QIcon::fromTheme("transform-move-vertical"));
0112     showScaling->setCheckable(true);
0113     showScaling->setMaximumSize(22, 22);
0114     showScaling->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0115     showScaling->setToolTip(i18n("Hides and shows the scaling side panel"));
0116     showScaling->setChecked(false);
0117 
0118     rightLayout->addWidget(toggleEnableCutoff);
0119     rightLayout->addWidget(cutoffValue);
0120     rightLayout->addWidget(maxValue);
0121     rightLayout->addLayout(sliderLayout);
0122     rightLayout->addWidget(minValue);
0123     rightLayout->addWidget(autoScale);
0124 
0125     selectionType = new QComboBox(this);
0126     selectionType->setToolTip(i18n("Changes the type of selection"));
0127     selectionType->addItem(i18n("Item"));
0128     selectionType->addItem(i18n("Horizontal"));
0129     selectionType->addItem(i18n("Vertical"));
0130     selectionType->setCurrentIndex(0);
0131 
0132     sliceB = new QPushButton(this);
0133     sliceB->setIcon(QIcon::fromTheme("view-object-histogram-linear"));
0134     sliceB->setCheckable(true);
0135     sliceB->setMaximumSize(22, 22);
0136     sliceB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0137     sliceB->setToolTip(i18n("Toggles the slice view when horizontal or vertical items are selected"));
0138     sliceB->setCheckable(true);
0139     sliceB->setChecked(false);
0140     sliceB->setEnabled(false);
0141     sliceB->setDefault(false);
0142 
0143     showCoordinates = new QPushButton(this);
0144     showCoordinates->setIcon(QIcon::fromTheme("coordinate"));
0145     showCoordinates->setCheckable(true);
0146     showCoordinates->setMaximumSize(22, 22);
0147     showCoordinates->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0148     showCoordinates->setToolTip(i18n("Shows the x, y coordinates of star centers in the frame"));
0149     showCoordinates->setChecked(false);
0150 
0151     HFRReport = new QPushButton(this);
0152     HFRReport->setToolTip(i18n("Shows the HFR of stars in the frame"));
0153     HFRReport->setIcon(QIcon::fromTheme("tool-measure"));
0154     HFRReport->setCheckable(true);
0155     HFRReport->setMaximumSize(22, 22);
0156     HFRReport->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0157     HFRReport->setChecked(true);
0158 
0159     reportBox = new QLabel(this);
0160 
0161     showPeakValues = new QPushButton(this);
0162     showPeakValues->setIcon(QIcon::fromTheme("kruler-east"));
0163     showPeakValues->setCheckable(true);
0164     showPeakValues->setMaximumSize(22, 22);
0165     showPeakValues->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0166     showPeakValues->setToolTip(i18n("Shows the peak values of star centers in the frame"));
0167     showPeakValues->setChecked(true);
0168 
0169     sampleSize = new QComboBox(this);
0170     sampleSize->setToolTip(i18n("Changes the sample size shown in the graph"));
0171     sampleSize->addItem(QString::number(16));
0172     sampleSize->addItem(QString::number(32));
0173     sampleSize->addItem(QString::number(64));
0174     sampleSize->addItem(QString::number(128));
0175     sampleSize->addItem(QString::number(256));
0176     sampleSize->addItem(QString::number(512));
0177     sampleSize->setCurrentIndex(3);
0178     sampleSize->setVisible(false);
0179 
0180     zoomView = new QComboBox(this);
0181     zoomView->setToolTip(i18n("Zooms the view to preset locations."));
0182     zoomView->addItem(i18n("ZoomTo"));
0183     zoomView->addItem(i18n("Front"));
0184     zoomView->addItem(i18n("Front High"));
0185     zoomView->addItem(i18n("Overhead"));
0186     zoomView->addItem(i18n("Iso. L"));
0187     zoomView->addItem(i18n("Iso. R"));
0188     zoomView->addItem(i18n("Selected"));
0189     zoomView->setCurrentIndex(0);
0190 
0191     QPushButton *selectorsVisible = new QPushButton(this);
0192     selectorsVisible->setIcon(QIcon::fromTheme("adjustlevels"));
0193     selectorsVisible->setCheckable(true);
0194     selectorsVisible->setMaximumSize(22, 22);
0195     selectorsVisible->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0196     selectorsVisible->setToolTip(i18n("Hides and shows the Vertical and Horizontal Selection Sliders"));
0197     selectorsVisible->setChecked(false);
0198 
0199     controlsLayout->addWidget(sampleSize);
0200     controlsLayout->addWidget(selectionType);
0201     controlsLayout->addWidget(selectorsVisible);
0202     controlsLayout->addWidget(sliceB);
0203     controlsLayout->addWidget(showScaling);
0204     //bottomLayout->addWidget(barSpacing);
0205     controlsLayout->addWidget(zoomView);
0206     //bottomLayout->addWidget(color);
0207     controlsLayout->addWidget(showCoordinates);
0208     controlsLayout->addWidget(HFRReport);
0209     controlsLayout->addWidget(showPeakValues);
0210     controlsLayout->addWidget(reportBox);
0211 
0212     QWidget *bottomSliderWidget= new QWidget(this);
0213     QGridLayout *bottomSliders = new QGridLayout(bottomSliderWidget);
0214     bottomSliderWidget->setLayout(bottomSliders);
0215     mainLayout->addWidget(bottomSliderWidget);
0216     bottomSliderWidget->setVisible(false);
0217 
0218     verticalSelector = new QSlider(Qt::Horizontal, this);
0219     verticalSelector->setToolTip(i18n("Selects the Vertical Value"));
0220     horizontalSelector = new QSlider(Qt::Horizontal, this);
0221     horizontalSelector->setToolTip(i18n("Selects the Horizontal Value"));
0222 
0223     bottomSliders->addWidget(new QLabel(i18n("Vertical: ")), 0, 0);
0224     bottomSliders->addWidget(verticalSelector, 0, 1);
0225     bottomSliders->addWidget(new QLabel(i18n("Horizontal: ")), 1, 0);
0226     bottomSliders->addWidget(horizontalSelector, 1, 1);
0227 
0228     QWidget *bottomControlsWidget= new QWidget(this);
0229     QHBoxLayout *bottomControlLayout = new QHBoxLayout(bottomControlsWidget);
0230     mainLayout->addWidget(bottomControlsWidget);\
0231     bottomControlsWidget->setVisible(false);
0232 
0233     exploreMode = new QPushButton(this);
0234     exploreMode->setIcon(QIcon::fromTheme("visibility"));
0235     exploreMode->setCheckable(true);
0236     exploreMode->setMaximumSize(22, 22);
0237     exploreMode->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0238     exploreMode->setToolTip(i18n("Zooms automatically as the sliders change"));
0239     exploreMode->setChecked(true);
0240 
0241     QDial *barSpacing=new QDial(this);
0242     barSpacing->setMinimum(0);
0243     barSpacing->setMaximum(100);
0244     barSpacing->setValue(50);
0245     barSpacing->setMaximumSize(32, 32);
0246     barSpacing->setWrapping(false);
0247     m_graph->setBarSpacing(QSizeF(0.5,0.5));
0248 
0249     QComboBox *color = new QComboBox(this);
0250     color->setToolTip(i18n("Changes the color scheme"));
0251 
0252     QLinearGradient grGtoR(50, 1, 0, 0);
0253     grGtoR.setColorAt(1.0, Qt::darkGreen);
0254     grGtoR.setColorAt(0.5, Qt::yellow);
0255     grGtoR.setColorAt(0.2, Qt::red);
0256     grGtoR.setColorAt(0.0, Qt::darkRed);
0257     QPixmap pm(50, 10);
0258     QPainter pmp(&pm);
0259     pmp.setPen(Qt::NoPen);
0260     pmp.setBrush(QBrush(grGtoR));
0261     pmp.drawRect(0, 0, 50, 10);
0262     color->addItem("");
0263     color->setItemIcon(0,QIcon(pm));
0264 
0265     QLinearGradient grBtoY(50, 1, 0, 0);
0266     grBtoY.setColorAt(1.0, Qt::black);
0267     grBtoY.setColorAt(0.67, Qt::blue);
0268     grBtoY.setColorAt(0.33, Qt::red);
0269     grBtoY.setColorAt(0.0, Qt::yellow);
0270     pmp.setBrush(QBrush(grBtoY));
0271     pmp.drawRect(0, 0, 50, 10);
0272     color->addItem("");
0273     color->setItemIcon(1,QIcon(pm));
0274 
0275     color->setIconSize(QSize(50, 10));
0276     color->setCurrentIndex(0);
0277     color->setMaximumWidth(80);
0278 
0279     pixelReport = new QLabel("", bottomControlsWidget);
0280 
0281     bottomControlLayout->addWidget(exploreMode);
0282     bottomControlLayout->addWidget(barSpacing);
0283     bottomControlLayout->addWidget(color);
0284     bottomControlLayout->addWidget(pixelReport);
0285 
0286     QObject::connect(selectionType,  SIGNAL(currentIndexChanged(int)),
0287                      this, SLOT(changeSelectionType(int)));
0288     QObject::connect(zoomView,  SIGNAL(currentIndexChanged(int)),
0289                      this, SLOT(zoomViewTo(int)));
0290     QObject::connect(sliceB,  &QPushButton::pressed,
0291                      this, &StarProfileViewer::toggleSlice);
0292     QObject::connect(showCoordinates,  &QCheckBox::toggled,
0293                      this, &StarProfileViewer::updateHFRandPeakSelection);
0294     QObject::connect(HFRReport,  &QCheckBox::toggled,
0295                      this, &StarProfileViewer::updateHFRandPeakSelection);
0296     QObject::connect(showPeakValues,  &QCheckBox::toggled,
0297                      this, &StarProfileViewer::updateHFRandPeakSelection);
0298     QObject::connect(blackPointSlider,  &QSlider::valueChanged,
0299                      this, &StarProfileViewer::updateVerticalAxis);
0300     QObject::connect(whitePointSlider,  &QSlider::valueChanged,
0301                      this, &StarProfileViewer::updateVerticalAxis);
0302     QObject::connect(cutoffSlider,  &QSlider::valueChanged,
0303                      this, &StarProfileViewer::updateDisplayData);
0304     QObject::connect(autoScale,  &QCheckBox::toggled,
0305                      this, &StarProfileViewer::updateScale);
0306     QObject::connect(showScaling,  &QCheckBox::toggled,
0307                      rightWidget, &QWidget::setVisible);
0308     QObject::connect(sampleSize,  SIGNAL(currentIndexChanged(QString)),
0309                      this, SLOT(updateSampleSize(QString)));
0310     QObject::connect(color,  SIGNAL(currentIndexChanged(int)),
0311                      this, SLOT(updateColor(int)));
0312     QObject::connect(verticalSelector,  &QSlider::valueChanged,
0313                      this, &StarProfileViewer::changeSelection);
0314     QObject::connect(horizontalSelector,  &QSlider::valueChanged,
0315                      this, &StarProfileViewer::changeSelection);
0316     QObject::connect(selectorsVisible,  &QCheckBox::toggled,
0317                      bottomSliderWidget, &QWidget::setVisible);
0318     QObject::connect(selectorsVisible,  &QCheckBox::toggled,
0319                      bottomControlsWidget, &QWidget::setVisible);
0320     QObject::connect(toggleEnableCutoff,  &QCheckBox::toggled,
0321                      this, &StarProfileViewer::toggleCutoffEnabled);
0322     QObject::connect(m_3DPixelSeries,  &QBar3DSeries::selectedBarChanged,
0323                      this, &StarProfileViewer::updateSelectorBars);
0324     QObject::connect(barSpacing,  &QSlider::valueChanged,
0325                      this, &StarProfileViewer::updateBarSpacing);
0326 
0327     m_graph->activeTheme()->setType(Q3DTheme::Theme(3)); //Stone Moss
0328 
0329     setGreenToRedGradient();
0330 
0331     m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront);
0332     m_graph->scene()->activeCamera()->setTarget(QVector3D(0.0f, 0.0f, 0.0f));
0333     m_graph->scene()->activeCamera()->setZoomLevel(110);
0334 
0335     //Note:  This is to prevent a button from being called the default button
0336     //and then executing when the user hits the enter key such as when on a Text Box
0337     #ifdef Q_OS_OSX
0338     QList<QPushButton *> qButtons = findChildren<QPushButton *>();
0339     for (auto &button : qButtons)
0340         button->setAutoDefault(false);
0341     #endif
0342 
0343     show();
0344 }
0345 
0346 StarProfileViewer::~StarProfileViewer()
0347 {
0348     delete m_graph;
0349 }
0350 
0351 void StarProfileViewer::loadData(QSharedPointer<FITSData> data, QRect sub, QList<Edge *> centers)
0352 {
0353     if(data)
0354     {
0355         imageData = data;
0356         subFrame=sub;
0357         starCenters=centers;               
0358 
0359         switch (data->getStatistics().dataType)
0360         {
0361         case TBYTE:
0362             loadDataPrivate<uint8_t>();
0363             break;
0364 
0365         case TSHORT:
0366             loadDataPrivate<int16_t>();
0367             break;
0368 
0369         case TUSHORT:
0370             loadDataPrivate<uint16_t>();
0371             break;
0372 
0373         case TLONG:
0374             loadDataPrivate<int32_t>();
0375             break;
0376 
0377         case TULONG:
0378             loadDataPrivate<uint32_t>();
0379             break;
0380 
0381         case TFLOAT:
0382             loadDataPrivate<float>();
0383             break;
0384 
0385         case TLONGLONG:
0386             loadDataPrivate<int64_t>();
0387             break;
0388 
0389         case TDOUBLE:
0390             loadDataPrivate<double>();
0391             break;
0392         }
0393 
0394         updateScale();
0395 
0396         // Add data to the data proxy (the data proxy assumes ownership of it)
0397         // We will retain a copy of the data set so that we can update the display
0398         updateDisplayData();
0399 
0400         updateHFRandPeakSelection();
0401 
0402         horizontalSelector->setRange(0, subFrame.width()-1);
0403         verticalSelector->setRange(0, subFrame.width()-1);  //Width and height are the same
0404     }
0405 }
0406 
0407 template <typename T>
0408 void StarProfileViewer::loadDataPrivate()
0409 {
0410     // Create data arrays
0411     dataSet = new QBarDataArray;
0412     QBarDataRow *dataRow;
0413     dataSet->reserve(subFrame.height());
0414     QStringList rowLabels;
0415     QStringList columnLabels;
0416 
0417     auto *buffer = reinterpret_cast<T const *>(imageData->getImageBuffer());
0418     int width = imageData->width();
0419 
0420     for (int j = subFrame.y(); j < subFrame.y() + subFrame.height(); j++)
0421     {
0422         if( j % 10 == 0 )
0423             rowLabels << QString::number(j);
0424         else
0425             rowLabels << "";
0426         dataRow = new QBarDataRow(subFrame.width());
0427         int x = 0;
0428         for (int i = subFrame.x(); i < subFrame.x() + subFrame.width(); i++)
0429         {
0430             if( i % 10 == 0 )
0431                 columnLabels << QString::number(i);
0432             else
0433                 columnLabels << "";
0434             if( i > 0 && i < imageData->width() && j > 0 && j < imageData->height())
0435                 (*dataRow)[x].setValue(*(buffer + i + j * width));
0436             x++;
0437         }
0438         dataSet->insert(0, dataRow); //Note the row axis is displayed in the opposite direction of the y axis in the image.
0439     }
0440 
0441     std::reverse(rowLabels.begin(), rowLabels.end());
0442 
0443     m_3DPixelSeries->dataProxy()->setRowLabels(rowLabels);
0444     m_3DPixelSeries->dataProxy()->setColumnLabels(columnLabels);
0445 }
0446 
0447 void StarProfileViewer::toggleCutoffEnabled(bool enable)
0448 {
0449     cutoffSlider->setEnabled(enable);
0450     cutOffEnabled = enable;
0451     updateDisplayData();
0452 }
0453 
0454 void StarProfileViewer::updateScale()
0455 {
0456 
0457     //We need to disconnect these so that changing their ranges doesn't affect things
0458     QObject::disconnect(blackPointSlider,  &QSlider::valueChanged,
0459                      this, &StarProfileViewer::updateVerticalAxis);
0460     QObject::disconnect(whitePointSlider,  &QSlider::valueChanged,
0461                      this, &StarProfileViewer::updateVerticalAxis);
0462     QObject::disconnect(cutoffSlider,  &QSlider::valueChanged,
0463                      this, &StarProfileViewer::updateDisplayData);
0464 
0465     float subFrameMin, subFrameMax;
0466     double dataMin, dataMax;
0467     float min, max;
0468     getSubFrameMinMax(&subFrameMin, &subFrameMax, &dataMin, &dataMax);
0469 
0470     int sliderDataMin = convertToSliderValue(dataMin) - 1; //Expands the slider range a little beyond the max and min values
0471     int sliderDataMax = convertToSliderValue(dataMax) + 1;
0472 
0473     if(autoScale->isChecked())
0474     {
0475         min = subFrameMin;
0476         max = subFrameMax;
0477         int sliderMin = convertToSliderValue(min) - 1; //Expands the slider range a little beyond the max and min values
0478         int sliderMax = convertToSliderValue(max) + 1;
0479         blackPointSlider->setRange(sliderMin, sliderMax);
0480         blackPointSlider->setTickInterval((sliderMax - sliderMin) / 100);
0481         whitePointSlider->setRange(sliderMin, sliderMax);
0482         whitePointSlider->setTickInterval((sliderMax - sliderMin) / 100);
0483         cutoffSlider->setRange(sliderMin, sliderDataMax);
0484         cutoffSlider->setTickInterval((sliderDataMax - sliderMin) / 100);
0485         blackPointSlider->setValue(sliderMin);
0486         whitePointSlider->setValue(sliderMax);
0487         cutoffSlider->setValue(sliderDataMax);
0488     }
0489     else
0490     {
0491         min = convertFromSliderValue(blackPointSlider->value());
0492         max = convertFromSliderValue(whitePointSlider->value());
0493         blackPointSlider->setRange(sliderDataMin, sliderDataMax);
0494         blackPointSlider->setTickInterval((sliderDataMax - sliderDataMin) / 100);
0495         whitePointSlider->setRange(sliderDataMin, sliderDataMax);
0496         whitePointSlider->setTickInterval((sliderDataMax - sliderDataMin) / 100);
0497         cutoffSlider->setRange(sliderDataMin, sliderDataMax);
0498         cutoffSlider->setTickInterval((sliderDataMax - sliderDataMin) / 100);
0499 
0500     }
0501     m_pixelValueAxis->setRange(min, max);
0502 
0503     if(cutOffEnabled)
0504         cutoffValue->setText(i18n("Cut: %1", roundf(convertFromSliderValue(cutoffSlider->value()) * 100) / 100));
0505     else
0506         cutoffValue->setText("Cut Disabled");
0507 
0508     if(max < 10 )
0509     {
0510         m_pixelValueAxis->setLabelFormat(QString(QStringLiteral("%.3f ")));
0511         m_3DPixelSeries->setItemLabelFormat(QString(QStringLiteral("%.3f ")));
0512         maxValue->setText(i18n("Max: %1", roundf(max * 100) / 100));
0513         minValue->setText(i18n("Min: %1", roundf(min * 100) / 100));
0514     }
0515     else
0516     {
0517         m_pixelValueAxis->setLabelFormat(QString(QStringLiteral("%.0f ")));
0518         m_3DPixelSeries->setItemLabelFormat(QString(QStringLiteral("%.0f ")));
0519         maxValue->setText(i18n("Max: %1", max));
0520         minValue->setText(i18n("Min: %1", min));
0521     }
0522 
0523     QObject::connect(blackPointSlider,  &QSlider::valueChanged,
0524                      this, &StarProfileViewer::updateVerticalAxis);
0525     QObject::connect(whitePointSlider,  &QSlider::valueChanged,
0526                      this, &StarProfileViewer::updateVerticalAxis);
0527     QObject::connect(cutoffSlider,  &QSlider::valueChanged,
0528                      this, &StarProfileViewer::updateDisplayData);
0529 }
0530 
0531 void StarProfileViewer::updateBarSpacing(int value)
0532 {
0533     float spacing = (float)value/100.0;
0534     m_graph->setBarSpacing(QSizeF(spacing, spacing));
0535 }
0536 
0537 void StarProfileViewer::zoomViewTo(int where)
0538 {
0539     if(where > 6) //One of the star centers
0540     {
0541         int star = where - 7;
0542         int x = starCenters[star]->x - subFrame.x();
0543         int y = subFrame.height() - (starCenters[star]->y - subFrame.y());
0544         m_graph->primarySeries()->setSelectedBar(QPoint( y , x )); //Note row, column    y, x
0545         where = 6; //This is so it will zoom to the target.
0546     }
0547 
0548     switch (where) {
0549     case 0: //Zoom To
0550         break;
0551 
0552     case 1: //Front
0553         m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront);
0554         m_graph->scene()->activeCamera()->setTarget(QVector3D(0.0f, 0.0f, 0.0f));
0555         m_graph->scene()->activeCamera()->setZoomLevel(110);
0556         zoomView->setCurrentIndex(0);
0557         break;
0558 
0559     case 2: //Front High
0560         m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFrontHigh);
0561         m_graph->scene()->activeCamera()->setTarget(QVector3D(0.0f, 0.0f, 0.0f));
0562         m_graph->scene()->activeCamera()->setZoomLevel(110);
0563         zoomView->setCurrentIndex(0);
0564         break;
0565 
0566     case 3: //Overhead
0567         m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetDirectlyAbove);
0568         m_graph->scene()->activeCamera()->setTarget(QVector3D(0.0f, 0.0f, 0.0f));
0569         m_graph->scene()->activeCamera()->setZoomLevel(110);
0570         zoomView->setCurrentIndex(0);
0571         break;
0572 
0573     case 4: //Isometric L
0574         m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetIsometricLeftHigh);
0575         m_graph->scene()->activeCamera()->setTarget(QVector3D(0.0f, 0.0f, 0.0f));
0576         m_graph->scene()->activeCamera()->setZoomLevel(110);
0577         zoomView->setCurrentIndex(0);
0578         break;
0579 
0580     case 5: //Isometric R
0581         m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetIsometricRightHigh);
0582         m_graph->scene()->activeCamera()->setTarget(QVector3D(0.0f, 0.0f, 0.0f));
0583         m_graph->scene()->activeCamera()->setZoomLevel(110);
0584         zoomView->setCurrentIndex(0);
0585         break;
0586 
0587     case 6: //Selected Item
0588     {
0589         QPoint selectedBar = m_graph->selectedSeries()
0590                 ? m_graph->selectedSeries()->selectedBar()
0591                 : QBar3DSeries::invalidSelectionPosition();
0592         if (selectedBar != QBar3DSeries::invalidSelectionPosition())
0593         {
0594             QVector3D target;
0595             float xMin = m_graph->columnAxis()->min();
0596             float xRange = m_graph->columnAxis()->max() - xMin;
0597             float zMin = m_graph->rowAxis()->min();
0598             float zRange = m_graph->rowAxis()->max() - zMin;
0599             target.setX((selectedBar.y() - xMin) / xRange * 2.0f - 1.0f);
0600             target.setZ((selectedBar.x() - zMin) / zRange * 2.0f - 1.0f);
0601 
0602             qreal endAngleX = qAtan(qreal(target.z() / target.x())) / M_PI * -180.0 + 90.0;
0603             if (target.x() > 0.0f)
0604                 endAngleX -= 180.0f;
0605             float barValue = m_graph->selectedSeries()->dataProxy()->itemAt(selectedBar.x(),
0606                                                                             selectedBar.y())->value();
0607             float endAngleY = 60.0f;
0608             float zoom = 150 * 1/qSqrt(barValue / convertFromSliderValue(whitePointSlider->value()));
0609             m_graph->scene()->activeCamera()->setCameraPosition(endAngleX, endAngleY, zoom);
0610             m_graph->scene()->activeCamera()->setTarget(target);
0611 
0612 
0613         }
0614         zoomView->setCurrentIndex(0);
0615         break;
0616     }
0617 
0618 
0619     default:
0620         zoomView->setCurrentIndex(0);
0621         break;
0622     }
0623 }
0624 
0625 void StarProfileViewer::changeSelectionType(int type)
0626 {
0627     switch (type) {
0628     case 0:
0629         m_graph->setSelectionMode(QAbstract3DGraph::SelectionItem);
0630         m_graph->scene()->setSlicingActive(false);
0631         sliceB->setEnabled(false);
0632         break;
0633 
0634     case 1:
0635         m_graph->setSelectionMode(QAbstract3DGraph::SelectionItemAndRow);
0636         sliceB->setEnabled(true);
0637         break;
0638 
0639     case 2:
0640         m_graph->setSelectionMode(QAbstract3DGraph::SelectionItemAndColumn);
0641         sliceB->setEnabled(true);
0642         break;
0643 
0644     default:
0645         break;
0646     }
0647 }
0648 
0649 void StarProfileViewer::changeSelection()
0650 {
0651     int x = horizontalSelector->value();
0652     int y = verticalSelector->value();
0653     m_graph->primarySeries()->setSelectedBar(QPoint( y , x )); //Note row, column    y, x
0654     if(exploreMode->isChecked())
0655         zoomViewTo(6); //Zoom to SelectedItem
0656     updatePixelReport();
0657 }
0658 
0659 void StarProfileViewer::updatePixelReport()
0660 {
0661     int x = horizontalSelector->value();
0662     int y = verticalSelector->value();
0663     //They need to be shifted to the location of the subframe
0664     x += subFrame.x();
0665     y = (subFrame.height() - 1 - y) + subFrame.y(); //Note: Y is in reverse order on the graph.
0666     float barValue = getImageDataValue(x, y);
0667     pixelReport->setText(i18n("Selected Pixel: (%1, %2): %3", x + 1, y + 1, roundf(barValue * 100) / 100)); //Have to add 1 because humans start counting at 1
0668 
0669 }
0670 
0671 
0672 void StarProfileViewer::updateSelectorBars(QPoint position)
0673 {
0674     //Note that we need to disconnect and then reconnect to avoid triggering changeSelection
0675     QObject::disconnect(verticalSelector,  &QSlider::valueChanged,
0676                      this, &StarProfileViewer::changeSelection);
0677     QObject::disconnect(horizontalSelector,  &QSlider::valueChanged,
0678                      this, &StarProfileViewer::changeSelection);
0679 
0680     //Note row, column    y, x
0681     verticalSelector->setValue(position.x());
0682     horizontalSelector->setValue(position.y());
0683     updatePixelReport();
0684 
0685     QObject::connect(verticalSelector,  &QSlider::valueChanged,
0686                      this, &StarProfileViewer::changeSelection);
0687     QObject::connect(horizontalSelector,  &QSlider::valueChanged,
0688                      this, &StarProfileViewer::changeSelection);
0689 }
0690 
0691 void StarProfileViewer::updateSampleSize(const QString &text)
0692 {
0693     emit sampleSizeUpdated(text.toInt());
0694 }
0695 
0696 void StarProfileViewer::enableTrackingBox(bool enable)
0697 {
0698     sampleSize->setVisible(enable);
0699 }
0700 
0701 void StarProfileViewer::updateDisplayData()
0702 {
0703     if(cutOffEnabled)
0704         cutoffValue->setText(i18n("Cut: %1", roundf(convertFromSliderValue(cutoffSlider->value()) * 100) / 100));
0705     else
0706         cutoffValue->setText(i18n("Cut Disabled"));
0707     if(dataSet != nullptr)
0708     {
0709         QBarDataArray *displayDataSet = new QBarDataArray;
0710         displayDataSet->reserve(dataSet->size());
0711 
0712         for (int row = 0; row < dataSet->size(); row++)
0713         {
0714             QBarDataRow *dataRow = dataSet->at(row);
0715             QBarDataRow *newDataRow;
0716             newDataRow = new QBarDataRow(dataRow->size());
0717             for (int column = 0; column < dataRow->size(); column++)
0718             {
0719                 if(cutOffEnabled && dataRow->value(column).value() > convertFromSliderValue(cutoffSlider->value()))
0720                     (*newDataRow)[column].setValue(0.0f);
0721                 else
0722                     (*newDataRow)[column].setValue(dataRow->value(column).value());
0723             }
0724             displayDataSet->append(newDataRow);
0725 
0726         }
0727         m_3DPixelSeries->dataProxy()->resetArray(displayDataSet); //, m_3DPixelSeries->dataProxy()->rowLabels(), m_3DPixelSeries->dataProxy()->columnLabels()
0728     }
0729 }
0730 
0731 void StarProfileViewer::getSubFrameMinMax(float *subFrameMin, float *subFrameMax, double *dataMin, double *dataMax)
0732 {
0733     imageData->getMinMax(dataMin,dataMax);
0734 
0735     //Backwards so that we can find the min and max in subFrame
0736     *subFrameMin = *dataMax;
0737     *subFrameMax = *dataMin;
0738 
0739     switch (imageData->getStatistics().dataType)
0740     {
0741     case TBYTE:
0742         getSubFrameMinMax<uint8_t>(subFrameMin, subFrameMax);
0743         break;
0744 
0745     case TSHORT:
0746         getSubFrameMinMax<int16_t>(subFrameMin, subFrameMax);
0747         break;
0748 
0749     case TUSHORT:
0750         getSubFrameMinMax<uint16_t>(subFrameMin, subFrameMax);
0751         break;
0752 
0753     case TLONG:
0754         getSubFrameMinMax<int32_t>(subFrameMin, subFrameMax);
0755         break;
0756 
0757     case TULONG:
0758         getSubFrameMinMax<uint32_t>(subFrameMin, subFrameMax);
0759         break;
0760 
0761     case TFLOAT:
0762         getSubFrameMinMax<float>(subFrameMin, subFrameMax);
0763         break;
0764 
0765     case TLONGLONG:
0766         getSubFrameMinMax<int64_t>(subFrameMin, subFrameMax);
0767         break;
0768 
0769     case TDOUBLE:
0770         getSubFrameMinMax<double>(subFrameMin, subFrameMax);
0771         break;
0772     }
0773 }
0774 
0775 template <typename T>
0776 void StarProfileViewer::getSubFrameMinMax(float *subFrameMin, float *subFrameMax)
0777 {
0778     auto *buffer = reinterpret_cast<T const *>(imageData->getImageBuffer());
0779     T min = std::numeric_limits<T>::max();
0780     T max = std::numeric_limits<T>::min();
0781     int width = imageData->width();
0782     for (int y = subFrame.y(); y < subFrame.y() + subFrame.height(); y++)
0783     {
0784         for (int x = subFrame.x(); x < subFrame.x() + subFrame.width(); x++)
0785         {
0786             if( x > 0 && x < imageData->width() && y > 0 && y < imageData->height())
0787             {
0788                 min = qMin(min, *(buffer + x + y * width));
0789                 max = qMax(max, *(buffer + x + y * width));
0790             }
0791         }
0792     }
0793 
0794     *subFrameMin = min;
0795     *subFrameMax = max;
0796 }
0797 
0798 template <typename T>
0799 float StarProfileViewer::getImageDataValue(int x, int y)
0800 {
0801     if(!imageData)
0802         return 0;
0803     auto *buffer = reinterpret_cast<T const *>(imageData->getImageBuffer());
0804     return (float) buffer[y * imageData->width() + x];
0805 }
0806 
0807 
0808 
0809 float StarProfileViewer::getImageDataValue(int x, int y)
0810 {
0811     switch (imageData->getStatistics().dataType)
0812     {
0813         case TBYTE:
0814             return getImageDataValue<uint8_t>(x, y);
0815             break;
0816 
0817         case TSHORT:
0818             return getImageDataValue<int16_t>(x, y);
0819             break;
0820 
0821         case TUSHORT:
0822             return getImageDataValue<uint16_t>(x, y);
0823             break;
0824 
0825         case TLONG:
0826             return getImageDataValue<int32_t>(x, y);
0827             break;
0828 
0829         case TULONG:
0830             return getImageDataValue<uint32_t>(x, y);
0831             break;
0832 
0833         case TFLOAT:
0834             return getImageDataValue<float>(x, y);
0835             break;
0836 
0837         case TLONGLONG:
0838             return getImageDataValue<int64_t>(x, y);
0839             break;
0840 
0841         case TDOUBLE:
0842             return getImageDataValue<double>(x, y);
0843             break;
0844 
0845         default:
0846             return 0;
0847             break;
0848     }
0849 }
0850 
0851 void StarProfileViewer::toggleSlice()
0852 {
0853     if(m_graph->selectionMode() == QAbstract3DGraph::SelectionItemAndRow || m_graph->selectionMode() == QAbstract3DGraph::SelectionItemAndColumn)
0854     {
0855 
0856         if(m_graph->scene()->isSlicingActive())
0857         {
0858             m_graph->scene()->setSlicingActive(false);
0859         }
0860         else
0861         {
0862             QPoint selectedBar = m_graph->selectedSeries()
0863                     ? m_graph->selectedSeries()->selectedBar()
0864                     : QBar3DSeries::invalidSelectionPosition();
0865             if (selectedBar != QBar3DSeries::invalidSelectionPosition())
0866                 m_graph->scene()->setSlicingActive(true);
0867         }
0868     }
0869 }
0870 
0871 void StarProfileViewer::updateVerticalAxis()
0872 {
0873     float blackPoint = convertFromSliderValue(blackPointSlider->value());
0874     float whitePoint = convertFromSliderValue(whitePointSlider->value());
0875     m_pixelValueAxis->setRange(blackPoint, whitePoint);
0876     maxValue->setText(i18n("Max: %1", roundf(whitePoint * 100) / 100));
0877     minValue->setText(i18n("Min: %1", roundf(blackPoint * 100) / 100));
0878 }
0879 
0880 void StarProfileViewer::updateHFRandPeakSelection()
0881 {
0882     m_graph->removeCustomItems();
0883 
0884     reportBox->setText("");
0885     QString reportString = "";
0886 
0887     //Removes all the stars from the combo box.
0888     while(zoomView->count() > 7)
0889         zoomView->removeItem(7);
0890 
0891     for (int i = 0; i < starCenters.count(); i++)
0892     {
0893         int x = starCenters[i]->x;
0894         int row = x - subFrame.x();
0895         int y = starCenters[i]->y;
0896         int col = subFrame.height() - (y - subFrame.y());
0897         if(subFrame.contains(x,y)){
0898             double newHFR = imageData->getHFR(x,y);
0899             int value = getImageDataValue(x, y);
0900             QCustom3DLabel *label = new QCustom3DLabel();
0901             label->setFacingCamera(true);
0902             QString labelString = i18n("Star %1: ", i + 1);
0903             if(showCoordinates->isChecked())
0904             {
0905                 labelString = labelString + i18n("(%1, %2) ", x + 1, y + 1);
0906             }
0907             if(HFRReport->isChecked())
0908             {
0909                 labelString = labelString + i18n("HFR: %1  ", roundf(newHFR * 100) / 100);
0910             }
0911             if(showPeakValues->isChecked())
0912             {
0913                 labelString = labelString + i18n("Peak: %1", value);
0914 
0915             }
0916             if(showCoordinates->isChecked() || HFRReport->isChecked() || showPeakValues->isChecked())
0917             {
0918                 if (!reportString.isEmpty())
0919                     reportString += '\n';
0920 
0921                 reportString += labelString;
0922                 label->setText(labelString);
0923                 label->setPosition(QVector3D(row, value, col));
0924                 label->setScaling(QVector3D(1.0f, 1.0f, 1.0f));
0925                 m_graph->addCustomItem(label);
0926             }
0927             //Adds this star to the combo box.
0928             zoomView->addItem(i18n("Star %1", i + 1));
0929         }
0930     }
0931     if (!reportString.isEmpty())
0932     {
0933         reportBox->setText(reportString);
0934     }
0935 }
0936 
0937 void StarProfileViewer::updateColor(int selection)
0938 {
0939     switch (selection) {
0940     case 0:
0941         setGreenToRedGradient();
0942         break;
0943 
0944     case 1:
0945         setBlackToYellowGradient();
0946         break;
0947 
0948     default:
0949         break;
0950     }
0951 }
0952 
0953 void StarProfileViewer::setBlackToYellowGradient()
0954 {
0955     QLinearGradient gr;
0956     gr.setColorAt(0.0, Qt::black);
0957     gr.setColorAt(0.33, Qt::blue);
0958     gr.setColorAt(0.67, Qt::red);
0959     gr.setColorAt(1.0, Qt::yellow);
0960 
0961     QLinearGradient highGr;
0962     highGr.setColorAt(0.0, Qt::yellow);
0963     highGr.setColorAt(1.0, Qt::yellow);
0964 
0965     QLinearGradient sinHighGr;
0966     sinHighGr.setColorAt(0.0, Qt::red);
0967     sinHighGr.setColorAt(1.0, Qt::red);
0968 
0969     m_3DPixelSeries->setColorStyle(Q3DTheme::ColorStyleRangeGradient);
0970     m_3DPixelSeries->setBaseGradient(gr);
0971     m_3DPixelSeries->setSingleHighlightGradient(sinHighGr);
0972     m_3DPixelSeries->setMultiHighlightGradient(highGr);
0973 }
0974 
0975 void StarProfileViewer::setGreenToRedGradient()
0976 {
0977     QLinearGradient gr;
0978     gr.setColorAt(0.0, Qt::darkGreen);
0979     gr.setColorAt(0.5, Qt::yellow);
0980     gr.setColorAt(0.8, Qt::red);
0981     gr.setColorAt(1.0, Qt::darkRed);
0982 
0983     QLinearGradient highGr;
0984     highGr.setColorAt(0.0, Qt::black);
0985     highGr.setColorAt(1.0, Qt::black);
0986 
0987     QLinearGradient sinHighGr;
0988     sinHighGr.setColorAt(0.0, Qt::red);
0989     sinHighGr.setColorAt(1.0, Qt::red);
0990 
0991     m_3DPixelSeries->setBaseGradient(gr);
0992     m_3DPixelSeries->setColorStyle(Q3DTheme::ColorStyleRangeGradient);
0993     m_3DPixelSeries->setSingleHighlightGradient(sinHighGr);
0994     m_3DPixelSeries->setMultiHighlightGradient(highGr);
0995 }
0996 
0997 //Multiplying by 1000000 will take care of preserving decimals in an int slider
0998 //The sqrt function makes the slider non-linear, emphasising the lower values
0999 //Note that it is actually multiplying the number on the slider by 1000 or so since it is square rooted.
1000 
1001 int StarProfileViewer::convertToSliderValue(float value)
1002 {
1003     return (int) qSqrt((value * 1000000.0));
1004 }
1005 
1006 float StarProfileViewer::convertFromSliderValue(int value)
1007 {
1008     return qPow((float)value,2) / 1000000.0;
1009 }
1010