1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
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
13 the Free Software Foundation, either version 3 of the License, or
14 (at your 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, see <http://www.gnu.org/licenses/>.
25 Takes multiply connected surface and tries to split surface at
26 multiply connected edges by duplicating points. Introduces concept of
27 - borderEdge. Edge with 4 faces connected to it.
28 - borderPoint. Point connected to exactly 2 borderEdges.
29 - borderLine. Connected list of borderEdges.
31 By duplicating borderPoints this will split 'borderLines'. As a
32 preprocessing step it can detect borderEdges without any borderPoints
33 and explicitly split these triangles.
35 The problems in this algorithm are:
36 - determining which two (of the four) faces form a surface. Done by walking
37 face-edge-face while keeping and edge or point on the borderEdge
39 - determining the outwards pointing normal to be used to slightly offset the
42 Uses sortedEdgeFaces quite a bit.
44 Is tested on simple borderLines resulting from extracting a surface
45 from a hex mesh. Will quite possibly go wrong on more complicated border
46 lines (i.e. ones forming a loop).
48 Dumps surface every so often since might take a long time to complete.
50 \*---------------------------------------------------------------------------*/
53 #include "triSurface.H"
56 #include "triSurfaceTools.H"
60 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
62 void writeOBJ(Ostream& os, const pointField& pts)
66 const point& pt = pts[i];
68 os << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z() << endl;
73 void dumpPoints(const triSurface& surf, const labelList& borderPoint)
75 fileName fName("borderPoints.obj");
77 Info<< "Dumping borderPoints as Lightwave .obj file to " << fName
78 << "\nThis can be visualized with e.g. javaview (www.javaview.de)\n\n";
82 forAll(borderPoint, pointI)
84 if (borderPoint[pointI] != -1)
86 const point& pt = surf.localPoints()[pointI];
88 os << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z() << endl;
94 void dumpEdges(const triSurface& surf, const boolList& borderEdge)
96 fileName fName("borderEdges.obj");
98 Info<< "Dumping borderEdges as Lightwave .obj file to " << fName
99 << "\nThis can be visualized with e.g. javaview (www.javaview.de)\n\n";
103 writeOBJ(os, surf.localPoints());
105 forAll(borderEdge, edgeI)
107 if (borderEdge[edgeI])
109 const edge& e = surf.edges()[edgeI];
111 os << "l " << e.start()+1 << ' ' << e.end()+1 << endl;
119 const fileName& fName,
120 const triSurface& surf,
121 const Map<label>& connectedFaces
124 Info<< "Dumping connectedFaces as Lightwave .obj file to " << fName
125 << "\nThis can be visualized with e.g. javaview (www.javaview.de)\n\n";
129 forAllConstIter(Map<label>, connectedFaces, iter)
131 point ctr = surf.localFaces()[iter.key()].centre(surf.localPoints());
133 os << "v " << ctr.x() << ' ' << ctr.y() << ' ' << ctr.z() << endl;
138 void testSortedEdgeFaces(const triSurface& surf)
140 const labelListList& edgeFaces = surf.edgeFaces();
141 const labelListList& sortedEdgeFaces = surf.sortedEdgeFaces();
143 forAll(edgeFaces, edgeI)
145 const labelList& myFaces = edgeFaces[edgeI];
146 const labelList& sortMyFaces = sortedEdgeFaces[edgeI];
150 if (findIndex(sortMyFaces, myFaces[i]) == -1)
152 FatalErrorIn("testSortedEdgeFaces(..)") << abort(FatalError);
155 forAll(sortMyFaces, i)
157 if (findIndex(myFaces, sortMyFaces[i]) == -1)
159 FatalErrorIn("testSortedEdgeFaces(..)") << abort(FatalError);
166 // Mark off all border edges. Return number of border edges.
167 label markBorderEdges
170 const triSurface& surf,
174 label nBorderEdges = 0;
176 const labelListList& edgeFaces = surf.edgeFaces();
178 forAll(edgeFaces, edgeI)
180 if (edgeFaces[edgeI].size() == 4)
182 borderEdge[edgeI] = true;
190 dumpEdges(surf, borderEdge);
197 // Mark off all border points. Return number of border points. Border points
198 // marked by setting value to newly introduced point.
199 label markBorderPoints
202 const triSurface& surf,
203 const boolList& borderEdge,
204 labelList& borderPoint
207 label nPoints = surf.nPoints();
209 const labelListList& pointEdges = surf.pointEdges();
211 forAll(pointEdges, pointI)
213 const labelList& pEdges = pointEdges[pointI];
215 label nBorderEdges = 0;
219 if (borderEdge[pEdges[i]])
225 if (nBorderEdges == 2 && borderPoint[pointI] == -1)
227 borderPoint[pointI] = nPoints++;
231 label nBorderPoints = nPoints - surf.nPoints();
235 dumpPoints(surf, borderPoint);
238 return nBorderPoints;
242 // Get minumum length of edges connected to pointI
243 // Serves to get some local length scale.
244 scalar minEdgeLen(const triSurface& surf, const label pointI)
246 const pointField& points = surf.localPoints();
248 const labelList& pEdges = surf.pointEdges()[pointI];
250 scalar minLen = GREAT;
254 label edgeI = pEdges[i];
256 scalar len = surf.edges()[edgeI].mag(points);
267 // Find edge among edgeLabels with endpoints v0,v1
270 const triSurface& surf,
271 const labelList& edgeLabels,
276 forAll(edgeLabels, i)
278 label edgeI = edgeLabels[i];
280 const edge& e = surf.edges()[edgeI];
299 FatalErrorIn("findEdge(..)") << "Cannot find edge with labels " << v0
300 << ' ' << v1 << " in candidates " << edgeLabels
301 << " with vertices:" << UIndirectList<edge>(surf.edges(), edgeLabels)()
302 << abort(FatalError);
308 // Get the other edge connected to pointI on faceI.
311 const triSurface& surf,
313 const label otherEdgeI,
317 const labelList& fEdges = surf.faceEdges()[faceI];
321 label edgeI = fEdges[i];
323 const edge& e = surf.edges()[edgeI];
338 FatalErrorIn("otherEdge(..)") << "Cannot find other edge on face " << faceI
339 << " verts:" << surf.localPoints()[faceI]
340 << " connected to point " << pointI
341 << " faceEdges:" << UIndirectList<edge>(surf.edges(), fEdges)()
342 << abort(FatalError);
348 // Starting from startPoint on startEdge on startFace walk along border
349 // and insert faces along the way. Walk keeps always one point or one edge
350 // on the border line.
353 const triSurface& surf,
354 const boolList& borderEdge,
355 const labelList& borderPoint,
357 const label startFaceI,
358 const label startEdgeI, // is border edge
359 const label startPointI, // is border point
361 Map<label>& faceToEdge,
362 Map<label>& faceToPoint
365 label faceI = startFaceI;
366 label edgeI = startEdgeI;
367 label pointI = startPointI;
372 // Stick to pointI and walk face-edge-face until back on border edge.
377 // Cross face to next edge.
378 edgeI = otherEdge(surf, faceI, edgeI, pointI);
380 if (borderEdge[edgeI])
382 if (!faceToEdge.insert(faceI, edgeI))
384 // Was already visited.
389 // First visit to this borderEdge. We're back on borderline.
393 else if (!faceToPoint.insert(faceI, pointI))
395 // Was already visited.
399 // Cross edge to other face
400 const labelList& eFaces = surf.edgeFaces()[edgeI];
402 if (eFaces.size() != 2)
404 FatalErrorIn("walkSplitPoint(..)")
405 << "Can only handle edges with 2 or 4 edges for now."
406 << abort(FatalError);
409 if (eFaces[0] == faceI)
413 else if (eFaces[1] == faceI)
419 FatalErrorIn("walkSplitPoint(..)") << abort(FatalError);
426 // Back on border edge. Cross to other point on edge.
429 pointI = surf.edges()[edgeI].otherVertex(pointI);
431 if (borderPoint[pointI] == -1)
441 // Find second face which is from same surface i.e. has points on the
442 // shared edge in reverse order.
445 const triSurface& surf,
446 const label firstFaceI,
447 const label sharedEdgeI
450 // Find ordering of face points in edge.
452 const edge& e = surf.edges()[sharedEdgeI];
454 const triSurface::FaceType& f = surf.localFaces()[firstFaceI];
456 label startIndex = findIndex(f, e.start());
458 // points in face in same order as edge
459 bool edgeOrder = (f[f.fcIndex(startIndex)] == e.end());
461 // Get faces using edge in sorted order. (sorted such that walking
462 // around them in anti-clockwise order corresponds to edge vector
463 // acc. to right-hand rule)
464 const labelList& eFaces = surf.sortedEdgeFaces()[sharedEdgeI];
466 // Get position of face in sorted edge faces
467 label faceIndex = findIndex(eFaces, firstFaceI);
471 // Get face before firstFaceI
472 return eFaces[eFaces.rcIndex(faceIndex)];
476 // Get face after firstFaceI
477 return eFaces[eFaces.fcIndex(faceIndex)];
482 // Calculate (inward pointing) normals on edges shared by faces in
483 // faceToEdge and averages them to pointNormals.
486 const triSurface& surf,
487 const Map<label>& faceToEdge,
488 const Map<label>& faceToPoint,
489 vectorField& borderPointVec
492 const labelListList& sortedEdgeFaces = surf.sortedEdgeFaces();
493 const edgeList& edges = surf.edges();
494 const pointField& points = surf.localPoints();
496 boolList edgeDone(surf.nEdges(), false);
498 forAllConstIter(Map<label>, faceToEdge, iter)
500 const label edgeI = iter();
502 if (!edgeDone[edgeI])
504 edgeDone[edgeI] = true;
506 // Get the two connected faces in sorted order
507 // Note: should have stored this when walking ...
512 const labelList& eFaces = sortedEdgeFaces[edgeI];
516 label faceI = eFaces[i];
518 if (faceToEdge.found(faceI))
524 else if (face1I == -1)
533 if (face0I == -1 && face1I == -1)
535 Info<< "Writing surface to errorSurf.obj" << endl;
537 surf.write("errorSurf.obj");
539 FatalErrorIn("calcPointVecs(..)")
540 << "Cannot find two faces using border edge " << edgeI
541 << " verts:" << edges[edgeI]
542 << " eFaces:" << eFaces << endl
543 << "face0I:" << face0I
544 << " face1I:" << face1I << nl
545 << "faceToEdge:" << faceToEdge << nl
546 << "faceToPoint:" << faceToPoint
547 << "Written surface to errorSurf.obj"
548 << abort(FatalError);
551 // Now we have edge and the two faces in counter-clockwise order
552 // as seen from edge vector. Calculate normal.
553 const edge& e = edges[edgeI];
554 vector eVec = e.vec(points);
556 // Determine vector as average of the vectors in the two faces.
557 // If there is only one face available use only one vector.
558 vector midVec(vector::zero);
562 label v0 = triSurfaceTools::oppositeVertex(surf, face0I, edgeI);
563 vector e0 = (points[v0] - points[e.start()]) ^ eVec;
570 label v1 = triSurfaceTools::oppositeVertex(surf, face1I, edgeI);
571 vector e1 = (points[e.start()] - points[v1]) ^ eVec;
576 scalar magMidVec = mag(midVec);
578 if (magMidVec > SMALL)
582 // Average to pointVec
583 borderPointVec[e.start()] += midVec;
584 borderPointVec[e.end()] += midVec;
591 // Renumbers vertices (of triangles in faceToEdge) of which the pointMap is
595 const triSurface& surf,
596 const labelList& pointMap,
597 const Map<label>& faceToEdge,
598 List<triSurface::FaceType>& newTris
601 forAllConstIter(Map<label>, faceToEdge, iter)
603 const label faceI = iter.key();
604 const triSurface::FaceType& f = surf.localFaces()[faceI];
608 if (pointMap[f[fp]] != -1)
610 newTris[faceI][fp] = pointMap[f[fp]];
617 // Split all borderEdges that don't have borderPoint. Return true if split
619 bool splitBorderEdges
622 const boolList& borderEdge,
623 const labelList& borderPoint
626 labelList edgesToBeSplit(surf.nEdges());
629 forAll(borderEdge, edgeI)
631 if (borderEdge[edgeI])
633 const edge& e = surf.edges()[edgeI];
635 if (borderPoint[e.start()] == -1 && borderPoint[e.end()] == -1)
637 // None of the points of the edge is borderPoint. Split edge
638 // to introduce border point.
639 edgesToBeSplit[nSplit++] = edgeI;
643 edgesToBeSplit.setSize(nSplit);
647 Info<< "Splitting surface along " << nSplit << " borderEdges that don't"
648 << " neighbour other borderEdges" << nl << endl;
650 surf = triSurfaceTools::greenRefine(surf, edgesToBeSplit);
656 Info<< "No edges to be split" <<nl << endl;
665 int main(int argc, char *argv[])
669 "split multiply connected surface edges by duplicating points"
671 argList::noParallel();
672 argList::validArgs.append("surfaceFile");
673 argList::validArgs.append("output surfaceFile");
674 argList::addBoolOption
677 "add debugging output"
680 argList args(argc, argv);
682 const fileName inSurfName = args[1];
683 const fileName outSurfName = args[2];
684 const bool debug = args.optionFound("debug");
686 Info<< "Reading surface from " << inSurfName << endl;
688 triSurface surf(inSurfName);
690 // Make sure sortedEdgeFaces is calculated correctly
691 testSortedEdgeFaces(surf);
693 // Get all quad connected edges. These are seen as borders when walking.
694 boolList borderEdge(surf.nEdges(), false);
695 markBorderEdges(debug, surf, borderEdge);
697 // Points on two sides connected to borderEdges are called
698 // borderPoints and will be duplicated. borderPoint contains label
699 // of newly introduced vertex.
700 labelList borderPoint(surf.nPoints(), -1);
701 markBorderPoints(debug, surf, borderEdge, borderPoint);
703 // Split edges where there would be no borderPoint to duplicate.
704 splitBorderEdges(surf, borderEdge, borderPoint);
707 Info<< "Writing split surface to " << outSurfName << nl << endl;
708 surf.write(outSurfName);
709 Info<< "Finished writing surface to " << outSurfName << nl << endl;
712 // Last iteration values.
713 label nOldBorderEdges = -1;
714 label nOldBorderPoints = -1;
720 // Redo borderEdge/borderPoint calculation.
721 boolList borderEdge(surf.nEdges(), false);
723 label nBorderEdges = markBorderEdges(debug, surf, borderEdge);
725 if (nBorderEdges == 0)
727 Info<< "Found no border edges. Exiting." << nl << nl;
732 // Label of newly introduced duplicate.
733 labelList borderPoint(surf.nPoints(), -1);
735 label nBorderPoints =
744 if (nBorderPoints == 0)
746 Info<< "Found no border points. Exiting." << nl << nl;
752 << " border edges :" << nBorderEdges << nl
753 << " border points:" << nBorderPoints << nl
758 nBorderPoints == nOldBorderPoints
759 && nBorderEdges == nOldBorderEdges
762 Info<< "Stopping since number of border edges and point is same"
763 << " as in previous iteration" << nl << endl;
769 // Define splitLine as a series of connected borderEdges. Find start
770 // of one (as edge and point on edge)
773 label startEdgeI = -1;
774 label startPointI = -1;
776 forAll(borderEdge, edgeI)
778 if (borderEdge[edgeI])
780 const edge& e = surf.edges()[edgeI];
782 if ((borderPoint[e[0]] != -1) && (borderPoint[e[1]] == -1))
789 else if ((borderPoint[e[0]] == -1) && (borderPoint[e[1]] != -1))
799 if (startEdgeI == -1)
801 Info<< "Cannot find starting point of splitLine\n" << endl;
806 // Pick any face using edge to start from.
807 const labelList& eFaces = surf.edgeFaces()[startEdgeI];
809 label firstFaceI = eFaces[0];
811 // Find second face which is from same surface i.e. has outwards
812 // pointing normal as well (actually bit more complex than this)
813 label secondFaceI = sharedFace(surf, firstFaceI, startEdgeI);
815 Info<< "Starting local walk from:" << endl
816 << " edge :" << startEdgeI << endl
817 << " point:" << startPointI << endl
818 << " face0:" << firstFaceI << endl
819 << " face1:" << secondFaceI << endl
822 // From face on border edge to edge.
823 Map<label> faceToEdge(2*nBorderEdges);
824 // From face connected to border point (but not border edge) to point.
825 Map<label> faceToPoint(2*nBorderPoints);
827 faceToEdge.insert(firstFaceI, startEdgeI);
843 faceToEdge.insert(secondFaceI, startEdgeI);
859 Info<< "Finished local walk and visited" << nl
860 << " border edges :" << faceToEdge.size() << nl
861 << " border points(but not edges):" << faceToPoint.size() << nl
866 dumpFaces("faceToEdge.obj", surf, faceToEdge);
867 dumpFaces("faceToPoint.obj", surf, faceToPoint);
871 // Create coordinates for borderPoints by duplicating the existing
872 // point and then slightly shifting it inwards. To determine the
873 // inwards direction get the average normal of both connectedFaces on
874 // the edge and then interpolate this to the (border)point.
877 vectorField borderPointVec(surf.nPoints(), vector(GREAT, GREAT, GREAT));
879 calcPointVecs(surf, faceToEdge, faceToPoint, borderPointVec);
882 // New position. Start off from copy of old points.
883 pointField newPoints(surf.localPoints());
884 newPoints.setSize(newPoints.size() + nBorderPoints);
886 forAll(borderPoint, pointI)
888 label newPointI = borderPoint[pointI];
892 scalar minLen = minEdgeLen(surf, pointI);
894 vector n = borderPointVec[pointI];
897 newPoints[newPointI] = newPoints[pointI] + 0.1 * minLen * n;
903 // Renumber all faces in connectedFaces
906 // Start off from copy of faces.
907 List<labelledTri> newTris(surf.size());
911 newTris[faceI] = surf.localFaces()[faceI];
912 newTris[faceI].region() = surf[faceI].region();
915 // Renumber all faces in faceToEdge
916 renumberFaces(surf, borderPoint, faceToEdge, newTris);
917 // Renumber all faces in faceToPoint
918 renumberFaces(surf, borderPoint, faceToPoint, newTris);
921 // Check if faces use unmoved points.
922 forAll(newTris, faceI)
924 const triSurface::FaceType& f = newTris[faceI];
928 const point& pt = newPoints[f[fp]];
930 if (mag(pt) >= GREAT/2)
932 Info<< "newTri:" << faceI << " verts:" << f
933 << " vert:" << f[fp] << " point:" << pt << endl;
939 surf = triSurface(newTris, surf.patches(), newPoints);
941 if (debug || (iteration != 0 && (iteration % 20) == 0))
943 Info<< "Writing surface to " << outSurfName << nl << endl;
945 surf.write(outSurfName);
947 Info<< "Finished writing surface to " << outSurfName << nl << endl;
950 // Save prev iteration values.
951 nOldBorderEdges = nBorderEdges;
952 nOldBorderPoints = nBorderPoints;
958 Info<< "Writing final surface to " << outSurfName << nl << endl;
960 surf.write(outSurfName);
962 Info<< "End\n" << endl;
968 // ************************************************************************* //