1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | cfMesh: A library for mesh generation
5 \\ / A nd | Author: Franjo Juretic (franjo.juretic@c-fields.com)
6 \\/ M anipulation | Copyright (C) Creative Fields, Ltd.
7 -------------------------------------------------------------------------------
9 This file is part of cfMesh.
11 cfMesh is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by the
13 Free Software Foundation; either version 3 of the License, or (at your
14 option) any later version.
16 cfMesh is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 You should have received a copy of the GNU General Public License
22 along with cfMesh. If not, see <http://www.gnu.org/licenses/>.
26 \*---------------------------------------------------------------------------*/
28 #include "triSurfaceCurvatureEstimator.H"
30 #include "quadricFitting.H"
31 #include "helperFunctions.H"
45 //#define DEBUGCurvatureEstimator
47 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
52 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
54 void writeSurfaceToVTK
61 file << "# vtk DataFile Version 3.0\n";
62 file << "vtk output\n";
64 file << "DATASET POLYDATA\n";
67 const pointField& points = surf.points();
68 file << "POINTS " << points.size() << " float\n";
71 const point& p = points[pI];
73 file << p.x() << ' ' << p.y() << ' ' << p.z() << ' ';
81 file << "\nPOLYGONS " << surf.size() << " " << 4*surf.size() << endl;
84 const labelledTri& tri = surf[triI];
85 file << 3 << " " << tri[0] << " " << tri[1] << " " << tri[2] << endl;
89 void writeSurfaceToVTK
93 const DynList<label>& triangles,
94 const labelHashSet& labels
98 file << "# vtk DataFile Version 3.0\n";
99 file << "vtk output\n";
101 file << "DATASET POLYDATA\n";
104 std::map<label, label> newPointLabel;
105 forAll(triangles, tI)
107 const labelledTri& tri = surf[triangles[tI]];
109 for(label pI=0;pI<3;++pI)
111 newPointLabel[tri[pI]];
115 forAllConstIter(labelHashSet, labels, it)
116 newPointLabel[it.key()];
118 const pointField& points = surf.points();
119 file << "POINTS " << label(newPointLabel.size()) << " float\n";
123 std::map<label, label>::iterator it=newPointLabel.begin();
124 it!=newPointLabel.end();
128 it->second = counter++;
130 const point& p = points[it->first];
132 file << p.x() << ' ' << p.y() << ' ' << p.z() << ' ';
139 file << "\nPOLYGONS " << triangles.size()
140 << " " << 4*triangles.size() << endl;
141 forAll(triangles, tI)
143 const labelledTri& tri = surf[triangles[tI]];
146 << " " << newPointLabel[tri[0]]
147 << " " << newPointLabel[tri[1]]
148 << " " << newPointLabel[tri[2]] << endl;
152 void writeSurfaceToVTK
156 const List<DynList<scalar, 1> >& data
159 OFstream file(name+".vtk");
161 writeSurfaceToVTK(file, surf);
163 //- write curvature fields
164 const pointField& points = surf.points();
166 file << "\nPOINT_DATA " << points.size() << "\n";
168 file << "SCALARS Values double\n";
169 file << "LOOKUP_TABLE default\n";
172 file << data[pI][0] << " ";
174 if( pI && pI % 5 == 0 )
179 void writeSurfaceToVTK
183 const List<DynList<vector, 1> >& data
186 OFstream file(name+".vtk");
188 writeSurfaceToVTK(file, surf);
190 //- write curvature fields
191 const pointField& points = surf.points();
193 file << "\nPOINT_DATA " << points.size() << "\n";
195 file << "VECTORS Values double\n";
196 file << "LOOKUP_TABLE default\n";
199 const vector& v = data[pI][0];
201 file << v[0] << " " << v[1] << " " << v[2] << " ";
203 if( pI && pI % 5 == 0 )
208 void triSurfaceCurvatureEstimator::calculateEdgeCurvature()
210 const pointField& points = surface_.points();
211 const edgeLongList& edges = surface_.edges();
212 const VRWGraph& pointEdges = surface_.pointEdges();
213 const VRWGraph& edgeFaces = surface_.edgeFacets();
215 edgePointCurvature_.setSize(points.size());
216 boolList featureEdge(edges.size());
219 # pragma omp parallel
223 # pragma omp for schedule(static, 1)
225 forAll(edgePointCurvature_, i)
226 edgePointCurvature_[i] = 0.0;
228 //- mark feature edges
230 # pragma omp for schedule(static, 1)
232 forAll(edgeFaces, eI)
234 if( edgeFaces.sizeOfRow(eI) != 2 )
236 featureEdge[eI] = false;
241 surface_[edgeFaces(eI, 0)].region() ==
242 surface_[edgeFaces(eI, 1)].region()
245 featureEdge[eI] = false;
249 featureEdge[eI] = true;
257 //- loop through the points and calculate the curvature for points
258 //- attached to two feature edges
260 # pragma omp for schedule(dynamic, 20)
262 forAll(pointEdges, pI)
264 DynList<label> features;
265 forAllRow(pointEdges, pI, peI)
267 const label edgeI = pointEdges(pI, peI);
268 if( featureEdge[edgeI] )
269 features.append(edgeI);
272 if( features.size() == 2 )
274 //- calculate the curvature and store it in the map
275 vector e1 = edges[features[0]].vec(points);
276 const scalar d1 = mag(e1) + VSMALL;
278 vector e2 = edges[features[1]].vec(points);
279 const scalar d2 = mag(e2) + VSMALL;
283 cs = Foam::min(1.0, cs);
284 cs = Foam::max(-1.0, cs);
286 const scalar curv = Foam::acos(cs) / (0.5 * (d1+d2+VSMALL));
287 edgePointCurvature_[pI] = Foam::mag(curv);
293 void triSurfaceCurvatureEstimator::calculateSurfaceCurvatures()
295 const VRWGraph& pointTriangles = surface_.pointFacets();
297 const pointField& points = surface_.points();
299 patchPositions_.setSize(surface_.size());
300 gaussianCurvature_.setSize(points.size());
301 meanCurvature_.setSize(points.size());
302 maxCurvature_.setSize(points.size());
303 minCurvature_.setSize(points.size());
304 maxCurvatureVector_.setSize(points.size());
305 minCurvatureVector_.setSize(points.size());
307 List<DynList<label, 4> > pointPatches(points.size());
310 # pragma omp parallel for schedule(dynamic, 40)
312 forAll(pointTriangles, pointI)
314 std::map<label, DynList<label> > regionTriangles;
315 Map<labelHashSet> otherLabels;
318 forAllRow(pointTriangles, pointI, ptI)
320 const label triI = pointTriangles(pointI, ptI);
321 const label regionI = surface_[triI].region();
323 if( !normals.found(regionI) )
324 normals.insert(regionI, vector::zero);
325 if( regionTriangles.find(regionI) != regionTriangles.end() )
326 regionTriangles.insert
334 if( !otherLabels.found(regionI) )
335 otherLabels.insert(regionI, labelHashSet());
337 regionTriangles[regionI].append(triI);
339 forAll(surface_[triI], tpI)
341 const label pI = surface_[triI][tpI];
346 otherLabels[regionI].insert(pI);
347 normals[regionI] += surface_[triI].normal(points);
351 forAllConstIter(Map<labelHashSet>, otherLabels, it)
353 const labelHashSet& currLabels = it();
355 if( otherLabels[it.key()].size() > 5 )
358 labelHashSet additionalPoints;
359 forAllConstIter(labelHashSet, currLabels, lit)
361 const label neiPointI = lit.key();
362 const constRow pTriangles = pointTriangles[neiPointI];
364 forAll(pTriangles, ptI)
366 const labelledTri& nTri = surface_[pTriangles[ptI]];
368 if( nTri.region() != it.key() )
372 additionalPoints.insert(nTri[pI]);
376 forAllConstIter(labelHashSet, additionalPoints, aIter)
377 otherLabels[it.key()].insert(aIter.key());
380 forAllIter(Map<vector>, normals, nit)
381 nit() /= (Foam::mag(nit()) + VSMALL);
383 forAllConstIter(Map<labelHashSet>, otherLabels, it)
385 const labelHashSet& labels = it();
387 const label regionI = it.key();
389 //- store the patches
390 pointPatches[pointI].append(regionI);
392 //- find the position of point in the patch triangles
393 const DynList<label>& rTriangles = regionTriangles[regionI];
394 forAll(rTriangles, i)
396 const label tI = rTriangles[i];
399 forAll(surface_[tI], j)
400 if( surface_[tI][j] == pointI )
406 patchPositions_(tI, pos) = gaussianCurvature_[pointI].size();
409 //- store point coordinates
412 forAllConstIter(labelHashSet, labels, lit)
413 op.append(points[lit.key()]);
415 //- fit the quadric patch to the surface
416 quadricFitting qfit(points[pointI], normals[it.key()], op);
418 # ifdef DEBUGCurvatureEstimator
419 Info << "Point " << pointI << " in patch " << regionI
420 << " normal " << normals[it.key()]
421 << " evalution points " << labels
422 << " has max curvature " << qfit.maxCurvature()
423 << " and min curvature " << qfit.minCurvature() << endl;
427 help::scalarToText(pointI) +
429 help::scalarToText(regionI) +
432 writeSurfaceToVTK(file, surface_, rTriangles, labels);
436 gaussianCurvature_[pointI].append(qfit.gaussianCurvature());
437 meanCurvature_[pointI].append(qfit.meanCurvature());
438 maxCurvature_[pointI].append(qfit.maxCurvature());
439 minCurvature_[pointI].append(qfit.minCurvature());
440 maxCurvatureVector_[pointI].append(qfit.maxCurvatureVector());
441 minCurvatureVector_[pointI].append(qfit.minCurvatureVector());
445 //- smooth curvatures using weighted Laplace
446 List<DynList<scalar, 1> > smoothMinCurv(points.size());
447 List<DynList<scalar, 1> > smoothMaxCurv(points.size());
450 # pragma omp parallel
453 for(label iteration=0;iteration<2;++iteration)
456 # pragma omp for schedule(static, 1)
458 forAll(pointTriangles, pointI)
460 const constRow pTriangles = pointTriangles[pointI];
462 //- find neighbouring points for each patch
463 Map<DynList<label> > patchNeiPoints;
464 forAll(pointPatches[pointI], ppI)
465 patchNeiPoints.insert
467 pointPatches[pointI][ppI],
471 forAll(pTriangles, ptI)
473 const labelledTri& tri = surface_[pTriangles[ptI]];
475 if( !patchNeiPoints.found(tri.region()) )
476 patchNeiPoints.insert(tri.region(), DynList<label>());
480 const label neiPointI = tri[pI];
482 if( neiPointI == pointI )
485 patchNeiPoints[tri.region()].appendIfNotIn(neiPointI);
489 smoothMinCurv[pointI].setSize(minCurvature_[pointI].size());
490 smoothMaxCurv[pointI].setSize(maxCurvature_[pointI].size());
492 //- update min curvature for all point patches
493 forAll(minCurvature_[pointI], patchI)
495 const label cPatch = pointPatches[pointI][patchI];
500 const DynList<label>& neiPoints = patchNeiPoints[cPatch];
502 if( neiPoints.size() == 0 )
504 smoothMinCurv[pointI][patchI] =
505 minCurvature_[pointI][patchI];
507 smoothMaxCurv[pointI][patchI] =
508 maxCurvature_[pointI][patchI];
513 const label neiPointI = neiPoints[i];
516 pointPatches[neiPointI].containsAtPosition(cPatch);
518 minCurv += minCurvature_[neiPointI][pos];
519 maxCurv += maxCurvature_[neiPointI][pos];
522 minCurv /= neiPoints.size();
523 maxCurv /= neiPoints.size();
526 smoothMinCurv[pointI][patchI] =
527 0.5 * (minCurv + minCurvature_[pointI][patchI]);
529 smoothMaxCurv[pointI][patchI] =
530 0.5 * (maxCurv + maxCurvature_[pointI][patchI]);
537 # pragma omp for schedule(static, 1)
539 forAll(minCurvature_, pointI)
541 forAll(minCurvature_[pointI], i)
543 minCurvature_[pointI][i] = smoothMinCurv[pointI][i];
544 maxCurvature_[pointI][i] = smoothMaxCurv[pointI][i];
553 //- update Gaussian and mean curvatures
555 # pragma omp for schedule(static, 1)
557 forAll(minCurvature_, pointI)
559 const DynList<scalar, 1>& minCurv = minCurvature_[pointI];
560 const DynList<scalar, 1>& maxCurv = maxCurvature_[pointI];
564 gaussianCurvature_[pointI][i] = minCurv[i] * maxCurv[i];
565 meanCurvature_[pointI][i] = 0.5 * (minCurv[i] + maxCurv[i]);
570 # ifdef DEBUGCurvatureEstimator
571 word name = "surfaceMeanCurv";
572 writeSurfaceToVTK(name, surface_, meanCurvature_);
573 writeSurfaceToVTK("surfaceGaussianCurv", surface_, gaussianCurvature_);
574 writeSurfaceToVTK("surfaceMaxCurv", surface_, maxCurvature_);
575 writeSurfaceToVTK("surfaceMinCurv", surface_, minCurvature_);
576 writeSurfaceToVTK("surfaceMaxCurvVec", surface_, maxCurvatureVector_);
577 writeSurfaceToVTK("surfaceMinCurvVec", surface_, minCurvatureVector_);
581 void triSurfaceCurvatureEstimator::calculateGaussianCurvature()
583 gaussianCurvature_.setSize(surface_.patches().size());
585 List<std::map<label, scalar> > magAreas(gaussianCurvature_.size());
587 forAll(surface_, triI)
589 const labelledTri& tri = surface_[triI];
590 const label region = tri.region();
594 gaussianCurvature_[region][tri[pI]] = 2 * M_PI;
595 magAreas[region][tri[pI]] = 0.0;
599 const pointField& points = surface_.points();
600 const labelListList& pointTriangles = surface_.pointFaces();
602 forAll(surface_, triI)
604 const labelledTri& tri = surface_[triI];
605 const label region = tri.region();
607 const scalar A = mag(tri.normal(points)) + VSMALL;
609 const edgeList edges = tri.edges();
614 ev[eI] = edges[eI].vec(points);
615 dv[eI] = mag(ev[eI]);
616 if( dv[eI] > VSMALL )
622 scalar cs = ev[(pI+2)%3] & ev[pI];
623 cs = Foam::min(1.0, cs);
624 cs = Foam::max(1.0, cs);
626 gaussianCurvature_[region][tri[pI]] -= Foam::acos(-1.0 * cs);
627 magAreas[region][tri[pI]] += A;
631 //- calculate tge gaussian curvature for each vertex
632 forAll(gaussianCurvature_, patchI)
634 std::map<label, scalar>& gc = gaussianCurvature_[patchI];
635 std::map<label, scalar>& magSf = magAreas[patchI];
639 std::map<label, scalar>::iterator it = gc.begin();
643 it->second = 3.0 * it->second / magSf[it->first];
646 //- smooth the curvature variation
647 const edgeList& edges = surface_.edges();
648 const labelListList& pointEdges = surface_.pointEdges();
649 forAll(meanCurvature_, patchI)
651 std::map<label, scalar>& gc = meanCurvature_[patchI];
653 std::map<label, scalar> newValues;
655 for(std::map<label, scalar>::iterator it=gc.begin();it!=gc.end();++it)
657 const labelList& pEdges = pointEdges[it->first];
659 scalar maxCurv(-VSMALL), minCurv(VSMALL);
664 const label ovI = edges[pEdges[peI]].otherVertex(it->first);
666 if( gc.find(ovI) != gc.end() )
668 maxCurv = Foam::max(maxCurv, gc[ovI]);
669 minCurv = Foam::min(minCurv, gc[ovI]);
676 if( gc[it->first] > maxCurv )
680 std::make_pair(it->first, 0.5*(maxCurv+gc[it->first]))
683 else if( gc[it->first] >= minCurv )
685 newValues.insert(*it);
691 std::make_pair(it->first, 0.5*(minCurv+gc[it->first]))
701 void triSurfaceCurvatureEstimator::calculateMeanCurvature()
703 const pointField& points = surface_.points();
705 meanCurvature_.setSize(surface_.patches().size());
707 List<Map<label> > nNeighbours(meanCurvature_.size());
709 const edgeList& edges = surface_.edges();
710 const labelListList& edgeFaces = surface_.edgeFaces();
712 //- calculate edge angles
715 const labelList& eFaces = edgeFaces[eI];
717 if( eFaces.size() != 2 )
719 if( surface_[eFaces[0]].region() != surface_[eFaces[1]].region() )
722 const label region = surface_[eFaces[0]].region();
723 std::map<label, scalar>& mc = meanCurvature_[region];
724 Map<label>& nn = nNeighbours[region];
726 //- calculate normal vectors
727 vector n0 = surface_[eFaces[0]].normal(points);
728 const scalar A0 = mag(n0);
732 vector n1 = surface_[eFaces[1]].normal(points);
733 const scalar A1 = mag(n1);
737 //- calculate the cosine of the angle between the two vectors
739 cs = Foam::min(1.0, cs);
740 cs = Foam::max(-1.0, cs);
742 //- calculate and normalize the edge vector
743 vector ev = edges[eI].vec(points);
744 const scalar length = mag(ev);
745 if( length > VSMALL )
748 //- calculate cross product of the normal vectors
749 const vector t = n0 ^ n1;
751 //- calculate the dot product between the edge vector and t
753 sn = Foam::min(1.0, sn);
754 sn = Foam::max(-1.0, sn);
756 //- calculate mean curvature over the edge
759 if( mag(sn) > VSMALL || mag(cs) > VSMALL )
761 //- calculate the angle
762 const scalar angle = Foam::atan2(sn, cs);
763 Hf += angle * length;
766 if( A0 > VSMALL || A1 > VSMALL )
768 Hf /= (A0 + A1 + VSMALL);
773 const edge& e = edges[eI];
774 if( mc.find(e.start()) == mc.end() )
776 mc.insert(std::make_pair(e.start(), 0.0));
777 nn.insert(e.start(), 0);
782 if( mc.find(e.end()) == mc.end() )
784 mc.insert(std::make_pair(e.end(), 0.0));
785 nn.insert(e.end(), 0);
791 //- calculate the curvature for each vertex
792 forAll(meanCurvature_, patchI)
794 std::map<label, scalar>& mc = meanCurvature_[patchI];
795 Map<label>& nn = nNeighbours[patchI];
797 forAllIter(Map<label>, nn, it)
805 mc[it.key()] = 0.5 * mc[it.key()] / it();
809 //- smooth the curvature variation
810 const labelListList& pointEdges = surface_.pointEdges();
811 forAll(meanCurvature_, patchI)
813 std::map<label, scalar>& mc = meanCurvature_[patchI];
815 std::map<label, scalar> newValues;
817 for(std::map<label, scalar>::iterator it=mc.begin();it!=mc.end();++it)
819 const labelList& pEdges = pointEdges[it->first];
821 scalar maxCurv(-VSMALL), minCurv(VSMALL);
826 const label ovI = edges[pEdges[peI]].otherVertex(it->first);
828 if( mc.find(ovI) != mc.end() )
830 maxCurv = Foam::max(maxCurv, mc[ovI]);
831 minCurv = Foam::min(minCurv, mc[ovI]);
838 if( mc[it->first] > maxCurv )
842 std::make_pair(it->first, 0.5*(maxCurv+mc[it->first]))
845 else if( mc[it->first] >= minCurv )
847 newValues.insert(*it);
853 std::make_pair(it->first, 0.5*(minCurv+mc[it->first]))
863 void triSurfaceCurvatureEstimator::calculateMinAndMaxCurvature()
865 maxCurvature_.setSize(meanCurvature_.size());
866 minCurvature_.setSize(meanCurvature_.size());
868 forAll(maxCurvature_, patchI)
870 std::map<label, scalar>& mc = meanCurvature_[patchI];
871 std::map<label, scalar>& gc = gaussianCurvature_[patchI];
874 std::map<label, scalar>::const_iterator it=mc.begin();
879 const label pI = it->first;
880 scalar disc = sqr(mc[pI]) - gc[pI];
882 disc = sqr(mc[pI]) + gc[pI];
884 const scalar sqrtdisc = sqrt(disc);
885 maxCurvature_[patchI][pI] = mc[pI] + sqrtdisc;
886 minCurvature_[patchI][pI] = mc[pI] - sqrtdisc;
892 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
894 } // End namespace Foam
896 // ************************************************************************* //