initial commit for version 1.5.x patch release
[OpenFOAM-1.5.x.git] / applications / utilities / surface / surfaceSplitNonManifolds / surfaceSplitNonManifolds.C
blob965465e16c21079ceba72fc3970260e743942c03
1 /*---------------------------------------------------------------------------*\
2   =========                 |
3   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
4    \\    /   O peration     |
5     \\  /    A nd           | Copyright (C) 1991-2008 OpenCFD Ltd.
6      \\/     M anipulation  |
7 -------------------------------------------------------------------------------
8 License
9     This file is part of OpenFOAM.
11     OpenFOAM 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 2 of the License, or (at your
14     option) any later version.
16     OpenFOAM 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
19     for more details.
21     You should have received a copy of the GNU General Public License
22     along with OpenFOAM; if not, write to the Free Software Foundation,
23     Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 Description
26     Takes multiply connected surface and tries to split surface at
27     multiply connected edges by duplicating points. Introduces concept of
28     - borderEdge. Edge with 4 faces connected to it.
29     - borderPoint. Point connected to exactly 2 borderEdges.
30     - borderLine. Connected list of borderEdges.
32     By duplicating borderPoints this will split 'borderLines'. As a
33     preprocessing step it can detect borderEdges without any borderPoints
34     and explicitly split these triangles.
36     The problems in this algorithm are:
37     - determining which two (of the four) faces form a surface. Done by walking
38       face-edge-face while keeping and edge or point on the borderEdge
39       borderPoint.
40     - determining the outwards pointing normal to be used to slightly offset the
41       duplicated point.
43     Uses sortedEdgeFaces quite a bit.
45     Is tested on simple borderLines resulting from extracting a surface
46     from a hex mesh. Will quite possibly go wrong on more complicated border
47     lines (i.e. ones forming a loop).
49     Dumps surface every so often since might take a long time to complete.
51 \*---------------------------------------------------------------------------*/
53 #include "argList.H"
54 #include "triSurface.H"
55 #include "OFstream.H"
56 #include "ListOps.H"
57 #include "triSurfaceTools.H"
59 using namespace Foam;
61 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
63 void writeOBJ(Ostream& os, const pointField& pts)
65     forAll(pts, i)
66     {
67         const point& pt = pts[i];
69         os << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z() << endl;
70     }
74 void dumpPoints(const triSurface& surf, const labelList& borderPoint)
76     fileName fName("borderPoints.obj");
78     Info<< "Dumping borderPoints as Lightwave .obj file to " << fName
79         << "\nThis can be visualized with e.g. javaview (www.javaview.de)\n\n";
81     OFstream os(fName);
83     forAll(borderPoint, pointI)
84     {
85         if (borderPoint[pointI] != -1)
86         {
87             const point& pt = surf.localPoints()[pointI];
89             os << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z() << endl;
90         }
91     }
95 void dumpEdges(const triSurface& surf, const boolList& borderEdge)
97     fileName fName("borderEdges.obj");
99     Info<< "Dumping borderEdges as Lightwave .obj file to " << fName
100         << "\nThis can be visualized with e.g. javaview (www.javaview.de)\n\n";
102     OFstream os(fName);
104     writeOBJ(os, surf.localPoints());
106     forAll(borderEdge, edgeI)
107     {
108         if (borderEdge[edgeI])
109         {
110             const edge& e = surf.edges()[edgeI];
112             os << "l " << e.start()+1 << ' ' << e.end()+1 << endl;
113         }
114     }
118 void dumpFaces
120     const fileName& fName,
121     const triSurface& surf,
122     const Map<label>& connectedFaces
125     Info<< "Dumping connectedFaces as Lightwave .obj file to " << fName
126         << "\nThis can be visualized with e.g. javaview (www.javaview.de)\n\n";
128     OFstream os(fName);
130     for
131     (
132         Map<label>::const_iterator iter = connectedFaces.begin();
133         iter != connectedFaces.end();
134         ++iter
135     )
136     {
137         const labelledTri& f = surf.localFaces()[iter.key()];
139         point ctr(f.centre(surf.localPoints()));
141         os << "v " << ctr.x() << ' ' << ctr.y() << ' ' << ctr.z() << endl;
142     }
146 void testSortedEdgeFaces(const triSurface& surf)
148     const labelListList& edgeFaces = surf.edgeFaces();
149     const labelListList& sortedEdgeFaces = surf.sortedEdgeFaces();
151     forAll(edgeFaces, edgeI)
152     {
153         const labelList& myFaces = edgeFaces[edgeI];
154         const labelList& sortMyFaces = sortedEdgeFaces[edgeI];
156         forAll(myFaces, i)
157         {
158             if (findIndex(sortMyFaces, myFaces[i]) == -1)
159             {
160                 FatalErrorIn("testSortedEdgeFaces") << abort(FatalError);
161             }
162         }
163         forAll(sortMyFaces, i)
164         {
165             if (findIndex(myFaces, sortMyFaces[i]) == -1)
166             {
167                 FatalErrorIn("testSortedEdgeFaces") << abort(FatalError);
168             }
169         }
170     }
174 // Mark off all border edges. Return number of border edges.
175 label markBorderEdges
177     const bool debug,
178     const triSurface& surf,
179     boolList& borderEdge
182     label nBorderEdges = 0;
184     const labelListList& edgeFaces = surf.edgeFaces();
186     forAll(edgeFaces, edgeI)
187     {
188         if (edgeFaces[edgeI].size() == 4)
189         {
190             borderEdge[edgeI] = true;
192             nBorderEdges++;
193         }
194     }
196     if (debug)
197     {
198         dumpEdges(surf, borderEdge);
199     }
201     return nBorderEdges;
205 // Mark off all border points. Return number of border points. Border points
206 // marked by setting value to newly introduced point.
207 label markBorderPoints
209     const bool debug,
210     const triSurface& surf,
211     const boolList& borderEdge,
212     labelList& borderPoint
215     label nPoints = surf.nPoints();
217     const labelListList& pointEdges = surf.pointEdges();
219     forAll(pointEdges, pointI)
220     {
221         const labelList& pEdges = pointEdges[pointI];
223         label nBorderEdges = 0;
225         forAll(pEdges, i)
226         {
227             if (borderEdge[pEdges[i]])
228             {
229                 nBorderEdges++;
230             }
231         }
233         if (nBorderEdges == 2 && borderPoint[pointI] == -1)
234         {
235             borderPoint[pointI] = nPoints++;
236         }
237     }
239     label nBorderPoints = nPoints - surf.nPoints();
241     if (debug)
242     {
243         dumpPoints(surf, borderPoint);
244     }
246     return nBorderPoints;
250 // Get minumum length of edges connected to pointI
251 // Serves to get some local length scale.
252 scalar minEdgeLen(const triSurface& surf, const label pointI)
254     const pointField& points = surf.localPoints();
256     const labelList& pEdges = surf.pointEdges()[pointI];
258     scalar minLen = GREAT;
260     forAll(pEdges, i)
261     {
262         label edgeI = pEdges[i];
264         scalar len = surf.edges()[edgeI].mag(points);
266         if (len < minLen)
267         {
268             minLen = len;
269         }
270     }
271     return minLen;
275 // Find edge among edgeLabels with endpoints v0,v1
276 label findEdge
278     const triSurface& surf,
279     const labelList& edgeLabels,
280     const label v0,
281     const label v1
284     forAll(edgeLabels, i)
285     {
286         label edgeI = edgeLabels[i];
288         const edge& e = surf.edges()[edgeI];
290         if
291         (
292             (
293                 e.start() == v0
294              && e.end() == v1
295             )
296          || (
297                 e.start() == v1
298              && e.end() == v0
299             )
300         )
301         {
302             return edgeI;
303         }
304     }
307     FatalErrorIn("findEdge") << "Cannot find edge with labels " << v0
308         << ' ' << v1 << " in candidates " << edgeLabels
309         << " with vertices:" << IndirectList<edge>(surf.edges(), edgeLabels)()
310         << abort(FatalError);
312     return -1;
316 // Get the other edge connected to pointI on faceI.
317 label otherEdge
319     const triSurface& surf,
320     const label faceI,
321     const label otherEdgeI,
322     const label pointI
325     const labelList& fEdges = surf.faceEdges()[faceI];
327     forAll(fEdges, i)
328     {
329         label edgeI = fEdges[i];
331         const edge& e = surf.edges()[edgeI];
333         if
334         (
335             edgeI != otherEdgeI
336          && (
337                 e.start() == pointI
338              || e.end() == pointI
339             )
340         )
341         {
342             return edgeI;
343         }
344     }
346     FatalErrorIn("otherEdge") << "Cannot find other edge on face " << faceI
347         << " verts:" << surf.localPoints()[faceI]
348         << " connected to point " << pointI
349         << " faceEdges:" << IndirectList<edge>(surf.edges(), fEdges)()
350         << abort(FatalError);
352     return -1;
354     
356 // Starting from startPoint on startEdge on startFace walk along border
357 // and insert faces along the way. Walk keeps always one point or one edge
358 // on the border line.
359 void walkSplitLine
361     const triSurface& surf,
362     const boolList& borderEdge,
363     const labelList& borderPoint,
365     const label startFaceI,
366     const label startEdgeI,     // is border edge
367     const label startPointI,    // is border point
369     Map<label>& faceToEdge,
370     Map<label>& faceToPoint
373     label faceI = startFaceI;
374     label edgeI = startEdgeI;
375     label pointI = startPointI;
377     do
378     {
379         //
380         // Stick to pointI and walk face-edge-face until back on border edge.
381         //
383         do
384         {
385             // Cross face to next edge.
386             edgeI = otherEdge(surf, faceI, edgeI, pointI);
388             if (borderEdge[edgeI])
389             {
390                 if (!faceToEdge.insert(faceI, edgeI))
391                 {
392                     // Was already visited.
393                     return;
394                 }
395                 else
396                 {
397                     // First visit to this borderEdge. We're back on borderline.
398                     break;
399                 }
400             }
401             else if (!faceToPoint.insert(faceI, pointI))
402             {
403                 // Was already visited.
404                 return;
405             }
407             // Cross edge to other face
408             const labelList& eFaces = surf.edgeFaces()[edgeI];
410             if (eFaces.size() != 2)
411             {
412                 FatalErrorIn("walkSplitPoint") << abort(FatalError);
413             }
415             if (eFaces[0] == faceI)
416             {
417                 faceI = eFaces[1];
418             }
419             else if (eFaces[1] == faceI)
420             {
421                 faceI = eFaces[0];
422             }
423             else
424             {
425                 FatalErrorIn("walkSplitPoint") << abort(FatalError);
426             }
427         }
428         while (true);
431         //
432         // Back on border edge. Cross to other point on edge.
433         //
435         pointI = surf.edges()[edgeI].otherVertex(pointI);
437         if (borderPoint[pointI] == -1)
438         {
439             return;
440         }
442     }
443     while (true);
447 // Find second face which is from same surface i.e. has points on the
448 // shared edge in reverse order.
449 label sharedFace
451     const triSurface& surf,
452     const label firstFaceI,
453     const label sharedEdgeI
456     // Find ordering of face points in edge.
458     const edge& e = surf.edges()[sharedEdgeI];
460     const labelledTri& f = surf.localFaces()[firstFaceI];
462     label startIndex = findIndex(f, e.start());
464     bool edgeOrder;
466     if (f[(startIndex + 1) % f.size()] == e.end())
467     {
468         // points in face in same order as edge
469         edgeOrder = true;
470     }
471     else
472     {
473         // points in face in reverse order as edge
474         edgeOrder = false;
475     }
477     // Get faces using edge in sorted order. (sorted such that walking
478     // around them in anti-clockwise order corresponds to edge vector
479     // acc. to right-hand rule)
480     const labelList& eFaces = surf.sortedEdgeFaces()[sharedEdgeI];
482     // Get position of face in sorted edge faces
483     label faceIndex = findIndex(eFaces, firstFaceI);
485     if (edgeOrder)
486     {
487         // Get face before firstFaceI
488         if (faceIndex == 0)
489         {
490             return eFaces[eFaces.size() - 1];
491         }
492         else
493         {
494             return eFaces[faceIndex - 1];
495         }
496     }
497     else
498     {
499         // Get face after firstFaceI
500         return eFaces[(faceIndex+1) % eFaces.size()];
501     }
505 // Calculate (inward pointing) normals on edges shared by faces in faceToEdge and
506 // averages them to pointNormals. 
507 void calcPointVecs
509     const triSurface& surf,
510     const Map<label>& faceToEdge,
511     const Map<label>& faceToPoint,
512     vectorField& borderPointVec
515     const labelListList& sortedEdgeFaces = surf.sortedEdgeFaces();
516     const edgeList& edges = surf.edges();
517     const pointField& points = surf.localPoints();
519     boolList edgeDone(surf.nEdges(), false);
521     for
522     (
523         Map<label>::const_iterator iter = faceToEdge.begin();
524         iter != faceToEdge.end();
525         ++iter
526     )
527     {
528         label edgeI = iter();
530         if (!edgeDone[edgeI])
531         {
532             edgeDone[edgeI] = true;
534             // Get the two connected faces in sorted order
535             // Note: should have stored this when walking ...
537             label face0I = -1;
538             label face1I = -1;
540             const labelList& eFaces = sortedEdgeFaces[edgeI];
542             forAll(eFaces, i)
543             {
544                 label faceI = eFaces[i];
546                 if (faceToEdge.found(faceI))
547                 {
548                     if (face0I == -1)
549                     {
550                         face0I = faceI;
551                     }
552                     else if (face1I == -1)
553                     {
554                         face1I = faceI;
556                         break;
557                     }
558                 }
559             }
561             if (face0I == -1 && face1I == -1)
562             {
563                 Info<< "Writing surface to errorSurf.ftr" << endl;
565                 surf.write("errorSurf.ftr");
567                 FatalErrorIn("calcPointVecs")
568                     << "Cannot find two faces using border edge " << edgeI
569                     << " verts:" << edges[edgeI]
570                     << " eFaces:" << eFaces << endl
571                     << "face0I:" << face0I
572                     << " face1I:" << face1I << nl
573                     << "faceToEdge:" << faceToEdge << nl
574                     << "faceToPoint:" << faceToPoint
575                     << "Written surface to errorSurf.ftr"
576                     << abort(FatalError);
577             }
579             // Now we have edge and the two faces in counter-clockwise order
580             // as seen from edge vector. Calculate normal.
581             const edge& e = edges[edgeI];
582             vector eVec = e.vec(points);
584             // Determine vector as average of the vectors in the two faces.
585             // If there is only one face available use only one vector.
586             vector midVec(vector::zero);
588             if (face0I != -1)
589             {
590                 label v0 = triSurfaceTools::oppositeVertex(surf, face0I, edgeI);
591                 vector e0 = (points[v0] - points[e.start()]) ^ eVec;
592                 e0 /= mag(e0);
593                 midVec = e0;
594             }
596             if (face1I != -1)
597             {
598                 label v1 = triSurfaceTools::oppositeVertex(surf, face1I, edgeI);
599                 vector e1 = (points[e.start()] - points[v1]) ^ eVec;
600                 e1 /= mag(e1);
601                 midVec += e1;
602             }
604             scalar magMidVec = mag(midVec);
605     
606             if (magMidVec > SMALL)
607             {
608                 midVec /= magMidVec;
610                 // Average to pointVec
611                 borderPointVec[e.start()] += midVec;
612                 borderPointVec[e.end()] += midVec;
613             }
614         }
615     }
619 // Renumbers vertices (of triangles in faceToEdge) of which the pointMap is
620 // not -1.
621 void renumberFaces
623     const triSurface& surf,
624     const labelList& pointMap,
625     const Map<label>& faceToEdge,
626     List<labelledTri>& newTris
629     for
630     (
631         Map<label>::const_iterator iter = faceToEdge.begin();
632         iter != faceToEdge.end();
633         ++iter
634     )
635     {
636         label faceI = iter.key();
638         const labelledTri& f = surf.localFaces()[faceI];
640         forAll(f, fp)
641         {
642             if (pointMap[f[fp]] != -1)
643             {
644                 newTris[faceI][fp] = pointMap[f[fp]];
645             }
646         }
647     }
651 // Split all borderEdges that don't have borderPoint. Return true if split
652 // anything.
653 bool splitBorderEdges
655     triSurface& surf,
656     const boolList& borderEdge,
657     const labelList& borderPoint
660     labelList edgesToBeSplit(surf.nEdges());
661     label nSplit = 0;
663     forAll(borderEdge, edgeI)
664     {
665         if (borderEdge[edgeI])
666         {
667             const edge& e = surf.edges()[edgeI];
669             if (borderPoint[e.start()] == -1 && borderPoint[e.end()] == -1)
670             {
671                 // None of the points of the edge is borderPoint. Split edge
672                 // to introduce border point.
673                 edgesToBeSplit[nSplit++] = edgeI;
674             }
675         }
676     }
677     edgesToBeSplit.setSize(nSplit);
679     if (nSplit > 0)
680     {
681         Info<< "Splitting surface along " << nSplit << " borderEdges that don't"
682             << " neighbour other borderEdges" << nl << endl;
684         surf = triSurfaceTools::greenRefine(surf, edgesToBeSplit);
686         return true;
687     }
688     else
689     {
690         Info<< "No edges to be split" <<nl << endl;
692         return false;
693     }
697 // Main program:
699 int main(int argc, char *argv[])
701     argList::noParallel();
702     argList::validArgs.clear();
704     argList::validArgs.append("surface file");
705     argList::validArgs.append("output surface file");
706     argList::validOptions.insert("debug", "");
708     argList args(argc, argv);
710     fileName inSurfName(args.additionalArgs()[0]);
711     fileName outSurfName(args.additionalArgs()[1]);
712     bool debug = args.options().found("debug");
715     Info<< "Reading surface from " << inSurfName << endl;
717     triSurface surf(inSurfName);
719     // Make sure sortedEdgeFaces is calculated correctly
720     testSortedEdgeFaces(surf);
722     // Get all quad connected edges. These are seen as borders when walking.
723     boolList borderEdge(surf.nEdges(), false);
724     markBorderEdges(debug, surf, borderEdge);
726     // Points on two sides connected to borderEdges are called borderPoints and
727     // will be duplicated. borderPoint contains label of newly introduced vertex.
728     labelList borderPoint(surf.nPoints(), -1);
729     markBorderPoints(debug, surf, borderEdge, borderPoint);
731     // Split edges where there would be no borderPoint to duplicate.
732     splitBorderEdges(surf, borderEdge, borderPoint);
735     Info<< "Writing split surface to " << outSurfName << nl << endl;
736     surf.write(outSurfName);
737     Info<< "Finished writing surface to " << outSurfName << nl << endl;
740     // Last iteration values.
741     label nOldBorderEdges = -1;
742     label nOldBorderPoints = -1;
744     label iteration = 0;
746     do
747     {
748         // Redo borderEdge/borderPoint calculation.
749         boolList borderEdge(surf.nEdges(), false);
751         label nBorderEdges = markBorderEdges(debug, surf, borderEdge);
753         if (nBorderEdges == 0)
754         {
755             Info<< "Found no border edges. Exiting." << nl << nl;
757             break;
758         }
760         // Label of newly introduced duplicate.
761         labelList borderPoint(surf.nPoints(), -1);
763         label nBorderPoints =
764             markBorderPoints
765             (
766                 debug,
767                 surf,
768                 borderEdge,
769                 borderPoint
770             );
772         if (nBorderPoints == 0)
773         {
774             Info<< "Found no border points. Exiting." << nl << nl;
776             break;
777         }
779         Info<< "Found:\n"
780             << "    border edges :" << nBorderEdges << nl
781             << "    border points:" << nBorderPoints << nl
782             << endl;
784         if
785         (
786             nBorderPoints == nOldBorderPoints
787          && nBorderEdges == nOldBorderEdges
788         )
789         {
790             Info<< "Stopping since number of border edges and point is same"
791                 << " as in previous iteration" << nl << endl;
793             break;
794         }
796         //
797         // Define splitLine as a series of connected borderEdges. Find start
798         // of one (as edge and point on edge)
799         //
801         label startEdgeI = -1;
802         label startPointI = -1;
804         forAll(borderEdge, edgeI)
805         {
806             if (borderEdge[edgeI])
807             {
808                 const edge& e = surf.edges()[edgeI];
810                 if ((borderPoint[e[0]] != -1) && (borderPoint[e[1]] == -1))
811                 {
812                     startEdgeI = edgeI;
813                     startPointI = e[0];
815                     break;
816                 }
817                 else if ((borderPoint[e[0]] == -1) && (borderPoint[e[1]] != -1))
818                 {
819                     startEdgeI = edgeI;
820                     startPointI = e[1];
822                     break;
823                 }
824             }
825         }
827         if (startEdgeI == -1)
828         {
829             Info<< "Cannot find starting point of splitLine\n" << endl;
831             break;
832         }
834         // Pick any face using edge to start from.
835         const labelList& eFaces = surf.edgeFaces()[startEdgeI];
837         label firstFaceI = eFaces[0];
839         // Find second face which is from same surface i.e. has outwards
840         // pointing normal as well (actually bit more complex than this)
841         label secondFaceI = sharedFace(surf, firstFaceI, startEdgeI);
843         Info<< "Starting local walk from:" << endl
844             << "    edge :" << startEdgeI << endl
845             << "    point:" << startPointI << endl
846             << "    face0:" << firstFaceI << endl
847             << "    face1:" << secondFaceI << endl
848             << endl;
850         // From face on border edge to edge.
851         Map<label> faceToEdge(2*nBorderEdges);
852         // From face connected to border point (but not border edge) to point.
853         Map<label> faceToPoint(2*nBorderPoints);
855         faceToEdge.insert(firstFaceI, startEdgeI);
857         walkSplitLine
858         (
859             surf,
860             borderEdge,
861             borderPoint,
863             firstFaceI,
864             startEdgeI,
865             startPointI,
867             faceToEdge,
868             faceToPoint
869         );
871         faceToEdge.insert(secondFaceI, startEdgeI);
873         walkSplitLine
874         (
875             surf,
876             borderEdge,
877             borderPoint,
879             secondFaceI,
880             startEdgeI,
881             startPointI,
883             faceToEdge,
884             faceToPoint
885         );
887         Info<< "Finished local walk and visited" << nl
888             << "    border edges                :" << faceToEdge.size() << nl
889             << "    border points(but not edges):" << faceToPoint.size() << nl
890             << endl;
892         if (debug)
893         {
894             dumpFaces("faceToEdge.obj", surf, faceToEdge);
895             dumpFaces("faceToPoint.obj", surf, faceToPoint);
896         }
898         //
899         // Create coordinates for borderPoints by duplicating the existing
900         // point and then slightly shifting it inwards. To determine the
901         // inwards direction get the average normal of both connectedFaces on
902         // the edge and then interpolate this to the (border)point.
903         //
905         vectorField borderPointVec(surf.nPoints(), vector(GREAT, GREAT, GREAT));
907         calcPointVecs(surf, faceToEdge, faceToPoint, borderPointVec);
910         // New position. Start off from copy of old points.
911         pointField newPoints(surf.localPoints());
912         newPoints.setSize(newPoints.size() + nBorderPoints);
914         forAll(borderPoint, pointI)
915         {
916             label newPointI = borderPoint[pointI];
918             if (newPointI != -1)
919             {
920                 scalar minLen = minEdgeLen(surf, pointI);
922                 vector n = borderPointVec[pointI];
923                 n /= mag(n);
925                 newPoints[newPointI] = newPoints[pointI] + 0.1 * minLen * n;
926             }
927         }
928         
930         //
931         // Renumber all faces in connectedFaces
932         //
934         // Start off from copy of faces.
935         List<labelledTri> newTris(surf.size());
937         forAll(surf, faceI)
938         {
939             newTris[faceI] = surf.localFaces()[faceI];
941             newTris[faceI].region() = surf[faceI].region();
942         }
944         // Renumber all faces in faceToEdge
945         renumberFaces(surf, borderPoint, faceToEdge, newTris);
946         // Renumber all faces in faceToPoint
947         renumberFaces(surf, borderPoint, faceToPoint, newTris);
950         // Check if faces use unmoved points.
951         forAll(newTris, faceI)
952         {
953             const labelledTri& f = newTris[faceI];
955             forAll(f, fp)
956             {
957                 const point& pt = newPoints[f[fp]];
959                 if (mag(pt) >= GREAT/2)
960                 {
961                     Info<< "newTri:" << faceI << " verts:" << f
962                         << " vert:" << f[fp] << " point:" << pt << endl;
963                 }
964             }
965         }
968         surf = triSurface(newTris, surf.patches(), newPoints);
970         if (debug || (iteration != 0 && (iteration % 20) == 0))
971         {
972             Info<< "Writing surface to " << outSurfName << nl << endl;
974             surf.write(outSurfName);
976             Info<< "Finished writing surface to " << outSurfName << nl << endl;
977         }
979         // Save prev iteration values.
980         nOldBorderEdges = nBorderEdges;
981         nOldBorderPoints = nBorderPoints;
983         iteration++;
984     }
985     while (true);
987     Info<< "Writing final surface to " << outSurfName << nl << endl;
989     surf.write(outSurfName);
991     Info << "End\n" << endl;
993     return 0;
997 // ************************************************************************* //