1 // Copyright (C) 2007-2013 CEA/DEN, EDF R&D
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 // File : UnitTetraIntersectionBary.cxx
21 // Created : Tue Dec 9 16:48:49 2008
22 // Author : Edward AGAPOV (eap)
24 #include "UnitTetraIntersectionBary.hxx"
26 #include "VectorUtils.hxx"
27 #include "InterpolationUtils.hxx"
28 #include "VolSurfFormulae.hxx"
30 #define NB_TETRA_SIDES 4
31 #define NB_TETRA_NODES 4
33 //#define DMP_UNITTETRAINTERSECTIONBARY
36 namespace INTERP_KERNEL
38 enum { _X=0, _Y, _Z };
40 inline bool samePoint( const double* p1, const double* p2 )
42 return ( epsilonEqual( p1[0], p2[0]) &&
43 epsilonEqual( p1[1], p2[1]) &&
44 epsilonEqual( p1[2], p2[2]));
47 //================================================================================
49 * \brief Creates a ready-to-use tool
51 //================================================================================
53 UnitTetraIntersectionBary::UnitTetraIntersectionBary(bool isTetraInversed)
54 :TransformedTriangle(),_int_volume(0),_isTetraInversed( isTetraInversed )
58 //================================================================================
60 * \brief Initializes fields
62 //================================================================================
64 void UnitTetraIntersectionBary::init(bool isTetraInversed)
67 _isTetraInversed = isTetraInversed;
72 //================================================================================
74 * \brief Stores a part of triangle common with the unit tetrahedron
75 * \param triangle - triangle side of other cell
77 //================================================================================
79 void UnitTetraIntersectionBary::addSide(const TransformedTriangle& triangle)
81 _int_volume += triangle.getVolume();
83 double triNormal[3], polyNormal[3];
84 crossprod<3>( triangle.getCorner(P),triangle.getCorner(Q),triangle.getCorner(R), triNormal);
86 const std::vector<double*> * pPolygonA = &triangle.getPolygonA();
87 if ( pPolygonA->size() < 3 )
89 if ( !epsilonEqual( triNormal[_Z], 0 ))
90 return; // not vertical triangle does not intersect the unit tetra
92 // Vertical triangle. Use inherited methods of TransformedTriangle to
93 // calculate intesection polygon
94 *((TransformedTriangle*)this) = triangle; // copy triangle fields
97 calculateIntersectionAndProjectionPolygons();
98 if (this->_polygonA.size() < 3)
100 calculatePolygonBarycenter(A, _barycenterA);
101 sortIntersectionPolygon(A, _barycenterA);
102 pPolygonA = & _polygonA;
105 // check if polygon orientation is same as the one of triangle
106 std::vector<double*>::const_iterator p = pPolygonA->begin(), pEnd = pPolygonA->end();
107 #ifdef DMP_UNITTETRAINTERSECTIONBARY
108 std::cout.precision(18);
109 std::cout << "**** int polygon() " << std::endl;
113 std::cout << pEnd - p << ": ( " << pp[0] << ", " << pp[1] << ", " << pp[2] << " )" << std::endl;
115 p = pPolygonA->begin();
119 while ( samePoint( p1, p2 ) && ++p != pEnd )
123 #ifdef DMP_UNITTETRAINTERSECTIONBARY
124 std::cout << "All points equal" << std::endl;
130 while (( samePoint( p2, p3 ) || samePoint( p1, p3 )) && ++p != pEnd )
134 #ifdef DMP_UNITTETRAINTERSECTIONBARY
135 std::cout << "Only two points differ" << std::endl;
140 crossprod<3>( p1, p2, p3, polyNormal );
141 bool reverse = ( dotprod<3>( triNormal, polyNormal ) < 0.0 );
142 if (_isTetraInversed) reverse = !reverse;
145 _faces.push_back( std::vector< double* > () );
146 std::vector< double* >& faceCorner = _faces.back();
147 faceCorner.resize( pPolygonA->size()/* + 1*/ );
152 std::vector<double*>::const_reverse_iterator polyF = pPolygonA->rbegin(), polyEnd;
153 for ( polyEnd = pPolygonA->rend(); polyF != polyEnd; ++i, ++polyF )
154 if ( i==0 || !samePoint( *polyF, faceCorner[i-1] ))
155 copyVector3( *polyF, faceCorner[i] = new double[3] );
158 polyNormal[0] *= -1.;
159 polyNormal[1] *= -1.;
160 polyNormal[2] *= -1.;
164 std::vector<double*>::const_iterator polyF = pPolygonA->begin(), polyEnd;
165 for ( polyEnd = pPolygonA->end(); polyF != polyEnd; ++i, ++polyF )
166 if ( i==0 || !samePoint( *polyF, faceCorner[i-1] ))
167 copyVector3( *polyF, faceCorner[i] = new double[3] );
173 clearPolygons(); // free memory of _polygonA
174 _polygonA = faceCorner;
179 if ( i < (int)pPolygonA->size() )
180 faceCorner.resize( i );
182 if ( _polyNormals.empty() )
183 _polyNormals.reserve(4);
184 _polyNormals.push_back( std::vector< double >( polyNormal, polyNormal+3 ));
187 #ifdef DMP_UNITTETRAINTERSECTIONBARY
188 std::cout << "**** addSide() " << _faces.size() << std::endl;
189 for ( int i = 0; i < faceCorner.size(); ++i )
191 double* p = faceCorner[i];
192 std::cout << i << ": ( " << p[0] << ", " << p[1] << ", " << p[2] << " )" << std::endl;
194 std::cout << "NORM: ( " << _polyNormals.back()[0] << ", " << _polyNormals.back()[1] << ", " << _polyNormals.back()[2] << " )" << std::endl;
199 //================================================================================
201 * \brief Computes and returns coordinates of barycentre
203 //================================================================================
205 bool UnitTetraIntersectionBary::getBary(double* baryCenter)
207 baryCenter[0] = baryCenter[1] = baryCenter[2] = -1.0;
208 if ( addSideFaces() < NB_TETRA_SIDES )
210 // tetra is not intersected
211 if ( fabs(_int_volume) > 1e-10 )
213 // tetra is fully inside the other cell
214 baryCenter[0] = baryCenter[1] = baryCenter[2] = 0.25;
215 _int_volume = 0.16666666666666666;
221 // - pick up one point P among the summits of the polyhedron
222 // - for each face of the polyhedron which does not contain the point :
223 // - compute the barycenter of the volume obtained by forming the "pyramid" with
224 // the face as a base and point P as a summit
225 // - compute the volume of the "pyramid"
226 // - Add up all barycenter positions weighting them with the volumes.
228 baryCenter[0] = baryCenter[1] = baryCenter[2] = 0.;
230 std::list< std::vector< double* > >::iterator f = _faces.begin(), fEnd = _faces.end();
231 double * PP = f->at(0);
233 for ( ++f; f != fEnd; ++f )
235 std::vector< double* >& polygon = *f;
236 if ( polygon.empty() )
239 bool pBelongsToPoly = false;
240 std::vector<double*>::iterator v = polygon.begin(), vEnd = polygon.end();
241 for ( ; !pBelongsToPoly && v != vEnd; ++v )
242 pBelongsToPoly = samePoint( PP, *v );
243 if ( pBelongsToPoly )
246 // Compute the barycenter of the volume. Barycenter of pyramid is on line
247 // ( barycenter of polygon -> PP ) with 1/4 of pyramid height from polygon.
249 double bary[] = { 0, 0, 0 };
252 for ( v = polygon.begin(); v != vEnd ; ++v )
259 bary[0] /= (int)polygon.size();
260 bary[1] /= (int)polygon.size();
261 bary[2] /= (int)polygon.size();
265 for ( int i = 0; i < (int)polygon.size(); ++i )
267 double* p1 = polygon[i];
268 double* p2 = polygon[(i+1)%polygon.size()];
269 vol += std::fabs( calculateVolumeForTetra( p1, p2, bary, PP ));
272 // put bary on the line ( barycenter of polygon -> PP ) and multiply by volume
273 baryCenter[0] += ( bary[0] * 0.75 + PP[0] * 0.25 ) * vol;
274 baryCenter[1] += ( bary[1] * 0.75 + PP[1] * 0.25 ) * vol;
275 baryCenter[2] += ( bary[2] * 0.75 + PP[2] * 0.25 ) * vol;
277 if ( _int_volume < 0. )
278 _int_volume = -_int_volume;
279 baryCenter[0] /= _int_volume;
280 baryCenter[1] /= _int_volume;
281 baryCenter[2] /= _int_volume;
283 #ifdef DMP_UNITTETRAINTERSECTIONBARY
284 std::cout.precision(5);
285 std::cout << "**** Barycenter " << baryCenter[0] <<", "<< baryCenter[1] <<", "<< baryCenter[2]
286 << "\t **** Volume " << _int_volume << std::endl;
287 std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl;
292 //================================================================================
294 * \brief Add faces of the intersection polyhedron formed on faces of the
295 * unit tetrahedron by sides of already added faces
296 * \retval int - number of faces of intersection polyhedron
298 //================================================================================
300 int UnitTetraIntersectionBary::addSideFaces()
302 int nbPolyhedraFaces = 0;
304 if ( _faces.empty() )
305 return nbPolyhedraFaces;
307 // -------------------------------------------
308 // Detect polygons laying on sides of a tetra
309 // -------------------------------------------
311 bool sideAdded[NB_TETRA_SIDES] = { false, false, false, false };
312 int nbAddedSides = 0;
313 std::list< std::vector< double* > >::iterator f = _faces.begin(), fEnd = _faces.end();
314 for ( ; f != fEnd; ++f )
316 std::vector< double* >& polygon = *f;
317 double coordSum[3] = {0,0,0};
318 for ( int i = 0; i < (int)polygon.size(); ++i )
320 double* p = polygon[i];
325 for ( int j = 0; j < 3 && !sideAdded[j]; ++j )
327 if ( epsilonEqual( coordSum[j], 0.0 ))
328 sideAdded[j] = ++nbAddedSides != 0 ;
330 if ( !sideAdded[3] &&
331 ( epsilonEqual( (coordSum[0]+coordSum[1]+coordSum[2]) / (int)polygon.size(), 1. )))
332 sideAdded[3] = ++nbAddedSides != 0 ;
334 if ( nbAddedSides == NB_TETRA_SIDES )
337 // ---------------------------------------------------------------------------------
338 // Add segments of already added polygons to future polygonal faces on sides of tetra
339 // ---------------------------------------------------------------------------------
341 std::size_t nbIntersectPolygs = _faces.size();
343 std::vector< double* > * sideFaces[ 4 ]; // future polygons on sides of tetra
344 for ( int i = 0; i < NB_TETRA_SIDES; ++i )
347 if ( !sideAdded[ i ] )
349 _faces.push_back( std::vector< double* > () );
350 sideFaces[ i ] = &_faces.back();
353 f = _faces.begin(), fEnd = _faces.end();
354 for ( std::size_t iF = 0; iF < nbIntersectPolygs; ++f, ++iF ) // loop on added intersection polygons
356 std::vector< double* >& polygon = *f;
357 for ( std::size_t i = 0; i < polygon.size(); ++i )
360 double* p1 = polygon[i];
361 double* p2 = polygon[(i+1)%polygon.size()];
362 bool p1OnSide, p2OnSide;//, onZeroSide = false;
363 for ( int j = 0; j < 3; ++j )
365 if ( !sideFaces[ j ] )
367 p1OnSide = epsilonEqual( p1[j], 0. );
368 p2OnSide = epsilonEqual( p2[j], 0. );
369 if ( p1OnSide && p2OnSide )
371 // segment p1-p2 is on j-th orthogonal side of tetra
372 sideFaces[j]->push_back( new double[3] );
373 copyVector3( p1, sideFaces[j]->back() );
374 sideFaces[j]->push_back( new double[3] );
375 copyVector3( p2, sideFaces[j]->back() );
379 // check if the segment p1-p2 is on the inclined side
381 epsilonEqual( p1[_X] + p1[_Y] + p1[_Z], 1.0 ) &&
382 epsilonEqual( p2[_X] + p2[_Y] + p2[_Z], 1.0 ))
384 sideFaces[3]->push_back( new double[3] );
385 copyVector3( p1, sideFaces[3]->back() );
386 sideFaces[3]->push_back( new double[3] );
387 copyVector3( p2, sideFaces[3]->back() );
391 #ifdef DMP_UNITTETRAINTERSECTIONBARY
392 std::cout << "**** after Add segments to sides " << std::endl;
393 for ( int i = 0; i < NB_TETRA_SIDES; ++i )
395 std::cout << "\t Side " << i << std::endl;
398 std::cout << "\t cut by triagle" << std::endl;
402 std::vector< double* >& sideFace = *sideFaces[i];
403 for ( int i = 0; i < sideFace.size(); ++i )
405 double* p = sideFace[i];
406 std::cout << "\t" << i << ": ( " << p[0] << ", " << p[1] << ", " << p[2] << " )" << std::endl;
412 // ---------------------------------------------------------------------------
413 // Make closed polygons on tetra sides by adding not cut off corners of tetra
414 // ---------------------------------------------------------------------------
416 double origin[3] = { 0,0,0 };
418 // find corners of tetra cut off by triangles of other tetra
419 // ---------------------------------------------------------
421 // corners are coded like this: index = 1*X + 2*Y + 3*Z
422 // (0,0,0) -> index == 0; (0,0,1) -> index == 3
423 int cutOffCorners[NB_TETRA_NODES] = { false, false, false, false };
424 int passedCorners[NB_TETRA_NODES] = { false, false, false, false };
426 // find cutOffCorners by normals of intersection polygons
427 int nbCutOffCorners = 0;
428 for ( int ic = 0; ic < NB_TETRA_NODES; ++ic )
430 f = _faces.begin(), fEnd = _faces.end();
431 for ( std::size_t iF = 0; iF < nbIntersectPolygs; ++f, ++iF ) // loop on added intersection polygons
433 std::vector< double* >& polygon = *f;
435 double corner2Poly[3] = { polygon[0][0], polygon[0][1], polygon[0][2] };
436 if ( ic ) corner2Poly[ ic-1 ] -= 1.0;
438 // _polyNormals are outside of a tetrahedron
439 double dot = dotprod<3>( corner2Poly, &_polyNormals[iF][0] );
440 if ( dot < -DEFAULT_ABS_TOL*DEFAULT_ABS_TOL )
442 #ifdef DMP_UNITTETRAINTERSECTIONBARY
443 std::cout << "side " << iF+1 << ": cut " << ic << std::endl;
445 cutOffCorners[ ic ] = true;
452 for ( int i = 0; i < 3; ++i ) // loop on orthogonal faces of the unit tetra
454 if ( !sideFaces[i] ) continue;
455 std::vector< double* >& sideFace = *sideFaces[i];
457 std::size_t nbPoints = sideFace.size();
459 continue; // not intersected face at all - no cut off corners can be detected
461 int ind1 = (i+1)%3, ind2 = (i+2)%3; // indices of coords on i-th tetra side
464 bool isSegmentOnEdge=false;
465 for ( std::size_t ip = 0; ip < nbPoints; ++ip )
467 std::size_t isSegmentEnd = ( ip % 2 );
469 double* p = sideFace[ ip ];
470 double* p2 = isSegmentEnd ? 0 : sideFace[ip+1];
473 isSegmentOnEdge = false; // initialize
475 int cutOffIndex = -1, passThIndex = -1;// no cut off neither pass through
476 int pCut[] = { 0,0,0 }, pPass[] = { 0,0,0 };
478 if ( epsilonEqual( p[ind1], 0.))
480 // point is on orthogonal edge
481 if ( !isSegmentEnd && epsilonEqual( p2[ind1], 0. ))
482 isSegmentOnEdge = true;
484 if ( !isSegmentOnEdge )
485 { // segment ends are on different edges
486 pCut[ind2] = (int)isSegmentEnd; // believe that cutting triangles are well oriented
487 cutOffIndex = pCut[0] + 2*pCut[1] + 3*pCut[2];
489 if ( epsilonEqual( p[ind2], 0.) || epsilonEqual( p[ind2], 1.))
491 pPass[ind2] = ( p[ind2] < 0.5 ) ? 0 : 1;
492 passThIndex = pPass[0] + 2*pPass[1] + 3*pPass[2];
495 else if ( epsilonEqual( p[ind2], 0.))
497 // point is on orthogonal edge
498 if ( !isSegmentEnd && epsilonEqual( p2[ind2], 0. ))
499 isSegmentOnEdge = true;
501 {// segment ends are on different edges
502 pCut[ind1] = 1-(int)isSegmentEnd;
503 cutOffIndex = pCut[0] + 2*pCut[1] + 3*pCut[2];
505 if ( epsilonEqual( p[ind1], 0.) || epsilonEqual( p[ind1], 1.))
507 pPass[ind1] = ( p[ind1] < 0.5 ) ? 0 : 1;
508 passThIndex = pPass[0] + 2*pPass[1] + 3*pPass[2];
511 else if ( epsilonEqual(p[ind1] + p[ind2], 1.0 ))
513 // point is on inclined edge
514 if ( !isSegmentEnd && epsilonEqual(p2[ind1] + p2[ind2], 1.0 ))
515 isSegmentOnEdge = true;
516 if ( !isSegmentOnEdge )
517 { //segment ends are on different edges
518 pCut[ind1] = (int)isSegmentEnd;
519 pCut[ind2] = 1-(int)isSegmentEnd;
520 cutOffIndex = pCut[0] + 2*pCut[1] + 3*pCut[2];
527 // remember cut off and passed through points
528 if ( passThIndex >= 0 )
530 passedCorners[ passThIndex ] = true;
531 if ( cutOffCorners[ passThIndex ] )
534 cutOffCorners[ passThIndex ] = false;
535 #ifdef DMP_UNITTETRAINTERSECTIONBARY
536 std::cout << "PASS THROUGH " << passThIndex << std::endl;
540 if ( cutOffIndex >= 0 )
543 if ( !passedCorners[ cutOffIndex ] && !cutOffCorners[ cutOffIndex ] )
546 cutOffCorners[ cutOffIndex ] = true;
549 } // loop on points on a unit tetra side
551 if ( nbCutOnSide == 0 && nbPoints <= 2 )
552 continue; // one segment laying on edge at most
554 if ( nbCutOffCorners == NB_TETRA_NODES )
555 break; // all tetra corners are cut off
557 if ( /*nbCutOnSide <= 2 &&*/ nbPoints >= 6 )
559 // at least 3 segments - all corners of a side are cut off
560 for (int cutIndex = 0; cutIndex < NB_TETRA_NODES; ++cutIndex )
561 if ( cutIndex != i+1 && !passedCorners[ cutIndex ] && !cutOffCorners[ cutIndex ])
562 cutOffCorners[ cutIndex ] = ++nbCutOffCorners != 0 ;
566 // loop on orthogonal faces of tetra
568 // check if all corners are cut off on the inclined tetra side
569 if ( sideFaces[ XYZ ] && sideFaces[ XYZ ]->size() >= 6 )
571 for (int cutIndex = 1; cutIndex < NB_TETRA_NODES; ++cutIndex )
572 if ( !passedCorners[ cutIndex ] && !cutOffCorners[ cutIndex ])
573 cutOffCorners[ cutIndex ] = ++nbCutOffCorners != 0 ;
576 // Add to faces on tetra sides the corners not cut off by segments of intersection polygons
577 // ----------------------------------------------------------------------------------
578 if ( nbCutOffCorners > 0 )
580 for ( int i = 0; i < NB_TETRA_SIDES; ++i )
582 if ( !sideFaces[ i ] ) continue;
583 std::vector< double* >& sideFace = *sideFaces[i];
585 int excludeCorner = (i + 1) % NB_TETRA_NODES;
586 for ( int ic = 0; ic < NB_TETRA_NODES; ++ic )
588 if ( !cutOffCorners[ ic ] && ic != excludeCorner )
590 sideFace.push_back( new double[3] );
591 copyVector3( origin, sideFace.back() );
593 sideFace.back()[ ic-1 ] = 1.0;
599 #ifdef DMP_UNITTETRAINTERSECTIONBARY
600 std::cout << "**** after Add corners to sides " << std::endl;
601 for ( int i = 0; i < NB_TETRA_SIDES; ++i )
603 std::cout << "\t Side " << i << std::endl;
604 if ( !sideFaces[i] ) {
605 std::cout << "\t cut by triagle" << std::endl;
609 std::vector< double* >& sideFace = *sideFaces[i];
610 for ( int i = 0; i < sideFace.size(); ++i )
612 double* p = sideFace[i];
613 std::cout << "\t" << i << ": ( " << p[0] << ", " << p[1] << ", " << p[2] << " )" << std::endl;
617 std::cout << "Cut off corners: ";
618 if ( nbCutOffCorners == 0 )
621 for ( int ic = 0; ic < NB_TETRA_NODES; ++ic )
622 std::cout << cutOffCorners[ ic ];
623 std::cout << std::endl;
625 // ------------------------------------------------------------------------
626 // Sort corners of filled up faces on tetra sides and exclude equal points
627 // ------------------------------------------------------------------------
630 for ( f = _faces.begin(); f != fEnd; ++f, ++iF )
632 std::vector< double* >& face = *f;
633 if ( face.size() >= 3 )
635 clearPolygons(); // free memory of _polygonA
638 face.reserve( _polygonA.size() );
639 if ( iF >= nbIntersectPolygs )
640 { // sort points of side faces
641 calculatePolygonBarycenter( A, _barycenterA );
642 setTriangleOnSide( (int)(iF-nbIntersectPolygs) );
643 sortIntersectionPolygon( A, _barycenterA );
645 // exclude equal points
646 std::vector< double* >::iterator v = _polygonA.begin(), vEnd = _polygonA.end();
647 face.push_back( *v );
649 for ( ++v; v != vEnd; ++v )
651 double* pPrev = face.back();
653 if ( !samePoint( p, pPrev ))
660 if ( face.size() < 3 )
661 { // size could decrease
662 clearPolygons(); // free memory of _polygonA
671 #ifdef DMP_UNITTETRAINTERSECTIONBARY
672 std::cout << "**** after HEALING all faces " << std::endl;
673 for (iF=0, f = _faces.begin(); f != fEnd; ++f, ++iF )
675 std::cout << "\t Side " << iF << std::endl;
676 std::vector< double* >& sideFace = *f;
677 for ( int i = 0; i < sideFace.size(); ++i )
679 double* p = sideFace[i];
680 std::cout << "\t" << i << ": ( " << p[0] << ", " << p[1] << ", " << p[2] << " )" << std::endl;
684 return nbPolyhedraFaces;
687 //================================================================================
689 * \brief set corners of inherited TransformedTriangle as corners of i-th side of
690 * the Unit tetra. It is necessary to sort points of faces on sides of the unit
691 * tetrahedron using sortIntersectionPolygon(A)
693 //================================================================================
695 void UnitTetraIntersectionBary::setTriangleOnSide(int iSide)
699 for(int i = 0 ; i < 3 ; ++i)
701 _coords[5*i] = _coords[5*i + 1] = _coords[5*i + 2] = 0.;
703 _coords[5*i + i] = 1.;
707 //================================================================================
709 * \brief Free memory of polygons
711 //================================================================================
713 void UnitTetraIntersectionBary::clearPolygons(bool andFaces)
715 for(std::vector<double*>::iterator it = _polygonA.begin() ; it != _polygonA.end() ; ++it)
719 for(std::vector<double*>::iterator it = _polygonB.begin() ; it != _polygonB.end() ; ++it)
730 std::list< std::vector< double* > >::iterator f = this->_faces.begin(), fEnd = this->_faces.end();
731 for ( ; f != fEnd; ++f )
733 std::vector< double* >& polygon = *f;
734 for(std::vector<double*>::iterator it = polygon.begin() ; it != polygon.end() ; ++it)
740 this->_faces.clear();
744 //================================================================================
746 * \brief Destructor clears coordinates of faces
748 //================================================================================
750 UnitTetraIntersectionBary::~UnitTetraIntersectionBary()
752 clearPolygons(/*andFaces=*/true );