/*---------------------------------------------------------------------------*\
========= |
\\ / 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