Moka kernel
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
gmg-topology.cc
Go to the documentation of this file.
1 /*
2  * lib-gmapkernel : Un noyau de 3-G-cartes et des opérations.
3  * Copyright (C) 2004, Moka Team, Université de Poitiers, Laboratoire SIC
4  * http://www.sic.sp2mi.univ-poitiers.fr/
5  * Copyright (C) 2009, Guillaume Damiand, CNRS, LIRIS,
6  * guillaume.damiand@liris.cnrs.fr, http://liris.cnrs.fr/
7  *
8  * This file is part of lib-gmapkernel
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program. If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 //******************************************************************************
25 #include "g-map-generic.hh"
26 #include <iostream>
27 #include <sstream>
28 #include <cstdlib>
29 using namespace std;
30 using namespace GMap3d;
31 //******************************************************************************
33  TOrbit AOrbit)
34 {
35  int mark = getNewMark();
36 
37  halfMarkOrbit(ADart1, AOrbit, mark);
38  bool same = isMarked(ADart2, mark);
39  unmarkOrbit(ADart1, AOrbit, mark);
40 
41  freeMark(mark);
42 
43  return same;
44 }
45 //******************************************************************************
47 {
48  assert(ADart!=NULL);
49  assert(AOrbit>=ORBIT_SELF && AOrbit<=ORBIT_CC);
50 
51  if (AOrbit!=ORBIT_VERTEX && AOrbit!=ORBIT_VOLUME && AOrbit!=ORBIT_CC)
52  return true;
53 
54  bool orientable = true;
55 
56  bool usedDim[4];
57  int selected = getNewMark();
58 
59  for (int dim=0; dim<=3; ++dim)
60  usedDim[dim] = AND_ORBIT(AOrbit, ORBIT_DIM[dim]) != ORBIT_SELF;
61 
62  CCoverage * cov = getDynamicCoverage(ADart, AOrbit);
63 
64  setMark(**cov, selected);
65 
66  for (; cov->cont() && orientable; ++(*cov))
67  if (isMarked(**cov, selected))
68  {
69  for (int dim=0; dim<=3; ++dim)
70  if (usedDim[dim] && !isFree(**cov, dim) &&
71  isMarked(alpha(**cov, dim), selected))
72  orientable = false;
73  }
74  else
75  {
76  for (int dim=0; dim<=3; ++dim)
77  if (usedDim[dim] && !isFree(**cov, dim))
78  setMark(alpha(**cov, dim), selected);
79  }
80 
81  delete cov;
82 
83  unmarkOrbit(ADart, AOrbit, selected);
84  freeMark(selected);
85 
86  return orientable;
87 }
88 //******************************************************************************
90 {
91  unsigned int res = 0;
92  int mark = getNewMark();
93  CDynamicCoverageAll it(this);
94  for (; it.cont(); ++it)
95  {
96  if ( !isMarked(*it, mark) )
97  {
98  if (!isLocalDegreeTwoSup(*it, 1)) ++res;
99  markOrbit(*it, ORBIT_EDGE,mark);
100  }
101  }
102  negateMaskMark(mark);
103  freeMark(mark);
104  return res;
105 }
106 //******************************************************************************
107 void CGMapGeneric::countBorders(int /*AMarkNumber*/,
108  int * ANb0, int * ANb1, int * ANb2, int * ANb3)
109 {
110  int * count[4] = { ANb0, ANb1, ANb2, ANb3 };
111  int dim[4];
112  int borderMark[4];
113  int nbAsked = 0;
114  int i;
115 
116  // Initialisations:
117  for (i=0; i<4; ++i)
118  if (count[i] != NULL)
119  {
120  count[nbAsked] = count[i];
121  * count[nbAsked] = 0;
122  dim[nbAsked] = i;
123  borderMark[nbAsked] = getNewMark();
124  ++nbAsked;
125  }
126 
127  CDynamicCoverageAll it(this);
128 
129  // Comptage et marquage des bords:
130  for (; it.cont(); ++it)
131  for (i=0; i<nbAsked; ++i)
132  if (isFree(*it, dim[i]) && !isMarked(*it, borderMark[i]))
133  {
134  ++ * count[i];
135  markOrbit(*it, ORBIT_BORDER[dim[i]], borderMark[i]);
136  }
137 
138  // Démarquage:
139  for (it.reinit(); it.cont(); ++it)
140  for (i=0; i<nbAsked; ++i)
141  if (isMarked(*it, borderMark[i]))
142  unmarkOrbit(*it, ORBIT_BORDER[dim[i]], borderMark[i]);
143 
144  // Libérations:
145  for (i=0; i<nbAsked; ++i)
146  freeMark(borderMark[i]);
147 }
148 //******************************************************************************
150  int * ANb0, int * ANb1, int * ANb2, int * ANb3)
151 {
152  assert(ADart != NULL);
153 
154  int * count[4] = { ANb0, ANb1, ANb2, ANb3 };
155  int dim[4];
156  int borderMark[4];
157  int nbAsked = 0;
158  int i;
159 
160  // Initialisations:
161  for (i=0; i<4; ++i)
162  if (count[i] != NULL)
163  {
164  count[nbAsked] = count[i];
165  * count[nbAsked] = 0;
166  dim[nbAsked] = i;
167  borderMark[nbAsked] = getNewMark();
168  ++nbAsked;
169  }
170 
171  CCoverage * cov = getDynamicCoverage(ADart, AOrbit);
172 
173  // Comptage et marquage des bords:
174  for (; cov->cont(); ++(*cov))
175  for (i=0; i<nbAsked; ++i)
176  if (isFree(**cov, dim[i]) && !isMarked(**cov, borderMark[i]))
177  {
178  ++ * count[i];
179  markOrbit(**cov, ORBIT_BORDER[dim[i]], borderMark[i]);
180  }
181 
182  // Démarquage:
183  for (cov->reinit(); cov->cont(); ++(*cov))
184  for (i=0; i<nbAsked; ++i)
185  if (isMarked(**cov, borderMark[i]))
186  unmarkOrbit(**cov, ORBIT_BORDER[dim[i]], borderMark[i]);
187 
188  // Libérations:
189  delete cov;
190 
191  for (i=0; i<nbAsked; ++i)
192  freeMark(borderMark[i]);
193 }
194 //******************************************************************************
195 void CGMapGeneric::countCells(int /*AMarkNumber*/,
196  int * ANb0, int * ANb1, int * ANb2,
197  int * ANb3, int * ANb4, int * ANbDarts)
198 {
199  int * count[5] = { ANb0, ANb1, ANb2, ANb3, ANb4 };
200  int dim[5];
201  int cellMark[5];
202  int nbAsked = 0;
203  int nbDarts = 0;
204  int i;
205 
206  // Initialisations:
207  for (i=0; i<5; ++i)
208  if (count[i] != NULL)
209  {
210  count[nbAsked] = count[i];
211  * count[nbAsked] = 0;
212  dim[nbAsked] = i;
213  cellMark[nbAsked] = getNewMark();
214  ++nbAsked;
215  }
216 
217  // Comptage et marquage des cellules:
218  CDynamicCoverageAll it(this);
219  for (; it.cont(); ++it, ++nbDarts)
220  for (i=0; i<nbAsked; ++i)
221  if (!isMarked(*it, cellMark[i]))
222  {
223  ++ * count[i];
224  markOrbit(*it, ORBIT_CELL[dim[i]], cellMark[i]);
225  }
226 
227  if (ANbDarts != NULL)
228  * ANbDarts = nbDarts;
229 
230  // Démarquage:
231  for (it.reinit(); it.cont(); ++it)
232  for (i=0; i<nbAsked; ++i)
233  if (isMarked(*it, cellMark[i]))
234  unmarkOrbit(*it, ORBIT_CELL[dim[i]], cellMark[i]);
235 
236  // Libérations:
237  for (i=0; i<nbAsked; ++i)
238  freeMark(cellMark[i]);
239 }
240 //******************************************************************************
241 void CGMapGeneric::countCells(CDart * ADart, TOrbit AOrbit,
242  int * ANb0, int * ANb1, int * ANb2,
243  int * ANb3, int * ANb4, int * ANbDarts)
244 {
245  assert(ADart != NULL);
246 
247  int * count[5] = { ANb0, ANb1, ANb2, ANb3, ANb4 };
248  int dim[5];
249  int cellMark[5];
250  int nbAsked = 0;
251  int nbDarts = 0;
252  int i;
253 
254  // Initialisations:
255  for (i=0; i<5; ++i)
256  if (count[i] != NULL)
257  {
258  count[nbAsked] = count[i];
259  * count[nbAsked] = 0;
260  dim[nbAsked] = i;
261  cellMark[nbAsked] = getNewMark();
262  ++nbAsked;
263  }
264 
265  CCoverage * cov = getDynamicCoverage(ADart, AOrbit);
266 
267  // Comptage et marquage des cellules:
268  for (; cov->cont(); ++(*cov), ++nbDarts)
269  for (i=0; i<nbAsked; ++i)
270  if (!isMarked(**cov, cellMark[i]))
271  {
272  ++ * count[i];
273  markOrbit(**cov, ORBIT_CELL[dim[i]], cellMark[i]);
274  }
275 
276  if (ANbDarts != NULL)
277  * ANbDarts = nbDarts;
278 
279  // Démarquage:
280  for (cov->reinit(); cov->cont(); ++(*cov))
281  for (i=0; i<nbAsked; ++i)
282  if (isMarked(**cov, cellMark[i]))
283  unmarkOrbit(**cov, ORBIT_CELL[dim[i]], cellMark[i]);
284 
285  // Libérations:
286  delete cov;
287 
288  for (i=0; i<nbAsked; ++i)
289  freeMark(cellMark[i]);
290 }
291 //******************************************************************************
293  int * ANbVertices, int * ANbEdges,
294  int * ANbFaces, int * ANbVolumes,
295  int * ANbCC,
296  int * ANb0Borders,
297  int * ANb1Borders,
298  int * ANb2Borders,
299  int * ANb3Borders)
300 {
301  int mark = getNewMark();
302 
303  negateMaskMark(mark);
304 
305  countCells (mark, ANbVertices, ANbEdges, ANbFaces, ANbVolumes, ANbCC,
306  ANbDarts);
307 
308  countBorders(mark, ANb0Borders, ANb1Borders, ANb2Borders, ANb3Borders);
309 
310  negateMaskMark(mark);
311  freeMark(mark);
312 }
313 //******************************************************************************
315  int * ANbDarts,
316  int * ANbVertices,
317  int * ANbEdges,
318  int * ANbFaces,
319  int * ANb0Borders,
320  int * ANb1Borders,
321  int * ANb2Borders,
322  int * ANb2BordersWhenClosed,
323  int * AEuler,
324  int * AOrient,
325  int * AGenus)
326 {
327  assert(ADart != NULL);
328 
329  int nd ; // nbDarts
330  int nc[3]; // nbCells
331  int nb[3]; // nbBorders
332 
333  countCells (ADart, ORBIT_VOLUME, & nc[0], & nc[1], & nc[2], NULL, NULL, & nd);
334  countBorders(ADart, ORBIT_VOLUME, & nb[0], & nb[1], & nb[2], NULL);
335 
336  if (ANbDarts != NULL) * ANbDarts = nd;
337  if (ANbVertices != NULL) * ANbVertices = nc[0];
338  if (ANbEdges != NULL) * ANbEdges = nc[1];
339  if (ANbFaces != NULL) * ANbFaces = nc[2];
340  if (ANb0Borders != NULL) * ANb0Borders = nb[0];
341  if (ANb1Borders != NULL) * ANb1Borders = nb[1];
342  if (ANb2Borders != NULL) * ANb2Borders = nb[2];
343 
344  bool needGenus = AGenus != NULL;
345  bool needOrient = AOrient != NULL || needGenus;
346  bool needEuler = AEuler != NULL || needOrient;
347 
348  int euler = 0, orient = 0, genus = 0;
349 
350  if (needEuler)
351  {
352  // Réservation des ressources:
353  int memoAlpha3 = getNewDirectInfo();
354  int newDart = getNewMark();
355 
356  CDynamicCoverageVolume vol(this, ADart);
357 
358  // Isolation du volume:
359  for (; vol.cont(); ++vol)
360  {
361  setDirectInfo(*vol, memoAlpha3, alpha3(*vol));
362  (*vol)->setFree3();
363  }
364 
365  // Fermetures:
366  negateMaskMark(newDart);
367 
368  for (vol.reinit(); vol.cont(); ++vol)
369  if (isFree0(*vol))
370  CGMapGeneric::stopUp(*vol, 0);
371 
372  for (vol.reinit(); vol.cont(); ++vol)
373  if (isFree1(*vol))
374  CGMapGeneric::stopUp(*vol, 1);
375 
376  negateMaskMark(newDart);
377 
378  // Recalcul du nombre de cellules et du nombre de bords:
379  countCells (ADart, ORBIT_VOLUME,
380  & nc[0], & nc[1], &nc[2], NULL, NULL, NULL);
381  countBorders(ADart, ORBIT_VOLUME, & nb[0], & nb[1], & nb[2], NULL);
382 
383  if (ANbVertices != NULL) * ANbVertices = nc[0];
384  if (ANbEdges != NULL) * ANbEdges = nc[1];
385  if (ANbFaces != NULL) * ANbFaces = nc[2];
386  if (ANb0Borders != NULL) * ANb0Borders = nb[0];
387  if (ANb1Borders != NULL) * ANb1Borders = nb[1];
388  if (ANb2Borders != NULL) * ANb2Borders = nb[2];
389 
390  if (ANb2BordersWhenClosed != NULL)
391  * ANb2BordersWhenClosed = nb[2];
392 
393  // Caractéristique d'Euler:
394  euler = nc[0] - nc[1] + nc[2];
395 
396  // Suppression des brins créés lors de la fermeture:
397  for (vol.reinit(); vol.cont(); )
398  {
399  CDart * current = vol++;
400 
401  if (isMarked(current, newDart))
402  {
403  alpha0(current)->setFree0();
404  alpha1(current)->setFree1();
405  alpha2(current)->setFree2();
406 
407  delMapDart(current);
408  }
409  }
410 
411  // Restauration des 3-coutures:
412  for (vol.reinit(); vol.cont(); ++vol)
413  (*vol)->setAlpha3(getDirectInfoAsDart(*vol, memoAlpha3));
414 
415  // Libération des ressources:
416  freeDirectInfo(memoAlpha3);
417  freeMark(newDart);
418 
419  // Écriture du résultat:
420  if (AEuler != NULL)
421  * AEuler = euler;
422  }
423 
424  if (needOrient)
425  {
426  orient =
427  isOrientable(ADart, ORBIT_VOLUME)
428  ? 0
429  : 2 - abs((euler + nb[2]) % 2);
430 
431  if (AOrient != NULL)
432  * AOrient = orient;
433  }
434 
435  if (needGenus)
436  {
437  assert((euler + nb[2] + orient) % 2 == 0);
438 
439  genus = 1 - (euler + nb[2] + orient) / 2;
440 
441  if (AGenus != NULL)
442  * AGenus = genus;
443  }
444 }
445 //******************************************************************************
446 std::ostream& CGMapGeneric::displayCharacteristics(std::ostream &AOs)
447 {
448  int nbd, nbv, nbe, nbf, nbvol, nbcc;
449  getGlobalCharacteristics(&nbd, &nbv, &nbe, &nbf, &nbvol, &nbcc,
450  NULL, NULL, NULL, NULL);
451  AOs<<"Nb of darts= "<<nbd<<"; vertices= "<<nbv<<"; edges="<<nbe
452  <<"; faces="<<nbf<<"; volumes="<<nbvol<<"; cc="<<nbcc;
453  return AOs;
454 }
455 //******************************************************************************
456 string CGMapGeneric::getSurfaceName(int AB2, int AQ, int AG)
457 {
458  assert(0 <= AB2);
459  assert(0 <= AQ && AQ <= 2);
460  assert(0 <= AG);
461 
462  ostringstream result;
463 
464  result << "S(" << AB2 << "," << AQ << "," << AG << ")" << ": ";
465 
466  switch (AQ)
467  {
468  case 0:
469  // Surface orientable:
470  {
471  if (AG == 0)
472  // Sans trou:
473  {
474  switch (AB2)
475  {
476  case 0 : result << "Sphere"; break;
477  case 1 : result << "Disk"; break;
478  case 2 : result << "Strip" ; break;
479  default: result << "Sphere with " << AB2 << " borders";
480  }
481  }
482  else
483  // Avec trou(s):
484  {
485  result << "Torus";
486 
487  if (AG > 1)
488  result << " with " << AG << " tunnels";
489 
490  if (AB2 > 0)
491  result << (AG > 1 ? " and" : " with")
492  << " " << AB2 << " border" << (AB2 > 1 ? "s" : "");
493  }
494  }
495  break;
496  case 1:
497  // Surface non orientable type "ruban de Möbius":
498  {
499  if (AB2 == 0)
500  result << "Projective plane";
501  else
502  result << "Möbius strip";
503 
504  if (AG > 0)
505  result << " with " << AG << " tunnel" << (AG > 1 ? "s" : "");
506 
507  if (AB2 > 1)
508  result << (AG > 0 ? " and " : " with ") << AB2 << " borders";
509  }
510  break;
511  case 2:
512  // Surface non orientable type "bouteille de Klein":
513  {
514  result << "Klein bottle";
515 
516  if (AG > 0)
517  result << " with " << AG << " tunnel" << (AG > 1 ? "s" : "");
518 
519  if (AB2 > 0)
520  result << (AG > 0 ? " and " : " with ")
521  << AB2 << " border" << (AB2 > 1 ? "s" : "");
522  }
523  break;
524  default:
525  assert(false);
526  }
527 
528  return result.str();
529 }
530 //******************************************************************************
532 {
533  bool connex = true;
534  int reached = getNewMark();
535 
536  // Marquage d'une composante connexe:
537  markOrbit(getFirstDart(), ORBIT_CC, reached);
538 
539  // Parcours des brins de la G-carte:
540  for (CDynamicCoverageAll it(this); it.cont() && connex; ++it)
541  if (! isMarked(*it, reached))
542  connex = false;
543 
544  if (connex)
545  negateMaskMark(reached);
546  else
547  unmarkAll(reached);
548 
549  freeMark(reached);
550 
551  return connex;
552 }
553 //******************************************************************************
555 {
556  // 1: On vérifie que pour tout brin B, on alpha B == alpha(alpha(B,i),i)
557  for (int dim=0; dim<=3; ++dim)
558  for (CDynamicCoverageAll it(this); it.cont(); ++it)
559  if (!isFree(*it, dim) && *it != alpha(alpha(*it,dim),dim))
560  {
561  cerr << "CGMapGeneric::integrity: The dart " << *it
562  << " does not satisfy the constraint "
563  << "alpha" << dim << "(alpha" << dim << "(brin)) == brin." << endl;
564 
565  return false;
566  }
567 
568  // 2: On vérifie que les involutions sont respectées:
569  bool ok = true;
570 
571  TOrbit involutions[3] = { ORBIT_02, ORBIT_03, ORBIT_13 };
572  int alphaI [3] = { 0 , 0 , 1 };
573  int alphaJ [3] = { 2, 3, 3 };
574 
575  int treated = getNewMark();
576 
577  for (int invoIndex=0; invoIndex<3 && ok; ++invoIndex)
578  {
579  TOrbit orbit = involutions[invoIndex];
580  int ai = alphaI[invoIndex];
581  int aj = alphaJ[invoIndex];
582 
583  for (CDynamicCoverageAll it(this); it.cont() && ok; ++it)
584  if (!isMarked(*it, treated))
585  {
586  if (isFree(*it, ai) || isFree(*it, aj))
587  setMark(*it, treated);
588  else
589  {
590  ok =
591  alpha( alpha(*it, ai) , aj) == alpha( alpha(*it, aj) , ai);
592 
593  if (!ok)
594  {
595  cerr << "CGMapGeneric::checkTopology: The involution "
596  << (invoIndex==0 ? "02" : invoIndex==1 ? "03" : "13")
597  << " is not satisfied for dart " << *it
598  << "." << endl;
599  }
600 
601  markOrbit(*it, orbit, treated);
602  }
603  }
604 
605  if (ok)
606  negateMaskMark(treated);
607  else
608  unmarkAll(treated);
609  }
610 
611  freeMark(treated);
612 
613  return ok;
614 }
615 //******************************************************************************
616 bool CGMapGeneric::checkEmbeddings(TOrbit AOrbit, int AAttributeIdentity,
617  bool AEmbeddingMustExist)
618 {
619  assert(isOrbitUsed(AOrbit));
620 
621  bool ok = true;
622 
623  int treated = getNewMark();
624 
625  for (CDynamicCoverageAll it(this); it.cont(); ++it)
626  if (!isMarked(*it, treated))
627  {
628  int i = 0;
629 
630  CCoverage * cov = getDynamicCoverage(*it, AOrbit);
631 
632  for (; cov->cont(); ++(*cov))
633  {
634  if ((**cov)->getAttribute(AOrbit, AAttributeIdentity) != NULL)
635  ++i;
636 
637  setMark(**cov, treated);
638  }
639 
640  delete cov;
641 
642  if (i>1 || (i==0 && AEmbeddingMustExist))
643  {
644  static const char * ORBIT_NAMES[16] =
645  {
646  "dart","0","1","01","2","02","12","volume","3","03","13","face",
647  "23","edge","vertex","connected component"
648  };
649 
650  cerr << "CGMapGeneric::checkEmbeddings: The cell '"
651  << ORBIT_NAMES[AOrbit] << "' incident to dart " << *it << " ";
652 
653  if (i==0)
654  cerr << "has no embedding!" << endl;
655  else
656  cerr << " has " << i << " embeddings!" << endl;
657 
658  ok = false;
659  }
660  }
661 
662  negateMaskMark(treated);
663  freeMark(treated);
664 
665  return ok;
666 }
667 //******************************************************************************
668 bool CGMapGeneric::isIClosedOrbit(CDart * ADart, int ADimension, TOrbit AOrbit)
669 {
670  assert(ADart!=NULL);
671  assert(0<=ADimension && ADimension<=3);
672 
673  CCoverage * cov = getDynamicCoverage(ADart, AOrbit);
674 
675  bool closed = true;
676 
677  for (; closed && cov->cont(); (*cov)++)
678  closed = ! isFree(**cov, ADimension);
679 
680  delete cov;
681 
682  return closed;
683 }
684 //------------------------------------------------------------------------------
685 bool CGMapGeneric::isIFreeOrbit(CDart * ADart, int ADimension, TOrbit AOrbit)
686 {
687  assert(ADart!=NULL);
688  assert(0<=ADimension && ADimension<=3);
689 
690  CCoverage * cov = getDynamicCoverage(ADart, AOrbit);
691 
692  bool free = true;
693 
694  for (; free && cov->cont(); (*cov)++)
695  free = isFree(**cov, ADimension);
696 
697  delete cov;
698 
699  return free;
700 }
701 //******************************************************************************
703 {
704  int res = -1;
705  int i = 0;
706  for (CDynamicCoverageAll it(this); it.cont(); ++it)
707  {
708  for (i = res + 1; i < 4; ++i)
709  if ( !isFree(*it, i) )
710  {
711  res = i;
712  if (res == 3) return res;
713  }
714  }
715 
716  return res;
717 }
718 //******************************************************************************
720 {
721  CDart* precDangling = ADart;
722 
723  // Go to dart preceding the dangling part
724  while( alpha2(precDangling)==alpha3(precDangling) &&
725  alpha10(precDangling)!=ADart )
726  {
727  // Work only for faces without boundary.
728  assert( !isFree1(precDangling) && !isFree0(alpha1(precDangling)) );
729  assert( !isFree2(precDangling) && !isFree3(precDangling) );
730  precDangling=alpha10(precDangling);
731  }
732 
733  if ( alpha2(precDangling)==alpha3(precDangling) )
734  return false; // Here the face is isolated
735 
736  // Here we are not in a dangling part. Now search the beginning of a
737  // dangling part.
738  CDart* current = precDangling;
739  do
740  {
741  // Work only for faces without boundary.
742  assert( !isFree0(current) && !isFree1(alpha0(current)) );
743  assert( !isFree2(current) && !isFree3(current) );
744  current=alpha01(current);
745  }
746  while( current!=precDangling && alpha2(current)!=alpha3(current) );
747 
748  if ( current==precDangling )
749  return false; // Here the face does not have a dangling part.
750 
751  // Here current is the first dart of a dangling part, and we know there is
752  // at least one non dangling part.
753  do
754  {
755  assert( !isFree0(current) && !isFree1(alpha0(current)) );
756  assert( !isFree2(current) && !isFree3(current) );
757  current=alpha01(current);
758  }
759  while( alpha2(current)==alpha3(current) );
760 
761  // Here current is the first dart of a non dangling part. This part must
762  // be connected till the end of the face.
763  do
764  {
765  current=alpha01(current);
766  // if ( alpha2(current)==alpha3(current) ) return false;
767 
768  CDart* d1 = current;
769  do
770  {
771  if ( isFree2(d1) || isFree1(alpha2(d1)) ) return false;
772  if ( alpha2(d1)==alpha3(d1) ) return false;
773  d1 = alpha21(d1);
774  }
775  while ( d1!=current);
776  }
777  while( current!=precDangling);
778  return true;
779 }
780 //******************************************************************************