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 r1.row(i) = p_MNEBemSurf.rr.row(p_MNEBemSurf.itris(i, 0));
86 r12.row(i) = p_MNEBemSurf.rr.row(p_MNEBemSurf.itris(i, 1)) - r1.row(i);
87 r13.row(i) = p_MNEBemSurf.rr.row(p_MNEBemSurf.itris(i, 2)) - r1.row(i);
88 a(i) = r12.row(i) * r12.row(i).transpose();
89 b(i) = r13.row(i) * r13.row(i).transpose();
90 c(i) = r12.row(i) * r13.row(i).transpose();
91 }
92
93 if (!(p_MNEBemSurf.tri_nn.isZero(0))) {
94 nn = p_MNEBemSurf.tri_nn.cast<float>();
95 } else {
96 for (int i = 0; i < p_MNEBemSurf.ntri; ++i) {
97 nn.row(i) = r12.row(i).transpose().cross(r13.row(i).transpose()).transpose();
98 }
99 }
100 det = (a.array() * b.array() - c.array() * c.array()).matrix();
101}
102
103//=============================================================================================================
104
105bool MNEProjectToSurface::find_closest_on_surface(const MatrixXf& r, const int np, MatrixXf& rTri,
106 VectorXi& nearest, VectorXf& dist)
107{
108 // resize output
109 nearest.resize(np);
110 dist.resize(np);
111 rTri.resize(np, 3);
112
113 if (this->r1.isZero(0)) {
114 qDebug() << "No surface loaded to make the projection./n";
115 return false;
116 }
117 int bestTri = -1;
118 float bestDist = -1;
119 Vector3f rTriK;
120 for (int k = 0; k < np; ++k) {
121 /*
122 * To do: decide_search_restriction for the use in an iterative closest point to plane algorithm
123 * For now it's OK to go through all triangles.
124 */
125 if (!this->project_to_surface(r.row(k).transpose(), rTriK, bestTri, bestDist)) {
126 qDebug() << "The projection of point number " << k << " didn't work./n";
127 return false;
128 }
129 rTri.row(k) = rTriK.transpose();
130 nearest[k] = bestTri;
131 dist[k] = bestDist;
132 }
133 return true;
134}
135
136//=============================================================================================================
137
138bool MNEProjectToSurface::project_to_surface(const Vector3f& r, Vector3f& rTri, int& bestTri, float& bestDist)
139{
140 float p = 0, q = 0, p0 = 0, q0 = 0, dist0 = 0;
141 bestDist = 0.0f;
142 bestTri = -1;
143 for (int tri = 0; tri < a.size(); ++tri) {
144 if (!this->nearest_triangle_point(r, tri, p0, q0, dist0)) {
145 qDebug() << "The projection on triangle " << tri << " didn't work./n";
146 return false;
147 }
148
149 if ((bestTri < 0) || (std::fabs(dist0) < std::fabs(bestDist))) {
150 bestDist = dist0;
151 p = p0;
152 q = q0;
153 bestTri = tri;
154 }
155 }
156
157 if (bestTri >= 0) {
158 if (!this->project_to_triangle(rTri, p, q, bestTri)) {
159 qDebug() << "The coordinate transform to cartesian system didn't work./n";
160 return false;
161 }
162 return true;
163 }
164
165 qDebug() << "No best Triangle found./n";
166 return false;
167}
168
169//=============================================================================================================
170
171bool MNEProjectToSurface::nearest_triangle_point(const Vector3f& r, const int tri, float& p, float& q, float& dist)
172{
173 //Calculate some helpers
174 Vector3f rr = r - this->r1.row(tri).transpose(); //Vector from triangle corner #1 to r
175 float v1 = this->r12.row(tri) * rr;
176 float v2 = this->r13.row(tri) * rr;
177
178 //Calculate the orthogonal projection of the point r on the plane
179 dist = this->nn.row(tri) * rr;
180 p = (this->b(tri) * v1 - this->c(tri) * v2) / det(tri);
181 q = (this->a(tri) * v2 - this->c(tri) * v1) / det(tri);
182
183 //If the point projects into the triangle we are done
184 if (p >= 0.0 && p <= 1.0 && q >= 0.0 && q <= 1.0 && (p + q) <= 1.0) {
185 return true;
186 }
187
188 /*
189 * Tough: must investigate the sides
190 * We might do something intelligent here. However, for now it is ok
191 * to do it in the hard way
192 */
193 float p0, q0, t0, dist0, best, bestp, bestq;
194
195 /*
196 * Side 1 -> 2
197 */
198 p0 = p + (q * this->c(tri)) / this->a(tri);
199 // Place the point in the corner if it is not on the side
200 if (p0 < 0.0) {
201 p0 = 0.0;
202 } else if (p0 > 1.0) {
203 p0 = 1.0;
204 }
205 q0 = 0;
206 // Distance
207 dist0 = sqrt((p - p0) * (p - p0) * this->a(tri) +
208 (q - q0) * (q - q0) * this->b(tri) +
209 2 * (p - p0) * (q - q0) * this->c(tri) +
210 dist * dist);
211
212 best = dist0;
213 bestp = p0;
214 bestq = q0;
215 /*
216 * Side 2 -> 3
217 */
218 t0 = ((a(tri) - c(tri)) * (-p) + (b(tri) - c(tri)) * q) / (a(tri) + b(tri) - 2 * c(tri));
219 // Place the point in the corner if it is not on the side
220 if (t0 < 0.0) {
221 t0 = 0.0;
222 } else if (t0 > 1.0) {
223 t0 = 1.0;
224 }
225 p0 = 1.0 - t0;
226 q0 = t0;
227 // Distance
228 dist0 = sqrt((p - p0) * (p - p0) * this->a(tri) +
229 (q - q0) * (q - q0) * this->b(tri) +
230 2 * (p - p0) * (q - q0) * this->c(tri) +
231 dist * dist);
232 if (dist0 < best) {
233 best = dist0;
234 bestp = p0;
235 bestq = q0;
236 }
237 /*
238 * Side 1 -> 3
239 */
240 p0 = 0.0;
241 q0 = q + (p * c(tri)) / b(tri);
242 // Place the point in the corner if it is not on the side
243 if (q0 < 0.0) {
244 q0 = 0.0;
245
246 } else if (q0 > 1.0) {
247 q0 = 1.0;
248 }
249 // Distance
250 dist0 = sqrt((p - p0) * (p - p0) * this->a(tri) +
251 (q - q0) * (q - q0) * this->b(tri) +
252 2 * (p - p0) * (q - q0) * this->c(tri) +
253 dist * dist);
254 if (dist0 < best) {
255 best = dist0;
256 bestp = p0;
257 bestq = q0;
258 }
259 dist = best;
260 p = bestp;
261 q = bestq;
262 return true;
263}
264
265//=============================================================================================================
266
267bool MNEProjectToSurface::project_to_triangle(Vector3f& rTri, const float p, const float q, const int tri)
268{
269 rTri = (this->r1.row(tri) + p * this->r12.row(tri) + q * this->r13.row(tri)).transpose();
270 return true;
271}
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...