//============================================================================ // Copyright (c) Kitware, Inc. // All rights reserved. // See LICENSE.txt for details. // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. //============================================================================ #ifndef vtk_m_worklet_ExternalFaces_h #define vtk_m_worklet_ExternalFaces_h #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace vtkm { namespace worklet { struct ExternalFaces { //Worklet that returns the number of external faces for each structured cell class NumExternalFacesPerStructuredCell : public vtkm::worklet::WorkletVisitCellsWithPoints { public: using ControlSignature = void(CellSetIn inCellSet, FieldOut numFacesInCell, FieldInPoint pointCoordinates); using ExecutionSignature = _2(CellShape, _3); using InputDomain = _1; VTKM_CONT NumExternalFacesPerStructuredCell(const vtkm::Vec3f_64& min_point, const vtkm::Vec3f_64& max_point) : MinPoint(min_point) , MaxPoint(max_point) { } VTKM_EXEC inline vtkm::IdComponent CountExternalFacesOnDimension(vtkm::Float64 grid_min, vtkm::Float64 grid_max, vtkm::Float64 cell_min, vtkm::Float64 cell_max) const { vtkm::IdComponent count = 0; bool cell_min_at_grid_boundary = cell_min <= grid_min; bool cell_max_at_grid_boundary = cell_max >= grid_max; if (cell_min_at_grid_boundary && !cell_max_at_grid_boundary) { count++; } else if (!cell_min_at_grid_boundary && cell_max_at_grid_boundary) { count++; } else if (cell_min_at_grid_boundary && cell_max_at_grid_boundary) { count += 2; } return count; } template VTKM_EXEC vtkm::IdComponent operator()(CellShapeTag shape, const PointCoordVecType& pointCoordinates) const { (void)shape; // C4100 false positive workaround VTKM_ASSERT(shape.Id == CELL_SHAPE_HEXAHEDRON); vtkm::IdComponent count = 0; count += CountExternalFacesOnDimension( MinPoint[0], MaxPoint[0], pointCoordinates[0][0], pointCoordinates[1][0]); count += CountExternalFacesOnDimension( MinPoint[1], MaxPoint[1], pointCoordinates[0][1], pointCoordinates[3][1]); count += CountExternalFacesOnDimension( MinPoint[2], MaxPoint[2], pointCoordinates[0][2], pointCoordinates[4][2]); return count; } private: vtkm::Vec3f_64 MinPoint; vtkm::Vec3f_64 MaxPoint; }; //Worklet that finds face connectivity for each structured cell class BuildConnectivityStructured : public vtkm::worklet::WorkletVisitCellsWithPoints { public: using ControlSignature = void(CellSetIn inCellSet, WholeCellSetIn<> inputCell, FieldOut faceShapes, FieldOut facePointCount, FieldOut faceConnectivity, FieldInPoint pointCoordinates); using ExecutionSignature = void(CellShape, VisitIndex, InputIndex, _2, _3, _4, _5, _6); using InputDomain = _1; using ScatterType = vtkm::worklet::ScatterCounting; template VTKM_CONT static ScatterType MakeScatter(const CountArrayType& countArray) { VTKM_IS_ARRAY_HANDLE(CountArrayType); return ScatterType(countArray); } VTKM_CONT BuildConnectivityStructured(const vtkm::Vec3f_64& min_point, const vtkm::Vec3f_64& max_point) : MinPoint(min_point) , MaxPoint(max_point) { } enum FaceType { FACE_GRID_MIN, FACE_GRID_MAX, FACE_GRID_MIN_AND_MAX, FACE_NONE }; VTKM_EXEC inline bool FoundFaceOnDimension(vtkm::Float64 grid_min, vtkm::Float64 grid_max, vtkm::Float64 cell_min, vtkm::Float64 cell_max, vtkm::IdComponent& faceIndex, vtkm::IdComponent& count, vtkm::IdComponent dimensionFaceOffset, vtkm::IdComponent visitIndex) const { bool cell_min_at_grid_boundary = cell_min <= grid_min; bool cell_max_at_grid_boundary = cell_max >= grid_max; FaceType Faces = FaceType::FACE_NONE; if (cell_min_at_grid_boundary && !cell_max_at_grid_boundary) { Faces = FaceType::FACE_GRID_MIN; } else if (!cell_min_at_grid_boundary && cell_max_at_grid_boundary) { Faces = FaceType::FACE_GRID_MAX; } else if (cell_min_at_grid_boundary && cell_max_at_grid_boundary) { Faces = FaceType::FACE_GRID_MIN_AND_MAX; } if (Faces == FaceType::FACE_NONE) return false; if (Faces == FaceType::FACE_GRID_MIN) { if (visitIndex == count) { faceIndex = dimensionFaceOffset; return true; } else { count++; } } else if (Faces == FaceType::FACE_GRID_MAX) { if (visitIndex == count) { faceIndex = dimensionFaceOffset + 1; return true; } else { count++; } } else if (Faces == FaceType::FACE_GRID_MIN_AND_MAX) { if (visitIndex == count) { faceIndex = dimensionFaceOffset; return true; } count++; if (visitIndex == count) { faceIndex = dimensionFaceOffset + 1; return true; } count++; } return false; } template VTKM_EXEC inline vtkm::IdComponent FindFaceIndexForVisit( vtkm::IdComponent visitIndex, const PointCoordVecType& pointCoordinates) const { vtkm::IdComponent count = 0; vtkm::IdComponent faceIndex = 0; // Search X dimension if (!FoundFaceOnDimension(MinPoint[0], MaxPoint[0], pointCoordinates[0][0], pointCoordinates[1][0], faceIndex, count, 0, visitIndex)) { // Search Y dimension if (!FoundFaceOnDimension(MinPoint[1], MaxPoint[1], pointCoordinates[0][1], pointCoordinates[3][1], faceIndex, count, 2, visitIndex)) { // Search Z dimension FoundFaceOnDimension(MinPoint[2], MaxPoint[2], pointCoordinates[0][2], pointCoordinates[4][2], faceIndex, count, 4, visitIndex); } } return faceIndex; } template VTKM_EXEC void operator()(CellShapeTag shape, vtkm::IdComponent visitIndex, vtkm::Id inputIndex, const CellSetType& cellSet, vtkm::UInt8& shapeOut, vtkm::IdComponent& numFacePointsOut, ConnectivityType& faceConnectivity, const PointCoordVecType& pointCoordinates) const { VTKM_ASSERT(shape.Id == CELL_SHAPE_HEXAHEDRON); vtkm::IdComponent faceIndex = FindFaceIndexForVisit(visitIndex, pointCoordinates); vtkm::IdComponent numFacePoints; vtkm::exec::CellFaceNumberOfPoints(faceIndex, shape, numFacePoints); VTKM_ASSERT(numFacePoints == faceConnectivity.GetNumberOfComponents()); typename CellSetType::IndicesType inCellIndices = cellSet.GetIndices(inputIndex); shapeOut = vtkm::CELL_SHAPE_QUAD; numFacePointsOut = 4; for (vtkm::IdComponent facePointIndex = 0; facePointIndex < numFacePoints; facePointIndex++) { vtkm::IdComponent localFaceIndex; vtkm::ErrorCode status = vtkm::exec::CellFaceLocalIndex(facePointIndex, faceIndex, shape, localFaceIndex); if (status == vtkm::ErrorCode::Success) { faceConnectivity[facePointIndex] = inCellIndices[localFaceIndex]; } else { // An error condition, but do we want to crash the operation? faceConnectivity[facePointIndex] = 0; } } } private: vtkm::Vec3f_64 MinPoint; vtkm::Vec3f_64 MaxPoint; }; //Worklet that returns the number of faces for each cell/shape class NumFacesPerCell : public vtkm::worklet::WorkletVisitCellsWithPoints { public: using ControlSignature = void(CellSetIn inCellSet, FieldOut numFacesInCell); using ExecutionSignature = void(CellShape, _2); using InputDomain = _1; template VTKM_EXEC void operator()(CellShapeTag shape, vtkm::IdComponent& numFaces) const { vtkm::exec::CellFaceNumberOfFaces(shape, numFaces); } }; //Worklet that identifies a cell face by a hash value. Not necessarily completely unique. class FaceHash : public vtkm::worklet::WorkletVisitCellsWithPoints { public: using ControlSignature = void(CellSetIn cellset, FieldOut faceHashes, FieldOut originCells, FieldOut originFaces); using ExecutionSignature = void(_2, _3, _4, CellShape, PointIndices, InputIndex, VisitIndex); using InputDomain = _1; using ScatterType = vtkm::worklet::ScatterCounting; template VTKM_EXEC void operator()(vtkm::HashType& faceHash, vtkm::Id& cellIndex, vtkm::IdComponent& faceIndex, CellShapeTag shape, const CellNodeVecType& cellNodeIds, vtkm::Id inputIndex, vtkm::IdComponent visitIndex) const { vtkm::Id3 faceId; vtkm::exec::CellFaceCanonicalId(visitIndex, shape, cellNodeIds, faceId); faceHash = vtkm::Hash(faceId); cellIndex = inputIndex; faceIndex = visitIndex; } }; // Worklet that identifies the number of cells written out per face. // Because there can be collisions in the face ids, this instance might // represent multiple faces, which have to be checked. The resulting // number is the total number of external faces. class FaceCounts : public vtkm::worklet::WorkletReduceByKey { public: using ControlSignature = void(KeysIn keys, WholeCellSetIn<> inputCells, ValuesIn originCells, ValuesIn originFaces, ReducedValuesOut numOutputCells); using ExecutionSignature = _5(_2, _3, _4); using InputDomain = _1; template VTKM_EXEC vtkm::IdComponent operator()(const CellSetType& cellSet, const OriginCellsType& originCells, const OriginFacesType& originFaces) const { vtkm::IdComponent numCellsOnHash = originCells.GetNumberOfComponents(); VTKM_ASSERT(originFaces.GetNumberOfComponents() == numCellsOnHash); // Start by assuming all faces are unique, then remove one for each // face we find a duplicate for. vtkm::IdComponent numExternalFaces = numCellsOnHash; for (vtkm::IdComponent myIndex = 0; myIndex < numCellsOnHash - 1; // Don't need to check last face myIndex++) { vtkm::Id3 myFace; vtkm::exec::CellFaceCanonicalId(originFaces[myIndex], cellSet.GetCellShape(originCells[myIndex]), cellSet.GetIndices(originCells[myIndex]), myFace); for (vtkm::IdComponent otherIndex = myIndex + 1; otherIndex < numCellsOnHash; otherIndex++) { vtkm::Id3 otherFace; vtkm::exec::CellFaceCanonicalId(originFaces[otherIndex], cellSet.GetCellShape(originCells[otherIndex]), cellSet.GetIndices(originCells[otherIndex]), otherFace); if (myFace == otherFace) { // Faces are the same. Must be internal. Remove 2, one for each face. We don't have to // worry about otherFace matching anything else because a proper topology will have at // most 2 cells sharing a face, so there should be no more matches. numExternalFaces -= 2; break; } } } return numExternalFaces; } }; private: // Resolves duplicate hashes by finding a specified unique face for a given hash. // Given a cell set (from a WholeCellSetIn) and the cell/face id pairs for each face // associated with a given hash, returns the index of the cell/face provided of the // visitIndex-th unique face. Basically, this method searches through all the cell/face // pairs looking for unique sets and returns the one associated with visitIndex. template VTKM_EXEC static vtkm::IdComponent FindUniqueFace(const CellSetType& cellSet, const OriginCellsType& originCells, const OriginFacesType& originFaces, vtkm::IdComponent visitIndex) { vtkm::IdComponent numCellsOnHash = originCells.GetNumberOfComponents(); VTKM_ASSERT(originFaces.GetNumberOfComponents() == numCellsOnHash); // Find the visitIndex-th unique face. vtkm::IdComponent numFound = 0; vtkm::IdComponent myIndex = 0; while (true) { VTKM_ASSERT(myIndex < numCellsOnHash); vtkm::Id3 myFace; vtkm::exec::CellFaceCanonicalId(originFaces[myIndex], cellSet.GetCellShape(originCells[myIndex]), cellSet.GetIndices(originCells[myIndex]), myFace); bool foundPair = false; for (vtkm::IdComponent otherIndex = myIndex + 1; otherIndex < numCellsOnHash; otherIndex++) { vtkm::Id3 otherFace; vtkm::exec::CellFaceCanonicalId(originFaces[otherIndex], cellSet.GetCellShape(originCells[otherIndex]), cellSet.GetIndices(originCells[otherIndex]), otherFace); if (myFace == otherFace) { // Faces are the same. Must be internal. foundPair = true; break; } } if (!foundPair) { if (numFound == visitIndex) { break; } else { numFound++; } } myIndex++; } return myIndex; } public: // Worklet that returns the number of points for each outputted face. // Have to manage the case where multiple faces have the same hash. class NumPointsPerFace : public vtkm::worklet::WorkletReduceByKey { public: using ControlSignature = void(KeysIn keys, WholeCellSetIn<> inputCells, ValuesIn originCells, ValuesIn originFaces, ReducedValuesOut numPointsInFace); using ExecutionSignature = void(_2, _3, _4, VisitIndex, _5); using InputDomain = _1; using ScatterType = vtkm::worklet::ScatterCounting; template VTKM_CONT static ScatterType MakeScatter(const CountArrayType& countArray) { VTKM_IS_ARRAY_HANDLE(CountArrayType); return ScatterType(countArray); } template VTKM_EXEC void operator()(const CellSetType& cellSet, const OriginCellsType& originCells, const OriginFacesType& originFaces, vtkm::IdComponent visitIndex, vtkm::IdComponent& numFacePoints) const { vtkm::IdComponent myIndex = ExternalFaces::FindUniqueFace(cellSet, originCells, originFaces, visitIndex); vtkm::exec::CellFaceNumberOfPoints( originFaces[myIndex], cellSet.GetCellShape(originCells[myIndex]), numFacePoints); } }; // Worklet that returns the shape and connectivity for each external face class BuildConnectivity : public vtkm::worklet::WorkletReduceByKey { public: using ControlSignature = void(KeysIn keys, WholeCellSetIn<> inputCells, ValuesIn originCells, ValuesIn originFaces, ReducedValuesOut shapesOut, ReducedValuesOut connectivityOut, ReducedValuesOut cellIdMapOut); using ExecutionSignature = void(_2, _3, _4, VisitIndex, _5, _6, _7); using InputDomain = _1; using ScatterType = vtkm::worklet::ScatterCounting; template VTKM_EXEC void operator()(const CellSetType& cellSet, const OriginCellsType& originCells, const OriginFacesType& originFaces, vtkm::IdComponent visitIndex, vtkm::UInt8& shapeOut, ConnectivityType& connectivityOut, vtkm::Id& cellIdMapOut) const { const vtkm::IdComponent myIndex = ExternalFaces::FindUniqueFace(cellSet, originCells, originFaces, visitIndex); const vtkm::IdComponent myFace = originFaces[myIndex]; typename CellSetType::CellShapeTag shapeIn = cellSet.GetCellShape(originCells[myIndex]); vtkm::exec::CellFaceShape(myFace, shapeIn, shapeOut); cellIdMapOut = originCells[myIndex]; vtkm::IdComponent numFacePoints; vtkm::exec::CellFaceNumberOfPoints(myFace, shapeIn, numFacePoints); VTKM_ASSERT(numFacePoints == connectivityOut.GetNumberOfComponents()); typename CellSetType::IndicesType inCellIndices = cellSet.GetIndices(originCells[myIndex]); for (vtkm::IdComponent facePointIndex = 0; facePointIndex < numFacePoints; facePointIndex++) { vtkm::IdComponent localFaceIndex; vtkm::ErrorCode status = vtkm::exec::CellFaceLocalIndex(facePointIndex, myFace, shapeIn, localFaceIndex); if (status == vtkm::ErrorCode::Success) { connectivityOut[facePointIndex] = inCellIndices[localFaceIndex]; } else { // An error condition, but do we want to crash the operation? connectivityOut[facePointIndex] = 0; } } } }; class IsPolyDataCell : public vtkm::worklet::WorkletVisitCellsWithPoints { public: using ControlSignature = void(CellSetIn inCellSet, FieldOut isPolyDataCell); using ExecutionSignature = _2(CellShape); using InputDomain = _1; template VTKM_EXEC vtkm::IdComponent operator()(CellShapeTag shape) const { vtkm::IdComponent numFaces; vtkm::exec::CellFaceNumberOfFaces(shape, numFaces); return !numFaces; } }; class CountPolyDataCellPoints : public vtkm::worklet::WorkletVisitCellsWithPoints { public: using ScatterType = vtkm::worklet::ScatterCounting; using ControlSignature = void(CellSetIn inCellSet, FieldOut numPoints); using ExecutionSignature = _2(PointCount); using InputDomain = _1; VTKM_EXEC vtkm::Id operator()(vtkm::Id count) const { return count; } }; class PassPolyDataCells : public vtkm::worklet::WorkletVisitCellsWithPoints { public: using ScatterType = vtkm::worklet::ScatterCounting; using ControlSignature = void(CellSetIn inputTopology, FieldOut shapes, FieldOut pointIndices, FieldOut cellIdMapOut); using ExecutionSignature = void(CellShape, PointIndices, InputIndex, _2, _3, _4); template VTKM_EXEC void operator()(const CellShape& inShape, const InPointIndexType& inPoints, vtkm::Id inputIndex, vtkm::UInt8& outShape, OutPointIndexType& outPoints, vtkm::Id& cellIdMapOut) const { cellIdMapOut = inputIndex; outShape = inShape.Id; vtkm::IdComponent numPoints = inPoints.GetNumberOfComponents(); VTKM_ASSERT(numPoints == outPoints.GetNumberOfComponents()); for (vtkm::IdComponent pointIndex = 0; pointIndex < numPoints; pointIndex++) { outPoints[pointIndex] = inPoints[pointIndex]; } } }; template struct BiasFunctor { VTKM_EXEC_CONT BiasFunctor(T bias = T(0)) : Bias(bias) { } VTKM_EXEC_CONT T operator()(T x) const { return x + this->Bias; } T Bias; }; public: VTKM_CONT ExternalFaces() : PassPolyData(true) { } VTKM_CONT void SetPassPolyData(bool flag) { this->PassPolyData = flag; } VTKM_CONT bool GetPassPolyData() const { return this->PassPolyData; } //---------------------------------------------------------------------------- template vtkm::cont::ArrayHandle ProcessCellField( const vtkm::cont::ArrayHandle& in) const { // Use a temporary permutation array to simplify the mapping: auto tmp = vtkm::cont::make_ArrayHandlePermutation(this->CellIdMap, in); // Copy into an array with default storage: vtkm::cont::ArrayHandle result; vtkm::cont::ArrayCopy(tmp, result); return result; } void ReleaseCellMapArrays() { this->CellIdMap.ReleaseResources(); } /////////////////////////////////////////////////// /// \brief ExternalFaces: Extract Faces on outside of geometry for regular grids. /// /// Faster Run() method for uniform and rectilinear grid types. /// Uses grid extents to find cells on the boundaries of the grid. template VTKM_CONT void Run( const vtkm::cont::CellSetStructured<3>& inCellSet, const vtkm::cont::CoordinateSystem& coord, vtkm::cont::CellSetExplicit& outCellSet) { vtkm::Vec3f_64 MinPoint; vtkm::Vec3f_64 MaxPoint; vtkm::Id3 PointDimensions = inCellSet.GetPointDimensions(); using DefaultHandle = vtkm::cont::ArrayHandle; using CartesianArrayHandle = vtkm::cont::ArrayHandleCartesianProduct; auto coordData = coord.GetData(); if (coordData.IsType()) { const auto vertices = coordData.AsArrayHandle(); const auto vertsSize = vertices.GetNumberOfValues(); const auto tmp = vtkm::cont::ArrayGetValues({ 0, vertsSize - 1 }, vertices); MinPoint = tmp[0]; MaxPoint = tmp[1]; } else { auto vertices = coordData.AsArrayHandle(); auto Coordinates = vertices.ReadPortal(); MinPoint = Coordinates.GetOrigin(); vtkm::Vec3f_64 spacing = Coordinates.GetSpacing(); vtkm::Vec3f_64 unitLength; unitLength[0] = static_cast(PointDimensions[0] - 1); unitLength[1] = static_cast(PointDimensions[1] - 1); unitLength[2] = static_cast(PointDimensions[2] - 1); MaxPoint = MinPoint + spacing * unitLength; } // Create a worklet to count the number of external faces on each cell vtkm::cont::ArrayHandle numExternalFaces; vtkm::worklet::DispatcherMapTopology numExternalFacesDispatcher((NumExternalFacesPerStructuredCell(MinPoint, MaxPoint))); numExternalFacesDispatcher.Invoke(inCellSet, numExternalFaces, coordData); vtkm::Id numberOfExternalFaces = vtkm::cont::Algorithm::Reduce(numExternalFaces, 0, vtkm::Sum()); auto scatterCellToExternalFace = BuildConnectivityStructured::MakeScatter(numExternalFaces); // Maps output cells to input cells. Store this for cell field mapping. this->CellIdMap = scatterCellToExternalFace.GetOutputToInputMap(); numExternalFaces.ReleaseResources(); vtkm::Id connectivitySize = 4 * numberOfExternalFaces; vtkm::cont::ArrayHandle faceConnectivity; vtkm::cont::ArrayHandle faceShapes; vtkm::cont::ArrayHandle facePointCount; // Must pre allocate because worklet invocation will not have enough // information to. faceConnectivity.Allocate(connectivitySize); vtkm::worklet::DispatcherMapTopology buildConnectivityStructuredDispatcher(BuildConnectivityStructured(MinPoint, MaxPoint), scatterCellToExternalFace); buildConnectivityStructuredDispatcher.Invoke( inCellSet, inCellSet, faceShapes, facePointCount, vtkm::cont::make_ArrayHandleGroupVec<4>(faceConnectivity), coordData); auto offsets = vtkm::cont::ConvertNumIndicesToOffsets(facePointCount); outCellSet.Fill(inCellSet.GetNumberOfPoints(), faceShapes, faceConnectivity, offsets); } /////////////////////////////////////////////////// /// \brief ExternalFaces: Extract Faces on outside of geometry template VTKM_CONT void Run( const InCellSetType& inCellSet, vtkm::cont::CellSetExplicit& outCellSet) { using PointCountArrayType = vtkm::cont::ArrayHandle; using ShapeArrayType = vtkm::cont::ArrayHandle; using OffsetsArrayType = vtkm::cont::ArrayHandle; using ConnectivityArrayType = vtkm::cont::ArrayHandle; //Create a worklet to map the number of faces to each cell vtkm::cont::ArrayHandle facesPerCell; vtkm::worklet::DispatcherMapTopology numFacesDispatcher; numFacesDispatcher.Invoke(inCellSet, facesPerCell); vtkm::worklet::ScatterCounting scatterCellToFace(facesPerCell); facesPerCell.ReleaseResources(); PointCountArrayType polyDataPointCount; ShapeArrayType polyDataShapes; OffsetsArrayType polyDataOffsets; ConnectivityArrayType polyDataConnectivity; vtkm::cont::ArrayHandle polyDataCellIdMap; vtkm::Id polyDataConnectivitySize = 0; if (this->PassPolyData) { vtkm::cont::ArrayHandle isPolyDataCell; vtkm::worklet::DispatcherMapTopology isPolyDataCellDispatcher; isPolyDataCellDispatcher.Invoke(inCellSet, isPolyDataCell); vtkm::worklet::ScatterCounting scatterPolyDataCells(isPolyDataCell); isPolyDataCell.ReleaseResources(); if (scatterPolyDataCells.GetOutputRange(inCellSet.GetNumberOfCells()) != 0) { vtkm::worklet::DispatcherMapTopology countPolyDataCellPointsDispatcher(scatterPolyDataCells); countPolyDataCellPointsDispatcher.Invoke(inCellSet, polyDataPointCount); vtkm::cont::ConvertNumIndicesToOffsets( polyDataPointCount, polyDataOffsets, polyDataConnectivitySize); vtkm::worklet::DispatcherMapTopology passPolyDataCellsDispatcher( scatterPolyDataCells); polyDataConnectivity.Allocate(polyDataConnectivitySize); passPolyDataCellsDispatcher.Invoke( inCellSet, polyDataShapes, vtkm::cont::make_ArrayHandleGroupVecVariable(polyDataConnectivity, polyDataOffsets), polyDataCellIdMap); } } if (scatterCellToFace.GetOutputRange(inCellSet.GetNumberOfCells()) == 0) { if (!polyDataConnectivitySize) { // Data has no faces. Output is empty. outCellSet.PrepareToAddCells(0, 0); outCellSet.CompleteAddingCells(inCellSet.GetNumberOfPoints()); return; } else { // Pass only input poly data to output outCellSet.Fill( inCellSet.GetNumberOfPoints(), polyDataShapes, polyDataConnectivity, polyDataOffsets); this->CellIdMap = polyDataCellIdMap; return; } } vtkm::cont::ArrayHandle faceHashes; vtkm::cont::ArrayHandle originCells; vtkm::cont::ArrayHandle originFaces; vtkm::worklet::DispatcherMapTopology faceHashDispatcher(scatterCellToFace); faceHashDispatcher.Invoke(inCellSet, faceHashes, originCells, originFaces); vtkm::worklet::Keys faceKeys(faceHashes); vtkm::cont::ArrayHandle faceOutputCount; vtkm::worklet::DispatcherReduceByKey faceCountDispatcher; faceCountDispatcher.Invoke(faceKeys, inCellSet, originCells, originFaces, faceOutputCount); auto scatterCullInternalFaces = NumPointsPerFace::MakeScatter(faceOutputCount); PointCountArrayType facePointCount; vtkm::worklet::DispatcherReduceByKey pointsPerFaceDispatcher( scatterCullInternalFaces); pointsPerFaceDispatcher.Invoke(faceKeys, inCellSet, originCells, originFaces, facePointCount); ShapeArrayType faceShapes; OffsetsArrayType faceOffsets; vtkm::Id connectivitySize; vtkm::cont::ConvertNumIndicesToOffsets(facePointCount, faceOffsets, connectivitySize); ConnectivityArrayType faceConnectivity; // Must pre allocate because worklet invocation will not have enough // information to. faceConnectivity.Allocate(connectivitySize); vtkm::worklet::DispatcherReduceByKey buildConnectivityDispatcher( scatterCullInternalFaces); vtkm::cont::ArrayHandle faceToCellIdMap; // Create a view that doesn't have the last offset: auto faceOffsetsTrim = vtkm::cont::make_ArrayHandleView(faceOffsets, 0, faceOffsets.GetNumberOfValues() - 1); buildConnectivityDispatcher.Invoke( faceKeys, inCellSet, originCells, originFaces, faceShapes, vtkm::cont::make_ArrayHandleGroupVecVariable(faceConnectivity, faceOffsets), faceToCellIdMap); if (!polyDataConnectivitySize) { outCellSet.Fill(inCellSet.GetNumberOfPoints(), faceShapes, faceConnectivity, faceOffsets); this->CellIdMap = faceToCellIdMap; } else { // Join poly data to face data output vtkm::cont::ArrayHandleConcatenate faceShapesArray( faceShapes, polyDataShapes); ShapeArrayType joinedShapesArray; vtkm::cont::ArrayCopy(faceShapesArray, joinedShapesArray); vtkm::cont::ArrayHandleConcatenate pointCountArray( facePointCount, polyDataPointCount); PointCountArrayType joinedPointCountArray; vtkm::cont::ArrayCopy(pointCountArray, joinedPointCountArray); vtkm::cont::ArrayHandleConcatenate connectivityArray(faceConnectivity, polyDataConnectivity); ConnectivityArrayType joinedConnectivity; vtkm::cont::ArrayCopy(connectivityArray, joinedConnectivity); // Adjust poly data offsets array with face connectivity size before join auto adjustedPolyDataOffsets = vtkm::cont::make_ArrayHandleTransform( polyDataOffsets, BiasFunctor(faceConnectivity.GetNumberOfValues())); auto offsetsArray = vtkm::cont::make_ArrayHandleConcatenate(faceOffsetsTrim, adjustedPolyDataOffsets); OffsetsArrayType joinedOffsets; vtkm::cont::ArrayCopy(offsetsArray, joinedOffsets); vtkm::cont::ArrayHandleConcatenate, vtkm::cont::ArrayHandle> cellIdMapArray(faceToCellIdMap, polyDataCellIdMap); vtkm::cont::ArrayHandle joinedCellIdMap; vtkm::cont::ArrayCopy(cellIdMapArray, joinedCellIdMap); outCellSet.Fill( inCellSet.GetNumberOfPoints(), joinedShapesArray, joinedConnectivity, joinedOffsets); this->CellIdMap = joinedCellIdMap; } } vtkm::cont::ArrayHandle GetCellIdMap() const { return this->CellIdMap; } private: vtkm::cont::ArrayHandle CellIdMap; bool PassPolyData; }; //struct ExternalFaces } } //namespace vtkm::worklet #endif //vtk_m_worklet_ExternalFaces_h