42#include <QtConcurrent>
50#ifndef _USE_MATH_DEFINES
51#define _USE_MATH_DEFINES
95int read_int3(QFile& in,
int& ival)
102 if (in.read(
reinterpret_cast<char*
>(&s), 3) != 3) {
103 qCritical(
"read_int3 could not read data");
107 ival = ((s >> 8) & 0xffffff);
111int read_int(QFile& in, qint32& ival)
117 if (in.read(
reinterpret_cast<char*
>(&s),
sizeof(qint32)) !=
static_cast<qint64
>(
sizeof(qint32))) {
118 qCritical(
"read_int could not read data");
125int read_int2(QFile& in,
int& ival)
131 if (in.read(
reinterpret_cast<char*
>(&s),
sizeof(
short)) !=
static_cast<qint64
>(
sizeof(
short))) {
132 qCritical(
"read_int2 could not read data");
139int read_float(QFile& in,
float& fval)
145 if (in.read(
reinterpret_cast<char*
>(&f),
sizeof(
float)) !=
static_cast<qint64
>(
sizeof(
float))) {
146 qCritical(
"read_float could not read data");
153int read_long(QFile& in,
long long& lval)
159 if (in.read(
reinterpret_cast<char*
>(&s),
sizeof(
long long)) !=
static_cast<qint64
>(
sizeof(
long long))) {
160 qCritical(
"read_long could not read data");
171int check_vertex(
int no,
int maxno)
173 if (no < 0 || no > maxno - 1) {
174 qCritical(
"Illegal vertex number %d (max %d).", no, maxno);
193std::unique_ptr<MNEVolGeom> read_vol_geom(QFile& fp)
202 auto vg = std::make_unique<MNEVolGeom>();
210 const auto readFloats = [](
const QList<QByteArray>& tok,
float* dest,
int n) {
211 for (
int i = 0; i < n; ++i) {
212 if (tok.size() <= 2 + i)
215 const float v = tok.at(2 + i).toFloat(&ok);
222 while (!fp.atEnd() && counter < 8) {
223 const QByteArray lineData = fp.readLine(256);
224 if (lineData.isEmpty())
227 const QList<QByteArray> tok = lineData.simplified().split(
' ');
228 if (tok.isEmpty() || tok.first().isEmpty())
231 const QByteArray& param = tok.first();
232 if (param ==
"valid") {
234 vg->valid = tok.at(2).toInt();
237 }
else if (param ==
"filename") {
239 vg->filename = QString::fromUtf8(tok.at(2));
241 }
else if (param ==
"volume") {
242 if (tok.size() > 4) {
243 vg->width = tok.at(2).toInt();
244 vg->height = tok.at(3).toInt();
245 vg->depth = tok.at(4).toInt();
248 }
else if (param ==
"voxelsize") {
249 float size[3] = {vg->xsize, vg->ysize, vg->zsize};
250 readFloats(tok, size, 3);
254 vg->xsize = size[0] / 1000.0f;
255 vg->ysize = size[1] / 1000.0f;
256 vg->zsize = size[2] / 1000.0f;
258 }
else if (param ==
"xras") {
259 readFloats(tok, vg->x_ras, 3);
261 }
else if (param ==
"yras") {
262 readFloats(tok, vg->y_ras, 3);
264 }
else if (param ==
"zras") {
265 readFloats(tok, vg->z_ras, 3);
267 }
else if (param ==
"cras") {
268 readFloats(tok, vg->c_ras, 3);
269 vg->c_ras[0] = vg->c_ras[0] / 1000.0f;
270 vg->c_ras[1] = vg->c_ras[1] / 1000.0f;
271 vg->c_ras[2] = vg->c_ras[2] / 1000.0f;
283 vg = std::make_unique<MNEVolGeom>();
292int read_tag_data(QFile& fp,
int tag,
long long nbytes,
unsigned char*& val,
long long& nbytesp)
297 size_t snbytes = nbytes;
301 auto dum = std::make_unique<unsigned char[]>(nbytes + 1);
302 if (fp.read(
reinterpret_cast<char*
>(dum.get()), nbytes) !=
static_cast<qint64
>(snbytes)) {
303 qCritical(
"Failed to read %d bytes of tag data",
static_cast<int>(nbytes));
311 auto g = read_vol_geom(fp);
324 int width, height, depth;
325 float xsize, ysize, zsize;
326 float x_ras[3], y_ras[3], z_ras[3];
329 QByteArray fn = g->
filename.toUtf8();
330 size_t totalSize =
sizeof(VolGeomPOD) + fn.size() + 1;
331 auto buf = std::make_unique<unsigned char[]>(totalSize);
333 pod.valid = g->
valid;
334 pod.width = g->
width;
336 pod.depth = g->
depth;
337 pod.xsize = g->
xsize;
338 pod.ysize = g->
ysize;
339 pod.zsize = g->
zsize;
340 std::memcpy(pod.x_ras, g->
x_ras, 3 *
sizeof(
float));
341 std::memcpy(pod.y_ras, g->
y_ras, 3 *
sizeof(
float));
342 std::memcpy(pod.z_ras, g->
z_ras, 3 *
sizeof(
float));
343 std::memcpy(pod.c_ras, g->
c_ras, 3 *
sizeof(
float));
344 std::memcpy(buf.get(), &pod,
sizeof(VolGeomPOD));
345 std::memcpy(buf.get() +
sizeof(VolGeomPOD), fn.constData(), fn.size() + 1);
347 nbytesp =
static_cast<long long>(totalSize);
349 auto vi = std::make_unique<int[]>(1);
350 if (read_int(fp, vi[0]) ==
FAIL)
352 val =
reinterpret_cast<unsigned char*
>(vi.release());
353 nbytesp =
sizeof(int);
355 qWarning(
"Encountered an unknown tag with no length specification : %d\n", tag);
367void add_mgh_tag_to_group(std::optional<MNEMghTagGroup>& g,
int tag,
long long len,
unsigned char* data)
371 auto new_tag = std::make_unique<MNEMghTag>();
374 new_tag->data = QByteArray(
reinterpret_cast<const char*
>(data),
static_cast<int>(len));
376 g->tags.push_back(std::move(new_tag));
383int read_next_tag(QFile& fp,
int& tagp,
long long& lenp,
unsigned char*& datap)
388 int ilen = 0, tag = 0;
391 if (read_int(fp, tag) ==
FAIL) {
401 if (read_int(fp, ilen) ==
FAIL)
411 if (read_long(fp, len) ==
FAIL)
417 if (read_tag_data(fp, tag, len, datap, lenp) ==
FAIL)
426int read_mgh_tags(QFile& fp, std::optional<MNEMghTagGroup>& tagsp)
433 unsigned char* tag_data;
436 if (read_next_tag(fp, tag, len, tag_data) ==
FAIL)
440 add_mgh_tag_to_group(tagsp, tag, len, tag_data);
449int read_curvature_file(
const QString& fname,
450 Eigen::VectorXf& curv)
456 float curvmin, curvmax;
458 int nface = 0, val_pervert = 0;
462 if (!fp.open(QIODevice::ReadOnly)) {
463 qCritical() << fname;
467 if (read_int3(fp, magic) != 0) {
468 qCritical() <<
"Bad magic in" << fname;
476 if (read_int(fp, ncurv) != 0) {
480 if (read_int(fp, nface) != 0) {
485 qInfo(
"nvert = %d nface = %d\n", ncurv, nface);
487 if (read_int(fp, val_pervert) != 0) {
491 if (val_pervert != 1) {
492 qCritical(
"Values per vertex not equal to one.");
500 curvmin = curvmax = 0.0;
501 for (k = 0; k < ncurv; k++) {
502 if (read_float(fp, fval) != 0) {
507 if (curv[k] > curvmax)
509 if (curv[k] < curvmin)
517 if (read_int3(fp, nface) != 0) {
522 qInfo(
"nvert = %d nface = %d\n", ncurv, nface);
528 curvmin = curvmax = 0.0;
529 for (k = 0; k < ncurv; k++) {
530 if (read_int2(fp, val) != 0) {
534 curv[k] =
static_cast<float>(val) / 100.0;
535 if (curv[k] > curvmax)
537 if (curv[k] < curvmin)
542 qInfo(
"Curvature range: %f...%f\n", curvmin, curvmax);
551int read_triangle_file(
const QString& fname,
553 TrianglesT& triangles,
554 std::optional<MNEMghTagGroup>* tagsp)
563 qint32 nvert = 0, ntri = 0, nquad = 0;
571 if (!fp.open(QIODevice::ReadOnly)) {
572 qCritical() << fname;
575 if (read_int3(fp, magic) != 0) {
576 qCritical() <<
"Bad magic in" << fname;
582 qCritical() <<
"Bad magic in" << fname;
589 qInfo(
"Triangle file : ");
590 for (fp.getChar(&c); c !=
'\n'; fp.getChar(&c)) {
592 qCritical() <<
"Bad triangle file.";
601 if (read_int(fp, nvert) != 0)
603 if (read_int(fp, ntri) != 0)
605 qInfo(
" nvert = %d ntri = %d\n", nvert, ntri);
606 vert.resize(nvert, 3);
611 for (k = 0; k < nvert; k++) {
612 if (read_float(fp, vert(k, 0)) != 0)
614 if (read_float(fp, vert(k, 1)) != 0)
616 if (read_float(fp, vert(k, 2)) != 0)
622 for (k = 0; k < ntri; k++) {
623 if (read_int(fp, tri(k, 0)) != 0)
625 if (check_vertex(tri(k, 0), nvert) !=
OK)
627 if (read_int(fp, tri(k, 1)) != 0)
629 if (check_vertex(tri(k, 1), nvert) !=
OK)
631 if (read_int(fp, tri(k, 2)) != 0)
633 if (check_vertex(tri(k, 2), nvert) !=
OK)
638 if (read_int3(fp, nvert) != 0)
640 if (read_int3(fp, nquad) != 0)
642 qInfo(
"%s file : nvert = %d nquad = %d\n",
645 vert.resize(nvert, 3);
647 for (k = 0; k < nvert; k++) {
648 if (read_int2(fp, val) != 0)
650 vert(k, 0) = val / 100.0;
651 if (read_int2(fp, val) != 0)
653 vert(k, 1) = val / 100.0;
654 if (read_int2(fp, val) != 0)
656 vert(k, 2) = val / 100.0;
659 for (k = 0; k < nvert; k++) {
660 if (read_float(fp, vert(k, 0)) != 0)
662 if (read_float(fp, vert(k, 1)) != 0)
664 if (read_float(fp, vert(k, 2)) != 0)
670 for (k = 0, ntri = 0; k < nquad; k++) {
671 for (p = 0; p < 4; p++) {
672 if (read_int3(fp, quad[p]) != 0)
680#define EVEN(n) ((((n) / 2) * 2) == n)
682#define WHICH_FACE_SPLIT(vno0, vno1) \
683 (1 * nearbyint(sqrt(1.9 * vno0) + sqrt(3.5 * vno1)))
685 which = WHICH_FACE_SPLIT(quad[0], quad[1]);
693 tri(ntri, 0) = quad[0];
694 tri(ntri, 1) = quad[1];
695 tri(ntri, 2) = quad[3];
698 tri(ntri, 0) = quad[2];
699 tri(ntri, 1) = quad[3];
700 tri(ntri, 2) = quad[1];
703 tri(ntri, 0) = quad[0];
704 tri(ntri, 1) = quad[1];
705 tri(ntri, 2) = quad[2];
708 tri(ntri, 0) = quad[0];
709 tri(ntri, 1) = quad[2];
710 tri(ntri, 2) = quad[3];
719 std::optional<MNEMghTagGroup> tags;
720 if (read_mgh_tags(fp, tags) ==
FAIL) {
723 *tagsp = std::move(tags);
729 vertices = std::move(vert);
730 triangles = std::move(tri);
738std::optional<MNEVolGeom> get_volume_geom_from_tag(
const MNEMghTagGroup* tagsp)
746 int width, height, depth;
747 float xsize, ysize, zsize;
748 float x_ras[3], y_ras[3], z_ras[3];
752 for (
const auto& t : tagsp->
tags) {
754 if (t->len <
static_cast<long long>(
sizeof(VolGeomPOD)))
757 const unsigned char* d =
reinterpret_cast<const unsigned char*
>(t->data.constData());
759 std::memcpy(&pod, d,
sizeof(VolGeomPOD));
762 result.
valid = pod.valid;
763 result.
width = pod.width;
764 result.
height = pod.height;
765 result.
depth = pod.depth;
766 result.
xsize = pod.xsize;
767 result.
ysize = pod.ysize;
768 result.
zsize = pod.zsize;
769 std::memcpy(result.
x_ras, pod.x_ras, 3 *
sizeof(
float));
770 std::memcpy(result.
y_ras, pod.y_ras, 3 *
sizeof(
float));
771 std::memcpy(result.
z_ras, pod.z_ras, 3 *
sizeof(
float));
772 std::memcpy(result.
c_ras, pod.c_ras, 3 *
sizeof(
float));
774 if (t->len >
static_cast<long long>(
sizeof(VolGeomPOD)))
775 result.
filename = QString::fromUtf8(
776 reinterpret_cast<const char*
>(d +
sizeof(VolGeomPOD)));
794 rr = PointsT::Zero(
np, 3);
795 nn = NormalsT::Zero(
np, 3);
834 cm[0] =
cm[1] =
cm[2] = 0.0;
849 auto copy = std::make_shared<MNESourceSpace>(this->
np);
850 copy->type = this->
type;
853 copy->ntri = this->
ntri;
857 copy->nuse = this->
nuse;
858 copy->inuse = this->
inuse;
859 copy->vertno = this->
vertno;
860 copy->itris = this->
itris;
864 copy->dist = this->
dist;
876 for (k = 0; k <
np; k++)
892 for (k = 0, xave = 0.0; k <
np; k++)
904 double xave =
rr.col(0).sum();
918 int k, p, nuse_count;
920 inuse = std::move(new_inuse);
922 for (k = 0, nuse_count = 0; k <
np; k++)
929 for (k = 0, p = 0; k <
np; k++)
949 qCritical(
"Coordinate transformation does not match with the source space coordinate system.");
952 for (k = 0; k <
np; k++) {
957 for (k = 0; k <
ntri; k++)
970 std::vector<std::optional<MNEPatchInfo>> pinfo(
nuse);
973 qInfo(
"Computing patch statistics...\n");
979 qCritical(
"The patch information is not available.");
989 qInfo(
"\tareas, average normals, and mean deviations...");
994 for (p = 1, q = 0; p <
np; p++) {
997 qCritical(
"No vertices belong to the patch of vertex %d", nearest_data[p - 1].
nearest);
1003 pinfo[q]->vert = nearest_data[p - 1].
nearest;
1004 this_patch = nearest_data + p - nave;
1005 pinfo[q]->memb_vert.resize(nave);
1006 for (k = 0; k < nave; k++) {
1007 pinfo[q]->memb_vert[k] = this_patch[k].
vert;
1008 this_patch[k].
patch = &(*pinfo[q]);
1010 pinfo[q]->calculate_area(
this);
1011 pinfo[q]->calculate_normal_stats(
this);
1019 qCritical(
"No vertices belong to the patch of vertex %d", nearest_data[p - 1].
nearest);
1024 pinfo[q]->vert = nearest_data[p - 1].
nearest;
1025 this_patch = nearest_data + p - nave;
1026 pinfo[q]->memb_vert.resize(nave);
1027 for (k = 0; k < nave; k++) {
1028 pinfo[q]->memb_vert[k] = this_patch[k].
vert;
1029 this_patch[k].
patch = &(*pinfo[q]);
1031 pinfo[q]->calculate_area(
this);
1032 pinfo[q]->calculate_normal_stats(
this);
1035 qInfo(
" %d/%d [done]\n", q,
nuse);
1048 for (k = 0,
nuse = 0; k <
np; k++)
1056 for (k = 0, p = 0; k <
np; k++)
1072 auto res = std::make_unique<MNESourceSpace>();
1075 res->rr = PointsT::Zero(
np, 3);
1076 res->nn = NormalsT::Zero(
np, 3);
1077 res->inuse = VectorXi::Zero(
np);
1078 res->vertno = VectorXi::Zero(
np);
1082 res->tot_area = 0.0;
1089 res->subject.clear();
1093 res->dist_limit = -1.0;
1095 res->voxel_surf_RAS_t.reset();
1096 res->vol_dims[0] = res->vol_dims[1] = res->vol_dims[2] = 0;
1098 res->MRI_volume.clear();
1099 res->MRI_surf_RAS_RAS_t.reset();
1100 res->MRI_voxel_surf_RAS_t.reset();
1101 res->MRI_vol_dims[0] = res->MRI_vol_dims[1] = res->MRI_vol_dims[2] = 0;
1102 res->interpolator.reset();
1104 res->vol_geom.reset();
1105 res->mgh_tags.reset();
1107 res->cm[0] = res->cm[1] = res->cm[2] = 0.0;
1115 const QString& curv_file)
1123 const QString& curv_file,
1125 bool check_too_many_neighbors)
1130 std::unique_ptr<MNESourceSpace> s;
1131 std::optional<MNEMghTagGroup> tags;
1132 Eigen::VectorXf curvs;
1136 if (read_triangle_file(surf_file,
1142 if (!curv_file.isEmpty()) {
1143 if (read_curvature_file(curv_file, curvs) == -1)
1145 if (curvs.size() != verts.rows()) {
1146 qCritical() <<
"Incorrect number of vertices in the curvature file.";
1151 s = std::make_unique<MNESourceSpace>(0);
1152 s->rr = std::move(verts);
1153 s->itris = std::move(
tris);
1154 s->ntri = s->itris.rows();
1155 s->np = s->rr.rows();
1156 if (curvs.size() > 0) {
1157 s->curv = std::move(curvs);
1159 s->val = Eigen::VectorXf::Zero(s->np);
1161 if (check_too_many_neighbors) {
1162 if (s->add_geometry_info(
true) !=
OK)
1165 if (s->add_geometry_info2(
true) !=
OK)
1168 }
else if (s->nn.rows() == 0) {
1169 if (s->add_vertex_normals() !=
OK)
1172 s->add_triangle_data();
1174 s->inuse = Eigen::VectorXi::Ones(s->np);
1175 s->vertno = Eigen::VectorXi::LinSpaced(s->np, 0, s->np - 1);
1176 s->mgh_tags = std::move(tags);
1177 s->vol_geom = get_volume_geom_from_tag(s->mgh_tags ? &(*s->mgh_tags) :
nullptr);
1184static std::optional<FiffCoordTrans> make_voxel_ras_trans(
const Eigen::Vector3f& r0,
1185 const Eigen::Vector3f& x_ras,
1186 const Eigen::Vector3f& y_ras,
1187 const Eigen::Vector3f& z_ras,
1188 const Eigen::Vector3f& voxel_size)
1190 Eigen::Matrix3f rot;
1191 rot.row(0) = x_ras.transpose() * voxel_size[0];
1192 rot.row(1) = y_ras.transpose() * voxel_size[1];
1193 rot.row(2) = z_ras.transpose() * voxel_size[2];
1203 Eigen::Vector3f minV, maxV,
cm;
1204 int minn[3], maxn[3];
1205 float maxdist,
dist;
1207 std::unique_ptr<MNESourceSpace> sp;
1208 int np, nplane, nrow;
1215 minV = maxV = surf.
rr.row(0).transpose();
1217 for (k = 0; k < surf.
np; k++) {
1218 Eigen::Vector3f node = surf.
rr.row(k).transpose();
1220 minV = minV.cwiseMin(node);
1221 maxV = maxV.cwiseMax(node);
1223 cm /=
static_cast<float>(surf.
np);
1228 for (k = 0; k < surf.
np; k++) {
1229 dist = (surf.
rr.row(k).transpose() -
cm).norm();
1233 qInfo(
"FsSurface CM = (%6.1f %6.1f %6.1f) mm\n",
1234 1000 *
cm[
X], 1000 *
cm[
Y], 1000 *
cm[
Z]);
1235 qInfo(
"FsSurface fits inside a sphere with radius %6.1f mm\n", 1000 * maxdist);
1236 qInfo(
"FsSurface extent:\n"
1237 "\tx = %6.1f ... %6.1f mm\n"
1238 "\ty = %6.1f ... %6.1f mm\n"
1239 "\tz = %6.1f ... %6.1f mm\n",
1240 1000 * minV[
X], 1000 * maxV[
X],
1241 1000 * minV[
Y], 1000 * maxV[
Y],
1242 1000 * minV[
Z], 1000 * maxV[
Z]);
1243 for (c = 0; c < 3; c++) {
1245 maxn[c] = floor(std::fabs(maxV[c]) / grid) + 1;
1247 maxn[c] = -floor(std::fabs(maxV[c]) / grid) - 1;
1249 minn[c] = floor(std::fabs(minV[c]) / grid) + 1;
1251 minn[c] = -floor(std::fabs(minV[c]) / grid) - 1;
1253 qInfo(
"Grid extent:\n"
1254 "\tx = %6.1f ... %6.1f mm\n"
1255 "\ty = %6.1f ... %6.1f mm\n"
1256 "\tz = %6.1f ... %6.1f mm\n",
1257 1000 * (minn[0] * grid), 1000 * (maxn[0] * grid),
1258 1000 * (minn[1] * grid), 1000 * (maxn[1] * grid),
1259 1000 * (minn[2] * grid), 1000 * (maxn[2] * grid));
1264 for (c = 0; c < 3; c++)
1265 np =
np * (maxn[c] - minn[c] + 1);
1266 nplane = (maxn[0] - minn[0] + 1) * (maxn[1] - minn[1] + 1);
1267 nrow = (maxn[0] - minn[0] + 1);
1270 sp->nneighbor_vert = Eigen::VectorXi::Constant(sp->np,
NNEIGHBORS);
1271 sp->neighbor_vert.resize(sp->np);
1272 for (k = 0; k < sp->np; k++) {
1275 sp->nn(k, 0) = sp->nn(k, 1) = 0.0;
1277 sp->neighbor_vert[k] = Eigen::VectorXi::Constant(
NNEIGHBORS, -1);
1280 for (k = 0, z = minn[2]; z <= maxn[2]; z++) {
1281 for (y = minn[1]; y <= maxn[1]; y++) {
1282 for (x = minn[0]; x <= maxn[0]; x++, k++) {
1283 sp->rr(k, 0) = x * grid;
1284 sp->rr(k, 1) = y * grid;
1285 sp->rr(k, 2) = z * grid;
1290 Eigen::VectorXi& neigh = sp->neighbor_vert[k];
1292 neigh[0] = k - nplane;
1296 neigh[2] = k + nrow;
1300 neigh[4] = k - nrow;
1302 neigh[5] = k + nplane;
1309 neigh[6] = k + 1 - nplane;
1311 neigh[7] = k + 1 + nrow - nplane;
1314 neigh[8] = k + nrow - nplane;
1317 neigh[9] = k - 1 + nrow - nplane;
1318 neigh[10] = k - 1 - nplane;
1320 neigh[11] = k - 1 - nrow - nplane;
1323 neigh[12] = k - nrow - nplane;
1325 neigh[13] = k + 1 - nrow - nplane;
1331 if (x < maxn[0] && y < maxn[1])
1332 neigh[14] = k + 1 + nrow;
1335 neigh[15] = k - 1 + nrow;
1337 neigh[16] = k - 1 - nrow;
1340 if (y > minn[1] && x < maxn[0])
1341 neigh[17] = k + 1 - nrow;
1347 neigh[18] = k + 1 + nplane;
1349 neigh[19] = k + 1 + nrow + nplane;
1352 neigh[20] = k + nrow + nplane;
1355 neigh[21] = k - 1 + nrow + nplane;
1356 neigh[22] = k - 1 + nplane;
1358 neigh[23] = k - 1 - nrow + nplane;
1361 neigh[24] = k - nrow + nplane;
1363 neigh[25] = k + 1 - nrow + nplane;
1369 qInfo(
"%d sources before omitting any.\n", sp->nuse);
1373 for (k = 0; k < sp->np; k++) {
1374 dist = (sp->rr.row(k).transpose() -
cm).norm();
1380 qInfo(
"%d sources after omitting infeasible sources.\n", sp->nuse);
1382 std::vector<std::unique_ptr<MNESourceSpace>> sp_vec;
1383 sp_vec.push_back(std::move(sp));
1387 sp = std::move(sp_vec[0]);
1389 qInfo(
"%d sources remaining after excluding the sources outside the surface and less than %6.1f mm inside.\n", sp->nuse, 1000 * mindist);
1392 Eigen::VectorXi used(sp->nuse);
1393 for (k = 0, c = 0; k < sp->np; k++)
1401 qInfo(
"Adjusting the neighborhood info...");
1402 for (k = 0; k < sp->np; k++) {
1403 Eigen::VectorXi& neigh = sp->neighbor_vert[k];
1404 nneigh = sp->nneighbor_vert[k];
1406 for (c = 0; c < nneigh; c++)
1407 if (!sp->inuse[neigh[c]])
1410 for (c = 0; c < nneigh; c++)
1419 Eigen::Vector3f r0(minn[0] * grid, minn[1] * grid, minn[2] * grid);
1420 Eigen::Vector3f
voxel_size(grid, grid, grid);
1421 Eigen::Vector3f x_ras = Eigen::Vector3f::UnitX();
1422 Eigen::Vector3f y_ras = Eigen::Vector3f::UnitY();
1423 Eigen::Vector3f z_ras = Eigen::Vector3f::UnitZ();
1424 int width = (maxn[0] - minn[0] + 1);
1425 int height = (maxn[1] - minn[1] + 1);
1426 int depth = (maxn[2] - minn[2] + 1);
1428 sp->voxel_surf_RAS_t = make_voxel_ras_trans(r0, x_ras, y_ras, z_ras,
voxel_size);
1429 if (!sp->voxel_surf_RAS_t || sp->voxel_surf_RAS_t->isEmpty())
1432 sp->vol_dims[0] = width;
1433 sp->vol_dims[1] = height;
1434 sp->vol_dims[2] = depth;
1435 Eigen::Map<Eigen::Vector3f>(sp->voxel_size) =
voxel_size;
1438 return sp.release();
1451 float mindist,
dist;
1453 int omit, omit_outside;
1455 int nspace =
static_cast<int>(spaces.size());
1458 qCritical(
"Source spaces are in head coordinates and no coordinate transform was provided!");
1464 qInfo(
"Source spaces are in ");
1466 qInfo(
"head coordinates.\n");
1468 qInfo(
"MRI coordinates.\n");
1470 qWarning(
"unknown (%d) coordinates.\n", spaces[0]->
coord_frame);
1471 qInfo(
"Checking that the sources are inside the bounding surface ");
1473 qInfo(
"and at least %6.1f mm away", 1000 * limit);
1474 qInfo(
" (will take a few...)\n");
1477 for (k = 0; k < nspace; k++) {
1478 s = spaces[k].get();
1479 for (p1 = 0; p1 < s->
np; p1++)
1481 r1 = s->
rr.row(p1).transpose();
1488 if (std::fabs(tot_angle - 1.0) > 1e-5) {
1493 *filtered << qSetFieldWidth(10) << qSetRealNumberPrecision(3) << Qt::fixed
1494 << 1000 * r1[
X] <<
" " << 1000 * r1[
Y] <<
" " << 1000 * r1[
Z] <<
"\n"
1495 << qSetFieldWidth(0);
1496 }
else if (limit > 0.0) {
1502 for (p2 = 0; p2 < surf.
np; p2++) {
1503 dist = (surf.
rr.row(p2).transpose() - r1).norm();
1504 if (
dist < mindist) {
1509 if (mindist < limit) {
1514 *filtered << qSetFieldWidth(10) << qSetRealNumberPrecision(3) << Qt::fixed
1515 << 1000 * r1[
X] <<
" " << 1000 * r1[
Y] <<
" " << 1000 * r1[
Z] <<
"\n"
1516 << qSetFieldWidth(0);
1522 if (omit_outside > 0)
1523 qInfo(
"%d source space points omitted because they are outside the inner skull surface.\n",
1526 qInfo(
"%d source space points omitted because of the %6.1f-mm distance limit.\n",
1527 omit, 1000 * limit);
1528 qInfo(
"Thank you for waiting.\n");
1539 int omit, omit_outside;
1541 float mindist,
dist;
1544 QSharedPointer<MNESurface> surf = a->
surf.toStrongRef();
1553 for (p1 = 0; p1 < a->
s->
np; p1++) {
1555 r1 = a->
s->
rr.row(p1).transpose();
1563 tot_angle = surf->sum_solids(r1) / (4 *
M_PI);
1564 if (std::fabs(tot_angle - 1.0) > 1e-5) {
1569 *a->
filtered << qSetFieldWidth(10) << qSetRealNumberPrecision(3) << Qt::fixed
1570 << 1000 * r1[
X] <<
" " << 1000 * r1[
Y] <<
" " << 1000 * r1[
Z] <<
"\n"
1571 << qSetFieldWidth(0);
1572 }
else if (a->
limit > 0.0) {
1578 for (p2 = 0; p2 < surf->np; p2++) {
1579 dist = (surf->rr.row(p2).transpose() - r1).norm();
1580 if (
dist < mindist) {
1585 if (mindist < a->limit) {
1590 *a->
filtered << qSetFieldWidth(10) << qSetRealNumberPrecision(3) << Qt::fixed
1591 << 1000 * r1[
X] <<
" " << 1000 * r1[
Y] <<
" " << 1000 * r1[
Z] <<
"\n"
1592 << qSetFieldWidth(0);
1598 if (omit_outside > 0)
1599 qInfo(
"%d source space points omitted because they are outside the inner skull surface.\n",
1602 qInfo(
"%d source space points omitted because of the %6.1f-mm distance limit.\n",
1603 omit, 1000 * a->
limit);
1615 QSharedPointer<MNESurface> surf;
1617 int nproc = QThread::idealThreadCount();
1618 int nspace =
static_cast<int>(spaces.size());
1620 if (bemfile.isEmpty())
1626 qCritical(
"BEM model does not have the inner skull triangulation!");
1629 surf.reset(rawSurf.release());
1634 qInfo(
"Source spaces are in ");
1636 qInfo(
"head coordinates.\n");
1638 qInfo(
"MRI coordinates.\n");
1640 qWarning(
"unknown (%d) coordinates.\n", spaces[0]->
coord_frame);
1641 qInfo(
"Checking that the sources are inside the inner skull ");
1643 qInfo(
"and at least %6.1f mm away", 1000 * limit);
1644 qInfo(
" (will take a few...)\n");
1645 if (nproc < 2 || nspace == 1 || !use_threads) {
1649 for (k = 0; k < nspace; k++) {
1650 auto a_ptr = std::make_unique<FilterThreadArg>();
1651 a_ptr->s = spaces[k].get();
1652 a_ptr->mri_head_t = std::make_unique<FiffCoordTrans>(mri_head_t);
1654 a_ptr->limit = limit;
1655 a_ptr->filtered = filtered;
1657 spaces[k]->rearrange_source_space();
1663 QList<FilterThreadArg*> args;
1665 std::vector<std::unique_ptr<FilterThreadArg>> arg_owners;
1666 for (k = 0; k < nspace; k++) {
1667 auto a_ptr = std::make_unique<FilterThreadArg>();
1668 a_ptr->s = spaces[k].get();
1669 a_ptr->mri_head_t = std::make_unique<FiffCoordTrans>(mri_head_t);
1671 a_ptr->limit = limit;
1672 a_ptr->filtered = filtered;
1673 args.append(a_ptr.get());
1674 arg_owners.push_back(std::move(a_ptr));
1681 for (k = 0; k < nspace; k++) {
1682 spaces[k]->rearrange_source_space();
1685 qInfo(
"Thank you for waiting.\n\n");
1700 std::vector<std::unique_ptr<MNESourceSpace>> local_spaces;
1701 std::unique_ptr<MNESourceSpace> new_space;
1702 QList<FiffDirNode::SPtr> sources;
1708 if (!stream->open()) {
1714 if (sources.size() == 0) {
1715 qCritical(
"No source spaces available here");
1719 for (j = 0; j < sources.size(); j++) {
1729 new_space->np = *t_pTag->toInt();
1730 if (new_space->np == 0) {
1731 qCritical(
"No points in this source space");
1739 MatrixXf tmp_rr = t_pTag->toFloatMatrix().transpose();
1740 new_space->rr = tmp_rr;
1745 MatrixXf tmp_nn = t_pTag->toFloatMatrix().transpose();
1746 new_space->nn = tmp_nn;
1750 new_space->coord_frame = *t_pTag->toInt();
1753 new_space->id = *t_pTag->toInt();
1756 new_space->subject = t_pTag->toString();
1759 new_space->type = *t_pTag->toInt();
1763 ntri = *t_pTag->toInt();
1765 ntri = *t_pTag->toInt();
1775 MatrixXi tmp_itris = t_pTag->toIntMatrix().transpose();
1776 tmp_itris.array() -= 1;
1777 new_space->itris = tmp_itris;
1778 new_space->ntri =
ntri;
1785 new_space->nuse = new_space->np;
1786 new_space->inuse = Eigen::VectorXi::Ones(new_space->nuse);
1787 new_space->vertno = Eigen::VectorXi::LinSpaced(new_space->nuse, 0, new_space->nuse - 1);
1793 new_space->nuse = 0;
1794 new_space->inuse = Eigen::VectorXi::Zero(new_space->np);
1795 new_space->vertno.resize(0);
1798 new_space->nuse = *t_pTag->toInt();
1805 Eigen::Map<Eigen::VectorXi> inuseMap(t_pTag->toInt(), new_space->np);
1806 new_space->inuse = inuseMap;
1808 if (new_space->nuse > 0) {
1809 new_space->vertno = Eigen::VectorXi::Zero(new_space->nuse);
1810 for (k = 0, p = 0; k < new_space->np; k++) {
1811 if (new_space->inuse[k])
1812 new_space->vertno[p++] = k;
1815 new_space->vertno.resize(0);
1822 ntri = *t_pTag->toInt();
1830 MatrixXi tmp_itris = t_pTag->toIntMatrix().transpose();
1831 tmp_itris.array() -= 1;
1832 new_space->use_itris = tmp_itris;
1833 new_space->nuse_tri =
ntri;
1839 Eigen::Map<Eigen::VectorXi> nearestMap(t_pTag->toInt(), new_space->np);
1840 new_space->nearest.resize(new_space->np);
1841 for (k = 0; k < new_space->np; k++) {
1842 new_space->nearest[k].vert = k;
1843 new_space->nearest[k].nearest = nearestMap[k];
1844 new_space->nearest[k].patch =
nullptr;
1851 Eigen::Map<const Eigen::VectorXf> nearestDistMap(t_pTag->toFloat(), new_space->np);
1852 for (k = 0; k < new_space->np; k++) {
1853 new_space->nearest[k].dist = nearestDistMap[k];
1860 new_space->dist_limit = *t_pTag->toFloat();
1868 auto dist_full = dist_lower->mne_add_upper_triangle_rcs();
1873 new_space->dist = std::move(*dist_full);
1875 new_space->dist_limit = 0.0;
1882 int ntot, nvert, ntot_count, nneigh;
1884 Eigen::VectorXi neighborsVec;
1885 Eigen::VectorXi nneighborsVec;
1888 ntot =
static_cast<int>(t_pTag->size() /
sizeof(
fiff_int_t));
1889 neighborsVec = Eigen::Map<Eigen::VectorXi>(t_pTag->toInt(), ntot);
1892 nvert =
static_cast<int>(t_pTag->size() /
sizeof(
fiff_int_t));
1893 nneighborsVec = Eigen::Map<Eigen::VectorXi>(t_pTag->toInt(), nvert);
1895 if (neighborsVec.size() > 0 && nneighborsVec.size() > 0) {
1896 if (nvert != new_space->np) {
1897 qCritical(
"Inconsistent neighborhood data in file.");
1901 for (k = 0, ntot_count = 0; k < nvert; k++)
1902 ntot_count += nneighborsVec[k];
1903 if (ntot_count != ntot) {
1904 qCritical(
"Inconsistent neighborhood data in file.");
1908 new_space->nneighbor_vert = Eigen::VectorXi::Zero(nvert);
1909 new_space->neighbor_vert.resize(nvert);
1910 for (k = 0, q = 0; k < nvert; k++) {
1911 new_space->nneighbor_vert[k] = nneigh = nneighborsVec[k];
1912 new_space->neighbor_vert[k] = Eigen::VectorXi(nneigh);
1913 for (p = 0; p < nneigh; p++, q++)
1914 new_space->neighbor_vert[k][p] = neighborsVec[q];
1922 Eigen::Map<Eigen::Vector3i> volDimsMap(t_pTag->toInt());
1923 Eigen::Map<Eigen::Vector3i>(new_space->vol_dims) = volDimsMap;
1928 if (mris.size() == 0) {
1931 new_space->MRI_volume = t_pTag->toString();
1938 new_space->MRI_volume = t_pTag->toString();
1947 new_space->MRI_vol_dims[0] = *t_pTag->toInt();
1950 new_space->MRI_vol_dims[1] = *t_pTag->toInt();
1953 new_space->MRI_vol_dims[2] = *t_pTag->toInt();
1958 new_space->add_triangle_data();
1959 local_spaces.push_back(std::move(new_space));
1963 spaces = std::move(local_spaces);
1977 int nspace =
static_cast<int>(spaces.size());
1979 for (k = 0; k < nspace; k++) {
1980 s = spaces[k].get();
1992 qCritical(
"Could not transform a source space because of transformation incompatibility.");
1996 qCritical(
"Could not transform a source space because of missing coordinate transformation.");
2006#define LH_LABEL_TAG "-lh.label"
2007#define RH_LABEL_TAG "-rh.label"
2017 Eigen::VectorXi lh_inuse;
2018 Eigen::VectorXi rh_inuse;
2019 Eigen::VectorXi sel;
2020 Eigen::VectorXi*
inuse =
nullptr;
2022 int nspace =
static_cast<int>(spaces.size());
2027 for (k = 0; k < nspace; k++) {
2029 lh = spaces[k].get();
2030 lh_inuse = Eigen::VectorXi::Zero(lh->
np);
2032 rh = spaces[k].get();
2033 rh_inuse = Eigen::VectorXi::Zero(rh->
np);
2039 for (k = 0; k < nlabel; k++) {
2050 qWarning(
"\tWarning: cannot assign label file %s to a hemisphere.\n", labels[k].toUtf8().constData());
2056 for (p = 0; p < sel.size(); p++) {
2057 if (sel[p] >= 0 && sel[p] < sp->
np)
2058 (*inuse)[sel[p]] = sp->
inuse[sel[p]];
2060 qWarning(
"vertex number out of range in %s (%d vs %d)\n",
2061 labels[k].toUtf8().constData(), sel[p], sp->
np);
2063 qInfo(
"Processed label file %s\n", labels[k].toUtf8().constData());
2086 QFile inFile(label);
2087 if (!inFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
2088 qCritical() << label;
2094 qCritical(
"FsLabel file does not start correctly.");
2101 while (inFile.getChar(&c) && c !=
'\n')
2104 QTextStream in(&inFile);
2106 if (in.status() != QTextStream::Ok) {
2107 qCritical(
"Could not read the number of labelled points.");
2112 for (k = 0; k < nlabel; k++) {
2113 in >> p >> fdum >> fdum >> fdum >> fdum;
2114 if (in.status() != QTextStream::Ok) {
2115 qCritical(
"Could not read label point # %d", k + 1);
2128int MNESourceSpace::writeVolumeInfo(
FiffStream::SPtr& stream,
bool selected_only)
const
2139 Eigen::VectorXi nneighbors;
2140 Eigen::VectorXi neighbors;
2142 if (selected_only) {
2143 Eigen::VectorXi inuse_map = Eigen::VectorXi::Constant(
np, -1);
2144 for (k = 0, p = 0, ntot = 0; k <
np; k++) {
2150 nneighbors.resize(
nuse);
2151 neighbors.resize(ntot);
2152 for (k = 0, nvert = 0, ntot = 0; k <
np; k++) {
2156 nneighbors[nvert++] = nneigh;
2157 for (p = 0; p < nneigh; p++)
2158 neighbors[ntot++] = neigh[p] < 0 ? -1 : inuse_map[neigh[p]];
2162 for (k = 0, ntot = 0; k <
np; k++)
2164 nneighbors.resize(
np);
2165 neighbors.resize(ntot);
2167 for (k = 0, ntot = 0; k <
np; k++) {
2170 nneighbors[k] = nneigh;
2171 for (p = 0; p < nneigh; p++)
2172 neighbors[ntot++] = neigh[p];
2179 if (!selected_only) {
2203 qCritical(
"Cannot write the interpolator for selection yet");
2217 qCritical(
"No points in the source space being saved");
2234 if (selected_only) {
2236 qCritical(
"No vertices in use. Cannot write active-only vertices from this source space");
2240 Eigen::MatrixXf sel(
nuse, 3);
2243 for (p = 0, pp = 0; p <
np; p++) {
2245 sel.row(pp) =
rr.row(p);
2251 for (p = 0, pp = 0; p <
np; p++) {
2253 sel.row(pp) =
nn.row(p);
2270 Eigen::MatrixXi file_tris =
itris.array() + 1;
2276 Eigen::MatrixXi file_use_tris =
use_itris.array() + 1;
2281 Eigen::VectorXi nearest_v(
np);
2282 Eigen::VectorXf nearest_dist_v(
np);
2284 std::sort(
const_cast<std::vector<MNENearest>&
>(
nearest).begin(),
2285 const_cast<std::vector<MNENearest>&
>(
nearest).end(),
2287 for (p = 0; p <
np; p++) {
2288 nearest_v[p] =
nearest[p].nearest;
2289 nearest_dist_v[p] =
nearest[p].dist;
2296 if (!
dist.is_empty()) {
2297 auto m =
dist.pickLowerTriangleRcs();
2305 if (writeVolumeInfo(stream, selected_only) !=
OK)
2318static Eigen::MatrixX3f generateIcoVertices(
int grade)
2322 const float z = 1.0f / std::sqrt(5.0f);
2323 const float r = 2.0f * z;
2324 std::vector<Eigen::Vector3f> verts = {{0, 0, 1}};
2325 for (
int k = 0; k < 5; ++k)
2326 verts.emplace_back(r * std::cos(0.4f * EIGEN_PI * k), r * std::sin(0.4f * EIGEN_PI * k), z);
2327 for (
int k = 0; k < 5; ++k)
2328 verts.emplace_back(r * std::cos(0.4f * EIGEN_PI * k - 0.2f * EIGEN_PI), r * std::sin(0.4f * EIGEN_PI * k - 0.2f * EIGEN_PI), -z);
2329 verts.emplace_back(0, 0, -1);
2331 std::vector<std::array<int, 3>> faces = {
2332 {0, 3, 4}, {0, 4, 5}, {0, 5, 1}, {0, 1, 2}, {0, 2, 3}, {3, 2, 8}, {3, 8, 9}, {3, 9, 4}, {4, 9, 10}, {4, 10, 5}, {5, 10, 6}, {5, 6, 1}, {1, 6, 7}, {1, 7, 2}, {2, 7, 8}, {8, 11, 9}, {9, 11, 10}, {10, 11, 6}, {6, 11, 7}, {7, 11, 8}};
2335 for (
int g = 0; g < grade; ++g) {
2336 std::map<std::pair<int, int>,
int> midpointCache;
2337 std::vector<std::array<int, 3>> newFaces;
2339 auto getMidpoint = [&](
int i1,
int i2) ->
int {
2340 auto key = std::make_pair(std::min(i1, i2), std::max(i1, i2));
2341 auto it = midpointCache.find(key);
2342 if (it != midpointCache.end())
2344 Eigen::Vector3f mid = (verts[i1] + verts[i2]).normalized();
2345 int idx =
static_cast<int>(verts.size());
2346 verts.push_back(mid);
2347 midpointCache[key] = idx;
2351 for (
const auto& f : faces) {
2352 int a = getMidpoint(f[0], f[1]);
2353 int b = getMidpoint(f[1], f[2]);
2354 int c = getMidpoint(f[2], f[0]);
2355 newFaces.push_back({f[0], a, c});
2356 newFaces.push_back({f[1], b, a});
2357 newFaces.push_back({f[2], c, b});
2358 newFaces.push_back({a, b, c});
2363 Eigen::MatrixX3f result(
static_cast<int>(verts.size()), 3);
2364 for (
int i = 0; i < static_cast<int>(verts.size()); ++i)
2365 result.row(i) = verts[i];
2375 if (hemi.
np <= 0 || hemi.
rr.rows() == 0) {
2376 qWarning(
"MNESourceSpace::icoDownsample - Hemisphere has no vertices.");
2381 Eigen::MatrixX3f icoVerts = generateIcoVertices(icoGrade);
2384 Eigen::MatrixX3f hemiNorm(hemi.
np, 3);
2385 for (
int i = 0; i < hemi.
np; ++i) {
2386 Eigen::Vector3f v = hemi.
rr.row(i);
2387 float len = v.norm();
2389 hemiNorm.row(i) = (v / len).transpose();
2391 hemiNorm.row(i) = v.transpose();
2395 result.
inuse = Eigen::VectorXi::Zero(result.
np);
2398 for (
int i = 0; i < icoVerts.rows(); ++i) {
2399 Eigen::Vector3f icoV = icoVerts.row(i);
2400 float bestDist = std::numeric_limits<float>::max();
2402 for (
int j = 0; j < hemi.
np; ++j) {
2403 float d = (hemiNorm.row(j).transpose() - icoV).squaredNorm();
2410 result.
inuse[bestIdx] = 1;
2417 for (
int i = 0; i < result.
np; ++i) {
2418 if (result.
inuse[i])
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
#define FIFFV_MNE_SURF_RIGHT_HEMI
#define FIFF_MNE_SOURCE_SPACE_ID
#define FIFF_MNE_SOURCE_SPACE_DIST_LIMIT
#define FIFF_MNE_SOURCE_SPACE_VOXEL_DIMS
#define FIFF_MNE_COORD_FRAME
#define FIFF_MNE_SOURCE_SPACE_TYPE
#define FIFFV_MNE_SURF_UNKNOWN
#define FIFF_MNE_SOURCE_SPACE_SELECTION
#define FIFFV_MNE_SURF_LEFT_HEMI
#define FIFF_MNE_SOURCE_SPACE_USE_TRIANGLES
#define FIFF_MNE_SOURCE_SPACE_NUSE_TRI
#define FIFF_MNE_SOURCE_SPACE_INTERPOLATOR
#define FIFF_MNE_SOURCE_SPACE_NORMALS
#define FIFFV_MNE_COORD_MRI_VOXEL
#define FIFF_MNE_SOURCE_SPACE_NEAREST_DIST
#define FIFF_MNE_SOURCE_SPACE_DIST
#define FIFF_MNE_SOURCE_SPACE_POINTS
#define FIFFV_MNE_SPACE_SURFACE
#define FIFF_MNE_SOURCE_SPACE_NTRI
#define FIFF_MNE_SOURCE_SPACE_MRI_FILE
#define FIFFB_MNE_SOURCE_SPACE
#define FIFF_MNE_SOURCE_SPACE_NPOINTS
#define FIFFV_MNE_SPACE_VOLUME
#define FIFF_MNE_SOURCE_SPACE_TRIANGLES
#define FIFFV_MNE_SPACE_UNKNOWN
#define FIFF_MNE_SOURCE_SPACE_NUSE
#define FIFFV_MNE_COORD_RAS
#define FIFF_MNE_FILE_NAME
#define FIFF_MNE_SOURCE_SPACE_NEAREST
#define FIFFB_MNE_PARENT_MRI_FILE
Endianness swap helpers for the FIFF binary tag I/O layer (FIFF is always written big-endian on disk)...
FIFF tag: the 16-byte tag header (kind, type, size, next) plus its decoded payload.
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
4x4 affine FIFF coordinate transform (FIFF_COORD_TRANS) annotated with source/destination coordinate-...
#define FIFF_BEM_SURF_TRIANGLES
#define FIFF_BEM_SURF_NTRI
#define FIFFV_BEM_SURF_ID_BRAIN
return FiffCoordTrans(from_frame, to_frame, R, moveVec)
FIFF sparse matrix: column / row-compressed sparse storage backed by Eigen::SparseMatrix.
#define MNE_SOURCE_SPACE_VOLUME
Per-hemisphere cortical surface bundle with decimation, patch info and rendering buffers.
Patch information (cluster of cortex vertices around each decimated source) used by orientation prior...
Lightweight triangulated surface (vertices, triangles, normals) used by surface-based routines.
Single-hemisphere source space (cortical surface or volume grid) loaded from FIFF.
#define QUAD_FILE_MAGIC_NUMBER
#define NEW_QUAD_FILE_MAGIC_NUMBER
#define FIFFV_MNE_COORD_SURFACE_RAS
#define FIFF_MNE_SOURCE_SPACE_NEIGHBORS
#define FIFF_MNE_SOURCE_SPACE_NNEIGHBORS
#define TAG_OLD_SURF_GEOM
#define TRIANGLE_FILE_MAGIC_NUMBER
constexpr int CURVATURE_FILE_MAGIC_NUMBER
constexpr int TAG_USEREALRAS
constexpr int TAG_OLD_MGH_XFORM
constexpr int TAG_OLD_USEREALRAS
constexpr int TAG_OLD_COLORTABLE
Per-source-space-vertex nearest-cortex-vertex mapping.
Ordered group of MNELIB::MNEMghTag entries appended to an MGH/MGZ file.
Argument record passed to a background raw-data filter worker.
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
float swap_float(float source)
qint32 swap_int(qint32 source)
qint64 swap_long(qint64 source)
qint16 swap_short(qint16 source)
Labelled 4x4 FIFF affine: source frame, destination frame, rotation, translation and cached inverse.
static FiffCoordTrans readTransformFromNode(FiffStream::SPtr &stream, const FiffDirNode::SPtr &node, int from, int to)
Eigen::MatrixX3f apply_inverse_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
FiffCoordTrans inverted() const
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
QSharedPointer< FiffDirNode > SPtr
Sparse FIFF matrix: CCS or RCS storage with the value / index / pointer triple as written by FiffStre...
static FiffSparseMatrix::UPtr fiff_get_float_sparse_matrix(const FIFFLIB::FiffTag::UPtr &tag)
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
QSharedPointer< FiffStream > SPtr
std::unique_ptr< FiffTag > UPtr
Thread-local arguments for parallel raw data filtering (channel range, filter kernel,...
QWeakPointer< MNESurface > surf
std::unique_ptr< FIFFLIB::FiffCoordTrans > mri_head_t
Hemisphere provides geometry information.
Collection of MNEMghTag entries from a FreeSurfer MGH/MGZ file footer.
std::vector< std::unique_ptr< MNEMghTag > > tags
This is used in the patch definitions.
Patch information for a single source space point including vertex members and area.
~MNESourceSpace() override
virtual MNESourceSpace::SPtr clone() const
static std::unique_ptr< MNESourceSpace > load_surface(const QString &surf_file, const QString &curv_file)
void rearrange_source_space()
qint32 find_source_space_hemi() const
std::shared_ptr< MNESourceSpace > SPtr
static int restrict_sources_to_labels(std::vector< std::unique_ptr< MNESourceSpace > > &spaces, const QStringList &labels, int nlabel)
static int filter_source_spaces(const MNESurface &surf, float limit, const FIFFLIB::FiffCoordTrans &mri_head_t, std::vector< std::unique_ptr< MNESourceSpace > > &spaces, QTextStream *filtered)
bool is_left_hemi() const
static int read_source_spaces(const QString &name, std::vector< std::unique_ptr< MNESourceSpace > > &spaces)
static std::unique_ptr< MNESourceSpace > create_source_space(int np)
static std::unique_ptr< MNESourceSpace > load_surface_geom(const QString &surf_file, const QString &curv_file, bool add_geometry, bool check_too_many_neighbors)
static MNEHemisphere icoDownsample(const MNEHemisphere &hemi, int icoGrade)
static void filter_source_space(FilterThreadArg *arg)
int writeToStream(FIFFLIB::FiffStream::SPtr &stream, bool selected_only) const
static MNESourceSpace * make_volume_source_space(const MNESurface &surf, float grid, float exclude, float mindist)
int transform_source_space(const FIFFLIB::FiffCoordTrans &t)
static int read_label(const QString &label, Eigen::VectorXi &sel)
static int transform_source_spaces_to(int coord_frame, const FIFFLIB::FiffCoordTrans &t, std::vector< std::unique_ptr< MNESourceSpace > > &spaces)
void update_inuse(Eigen::VectorXi new_inuse)
void enable_all_sources()
Lightweight triangulated surface (vertices, triangles, normals).
double sum_solids(const Eigen::Vector3f &from) const
static std::unique_ptr< MNESurface > read_bem_surface(const QString &name, int which, bool add_geometry)
std::vector< Eigen::VectorXi > neighbor_tri
std::vector< Eigen::VectorXi > neighbor_vert
std::optional< FIFFLIB::FiffCoordTrans > MRI_surf_RAS_RAS_t
int add_geometry_info(bool do_normals, bool check_too_many_neighbors)
FIFFLIB::FiffSparseMatrix dist
std::vector< MNENearest > nearest
Eigen::VectorXi nneighbor_vert
std::optional< FIFFLIB::FiffSparseMatrix > interpolator
Eigen::Matrix< int, Eigen::Dynamic, 3, Eigen::RowMajor > TrianglesT
std::optional< MNEVolGeom > vol_geom
std::vector< std::optional< MNEPatchInfo > > patches
std::optional< FIFFLIB::FiffCoordTrans > MRI_voxel_surf_RAS_t
std::vector< MNETriangle > tris
std::optional< MNEMghTagGroup > mgh_tags
std::optional< FIFFLIB::FiffCoordTrans > voxel_surf_RAS_t
Eigen::Matrix< float, Eigen::Dynamic, 3, Eigen::RowMajor > PointsT
MRI data volume geometry information like FreeSurfer keeps it.