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 for (qint32 j = 0; j < 3; ++j) {
117 k = this->itris(i, j);
118
119 r(j, 0) = this->rr(k, 0);
120 r(j, 1) = this->rr(k, 1);
121 r(j, 2) = this->rr(k, 2);
122
123 this->tri_cent(i, 0) += this->rr(k, 0);
124 this->tri_cent(i, 1) += this->rr(k, 1);
125 this->tri_cent(i, 2) += this->rr(k, 2);
126 }
127 this->tri_cent.row(i) /= 3.0f;
128
129 //cross product {cross((r2-r1),(r3-r1))}
130 a = (r.row(1) - r.row(0)).transpose();
131 b = (r.row(2) - r.row(0)).transpose();
132 this->tri_nn(i, 0) = a(1) * b(2) - a(2) * b(1);
133 this->tri_nn(i, 1) = a(2) * b(0) - a(0) * b(2);
134 this->tri_nn(i, 2) = a(0) * b(1) - a(1) * b(0);
135
136 //area
137 size = this->tri_nn.row(i) * this->tri_nn.row(i).transpose();
138 size = std::pow(size, 0.5f);
139
140 this->tri_area(i) = size / 2.0f;
141 this->tri_nn.row(i) /= size;
142 }
143
144 std::fstream doc("./Output/tri_area.dat", std::ofstream::out | std::ofstream::trunc);
145 if (doc) // if succesfully opened
146 {
147 // instructions
148 doc << this->tri_area << "\n";
149 doc.close();
150 }
151
152 qInfo("Adding additional geometry info\n");
154
155 qInfo("[done]\n");
156
157 return true;
158}
159
160//=============================================================================================================
161
163{
164 int k, c, p, q;
165 bool found;
166
167 //Create neighboring triangle vector using temporary std::vector for efficient appending
168 {
169 std::vector<std::vector<int>> temp_ntri(this->itris.rows());
170 for (p = 0; p < this->itris.rows(); p++) {
171 for (k = 0; k < 3; k++) {
172 temp_ntri[this->itris(p, k)].push_back(p);
173 }
174 }
175 neighbor_tri.resize(this->itris.rows());
176 for (k = 0; k < static_cast<int>(temp_ntri.size()); k++) {
177 neighbor_tri[k] = Eigen::Map<Eigen::VectorXi>(temp_ntri[k].data(), temp_ntri[k].size());
178 }
179 }
180
181 //Create the neighboring vertices vector using temporary std::vector
182 {
183 std::vector<std::vector<int>> temp_nvert(this->np);
184 for (k = 0; k < this->np; k++) {
185 for (p = 0; p < neighbor_tri[k].size(); p++) {
186 //Fit in the other vertices of the neighboring triangle
187 for (c = 0; c < 3; c++) {
188 int vert = this->itris(neighbor_tri[k][p], c);
189
190 if (vert != k) {
191 found = false;
192
193 for (q = 0; q < static_cast<int>(temp_nvert[k].size()); q++) {
194 if (temp_nvert[k][q] == vert) {
195 found = true;
196 break;
197 }
198 }
199
200 if (!found) {
201 temp_nvert[k].push_back(vert);
202 }
203 }
204 }
205 }
206 }
207 neighbor_vert.resize(this->np);
208 for (k = 0; k < this->np; k++) {
209 neighbor_vert[k] = Eigen::Map<Eigen::VectorXi>(temp_nvert[k].data(), temp_nvert[k].size());
210 }
211 }
212
213 return true;
214}
215
216//=============================================================================================================
217
219{
220 //
221 // Accumulate the vertex normals from scratch (MNE-C mne_add_vertex_normals)
222 //
223 this->nn = NormalsT::Zero(this->np, 3);
224
225 for (qint32 p = 0; p < this->ntri; ++p) //check each triangle
226 {
227 for (qint32 j = 0; j < 3; ++j) {
228 int nodenr;
229 nodenr = this->itris(p, j); //find the corners(nodes) of the triangles
230 this->nn(nodenr, 0) += this->tri_nn(p, 0); //add the triangle normal to the nodenormal
231 this->nn(nodenr, 1) += this->tri_nn(p, 1);
232 this->nn(nodenr, 2) += this->tri_nn(p, 2);
233 }
234 }
235
236 // normalize
237 for (qint32 p = 0; p < this->np; ++p) {
238 const float size = this->nn.row(p).norm();
239 if (size > 0.0f)
240 this->nn.row(p) /= size;
241 }
242
243 return true;
244}
245
246
247//=============================================================================================================
248
250{
251 switch (id) {
253 return "Brain";
255 return "Skull";
257 return "Head";
259 return "Unknown";
260 default:
261 return "Unknown";
262 }
263}
264
265//=============================================================================================================
266
270static double edgeLenSq(const Eigen::MatrixX3d& rr, int v0, int v1)
271{
272 return (rr.row(v0) - rr.row(v1)).squaredNorm();
273}
274
275//=============================================================================================================
276
278 const MNEBemSurface& outerSkin,
279 const QList<int>& targetVertices)
280{
281 QList<MNEBemSurface> results;
282
283 if (outerSkin.np <= 0 || outerSkin.ntri <= 0) {
284 qWarning("MNEBemSurface::makeScalpSurfaces - Input surface is empty.");
285 return results;
286 }
287
288 for (int target : targetVertices) {
289 if (target >= outerSkin.np) {
290 // No decimation needed, just copy
291 results.append(MNEBemSurface(outerSkin));
292 continue;
293 }
294
295 // Work on a copy
296 MNEBemSurface surf(outerSkin);
297
298 // Build vertex-referenced rr as doubles, triangles as ints
299 // Use iterative edge collapse: find shortest edge, collapse to midpoint
300 Eigen::MatrixX3d rr = surf.rr.cast<double>();
301 Eigen::MatrixX3d nn = surf.nn.cast<double>();
302
303 // Build triangle list as std::vector for easy manipulation
304 int nTri = surf.ntri;
305 std::vector<std::array<int, 3>> tris(nTri);
306 for (int i = 0; i < nTri; ++i) {
307 tris[i] = {surf.itris(i, 0), surf.itris(i, 1), surf.itris(i, 2)};
308 }
309
310 // Track which vertices are alive
311 int currentNp = surf.np;
312 std::vector<bool> vertAlive(currentNp, true);
313 // Redirect map: when a vertex is collapsed, redirect to its replacement
314 std::vector<int> redirect(currentNp);
315 for (int i = 0; i < currentNp; ++i)
316 redirect[i] = i;
317
318 auto resolve = [&](int v) -> int {
319 while (redirect[v] != v)
320 v = redirect[v];
321 return v;
322 };
323
324 while (currentNp > target) {
325 // Find shortest edge in the mesh
326 double bestLen = std::numeric_limits<double>::max();
327 int bestTri = -1;
328 int bestEdge = -1;
329
330 for (int t = 0; t < static_cast<int>(tris.size()); ++t) {
331 int v0 = resolve(tris[t][0]);
332 int v1 = resolve(tris[t][1]);
333 int v2 = resolve(tris[t][2]);
334 // Skip degenerate triangles
335 if (v0 == v1 || v1 == v2 || v0 == v2)
336 continue;
337
338 double d01 = edgeLenSq(rr, v0, v1);
339 double d12 = edgeLenSq(rr, v1, v2);
340 double d20 = edgeLenSq(rr, v2, v0);
341
342 if (d01 < bestLen) {
343 bestLen = d01;
344 bestTri = t;
345 bestEdge = 0;
346 }
347 if (d12 < bestLen) {
348 bestLen = d12;
349 bestTri = t;
350 bestEdge = 1;
351 }
352 if (d20 < bestLen) {
353 bestLen = d20;
354 bestTri = t;
355 bestEdge = 2;
356 }
357 }
358
359 if (bestTri < 0)
360 break;
361
362 // Collapse edge: merge the two vertices into the midpoint
363 int va = resolve(tris[bestTri][bestEdge]);
364 int vb = resolve(tris[bestTri][(bestEdge + 1) % 3]);
365
366 // Place midpoint at va
367 rr.row(va) = (rr.row(va) + rr.row(vb)) * 0.5;
368 nn.row(va) = (nn.row(va) + nn.row(vb)).normalized();
369
370 // Redirect vb -> va
371 redirect[vb] = va;
372 vertAlive[vb] = false;
373 currentNp--;
374 }
375
376 // Rebuild compact vertex array and triangle array
377 std::vector<int> oldToNew(surf.np, -1);
378 int newNp = 0;
379 for (int i = 0; i < surf.np; ++i) {
380 if (vertAlive[i] && resolve(i) == i) {
381 oldToNew[i] = newNp++;
382 }
383 }
384
385 MNEBemSurface decimated;
386 decimated.id = surf.id;
387 decimated.coord_frame = surf.coord_frame;
388 decimated.sigma = surf.sigma;
389 decimated.np = newNp;
390
391 // Use Eigen types matching MNESurfaceOrVolume
392 MNESurfaceOrVolume::PointsT newRr(newNp, 3);
393 Eigen::MatrixX3f newNn(newNp, 3);
394 for (int i = 0; i < surf.np; ++i) {
395 if (oldToNew[i] >= 0) {
396 newRr.row(oldToNew[i]) = rr.row(i).cast<float>();
397 newNn.row(oldToNew[i]) = nn.row(i).cast<float>();
398 }
399 }
400 decimated.rr = newRr;
401 decimated.nn = newNn;
402
403 // Rebuild triangles
404 std::vector<std::array<int, 3>> newTris;
405 for (const auto& tri : tris) {
406 int a = oldToNew[resolve(tri[0])];
407 int b = oldToNew[resolve(tri[1])];
408 int c = oldToNew[resolve(tri[2])];
409 if (a >= 0 && b >= 0 && c >= 0 && a != b && b != c && a != c) {
410 newTris.push_back({a, b, c});
411 }
412 }
413
414 decimated.ntri = static_cast<int>(newTris.size());
415 MNESurfaceOrVolume::TrianglesT newItris(decimated.ntri, 3);
416 for (int i = 0; i < decimated.ntri; ++i) {
417 newItris(i, 0) = newTris[i][0];
418 newItris(i, 1) = newTris[i][1];
419 newItris(i, 2) = newTris[i][2];
420 }
421 decimated.itris = newItris;
422
423 // Recompute triangle data
424 decimated.addTriangleData();
425
426 results.append(decimated);
427 }
428
429 return results;
430}
#define FIFFV_BEM_SURF_ID_UNKNOWN
Definition fiff_file.h:741
#define FIFFV_BEM_SURF_ID_SKULL
Definition fiff_file.h:744
#define FIFFV_BEM_SURF_ID_HEAD
Definition fiff_file.h:745
#define FIFFV_BEM_SURF_ID_BRAIN
Definition fiff_file.h:742
Single closed BEM surface (triangulation, normals, conductivity).
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
Eigen::VectorXd tri_area
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