/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2016-2020 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. OpenFOAM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenFOAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenFOAM. If not, see . Application surfaceBooleanFeatures Group grpSurfaceUtilities Description Generates the extendedFeatureEdgeMesh for the interface between a boolean operation on two surfaces. Assumes that the orientation of the surfaces is correct: - if the operation is union or intersection, that both surface's normals (n) have the same orientation with respect to a point, i.e. surfaces A and B are orientated the same with respect to point x: \verbatim _______ | |--> n | ___|___ x |A | | |--> n |___|___| B| | | |_______| \endverbatim - if the operation is a subtraction, the surfaces should be oppositely oriented with respect to a point, i.e. for (A - B), then B's orientation should be such that x is "inside", and A's orientation such that x is "outside" \verbatim _______ | |--> n | ___|___ x |A | | | |___|___| B| | n <--| |_______| \endverbatim When the operation is peformed - for union, all of the edges generates where one surfaces cuts another are all "internal" for union, and "external" for intersection, (B - A) and (A - B). This has been assumed, formal (dis)proof is invited. \*---------------------------------------------------------------------------*/ #include "triSurface.H" #include "argList.H" #include "Time.H" #include "featureEdgeMesh.H" #include "extendedFeatureEdgeMesh.H" #include "triSurfaceSearch.H" #include "triSurfaceMesh.H" #include "OFstream.H" #include "OBJstream.H" #include "booleanSurface.H" #include "edgeIntersections.H" #include "meshTools.H" #include "DynamicField.H" #include "Enum.H" #ifndef NO_CGAL #include #include #include #include "CGALIndexedPolyhedron.H" #include "PolyhedronReader.H" typedef CGAL::AABB_face_graph_triangle_primitive < Polyhedron, CGAL::Default, CGAL::Tag_false > Primitive; typedef CGAL::AABB_traits Traits; typedef CGAL::AABB_tree Tree; typedef boost::optional < Tree::Intersection_and_primitive_id::Type > Segment_intersection; #endif // NO_CGAL using namespace Foam; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // bool intersectSurfaces ( triSurface& surf, edgeIntersections& edgeCuts ) { bool hasMoved = false; for (label iter = 0; iter < 10; iter++) { Info<< "Determining intersections of surface edges with itself" << endl; // Determine surface edge intersections. Allow surface to be moved. // Number of iterations needed to resolve degenerates label nIters = 0; { triSurfaceSearch querySurf(surf); scalarField surfPointTol ( max(1e-3*edgeIntersections::minEdgeLength(surf), SMALL) ); // Determine raw intersections edgeCuts = edgeIntersections ( surf, querySurf, surfPointTol ); // Shuffle a bit to resolve degenerate edge-face hits { pointField points(surf.points()); nIters = edgeCuts.removeDegenerates ( 5, // max iterations surf, querySurf, surfPointTol, points // work array ); if (nIters != 0) { // Update geometric quantities surf.movePoints(points); hasMoved = true; } } } } if (hasMoved) { fileName newFile("surf.obj"); Info<< "Surface has been moved. Writing to " << newFile << endl; surf.write(newFile); } return hasMoved; } // Keep on shuffling surface points until no more degenerate intersections. // Moves both surfaces and updates set of edge cuts. bool intersectSurfaces ( triSurface& surf1, edgeIntersections& edgeCuts1, triSurface& surf2, edgeIntersections& edgeCuts2 ) { bool hasMoved1 = false; bool hasMoved2 = false; for (label iter = 0; iter < 10; iter++) { Info<< "Determining intersections of surf1 edges with surf2" << " faces" << endl; // Determine surface1 edge intersections. Allow surface to be moved. // Number of iterations needed to resolve degenerates label nIters1 = 0; { triSurfaceSearch querySurf2(surf2); scalarField surf1PointTol ( max(1e-3*edgeIntersections::minEdgeLength(surf1), SMALL) ); // Determine raw intersections edgeCuts1 = edgeIntersections ( surf1, querySurf2, surf1PointTol ); // Shuffle a bit to resolve degenerate edge-face hits { pointField points1(surf1.points()); nIters1 = edgeCuts1.removeDegenerates ( 5, // max iterations surf1, querySurf2, surf1PointTol, points1 // work array ); if (nIters1 != 0) { // Update geometric quantities surf1.movePoints(points1); hasMoved1 = true; } } } Info<< "Determining intersections of surf2 edges with surf1" << " faces" << endl; label nIters2 = 0; { triSurfaceSearch querySurf1(surf1); scalarField surf2PointTol ( max(1e-3*edgeIntersections::minEdgeLength(surf2), SMALL) ); // Determine raw intersections edgeCuts2 = edgeIntersections ( surf2, querySurf1, surf2PointTol ); // Shuffle a bit to resolve degenerate edge-face hits { pointField points2(surf2.points()); nIters2 = edgeCuts2.removeDegenerates ( 5, // max iterations surf2, querySurf1, surf2PointTol, points2 // work array ); if (nIters2 != 0) { // Update geometric quantities surf2.movePoints(points2); hasMoved2 = true; } } } if (nIters1 == 0 && nIters2 == 0) { Info<< "** Resolved all intersections to be proper edge-face pierce" << endl; break; } } if (hasMoved1) { fileName newFile("surf1.obj"); Info<< "Surface 1 has been moved. Writing to " << newFile << endl; surf1.write(newFile); } if (hasMoved2) { fileName newFile("surf2.obj"); Info<< "Surface 2 has been moved. Writing to " << newFile << endl; surf2.write(newFile); } return hasMoved1 || hasMoved2; } label calcNormalDirection ( const vector& normal, const vector& otherNormal, const vector& edgeDir, const vector& faceCentre, const vector& pointOnEdge ) { const vector cross = normalised(normal ^ edgeDir); const vector fC0tofE0 = normalised(faceCentre - pointOnEdge); label nDir = ((cross & fC0tofE0) > 0.0 ? 1 : -1); nDir *= ((otherNormal & fC0tofE0) > 0.0 ? -1 : 1); return nDir; } void calcEdgeCuts ( triSurface& surf1, triSurface& surf2, const bool perturb, edgeIntersections& edgeCuts1, edgeIntersections& edgeCuts2 ) { if (perturb) { intersectSurfaces ( surf1, edgeCuts1, surf2, edgeCuts2 ); } else { triSurfaceSearch querySurf2(surf2); Info<< "Determining intersections of surf1 edges with surf2 faces" << endl; edgeCuts1 = edgeIntersections ( surf1, querySurf2, max(1e-3*edgeIntersections::minEdgeLength(surf1), SMALL) ); triSurfaceSearch querySurf1(surf1); Info<< "Determining intersections of surf2 edges with surf1 faces" << endl; edgeCuts2 = edgeIntersections ( surf2, querySurf1, max(1e-3*edgeIntersections::minEdgeLength(surf2), SMALL) ); } } // CGAL variants #ifndef NO_CGAL void visitPointRegion ( const triSurface& s, const label zoneI, const label pointI, const label startEdgeI, const label startFaceI, labelList& pFacesZone ) { const labelList& eFaces = s.edgeFaces()[startEdgeI]; if (eFaces.size() == 2) { label nextFaceI; if (eFaces[0] == startFaceI) { nextFaceI = eFaces[1]; } else if (eFaces[1] == startFaceI) { nextFaceI = eFaces[0]; } else { FatalErrorInFunction << "problem" << exit(FatalError); nextFaceI = -1; } label index = s.pointFaces()[pointI].find(nextFaceI); if (pFacesZone[index] == -1) { // Mark face as been visited. pFacesZone[index] = zoneI; // Step to next edge on face which is still using pointI const labelList& fEdges = s.faceEdges()[nextFaceI]; label nextEdgeI = -1; forAll(fEdges, i) { label edgeI = fEdges[i]; const edge& e = s.edges()[edgeI]; if (edgeI != startEdgeI && (e[0] == pointI || e[1] == pointI)) { nextEdgeI = edgeI; break; } } if (nextEdgeI == -1) { FatalErrorInFunction << "Problem: cannot find edge out of " << fEdges << "on face " << nextFaceI << " that uses point " << pointI << " and is not edge " << startEdgeI << abort(FatalError); } visitPointRegion ( s, zoneI, pointI, nextEdgeI, nextFaceI, pFacesZone ); } } } label dupNonManifoldPoints(triSurface& s, labelList& pointMap) { const labelListList& pf = s.pointFaces(); const labelListList& fe = s.faceEdges(); const edgeList& edges = s.edges(); DynamicField newPoints(s.points()); // From dupSurf back to s.pointa DynamicList