//"PER-CELL" METHOD: FLAG DUPLICATE POINTS //ADDITIONAL PARAMETERS (1): // [0] -> (double*) maxSquareDistBetweenPoints: max square distance between points bool GeometricalAnalysisTools::flagDuplicatePointsInACellAtLevel( const DgmOctree::octreeCell& cell, void** additionalParameters, NormalizedProgress* nProgress/*=0*/) { //parameter(s) double minDistBetweenPoints = *static_cast<double*>(additionalParameters[0]); //structure for nearest neighbors search DgmOctree::NearestNeighboursSphericalSearchStruct nNSS; nNSS.level = cell.level; nNSS.prepare(static_cast<PointCoordinateType>(minDistBetweenPoints),cell.parentOctree->getCellSize(nNSS.level)); cell.parentOctree->getCellPos(cell.truncatedCode,cell.level,nNSS.cellPos,true); cell.parentOctree->computeCellCenter(nNSS.cellPos,cell.level,nNSS.cellCenter); unsigned n = cell.points->size(); //number of points in the current cell //for each point in the cell for (unsigned i=0; i<n; ++i) { //don't process points already flagged as 'duplicate' if (cell.points->getPointScalarValue(i) == 0) { cell.points->getPoint(i,nNSS.queryPoint); //look for neighbors in a sphere //warning: there may be more points at the end of nNSS.pointsInNeighbourhood than the actual nearest neighbors (neighborCount)! unsigned neighborCount = cell.parentOctree->findNeighborsInASphereStartingFromCell(nNSS,minDistBetweenPoints,false); if (neighborCount > 1) //the point itself lies in the neighborhood { unsigned iIndex = cell.points->getPointGlobalIndex(i); for (unsigned j=0; j<neighborCount; ++j) { if (nNSS.pointsInNeighbourhood[j].pointIndex != iIndex) { //flag this point as 'duplicate' cell.points->getAssociatedCloud()->setPointScalarValue(nNSS.pointsInNeighbourhood[j].pointIndex,static_cast<ScalarType>(1)); } } } } if (nProgress && !nProgress->oneStep()) return false; } return true; }
//"PER-CELL" METHOD: LOCAL DENSITY //ADDITIONNAL PARAMETERS (2): // [0] -> (PointCoordinateType*) kernelRadius : spherical neighborhood radius // [1] -> (ScalarType*) sphereVolume : spherical neighborhood volume bool GeometricalAnalysisTools::computePointsDensityInACellAtLevel( const DgmOctree::octreeCell& cell, void** additionalParameters, NormalizedProgress* nProgress/*=0*/) { //parameter(s) PointCoordinateType radius = *static_cast<PointCoordinateType*>(additionalParameters[0]); double dimensionalCoef = *static_cast<double*>(additionalParameters[1]); assert(dimensionalCoef > 0); //structure for nearest neighbors search DgmOctree::NearestNeighboursSphericalSearchStruct nNSS; nNSS.level = cell.level; nNSS.prepare(radius,cell.parentOctree->getCellSize(nNSS.level)); cell.parentOctree->getCellPos(cell.truncatedCode,cell.level,nNSS.cellPos,true); cell.parentOctree->computeCellCenter(nNSS.cellPos,cell.level,nNSS.cellCenter); unsigned n = cell.points->size(); //number of points in the current cell //for each point in the cell for (unsigned i=0; i<n; ++i) { cell.points->getPoint(i,nNSS.queryPoint); //look for neighbors inside a sphere //warning: there may be more points at the end of nNSS.pointsInNeighbourhood than the actual nearest neighbors (neighborCount)! unsigned neighborCount = cell.parentOctree->findNeighborsInASphereStartingFromCell(nNSS,radius,false); ScalarType density = static_cast<ScalarType>(neighborCount/dimensionalCoef); cell.points->setPointScalarValue(i,density); if (nProgress && !nProgress->oneStep()) return false; } return true; }
ReferenceCloud* CloudSamplingTools::resampleCloudSpatially(GenericIndexedCloudPersist* theCloud, PointCoordinateType minDistance, DgmOctree* theOctree/*=0*/, GenericProgressCallback* progressCb/*=0*/) { assert(theCloud); unsigned cloudSize = theCloud->size(); DgmOctree *_theOctree=theOctree; if (!_theOctree) { _theOctree = new DgmOctree(theCloud); if (_theOctree->build()<(int)cloudSize) { delete _theOctree; return 0; } } ReferenceCloud* sampledCloud = new ReferenceCloud(theCloud); if (!sampledCloud->reserve(cloudSize)) { if (!theOctree) delete _theOctree; return 0; } GenericChunkedArray<1,bool>* markers = new GenericChunkedArray<1,bool>(); //DGM: upgraded from vector, as this can be quite huge! if (!markers->resize(cloudSize,true,true)) { markers->release(); if (!theOctree) delete _theOctree; delete sampledCloud; return 0; } NormalizedProgress* normProgress=0; if (progressCb) { progressCb->setInfo("Spatial resampling"); normProgress = new NormalizedProgress(progressCb,cloudSize); progressCb->reset(); progressCb->start(); } //for each point in the cloud that is still 'marked', we look //for its neighbors and remove their own marks DgmOctree::NearestNeighboursSphericalSearchStruct nss; nss.level = _theOctree->findBestLevelForAGivenNeighbourhoodSizeExtraction(minDistance); markers->placeIteratorAtBegining(); for (unsigned i=0; i<cloudSize; i++, markers->forwardIterator()) { //progress indicator if (normProgress && !normProgress->oneStep()) { //cancel process delete sampledCloud; sampledCloud = 0; break; } //no mark? we skip this point if (!markers->getCurrentValue()) continue; //init neighbor search structure theCloud->getPoint(i,nss.queryPoint); bool inbounds = false; _theOctree->getTheCellPosWhichIncludesThePoint(&nss.queryPoint, nss.cellPos, nss.level, inbounds); nss.truncatedCellCode = (inbounds ? _theOctree->generateTruncatedCellCode(nss.cellPos, nss.level) : DgmOctree::INVALID_CELL_CODE); _theOctree->computeCellCenter(nss.cellPos, nss.level, nss.cellCenter); //add the points that lie in the same cell (faster) { ReferenceCloud* Y = _theOctree->getPointsInCell(nss.truncatedCellCode, nss.level, true); unsigned count = Y->size(); try { nss.pointsInNeighbourhood.resize(count); } catch (std::bad_alloc) //out of memory { //stop process delete sampledCloud; sampledCloud = 0; break; } unsigned realCount = 0; DgmOctree::NeighboursSet::iterator it = nss.pointsInNeighbourhood.begin(); for (unsigned j=0; j<count; ++j) { unsigned index = Y->getPointGlobalIndex(j); if (index != i && markers->getValue(index)) //no need to add the point itself and those already flagged off { it->point = Y->getPointPersistentPtr(j); it->pointIndex = index; ++it; ++realCount; } } nss.pointsInNeighbourhood.resize(realCount); //should be ok as realCount<=count nss.alreadyVisitedNeighbourhoodSize = 1; } #ifdef TEST_CELLS_FOR_SPHERICAL_NN nss.pointsInSphericalNeighbourhood.clear(); #endif nss.prepare(minDistance,_theOctree->getCellSize(nss.level)); //look for neighbors and 'de-mark' them { unsigned nbNeighbors = _theOctree->findNeighborsInASphereStartingFromCell(nss, minDistance, false); DgmOctree::NeighboursSet::iterator it = nss.pointsInNeighbourhood.begin(); for (unsigned j=0; j<nbNeighbors; ++j, ++it) if (it->pointIndex != i) markers->setValue(it->pointIndex,false); } //At this stage, the ith point is the only one marked in a radius of <minDistance>. //Therefore it will necessarily be in the final cloud! if (!sampledCloud->addPointIndex(i)) //not enough memory { //stop process delete sampledCloud; sampledCloud = 0; break; } } if(normProgress) { delete normProgress; normProgress = 0; } if (!theOctree) delete _theOctree; markers->release(); return sampledCloud; }
bool CloudSamplingTools::applyNoiseFilterAtLevel( const DgmOctree::octreeCell& cell, void** additionalParameters, NormalizedProgress* nProgress/*=0*/) { ReferenceCloud* cloud = static_cast<ReferenceCloud*>(additionalParameters[0]); PointCoordinateType kernelRadius = *static_cast<PointCoordinateType*>(additionalParameters[1]); double nSigma = *static_cast<double*>(additionalParameters[2]); bool removeIsolatedPoints = *static_cast<bool*>(additionalParameters[3]); bool useKnn = *static_cast<bool*>(additionalParameters[4]); int knn = *static_cast<int*>(additionalParameters[5]); bool useAbsoluteError = *static_cast<bool*>(additionalParameters[6]); double absoluteError = *static_cast<double*>(additionalParameters[7]); //structure for nearest neighbors search DgmOctree::NearestNeighboursSphericalSearchStruct nNSS; nNSS.level = cell.level; nNSS.prepare(kernelRadius,cell.parentOctree->getCellSize(nNSS.level)); if (useKnn) { nNSS.minNumberOfNeighbors = knn; } cell.parentOctree->getCellPos(cell.truncatedCode,cell.level,nNSS.cellPos,true); cell.parentOctree->computeCellCenter(nNSS.cellPos,cell.level,nNSS.cellCenter); unsigned n = cell.points->size(); //number of points in the current cell //for each point in the cell for (unsigned i=0; i<n; ++i) { cell.points->getPoint(i,nNSS.queryPoint); //look for neighbors (either inside a sphere or the k nearest ones) //warning: there may be more points at the end of nNSS.pointsInNeighbourhood than the actual nearest neighbors (neighborCount)! unsigned neighborCount = 0; if (useKnn) neighborCount = cell.parentOctree->findNearestNeighborsStartingFromCell(nNSS); else neighborCount = cell.parentOctree->findNeighborsInASphereStartingFromCell(nNSS,kernelRadius,false); if (neighborCount > 3) //we want 3 points or more (other than the point itself!) { //find the query point in the nearest neighbors set and place it at the end const unsigned globalIndex = cell.points->getPointGlobalIndex(i); unsigned localIndex = 0; while (localIndex < neighborCount && nNSS.pointsInNeighbourhood[localIndex].pointIndex != globalIndex) ++localIndex; //the query point should be in the nearest neighbors set! assert(localIndex < neighborCount); if (localIndex+1 < neighborCount) //no need to swap with another point if it's already at the end! { std::swap(nNSS.pointsInNeighbourhood[localIndex],nNSS.pointsInNeighbourhood[neighborCount-1]); } unsigned realNeighborCount = neighborCount-1; DgmOctreeReferenceCloud neighboursCloud(&nNSS.pointsInNeighbourhood,realNeighborCount); //we don't take the query point into account! Neighbourhood Z(&neighboursCloud); const PointCoordinateType* lsPlane = Z.getLSPlane(); if (lsPlane) { double maxD = absoluteError; if (!useAbsoluteError) { //compute the std. dev. to this plane double sum_d = 0; double sum_d2 = 0; for (unsigned j=0; j<realNeighborCount; ++j) { const CCVector3* P = neighboursCloud.getPoint(j); double d = CCLib::DistanceComputationTools::computePoint2PlaneDistance(P,lsPlane); sum_d += d; sum_d2 += d*d; } double stddev = sqrt(fabs(sum_d2*realNeighborCount - sum_d*sum_d))/realNeighborCount; maxD = stddev * nSigma; } //distance from the query point to the plane double d = fabs(CCLib::DistanceComputationTools::computePoint2PlaneDistance(&nNSS.queryPoint,lsPlane)); if (d <= maxD) cloud->addPointIndex(globalIndex); } else { //TODO: ??? } } else { //not enough points to fit a plane AND compute distances to it if (!removeIsolatedPoints) { //we keep the point unsigned globalIndex = cell.points->getPointGlobalIndex(i); cloud->addPointIndex(globalIndex); } } if (nProgress && !nProgress->oneStep()) { return false; } } return true; }
//"PER-CELL" METHOD: CURVATURE ESTIMATION (WITH QUADRATIC FIT) //ADDITIONAL PARAMETERS (2): // [0] -> (CC_CURVATURE_TYPE*) cType : curvature type // [1] -> (PointCoordinateType*) radius : sphere radius bool GeometricalAnalysisTools::computeCellCurvatureAtLevel( const DgmOctree::octreeCell& cell, void** additionalParameters, NormalizedProgress* nProgress/*=0*/) { //parameters Neighbourhood::CC_CURVATURE_TYPE cType = *static_cast<Neighbourhood::CC_CURVATURE_TYPE*>(additionalParameters[0]); PointCoordinateType radius = *static_cast<PointCoordinateType*>(additionalParameters[1]); //structure for nearest neighbors search DgmOctree::NearestNeighboursSphericalSearchStruct nNSS; nNSS.level = cell.level; nNSS.prepare(radius,cell.parentOctree->getCellSize(nNSS.level)); cell.parentOctree->getCellPos(cell.truncatedCode,cell.level,nNSS.cellPos,true); cell.parentOctree->computeCellCenter(nNSS.cellPos,cell.level,nNSS.cellCenter); unsigned n = cell.points->size(); //number of points in the current cell //we already know some of the neighbours: the points in the current cell! { try { nNSS.pointsInNeighbourhood.resize(n); } catch (.../*const std::bad_alloc&*/) //out of memory { return false; } DgmOctree::NeighboursSet::iterator it = nNSS.pointsInNeighbourhood.begin(); for (unsigned i=0; i<n; ++i,++it) { it->point = cell.points->getPointPersistentPtr(i); it->pointIndex = cell.points->getPointGlobalIndex(i); } } nNSS.alreadyVisitedNeighbourhoodSize = 1; //for each point in the cell for (unsigned i=0; i<n; ++i) { ScalarType curv = NAN_VALUE; cell.points->getPoint(i,nNSS.queryPoint); //look for neighbors in a sphere //warning: there may be more points at the end of nNSS.pointsInNeighbourhood than the actual nearest neighbors (neighborCount)! unsigned neighborCount = cell.parentOctree->findNeighborsInASphereStartingFromCell(nNSS,radius,false); //neighborCount = std::min(neighborCount,16); if (neighborCount > 5) { //current point index unsigned index = cell.points->getPointGlobalIndex(i); //current point index in neighbourhood (to compute curvature at the right position!) unsigned indexInNeighbourhood = 0; DgmOctreeReferenceCloud neighboursCloud(&nNSS.pointsInNeighbourhood,neighborCount); Neighbourhood Z(&neighboursCloud); //look for local index for (unsigned j=0;j<neighborCount;++j) { if (nNSS.pointsInNeighbourhood[j].pointIndex == index) { indexInNeighbourhood = j; break; } } curv = Z.computeCurvature(indexInNeighbourhood,cType); } cell.points->setPointScalarValue(i,curv); if (nProgress && !nProgress->oneStep()) return false; } return true; }
//"PER-CELL" METHOD: ROUGHNESS ESTIMATION (LEAST SQUARES PLANE FIT) //ADDITIONNAL PARAMETERS (1): // [0] -> (PointCoordinateType*) kernelRadius : neighbourhood radius bool GeometricalAnalysisTools::computePointsRoughnessInACellAtLevel(const DgmOctree::octreeCell& cell, void** additionalParameters, NormalizedProgress* nProgress/*=0*/) { //parameter(s) PointCoordinateType radius = *static_cast<PointCoordinateType*>(additionalParameters[0]); //structure for nearest neighbors search DgmOctree::NearestNeighboursSphericalSearchStruct nNSS; nNSS.level = cell.level; nNSS.prepare(radius,cell.parentOctree->getCellSize(nNSS.level)); cell.parentOctree->getCellPos(cell.truncatedCode,cell.level,nNSS.cellPos,true); cell.parentOctree->computeCellCenter(nNSS.cellPos,cell.level,nNSS.cellCenter); unsigned n = cell.points->size(); //number of points in the current cell //for each point in the cell for (unsigned i=0; i<n; ++i) { ScalarType d = NAN_VALUE; cell.points->getPoint(i,nNSS.queryPoint); //look for neighbors inside a sphere //warning: there may be more points at the end of nNSS.pointsInNeighbourhood than the actual nearest neighbors (= neighborCount)! unsigned neighborCount = cell.parentOctree->findNeighborsInASphereStartingFromCell(nNSS,radius,false); if (neighborCount > 3) { //find the query point in the nearest neighbors set and place it at the end const unsigned globalIndex = cell.points->getPointGlobalIndex(i); unsigned localIndex = 0; while (localIndex < neighborCount && nNSS.pointsInNeighbourhood[localIndex].pointIndex != globalIndex) ++localIndex; //the query point should be in the nearest neighbors set! assert(localIndex < neighborCount); if (localIndex+1 < neighborCount) //no need to swap with another point if it's already at the end! { std::swap(nNSS.pointsInNeighbourhood[localIndex],nNSS.pointsInNeighbourhood[neighborCount-1]); } DgmOctreeReferenceCloud neighboursCloud(&nNSS.pointsInNeighbourhood,neighborCount-1); //we don't take the query point into account! Neighbourhood Z(&neighboursCloud); const PointCoordinateType* lsPlane = Z.getLSPlane(); if (lsPlane) d = fabs(DistanceComputationTools::computePoint2PlaneDistance(&nNSS.queryPoint,lsPlane)); //swap the points back to their original position (DGM: not necessary) //if (localIndex+1 < neighborCount) //{ // std::swap(nNSS.pointsInNeighbourhood[localIndex],nNSS.pointsInNeighbourhood[neighborCount-1]); //} } cell.points->setPointScalarValue(i,d); if (nProgress && !nProgress->oneStep()) return false; } return true; }
//FONCTION "CELLULAIRE" DE CALCUL DU FILTRE GAUSSIEN (PAR PROJECTION SUR LE PLAN AUX MOINDRES CARRES) //DETAIL DES PARAMETRES ADDITIONNELS (2) : // [0] -> (PointCoordinateType*) sigma : gauss function sigma // [1] -> (PointCoordinateType*) sigmaSF : used when in "bilateral modality" - if -1 pure gaussian filtering is performed bool ScalarFieldTools::computeCellGaussianFilter( const DgmOctree::octreeCell& cell, void** additionalParameters, NormalizedProgress* nProgress/*=0*/) { //variables additionnelles PointCoordinateType sigma = *((PointCoordinateType*)additionalParameters[0]); PointCoordinateType sigmaSF = *((PointCoordinateType*)additionalParameters[1]); //we use only the squared value of sigma PointCoordinateType sigma2 = 2*sigma*sigma; PointCoordinateType radius = 3*sigma; //2.5 sigma > 99% //we use only the squared value of sigmaSF PointCoordinateType sigmaSF2 = 2*sigmaSF*sigmaSF; //number of points inside the current cell unsigned n = cell.points->size(); //structures pour la recherche de voisinages SPECIFIQUES DgmOctree::NearestNeighboursSphericalSearchStruct nNSS; nNSS.level = cell.level; nNSS.prepare(radius,cell.parentOctree->getCellSize(nNSS.level)); cell.parentOctree->getCellPos(cell.truncatedCode,cell.level,nNSS.cellPos,true); cell.parentOctree->computeCellCenter(nNSS.cellPos,cell.level,nNSS.cellCenter); //we already know the points lying in the first cell (this is the one we are treating :) try { nNSS.pointsInNeighbourhood.resize(n); } catch (.../*const std::bad_alloc&*/) //out of memory { return false; } DgmOctree::NeighboursSet::iterator it = nNSS.pointsInNeighbourhood.begin(); { for (unsigned i=0; i<n; ++i,++it) { it->point = cell.points->getPointPersistentPtr(i); it->pointIndex = cell.points->getPointGlobalIndex(i); } } nNSS.alreadyVisitedNeighbourhoodSize = 1; const GenericIndexedCloudPersist* cloud = cell.points->getAssociatedCloud(); //Pure Gaussian Filtering if (sigmaSF == -1) { for (unsigned i=0; i<n; ++i) //for each point in cell { //we get the points inside a spherical neighbourhood (radius: '3*sigma') cell.points->getPoint(i,nNSS.queryPoint); //warning: there may be more points at the end of nNSS.pointsInNeighbourhood than the actual nearest neighbors (k)! unsigned k = cell.parentOctree->findNeighborsInASphereStartingFromCell(nNSS,radius,false); //each point adds a contribution weighted by its distance to the sphere center it = nNSS.pointsInNeighbourhood.begin(); double meanValue = 0.0; double wSum = 0.0; for (unsigned j=0;j<k;++j,++it) { double weight = exp(-(it->squareDistd)/sigma2); //PDF: -exp(-(x-mu)^2/(2*sigma^2)) ScalarType val = cloud->getPointScalarValue(it->pointIndex); //scalar value must be valid if (ScalarField::ValidValue(val)) { meanValue += static_cast<double>(val) * weight; wSum += weight; } } ScalarType newValue = (wSum > 0.0 ? static_cast<ScalarType>(meanValue / wSum) : NAN_VALUE); cell.points->setPointScalarValue(i,newValue); if (nProgress && !nProgress->oneStep()) return false; } } //Bilateral Filtering using the second sigma parameters on values (when given) else { for (unsigned i=0;i<n;++i) //for each point in cell { ScalarType queryValue = cell.points->getPointScalarValue(i); //scalar of the query point //we get the points inside a spherical neighbourhood (radius: '3*sigma') cell.points->getPoint(i,nNSS.queryPoint); //warning: there may be more points at the end of nNSS.pointsInNeighbourhood than the actual nearest neighbors (k)! unsigned k = cell.parentOctree->findNeighborsInASphereStartingFromCell(nNSS,radius,false); //each point adds a contribution weighted by its distance to the sphere center it = nNSS.pointsInNeighbourhood.begin(); double meanValue = 0.0; double wSum = 0.0; for (unsigned j=0;j<k;++j,++it) { ScalarType val = cloud->getPointScalarValue(it->pointIndex); ScalarType dSF = queryValue - val; double weight = exp(-(it->squareDistd)/sigma2) * exp(-(dSF*dSF)/sigmaSF2); //scalar value must be valid if (ScalarField::ValidValue(val)) { meanValue += (double)val * weight; wSum += weight; } } cell.points->setPointScalarValue(i,wSum > 0.0 ? (ScalarType)(meanValue / wSum) : NAN_VALUE); if (nProgress && !nProgress->oneStep()) return false; } } return true; }
bool ScalarFieldTools::computeMeanGradientOnPatch( const DgmOctree::octreeCell& cell, void** additionalParameters, NormalizedProgress* nProgress/*=0*/) { //additional parameters bool euclideanDistances = *reinterpret_cast<bool*>(additionalParameters[0]); PointCoordinateType radius = *reinterpret_cast<PointCoordinateType*>(additionalParameters[1]); ScalarField* theGradientNorms = reinterpret_cast<ScalarField*>(additionalParameters[2]); //number of points inside the current cell unsigned n = cell.points->size(); //spherical neighborhood extraction structure DgmOctree::NearestNeighboursSphericalSearchStruct nNSS; nNSS.level = cell.level; nNSS.prepare(radius,cell.parentOctree->getCellSize(nNSS.level)); cell.parentOctree->getCellPos(cell.truncatedCode,cell.level,nNSS.cellPos,true); cell.parentOctree->computeCellCenter(nNSS.cellPos,cell.level,nNSS.cellCenter); //we already know the points inside the current cell { try { nNSS.pointsInNeighbourhood.resize(n); } catch (.../*const std::bad_alloc&*/) //out of memory { return false; } DgmOctree::NeighboursSet::iterator it = nNSS.pointsInNeighbourhood.begin(); for (unsigned j=0; j<n; ++j,++it) { it->point = cell.points->getPointPersistentPtr(j); it->pointIndex = cell.points->getPointGlobalIndex(j); } nNSS.alreadyVisitedNeighbourhoodSize = 1; } const GenericIndexedCloudPersist* cloud = cell.points->getAssociatedCloud(); for (unsigned i=0; i<n; ++i) { ScalarType gN = NAN_VALUE; ScalarType d1 = cell.points->getPointScalarValue(i); if (ScalarField::ValidValue(d1)) { cell.points->getPoint(i,nNSS.queryPoint); //we extract the point's neighbors //warning: there may be more points at the end of nNSS.pointsInNeighbourhood than the actual nearest neighbors (k)! unsigned k = cell.parentOctree->findNeighborsInASphereStartingFromCell(nNSS,radius,true); //if more than one neighbour (the query point itself) if (k > 1) { CCVector3d sum(0,0,0); unsigned counter = 0; //j = 1 because the first point is the query point itself --> contribution = 0 for (unsigned j=1; j<k; ++j) { ScalarType d2 = cloud->getPointScalarValue(nNSS.pointsInNeighbourhood[j].pointIndex); if (ScalarField::ValidValue(d2)) { CCVector3 u = *nNSS.pointsInNeighbourhood[j].point - nNSS.queryPoint; double norm2 = u.norm2d(); if (norm2 > ZERO_TOLERANCE) { double dd = d2 - d1; if (!euclideanDistances || dd*dd < 1.01 * norm2) { dd /= norm2; sum.x += u.x * dd; //warning: and here 'dd'=dd/norm2 ;) sum.y += u.y * dd; sum.z += u.z * dd; ++counter; } } } } if (counter != 0) gN = static_cast<ScalarType>(sum.norm()/counter); } } if (theGradientNorms) //if "IN" and "OUT" SFs are the same theGradientNorms->setValue(cell.points->getPointGlobalIndex(i),gN); else //if "IN" and "OUT" SFs are different cell.points->setPointScalarValue(i,gN); if (nProgress && !nProgress->oneStep()) return false; } return true; }