//============================================================================ // 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. // // Copyright 2019 National Technology & Engineering Solutions of Sandia, LLC (NTESS). // Copyright 2019 UT-Battelle, LLC. // Copyright 2019 Los Alamos National Security. // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National // Laboratory (LANL), the U.S. Government retains certain rights in // this software. //============================================================================ #ifndef vtkm_m_worklet_TriangleWinding_h #define vtkm_m_worklet_TriangleWinding_h #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace vtkm { namespace worklet { /** * This worklet ensures that triangle windings are consistent with provided * cell normals. The triangles are wound CCW around the cell normals, and * all other cells are ignored. * * The input cellset must be unstructured. */ class TriangleWinding { public: // Used by Explicit and SingleType specializations struct WorkletWindToCellNormals : public WorkletMapField { using ControlSignature = void(FieldIn cellNormals, FieldInOut cellPoints, WholeArrayIn coords); using ExecutionSignature = void(_1 cellNormal, _2 cellPoints, _3 coords); template VTKM_EXEC void operator()(const vtkm::Vec& cellNormal, CellPointsType& cellPoints, const CoordsPortal& coords) const { // We only care about triangles: if (cellPoints.GetNumberOfComponents() != 3) { return; } using NormalType = vtkm::Vec; const NormalType p0 = coords.Get(cellPoints[0]); const NormalType p1 = coords.Get(cellPoints[1]); const NormalType p2 = coords.Get(cellPoints[2]); const NormalType v01 = p1 - p0; const NormalType v02 = p2 - p0; const NormalType triangleNormal = vtkm::Cross(v01, v02); if (vtkm::Dot(cellNormal, triangleNormal) < 0) { // Can't just use std::swap from exec function: const vtkm::Id tmp = cellPoints[1]; cellPoints[1] = cellPoints[2]; cellPoints[2] = tmp; } } }; // Used by generic implementations: struct WorkletGetCellShapesAndSizes : public WorkletVisitCellsWithPoints { using ControlSignature = void(CellSetIn cells, FieldOutCell shapes, FieldOutCell sizes); using ExecutionSignature = void(CellShape, PointCount, _2, _3); template VTKM_EXEC void operator()(const CellShapeTag cellShapeIn, const vtkm::IdComponent cellSizeIn, vtkm::UInt8& cellShapeOut, vtkm::IdComponent& cellSizeOut) const { cellSizeOut = cellSizeIn; cellShapeOut = cellShapeIn.Id; } }; struct WorkletWindToCellNormalsGeneric : public WorkletVisitCellsWithPoints { using ControlSignature = void(CellSetIn cellsIn, WholeArrayIn coords, FieldInCell cellNormals, FieldOutCell cellsOut); using ExecutionSignature = void(PointIndices, _2, _3, _4); template VTKM_EXEC void operator()(const InputIds& inputIds, const Coords& coords, const Normal& normal, OutputIds& outputIds) const { VTKM_ASSERT(inputIds.GetNumberOfComponents() == outputIds.GetNumberOfComponents()); // We only care about triangles: if (inputIds.GetNumberOfComponents() != 3) { // Just passthrough non-triangles // Cannot just assign here, must do a manual component-wise copy to // support VecFromPortal: for (vtkm::IdComponent i = 0; i < inputIds.GetNumberOfComponents(); ++i) { outputIds[i] = inputIds[i]; } return; } const Normal p0 = coords.Get(inputIds[0]); const Normal p1 = coords.Get(inputIds[1]); const Normal p2 = coords.Get(inputIds[2]); const Normal v01 = p1 - p0; const Normal v02 = p2 - p0; const Normal triangleNormal = vtkm::Cross(v01, v02); if (vtkm::Dot(normal, triangleNormal) < 0) { // Reorder triangle: outputIds[0] = inputIds[0]; outputIds[1] = inputIds[2]; outputIds[2] = inputIds[1]; } else { // passthrough: outputIds[0] = inputIds[0]; outputIds[1] = inputIds[1]; outputIds[2] = inputIds[2]; } } }; struct Launcher { vtkm::cont::DynamicCellSet Result; // Generic handler: template VTKM_CONT void operator()(const CellSetType& cellSet, const CoordsType& coords, const CellNormalsType& cellNormals, ...) { const auto numCells = cellSet.GetNumberOfCells(); if (numCells == 0) { this->Result = cellSet; return; } vtkm::cont::Invoker invoker; // Get each cell's size: vtkm::cont::ArrayHandle numIndices; vtkm::cont::ArrayHandle cellShapes; { WorkletGetCellShapesAndSizes worklet; invoker(worklet, cellSet, cellShapes, numIndices); } // Check to see if we can use CellSetSingleType: vtkm::IdComponent cellSize = 0; // 0 if heterogeneous, >0 if homogeneous vtkm::UInt8 cellShape = 0; // only valid if homogeneous { auto rangeHandleSizes = vtkm::cont::ArrayRangeCompute(numIndices); auto rangeHandleShapes = vtkm::cont::ArrayRangeCompute(cellShapes); cellShapes.ReleaseResourcesExecution(); auto rangeSizes = rangeHandleSizes.ReadPortal().Get(0); auto rangeShapes = rangeHandleShapes.ReadPortal().Get(0); const bool sameSize = vtkm::Abs(rangeSizes.Max - rangeSizes.Min) < 0.5; const bool sameShape = vtkm::Abs(rangeShapes.Max - rangeShapes.Min) < 0.5; if (sameSize && sameShape) { cellSize = static_cast(rangeSizes.Min + 0.5); cellShape = static_cast(rangeShapes.Min + 0.5); } } if (cellSize > 0) { // Single cell type: // don't need these anymore: numIndices.ReleaseResources(); cellShapes.ReleaseResources(); vtkm::cont::ArrayHandle conn; conn.Allocate(cellSize * numCells); auto offsets = vtkm::cont::make_ArrayHandleCounting(0, cellSize, numCells); auto connGroupVec = vtkm::cont::make_ArrayHandleGroupVecVariable(conn, offsets); WorkletWindToCellNormalsGeneric worklet; invoker(worklet, cellSet, coords, cellNormals, connGroupVec); vtkm::cont::CellSetSingleType<> outCells; outCells.Fill(cellSet.GetNumberOfPoints(), cellShape, cellSize, conn); this->Result = outCells; } else { // Multiple cell types: vtkm::cont::ArrayHandle offsets; vtkm::Id connSize; vtkm::cont::ConvertNumIndicesToOffsets(numIndices, offsets, connSize); numIndices.ReleaseResourcesExecution(); vtkm::cont::ArrayHandle conn; conn.Allocate(connSize); // Trim the last value off for the group vec array: auto connGroupVec = vtkm::cont::make_ArrayHandleGroupVecVariable(conn, offsets); WorkletWindToCellNormalsGeneric worklet; invoker(worklet, cellSet, coords, cellNormals, connGroupVec); vtkm::cont::CellSetExplicit<> outCells; outCells.Fill(cellSet.GetNumberOfPoints(), cellShapes, conn, offsets); this->Result = outCells; } } // Specialization for CellSetExplicit template VTKM_CONT void operator()(const vtkm::cont::CellSetExplicit& cellSet, const CoordsType& coords, const CellNormalsType& cellNormals, int) { using WindToCellNormals = vtkm::worklet::DispatcherMapField; const auto numCells = cellSet.GetNumberOfCells(); if (numCells == 0) { this->Result = cellSet; return; } vtkm::cont::ArrayHandle conn; { const auto& connIn = cellSet.GetConnectivityArray(vtkm::TopologyElementTagCell{}, vtkm::TopologyElementTagPoint{}); vtkm::cont::Algorithm::Copy(connIn, conn); } const auto& offsets = cellSet.GetOffsetsArray(vtkm::TopologyElementTagCell{}, vtkm::TopologyElementTagPoint{}); auto cells = vtkm::cont::make_ArrayHandleGroupVecVariable(conn, offsets); WindToCellNormals dispatcher; dispatcher.Invoke(cellNormals, cells, coords); const auto& shapes = cellSet.GetShapesArray(vtkm::TopologyElementTagCell{}, vtkm::TopologyElementTagPoint{}); vtkm::cont::CellSetExplicit newCells; newCells.Fill(cellSet.GetNumberOfPoints(), shapes, conn, offsets); this->Result = newCells; } // Specialization for CellSetSingleType template VTKM_CONT void operator()(const vtkm::cont::CellSetSingleType& cellSet, const CoordsType& coords, const CellNormalsType& cellNormals, int) { using WindToCellNormals = vtkm::worklet::DispatcherMapField; const auto numCells = cellSet.GetNumberOfCells(); if (numCells == 0) { this->Result = cellSet; return; } vtkm::cont::ArrayHandle conn; { const auto& connIn = cellSet.GetConnectivityArray(vtkm::TopologyElementTagCell{}, vtkm::TopologyElementTagPoint{}); vtkm::cont::Algorithm::Copy(connIn, conn); } const auto& offsets = cellSet.GetOffsetsArray(vtkm::TopologyElementTagCell{}, vtkm::TopologyElementTagPoint{}); auto cells = vtkm::cont::make_ArrayHandleGroupVecVariable(conn, offsets); WindToCellNormals dispatcher; dispatcher.Invoke(cellNormals, cells, coords); vtkm::cont::CellSetSingleType newCells; newCells.Fill(cellSet.GetNumberOfPoints(), cellSet.GetCellShape(0), cellSet.GetNumberOfPointsInCell(0), conn); this->Result = newCells; } }; template VTKM_CONT static vtkm::cont::DynamicCellSet Run(const CellSetType& cellSet, const CoordsType& coords, const CellNormalsType& cellNormals) { Launcher launcher; // The last arg is just to help with overload resolution on the templated // Launcher::operator() method, so that the more specialized impls are // preferred over the generic one. vtkm::cont::CastAndCall(cellSet, launcher, coords, cellNormals, 0); return launcher.Result; } }; } } // end namespace vtkm::worklet #endif // vtkm_m_worklet_TriangleWinding_h