587 lines
16 KiB
C
587 lines
16 KiB
C
/*---------------------------------------------------------------------------*\
|
|
========= |
|
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
|
\\ / O peration |
|
|
\\ / A nd | www.openfoam.com
|
|
\\/ M anipulation |
|
|
-------------------------------------------------------------------------------
|
|
Copyright (C) 2014-2017 OpenFOAM Foundation
|
|
Copyright (C) 2020-2022 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 <http://www.gnu.org/licenses/>.
|
|
|
|
Application
|
|
surfaceHookUp
|
|
|
|
Group
|
|
grpSurfaceUtilities
|
|
|
|
Description
|
|
Find close open edges and stitches the surface along them
|
|
|
|
Usage
|
|
- surfaceHookUp hookDistance [OPTION]
|
|
|
|
\*---------------------------------------------------------------------------*/
|
|
|
|
#include "argList.H"
|
|
#include "Time.H"
|
|
|
|
#include "triSurfaceMesh.H"
|
|
#include "indexedOctree.H"
|
|
#include "treeBoundBox.H"
|
|
#include "bitSet.H"
|
|
#include "unitConversion.H"
|
|
#include "searchableSurfaces.H"
|
|
#include "IOdictionary.H"
|
|
|
|
using namespace Foam;
|
|
|
|
// Split facei along edgeI at position newPointi
|
|
void greenRefine
|
|
(
|
|
const triSurface& surf,
|
|
const label facei,
|
|
const label edgeI,
|
|
const label newPointi,
|
|
DynamicList<labelledTri>& newFaces
|
|
)
|
|
{
|
|
const labelledTri& f = surf.localFaces()[facei];
|
|
const edge& e = surf.edges()[edgeI];
|
|
|
|
// Find index of edge in face.
|
|
|
|
label fp0 = f.find(e[0]);
|
|
label fp1 = f.fcIndex(fp0);
|
|
label fp2 = f.fcIndex(fp1);
|
|
|
|
if (f[fp1] == e[1])
|
|
{
|
|
// Edge oriented like face
|
|
newFaces.append
|
|
(
|
|
labelledTri
|
|
(
|
|
f[fp0],
|
|
newPointi,
|
|
f[fp2],
|
|
f.region()
|
|
)
|
|
);
|
|
newFaces.append
|
|
(
|
|
labelledTri
|
|
(
|
|
newPointi,
|
|
f[fp1],
|
|
f[fp2],
|
|
f.region()
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
newFaces.append
|
|
(
|
|
labelledTri
|
|
(
|
|
f[fp2],
|
|
newPointi,
|
|
f[fp1],
|
|
f.region()
|
|
)
|
|
);
|
|
newFaces.append
|
|
(
|
|
labelledTri
|
|
(
|
|
newPointi,
|
|
f[fp0],
|
|
f[fp1],
|
|
f.region()
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
//scalar checkEdgeAngle
|
|
//(
|
|
// const triSurface& surf,
|
|
// const label edgeIndex,
|
|
// const label pointIndex,
|
|
// const scalar& angle
|
|
//)
|
|
//{
|
|
// const edge& e = surf.edges()[edgeIndex];
|
|
|
|
// const vector eVec = e.unitVec(surf.localPoints());
|
|
|
|
// const labelList& pEdges = surf.pointEdges()[pointIndex];
|
|
//
|
|
// forAll(pEdges, eI)
|
|
// {
|
|
// const edge& nearE = surf.edges()[pEdges[eI]];
|
|
|
|
// const vector nearEVec = nearE.unitVec(surf.localPoints());
|
|
|
|
// const scalar dot = eVec & nearEVec;
|
|
// const scalar minCos = degToRad(angle);
|
|
|
|
// if (mag(dot) > minCos)
|
|
// {
|
|
// return false;
|
|
// }
|
|
// }
|
|
|
|
// return true;
|
|
//}
|
|
|
|
|
|
void createBoundaryEdgeTrees
|
|
(
|
|
const PtrList<triSurfaceMesh>& surfs,
|
|
PtrList<indexedOctree<treeDataEdge>>& bEdgeTrees,
|
|
labelListList& treeBoundaryEdges
|
|
)
|
|
{
|
|
forAll(surfs, surfI)
|
|
{
|
|
const triSurface& surf = surfs[surfI];
|
|
|
|
// Boundary edges
|
|
treeBoundaryEdges[surfI] =
|
|
identity(surf.nBoundaryEdges(), surf.nInternalEdges());
|
|
|
|
Random rndGen(17301893);
|
|
|
|
// Slightly extended bb. Slightly off-centred just so on symmetric
|
|
// geometry there are less face/edge aligned items.
|
|
treeBoundBox bb
|
|
(
|
|
treeBoundBox(surf.localPoints()).extend(rndGen, 1e-4, ROOTVSMALL)
|
|
);
|
|
|
|
bEdgeTrees.set
|
|
(
|
|
surfI,
|
|
new indexedOctree<treeDataEdge>
|
|
(
|
|
treeDataEdge
|
|
(
|
|
surf.edges(),
|
|
surf.localPoints(),
|
|
treeBoundaryEdges[surfI] // selected edges
|
|
),
|
|
bb, // bb
|
|
8, // maxLevel
|
|
10, // leafsize
|
|
3.0 // duplicity
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
class findNearestOpSubset
|
|
{
|
|
const indexedOctree<treeDataEdge>& tree_;
|
|
|
|
DynamicList<label>& shapeMask_;
|
|
|
|
public:
|
|
|
|
findNearestOpSubset
|
|
(
|
|
const indexedOctree<treeDataEdge>& tree,
|
|
DynamicList<label>& shapeMask
|
|
)
|
|
:
|
|
tree_(tree),
|
|
shapeMask_(shapeMask)
|
|
{}
|
|
|
|
void operator()
|
|
(
|
|
const labelUList& indices,
|
|
const point& sample,
|
|
|
|
scalar& nearestDistSqr,
|
|
label& minIndex,
|
|
point& nearestPoint
|
|
) const
|
|
{
|
|
const treeDataEdge& shape = tree_.shapes();
|
|
|
|
for (const label index : indices)
|
|
{
|
|
const label edgeIndex = shape.objectIndex(index);
|
|
|
|
if (shapeMask_.found(edgeIndex))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
pointHit nearHit = shape.line(index).nearestDist(sample);
|
|
|
|
// Only register hit if closest point is not an edge point
|
|
if (nearHit.hit())
|
|
{
|
|
const scalar distSqr = sqr(nearHit.distance());
|
|
|
|
if (distSqr < nearestDistSqr)
|
|
{
|
|
nearestDistSqr = distSqr;
|
|
minIndex = index;
|
|
nearestPoint = nearHit.point();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
argList::addNote
|
|
(
|
|
"Hook surfaces to other surfaces by moving and retriangulating their"
|
|
" boundary edges to match other surface boundary edges"
|
|
);
|
|
argList::noParallel();
|
|
argList::addArgument("hookTolerance", "The point merge tolerance");
|
|
argList::addOption("dict", "file", "Alternative surfaceHookUpDict");
|
|
|
|
#include "setRootCase.H"
|
|
#include "createTime.H"
|
|
|
|
const word dictName("surfaceHookUpDict");
|
|
#include "setSystemRunTimeDictionaryIO.H"
|
|
|
|
Info<< "Reading " << dictIO.name() << nl << endl;
|
|
|
|
const IOdictionary dict(dictIO);
|
|
|
|
const scalar dist(args.get<scalar>(1));
|
|
const scalar matchTolerance(max(1e-6*dist, SMALL));
|
|
const label maxIters = 100;
|
|
|
|
Info<< "Hooking distance = " << dist << endl;
|
|
|
|
searchableSurfaces surfs
|
|
(
|
|
IOobject
|
|
(
|
|
"surfacesToHook",
|
|
runTime.constant(),
|
|
"triSurface",
|
|
runTime
|
|
),
|
|
dict,
|
|
true // assume single-region names get surface name
|
|
);
|
|
|
|
Info<< nl << "Reading surfaces: " << endl;
|
|
forAll(surfs, surfI)
|
|
{
|
|
Info<< incrIndent;
|
|
Info<< nl << indent << "Surface = " << surfs.names()[surfI] << endl;
|
|
|
|
const wordList& regions = surfs[surfI].regions();
|
|
forAll(regions, surfRegionI)
|
|
{
|
|
Info<< incrIndent;
|
|
Info<< indent << "Regions = " << regions[surfRegionI] << endl;
|
|
Info<< decrIndent;
|
|
}
|
|
Info<< decrIndent;
|
|
}
|
|
|
|
PtrList<indexedOctree<treeDataEdge>> bEdgeTrees(surfs.size());
|
|
labelListList treeBoundaryEdges(surfs.size());
|
|
|
|
List<DynamicList<labelledTri>> newFaces(surfs.size());
|
|
List<DynamicList<point>> newPoints(surfs.size());
|
|
List<bitSet> visitedFace(surfs.size());
|
|
|
|
PtrList<triSurfaceMesh> newSurfaces(surfs.size());
|
|
forAll(surfs, surfI)
|
|
{
|
|
const triSurfaceMesh& surf =
|
|
refCast<const triSurfaceMesh>(surfs[surfI]);
|
|
|
|
newSurfaces.set
|
|
(
|
|
surfI,
|
|
new triSurfaceMesh
|
|
(
|
|
IOobject
|
|
(
|
|
"hookedSurface_" + surfs.names()[surfI],
|
|
runTime.constant(),
|
|
"triSurface",
|
|
runTime
|
|
),
|
|
surf
|
|
)
|
|
);
|
|
}
|
|
|
|
label nChanged = 0;
|
|
label nIters = 1;
|
|
|
|
do
|
|
{
|
|
Info<< nl << "Iteration = " << nIters++ << endl;
|
|
nChanged = 0;
|
|
|
|
createBoundaryEdgeTrees(newSurfaces, bEdgeTrees, treeBoundaryEdges);
|
|
|
|
forAll(newSurfaces, surfI)
|
|
{
|
|
const triSurface& newSurf = newSurfaces[surfI];
|
|
|
|
newFaces[surfI] = newSurf.localFaces();
|
|
newPoints[surfI] = newSurf.localPoints();
|
|
visitedFace[surfI] = bitSet(newSurf.size(), false);
|
|
}
|
|
|
|
forAll(newSurfaces, surfI)
|
|
{
|
|
const triSurface& surf = newSurfaces[surfI];
|
|
|
|
List<pointIndexHit> bPointsTobEdges(surf.boundaryPoints().size());
|
|
labelList bPointsHitTree(surf.boundaryPoints().size(), -1);
|
|
|
|
const labelListList& pointEdges = surf.pointEdges();
|
|
|
|
forAll(bPointsTobEdges, bPointi)
|
|
{
|
|
pointIndexHit& nearestHit = bPointsTobEdges[bPointi];
|
|
|
|
const label pointi = surf.boundaryPoints()[bPointi];
|
|
const point& samplePt = surf.localPoints()[pointi];
|
|
|
|
const labelList& pEdges = pointEdges[pointi];
|
|
|
|
// Add edges connected to the edge to the shapeMask
|
|
DynamicList<label> shapeMask;
|
|
shapeMask.append(pEdges);
|
|
|
|
forAll(bEdgeTrees, treeI)
|
|
{
|
|
const indexedOctree<treeDataEdge>& bEdgeTree =
|
|
bEdgeTrees[treeI];
|
|
|
|
pointIndexHit currentHit =
|
|
bEdgeTree.findNearest
|
|
(
|
|
samplePt,
|
|
sqr(dist),
|
|
findNearestOpSubset
|
|
(
|
|
bEdgeTree,
|
|
shapeMask
|
|
)
|
|
);
|
|
|
|
if
|
|
(
|
|
currentHit.hit()
|
|
&&
|
|
(
|
|
!nearestHit.hit()
|
|
||
|
|
(
|
|
currentHit.point().distSqr(samplePt)
|
|
< nearestHit.point().distSqr(samplePt)
|
|
)
|
|
)
|
|
)
|
|
{
|
|
nearestHit = currentHit;
|
|
bPointsHitTree[bPointi] = treeI;
|
|
}
|
|
}
|
|
|
|
if (nearestHit.hit())
|
|
{
|
|
// bool rejectEdge =
|
|
// checkEdgeAngle
|
|
// (
|
|
// surf,
|
|
// nearestHit.index(),
|
|
// pointi,
|
|
// 30
|
|
// );
|
|
|
|
scalar distSqr = nearestHit.point().distSqr(samplePt);
|
|
|
|
if (distSqr > Foam::sqr(dist))
|
|
{
|
|
nearestHit.setMiss();
|
|
}
|
|
}
|
|
}
|
|
|
|
forAll(bPointsTobEdges, bPointi)
|
|
{
|
|
const pointIndexHit& eHit = bPointsTobEdges[bPointi];
|
|
|
|
if (eHit.hit())
|
|
{
|
|
const label hitSurfI = bPointsHitTree[bPointi];
|
|
const triSurface& hitSurf = newSurfaces[hitSurfI];
|
|
|
|
const label eIndex =
|
|
treeBoundaryEdges[hitSurfI][eHit.index()];
|
|
const edge& e = hitSurf.edges()[eIndex];
|
|
|
|
const label pointi = surf.boundaryPoints()[bPointi];
|
|
|
|
const labelList& eFaces = hitSurf.edgeFaces()[eIndex];
|
|
|
|
if (eFaces.size() != 1)
|
|
{
|
|
WarningInFunction
|
|
<< "Edge is attached to " << eFaces.size()
|
|
<< " faces." << endl;
|
|
|
|
continue;
|
|
}
|
|
|
|
const label facei = eFaces[0];
|
|
|
|
if (visitedFace[hitSurfI][facei])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
DynamicList<labelledTri> newFacesFromSplit(2);
|
|
|
|
const point& pt = surf.localPoints()[pointi];
|
|
|
|
if
|
|
(
|
|
(
|
|
pt.distSqr(hitSurf.localPoints()[e.start()])
|
|
< matchTolerance
|
|
)
|
|
|| (
|
|
pt.distSqr(hitSurf.localPoints()[e.end()])
|
|
< matchTolerance
|
|
)
|
|
)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
nChanged++;
|
|
|
|
label newPointi = -1;
|
|
|
|
// Keep the points in the same place and move the edge
|
|
if (hitSurfI == surfI)
|
|
{
|
|
newPointi = pointi;
|
|
}
|
|
else
|
|
{
|
|
newPoints[hitSurfI].append(newPoints[surfI][pointi]);
|
|
newPointi = newPoints[hitSurfI].size() - 1;
|
|
}
|
|
|
|
// Split the other face.
|
|
greenRefine
|
|
(
|
|
hitSurf,
|
|
facei,
|
|
eIndex,
|
|
newPointi,
|
|
newFacesFromSplit
|
|
);
|
|
|
|
visitedFace[hitSurfI].set(facei);
|
|
|
|
forAll(newFacesFromSplit, newFacei)
|
|
{
|
|
const labelledTri& fN = newFacesFromSplit[newFacei];
|
|
|
|
if (newFacei == 0)
|
|
{
|
|
newFaces[hitSurfI][facei] = fN;
|
|
}
|
|
else
|
|
{
|
|
newFaces[hitSurfI].append(fN);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Info<< " Number of edges split = " << nChanged << endl;
|
|
|
|
forAll(newSurfaces, surfI)
|
|
{
|
|
newSurfaces.set
|
|
(
|
|
surfI,
|
|
new triSurfaceMesh
|
|
(
|
|
IOobject
|
|
(
|
|
"hookedSurface_" + surfs.names()[surfI],
|
|
runTime.constant(),
|
|
"triSurface",
|
|
runTime
|
|
),
|
|
triSurface
|
|
(
|
|
newFaces[surfI],
|
|
newSurfaces[surfI].patches(),
|
|
pointField(newPoints[surfI])
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
} while (nChanged > 0 && nIters <= maxIters);
|
|
|
|
Info<< endl;
|
|
|
|
forAll(newSurfaces, surfI)
|
|
{
|
|
const triSurfaceMesh& newSurf = newSurfaces[surfI];
|
|
|
|
Info<< "Writing hooked surface " << newSurf.searchableSurface::name()
|
|
<< endl;
|
|
|
|
newSurf.searchableSurface::write();
|
|
}
|
|
|
|
Info<< "\nEnd\n" << endl;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// ************************************************************************* //
|