Kstars

focushfrvplot.cpp
1/*
2 SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
3 SPDX-FileCopyrightText: 2021 Wolfgang Reissenberger <sterne-jaeger@openfuture.de>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6*/
7
8#include "focushfrvplot.h"
9
10#include "klocalizedstring.h"
11
12#include "curvefit.h"
13#include <QToolTip>
14
15#define DEFAULT_BASIC_FONT_SIZE 10
16
17FocusHFRVPlot::FocusHFRVPlot(QWidget *parent) : QCustomPlot (parent)
18{
19 setBackground(QBrush(Qt::black));
20
21 xAxis->setBasePen(QPen(Qt::white, 1));
22 yAxis->setBasePen(QPen(Qt::white, 1));
23
24 xAxis->setTickPen(QPen(Qt::white, 1));
25 yAxis->setTickPen(QPen(Qt::white, 1));
26
27 xAxis->setSubTickPen(QPen(Qt::white, 1));
28 yAxis->setSubTickPen(QPen(Qt::white, 1));
29
30 xAxis->setTickLabelColor(Qt::white);
31 yAxis->setTickLabelColor(Qt::white);
32
33 xAxis->setLabelColor(Qt::white);
34 yAxis->setLabelColor(Qt::white);
35
36 xAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
37 yAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
38 xAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
39 yAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
40 xAxis->grid()->setZeroLinePen(Qt::NoPen);
41 yAxis->grid()->setZeroLinePen(Qt::NoPen);
42
43 yAxis->setLabel(i18n("Value"));
44
45 setInteractions(QCP::iRangeZoom);
46 setInteraction(QCP::iRangeDrag, true);
47
48 polynomialGraph = addGraph();
49 polynomialGraph->setLineStyle(QCPGraph::lsLine);
50 polynomialGraph->setPen(QPen(QColor(140, 140, 140), 2, Qt::DotLine));
51 polynomialGraph->setScatterStyle(QCPScatterStyle::ssNone);
52
53 v_graph = addGraph();
54 v_graph->setLineStyle(QCPGraph::lsNone);
55 v_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::white, Qt::white, 14));
56
57 focusPoint = addGraph();
58 focusPoint->setLineStyle(QCPGraph::lsImpulse);
59 focusPoint->setPen(QPen(QColor(140, 140, 140), 2, Qt::SolidLine));
60 focusPoint->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::white, Qt::yellow, 10));
61
62 // determine font size
63 if (parent != nullptr)
64 setBasicFontSize(parent->font().pointSize());
65 else
66 setBasicFontSize(DEFAULT_BASIC_FONT_SIZE);
67
68 connect(this, &QCustomPlot::mouseMove, [this](QMouseEvent * event)
69 {
70 double key = xAxis->pixelToCoord(event->localPos().x());
71 if (xAxis->range().contains(key))
72 {
73 QCPGraph *graph = qobject_cast<QCPGraph *>(plottableAt(event->pos(), false));
74
75 if (graph)
76 {
77 if(graph == v_graph)
78 {
79 int positionKey = v_graph->findBegin(key);
80 double focusPosition = v_graph->dataMainKey(positionKey);
81 double focusValue = v_graph->dataMainValue(positionKey);
83 event->globalPos(),
84 i18nc("Graphics tooltip; %1 is the Focus Position; %2 is the Focus Value;",
85 "<table>"
86 "<tr><td>POS: </td><td>%1</td></tr>"
87 "<tr><td>VAL: </td><td>%2</td></tr>"
88 "</table>",
89 QString::number(focusPosition, 'f', 0),
90 QString::number(focusValue, 'g', 3)));
91 }
92 else if (graph == focusPoint)
93 {
94 int positionKey = focusPoint->findBegin(key);
95 double focusPosition = focusPoint->dataMainKey(positionKey);
96 double focusValue = focusPoint->dataMainValue(positionKey);
98 event->globalPos(),
99 i18nc("Graphics tooltip; %1 is the Minimum Focus Position; %2 is the Focus Value;",
100 "<table>"
101 "<tr><td>MIN: </td><td>%1</td></tr>"
102 "<tr><td>VAL: </td><td>%2</td></tr>"
103 "</table>",
104 QString::number(focusPosition, 'f', 0),
105 QString::number(focusValue, 'g', 3)));
106
107 }
108 }
109 }
110 });
111 // Add the error bars
112 errorBars = new QCPErrorBars((this)->xAxis, (this)->yAxis);
113}
114
115void FocusHFRVPlot::drawHFRIndices()
116{
117 // Setup error bars
118 QVector<double> err;
119 if (m_useErrorBars)
120 {
121 errorBars->removeFromLegend();
122 errorBars->setAntialiased(false);
123 errorBars->setDataPlottable((this)->v_graph);
124 errorBars->setPen(QPen(QColor(180, 180, 180)));
125 }
126
127 // Put the sample number inside the plot point's circle.
128 for (int i = 0; i < m_position.size(); ++i)
129 {
130 QCPItemText *textLabel = new QCPItemText(this);
132 textLabel->position->setType(QCPItemPosition::ptPlotCoords);
133 textLabel->position->setCoords(m_position[i], m_displayValue[i]);
134 if (m_goodPosition[i])
135 {
136 textLabel->setText(QString::number(i + 1));
137 textLabel->setFont(QFont(font().family(), (int) std::round(1.2 * basicFontSize())));
138 textLabel->setColor(Qt::red);
139 }
140 else
141 {
142 textLabel->setText("X");
143 textLabel->setFont(QFont(font().family(), (int) std::round(2 * basicFontSize())));
144 textLabel->setColor(Qt::black);
145 }
146 textLabel->setPen(Qt::NoPen);
147
148 if (m_useErrorBars)
149 err.push_front(m_sigma[i]);
150 }
151 // Setup the error bar data if we're using it
152 errorBars->setVisible(m_useErrorBars);
153 if (m_useErrorBars)
154 errorBars->setData(err);
155}
156
157void FocusHFRVPlot::init(QString yAxisLabel, double starUnits, bool minimum, bool useWeights, bool showPosition)
158{
159 yAxis->setLabel(yAxisLabel);
160 m_starUnits = starUnits;
161 m_Minimum = minimum;
162 m_showPositions = showPosition;
163 m_position.clear();
164 m_value.clear();
165 m_displayValue.clear();
166 m_sigma.clear();
167 m_goodPosition.clear();
168 polynomialGraph->data()->clear();
169 focusPoint->data().clear();
170 m_useErrorBars = useWeights;
171 errorBars->data().clear();
172 // the next step seems necessary (QCP bug?)
173 v_graph->setData(QVector<double> {}, QVector<double> {});
174 focusPoint->setData(QVector<double> {}, QVector<double> {});
175 m_polynomialGraphIsVisible = false;
176 m_isVShape = false;
177 minValue = -1;
178 maxValue = -1;
179 FocusHFRVPlot::clearItems();
180 replot();
181}
182
183void FocusHFRVPlot::drawHFRPlot(double currentValue, int pulseDuration)
184{
185 // DrawHFRPlot is the base on which other things are built upon.
186 // Clear any previous annotations.
187 FocusHFRVPlot::clearItems();
188
189 v_graph->setData(m_position, m_displayValue);
190 drawHFRIndices();
191
192 double currentDisplayValue = getDisplayValue(currentValue);
193
194 if (minValue > currentDisplayValue || minValue < 0.0)
195 minValue = currentDisplayValue;
196 if (maxValue < currentDisplayValue)
197 maxValue = currentDisplayValue;
198
199 double minVal = currentDisplayValue / 2.5;
200 if (m_displayValue.size() > 0)
201 minVal = std::min(minValue, *std::min_element(m_displayValue.begin(), m_displayValue.end()));
202
203 // True for the position-based algorithms and those that simulate position.
204 if (m_showPositions)
205 {
206 const double minPosition = m_position.empty() ?
207 0 : *std::min_element(m_position.constBegin(), m_position.constEnd());
208 const double maxPosition = m_position.empty() ?
209 1e6 : *std::max_element(m_position.constBegin(), m_position.constEnd());
210 xAxis->setRange(minPosition - pulseDuration, maxPosition + pulseDuration);
211 }
212 else
213 {
214 //xAxis->setLabel(i18n("Iteration"));
215 xAxis->setRange(1, m_displayValue.count() + 1);
216 }
217
218 if (m_displayValue.size() == 1)
219 // 1 point gets placed in the middle vertically.
220 yAxis->setRange(0, 2 * getDisplayValue(maxValue));
221 else
222 {
223 double upper;
224 m_Minimum ? upper = 1.5 * maxValue : upper = 1.2 * maxValue;
225 yAxis->setRange(minVal - (0.25 * (upper - minVal)), upper);
226 }
227 replot();
228}
229
230void FocusHFRVPlot::addPosition(double pos, double newValue, double sigma, bool outlier, int pulseDuration, bool plot)
231{
232 m_position.append(pos);
233 m_value.append(newValue);
234 m_displayValue.append(getDisplayValue(newValue));
235 m_sigma.append(sigma);
236 outlier ? m_goodPosition.append(false) : m_goodPosition.append(true);
237
238 if (plot)
239 drawHFRPlot(newValue, pulseDuration);
240}
241
242void FocusHFRVPlot::setTitle(const QString &title, bool plot)
243{
244 plotTitle = new QCPItemText(this);
245 plotTitle->setColor(QColor(255, 255, 255));
247 plotTitle->position->setType(QCPItemPosition::ptAxisRectRatio);
248 plotTitle->position->setCoords(0.5, 0);
249 plotTitle->setText("");
250 plotTitle->setFont(QFont(font().family(), 11));
251 plotTitle->setVisible(true);
252
253 plotTitle->setText(title);
254 if (plot) replot();
255}
256
257QString FocusHFRVPlot::title() const
258{
259 if (plotTitle)
260 return plotTitle->text();
261 return QString();
262}
263
264void FocusHFRVPlot::finalUpdates(const QString &title, bool plot)
265{
266 // Update a previously set title without having to redraw everything
267 if (plotTitle != nullptr)
268 {
269 plotTitle->setText(title);
270 if (plot) replot();
271 }
272}
273void FocusHFRVPlot::setSolutionVShape(bool isVShape)
274{
275 m_isVShape = isVShape;
276
277 QPen pen;
278 pen.setWidth(1);
279
280 if (isVShape)
281 {
282 pen.setColor(QColor(180, 180, 180));
283 }
284 else
285 {
286 pen.setColor(QColor(254, 0, 0));
287 // clear focus point
288 focusPoint->data().clear();
289 }
290
291 polynomialGraph->setPen(pen);
292}
293
294void FocusHFRVPlot::clearItems()
295{
296 // Clear all the items on the HFR plot and reset pointers
298 plotTitle = nullptr;
299 CFZ = nullptr;
300}
301
302void FocusHFRVPlot::drawMinimum(double solutionPosition, double solutionValue, bool plot)
303{
304 focusPoint->data()->clear();
305
306 // do nothing for invalid positions
307 if (solutionPosition < 0)
308 return;
309
310 double displayValue = getDisplayValue(solutionValue);
311 minValue = std::min(minValue, displayValue);
312 maxValue = std::max(maxValue, displayValue);
313
314 focusPoint->addData(solutionPosition, displayValue);
315 QCPItemText *textLabel = new QCPItemText(this);
317 textLabel->setColor(Qt::red);
318 textLabel->setPadding(QMargins(0, 0, 0, 0));
319 textLabel->setBrush(Qt::white);
320 textLabel->setPen(Qt::NoPen);
321 textLabel->setFont(QFont(font().family(), (int) std::round(0.8 * basicFontSize())));
322 textLabel->position->setType(QCPItemPosition::ptPlotCoords);
323 textLabel->setText(QString::number(solutionPosition, 'f', 0));
324 if (m_Minimum)
325 textLabel->position->setCoords(solutionPosition, (maxValue + 2 * displayValue) / 3);
326 else
327 textLabel->position->setCoords(solutionPosition, (2 * displayValue + minValue) / 3);
328 if (plot) replot();
329}
330
331void FocusHFRVPlot::drawCFZ(double solutionPosition, double solutionValue, int cfzSteps, bool plot)
332{
333 // do nothing for invalid positions
334 if (solutionPosition < 0 || solutionValue < 0)
335 return;
336
337 if (!plot)
338 {
339 if (CFZ)
340 CFZ->setVisible(false);
341 }
342 else
343 {
344 if (!CFZ)
345 CFZ = new QCPItemBracket(this);
346
349
350 double y;
351 if (m_Minimum)
352 y = (7 * minValue - maxValue) / 6;
353 else
354 y = (maxValue + 2 * minValue) / 3;
355
356 CFZ->left->setCoords(solutionPosition + cfzSteps / 2.0, y);
357 CFZ->right->setCoords(solutionPosition - cfzSteps / 2.0, y);
358 CFZ->setLength(15);
359 CFZ->setAntialiased(false);
361 CFZ->setVisible(true);
362 }
363 replot();
364}
365
366void FocusHFRVPlot::drawPolynomial(Ekos::PolynomialFit *polyFit, bool isVShape, bool makeVisible, bool plot)
367{
368 if (polyFit == nullptr)
369 return;
370
371 // do nothing if graph is not visible and should not be made as such
372 if(makeVisible)
373 m_polynomialGraphIsVisible = true;
374 else if (m_polynomialGraphIsVisible == false)
375 return;
376
377 setSolutionVShape(isVShape);
378 if (polynomialGraph != nullptr)
379 {
380 polynomialGraph->data()->clear();
381 QCPRange range = xAxis->range();
382 double interval = range.size() / 20.0;
383
384 for(double x = range.lower ; x < range.upper ; x += interval)
385 {
386 double y = getDisplayValue(polyFit->f(x));
387 polynomialGraph->addData(x, y);
388 }
389 if (plot) replot();
390 }
391}
392
393void FocusHFRVPlot::drawCurve(Ekos::CurveFitting *curveFit, bool isVShape, bool makeVisible, bool plot)
394{
395 if (curveFit == nullptr)
396 return;
397
398 // do nothing if graph is not visible and should not be made as such
399 if(makeVisible)
400 m_polynomialGraphIsVisible = true;
401 else if (!makeVisible || !m_polynomialGraphIsVisible)
402 return;
403
404 setSolutionVShape(isVShape);
405 if (polynomialGraph != nullptr)
406 {
407 polynomialGraph->data()->clear();
408 QCPRange range = xAxis->range();
409 double interval = range.size() / 20.0;
410
411 for(double x = range.lower ; x < range.upper ; x += interval)
412 {
413 double y = getDisplayValue(curveFit->f(x));
414 polynomialGraph->addData(x, y);
415 }
416 if (plot) replot();
417 }
418}
419
420void FocusHFRVPlot::redraw(Ekos::PolynomialFit *polyFit, double solutionPosition, double solutionValue)
421{
422 if (m_value.empty() == false)
423 drawHFRPlot(m_value.last(), 0);
424
425 drawPolynomial(polyFit, m_isVShape, false);
426 drawMinimum(solutionPosition, solutionValue);
427}
428
429void FocusHFRVPlot::redrawCurve(Ekos::CurveFitting *curveFit, double solutionPosition, double solutionValue)
430{
431 if (m_value.empty() == false)
432 drawHFRPlot(solutionValue, 0);
433
434 drawCurve(curveFit, m_isVShape, false);
435 drawMinimum(solutionPosition, solutionValue);
436}
437
438void FocusHFRVPlot::setBasicFontSize(int basicFontSize)
439{
440 m_basicFontSize = basicFontSize;
441
442 // Axis Labels Settings
443 yAxis->setLabelFont(QFont(font().family(), basicFontSize));
444 xAxis->setTickLabelFont(QFont(font().family(), (int) std::round(0.9 * basicFontSize)));
445 yAxis->setTickLabelFont(QFont(font().family(), (int) std::round(0.9 * basicFontSize)));
446}
447
448// Internally calculations are done in units of pixels for HFR and FWHM
449// If user preference is arcsecs then convert values for display purposes.
450double FocusHFRVPlot::getDisplayValue(const double value)
451{
452 return value * m_starUnits;
453}
454
void setPen(const QPen &pen)
void setTickLabelFont(const QFont &font)
void setLabel(const QString &str)
void setLabelFont(const QFont &font)
Q_SLOT void setRange(const QCPRange &range)
A plottable that adds a set of error bars to other plottables.
A plottable representing a graph in a plot.
QSharedPointer< QCPGraphDataContainer > data() const
void setData(QSharedPointer< QCPGraphDataContainer > data)
@ lsLine
data points are connected by a straight line
@ lsImpulse
each data point is represented by a line parallel to the value axis, which reaches from the data poin...
@ lsNone
data points are not connected with any lines (e.g.
void addData(const QVector< double > &keys, const QVector< double > &values, bool alreadySorted=false)
A bracket for referencing/highlighting certain parts in the plot.
void setPen(const QPen &pen)
void setLength(double length)
void setType(PositionType type)
void setCoords(double key, double value)
@ ptAxisRectRatio
Static positioning given by a fraction of the axis rect size (see setAxisRect).
@ ptPlotCoords
Dynamic positioning at a plot coordinate defined by two axes (see setAxes).
A text label.
void setBrush(const QBrush &brush)
void setText(const QString &text)
void setPositionAlignment(Qt::Alignment alignment)
void setFont(const QFont &font)
void setPen(const QPen &pen)
void setColor(const QColor &color)
void setPadding(const QMargins &padding)
void setVisible(bool on)
void setAntialiased(bool enabled)
Represents the range an axis is encompassing.
double size() const
Represents the visual appearance of scatter points.
@ ssCircle
\enumimage{ssCircle.png} a circle
@ ssNone
no scatter symbols are drawn (e.g. in QCPGraph, data only represented with lines)
The central class of the library. This is the QWidget which displays the plot and interacts with the ...
void mouseMove(QMouseEvent *event)
QCPAxis * xAxis
Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpRefreshHint)
QCPAxis * yAxis
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
@ iRangeDrag
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes)
@ iRangeZoom
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom,...
void push_front(parameter_type value)
void setColor(const QColor &color)
void setWidth(int width)
QString number(double n, char format, int precision)
AlignCenter
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void showText(const QPoint &pos, const QString &text, QWidget *w, const QRect &rect, int msecDisplayTime)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:16:40 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.