diff --git a/docs/changelog/contour-int-isovalue.md b/docs/changelog/contour-int-isovalue.md new file mode 100644 index 000000000..9d6b045f2 --- /dev/null +++ b/docs/changelog/contour-int-isovalue.md @@ -0,0 +1,17 @@ +# Allow floating-point isovalues for contours of integer fields + +The flying edges version of the contouring filter converted the isovalues +provided into the same type as the field. This is fine for a floating point +field, but for an integer field the isovalue was truncated to the nearest +integer. + +This is problematic because it is common to provide a fractional isovalue +(usually N + 0.5) for integer fields to avoid degenerate cases of the +contour intersecting vertices. It also means the behavior changes between +an integer type that is directly supported (like a `signed char`) or an +integer type that is not directly supported and converted to a floating +point field (like potentially a `char`). + +This change updates the worklets to allow the isovalue to have a different +type than the field and to always use a floating point type for the +isovalue. diff --git a/vtkm/filter/contour/ContourFlyingEdges.cxx b/vtkm/filter/contour/ContourFlyingEdges.cxx index 457565e55..301bd248d 100644 --- a/vtkm/filter/contour/ContourFlyingEdges.cxx +++ b/vtkm/filter/contour/ContourFlyingEdges.cxx @@ -63,10 +63,11 @@ vtkm::cont::DataSet ContourFlyingEdges::DoExecute(const vtkm::cont::DataSet& inD auto resolveFieldType = [&](const auto& concrete) { // use std::decay to remove const ref from the decltype of concrete. using T = typename std::decay_t::ValueType; - std::vector ivalues(this->IsoValues.size()); + using IVType = std::conditional_t<(sizeof(T) > 4), vtkm::Float64, vtkm::FloatDefault>; + std::vector ivalues(this->IsoValues.size()); for (std::size_t i = 0; i < ivalues.size(); ++i) { - ivalues[i] = static_cast(this->IsoValues[i]); + ivalues[i] = static_cast(this->IsoValues[i]); } if (this->GenerateNormals && !this->GetComputeFastNormals()) diff --git a/vtkm/filter/contour/worklet/ContourFlyingEdges.h b/vtkm/filter/contour/worklet/ContourFlyingEdges.h index 1a77c014a..471a9d42b 100644 --- a/vtkm/filter/contour/worklet/ContourFlyingEdges.h +++ b/vtkm/filter/contour/worklet/ContourFlyingEdges.h @@ -66,13 +66,14 @@ public: void ReleaseCellMapArrays() { this->SharedState.CellIdMap.ReleaseResources(); } // Filter called without normals generation - template vtkm::cont::CellSetSingleType<> Run( - const std::vector& isovalues, + const std::vector& isovalues, const vtkm::cont::CellSetStructured<3>& cells, const CoordsType& coordinateSystem, const vtkm::cont::ArrayHandle& input, @@ -87,14 +88,15 @@ public: } // Filter called with normals generation - template vtkm::cont::CellSetSingleType<> Run( - const std::vector& isovalues, + const std::vector& isovalues, const vtkm::cont::CellSetStructured<3>& cells, const CoordsType& coordinateSystem, const vtkm::cont::ArrayHandle& input, diff --git a/vtkm/filter/contour/worklet/contour/FlyingEdges.h b/vtkm/filter/contour/worklet/contour/FlyingEdges.h index e5bd379c8..81c2a17b1 100644 --- a/vtkm/filter/contour/worklet/contour/FlyingEdges.h +++ b/vtkm/filter/contour/worklet/contour/FlyingEdges.h @@ -40,7 +40,8 @@ vtkm::Id extend_by(vtkm::cont::ArrayHandle& handle, vtkm::Id size) } //---------------------------------------------------------------------------- -template execute( const vtkm::cont::CellSetStructured<3>& cells, const CoordsType coordinateSystem, - const std::vector& isovalues, + const std::vector& isovalues, const vtkm::cont::ArrayHandle& inputField, vtkm::cont::ArrayHandle, StorageTagVertices>& points, vtkm::cont::ArrayHandle, StorageTagNormals>& normals, @@ -82,7 +83,7 @@ vtkm::cont::CellSetSingleType<> execute( { auto multiContourCellOffset = sharedState.CellIdMap.GetNumberOfValues(); auto multiContourPointOffset = sharedState.InterpolationWeights.GetNumberOfValues(); - ValueType isoval = isovalues[i]; + IVType isoval = isovalues[i]; //---------------------------------------------------------------------------- // PASS 1: Process all of the voxel edges that compose each row. Determine the @@ -100,7 +101,7 @@ vtkm::cont::CellSetSingleType<> execute( // Additionally GPU's does significantly better when you do an initial fill // and write only non-below values // - ComputePass1 worklet1(isoval, pdims); + ComputePass1 worklet1(isoval, pdims); vtkm::cont::TryExecuteOnDevice(invoke.GetDevice(), launchComputePass1{}, worklet1, diff --git a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass1.h b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass1.h index b76a60576..cfea94c1d 100644 --- a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass1.h +++ b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass1.h @@ -147,9 +147,13 @@ struct ComputePass1 : public vtkm::worklet::WorkletVisitPointsWithCells struct launchComputePass1 { - template + template VTKM_CONT bool LaunchXAxis(DeviceAdapterTag device, - const ComputePass1& worklet, + const ComputePass1& worklet, const vtkm::cont::ArrayHandle& inputField, vtkm::cont::ArrayHandle& edgeCases, vtkm::cont::CellSetStructured<2>& metaDataMesh2D, @@ -162,9 +166,13 @@ struct launchComputePass1 return true; } - template + template VTKM_CONT bool LaunchYAxis(DeviceAdapterTag device, - const ComputePass1& worklet, + const ComputePass1& worklet, const vtkm::cont::ArrayHandle& inputField, vtkm::cont::ArrayHandle& edgeCases, vtkm::cont::CellSetStructured<2>& metaDataMesh2D, diff --git a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4.h b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4.h index fe66dac49..088f12dd9 100644 --- a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4.h +++ b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4.h @@ -42,6 +42,7 @@ struct launchComputePass4 } template VTKM_CONT bool LaunchXAxis(DeviceAdapterTag device, vtkm::Id vtkmNotUsed(newPointSize), - T isoval, + IVType isoval, CoordsType coordinateSystem, const vtkm::cont::ArrayHandle& inputField, vtkm::cont::ArrayHandle edgeCases, @@ -67,7 +68,7 @@ struct launchComputePass4 vtkm::cont::Invoker invoke(device); if (sharedState.GenerateNormals) { - ComputePass4XWithNormals worklet4( + ComputePass4XWithNormals worklet4( isoval, this->PointDims, this->CellWriteOffset, this->PointWriteOffset); invoke(worklet4, metaDataMesh2D, @@ -87,7 +88,7 @@ struct launchComputePass4 } else { - ComputePass4X worklet4( + ComputePass4X worklet4( isoval, this->PointDims, this->CellWriteOffset, this->PointWriteOffset); invoke(worklet4, metaDataMesh2D, @@ -109,6 +110,7 @@ struct launchComputePass4 } template VTKM_CONT bool LaunchYAxis(DeviceAdapterTag device, vtkm::Id newPointSize, - T isoval, + IVType isoval, CoordsType coordinateSystem, const vtkm::cont::ArrayHandle& inputField, vtkm::cont::ArrayHandle edgeCases, @@ -133,7 +135,7 @@ struct launchComputePass4 { vtkm::cont::Invoker invoke(device); - ComputePass4Y worklet4( + ComputePass4Y worklet4( isoval, this->PointDims, this->CellWriteOffset, this->PointWriteOffset); invoke(worklet4, metaDataMesh2D, @@ -149,7 +151,8 @@ struct launchComputePass4 sharedState.CellIdMap); //This needs to be done on array handle view ( start = this->PointWriteOffset, len = newPointSize) - ComputePass5Y worklet5(this->PointDims, this->PointWriteOffset, sharedState.GenerateNormals); + ComputePass5Y worklet5( + this->PointDims, this->PointWriteOffset, sharedState.GenerateNormals); invoke(worklet5, vtkm::cont::make_ArrayHandleView(