52 qWarning() <<
"SourceEstimateOverlay::loadStc - Failed to read STC file:" << path;
59 qDebug() <<
"SourceEstimateOverlay: Loaded LH with" << stc.
data.rows() <<
"vertices,"
60 << stc.
data.cols() <<
"time points";
64 qDebug() <<
"SourceEstimateOverlay: Loaded RH with" << stc.
data.rows() <<
"vertices,"
65 << stc.
data.cols() <<
"time points";
67 invalidateColorCache();
70 if (m_hasLh || m_hasRh) {
71 double minVal, maxVal;
75 m_threshMid = (minVal + maxVal) / 2.0;
76 qDebug() <<
"SourceEstimateOverlay: Auto thresholds set to" << m_threshMin << m_threshMid << m_threshMax;
86 return m_hasLh || m_hasRh;
95 int hemi = surface->
hemi();
97 QSharedPointer<Eigen::SparseMatrix<float>> interpMat;
99 if (hemi == 0 && m_hasLh) {
101 interpMat = m_interpolationMatLh;
102 }
else if (hemi == 1 && m_hasRh) {
104 interpMat = m_interpolationMatRh;
112 int tIdx = qBound(0, timeIndex,
static_cast<int>(stc->
data.cols()) - 1);
113 const uint32_t vertexCount = surface->
vertexCount();
121 const ColorCacheKey cacheKey(hemi,
static_cast<int>(vertexCount));
122 auto bucketIt = m_colorCache.find(cacheKey);
123 if (bucketIt != m_colorCache.end()) {
124 auto frameIt = bucketIt.value().constFind(tIdx);
125 if (frameIt != bucketIt.value().constEnd()) {
132 Eigen::VectorXf sourceData = stc->
data.col(tIdx).cwiseAbs().cast<
float>();
135 QVector<uint32_t> colors(vertexCount, 0xFF808080);
138 Eigen::VectorXf interpolatedData;
140 if (interpMat && interpMat->rows() ==
static_cast<int>(vertexCount) &&
141 interpMat->cols() == sourceData.size()) {
147 interpolatedData = Eigen::VectorXf::Zero(vertexCount);
148 const Eigen::VectorXi &srcVertices = stc->
vertices;
149 for (
int i = 0; i < srcVertices.size() && i < sourceData.size(); ++i) {
150 int vertIdx = srcVertices(i);
151 if (vertIdx >= 0 && vertIdx <
static_cast<int>(vertexCount)) {
152 interpolatedData(vertIdx) = sourceData(i);
158 for (
int i = 0; i < static_cast<int>(vertexCount); ++i) {
159 float value = interpolatedData(i);
162 double normalized = 0.0;
163 if (m_threshMax > m_threshMin) {
164 normalized = (value - m_threshMin) / (m_threshMax - m_threshMin);
165 normalized = qBound(0.0, normalized, 1.0);
170 if (value < m_threshMin) {
172 }
else if (value < m_threshMid) {
174 float range = m_threshMid - m_threshMin;
176 alpha =
static_cast<uint8_t
>(255.0f * (value - m_threshMin) / range);
180 colors[i] = valueToColor(normalized, alpha);
185 m_colorCache[cacheKey].insert(tIdx, colors);
194 if (m_colormap == name)
return;
196 invalidateColorCache();
203 if (m_threshMin == min && m_threshMid == mid && m_threshMax == max)
return;
207 invalidateColorCache();
214 if (m_hasLh)
return m_stcLh.data.cols();
215 if (m_hasRh)
return m_stcRh.data.cols();
223 if (m_hasLh && idx < m_stcLh.times.size()) {
224 return m_stcLh.times(idx);
226 if (m_hasRh && idx < m_stcRh.times.size()) {
227 return m_stcRh.times(idx);
236 if (m_hasLh)
return m_stcLh.tmin;
237 if (m_hasRh)
return m_stcRh.tmin;
245 if (m_hasLh)
return m_stcLh.tstep;
246 if (m_hasRh)
return m_stcRh.tstep;
254 minVal = std::numeric_limits<double>::max();
255 maxVal = std::numeric_limits<double>::lowest();
258 double lhMin = m_stcLh.data.minCoeff();
259 double lhMax = m_stcLh.data.maxCoeff();
260 minVal = qMin(minVal, std::abs(lhMin));
261 maxVal = qMax(maxVal, std::abs(lhMax));
265 double rhMin = m_stcRh.data.minCoeff();
266 double rhMax = m_stcRh.data.maxCoeff();
267 minVal = qMin(minVal, std::abs(rhMin));
268 maxVal = qMax(maxVal, std::abs(rhMax));
272 if (minVal > maxVal) {
280uint32_t SourceEstimateOverlay::valueToColor(
double value, uint8_t alpha)
const
284 uint32_t r = qRed(rgb);
285 uint32_t g = qGreen(rgb);
286 uint32_t b = qBlue(rgb);
289 return packABGR(r, g, b,
static_cast<uint32_t
>(alpha));
296 if (!surface)
return;
299 QSharedPointer<Eigen::SparseMatrix<float>> *pMatPtr =
nullptr;
301 if (hemi == 0 && m_hasLh) {
303 pMatPtr = &m_interpolationMatLh;
304 }
else if (hemi == 1 && m_hasRh) {
306 pMatPtr = &m_interpolationMatRh;
313 qDebug() <<
"SourceEstimateOverlay: Computing interpolation matrix for hemi" << hemi;
320 Eigen::VectorXi vecSourceVertices = stc->
vertices;
322 qDebug() <<
"SourceEstimateOverlay: FsSurface has" << matVertices.rows() <<
"vertices,"
323 << vecSourceVertices.size() <<
"sources";
325 if (vecSourceVertices.size() == 0) {
326 qWarning() <<
"SourceEstimateOverlay: No source vertices found";
333 qDebug() <<
"SourceEstimateOverlay: Computing distance table (SCDC)...";
341 if (!distTable || distTable->rows() == 0) {
342 qWarning() <<
"SourceEstimateOverlay: Failed to compute distance table";
347 qDebug() <<
"SourceEstimateOverlay: Creating interpolation matrix...";
355 if (*pMatPtr && (*pMatPtr)->rows() > 0) {
356 qDebug() <<
"SourceEstimateOverlay: Interpolation matrix created:"
357 << (*pMatPtr)->rows() <<
"x" << (*pMatPtr)->cols();
359 qWarning() <<
"SourceEstimateOverlay: Failed to compute interpolation matrix";
374 invalidateColorCache();
382 m_interpolationMatLh = mat;
384 m_interpolationMatRh = mat;
386 invalidateColorCache();
393 if (m_hasLh || m_hasRh) {
394 double minVal, maxVal;
396 m_threshMin = minVal;
397 m_threshMax = maxVal;
398 m_threshMid = (minVal + maxVal) / 2.0;
399 invalidateColorCache();
400 qDebug() <<
"SourceEstimateOverlay: Auto thresholds set to" << m_threshMin << m_threshMid << m_threshMax;
408 int nLh = m_hasLh ? m_stcLh.data.rows() : 0;
409 int nRh = m_hasRh ? m_stcRh.data.rows() : 0;
411 if (nLh == 0 && nRh == 0) {
412 return Eigen::VectorXd();
415 Eigen::VectorXd result(nLh + nRh);
418 int tIdx = qBound(0, timeIndex,
static_cast<int>(m_stcLh.data.cols()) - 1);
419 result.head(nLh) = m_stcLh.data.col(tIdx);
423 int tIdx = qBound(0, timeIndex,
static_cast<int>(m_stcRh.data.cols()) - 1);
424 result.segment(nLh, nRh) = m_stcRh.data.col(tIdx);
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...
Lightweight render-related enums (ShaderMode, VisualizationMode) shared across disp3D.
uint32_t packABGR(uint32_t r, uint32_t g, uint32_t b, uint32_t a=0xFF)
Distance-based sparse interpolation weights and per-frame signal smoothing on triangulated meshes.
Surface-constrained geodesic distance and sensor-to-mesh projection helpers.
Static scalar-to-colour lookup helpers (Jet, Hot, Bone, Viridis, Cool, RedBlue, MNE) used by every pl...
static QRgb valueToColor(double v, const QString &sMap)
static QSharedPointer< Eigen::MatrixXd > scdc(const Eigen::MatrixX3f &matVertices, const std::vector< Eigen::VectorXi > &vecNeighborVertices, Eigen::VectorXi &vecVertSubset, double dCancelDist=FLOAT_INFINITY)
scdc Calculates surface constrained distances on a mesh.
static Eigen::VectorXf interpolateSignal(const QSharedPointer< Eigen::SparseMatrix< float > > matInterpolationMatrix, const QSharedPointer< Eigen::VectorXf > &vecMeasurementData)
interpolateSignal Interpolates sensor data using the weight matrix (shared pointer version).
static double cubic(const double dIn)
cubic Cubic hyperbola interpolation function.
static QSharedPointer< Eigen::SparseMatrix< float > > createInterpolationMat(const Eigen::VectorXi &vecProjectedSensors, const QSharedPointer< Eigen::MatrixXd > matDistanceTable, double(*interpolationFunction)(double), const double dCancelDist=FLOAT_INFINITY, const Eigen::VectorXi &vecExcludeIndex=Eigen::VectorXi())
createInterpolationMat Calculates the weight matrix for interpolation.
Renderable cortical surface mesh with per-vertex color, curvature data, and GPU buffer management.
uint32_t vertexCount() const
void applySourceEstimateColors(const QVector< uint32_t > &colors)
Eigen::MatrixX3f verticesAsMatrix() const
std::vector< Eigen::VectorXi > computeNeighbors() const
Eigen::VectorXd sourceDataColumn(int timeIndex) const
void setStcData(const INVLIB::InvSourceEstimate &stc, int hemi)
void getDataRange(double &minVal, double &maxVal) const
void setThresholds(float min, float mid, float max)
void applyToSurface(BrainSurface *surface, int timeIndex)
float timeAtIndex(int idx) const
int numTimePoints() const
void computeInterpolationMatrix(BrainSurface *surface, int hemi, double cancelDist=0.05)
void setColormap(const QString &name)
void updateThresholdsFromData()
void setInterpolationMatrix(QSharedPointer< Eigen::SparseMatrix< float > > mat, int hemi)
bool loadStc(const QString &path, int hemi)
Source-space inverse-solution container with dense grid plus optional focal-dipole,...
static bool read(QIODevice &p_IODevice, InvSourceEstimate &p_stc)