diff --git a/applications/utilities/surface/surfaceBooleanFeatures/Make/files b/applications/utilities/surface/surfaceBooleanFeatures/Make/files
new file mode 100644
index 0000000000..3197270691
--- /dev/null
+++ b/applications/utilities/surface/surfaceBooleanFeatures/Make/files
@@ -0,0 +1,3 @@
+surfaceBooleanFeatures.C
+
+EXE = $(FOAM_APPBIN)/surfaceBooleanFeatures
diff --git a/applications/utilities/surface/surfaceBooleanFeatures/Make/options b/applications/utilities/surface/surfaceBooleanFeatures/Make/options
new file mode 100644
index 0000000000..727dbd1c2b
--- /dev/null
+++ b/applications/utilities/surface/surfaceBooleanFeatures/Make/options
@@ -0,0 +1,9 @@
+EXE_INC = \
+ -I$(LIB_SRC)/triSurface/lnInclude \
+ -I$(LIB_SRC)/edgeMesh/lnInclude \
+ -I$(LIB_SRC)/meshTools/lnInclude
+
+EXE_LIBS = \
+ -ltriSurface \
+ -ledgeMesh \
+ -lmeshTools
diff --git a/applications/utilities/surface/surfaceBooleanFeatures/surfaceBooleanFeatures.C b/applications/utilities/surface/surfaceBooleanFeatures/surfaceBooleanFeatures.C
new file mode 100644
index 0000000000..3f221849c4
--- /dev/null
+++ b/applications/utilities/surface/surfaceBooleanFeatures/surfaceBooleanFeatures.C
@@ -0,0 +1,464 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | Copyright (C) 2010-2010 OpenCFD Ltd.
+ \\/ 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 featureEdgeMesh 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 "triSurfaceSearch.H"
+#include "OFstream.H"
+#include "booleanSurface.H"
+#include "edgeIntersections.H"
+
+using namespace Foam;
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// 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;
+}
+
+
+int main(int argc, char *argv[])
+{
+ argList::validArgs.clear();
+ argList::noParallel();
+ argList::validArgs.append("action");
+ argList::validArgs.append("surface file");
+ argList::validArgs.append("surface file");
+ argList::validArgs.append("output file");
+
+ 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() << exit(FatalError);
+ }
+
+ Info<< "Reading surface 1 .." << endl;
+ fileName surf1Name(args[2]);
+ triSurface surf1(surf1Name);
+
+ Info<< "Surface 1 statistics:" << endl;
+ surf1.writeStats(Info);
+ Info<< endl;
+
+ Info<< "Reading surface 2 .." << endl;
+ fileName surf2Name(args[3]);
+ triSurface surf2(surf2Name);
+
+ Info<< "Surface 2 statistics:" << endl;
+ surf2.writeStats(Info);
+ Info<< endl;
+
+ fileName outFileName(args[4]);
+ Info<< "Output file name " << outFileName << endl;
+
+ Info<< "Intersecting surface 1 and 2" << endl;
+
+ 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);
+ }
+
+ if (args.optionFound("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)
+ );
+ }
+
+ // Determine intersection edges
+ surfaceIntersection inter(surf1, edge1Cuts, surf2, edge2Cuts);
+
+ OFstream intFile(outFileName);
+
+ fileName sFeatFileName =
+ surf1Name.lessExt().name()
+ + "_"
+ + surf2Name.lessExt().name()
+ + "_"
+ + action;
+
+ label nFeatEds = inter.cutEdges().size();
+
+ vectorField normals(2*nFeatEds, vector::zero);
+ vectorField edgeDirections(nFeatEds, vector::zero);
+ labelListList edgeNormals(nFeatEds, labelList(2, -1));
+
+ triSurfaceSearch querySurf1(surf1);
+ triSurfaceSearch querySurf2(surf2);
+
+ OFstream normalFile(sFeatFileName + "_normals.obj");
+
+ forAll(inter.cutEdges(), i)
+ {
+ const edge& fE(inter.cutEdges()[i]);
+
+ point fEC = fE.centre(inter.cutPoints());
+
+ pointIndexHit nearest1 = querySurf1.tree().findNearest(fEC, sqr(GREAT));
+ pointIndexHit nearest2 = querySurf2.tree().findNearest(fEC, sqr(GREAT));
+
+ normals[2*i] = surf1.faceNormals()[nearest1.index()];
+ normals[2*i + 1] = surf2.faceNormals()[nearest2.index()];
+
+ edgeNormals[i][0] = 2*i;
+ edgeNormals[i][1] = 2*i + 1;
+
+ edgeDirections[i] = fE.vec(inter.cutPoints());
+
+ {
+ scalar scale = 3*fE.mag(inter.cutPoints());
+
+ meshTools::writeOBJ(normalFile, inter.cutPoints()[fE.start()]);
+ meshTools::writeOBJ(normalFile, inter.cutPoints()[fE.end()]);
+
+ normalFile<< "l " << (5*i) + 1 << " " << (5*i) + 2<< endl;
+
+ meshTools::writeOBJ(normalFile, fEC);
+ meshTools::writeOBJ(normalFile, fEC + scale*normals[2*i]);
+ meshTools::writeOBJ(normalFile, fEC + scale*normals[2*i + 1]);
+
+ normalFile<< "l " << (5*i) + 3 << " " << (5*i) + 4 << endl;
+ normalFile<< "l " << (5*i) + 3 << " " << (5*i) + 5 << endl;
+ }
+ }
+
+ label internalStart = -1;
+
+ if (validActions[action] == booleanSurface::UNION)
+ {
+ if (!invertedSpace)
+ {
+ // All edges are internal
+ internalStart = 0;
+ }
+ else
+ {
+ // All edges are external
+ internalStart = nFeatEds;
+ }
+ }
+ else if (validActions[action] == booleanSurface::INTERSECTION)
+ {
+ if (!invertedSpace)
+ {
+ // All edges are external
+ internalStart = nFeatEds;
+ }
+ else
+ {
+ // All edges are internal
+ internalStart = 0;
+ }
+ }
+ else if (validActions[action] == booleanSurface::DIFFERENCE)
+ {
+ // All edges are external
+ internalStart = nFeatEds;
+ }
+ else
+ {
+ FatalErrorIn(args.executable())
+ << "Unsupported booleanSurface:booleanOpType and space "
+ << action << " " << invertedSpace
+ << abort(FatalError);
+ }
+
+ // There are no feature points supported by surfaceIntersection
+ // Flat, open or multiple edges are assumed to be impossible
+ // Region edges are not explicitly supported by surfaceIntersection
+
+ featureEdgeMesh feMesh
+ (
+ IOobject
+ (
+ sFeatFileName + ".featureEdgeMesh",
+ runTime.constant(),
+ "featureEdgeMesh",
+ runTime,
+ IOobject::NO_READ,
+ IOobject::NO_WRITE
+ ),
+ inter.cutPoints(),
+ inter.cutEdges(),
+ 0, // concaveStart,
+ 0, // mixedStart,
+ 0, // nonFeatureStart,
+ internalStart, // internalStart,
+ nFeatEds, // flatStart,
+ nFeatEds, // openStart,
+ nFeatEds, // multipleStart,
+ normals,
+ edgeDirections,
+ edgeNormals,
+ labelListList(0), // featurePointNormals,
+ labelList(0) // regionEdges
+ );
+
+ feMesh.write();
+
+ feMesh.writeObj(sFeatFileName);
+
+ Info << "End\n" << endl;
+
+ return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/triSurface/booleanOps/booleanSurface/booleanSurface.C b/src/meshTools/triSurface/booleanOps/booleanSurface/booleanSurface.C
index dc0da28405..e5fd2a7695 100644
--- a/src/meshTools/triSurface/booleanOps/booleanSurface/booleanSurface.C
+++ b/src/meshTools/triSurface/booleanOps/booleanSurface/booleanSurface.C
@@ -997,9 +997,9 @@ Foam::booleanSurface::booleanSurface
// Depending on operation include certain faces.
- // AND: faces on inside of 1 and of 2
- // OR: faces on outside of 1 and of 2
- // XOR: faces on outside of 1 and inside of 2
+ // INTERSECTION: faces on inside of 1 and of 2
+ // UNION: faces on outside of 1 and of 2
+ // DIFFERENCE: faces on outside of 1 and inside of 2
boolList include(combinedSurf.size(), false);
@@ -1018,30 +1018,30 @@ Foam::booleanSurface::booleanSurface
}
else if (side[faceI] == OUTSIDE)
{
- if (booleanOp == booleanSurface::OR)
+ if (booleanOp == booleanSurface::UNION)
{
include[faceI] = true;
}
- else if (booleanOp == booleanSurface::AND)
+ else if (booleanOp == booleanSurface::INTERSECTION)
{
include[faceI] = false;
}
- else // xor
+ else // difference
{
include[faceI] = (faceI < cutSurf1.size()); // face from surf1
}
}
else // inside
{
- if (booleanOp == booleanSurface::OR)
+ if (booleanOp == booleanSurface::UNION)
{
include[faceI] = false;
}
- else if (booleanOp == booleanSurface::AND)
+ else if (booleanOp == booleanSurface::INTERSECTION)
{
include[faceI] = true;
}
- else // xor
+ else // difference
{
include[faceI] = (faceI >= cutSurf1.size()); // face from surf2
}
diff --git a/src/meshTools/triSurface/booleanOps/booleanSurface/booleanSurface.H b/src/meshTools/triSurface/booleanOps/booleanSurface/booleanSurface.H
index 2dcefae524..c6bf3282b7 100644
--- a/src/meshTools/triSurface/booleanOps/booleanSurface/booleanSurface.H
+++ b/src/meshTools/triSurface/booleanOps/booleanSurface/booleanSurface.H
@@ -30,7 +30,7 @@ Description
Called 'boolean' since the volume of resulting surface will encompass
the volumes of the original surface according to some boolean operation:
- all which is in surface1 AND in surface2 (intersection)
- - all which is in surface1 AND NOT in surface2 ('chop' out surface2)
+ - all which is in surface1 AND NOT in surface2 (surface1 minus surface2)
- all which is in surface1 OR in surface2 (union)
Algorithm:
@@ -163,11 +163,11 @@ public:
//- Enumeration listing the possible volume operator types
enum booleanOpType
{
- OR, // Union of volumes
- AND, // Intersection of volumes
- XOR, // Difference of volumes
- ALL // Special: does not subset combined surface. (Produces
- // multiply connected surface)
+ UNION, // Union of volumes
+ INTERSECTION, // Intersection of volumes
+ DIFFERENCE, // Difference of volumes
+ ALL // Special: does not subset combined
+ // surface. (Produces multiply connected surface)
};