25#include <QCoreApplication>
44 const QString &megHelmetOverridePath)
49 if (!file.exists())
return result;
58 if (stream->read_meas_info(tree, info, nodeInfo)) {
67 QMatrix4x4 devHeadQTrans;
68 bool hasDevHead =
false;
80 for (
const auto &ch : info.
chs) {
82 QVector3D pos(ch.chpos.r0(0), ch.chpos.r0(1), ch.chpos.r0(2));
85 pos = devHeadQTrans.map(pos);
88 auto *item =
new SensorTreeItem(ch.ch_name, pos, QColor(100, 100, 100), 0.01f);
92 for (
int r = 0; r < 3; ++r)
93 for (
int c = 0; c < 3; ++c)
94 orient(r, c) = ch.coil_trans(r, c);
96 QMatrix4x4 devHeadRot;
97 for (
int r = 0; r < 3; ++r)
98 for (
int c = 0; c < 3; ++c)
99 devHeadRot(r, c) = devHeadQTrans(r, c);
100 orient = devHeadRot * orient;
102 item->setOrientation(orient);
110 QVector3D pos(ch.chpos.r0(0), ch.chpos.r0(1), ch.chpos.r0(2));
117 auto pickHelmetFile = [&info]() -> QString {
120 for (
const auto &ch : info.
chs) {
122 coilType = ch.chpos.coil_type & 0xFFFF;
127 QString fileName =
"306m.fif";
129 fileName =
"BabySQUID.fif";
131 fileName =
"122m.fif";
133 fileName =
"CTF_275.fif";
135 fileName =
"KIT.fif";
137 fileName = (nMeg > 150) ?
"Magnes_3600wh.fif" :
"Magnes_2500wh.fif";
138 }
else if (coilType / 1000 == 3) {
139 fileName =
"306m.fif";
142 return QCoreApplication::applicationDirPath()
143 +
"/../resources/general/sensorSurfaces/" + fileName;
147 if (!megHelmetOverridePath.isEmpty()) {
148 helmetPath = megHelmetOverridePath;
149 if (!QFile::exists(helmetPath)) {
150 qWarning() <<
"MEG helmet override file not found:" << helmetPath
151 <<
"- falling back to auto selection.";
156 if (helmetPath.isEmpty()) {
157 helmetPath = pickHelmetFile();
161 if (!QFile::exists(helmetPath)) {
162 QString fn = QFileInfo(helmetPath).fileName();
163 if (fn.isEmpty()) fn = QStringLiteral(
"306m.fif");
164 helmetPath = QStringLiteral(
"/resources/general/sensorSurfaces/") + fn;
169 if (!QFile::exists(helmetPath)) {
170 QString fallback = QCoreApplication::applicationDirPath()
171 +
"/../resources/general/sensorSurfaces/306m.fif";
172 if (QFile::exists(fallback)) {
173 helmetPath = fallback;
181 if (!QFile::exists(helmetPath)) {
182 QString wasmFallback = QStringLiteral(
"/resources/general/sensorSurfaces/")
183 + QFileInfo(helmetPath).fileName();
184 if (wasmFallback.endsWith(
'/')) wasmFallback +=
"306m.fif";
185 if (QFile::exists(wasmFallback)) {
186 helmetPath = wasmFallback;
191 if (!QFile::exists(helmetPath)) {
192 qWarning() <<
"MEG helmet surface file not found. Checked:" << helmetPath;
195 if (QFile::exists(helmetPath)) {
196 QFile helmetFile(helmetPath);
197 MNEBem helmetBem(helmetFile);
198 if (helmetBem.
size() > 0) {
200 if (helmetSurf.
nn.rows() != helmetSurf.
rr.rows()) {
205 QMatrix3x3 normalMat = devHeadQTrans.normalMatrix();
206 for (
int i = 0; i < helmetSurf.
rr.rows(); ++i) {
207 QVector3D pos(helmetSurf.
rr(i, 0), helmetSurf.
rr(i, 1), helmetSurf.
rr(i, 2));
208 pos = devHeadQTrans.map(pos);
209 helmetSurf.
rr(i, 0) = pos.x();
210 helmetSurf.
rr(i, 1) = pos.y();
211 helmetSurf.
rr(i, 2) = pos.z();
213 QVector3D nn(helmetSurf.
nn(i, 0), helmetSurf.
nn(i, 1), helmetSurf.
nn(i, 2));
214 const float *d = normalMat.constData();
215 float nx = d[0] * nn.x() + d[3] * nn.y() + d[6] * nn.z();
216 float ny = d[1] * nn.x() + d[4] * nn.y() + d[7] * nn.z();
217 float nz = d[2] * nn.x() + d[5] * nn.y() + d[8] * nn.z();
218 QVector3D n = QVector3D(nx, ny, nz).normalized();
219 helmetSurf.
nn(i, 0) = n.x();
220 helmetSurf.
nn(i, 1) = n.y();
221 helmetSurf.
nn(i, 2) = n.z();
225 auto helmetSurface = std::make_shared<BrainSurface>();
226 helmetSurface->fromBemSurface(helmetSurf, QColor(0, 0, 77, 200));
227 helmetSurface->setVisible(
true);
230 qWarning() <<
"DataLoader::loadSensors: helmetBem[0] has 0 verts/tris!";
243 if (digSet.
size() > 0) {
244 for (
int i = 0; i < digSet.
size(); ++i) {
257 const QString &helmetFilePath,
258 const QMatrix4x4 &devHeadTrans,
261 if (!QFile::exists(helmetFilePath)) {
262 qWarning() <<
"DataLoader::loadHelmetSurface: file not found:" << helmetFilePath;
266 QFile helmetFile(helmetFilePath);
267 MNEBem helmetBem(helmetFile);
268 if (helmetBem.
size() == 0) {
269 qWarning() <<
"DataLoader::loadHelmetSurface: no BEM surfaces in" << helmetFilePath;
275 if (helmetSurf.
nn.rows() != helmetSurf.
rr.rows()) {
280 QMatrix3x3 normalMat = devHeadTrans.normalMatrix();
281 for (
int i = 0; i < helmetSurf.
rr.rows(); ++i) {
282 QVector3D pos(helmetSurf.
rr(i, 0), helmetSurf.
rr(i, 1), helmetSurf.
rr(i, 2));
283 pos = devHeadTrans.map(pos);
284 helmetSurf.
rr(i, 0) = pos.x();
285 helmetSurf.
rr(i, 1) = pos.y();
286 helmetSurf.
rr(i, 2) = pos.z();
288 QVector3D nn(helmetSurf.
nn(i, 0), helmetSurf.
nn(i, 1), helmetSurf.
nn(i, 2));
289 const float *d = normalMat.constData();
290 float nx = d[0] * nn.x() + d[3] * nn.y() + d[6] * nn.z();
291 float ny = d[1] * nn.x() + d[4] * nn.y() + d[7] * nn.z();
292 float nz = d[2] * nn.x() + d[5] * nn.y() + d[8] * nn.z();
293 QVector3D n = QVector3D(nx, ny, nz).normalized();
294 helmetSurf.
nn(i, 0) = n.x();
295 helmetSurf.
nn(i, 1) = n.y();
296 helmetSurf.
nn(i, 2) = n.z();
300 auto surface = std::make_shared<BrainSurface>();
301 surface->fromBemSurface(helmetSurf, QColor(0, 0, 77, 200));
302 surface->setVisible(
true);
312 if (ecdSet.
size() == 0) {
313 qWarning() <<
"DataLoader: Failed to load dipoles from" << dipPath;
323 if (!file.exists()) {
324 qWarning() <<
"DataLoader: Source space file not found:" << fwdPath;
330 if (!stream->open()) {
331 qWarning() <<
"DataLoader: Failed to open FIF stream for source space";
336 qWarning() <<
"DataLoader: Failed to read source space from" << fwdPath;
341 qWarning() <<
"DataLoader: Source space is empty";
353 QFile file(transPath);
357 qWarning() <<
"DataLoader: Failed to load transformation from" << transPath;
371 qWarning() <<
"DataLoader: Loaded transformation is not Head<->MRI (from"
372 << raw.
from <<
"to" << raw.
to <<
"). Using as is.";
383 QFile file(evokedPath);
384 if (!file.exists()) {
385 qWarning() <<
"DataLoader: Sensor evoked file not found:" << evokedPath;
391 qWarning() <<
"DataLoader: Failed to read evoked data from" << evokedPath;
401 QFile file(evokedPath);
402 if (!file.exists()) {
407 for (
int i = 0; i < evokedSet.
evoked.size(); ++i) {
408 const auto &ev = evokedSet.
evoked.at(i);
409 QString label = QString(
"%1: %2 (%3, nave=%4)")
411 .arg(ev.comment.isEmpty() ? QStringLiteral(
"Set %1").arg(i) : ev.comment)
412 .arg(ev.aspectKindToString())
414 result.append(label);
Renderable cortical / BEM mesh with interleaved vertex attributes and Qt-RHI buffer management.
String key constants for surfaces in the DISP3DLIB scene map.
Static helpers for loading FreeSurfer / MNE source-space and BEM data into the disp3D model tree.
Tree item for a single MEG / EEG sensor with position, optional coil orientation and rendered scale.
Reader and in-memory representation of a single FreeSurfer triangular surface (e.g....
Boundary element model bundle (inner skull, outer skull, outer skin) loaded from -bem....
Container for the FIFF_DIG_POINT records of a measurement (a parsed FIFFB_ISOTRAK block).
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
#define FIFFV_COIL_CTF_GRAD
#define FIFFV_COORD_DEVICE
#define FIFFV_COIL_NM_122
#define FIFFV_COIL_MAGNES_MAG
#define FIFFV_COIL_BABY_GRAD
#define FIFFV_COIL_MAGNES_GRAD
#define FIFFV_COIL_KIT_GRAD
Static MATLAB-style FIFF facade: thin wrapper functions kept for parity with the historical mne-matla...
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
QMatrix4x4 toQMatrix4x4(const Eigen::Matrix4f &m)
Inverse source estimation (MNE, dSPM, sLORETA, dipole fitting).
static SensorLoadResult loadSensors(const QString &fifPath, const QString &megHelmetOverridePath={})
static QStringList probeEvokedSets(const QString &evokedPath)
static MNELIB::MNESourceSpaces loadSourceSpace(const QString &fwdPath)
static bool loadHeadToMriTransform(const QString &transPath, FIFFLIB::FiffCoordTrans &trans)
static std::shared_ptr< BrainSurface > loadHelmetSurface(const QString &helmetFilePath, const QMatrix4x4 &devHeadTrans=QMatrix4x4(), bool applyTrans=false)
static INVLIB::InvEcdSet loadDipoles(const QString &dipPath)
static FIFFLIB::FiffEvoked loadEvoked(const QString &evokedPath, int aveIndex=0)
Return value bundling loaded sensor geometry, labels, and channel-to-sensor mapping.
std::shared_ptr< BrainSurface > helmetSurface
May be null.
QList< QStandardItem * > megGradItems
Ownership passes to caller.
FIFFLIB::FiffInfo info
Channel / dig info.
QMatrix4x4 devHeadTrans
Device→Head transform (identity if absent).
QList< QStandardItem * > eegItems
bool hasDevHead
Whether a valid dev→head transform was found.
QList< FIFFLIB::FiffDigPoint > digitizerPoints
QList< QStandardItem * > megMagItems
Tree item representing MEG or EEG sensor positions in the 3-D scene hierarchy.
Labelled 4x4 FIFF affine: source frame, destination frame, rotation, translation and cached inverse.
static bool read(QIODevice &p_IODevice, FiffCoordTrans &p_Trans)
Eigen::Matrix< float, 4, 4, Eigen::DontAlign > trans
Eigen::Matrix< float, 4, 4, Eigen::DontAlign > invtrans
Collection of FiffDigPoint records as parsed from a FIFFB_ISOTRAK block.
QSharedPointer< FiffDirNode > SPtr
Single averaged evoked response: time axis, data, baseline, channel info and averaging metadata.
Set of FiffEvoked instances sharing one FiffInfo, plus channel-picking and compensation helpers.
QList< FiffEvoked > evoked
Full FIFF measurement info: per-channel descriptors, sampling and filter setup, projectors,...
QList< FiffDigPoint > dig
FiffCoordTrans dev_head_t
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
QSharedPointer< FiffStream > SPtr
static Eigen::MatrixX3f compute_normals(const Eigen::MatrixX3f &rr, const Eigen::MatrixX3i &tris)
Holds a set of Electric Current Dipoles.
static InvEcdSet read_dipoles_dip(const QString &fileName)
Aggregated boundary element model loaded from a -bem.fif file.
BEM surface provides geometry information.
List of MNESourceSpace objects forming a subject source space.
static bool readFromStream(FIFFLIB::FiffStream::SPtr &p_pStream, bool add_geom, MNESourceSpaces &p_SourceSpace)