/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation \\/ M anipulation | ------------------------------------------------------------------------------- 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 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 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 "OFstream.H" #include "booleanSurface.H" #include "edgeIntersections.H" #include "meshTools.H" #include "labelPair.H" 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 ( 1e-3*edgeIntersections::minEdgeLength(surf) ); // 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 ( 1e-3*edgeIntersections::minEdgeLength(surf1) ); // 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 ( 1e-3*edgeIntersections::minEdgeLength(surf2) ); // 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 ) { vector cross = (normal ^ edgeDir); cross /= mag(cross); vector fC0tofE0 = faceCentre - pointOnEdge; fC0tofE0 /= mag(fC0tofE0); 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& edge1Cuts, edgeIntersections& edge2Cuts ) { if (perturb) { intersectSurfaces ( surf1, edge1Cuts, surf2, edge2Cuts ); } else { triSurfaceSearch querySurf2(surf2); Info<< "Determining intersections of surf1 edges with surf2 faces" << endl; edge1Cuts = edgeIntersections ( surf1, querySurf2, 1e-3*edgeIntersections::minEdgeLength(surf1) ); triSurfaceSearch querySurf1(surf1); Info<< "Determining intersections of surf2 edges with surf1 faces" << endl; edge2Cuts = edgeIntersections ( surf2, querySurf1, 1e-3*edgeIntersections::minEdgeLength(surf2) ); } } void calcFeaturePoints(const pointField& points, const edgeList& edges) { edgeMesh eMesh(points, edges); const labelListList& pointEdges = eMesh.pointEdges(); // Get total number of feature points label nFeaturePoints = 0; forAll(pointEdges, pI) { const labelList& pEdges = pointEdges[pI]; if (pEdges.size() == 1) { nFeaturePoints++; } } // Calculate addressing from feature point to cut point and cut edge labelList featurePointToCutPoint(nFeaturePoints); labelList featurePointToCutEdge(nFeaturePoints); label nFeatPts = 0; forAll(pointEdges, pI) { const labelList& pEdges = pointEdges[pI]; if (pEdges.size() == 1) { featurePointToCutPoint[nFeatPts] = pI; featurePointToCutEdge[nFeatPts] = pEdges[0]; nFeatPts++; } } label concaveStart = 0; label mixedStart = 0; label nonFeatureStart = nFeaturePoints; labelListList featurePointNormals(nFeaturePoints); labelListList featurePointEdges(nFeaturePoints); labelList regionEdges; } int main(int argc, char *argv[]) { argList::noParallel(); argList::validArgs.append("action"); argList::validArgs.append("surface file"); argList::validArgs.append("surface file"); argList::addBoolOption ( "surf1Baffle", "Mark surface 1 as a baffle" ); argList::addBoolOption ( "surf2Baffle", "Mark surface 2 as a baffle" ); argList::addBoolOption ( "perturb", "Perturb surface points to escape degenerate intersections" ); argList::addBoolOption ( "invertedSpace", "do the surfaces have inverted space orientation, " "i.e. a point at infinity is considered inside. " "This is only sensible for union and intersection." ); # include "setRootCase.H" # include "createTime.H" word action(args.args()[1]); HashTable validActions; validActions.insert("intersection", booleanSurface::INTERSECTION); validActions.insert("union", booleanSurface::UNION); validActions.insert("difference", booleanSurface::DIFFERENCE); if (!validActions.found(action)) { FatalErrorIn(args.executable()) << "Unsupported action " << action << endl << "Supported actions:" << validActions.toc() << abort(FatalError); } fileName surf1Name(args.args()[2]); Info<< "Reading surface " << surf1Name << endl; triSurface surf1(surf1Name); Info<< surf1Name << " statistics:" << endl; surf1.writeStats(Info); Info<< endl; fileName surf2Name(args[3]); Info<< "Reading surface " << surf2Name << endl; triSurface surf2(surf2Name); Info<< surf2Name << " statistics:" << endl; surf2.writeStats(Info); Info<< endl; const bool surf1Baffle = args.optionFound("surf1Baffle"); const bool surf2Baffle = args.optionFound("surf2Baffle"); edgeIntersections edge1Cuts; edgeIntersections edge2Cuts; bool invertedSpace = args.optionFound("invertedSpace"); if (invertedSpace && validActions[action] == booleanSurface::DIFFERENCE) { FatalErrorIn(args.executable()) << "Inverted space only makes sense for union or intersection." << exit(FatalError); } // Calculate the points where the edges are cut by the other surface calcEdgeCuts ( surf1, surf2, args.optionFound("perturb"), edge1Cuts, edge2Cuts ); // Determine intersection edges from the edge cuts surfaceIntersection inter ( surf1, edge1Cuts, surf2, edge2Cuts ); fileName sFeatFileName ( surf1Name.lessExt().name() + "_" + surf2Name.lessExt().name() + "_" + action ); label nFeatEds = inter.cutEdges().size(); DynamicList normals(2*nFeatEds); vectorField edgeDirections(nFeatEds, vector::zero); DynamicList normalVolumeTypes ( 2*nFeatEds ); List > edgeNormals(nFeatEds); List > normalDirections(nFeatEds); forAllConstIter(labelPairLookup, inter.facePairToEdge(), iter) { const label& cutEdgeI = iter(); const labelPair& facePair = iter.key(); const edge& fE = inter.cutEdges()[cutEdgeI]; const vector& norm1 = surf1.faceNormals()[facePair.first()]; const vector& norm2 = surf2.faceNormals()[facePair.second()]; DynamicList