v2.0.0
Loading...
Searching...
No Matches
mne_surface.cpp
Go to the documentation of this file.
1//=============================================================================================================
19
20//=============================================================================================================
21// INCLUDES
22//=============================================================================================================
23
24#include "mne_surface.h"
25#include "mne_source_space.h"
26#include "mne_triangle.h"
27#include "mne_proj_data.h"
28
29#include <fiff/fiff_stream.h>
31
32#include <QFile>
33#include <QDebug>
34
35#define _USE_MATH_DEFINES
36#include <math.h>
37
38#include <Eigen/Dense>
39
40//=============================================================================================================
41// USED NAMESPACES
42//=============================================================================================================
43
44using namespace Eigen;
45using namespace FIFFLIB;
46using namespace MNELIB;
47
48//=============================================================================================================
49
50constexpr int FAIL = -1;
51constexpr int OK = 0;
52
53constexpr int X = 0;
54constexpr int Y = 1;
55constexpr int Z = 2;
56
57//=============================================================================================================
58// DEFINE MEMBER METHODS
59//=============================================================================================================
60
64
65//=============================================================================================================
66
70
71//=============================================================================================================
72// Const geometry methods
73//=============================================================================================================
74
75double MNESurface::sum_solids(const Eigen::Vector3f& from) const
76{
77 int k;
78 double tot_angle, angle;
79 for (k = 0, tot_angle = 0.0; k < ntri; k++) {
80 angle = solid_angle(from, tris[k]);
81 tot_angle += angle;
82 }
83 return tot_angle;
84}
85
86//=============================================================================================================
87
88void MNESurface::triangle_coords(const Eigen::Vector3f& r, int tri, float &x, float &y, float &z) const
89{
90 double a, b, c, v1, v2, det;
91 const MNETriangle* this_tri;
92
93 this_tri = &tris[tri];
94
95 Eigen::Vector3d rr = (r - this_tri->r1).cast<double>();
96 z = rr.dot(this_tri->nn.cast<double>());
97
98 a = this_tri->r12.cast<double>().squaredNorm();
99 b = this_tri->r13.cast<double>().squaredNorm();
100 c = this_tri->r12.cast<double>().dot(this_tri->r13.cast<double>());
101
102 v1 = rr.dot(this_tri->r12.cast<double>());
103 v2 = rr.dot(this_tri->r13.cast<double>());
104
105 det = a * b - c * c;
106
107 x = (b * v1 - c * v2) / det;
108 y = (a * v2 - c * v1) / det;
109}
110
111//=============================================================================================================
112
113int MNESurface::nearest_triangle_point(const Eigen::Vector3f& r, const MNEProjData *user, int tri, float &x, float &y, float &z) const
114{
115 double p, q, p0, q0, t0;
116 double a, b, c, v1, v2, det;
117 double best, dist, dist0;
118 const MNEProjData* pd = user;
119 const MNETriangle* this_tri;
120
121 this_tri = &tris[tri];
122 Eigen::Vector3d rr = (r - this_tri->r1).cast<double>();
123 dist = rr.dot(this_tri->nn.cast<double>());
124
125 if (pd) {
126 if (!pd->act[tri])
127 return false;
128 a = pd->a[tri];
129 b = pd->b[tri];
130 c = pd->c[tri];
131 }
132 else {
133 a = this_tri->r12.cast<double>().squaredNorm();
134 b = this_tri->r13.cast<double>().squaredNorm();
135 c = this_tri->r12.cast<double>().dot(this_tri->r13.cast<double>());
136 }
137
138 v1 = rr.dot(this_tri->r12.cast<double>());
139 v2 = rr.dot(this_tri->r13.cast<double>());
140
141 det = a * b - c * c;
142
143 p = (b * v1 - c * v2) / det;
144 q = (a * v2 - c * v1) / det;
145
146 if (p >= 0.0 && p <= 1.0 &&
147 q >= 0.0 && q <= 1.0 &&
148 q <= 1.0 - p) {
149 x = p;
150 y = q;
151 z = dist;
152 return true;
153 }
154 /*
155 * Side 1 -> 2
156 */
157 p0 = p + 0.5 * (q * c) / a;
158 if (p0 < 0.0) p0 = 0.0;
159 else if (p0 > 1.0) p0 = 1.0;
160 q0 = 0.0;
161 dist0 = sqrt((p - p0) * (p - p0) * a +
162 (q - q0) * (q - q0) * b +
163 (p - p0) * (q - q0) * c +
164 dist * dist);
165 best = dist0;
166 x = p0;
167 y = q0;
168 z = dist0;
169 /*
170 * Side 2 -> 3
171 */
172 t0 = 0.5 * ((2.0 * a - c) * (1.0 - p) + (2.0 * b - c) * q) / (a + b - c);
173 if (t0 < 0.0) t0 = 0.0;
174 else if (t0 > 1.0) t0 = 1.0;
175 p0 = 1.0 - t0;
176 q0 = t0;
177 dist0 = sqrt((p - p0) * (p - p0) * a +
178 (q - q0) * (q - q0) * b +
179 (p - p0) * (q - q0) * c +
180 dist * dist);
181 if (dist0 < best) {
182 best = dist0;
183 x = p0;
184 y = q0;
185 z = dist0;
186 }
187 /*
188 * Side 1 -> 3
189 */
190 p0 = 0.0;
191 q0 = q + 0.5 * (p * c) / b;
192 if (q0 < 0.0) q0 = 0.0;
193 else if (q0 > 1.0) q0 = 1.0;
194 dist0 = sqrt((p - p0) * (p - p0) * a +
195 (q - q0) * (q - q0) * b +
196 (p - p0) * (q - q0) * c +
197 dist * dist);
198 if (dist0 < best) {
199 best = dist0;
200 x = p0;
201 y = q0;
202 z = dist0;
203 }
204 return true;
205}
206
207//=============================================================================================================
208
209int MNESurface::nearest_triangle_point(const Eigen::Vector3f& r, int tri, float &x, float &y, float &z) const
210{
211 return nearest_triangle_point(r, nullptr, tri, x, y, z);
212}
213
214//=============================================================================================================
215
216Eigen::Vector3f MNESurface::project_to_triangle(int tri, float p, float q) const
217{
218 const MNETriangle* this_tri = &tris[tri];
219
220 return Eigen::Vector3f(
221 this_tri->r1 + p * this_tri->r12 + q * this_tri->r13
222 );
223}
224
225//=============================================================================================================
226
227Eigen::Vector3f MNESurface::project_to_triangle(int best, const Eigen::Vector3f& r) const
228{
229 float p, q, dist;
230 nearest_triangle_point(r, best, p, q, dist);
231 return project_to_triangle(best, p, q);
232}
233
234//=============================================================================================================
235
236int MNESurface::project_to_surface(const MNEProjData *proj_data, const Eigen::Vector3f& r, float &distp) const
237{
238 float dist;
239 float p, q;
240 float p0, q0, dist0;
241 int best;
242 int k;
243
244 p0 = q0 = 0.0;
245 dist0 = 0.0;
246 for (best = -1, k = 0; k < ntri; k++) {
247 if (nearest_triangle_point(r, proj_data, k, p, q, dist)) {
248 if (best < 0 || std::fabs(dist) < std::fabs(dist0)) {
249 dist0 = dist;
250 best = k;
251 p0 = p;
252 q0 = q;
253 }
254 }
255 }
256 distp = dist0;
257 return best;
258}
259
260//=============================================================================================================
261
263 Eigen::VectorXi& nearest_tri,
264 Eigen::VectorXf& dist, int nstep) const
265{
266 auto p = std::make_unique<MNEProjData>(this);
267 int k, was;
268
269 qInfo("%s for %d points %d steps...", nearest_tri[0] < 0 ? "Closest" : "Approx closest", np_points, nstep);
270
271 for (k = 0; k < np_points; k++) {
272 was = nearest_tri[k];
273 Eigen::Vector3f pt = Eigen::Map<const Eigen::Vector3f>(r.row(k).data());
274 decide_search_restriction(*p, nearest_tri[k], nstep, pt);
275 nearest_tri[k] = project_to_surface(p.get(), pt, dist[k]);
276 if (nearest_tri[k] < 0) {
277 decide_search_restriction(*p, -1, nstep, pt);
278 nearest_tri[k] = project_to_surface(p.get(), pt, dist[k]);
279 }
280 }
281 (void)was;
282
283 qInfo("[done]\n");
284}
285
286//=============================================================================================================
287
289 int approx_best,
290 int nstep,
291 const Eigen::Vector3f& r) const
292{
293 int k;
294 Eigen::Vector3f diff_vec;
295 float dist_val, mindist;
296 int minvert;
297
298 for (k = 0; k < ntri; k++)
299 p.act[k] = 0;
300
301 if (approx_best < 0) {
302 mindist = 1000.0;
303 minvert = 0;
304 for (k = 0; k < np; k++) {
305 diff_vec = rr.row(k).transpose() - r;
306 dist_val = diff_vec.norm();
307 if (dist_val < mindist && nneighbor_tri[k] > 0) {
308 mindist = dist_val;
309 minvert = k;
310 }
311 }
312 }
313 else {
314 const MNETriangle* this_tri = &tris[approx_best];
315 diff_vec = this_tri->r1 - r;
316 mindist = diff_vec.norm();
317 minvert = this_tri->vert[0];
318
319 diff_vec = this_tri->r2 - r;
320 dist_val = diff_vec.norm();
321 if (dist_val < mindist) {
322 mindist = dist_val;
323 minvert = this_tri->vert[1];
324 }
325 diff_vec = this_tri->r3 - r;
326 dist_val = diff_vec.norm();
327 if (dist_val < mindist) {
328 mindist = dist_val;
329 minvert = this_tri->vert[2];
330 }
331 }
332
333 activate_neighbors(minvert, p.act, nstep);
334
335 for (k = 0, p.nactive = 0; k < ntri; k++)
336 if (p.act[k])
337 p.nactive++;
338}
339
340//=============================================================================================================
341
342void MNESurface::activate_neighbors(int start, Eigen::VectorXi &act, int nstep) const
343{
344 int k;
345
346 if (nstep == 0)
347 return;
348
349 for (k = 0; k < nneighbor_tri[start]; k++)
350 act[neighbor_tri[start][k]] = 1;
351 for (k = 0; k < nneighbor_vert[start]; k++)
352 activate_neighbors(neighbor_vert[start][k], act, nstep - 1);
353}
354
355//=============================================================================================================
356// Static factory methods
357//=============================================================================================================
358
359std::unique_ptr<MNESurface> MNESurface::read_bem_surface(const QString& name, int which, bool add_geometry)
360{
361 float sigma;
362 return read_bem_surface(name, which, add_geometry, sigma, true);
363}
364
365//=============================================================================================================
366
367std::unique_ptr<MNESurface> MNESurface::read_bem_surface(const QString& name, int which, bool add_geometry, float& sigma)
368{
369 return read_bem_surface(name, which, add_geometry, sigma, true);
370}
371
372//=============================================================================================================
373
374std::unique_ptr<MNESurface> MNESurface::read_bem_surface2(const QString& name, int which, bool add_geometry)
375{
376 float sigma;
377 return read_bem_surface(name, which, add_geometry, sigma, false);
378}
379
380//=============================================================================================================
381
382std::unique_ptr<MNESurface> MNESurface::read_bem_surface(const QString& name, int which, bool add_geometry, float& sigma, bool check_too_many_neighbors)
383{
384 QFile file(name);
385 FiffStream::SPtr stream(new FiffStream(&file));
386
387 QList<FiffDirNode::SPtr> surfs;
388 QList<FiffDirNode::SPtr> bems;
390 FiffTag::UPtr t_pTag;
391
392 int id = -1;
393 int nnode, ntri_count;
394 MNESurface* s = nullptr;
395 std::unique_ptr<MNESurface> s_ptr;
396 int k;
397 MatrixXf tmp_node_normals;
399 float sigmaLocal = -1.0;
400 MatrixXf tmp_nodes;
401 MatrixXi tmp_triangles;
402
403 if (!stream->open())
404 return nullptr;
405
406 bems = stream->dirtree()->dir_tree_find(FIFFB_BEM);
407 if (bems.size() > 0) {
408 node = bems[0];
409 if (node->find_tag(stream, FIFF_BEM_COORD_FRAME, t_pTag)) {
410 coord_frame = *t_pTag->toInt();
411 }
412 }
413 surfs = stream->dirtree()->dir_tree_find(FIFFB_BEM_SURF);
414 if (surfs.size() == 0) {
415 qCritical("No BEM surfaces found in %s", name.toUtf8().constData());
416 stream->close(); return nullptr;
417 }
418 if (which >= 0) {
419 for (k = 0; k < surfs.size(); ++k) {
420 node = surfs[k];
421 if (node->find_tag(stream, FIFF_BEM_SURF_ID, t_pTag)) {
422 id = *t_pTag->toInt();
423 if (id == which)
424 break;
425 }
426 }
427 if (id != which) {
428 qCritical("Desired surface not found in %s", name.toUtf8().constData());
429 stream->close(); return nullptr;
430 }
431 }
432 else
433 node = surfs[0];
434
435 if (!node->find_tag(stream, FIFF_BEM_SURF_NNODE, t_pTag)) {
436 stream->close(); return nullptr;
437 }
438 nnode = *t_pTag->toInt();
439
440 if (!node->find_tag(stream, FIFF_BEM_SURF_NTRI, t_pTag)) {
441 stream->close(); return nullptr;
442 }
443 ntri_count = *t_pTag->toInt();
444
445 if (!node->find_tag(stream, FIFF_BEM_SURF_NODES, t_pTag)) {
446 stream->close(); return nullptr;
447 }
448 tmp_nodes = t_pTag->toFloatMatrix().transpose();
449
450 if (node->find_tag(stream, FIFF_BEM_SURF_NORMALS, t_pTag)) {
451 tmp_node_normals = t_pTag->toFloatMatrix().transpose();
452 }
453
454 if (!node->find_tag(stream, FIFF_BEM_SURF_TRIANGLES, t_pTag)) {
455 stream->close(); return nullptr;
456 }
457 tmp_triangles = t_pTag->toIntMatrix().transpose();
458
459 if (node->find_tag(stream, FIFF_MNE_COORD_FRAME, t_pTag)) {
460 coord_frame = *t_pTag->toInt();
461 }
462 else if (node->find_tag(stream, FIFF_BEM_COORD_FRAME, t_pTag)) {
463 coord_frame = *t_pTag->toInt();
464 }
465 if (node->find_tag(stream, FIFF_BEM_SIGMA, t_pTag)) {
466 sigmaLocal = *t_pTag->toFloat();
467 }
468
469 stream->close();
470
471 s_ptr = std::make_unique<MNESurface>();
472 s = s_ptr.get();
473 tmp_triangles.array() -= 1;
474 s->itris = tmp_triangles;
475 s->id = which;
476 s->sigma = sigmaLocal;
478 s->rr = tmp_nodes;
479 if (tmp_node_normals.rows() > 0)
480 s->nn = tmp_node_normals;
481 s->ntri = ntri_count;
482 s->np = nnode;
484 s->nuse_tri = 0;
485 s->tot_area = 0.0;
486 s->dist_limit = -1.0;
487 s->vol_dims[0] = s->vol_dims[1] = s->vol_dims[2] = 0;
488 s->MRI_vol_dims[0] = s->MRI_vol_dims[1] = s->MRI_vol_dims[2] = 0;
489 s->cm[0] = s->cm[1] = s->cm[2] = 0.0;
490
491 if (add_geometry) {
492 if (check_too_many_neighbors) {
493 if (s->add_geometry_info(s->nn.rows() == 0) != OK) {
494 return nullptr;
495 }
496 }
497 else {
498 if (s->add_geometry_info2(s->nn.rows() == 0) != OK) {
499 return nullptr;
500 }
501 }
502 }
503 else if (s->nn.rows() == 0) {
504 if (s->add_vertex_normals() != OK) {
505 return nullptr;
506 }
507 }
508 else
510
511 s->nuse = s->np;
512 s->inuse = Eigen::VectorXi::Ones(s->np);
513 s->vertno = Eigen::VectorXi::LinSpaced(s->np, 0, s->np - 1);
514 sigma = sigmaLocal;
515
516 return s_ptr;
517}
constexpr int FAIL
constexpr int Y
constexpr int Z
constexpr int OK
constexpr int X
Lightweight triangulated surface (vertices, triangles, normals) used by surface-based routines.
Single-hemisphere source space (cortical surface or volume grid) loaded from FIFF.
Legacy MNE-C aggregator for SSP projections plus the channel list they apply to.
Triangle descriptor with cached centroid, area and normal vectors.
#define FIFF_MNE_COORD_FRAME
#define FIFFV_MNE_SPACE_SURFACE
#define FIFFV_COORD_MRI
#define FIFFB_BEM_SURF
Definition fiff_file.h:397
#define FIFF_BEM_SURF_NNODE
Definition fiff_file.h:726
#define FIFF_BEM_SURF_NODES
Definition fiff_file.h:728
#define FIFF_BEM_SURF_TRIANGLES
Definition fiff_file.h:729
#define FIFF_BEM_COORD_FRAME
Definition fiff_file.h:736
#define FIFFB_BEM
Definition fiff_file.h:396
#define FIFF_BEM_SURF_ID
Definition fiff_file.h:724
#define FIFF_BEM_SURF_NTRI
Definition fiff_file.h:727
#define FIFF_BEM_SIGMA
Definition fiff_file.h:737
#define FIFF_BEM_SURF_NORMALS
Definition fiff_file.h:730
4x4 affine FIFF coordinate transform (FIFF_COORD_TRANS) annotated with source/destination coordinate-...
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.
QSharedPointer< FiffDirNode > SPtr
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
QSharedPointer< FiffStream > SPtr
std::unique_ptr< FiffTag > UPtr
Definition fiff_tag.h:164
Auxiliary projection data computed from MNEProjOp for efficient repeated application.
Eigen::VectorXf b
Eigen::VectorXf c
Eigen::VectorXi act
Eigen::VectorXf a
double sum_solids(const Eigen::Vector3f &from) const
void find_closest_on_surface_approx(const PointsT &r, int np, Eigen::VectorXi &nearest_tri, Eigen::VectorXf &dist, int nstep) const
Eigen::Vector3f project_to_triangle(int tri, float p, float q) const
static std::unique_ptr< MNESurface > read_bem_surface2(const QString &name, int which, bool add_geometry)
void triangle_coords(const Eigen::Vector3f &r, int tri, float &x, float &y, float &z) const
static std::unique_ptr< MNESurface > read_bem_surface(const QString &name, int which, bool add_geometry)
int project_to_surface(const MNEProjData *proj_data, const Eigen::Vector3f &r, float &distp) const
void decide_search_restriction(MNEProjData &p, int approx_best, int nstep, const Eigen::Vector3f &r) const
int nearest_triangle_point(const Eigen::Vector3f &r, const MNEProjData *user, int tri, float &x, float &y, float &z) const
void activate_neighbors(int start, Eigen::VectorXi &act, int nstep) const
std::vector< Eigen::VectorXi > neighbor_tri
std::vector< Eigen::VectorXi > neighbor_vert
int add_geometry_info(bool do_normals, bool check_too_many_neighbors)
FIFFLIB::FiffSparseMatrix dist
static double solid_angle(const Eigen::Vector3f &from, const MNELIB::MNETriangle &tri)
std::vector< MNETriangle > tris
Eigen::Matrix< float, Eigen::Dynamic, 3, Eigen::RowMajor > PointsT
int add_geometry_info2(bool do_normals)
Per-triangle geometric data for a cortical or BEM surface.
Eigen::Vector3f r13
Eigen::Vector3f nn
Eigen::Vector3f r2
Eigen::Vector3f r1
Eigen::Vector3f r3
Eigen::Vector3f r12