64 if (m_loadingThread) {
65 m_loadingThread->quit();
66 m_loadingThread->wait();
73 const QMap<QString, std::shared_ptr<BrainSurface>> &surfaces,
74 const QString &activeSurfaceType)
77 qWarning() <<
"SourceEstimateManager: STC loading already in progress";
85 const QString lhKey =
"lh_" + activeSurfaceType;
86 const QString rhKey =
"rh_" + activeSurfaceType;
88 if (surfaces.contains(lhKey))
89 lhSurface = surfaces[lhKey].get();
90 if (surfaces.contains(rhKey))
91 rhSurface = surfaces[rhKey].get();
94 if (!lhSurface || !rhSurface) {
95 for (
auto it = surfaces.begin(); it != surfaces.end(); ++it) {
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();
108 if (!lhSurface && !rhSurface) {
109 qWarning() <<
"SourceEstimateManager: No surfaces available for STC loading."
110 <<
"Active surface type:" << activeSurfaceType
111 <<
"Available keys:" << surfaces.keys();
116 if (m_loadingThread) {
117 m_loadingThread->quit();
118 m_loadingThread->wait();
119 delete m_loadingThread;
120 m_loadingThread =
nullptr;
124 m_overlay = std::make_unique<SourceEstimateOverlay>();
127 m_loadingThread =
new QThread(
this);
129 m_stcWorker->moveToThread(m_loadingThread);
135 connect(m_loadingThread, &QThread::finished, m_stcWorker, &QObject::deleteLater);
138 m_loadingThread->start();
147 return m_overlay && m_overlay->isLoaded();
152void SourceEstimateManager::onStcLoadingFinished(
bool success)
156 if (!success || !m_stcWorker) {
157 qWarning() <<
"SourceEstimateManager: Async STC loading failed";
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);
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);
175 m_overlay->updateThresholdsFromData();
177 m_overlay->thresholdMid(),
178 m_overlay->thresholdMax());
180 if (m_overlay->isLoaded()) {
181 emit
loaded(m_overlay->numTimePoints());
190 const QMap<QString, std::shared_ptr<BrainSurface>> &surfaces,
192 const QVector<SubView> &subViews)
194 if (!m_overlay || !m_overlay->isLoaded())
return;
196 m_currentTimePoint = qBound(0, index, m_overlay->numTimePoints() - 1);
199 QSet<QString> activeTypes;
201 for (
int i = 0; i < subViews.size(); ++i)
202 activeTypes.insert(subViews[i].surfaceType);
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);
214 emit
timePointChanged(m_currentTimePoint, m_overlay->timeAtIndex(m_currentTimePoint));
221 return (m_overlay && m_overlay->isLoaded()) ? m_overlay->tstep() : 0.0f;
228 return (m_overlay && m_overlay->isLoaded()) ? m_overlay->tmin() : 0.0f;
235 return (m_overlay && m_overlay->isLoaded()) ? m_overlay->numTimePoints() : 0;
242 if (!m_overlay || !m_overlay->isLoaded())
return -1;
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;
249 const int idx = qRound((timeSec - t0) / dt);
250 return qBound(0, idx, numPts - 1);
258 m_overlay->setColormap(name);
266 m_overlay->setThresholds(min, mid, max);
269 m_rtController->setThresholds(min, mid, max);
276 const QVector<SubView> &subViews)
283 qDebug() <<
"SourceEstimateManager: Real-time streaming already active";
287 if (!m_overlay || !m_overlay->isLoaded()) {
288 qWarning() <<
"SourceEstimateManager: Cannot start streaming — no source estimate loaded";
293 if (!m_rtController) {
294 m_rtController = std::make_unique<RtSourceDataController>(
this);
300 m_rtController->setInterpolationMatrixLeft(m_overlay->interpolationMatLh());
301 m_rtController->setInterpolationMatrixRight(m_overlay->interpolationMatRh());
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());
311 const int nTimePoints = m_overlay->numTimePoints();
312 qDebug() <<
"SourceEstimateManager: Feeding" << nTimePoints <<
"time points into real-time queue";
313 m_rtController->clearData();
315 for (
int t = 0; t < nTimePoints; ++t) {
316 Eigen::VectorXd col = m_overlay->sourceDataColumn(t);
318 m_rtController->addData(col);
321 m_rtController->setStreamingState(
true);
322 m_isStreaming =
true;
324 qDebug() <<
"SourceEstimateManager: Real-time streaming started";
331 if (!m_isStreaming)
return;
334 m_rtController->setStreamingState(
false);
336 m_isStreaming =
false;
337 qDebug() <<
"SourceEstimateManager: Real-time streaming stopped";
345 m_rtController->addData(data);
353 m_rtController->setTimeInterval(msec);
361 m_rtController->setLoopState(enabled);
368 return m_overlay.get();
SourceEstimateManager class declaration — owns STC overlay, loading, and real-time streaming.
ViewState declarations — per-view data structures and conversion helpers.
StcLoadingWorker class declaration.
RtSourceDataController class declaration.
BrainSurface class declaration.
SourceEstimateOverlay class declaration.
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)