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
14#define DEFAULT_BASIC_FONT_SIZE 10
15
16FocusHFRVPlot::FocusHFRVPlot(QWidget *parent) : QCustomPlot (parent)
17{
18 setBackground(QBrush(Qt::black));
19
20 xAxis->setBasePen(QPen(Qt::white, 1));
21 yAxis->setBasePen(QPen(Qt::white, 1));
22
23 xAxis->setTickPen(QPen(Qt::white, 1));
24 yAxis->setTickPen(QPen(Qt::white, 1));
25
26 xAxis->setSubTickPen(QPen(Qt::white, 1));
27 yAxis->setSubTickPen(QPen(Qt::white, 1));
28
29 xAxis->setTickLabelColor(Qt::white);
30 yAxis->setTickLabelColor(Qt::white);
31
32 xAxis->setLabelColor(Qt::white);
33 yAxis->setLabelColor(Qt::white);
34
35 xAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
36 yAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
37 xAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
38 yAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
39 xAxis->grid()->setZeroLinePen(Qt::NoPen);
40 yAxis->grid()->setZeroLinePen(Qt::NoPen);
41
42 yAxis->setLabel(i18n("Value"));
43
44 setInteractions(QCP::iRangeZoom);
45 setInteraction(QCP::iRangeDrag, true);
46
47 polynomialGraph = addGraph();
48 polynomialGraph->setLineStyle(QCPGraph::lsLine);
49 polynomialGraph->setPen(QPen(QColor(140, 140, 140), 2, Qt::DotLine));
50 polynomialGraph->setScatterStyle(QCPScatterStyle::ssNone);
51
52 v_graph = addGraph();
53 v_graph->setLineStyle(QCPGraph::lsNone);
54 v_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::white, Qt::white, 14));
55
56 focusPoint = addGraph();
57 focusPoint->setLineStyle(QCPGraph::lsImpulse);
58 focusPoint->setPen(QPen(QColor(140, 140, 140), 2, Qt::SolidLine));
59 focusPoint->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::white, Qt::yellow, 10));
60
61 // determine font size
62 if (parent != nullptr)
63 setBasicFontSize(parent->font().pointSize());
64 else
65 setBasicFontSize(DEFAULT_BASIC_FONT_SIZE);
66
67 connect(this, &QCustomPlot::mouseMove, [this](QMouseEvent * event)
68 {
69 double key = xAxis->pixelToCoord(event->localPos().x());
70 if (xAxis->range().contains(key))
71 {
72 QCPGraph *graph = qobject_cast<QCPGraph *>(plottableAt(event->pos(), false));
73
74 if (graph)
75 {
76 if(graph == v_graph)
77 {
78 int positionKey = v_graph->findBegin(key);
79 double focusPosition = v_graph->dataMainKey(positionKey);
80 double focusValue = v_graph->dataMainValue(positionKey);
82 event->globalPos(),
83 i18nc("Graphics tooltip; %1 is the Focus Position; %2 is the Focus Value;",
84 "<table>"
85 "<tr><td>POS: </td><td>%1</td></tr>"
86 "<tr><td>VAL: </td><td>%2</td></tr>"
87 "</table>",
88 QString::number(focusPosition, 'f', 0),
89 QString::number(focusValue, 'g', 3)));
90 }
91 else if (graph == focusPoint)
92 {
93 int positionKey = focusPoint->findBegin(key);
94 double focusPosition = focusPoint->dataMainKey(positionKey);
95 double focusValue = focusPoint->dataMainValue(positionKey);
97 event->globalPos(),
98 i18nc("Graphics tooltip; %1 is the Minimum Focus Position; %2 is the Focus Value;",
99 "<table>"
100 "<tr><td>MIN: </td><td>%1</td></tr>"
101 "<tr><td>VAL: </td><td>%2</td></tr>"
102 "</table>",
103 QString::number(focusPosition, 'f', 0),
104 QString::number(focusValue, 'g', 3)));
105
106 }
107 }
108 }
109 });
110 // Add the error bars
111 errorBars = new QCPErrorBars((this)->xAxis, (this)->yAxis);
112}
113
114void FocusHFRVPlot::drawHFRIndices()
115{
116 // Setup error bars
117 QVector<double> err;
118 if (m_useErrorBars)
119 {
120 errorBars->removeFromLegend();
121 errorBars->setAntialiased(false);
122 errorBars->setDataPlottable((this)->v_graph);
123 errorBars->setPen(QPen(QColor(180, 180, 180)));
124 }
125
126 // Put the sample number inside the plot point's circle.
127 for (int i = 0; i < m_position.size(); ++i)
128 {
129 QCPItemText *textLabel = new QCPItemText(this);
131 textLabel->position->setType(QCPItemPosition::ptPlotCoords);
132 textLabel->position->setCoords(m_position[i], m_displayValue[i]);
133 if (m_goodPosition[i])
134 {
135 textLabel->setText(QString::number(i + 1));
136 textLabel->setFont(QFont(font().family(), (int) std::round(1.2 * basicFontSize())));
137 textLabel->setColor(Qt::red);
138 }
139 else
140 {
141 textLabel->setText("X");
142 textLabel->setFont(QFont(font().family(), (int) std::round(2 * basicFontSize())));
143 textLabel->setColor(Qt::black);
144 }
145 textLabel->setPen(Qt::NoPen);
146
147 if (m_useErrorBars)
148 err.push_front(m_sigma[i]);
149 }
150 // Setup the error bar data if we're using it
151 errorBars->setVisible(m_useErrorBars);
152 if (m_useErrorBars)
153 errorBars->setData(err);
154}
155
156void FocusHFRVPlot::init(QString yAxisLabel, double starUnits, bool minimum, bool useWeights, bool showPosition)
157{
158 yAxis->setLabel(yAxisLabel);
159 m_starUnits = starUnits;
160 m_Minimum = minimum;
161 m_showPositions = showPosition;
162 m_position.clear();
163 m_value.clear();
164 m_displayValue.clear();
165 m_sigma.clear();
166 m_goodPosition.clear();
167 polynomialGraph->data()->clear();
168 focusPoint->data().clear();
169 m_useErrorBars = useWeights;
170 errorBars->data().clear();
171 // the next step seems necessary (QCP bug?)
172 v_graph->setData(QVector<double> {}, QVector<double> {});
173 focusPoint->setData(QVector<double> {}, QVector<double> {});
174 m_polynomialGraphIsVisible = false;
175 m_isVShape = false;
176 minValue = -1;
177 maxValue = -1;
178 FocusHFRVPlot::clearItems();
179 replot();
180}
181
182void FocusHFRVPlot::drawHFRPlot(double currentValue, int pulseDuration)
183{
184 // DrawHFRPlot is the base on which other things are built upon.
185 // Clear any previous annotations.
186 FocusHFRVPlot::clearItems();
187
188 v_graph->setData(m_position, m_displayValue);
189 drawHFRIndices();
190
191 double currentDisplayValue = getDisplayValue(currentValue);
192
193 if (minValue > currentDisplayValue || minValue < 0.0)
194 minValue = currentDisplayValue;
195 if (maxValue < currentDisplayValue)
196 maxValue = currentDisplayValue;
197
198 double minVal = currentDisplayValue / 2.5;
199 if (m_displayValue.size() > 0)
200 minVal = std::min(minValue, *std::min_element(m_displayValue.begin(), m_displayValue.end()));
201
202 // True for the position-based algorithms and those that simulate position.
203 if (m_showPositions)
204 {
205 const double minPosition = m_position.empty() ?
206 0 : *std::min_element(m_position.constBegin(), m_position.constEnd());
207 const double maxPosition = m_position.empty() ?
208 1e6 : *std::max_element(m_position.constBegin(), m_position.constEnd());
209 xAxis->setRange(minPosition - pulseDuration, maxPosition + pulseDuration);
210 }
211 else
212 {
213 //xAxis->setLabel(i18n("Iteration"));
214 xAxis->setRange(1, m_displayValue.count() + 1);
215 }
216
217 if (m_displayValue.size() == 1)
218 // 1 point gets placed in the middle vertically.
219 yAxis->setRange(0, 2 * getDisplayValue(maxValue));
220 else
221 {
222 double upper;
223 m_Minimum ? upper = 1.5 * maxValue : upper = 1.2 * maxValue;
224 yAxis->setRange(minVal - (0.25 * (upper - minVal)), upper);
225 }
226 replot();
227}
228
229void FocusHFRVPlot::addPosition(double pos, double newValue, double sigma, bool outlier, int pulseDuration, bool plot)
230{
231 m_position.append(pos);
232 m_value.append(newValue);
233 m_displayValue.append(getDisplayValue(newValue));
234 m_sigma.append(sigma);
235 outlier ? m_goodPosition.append(false) : m_goodPosition.append(true);
236
237 if (plot)
238 drawHFRPlot(newValue, pulseDuration);
239}
240
241void FocusHFRVPlot::setTitle(const QString &title, bool plot)
242{
243 plotTitle = new QCPItemText(this);
244 plotTitle->setColor(QColor(255, 255, 255));
246 plotTitle->position->setType(QCPItemPosition::ptAxisRectRatio);
247 plotTitle->position->setCoords(0.5, 0);
248 plotTitle->setText("");
249 plotTitle->setFont(QFont(font().family(), 11));
250 plotTitle->setVisible(true);
251
252 plotTitle->setText(title);
253 if (plot) replot();
254}
255
256void FocusHFRVPlot::finalUpdates(const QString &title, bool plot)
257{
258 // Update a previously set title without having to redraw everything
259 if (plotTitle != nullptr)
260 {
261 plotTitle->setText(title);
262 if (plot) replot();
263 }
264}
265void FocusHFRVPlot::setSolutionVShape(bool isVShape)
266{
267 m_isVShape = isVShape;
268
269 QPen pen;
270 pen.setWidth(1);
271
272 if (isVShape)
273 {
274 pen.setColor(QColor(180, 180, 180));
275 }
276 else
277 {
278 pen.setColor(QColor(254, 0, 0));
279 // clear focus point
280 focusPoint->data().clear();
281 }
282
283 polynomialGraph->setPen(pen);
284}
285
286void FocusHFRVPlot::clearItems()
287{
288 // Clear all the items on the HFR plot and reset pointers
290 plotTitle = nullptr;
291 CFZ = nullptr;
292}
293
294void FocusHFRVPlot::drawMinimum(double solutionPosition, double solutionValue, bool plot)
295{
296 focusPoint->data()->clear();
297
298 // do nothing for invalid positions
299 if (solutionPosition < 0)
300 return;
301
302 double displayValue = getDisplayValue(solutionValue);
303 minValue = std::min(minValue, displayValue);
304 maxValue = std::max(maxValue, displayValue);
305
306 focusPoint->addData(solutionPosition, displayValue);
307 QCPItemText *textLabel = new QCPItemText(this);
309 textLabel->setColor(Qt::red);
310 textLabel->setPadding(QMargins(0, 0, 0, 0));
311 textLabel->setBrush(Qt::white);
312 textLabel->setPen(Qt::NoPen);
313 textLabel->setFont(QFont(font().family(), (int) std::round(0.8 * basicFontSize())));
314 textLabel->position->setType(QCPItemPosition::ptPlotCoords);
315 textLabel->setText(QString::number(solutionPosition, 'f', 0));
316 if (m_Minimum)
317 textLabel->position->setCoords(solutionPosition, (maxValue + 2 * displayValue) / 3);
318 else
319 textLabel->position->setCoords(solutionPosition, (2 * displayValue + minValue) / 3);
320 if (plot) replot();
321}
322
323void FocusHFRVPlot::drawCFZ(double solutionPosition, double solutionValue, int cfzSteps, bool plot)
324{
325 // do nothing for invalid positions
326 if (solutionPosition < 0 || solutionValue < 0)
327 return;
328
329 if (!plot)
330 {
331 if (CFZ)
332 CFZ->setVisible(false);
333 }
334 else
335 {
336 if (!CFZ)
337 CFZ = new QCPItemBracket(this);
338
341
342 double y;
343 if (m_Minimum)
344 y = (7 * minValue - maxValue) / 6;
345 else
346 y = (maxValue + 2 * minValue) / 3;
347
348 CFZ->left->setCoords(solutionPosition + cfzSteps / 2.0, y);
349 CFZ->right->setCoords(solutionPosition - cfzSteps / 2.0, y);
350 CFZ->setLength(15);
351 CFZ->setAntialiased(false);
353 CFZ->setVisible(true);
354 }
355 replot();
356}
357
358void FocusHFRVPlot::drawPolynomial(Ekos::PolynomialFit *polyFit, bool isVShape, bool makeVisible, bool plot)
359{
360 if (polyFit == nullptr)
361 return;
362
363 // do nothing if graph is not visible and should not be made as such
364 if(makeVisible)
365 m_polynomialGraphIsVisible = true;
366 else if (m_polynomialGraphIsVisible == false)
367 return;
368
369 setSolutionVShape(isVShape);
370 if (polynomialGraph != nullptr)
371 {
372 polynomialGraph->data()->clear();
373 QCPRange range = xAxis->range();
374 double interval = range.size() / 20.0;
375
376 for(double x = range.lower ; x < range.upper ; x += interval)
377 {
378 double y = getDisplayValue(polyFit->f(x));
379 polynomialGraph->addData(x, y);
380 }
381 if (plot) replot();
382 }
383}
384
385void FocusHFRVPlot::drawCurve(Ekos::CurveFitting *curveFit, bool isVShape, bool makeVisible, bool plot)
386{
387 if (curveFit == nullptr)
388 return;
389
390 // do nothing if graph is not visible and should not be made as such
391 if(makeVisible)
392 m_polynomialGraphIsVisible = true;
393 else if (!makeVisible || !m_polynomialGraphIsVisible)
394 return;
395
396 setSolutionVShape(isVShape);
397 if (polynomialGraph != nullptr)
398 {
399 polynomialGraph->data()->clear();
400 QCPRange range = xAxis->range();
401 double interval = range.size() / 20.0;
402
403 for(double x = range.lower ; x < range.upper ; x += interval)
404 {
405 double y = getDisplayValue(curveFit->f(x));
406 polynomialGraph->addData(x, y);
407 }
408 if (plot) replot();
409 }
410}
411
412void FocusHFRVPlot::redraw(Ekos::PolynomialFit *polyFit, double solutionPosition, double solutionValue)
413{
414 if (m_value.empty() == false)
415 drawHFRPlot(m_value.last(), 0);
416
417 drawPolynomial(polyFit, m_isVShape, false);
418 drawMinimum(solutionPosition, solutionValue);
419}
420
421void FocusHFRVPlot::redrawCurve(Ekos::CurveFitting *curveFit, double solutionPosition, double solutionValue)
422{
423 if (m_value.empty() == false)
424 drawHFRPlot(solutionValue, 0);
425
426 drawCurve(curveFit, m_isVShape, false);
427 drawMinimum(solutionPosition, solutionValue);
428}
429
430void FocusHFRVPlot::setBasicFontSize(int basicFontSize)
431{
432 m_basicFontSize = basicFontSize;
433
434 // Axis Labels Settings
435 yAxis->setLabelFont(QFont(font().family(), basicFontSize));
436 xAxis->setTickLabelFont(QFont(font().family(), (int) std::round(0.9 * basicFontSize)));
437 yAxis->setTickLabelFont(QFont(font().family(), (int) std::round(0.9 * basicFontSize)));
438}
439
440// Internally calculations are done in units of pixels for HFR and FWHM
441// If user preference is arcsecs then convert values for display purposes.
442double FocusHFRVPlot::getDisplayValue(const double value)
443{
444 return value * m_starUnits;
445}
446
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 Fri Jul 26 2024 11:59:51 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.