diff --git a/src/sampling/surface/isoSurface/isoSurfaceBase.H b/src/sampling/surface/isoSurface/isoSurfaceBase.H index 3e29657bd111202d18d4016a15835396a8515874..a856f06f263e1b763d1a9298e6e423226f2e4ca1 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceBase.H +++ b/src/sampling/surface/isoSurface/isoSurfaceBase.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2019-2020 OpenCFD Ltd. + Copyright (C) 2019-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -127,26 +127,6 @@ protected: // Protected Member Functions - //- Check for tet values above/below given (iso) value - // Result encoded as a single integer - inline static constexpr int getTetCutIndex - ( - const scalar a, - const scalar b, - const scalar c, - const scalar d, - const scalar isoval - ) noexcept - { - return - ( - (a < isoval ? 1 : 0) - | (b < isoval ? 2 : 0) - | (c < isoval ? 4 : 0) - | (d < isoval ? 8 : 0) - ); - } - //- Count the number of cuts matching the mask type // Checks as bitmask or as zero. static label countCutType diff --git a/src/sampling/surface/isoSurface/isoSurfaceParams.C b/src/sampling/surface/isoSurface/isoSurfaceParams.C index cf9494ed5d39d9167e93134ae7c1f568e9923a75..60b003dcb414d76d88f510361c84b945bd76a76e 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceParams.C +++ b/src/sampling/surface/isoSurface/isoSurfaceParams.C @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2020 OpenCFD Ltd. + Copyright (C) 2020-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -52,10 +52,12 @@ const Foam::Enum Foam::isoSurfaceParams::filterNames ({ { filterType::NONE, "none" }, - { filterType::CELL, "cell" }, - { filterType::DIAGCELL, "diagcell" }, { filterType::PARTIAL, "partial" }, { filterType::FULL, "full" }, + { filterType::CLEAN, "clean" }, + + { filterType::CELL, "cell" }, + { filterType::DIAGCELL, "diagcell" }, }); @@ -137,6 +139,7 @@ Foam::isoSurfaceParams::isoSurfaceParams : algo_(algo), filter_(filter), + snap_(true), mergeTol_(1e-6), clipBounds_(boundBox::invertedBox) {} @@ -152,6 +155,7 @@ Foam::isoSurfaceParams::isoSurfaceParams { algo_ = getAlgorithmType(dict, algo_); filter_ = getFilterType(dict, filter_); + snap_ = dict.getOrDefault("snap", true); dict.readIfPresent("mergeTol", mergeTol_); dict.readIfPresent("bounds", clipBounds_); } diff --git a/src/sampling/surface/isoSurface/isoSurfaceParams.H b/src/sampling/surface/isoSurface/isoSurfaceParams.H index 1b794f70595cbcb5efbdb934f996d73bf0687d1b..d73da00c2749d6eb30625bafcd76d64a4af7e265 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceParams.H +++ b/src/sampling/surface/isoSurface/isoSurfaceParams.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2020 OpenCFD Ltd. + Copyright (C) 2020-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -35,12 +35,22 @@ Description isoMethod | Algorithm (cell/topo/point/default) | no | default regularise | Face simplification (enum or bool) | no | true mergeTol | Point merge tolerance (cell/point) | no | 1e-6 + snap | Point snapping (topo) | no | true bounds | Optional clip bounds | no | inverted \endtable The default algorithm denotes the use of the current \em standard algorithm. +Filtering types (for topological iso-surface) + - \c none : leave tet cuts untouched + - \c partial , \c cell : Combine intra-cell faces + - \c full , \c diagcell : Perform \c partial and remove face-diagonal + points + - \c clean : Perform \c full and eliminate open edges as well. + (<b>May cause excessive erosion!</b>) + . + SourceFiles isoSurfaceParams.C @@ -85,8 +95,12 @@ public: NONE = 0, //!< No filtering CELL, //!< Remove pyramid edge points DIAGCELL, //!< Remove pyramid edge points, face-diagonals + NONMANIFOLD, //!< Remove pyramid edge points, face-diagonals + //!< and non-manifold faces + PARTIAL = CELL, //!< Same as CELL FULL = DIAGCELL, //!< Same as DIAGCELL + CLEAN = NONMANIFOLD //!< Same as NONMANIFOLD }; @@ -100,6 +114,9 @@ private: //- Filtering for iso-surface faces/points filterType filter_; + //- Point snapping enabled + bool snap_; + //- Merge tolerance for cell/point (default: 1e-6) scalar mergeTol_; @@ -186,6 +203,18 @@ public: filter_ = fltr; } + //- Get point snapping flag + bool snap() const noexcept + { + return snap_; + } + + //- Set point snapping flag + void snap(bool on) noexcept + { + snap_ = on; + } + //- Get current merge tolerance scalar mergeTol() const noexcept { diff --git a/src/sampling/surface/isoSurface/isoSurfaceTopo.C b/src/sampling/surface/isoSurface/isoSurfaceTopo.C index e8ff86266f7fb65c87ce560f6fd9317f3bfe43d3..c0fe1a80775c351fa8724b5c654e88b9c8b43830 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceTopo.C +++ b/src/sampling/surface/isoSurface/isoSurfaceTopo.C @@ -29,13 +29,16 @@ License #include "isoSurfaceTopo.H" #include "polyMesh.H" #include "volFields.H" +#include "edgeHashes.H" #include "tetCell.H" -#include "tetMatcher.H" #include "tetPointRef.H" #include "DynamicField.H" #include "syncTools.H" #include "uindirectPrimitivePatch.H" #include "polyMeshTetDecomposition.H" +#include "foamVtkInternalMeshWriter.H" +#include "foamVtkLineWriter.H" +#include "foamVtkSurfaceWriter.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -51,452 +54,624 @@ namespace Foam } -// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // +// Get/set snapIndex (0, 1 or 2) at given position +// 0 = no snap +// 1 = snap to first edge end +// 2 = snap to second edge end +// NB: 4 lower bits left free for regular tet-cut information + +#undef SNAP_END_VALUE +#undef SNAP_END_ENCODE +#define SNAP_END_ENCODE(pos, val) (((val) << (4 + 2 * pos))) +#define SNAP_END_VALUE(pos, val) (((val) >> (4 + 2 * pos)) & 0x3) + -Foam::label Foam::isoSurfaceTopo::generatePoint +// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // + +namespace Foam +{ + +// Check for tet values above/below given (iso) value +// Result encoded as an integer, with possible snapping information too +inline static int getTetCutIndex ( - const label facei, - const bool edgeIsDiag, - const edge& vertices, + scalar p0, + scalar p1, + scalar p2, + scalar p3, + const scalar val, + const bool doSnap +) noexcept +{ + int cutIndex + ( + (p0 < val ? 1 : 0) // point 0 + | (p1 < val ? 2 : 0) // point 1 + | (p2 < val ? 4 : 0) // point 2 + | (p3 < val ? 8 : 0) // point 3 + ); - DynamicList<edge>& pointToVerts, - DynamicList<label>& pointToFace, - DynamicList<bool>& pointFromDiag, - EdgeMap<label>& vertsToPoint -) const + if (doSnap && cutIndex && cutIndex != 0xF) + { + // Not all below or all + + // Calculate distances (for snapping) + p0 -= val; if (cutIndex & 1) p0 *= -1; + p1 -= val; if (cutIndex & 2) p1 *= -1; + p2 -= val; if (cutIndex & 4) p2 *= -1; + p3 -= val; if (cutIndex & 8) p3 *= -1; + + // Add snap index into regular edge cut index + // Snap to end if less than approx 1% of the distance. + // - only valid if there is also a corresponding sign change + #undef ADD_SNAP_INDEX + #define ADD_SNAP_INDEX(pos, d1, d2, idx1, idx2) \ + switch (cutIndex & (idx1 | idx2)) \ + { \ + case idx1 : /* first below, second above */ \ + case idx2 : /* first above, second below */ \ + cutIndex |= SNAP_END_ENCODE \ + ( \ + pos, \ + ((d1 * 100 < d2) ? 1 : (d2 * 100 < d1) ? 2 : 0) \ + ); \ + break; \ + } + + ADD_SNAP_INDEX(0, p0, p1, 1, 2); // Edge 0: 0 -> 1 + ADD_SNAP_INDEX(1, p0, p2, 1, 4); // Edge 1: 0 -> 2 + ADD_SNAP_INDEX(2, p0, p3, 1, 8); // Edge 2: 0 -> 3 + ADD_SNAP_INDEX(3, p3, p1, 8, 2); // Edge 3: 3 -> 1 + ADD_SNAP_INDEX(4, p1, p2, 2, 4); // Edge 4: 1 -> 2 + ADD_SNAP_INDEX(5, p3, p2, 8, 4); // Edge 5: 3 -> 2 + #undef ADD_SNAP_INDEX + } + + return cutIndex; +} + + +// Append three labels to list. +// Filter out degenerate (eg, snapped) tris. Flip face as requested +inline static void appendTriLabels +( + DynamicList<label>& verts, + const label a, + const label b, + const label c, + const bool flip // Flip normals +) +{ + if (a != b && b != c && c != a) + { + verts.append(a); + if (flip) + { + verts.append(c); + verts.append(b); + } + else + { + verts.append(b); + verts.append(c); + } + } +} + + +// Return point reference to mesh points or cell-centres +inline static const point& getMeshPointRef +( + const polyMesh& mesh, + const label pointi +) +{ + return + ( + pointi < mesh.nPoints() + ? mesh.points()[pointi] + : mesh.cellCentres()[pointi - mesh.nPoints()] + ); +} + +} // End namespace Foam + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::isoSurfaceTopo::tetCutAddressing::tetCutAddressing +( + const label nCutCells, + const bool useSnap, + const bool useDebugCuts +) +: + vertsToPointLookup_(12*nCutCells), + snapVertsLookup_(0), + + pointToFace_(10*nCutCells), + pointFromDiag_(10*nCutCells), + + pointToVerts_(10*nCutCells), + cutPoints_(12*nCutCells), + + debugCutTets_(), + debugCutTetsOn_(useDebugCuts) +{ + // Per cell: 5 pyramids cut, each generating 2 triangles + + // Per cell: number of intersected edges: + // - four faces cut so 4 mesh edges + 4 face-diagonal edges + // - 4 of the pyramid edges + + if (useSnap) + { + // Some, but not all, cells may have point snapping + snapVertsLookup_.resize(4*nCutCells); + } + if (debugCutTetsOn_) + { + debugCutTets_.reserve(6*nCutCells); + } +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::isoSurfaceTopo::tetCutAddressing::clearDebug() +{ + debugCutTets_.clearStorage(); +} + + +void Foam::isoSurfaceTopo::tetCutAddressing::clearDiagonal() +{ + pointToFace_.clearStorage(); + pointFromDiag_.clearStorage(); +} + + +void Foam::isoSurfaceTopo::tetCutAddressing::clearHashes() +{ + vertsToPointLookup_.clear(); + snapVertsLookup_.clear(); +} + + +Foam::label Foam::isoSurfaceTopo::tetCutAddressing::generatePoint +( + label facei, + bool edgeIsDiagonal, + const int snapEnd, + const edge& vertices +) { // Generate new point, unless it already exists for edge + // or corresponds to a snapped point (from another edge) - label pointi = vertsToPoint.lookup(vertices, -1); + label pointi = vertsToPointLookup_.lookup(vertices, -1); if (pointi == -1) { - pointi = pointToVerts.size(); + bool addNewPoint(true); + + const label snapPointi = + ( + (snapEnd == 1) ? vertices.first() + : (snapEnd == 2) ? vertices.second() + : -1 + ); + + if (snapPointi == -1) + { + // No snapped point + pointi = pointToVerts_.size(); + pointToVerts_.append(vertices); + } + else + { + // Snapped point. No corresponding face or diagonal + facei = -1; + edgeIsDiagonal = false; + + pointi = snapVertsLookup_.lookup(snapPointi, -1); + addNewPoint = (pointi == -1); + if (addNewPoint) + { + pointi = pointToVerts_.size(); + snapVertsLookup_.insert(snapPointi, pointi); + pointToVerts_.append(edge(snapPointi, snapPointi)); + } + } - pointToVerts.append(vertices); - pointToFace.append(facei); - pointFromDiag.append(edgeIsDiag); - vertsToPoint.insert(vertices, pointi); + if (addNewPoint) + { + pointToFace_.append(facei); + pointFromDiag_.append(edgeIsDiagonal); + } + + vertsToPointLookup_.insert(vertices, pointi); } return pointi; } -void Foam::isoSurfaceTopo::generateTriPoints +bool Foam::isoSurfaceTopo::tetCutAddressing::generatePoints ( const label facei, const int tetCutIndex, const tetCell& tetLabels, - const FixedList<bool, 6>& edgeIsDiag,// Per tet edge whether is face diag - DynamicList<edge>& pointToVerts, - DynamicList<label>& pointToFace, - DynamicList<bool>& pointFromDiag, - - EdgeMap<label>& vertsToPoint, - DynamicList<label>& verts // Every three verts is new triangle -) const + // Per tet edge whether is face diag etc + const FixedList<bool, 6>& edgeIsDiagonal +) { + bool flip(false); + const label nCutPointsOld(cutPoints_.size()); + // Form the vertices of the triangles for each case - switch (tetCutIndex) + switch (tetCutIndex & 0x0F) { case 0x00: case 0x0F: break; - case 0x01: - case 0x0E: + // Cut point 0 + case 0x0E: flip = true; [[fallthrough]]; // Point 0 above cut + case 0x01: // Point 0 below cut { - verts.append + const label cutA ( generatePoint ( facei, - edgeIsDiag[0], - tetLabels.edge(0), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[0], + SNAP_END_VALUE(0, tetCutIndex), + tetLabels.edge(0) // 0 -> 1 ) ); - verts.append + const label cutB ( generatePoint ( facei, - edgeIsDiag[1], - tetLabels.edge(1), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[1], + SNAP_END_VALUE(1, tetCutIndex), + tetLabels.edge(1) // 0 -> 2 ) ); - verts.append + const label cutC ( generatePoint ( facei, - edgeIsDiag[2], - tetLabels.edge(2), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[2], + SNAP_END_VALUE(2, tetCutIndex), + tetLabels.edge(2) // 0 -> 3 ) ); - if (tetCutIndex == 0x0E) - { - // Flip normals - const label sz = verts.size(); - std::swap(verts[sz-2], verts[sz-1]); - } + appendTriLabels(cutPoints_, cutA, cutB, cutC, flip); } break; - case 0x02: - case 0x0D: + // Cut point 1 + case 0x0D: flip = true; [[fallthrough]]; // Point 1 above cut + case 0x02: // Point 1 below cut { - verts.append + const label cutA ( generatePoint ( facei, - edgeIsDiag[0], - tetLabels.reverseEdge(0), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[0], + SNAP_END_VALUE(0, tetCutIndex), + tetLabels.edge(0) // 0 -> 1 ) ); - verts.append + const label cutB ( generatePoint ( facei, - edgeIsDiag[3], - tetLabels.reverseEdge(3), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[3], + SNAP_END_VALUE(3, tetCutIndex), + tetLabels.edge(3) // 3 -> 1 ) ); - verts.append + const label cutC ( generatePoint ( facei, - edgeIsDiag[4], - tetLabels.edge(4), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[4], + SNAP_END_VALUE(4, tetCutIndex), + tetLabels.edge(4) // 1 -> 2 ) ); - if (tetCutIndex == 0x0D) - { - // Flip normals - const label sz = verts.size(); - std::swap(verts[sz-2], verts[sz-1]); - } + appendTriLabels(cutPoints_, cutA, cutB, cutC, flip); } break; - case 0x03: - case 0x0C: + // Cut point 0/1 | 2/3 + case 0x0C: flip = true; [[fallthrough]]; // Point 0/1 above cut + case 0x03: // Point 0/1 below cut { - const label p0p2 + const label cutA ( generatePoint ( facei, - edgeIsDiag[1], - tetLabels.edge(1), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[1], + SNAP_END_VALUE(1, tetCutIndex), + tetLabels.edge(1) // 0 -> 2 ) ); - const label p1p3 + const label cutB ( generatePoint ( facei, - edgeIsDiag[3], - tetLabels.reverseEdge(3), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[2], + SNAP_END_VALUE(2, tetCutIndex), + tetLabels.edge(2) // 0 -> 3 ) ); - - verts.append + const label cutC ( generatePoint ( facei, - edgeIsDiag[2], - tetLabels.edge(2), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[3], + SNAP_END_VALUE(3, tetCutIndex), + tetLabels.edge(3) // 3 -> 1 ) ); - verts.append(p1p3); - verts.append(p0p2); - verts.append(p1p3); - verts.append + const label cutD ( generatePoint ( facei, - edgeIsDiag[4], - tetLabels.edge(4), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[4], + SNAP_END_VALUE(4, tetCutIndex), + tetLabels.edge(4) // 1 -> 2 ) ); - verts.append(p0p2); - if (tetCutIndex == 0x0C) - { - // Flip normals - const label sz = verts.size(); - std::swap(verts[sz-5], verts[sz-4]); - std::swap(verts[sz-2], verts[sz-1]); - } + appendTriLabels(cutPoints_, cutA, cutB, cutC, flip); + appendTriLabels(cutPoints_, cutA, cutC, cutD, flip); } break; - case 0x04: - case 0x0B: + // Cut point 2 + case 0x0B: flip = true; [[fallthrough]]; // Point 2 above cut + case 0x04: // Point 2 below cut { - verts.append + const label cutA ( generatePoint ( facei, - edgeIsDiag[1], - tetLabels.reverseEdge(1), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[1], + SNAP_END_VALUE(1, tetCutIndex), + tetLabels.edge(1) // 0 -> 2 ) ); - verts.append + const label cutB ( generatePoint ( facei, - edgeIsDiag[4], - tetLabels.reverseEdge(4), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[4], + SNAP_END_VALUE(4, tetCutIndex), + tetLabels.edge(4) // 1 -> 2 ) ); - verts.append + const label cutC ( generatePoint ( facei, - edgeIsDiag[5], - tetLabels.reverseEdge(5), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[5], + SNAP_END_VALUE(5, tetCutIndex), + tetLabels.edge(5) // 3 -> 2 ) ); - if (tetCutIndex == 0x0B) - { - // Flip normals - const label sz = verts.size(); - std::swap(verts[sz-2], verts[sz-1]); - } + appendTriLabels(cutPoints_, cutA, cutB, cutC, flip); } break; - case 0x05: - case 0x0A: + // Cut point 0/2 | 1/3 + case 0x0A: flip = true; [[fallthrough]]; // Point 0/2 above cut + case 0x05: // Point 0/2 below cut { - const label p0p1 + const label cutA ( generatePoint ( facei, - edgeIsDiag[0], - tetLabels.edge(0), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[0], + SNAP_END_VALUE(0, tetCutIndex), + tetLabels.edge(0) // 0 -> 1 ) ); - const label p2p3 + const label cutB ( generatePoint ( facei, - edgeIsDiag[5], - tetLabels.reverseEdge(5), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[4], + SNAP_END_VALUE(4, tetCutIndex), + tetLabels.edge(4) // 1 -> 2 ) ); - - verts.append(p0p1); - verts.append(p2p3); - verts.append + const label cutC ( generatePoint ( facei, - edgeIsDiag[2], - tetLabels.edge(2), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[5], + SNAP_END_VALUE(5, tetCutIndex), + tetLabels.edge(5) // 3 -> 2 ) ); - verts.append(p0p1); - verts.append + const label cutD ( generatePoint ( facei, - edgeIsDiag[4], - tetLabels.edge(4), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[2], + SNAP_END_VALUE(2, tetCutIndex), + tetLabels.edge(2) // 0 -> 3 ) ); - verts.append(p2p3); - if (tetCutIndex == 0x0A) - { - // Flip normals - const label sz = verts.size(); - std::swap(verts[sz-5], verts[sz-4]); - std::swap(verts[sz-2], verts[sz-1]); - } + appendTriLabels(cutPoints_, cutA, cutB, cutC, flip); + appendTriLabels(cutPoints_, cutA, cutC, cutD, flip); } break; - case 0x06: - case 0x09: + // Cut point 1/2 | 0/3 + case 0x09: flip = true; [[fallthrough]]; // Point 1/2 above cut + case 0x06: // Point 1/2 below cut { - const label p0p1 + const label cutA ( generatePoint ( facei, - edgeIsDiag[0], - tetLabels.edge(0), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[0], + SNAP_END_VALUE(0, tetCutIndex), + tetLabels.edge(0) // 0 -> 1 ) ); - const label p2p3 + const label cutB ( generatePoint ( facei, - edgeIsDiag[5], - tetLabels.reverseEdge(5), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[3], + SNAP_END_VALUE(3, tetCutIndex), + tetLabels.edge(3) // 3 -> 1 ) ); - - verts.append(p0p1); - verts.append + const label cutC ( generatePoint ( facei, - edgeIsDiag[3], - tetLabels.reverseEdge(3), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[5], + SNAP_END_VALUE(5, tetCutIndex), + tetLabels.edge(5) // 3 -> 2 ) ); - verts.append(p2p3); - verts.append(p0p1); - verts.append(p2p3); - verts.append + const label cutD ( generatePoint ( facei, - edgeIsDiag[1], - tetLabels.edge(1), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[1], + SNAP_END_VALUE(1, tetCutIndex), + tetLabels.edge(1) // 0 -> 2 ) ); - if (tetCutIndex == 0x09) - { - // Flip normals - const label sz = verts.size(); - std::swap(verts[sz-5], verts[sz-4]); - std::swap(verts[sz-2], verts[sz-1]); - } + appendTriLabels(cutPoints_, cutA, cutB, cutC, flip); + appendTriLabels(cutPoints_, cutA, cutC, cutD, flip); } break; - case 0x08: - case 0x07: + // Cut point 3 + case 0x07: flip = true; [[fallthrough]]; // Point 3 above cut + case 0x08: // Point 3 below cut { - verts.append + const label cutA ( generatePoint ( facei, - edgeIsDiag[2], - tetLabels.reverseEdge(2), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[2], + SNAP_END_VALUE(2, tetCutIndex), + tetLabels.edge(2) // 0 -> 3 ) ); - verts.append + const label cutB ( generatePoint ( facei, - edgeIsDiag[5], - tetLabels.reverseEdge(5), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[5], + SNAP_END_VALUE(5, tetCutIndex), + tetLabels.edge(5) // 3 -> 2 ) ); - verts.append + const label cutC ( generatePoint ( facei, - edgeIsDiag[3], - tetLabels.edge(3), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[3], + SNAP_END_VALUE(3, tetCutIndex), + tetLabels.edge(3) // 3 -> 1 ) ); - if (tetCutIndex == 0x07) - { - // Flip normals - const label sz = verts.size(); - std::swap(verts[sz-2], verts[sz-1]); - } + + appendTriLabels(cutPoints_, cutA, cutB, cutC, flip); } break; } + + const bool added(nCutPointsOld != cutPoints_.size()); + + if (added && debugCutTetsOn_) + { + debugCutTets_.append(tetLabels.shape()); + } + + return added; } +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +// Requires mesh_, tetBasePtIs void Foam::isoSurfaceTopo::generateTriPoints ( const label celli, const bool isTet, - - DynamicList<edge>& pointToVerts, - DynamicList<label>& pointToFace, - DynamicList<bool>& pointFromDiag, - - EdgeMap<label>& vertsToPoint, - DynamicList<label>& verts, - DynamicList<label>& faceLabels + const labelList& tetBasePtIs, + tetCutAddressing& tetCutAddr ) const { const faceList& faces = mesh_.faces(); const labelList& faceOwner = mesh_.faceOwner(); const cell& cFaces = mesh_.cells()[celli]; + const bool doSnap = this->snap(); if (isTet) { // For tets don't do cell-centre decomposition, just use the // tet points and values - const label startTrii = verts.size(); - const label facei = cFaces[0]; const face& f0 = faces[facei]; // Get the other point from f1. Tbd: check if not duplicate face // (ACMI / ignoreBoundaryFaces_). const face& f1 = faces[cFaces[1]]; - label oppositeI = -1; + label apexi = -1; forAll(f1, fp) { - oppositeI = f1[fp]; - if (!f0.found(oppositeI)) + apexi = f1[fp]; + if (!f0.found(apexi)) { break; } } - label p0 = f0[0]; + const label p0 = f0[0]; label p1 = f0[1]; label p2 = f0[2]; @@ -505,31 +680,27 @@ void Foam::isoSurfaceTopo::generateTriPoints std::swap(p1, p2); } - generateTriPoints + const tetCell tetLabels(p0, p1, p2, apexi); + const int tetCutIndex ( - facei, getTetCutIndex ( pVals_[p0], pVals_[p1], pVals_[p2], - pVals_[oppositeI], - iso_ - ), - tetCell(p0, p1, p2, oppositeI), - FixedList<bool, 6>(false), // edgeIsDiag = false - - pointToVerts, - pointToFace, - pointFromDiag, - vertsToPoint, - verts // Every three verts is new triangle + pVals_[apexi], + iso_, + doSnap + ) ); - for (label nTris = (verts.size()-startTrii)/3; nTris; --nTris) - { - faceLabels.append(facei); - } + tetCutAddr.generatePoints + ( + facei, + tetCutIndex, + tetLabels, + FixedList<bool, 6>(false) // Not face diagonal + ); } else { @@ -544,11 +715,9 @@ void Foam::isoSurfaceTopo::generateTriPoints continue; } - const label startTrii = verts.size(); - const face& f = faces[facei]; - label fp0 = tetBasePtIs_[facei]; + label fp0 = tetBasePtIs[facei]; // Fallback if (fp0 < 0) @@ -556,55 +725,48 @@ void Foam::isoSurfaceTopo::generateTriPoints fp0 = 0; } + const label p0 = f[fp0]; label fp = f.fcIndex(fp0); for (label i = 2; i < f.size(); ++i) { - const label nextFp = f.fcIndex(fp); - - FixedList<bool, 6> edgeIsDiag(false); - - label p0 = f[fp0]; label p1 = f[fp]; - label p2 = f[nextFp]; + fp = f.fcIndex(fp); + label p2 = f[fp]; + + FixedList<bool, 6> edgeIsDiagonal(false); if (faceOwner[facei] == celli) { std::swap(p1, p2); - if (i != 2) edgeIsDiag[1] = true; - if (i != f.size()-1) edgeIsDiag[0] = true; + if (i != 2) edgeIsDiagonal[1] = true; + if (i != f.size()-1) edgeIsDiagonal[0] = true; } else { - if (i != 2) edgeIsDiag[0] = true; - if (i != f.size()-1) edgeIsDiag[1] = true; + if (i != 2) edgeIsDiagonal[0] = true; + if (i != f.size()-1) edgeIsDiagonal[1] = true; } - generateTriPoints + const tetCell tetLabels(p0, p1, p2, mesh_.nPoints()+celli); + const int tetCutIndex ( - facei, getTetCutIndex ( pVals_[p0], pVals_[p1], pVals_[p2], cVals_[celli], - iso_ - ), - tetCell(p0, p1, p2, mesh_.nPoints()+celli), - edgeIsDiag, - - pointToVerts, - pointToFace, - pointFromDiag, - vertsToPoint, - verts // Every three verts is new triangle + iso_, + doSnap + ) ); - fp = nextFp; - } - - for (label nTris = (verts.size()-startTrii)/3; nTris; --nTris) - { - faceLabels.append(facei); + tetCutAddr.generatePoints + ( + facei, + tetCutIndex, + tetLabels, + edgeIsDiagonal + ); } } } @@ -708,11 +870,11 @@ void Foam::isoSurfaceTopo::removeInsidePoints const pointField& points = s.points(); - DynamicList<face> compactFaces(s.size()/8); + DynamicList<face> compactFaces(s.size()/4); for (label celli = 0; celli < start.size()-1; ++celli) { - // All triangles for the current cell + // Triangles for the current cell const label nTris = start[celli+1]-start[celli]; @@ -730,7 +892,6 @@ void Foam::isoSurfaceTopo::removeInsidePoints pp, pointFromDiag, pointToFace, - //protectedFace, celli, compactFaces, @@ -791,9 +952,11 @@ Foam::isoSurfaceTopo::isoSurfaceTopo const bitSet& ignoreCells ) : - isoSurfaceBase(mesh, cellValues, pointValues, iso, params), - cellCutType_(mesh.nCells(), cutType::UNVISITED) + isoSurfaceBase(mesh, cellValues, pointValues, iso, params) { + // The cell cut type + List<cutType> cellCutType_(mesh.nCells(), cutType::UNVISITED); + if (debug) { Pout<< "isoSurfaceTopo:" << nl @@ -819,9 +982,11 @@ Foam::isoSurfaceTopo::isoSurfaceTopo nBlockedCells += blockCells(cellCutType_, params.getClipBounds(), volumeType::OUTSIDE); - - // Adjust tet base points to improve tet quality - tetBasePtIs_ = polyMeshTetDecomposition::adjustTetBasePtIs(mesh_, debug); + // Adjusted tet base points to improve tet quality + labelList tetBasePtIs + ( + polyMeshTetDecomposition::adjustTetBasePtIs(mesh_, debug) + ); // Determine cell cuts @@ -851,7 +1016,7 @@ Foam::isoSurfaceTopo::isoSurfaceTopo false ), fvmesh, - dimensionedScalar(dimless, Zero) + dimensionedScalar(dimless) ); auto& debugFld = debugField.primitiveFieldRef(); @@ -861,68 +1026,133 @@ Foam::isoSurfaceTopo::isoSurfaceTopo debugFld[celli] = cellCutType_[celli]; } - Pout<< "Writing cut types:" - << debugField.objectPath() << endl; - + Info<< "Writing cut types: " << debugField.objectRelPath() << nl; debugField.write(); } + // Additional debugging + if (debug & 8) + { + const word timeDesc + ( + word::printf("%08d", mesh_.time().timeIndex()) + ); - // Per cell: 5 pyramids cut, each generating 2 triangles - // - pointToVerts : from generated iso point to originating mesh verts - DynamicList<edge> pointToVerts(10*nCutCells); - // - pointToFace : from generated iso point to originating mesh face - DynamicList<label> pointToFace(10*nCutCells); - // - pointFromDiag : if generated iso point is on face diagonal - DynamicList<bool> pointFromDiag(10*nCutCells); + // Write debug cuts cells in VTK format + { + constexpr uint8_t realCut(cutType::CUT | cutType::TETCUT); + labelList debugCutCells(nCutCells, Zero); - // Per cell: number of intersected edges: - // - four faces cut so 4 mesh edges + 4 face-diagonal edges - // - 4 of the pyramid edges - EdgeMap<label> vertsToPoint(12*nCutCells); - DynamicList<label> verts(12*nCutCells); - // Per cell: 5 pyramids cut (since only one pyramid not cut) - DynamicList<label> faceLabels(5*nCutCells); - DynamicList<label> cellLabels(5*nCutCells); + label nout = 0; + forAll(cellCutType_, celli) + { + if ((cellCutType_[celli] & realCut) != 0) + { + debugCutCells[nout] = celli; + ++nout; + if (nout >= nCutCells) break; + } + } + // The mesh subset cut + vtk::vtuCells vtuCells; + vtuCells.reset(mesh_, debugCutCells); - labelList startTri(mesh_.nCells()+1, Zero); + vtk::internalMeshWriter writer + ( + mesh_, + vtuCells, + fileName + ( + mesh_.time().globalPath() + / ("isoSurfaceTopo." + timeDesc + "-cutCells") + ) + ); + + writer.writeGeometry(); + + // Cell qualities + writer.beginCellData(); + writer.writeProcIDs(); + writer.writeCellData("cutField", cVals_); + // Point qualities + writer.beginPointData(); + writer.writePointData("cutField", pVals_); + + Info<< "isoSurfaceTopo : (debug) wrote " + << returnReduce(nCutCells, sumOp<label>()) + << " cut cells: " + << writer.output().name() << nl; + } + } + + + tetCutAddressing tetCutAddr + ( + nCutCells, + this->snap(), + (debug & 8) // Enable debug tets + ); + + labelList startTri(mesh_.nCells()+1, Zero); for (label celli = 0; celli < mesh_.nCells(); ++celli) { - startTri[celli] = faceLabels.size(); + startTri[celli] = tetCutAddr.nFaces(); if ((cellCutType_[celli] & cutType::ANYCUT) != 0) { generateTriPoints ( celli, - // Same as tetMatcher::test(mesh_, celli), - bool(cellCutType_[celli] & cutType::TETCUT), // isTet - - pointToVerts, - pointToFace, - pointFromDiag, + bool(cellCutType_[celli] & cutType::TETCUT), - vertsToPoint, - verts, - faceLabels + tetBasePtIs, + tetCutAddr ); + } + } + startTri.last() = tetCutAddr.nFaces(); - for (label i = startTri[celli]; i < faceLabels.size(); ++i) - { - cellLabels.append(celli); - } + // Information not needed anymore: + tetBasePtIs.clear(); + tetCutAddr.clearHashes(); + + + // From list of vertices -> triangular faces + faceList allTriFaces(startTri.last()); + { + auto& verts = tetCutAddr.cutPoints(); + + label verti = 0; + for (face& f : allTriFaces) + { + f.resize(3); + f[0] = verts[verti++]; + f[1] = verts[verti++]; + f[2] = verts[verti++]; } + verts.clearStorage(); // Not needed anymore } - startTri[mesh_.nCells()] = faceLabels.size(); - pointToVerts_.transfer(pointToVerts); - meshCells_.transfer(cellLabels); - pointToFace_.transfer(pointToFace); + // The cells cut by the triangular faces + meshCells_.resize(startTri.last()); + for (label celli = 0; celli < startTri.size()-1; ++celli) + { + // All triangles for the current cell + labelList::subList + ( + meshCells_, + (startTri[celli+1] - startTri[celli]), + startTri[celli] + ) = celli; + } + + + pointToVerts_.transfer(tetCutAddr.pointToVerts()); - pointField allPoints + pointField allTriPoints ( this->interpolateTemplate ( @@ -933,37 +1163,16 @@ Foam::isoSurfaceTopo::isoSurfaceTopo // Assign to MeshedSurface - faceList allTris(faceLabels.size()); - label verti = 0; - for (face& allTri : allTris) - { - allTri.resize(3); - allTri[0] = verts[verti++]; - allTri[1] = verts[verti++]; - allTri[2] = verts[verti++]; - } - - - surfZoneList allZones(one{}, surfZone("allFaces", allTris.size())); Mesh::clear(); Mesh updated ( - std::move(allPoints), - std::move(allTris), - std::move(allZones) + std::move(allTriPoints), + std::move(allTriFaces), + surfZoneList(one{}, surfZone("allFaces", allTriFaces.size())) ); Mesh::transfer(updated); - // Now: - // - generated faces and points are assigned to *this - // - per point we know: - // - pointOnDiag: whether it is on a face-diagonal edge - // - pointToFace_: from what pyramid (cell+face) it was produced - // (note that the pyramid faces are shared between multiple mesh faces) - // - pointToVerts_ : originating mesh vertex or cell centre - - if (debug) { Pout<< "isoSurfaceTopo : generated " @@ -972,6 +1181,16 @@ Foam::isoSurfaceTopo::isoSurfaceTopo } + // Now: + // - generated faces and points are assigned to *this + // - per point we know: + // - pointOnDiag: whether it is on a face-diagonal edge + // - pointToFace: from what pyramid (cell+face) it was produced + // (note that the pyramid faces are shared between multiple mesh faces) + // - pointToVerts_ : originating mesh vertex or cell centre + + // Basic filtering + if (params.filter() != filterType::NONE) { // Triangulate outside (filter edges to cell centres and optionally @@ -982,22 +1201,21 @@ Foam::isoSurfaceTopo::isoSurfaceTopo removeInsidePoints ( *this, - (params.filter() == filterType::DIAGCELL ? true : false), - - pointFromDiag, - pointToFace_, + // Filter face diagonal + ( + params.filter() == filterType::DIAGCELL + || params.filter() == filterType::NONMANIFOLD + ), + tetCutAddr.pointFromDiag(), + tetCutAddr.pointToFace(), startTri, pointCompactMap, compactCellIDs ); pointToVerts_ = UIndirectList<edge>(pointToVerts_, pointCompactMap)(); - pointToFace_ = UIndirectList<label>(pointToFace_, pointCompactMap)(); meshCells_.transfer(compactCellIDs); - pointCompactMap.clearStorage(); - compactCellIDs.clearStorage(); - if (debug) { Pout<< "isoSurfaceTopo :" @@ -1008,33 +1226,35 @@ Foam::isoSurfaceTopo::isoSurfaceTopo } } - // Not required after this stage - pointFromDiag.clearStorage(); + // Diagonal filter information not needed anymore + tetCutAddr.clearDiagonal(); + + // For more advanced filtering (eg, removal of open edges) + // need the boundary and other 'protected' points - if (params.filter() == filterType::DIAGCELL) + bitSet isProtectedPoint; + if + ( + (params.filter() == filterType::NONMANIFOLD) + || tetCutAddr.debugCutTetsOn() + ) { - // We remove verts on face diagonals. This is in fact just - // straightening the edges of the face through the cell. This can - // close off 'pockets' of triangles and create open or - // multiply-connected triangles + // Mark points on mesh outside as 'protected' + // - never erode these edges - // Solved by eroding open-edges - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + isProtectedPoint.resize(mesh_.nPoints()); - // Mark points on mesh outside. - bitSet isBoundaryPoint(mesh.nPoints()); for ( - label facei = mesh.nInternalFaces(); - facei < mesh.nFaces(); + label facei = mesh_.nInternalFaces(); + facei < mesh_.nFaces(); ++facei ) { - isBoundaryPoint.set(mesh.faces()[facei]); + isProtectedPoint.set(mesh_.faces()[facei]); } - // Include faces that would be exposed from mesh subset if (nBlockedCells) { @@ -1051,12 +1271,227 @@ Foam::isoSurfaceTopo::isoSurfaceTopo != (cellCutType_[faceNei[facei]] == cutType::BLOCKED) ) { - isBoundaryPoint.set(mesh.faces()[facei]); + isProtectedPoint.set(mesh_.faces()[facei]); + } + } + } + } + + // Initial cell cut information not needed anymore + cellCutType_.clear(); + + + // Additional debugging + if (tetCutAddr.debugCutTetsOn()) + { + const word timeDesc + ( + word::printf("%08d", mesh_.time().timeIndex()) + ); + + // Write debug cut tets in VTK format + { + const auto& debugCuts = tetCutAddr.debugCutTets(); + + // The TET shapes, using the mesh_ points information + vtk::vtuCells vtuCells; + vtuCells.resetShapes(debugCuts); + + // Use all points and all cell-centres + vtuCells.setNumPoints(mesh_.nPoints()); + vtuCells.addPointCellLabels(identity(mesh_.nCells())); + + vtk::internalMeshWriter writer + ( + mesh_, + vtuCells, + fileName + ( + mesh_.time().globalPath() + / ("isoSurfaceTopo." + timeDesc + "-cutTets") + ) + ); + + writer.writeGeometry(); + + writer.beginCellData(); + writer.writeProcIDs(); + + // Quality of the cut tets + { + Field<scalar> cutTetQuality(debugCuts.size()); + forAll(cutTetQuality, teti) + { + cutTetQuality[teti] = tetPointRef + ( + getMeshPointRef(mesh_, debugCuts[teti][0]), + getMeshPointRef(mesh_, debugCuts[teti][1]), + getMeshPointRef(mesh_, debugCuts[teti][2]), + getMeshPointRef(mesh_, debugCuts[teti][3]) + ).quality(); + } + writer.writeCellData("tetQuality", cutTetQuality); + } + + if (this->snap()) + { + labelList snapped(vtuCells.nFieldPoints(), Zero); + + for (const edge& verts : pointToVerts_) + { + if (verts.first() == verts.second()) + { + // Duplicate index (ie, snapped) + snapped[verts.first()] = 1; + } + } + + writer.beginPointData(); + writer.writePointData("snapped", snapped); + } + + Info<< "isoSurfaceTopo : (debug) wrote " + << returnReduce(debugCuts.size(), sumOp<label>()) + << " cut tets: " + << writer.output().name() << nl; + } + + // Determining open edges. Same logic as used later... + + labelHashSet openEdgeIds(0); + + { + const Mesh& s = *this; + + const labelList& mp = s.meshPoints(); + const edgeList& surfEdges = s.edges(); + const labelListList& edgeFaces = s.edgeFaces(); + openEdgeIds.resize(2*s.size()); + + forAll(edgeFaces, edgei) + { + const labelList& eFaces = edgeFaces[edgei]; + if (eFaces.size() == 1) + { + // Open edge (not originating from a boundary face) + + const edge& e = surfEdges[edgei]; + const edge& verts0 = pointToVerts_[mp[e.first()]]; + const edge& verts1 = pointToVerts_[mp[e.second()]]; + + if + ( + isProtectedPoint.test(verts0.first()) + && isProtectedPoint.test(verts0.second()) + && isProtectedPoint.test(verts1.first()) + && isProtectedPoint.test(verts1.second()) + ) + { + // Open edge on boundary face. Keep + } + else + { + // Open edge + openEdgeIds.insert(edgei); + } + } + } + + const label nOpenEdges + ( + returnReduce(openEdgeIds.size(), sumOp<label>()) + ); + + if (nOpenEdges) + { + const edgeList debugEdges + ( + surfEdges, + openEdgeIds.sortedToc() + ); + + vtk::lineWriter writer + ( + s.points(), + debugEdges, + fileName + ( + mesh_.time().globalPath() + / ("isoSurfaceTopo." + timeDesc + "-openEdges") + ) + ); + + writer.writeGeometry(); + + writer.beginCellData(); + writer.writeProcIDs(); + + Info<< "isoSurfaceTopo : (debug) wrote " + << nOpenEdges << " open edges: " + << writer.output().name() << nl; + } + else + { + Info<< "isoSurfaceTopo : no open edges" << nl; + } + } + + // Write debug surface with snaps + if (this->snap()) + { + const Mesh& s = *this; + + vtk::surfaceWriter writer + ( + s.points(), + s, + fileName + ( + mesh_.time().globalPath() + / ("isoSurfaceTopo." + timeDesc + "-surface") + ) + ); + + writer.writeGeometry(); + + writer.beginCellData(); + writer.writeProcIDs(); + + { + labelList snapped(s.nPoints(), Zero); + forAll(pointToVerts_, i) + { + const edge& verts =pointToVerts_[i]; + if (verts.first() == verts.second()) + { + // Duplicate index (ie, snapped) + snapped[i] = 1; + } } + + writer.beginPointData(); + writer.write("snapped", snapped); } + + Info<< "isoSurfaceTopo : (debug) wrote " + << returnReduce(s.size(), sumOp<label>()) + << " faces : " + << writer.output().name() << nl; } + } + tetCutAddr.clearDebug(); + if (params.filter() == filterType::NONMANIFOLD) + { + // We remove verts on face diagonals. This is in fact just + // straightening the edges of the face through the cell. This can + // close off 'pockets' of triangles and create open or + // multiply-connected triangles + + // Solved by eroding open-edges + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // The list of surface faces that should be retained after erosion Mesh& surf = *this; labelList faceAddr(identity(surf.size())); @@ -1086,19 +1521,18 @@ Foam::isoSurfaceTopo::isoSurfaceTopo const labelList& eFaces = edgeFaces[edgei]; if (eFaces.size() == 1) { - // Open edge. Check that vertices do not originate - // from a boundary face - const edge& e = surfEdges[edgei]; + // Open edge (not originating from a boundary face) + const edge& e = surfEdges[edgei]; const edge& verts0 = pointToVerts_[mp[e.first()]]; const edge& verts1 = pointToVerts_[mp[e.second()]]; if ( - isBoundaryPoint.test(verts0.first()) - && isBoundaryPoint.test(verts0.second()) - && isBoundaryPoint.test(verts1.first()) - && isBoundaryPoint.test(verts1.second()) + isProtectedPoint.test(verts0.first()) + && isProtectedPoint.test(verts0.second()) + && isProtectedPoint.test(verts1.first()) + && isProtectedPoint.test(verts1.second()) ) { // Open edge on boundary face. Keep @@ -1160,7 +1594,6 @@ void Foam::isoSurfaceTopo::inplaceSubsetMesh(const bitSet& include) meshCells_ = UIndirectList<label>(meshCells_, faceMap)(); pointToVerts_ = UIndirectList<edge>(pointToVerts_, pointMap)(); - pointToFace_ = UIndirectList<label>(pointToFace_, pointMap)(); } diff --git a/src/sampling/surface/isoSurface/isoSurfaceTopo.H b/src/sampling/surface/isoSurface/isoSurfaceTopo.H index 29fd36d8d4d2454a58985a16440a9093d7e9a69f..cd5886d135887577c42714186feb990c20a5b616 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceTopo.H +++ b/src/sampling/surface/isoSurface/isoSurfaceTopo.H @@ -50,6 +50,7 @@ namespace Foam { // Forward Declarations +class cellShape; class polyMesh; /*---------------------------------------------------------------------------*\ @@ -62,63 +63,103 @@ class isoSurfaceTopo { // Private Data - //- Corrected version of tetBasePtIs - labelList tetBasePtIs_; - - //- Per point: originating mesh vertex/cc. See encoding above + //- Per point: originating mesh vertex/cell centre combination. + // Vertices less than nPoints are mesh vertices, + // duplicate vertices indicate a snapped point edgeList pointToVerts_; - //- For every point the originating face in mesh - labelList pointToFace_; - //- The cell cut type - List<cutType> cellCutType_; + // Private Classes + //- Handling, bookkeeping for tet cuts + class tetCutAddressing + { + // Bookkeeping hashes used during construction + EdgeMap<label> vertsToPointLookup_; + Map<label> snapVertsLookup_; - // Private Member Functions + // Filter information for face diagonals + DynamicList<label> pointToFace_; + DynamicList<bool> pointFromDiag_; - //- Generate single point on edge - label generatePoint - ( - const label facei, - const bool edgeIsDiag, - const edge& vertices, - - DynamicList<edge>& pointToVerts, - DynamicList<label>& pointToFace, - DynamicList<bool>& pointFromDiag, - EdgeMap<label>& vertsToPoint - ) const; + // Final output + DynamicList<edge> pointToVerts_; + DynamicList<label> cutPoints_; - //- Generate triangles from tet - void generateTriPoints - ( - const label facei, - const int tetCutIndex, //!< Encoded tet cuts. getTetCutIndex() - const tetCell& tetLabels, - const FixedList<bool, 6>& edgeIsDiag, + //- List of cut (decomposed) cell tets. Debug use only. + DynamicList<cellShape> debugCutTets_; - DynamicList<edge>& pointToVerts, - DynamicList<label>& pointToFace, - DynamicList<bool>& pointFromDiag, + bool debugCutTetsOn_; - EdgeMap<label>& vertsToPoint, - DynamicList<label>& verts - ) const; - //- Generate triangles from cell + public: + + // Constructors + + //- Construct with reserved sizes + tetCutAddressing + ( + const label nCutCells, + const bool useSnap, + const bool useDebugCuts = false + ); + + + // Member Functions + + //- Effective number of faces + label nFaces() const { return cutPoints_.size()/3; } + + DynamicList<label>& pointToFace() { return pointToFace_; } + DynamicList<bool>& pointFromDiag() { return pointFromDiag_; } + + DynamicList<edge>& pointToVerts() { return pointToVerts_; } + DynamicList<label>& cutPoints() { return cutPoints_; } + DynamicList<cellShape>& debugCutTets() { return debugCutTets_; } + + //- Number of debug cut tets + label nDebugTets() const { return debugCutTets_.size(); } + + //- Debugging cut tets active + bool debugCutTetsOn() const { return debugCutTetsOn_; } + + void clearDebug(); + void clearDiagonal(); + void clearHashes(); + + //- Generate single point on edge + label generatePoint + ( + label facei, //!< Originating mesh face for cut-point + bool edgeIsDiagonal, //!< Edge on face diagonal + + // 0: no snap, 1: snap first, 2: snap second + const int snapEnd, + + const edge& vertices + ); + + //- Generate triangles from tet cut + bool generatePoints + ( + const label facei, + const int tetCutIndex, //!< Encoded tet cut + tet snapping + const tetCell& tetLabels, + const FixedList<bool, 6>& edgeIsDiagonal + ); + }; + + + // Private Member Functions + + //- Generate triangle points from cell void generateTriPoints ( const label celli, const bool isTet, + const labelList& tetBasePtIs, - DynamicList<edge>& pointToVerts, - DynamicList<label>& pointToFace, - DynamicList<bool>& pointFromDiag, - - EdgeMap<label>& vertsToPoint, - DynamicList<label>& verts, - DynamicList<label>& faceLabels + tetCutAddressing& tetCutAddr ) const; @@ -165,12 +206,12 @@ protected: // Sampling - //- Interpolates cCoords,pCoords. + //- Interpolates cellData and pointData fields template<class Type> tmp<Field<Type>> interpolateTemplate ( - const Field<Type>& cCoords, - const Field<Type>& pCoords + const Field<Type>& cellData, + const Field<Type>& pointData ) const; public: @@ -205,14 +246,10 @@ public: // Member Functions - //- For every point originating face (pyramid) in mesh - const labelList& pointToFace() const - { - return pointToFace_; - } - - //- Per point: originating mesh vertex/cc. See encoding above - const edgeList& pointToVerts() const + //- Per point: originating mesh vertex/cell centre combination. + // Vertices less than nPoints are mesh vertices (encoding above), + // duplicate vertices indicate a snapped point + const edgeList& pointToVerts() const noexcept { return pointToVerts_; } diff --git a/src/sampling/surface/isoSurface/isoSurfaceTopoTemplates.C b/src/sampling/surface/isoSurface/isoSurfaceTopoTemplates.C index 8ffec74e8db56b714118824d188edb711456ef95..5056fb38a5e0b646cdfba93e45fb3aac4b1e1208 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceTopoTemplates.C +++ b/src/sampling/surface/isoSurface/isoSurfaceTopoTemplates.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2018 OpenFOAM Foundation - Copyright (C) 2020 OpenCFD Ltd. + Copyright (C) 2020-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -32,8 +32,8 @@ template<class Type> Foam::tmp<Foam::Field<Type>> Foam::isoSurfaceTopo::interpolateTemplate ( - const Field<Type>& cellCoords, - const Field<Type>& pointCoords + const Field<Type>& cellData, + const Field<Type>& pointData ) const { auto tfld = tmp<Field<Type>>::New(pointToVerts_.size()); @@ -41,41 +41,50 @@ Foam::isoSurfaceTopo::interpolateTemplate forAll(pointToVerts_, i) { + const edge& verts = pointToVerts_[i]; + Type& val = fld[i]; + scalar s0; - Type p0; + Type v0; { - label idx = pointToVerts_[i].first(); + label idx = verts.first(); if (idx < mesh_.nPoints()) { // Point index s0 = pVals_[idx]; - p0 = pointCoords[idx]; + v0 = pointData[idx]; } else { // Cell index idx -= mesh_.nPoints(); s0 = cVals_[idx]; - p0 = cellCoords[idx]; + v0 = cellData[idx]; } } scalar s1; - Type p1; + Type v1; { - label idx = pointToVerts_[i].second(); - if (idx < mesh_.nPoints()) + label idx = verts.second(); + if (idx == verts.first()) + { + // Duplicate index (ie, snapped) + val = v0; + continue; + } + else if (idx < mesh_.nPoints()) { // Point index s1 = pVals_[idx]; - p1 = pointCoords[idx]; + v1 = pointData[idx]; } else { // Cell index idx -= mesh_.nPoints(); s1 = cVals_[idx]; - p1 = cellCoords[idx]; + v1 = cellData[idx]; } } @@ -83,11 +92,11 @@ Foam::isoSurfaceTopo::interpolateTemplate if (mag(d) > VSMALL) { const scalar s = (iso_-s0)/d; - fld[i] = s*p1+(1.0-s)*p0; + val = s*v1+(1.0-s)*v0; } else { - fld[i] = 0.5*(p0+p1); + val = 0.5*(v0+v1); } }