//============================================================================ // 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_exec_CellLocatorTwoLevel_h #define vtk_m_exec_CellLocatorTwoLevel_h #include #include #include #include #include #include #include #include #include namespace vtkm { namespace internal { namespace cl_uniform_bins { using DimensionType = vtkm::Int16; using DimVec3 = vtkm::Vec; using FloatVec3 = vtkm::Vec3f; struct Grid { DimVec3 Dimensions; // Bug in CUDA 9.2 where having this gap for alignment was for some reason setting garbage // in a union with other cell locators (or perhaps not properly copying data). This appears // to be fixed by CUDA 10.2. DimensionType Padding; FloatVec3 Origin; FloatVec3 BinSize; }; struct Bounds { FloatVec3 Min; FloatVec3 Max; }; VTKM_EXEC inline vtkm::Id ComputeFlatIndex(const DimVec3& idx, const DimVec3 dim) { return idx[0] + (dim[0] * (idx[1] + (dim[1] * idx[2]))); } VTKM_EXEC inline Grid ComputeLeafGrid(const DimVec3& idx, const DimVec3& dim, const Grid& l1Grid) { return { dim, 0, l1Grid.Origin + (static_cast(idx) * l1Grid.BinSize), l1Grid.BinSize / static_cast(dim) }; } template VTKM_EXEC inline Bounds ComputeCellBounds(const PointsVecType& points) { using CoordsType = typename vtkm::VecTraits::ComponentType; auto numPoints = vtkm::VecTraits::GetNumberOfComponents(points); CoordsType minp = points[0], maxp = points[0]; for (vtkm::IdComponent i = 1; i < numPoints; ++i) { minp = vtkm::Min(minp, points[i]); maxp = vtkm::Max(maxp, points[i]); } return { FloatVec3(minp), FloatVec3(maxp) }; } } } } // vtkm::internal::cl_uniform_bins namespace vtkm { namespace exec { //-------------------------------------------------------------------- template class VTKM_ALWAYS_EXPORT CellLocatorTwoLevel { private: using DimVec3 = vtkm::internal::cl_uniform_bins::DimVec3; using FloatVec3 = vtkm::internal::cl_uniform_bins::FloatVec3; template using ReadPortal = typename vtkm::cont::ArrayHandle::ReadPortalType; using CoordsPortalType = typename vtkm::cont::CoordinateSystem::MultiplexerArrayType::ReadPortalType; // TODO: This function may return false positives for non 3D cells as the // tests are done on the projection of the point on the cell. Extra checks // should be added to test if the point actually falls on the cell. template VTKM_EXEC static vtkm::ErrorCode PointInsideCell(FloatVec3 point, CellShapeTag cellShape, CoordsType cellPoints, FloatVec3& parametricCoordinates, bool& inside) { auto bounds = vtkm::internal::cl_uniform_bins::ComputeCellBounds(cellPoints); if (point[0] >= bounds.Min[0] && point[0] <= bounds.Max[0] && point[1] >= bounds.Min[1] && point[1] <= bounds.Max[1] && point[2] >= bounds.Min[2] && point[2] <= bounds.Max[2]) { VTKM_RETURN_ON_ERROR(vtkm::exec::WorldCoordinatesToParametricCoordinates( cellPoints, point, cellShape, parametricCoordinates)); inside = vtkm::exec::CellInside(parametricCoordinates, cellShape); } else { inside = false; } // Return success error code even point is not inside this cell return vtkm::ErrorCode::Success; } public: template VTKM_CONT CellLocatorTwoLevel(const vtkm::internal::cl_uniform_bins::Grid& topLevelGrid, const vtkm::cont::ArrayHandle& leafDimensions, const vtkm::cont::ArrayHandle& leafStartIndex, const vtkm::cont::ArrayHandle& cellStartIndex, const vtkm::cont::ArrayHandle& cellCount, const vtkm::cont::ArrayHandle& cellIds, const CellSetType& cellSet, const vtkm::cont::CoordinateSystem& coords, vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) : TopLevel(topLevelGrid) , LeafDimensions(leafDimensions.PrepareForInput(device, token)) , LeafStartIndex(leafStartIndex.PrepareForInput(device, token)) , CellStartIndex(cellStartIndex.PrepareForInput(device, token)) , CellCount(cellCount.PrepareForInput(device, token)) , CellIds(cellIds.PrepareForInput(device, token)) , CellSet(cellSet.PrepareForInput(device, vtkm::TopologyElementTagCell{}, vtkm::TopologyElementTagPoint{}, token)) , Coords(coords.GetDataAsMultiplexer().PrepareForInput(device, token)) { } struct LastCell { vtkm::Id CellId = -1; vtkm::Id LeafIdx = -1; }; VTKM_EXEC vtkm::ErrorCode FindCell(const FloatVec3& point, vtkm::Id& cellId, FloatVec3& parametric) const { LastCell lastCell; return this->FindCellImpl(point, cellId, parametric, lastCell); } VTKM_EXEC vtkm::ErrorCode FindCell(const FloatVec3& point, vtkm::Id& cellId, FloatVec3& parametric, LastCell& lastCell) const { vtkm::Vec3f pc; //See if point is inside the last cell. if ((lastCell.CellId >= 0) && (lastCell.CellId < this->CellSet.GetNumberOfElements()) && this->PointInCell(point, lastCell.CellId, pc) == vtkm::ErrorCode::Success) { parametric = pc; cellId = lastCell.CellId; return vtkm::ErrorCode::Success; } //See if it's in the last leaf. if ((lastCell.LeafIdx >= 0) && (lastCell.LeafIdx < this->CellCount.GetNumberOfValues()) && this->PointInLeaf(point, lastCell.LeafIdx, cellId, pc) == vtkm::ErrorCode::Success) { parametric = pc; lastCell.CellId = cellId; return vtkm::ErrorCode::Success; } //Call the full point search. return this->FindCellImpl(point, cellId, parametric, lastCell); } private: VTKM_EXEC vtkm::ErrorCode PointInCell(const vtkm::Vec3f& point, const vtkm::Id& cid, vtkm::Vec3f& parametric) const { auto indices = this->CellSet.GetIndices(cid); auto pts = vtkm::make_VecFromPortalPermute(&indices, this->Coords); vtkm::Vec3f pc; bool inside; auto status = PointInsideCell(point, this->CellSet.GetCellShape(cid), pts, pc, inside); if (status == vtkm::ErrorCode::Success && inside) { parametric = pc; return vtkm::ErrorCode::Success; } return vtkm::ErrorCode::CellNotFound; } VTKM_EXEC vtkm::ErrorCode PointInLeaf(const FloatVec3& point, const vtkm::Id& leafIdx, vtkm::Id& cellId, FloatVec3& parametric) const { vtkm::Id start = this->CellStartIndex.Get(leafIdx); vtkm::Id end = start + this->CellCount.Get(leafIdx); for (vtkm::Id i = start; i < end; ++i) { vtkm::Vec3f pc; vtkm::Id cid = this->CellIds.Get(i); if (this->PointInCell(point, cid, pc) == vtkm::ErrorCode::Success) { cellId = cid; parametric = pc; return vtkm::ErrorCode::Success; } } return vtkm::ErrorCode::CellNotFound; } VTKM_EXEC vtkm::ErrorCode FindCellImpl(const FloatVec3& point, vtkm::Id& cellId, FloatVec3& parametric, LastCell& lastCell) const { using namespace vtkm::internal::cl_uniform_bins; cellId = -1; lastCell.CellId = -1; lastCell.LeafIdx = -1; DimVec3 binId3 = static_cast((point - this->TopLevel.Origin) / this->TopLevel.BinSize); if (binId3[0] >= 0 && binId3[0] < this->TopLevel.Dimensions[0] && binId3[1] >= 0 && binId3[1] < this->TopLevel.Dimensions[1] && binId3[2] >= 0 && binId3[2] < this->TopLevel.Dimensions[2]) { vtkm::Id binId = ComputeFlatIndex(binId3, this->TopLevel.Dimensions); auto ldim = this->LeafDimensions.Get(binId); if (!ldim[0] || !ldim[1] || !ldim[2]) { return vtkm::ErrorCode::CellNotFound; } auto leafGrid = ComputeLeafGrid(binId3, ldim, this->TopLevel); DimVec3 leafId3 = static_cast((point - leafGrid.Origin) / leafGrid.BinSize); // precision issues may cause leafId3 to be out of range so clamp it leafId3 = vtkm::Max(DimVec3(0), vtkm::Min(ldim - DimVec3(1), leafId3)); vtkm::Id leafStart = this->LeafStartIndex.Get(binId); vtkm::Id leafIdx = leafStart + ComputeFlatIndex(leafId3, leafGrid.Dimensions); if (this->PointInLeaf(point, leafIdx, cellId, parametric) == vtkm::ErrorCode::Success) { lastCell.CellId = cellId; lastCell.LeafIdx = leafIdx; return vtkm::ErrorCode::Success; } } return vtkm::ErrorCode::CellNotFound; } vtkm::internal::cl_uniform_bins::Grid TopLevel; ReadPortal LeafDimensions; ReadPortal LeafStartIndex; ReadPortal CellStartIndex; ReadPortal CellCount; ReadPortal CellIds; CellStructureType CellSet; CoordsPortalType Coords; }; } } // vtkm::exec #endif //vtk_m_exec_CellLocatorTwoLevel_h