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) {
// use std::decay to remove const ref from the decltype of concrete.
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)
{
ivalues[i] = static_cast<T>(this->IsoValues[i]);
ivalues[i] = static_cast<IVType>(this->IsoValues[i]);
}
if (this->GenerateNormals && !this->GetComputeFastNormals())

@ -66,13 +66,14 @@ public:
void ReleaseCellMapArrays() { this->SharedState.CellIdMap.ReleaseResources(); }
// Filter called without normals generation
template <typename ValueType,
template <typename IVType,
typename ValueType,
typename CoordsType,
typename StorageTagField,
typename CoordinateType,
typename StorageTagVertices>
vtkm::cont::CellSetSingleType<> Run(
const std::vector<ValueType>& isovalues,
const std::vector<IVType>& isovalues,
const vtkm::cont::CellSetStructured<3>& cells,
const CoordsType& coordinateSystem,
const vtkm::cont::ArrayHandle<ValueType, StorageTagField>& input,
@ -87,14 +88,15 @@ public:
}
// Filter called with normals generation
template <typename ValueType,
template <typename IVType,
typename ValueType,
typename CoordsType,
typename StorageTagField,
typename CoordinateType,
typename StorageTagVertices,
typename StorageTagNormals>
vtkm::cont::CellSetSingleType<> Run(
const std::vector<ValueType>& isovalues,
const std::vector<IVType>& isovalues,
const vtkm::cont::CellSetStructured<3>& cells,
const CoordsType& coordinateSystem,
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 StorageTagField,
typename StorageTagVertices,
@ -50,7 +51,7 @@ template <typename ValueType,
vtkm::cont::CellSetSingleType<> execute(
const vtkm::cont::CellSetStructured<3>& cells,
const CoordsType coordinateSystem,
const std::vector<ValueType>& isovalues,
const std::vector<IVType>& isovalues,
const vtkm::cont::ArrayHandle<ValueType, StorageTagField>& inputField,
vtkm::cont::ArrayHandle<vtkm::Vec<CoordinateType, 3>, StorageTagVertices>& points,
vtkm::cont::ArrayHandle<vtkm::Vec<NormalType, 3>, 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<ValueType> worklet1(isoval, pdims);
ComputePass1<IVType> worklet1(isoval, pdims);
vtkm::cont::TryExecuteOnDevice(invoke.GetDevice(),
launchComputePass1{},
worklet1,

@ -147,9 +147,13 @@ struct ComputePass1 : public vtkm::worklet::WorkletVisitPointsWithCells
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,
const ComputePass1<T>& worklet,
const ComputePass1<IVType>& worklet,
const vtkm::cont::ArrayHandle<T, StorageTagField>& inputField,
vtkm::cont::ArrayHandle<vtkm::UInt8>& edgeCases,
vtkm::cont::CellSetStructured<2>& metaDataMesh2D,
@ -162,9 +166,13 @@ struct launchComputePass1
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,
const ComputePass1<T>& worklet,
const ComputePass1<IVType>& worklet,
const vtkm::cont::ArrayHandle<T, StorageTagField>& inputField,
vtkm::cont::ArrayHandle<vtkm::UInt8>& edgeCases,
vtkm::cont::CellSetStructured<2>& metaDataMesh2D,

@ -42,6 +42,7 @@ struct launchComputePass4
}
template <typename DeviceAdapterTag,
typename IVType,
typename T,
typename CoordsType,
typename StorageTagField,
@ -50,7 +51,7 @@ struct launchComputePass4
typename NormalType>
VTKM_CONT bool LaunchXAxis(DeviceAdapterTag device,
vtkm::Id vtkmNotUsed(newPointSize),
T isoval,
IVType isoval,
CoordsType coordinateSystem,
const vtkm::cont::ArrayHandle<T, StorageTagField>& inputField,
vtkm::cont::ArrayHandle<vtkm::UInt8> edgeCases,
@ -67,7 +68,7 @@ struct launchComputePass4
vtkm::cont::Invoker invoke(device);
if (sharedState.GenerateNormals)
{
ComputePass4XWithNormals<T> worklet4(
ComputePass4XWithNormals<IVType> worklet4(
isoval, this->PointDims, this->CellWriteOffset, this->PointWriteOffset);
invoke(worklet4,
metaDataMesh2D,
@ -87,7 +88,7 @@ struct launchComputePass4
}
else
{
ComputePass4X<T> worklet4(
ComputePass4X<IVType> worklet4(
isoval, this->PointDims, this->CellWriteOffset, this->PointWriteOffset);
invoke(worklet4,
metaDataMesh2D,
@ -109,6 +110,7 @@ struct launchComputePass4
}
template <typename DeviceAdapterTag,
typename IVType,
typename T,
typename CoordsType,
typename StorageTagField,
@ -117,7 +119,7 @@ struct launchComputePass4
typename NormalType>
VTKM_CONT bool LaunchYAxis(DeviceAdapterTag device,
vtkm::Id newPointSize,
T isoval,
IVType isoval,
CoordsType coordinateSystem,
const vtkm::cont::ArrayHandle<T, StorageTagField>& inputField,
vtkm::cont::ArrayHandle<vtkm::UInt8> edgeCases,
@ -133,7 +135,7 @@ struct launchComputePass4
{
vtkm::cont::Invoker invoke(device);
ComputePass4Y<T> worklet4(
ComputePass4Y<IVType> 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<T> worklet5(this->PointDims, this->PointWriteOffset, sharedState.GenerateNormals);
ComputePass5Y<IVType> worklet5(
this->PointDims, this->PointWriteOffset, sharedState.GenerateNormals);
invoke(worklet5,
vtkm::cont::make_ArrayHandleView(