v2.0.0
Loading...
Searching...
No Matches
mne_project_to_surface.cpp
Go to the documentation of this file.
1//=============================================================================================================
22
23//=============================================================================================================
24// INCLUDES
25//=============================================================================================================
26
28
29//=============================================================================================================
30// INCLUDES
31//=============================================================================================================
32
33#include <mne/mne_bem_surface.h>
34
35//=============================================================================================================
36// QT INCLUDES
37//=============================================================================================================
38
39//=============================================================================================================
40// EIGEN INCLUDES
41//=============================================================================================================
42
43#include <Eigen/Geometry>
44
45//=============================================================================================================
46// USED NAMESPACES
47//=============================================================================================================
48
49using namespace MNELIB;
50using namespace Eigen;
51
52//=============================================================================================================
53// DEFINE GLOBAL METHODS
54//=============================================================================================================
55
56//=============================================================================================================
57// DEFINE MEMBER METHODS
58//=============================================================================================================
59
61: r1(MatrixX3f::Zero(1,3))
62, r12(MatrixX3f::Zero(1,3))
63, r13(MatrixX3f::Zero(1,3))
64, nn(MatrixX3f::Zero(1,3))
65, a(VectorXf::Zero(1))
66, b(VectorXf::Zero(1))
67, c(VectorXf::Zero(1))
68, det(VectorXf::Zero(1))
69{
70}
71
72//=============================================================================================================
73
75: r1(MatrixX3f::Zero(p_MNEBemSurf.ntri,3))
76, r12(MatrixX3f::Zero(p_MNEBemSurf.ntri,3))
77, r13(MatrixX3f::Zero(p_MNEBemSurf.ntri,3))
78, nn(MatrixX3f::Zero(p_MNEBemSurf.ntri,3))
79, a(VectorXf::Zero(p_MNEBemSurf.ntri))
80, b(VectorXf::Zero(p_MNEBemSurf.ntri))
81, c(VectorXf::Zero(p_MNEBemSurf.ntri))
82, det(VectorXf::Zero(p_MNEBemSurf.ntri))
83{
84 for (int i = 0; i < p_MNEBemSurf.ntri; ++i)
85 {
86 r1.row(i) = p_MNEBemSurf.rr.row(p_MNEBemSurf.itris(i,0));
87 r12.row(i) = p_MNEBemSurf.rr.row(p_MNEBemSurf.itris(i,1)) - r1.row(i);
88 r13.row(i) = p_MNEBemSurf.rr.row(p_MNEBemSurf.itris(i,2)) - r1.row(i);
89 a(i) = r12.row(i) * r12.row(i).transpose();
90 b(i) = r13.row(i) * r13.row(i).transpose();
91 c(i) = r12.row(i) * r13.row(i).transpose();
92 }
93
94 if (!(p_MNEBemSurf.tri_nn.isZero(0)))
95 {
96 nn = p_MNEBemSurf.tri_nn.cast<float>();
97 }
98 else
99 {
100 for (int i = 0; i < p_MNEBemSurf.ntri; ++i)
101 {
102 nn.row(i) = r12.row(i).transpose().cross(r13.row(i).transpose()).transpose();
103 }
104 }
105 det = (a.array()*b.array() - c.array()*c.array()).matrix();
106}
107
108//=============================================================================================================
109
110bool MNEProjectToSurface::find_closest_on_surface(const MatrixXf &r, const int np, MatrixXf &rTri,
111 VectorXi &nearest, VectorXf &dist)
112{
113 // resize output
114 nearest.resize(np);
115 dist.resize(np);
116 rTri.resize(np,3);
117
118 if (this->r1.isZero(0))
119 {
120 qDebug() << "No surface loaded to make the projection./n";
121 return false;
122 }
123 int bestTri = -1;
124 float bestDist = -1;
125 Vector3f rTriK;
126 for (int k = 0; k < np; ++k)
127 {
128 /*
129 * To do: decide_search_restriction for the use in an iterative closest point to plane algorithm
130 * For now it's OK to go through all triangles.
131 */
132 if (!this->project_to_surface(r.row(k).transpose(), rTriK, bestTri, bestDist))
133 {
134 qDebug() << "The projection of point number " << k << " didn't work./n";
135 return false;
136 }
137 rTri.row(k) = rTriK.transpose();
138 nearest[k] = bestTri;
139 dist[k] = bestDist;
140 }
141 return true;
142}
143
144//=============================================================================================================
145
146bool MNEProjectToSurface::project_to_surface(const Vector3f &r, Vector3f &rTri, int &bestTri, float &bestDist)
147{
148 float p = 0, q = 0, p0 = 0, q0 = 0, dist0 = 0;
149 bestDist = 0.0f;
150 bestTri = -1;
151 for (int tri = 0; tri < a .size(); ++tri)
152 {
153 if (!this->nearest_triangle_point(r, tri, p0, q0, dist0))
154 {
155 qDebug() << "The projection on triangle " << tri << " didn't work./n";
156 return false;
157 }
158
159 if ((bestTri < 0) || (std::fabs(dist0) < std::fabs(bestDist)))
160 {
161 bestDist = dist0;
162 p = p0;
163 q = q0;
164 bestTri = tri;
165 }
166 }
167
168 if (bestTri >= 0)
169 {
170 if (!this->project_to_triangle(rTri, p, q, bestTri))
171 {
172 qDebug() << "The coordinate transform to cartesian system didn't work./n";
173 return false;
174 }
175 return true;
176 }
177
178 qDebug() << "No best Triangle found./n";
179 return false;
180}
181
182//=============================================================================================================
183
184bool MNEProjectToSurface::nearest_triangle_point(const Vector3f &r, const int tri, float &p, float &q, float &dist)
185{
186 //Calculate some helpers
187 Vector3f rr = r - this->r1.row(tri).transpose(); //Vector from triangle corner #1 to r
188 float v1 = this->r12.row(tri)*rr;
189 float v2 = this->r13.row(tri)*rr;
190
191 //Calculate the orthogonal projection of the point r on the plane
192 dist = this->nn.row(tri)*rr;
193 p = (this->b(tri)*v1 - this->c(tri)*v2)/det(tri);
194 q = (this->a(tri)*v2 - this->c(tri)*v1)/det(tri);
195
196 //If the point projects into the triangle we are done
197 if (p >= 0.0 && p <= 1.0 && q >= 0.0 && q <= 1.0 && (p+q) <= 1.0)
198 {
199 return true;
200 }
201
202 /*
203 * Tough: must investigate the sides
204 * We might do something intelligent here. However, for now it is ok
205 * to do it in the hard way
206 */
207 float p0, q0, t0, dist0, best, bestp, bestq;
208
209 /*
210 * Side 1 -> 2
211 */
212 p0 = p + (q * this->c(tri)) / this->a(tri);
213 // Place the point in the corner if it is not on the side
214 if (p0 < 0.0)
215 {
216 p0 = 0.0;
217 }
218 else if (p0 > 1.0)
219 {
220 p0 = 1.0;
221 }
222 q0 = 0;
223 // Distance
224 dist0 = sqrt((p-p0)*(p-p0)*this->a(tri) +
225 (q-q0)*(q-q0)*this->b(tri) +
226 2*(p-p0)*(q-q0)*this->c(tri) +
227 dist*dist);
228
229 best = dist0;
230 bestp = p0;
231 bestq = q0;
232 /*
233 * Side 2 -> 3
234 */
235 t0 = ((a(tri)-c(tri))*(-p) + (b(tri)-c(tri))*q)/(a(tri)+b(tri)-2*c(tri));
236 // Place the point in the corner if it is not on the side
237 if (t0 < 0.0)
238 {
239 t0 = 0.0;
240 }
241 else if (t0 > 1.0)
242 {
243 t0 = 1.0;
244 }
245 p0 = 1.0 - t0;
246 q0 = t0;
247 // Distance
248 dist0 = sqrt((p-p0)*(p-p0)*this->a(tri) +
249 (q-q0)*(q-q0)*this->b(tri) +
250 2*(p-p0)*(q-q0)*this->c(tri) +
251 dist*dist);
252 if (dist0 < best)
253 {
254 best = dist0;
255 bestp = p0;
256 bestq = q0;
257 }
258 /*
259 * Side 1 -> 3
260 */
261 p0 = 0.0;
262 q0 = q + (p * c(tri))/b(tri);
263 // Place the point in the corner if it is not on the side
264 if (q0 < 0.0)
265 {
266 q0 = 0.0;
267
268 }
269 else if (q0 > 1.0)
270 {
271 q0 = 1.0;
272 }
273 // Distance
274 dist0 = sqrt((p-p0)*(p-p0)*this->a(tri) +
275 (q-q0)*(q-q0)*this->b(tri) +
276 2*(p-p0)*(q-q0)*this->c(tri) +
277 dist*dist);
278 if (dist0 < best)
279 {
280 best = dist0;
281 bestp = p0;
282 bestq = q0;
283 }
284 dist = best;
285 p = bestp;
286 q = bestq;
287 return true;
288}
289
290//=============================================================================================================
291
292bool MNEProjectToSurface::project_to_triangle(Vector3f &rTri, const float p, const float q, const int tri)
293{
294 rTri = (this->r1.row(tri) + p*this->r12.row(tri) + q*this->r13.row(tri)).transpose();
295 return true;
296}
Single closed BEM surface (triangulation, normals, conductivity).
Geometric projection of a 3D point onto the closest cortex triangle.
Core MNE data structures (source spaces, source estimates, hemispheres).
BEM surface provides geometry information.
Eigen::MatrixX3d tri_nn
bool find_closest_on_surface(const Eigen::MatrixXf &r, const int np, Eigen::MatrixXf &rTri, Eigen::VectorXi &nearest, Eigen::VectorXf &dist)
Project a set of points onto the surface and return the nearest triangle and signed distance per poin...