v2.0.0
Loading...
Searching...
No Matches
mne_bem_surface.cpp
Go to the documentation of this file.
1//=============================================================================================================
21
22//=============================================================================================================
23// INCLUDES
24//=============================================================================================================
25
26#include "mne_bem_surface.h"
27#include <fstream>
28
29//=============================================================================================================
30// USED NAMESPACES
31//=============================================================================================================
32
33using namespace MNELIB;
34using namespace Eigen;
35using namespace FIFFLIB;
36
37//=============================================================================================================
38// DEFINE MEMBER METHODS
39//=============================================================================================================
40
42: MNESurface()
43, tri_cent(MatrixX3d::Zero(0,3))
44, tri_nn(MatrixX3d::Zero(0,3))
45, tri_area(VectorXd::Zero(0))
46{
47 id = -1;
48 np = -1;
49 ntri = -1;
50 coord_frame = -1;
51 sigma = -1;
52}
53
54//=============================================================================================================
55
57: MNESurface()
58, tri_cent(p_MNEBemSurface.tri_cent)
59, tri_nn(p_MNEBemSurface.tri_nn)
60, tri_area(p_MNEBemSurface.tri_area)
61{
62 id = p_MNEBemSurface.id;
63 np = p_MNEBemSurface.np;
64 ntri = p_MNEBemSurface.ntri;
65 coord_frame = p_MNEBemSurface.coord_frame;
66 sigma = p_MNEBemSurface.sigma;
67 rr = p_MNEBemSurface.rr;
68 nn = p_MNEBemSurface.nn;
69 itris = p_MNEBemSurface.itris;
70 neighbor_tri = p_MNEBemSurface.neighbor_tri;
71 neighbor_vert = p_MNEBemSurface.neighbor_vert;
72}
73
74//=============================================================================================================
75
79
80//=============================================================================================================
81
83{
84 id = -1;
85 np = -1;
86 ntri = -1;
87 coord_frame = -1;
88 sigma = -1;
89 rr.resize(0, 3);
90 nn.resize(0, 3);
91 itris.resize(0, 3);
92 tri_cent = MatrixX3d::Zero(0,3);
93 tri_nn = MatrixX3d::Zero(0,3);
94 tri_area = VectorXd::Zero(0);
95 neighbor_tri.clear();
96 neighbor_vert.clear();
97}
98
99//=============================================================================================================
100
102{
103 //
104 // Main triangulation
105 //
106 qInfo("\tCompleting triangulation info...");
107 this->tri_cent = MatrixX3d::Zero(this->ntri,3);
108 this->tri_nn = MatrixX3d::Zero(this->ntri,3);
109 this->tri_area = VectorXd::Zero(this->ntri);
110
111 Matrix3d r;
112 Vector3d a, b;
113 int k = 0;
114 float size = 0;
115 for (qint32 i = 0; i < this->ntri; ++i)
116 {
117 for ( qint32 j = 0; j < 3; ++j)
118 {
119 k = this->itris(i, j);
120
121 r(j,0) = this->rr(k, 0);
122 r(j,1) = this->rr(k, 1);
123 r(j,2) = this->rr(k, 2);
124
125 this->tri_cent(i, 0) += this->rr(k, 0);
126 this->tri_cent(i, 1) += this->rr(k, 1);
127 this->tri_cent(i, 2) += this->rr(k, 2);
128 }
129 this->tri_cent.row(i) /= 3.0f;
130
131 //cross product {cross((r2-r1),(r3-r1))}
132 a = (r.row(1) - r.row(0 )).transpose();
133 b = (r.row(2) - r.row(0)).transpose();
134 this->tri_nn(i,0) = a(1)*b(2)-a(2)*b(1);
135 this->tri_nn(i,1) = a(2)*b(0)-a(0)*b(2);
136 this->tri_nn(i,2) = a(0)*b(1)-a(1)*b(0);
137
138 //area
139 size = this->tri_nn.row(i)*this->tri_nn.row(i).transpose();
140 size = std::pow(size, 0.5f );
141
142 this->tri_area(i) = size/2.0f;
143 this->tri_nn.row(i) /= size;
144 }
145
146 std::fstream doc("./Output/tri_area.dat", std::ofstream::out | std::ofstream::trunc);
147 if(doc) // if succesfully opened
148 {
149 // instructions
150 doc << this->tri_area << "\n";
151 doc.close();
152 }
153
154 qInfo("Adding additional geometry info\n");
156
157 qInfo("[done]\n");
158
159 return true;
160}
161
162//=============================================================================================================
163
165{
166 int k,c,p,q;
167 bool found;
168
169 //Create neighboring triangle vector using temporary std::vector for efficient appending
170 {
171 std::vector<std::vector<int>> temp_ntri(this->itris.rows());
172 for (p = 0; p < this->itris.rows(); p++) {
173 for (k = 0; k < 3; k++) {
174 temp_ntri[this->itris(p,k)].push_back(p);
175 }
176 }
177 neighbor_tri.resize(this->itris.rows());
178 for (k = 0; k < static_cast<int>(temp_ntri.size()); k++) {
179 neighbor_tri[k] = Eigen::Map<Eigen::VectorXi>(temp_ntri[k].data(), temp_ntri[k].size());
180 }
181 }
182
183 //Create the neighboring vertices vector using temporary std::vector
184 {
185 std::vector<std::vector<int>> temp_nvert(this->np);
186 for (k = 0; k < this->np; k++) {
187 for (p = 0; p < neighbor_tri[k].size(); p++) {
188 //Fit in the other vertices of the neighboring triangle
189 for (c = 0; c < 3; c++) {
190 int vert = this->itris(neighbor_tri[k][p], c);
191
192 if (vert != k) {
193 found = false;
194
195 for (q = 0; q < static_cast<int>(temp_nvert[k].size()); q++) {
196 if (temp_nvert[k][q] == vert) {
197 found = true;
198 break;
199 }
200 }
201
202 if(!found) {
203 temp_nvert[k].push_back(vert);
204 }
205 }
206 }
207 }
208 }
209 neighbor_vert.resize(this->np);
210 for (k = 0; k < this->np; k++) {
211 neighbor_vert[k] = Eigen::Map<Eigen::VectorXi>(temp_nvert[k].data(), temp_nvert[k].size());
212 }
213 }
214
215 return true;
216}
217
218//=============================================================================================================
219
221{
222
223 //
224 // Accumulate the vertex normals
225 //
226
227// this->nn.resize(this->np,3);
228
229 for (qint32 p = 0; p < this->ntri; ++p) //check each triangle
230 {
231 for (qint32 j = 0; j < 3 ; ++j)
232 {
233 int nodenr;
234 nodenr = this->itris(p,j); //find the corners(nodes) of the triangles
235 this->nn(nodenr,0) += this->tri_nn(p,0); //add the triangle normal to the nodenormal
236 this->nn(nodenr,1) += this->tri_nn(p,1);
237 this->nn(nodenr,2) += this->tri_nn(p,2);
238 }
239 }
240
241 // normalize
242 for (qint32 p = 0; p < this->np; ++p)
243 {
244 float size = 0;
245 size = this->nn.row(p)*this->nn.row(p).transpose();
246 size = std::pow(size, 0.5f );
247 this->nn.row(p) /= size;
248 }
249
250return true;
251}
252
253//=============================================================================================================
254
256{
257 if(this->id <=0)
258 this->id=FIFFV_MNE_SURF_UNKNOWN;
259 if(this->sigma>0.0)
260 p_pStream->write_float(FIFF_BEM_SIGMA, &this->sigma);
261 p_pStream->write_int(FIFF_BEM_SURF_ID, &this->id);
262 p_pStream->write_int(FIFF_MNE_COORD_FRAME, &this->coord_frame);
263 p_pStream->write_int(FIFF_BEM_SURF_NNODE, &this->np);
264 p_pStream->write_int(FIFF_BEM_SURF_NTRI, &this->ntri);
265 p_pStream->write_float_matrix(FIFF_BEM_SURF_NODES, Eigen::MatrixXf(this->rr));
266 if (this->ntri > 0)
267 p_pStream->write_int_matrix(FIFF_BEM_SURF_TRIANGLES, Eigen::MatrixXi(this->itris.array() + 1));
268 p_pStream->write_float_matrix(FIFF_BEM_SURF_NORMALS, Eigen::MatrixXf(this->nn));
269}
270
271//=============================================================================================================
272
274{
275 switch(id) {
276 case FIFFV_BEM_SURF_ID_BRAIN: return "Brain";
277 case FIFFV_BEM_SURF_ID_SKULL: return "Skull";
278 case FIFFV_BEM_SURF_ID_HEAD: return "Head";
279 case FIFFV_BEM_SURF_ID_UNKNOWN: return "Unknown";
280 default: return "Unknown";
281 }
282}
283
284//=============================================================================================================
285
289static double edgeLenSq(const Eigen::MatrixX3d& rr, int v0, int v1)
290{
291 return (rr.row(v0) - rr.row(v1)).squaredNorm();
292}
293
294//=============================================================================================================
295
297 const MNEBemSurface& outerSkin,
298 const QList<int>& targetVertices)
299{
300 QList<MNEBemSurface> results;
301
302 if (outerSkin.np <= 0 || outerSkin.ntri <= 0) {
303 qWarning("MNEBemSurface::makeScalpSurfaces - Input surface is empty.");
304 return results;
305 }
306
307 for (int target : targetVertices) {
308 if (target >= outerSkin.np) {
309 // No decimation needed, just copy
310 results.append(MNEBemSurface(outerSkin));
311 continue;
312 }
313
314 // Work on a copy
315 MNEBemSurface surf(outerSkin);
316
317 // Build vertex-referenced rr as doubles, triangles as ints
318 // Use iterative edge collapse: find shortest edge, collapse to midpoint
319 Eigen::MatrixX3d rr = surf.rr.cast<double>();
320 Eigen::MatrixX3d nn = surf.nn.cast<double>();
321
322 // Build triangle list as std::vector for easy manipulation
323 int nTri = surf.ntri;
324 std::vector<std::array<int,3>> tris(nTri);
325 for (int i = 0; i < nTri; ++i) {
326 tris[i] = {surf.itris(i, 0), surf.itris(i, 1), surf.itris(i, 2)};
327 }
328
329 // Track which vertices are alive
330 int currentNp = surf.np;
331 std::vector<bool> vertAlive(currentNp, true);
332 // Redirect map: when a vertex is collapsed, redirect to its replacement
333 std::vector<int> redirect(currentNp);
334 for (int i = 0; i < currentNp; ++i)
335 redirect[i] = i;
336
337 auto resolve = [&](int v) -> int {
338 while (redirect[v] != v)
339 v = redirect[v];
340 return v;
341 };
342
343 while (currentNp > target) {
344 // Find shortest edge in the mesh
345 double bestLen = std::numeric_limits<double>::max();
346 int bestTri = -1;
347 int bestEdge = -1;
348
349 for (int t = 0; t < static_cast<int>(tris.size()); ++t) {
350 int v0 = resolve(tris[t][0]);
351 int v1 = resolve(tris[t][1]);
352 int v2 = resolve(tris[t][2]);
353 // Skip degenerate triangles
354 if (v0 == v1 || v1 == v2 || v0 == v2)
355 continue;
356
357 double d01 = edgeLenSq(rr, v0, v1);
358 double d12 = edgeLenSq(rr, v1, v2);
359 double d20 = edgeLenSq(rr, v2, v0);
360
361 if (d01 < bestLen) { bestLen = d01; bestTri = t; bestEdge = 0; }
362 if (d12 < bestLen) { bestLen = d12; bestTri = t; bestEdge = 1; }
363 if (d20 < bestLen) { bestLen = d20; bestTri = t; bestEdge = 2; }
364 }
365
366 if (bestTri < 0)
367 break;
368
369 // Collapse edge: merge the two vertices into the midpoint
370 int va = resolve(tris[bestTri][bestEdge]);
371 int vb = resolve(tris[bestTri][(bestEdge + 1) % 3]);
372
373 // Place midpoint at va
374 rr.row(va) = (rr.row(va) + rr.row(vb)) * 0.5;
375 nn.row(va) = (nn.row(va) + nn.row(vb)).normalized();
376
377 // Redirect vb -> va
378 redirect[vb] = va;
379 vertAlive[vb] = false;
380 currentNp--;
381 }
382
383 // Rebuild compact vertex array and triangle array
384 std::vector<int> oldToNew(surf.np, -1);
385 int newNp = 0;
386 for (int i = 0; i < surf.np; ++i) {
387 if (vertAlive[i] && resolve(i) == i) {
388 oldToNew[i] = newNp++;
389 }
390 }
391
392 MNEBemSurface decimated;
393 decimated.id = surf.id;
394 decimated.coord_frame = surf.coord_frame;
395 decimated.sigma = surf.sigma;
396 decimated.np = newNp;
397
398 // Use Eigen types matching MNESurfaceOrVolume
399 MNESurfaceOrVolume::PointsT newRr(newNp, 3);
400 Eigen::MatrixX3f newNn(newNp, 3);
401 for (int i = 0; i < surf.np; ++i) {
402 if (oldToNew[i] >= 0) {
403 newRr.row(oldToNew[i]) = rr.row(i).cast<float>();
404 newNn.row(oldToNew[i]) = nn.row(i).cast<float>();
405 }
406 }
407 decimated.rr = newRr;
408 decimated.nn = newNn;
409
410 // Rebuild triangles
411 std::vector<std::array<int,3>> newTris;
412 for (const auto& tri : tris) {
413 int a = oldToNew[resolve(tri[0])];
414 int b = oldToNew[resolve(tri[1])];
415 int c = oldToNew[resolve(tri[2])];
416 if (a >= 0 && b >= 0 && c >= 0 && a != b && b != c && a != c) {
417 newTris.push_back({a, b, c});
418 }
419 }
420
421 decimated.ntri = static_cast<int>(newTris.size());
422 MNESurfaceOrVolume::TrianglesT newItris(decimated.ntri, 3);
423 for (int i = 0; i < decimated.ntri; ++i) {
424 newItris(i, 0) = newTris[i][0];
425 newItris(i, 1) = newTris[i][1];
426 newItris(i, 2) = newTris[i][2];
427 }
428 decimated.itris = newItris;
429
430 // Recompute triangle data
431 decimated.addTriangleData();
432
433 results.append(decimated);
434 }
435
436 return results;
437}
Single closed BEM surface (triangulation, normals, conductivity).
#define FIFF_MNE_COORD_FRAME
#define FIFFV_MNE_SURF_UNKNOWN
#define FIFFV_BEM_SURF_ID_UNKNOWN
Definition fiff_file.h:741
#define FIFF_BEM_SURF_NNODE
Definition fiff_file.h:726
#define FIFFV_BEM_SURF_ID_SKULL
Definition fiff_file.h:744
#define FIFF_BEM_SURF_NODES
Definition fiff_file.h:728
#define FIFF_BEM_SURF_TRIANGLES
Definition fiff_file.h:729
#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 FIFFV_BEM_SURF_ID_HEAD
Definition fiff_file.h:745
#define FIFF_BEM_SURF_NORMALS
Definition fiff_file.h:730
#define FIFFV_BEM_SURF_ID_BRAIN
Definition fiff_file.h:742
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
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)
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)
Eigen::VectorXd tri_area
void writeToStream(FIFFLIB::FiffStream *p_pStream)
Eigen::MatrixX3d tri_nn
Eigen::MatrixX3d tri_cent
static QString id_name(int id)
static QList< MNEBemSurface > makeScalpSurfaces(const MNEBemSurface &outerSkin, const QList< int > &targetVertices={2562, 10242, 40962})
std::vector< Eigen::VectorXi > neighbor_tri
std::vector< Eigen::VectorXi > neighbor_vert
Eigen::Matrix< int, Eigen::Dynamic, 3, Eigen::RowMajor > TrianglesT
std::vector< MNETriangle > tris
Eigen::Matrix< float, Eigen::Dynamic, 3, Eigen::RowMajor > PointsT