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.
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.
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)