1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2009 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:" << UIndirectList<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:" << UIndirectList<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());
464 // points in face in same order as edge
465 bool edgeOrder = (f[f.fcIndex(startIndex)] == e.end());
467 // Get faces using edge in sorted order. (sorted such that walking
468 // around them in anti-clockwise order corresponds to edge vector
469 // acc. to right-hand rule)
470 const labelList& eFaces = surf.sortedEdgeFaces()[sharedEdgeI];
472 // Get position of face in sorted edge faces
473 label faceIndex = findIndex(eFaces, firstFaceI);
477 // Get face before firstFaceI
478 return eFaces[eFaces.rcIndex(faceIndex)];
482 // Get face after firstFaceI
483 return eFaces[eFaces.fcIndex(faceIndex)];
488 // Calculate (inward pointing) normals on edges shared by faces in faceToEdge and
489 // averages them to pointNormals.
492 const triSurface& surf,
493 const Map<label>& faceToEdge,
494 const Map<label>& faceToPoint,
495 vectorField& borderPointVec
498 const labelListList& sortedEdgeFaces = surf.sortedEdgeFaces();
499 const edgeList& edges = surf.edges();
500 const pointField& points = surf.localPoints();
502 boolList edgeDone(surf.nEdges(), false);
506 Map<label>::const_iterator iter = faceToEdge.begin();
507 iter != faceToEdge.end();
511 label edgeI = iter();
513 if (!edgeDone[edgeI])
515 edgeDone[edgeI] = true;
517 // Get the two connected faces in sorted order
518 // Note: should have stored this when walking ...
523 const labelList& eFaces = sortedEdgeFaces[edgeI];
527 label faceI = eFaces[i];
529 if (faceToEdge.found(faceI))
535 else if (face1I == -1)
544 if (face0I == -1 && face1I == -1)
546 Info<< "Writing surface to errorSurf.ftr" << endl;
548 surf.write("errorSurf.ftr");
550 FatalErrorIn("calcPointVecs")
551 << "Cannot find two faces using border edge " << edgeI
552 << " verts:" << edges[edgeI]
553 << " eFaces:" << eFaces << endl
554 << "face0I:" << face0I
555 << " face1I:" << face1I << nl
556 << "faceToEdge:" << faceToEdge << nl
557 << "faceToPoint:" << faceToPoint
558 << "Written surface to errorSurf.ftr"
559 << abort(FatalError);
562 // Now we have edge and the two faces in counter-clockwise order
563 // as seen from edge vector. Calculate normal.
564 const edge& e = edges[edgeI];
565 vector eVec = e.vec(points);
567 // Determine vector as average of the vectors in the two faces.
568 // If there is only one face available use only one vector.
569 vector midVec(vector::zero);
573 label v0 = triSurfaceTools::oppositeVertex(surf, face0I, edgeI);
574 vector e0 = (points[v0] - points[e.start()]) ^ eVec;
581 label v1 = triSurfaceTools::oppositeVertex(surf, face1I, edgeI);
582 vector e1 = (points[e.start()] - points[v1]) ^ eVec;
587 scalar magMidVec = mag(midVec);
589 if (magMidVec > SMALL)
593 // Average to pointVec
594 borderPointVec[e.start()] += midVec;
595 borderPointVec[e.end()] += midVec;
602 // Renumbers vertices (of triangles in faceToEdge) of which the pointMap is
606 const triSurface& surf,
607 const labelList& pointMap,
608 const Map<label>& faceToEdge,
609 List<labelledTri>& newTris
614 Map<label>::const_iterator iter = faceToEdge.begin();
615 iter != faceToEdge.end();
619 label faceI = iter.key();
621 const labelledTri& f = surf.localFaces()[faceI];
625 if (pointMap[f[fp]] != -1)
627 newTris[faceI][fp] = pointMap[f[fp]];
634 // Split all borderEdges that don't have borderPoint. Return true if split
636 bool splitBorderEdges
639 const boolList& borderEdge,
640 const labelList& borderPoint
643 labelList edgesToBeSplit(surf.nEdges());
646 forAll(borderEdge, edgeI)
648 if (borderEdge[edgeI])
650 const edge& e = surf.edges()[edgeI];
652 if (borderPoint[e.start()] == -1 && borderPoint[e.end()] == -1)
654 // None of the points of the edge is borderPoint. Split edge
655 // to introduce border point.
656 edgesToBeSplit[nSplit++] = edgeI;
660 edgesToBeSplit.setSize(nSplit);
664 Info<< "Splitting surface along " << nSplit << " borderEdges that don't"
665 << " neighbour other borderEdges" << nl << endl;
667 surf = triSurfaceTools::greenRefine(surf, edgesToBeSplit);
673 Info<< "No edges to be split" <<nl << endl;
682 int main(int argc, char *argv[])
684 argList::noParallel();
685 argList::validArgs.clear();
687 argList::validArgs.append("surface file");
688 argList::validArgs.append("output surface file");
689 argList::validOptions.insert("debug", "");
691 argList args(argc, argv);
693 fileName inSurfName(args.additionalArgs()[0]);
694 fileName outSurfName(args.additionalArgs()[1]);
695 bool debug = args.optionFound("debug");
698 Info<< "Reading surface from " << inSurfName << endl;
700 triSurface surf(inSurfName);
702 // Make sure sortedEdgeFaces is calculated correctly
703 testSortedEdgeFaces(surf);
705 // Get all quad connected edges. These are seen as borders when walking.
706 boolList borderEdge(surf.nEdges(), false);
707 markBorderEdges(debug, surf, borderEdge);
709 // Points on two sides connected to borderEdges are called borderPoints and
710 // will be duplicated. borderPoint contains label of newly introduced vertex.
711 labelList borderPoint(surf.nPoints(), -1);
712 markBorderPoints(debug, surf, borderEdge, borderPoint);
714 // Split edges where there would be no borderPoint to duplicate.
715 splitBorderEdges(surf, borderEdge, borderPoint);
718 Info<< "Writing split surface to " << outSurfName << nl << endl;
719 surf.write(outSurfName);
720 Info<< "Finished writing surface to " << outSurfName << nl << endl;
723 // Last iteration values.
724 label nOldBorderEdges = -1;
725 label nOldBorderPoints = -1;
731 // Redo borderEdge/borderPoint calculation.
732 boolList borderEdge(surf.nEdges(), false);
734 label nBorderEdges = markBorderEdges(debug, surf, borderEdge);
736 if (nBorderEdges == 0)
738 Info<< "Found no border edges. Exiting." << nl << nl;
743 // Label of newly introduced duplicate.
744 labelList borderPoint(surf.nPoints(), -1);
746 label nBorderPoints =
755 if (nBorderPoints == 0)
757 Info<< "Found no border points. Exiting." << nl << nl;
763 << " border edges :" << nBorderEdges << nl
764 << " border points:" << nBorderPoints << nl
769 nBorderPoints == nOldBorderPoints
770 && nBorderEdges == nOldBorderEdges
773 Info<< "Stopping since number of border edges and point is same"
774 << " as in previous iteration" << nl << endl;
780 // Define splitLine as a series of connected borderEdges. Find start
781 // of one (as edge and point on edge)
784 label startEdgeI = -1;
785 label startPointI = -1;
787 forAll(borderEdge, edgeI)
789 if (borderEdge[edgeI])
791 const edge& e = surf.edges()[edgeI];
793 if ((borderPoint[e[0]] != -1) && (borderPoint[e[1]] == -1))
800 else if ((borderPoint[e[0]] == -1) && (borderPoint[e[1]] != -1))
810 if (startEdgeI == -1)
812 Info<< "Cannot find starting point of splitLine\n" << endl;
817 // Pick any face using edge to start from.
818 const labelList& eFaces = surf.edgeFaces()[startEdgeI];
820 label firstFaceI = eFaces[0];
822 // Find second face which is from same surface i.e. has outwards
823 // pointing normal as well (actually bit more complex than this)
824 label secondFaceI = sharedFace(surf, firstFaceI, startEdgeI);
826 Info<< "Starting local walk from:" << endl
827 << " edge :" << startEdgeI << endl
828 << " point:" << startPointI << endl
829 << " face0:" << firstFaceI << endl
830 << " face1:" << secondFaceI << endl
833 // From face on border edge to edge.
834 Map<label> faceToEdge(2*nBorderEdges);
835 // From face connected to border point (but not border edge) to point.
836 Map<label> faceToPoint(2*nBorderPoints);
838 faceToEdge.insert(firstFaceI, startEdgeI);
854 faceToEdge.insert(secondFaceI, startEdgeI);
870 Info<< "Finished local walk and visited" << nl
871 << " border edges :" << faceToEdge.size() << nl
872 << " border points(but not edges):" << faceToPoint.size() << nl
877 dumpFaces("faceToEdge.obj", surf, faceToEdge);
878 dumpFaces("faceToPoint.obj", surf, faceToPoint);
882 // Create coordinates for borderPoints by duplicating the existing
883 // point and then slightly shifting it inwards. To determine the
884 // inwards direction get the average normal of both connectedFaces on
885 // the edge and then interpolate this to the (border)point.
888 vectorField borderPointVec(surf.nPoints(), vector(GREAT, GREAT, GREAT));
890 calcPointVecs(surf, faceToEdge, faceToPoint, borderPointVec);
893 // New position. Start off from copy of old points.
894 pointField newPoints(surf.localPoints());
895 newPoints.setSize(newPoints.size() + nBorderPoints);
897 forAll(borderPoint, pointI)
899 label newPointI = borderPoint[pointI];
903 scalar minLen = minEdgeLen(surf, pointI);
905 vector n = borderPointVec[pointI];
908 newPoints[newPointI] = newPoints[pointI] + 0.1 * minLen * n;
914 // Renumber all faces in connectedFaces
917 // Start off from copy of faces.
918 List<labelledTri> newTris(surf.size());
922 newTris[faceI] = surf.localFaces()[faceI];
924 newTris[faceI].region() = surf[faceI].region();
927 // Renumber all faces in faceToEdge
928 renumberFaces(surf, borderPoint, faceToEdge, newTris);
929 // Renumber all faces in faceToPoint
930 renumberFaces(surf, borderPoint, faceToPoint, newTris);
933 // Check if faces use unmoved points.
934 forAll(newTris, faceI)
936 const labelledTri& f = newTris[faceI];
940 const point& pt = newPoints[f[fp]];
942 if (mag(pt) >= GREAT/2)
944 Info<< "newTri:" << faceI << " verts:" << f
945 << " vert:" << f[fp] << " point:" << pt << endl;
951 surf = triSurface(newTris, surf.patches(), newPoints);
953 if (debug || (iteration != 0 && (iteration % 20) == 0))
955 Info<< "Writing surface to " << outSurfName << nl << endl;
957 surf.write(outSurfName);
959 Info<< "Finished writing surface to " << outSurfName << nl << endl;
962 // Save prev iteration values.
963 nOldBorderEdges = nBorderEdges;
964 nOldBorderPoints = nBorderPoints;
970 Info<< "Writing final surface to " << outSurfName << nl << endl;
972 surf.write(outSurfName);
974 Info << "End\n" << endl;
980 // ************************************************************************* //