v2.0.0
Loading...
Searching...
No Matches
rtsourcedataworker.cpp
Go to the documentation of this file.
1//=============================================================================================================
34
35//=============================================================================================================
36// INCLUDES
37//=============================================================================================================
38
39#include "rtsourcedataworker.h"
40#include "core/rendertypes.h"
41
43#include <QMutexLocker>
44#include <QDebug>
45#include <cmath>
46
47using namespace DISP3DRHILIB;
48
49//=============================================================================================================
50// DEFINE MEMBER METHODS
51//=============================================================================================================
52
54 : QObject(parent)
55{
56}
57
58//=============================================================================================================
59
60void RtSourceDataWorker::addData(const Eigen::VectorXd &data)
61{
62 QMutexLocker locker(&m_mutex);
63
64 // Cap queue size at sampling frequency (1 second of data)
65 if (m_lDataQ.size() < static_cast<int>(m_dSFreq)) {
66 m_lDataQ.append(data);
67 }
68
69 // Also store for looping
70 if (m_bIsLooping) {
71 m_lDataLoopQ.append(data);
72 // Cap loop queue too
73 if (m_lDataLoopQ.size() > static_cast<int>(m_dSFreq) * 10) {
74 m_lDataLoopQ.removeFirst();
75 }
76 }
77}
78
79//=============================================================================================================
80
82{
83 QMutexLocker locker(&m_mutex);
84 m_lDataQ.clear();
85 m_lDataLoopQ.clear();
86 m_vecAverage = Eigen::VectorXd();
87 m_iSampleCtr = 0;
88 m_iCurrentSample = 0;
89}
90
91//=============================================================================================================
92
93void RtSourceDataWorker::setInterpolationMatrixLeft(QSharedPointer<Eigen::SparseMatrix<float>> mat)
94{
95 QMutexLocker locker(&m_mutex);
96 m_interpMatLh = mat;
97}
98
99//=============================================================================================================
100
101void RtSourceDataWorker::setInterpolationMatrixRight(QSharedPointer<Eigen::SparseMatrix<float>> mat)
102{
103 QMutexLocker locker(&m_mutex);
104 m_interpMatRh = mat;
105}
106
107//=============================================================================================================
108
110{
111 QMutexLocker locker(&m_mutex);
112 m_iNumAverages = qMax(1, numAvr);
113}
114
115//=============================================================================================================
116
117void RtSourceDataWorker::setColormapType(const QString &name)
118{
119 QMutexLocker locker(&m_mutex);
120 m_sColormapType = name;
121}
122
123//=============================================================================================================
124
125void RtSourceDataWorker::setThresholds(double min, double mid, double max)
126{
127 QMutexLocker locker(&m_mutex);
128 m_dThreshMin = min;
129 m_dThreshMid = mid;
130 m_dThreshMax = max;
131}
132
133//=============================================================================================================
134
136{
137 QMutexLocker locker(&m_mutex);
138 m_bIsLooping = enabled;
139}
140
141//=============================================================================================================
142
144{
145 QMutexLocker locker(&m_mutex);
146 m_dSFreq = qMax(1.0, sFreq);
147}
148
149//=============================================================================================================
150
151void RtSourceDataWorker::setSurfaceColor(const QVector<uint32_t> &baseColorsLh,
152 const QVector<uint32_t> &baseColorsRh)
153{
154 QMutexLocker locker(&m_mutex);
155 m_baseColorsLh = baseColorsLh;
156 m_baseColorsRh = baseColorsRh;
157}
158
159//=============================================================================================================
160
161void RtSourceDataWorker::setStreamSmoothedData(bool bStreamSmoothedData)
162{
163 QMutexLocker locker(&m_mutex);
164 m_bStreamSmoothedData = bStreamSmoothedData;
165}
166
167//=============================================================================================================
168
170{
171 QMutexLocker locker(&m_mutex);
172
173 // Try to get data from queue
174 Eigen::VectorXd vecCurrentData;
175
176 if (!m_lDataQ.isEmpty()) {
177 vecCurrentData = m_lDataQ.takeFirst();
178 } else if (m_bIsLooping && !m_lDataLoopQ.isEmpty()) {
179 // Loop: replay from stored data
180 vecCurrentData = m_lDataLoopQ[m_iCurrentSample % m_lDataLoopQ.size()];
181 m_iCurrentSample++;
182 } else {
183 // No data available
184 return;
185 }
186
187 // Averaging
188 if (m_iNumAverages > 1) {
189 if (m_vecAverage.size() != vecCurrentData.size()) {
190 m_vecAverage = vecCurrentData;
191 m_iSampleCtr = 1;
192 } else {
193 m_vecAverage += vecCurrentData;
194 m_iSampleCtr++;
195 }
196
197 if (m_iSampleCtr < m_iNumAverages) {
198 return; // Accumulate more samples
199 }
200
201 vecCurrentData = m_vecAverage / static_cast<double>(m_iSampleCtr);
202 m_vecAverage = Eigen::VectorXd();
203 m_iSampleCtr = 0;
204 }
205
206 // Take absolute values (source power is always positive)
207 Eigen::VectorXf vecAbsData = vecCurrentData.cwiseAbs().cast<float>();
208
209 // Determine split point between LH and RH based on interpolation matrix column counts
210 int nSourcesLh = 0;
211 int nSourcesRh = 0;
212
213 if (m_interpMatLh) {
214 nSourcesLh = m_interpMatLh->cols();
215 }
216 if (m_interpMatRh) {
217 nSourcesRh = m_interpMatRh->cols();
218 }
219
220 int nTotalExpected = nSourcesLh + nSourcesRh;
221
222 // Handle case where data doesn't match expected source count
223 if (vecAbsData.size() != nTotalExpected && nTotalExpected > 0) {
224 // Try to use whatever data we have
225 if (vecAbsData.size() < nTotalExpected) {
226 qWarning() << "RtSourceDataWorker: Data size" << vecAbsData.size()
227 << "< expected" << nTotalExpected;
228 return;
229 }
230 }
231
232 // Split data into LH and RH
233 Eigen::VectorXf dataLh, dataRh;
234 if (nSourcesLh > 0 && vecAbsData.size() >= nSourcesLh) {
235 dataLh = vecAbsData.head(nSourcesLh);
236 }
237 if (nSourcesRh > 0 && vecAbsData.size() >= nSourcesLh + nSourcesRh) {
238 dataRh = vecAbsData.segment(nSourcesLh, nSourcesRh);
239 }
240
241 // Check streaming mode
242 if (!m_bStreamSmoothedData) {
243 // Raw mode: emit source values without interpolation
244 Eigen::VectorXd rawLh = dataLh.cast<double>();
245 Eigen::VectorXd rawRh = dataRh.cast<double>();
246
247 locker.unlock();
248 emit newRtRawData(rawLh, rawRh);
249 return;
250 }
251
252 // Compute per-vertex colors for each hemisphere
253 QVector<uint32_t> colorsLh = computeHemiColors(dataLh, m_interpMatLh, m_baseColorsLh);
254 QVector<uint32_t> colorsRh = computeHemiColors(dataRh, m_interpMatRh, m_baseColorsRh);
255
256 // Unlock before emitting (avoid deadlock if slot is direct connection)
257 locker.unlock();
258
259 emit newRtSmoothedData(colorsLh, colorsRh);
260}
261
262//=============================================================================================================
263
264QVector<uint32_t> RtSourceDataWorker::computeHemiColors(
265 const Eigen::VectorXf &sourceData,
266 const QSharedPointer<Eigen::SparseMatrix<float>> &interpMat,
267 const QVector<uint32_t> &baseColors) const
268{
269 if (sourceData.size() == 0 || !interpMat || interpMat->rows() == 0) {
270 return QVector<uint32_t>();
271 }
272
273 // Interpolate source data to all surface vertices
274 Eigen::VectorXf interpolated = (*interpMat) * sourceData;
275
276 int nVertices = interpolated.size();
277 QVector<uint32_t> colors(nVertices);
278
279 bool hasBaseColors = (baseColors.size() == nVertices);
280
281 double range = m_dThreshMax - m_dThreshMin;
282 if (range <= 0.0) range = 1.0;
283
284 for (int i = 0; i < nVertices; ++i) {
285 float value = interpolated(i);
286
287 // Normalize based on thresholds
288 double normalized = (static_cast<double>(value) - m_dThreshMin) / range;
289 normalized = qBound(0.0, normalized, 1.0);
290
291 // Calculate alpha based on threshold
292 uint8_t alpha = 255;
293 if (value < m_dThreshMin) {
294 // Below threshold: use base surface color or transparent
295 if (hasBaseColors) {
296 colors[i] = baseColors[i];
297 } else {
298 colors[i] = 0; // Fully transparent
299 }
300 continue;
301 } else if (value < m_dThreshMid) {
302 // Fade in from min to mid
303 double fadeRange = m_dThreshMid - m_dThreshMin;
304 if (fadeRange > 0.0) {
305 alpha = static_cast<uint8_t>(255.0 * (value - m_dThreshMin) / fadeRange);
306 }
307
308 // Blend activation color with base color
309 if (hasBaseColors && alpha < 255) {
310 uint32_t actColor = valueToColor(normalized, 255);
311 uint32_t baseColor = baseColors[i];
312
313 // Simple alpha blend: result = act * alpha/255 + base * (255-alpha)/255
314 uint32_t aR = actColor & 0xFF;
315 uint32_t aG = (actColor >> 8) & 0xFF;
316 uint32_t aB = (actColor >> 16) & 0xFF;
317 uint32_t bR = baseColor & 0xFF;
318 uint32_t bG = (baseColor >> 8) & 0xFF;
319 uint32_t bB = (baseColor >> 16) & 0xFF;
320
321 uint32_t rR = (aR * alpha + bR * (255 - alpha)) / 255;
322 uint32_t rG = (aG * alpha + bG * (255 - alpha)) / 255;
323 uint32_t rB = (aB * alpha + bB * (255 - alpha)) / 255;
324
325 colors[i] = packABGR(rR, rG, rB);
326 continue;
327 }
328 }
329
330 colors[i] = valueToColor(normalized, alpha);
331 }
332
333 return colors;
334}
335
336//=============================================================================================================
337
338uint32_t RtSourceDataWorker::valueToColor(double value, uint8_t alpha) const
339{
340 QRgb rgb = DISPLIB::ColorMap::valueToColor(value, m_sColormapType);
341
342 uint32_t r = qRed(rgb);
343 uint32_t g = qGreen(rgb);
344 uint32_t b = qBlue(rgb);
345
346 // Pack as ABGR (same format as BrainSurface uses)
347 return packABGR(r, g, b, static_cast<uint32_t>(alpha));
348}
ColorMap class declaration.
RtSourceDataWorker class declaration.
Lightweight render-related enums shared across the disp3D_rhi library.
uint32_t packABGR(uint32_t r, uint32_t g, uint32_t b, uint32_t a=0xFF)
Definition rendertypes.h:60
3-D brain visualisation using the Qt RHI rendering backend.
static QRgb valueToColor(double v, const QString &sMap)
Definition colormap.h:688
void newRtSmoothedData(const QVector< uint32_t > &colorsLh, const QVector< uint32_t > &colorsRh)
void setInterpolationMatrixRight(QSharedPointer< Eigen::SparseMatrix< float > > mat)
void setColormapType(const QString &name)
void setInterpolationMatrixLeft(QSharedPointer< Eigen::SparseMatrix< float > > mat)
RtSourceDataWorker(QObject *parent=nullptr)
void newRtRawData(const Eigen::VectorXd &dataLh, const Eigen::VectorXd &dataRh)
void setStreamSmoothedData(bool bStreamSmoothedData)
void setThresholds(double min, double mid, double max)
void addData(const Eigen::VectorXd &data)
void setSurfaceColor(const QVector< uint32_t > &baseColorsLh, const QVector< uint32_t > &baseColorsRh)