v2.0.0
Loading...
Searching...
No Matches
spline.cpp
Go to the documentation of this file.
1//=============================================================================================================
34
35//=============================================================================================================
36// INCLUDES
37//=============================================================================================================
38
39#include "spline.h"
40
41#include "helpers/colormap.h"
42
43//=============================================================================================================
44// QT INCLUDES
45//=============================================================================================================
46
47#include <QPainter>
48#include <QPainterPath>
49#include <QPaintEvent>
50#include <QMouseEvent>
51#include <QLinearGradient>
52#include <QDebug>
53
54//=============================================================================================================
55// EIGEN INCLUDES
56//=============================================================================================================
57
58//=============================================================================================================
59// USED NAMESPACES
60//=============================================================================================================
61
62using namespace DISPLIB;
63
64//=============================================================================================================
65// DEFINE MEMBER METHODS
66//=============================================================================================================
67
68Spline::Spline(QWidget* parent, const QString& title)
69: QWidget(parent)
70, m_dMinAxisX(0)
71, m_dMaxAxisX(0)
75, m_bHasData(false)
76, m_bHasThresholds(false)
78{
79 Q_UNUSED(title)
80 setMinimumSize(200, 150);
81 setBackgroundRole(QPalette::Base);
82 setAutoFillBackground(true);
83}
84
85//=============================================================================================================
86
87double Spline::dataToPixelX(double dataX) const
88{
89 const int plotW = width() - leftMargin() - rightMargin();
91 return leftMargin();
92 return leftMargin() + (dataX - m_dMinAxisX) / (m_dMaxAxisX - m_dMinAxisX) * plotW;
93}
94
95//=============================================================================================================
96
97double Spline::dataToPixelY(double dataY) const
98{
99 const int plotH = height() - topMargin() - bottomMargin();
100 const double maxY = (m_iMaximumFrequency > 0) ? static_cast<double>(m_iMaximumFrequency) : 1.0;
101 return topMargin() + plotH - (dataY / maxY) * plotH;
102}
103
104//=============================================================================================================
105
106double Spline::pixelToDataX(double pixelX) const
107{
108 const int plotW = width() - leftMargin() - rightMargin();
109 if (plotW <= 0)
110 return m_dMinAxisX;
111 return m_dMinAxisX + (pixelX - leftMargin()) / plotW * (m_dMaxAxisX - m_dMinAxisX);
112}
113
114//=============================================================================================================
115
116void Spline::paintEvent(QPaintEvent * /*event*/)
117{
118 QPainter painter(this);
119 painter.setRenderHint(QPainter::Antialiasing);
120
121 const int w = width();
122 const int h = height();
123 const int mLeft = leftMargin();
124 const int mRight = rightMargin();
125 const int mTop = topMargin();
126 const int mBottom = bottomMargin();
127 const int plotW = w - mLeft - mRight;
128 const int plotH = h - mTop - mBottom;
129
130 Q_UNUSED(h)
131
132 if (plotW <= 0 || plotH <= 0)
133 return;
134
135 // Draw color map background if thresholds are set
136 if (m_bHasThresholds && !m_colorMap.isEmpty() && m_dMaxAxisX > m_dMinAxisX) {
137 double leftNorm = (m_dLeftThreshold - m_dMinAxisX) / (m_dMaxAxisX - m_dMinAxisX);
138 double middleNorm = (m_dMiddleThreshold - m_dMinAxisX) / (m_dMaxAxisX - m_dMinAxisX);
139 double rightNorm = (m_dRightThreshold - m_dMinAxisX) / (m_dMaxAxisX - m_dMinAxisX);
140
141 QLinearGradient gradient(mLeft, 0, mLeft + plotW, 0);
142 const int steps = 25;
143 double stepLeftMiddle = (middleNorm - leftNorm) / steps;
144 double stepMiddleRight = (rightNorm - middleNorm) / steps;
145
146 gradient.setColorAt(qBound(0.0, leftNorm, 1.0), ColorMap::valueToColor(0.0, m_colorMap));
147 for (int i = 1; i < steps; ++i) {
148 double pos1 = leftNorm + stepLeftMiddle * i;
149 double val1 = static_cast<double>(i) * (0.5 / static_cast<double>(steps));
150 gradient.setColorAt(qBound(0.0, pos1, 1.0), ColorMap::valueToColor(val1, m_colorMap));
151
152 double pos2 = middleNorm + stepMiddleRight * i;
153 double val2 = 0.5 + static_cast<double>(i) * (0.5 / static_cast<double>(steps));
154 gradient.setColorAt(qBound(0.0, pos2, 1.0), ColorMap::valueToColor(val2, m_colorMap));
155 }
156 gradient.setColorAt(qBound(0.0, rightNorm, 1.0), ColorMap::valueToColor(1.0, m_colorMap));
157
158 painter.fillRect(QRect(mLeft, mTop, plotW, plotH), gradient);
159 }
160
161 // Draw axes
162 painter.setPen(QPen(Qt::black, 1));
163 painter.drawLine(mLeft, mTop, mLeft, mTop + plotH);
164 painter.drawLine(mLeft, mTop + plotH, mLeft + plotW, mTop + plotH);
165
166 // Y-axis ticks
167 const int maxFreq = (m_iMaximumFrequency > 0) ? m_iMaximumFrequency : 1;
168 const int nYTicks = 5;
169 for (int i = 0; i <= nYTicks; ++i) {
170 int yVal = maxFreq * i / nYTicks;
171 int y = mTop + plotH - (plotH * i / nYTicks);
172 painter.drawLine(mLeft - 5, y, mLeft, y);
173 painter.drawText(QRect(0, y - 10, mLeft - 8, 20), Qt::AlignRight | Qt::AlignVCenter, QString::number(yVal));
174 }
175
176 // X-axis ticks
177 const int nXTicks = 5;
178 for (int i = 0; i <= nXTicks; ++i) {
179 double dataVal = m_dMinAxisX + (m_dMaxAxisX - m_dMinAxisX) * i / nXTicks;
180 int x = mLeft + plotW * i / nXTicks;
181 painter.drawLine(x, mTop + plotH, x, mTop + plotH + 5);
182 painter.drawText(QRect(x - 30, mTop + plotH + 6, 60, 20), Qt::AlignCenter, QString::number(dataVal, 'g', 3));
183 }
184
185 if (!m_bHasData || m_seriesData.isEmpty())
186 return;
187
188 // Draw spline curve using Catmull-Rom to cubic Bezier conversion
189 QPainterPath path;
190 const int n = m_seriesData.size();
191
192 if (n == 1) {
193 // Single point: draw a dot
194 double px = dataToPixelX(m_seriesData[0].x());
195 double py = dataToPixelY(m_seriesData[0].y());
196 painter.setBrush(QColor(70, 130, 180));
197 painter.drawEllipse(QPointF(px, py), 3, 3);
198 } else {
199 // Catmull-Rom spline
200 auto getPoint = [this](int idx) -> QPointF {
201 const int count = m_seriesData.size();
202 if (idx < 0) idx = 0;
203 if (idx >= count) idx = count - 1;
204 return QPointF(dataToPixelX(m_seriesData[idx].x()),
205 dataToPixelY(m_seriesData[idx].y()));
206 };
207
208 path.moveTo(getPoint(0));
209
210 for (int i = 0; i < n - 1; ++i) {
211 QPointF p0 = getPoint(i - 1);
212 QPointF p1 = getPoint(i);
213 QPointF p2 = getPoint(i + 1);
214 QPointF p3 = getPoint(i + 2);
215
216 // Catmull-Rom to cubic Bezier control points
217 QPointF cp1(p1.x() + (p2.x() - p0.x()) / 6.0,
218 p1.y() + (p2.y() - p0.y()) / 6.0);
219 QPointF cp2(p2.x() - (p3.x() - p1.x()) / 6.0,
220 p2.y() - (p3.y() - p1.y()) / 6.0);
221
222 path.cubicTo(cp1, cp2, p2);
223 }
224
225 painter.setPen(QPen(QColor(70, 130, 180), 2));
226 painter.setBrush(Qt::NoBrush);
227 painter.drawPath(path);
228 }
229
230 // Draw threshold lines
231 if (m_bHasThresholds) {
232 auto drawThresholdLine = [&](double dataX, const QColor& color) {
233 if (dataX >= m_dMinAxisX && dataX <= m_dMaxAxisX) {
234 double px = dataToPixelX(dataX);
235 painter.setPen(QPen(color, 2));
236 painter.drawLine(QPointF(px, mTop), QPointF(px, mTop + plotH));
237 }
238 };
239
240 drawThresholdLine(m_dLeftThreshold, Qt::red);
241 drawThresholdLine(m_dMiddleThreshold, Qt::green);
242 drawThresholdLine(m_dRightThreshold, Qt::blue);
243 }
244}
245
246//=============================================================================================================
247
248void Spline::mousePressEvent(QMouseEvent *event)
249{
250 if (!m_bHasData || m_seriesData.isEmpty()) {
251 qDebug() << "Data set not found.";
252 return;
253 }
254
255 double dataX = pixelToDataX(event->position().x());
256
257 // Check bounds
258 if (dataX < m_dMinAxisX || dataX > m_dMaxAxisX)
259 return;
260
261 if (event->buttons() == Qt::LeftButton) {
262 if (dataX < m_dMiddleThreshold && dataX < m_dRightThreshold) {
263 m_dLeftThreshold = dataX;
264 m_bHasThresholds = true;
265 }
266 } else if (event->buttons() == Qt::MiddleButton) {
267 if (dataX > m_dLeftThreshold && dataX < m_dRightThreshold) {
268 m_dMiddleThreshold = dataX;
269 m_bHasThresholds = true;
270 }
271 } else if (event->buttons() == Qt::RightButton) {
272 if (dataX > m_dLeftThreshold && dataX > m_dMiddleThreshold) {
273 m_dRightThreshold = dataX;
274 m_bHasThresholds = true;
275 }
276 }
277
278 double emitLeft = m_dLeftThreshold * pow(10, m_vecResultExponentValues[0]);
279 double emitMiddle = m_dMiddleThreshold * pow(10, m_vecResultExponentValues[0]);
280 double emitRight = m_dRightThreshold * pow(10, m_vecResultExponentValues[0]);
281 emit borderChanged(emitLeft, emitMiddle, emitRight);
282
284 QWidget::update();
285}
286
287//=============================================================================================================
288
289void Spline::setThreshold(const QVector3D& vecThresholdValues)
290{
291 if (!m_bHasData || m_seriesData.isEmpty()) {
292 qDebug() << "Data set not found.";
293 return;
294 }
295
296 QVector3D correctedVectorThreshold = correctionDisplayTrueValue(vecThresholdValues, "up");
297
298 // Check if all values are within range
299 if (correctedVectorThreshold.x() < m_dMinAxisX || correctedVectorThreshold.y() < m_dMinAxisX || correctedVectorThreshold.z() < m_dMinAxisX ||
300 correctedVectorThreshold.x() > m_dMaxAxisX || correctedVectorThreshold.y() > m_dMaxAxisX || correctedVectorThreshold.z() > m_dMaxAxisX) {
301 qDebug() << "One or more of the values given are out of the minimum and maximum range. Changed to default thresholds.";
305 } else {
306 // Sort the three values into left < middle < right
307 double vals[3] = { static_cast<double>(correctedVectorThreshold.x()),
308 static_cast<double>(correctedVectorThreshold.y()),
309 static_cast<double>(correctedVectorThreshold.z()) };
310 std::sort(vals, vals + 3);
311 m_dLeftThreshold = vals[0];
312 m_dMiddleThreshold = vals[1];
313 m_dRightThreshold = vals[2];
314 }
315
316 m_bHasThresholds = true;
318 QWidget::update();
319}
320
321//=============================================================================================================
322
323void Spline::setColorMap(const QString& colorMap)
324{
325 m_colorMap = colorMap;
326 QWidget::update();
327}
328
329//=============================================================================================================
330
331const QVector3D& Spline::getThreshold()
332{
333 QVector3D originalVector;
334 originalVector.setX(static_cast<float>(m_dLeftThreshold));
335 originalVector.setY(static_cast<float>(m_dMiddleThreshold));
336 originalVector.setZ(static_cast<float>(m_dRightThreshold));
337 m_vecReturnVector = correctionDisplayTrueValue(originalVector, "down");
338 return m_vecReturnVector;
339}
340
341//=============================================================================================================
342
343QVector3D Spline::correctionDisplayTrueValue(QVector3D vecOriginalValues, QString upOrDown)
344{
345 QVector3D returnCorrectedVector;
346
347 if(m_vecResultExponentValues.rows() > 0) {
348 int exponent = 0;
349 if (upOrDown == "up")
350 {
351 if (m_vecResultExponentValues[0] < 0)
352 {
353 exponent = std::abs(m_vecResultExponentValues[0]);
354 }
355 else if (m_vecResultExponentValues[0] > 0)
356 {
357 exponent = -(std::abs(m_vecResultExponentValues[0]));
358 }
359 }
360 else if (upOrDown == "down")
361 {
362 if (m_vecResultExponentValues[0] < 0)
363 {
364 exponent = -(std::abs(m_vecResultExponentValues[0]));
365 }
366 else if (m_vecResultExponentValues[0] > 0)
367 {
368 exponent = std::abs(m_vecResultExponentValues[0]);
369 }
370 }
371 else
372 {
373 qDebug() << "Spline::correctionDisplayTrueValue error.";
374 }
375
376 returnCorrectedVector.setX(vecOriginalValues.x() * static_cast<float>(pow(10, exponent)));
377 returnCorrectedVector.setY(vecOriginalValues.y() * static_cast<float>(pow(10, exponent)));
378 returnCorrectedVector.setZ(vecOriginalValues.z() * static_cast<float>(pow(10, exponent)));
379 }
380
381 return returnCorrectedVector;
382}
Spline class declaration.
ColorMap class declaration.
2-D display widgets and visualisation helpers (charts, topography, colour maps).
static QRgb valueToColor(double v, const QString &sMap)
Definition colormap.h:688
double m_dLeftThreshold
Definition spline.h:234
QString m_colorMap
Definition spline.h:240
double dataToPixelX(double dataX) const
Definition spline.cpp:87
QVector3D correctionDisplayTrueValue(QVector3D vecOriginalValues, QString upOrDown)
Definition spline.cpp:343
void mousePressEvent(QMouseEvent *event) override
Definition spline.cpp:248
void setThreshold(const QVector3D &vecThresholdValues)
Definition spline.cpp:289
QList< QPointF > m_seriesData
Definition spline.h:233
bool m_bHasData
Definition spline.h:237
int m_iMaximumFrequency
Definition spline.h:239
double m_dMiddleThreshold
Definition spline.h:235
double pixelToDataX(double pixelX) const
Definition spline.cpp:106
int topMargin() const
Definition spline.h:225
QVector3D m_vecReturnVector
Definition spline.h:241
Eigen::VectorXi m_vecResultExponentValues
Definition spline.h:178
Spline(QWidget *parent=nullptr, const QString &title="Spline Histogram")
Definition spline.cpp:68
int bottomMargin() const
Definition spline.h:231
double m_dRightThreshold
Definition spline.h:236
void borderChanged(double leftThreshold, double middleThreshold, double rightThreshold)
void setColorMap(const QString &colorMap)
Definition spline.cpp:323
double dataToPixelY(double dataY) const
Definition spline.cpp:97
double m_dMaxAxisX
Definition spline.h:180
const QVector3D & getThreshold()
Definition spline.cpp:331
double m_dMinAxisX
Definition spline.h:179
void paintEvent(QPaintEvent *event) override
Definition spline.cpp:116
int rightMargin() const
Definition spline.h:219
bool m_bHasThresholds
Definition spline.h:238
int leftMargin() const
Definition spline.h:213