2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU Lesser General Public License as
4 * published by the Free Software Foundation, either version 3 of the
5 * License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * @author Bertrand Kerautret (\c kerautre@loria.fr )
20 * LORIA (CNRS, UMR 7503), University of Nancy, France
24 * Implementation of inline methods defined in Mesh.h
26 * This file is part of the DGtal library.
30 //////////////////////////////////////////////////////////////////////////////
33 #include <DGtal/kernel/BasicPointPredicates.h>
34 //////////////////////////////////////////////////////////////////////////////
36 ///////////////////////////////////////////////////////////////////////////////
37 // IMPLEMENTATION of inline methods.
38 ///////////////////////////////////////////////////////////////////////////////
40 ///////////////////////////////////////////////////////////////////////////////
41 // ----------------------- Standard services ------------------------------
47 template <typename TPoint>
49 DGtal::Mesh<TPoint>::Mesh(bool saveFaceColor)
51 mySaveFaceColor=saveFaceColor;
52 myDefaultColor = DGtal::Color::White;
58 template <typename TPoint>
60 DGtal::Mesh<TPoint>::Mesh(const DGtal::Color &aColor)
62 mySaveFaceColor=false;
63 myDefaultColor = aColor;
69 template <typename TPoint>
71 DGtal::Mesh<TPoint>::~Mesh()
76 template <typename TPoint>
78 DGtal::Mesh<TPoint>::Mesh ( const Mesh & other ): myFaceList(other.myFaceList),
79 myVertexList(other.myVertexList),
80 myFaceColorList(other.myFaceColorList),
81 mySaveFaceColor(other.mySaveFaceColor),
82 myDefaultColor(other.myDefaultColor)
87 template <typename TPoint>
90 DGtal::Mesh<TPoint>::operator= ( const Mesh & other )
92 myFaceList = other.myFaceList;
93 myVertexList = other.myVertexList;
94 myFaceColorList = other.myFaceColorList;
95 mySaveFaceColor = other.mySaveFaceColor;
96 myDefaultColor = other. myDefaultColor;
101 ///////////////////////////////////////////////////////////////////////////////
102 // Interface - public :
105 * Writes/Displays the object on an output stream.
106 * @param out the output stream where the object is written.
108 template <typename TPoint>
111 DGtal::Mesh<TPoint>::selfDisplay ( std::ostream & out ) const
117 * Checks the validity/consistency of the object.
118 * @return 'true' if the object is valid, 'false' otherwise.
120 template <typename TPoint>
123 DGtal::Mesh<TPoint>::isValid() const
131 ///////////////////////////////////////////////////////////////////////////////
132 // Implementation of inline functions //
140 template<typename TPoint>
142 DGtal::Mesh<TPoint>::Mesh(const VertexStorage &vertexSet)
144 mySaveFaceColor=false;
145 for(int i =0; i< vertexSet.size(); i++)
147 myVertexList.push_back(vertexSet.at(i));
154 template<typename TPoint>
157 DGtal::Mesh<TPoint>::addVertex(const TPoint &point)
159 myVertexList.push_back(point);
164 template<typename TPoint>
167 DGtal::Mesh<TPoint>::addTriangularFace(Index indexVertex1, Index indexVertex2,
168 Index indexVertex3, const DGtal::Color &aColor)
171 aFace.push_back(indexVertex1);
172 aFace.push_back(indexVertex2);
173 aFace.push_back(indexVertex3);
174 myFaceList.push_back(aFace);
177 myFaceColorList.push_back(aColor);
184 template<typename TPoint>
187 DGtal::Mesh<TPoint>::addQuadFace(Index indexVertex1,Index indexVertex2,
188 Index indexVertex3, Index indexVertex4,
189 const DGtal::Color &aColor)
192 aFace.push_back(indexVertex1);
193 aFace.push_back(indexVertex2);
194 aFace.push_back(indexVertex3);
195 aFace.push_back(indexVertex4);
196 myFaceList.push_back(aFace);
199 myFaceColorList.push_back(aColor);
205 template<typename TPoint>
208 DGtal::Mesh<TPoint>::addFace(const MeshFace &aFace, const DGtal::Color &aColor){
209 myFaceList.push_back(aFace);
212 myFaceColorList.push_back(aColor);
218 template<typename TPoint>
221 DGtal::Mesh<TPoint>::removeFaces(const std::vector<Index> &facesIndex){
222 DGtal::Mesh<TPoint> newMesh(true);
223 std::vector<unsigned int> indexVertexFaceCard(nbVertex());
224 std::vector<bool> indexFaceOK(nbFaces());
225 std::fill(indexVertexFaceCard.begin(), indexVertexFaceCard.end(), 0);
226 std::fill(indexFaceOK.begin(), indexFaceOK.end(), true);
227 for (unsigned int i = 0; i<facesIndex.size(); i++){
228 indexFaceOK[facesIndex[i]]=false;
230 // for each face remaining in the mesh we add +1 to each vertex used in a face
231 for(unsigned int i = 0; i < nbFaces(); i++){
232 if( indexFaceOK[i] ){
233 DGtal::Mesh<TPoint>::MeshFace aFace = getFace(i);
234 for (unsigned int j=0; j< aFace.size() ; j++) {
235 indexVertexFaceCard[aFace[j]] += 1;
239 // we remove all vertex with a face == 0 and compute the new vertex association:
240 std::vector<unsigned int> newVertexIndex;
241 unsigned int currentIndex=0;
242 for (unsigned int i=0; i< nbVertex(); i++) {
243 if (indexVertexFaceCard[i]!=0){
244 newMesh.addVertex(getVertex(i));
245 newVertexIndex.push_back(currentIndex);
248 newVertexIndex.push_back(0);
251 for (unsigned int i = 0; i < nbFaces(); i++) {
253 MeshFace aFace = getFace(i);
254 MeshFace aNewFace = aFace;
255 // translate the old face with new index:
256 for (unsigned int j=0; j< aFace.size() ; j++) {
257 aNewFace[j] = newVertexIndex[aFace[j]];
259 newMesh.addFace(aNewFace);
260 newMesh.setFaceColor(newMesh.nbFaces()-1, getFaceColor(i));
263 myFaceList = newMesh.myFaceList;
264 myVertexList = newMesh.myVertexList;
265 myFaceColorList = newMesh.myFaceColorList;
272 template<typename TPoint>
275 DGtal::Mesh<TPoint>::getVertex(Index i) const
277 return myVertexList.at(i);
282 template<typename TPoint>
285 DGtal::Mesh<TPoint>::getVertex(Index i)
287 return myVertexList.at(i);
292 template<typename TPoint>
294 const typename DGtal::Mesh<TPoint>::MeshFace &
295 DGtal::Mesh<TPoint>::getFace(Index i) const
297 return myFaceList.at(i);
301 template<typename TPoint>
303 typename DGtal::Mesh<TPoint>::MeshFace &
304 DGtal::Mesh<TPoint>::getFace(Index i)
306 return myFaceList.at(i);
310 template<typename TPoint>
312 typename DGtal::Mesh<TPoint>::RealPoint
313 DGtal::Mesh<TPoint>::getFaceBarycenter(Index i) const
315 DGtal::Mesh<TPoint>::RealPoint c;
316 MeshFace aFace = getFace(i);
317 for ( auto &j: aFace){
318 TPoint p = getVertex(j);
319 for (typename TPoint::Dimension k = 0; k < TPoint::dimension; k++){
320 c[k] += static_cast<typename RealPoint::Component>(p[k]) ;
323 return c/static_cast<typename RealPoint::Component>(aFace.size());
327 template<typename TPoint>
329 typename DGtal::Mesh<TPoint>::Size
330 DGtal::Mesh<TPoint>::nbFaces() const
332 return myFaceList.size();
335 template<typename TPoint>
337 typename DGtal::Mesh<TPoint>::Size
338 DGtal::Mesh<TPoint>::nbVertex() const
340 return myVertexList.size();
344 template<typename TPoint>
347 DGtal::Mesh<TPoint>::getFaceColor(Index i) const
351 return myFaceColorList.at(i);
355 return myDefaultColor;
359 template <typename TPoint>
360 struct MeshBoundingBoxCompPoints
362 MeshBoundingBoxCompPoints(typename TPoint::Dimension d): myDim(d){};
363 bool operator() (const TPoint &p1, const TPoint &p2){return p1[myDim]<p2[myDim];};
364 typename TPoint::Dimension myDim;
367 template<typename TPoint>
369 std::pair<TPoint, TPoint>
370 DGtal::Mesh<TPoint>::getBoundingBox() const
372 std::pair<TPoint, TPoint> theResult;
373 TPoint lowerBound, upperBound;
374 for(unsigned int i=0; i< TPoint::size(); i++)
376 const MeshBoundingBoxCompPoints<TPoint> cmp_points(i);
377 upperBound[i] = (*(std::max_element(vertexBegin(), vertexEnd(), cmp_points)))[i];
378 lowerBound[i] = (*(std::min_element(vertexBegin(), vertexEnd(), cmp_points)))[i];
380 theResult.first = lowerBound ;
381 theResult.second = upperBound ;
386 template<typename TPoint>
389 DGtal::Mesh<TPoint>::setFaceColor(const Index index,
390 const DGtal::Color &aColor)
392 if (!mySaveFaceColor)
394 for(unsigned int i = 0; i<myFaceList.size(); i++)
396 myFaceColorList.push_back(myDefaultColor);
398 mySaveFaceColor=true;
400 myFaceColorList.at(index) = aColor;
404 template<typename TPoint>
407 DGtal::Mesh<TPoint>::isStoringFaceColors() const
409 return mySaveFaceColor;
415 template<typename TPoint>
418 DGtal::Mesh<TPoint>::invertVertexFaceOrder(){
419 for(unsigned int i=0; i<myFaceList.size(); i++)
421 auto aFace = myFaceList.at(i);
422 for(unsigned int j=0; j < aFace.size()/2; j++)
424 const auto tmp=aFace.at(j);
425 aFace.at(j)=aFace.at(aFace.size()-1-j);
426 aFace.at(aFace.size()-1-j)=tmp;
431 template<typename TPoint>
434 DGtal::Mesh<TPoint>::clearFaces(){
438 template<typename TPoint>
441 DGtal::Mesh<TPoint>::changeScale(const typename TPoint::Component aScale){
442 for(typename VertexStorage::iterator it = vertexBegin(); it != vertexEnd(); it++)
449 template<typename TPoint>
452 DGtal::Mesh<TPoint>::subDivideTriangularFaces(const double minArea){
454 std::vector<Mesh<TPoint>::MeshFace> facesToAdd;
455 for(unsigned int i =0; i< nbFaces(); i++)
457 typename Mesh<TPoint>::MeshFace aFace = getFace(i);
460 TPoint p1 = getVertex(aFace[0]);
461 TPoint p2 = getVertex(aFace[1]);
462 TPoint p3 = getVertex(aFace[2]);
463 TPoint c = (p1+p2+p3)/3.0;
464 double a = ((p2-p1).crossProduct(p3-p1)).norm()/2.0;
474 f1.push_back(aFace[0]);
475 f1.push_back(aFace[1]);
476 f1.push_back(nbVertex()-1);
477 facesToAdd.push_back(f1);
479 f2.push_back(aFace[1]);
480 f2.push_back(aFace[2]);
481 f2.push_back(nbVertex()-1);
482 facesToAdd.push_back(f2);
484 f3.push_back(aFace[2]);
485 f3.push_back(aFace[0]);
486 f3.push_back(nbVertex()-1);
487 facesToAdd.push_back(f3);
491 facesToAdd.push_back(aFace);
497 for(unsigned i=0; i<facesToAdd.size(); i++)
499 addFace(facesToAdd[i]);
506 template<typename TPoint>
509 DGtal::Mesh<TPoint>::quadToTriangularFaces(){
510 unsigned int nbQuadT=0;
511 std::vector<Mesh<TPoint>::MeshFace> facesToAdd;
512 for(unsigned int i =0; i< nbFaces(); i++)
514 typename Mesh<TPoint>::MeshFace aFace = getFace(i);
518 f1.push_back(aFace[0]);
519 f1.push_back(aFace[1]);
520 f1.push_back(aFace[2]);
521 facesToAdd.push_back(f1);
523 f2.push_back(aFace[2]);
524 f2.push_back(aFace[3]);
525 f2.push_back(aFace[0]);
526 facesToAdd.push_back(f2);
531 facesToAdd.push_back(aFace);
535 for(unsigned i=0; i<facesToAdd.size(); i++)
537 addFace(facesToAdd[i]);
545 //------------------------------------------------------------------------------
546 template<typename TPoint>
549 DGtal::Mesh<TPoint>::className() const
559 template <typename TPoint>
562 DGtal::Mesh<TPoint>::createTubularMesh(DGtal::Mesh<TPoint> &aMesh, const std::vector<TPoint> &aSkeleton,
563 const double aRadius,
564 const double angleStep, const DGtal::Color &aMeshColor)
566 std::vector<double> aVecR;
567 aVecR.push_back(aRadius);
568 DGtal::Mesh<TPoint>::createTubularMesh(aMesh, aSkeleton,
569 aVecR, angleStep, aMeshColor);
574 template <typename TPoint>
577 DGtal::Mesh<TPoint>::createTubularMesh(DGtal::Mesh<TPoint> &aMesh, const std::vector<TPoint> &aSkeleton,
578 const std::vector<double> &aVectRadius,
579 const double angleStep, const DGtal::Color &aMeshColor)
581 auto nbVertexInitial = aMesh.nbVertex();
582 ASSERT(aVectRadius.size() > 0);
583 // Generating vertices..
584 for(auto i = 0; i< (int)aSkeleton.size(); i++)
587 TPoint uDir1, uDirPrec;
591 if(i != (int)aSkeleton.size()-1)
593 vectDir = aSkeleton.at(i+1) - aSkeleton.at(i);
597 vectDir = aSkeleton.at(i) - aSkeleton.at(i-1);
600 double d = -vectDir[0]* aSkeleton.at(i)[0] - vectDir[1]*aSkeleton.at(i)[1]
601 - vectDir[2]*aSkeleton.at(i)[2];
605 pRefOrigin [0]= -d/vectDir[0];
608 if(aSkeleton.at(i) == pRefOrigin ||
609 (vectDir[1]==0 && vectDir[2]==0))
615 else if (vectDir[1]!=0)
618 pRefOrigin [1]= -d/vectDir[1];
620 if(aSkeleton.at(i) == pRefOrigin ||
621 (vectDir[0]==0 && vectDir[2]==0))
625 }else if (vectDir[2]!=0)
629 pRefOrigin [2]= -d/vectDir[2];
630 if(aSkeleton.at(i) == pRefOrigin ||
631 (vectDir[0]==0 && vectDir[1]==0))
636 uDir1=(pRefOrigin-aSkeleton.at(i))/((pRefOrigin-aSkeleton.at(i)).norm());
637 uDir2[0] = uDir1[1]*vectDir[2]-uDir1[2]*vectDir[1];
638 uDir2[1] = uDir1[2]*vectDir[0]-uDir1[0]*vectDir[2];
639 uDir2[2] = uDir1[0]*vectDir[1]-uDir1[1]*vectDir[0];
641 for(double a = 0.0; a < 2.0*M_PI; a += angleStep)
643 TPoint vMove = aVectRadius.at(i%aVectRadius.size())*(uDir1*cos(a) + uDir2*sin(a));
644 aMesh.addVertex(vMove + aSkeleton[i]);
647 firstPoint = vMove + aSkeleton[i]+vectDir;
652 unsigned int nbPtPerFaces = static_cast<unsigned int>((aMesh.nbVertex()-nbVertexInitial)/aSkeleton.size());
654 // Generating faces...
655 for(auto i = 0; i< (int)aSkeleton.size()-1; i++)
657 if (aSkeleton.at(i)==aSkeleton.at(i+1)){
658 trace.warning() << "Two skeleton points are identical, ignoring one point." << std::endl;
661 // Computing best shift between two consecutive ring points to generate tube face.
662 // (criteria defined by the minimal distance between 4 sampling points)
663 double minDistance = std::numeric_limits<double>::max();
664 TPoint ptRefRing1 = aMesh.getVertex(nbVertexInitial+i*nbPtPerFaces);
665 TPoint ptRefRing2 = aMesh.getVertex(nbVertexInitial+i*nbPtPerFaces+nbPtPerFaces/4);
666 TPoint ptRefRing3 = aMesh.getVertex(nbVertexInitial+i*nbPtPerFaces+2*(nbPtPerFaces/4));
667 TPoint ptRefRing4 = aMesh.getVertex(nbVertexInitial+i*nbPtPerFaces+3*(nbPtPerFaces/4));
669 unsigned int shift = 0;
671 if(i != (int)aSkeleton.size()-1)
673 vectDir = aSkeleton.at(i+1) - aSkeleton.at(i);
677 vectDir = aSkeleton.at(i) - aSkeleton.at(i-1);
680 for(unsigned int k=0; k<nbPtPerFaces; k++)
682 TPoint pScan1 = aMesh.getVertex(nbVertexInitial+(i+1)*nbPtPerFaces+k);
683 TPoint pScan2 = aMesh.getVertex(nbVertexInitial+(i+1)*nbPtPerFaces+
684 (nbPtPerFaces/4+k)%nbPtPerFaces);
685 TPoint pScan3 = aMesh.getVertex(nbVertexInitial+(i+1)*nbPtPerFaces+
686 (2*(nbPtPerFaces/4)+k)%nbPtPerFaces);
687 TPoint pScan4 = aMesh.getVertex(nbVertexInitial+(i+1)*nbPtPerFaces+
688 (3*(nbPtPerFaces/4)+k)%nbPtPerFaces);
689 double distance = (ptRefRing1 - pScan1).norm()+(ptRefRing2 - pScan2).norm()+
690 (ptRefRing3 - pScan3).norm()+(ptRefRing4 - pScan4).norm();
691 if(distance<minDistance){
693 minDistance = distance;
697 for(unsigned int k=0; k<nbPtPerFaces; k++)
699 Mesh<TPoint>::MeshFace aFace;
700 aMesh.addQuadFace(nbVertexInitial+k+i*nbPtPerFaces,
701 nbVertexInitial+(shift+k)%nbPtPerFaces+nbPtPerFaces*(i+1),
702 nbVertexInitial+(shift+k+1)%nbPtPerFaces+nbPtPerFaces*(i+1),
703 nbVertexInitial+(k+1)%nbPtPerFaces+i*nbPtPerFaces,
713 template <typename TPoint>
714 template <typename TValue>
717 DGtal::Mesh<TPoint>::createMeshFromHeightSequence(Mesh<TPoint> &aMesh, const std::vector<TValue> & anValueSequence,
718 const unsigned int lengthSequence,
719 double stepX, double stepY, double stepZ,
720 const DGtal::Color &aMeshColor ){
721 const auto nbVertexInitial = aMesh.nbVertex();
722 // Generating vertices..
724 unsigned int posY = 0;
725 while(i+(int)lengthSequence-1 < (int)anValueSequence.size()){
726 for(unsigned int j = 0; j < lengthSequence; j++, i++){
727 aMesh.addVertex(TPoint(j*stepX, posY*stepY, stepZ*anValueSequence.at(i)));
731 // Generating faces...
734 while(i+(int)lengthSequence-1 < (int)anValueSequence.size() - (int)lengthSequence){
735 for(auto j = 0; j < (int)lengthSequence-1; j++, i++){
736 aMesh.addQuadFace(nbVertexInitial+i, nbVertexInitial+i+1,
737 nbVertexInitial+i+1+lengthSequence,
738 nbVertexInitial+i+lengthSequence,
749 template <typename TPoint>
752 DGtal::operator<< ( std::ostream & out,
753 const Mesh<TPoint> & object )
755 object.selfDisplay( out );
764 ///////////////////////////////////////////////////////////////////////////////