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