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