25#include <QCoreApplication>
47 const QString& megHelmetOverridePath)
62 if (stream->read_meas_info(tree, info, nodeInfo)) {
71 QMatrix4x4 devHeadQTrans;
72 bool hasDevHead =
false;
84 for (
const auto& ch : info.
chs) {
86 QVector3D pos(ch.chpos.r0(0), ch.chpos.r0(1), ch.chpos.r0(2));
89 pos = devHeadQTrans.map(pos);
92 auto* item =
new SensorTreeItem(ch.ch_name, pos, QColor(100, 100, 100), 0.01f);
96 for (
int r = 0; r < 3; ++r)
97 for (
int c = 0; c < 3; ++c)
98 orient(r, c) = ch.coil_trans(r, c);
100 QMatrix4x4 devHeadRot;
101 for (
int r = 0; r < 3; ++r)
102 for (
int c = 0; c < 3; ++c)
103 devHeadRot(r, c) = devHeadQTrans(r, c);
104 orient = devHeadRot * orient;
106 item->setOrientation(orient);
114 QVector3D pos(ch.chpos.r0(0), ch.chpos.r0(1), ch.chpos.r0(2));
121 auto pickHelmetFile = [&info]() -> QString {
124 for (
const auto& ch : info.
chs) {
126 coilType = ch.chpos.coil_type & 0xFFFF;
131 QString fileName =
"306m.fif";
133 fileName =
"BabySQUID.fif";
135 fileName =
"122m.fif";
137 fileName =
"CTF_275.fif";
139 fileName =
"KIT.fif";
141 fileName = (nMeg > 150) ?
"Magnes_3600wh.fif" :
"Magnes_2500wh.fif";
142 }
else if (coilType / 1000 == 3) {
143 fileName =
"306m.fif";
146 return QCoreApplication::applicationDirPath() +
"/../resources/general/sensorSurfaces/" + fileName;
150 if (!megHelmetOverridePath.isEmpty()) {
151 helmetPath = megHelmetOverridePath;
152 if (!QFile::exists(helmetPath)) {
153 qWarning() <<
"MEG helmet override file not found:" << helmetPath
154 <<
"- falling back to auto selection.";
159 if (helmetPath.isEmpty()) {
160 helmetPath = pickHelmetFile();
164 if (!QFile::exists(helmetPath)) {
165 QString fn = QFileInfo(helmetPath).fileName();
167 fn = QStringLiteral(
"306m.fif");
168 helmetPath = QStringLiteral(
"/resources/general/sensorSurfaces/") + fn;
173 if (!QFile::exists(helmetPath)) {
174 QString fallback = QCoreApplication::applicationDirPath() +
"/../resources/general/sensorSurfaces/306m.fif";
175 if (QFile::exists(fallback)) {
176 helmetPath = fallback;
184 if (!QFile::exists(helmetPath)) {
185 QString wasmFallback = QStringLiteral(
"/resources/general/sensorSurfaces/") + QFileInfo(helmetPath).fileName();
186 if (wasmFallback.endsWith(
'/'))
187 wasmFallback +=
"306m.fif";
188 if (QFile::exists(wasmFallback)) {
189 helmetPath = wasmFallback;
194 if (!QFile::exists(helmetPath)) {
195 qWarning() <<
"MEG helmet surface file not found. Checked:" << helmetPath;
198 if (QFile::exists(helmetPath)) {
199 QFile helmetFile(helmetPath);
200 MNEBem helmetBem(helmetFile);
201 if (helmetBem.
size() > 0) {
203 if (helmetSurf.
nn.rows() != helmetSurf.
rr.rows()) {
208 QMatrix3x3 normalMat = devHeadQTrans.normalMatrix();
209 for (
int i = 0; i < helmetSurf.
rr.rows(); ++i) {
210 QVector3D pos(helmetSurf.
rr(i, 0), helmetSurf.
rr(i, 1), helmetSurf.
rr(i, 2));
211 pos = devHeadQTrans.map(pos);
212 helmetSurf.
rr(i, 0) = pos.x();
213 helmetSurf.
rr(i, 1) = pos.y();
214 helmetSurf.
rr(i, 2) = pos.z();
216 QVector3D nn(helmetSurf.
nn(i, 0), helmetSurf.
nn(i, 1), helmetSurf.
nn(i, 2));
217 const float* d = normalMat.constData();
218 float nx = d[0] * nn.x() + d[3] * nn.y() + d[6] * nn.z();
219 float ny = d[1] * nn.x() + d[4] * nn.y() + d[7] * nn.z();
220 float nz = d[2] * nn.x() + d[5] * nn.y() + d[8] * nn.z();
221 QVector3D n = QVector3D(nx, ny, nz).normalized();
222 helmetSurf.
nn(i, 0) = n.x();
223 helmetSurf.
nn(i, 1) = n.y();
224 helmetSurf.
nn(i, 2) = n.z();
228 auto helmetSurface = std::make_shared<BrainSurface>();
229 helmetSurface->fromBemSurface(helmetSurf, QColor(0, 0, 77, 200));
230 helmetSurface->setVisible(
true);
233 qWarning() <<
"DataLoader::loadSensors: helmetBem[0] has 0 verts/tris!";
246 if (digSet.
size() > 0) {
247 for (
int i = 0; i < digSet.
size(); ++i) {
260 const QString& helmetFilePath,
261 const QMatrix4x4& devHeadTrans,
264 if (!QFile::exists(helmetFilePath)) {
265 qWarning() <<
"DataLoader::loadHelmetSurface: file not found:" << helmetFilePath;
269 QFile helmetFile(helmetFilePath);
270 MNEBem helmetBem(helmetFile);
271 if (helmetBem.
size() == 0) {
272 qWarning() <<
"DataLoader::loadHelmetSurface: no BEM surfaces in" << helmetFilePath;
278 if (helmetSurf.
nn.rows() != helmetSurf.
rr.rows()) {
283 QMatrix3x3 normalMat = devHeadTrans.normalMatrix();
284 for (
int i = 0; i < helmetSurf.
rr.rows(); ++i) {
285 QVector3D pos(helmetSurf.
rr(i, 0), helmetSurf.
rr(i, 1), helmetSurf.
rr(i, 2));
286 pos = devHeadTrans.map(pos);
287 helmetSurf.
rr(i, 0) = pos.x();
288 helmetSurf.
rr(i, 1) = pos.y();
289 helmetSurf.
rr(i, 2) = pos.z();
291 QVector3D nn(helmetSurf.
nn(i, 0), helmetSurf.
nn(i, 1), helmetSurf.
nn(i, 2));
292 const float* d = normalMat.constData();
293 float nx = d[0] * nn.x() + d[3] * nn.y() + d[6] * nn.z();
294 float ny = d[1] * nn.x() + d[4] * nn.y() + d[7] * nn.z();
295 float nz = d[2] * nn.x() + d[5] * nn.y() + d[8] * nn.z();
296 QVector3D n = QVector3D(nx, ny, nz).normalized();
297 helmetSurf.
nn(i, 0) = n.x();
298 helmetSurf.
nn(i, 1) = n.y();
299 helmetSurf.
nn(i, 2) = n.z();
303 auto surface = std::make_shared<BrainSurface>();
304 surface->fromBemSurface(helmetSurf, QColor(0, 0, 77, 200));
305 surface->setVisible(
true);
315 if (ecdSet.
size() == 0) {
316 qWarning() <<
"DataLoader: Failed to load dipoles from" << dipPath;
326 if (!file.exists()) {
327 qWarning() <<
"DataLoader: Source space file not found:" << fwdPath;
333 if (!stream->open()) {
334 qWarning() <<
"DataLoader: Failed to open FIF stream for source space";
339 qWarning() <<
"DataLoader: Failed to read source space from" << fwdPath;
344 qWarning() <<
"DataLoader: Source space is empty";
365 QFile file(evokedPath);
366 if (!file.exists()) {
367 qWarning() <<
"DataLoader: Sensor evoked file not found:" << evokedPath;
373 qWarning() <<
"DataLoader: Failed to read evoked data from" << evokedPath;
383 QFile file(evokedPath);
384 if (!file.exists()) {
389 for (
int i = 0; i < evokedSet.
evoked.size(); ++i) {
390 const auto& ev = evokedSet.
evoked.at(i);
391 QString label = QString(
"%1: %2 (%3, nave=%4)")
393 .arg(ev.comment.isEmpty() ? QStringLiteral(
"Set %1").arg(i) : ev.comment)
394 .arg(ev.aspectKindToString())
396 result.append(label);
Static MATLAB-style FIFF facade: thin wrapper functions kept for parity with the historical mne-matla...
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
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
Container for the FIFF_DIG_POINT records of a measurement (a parsed FIFFB_ISOTRAK block).
Tree item for a single MEG / EEG sensor with position, optional coil orientation and rendered scale.
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.
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....
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
3-D brain visualisation using the Qt RHI rendering backend.
QMatrix4x4 toQMatrix4x4(const Eigen::Matrix4f &m)
Inverse source estimation (MNE, dSPM, sLORETA, dipole fitting).
static std::shared_ptr< BrainSurface > loadHelmetSurface(const QString &helmetFilePath, const QMatrix4x4 &devHeadTrans=QMatrix4x4(), bool applyTrans=false)
static FIFFLIB::FiffEvoked loadEvoked(const QString &evokedPath, int aveIndex=0)
static MNELIB::MNESourceSpaces loadSourceSpace(const QString &fwdPath)
static INVLIB::InvEcdSet loadDipoles(const QString &dipPath)
static QStringList probeEvokedSets(const QString &evokedPath)
static SensorLoadResult loadSensors(const QString &fifPath, const QString &megHelmetOverridePath={})
static bool loadHeadToMriTransform(const QString &transPath, FIFFLIB::FiffCoordTrans &trans)
Return value bundling loaded sensor geometry, labels, and channel-to-sensor mapping.
FIFFLIB::FiffInfo info
Channel / dig info.
QMatrix4x4 devHeadTrans
Device→Head transform (identity if absent).
QList< QStandardItem * > megGradItems
Ownership passes to caller.
QList< FIFFLIB::FiffDigPoint > digitizerPoints
QList< QStandardItem * > megMagItems
QList< QStandardItem * > eegItems
bool hasDevHead
Whether a valid dev→head transform was found.
std::shared_ptr< BrainSurface > helmetSurface
May be null.
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.
Eigen::Matrix< float, 4, 4, Eigen::DontAlign > trans
static FiffCoordTrans readTransform(const QString &name, int from, int to)
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)