1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2008 OpenCFD Ltd.
7 -------------------------------------------------------------------------------
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
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
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
40 - determining the outwards pointing normal to be used to slightly offset the
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 \*---------------------------------------------------------------------------*/
54 #include "triSurface.H"
57 #include "triSurfaceTools.H"
61 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
63 void writeOBJ(Ostream& os, const pointField& pts)
67 const point& pt = pts[i];
69 os << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z() << endl;
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";
83 forAll(borderPoint, pointI)
85 if (borderPoint[pointI] != -1)
87 const point& pt = surf.localPoints()[pointI];
89 os << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z() << endl;
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";
104 writeOBJ(os, surf.localPoints());
106 forAll(borderEdge, edgeI)
108 if (borderEdge[edgeI])
110 const edge& e = surf.edges()[edgeI];
112 os << "l " << e.start()+1 << ' ' << e.end()+1 << endl;
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";
132 Map<label>::const_iterator iter = connectedFaces.begin();
133 iter != connectedFaces.end();
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;
146 void testSortedEdgeFaces(const triSurface& surf)
148 const labelListList& edgeFaces = surf.edgeFaces();
149 const labelListList& sortedEdgeFaces = surf.sortedEdgeFaces();
151 forAll(edgeFaces, edgeI)
153 const labelList& myFaces = edgeFaces[edgeI];
154 const labelList& sortMyFaces = sortedEdgeFaces[edgeI];
158 if (findIndex(sortMyFaces, myFaces[i]) == -1)
160 FatalErrorIn("testSortedEdgeFaces") << abort(FatalError);
163 forAll(sortMyFaces, i)
165 if (findIndex(myFaces, sortMyFaces[i]) == -1)
167 FatalErrorIn("testSortedEdgeFaces") << abort(FatalError);
174 // Mark off all border edges. Return number of border edges.
175 label markBorderEdges
178 const triSurface& surf,
182 label nBorderEdges = 0;
184 const labelListList& edgeFaces = surf.edgeFaces();
186 forAll(edgeFaces, edgeI)
188 if (edgeFaces[edgeI].size() == 4)
190 borderEdge[edgeI] = true;
198 dumpEdges(surf, borderEdge);
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
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)
221 const labelList& pEdges = pointEdges[pointI];
223 label nBorderEdges = 0;
227 if (borderEdge[pEdges[i]])
233 if (nBorderEdges == 2 && borderPoint[pointI] == -1)
235 borderPoint[pointI] = nPoints++;
239 label nBorderPoints = nPoints - surf.nPoints();
243 dumpPoints(surf, borderPoint);
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;
262 label edgeI = pEdges[i];
264 scalar len = surf.edges()[edgeI].mag(points);
275 // Find edge among edgeLabels with endpoints v0,v1
278 const triSurface& surf,
279 const labelList& edgeLabels,
284 forAll(edgeLabels, i)
286 label edgeI = edgeLabels[i];
288 const edge& e = surf.edges()[edgeI];
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);
316 // Get the other edge connected to pointI on faceI.
319 const triSurface& surf,
321 const label otherEdgeI,
325 const labelList& fEdges = surf.faceEdges()[faceI];
329 label edgeI = fEdges[i];
331 const edge& e = surf.edges()[edgeI];
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);
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.
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;
380 // Stick to pointI and walk face-edge-face until back on border edge.
385 // Cross face to next edge.
386 edgeI = otherEdge(surf, faceI, edgeI, pointI);
388 if (borderEdge[edgeI])
390 if (!faceToEdge.insert(faceI, edgeI))
392 // Was already visited.
397 // First visit to this borderEdge. We're back on borderline.
401 else if (!faceToPoint.insert(faceI, pointI))
403 // Was already visited.
407 // Cross edge to other face
408 const labelList& eFaces = surf.edgeFaces()[edgeI];
410 if (eFaces.size() != 2)
412 FatalErrorIn("walkSplitPoint") << abort(FatalError);
415 if (eFaces[0] == faceI)
419 else if (eFaces[1] == faceI)
425 FatalErrorIn("walkSplitPoint") << abort(FatalError);
432 // Back on border edge. Cross to other point on edge.
435 pointI = surf.edges()[edgeI].otherVertex(pointI);
437 if (borderPoint[pointI] == -1)
447 // Find second face which is from same surface i.e. has points on the
448 // shared edge in reverse order.
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());
466 if (f[(startIndex + 1) % f.size()] == e.end())
468 // points in face in same order as edge
473 // points in face in reverse order as edge
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);
487 // Get face before firstFaceI
490 return eFaces[eFaces.size() - 1];
494 return eFaces[faceIndex - 1];
499 // Get face after firstFaceI
500 return eFaces[(faceIndex+1) % eFaces.size()];
505 // Calculate (inward pointing) normals on edges shared by faces in faceToEdge and
506 // averages them to pointNormals.
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);
523 Map<label>::const_iterator iter = faceToEdge.begin();
524 iter != faceToEdge.end();
528 label edgeI = iter();
530 if (!edgeDone[edgeI])
532 edgeDone[edgeI] = true;
534 // Get the two connected faces in sorted order
535 // Note: should have stored this when walking ...
540 const labelList& eFaces = sortedEdgeFaces[edgeI];
544 label faceI = eFaces[i];
546 if (faceToEdge.found(faceI))
552 else if (face1I == -1)
561 if (face0I == -1 && face1I == -1)
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);
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);
590 label v0 = triSurfaceTools::oppositeVertex(surf, face0I, edgeI);
591 vector e0 = (points[v0] - points[e.start()]) ^ eVec;
598 label v1 = triSurfaceTools::oppositeVertex(surf, face1I, edgeI);
599 vector e1 = (points[e.start()] - points[v1]) ^ eVec;
604 scalar magMidVec = mag(midVec);
606 if (magMidVec > SMALL)
610 // Average to pointVec
611 borderPointVec[e.start()] += midVec;
612 borderPointVec[e.end()] += midVec;
619 // Renumbers vertices (of triangles in faceToEdge) of which the pointMap is
623 const triSurface& surf,
624 const labelList& pointMap,
625 const Map<label>& faceToEdge,
626 List<labelledTri>& newTris
631 Map<label>::const_iterator iter = faceToEdge.begin();
632 iter != faceToEdge.end();
636 label faceI = iter.key();
638 const labelledTri& f = surf.localFaces()[faceI];
642 if (pointMap[f[fp]] != -1)
644 newTris[faceI][fp] = pointMap[f[fp]];
651 // Split all borderEdges that don't have borderPoint. Return true if split
653 bool splitBorderEdges
656 const boolList& borderEdge,
657 const labelList& borderPoint
660 labelList edgesToBeSplit(surf.nEdges());
663 forAll(borderEdge, edgeI)
665 if (borderEdge[edgeI])
667 const edge& e = surf.edges()[edgeI];
669 if (borderPoint[e.start()] == -1 && borderPoint[e.end()] == -1)
671 // None of the points of the edge is borderPoint. Split edge
672 // to introduce border point.
673 edgesToBeSplit[nSplit++] = edgeI;
677 edgesToBeSplit.setSize(nSplit);
681 Info<< "Splitting surface along " << nSplit << " borderEdges that don't"
682 << " neighbour other borderEdges" << nl << endl;
684 surf = triSurfaceTools::greenRefine(surf, edgesToBeSplit);
690 Info<< "No edges to be split" <<nl << endl;
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;
748 // Redo borderEdge/borderPoint calculation.
749 boolList borderEdge(surf.nEdges(), false);
751 label nBorderEdges = markBorderEdges(debug, surf, borderEdge);
753 if (nBorderEdges == 0)
755 Info<< "Found no border edges. Exiting." << nl << nl;
760 // Label of newly introduced duplicate.
761 labelList borderPoint(surf.nPoints(), -1);
763 label nBorderPoints =
772 if (nBorderPoints == 0)
774 Info<< "Found no border points. Exiting." << nl << nl;
780 << " border edges :" << nBorderEdges << nl
781 << " border points:" << nBorderPoints << nl
786 nBorderPoints == nOldBorderPoints
787 && nBorderEdges == nOldBorderEdges
790 Info<< "Stopping since number of border edges and point is same"
791 << " as in previous iteration" << nl << endl;
797 // Define splitLine as a series of connected borderEdges. Find start
798 // of one (as edge and point on edge)
801 label startEdgeI = -1;
802 label startPointI = -1;
804 forAll(borderEdge, edgeI)
806 if (borderEdge[edgeI])
808 const edge& e = surf.edges()[edgeI];
810 if ((borderPoint[e[0]] != -1) && (borderPoint[e[1]] == -1))
817 else if ((borderPoint[e[0]] == -1) && (borderPoint[e[1]] != -1))
827 if (startEdgeI == -1)
829 Info<< "Cannot find starting point of splitLine\n" << endl;
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
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);
871 faceToEdge.insert(secondFaceI, startEdgeI);
887 Info<< "Finished local walk and visited" << nl
888 << " border edges :" << faceToEdge.size() << nl
889 << " border points(but not edges):" << faceToPoint.size() << nl
894 dumpFaces("faceToEdge.obj", surf, faceToEdge);
895 dumpFaces("faceToPoint.obj", surf, faceToPoint);
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.
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)
916 label newPointI = borderPoint[pointI];
920 scalar minLen = minEdgeLen(surf, pointI);
922 vector n = borderPointVec[pointI];
925 newPoints[newPointI] = newPoints[pointI] + 0.1 * minLen * n;
931 // Renumber all faces in connectedFaces
934 // Start off from copy of faces.
935 List<labelledTri> newTris(surf.size());
939 newTris[faceI] = surf.localFaces()[faceI];
941 newTris[faceI].region() = surf[faceI].region();
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)
953 const labelledTri& f = newTris[faceI];
957 const point& pt = newPoints[f[fp]];
959 if (mag(pt) >= GREAT/2)
961 Info<< "newTri:" << faceI << " verts:" << f
962 << " vert:" << f[fp] << " point:" << pt << endl;
968 surf = triSurface(newTris, surf.patches(), newPoints);
970 if (debug || (iteration != 0 && (iteration % 20) == 0))
972 Info<< "Writing surface to " << outSurfName << nl << endl;
974 surf.write(outSurfName);
976 Info<< "Finished writing surface to " << outSurfName << nl << endl;
979 // Save prev iteration values.
980 nOldBorderEdges = nBorderEdges;
981 nOldBorderPoints = nBorderPoints;
987 Info<< "Writing final surface to " << outSurfName << nl << endl;
989 surf.write(outSurfName);
991 Info << "End\n" << endl;
997 // ************************************************************************* //