59#include <QtConcurrent>
65#define _USE_MATH_DEFINES
107int read_int3(QFile &in,
int &ival)
114 if (in.read(
reinterpret_cast<char*
>(&s), 3) != 3) {
115 qCritical(
"read_int3 could not read data");
119 ival = ((s >> 8) & 0xffffff);
123int read_int(QFile &in, qint32 &ival)
129 if (in.read(
reinterpret_cast<char*
>(&s),
sizeof(qint32)) !=
static_cast<qint64
>(
sizeof(qint32))) {
130 qCritical(
"read_int could not read data");
137int read_int2(QFile &in,
int &ival)
143 if (in.read(
reinterpret_cast<char*
>(&s),
sizeof(
short)) !=
static_cast<qint64
>(
sizeof(
short))) {
144 qCritical(
"read_int2 could not read data");
151int read_float(QFile &in,
float &fval)
157 if (in.read(
reinterpret_cast<char*
>(&f),
sizeof(
float)) !=
static_cast<qint64
>(
sizeof(
float))) {
158 qCritical(
"read_float could not read data");
165int read_long(QFile &in,
long long &lval)
171 if (in.read(
reinterpret_cast<char*
>(&s),
sizeof(
long long)) !=
static_cast<qint64
>(
sizeof(
long long))) {
172 qCritical(
"read_long could not read data");
183int check_vertex(
int no,
int maxno)
185 if (no < 0 || no > maxno-1) {
186 qCritical(
"Illegal vertex number %d (max %d).",no,maxno);
204std::unique_ptr<MNEVolGeom> read_vol_geom(QFile &fp)
216 auto vg = std::make_unique<MNEVolGeom>();
218 while (!fp.atEnd() && counter < 8)
220 QByteArray lineData = fp.readLine(256);
221 if (lineData.isEmpty())
223 const char *line = lineData.constData();
224 if (strlen(line) == 0)
226 sscanf(line,
"%s %s %*s", param, eq);
227 if (!strcmp(param,
"valid")) {
228 sscanf(line,
"%s %s %d", param, eq, &vg->valid);
232 else if (!strcmp(param,
"filename")) {
233 if (sscanf(line,
"%s %s %s", param, eq, buf) >= 3)
234 vg->filename = QString::fromUtf8(buf);
237 else if (!strcmp(param,
"volume")) {
238 sscanf(line,
"%s %s %d %d %d",
239 param, eq, &vg->width, &vg->height, &vg->depth);
242 else if (!strcmp(param,
"voxelsize")) {
243 sscanf(line,
"%s %s %f %f %f",
244 param, eq, &vg->xsize, &vg->ysize, &vg->zsize);
248 vg->xsize = vg->xsize/1000.0;
249 vg->ysize = vg->ysize/1000.0;
250 vg->zsize = vg->zsize/1000.0;
253 else if (!strcmp(param,
"xras")) {
254 sscanf(line,
"%s %s %f %f %f",
255 param, eq, vg->x_ras, vg->x_ras+1, vg->x_ras+2);
258 else if (!strcmp(param,
"yras")) {
259 sscanf(line,
"%s %s %f %f %f",
260 param, eq, vg->y_ras, vg->y_ras+1, vg->y_ras+2);
263 else if (!strcmp(param,
"zras")) {
264 sscanf(line,
"%s %s %f %f %f",
265 param, eq, vg->z_ras, vg->z_ras+1, vg->z_ras+2);
268 else if (!strcmp(param,
"cras")) {
269 sscanf(line,
"%s %s %f %f %f",
270 param, eq, vg->c_ras, vg->c_ras+1, vg->c_ras+2);
271 vg->c_ras[0] = vg->c_ras[0]/1000.0;
272 vg->c_ras[1] = vg->c_ras[1]/1000.0;
273 vg->c_ras[2] = vg->c_ras[2]/1000.0;
285 vg = std::make_unique<MNEVolGeom>();
294int read_tag_data(QFile &fp,
int tag,
long long nbytes,
unsigned char *&val,
long long &nbytesp)
299 size_t snbytes = nbytes;
303 auto dum = std::make_unique<unsigned char[]>(nbytes+1);
304 if (fp.read(
reinterpret_cast<char*
>(dum.get()), nbytes) !=
static_cast<qint64
>(snbytes)) {
305 qCritical(
"Failed to read %d bytes of tag data",
static_cast<int>(nbytes));
314 auto g = read_vol_geom(fp);
326 int width, height, depth;
327 float xsize, ysize, zsize;
328 float x_ras[3], y_ras[3], z_ras[3];
331 QByteArray fn = g->
filename.toUtf8();
332 size_t totalSize =
sizeof(VolGeomPOD) + fn.size() + 1;
333 auto buf = std::make_unique<unsigned char[]>(totalSize);
335 pod.valid = g->
valid;
338 std::memcpy(pod.x_ras, g->
x_ras, 3 *
sizeof(
float));
339 std::memcpy(pod.y_ras, g->
y_ras, 3 *
sizeof(
float));
340 std::memcpy(pod.z_ras, g->
z_ras, 3 *
sizeof(
float));
341 std::memcpy(pod.c_ras, g->
c_ras, 3 *
sizeof(
float));
342 std::memcpy(buf.get(), &pod,
sizeof(VolGeomPOD));
343 std::memcpy(buf.get() +
sizeof(VolGeomPOD), fn.constData(), fn.size() + 1);
345 nbytesp =
static_cast<long long>(totalSize);
348 auto vi = std::make_unique<int[]>(1);
349 if (read_int(fp, vi[0]) ==
FAIL)
351 val =
reinterpret_cast<unsigned char *
>(vi.release());
352 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)
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,val_pervert;
462 if (!fp.open(QIODevice::ReadOnly)) {
463 qCritical() << fname;
464 curv.resize(0);
return FAIL;
466 if (read_int3(fp,magic) != 0) {
467 qCritical() <<
"Bad magic in" << fname;
468 curv.resize(0);
return FAIL;
474 if (read_int(fp,ncurv) != 0) {
475 curv.resize(0);
return FAIL;
477 if (read_int(fp,nface) != 0) {
478 curv.resize(0);
return FAIL;
481 qInfo(
"nvert = %d nface = %d\n",ncurv,nface);
483 if (read_int(fp,val_pervert) != 0) {
484 curv.resize(0);
return FAIL;
486 if (val_pervert != 1) {
487 qCritical(
"Values per vertex not equal to one.");
488 curv.resize(0);
return FAIL;
494 curvmin = curvmax = 0.0;
495 for (k = 0; k < ncurv; k++) {
496 if (read_float(fp,fval) != 0) {
497 curv.resize(0);
return FAIL;
500 if (curv[k] > curvmax)
502 if (curv[k] < curvmin)
511 if (read_int3(fp,nface) != 0) {
512 curv.resize(0);
return FAIL;
515 qInfo(
"nvert = %d nface = %d\n",ncurv,nface);
521 curvmin = curvmax = 0.0;
522 for (k = 0; k < ncurv; k++) {
523 if (read_int2(fp,val) != 0) {
524 curv.resize(0);
return FAIL;
526 curv[k] =
static_cast<float>(val)/100.0;
527 if (curv[k] > curvmax)
529 if (curv[k] < curvmin)
535 qInfo(
"Curvature range: %f...%f\n",curvmin,curvmax);
544int read_triangle_file(
const QString& fname,
546 TrianglesT& triangles,
547 std::optional<MNEMghTagGroup>* tagsp)
556 qint32 nvert,ntri,nquad;
564 if (!fp.open(QIODevice::ReadOnly)) {
565 qCritical() << fname;
568 if (read_int3(fp,magic) != 0) {
569 qCritical() <<
"Bad magic in" << fname;
575 qCritical() <<
"Bad magic in" << fname;
582 qInfo(
"Triangle file : ");
583 for (fp.getChar(&c); c !=
'\n'; fp.getChar(&c)) {
585 qCritical()<<
"Bad triangle file.";
594 if (read_int(fp,nvert) != 0)
596 if (read_int(fp,ntri) != 0)
598 qInfo(
" nvert = %d ntri = %d\n",nvert,ntri);
599 vert.resize(nvert, 3);
604 for (k = 0; k < nvert; k++) {
605 if (read_float(fp,vert(k,0)) != 0)
607 if (read_float(fp,vert(k,1)) != 0)
609 if (read_float(fp,vert(k,2)) != 0)
615 for (k = 0; k < ntri; k++) {
616 if (read_int(fp,tri(k,0)) != 0)
618 if (check_vertex(tri(k,0),nvert) !=
OK)
620 if (read_int(fp,tri(k,1)) != 0)
622 if (check_vertex(tri(k,1),nvert) !=
OK)
624 if (read_int(fp,tri(k,2)) != 0)
626 if (check_vertex(tri(k,2),nvert) !=
OK)
632 if (read_int3(fp,nvert) != 0)
634 if (read_int3(fp,nquad) != 0)
636 qInfo(
"%s file : nvert = %d nquad = %d\n",
639 vert.resize(nvert, 3);
641 for (k = 0; k < nvert; k++) {
642 if (read_int2(fp,val) != 0)
644 vert(k,0) = val/100.0;
645 if (read_int2(fp,val) != 0)
647 vert(k,1) = val/100.0;
648 if (read_int2(fp,val) != 0)
650 vert(k,2) = val/100.0;
654 for (k = 0; k < nvert; k++) {
655 if (read_float(fp,vert(k,0)) != 0)
657 if (read_float(fp,vert(k,1)) != 0)
659 if (read_float(fp,vert(k,2)) != 0)
665 for (k = 0, ntri = 0; k < nquad; k++) {
666 for (p = 0; p < 4; p++) {
667 if (read_int3(fp,quad[p]) != 0)
675#define EVEN(n) ((((n) / 2) * 2) == n)
677#define WHICH_FACE_SPLIT(vno0, vno1) \
678 (1*nearbyint(sqrt(1.9*vno0) + sqrt(3.5*vno1)))
680 which = WHICH_FACE_SPLIT(quad[0], quad[1]) ;
688 tri(ntri,0) = quad[0];
689 tri(ntri,1) = quad[1];
690 tri(ntri,2) = quad[3];
693 tri(ntri,0) = quad[2];
694 tri(ntri,1) = quad[3];
695 tri(ntri,2) = quad[1];
699 tri(ntri,0) = quad[0];
700 tri(ntri,1) = quad[1];
701 tri(ntri,2) = quad[2];
704 tri(ntri,0) = quad[0];
705 tri(ntri,1) = quad[2];
706 tri(ntri,2) = quad[3];
715 std::optional<MNEMghTagGroup> tags;
716 if (read_mgh_tags(fp, tags) ==
FAIL) {
719 *tagsp = std::move(tags);
725 vertices = std::move(vert);
726 triangles = std::move(tri);
734std::optional<MNEVolGeom> get_volume_geom_from_tag(
const MNEMghTagGroup *tagsp)
741 int width, height, depth;
742 float xsize, ysize, zsize;
743 float x_ras[3], y_ras[3], z_ras[3];
747 for (
const auto &t : tagsp->
tags) {
749 if (t->len <
static_cast<long long>(
sizeof(VolGeomPOD)))
752 const unsigned char *d =
reinterpret_cast<const unsigned char *
>(t->data.constData());
754 std::memcpy(&pod, d,
sizeof(VolGeomPOD));
757 result.
valid = pod.valid;
758 result.
width = pod.width; result.
height = pod.height; result.
depth = pod.depth;
759 result.
xsize = pod.xsize; result.
ysize = pod.ysize; result.
zsize = pod.zsize;
760 std::memcpy(result.
x_ras, pod.x_ras, 3 *
sizeof(
float));
761 std::memcpy(result.
y_ras, pod.y_ras, 3 *
sizeof(
float));
762 std::memcpy(result.
z_ras, pod.z_ras, 3 *
sizeof(
float));
763 std::memcpy(result.
c_ras, pod.c_ras, 3 *
sizeof(
float));
765 if (t->len >
static_cast<long long>(
sizeof(VolGeomPOD)))
766 result.
filename = QString::fromUtf8(
767 reinterpret_cast<const char *
>(d +
sizeof(VolGeomPOD)));
785 rr = PointsT::Zero(
np, 3);
786 nn = NormalsT::Zero(
np, 3);
825 cm[0] =
cm[1] =
cm[2] = 0.0;
840 auto copy = std::make_shared<MNESourceSpace>(this->
np);
841 copy->type = this->
type;
844 copy->ntri = this->
ntri;
848 copy->nuse = this->
nuse;
849 copy->inuse = this->
inuse;
850 copy->vertno = this->
vertno;
851 copy->itris = this->
itris;
855 copy->dist = this->
dist;
867 for (k = 0; k <
np; k++)
883 for (k = 0, xave = 0.0; k <
np; k++)
895 double xave =
rr.col(0).sum();
911 inuse = std::move(new_inuse);
913 for (k = 0, nuse_count = 0; k <
np; k++)
920 for (k = 0, p = 0; k <
np; k++)
941 qCritical(
"Coordinate transformation does not match with the source space coordinate system.");
944 for (k = 0; k <
np; k++) {
949 for (k = 0; k <
ntri; k++)
962 std::vector<std::optional<MNEPatchInfo>> pinfo(
nuse);
965 qInfo(
"Computing patch statistics...\n");
971 qCritical(
"The patch information is not available.");
981 qInfo(
"\tareas, average normals, and mean deviations...");
986 for (p = 1, q = 0; p <
np; p++) {
989 qCritical(
"No vertices belong to the patch of vertex %d",nearest_data[p-1].
nearest);
995 pinfo[q]->vert = nearest_data[p-1].
nearest;
996 this_patch = nearest_data+p-nave;
997 pinfo[q]->memb_vert.resize(nave);
998 for (k = 0; k < nave; k++) {
999 pinfo[q]->memb_vert[k] = this_patch[k].
vert;
1000 this_patch[k].
patch = &(*pinfo[q]);
1002 pinfo[q]->calculate_area(
this);
1003 pinfo[q]->calculate_normal_stats(
this);
1011 qCritical(
"No vertices belong to the patch of vertex %d",nearest_data[p-1].
nearest);
1016 pinfo[q]->vert = nearest_data[p-1].
nearest;
1017 this_patch = nearest_data+p-nave;
1018 pinfo[q]->memb_vert.resize(nave);
1019 for (k = 0; k < nave; k++) {
1020 pinfo[q]->memb_vert[k] = this_patch[k].
vert;
1021 this_patch[k].
patch = &(*pinfo[q]);
1023 pinfo[q]->calculate_area(
this);
1024 pinfo[q]->calculate_normal_stats(
this);
1027 qInfo(
" %d/%d [done]\n",q,
nuse);
1040 for (k = 0,
nuse = 0; k <
np; k++)
1049 for (k = 0, p = 0; k <
np; k++)
1065 auto res = std::make_unique<MNESourceSpace>();
1068 res->rr = PointsT::Zero(
np, 3);
1069 res->nn = NormalsT::Zero(
np, 3);
1070 res->inuse = VectorXi::Zero(
np);
1071 res->vertno = VectorXi::Zero(
np);
1075 res->tot_area = 0.0;
1082 res->subject.clear();
1086 res->dist_limit = -1.0;
1088 res->voxel_surf_RAS_t.reset();
1089 res->vol_dims[0] = res->vol_dims[1] = res->vol_dims[2] = 0;
1091 res->MRI_volume.clear();
1092 res->MRI_surf_RAS_RAS_t.reset();
1093 res->MRI_voxel_surf_RAS_t.reset();
1094 res->MRI_vol_dims[0] = res->MRI_vol_dims[1] = res->MRI_vol_dims[2] = 0;
1095 res->interpolator.reset();
1097 res->vol_geom.reset();
1098 res->mgh_tags.reset();
1100 res->cm[0] = res->cm[1] = res->cm[2] = 0.0;
1108 const QString& curv_file)
1116 const QString& curv_file,
1118 bool check_too_many_neighbors)
1124 std::unique_ptr<MNESourceSpace> s;
1125 std::optional<MNEMghTagGroup> tags;
1126 Eigen::VectorXf curvs;
1130 if (read_triangle_file(surf_file,
1136 if (!curv_file.isEmpty()) {
1137 if (read_curvature_file(curv_file, curvs) == -1)
1139 if (curvs.size() != verts.rows()) {
1140 qCritical()<<
"Incorrect number of vertices in the curvature file.";
1145 s = std::make_unique<MNESourceSpace>(0);
1146 s->rr = std::move(verts);
1147 s->itris = std::move(
tris);
1148 s->ntri = s->itris.rows();
1149 s->np = s->rr.rows();
1150 if (curvs.size() > 0) {
1151 s->curv = std::move(curvs);
1153 s->val = Eigen::VectorXf::Zero(s->np);
1155 if (check_too_many_neighbors) {
1156 if (s->add_geometry_info(
true) !=
OK)
1160 if (s->add_geometry_info2(
true) !=
OK)
1164 else if (s->nn.rows() == 0) {
1165 if (s->add_vertex_normals() !=
OK)
1169 s->add_triangle_data();
1171 s->inuse = Eigen::VectorXi::Ones(s->np);
1172 s->vertno = Eigen::VectorXi::LinSpaced(s->np, 0, s->np - 1);
1173 s->mgh_tags = std::move(tags);
1174 s->vol_geom = get_volume_geom_from_tag(s->mgh_tags ? &(*s->mgh_tags) :
nullptr);
1181static std::optional<FiffCoordTrans> make_voxel_ras_trans(
const Eigen::Vector3f& r0,
1182 const Eigen::Vector3f& x_ras,
1183 const Eigen::Vector3f& y_ras,
1184 const Eigen::Vector3f& z_ras,
1185 const Eigen::Vector3f& voxel_size)
1187 Eigen::Matrix3f rot;
1188 rot.row(0) = x_ras.transpose() * voxel_size[0];
1189 rot.row(1) = y_ras.transpose() * voxel_size[1];
1190 rot.row(2) = z_ras.transpose() * voxel_size[2];
1200 Eigen::Vector3f minV, maxV,
cm;
1201 int minn[3],maxn[3];
1204 std::unique_ptr<MNESourceSpace> sp;
1212 minV = maxV = surf.
rr.row(0).transpose();
1214 for (k = 0; k < surf.
np; k++) {
1215 Eigen::Vector3f node = surf.
rr.row(k).transpose();
1217 minV = minV.cwiseMin(node);
1218 maxV = maxV.cwiseMax(node);
1220 cm /=
static_cast<float>(surf.
np);
1225 for (k = 0; k < surf.
np; k++) {
1226 dist = (surf.
rr.row(k).transpose() -
cm).norm();
1230 qInfo(
"FsSurface CM = (%6.1f %6.1f %6.1f) mm\n",
1232 qInfo(
"FsSurface fits inside a sphere with radius %6.1f mm\n",1000*maxdist);
1233 qInfo(
"FsSurface extent:\n"
1234 "\tx = %6.1f ... %6.1f mm\n"
1235 "\ty = %6.1f ... %6.1f mm\n"
1236 "\tz = %6.1f ... %6.1f mm\n",
1237 1000*minV[
X],1000*maxV[
X],
1238 1000*minV[
Y],1000*maxV[
Y],
1239 1000*minV[
Z],1000*maxV[
Z]);
1240 for (c = 0; c < 3; c++) {
1242 maxn[c] = floor(std::fabs(maxV[c])/grid)+1;
1244 maxn[c] = -floor(std::fabs(maxV[c])/grid)-1;
1246 minn[c] = floor(std::fabs(minV[c])/grid)+1;
1248 minn[c] = -floor(std::fabs(minV[c])/grid)-1;
1250 qInfo(
"Grid extent:\n"
1251 "\tx = %6.1f ... %6.1f mm\n"
1252 "\ty = %6.1f ... %6.1f mm\n"
1253 "\tz = %6.1f ... %6.1f mm\n",
1254 1000*(minn[0]*grid),1000*(maxn[0]*grid),
1255 1000*(minn[1]*grid),1000*(maxn[1]*grid),
1256 1000*(minn[2]*grid),1000*(maxn[2]*grid));
1261 for (c = 0; c < 3; c++)
1262 np =
np*(maxn[c]-minn[c]+1);
1263 nplane = (maxn[0]-minn[0]+1)*(maxn[1]-minn[1]+1);
1264 nrow = (maxn[0]-minn[0]+1);
1267 sp->nneighbor_vert = Eigen::VectorXi::Constant(sp->np,
NNEIGHBORS);
1268 sp->neighbor_vert.resize(sp->np);
1269 for (k = 0; k < sp->np; k++) {
1272 sp->nn(k,0) = sp->nn(k,1) = 0.0;
1274 sp->neighbor_vert[k] = Eigen::VectorXi::Constant(
NNEIGHBORS, -1);
1277 for (k = 0, z = minn[2]; z <= maxn[2]; z++) {
1278 for (y = minn[1]; y <= maxn[1]; y++) {
1279 for (x = minn[0]; x <= maxn[0]; x++, k++) {
1280 sp->rr(k,0) = x*grid;
1281 sp->rr(k,1) = y*grid;
1282 sp->rr(k,2) = z*grid;
1287 Eigen::VectorXi& neigh = sp->neighbor_vert[k];
1289 neigh[0] = k - nplane;
1293 neigh[2] = k + nrow;
1297 neigh[4] = k - nrow;
1299 neigh[5] = k + nplane;
1306 neigh[6] = k + 1 - nplane;
1308 neigh[7] = k + 1 + nrow - nplane;
1311 neigh[8] = k + nrow - nplane;
1314 neigh[9] = k - 1 + nrow - nplane;
1315 neigh[10] = k - 1 - nplane;
1317 neigh[11] = k - 1 - nrow - nplane;
1320 neigh[12] = k - nrow - nplane;
1322 neigh[13] = k + 1 - nrow - nplane;
1328 if (x < maxn[0] && y < maxn[1])
1329 neigh[14] = k + 1 + nrow;
1332 neigh[15] = k - 1 + nrow;
1334 neigh[16] = k - 1 - nrow;
1336 if (y > minn[1] && x < maxn[0])
1337 neigh[17] = k + 1 - nrow - nplane;
1343 neigh[18] = k + 1 + nplane;
1345 neigh[19] = k + 1 + nrow + nplane;
1348 neigh[20] = k + nrow + nplane;
1351 neigh[21] = k - 1 + nrow + nplane;
1352 neigh[22] = k - 1 + nplane;
1354 neigh[23] = k - 1 - nrow + nplane;
1357 neigh[24] = k - nrow + nplane;
1359 neigh[25] = k + 1 - nrow + nplane;
1365 qInfo(
"%d sources before omitting any.\n",sp->nuse);
1369 for (k = 0; k < sp->np; k++) {
1370 dist = (sp->rr.row(k).transpose() -
cm).norm();
1376 qInfo(
"%d sources after omitting infeasible sources.\n",sp->nuse);
1378 std::vector<std::unique_ptr<MNESourceSpace>> sp_vec;
1379 sp_vec.push_back(std::move(sp));
1383 sp = std::move(sp_vec[0]);
1385 qInfo(
"%d sources remaining after excluding the sources outside the surface and less than %6.1f mm inside.\n",sp->nuse,1000*mindist);
1389 qInfo(
"Adjusting the neighborhood info...");
1390 for (k = 0; k < sp->np; k++) {
1391 Eigen::VectorXi& neigh = sp->neighbor_vert[k];
1392 nneigh = sp->nneighbor_vert[k];
1394 for (c = 0; c < nneigh; c++)
1395 if (!sp->inuse[neigh[c]])
1399 for (c = 0; c < nneigh; c++)
1408 Eigen::Vector3f r0(minn[0]*grid, minn[1]*grid, minn[2]*grid);
1409 Eigen::Vector3f
voxel_size(grid, grid, grid);
1410 Eigen::Vector3f x_ras = Eigen::Vector3f::UnitX();
1411 Eigen::Vector3f y_ras = Eigen::Vector3f::UnitY();
1412 Eigen::Vector3f z_ras = Eigen::Vector3f::UnitZ();
1413 int width = (maxn[0]-minn[0]+1);
1414 int height = (maxn[1]-minn[1]+1);
1415 int depth = (maxn[2]-minn[2]+1);
1417 sp->voxel_surf_RAS_t = make_voxel_ras_trans(r0,x_ras,y_ras,z_ras,
voxel_size);
1418 if (!sp->voxel_surf_RAS_t || sp->voxel_surf_RAS_t->isEmpty())
1421 sp->vol_dims[0] = width;
1422 sp->vol_dims[1] = height;
1423 sp->vol_dims[2] = depth;
1424 Eigen::Map<Eigen::Vector3f>(sp->voxel_size) =
voxel_size;
1427 return sp.release();
1442 int omit,omit_outside;
1444 int nspace =
static_cast<int>(spaces.size());
1447 qCritical(
"Source spaces are in head coordinates and no coordinate transform was provided!");
1453 qInfo(
"Source spaces are in ");
1455 qInfo(
"head coordinates.\n");
1457 qInfo(
"MRI coordinates.\n");
1459 qWarning(
"unknown (%d) coordinates.\n",spaces[0]->
coord_frame);
1460 qInfo(
"Checking that the sources are inside the bounding surface ");
1462 qInfo(
"and at least %6.1f mm away",1000*limit);
1463 qInfo(
" (will take a few...)\n");
1466 for (k = 0; k < nspace; k++) {
1467 s = spaces[k].get();
1468 for (p1 = 0; p1 < s->
np; p1++)
1470 r1 = s->
rr.row(p1).transpose();
1477 if (std::fabs(tot_angle-1.0) > 1e-5) {
1482 *filtered << qSetFieldWidth(10) << qSetRealNumberPrecision(3) << Qt::fixed
1483 << 1000*r1[
X] <<
" " << 1000*r1[
Y] <<
" " << 1000*r1[
Z] <<
"\n" << qSetFieldWidth(0);
1485 else if (limit > 0.0) {
1491 for (p2 = 0; p2 < surf.
np; p2++) {
1492 dist = (surf.
rr.row(p2).transpose() - r1).norm();
1493 if (
dist < mindist) {
1498 if (mindist < limit) {
1503 *filtered << qSetFieldWidth(10) << qSetRealNumberPrecision(3) << Qt::fixed
1504 << 1000*r1[
X] <<
" " << 1000*r1[
Y] <<
" " << 1000*r1[
Z] <<
"\n" << qSetFieldWidth(0);
1510 if (omit_outside > 0)
1511 qInfo(
"%d source space points omitted because they are outside the inner skull surface.\n",
1514 qInfo(
"%d source space points omitted because of the %6.1f-mm distance limit.\n",
1516 qInfo(
"Thank you for waiting.\n");
1527 int omit,omit_outside;
1532 QSharedPointer<MNESurface> surf = a->
surf.toStrongRef();
1541 for (p1 = 0; p1 < a->
s->
np; p1++) {
1543 r1 = a->
s->
rr.row(p1).transpose();
1551 tot_angle = surf->sum_solids(r1)/(4*
M_PI);
1552 if (std::fabs(tot_angle-1.0) > 1e-5) {
1557 *a->
filtered << qSetFieldWidth(10) << qSetRealNumberPrecision(3) << Qt::fixed
1558 << 1000*r1[
X] <<
" " << 1000*r1[
Y] <<
" " << 1000*r1[
Z] <<
"\n" << qSetFieldWidth(0);
1560 else if (a->
limit > 0.0) {
1566 for (p2 = 0; p2 < surf->np; p2++) {
1567 dist = (surf->rr.row(p2).transpose() - r1).norm();
1568 if (
dist < mindist) {
1573 if (mindist < a->limit) {
1578 *a->
filtered << qSetFieldWidth(10) << qSetRealNumberPrecision(3) << Qt::fixed
1579 << 1000*r1[
X] <<
" " << 1000*r1[
Y] <<
" " << 1000*r1[
Z] <<
"\n" << qSetFieldWidth(0);
1585 if (omit_outside > 0)
1586 qInfo(
"%d source space points omitted because they are outside the inner skull surface.\n",
1589 qInfo(
"%d source space points omitted because of the %6.1f-mm distance limit.\n",
1590 omit,1000*a->
limit);
1602 QSharedPointer<MNESurface> surf;
1604 int nproc = QThread::idealThreadCount();
1605 int nspace =
static_cast<int>(spaces.size());
1607 if (bemfile.isEmpty())
1613 qCritical(
"BEM model does not have the inner skull triangulation!");
1616 surf.reset(rawSurf.release());
1621 qInfo(
"Source spaces are in ");
1623 qInfo(
"head coordinates.\n");
1625 qInfo(
"MRI coordinates.\n");
1627 qWarning(
"unknown (%d) coordinates.\n",spaces[0]->
coord_frame);
1628 qInfo(
"Checking that the sources are inside the inner skull ");
1630 qInfo(
"and at least %6.1f mm away",1000*limit);
1631 qInfo(
" (will take a few...)\n");
1632 if (nproc < 2 || nspace == 1 || !use_threads) {
1636 for (k = 0; k < nspace; k++) {
1637 auto a_ptr = std::make_unique<FilterThreadArg>();
1638 a_ptr->s = spaces[k].get();
1639 a_ptr->mri_head_t = std::make_unique<FiffCoordTrans>(mri_head_t);
1641 a_ptr->limit = limit;
1642 a_ptr->filtered = filtered;
1644 spaces[k]->rearrange_source_space();
1651 QList<FilterThreadArg*> args;
1653 std::vector<std::unique_ptr<FilterThreadArg>> arg_owners;
1654 for (k = 0; k < nspace; k++) {
1655 auto a_ptr = std::make_unique<FilterThreadArg>();
1656 a_ptr->s = spaces[k].get();
1657 a_ptr->mri_head_t = std::make_unique<FiffCoordTrans>(mri_head_t);
1659 a_ptr->limit = limit;
1660 a_ptr->filtered = filtered;
1661 args.append(a_ptr.get());
1662 arg_owners.push_back(std::move(a_ptr));
1669 for (k = 0; k < nspace; k++) {
1670 spaces[k]->rearrange_source_space();
1673 qInfo(
"Thank you for waiting.\n\n");
1688 std::vector<std::unique_ptr<MNESourceSpace>> local_spaces;
1689 std::unique_ptr<MNESourceSpace> new_space;
1690 QList<FiffDirNode::SPtr> sources;
1696 if(!stream->open()) {
1702 if (sources.size() == 0) {
1703 qCritical(
"No source spaces available here");
1707 for (j = 0; j < sources.size(); j++) {
1717 new_space->np = *t_pTag->toInt();
1718 if (new_space->np == 0) {
1719 qCritical(
"No points in this source space");
1727 MatrixXf tmp_rr = t_pTag->toFloatMatrix().transpose();
1728 new_space->rr = tmp_rr;
1733 MatrixXf tmp_nn = t_pTag->toFloatMatrix().transpose();
1734 new_space->nn = tmp_nn;
1739 new_space->coord_frame = *t_pTag->toInt();
1742 new_space->id = *t_pTag->toInt();
1745 new_space->subject = t_pTag->toString();
1748 new_space->type = *t_pTag->toInt();
1752 ntri = *t_pTag->toInt();
1755 ntri = *t_pTag->toInt();
1766 MatrixXi tmp_itris = t_pTag->toIntMatrix().transpose();
1767 tmp_itris.array() -= 1;
1768 new_space->itris = tmp_itris;
1769 new_space->ntri =
ntri;
1776 new_space->nuse = new_space->np;
1777 new_space->inuse = Eigen::VectorXi::Ones(new_space->nuse);
1778 new_space->vertno = Eigen::VectorXi::LinSpaced(new_space->nuse, 0, new_space->nuse - 1);
1785 new_space->nuse = 0;
1786 new_space->inuse = Eigen::VectorXi::Zero(new_space->np);
1787 new_space->vertno.resize(0);
1791 new_space->nuse = *t_pTag->toInt();
1798 Eigen::Map<Eigen::VectorXi> inuseMap(t_pTag->toInt(), new_space->np);
1799 new_space->inuse = inuseMap;
1801 if (new_space->nuse > 0) {
1802 new_space->vertno = Eigen::VectorXi::Zero(new_space->nuse);
1803 for (k = 0, p = 0; k < new_space->np; k++) {
1804 if (new_space->inuse[k])
1805 new_space->vertno[p++] = k;
1809 new_space->vertno.resize(0);
1816 ntri = *t_pTag->toInt();
1825 MatrixXi tmp_itris = t_pTag->toIntMatrix().transpose();
1826 tmp_itris.array() -= 1;
1827 new_space->use_itris = tmp_itris;
1828 new_space->nuse_tri =
ntri;
1834 Eigen::Map<Eigen::VectorXi> nearestMap(t_pTag->toInt(), new_space->np);
1835 new_space->nearest.resize(new_space->np);
1836 for (k = 0; k < new_space->np; k++) {
1837 new_space->nearest[k].vert = k;
1838 new_space->nearest[k].nearest = nearestMap[k];
1839 new_space->nearest[k].patch =
nullptr;
1846 Eigen::Map<const Eigen::VectorXf> nearestDistMap(t_pTag->toFloat(), new_space->np);
1847 for (k = 0; k < new_space->np; k++) {
1848 new_space->nearest[k].dist = nearestDistMap[k];
1855 new_space->dist_limit = *t_pTag->toFloat();
1863 auto dist_full = dist_lower->mne_add_upper_triangle_rcs();
1868 new_space->dist = std::move(*dist_full);
1871 new_space->dist_limit = 0.0;
1878 int ntot,nvert,ntot_count,nneigh;
1880 Eigen::VectorXi neighborsVec;
1881 Eigen::VectorXi nneighborsVec;
1885 neighborsVec = Eigen::Map<Eigen::VectorXi>(t_pTag->toInt(), ntot);
1889 nneighborsVec = Eigen::Map<Eigen::VectorXi>(t_pTag->toInt(), nvert);
1891 if (neighborsVec.size() > 0 && nneighborsVec.size() > 0) {
1892 if (nvert != new_space->np) {
1893 qCritical(
"Inconsistent neighborhood data in file.");
1897 for (k = 0, ntot_count = 0; k < nvert; k++)
1898 ntot_count += nneighborsVec[k];
1899 if (ntot_count != ntot) {
1900 qCritical(
"Inconsistent neighborhood data in file.");
1904 new_space->nneighbor_vert = Eigen::VectorXi::Zero(nvert);
1905 new_space->neighbor_vert.resize(nvert);
1906 for (k = 0, q = 0; k < nvert; k++) {
1907 new_space->nneighbor_vert[k] = nneigh = nneighborsVec[k];
1908 new_space->neighbor_vert[k] = Eigen::VectorXi(nneigh);
1909 for (p = 0; p < nneigh; p++,q++)
1910 new_space->neighbor_vert[k][p] = neighborsVec[q];
1918 Eigen::Map<Eigen::Vector3i> volDimsMap(t_pTag->toInt());
1919 Eigen::Map<Eigen::Vector3i>(new_space->vol_dims) = volDimsMap;
1924 if (mris.size() == 0) {
1927 new_space->MRI_volume = t_pTag->toString();
1935 new_space->MRI_volume = t_pTag->toString();
1944 new_space->MRI_vol_dims[0] = *t_pTag->toInt();
1947 new_space->MRI_vol_dims[1] = *t_pTag->toInt();
1950 new_space->MRI_vol_dims[2] = *t_pTag->toInt();
1955 new_space->add_triangle_data();
1956 local_spaces.push_back(std::move(new_space));
1960 spaces = std::move(local_spaces);
1974 int nspace =
static_cast<int>(spaces.size());
1976 for (k = 0; k < nspace; k++) {
1977 s = spaces[k].get();
1991 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);
2033 rh = spaces[k].get();
2034 rh_inuse = Eigen::VectorXi::Zero(rh->
np);
2040 for (k = 0; k < nlabel; k++) {
2053 qWarning(
"\tWarning: cannot assign label file %s to a hemisphere.\n",labels[k].toUtf8().constData());
2059 for (p = 0; p < sel.size(); p++) {
2060 if (sel[p] >= 0 && sel[p] < sp->
np)
2061 (*inuse)[sel[p]] = sp->
inuse[sel[p]];
2063 qWarning(
"vertex number out of range in %s (%d vs %d)\n",
2064 labels[k].toUtf8().constData(),sel[p],sp->
np);
2066 qInfo(
"Processed label file %s\n",labels[k].toUtf8().constData());
2087 QFile inFile(label);
2088 if (!inFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
2089 qCritical() << label;
2095 qCritical(
"FsLabel file does not start correctly.");
2102 while (inFile.getChar(&c) && c !=
'\n')
2105 QTextStream in(&inFile);
2107 if (in.status() != QTextStream::Ok) {
2108 qCritical(
"Could not read the number of labelled points.");
2113 for (k = 0; k < nlabel; k++) {
2114 in >> p >> fdum >> fdum >> fdum >> fdum;
2115 if (in.status() != QTextStream::Ok) {
2116 qCritical(
"Could not read label point # %d",k+1);
2129int MNESourceSpace::writeVolumeInfo(
FiffStream::SPtr& stream,
bool selected_only)
const
2140 Eigen::VectorXi nneighbors;
2141 Eigen::VectorXi neighbors;
2143 if (selected_only) {
2144 Eigen::VectorXi inuse_map = Eigen::VectorXi::Constant(
np, -1);
2145 for (k = 0,p = 0, ntot = 0; k <
np; k++) {
2151 nneighbors.resize(
nuse);
2152 neighbors.resize(ntot);
2153 for (k = 0, nvert = 0, ntot = 0; k <
np; k++) {
2157 nneighbors[nvert++] = nneigh;
2158 for (p = 0; p < nneigh; p++)
2159 neighbors[ntot++] = neigh[p] < 0 ? -1 : inuse_map[neigh[p]];
2164 for (k = 0, ntot = 0; k <
np; k++)
2166 nneighbors.resize(
np);
2167 neighbors.resize(ntot);
2169 for (k = 0, ntot = 0; k <
np; k++) {
2172 nneighbors[k] = nneigh;
2173 for (p = 0; p < nneigh; p++)
2174 neighbors[ntot++] = neigh[p];
2181 if (!selected_only) {
2206 qCritical(
"Cannot write the interpolator for selection yet");
2220 qCritical(
"No points in the source space being saved");
2237 if (selected_only) {
2239 qCritical(
"No vertices in use. Cannot write active-only vertices from this source space");
2243 Eigen::MatrixXf sel(
nuse, 3);
2246 for (p = 0, pp = 0; p <
np; p++) {
2248 sel.row(pp) =
rr.row(p);
2254 for (p = 0, pp = 0; p <
np; p++) {
2256 sel.row(pp) =
nn.row(p);
2274 Eigen::MatrixXi file_tris =
itris.array() + 1;
2280 Eigen::MatrixXi file_use_tris =
use_itris.array() + 1;
2285 Eigen::VectorXi nearest_v(
np);
2286 Eigen::VectorXf nearest_dist_v(
np);
2288 std::sort(
const_cast<std::vector<MNENearest>&
>(
nearest).begin(),
2289 const_cast<std::vector<MNENearest>&
>(
nearest).end(),
2291 for (p = 0; p <
np; p++) {
2292 nearest_v[p] =
nearest[p].nearest;
2293 nearest_dist_v[p] =
nearest[p].dist;
2300 if (!
dist.is_empty()) {
2301 auto m =
dist.pickLowerTriangleRcs();
2309 if (writeVolumeInfo(stream, selected_only) !=
OK)
2322static Eigen::MatrixX3f generateIcoVertices(
int grade)
2325 const float t = (1.0f + std::sqrt(5.0f)) / 2.0f;
2326 std::vector<Eigen::Vector3f> verts = {
2327 {-1, t, 0}, { 1, t, 0}, {-1, -t, 0}, { 1, -t, 0},
2328 { 0, -1, t}, { 0, 1, t}, { 0, -1, -t}, { 0, 1, -t},
2329 { t, 0, -1}, { t, 0, 1}, {-t, 0, -1}, {-t, 0, 1}
2332 for (
auto& v : verts)
2336 std::vector<std::array<int,3>> faces = {
2337 {0,11,5}, {0,5,1}, {0,1,7}, {0,7,10}, {0,10,11},
2338 {1,5,9}, {5,11,4}, {11,10,2}, {10,7,6}, {7,1,8},
2339 {3,9,4}, {3,4,2}, {3,2,6}, {3,6,8}, {3,8,9},
2340 {4,9,5}, {2,4,11}, {6,2,10}, {8,6,7}, {9,8,1}
2344 for (
int g = 0; g < grade; ++g) {
2345 std::map<std::pair<int,int>,
int> midpointCache;
2346 std::vector<std::array<int,3>> newFaces;
2348 auto getMidpoint = [&](
int i1,
int i2) ->
int {
2349 auto key = std::make_pair(std::min(i1, i2), std::max(i1, i2));
2350 auto it = midpointCache.find(key);
2351 if (it != midpointCache.end())
2353 Eigen::Vector3f mid = (verts[i1] + verts[i2]).normalized();
2354 int idx =
static_cast<int>(verts.size());
2355 verts.push_back(mid);
2356 midpointCache[key] = idx;
2360 for (
const auto& f : faces) {
2361 int a = getMidpoint(f[0], f[1]);
2362 int b = getMidpoint(f[1], f[2]);
2363 int c = getMidpoint(f[2], f[0]);
2364 newFaces.push_back({f[0], a, c});
2365 newFaces.push_back({f[1], b, a});
2366 newFaces.push_back({f[2], c, b});
2367 newFaces.push_back({a, b, c});
2372 Eigen::MatrixX3f result(
static_cast<int>(verts.size()), 3);
2373 for (
int i = 0; i < static_cast<int>(verts.size()); ++i)
2374 result.row(i) = verts[i];
2384 if (hemi.
np <= 0 || hemi.
rr.rows() == 0) {
2385 qWarning(
"MNESourceSpace::icoDownsample - Hemisphere has no vertices.");
2390 Eigen::MatrixX3f icoVerts = generateIcoVertices(icoGrade);
2393 Eigen::MatrixX3f hemiNorm(hemi.
np, 3);
2394 for (
int i = 0; i < hemi.
np; ++i) {
2395 Eigen::Vector3f v = hemi.
rr.row(i);
2396 float len = v.norm();
2398 hemiNorm.row(i) = (v / len).transpose();
2400 hemiNorm.row(i) = v.transpose();
2404 result.
inuse = Eigen::VectorXi::Zero(result.
np);
2407 for (
int i = 0; i < icoVerts.rows(); ++i) {
2408 Eigen::Vector3f icoV = icoVerts.row(i);
2409 float bestDist = std::numeric_limits<float>::max();
2411 for (
int j = 0; j < hemi.
np; ++j) {
2412 float d = (hemiNorm.row(j).transpose() - icoV).squaredNorm();
2419 result.
inuse[bestIdx] = 1;
2426 for (
int i = 0; i < result.
np; ++i) {
2427 if (result.
inuse[i])
FiffCoordTrans class declaration.
#define FIFF_BEM_SURF_TRIANGLES
#define FIFF_BEM_SURF_NTRI
#define FIFFV_BEM_SURF_ID_BRAIN
FiffStream class declaration.
FiffTag class declaration, which provides fiff tag I/O and processing methods.
FiffSparseMatrix class declaration.
#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
Byte-swap utility functions for FIFF binary I/O.
#define MNE_SOURCE_SPACE_VOLUME
MNESourceSpace class declaration.
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
MNE Patch Information (MNEPatchInfo) class declaration.
MNENearest class declaration.
#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
MNEMghTagGroup class declaration.
MNEHemisphere class declaration.
Filter Thread Argument (FilterThreadArg) class declaration.
MNESurface class declaration.
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
float swap_float(float source)
qint32 swap_int(qint32 source)
qint64 swap_long(qint64 source)
qint16 swap_short(qint16 source)
Coordinate transformation description.
static FiffCoordTrans readTransformFromNode(FiffStream::SPtr &stream, const FiffDirNode::SPtr &node, int from, int to)
FiffCoordTrans inverted() const
Eigen::MatrixX3f apply_inverse_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
QSharedPointer< FiffDirNode > SPtr
FIFF sparse matrix storage backed by Eigen.
static FiffSparseMatrix::UPtr fiff_get_float_sparse_matrix(const FIFFLIB::FiffTag::UPtr &tag)
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()
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.