File indexing completed on 2024-05-05 15:55:12
0001 /* 0002 SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com> 0003 SPDX-FileCopyrightText: 2021 Wolfgang Reissenberger <sterne-jaeger@openfuture.de> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "focushfrvplot.h" 0009 0010 #include "klocalizedstring.h" 0011 0012 #include "curvefit.h" 0013 0014 #define DEFAULT_BASIC_FONT_SIZE 10 0015 0016 FocusHFRVPlot::FocusHFRVPlot(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 yAxis->setLabel(i18n("Value")); 0043 0044 setInteractions(QCP::iRangeZoom); 0045 setInteraction(QCP::iRangeDrag, true); 0046 0047 polynomialGraph = addGraph(); 0048 polynomialGraph->setLineStyle(QCPGraph::lsLine); 0049 polynomialGraph->setPen(QPen(QColor(140, 140, 140), 2, Qt::DotLine)); 0050 polynomialGraph->setScatterStyle(QCPScatterStyle::ssNone); 0051 0052 v_graph = addGraph(); 0053 v_graph->setLineStyle(QCPGraph::lsNone); 0054 v_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::white, Qt::white, 14)); 0055 0056 focusPoint = addGraph(); 0057 focusPoint->setLineStyle(QCPGraph::lsImpulse); 0058 focusPoint->setPen(QPen(QColor(140, 140, 140), 2, Qt::SolidLine)); 0059 focusPoint->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::white, Qt::yellow, 10)); 0060 0061 // determine font size 0062 if (parent != nullptr) 0063 setBasicFontSize(parent->font().pointSize()); 0064 else 0065 setBasicFontSize(DEFAULT_BASIC_FONT_SIZE); 0066 0067 connect(this, &QCustomPlot::mouseMove, [this](QMouseEvent * event) 0068 { 0069 double key = xAxis->pixelToCoord(event->localPos().x()); 0070 if (xAxis->range().contains(key)) 0071 { 0072 QCPGraph *graph = qobject_cast<QCPGraph *>(plottableAt(event->pos(), false)); 0073 0074 if (graph) 0075 { 0076 if(graph == v_graph) 0077 { 0078 int positionKey = v_graph->findBegin(key); 0079 double focusPosition = v_graph->dataMainKey(positionKey); 0080 double focusValue = v_graph->dataMainValue(positionKey); 0081 QToolTip::showText( 0082 event->globalPos(), 0083 i18nc("Graphics tooltip; %1 is the Focus Position; %2 is the Focus Value;", 0084 "<table>" 0085 "<tr><td>POS: </td><td>%1</td></tr>" 0086 "<tr><td>VAL: </td><td>%2</td></tr>" 0087 "</table>", 0088 QString::number(focusPosition, 'f', 0), 0089 QString::number(focusValue, 'g', 3))); 0090 } 0091 else if (graph == focusPoint) 0092 { 0093 int positionKey = focusPoint->findBegin(key); 0094 double focusPosition = focusPoint->dataMainKey(positionKey); 0095 double focusValue = focusPoint->dataMainValue(positionKey); 0096 QToolTip::showText( 0097 event->globalPos(), 0098 i18nc("Graphics tooltip; %1 is the Minimum Focus Position; %2 is the Focus Value;", 0099 "<table>" 0100 "<tr><td>MIN: </td><td>%1</td></tr>" 0101 "<tr><td>VAL: </td><td>%2</td></tr>" 0102 "</table>", 0103 QString::number(focusPosition, 'f', 0), 0104 QString::number(focusValue, 'g', 3))); 0105 0106 } 0107 } 0108 } 0109 }); 0110 // Add the error bars 0111 errorBars = new QCPErrorBars((this)->xAxis, (this)->yAxis); 0112 } 0113 0114 void FocusHFRVPlot::drawHFRIndices() 0115 { 0116 // Setup error bars 0117 QVector<double> err; 0118 if (m_useErrorBars) 0119 { 0120 errorBars->removeFromLegend(); 0121 errorBars->setAntialiased(false); 0122 errorBars->setDataPlottable((this)->v_graph); 0123 errorBars->setPen(QPen(QColor(180, 180, 180))); 0124 } 0125 0126 // Put the sample number inside the plot point's circle. 0127 for (int i = 0; i < m_position.size(); ++i) 0128 { 0129 QCPItemText *textLabel = new QCPItemText(this); 0130 textLabel->setPositionAlignment(Qt::AlignCenter | Qt::AlignHCenter); 0131 textLabel->position->setType(QCPItemPosition::ptPlotCoords); 0132 textLabel->position->setCoords(m_position[i], m_displayValue[i]); 0133 if (m_goodPosition[i]) 0134 { 0135 textLabel->setText(QString::number(i + 1)); 0136 textLabel->setFont(QFont(font().family(), (int) std::round(1.2 * basicFontSize()))); 0137 textLabel->setColor(Qt::red); 0138 } 0139 else 0140 { 0141 textLabel->setText("X"); 0142 textLabel->setFont(QFont(font().family(), (int) std::round(2 * basicFontSize()))); 0143 textLabel->setColor(Qt::black); 0144 } 0145 textLabel->setPen(Qt::NoPen); 0146 0147 if (m_useErrorBars) 0148 err.push_front(m_sigma[i]); 0149 } 0150 // Setup the error bar data if we're using it 0151 errorBars->setVisible(m_useErrorBars); 0152 if (m_useErrorBars) 0153 errorBars->setData(err); 0154 } 0155 0156 void FocusHFRVPlot::init(QString yAxisLabel, double starUnits, bool minimum, bool useWeights, bool showPosition) 0157 { 0158 yAxis->setLabel(yAxisLabel); 0159 m_starUnits = starUnits; 0160 m_Minimum = minimum; 0161 m_showPositions = showPosition; 0162 m_position.clear(); 0163 m_value.clear(); 0164 m_displayValue.clear(); 0165 m_sigma.clear(); 0166 m_goodPosition.clear(); 0167 polynomialGraph->data()->clear(); 0168 focusPoint->data().clear(); 0169 m_useErrorBars = useWeights; 0170 errorBars->data().clear(); 0171 // the next step seems necessary (QCP bug?) 0172 v_graph->setData(QVector<double> {}, QVector<double> {}); 0173 focusPoint->setData(QVector<double> {}, QVector<double> {}); 0174 m_polynomialGraphIsVisible = false; 0175 m_isVShape = false; 0176 minValue = -1; 0177 maxValue = -1; 0178 FocusHFRVPlot::clearItems(); 0179 replot(); 0180 } 0181 0182 void FocusHFRVPlot::drawHFRPlot(double currentValue, int pulseDuration) 0183 { 0184 // DrawHFRPlot is the base on which other things are built upon. 0185 // Clear any previous annotations. 0186 FocusHFRVPlot::clearItems(); 0187 0188 v_graph->setData(m_position, m_displayValue); 0189 drawHFRIndices(); 0190 0191 double currentDisplayValue = getDisplayValue(currentValue); 0192 0193 if (minValue > currentDisplayValue || minValue < 0.0) 0194 minValue = currentDisplayValue; 0195 if (maxValue < currentDisplayValue) 0196 maxValue = currentDisplayValue; 0197 0198 double minVal = currentDisplayValue / 2.5; 0199 if (m_displayValue.size() > 0) 0200 minVal = std::max(0.0, std::min(minValue, *std::min_element(m_displayValue.begin(), m_displayValue.end()))); 0201 0202 // True for the position-based algorithms and those that simulate position. 0203 if (m_showPositions) 0204 { 0205 const double minPosition = m_position.empty() ? 0206 0 : *std::min_element(m_position.constBegin(), m_position.constEnd()); 0207 const double maxPosition = m_position.empty() ? 0208 1e6 : *std::max_element(m_position.constBegin(), m_position.constEnd()); 0209 xAxis->setRange(minPosition - pulseDuration, maxPosition + pulseDuration); 0210 } 0211 else 0212 { 0213 //xAxis->setLabel(i18n("Iteration")); 0214 xAxis->setRange(1, m_displayValue.count() + 1); 0215 } 0216 0217 if (m_displayValue.size() == 1) 0218 // 1 point gets placed in the middle vertically. 0219 yAxis->setRange(0, 2 * getDisplayValue(maxValue)); 0220 else 0221 { 0222 double upper; 0223 m_Minimum ? upper = 1.5 * maxValue : upper = 1.2 * maxValue; 0224 yAxis->setRange(minVal - (0.25 * (upper - minVal)), upper); 0225 } 0226 replot(); 0227 } 0228 0229 void FocusHFRVPlot::addPosition(double pos, double newValue, double sigma, bool outlier, int pulseDuration, bool plot) 0230 { 0231 m_position.append(pos); 0232 m_value.append(newValue); 0233 m_displayValue.append(getDisplayValue(newValue)); 0234 m_sigma.append(sigma); 0235 outlier ? m_goodPosition.append(false) : m_goodPosition.append(true); 0236 0237 if (plot) 0238 drawHFRPlot(newValue, pulseDuration); 0239 } 0240 0241 void FocusHFRVPlot::setTitle(const QString &title, bool plot) 0242 { 0243 plotTitle = new QCPItemText(this); 0244 plotTitle->setColor(QColor(255, 255, 255)); 0245 plotTitle->setPositionAlignment(Qt::AlignTop | Qt::AlignHCenter); 0246 plotTitle->position->setType(QCPItemPosition::ptAxisRectRatio); 0247 plotTitle->position->setCoords(0.5, 0); 0248 plotTitle->setText(""); 0249 plotTitle->setFont(QFont(font().family(), 11)); 0250 plotTitle->setVisible(true); 0251 0252 plotTitle->setText(title); 0253 if (plot) replot(); 0254 } 0255 0256 void FocusHFRVPlot::finalUpdates(const QString &title, bool plot) 0257 { 0258 // Update a previously set title without having to redraw everything 0259 if (plotTitle != nullptr) 0260 { 0261 plotTitle->setText(title); 0262 if (plot) replot(); 0263 } 0264 } 0265 void FocusHFRVPlot::setSolutionVShape(bool isVShape) 0266 { 0267 m_isVShape = isVShape; 0268 0269 QPen pen; 0270 pen.setWidth(1); 0271 0272 if (isVShape) 0273 { 0274 pen.setColor(QColor(180, 180, 180)); 0275 } 0276 else 0277 { 0278 pen.setColor(QColor(254, 0, 0)); 0279 // clear focus point 0280 focusPoint->data().clear(); 0281 } 0282 0283 polynomialGraph->setPen(pen); 0284 } 0285 0286 void FocusHFRVPlot::clearItems() 0287 { 0288 // Clear all the items on the HFR plot and reset pointers 0289 QCustomPlot::clearItems(); 0290 plotTitle = nullptr; 0291 CFZ = nullptr; 0292 } 0293 0294 void FocusHFRVPlot::drawMinimum(double solutionPosition, double solutionValue, bool plot) 0295 { 0296 focusPoint->data()->clear(); 0297 0298 // do nothing for invalid positions 0299 if (solutionPosition < 0) 0300 return; 0301 0302 double displayValue = getDisplayValue(solutionValue); 0303 minValue = std::min(minValue, displayValue); 0304 maxValue = std::max(maxValue, displayValue); 0305 0306 focusPoint->addData(solutionPosition, displayValue); 0307 QCPItemText *textLabel = new QCPItemText(this); 0308 textLabel->setPositionAlignment(Qt::AlignVCenter | Qt::AlignHCenter); 0309 textLabel->setColor(Qt::red); 0310 textLabel->setPadding(QMargins(0, 0, 0, 0)); 0311 textLabel->setBrush(Qt::white); 0312 textLabel->setPen(Qt::NoPen); 0313 textLabel->setFont(QFont(font().family(), (int) std::round(0.8 * basicFontSize()))); 0314 textLabel->position->setType(QCPItemPosition::ptPlotCoords); 0315 textLabel->setText(QString::number(solutionPosition, 'f', 0)); 0316 if (m_Minimum) 0317 textLabel->position->setCoords(solutionPosition, (maxValue + 2 * displayValue) / 3); 0318 else 0319 textLabel->position->setCoords(solutionPosition, (2 * displayValue + minValue) / 3); 0320 if (plot) replot(); 0321 } 0322 0323 void FocusHFRVPlot::drawCFZ(double solutionPosition, double solutionValue, int cfzSteps, bool plot) 0324 { 0325 // do nothing for invalid positions 0326 if (solutionPosition < 0 || solutionValue < 0) 0327 return; 0328 0329 if (!plot) 0330 { 0331 if (CFZ) 0332 CFZ->setVisible(false); 0333 } 0334 else 0335 { 0336 if (!CFZ) 0337 CFZ = new QCPItemBracket(this); 0338 0339 CFZ->left->setType(QCPItemPosition::ptPlotCoords); 0340 CFZ->right->setType(QCPItemPosition::ptPlotCoords); 0341 0342 double displayValue = getDisplayValue(solutionValue); 0343 double y; 0344 if (m_Minimum) 0345 y = (7 * minValue - maxValue) / 6; 0346 else 0347 y = (maxValue + 2 * minValue) / 3; 0348 0349 CFZ->left->setCoords(solutionPosition + cfzSteps / 2.0, y); 0350 CFZ->right->setCoords(solutionPosition - cfzSteps / 2.0, y); 0351 CFZ->setLength(15); 0352 CFZ->setAntialiased(false); 0353 CFZ->setPen(QPen(QColor(Qt::yellow))); 0354 CFZ->setVisible(true); 0355 } 0356 replot(); 0357 } 0358 0359 void FocusHFRVPlot::drawPolynomial(Ekos::PolynomialFit *polyFit, bool isVShape, bool makeVisible, bool plot) 0360 { 0361 if (polyFit == nullptr) 0362 return; 0363 0364 // do nothing if graph is not visible and should not be made as such 0365 if(makeVisible) 0366 m_polynomialGraphIsVisible = true; 0367 else if (m_polynomialGraphIsVisible == false) 0368 return; 0369 0370 setSolutionVShape(isVShape); 0371 if (polynomialGraph != nullptr) 0372 { 0373 polynomialGraph->data()->clear(); 0374 QCPRange range = xAxis->range(); 0375 double interval = range.size() / 20.0; 0376 0377 for(double x = range.lower ; x < range.upper ; x += interval) 0378 { 0379 double y = getDisplayValue(polyFit->f(x)); 0380 polynomialGraph->addData(x, y); 0381 } 0382 if (plot) replot(); 0383 } 0384 } 0385 0386 void FocusHFRVPlot::drawCurve(Ekos::CurveFitting *curveFit, bool isVShape, bool makeVisible, bool plot) 0387 { 0388 if (curveFit == nullptr) 0389 return; 0390 0391 // do nothing if graph is not visible and should not be made as such 0392 if(makeVisible) 0393 m_polynomialGraphIsVisible = true; 0394 else if (!makeVisible || !m_polynomialGraphIsVisible) 0395 return; 0396 0397 setSolutionVShape(isVShape); 0398 if (polynomialGraph != nullptr) 0399 { 0400 polynomialGraph->data()->clear(); 0401 QCPRange range = xAxis->range(); 0402 double interval = range.size() / 20.0; 0403 0404 for(double x = range.lower ; x < range.upper ; x += interval) 0405 { 0406 double y = getDisplayValue(curveFit->f(x)); 0407 polynomialGraph->addData(x, y); 0408 } 0409 if (plot) replot(); 0410 } 0411 } 0412 0413 void FocusHFRVPlot::redraw(Ekos::PolynomialFit *polyFit, double solutionPosition, double solutionValue) 0414 { 0415 if (m_value.empty() == false) 0416 drawHFRPlot(m_value.last(), 0); 0417 0418 drawPolynomial(polyFit, m_isVShape, false); 0419 drawMinimum(solutionPosition, solutionValue); 0420 } 0421 0422 void FocusHFRVPlot::redrawCurve(Ekos::CurveFitting *curveFit, double solutionPosition, double solutionValue) 0423 { 0424 if (m_value.empty() == false) 0425 drawHFRPlot(solutionValue, 0); 0426 0427 drawCurve(curveFit, m_isVShape, false); 0428 drawMinimum(solutionPosition, solutionValue); 0429 } 0430 0431 void FocusHFRVPlot::setBasicFontSize(int basicFontSize) 0432 { 0433 m_basicFontSize = basicFontSize; 0434 0435 // Axis Labels Settings 0436 yAxis->setLabelFont(QFont(font().family(), basicFontSize)); 0437 xAxis->setTickLabelFont(QFont(font().family(), (int) std::round(0.9 * basicFontSize))); 0438 yAxis->setTickLabelFont(QFont(font().family(), (int) std::round(0.9 * basicFontSize))); 0439 } 0440 0441 // Internally calculations are done in units of pixels for HFR and FWHM 0442 // If user preference is arcsecs then convert values for display purposes. 0443 double FocusHFRVPlot::getDisplayValue(const double value) 0444 { 0445 return value * m_starUnits; 0446 } 0447