now all path functions in 'IOobject' are either templated on the type or require a 'globalFile' argument to specify if the type is case global e.g. 'IOdictionary' or decomposed in parallel, e.g. almost everything else. The 'global()' and 'globalFile()' virtual functions are now in 'regIOobject' abstract base-class and overridden as required by derived classes. The path functions using 'global()' and 'globalFile()' to differentiate between global and processor local objects are now also in 'regIOobject' rather than 'IOobject' to ensure the path returned is absolutely consistent with the type. Unfortunately there is still potential for unexpected IO behaviour inconsistent with the global/local nature of the type due to the 'fileOperation' classes searching the processor directory for case global objects before searching the case directory. This approach appears to be a work-around for incomplete integration with and rationalisation of 'IOobject' but with the changes above it is no longer necessary. Unfortunately this "up" searching is baked-in at a low level and mixed-up with various complex ways to pick the processor directory name out of the object path and will take some unravelling but this work will undertaken as time allows.
808 lines
21 KiB
C++
808 lines
21 KiB
C++
/*---------------------------------------------------------------------------*\
|
|
========= |
|
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
|
\\ / O peration | Website: https://openfoam.org
|
|
\\ / A nd | Copyright (C) 2011-2021 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 <http://www.gnu.org/licenses/>.
|
|
|
|
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 performed - 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++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
argList::noParallel();
|
|
argList::validArgs.append("surface file");
|
|
argList::validArgs.append("surface file");
|
|
argList::validArgs.append("action");
|
|
|
|
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()[3]);
|
|
|
|
HashTable<booleanSurface::booleanOpType> validActions;
|
|
validActions.insert("intersection", booleanSurface::INTERSECTION);
|
|
validActions.insert("union", booleanSurface::UNION);
|
|
validActions.insert("difference", booleanSurface::DIFFERENCE);
|
|
|
|
if (!validActions.found(action))
|
|
{
|
|
FatalErrorInFunction
|
|
<< "Unsupported action " << action << endl
|
|
<< "Supported actions:" << validActions.toc() << abort(FatalError);
|
|
}
|
|
|
|
fileName surf1Name = args[1];
|
|
Info<< "Reading surface " << surf1Name << endl;
|
|
triSurface surf1(surf1Name);
|
|
|
|
Info<< surf1Name << " statistics:" << endl;
|
|
surf1.writeStats(Info);
|
|
Info<< endl;
|
|
|
|
fileName surf2Name = args[2];
|
|
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)
|
|
{
|
|
FatalErrorInFunction
|
|
<< "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<vector> normals(2*nFeatEds);
|
|
vectorField edgeDirections(nFeatEds, Zero);
|
|
DynamicList<extendedFeatureEdgeMesh::sideVolumeType> normalVolumeTypes
|
|
(
|
|
2*nFeatEds
|
|
);
|
|
List<DynamicList<label>> edgeNormals(nFeatEds);
|
|
List<DynamicList<label>> 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<label>& eNormals = edgeNormals[cutEdgeI];
|
|
DynamicList<label>& nDirections = normalDirections[cutEdgeI];
|
|
|
|
edgeDirections[cutEdgeI] = fE.vec(inter.cutPoints());
|
|
|
|
normals.append(norm1);
|
|
eNormals.append(normals.size() - 1);
|
|
|
|
if (surf1Baffle)
|
|
{
|
|
normalVolumeTypes.append(extendedFeatureEdgeMesh::BOTH);
|
|
|
|
nDirections.append(1);
|
|
}
|
|
else
|
|
{
|
|
normalVolumeTypes.append(extendedFeatureEdgeMesh::INSIDE);
|
|
nDirections.append
|
|
(
|
|
calcNormalDirection
|
|
(
|
|
norm1,
|
|
norm2,
|
|
edgeDirections[cutEdgeI],
|
|
surf1[facePair.first()].centre(surf1.points()),
|
|
inter.cutPoints()[fE.start()]
|
|
)
|
|
);
|
|
}
|
|
|
|
normals.append(norm2);
|
|
eNormals.append(normals.size() - 1);
|
|
|
|
if (surf2Baffle)
|
|
{
|
|
normalVolumeTypes.append(extendedFeatureEdgeMesh::BOTH);
|
|
|
|
nDirections.append(1);
|
|
}
|
|
else
|
|
{
|
|
normalVolumeTypes.append(extendedFeatureEdgeMesh::INSIDE);
|
|
|
|
nDirections.append
|
|
(
|
|
calcNormalDirection
|
|
(
|
|
norm2,
|
|
norm1,
|
|
edgeDirections[cutEdgeI],
|
|
surf2[facePair.second()].centre(surf2.points()),
|
|
inter.cutPoints()[fE.start()]
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
if (surf1Baffle)
|
|
{
|
|
normals.append(norm2);
|
|
|
|
if (surf2Baffle)
|
|
{
|
|
normalVolumeTypes.append(extendedFeatureEdgeMesh::BOTH);
|
|
|
|
nDirections.append(1);
|
|
}
|
|
else
|
|
{
|
|
normalVolumeTypes.append(extendedFeatureEdgeMesh::INSIDE);
|
|
|
|
nDirections.append
|
|
(
|
|
calcNormalDirection
|
|
(
|
|
norm2,
|
|
norm1,
|
|
edgeDirections[cutEdgeI],
|
|
surf2[facePair.second()].centre(surf2.points()),
|
|
inter.cutPoints()[fE.start()]
|
|
)
|
|
);
|
|
}
|
|
|
|
eNormals.append(normals.size() - 1);
|
|
}
|
|
|
|
if (surf2Baffle)
|
|
{
|
|
normals.append(norm1);
|
|
|
|
if (surf1Baffle)
|
|
{
|
|
normalVolumeTypes.append(extendedFeatureEdgeMesh::BOTH);
|
|
|
|
nDirections.append(1);
|
|
}
|
|
else
|
|
{
|
|
normalVolumeTypes.append(extendedFeatureEdgeMesh::INSIDE);
|
|
|
|
nDirections.append
|
|
(
|
|
calcNormalDirection
|
|
(
|
|
norm1,
|
|
norm2,
|
|
edgeDirections[cutEdgeI],
|
|
surf1[facePair.first()].centre(surf1.points()),
|
|
inter.cutPoints()[fE.start()]
|
|
)
|
|
);
|
|
}
|
|
|
|
eNormals.append(normals.size() - 1);
|
|
}
|
|
}
|
|
|
|
|
|
label internalStart = -1;
|
|
label nIntOrExt = 0;
|
|
label nFlat = 0;
|
|
label nOpen = 0;
|
|
label nMultiple = 0;
|
|
|
|
forAll(edgeNormals, eI)
|
|
{
|
|
label nEdNorms = edgeNormals[eI].size();
|
|
|
|
if (nEdNorms == 1)
|
|
{
|
|
nOpen++;
|
|
}
|
|
else if (nEdNorms == 2)
|
|
{
|
|
const vector& n0(normals[edgeNormals[eI][0]]);
|
|
const vector& n1(normals[edgeNormals[eI][1]]);
|
|
|
|
if ((n0 & n1) > extendedFeatureEdgeMesh::cosNormalAngleTol_)
|
|
{
|
|
nFlat++;
|
|
}
|
|
else
|
|
{
|
|
nIntOrExt++;
|
|
}
|
|
}
|
|
else if (nEdNorms > 2)
|
|
{
|
|
nMultiple++;
|
|
}
|
|
}
|
|
|
|
if (validActions[action] == booleanSurface::UNION)
|
|
{
|
|
if (!invertedSpace)
|
|
{
|
|
// All edges are internal
|
|
internalStart = 0;
|
|
}
|
|
else
|
|
{
|
|
// All edges are external
|
|
internalStart = nIntOrExt;
|
|
}
|
|
}
|
|
else if (validActions[action] == booleanSurface::INTERSECTION)
|
|
{
|
|
if (!invertedSpace)
|
|
{
|
|
// All edges are external
|
|
internalStart = nIntOrExt;
|
|
}
|
|
else
|
|
{
|
|
// All edges are internal
|
|
internalStart = 0;
|
|
}
|
|
}
|
|
else if (validActions[action] == booleanSurface::DIFFERENCE)
|
|
{
|
|
// All edges are external
|
|
internalStart = nIntOrExt;
|
|
}
|
|
else
|
|
{
|
|
FatalErrorInFunction
|
|
<< "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
|
|
|
|
vectorField normalsTmp(normals);
|
|
List<extendedFeatureEdgeMesh::sideVolumeType> normalVolumeTypesTmp
|
|
(
|
|
normalVolumeTypes
|
|
);
|
|
labelListList edgeNormalsTmp(edgeNormals.size());
|
|
forAll(edgeNormalsTmp, i)
|
|
{
|
|
edgeNormalsTmp[i] = edgeNormals[i];
|
|
}
|
|
labelListList normalDirectionsTmp(normalDirections.size());
|
|
forAll(normalDirectionsTmp, i)
|
|
{
|
|
normalDirectionsTmp[i] = normalDirections[i];
|
|
}
|
|
|
|
calcFeaturePoints(inter.cutPoints(), inter.cutEdges());
|
|
|
|
extendedFeatureEdgeMesh feMesh
|
|
(
|
|
IOobject
|
|
(
|
|
sFeatFileName + ".extendedFeatureEdgeMesh",
|
|
runTime.constant(),
|
|
"extendedFeatureEdgeMesh",
|
|
runTime,
|
|
IOobject::NO_READ,
|
|
IOobject::NO_WRITE
|
|
),
|
|
inter.cutPoints(),
|
|
inter.cutEdges(),
|
|
|
|
0, // concaveStart,
|
|
0, // mixedStart,
|
|
0, // nonFeatureStart,
|
|
|
|
internalStart, // internalStart,
|
|
nIntOrExt, // flatStart,
|
|
nIntOrExt + nFlat, // openStart,
|
|
nIntOrExt + nFlat + nOpen, // multipleStart,
|
|
|
|
normalsTmp,
|
|
normalVolumeTypesTmp,
|
|
edgeDirections,
|
|
normalDirectionsTmp,
|
|
edgeNormalsTmp,
|
|
|
|
labelListList(0), // featurePointNormals,
|
|
labelListList(0), // featurePointEdges,
|
|
labelList(0) // regionEdges
|
|
);
|
|
|
|
feMesh.write();
|
|
|
|
feMesh.writeObj(feMesh.path()/sFeatFileName);
|
|
|
|
{
|
|
// Write a featureEdgeMesh for backwards compatibility
|
|
featureEdgeMesh bfeMesh
|
|
(
|
|
IOobject
|
|
(
|
|
sFeatFileName + ".eMesh",
|
|
runTime.constant(),
|
|
searchableSurface::geometryDir(runTime),
|
|
runTime,
|
|
IOobject::NO_READ,
|
|
IOobject::NO_WRITE,
|
|
false
|
|
),
|
|
feMesh.points(),
|
|
feMesh.edges()
|
|
);
|
|
|
|
Info<< nl << "Writing featureEdgeMesh to "
|
|
<< bfeMesh.relativeObjectPath() << endl;
|
|
|
|
bfeMesh.regIOobject::write();
|
|
}
|
|
|
|
Info << "End\n" << endl;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// ************************************************************************* //
|