v2.0.0
Loading...
Searching...
No Matches
sourceestimatemanager.cpp
Go to the documentation of this file.
1//=============================================================================================================
34
35//=============================================================================================================
36// INCLUDES
37//=============================================================================================================
38
44#include "../core/viewstate.h"
45
46#include <QThread>
47#include <QDebug>
48#include <QSet>
49#include <cmath>
50
51//=============================================================================================================
52// DEFINE MEMBER METHODS
53//=============================================================================================================
54
56 : QObject(parent)
57{
58}
59
60//=============================================================================================================
61
63{
64 if (m_loadingThread) {
65 m_loadingThread->quit();
66 m_loadingThread->wait();
67 }
68}
69
70//=============================================================================================================
71
72bool SourceEstimateManager::load(const QString &lhPath, const QString &rhPath,
73 const QMap<QString, std::shared_ptr<BrainSurface>> &surfaces,
74 const QString &activeSurfaceType)
75{
76 if (m_isLoading) {
77 qWarning() << "SourceEstimateManager: STC loading already in progress";
78 return false;
79 }
80
81 // Find surfaces for the active surface type
82 BrainSurface *lhSurface = nullptr;
83 BrainSurface *rhSurface = nullptr;
84
85 const QString lhKey = "lh_" + activeSurfaceType;
86 const QString rhKey = "rh_" + activeSurfaceType;
87
88 if (surfaces.contains(lhKey))
89 lhSurface = surfaces[lhKey].get();
90 if (surfaces.contains(rhKey))
91 rhSurface = surfaces[rhKey].get();
92
93 // Fallback: search for any lh_*/rh_* brain surface
94 if (!lhSurface || !rhSurface) {
95 for (auto it = surfaces.begin(); it != surfaces.end(); ++it) {
96 if (it.value() && it.value()->tissueType() == BrainSurface::TissueBrain) {
97 if (!lhSurface && it.key().startsWith("lh_")) {
98 lhSurface = it.value().get();
99 qDebug() << "SourceEstimateManager: Using fallback LH surface:" << it.key();
100 } else if (!rhSurface && it.key().startsWith("rh_")) {
101 rhSurface = it.value().get();
102 qDebug() << "SourceEstimateManager: Using fallback RH surface:" << it.key();
103 }
104 }
105 }
106 }
107
108 if (!lhSurface && !rhSurface) {
109 qWarning() << "SourceEstimateManager: No surfaces available for STC loading."
110 << "Active surface type:" << activeSurfaceType
111 << "Available keys:" << surfaces.keys();
112 return false;
113 }
114
115 // Clean up any previous loading thread
116 if (m_loadingThread) {
117 m_loadingThread->quit();
118 m_loadingThread->wait();
119 delete m_loadingThread;
120 m_loadingThread = nullptr;
121 }
122
123 // Create overlay for results
124 m_overlay = std::make_unique<SourceEstimateOverlay>();
125
126 // Create worker and thread
127 m_loadingThread = new QThread(this);
128 m_stcWorker = new StcLoadingWorker(lhPath, rhPath, lhSurface, rhSurface);
129 m_stcWorker->moveToThread(m_loadingThread);
130
131 connect(m_loadingThread, &QThread::started, m_stcWorker, &StcLoadingWorker::process);
133 connect(m_stcWorker, &StcLoadingWorker::finished, this, &SourceEstimateManager::onStcLoadingFinished);
134 connect(m_stcWorker, &StcLoadingWorker::finished, m_loadingThread, &QThread::quit);
135 connect(m_loadingThread, &QThread::finished, m_stcWorker, &QObject::deleteLater);
136
137 m_isLoading = true;
138 m_loadingThread->start();
139
140 return true;
141}
142
143//=============================================================================================================
144
146{
147 return m_overlay && m_overlay->isLoaded();
148}
149
150//=============================================================================================================
151
152void SourceEstimateManager::onStcLoadingFinished(bool success)
153{
154 m_isLoading = false;
155
156 if (!success || !m_stcWorker) {
157 qWarning() << "SourceEstimateManager: Async STC loading failed";
158 m_overlay.reset();
159 return;
160 }
161
162 // Transfer data from worker to overlay
163 if (m_stcWorker->hasLh()) {
164 m_overlay->setStcData(m_stcWorker->stcLh(), 0);
165 if (m_stcWorker->interpolationMatLh())
166 m_overlay->setInterpolationMatrix(m_stcWorker->interpolationMatLh(), 0);
167 }
168
169 if (m_stcWorker->hasRh()) {
170 m_overlay->setStcData(m_stcWorker->stcRh(), 1);
171 if (m_stcWorker->interpolationMatRh())
172 m_overlay->setInterpolationMatrix(m_stcWorker->interpolationMatRh(), 1);
173 }
174
175 m_overlay->updateThresholdsFromData();
176 emit thresholdsUpdated(m_overlay->thresholdMin(),
177 m_overlay->thresholdMid(),
178 m_overlay->thresholdMax());
179
180 if (m_overlay->isLoaded()) {
181 emit loaded(m_overlay->numTimePoints());
182 } else {
183 m_overlay.reset();
184 }
185}
186
187//=============================================================================================================
188
190 const QMap<QString, std::shared_ptr<BrainSurface>> &surfaces,
191 const SubView &singleView,
192 const QVector<SubView> &subViews)
193{
194 if (!m_overlay || !m_overlay->isLoaded()) return;
195
196 m_currentTimePoint = qBound(0, index, m_overlay->numTimePoints() - 1);
197
198 // Collect all distinct surface types used across single + multi views
199 QSet<QString> activeTypes;
200 activeTypes.insert(singleView.surfaceType);
201 for (int i = 0; i < subViews.size(); ++i)
202 activeTypes.insert(subViews[i].surfaceType);
203
204 // Apply source estimate to surfaces matching ANY active type
205 for (auto it = surfaces.begin(); it != surfaces.end(); ++it) {
206 for (const QString &type : activeTypes) {
207 if (it.key().endsWith(type)) {
208 m_overlay->applyToSurface(it.value().get(), m_currentTimePoint);
209 break;
210 }
211 }
212 }
213
214 emit timePointChanged(m_currentTimePoint, m_overlay->timeAtIndex(m_currentTimePoint));
215}
216
217//=============================================================================================================
218
220{
221 return (m_overlay && m_overlay->isLoaded()) ? m_overlay->tstep() : 0.0f;
222}
223
224//=============================================================================================================
225
227{
228 return (m_overlay && m_overlay->isLoaded()) ? m_overlay->tmin() : 0.0f;
229}
230
231//=============================================================================================================
232
234{
235 return (m_overlay && m_overlay->isLoaded()) ? m_overlay->numTimePoints() : 0;
236}
237
238//=============================================================================================================
239
241{
242 if (!m_overlay || !m_overlay->isLoaded()) return -1;
243
244 const float t0 = m_overlay->tmin();
245 const float dt = m_overlay->tstep();
246 const int numPts = m_overlay->numTimePoints();
247 if (numPts <= 0 || dt <= 0.0f) return -1;
248
249 const int idx = qRound((timeSec - t0) / dt);
250 return qBound(0, idx, numPts - 1);
251}
252
253//=============================================================================================================
254
255void SourceEstimateManager::setColormap(const QString &name)
256{
257 if (m_overlay)
258 m_overlay->setColormap(name);
259}
260
261//=============================================================================================================
262
263void SourceEstimateManager::setThresholds(float min, float mid, float max)
264{
265 if (m_overlay)
266 m_overlay->setThresholds(min, mid, max);
267
268 if (m_rtController)
269 m_rtController->setThresholds(min, mid, max);
270}
271
272//=============================================================================================================
273
274void SourceEstimateManager::startStreaming(const QMap<QString, std::shared_ptr<BrainSurface>> &surfaces,
275 const SubView &singleView,
276 const QVector<SubView> &subViews)
277{
278 Q_UNUSED(surfaces)
279 Q_UNUSED(singleView)
280 Q_UNUSED(subViews)
281
282 if (m_isStreaming) {
283 qDebug() << "SourceEstimateManager: Real-time streaming already active";
284 return;
285 }
286
287 if (!m_overlay || !m_overlay->isLoaded()) {
288 qWarning() << "SourceEstimateManager: Cannot start streaming — no source estimate loaded";
289 return;
290 }
291
292 // Create controller on first use
293 if (!m_rtController) {
294 m_rtController = std::make_unique<RtSourceDataController>(this);
295 connect(m_rtController.get(), &RtSourceDataController::newSmoothedDataAvailable,
297 }
298
299 // Propagate interpolation matrices from the overlay
300 m_rtController->setInterpolationMatrixLeft(m_overlay->interpolationMatLh());
301 m_rtController->setInterpolationMatrixRight(m_overlay->interpolationMatRh());
302
303 // Propagate current visualization parameters
304 m_rtController->setColormapType(m_overlay->colormap());
305 m_rtController->setThresholds(m_overlay->thresholdMin(),
306 m_overlay->thresholdMid(),
307 m_overlay->thresholdMax());
308 m_rtController->setSFreq(1.0 / m_overlay->tstep());
309
310 // Feed all STC time-points into the queue
311 const int nTimePoints = m_overlay->numTimePoints();
312 qDebug() << "SourceEstimateManager: Feeding" << nTimePoints << "time points into real-time queue";
313 m_rtController->clearData();
314
315 for (int t = 0; t < nTimePoints; ++t) {
316 Eigen::VectorXd col = m_overlay->sourceDataColumn(t);
317 if (col.size() > 0)
318 m_rtController->addData(col);
319 }
320
321 m_rtController->setStreamingState(true);
322 m_isStreaming = true;
323
324 qDebug() << "SourceEstimateManager: Real-time streaming started";
325}
326
327//=============================================================================================================
328
330{
331 if (!m_isStreaming) return;
332
333 if (m_rtController)
334 m_rtController->setStreamingState(false);
335
336 m_isStreaming = false;
337 qDebug() << "SourceEstimateManager: Real-time streaming stopped";
338}
339
340//=============================================================================================================
341
342void SourceEstimateManager::pushData(const Eigen::VectorXd &data)
343{
344 if (m_rtController)
345 m_rtController->addData(data);
346}
347
348//=============================================================================================================
349
351{
352 if (m_rtController)
353 m_rtController->setTimeInterval(msec);
354}
355
356//=============================================================================================================
357
359{
360 if (m_rtController)
361 m_rtController->setLoopState(enabled);
362}
363
364//=============================================================================================================
365
367{
368 return m_overlay.get();
369}
SourceEstimateManager class declaration — owns STC overlay, loading, and real-time streaming.
RtSourceDataController class declaration.
StcLoadingWorker class declaration.
BrainSurface class declaration.
SourceEstimateOverlay class declaration.
ViewState declarations — per-view data structures and conversion helpers.
Viewport subdivision holding its own camera, projection, and scissor rectangle.
Definition viewstate.h:148
QString surfaceType
Definition viewstate.h:150
Renderable cortical surface mesh with per-vertex color, curvature data, and GPU buffer management.
Color-mapped source estimate overlay that interpolates activation values onto a cortical surface mesh...
void pushData(const Eigen::VectorXd &data)
bool load(const QString &lhPath, const QString &rhPath, const QMap< QString, std::shared_ptr< BrainSurface > > &surfaces, const QString &activeSurfaceType)
void loadingProgress(int percent, const QString &message)
void setThresholds(float min, float mid, float max)
void setColormap(const QString &name)
void startStreaming(const QMap< QString, std::shared_ptr< BrainSurface > > &surfaces, const SubView &singleView, const QVector< SubView > &subViews)
void timePointChanged(int index, float time)
void loaded(int numTimePoints)
void thresholdsUpdated(float min, float mid, float max)
const SourceEstimateOverlay * overlay() const
void setTimePoint(int index, const QMap< QString, std::shared_ptr< BrainSurface > > &surfaces, const SubView &singleView, const QVector< SubView > &subViews)
int closestIndex(float timeSec) const
void realtimeColorsAvailable(const QVector< uint32_t > &colorsLh, const QVector< uint32_t > &colorsRh)
SourceEstimateManager(QObject *parent=nullptr)
void newSmoothedDataAvailable(const QVector< uint32_t > &colorsLh, const QVector< uint32_t > &colorsRh)
Background worker that loads source estimate (STC) files and emits loaded data for visualization.
void progress(int percent, const QString &message)
void finished(bool success)