vtk-m/vtkm/worklet/Clip.h
2015-11-12 10:28:22 -05:00

718 lines
23 KiB
C++

//============================================================================
// 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 2014 Sandia Corporation.
// Copyright 2014 UT-Battelle, LLC.
// Copyright 2014 Los Alamos National Security.
//
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// 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_Clip_h
#define vtkm_m_worklet_Clip_h
#include <vtkm/worklet/DispatcherMapTopology.h>
#include <vtkm/worklet/WorkletMapTopology.h>
#include <vtkm/worklet/internal/ClipTables.h>
#include <vtkm/cont/CellSetExplicit.h>
#include <vtkm/cont/CoordinateSystem.h>
#include <vtkm/cont/DeviceAdapterAlgorithm.h>
#include <vtkm/cont/DynamicArrayHandle.h>
#include <vtkm/cont/DynamicCellSet.h>
#include <vtkm/cont/Timer.h>
#include <vtkm/ImplicitFunctions.h>
#include <vtkm/exec/ExecutionWholeArray.h>
#include <vtkm/exec/FunctorBase.h>
#if defined(THRUST_MAJOR_VERSION) && THRUST_MAJOR_VERSION == 1 && \
THRUST_MINOR_VERSION == 8 && THRUST_SUBMINOR_VERSION < 3
// Workaround a bug in thrust 1.8.0 - 1.8.2 scan implementations which produces
// wrong results
#include <thrust/detail/type_traits.h>
#define THRUST_SCAN_WORKAROUND
#endif
namespace vtkm {
namespace worklet {
namespace internal {
// TODO: The following code is meant to be temporary until similar functionality is
// implemented elsewhere.
// The current DeviceAdapterAlgorithm::Copy resizes the destination array, and
// therefore, is not usable for our purpose here.
template <typename T, typename StorageIn, typename StorageOut, typename DeviceAdapter>
class CopyArray : public vtkm::exec::FunctorBase
{
public:
typedef typename vtkm::cont::ArrayHandle<T, StorageIn>
::template ExecutionTypes<DeviceAdapter>::PortalConst SourcePortalType;
typedef typename vtkm::cont::ArrayHandle<T, StorageOut>
::template ExecutionTypes<DeviceAdapter>::Portal DestPortalType;
VTKM_CONT_EXPORT
CopyArray(SourcePortalType input, DestPortalType output)
: Input(input), Output(output)
{
}
VTKM_EXEC_EXPORT
void operator()(vtkm::Id idx) const
{
this->Output.Set(idx, this->Input.Get(idx));
}
private:
SourcePortalType Input;
DestPortalType Output;
};
template <typename T, typename StorageIn, typename StorageOut, typename DeviceAdapter>
VTKM_CONT_EXPORT
void ResizeArrayHandle(const vtkm::cont::ArrayHandle<T, StorageIn> &input,
vtkm::Id size,
vtkm::cont::ArrayHandle<T, StorageOut> &output,
DeviceAdapter)
{
typedef vtkm::cont::DeviceAdapterAlgorithm<DeviceAdapter> Algorithm;
output.Allocate(size);
CopyArray<T, StorageIn, StorageOut, DeviceAdapter>
copyArray(input.PrepareForInput(DeviceAdapter()),
output.PrepareForInPlace(DeviceAdapter()));
Algorithm::Schedule(copyArray, input.GetNumberOfValues());
}
template<typename T>
VTKM_EXEC_EXPORT
void swap(T &v1, T &v2)
{
T temp = v1;
v1 = v2;
v2 = temp;
}
template <typename T>
VTKM_EXEC_CONT_EXPORT
T Scale(const T &val, vtkm::Float64 scale)
{
return static_cast<T>(scale * static_cast<vtkm::Float64>(val));
}
template <typename T, vtkm::IdComponent NumComponents>
VTKM_EXEC_CONT_EXPORT
vtkm::Vec<T, NumComponents> Scale(const vtkm::Vec<T, NumComponents> &val,
vtkm::Float64 scale)
{
return val * scale;
}
template <typename DeviceAdapter>
class ExecutionConnectivityExplicit : vtkm::exec::ExecutionObjectBase
{
private:
typedef typename vtkm::cont::ArrayHandle<vtkm::UInt8>
::template ExecutionTypes<DeviceAdapter>::Portal UInt8Portal;
typedef typename vtkm::cont::ArrayHandle<vtkm::IdComponent>
::template ExecutionTypes<DeviceAdapter>::Portal IdComponentPortal;
typedef typename vtkm::cont::ArrayHandle<vtkm::Id>
::template ExecutionTypes<DeviceAdapter>::Portal IdPortal;
public:
VTKM_CONT_EXPORT
ExecutionConnectivityExplicit()
: Shapes(), NumIndices(), Connectivity(), IndexOffsets()
{
}
VTKM_CONT_EXPORT
ExecutionConnectivityExplicit(const UInt8Portal &shapes,
const IdComponentPortal &numIndices,
const IdPortal &connectivity,
const IdPortal &indexOffsets)
: Shapes(shapes),
NumIndices(numIndices),
Connectivity(connectivity),
IndexOffsets(indexOffsets)
{
}
VTKM_EXEC_EXPORT
void SetCellShape(vtkm::Id cellIndex, vtkm::UInt8 shape)
{
this->Shapes.Set(cellIndex, shape);
}
VTKM_EXEC_EXPORT
void SetNumberOfIndices(vtkm::Id cellIndex, vtkm::IdComponent numIndices)
{
this->NumIndices.Set(cellIndex, numIndices);
}
VTKM_EXEC_EXPORT
void SetIndexOffset(vtkm::Id cellIndex, vtkm::Id indexOffset)
{
this->IndexOffsets.Set(cellIndex, indexOffset);
}
VTKM_EXEC_EXPORT
void SetConnectivity(vtkm::Id connectivityIndex, vtkm::Id pointIndex)
{
this->Connectivity.Set(connectivityIndex, pointIndex);
}
private:
UInt8Portal Shapes;
IdComponentPortal NumIndices;
IdPortal Connectivity;
IdPortal IndexOffsets;
};
} // namespace internal
struct ClipStats
{
vtkm::Id NumberOfCells;
vtkm::Id NumberOfIndices;
vtkm::Id NumberOfNewPoints;
struct SumOp
{
VTKM_EXEC_CONT_EXPORT
ClipStats operator()(const ClipStats &cs1, const ClipStats &cs2) const
{
ClipStats sum = cs1;
sum.NumberOfCells += cs2.NumberOfCells;
sum.NumberOfIndices += cs2.NumberOfIndices;
sum.NumberOfNewPoints += cs2.NumberOfNewPoints;
return sum;
}
};
};
struct EdgeInterpolation
{
vtkm::Id Vertex1, Vertex2;
vtkm::Float64 Weight;
struct LessThanOp
{
VTKM_EXEC_EXPORT
bool operator()(const EdgeInterpolation &v1, const EdgeInterpolation &v2) const
{
return (v1.Vertex1 < v2.Vertex1) ||
(v1.Vertex1 == v2.Vertex1 && v1.Vertex2 < v2.Vertex2);
}
};
struct EqualToOp
{
VTKM_EXEC_EXPORT
bool operator()(const EdgeInterpolation &v1, const EdgeInterpolation &v2) const
{
return v1.Vertex1 == v2.Vertex1 && v1.Vertex2 == v2.Vertex2;
}
};
};
VTKM_EXEC_CONT_EXPORT
std::ostream& operator<<(std::ostream &out, const ClipStats &val)
{
return out << std::endl << "{ Cells: " << val.NumberOfCells
<< ", Indices: " << val.NumberOfIndices
<< ", NewPoints: " << val.NumberOfNewPoints << " }";
}
VTKM_EXEC_CONT_EXPORT
std::ostream& operator<<(std::ostream &out, const EdgeInterpolation &val)
{
return out << std::endl << "{ Vertex1: " << val.Vertex1
<< ", Vertex2: " << val.Vertex2
<< ", Weight: " << val.Weight << " }";
}
template<typename DeviceAdapter>
class Clip
{
private:
typedef internal::ClipTables::DevicePortal<DeviceAdapter> ClipTablesPortal;
typedef typename vtkm::cont::ArrayHandle<vtkm::Id>
::template ExecutionTypes<DeviceAdapter>::Portal IdPortal;
typedef typename vtkm::cont::ArrayHandle<vtkm::Id>
::template ExecutionTypes<DeviceAdapter>::PortalConst IdPortalConst;
typedef typename vtkm::cont::ArrayHandle<EdgeInterpolation>
::template ExecutionTypes<DeviceAdapter>::Portal EdgeInterpolationPortal;
typedef typename vtkm::cont::ArrayHandle<EdgeInterpolation>
::template ExecutionTypes<DeviceAdapter>::PortalConst
EdgeInterpolationPortalConst;
typedef vtkm::cont::DeviceAdapterAlgorithm<DeviceAdapter> Algorithm;
public:
struct TypeClipStats : vtkm::ListTagBase<ClipStats> { };
class ComputeStats : public vtkm::worklet::WorkletMapPointToCell
{
public:
typedef void ControlSignature(TopologyIn topology,
FieldInPoint<ScalarAll> scalars,
FieldOutCell<IdType> clipTableIdxs,
FieldOutCell<TypeClipStats> stats);
typedef void ExecutionSignature(_2, CellShape, FromCount, _3, _4);
VTKM_CONT_EXPORT
ComputeStats(vtkm::Float64 value, const ClipTablesPortal &clipTables)
: Value(value), ClipTables(clipTables)
{
}
template<typename ScalarsVecType, typename CellShapeTag>
VTKM_EXEC_EXPORT
void operator()(const ScalarsVecType &scalars,
CellShapeTag shape,
vtkm::Id count,
vtkm::Id& clipTableIdx,
ClipStats& stats) const
{
const vtkm::Id mask[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
vtkm::Id caseId = 0;
for (vtkm::IdComponent i = 0; i < count; ++i)
{
caseId |= (static_cast<vtkm::Float64>(scalars[i]) > this->Value) ?
mask[i] : 0;
}
vtkm::Id idx = this->ClipTables.GetCaseIndex(shape.Id, caseId);
clipTableIdx = idx;
vtkm::Id numberOfCells = this->ClipTables.ValueAt(idx++);
vtkm::Id numberOfIndices = 0;
vtkm::Id numberOfNewPoints = 0;
for (vtkm::Id cell = 0; cell < numberOfCells; ++cell)
{
++idx; // skip shape-id
vtkm::Id npts = this->ClipTables.ValueAt(idx++);
numberOfIndices += npts;
while (npts--)
{
// value < 100 means a new point needs to be generated by clipping an edge
numberOfNewPoints += (this->ClipTables.ValueAt(idx++) < 100) ? 1 : 0;
}
}
stats.NumberOfCells = numberOfCells;
stats.NumberOfIndices = numberOfIndices;
stats.NumberOfNewPoints = numberOfNewPoints;
}
private:
vtkm::Float64 Value;
ClipTablesPortal ClipTables;
};
class GenerateCellSet : public vtkm::worklet::WorkletMapPointToCell
{
public:
typedef void ControlSignature(TopologyIn topology,
FieldInPoint<ScalarAll> scalars,
FieldInCell<IdType> clipTableIdxs,
FieldInCell<TypeClipStats> cellSetIdxs,
ExecObject connectivityExplicit,
ExecObject interpolation,
ExecObject newPointsConnectivityReverseMap);
typedef void ExecutionSignature(CellShape, _2, FromIndices, _3, _4, _5, _6, _7);
VTKM_CONT_EXPORT
GenerateCellSet(vtkm::Float64 value, const ClipTablesPortal clipTables)
: Value(value), ClipTables(clipTables)
{
}
template<typename CellShapeTag,
typename ScalarsVecType,
typename IndicesVecType,
typename Storage>
VTKM_EXEC_EXPORT
void operator()(
CellShapeTag shape,
const ScalarsVecType &scalars,
const IndicesVecType &indices,
vtkm::Id clipTableIdx,
ClipStats cellSetIndices,
internal::ExecutionConnectivityExplicit<DeviceAdapter> connectivityExplicit,
vtkm::exec::ExecutionWholeArray<EdgeInterpolation, Storage, DeviceAdapter>
interpolation,
vtkm::exec::ExecutionWholeArray<vtkm::Id, Storage, DeviceAdapter>
newPointsConnectivityReverseMap) const
{
vtkm::Id idx = clipTableIdx;
// index of first cell
vtkm::Id cellIdx = cellSetIndices.NumberOfCells;
// index of first cell in connectivity array
vtkm::Id connectivityIdx = cellSetIndices.NumberOfIndices;
// index of new points generated by first cell
vtkm::Id newPtsIdx = cellSetIndices.NumberOfNewPoints;
vtkm::Id numberOfCells = this->ClipTables.ValueAt(idx++);
for (vtkm::Id cell = 0; cell < numberOfCells; ++cell, ++cellIdx)
{
connectivityExplicit.SetCellShape(cellIdx, this->ClipTables.ValueAt(idx++));
vtkm::IdComponent numPoints = this->ClipTables.ValueAt(idx++);
connectivityExplicit.SetNumberOfIndices(cellIdx, numPoints);
connectivityExplicit.SetIndexOffset(cellIdx, connectivityIdx);
for (vtkm::Id pt = 0; pt < numPoints; ++pt, ++idx)
{
vtkm::IdComponent entry =
static_cast<vtkm::IdComponent>(this->ClipTables.ValueAt(idx));
if (entry >= 100) // existing point
{
connectivityExplicit.SetConnectivity(connectivityIdx++,
indices[entry - 100]);
}
else // edge, new point to be generated by cutting the egde
{
internal::ClipTables::EdgeVec edge =
this->ClipTables.GetEdge(shape.Id, entry);
EdgeInterpolation ei;
ei.Vertex1 = indices[edge[0]];
ei.Vertex2 = indices[edge[1]];
if (ei.Vertex1 > ei.Vertex2)
{
internal::swap(ei.Vertex1, ei.Vertex2);
internal::swap(edge[0], edge[1]);
}
ei.Weight = (static_cast<vtkm::Float64>(scalars[edge[0]]) - this->Value) /
static_cast<vtkm::Float64>(scalars[edge[0]] - scalars[edge[1]]);
interpolation.Set(newPtsIdx , ei);
newPointsConnectivityReverseMap.Set(newPtsIdx, connectivityIdx++);
++newPtsIdx;
}
}
}
}
private:
vtkm::Float64 Value;
ClipTablesPortal ClipTables;
};
// The following can be done using DeviceAdapterAlgorithm::LowerBounds followed by
// a worklet for updating connectivity. We are going with a custom worklet, that
// combines lower-bounds computation and connectivity update, because this is
// currently faster and uses less memory.
class AmendConnectivity : public vtkm::exec::FunctorBase
{
public:
VTKM_CONT_EXPORT
AmendConnectivity(EdgeInterpolationPortalConst newPoints,
EdgeInterpolationPortalConst uniqueNewPoints,
IdPortalConst newPointsConnectivityReverseMap,
vtkm::Id newPointsOffset,
IdPortal connectivity)
: NewPoints(newPoints),
UniqueNewPoints(uniqueNewPoints),
NewPointsConnectivityReverseMap(newPointsConnectivityReverseMap),
NewPointsOffset(newPointsOffset),
Connectivity(connectivity)
{
}
VTKM_EXEC_EXPORT
void operator()(vtkm::Id idx) const
{
EdgeInterpolation current = this->NewPoints.Get(idx);
typename EdgeInterpolation::LessThanOp lt;
// find point index by looking up in the unique points array (binary search)
vtkm::Id count = UniqueNewPoints.GetNumberOfValues();
vtkm::Id first = 0;
while (count > 0)
{
vtkm::Id step = count/2;
vtkm::Id mid = first + step;
if (lt(this->UniqueNewPoints.Get(mid), current))
{
first = ++mid;
count -= step + 1;
}
else
{
count = step;
}
}
this->Connectivity.Set(this->NewPointsConnectivityReverseMap.Get(idx),
this->NewPointsOffset + first);
}
private:
EdgeInterpolationPortalConst NewPoints;
EdgeInterpolationPortalConst UniqueNewPoints;
IdPortalConst NewPointsConnectivityReverseMap;
vtkm::Id NewPointsOffset;
IdPortal Connectivity;
};
Clip()
: ClipTablesInstance(), NewPointsInterpolation(), NewPointsOffset()
{
}
template <typename ScalarsArrayHandle>
vtkm::cont::CellSetExplicit<> Run(const vtkm::cont::DynamicCellSet &cellSet,
const ScalarsArrayHandle &scalars,
vtkm::Float64 value)
{
DeviceAdapter device;
ClipTablesPortal clipTablesDevicePortal =
this->ClipTablesInstance.GetDevicePortal(device);
// Step 1. compute counts for the elements of the cell set data structure
vtkm::cont::ArrayHandle<vtkm::Id> clipTableIdxs;
vtkm::cont::ArrayHandle<ClipStats> stats;
ComputeStats computeStats(value, clipTablesDevicePortal);
DispatcherMapTopology<ComputeStats>(computeStats).Invoke(cellSet, scalars,
clipTableIdxs, stats);
// compute offsets for each invocation
ClipStats zero = { 0, 0, 0 };
vtkm::cont::ArrayHandle<ClipStats> cellSetIndices;
ClipStats total = Algorithm::ScanExclusive(stats, cellSetIndices,
ClipStats::SumOp(), zero);
stats.ReleaseResources();
// Step 2. generate the output cell set
vtkm::cont::ArrayHandle<vtkm::UInt8> shapes;
vtkm::cont::ArrayHandle<vtkm::IdComponent> numIndices;
vtkm::cont::ArrayHandle<vtkm::Id> connectivity;
vtkm::cont::ArrayHandle<vtkm::Id> cellToConnectivityMap;
internal::ExecutionConnectivityExplicit<DeviceAdapter> outConnectivity(
shapes.PrepareForOutput(total.NumberOfCells, device),
numIndices.PrepareForOutput(total.NumberOfCells, device),
connectivity.PrepareForOutput(total.NumberOfIndices, device),
cellToConnectivityMap.PrepareForOutput(total.NumberOfCells, device));
vtkm::cont::ArrayHandle<EdgeInterpolation> newPoints;
// reverse map from the new points to connectivity array
vtkm::cont::ArrayHandle<vtkm::Id> newPointsConnectivityReverseMap;
GenerateCellSet generateCellSet(value, clipTablesDevicePortal);
DispatcherMapTopology<GenerateCellSet>(generateCellSet).Invoke(cellSet, scalars,
clipTableIdxs, cellSetIndices, outConnectivity,
vtkm::exec::ExecutionWholeArray<
EdgeInterpolation,
VTKM_DEFAULT_STORAGE_TAG,
DeviceAdapter>(newPoints, total.NumberOfNewPoints),
vtkm::exec::ExecutionWholeArray<
vtkm::Id,
VTKM_DEFAULT_STORAGE_TAG,
DeviceAdapter>(newPointsConnectivityReverseMap, total.NumberOfNewPoints));
cellSetIndices.ReleaseResources();
// Step 3. remove duplicates from the list of new points
vtkm::cont::ArrayHandle<vtkm::worklet::EdgeInterpolation> uniqueNewPoints;
Algorithm::SortByKey(newPoints, newPointsConnectivityReverseMap,
EdgeInterpolation::LessThanOp());
Algorithm::Copy(newPoints, uniqueNewPoints);
Algorithm::Unique(uniqueNewPoints, EdgeInterpolation::EqualToOp());
this->NewPointsInterpolation = uniqueNewPoints;
this->NewPointsOffset = scalars.GetNumberOfValues();
// Step 4. update the connectivity array with indexes to the new, unique points
AmendConnectivity computeNewPointsConnectivity(
newPoints.PrepareForInput(device),
uniqueNewPoints.PrepareForInput(device),
newPointsConnectivityReverseMap.PrepareForInput(device),
this->NewPointsOffset,
connectivity.PrepareForInPlace(device));
Algorithm::Schedule(computeNewPointsConnectivity, total.NumberOfNewPoints);
vtkm::cont::CellSetExplicit<> output;
output.Fill(shapes, numIndices, connectivity);
return output;
}
template<typename ImplicitFunction>
class ClipWithImplicitFunction
{
public:
VTKM_CONT_EXPORT
ClipWithImplicitFunction(Clip *clipper, const vtkm::cont::DynamicCellSet &cellSet,
ImplicitFunction function, vtkm::cont::CellSetExplicit<> *result)
: Clipper(clipper), CellSet(&cellSet), Function(function), Result(result)
{ }
template <typename ArrayHandleType>
VTKM_CONT_EXPORT
void operator()(const ArrayHandleType &handle) const
{
vtkm::cont::ArrayHandleTransform<
vtkm::FloatDefault,
ArrayHandleType,
vtkm::ImplicitFunctionValue<ImplicitFunction> > clipScalars(handle,
this->Function);
*this->Result = this->Clipper->Run(*this->CellSet, clipScalars, 0.0);
}
private:
Clip *Clipper;
const vtkm::cont::DynamicCellSet *CellSet;
const vtkm::ImplicitFunctionValue<ImplicitFunction> Function;
vtkm::cont::CellSetExplicit<> *Result;
};
template <typename ImplicitFunction>
vtkm::cont::CellSetExplicit<> Run(const vtkm::cont::DynamicCellSet &cellSet,
const ImplicitFunction &clipFunction,
const vtkm::cont::CoordinateSystem &coords)
{
vtkm::cont::CellSetExplicit<> output;
ClipWithImplicitFunction<ImplicitFunction> clip(this, cellSet, clipFunction,
&output);
coords.GetData().CastAndCall(clip);
return output;
}
class InterpolateField
{
public:
template <typename T>
class Kernel : public vtkm::exec::FunctorBase
{
public:
typedef typename vtkm::cont::ArrayHandle<T>
::template ExecutionTypes<DeviceAdapter>::Portal FieldPortal;
VTKM_CONT_EXPORT
Kernel(EdgeInterpolationPortalConst interpolation,
vtkm::Id newPointsOffset,
FieldPortal field)
: Interpolation(interpolation), NewPointsOffset(newPointsOffset), Field(field)
{
}
VTKM_EXEC_EXPORT
void operator()(vtkm::Id idx) const
{
EdgeInterpolation ei = this->Interpolation.Get(idx);
T v1 = Field.Get(ei.Vertex1);
T v2 = Field.Get(ei.Vertex2);
Field.Set(this->NewPointsOffset + idx,
internal::Scale(T(v2 - v1), ei.Weight) + v1);
}
private:
EdgeInterpolationPortalConst Interpolation;
vtkm::Id NewPointsOffset;
FieldPortal Field;
};
template <typename T, typename Storage>
VTKM_CONT_EXPORT
void operator()(const vtkm::cont::ArrayHandle<T, Storage> &field) const
{
vtkm::Id count = this->InterpolationArray.GetNumberOfValues();
vtkm::cont::ArrayHandle<T> result;
internal::ResizeArrayHandle(field, field.GetNumberOfValues() + count,
result, DeviceAdapter());
Kernel<T> kernel(
this->InterpolationArray.PrepareForInput(DeviceAdapter()),
this->NewPointsOffset,
result.PrepareForInPlace(DeviceAdapter()));
Algorithm::Schedule(kernel, count);
*(this->Output) = vtkm::cont::DynamicArrayHandle(result);
}
VTKM_CONT_EXPORT
InterpolateField(
vtkm::cont::ArrayHandle<EdgeInterpolation> interpolationArray,
vtkm::Id newPointsOffset,
vtkm::cont::DynamicArrayHandle *output)
: InterpolationArray(interpolationArray),
NewPointsOffset(newPointsOffset),
Output(output)
{
}
private:
vtkm::cont::ArrayHandle<EdgeInterpolation> InterpolationArray;
vtkm::Id NewPointsOffset;
vtkm::cont::DynamicArrayHandle *Output;
};
template <typename DynamicArrayHandleType>
vtkm::cont::DynamicArrayHandle ProcessField(
const DynamicArrayHandleType &fieldData) const
{
vtkm::cont::DynamicArrayHandle output;
fieldData.CastAndCall(InterpolateField(this->NewPointsInterpolation,
this->NewPointsOffset,
&output));
return output;
}
private:
internal::ClipTables ClipTablesInstance;
vtkm::cont::ArrayHandle<EdgeInterpolation> NewPointsInterpolation;
vtkm::Id NewPointsOffset;
};
}
} // namespace vtkm::worklet
#if defined(THRUST_SCAN_WORKAROUND)
namespace thrust {
namespace detail {
// causes a different code path which does not have the bug
template<> struct is_integral<vtkm::worklet::ClipStats> : public true_type {};
}
} // namespace thrust::detail
#endif
#endif // vtkm_m_worklet_Clip_h