Merge topic 'contour-int-isovalue'

8fc341e71 Allow floating-point isovalues for contours of integer fields

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !3169
This commit is contained in:
Kenneth Moreland 2024-01-02 16:28:32 +00:00 committed by Kitware Robot
commit 974e964920
6 changed files with 52 additions and 20 deletions

@ -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.

@ -63,10 +63,11 @@ vtkm::cont::DataSet ContourFlyingEdges::DoExecute(const vtkm::cont::DataSet& inD
auto resolveFieldType = [&](const auto& concrete) { auto resolveFieldType = [&](const auto& concrete) {
// use std::decay to remove const ref from the decltype of concrete. // use std::decay to remove const ref from the decltype of concrete.
using T = typename std::decay_t<decltype(concrete)>::ValueType; using T = typename std::decay_t<decltype(concrete)>::ValueType;
std::vector<T> ivalues(this->IsoValues.size()); using IVType = std::conditional_t<(sizeof(T) > 4), vtkm::Float64, vtkm::FloatDefault>;
std::vector<IVType> ivalues(this->IsoValues.size());
for (std::size_t i = 0; i < ivalues.size(); ++i) for (std::size_t i = 0; i < ivalues.size(); ++i)
{ {
ivalues[i] = static_cast<T>(this->IsoValues[i]); ivalues[i] = static_cast<IVType>(this->IsoValues[i]);
} }
if (this->GenerateNormals && !this->GetComputeFastNormals()) if (this->GenerateNormals && !this->GetComputeFastNormals())

@ -66,13 +66,14 @@ public:
void ReleaseCellMapArrays() { this->SharedState.CellIdMap.ReleaseResources(); } void ReleaseCellMapArrays() { this->SharedState.CellIdMap.ReleaseResources(); }
// Filter called without normals generation // Filter called without normals generation
template <typename ValueType, template <typename IVType,
typename ValueType,
typename CoordsType, typename CoordsType,
typename StorageTagField, typename StorageTagField,
typename CoordinateType, typename CoordinateType,
typename StorageTagVertices> typename StorageTagVertices>
vtkm::cont::CellSetSingleType<> Run( vtkm::cont::CellSetSingleType<> Run(
const std::vector<ValueType>& isovalues, const std::vector<IVType>& isovalues,
const vtkm::cont::CellSetStructured<3>& cells, const vtkm::cont::CellSetStructured<3>& cells,
const CoordsType& coordinateSystem, const CoordsType& coordinateSystem,
const vtkm::cont::ArrayHandle<ValueType, StorageTagField>& input, const vtkm::cont::ArrayHandle<ValueType, StorageTagField>& input,
@ -87,14 +88,15 @@ public:
} }
// Filter called with normals generation // Filter called with normals generation
template <typename ValueType, template <typename IVType,
typename ValueType,
typename CoordsType, typename CoordsType,
typename StorageTagField, typename StorageTagField,
typename CoordinateType, typename CoordinateType,
typename StorageTagVertices, typename StorageTagVertices,
typename StorageTagNormals> typename StorageTagNormals>
vtkm::cont::CellSetSingleType<> Run( vtkm::cont::CellSetSingleType<> Run(
const std::vector<ValueType>& isovalues, const std::vector<IVType>& isovalues,
const vtkm::cont::CellSetStructured<3>& cells, const vtkm::cont::CellSetStructured<3>& cells,
const CoordsType& coordinateSystem, const CoordsType& coordinateSystem,
const vtkm::cont::ArrayHandle<ValueType, StorageTagField>& input, const vtkm::cont::ArrayHandle<ValueType, StorageTagField>& input,

@ -40,7 +40,8 @@ vtkm::Id extend_by(vtkm::cont::ArrayHandle<T, S>& handle, vtkm::Id size)
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
template <typename ValueType, template <typename IVType,
typename ValueType,
typename CoordsType, typename CoordsType,
typename StorageTagField, typename StorageTagField,
typename StorageTagVertices, typename StorageTagVertices,
@ -50,7 +51,7 @@ template <typename ValueType,
vtkm::cont::CellSetSingleType<> execute( vtkm::cont::CellSetSingleType<> execute(
const vtkm::cont::CellSetStructured<3>& cells, const vtkm::cont::CellSetStructured<3>& cells,
const CoordsType coordinateSystem, const CoordsType coordinateSystem,
const std::vector<ValueType>& isovalues, const std::vector<IVType>& isovalues,
const vtkm::cont::ArrayHandle<ValueType, StorageTagField>& inputField, const vtkm::cont::ArrayHandle<ValueType, StorageTagField>& inputField,
vtkm::cont::ArrayHandle<vtkm::Vec<CoordinateType, 3>, StorageTagVertices>& points, vtkm::cont::ArrayHandle<vtkm::Vec<CoordinateType, 3>, StorageTagVertices>& points,
vtkm::cont::ArrayHandle<vtkm::Vec<NormalType, 3>, StorageTagNormals>& normals, vtkm::cont::ArrayHandle<vtkm::Vec<NormalType, 3>, StorageTagNormals>& normals,
@ -82,7 +83,7 @@ vtkm::cont::CellSetSingleType<> execute(
{ {
auto multiContourCellOffset = sharedState.CellIdMap.GetNumberOfValues(); auto multiContourCellOffset = sharedState.CellIdMap.GetNumberOfValues();
auto multiContourPointOffset = sharedState.InterpolationWeights.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 // 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 // Additionally GPU's does significantly better when you do an initial fill
// and write only non-below values // and write only non-below values
// //
ComputePass1<ValueType> worklet1(isoval, pdims); ComputePass1<IVType> worklet1(isoval, pdims);
vtkm::cont::TryExecuteOnDevice(invoke.GetDevice(), vtkm::cont::TryExecuteOnDevice(invoke.GetDevice(),
launchComputePass1{}, launchComputePass1{},
worklet1, worklet1,

@ -147,9 +147,13 @@ struct ComputePass1 : public vtkm::worklet::WorkletVisitPointsWithCells
struct launchComputePass1 struct launchComputePass1
{ {
template <typename DeviceAdapterTag, typename T, typename StorageTagField, typename... Args> template <typename DeviceAdapterTag,
typename IVType,
typename T,
typename StorageTagField,
typename... Args>
VTKM_CONT bool LaunchXAxis(DeviceAdapterTag device, VTKM_CONT bool LaunchXAxis(DeviceAdapterTag device,
const ComputePass1<T>& worklet, const ComputePass1<IVType>& worklet,
const vtkm::cont::ArrayHandle<T, StorageTagField>& inputField, const vtkm::cont::ArrayHandle<T, StorageTagField>& inputField,
vtkm::cont::ArrayHandle<vtkm::UInt8>& edgeCases, vtkm::cont::ArrayHandle<vtkm::UInt8>& edgeCases,
vtkm::cont::CellSetStructured<2>& metaDataMesh2D, vtkm::cont::CellSetStructured<2>& metaDataMesh2D,
@ -162,9 +166,13 @@ struct launchComputePass1
return true; return true;
} }
template <typename DeviceAdapterTag, typename T, typename StorageTagField, typename... Args> template <typename DeviceAdapterTag,
typename IVType,
typename T,
typename StorageTagField,
typename... Args>
VTKM_CONT bool LaunchYAxis(DeviceAdapterTag device, VTKM_CONT bool LaunchYAxis(DeviceAdapterTag device,
const ComputePass1<T>& worklet, const ComputePass1<IVType>& worklet,
const vtkm::cont::ArrayHandle<T, StorageTagField>& inputField, const vtkm::cont::ArrayHandle<T, StorageTagField>& inputField,
vtkm::cont::ArrayHandle<vtkm::UInt8>& edgeCases, vtkm::cont::ArrayHandle<vtkm::UInt8>& edgeCases,
vtkm::cont::CellSetStructured<2>& metaDataMesh2D, vtkm::cont::CellSetStructured<2>& metaDataMesh2D,

@ -42,6 +42,7 @@ struct launchComputePass4
} }
template <typename DeviceAdapterTag, template <typename DeviceAdapterTag,
typename IVType,
typename T, typename T,
typename CoordsType, typename CoordsType,
typename StorageTagField, typename StorageTagField,
@ -50,7 +51,7 @@ struct launchComputePass4
typename NormalType> typename NormalType>
VTKM_CONT bool LaunchXAxis(DeviceAdapterTag device, VTKM_CONT bool LaunchXAxis(DeviceAdapterTag device,
vtkm::Id vtkmNotUsed(newPointSize), vtkm::Id vtkmNotUsed(newPointSize),
T isoval, IVType isoval,
CoordsType coordinateSystem, CoordsType coordinateSystem,
const vtkm::cont::ArrayHandle<T, StorageTagField>& inputField, const vtkm::cont::ArrayHandle<T, StorageTagField>& inputField,
vtkm::cont::ArrayHandle<vtkm::UInt8> edgeCases, vtkm::cont::ArrayHandle<vtkm::UInt8> edgeCases,
@ -67,7 +68,7 @@ struct launchComputePass4
vtkm::cont::Invoker invoke(device); vtkm::cont::Invoker invoke(device);
if (sharedState.GenerateNormals) if (sharedState.GenerateNormals)
{ {
ComputePass4XWithNormals<T> worklet4( ComputePass4XWithNormals<IVType> worklet4(
isoval, this->PointDims, this->CellWriteOffset, this->PointWriteOffset); isoval, this->PointDims, this->CellWriteOffset, this->PointWriteOffset);
invoke(worklet4, invoke(worklet4,
metaDataMesh2D, metaDataMesh2D,
@ -87,7 +88,7 @@ struct launchComputePass4
} }
else else
{ {
ComputePass4X<T> worklet4( ComputePass4X<IVType> worklet4(
isoval, this->PointDims, this->CellWriteOffset, this->PointWriteOffset); isoval, this->PointDims, this->CellWriteOffset, this->PointWriteOffset);
invoke(worklet4, invoke(worklet4,
metaDataMesh2D, metaDataMesh2D,
@ -109,6 +110,7 @@ struct launchComputePass4
} }
template <typename DeviceAdapterTag, template <typename DeviceAdapterTag,
typename IVType,
typename T, typename T,
typename CoordsType, typename CoordsType,
typename StorageTagField, typename StorageTagField,
@ -117,7 +119,7 @@ struct launchComputePass4
typename NormalType> typename NormalType>
VTKM_CONT bool LaunchYAxis(DeviceAdapterTag device, VTKM_CONT bool LaunchYAxis(DeviceAdapterTag device,
vtkm::Id newPointSize, vtkm::Id newPointSize,
T isoval, IVType isoval,
CoordsType coordinateSystem, CoordsType coordinateSystem,
const vtkm::cont::ArrayHandle<T, StorageTagField>& inputField, const vtkm::cont::ArrayHandle<T, StorageTagField>& inputField,
vtkm::cont::ArrayHandle<vtkm::UInt8> edgeCases, vtkm::cont::ArrayHandle<vtkm::UInt8> edgeCases,
@ -133,7 +135,7 @@ struct launchComputePass4
{ {
vtkm::cont::Invoker invoke(device); vtkm::cont::Invoker invoke(device);
ComputePass4Y<T> worklet4( ComputePass4Y<IVType> worklet4(
isoval, this->PointDims, this->CellWriteOffset, this->PointWriteOffset); isoval, this->PointDims, this->CellWriteOffset, this->PointWriteOffset);
invoke(worklet4, invoke(worklet4,
metaDataMesh2D, metaDataMesh2D,
@ -149,7 +151,8 @@ struct launchComputePass4
sharedState.CellIdMap); sharedState.CellIdMap);
//This needs to be done on array handle view ( start = this->PointWriteOffset, len = newPointSize) //This needs to be done on array handle view ( start = this->PointWriteOffset, len = newPointSize)
ComputePass5Y<T> worklet5(this->PointDims, this->PointWriteOffset, sharedState.GenerateNormals); ComputePass5Y<IVType> worklet5(
this->PointDims, this->PointWriteOffset, sharedState.GenerateNormals);
invoke(worklet5, invoke(worklet5,
vtkm::cont::make_ArrayHandleView( vtkm::cont::make_ArrayHandleView(