164 qint32 p_iClusterSize,
168 QString p_sMethod)
const
170 printf(
"Cluster forward solution using %s.\n", p_sMethod.toUtf8().constData());
177 qWarning(
"MNEForwardSolution::cluster_forward_solution - Cov names do match with info channel names but have a different naming convention.");
180 qWarning(
"MNEForwardSolution::cluster_forward_solution - Cov channel names do not match with info channel names.");
192 printf(
"Error: Fixed orientation not implemented jet!\n");
205 MatrixXd t_G_Whitened(0,0);
206 bool t_bUseWhitened =
false;
214 MatrixXd p_outWhitener;
215 qint32 p_outNumNonZero;
217 this->
prepare_forward(p_pInfo, p_pNoise_cov,
false, p_outFwdInfo, t_G_Whitened, p_outNoiseCov, p_outWhitener, p_outNumNonZero);
218 printf(
"\tWhitening the forward solution.\n");
220 t_G_Whitened = p_outWhitener*t_G_Whitened;
221 t_bUseWhitened =
true;
232 for(qint32 h = 0; h < this->
src.
size(); ++h )
240 for(qint32 j = 0; j < h; ++j)
241 offset += this->
src[j].nuse;
244 printf(
"Cluster Left Hemisphere\n");
246 printf(
"Cluster Right Hemisphere\n");
248 const Annotation annotation = p_AnnotationSet[h];
250 VectorXi label_ids = t_CurrentColorTable.
getLabelIds();
253 VectorXi vertno_labeled = VectorXi::Zero(this->
src[h].vertno.rows());
256 for(qint32 i = 0; i < vertno_labeled.rows(); ++i)
257 vertno_labeled[i] = p_AnnotationSet[h].getLabelIds()[this->
src[h].vertno[i]];
260 QList<RegionData> m_qListRegionDataIn;
265 for (qint32 i = 0; i < label_ids.rows(); ++i)
267 if (label_ids[i] != 0)
269 QString curr_name = t_CurrentColorTable.
struct_names[i];
270 printf(
"\tCluster %d / %ld %s...", i+1, label_ids.rows(), curr_name.toUtf8().constData());
275 VectorXi idcs = VectorXi::Zero(vertno_labeled.rows());
279 for(qint32 j = 0; j < vertno_labeled.rows(); ++j)
281 if(vertno_labeled[j] == label_ids[i])
287 idcs.conservativeResize(c);
290 MatrixXd t_G(this->
sol->data.rows(), idcs.rows()*3);
291 MatrixXd t_G_Whitened_Roi(t_G_Whitened.rows(), idcs.rows()*3);
293 for(qint32 j = 0; j < idcs.rows(); ++j)
295 t_G.block(0, j*3, t_G.rows(), 3) = this->
sol->data.block(0, (idcs[j]+offset)*3, t_G.rows(), 3);
297 t_G_Whitened_Roi.block(0, j*3, t_G_Whitened_Roi.rows(), 3) = t_G_Whitened.block(0, (idcs[j]+offset)*3, t_G_Whitened_Roi.rows(), 3);
300 qint32 nSens = t_G.rows();
301 qint32 nSources = t_G.cols()/3;
309 t_sensG.
nClusters = ceil((
double)nSources/(
double)p_iClusterSize);
315 printf(
"%d Cluster(s)... ", t_sensG.
nClusters);
318 t_sensG.
matRoiG = MatrixXd(t_G.cols()/3, 3*nSens);
320 t_sensG.
matRoiGWhitened = MatrixXd(t_G_Whitened_Roi.cols()/3, 3*nSens);
322 for(qint32 j = 0; j < nSens; ++j)
324 for(qint32
k = 0;
k < t_sensG.
matRoiG.rows(); ++
k)
325 t_sensG.
matRoiG.block(
k,j*3,1,3) = t_G.block(j,
k*3,1,3);
335 m_qListRegionDataIn.append(t_sensG);
341 printf(
"failed! Label contains no sources.\n");
349 printf(
"Clustering... ");
350 QFuture< RegionDataOut > res;
351 res = QtConcurrent::mapped(m_qListRegionDataIn, &RegionData::cluster);
352 res.waitForFinished();
357 MatrixXd t_G_partial;
361 QList<RegionData>::const_iterator itIn;
362 itIn = m_qListRegionDataIn.begin();
363 QFuture<RegionDataOut>::const_iterator itOut;
364 for (itOut = res.constBegin(); itOut != res.constEnd(); ++itOut)
366 nClusters = itOut->ctrs.rows();
367 nSens = itOut->ctrs.cols()/3;
368 t_G_partial = MatrixXd::Zero(nSens, nClusters*3);
376 for(qint32 j = 0; j < nSens; ++j)
377 for(qint32
k = 0;
k < nClusters; ++
k)
378 t_G_partial.block(j,
k*3, 1, 3) = itOut->ctrs.block(
k,j*3,1,3);
383 for(qint32 j = 0; j < nClusters; ++j)
385 VectorXi clusterIdcs = VectorXi::Zero(itOut->roiIdx.rows());
386 VectorXd clusterDistance = VectorXd::Zero(itOut->roiIdx.rows());
387 MatrixX3f clusterSource_rr = MatrixX3f::Zero(itOut->roiIdx.rows(), 3);
388 qint32 nClusterIdcs = 0;
389 for(qint32
k = 0;
k < itOut->roiIdx.rows(); ++
k)
391 if(itOut->roiIdx[
k] == j)
393 clusterIdcs[nClusterIdcs] = itIn->idcs[
k];
395 qint32 offset = h == 0 ? 0 : this->
src[0].nuse;
396 clusterSource_rr.row(nClusterIdcs) = this->
source_rr.row(offset + itIn->idcs[
k]);
397 clusterDistance[nClusterIdcs] = itOut->D(
k,j);
401 clusterIdcs.conservativeResize(nClusterIdcs);
402 clusterSource_rr.conservativeResize(nClusterIdcs,3);
403 clusterDistance.conservativeResize(nClusterIdcs);
405 VectorXi clusterVertnos = VectorXi::Zero(clusterIdcs.size());
406 for(qint32
k = 0;
k < clusterVertnos.size(); ++
k)
407 clusterVertnos(
k) = this->
src[h].vertno[clusterIdcs(
k)];
409 p_fwdOut.
src[h].cluster_info.clusterVertnos.append(clusterVertnos);
410 p_fwdOut.
src[h].cluster_info.clusterSource_rr.append(clusterSource_rr);
411 p_fwdOut.
src[h].cluster_info.clusterDistances.append(clusterDistance);
412 p_fwdOut.
src[h].cluster_info.clusterLabelIds.append(label_ids[itOut->iLabelIdxOut]);
413 p_fwdOut.
src[h].cluster_info.clusterLabelNames.append(t_CurrentColorTable.
getNames()[itOut->iLabelIdxOut]);
419 if(t_G_partial.rows() > 0 && t_G_partial.cols() > 0)
421 t_G_new.conservativeResize(t_G_partial.rows(), t_G_new.cols() + t_G_partial.cols());
422 t_G_new.block(0, t_G_new.cols() - t_G_partial.cols(), t_G_new.rows(), t_G_partial.cols()) = t_G_partial;
425 for(qint32
k = 0;
k < nClusters; ++
k)
429 double sqec = sqrt((itIn->matRoiGOrig.block(0, j*3, itIn->matRoiGOrig.rows(), 3) - t_G_partial.block(0,
k*3, t_G_partial.rows(), 3)).array().pow(2).sum());
430 double sqec_min = sqec;
433 for(qint32 j = 1; j < itIn->idcs.rows(); ++j)
435 sqec = sqrt((itIn->matRoiGOrig.block(0, j*3, itIn->matRoiGOrig.rows(), 3) - t_G_partial.block(0,
k*3, t_G_partial.rows(), 3)).array().pow(2).sum());
448 qint32 sel_idx = itIn->idcs[j_min];
450 p_fwdOut.
src[h].cluster_info.centroidVertno.append(this->
src[h].vertno[sel_idx]);
451 p_fwdOut.
src[h].cluster_info.centroidSource_rr.append(this->
src[h].rr.row(this->src[h].vertno[sel_idx]));
457 p_fwdOut.
src[h].vertno[count] = p_fwdOut.
src[h].cluster_info.clusterLabelIds[count];
474 p_fwdOut.
src[h].vertno.conservativeResize(count);
482 qint32 totalNumOfClust = 0;
483 for (qint32 h = 0; h < 2; ++h)
484 totalNumOfClust += p_fwdOut.
src[h].cluster_info.clusterVertnos.
size();
487 p_D = MatrixXd::Zero(this->
sol->data.cols(), totalNumOfClust);
489 p_D = MatrixXd::Zero(this->
sol->data.cols(), totalNumOfClust*3);
496 qint32 currentCluster = 0;
497 for (qint32 h = 0; h < 2; ++h)
499 int hemiOffset = h == 0 ? 0 : t_vertnos[0].size();
500 for(qint32 i = 0; i < p_fwdOut.
src[h].cluster_info.clusterVertnos.
size(); ++i)
509 idx_sel.array() += hemiOffset;
513 double selectWeight = 1.0/idx_sel.size();
516 for(qint32 j = 0; j < idx_sel.size(); ++j)
517 p_D.col(currentCluster)[idx_sel(j)] = selectWeight;
521 qint32 clustOffset = currentCluster*3;
522 for(qint32 j = 0; j < idx_sel.size(); ++j)
524 qint32 idx_sel_Offset = idx_sel(j)*3;
526 p_D(idx_sel_Offset,clustOffset) = selectWeight;
528 p_D(idx_sel_Offset+1, clustOffset+1) = selectWeight;
530 p_D(idx_sel_Offset+2, clustOffset+2) = selectWeight;
692 p_fwdOut.
sol->data = t_G_new;
693 p_fwdOut.
sol->ncol = t_G_new.cols();
1099 MatrixXd &p_outWhitener,
1100 qint32 &p_outNumNonZero)
const
1102 QStringList fwd_ch_names, ch_names;
1103 for(qint32 i = 0; i < this->
info.
chs.size(); ++i)
1104 fwd_ch_names << this->
info.
chs[i].ch_name;
1107 for(qint32 i = 0; i < p_info.
chs.size(); ++i)
1108 if(!p_info.
bads.contains(p_info.
chs[i].ch_name)
1109 && !p_noise_cov.
bads.contains(p_info.
chs[i].ch_name)
1110 && p_noise_cov.
names.contains(p_info.
chs[i].ch_name)
1111 && fwd_ch_names.contains(p_info.
chs[i].ch_name))
1112 ch_names << p_info.
chs[i].ch_name;
1114 qint32 n_chan = ch_names.size();
1115 printf(
"Computing inverse operator with %d channels.\n", n_chan);
1123 p_outNumNonZero = 0;
1124 VectorXi t_vecNonZero = VectorXi::Zero(n_chan);
1125 for(qint32 i = 0; i < p_outNoiseCov.
eig.rows(); ++i)
1127 if(p_outNoiseCov.
eig[i] > 0)
1129 t_vecNonZero[p_outNumNonZero] = i;
1133 if(p_outNumNonZero > 0)
1134 t_vecNonZero.conservativeResize(p_outNumNonZero);
1136 if(p_outNumNonZero > 0)
1140 qWarning(
"Warning in MNEForwardSolution::prepare_forward: if (p_pca) havent been debugged.");
1141 p_outWhitener = MatrixXd::Zero(n_chan, p_outNumNonZero);
1143 for(qint32 i = 0; i < p_outNumNonZero; ++i)
1144 p_outWhitener.col(t_vecNonZero[i]) = p_outNoiseCov.
eigvec.col(t_vecNonZero[i]).array() / sqrt(p_outNoiseCov.
eig(t_vecNonZero[i]));
1145 printf(
"\tReducing data rank to %d.\n", p_outNumNonZero);
1149 printf(
"Creating non pca whitener.\n");
1150 p_outWhitener = MatrixXd::Zero(n_chan, n_chan);
1151 for(qint32 i = 0; i < p_outNumNonZero; ++i)
1152 p_outWhitener(t_vecNonZero[i],t_vecNonZero[i]) = 1.0 / sqrt(p_outNoiseCov.
eig(t_vecNonZero[i]));
1154 p_outWhitener *= p_outNoiseCov.
eigvec;
1158 VectorXi fwd_idx = VectorXi::Zero(ch_names.size());
1159 VectorXi info_idx = VectorXi::Zero(ch_names.size());
1161 qint32 count_fwd_idx = 0;
1162 qint32 count_info_idx = 0;
1163 for(qint32 i = 0; i < ch_names.size(); ++i)
1165 idx = fwd_ch_names.indexOf(ch_names[i]);
1168 fwd_idx[count_fwd_idx] = idx;
1171 idx = p_info.
ch_names.indexOf(ch_names[i]);
1174 info_idx[count_info_idx] = idx;
1178 fwd_idx.conservativeResize(count_fwd_idx);
1179 info_idx.conservativeResize(count_info_idx);
1181 gain.resize(count_fwd_idx, this->
sol->data.cols());
1182 for(qint32 i = 0; i < count_fwd_idx; ++i)
1183 gain.row(i) = this->
sol->data.row(fwd_idx[i]);
1185 p_outFwdInfo = p_info.
pick_info(info_idx);
1187 printf(
"\tTotal rank is %d\n", p_outNumNonZero);
1196 const QStringList& include,
1197 const QStringList& exclude,
1202 printf(
"Reading forward solution from %s...\n", t_pStream->streamName().toUtf8().constData());
1203 if(!t_pStream->open())
1208 QList<FiffDirNode::SPtr> fwds = t_pStream->dirtree()->dir_tree_find(FIFFB_MNE_FORWARD_SOLUTION);
1210 if (fwds.size() == 0)
1213 std::cout <<
"No forward solutions in " << t_pStream->streamName().toUtf8().constData();
1219 QList<FiffDirNode::SPtr> parent_mri = t_pStream->dirtree()->dir_tree_find(FIFFB_MNE_PARENT_MRI_FILE);
1220 if (parent_mri.size() == 0)
1223 std::cout <<
"No parent MRI information in " << t_pStream->streamName().toUtf8().constData();
1231 std::cout <<
"Could not read the source spaces\n";
1236 for(qint32
k = 0;
k < t_SourceSpace.
size(); ++
k)
1245 bads = t_pStream->read_bad_channels(t_pStream->dirtree());
1248 printf(
"\t%lld bad channels ( ",bads.size());
1249 for(qint32 i = 0; i < bads.size(); ++i)
1250 printf(
"\"%s\" ", bads[i].toUtf8().constData());
1261 for(qint32
k = 0;
k < fwds.size(); ++
k)
1263 if(!fwds[
k]->find_tag(t_pStream, FIFF_MNE_INCLUDED_METHODS, t_pTag))
1266 std::cout <<
"Methods not listed for one of the forward solutions\n";
1269 if (*t_pTag->toInt() == FIFFV_MNE_MEG)
1271 printf(
"MEG solution found\n");
1274 else if(*t_pTag->toInt() == FIFFV_MNE_EEG)
1276 printf(
"EEG solution found\n");
1283 if (read_one(t_pStream, megnode, megfwd))
1285 if (megfwd.
source_ori == FIFFV_MNE_FIXED_ORI)
1286 ori = QString(
"fixed");
1288 ori = QString(
"free");
1289 printf(
"\tRead MEG forward solution (%d sources, %d channels, %s orientations)\n", megfwd.
nsource,megfwd.
nchan,ori.toUtf8().constData());
1292 if (read_one(t_pStream, eegnode, eegfwd))
1294 if (eegfwd.
source_ori == FIFFV_MNE_FIXED_ORI)
1295 ori = QString(
"fixed");
1297 ori = QString(
"free");
1298 printf(
"\tRead EEG forward solution (%d sources, %d channels, %s orientations)\n", eegfwd.
nsource,eegfwd.
nchan,ori.toUtf8().constData());
1308 if (megfwd.
sol->data.cols() != eegfwd.
sol->data.cols() ||
1314 std::cout <<
"The MEG and EEG forward solutions do not match\n";
1319 fwd.
sol->data = MatrixXd(megfwd.
sol->nrow + eegfwd.
sol->nrow, megfwd.
sol->ncol);
1321 fwd.
sol->data.block(0,0,megfwd.
sol->nrow,megfwd.
sol->ncol) = megfwd.
sol->data;
1322 fwd.
sol->data.block(megfwd.
sol->nrow,0,eegfwd.
sol->nrow,eegfwd.
sol->ncol) = eegfwd.
sol->data;
1323 fwd.
sol->nrow = megfwd.
sol->nrow + eegfwd.
sol->nrow;
1324 fwd.
sol->row_names.append(eegfwd.
sol->row_names);
1337 printf(
"\tMEG and EEG forward solutions combined\n");
1350 std::cout <<
"MRI/head coordinate transformation not found\n";
1363 std::cout <<
"MRI/head coordinate transformation not found\n";
1372 t_pStream->read_meas_info_base(t_pStream->dirtree(), fwd.
info);
1382 std::cout <<
"Only forward solutions computed in MRI or head coordinates are acceptable";
1389 for(qint32
k = 0;
k < t_SourceSpace.
size(); ++
k)
1390 nuse += t_SourceSpace[
k].nuse;
1393 qDebug() <<
"Source spaces do not match the forward solution.\n";
1397 printf(
"\tSource spaces transformed to the forward solution coordinate frame\n");
1398 fwd.
src = t_SourceSpace;
1407 for(qint32
k = 0;
k < t_SourceSpace.
size();++
k)
1409 for(qint32 q = 0; q < t_SourceSpace[
k].nuse; ++q)
1411 fwd.
source_rr.block(q,0,1,3) = t_SourceSpace[
k].rr.block(t_SourceSpace[
k].vertno(q),0,1,3);
1412 fwd.
source_nn.block(q,0,1,3) = t_SourceSpace[
k].nn.block(t_SourceSpace[
k].vertno(q),0,1,3);
1414 nuse += t_SourceSpace[
k].nuse;
1421 printf(
"\tChanging to fixed-orientation forward solution...");
1423 MatrixXd tmp = fwd.
source_nn.transpose().cast<
double>();
1425 fwd.
sol->data *= (*fix_rot);
1431 SparseMatrix<double> t_matKron;
1432 SparseMatrix<double> t_eye(3,3);
1433 for (qint32 i = 0; i < 3; ++i)
1434 t_eye.insert(i,i) = 1.0f;
1435 t_matKron = kroneckerProduct(*fix_rot,t_eye);
1448 printf(
"\tConverting to surface-based source orientations...");
1450 bool use_ave_nn =
false;
1451 if(t_SourceSpace[0].patch_inds.
size() > 0)
1454 printf(
"\tAverage patch normals will be employed in the rotation to the local surface coordinates...\n");
1462 qWarning(
"Warning source_ori: Rotating the source coordinate system haven't been verified --> Singular Vectors U are different from MATLAB!");
1464 for(qint32
k = 0;
k < t_SourceSpace.
size();++
k)
1467 for (qint32 q = 0; q < t_SourceSpace[
k].nuse; ++q)
1468 fwd.
source_rr.block(q+nuse,0,1,3) = t_SourceSpace[
k].rr.block(t_SourceSpace[
k].vertno(q),0,1,3);
1470 for (qint32 p = 0; p < t_SourceSpace[
k].nuse; ++p)
1478 VectorXi t_vIdx = t_SourceSpace[
k].pinfo[t_SourceSpace[
k].patch_inds[p]];
1479 Matrix3Xf t_nn(3, t_vIdx.size());
1480 for(qint32 i = 0; i < t_vIdx.size(); ++i)
1481 t_nn.col(i) = t_SourceSpace[
k].nn.block(t_vIdx[i],0,1,3).transpose();
1482 nn = t_nn.rowwise().sum();
1483 nn.array() /= nn.norm();
1486 nn = t_SourceSpace[
k].nn.block(t_SourceSpace[
k].vertno(p),0,1,3).transpose();
1488 Matrix3f tmp = Matrix3f::Identity(nn.rows(), nn.rows()) - nn*nn.transpose();
1490 JacobiSVD<MatrixXf> t_svd(tmp, Eigen::ComputeThinU);
1492 VectorXf t_s = t_svd.singularValues();
1493 MatrixXf U = t_svd.matrixU();
1494 MNEMath::sort<float>(t_s, U);
1499 if ((nn.transpose() * U.block(0,2,3,1))(0,0) < 0)
1501 fwd.
source_nn.block(pp, 0, 3, 3) = U.transpose();
1504 nuse += t_SourceSpace[
k].nuse;
1506 MatrixXd tmp = fwd.
source_nn.transpose().cast<
double>();
1509 fwd.
sol->data *= *surf_rot;
1513 SparseMatrix<double> t_matKron;
1514 SparseMatrix<double> t_eye(3,3);
1515 for (qint32 i = 0; i < 3; ++i)
1516 t_eye.insert(i,i) = 1.0f;
1517 t_matKron = kroneckerProduct(*surf_rot,t_eye);
1525 printf(
"\tCartesian source orientations...");
1528 for(qint32
k = 0;
k < t_SourceSpace.
size(); ++
k)
1530 for (qint32 q = 0; q < t_SourceSpace[
k].nuse; ++q)
1531 fwd.
source_rr.block(q+nuse,0,1,3) = t_SourceSpace[
k].rr.block(t_SourceSpace[
k].vertno(q),0,1,3);
1533 nuse += t_SourceSpace[
k].nuse;
1536 MatrixXf t_ones = MatrixXf::Ones(fwd.
nsource,1);
1537 Matrix3f t_eye = Matrix3f::Identity();
1538 fwd.
source_nn = kroneckerProduct(t_ones,t_eye);
1546 QStringList exclude_bads = exclude;
1547 if (bads.size() > 0)
1549 for(qint32
k = 0;
k < bads.size(); ++
k)
1550 if(!exclude_bads.contains(bads[
k],Qt::CaseInsensitive))
1551 exclude_bads << bads[
k];
1837 MatrixX3f matSourceVertLeft, matSourceVertRight, matSourcePositions;
1839 if(lPickedLabels.isEmpty()) {
1840 qWarning() <<
"MNEForwardSolution::getSourcePositionsByLabel - picked label list is empty. Returning.";
1841 return matSourcePositions;
1844 if(tSurfSetInflated.
isEmpty()) {
1845 qWarning() <<
"MNEForwardSolution::getSourcePositionsByLabel - tSurfSetInflated is empty. Returning.";
1846 return matSourcePositions;
1850 for(
int j = 0; j < this->
src[0].vertno.rows(); ++j) {
1851 for(
int k = 0;
k < lPickedLabels.size();
k++) {
1852 if(this->
src[0].vertno(j) == lPickedLabels.at(
k).label_id) {
1853 matSourceVertLeft.conservativeResize(matSourceVertLeft.rows()+1,3);
1854 matSourceVertLeft.row(matSourceVertLeft.rows()-1) = tSurfSetInflated[0].rr().row(this->
src[0].cluster_info.centroidVertno.at(j)) - tSurfSetInflated[0].offset().transpose();
1860 for(
int j = 0; j < this->
src[1].vertno.rows(); ++j) {
1861 for(
int k = 0;
k < lPickedLabels.size();
k++) {
1862 if(this->
src[1].vertno(j) == lPickedLabels.at(
k).label_id) {
1863 matSourceVertRight.conservativeResize(matSourceVertRight.rows()+1,3);
1864 matSourceVertRight.row(matSourceVertRight.rows()-1) = tSurfSetInflated[1].rr().row(this->
src[1].cluster_info.centroidVertno.at(j)) - tSurfSetInflated[1].offset().transpose();
1870 for(
int j = 0; j < this->
src[0].vertno.rows(); ++j) {
1871 for(
int k = 0;
k < lPickedLabels.size();
k++) {
1872 for(
int l = 0; l < lPickedLabels.at(
k).vertices.rows(); l++) {
1873 if(this->
src[0].vertno(j) == lPickedLabels.at(
k).vertices(l) && lPickedLabels.at(
k).hemi == 0) {
1874 matSourceVertLeft.conservativeResize(matSourceVertLeft.rows()+1,3);
1875 matSourceVertLeft.row(matSourceVertLeft.rows()-1) = tSurfSetInflated[0].rr().row(this->
src[0].vertno(j)) - tSurfSetInflated[0].offset().transpose();
1882 for(
int j = 0; j < this->
src[1].vertno.rows(); ++j) {
1883 for(
int k = 0;
k < lPickedLabels.size();
k++) {
1884 for(
int l = 0; l < lPickedLabels.at(
k).vertices.rows(); l++) {
1885 if(this->
src[1].vertno(j) == lPickedLabels.at(
k).vertices(l) && lPickedLabels.at(
k).hemi == 1) {
1886 matSourceVertRight.conservativeResize(matSourceVertRight.rows()+1,3);
1887 matSourceVertRight.row(matSourceVertRight.rows()-1) = tSurfSetInflated[1].rr().row(this->
src[1].vertno(j)) - tSurfSetInflated[1].offset().transpose();
1895 matSourcePositions.resize(matSourceVertLeft.rows()+matSourceVertRight.rows(),3);
1896 matSourcePositions << matSourceVertLeft, matSourceVertRight;
1898 return matSourcePositions;