Files
openfoam/src/sampling/sampledSurface/isoSurface/isoSurfaceCell.C
2015-11-26 11:53:16 +00:00

1653 lines
43 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2015 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/>.
\*---------------------------------------------------------------------------*/
#include "isoSurfaceCell.H"
#include "dictionary.H"
#include "polyMesh.H"
#include "mergePoints.H"
#include "tetMatcher.H"
#include "syncTools.H"
#include "addToRunTimeSelectionTable.H"
#include "Time.H"
#include "triPoints.H"
#include "isoSurface.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(isoSurfaceCell, 0);
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::scalar Foam::isoSurfaceCell::isoFraction
(
const scalar s0,
const scalar s1
) const
{
scalar d = s1-s0;
if (mag(d) > VSMALL)
{
return (iso_-s0)/d;
}
else
{
return -1.0;
}
}
bool Foam::isoSurfaceCell::isTriCut
(
const triFace& tri,
const scalarField& pointValues
) const
{
bool aLower = (pointValues[tri[0]] < iso_);
bool bLower = (pointValues[tri[1]] < iso_);
bool cLower = (pointValues[tri[2]] < iso_);
return !(aLower == bLower && aLower == cLower);
}
Foam::isoSurfaceCell::cellCutType Foam::isoSurfaceCell::calcCutType
(
const PackedBoolList& isTet,
const scalarField& cellValues,
const scalarField& pointValues,
const label cellI
) const
{
const cell& cFaces = mesh_.cells()[cellI];
if (isTet.get(cellI) == 1)
{
forAll(cFaces, cFaceI)
{
const face& f = mesh_.faces()[cFaces[cFaceI]];
for (label fp = 1; fp < f.size() - 1; fp++)
{
triFace tri(f[0], f[fp], f[f.fcIndex(fp)]);
if (isTriCut(tri, pointValues))
{
return CUT;
}
}
}
return NOTCUT;
}
else
{
bool cellLower = (cellValues[cellI] < iso_);
// First check if there is any cut in cell
bool edgeCut = false;
forAll(cFaces, cFaceI)
{
label faceI = cFaces[cFaceI];
const face& f = mesh_.faces()[faceI];
// Check pyramids cut
forAll(f, fp)
{
if ((pointValues[f[fp]] < iso_) != cellLower)
{
edgeCut = true;
break;
}
}
if (edgeCut)
{
break;
}
const label fp0 = mesh_.tetBasePtIs()[faceI];
label fp = f.fcIndex(fp0);
for (label i = 2; i < f.size(); i++)
{
label nextFp = f.fcIndex(fp);
if (isTriCut(triFace(f[fp0], f[fp], f[nextFp]), pointValues))
{
edgeCut = true;
break;
}
fp = nextFp;
}
if (edgeCut)
{
break;
}
}
if (edgeCut)
{
// Count actual cuts (expensive since addressing needed)
// Note: not needed if you don't want to preserve maxima/minima
// centred around cellcentre. In that case just always return CUT
const labelList& cPoints = mesh_.cellPoints(cellI);
label nPyrCuts = 0;
forAll(cPoints, i)
{
if ((pointValues[cPoints[i]] < iso_) != cellLower)
{
nPyrCuts++;
}
}
if (nPyrCuts == cPoints.size())
{
return SPHERE;
}
else
{
return CUT;
}
}
else
{
return NOTCUT;
}
}
}
void Foam::isoSurfaceCell::calcCutTypes
(
const PackedBoolList& isTet,
const scalarField& cVals,
const scalarField& pVals
)
{
cellCutType_.setSize(mesh_.nCells());
nCutCells_ = 0;
forAll(mesh_.cells(), cellI)
{
cellCutType_[cellI] = calcCutType(isTet, cVals, pVals, cellI);
if (cellCutType_[cellI] == CUT)
{
nCutCells_++;
}
}
if (debug)
{
Pout<< "isoSurfaceCell : detected " << nCutCells_
<< " candidate cut cells." << endl;
}
}
// Return the two common points between two triangles
Foam::labelPair Foam::isoSurfaceCell::findCommonPoints
(
const labelledTri& tri0,
const labelledTri& tri1
)
{
labelPair common(-1, -1);
label fp0 = 0;
label fp1 = findIndex(tri1, tri0[fp0]);
if (fp1 == -1)
{
fp0 = 1;
fp1 = findIndex(tri1, tri0[fp0]);
}
if (fp1 != -1)
{
// So tri0[fp0] is tri1[fp1]
// Find next common point
label fp0p1 = tri0.fcIndex(fp0);
label fp1p1 = tri1.fcIndex(fp1);
label fp1m1 = tri1.rcIndex(fp1);
if (tri0[fp0p1] == tri1[fp1p1] || tri0[fp0p1] == tri1[fp1m1])
{
common[0] = tri0[fp0];
common[1] = tri0[fp0p1];
}
}
return common;
}
// Caculate centre of surface.
Foam::point Foam::isoSurfaceCell::calcCentre(const triSurface& s)
{
vector sum = vector::zero;
forAll(s, i)
{
sum += s[i].centre(s.points());
}
return sum/s.size();
}
// Replace surface (localPoints, localTris) with single point. Returns
// point. Destructs arguments.
Foam::pointIndexHit Foam::isoSurfaceCell::collapseSurface
(
const label cellI,
pointField& localPoints,
DynamicList<labelledTri, 64>& localTris
) const
{
pointIndexHit info(false, vector::zero, localTris.size());
if (localTris.size() == 1)
{
const labelledTri& tri = localTris[0];
info.setPoint(tri.centre(localPoints));
info.setHit();
}
else if (localTris.size() == 2)
{
// Check if the two triangles share an edge.
const labelledTri& tri0 = localTris[0];
const labelledTri& tri1 = localTris[1];
labelPair shared = findCommonPoints(tri0, tri1);
if (shared[0] != -1)
{
vector n0 = tri0.normal(localPoints);
n0 /= mag(n0);
vector n1 = tri1.normal(localPoints);
n1 /= mag(n1);
if ((n0 & n1) < 0)
{
// Too big an angle. Do not simplify.
}
else
{
info.setPoint
(
0.5
* (
tri0.centre(localPoints)
+ tri1.centre(localPoints)
)
);
info.setHit();
}
}
}
else if (localTris.size())
{
// Check if single region. Rare situation.
triSurface surf
(
localTris,
geometricSurfacePatchList(0),
localPoints,
true
);
localTris.clearStorage();
labelList faceZone;
label nZones = surf.markZones
(
boolList(surf.nEdges(), false),
faceZone
);
if (nZones == 1)
{
// Check that all normals make a decent angle
scalar minCos = GREAT;
const vector& n0 = surf.faceNormals()[0];
for (label i = 1; i < surf.size(); i++)
{
scalar cosAngle = (n0 & surf.faceNormals()[i]);
if (cosAngle < minCos)
{
minCos = cosAngle;
}
}
if (minCos > 0)
{
info.setPoint(calcCentre(surf));
info.setHit();
}
}
}
return info;
}
void Foam::isoSurfaceCell::calcSnappedCc
(
const PackedBoolList& isTet,
const scalarField& cVals,
const scalarField& pVals,
DynamicList<point>& snappedPoints,
labelList& snappedCc
) const
{
const pointField& cc = mesh_.cellCentres();
const pointField& pts = mesh_.points();
snappedCc.setSize(mesh_.nCells());
snappedCc = -1;
// Work arrays
DynamicList<point, 64> localPoints(64);
DynamicList<labelledTri, 64> localTris(64);
Map<label> pointToLocal(64);
forAll(mesh_.cells(), cellI)
{
if (cellCutType_[cellI] == CUT && isTet.get(cellI) == 0)
{
scalar cVal = cVals[cellI];
const cell& cFaces = mesh_.cells()[cellI];
localPoints.clear();
localTris.clear();
pointToLocal.clear();
// Create points for all intersections close to cell centre
// (i.e. from pyramid edges)
forAll(cFaces, cFaceI)
{
const face& f = mesh_.faces()[cFaces[cFaceI]];
forAll(f, fp)
{
label pointI = f[fp];
scalar s = isoFraction(cVal, pVals[pointI]);
if (s >= 0.0 && s <= 0.5)
{
if (pointToLocal.insert(pointI, localPoints.size()))
{
localPoints.append((1.0-s)*cc[cellI]+s*pts[pointI]);
}
}
}
}
if (localPoints.size() == 1)
{
// No need for any analysis.
snappedCc[cellI] = snappedPoints.size();
snappedPoints.append(localPoints[0]);
//Pout<< "cell:" << cellI
// << " at " << mesh_.cellCentres()[cellI]
// << " collapsing " << localPoints
// << " intersections down to "
// << snappedPoints[snappedCc[cellI]] << endl;
}
else if (localPoints.size() == 2)
{
//? No need for any analysis.???
snappedCc[cellI] = snappedPoints.size();
snappedPoints.append(0.5*(localPoints[0]+localPoints[1]));
//Pout<< "cell:" << cellI
// << " at " << mesh_.cellCentres()[cellI]
// << " collapsing " << localPoints
// << " intersections down to "
// << snappedPoints[snappedCc[cellI]] << endl;
}
else if (localPoints.size())
{
// Need to analyse
forAll(cFaces, cFaceI)
{
label faceI = cFaces[cFaceI];
const face& f = mesh_.faces()[faceI];
// Do a tetrahedralisation. Each face to cc becomes pyr.
// Each pyr gets split into tets by diagonalisation
// of face.
const label fp0 = mesh_.tetBasePtIs()[faceI];
label fp = f.fcIndex(fp0);
for (label i = 2; i < f.size(); i++)
{
label nextFp = f.fcIndex(fp);
triFace tri(f[fp0], f[fp], f[nextFp]);
// Get fractions for the three edges to cell centre
FixedList<scalar, 3> s(3);
s[0] = isoFraction(cVal, pVals[tri[0]]);
s[1] = isoFraction(cVal, pVals[tri[1]]);
s[2] = isoFraction(cVal, pVals[tri[2]]);
if
(
(s[0] >= 0.0 && s[0] <= 0.5)
&& (s[1] >= 0.0 && s[1] <= 0.5)
&& (s[2] >= 0.0 && s[2] <= 0.5)
)
{
if
(
(mesh_.faceOwner()[faceI] == cellI)
== (cVal >= pVals[tri[0]])
)
{
localTris.append
(
labelledTri
(
pointToLocal[tri[1]],
pointToLocal[tri[0]],
pointToLocal[tri[2]],
0
)
);
}
else
{
localTris.append
(
labelledTri
(
pointToLocal[tri[0]],
pointToLocal[tri[1]],
pointToLocal[tri[2]],
0
)
);
}
}
fp = nextFp;
}
}
pointField surfPoints;
surfPoints.transfer(localPoints);
pointIndexHit info = collapseSurface
(
cellI,
surfPoints,
localTris
);
if (info.hit())
{
snappedCc[cellI] = snappedPoints.size();
snappedPoints.append(info.hitPoint());
//Pout<< "cell:" << cellI
// << " at " << mesh_.cellCentres()[cellI]
// << " collapsing " << surfPoints
// << " intersections down to "
// << snappedPoints[snappedCc[cellI]] << endl;
}
}
}
}
}
// Generate triangles for face connected to pointI
void Foam::isoSurfaceCell::genPointTris
(
const scalarField& cellValues,
const scalarField& pointValues,
const label pointI,
const label faceI,
const label cellI,
DynamicList<point, 64>& localTriPoints
) const
{
const pointField& cc = mesh_.cellCentres();
const pointField& pts = mesh_.points();
const face& f = mesh_.faces()[faceI];
const label fp0 = mesh_.tetBasePtIs()[faceI];
label fp = f.fcIndex(fp0);
for (label i = 2; i < f.size(); i++)
{
label nextFp = f.fcIndex(fp);
triFace tri(f[fp0], f[fp], f[nextFp]);
label index = findIndex(tri, pointI);
if (index == -1)
{
continue;
}
// Tet between index..index-1, index..index+1, index..cc
label b = tri[tri.fcIndex(index)];
label c = tri[tri.rcIndex(index)];
// Get fractions for the three edges emanating from point
FixedList<scalar, 3> s(3);
s[0] = isoFraction(pointValues[pointI], pointValues[b]);
s[1] = isoFraction(pointValues[pointI], pointValues[c]);
s[2] = isoFraction(pointValues[pointI], cellValues[cellI]);
if
(
(s[0] >= 0.0 && s[0] <= 0.5)
&& (s[1] >= 0.0 && s[1] <= 0.5)
&& (s[2] >= 0.0 && s[2] <= 0.5)
)
{
point p0 = (1.0-s[0])*pts[pointI] + s[0]*pts[b];
point p1 = (1.0-s[1])*pts[pointI] + s[1]*pts[c];
point p2 = (1.0-s[2])*pts[pointI] + s[2]*cc[cellI];
if
(
(mesh_.faceOwner()[faceI] == cellI)
== (pointValues[pointI] > cellValues[cellI])
)
{
localTriPoints.append(p0);
localTriPoints.append(p1);
localTriPoints.append(p2);
}
else
{
localTriPoints.append(p1);
localTriPoints.append(p0);
localTriPoints.append(p2);
}
}
fp = nextFp;
}
}
// Generate triangle for tet connected to pointI
void Foam::isoSurfaceCell::genPointTris
(
const scalarField& pointValues,
const label pointI,
const label faceI,
const label cellI,
DynamicList<point, 64>& localTriPoints
) const
{
const pointField& pts = mesh_.points();
const cell& cFaces = mesh_.cells()[cellI];
FixedList<label, 4> tet;
// Make tet from this face to the 4th point (same as cellcentre in
// non-tet cells)
const face& f = mesh_.faces()[faceI];
// Find 4th point
label ccPointI = -1;
forAll(cFaces, cFaceI)
{
const face& f1 = mesh_.faces()[cFaces[cFaceI]];
forAll(f1, fp)
{
label p1 = f1[fp];
if (findIndex(f, p1) == -1)
{
ccPointI = p1;
break;
}
}
if (ccPointI != -1)
{
break;
}
}
// Tet between index..index-1, index..index+1, index..cc
label index = findIndex(f, pointI);
label b = f[f.fcIndex(index)];
label c = f[f.rcIndex(index)];
//Pout<< " p0:" << pointI << " b:" << b << " c:" << c
//<< " d:" << ccPointI << endl;
// Get fractions for the three edges emanating from point
FixedList<scalar, 3> s(3);
s[0] = isoFraction(pointValues[pointI], pointValues[b]);
s[1] = isoFraction(pointValues[pointI], pointValues[c]);
s[2] = isoFraction(pointValues[pointI], pointValues[ccPointI]);
if
(
(s[0] >= 0.0 && s[0] <= 0.5)
&& (s[1] >= 0.0 && s[1] <= 0.5)
&& (s[2] >= 0.0 && s[2] <= 0.5)
)
{
point p0 = (1.0-s[0])*pts[pointI] + s[0]*pts[b];
point p1 = (1.0-s[1])*pts[pointI] + s[1]*pts[c];
point p2 = (1.0-s[2])*pts[pointI] + s[2]*pts[ccPointI];
if (mesh_.faceOwner()[faceI] != cellI)
{
localTriPoints.append(p0);
localTriPoints.append(p1);
localTriPoints.append(p2);
}
else
{
localTriPoints.append(p1);
localTriPoints.append(p0);
localTriPoints.append(p2);
}
}
}
void Foam::isoSurfaceCell::calcSnappedPoint
(
const PackedBoolList& isTet,
const scalarField& cVals,
const scalarField& pVals,
DynamicList<point>& snappedPoints,
labelList& snappedPoint
) const
{
// Determine if point is on boundary. Points on boundaries are never
// snapped. Coupled boundaries are handled explicitly so not marked here.
PackedBoolList isBoundaryPoint(mesh_.nPoints());
const polyBoundaryMesh& patches = mesh_.boundaryMesh();
forAll(patches, patchI)
{
const polyPatch& pp = patches[patchI];
if (!pp.coupled())
{
label faceI = pp.start();
forAll(pp, i)
{
const face& f = mesh_.faces()[faceI++];
forAll(f, fp)
{
isBoundaryPoint.set(f[fp], 1);
}
}
}
}
const point greatPoint(GREAT, GREAT, GREAT);
pointField collapsedPoint(mesh_.nPoints(), greatPoint);
// Work arrays
DynamicList<point, 64> localTriPoints(100);
labelHashSet localPointCells(100);
forAll(mesh_.pointFaces(), pointI)
{
if (isBoundaryPoint.get(pointI) == 1)
{
continue;
}
const labelList& pFaces = mesh_.pointFaces()[pointI];
bool anyCut = false;
forAll(pFaces, i)
{
label faceI = pFaces[i];
if
(
cellCutType_[mesh_.faceOwner()[faceI]] == CUT
|| (
mesh_.isInternalFace(faceI)
&& cellCutType_[mesh_.faceNeighbour()[faceI]] == CUT
)
)
{
anyCut = true;
break;
}
}
if (!anyCut)
{
continue;
}
// Do a pointCells walk (by using pointFaces)
localPointCells.clear();
localTriPoints.clear();
forAll(pFaces, pFaceI)
{
label faceI = pFaces[pFaceI];
label own = mesh_.faceOwner()[faceI];
if (isTet.get(own) == 1)
{
// Since tets have no cell centre to include make sure
// we only generate a triangle once per point.
if (localPointCells.insert(own))
{
genPointTris(pVals, pointI, faceI, own, localTriPoints);
}
}
else
{
genPointTris
(
cVals,
pVals,
pointI,
faceI,
own,
localTriPoints
);
}
if (mesh_.isInternalFace(faceI))
{
label nei = mesh_.faceNeighbour()[faceI];
if (isTet.get(nei) == 1)
{
if (localPointCells.insert(nei))
{
genPointTris(pVals, pointI, faceI, nei, localTriPoints);
}
}
else
{
genPointTris
(
cVals,
pVals,
pointI,
faceI,
nei,
localTriPoints
);
}
}
}
if (localTriPoints.size() == 3)
{
// Single triangle. No need for any analysis. Average points.
pointField points;
points.transfer(localTriPoints);
collapsedPoint[pointI] = sum(points)/points.size();
//Pout<< " point:" << pointI
// << " replacing coord:" << mesh_.points()[pointI]
// << " by average:" << collapsedPoint[pointI] << endl;
}
else if (localTriPoints.size())
{
// Convert points into triSurface.
// Merge points and compact out non-valid triangles
labelList triMap; // merged to unmerged triangle
labelList triPointReverseMap; // unmerged to merged point
triSurface surf
(
stitchTriPoints
(
false, // do not check for duplicate tris
localTriPoints,
triPointReverseMap,
triMap
)
);
labelList faceZone;
label nZones = surf.markZones
(
boolList(surf.nEdges(), false),
faceZone
);
if (nZones == 1)
{
// Check that all normals make a decent angle
scalar minCos = GREAT;
const vector& n0 = surf.faceNormals()[0];
for (label i = 1; i < surf.size(); i++)
{
const vector& n = surf.faceNormals()[i];
scalar cosAngle = (n0 & n);
if (cosAngle < minCos)
{
minCos = cosAngle;
}
}
if (minCos > 0)
{
collapsedPoint[pointI] = calcCentre(surf);
}
}
}
}
syncTools::syncPointPositions
(
mesh_,
collapsedPoint,
minMagSqrEqOp<point>(),
greatPoint
);
snappedPoint.setSize(mesh_.nPoints());
snappedPoint = -1;
forAll(collapsedPoint, pointI)
{
// Cannot do == comparison since might be transformed so have
// truncation errors.
if (magSqr(collapsedPoint[pointI]) < 0.5*magSqr(greatPoint))
{
snappedPoint[pointI] = snappedPoints.size();
snappedPoints.append(collapsedPoint[pointI]);
}
}
}
Foam::triSurface Foam::isoSurfaceCell::stitchTriPoints
(
const bool checkDuplicates,
const List<point>& triPoints,
labelList& triPointReverseMap, // unmerged to merged point
labelList& triMap // merged to unmerged triangle
) const
{
label nTris = triPoints.size()/3;
if ((triPoints.size() % 3) != 0)
{
FatalErrorIn("isoSurfaceCell::stitchTriPoints(..)")
<< "Problem: number of points " << triPoints.size()
<< " not a multiple of 3." << abort(FatalError);
}
pointField newPoints;
mergePoints
(
triPoints,
mergeDistance_,
false,
triPointReverseMap,
newPoints
);
// Check that enough merged.
if (debug)
{
Pout<< "isoSurfaceCell : merged from " << triPoints.size()
<< " points down to " << newPoints.size() << endl;
pointField newNewPoints;
labelList oldToNew;
bool hasMerged = mergePoints
(
newPoints,
mergeDistance_,
true,
oldToNew,
newNewPoints
);
if (hasMerged)
{
FatalErrorIn("isoSurfaceCell::stitchTriPoints(..)")
<< "Merged points contain duplicates"
<< " when merging with distance " << mergeDistance_ << endl
<< "merged:" << newPoints.size() << " re-merged:"
<< newNewPoints.size()
<< abort(FatalError);
}
}
List<labelledTri> tris;
{
DynamicList<labelledTri> dynTris(nTris);
label rawPointI = 0;
DynamicList<label> newToOldTri(nTris);
for (label oldTriI = 0; oldTriI < nTris; oldTriI++)
{
labelledTri tri
(
triPointReverseMap[rawPointI],
triPointReverseMap[rawPointI+1],
triPointReverseMap[rawPointI+2],
0
);
if ((tri[0] != tri[1]) && (tri[0] != tri[2]) && (tri[1] != tri[2]))
{
newToOldTri.append(oldTriI);
dynTris.append(tri);
}
rawPointI += 3;
}
triMap.transfer(newToOldTri);
tris.transfer(dynTris);
}
// Use face centres to determine 'flat hole' situation (see RMT paper).
// Two unconnected triangles get connected because (some of) the edges
// separating them get collapsed. Below only checks for duplicate triangles,
// not non-manifold edge connectivity.
if (checkDuplicates)
{
if (debug)
{
Pout<< "isoSurfaceCell : merged from " << nTris
<< " down to " << tris.size() << " triangles." << endl;
}
pointField centres(tris.size());
forAll(tris, triI)
{
centres[triI] = tris[triI].centre(newPoints);
}
pointField mergedCentres;
labelList oldToMerged;
bool hasMerged = mergePoints
(
centres,
mergeDistance_,
false,
oldToMerged,
mergedCentres
);
if (debug)
{
Pout<< "isoSurfaceCell : detected "
<< centres.size()-mergedCentres.size()
<< " duplicate triangles." << endl;
}
if (hasMerged)
{
// Filter out duplicates.
label newTriI = 0;
DynamicList<label> newToOldTri(tris.size());
labelList newToMaster(mergedCentres.size(), -1);
forAll(tris, triI)
{
label mergedI = oldToMerged[triI];
if (newToMaster[mergedI] == -1)
{
newToMaster[mergedI] = triI;
newToOldTri.append(triMap[triI]);
tris[newTriI++] = tris[triI];
}
}
triMap.transfer(newToOldTri);
tris.setSize(newTriI);
}
}
return triSurface(tris, geometricSurfacePatchList(0), newPoints, true);
}
// Does face use valid vertices?
bool Foam::isoSurfaceCell::validTri(const triSurface& surf, const label faceI)
{
// Simple check on indices ok.
const labelledTri& f = surf[faceI];
forAll(f, fp)
{
if (f[fp] < 0 || f[fp] >= surf.points().size())
{
WarningIn("validTri(const triSurface&, const label)")
<< "triangle " << faceI << " vertices " << f
<< " uses point indices outside point range 0.."
<< surf.points().size()-1 << endl;
return false;
}
}
if ((f[0] == f[1]) || (f[0] == f[2]) || (f[1] == f[2]))
{
WarningIn("validTri(const triSurface&, const label)")
<< "triangle " << faceI
<< " uses non-unique vertices " << f
<< endl;
return false;
}
// duplicate triangle check
const labelList& fFaces = surf.faceFaces()[faceI];
// Check if faceNeighbours use same points as this face.
// Note: discards normal information - sides of baffle are merged.
forAll(fFaces, i)
{
label nbrFaceI = fFaces[i];
if (nbrFaceI <= faceI)
{
// lower numbered faces already checked
continue;
}
const labelledTri& nbrF = surf[nbrFaceI];
if
(
((f[0] == nbrF[0]) || (f[0] == nbrF[1]) || (f[0] == nbrF[2]))
&& ((f[1] == nbrF[0]) || (f[1] == nbrF[1]) || (f[1] == nbrF[2]))
&& ((f[2] == nbrF[0]) || (f[2] == nbrF[1]) || (f[2] == nbrF[2]))
)
{
WarningIn("validTri(const triSurface&, const label)")
<< "triangle " << faceI << " vertices " << f
<< " coords:" << f.points(surf.points())
<< " has the same vertices as triangle " << nbrFaceI
<< " vertices " << nbrF
<< endl;
return false;
}
}
return true;
}
void Foam::isoSurfaceCell::calcAddressing
(
const triSurface& surf,
List<FixedList<label, 3> >& faceEdges,
labelList& edgeFace0,
labelList& edgeFace1,
Map<labelList>& edgeFacesRest
) const
{
const pointField& points = surf.points();
pointField edgeCentres(3*surf.size());
label edgeI = 0;
forAll(surf, triI)
{
const labelledTri& tri = surf[triI];
edgeCentres[edgeI++] = 0.5*(points[tri[0]]+points[tri[1]]);
edgeCentres[edgeI++] = 0.5*(points[tri[1]]+points[tri[2]]);
edgeCentres[edgeI++] = 0.5*(points[tri[2]]+points[tri[0]]);
}
pointField mergedCentres;
labelList oldToMerged;
bool hasMerged = mergePoints
(
edgeCentres,
mergeDistance_,
false,
oldToMerged,
mergedCentres
);
if (debug)
{
Pout<< "isoSurfaceCell : detected "
<< mergedCentres.size()
<< " edges on " << surf.size() << " triangles." << endl;
}
if (!hasMerged)
{
return;
}
// Determine faceEdges
faceEdges.setSize(surf.size());
edgeI = 0;
forAll(surf, triI)
{
faceEdges[triI][0] = oldToMerged[edgeI++];
faceEdges[triI][1] = oldToMerged[edgeI++];
faceEdges[triI][2] = oldToMerged[edgeI++];
}
// Determine edgeFaces
edgeFace0.setSize(mergedCentres.size());
edgeFace0 = -1;
edgeFace1.setSize(mergedCentres.size());
edgeFace1 = -1;
edgeFacesRest.clear();
forAll(oldToMerged, oldEdgeI)
{
label triI = oldEdgeI / 3;
label edgeI = oldToMerged[oldEdgeI];
if (edgeFace0[edgeI] == -1)
{
edgeFace0[edgeI] = triI;
}
else if (edgeFace1[edgeI] == -1)
{
edgeFace1[edgeI] = triI;
}
else
{
//WarningIn("orientSurface(triSurface&)")
// << "Edge " << edgeI << " with centre " << mergedCentres[edgeI]
// << " used by more than two triangles: " << edgeFace0[edgeI]
// << ", "
// << edgeFace1[edgeI] << " and " << triI << endl;
Map<labelList>::iterator iter = edgeFacesRest.find(edgeI);
if (iter != edgeFacesRest.end())
{
labelList& eFaces = iter();
label sz = eFaces.size();
eFaces.setSize(sz+1);
eFaces[sz] = triI;
}
else
{
edgeFacesRest.insert(edgeI, labelList(1, triI));
}
}
}
}
// Checks if triangle is connected through edgeI only.
bool Foam::isoSurfaceCell::danglingTriangle
(
const FixedList<label, 3>& fEdges,
const labelList& edgeFace1
)
{
label nOpen = 0;
forAll(fEdges, i)
{
if (edgeFace1[fEdges[i]] == -1)
{
nOpen++;
}
}
if (nOpen == 1 || nOpen == 2 || nOpen == 3)
{
return true;
}
else
{
return false;
}
}
// Mark triangles to keep. Returns number of dangling triangles.
Foam::label Foam::isoSurfaceCell::markDanglingTriangles
(
const List<FixedList<label, 3> >& faceEdges,
const labelList& edgeFace0,
const labelList& edgeFace1,
const Map<labelList>& edgeFacesRest,
boolList& keepTriangles
)
{
keepTriangles.setSize(faceEdges.size());
keepTriangles = true;
label nDangling = 0;
// Remove any dangling triangles
forAllConstIter(Map<labelList>, edgeFacesRest, iter)
{
// These are all the non-manifold edges. Filter out all triangles
// with only one connected edge (= this edge)
label edgeI = iter.key();
const labelList& otherEdgeFaces = iter();
// Remove all dangling triangles
if (danglingTriangle(faceEdges[edgeFace0[edgeI]], edgeFace1))
{
keepTriangles[edgeFace0[edgeI]] = false;
nDangling++;
}
if (danglingTriangle(faceEdges[edgeFace1[edgeI]], edgeFace1))
{
keepTriangles[edgeFace1[edgeI]] = false;
nDangling++;
}
forAll(otherEdgeFaces, i)
{
label triI = otherEdgeFaces[i];
if (danglingTriangle(faceEdges[triI], edgeFace1))
{
keepTriangles[triI] = false;
nDangling++;
}
}
}
return nDangling;
}
Foam::triSurface Foam::isoSurfaceCell::subsetMesh
(
const triSurface& s,
const labelList& newToOldFaces,
labelList& oldToNewPoints,
labelList& newToOldPoints
)
{
const boolList include
(
createWithValues<boolList>
(
s.size(),
false,
newToOldFaces,
true
)
);
newToOldPoints.setSize(s.points().size());
oldToNewPoints.setSize(s.points().size());
oldToNewPoints = -1;
{
label pointI = 0;
forAll(include, oldFacei)
{
if (include[oldFacei])
{
// Renumber labels for face
const triSurface::FaceType& f = s[oldFacei];
forAll(f, fp)
{
label oldPointI = f[fp];
if (oldToNewPoints[oldPointI] == -1)
{
oldToNewPoints[oldPointI] = pointI;
newToOldPoints[pointI++] = oldPointI;
}
}
}
}
newToOldPoints.setSize(pointI);
}
// Extract points
pointField newPoints(newToOldPoints.size());
forAll(newToOldPoints, i)
{
newPoints[i] = s.points()[newToOldPoints[i]];
}
// Extract faces
List<labelledTri> newTriangles(newToOldFaces.size());
forAll(newToOldFaces, i)
{
// Get old vertex labels
const labelledTri& tri = s[newToOldFaces[i]];
newTriangles[i][0] = oldToNewPoints[tri[0]];
newTriangles[i][1] = oldToNewPoints[tri[1]];
newTriangles[i][2] = oldToNewPoints[tri[2]];
newTriangles[i].region() = tri.region();
}
// Reuse storage.
return triSurface(newTriangles, s.patches(), newPoints, true);
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::isoSurfaceCell::isoSurfaceCell
(
const polyMesh& mesh,
const scalarField& cVals,
const scalarField& pVals,
const scalar iso,
const bool regularise,
const boundBox& bounds,
const scalar mergeTol
)
:
mesh_(mesh),
cVals_(cVals),
pVals_(pVals),
iso_(iso),
bounds_(bounds),
mergeDistance_(mergeTol*mesh.bounds().mag())
{
if (debug)
{
Pout<< "isoSurfaceCell : mergeTol:" << mergeTol
<< " mesh span:" << mesh.bounds().mag()
<< " mergeDistance:" << mergeDistance_ << endl;
}
// Determine if cell is tet
PackedBoolList isTet(mesh_.nCells());
{
tetMatcher tet;
forAll(isTet, cellI)
{
if (tet.isA(mesh_, cellI))
{
isTet.set(cellI, 1);
}
}
}
// Determine if any cut through cell
calcCutTypes(isTet, cVals, pVals);
DynamicList<point> snappedPoints(nCutCells_);
// Per cc -1 or a point inside snappedPoints.
labelList snappedCc;
if (regularise)
{
calcSnappedCc
(
isTet,
cVals,
pVals,
snappedPoints,
snappedCc
);
}
else
{
snappedCc.setSize(mesh_.nCells());
snappedCc = -1;
}
if (debug)
{
Pout<< "isoSurfaceCell : shifted " << snappedPoints.size()
<< " cell centres to intersection." << endl;
}
snappedPoints.shrink();
label nCellSnaps = snappedPoints.size();
// Per point -1 or a point inside snappedPoints.
labelList snappedPoint;
if (regularise)
{
calcSnappedPoint
(
isTet,
cVals,
pVals,
snappedPoints,
snappedPoint
);
}
else
{
snappedPoint.setSize(mesh_.nPoints());
snappedPoint = -1;
}
if (debug)
{
Pout<< "isoSurfaceCell : shifted " << snappedPoints.size()-nCellSnaps
<< " vertices to intersection." << endl;
}
{
DynamicList<point> triPoints(nCutCells_);
DynamicList<label> triMeshCells(nCutCells_);
generateTriPoints
(
cVals,
pVals,
mesh_.cellCentres(),
mesh_.points(),
snappedPoints,
snappedCc,
snappedPoint,
triPoints,
triMeshCells
);
if (debug)
{
Pout<< "isoSurfaceCell : generated " << triMeshCells.size()
<< " unmerged triangles." << endl;
}
label nOldPoints = triPoints.size();
// Trimmed to original triangle
DynamicList<label> trimTriMap;
// Trimmed to original point
labelList trimTriPointMap;
if (bounds_ != boundBox::greatBox)
{
isoSurface::trimToBox
(
treeBoundBox(bounds_),
triPoints, // new points
trimTriMap, // map from (new) triangle to original
trimTriPointMap, // map from (new) point to original
interpolatedPoints_, // labels of newly introduced points
interpolatedOldPoints_, // and their interpolation
interpolationWeights_
);
triMeshCells = labelField(triMeshCells, trimTriMap);
}
// Merge points and compact out non-valid triangles
labelList triMap;
triSurface::operator=
(
stitchTriPoints
(
regularise, // check for duplicate tris
triPoints,
triPointMergeMap_, // unmerged to merged point
triMap // merged to unmerged triangle
)
);
if (debug)
{
Pout<< "isoSurfaceCell : generated " << triMap.size()
<< " merged triangles." << endl;
}
if (bounds_ != boundBox::greatBox)
{
// Adjust interpolatedPoints_
inplaceRenumber(triPointMergeMap_, interpolatedPoints_);
// Adjust triPointMergeMap_
labelList newTriPointMergeMap(nOldPoints, -1);
forAll(trimTriPointMap, trimPointI)
{
label oldPointI = trimTriPointMap[trimPointI];
if (oldPointI >= 0)
{
label pointI = triPointMergeMap_[trimPointI];
if (pointI >= 0)
{
newTriPointMergeMap[oldPointI] = pointI;
}
}
}
triPointMergeMap_.transfer(newTriPointMergeMap);
}
meshCells_.setSize(triMap.size());
forAll(triMap, i)
{
meshCells_[i] = triMeshCells[triMap[i]];
}
}
if (debug)
{
Pout<< "isoSurfaceCell : checking " << size()
<< " triangles for validity." << endl;
forAll(*this, triI)
{
// Copied from surfaceCheck
validTri(*this, triI);
}
}
if (regularise)
{
List<FixedList<label, 3> > faceEdges;
labelList edgeFace0, edgeFace1;
Map<labelList> edgeFacesRest;
while (true)
{
// Calculate addressing
calcAddressing
(
*this,
faceEdges,
edgeFace0,
edgeFace1,
edgeFacesRest
);
// See if any dangling triangles
boolList keepTriangles;
label nDangling = markDanglingTriangles
(
faceEdges,
edgeFace0,
edgeFace1,
edgeFacesRest,
keepTriangles
);
if (debug)
{
Pout<< "isoSurfaceCell : detected " << nDangling
<< " dangling triangles." << endl;
}
if (nDangling == 0)
{
break;
}
// Create face map (new to old)
labelList subsetTriMap(findIndices(keepTriangles, true));
labelList subsetPointMap;
labelList reversePointMap;
triSurface::operator=
(
subsetMesh
(
*this,
subsetTriMap,
reversePointMap,
subsetPointMap
)
);
meshCells_ = labelField(meshCells_, subsetTriMap);
inplaceRenumber(reversePointMap, triPointMergeMap_);
}
}
}
// ************************************************************************* //