50 m_stcWorker->requestCancel();
51 if (m_loadingThread) {
52 m_loadingThread->quit();
53 m_loadingThread->wait();
60 const QMap<QString, std::shared_ptr<BrainSurface>> &surfaces,
61 const QString &activeSurfaceType)
64 qWarning() <<
"SourceEstimateManager: STC loading already in progress";
72 const QString lhKey =
"lh_" + activeSurfaceType;
73 const QString rhKey =
"rh_" + activeSurfaceType;
75 if (surfaces.contains(lhKey))
76 lhSurface = surfaces[lhKey].get();
77 if (surfaces.contains(rhKey))
78 rhSurface = surfaces[rhKey].get();
81 if (!lhSurface || !rhSurface) {
82 for (
auto it = surfaces.begin(); it != surfaces.end(); ++it) {
84 if (!lhSurface && it.key().startsWith(
"lh_")) {
85 lhSurface = it.value().get();
86 qDebug() <<
"SourceEstimateManager: Using fallback LH surface:" << it.key();
87 }
else if (!rhSurface && it.key().startsWith(
"rh_")) {
88 rhSurface = it.value().get();
89 qDebug() <<
"SourceEstimateManager: Using fallback RH surface:" << it.key();
95 if (!lhSurface && !rhSurface) {
96 qWarning() <<
"SourceEstimateManager: No surfaces available for STC loading."
97 <<
"Active surface type:" << activeSurfaceType
98 <<
"Available keys:" << surfaces.keys();
103 if (m_loadingThread) {
104 m_loadingThread->quit();
105 m_loadingThread->wait();
106 delete m_loadingThread;
107 m_loadingThread =
nullptr;
111 m_overlay = std::make_unique<SourceEstimateOverlay>();
114 m_loadingThread =
new QThread(
this);
116 m_stcWorker->moveToThread(m_loadingThread);
122 connect(m_loadingThread, &QThread::finished, m_stcWorker, &QObject::deleteLater);
125 m_loadingThread->start();
134 return m_overlay && m_overlay->isLoaded();
139void SourceEstimateManager::onStcLoadingFinished(
bool success)
143 if (!success || !m_stcWorker) {
144 qWarning() <<
"SourceEstimateManager: Async STC loading failed";
150 if (m_stcWorker->hasLh()) {
151 m_overlay->setStcData(m_stcWorker->stcLh(), 0);
152 if (m_stcWorker->interpolationMatLh())
153 m_overlay->setInterpolationMatrix(m_stcWorker->interpolationMatLh(), 0);
156 if (m_stcWorker->hasRh()) {
157 m_overlay->setStcData(m_stcWorker->stcRh(), 1);
158 if (m_stcWorker->interpolationMatRh())
159 m_overlay->setInterpolationMatrix(m_stcWorker->interpolationMatRh(), 1);
162 m_overlay->updateThresholdsFromData();
164 m_overlay->thresholdMid(),
165 m_overlay->thresholdMax());
167 if (m_overlay->isLoaded()) {
168 emit
loaded(m_overlay->numTimePoints());
177 const QMap<QString, std::shared_ptr<BrainSurface>> &surfaces,
179 const QVector<SubView> &subViews)
181 if (!m_overlay || !m_overlay->isLoaded())
return;
183 m_currentTimePoint = qBound(0, index, m_overlay->numTimePoints() - 1);
186 QSet<QString> activeTypes;
188 for (
int i = 0; i < subViews.size(); ++i)
189 activeTypes.insert(subViews[i].surfaceType);
192 for (
auto it = surfaces.begin(); it != surfaces.end(); ++it) {
193 for (
const QString &type : activeTypes) {
194 if (it.key().endsWith(type)) {
195 m_overlay->applyToSurface(it.value().get(), m_currentTimePoint);
201 emit
timePointChanged(m_currentTimePoint, m_overlay->timeAtIndex(m_currentTimePoint));
208 return (m_overlay && m_overlay->isLoaded()) ? m_overlay->tstep() : 0.0f;
215 return (m_overlay && m_overlay->isLoaded()) ? m_overlay->tmin() : 0.0f;
222 return (m_overlay && m_overlay->isLoaded()) ? m_overlay->numTimePoints() : 0;
229 if (!m_overlay || !m_overlay->isLoaded())
return -1;
231 const float t0 = m_overlay->tmin();
232 const float dt = m_overlay->tstep();
233 const int numPts = m_overlay->numTimePoints();
234 if (numPts <= 0 || dt <= 0.0f)
return -1;
236 const int idx = qRound((timeSec - t0) / dt);
237 return qBound(0, idx, numPts - 1);
245 m_overlay->setColormap(name);
253 m_overlay->setThresholds(min, mid, max);
256 m_rtController->setThresholds(min, mid, max);
263 const QVector<SubView> &subViews)
270 qDebug() <<
"SourceEstimateManager: Real-time streaming already active";
274 if (!m_overlay || !m_overlay->isLoaded()) {
275 qWarning() <<
"SourceEstimateManager: Cannot start streaming — no source estimate loaded";
280 if (!m_rtController) {
281 m_rtController = std::make_unique<RtSourceDataController>(
this);
287 m_rtController->setInterpolationMatrixLeft(m_overlay->interpolationMatLh());
288 m_rtController->setInterpolationMatrixRight(m_overlay->interpolationMatRh());
291 m_rtController->setColormapType(m_overlay->colormap());
292 m_rtController->setThresholds(m_overlay->thresholdMin(),
293 m_overlay->thresholdMid(),
294 m_overlay->thresholdMax());
295 m_rtController->setSFreq(1.0 / m_overlay->tstep());
298 const int nTimePoints = m_overlay->numTimePoints();
299 qDebug() <<
"SourceEstimateManager: Feeding" << nTimePoints <<
"time points into real-time queue";
300 m_rtController->clearData();
302 for (
int t = 0; t < nTimePoints; ++t) {
303 Eigen::VectorXd col = m_overlay->sourceDataColumn(t);
305 m_rtController->addData(col);
308 m_rtController->setStreamingState(
true);
309 m_isStreaming =
true;
311 qDebug() <<
"SourceEstimateManager: Real-time streaming started";
318 if (!m_isStreaming)
return;
321 m_rtController->setStreamingState(
false);
323 m_isStreaming =
false;
324 qDebug() <<
"SourceEstimateManager: Real-time streaming stopped";
332 m_rtController->addData(data);
340 m_rtController->setTimeInterval(msec);
348 m_rtController->setLoopState(enabled);
355 return m_overlay.get();
Renderable cortical / BEM mesh with interleaved vertex attributes and Qt-RHI buffer management.
Colour-mapped source-time-course overlay that interpolates STC activation onto a cortical mesh and up...
Real-time source-estimate streaming controller that owns the data worker and the per-hemisphere inter...
Background worker that loads source-time-course (.stc) files and prepares per-hemisphere interpolatio...
Per-viewport state (camera preset, zoom, pan, visibility filter) and serialisation helpers.
Owns the source-time-course overlay together with its loader, real-time controller and target cortica...
Viewport subdivision holding its own camera, projection, and scissor rectangle.
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)
~SourceEstimateManager() override
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 setLooping(bool enabled)
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 setInterval(int msec)
int numTimePoints() 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)