Fem: Add upstream vtkCleanUnstructuredGrid class source files

This commit is contained in:
marioalexis
2025-09-14 00:34:06 -03:00
parent 3e1741dfea
commit b9129f4453
6 changed files with 1449 additions and 6 deletions

View File

@@ -87,6 +87,14 @@ if(BUILD_FEM_VTK)
VTKExtensions/vtkFemFrameSourceAlgorithm.h
VTKExtensions/vtkFemFrameSourceAlgorithm.cpp
)
SET(VTK_SRCS_0903
VTKExtensions/vtkCleanUnstructuredGrid.h
VTKExtensions/vtkCleanUnstructuredGrid.cxx
VTKExtensions/vtkSMPTools.h
)
if (${VTK_MAJOR_VERSION} EQUAL 9 AND ${VTK_MINOR_VERSION} LESS 3)
list(APPEND FemVTK_SRCS ${VTK_SRCS_0903})
endif()
SOURCE_GROUP("VTKExtensions" FILES ${FemVTK_SRCS})
endif(BUILD_FEM_VTK)

View File

@@ -148,10 +148,14 @@
#include <gp_Vec.hxx>
// VTK
#include <vtkVersionMacros.h>
#include <vtkAlgorithmOutput.h>
#include <vtkAppendFilter.h>
#include <vtkArrayCalculator.h>
#include <vtkCellArray.h>
#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 0)
#include <vtkCleanUnstructuredGrid.h>
#endif
#include <vtkCompositeDataSet.h>
#include <vtkDataArray.h>
#include <vtkDataSetReader.h>

View File

@@ -1,6 +0,0 @@
SET(FemVTK_SRCS
vtkFemFrameSourceAlgorithm.h
vtkFemFrameSourceAlgorithm.cpp
)
target_sources(Fem PRIVATE ${FemVTK_SRCS})

View File

@@ -0,0 +1,680 @@
// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-FileCopyrightText: Copyright (c) Kitware, Inc.
// SPDX-License-Identifier: BSD-3-Clause
#include "PreCompiled.h"
#ifndef _PreComp_
#include <vtkArrayDispatch.h>
#include <vtkArrayDispatchArrayList.h>
#include <vtkBitArray.h>
#include <vtkCell.h>
#include <vtkCellData.h>
#include <vtkCellSizeFilter.h>
#include <vtkCellTypes.h>
#include <vtkCollection.h>
#include <vtkDataArrayRange.h>
#include <vtkDataSet.h>
#include <vtkDoubleArray.h>
#include <vtkIncrementalPointLocator.h>
#include <vtkInformation.h>
#include <vtkInformationVector.h>
#include <vtkIntArray.h>
#include <vtkMergePoints.h>
#include <vtkObjectFactory.h>
#include <vtkPointData.h>
#include <vtkPointSet.h>
#include <vtkPoints.h>
#include <vtkRectilinearGrid.h>
#include <vtkSMPThreadLocalObject.h>
#include <vtkStringArray.h>
#include <vtkUnstructuredGrid.h>
#include <unordered_set>
#endif
#include "vtkCleanUnstructuredGrid.h"
#include "vtkSMPTools.h"
namespace
{
int GetDimension(unsigned char type)
{
// For the most common cell types, this is a fast call. If the cell type is
// more exotic, then the cell must be grabbed and queried directly, which is
// slow.
switch (type) {
case VTK_EMPTY_CELL:
case VTK_VERTEX:
case VTK_POLY_VERTEX:
return 0;
case VTK_LINE:
case VTK_POLY_LINE:
case VTK_QUADRATIC_EDGE:
case VTK_CUBIC_LINE:
case VTK_LAGRANGE_CURVE:
case VTK_BEZIER_CURVE:
return 1;
case VTK_TRIANGLE:
case VTK_QUAD:
case VTK_PIXEL:
case VTK_POLYGON:
case VTK_TRIANGLE_STRIP:
case VTK_QUADRATIC_TRIANGLE:
case VTK_QUADRATIC_QUAD:
case VTK_QUADRATIC_POLYGON:
case VTK_BIQUADRATIC_QUAD:
case VTK_BIQUADRATIC_TRIANGLE:
case VTK_LAGRANGE_TRIANGLE:
case VTK_LAGRANGE_QUADRILATERAL:
case VTK_BEZIER_TRIANGLE:
case VTK_BEZIER_QUADRILATERAL:
return 2;
case VTK_TETRA:
case VTK_VOXEL:
case VTK_HEXAHEDRON:
case VTK_WEDGE:
case VTK_PYRAMID:
case VTK_PENTAGONAL_PRISM:
case VTK_HEXAGONAL_PRISM:
case VTK_QUADRATIC_TETRA:
case VTK_QUADRATIC_HEXAHEDRON:
case VTK_QUADRATIC_WEDGE:
case VTK_QUADRATIC_PYRAMID:
case VTK_BIQUADRATIC_QUADRATIC_HEXAHEDRON:
case VTK_BIQUADRATIC_QUADRATIC_WEDGE:
case VTK_TRIQUADRATIC_HEXAHEDRON:
case VTK_TRIQUADRATIC_PYRAMID:
case VTK_LAGRANGE_TETRAHEDRON:
case VTK_LAGRANGE_HEXAHEDRON:
case VTK_LAGRANGE_WEDGE:
case VTK_BEZIER_TETRAHEDRON:
case VTK_BEZIER_HEXAHEDRON:
case VTK_BEZIER_WEDGE:
return 3;
default:
vtkNew<vtkGenericCell> cell;
cell->SetCellType(type);
return cell->GetCellDimension();
}
}
constexpr unsigned char MAX_CELL_DIM = 3;
void AllocatePointAttributes(vtkPointData* inPD, vtkPointData* outPD, vtkIdType nPoints)
{
for (vtkIdType iArr = 0; iArr < inPD->GetNumberOfArrays(); ++iArr) {
auto inArr = inPD->GetAbstractArray(iArr);
auto outArr = outPD->GetAbstractArray(inArr->GetName());
if (!outArr) {
vtkGenericWarningMacro(<< inArr->GetName() << " output array is nullptr");
continue;
}
outArr->SetNumberOfComponents(inArr->GetNumberOfComponents());
outArr->SetNumberOfTuples(nPoints);
}
}
unsigned char GetTopologicalDimension(vtkDataSet* ds)
{
vtkNew<vtkCellTypes> cTypes;
ds->GetCellTypes(cTypes);
unsigned char topoDim = 0;
for (vtkIdType iC = 0; iC < cTypes->GetNumberOfTypes(); ++iC) {
unsigned char dimC = static_cast<unsigned char>(GetDimension(cTypes->GetCellType(iC)));
topoDim = std::max(topoDim, dimC);
if (topoDim >= MAX_CELL_DIM) {
break;
}
}
if (topoDim > MAX_CELL_DIM) {
vtkErrorWithObjectMacro(
nullptr,
"Topological dimension of data set is larger than the maximal cell dimension");
return MAX_CELL_DIM;
}
return topoDim;
}
struct WeighingStrategy
{
virtual ~WeighingStrategy() = default;
virtual vtkSmartPointer<vtkDoubleArray> ComputeWeights(vtkDataSet* ds,
const std::vector<vtkIdType>& ptMap) = 0;
};
struct FirstPointStrategy: public WeighingStrategy
{
vtkSmartPointer<vtkDoubleArray> ComputeWeights(vtkDataSet* ds,
const std::vector<vtkIdType>& ptMap) override
{
if (ds->GetNumberOfPoints() != static_cast<vtkIdType>(ptMap.size())) {
vtkGenericWarningMacro(
"Number of points in dataset and number of entries in point map don't line up.");
return nullptr;
}
vtkNew<vtkDoubleArray> weights;
weights->SetNumberOfComponents(1);
weights->SetNumberOfTuples(ds->GetNumberOfPoints());
weights->Fill(0.0);
std::unordered_set<vtkIdType> firstPoints;
for (vtkIdType iP = 0; iP < ds->GetNumberOfPoints(); ++iP) {
if (firstPoints.find(ptMap[iP]) != firstPoints.end()) {
continue;
}
firstPoints.insert(ptMap[iP]);
weights->SetValue(iP, 1.0);
}
return weights;
}
};
struct AveragingStrategy: public WeighingStrategy
{
vtkSmartPointer<vtkDoubleArray> ComputeWeights(vtkDataSet* ds,
const std::vector<vtkIdType>& ptMap) override
{
if (ds->GetNumberOfPoints() != static_cast<vtkIdType>(ptMap.size())) {
vtkGenericWarningMacro(
"Number of points in dataset and number of entries in point map don't line up.");
return nullptr;
}
std::vector<double> counts(ds->GetNumberOfPoints(), 0.0);
for (vtkIdType iP = 0; iP < ds->GetNumberOfPoints(); ++iP) {
if (ptMap[iP] < 0) {
continue;
}
counts[ptMap[iP]] += 1.0;
}
vtkNew<vtkDoubleArray> weights;
weights->SetNumberOfComponents(1);
weights->SetNumberOfTuples(ds->GetNumberOfPoints());
weights->Fill(0.0);
auto wRange = vtk::DataArrayValueRange<1>(weights);
for (vtkIdType iP = 0; iP < ds->GetNumberOfPoints(); ++iP) {
if (ptMap[iP] < 0) {
continue;
}
wRange[iP] = (counts[ptMap[iP]] ? 1.0 / counts[ptMap[iP]] : 0.0);
}
return weights;
}
};
struct SpatialDensityStrategy: public WeighingStrategy
{
vtkSmartPointer<vtkDoubleArray> ComputeWeights(vtkDataSet* ds,
const std::vector<vtkIdType>& ptMap) override
{
if (ds->GetNumberOfPoints() != static_cast<vtkIdType>(ptMap.size())) {
vtkGenericWarningMacro(
"Number of points in dataset and number of entries in point map don't line up.");
return nullptr;
}
// Get topological dimension of data set
auto topoDim = GetTopologicalDimension(ds);
// Calculate cell measures
vtkSmartPointer<vtkDataArray> measures;
{
// this scope is so that any extra memory related to the cell size filter gets released
// when no longer needed
vtkNew<vtkCellSizeFilter> cellSizeFilter;
cellSizeFilter->SetInputData(ds);
cellSizeFilter->Update();
auto output = vtkDataSet::SafeDownCast(cellSizeFilter->GetOutputDataObject(0));
auto cData = output->GetCellData();
if (!cData || !cData->HasArray("VertexCount") || !cData->HasArray("Length")
|| !cData->HasArray("Area") || !cData->HasArray("Volume")) {
vtkErrorWithObjectMacro(
nullptr,
"Could not find correct cell data in output of cell size filter");
return nullptr;
}
switch (topoDim) {
case 0:
measures = cData->GetArray("VertexCount");
break;
case 1:
measures = cData->GetArray("Length");
break;
case 2:
measures = cData->GetArray("Area");
break;
case 3:
measures = cData->GetArray("Volume");
break;
default:
vtkErrorWithObjectMacro(nullptr,
"Topological dimension of data set is higher than 3. "
"Cannot deal with that.");
return nullptr;
}
}
// Distribute spatial density to points
vtkNew<vtkDoubleArray> density;
density->SetNumberOfComponents(1);
density->SetNumberOfTuples(ds->GetNumberOfPoints());
density->Fill(0.0);
auto dRange = vtk::DataArrayValueRange<1>(density);
auto mRange = vtk::DataArrayValueRange<1>(measures);
if (ds->GetNumberOfCells() > 0) {
vtkNew<vtkIdList> pointIdList;
ds->GetCellPoints(0, pointIdList);
}
vtkSMPThreadLocalObject<vtkIdList> localPointIds;
auto distribute = [&](vtkIdType begin, vtkIdType end) {
for (vtkIdType iC = begin; iC < end; ++iC) {
ds->GetCellPoints(iC, localPointIds.Local());
double participation = mRange[iC] / localPointIds.Local()->GetNumberOfIds();
for (vtkIdType iP = 0; iP < localPointIds.Local()->GetNumberOfIds(); ++iP) {
dRange[localPointIds.Local()->GetId(iP)] += participation;
}
}
};
// For thread safety
if (ds->GetNumberOfCells() > 0) {
vtkNew<vtkIdList> buffer;
ds->GetCellPoints(0, buffer);
}
distribute(0, ds->GetNumberOfCells());
// Merits a dedicated struct with a reduce operation
// collisions occuring in the += operation
// vtkSMPTools::For(0, ds->GetNumberOfCells(), distribute);
// Normalize spatial densities with respect to point map
{
std::vector<double> masses(*std::max_element(ptMap.begin(), ptMap.end()) + 1, 0);
auto computeMasses = [&dRange, &masses, &ptMap](vtkIdType begin, vtkIdType end) {
for (vtkIdType iP = begin; iP < end; ++iP) {
if (ptMap[iP] < 0) {
dRange[iP] = 0.0;
continue;
}
masses[ptMap[iP]] += dRange[iP];
}
};
computeMasses(0, ds->GetNumberOfPoints());
// Merits a dedicated struct with a reduce operation
// collisions occuring in the += operation
// vtkSMPTools::For(0, ds->GetNumberOfPoints(), computeMasses);
vtkSMPTools::For(0,
ds->GetNumberOfPoints(),
[&dRange, &masses, &ptMap](vtkIdType begin, vtkIdType end) {
for (vtkIdType iP = begin; iP < end; ++iP) {
if (ptMap[iP] < 0) {
continue;
}
dRange[iP] =
(masses[ptMap[iP]] != 0 ? dRange[iP] / masses[ptMap[iP]]
: 0.0);
}
});
}
return density;
}
};
struct WeighingStrategyFactory
{
std::shared_ptr<WeighingStrategy> operator()(vtkCleanUnstructuredGrid::DataWeighingType dwt)
{
switch (dwt) {
case vtkCleanUnstructuredGrid::FIRST_POINT:
return std::make_shared<FirstPointStrategy>();
case vtkCleanUnstructuredGrid::AVERAGING:
return std::make_shared<AveragingStrategy>();
case vtkCleanUnstructuredGrid::SPATIAL_DENSITY:
return std::make_shared<SpatialDensityStrategy>();
default:
vtkGenericWarningMacro("Incorrect weighing strategy type passed to factory. "
"defaulting to FIRST_POINT.");
return std::make_shared<FirstPointStrategy>();
}
}
};
struct WeighingWorklet
{
template<typename ArrayTypeIn, typename ArrayTypeOut>
void operator()(ArrayTypeIn* inArray,
ArrayTypeOut* outArray,
vtkDoubleArray* weights,
const std::vector<vtkIdType>& ptMap)
{
outArray->Fill(0);
auto inRange = vtk::DataArrayTupleRange(inArray);
auto outRange = vtk::DataArrayTupleRange(outArray);
auto wRange = vtk::DataArrayValueRange<1>(weights);
auto weighing = [&](vtkIdType begin, vtkIdType end) {
for (vtkIdType iP = begin; iP < end; ++iP) {
if (ptMap[iP] < 0) {
continue;
}
auto inT = inRange[iP];
auto outT = outRange[ptMap[iP]];
for (vtkIdType iT = 0; iT < inArray->GetNumberOfComponents(); ++iT) {
outT[iT] += wRange[iP] * inT[iT];
}
}
};
weighing(0, inArray->GetNumberOfTuples());
// Merits a dedicated struct with a reduce operation
// collisions occuring in the += operation
// vtkSMPTools::For(0, inArray->GetNumberOfTuples(), weighing);
}
};
template<>
void WeighingWorklet::operator()(vtkBitArray* inArray,
vtkBitArray* outArray,
vtkDoubleArray* vtkNotUsed(weights),
const std::vector<vtkIdType>& ptMap)
{
outArray->Fill(0);
for (vtkIdType iP = 0; iP < inArray->GetNumberOfValues(); ++iP) {
if (ptMap[iP] < 0) {
continue;
}
outArray->SetValue(ptMap[iP], inArray->GetValue(iP));
}
}
template<>
void WeighingWorklet::operator()(vtkStringArray* inArray,
vtkStringArray* outArray,
vtkDoubleArray* vtkNotUsed(weights),
const std::vector<vtkIdType>& ptMap)
{
for (vtkIdType iP = 0; iP < inArray->GetNumberOfValues(); ++iP) {
if (ptMap[iP] < 0) {
continue;
}
outArray->SetValue(ptMap[iP], inArray->GetValue(iP));
}
}
template<>
void WeighingWorklet::operator()(vtkAbstractArray* inArray,
vtkAbstractArray* outArray,
vtkDoubleArray* vtkNotUsed(weights),
const std::vector<vtkIdType>& ptMap)
{
for (vtkIdType iP = 0; iP < inArray->GetNumberOfTuples(); ++iP) {
if (ptMap[iP] < 0) {
continue;
}
outArray->InsertTuple(ptMap[iP], iP, inArray);
}
}
void WeightAttributes(vtkPointData* inPD,
vtkPointData* outPD,
vtkDoubleArray* weights,
const std::vector<vtkIdType>& ptMap)
{
// better here to use a Dispatch2BySameArrayType, but that doesn't exist
using Dispatcher = vtkArrayDispatch::Dispatch2BySameValueType<vtkArrayDispatch::AllTypes>;
WeighingWorklet worker;
for (vtkIdType iArr = 0; iArr < inPD->GetNumberOfArrays(); ++iArr) {
auto inArr = inPD->GetArray(iArr);
// if not data array check for abstract
if (!inArr) {
auto inAbsArr = inPD->GetAbstractArray(iArr);
if (!inAbsArr) {
vtkGenericWarningMacro("One of the arrays in the point data is nullptr.");
continue;
}
auto outAbsArr = outPD->GetAbstractArray(inAbsArr->GetName());
if (!outAbsArr) {
vtkGenericWarningMacro("Output array " << inAbsArr->GetName() << " is nullptr.");
continue;
}
// if string array go to dedicated path
auto inStrArr = vtkStringArray::SafeDownCast(inAbsArr);
if (inStrArr) {
auto outStrArr = vtkStringArray::SafeDownCast(outAbsArr);
if (!outStrArr) {
vtkGenericWarningMacro("Output array "
<< inStrArr->GetName()
<< " is not the same type as input string array.");
continue;
}
worker(inStrArr, outStrArr, weights, ptMap);
continue;
}
worker(inAbsArr, outAbsArr, weights, ptMap);
continue;
}
auto outArr = outPD->GetArray(inArr->GetName());
if (!outArr) {
vtkGenericWarningMacro("Output array " << inArr->GetName()
<< " is nullptr or not a vtkDataArray.");
continue;
}
if (!Dispatcher::Execute(inArr, outArr, worker, weights, ptMap)) {
auto inBitArr = vtkBitArray::SafeDownCast(inArr);
auto outBitArr = vtkBitArray::SafeDownCast(outArr);
if (inBitArr && outBitArr) {
worker(inBitArr, outBitArr, weights, ptMap);
}
worker(inArr, outArr, weights, ptMap);
}
}
}
} // namespace
vtkStandardNewMacro(vtkCleanUnstructuredGrid);
vtkCxxSetSmartPointerMacro(vtkCleanUnstructuredGrid, Locator, vtkIncrementalPointLocator);
//----------------------------------------------------------------------------
vtkCleanUnstructuredGrid::vtkCleanUnstructuredGrid() = default;
//----------------------------------------------------------------------------
vtkCleanUnstructuredGrid::~vtkCleanUnstructuredGrid() = default;
//----------------------------------------------------------------------------
void vtkCleanUnstructuredGrid::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
if (this->Locator) {
os << indent << "Locator: ";
this->Locator->PrintSelf(os, indent.GetNextIndent());
}
else {
os << indent << "Locator: none\n";
}
os << indent << "ToleranceIsAbsolute: " << (this->ToleranceIsAbsolute ? "On\n" : "Off\n");
os << indent << "Tolerance: " << this->Tolerance << std::endl;
os << indent << "AbsoluteTolerance: " << this->AbsoluteTolerance << std::endl;
os << indent
<< "RemovePointsWithoutCells: " << (this->RemovePointsWithoutCells ? "On\n" : "Off\n");
os << indent << "OutputPointsPrecision: " << this->OutputPointsPrecision << std::endl;
}
//----------------------------------------------------------------------------
int vtkCleanUnstructuredGrid::RequestData(vtkInformation* vtkNotUsed(request),
vtkInformationVector** inputVector,
vtkInformationVector* outputVector)
{
vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
vtkInformation* outInfo = outputVector->GetInformationObject(0);
vtkDataSet* input = vtkDataSet::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()));
vtkUnstructuredGrid* output =
vtkUnstructuredGrid::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));
if (input->GetNumberOfCells() == 0) {
// set up a ugrid with same data arrays as input, but
// no points, cells or data.
output->Allocate(1);
output->GetPointData()->CopyAllocate(input->GetPointData(), VTK_CELL_SIZE);
output->GetCellData()->CopyAllocate(input->GetCellData(), 1);
vtkNew<vtkPoints> pts;
output->SetPoints(pts);
return 1;
}
output->GetPointData()->CopyAllocate(input->GetPointData());
output->GetCellData()->PassData(input->GetCellData());
// First, create a new points array that eliminate duplicate points.
// Also create a mapping from the old point id to the new.
vtkNew<vtkPoints> newPts;
// Set the desired precision for the points in the output.
if (this->OutputPointsPrecision == vtkAlgorithm::DEFAULT_PRECISION) {
// The logical behaviour would be to use the data type from the input.
// However, input is a vtkDataSet, which has no point data type; only the
// derived class vtkPointSet has a vtkPoints attribute, so only for that
// the logical practice can be applied, while for others (currently
// vtkImageData and vtkRectilinearGrid) the data type is the default
// for vtkPoints - which is VTK_FLOAT.
vtkPointSet* ps = vtkPointSet::SafeDownCast(input);
if (ps) {
newPts->SetDataType(ps->GetPoints()->GetDataType());
}
}
else if (this->OutputPointsPrecision == vtkAlgorithm::SINGLE_PRECISION) {
newPts->SetDataType(VTK_FLOAT);
}
else if (this->OutputPointsPrecision == vtkAlgorithm::DOUBLE_PRECISION) {
newPts->SetDataType(VTK_DOUBLE);
}
vtkIdType num = input->GetNumberOfPoints();
vtkIdType id;
vtkIdType newId;
std::vector<vtkIdType> ptMap(num);
double pt[3];
this->CreateDefaultLocator(input);
if (this->ToleranceIsAbsolute) {
this->Locator->SetTolerance(this->AbsoluteTolerance);
}
else {
this->Locator->SetTolerance(this->Tolerance * input->GetLength());
}
double bounds[6];
input->GetBounds(bounds);
this->Locator->InitPointInsertion(newPts, bounds);
vtkIdType progressStep = num / 100;
if (progressStep == 0) {
progressStep = 1;
}
vtkNew<vtkIdList> pointCells;
for (id = 0; id < num; ++id) {
if (id % progressStep == 0) {
this->UpdateProgress(0.8 * ((float)id / num));
}
bool insert = true;
if (this->RemovePointsWithoutCells) {
input->GetPointCells(id, pointCells);
if (pointCells->GetNumberOfIds() == 0) {
insert = false;
}
}
if (insert) {
input->GetPoint(id, pt);
this->Locator->InsertUniquePoint(pt, newId);
ptMap[id] = newId;
}
else {
// Strictly speaking, this is not needed
// as this is never accessed, but better not let
// an id undefined.
ptMap[id] = -1;
}
}
output->SetPoints(newPts);
::WeighingStrategyFactory factory;
auto strategy = factory(static_cast<DataWeighingType>(this->PointDataWeighingStrategy));
auto weights = strategy->ComputeWeights(input, ptMap);
auto inPD = input->GetPointData();
auto outPD = output->GetPointData();
::AllocatePointAttributes(inPD, outPD, output->GetNumberOfPoints());
::WeightAttributes(inPD, outPD, weights, ptMap);
// Now copy the cells.
vtkNew<vtkIdList> cellPoints;
num = input->GetNumberOfCells();
output->Allocate(num);
for (id = 0; id < num; ++id) {
if (id % progressStep == 0) {
this->UpdateProgress(0.8 + 0.2 * (static_cast<float>(id) / num));
}
// special handling for polyhedron cells
if (vtkUnstructuredGrid::SafeDownCast(input) && input->GetCellType(id) == VTK_POLYHEDRON) {
vtkUnstructuredGrid::SafeDownCast(input)->GetFaceStream(id, cellPoints);
vtkUnstructuredGrid::ConvertFaceStreamPointIds(cellPoints, ptMap.data());
}
else {
input->GetCellPoints(id, cellPoints);
for (int i = 0; i < cellPoints->GetNumberOfIds(); i++) {
int cellPtId = cellPoints->GetId(i);
newId = ptMap[cellPtId];
cellPoints->SetId(i, newId);
}
}
output->InsertNextCell(input->GetCellType(id), cellPoints);
}
output->Squeeze();
return 1;
}
//----------------------------------------------------------------------------
int vtkCleanUnstructuredGrid::FillInputPortInformation(int vtkNotUsed(port), vtkInformation* info)
{
info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet");
return 1;
}
//------------------------------------------------------------------------------
vtkIncrementalPointLocator* vtkCleanUnstructuredGrid::GetLocator()
{
return this->Locator;
}
//----------------------------------------------------------------------------
void vtkCleanUnstructuredGrid::CreateDefaultLocator(vtkDataSet* input)
{
double tol;
if (this->ToleranceIsAbsolute) {
tol = this->AbsoluteTolerance;
}
else {
if (input) {
tol = this->Tolerance * input->GetLength();
}
else {
tol = this->Tolerance;
}
}
if (this->Locator.Get() == nullptr) {
if (tol == 0.0) {
this->Locator = vtkSmartPointer<vtkMergePoints>::New();
}
else {
this->Locator = vtkSmartPointer<vtkPointLocator>::New();
}
}
else {
// check that the tolerance wasn't changed from zero to non-zero
if ((tol > 0.0) && (this->GetLocator()->GetTolerance() == 0.0)) {
this->Locator = vtkSmartPointer<vtkPointLocator>::New();
}
}
}

View File

@@ -0,0 +1,153 @@
// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-FileCopyrightText: Copyright (c) Kitware, Inc.
// SPDX-License-Identifier: BSD-3-Clause
/**
* @class vtkCleanUnstructuredGrid
* @brief merge duplicate points
*
*
* vtkCleanUnstructuredGrid is a filter that takes unstructured grid data as
* input and generates unstructured grid data as output. vtkCleanUnstructuredGrid can
* merge duplicate points (with coincident coordinates) using the vtkMergePoints object
* to merge points.
*
* @sa
* vtkCleanPolyData
*/
#ifndef vtkCleanUnstructuredGrid_h
#define vtkCleanUnstructuredGrid_h
#include "vtkFiltersGeneralModule.h" // For export macro
#include "vtkSmartPointer.h"
#include "vtkUnstructuredGridAlgorithm.h"
/*VTK_ABI_NAMESPACE_BEGIN*/
class vtkIncrementalPointLocator;
class vtkDataSet;
class /*VTKFILTERSGENERAL_EXPORT*/ vtkCleanUnstructuredGrid: public vtkUnstructuredGridAlgorithm
{
public:
static vtkCleanUnstructuredGrid* New();
vtkTypeMacro(vtkCleanUnstructuredGrid, vtkUnstructuredGridAlgorithm);
void PrintSelf(ostream& os, vtkIndent indent) override;
///@{
/**
* By default ToleranceIsAbsolute is false and Tolerance is
* a fraction of Bounding box diagonal, if true, AbsoluteTolerance is
* used when adding points to locator (merging)
*/
vtkSetMacro(ToleranceIsAbsolute, bool);
vtkBooleanMacro(ToleranceIsAbsolute, bool);
vtkGetMacro(ToleranceIsAbsolute, bool);
///@}
///@{
/**
* Specify tolerance in terms of fraction of bounding box length.
* Default is 0.0.
*/
vtkSetClampMacro(Tolerance, double, 0.0, 1.0);
vtkGetMacro(Tolerance, double);
///@}
///@{
/**
* Specify tolerance in absolute terms. Default is 1.0.
*/
vtkSetClampMacro(AbsoluteTolerance, double, 0.0, VTK_DOUBLE_MAX);
vtkGetMacro(AbsoluteTolerance, double);
///@}
///@{
/**
* Set/Get a spatial locator for speeding the search process. By
* default an instance of vtkMergePoints is used.
*/
virtual void SetLocator(vtkIncrementalPointLocator* locator);
virtual vtkIncrementalPointLocator* GetLocator();
///@}
/**
* Create default locator. Used to create one when none is specified.
*/
void CreateDefaultLocator(vtkDataSet* input = nullptr);
/**
* Release locator
*/
void ReleaseLocator()
{
this->SetLocator(nullptr);
}
///@{
/**
* Set/get the desired precision for the output types. See the documentation
* for the vtkAlgorithm::DesiredOutputPrecision enum for an explanation of
* the available precision settings.
*/
vtkSetMacro(OutputPointsPrecision, int);
vtkGetMacro(OutputPointsPrecision, int);
///@}
///@{
/**
* Set/Get whether to remove points that do not
* have any cells associated with it.
* Default is false
*/
vtkSetMacro(RemovePointsWithoutCells, bool);
vtkGetMacro(RemovePointsWithoutCells, bool);
vtkBooleanMacro(RemovePointsWithoutCells, bool);
///@}
///@{
/**
* Set/Get the strategy used to weigh point data on merging points
*
* Possibilities:
* - FIRST_POINT (int(0), default): the point with the lowest index imposes its data on to the
* merged point
* - AVERAGING (int(1)): a number average is performed on all the duplicate points
* - SPATIAL_DENSITY (int(2)): an average by attached cell volume (i.e. for every cell the point
* is connected to sum cell_volume/number_cell_points) is performed on the point data
*/
vtkGetMacro(PointDataWeighingStrategy, int);
vtkSetClampMacro(PointDataWeighingStrategy, int, FIRST_POINT, NUMBER_OF_WEIGHING_TYPES - 1);
///@}
enum DataWeighingType
{
FIRST_POINT = 0,
AVERAGING,
SPATIAL_DENSITY,
NUMBER_OF_WEIGHING_TYPES
};
protected:
vtkCleanUnstructuredGrid();
~vtkCleanUnstructuredGrid() override;
bool ToleranceIsAbsolute = false;
double Tolerance = 0.0;
double AbsoluteTolerance = 1.0;
bool RemovePointsWithoutCells = false;
vtkSmartPointer<vtkIncrementalPointLocator> Locator;
int OutputPointsPrecision = vtkAlgorithm::DEFAULT_PRECISION;
int PointDataWeighingStrategy = FIRST_POINT;
int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override;
int FillInputPortInformation(int port, vtkInformation* info) override;
private:
vtkCleanUnstructuredGrid(const vtkCleanUnstructuredGrid&) = delete;
void operator=(const vtkCleanUnstructuredGrid&) = delete;
};
/*VTK_ABI_NAMESPACE_END*/
#endif
// VTK-HeaderTest-Exclude: vtkCleanUnstructuredGrid.h

View File

@@ -0,0 +1,604 @@
/*=========================================================================
Program: Visualization Toolkit
Module: vtkSMPTools.h
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm 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.
=========================================================================*/
/**
* @class vtkSMPTools
* @brief A set of parallel (multi-threaded) utility functions.
*
* vtkSMPTools provides a set of utility functions that can
* be used to parallelize parts of VTK code using multiple threads.
* There are several back-end implementations of parallel functionality
* (currently Sequential, TBB, OpenMP and STDThread) that actual execution is
* delegated to.
*
* @sa
* vtkSMPThreadLocal
* vtkSMPThreadLocalObject
*/
#ifndef vtkSMPTools_h
#define vtkSMPTools_h
#include "vtkCommonCoreModule.h" // For export macro
#include "vtkObject.h"
#include "SMP/Common/vtkSMPToolsAPI.h"
#include "vtkSMPThreadLocal.h" // For Initialized
#include <functional> // For std::function
#include <iterator> // For std::iterator
#include <type_traits> // For std:::enable_if
#ifndef DOXYGEN_SHOULD_SKIP_THIS
namespace vtk
{
namespace detail
{
namespace smp
{
template<typename T>
class vtkSMPTools_Has_Initialize
{
typedef char (&no_type)[1];
typedef char (&yes_type)[2];
template<typename U, void (U::*)()>
struct V
{
};
template<typename U>
static yes_type check(V<U, &U::Initialize>*);
template<typename U>
static no_type check(...);
public:
static bool const value = sizeof(check<T>(nullptr)) == sizeof(yes_type);
};
template<typename T>
class vtkSMPTools_Has_Initialize_const
{
typedef char (&no_type)[1];
typedef char (&yes_type)[2];
template<typename U, void (U::*)() const>
struct V
{
};
template<typename U>
static yes_type check(V<U, &U::Initialize>*);
template<typename U>
static no_type check(...);
public:
static bool const value = sizeof(check<T>(0)) == sizeof(yes_type);
};
template<typename Functor, bool Init>
struct vtkSMPTools_FunctorInternal;
template<typename Functor>
struct vtkSMPTools_FunctorInternal<Functor, false>
{
Functor& F;
vtkSMPTools_FunctorInternal(Functor& f)
: F(f)
{}
void Execute(vtkIdType first, vtkIdType last)
{
this->F(first, last);
}
void For(vtkIdType first, vtkIdType last, vtkIdType grain)
{
auto& SMPToolsAPI = vtkSMPToolsAPI::GetInstance();
SMPToolsAPI.For(first, last, grain, *this);
}
vtkSMPTools_FunctorInternal<Functor, false>&
operator=(const vtkSMPTools_FunctorInternal<Functor, false>&);
vtkSMPTools_FunctorInternal(const vtkSMPTools_FunctorInternal<Functor, false>&);
};
template<typename Functor>
struct vtkSMPTools_FunctorInternal<Functor, true>
{
Functor& F;
vtkSMPThreadLocal<unsigned char> Initialized;
vtkSMPTools_FunctorInternal(Functor& f)
: F(f)
, Initialized(0)
{}
void Execute(vtkIdType first, vtkIdType last)
{
unsigned char& inited = this->Initialized.Local();
if (!inited) {
this->F.Initialize();
inited = 1;
}
this->F(first, last);
}
void For(vtkIdType first, vtkIdType last, vtkIdType grain)
{
auto& SMPToolsAPI = vtkSMPToolsAPI::GetInstance();
SMPToolsAPI.For(first, last, grain, *this);
this->F.Reduce();
}
vtkSMPTools_FunctorInternal<Functor, true>&
operator=(const vtkSMPTools_FunctorInternal<Functor, true>&);
vtkSMPTools_FunctorInternal(const vtkSMPTools_FunctorInternal<Functor, true>&);
};
template<typename Functor>
class vtkSMPTools_Lookup_For
{
static bool const init = vtkSMPTools_Has_Initialize<Functor>::value;
public:
typedef vtkSMPTools_FunctorInternal<Functor, init> type;
};
template<typename Functor>
class vtkSMPTools_Lookup_For<Functor const>
{
static bool const init = vtkSMPTools_Has_Initialize_const<Functor>::value;
public:
typedef vtkSMPTools_FunctorInternal<Functor const, init> type;
};
template<typename Iterator, typename Functor, bool Init>
struct vtkSMPTools_RangeFunctor;
template<typename Iterator, typename Functor>
struct vtkSMPTools_RangeFunctor<Iterator, Functor, false>
{
Functor& F;
Iterator& Begin;
vtkSMPTools_RangeFunctor(Iterator& begin, Functor& f)
: F(f)
, Begin(begin)
{}
void operator()(vtkIdType first, vtkIdType last)
{
Iterator itFirst(Begin);
std::advance(itFirst, first);
Iterator itLast(itFirst);
std::advance(itLast, last - first);
this->F(itFirst, itLast);
}
};
template<typename Iterator, typename Functor>
struct vtkSMPTools_RangeFunctor<Iterator, Functor, true>
{
Functor& F;
Iterator& Begin;
vtkSMPTools_RangeFunctor(Iterator& begin, Functor& f)
: F(f)
, Begin(begin)
{}
void Initialize()
{
this->F.Initialize();
}
void operator()(vtkIdType first, vtkIdType last)
{
Iterator itFirst(Begin);
std::advance(itFirst, first);
Iterator itLast(itFirst);
std::advance(itLast, last - first);
this->F(itFirst, itLast);
}
void Reduce()
{
this->F.Reduce();
}
};
template<typename Iterator, typename Functor>
class vtkSMPTools_Lookup_RangeFor
{
static bool const init = vtkSMPTools_Has_Initialize<Functor>::value;
public:
typedef vtkSMPTools_RangeFunctor<Iterator, Functor, init> type;
};
template<typename Iterator, typename Functor>
class vtkSMPTools_Lookup_RangeFor<Iterator, Functor const>
{
static bool const init = vtkSMPTools_Has_Initialize_const<Functor>::value;
public:
typedef vtkSMPTools_RangeFunctor<Iterator, Functor const, init> type;
};
template<typename T>
using resolvedNotInt = typename std::enable_if<!std::is_integral<T>::value, void>::type;
} // namespace smp
} // namespace detail
} // namespace vtk
#endif // DOXYGEN_SHOULD_SKIP_THIS
class VTKCOMMONCORE_EXPORT vtkSMPTools
{
public:
///@{
/**
* Execute a for operation in parallel. First and last
* define the range over which to operate (which is defined
* by the operator). The operation executed is defined by
* operator() of the functor object. The grain gives the parallel
* engine a hint about the coarseness over which to parallelize
* the function (as defined by last-first of each execution of
* operator() ).
*/
template<typename Functor>
static void For(vtkIdType first, vtkIdType last, vtkIdType grain, Functor& f)
{
typename vtk::detail::smp::vtkSMPTools_Lookup_For<Functor>::type fi(f);
fi.For(first, last, grain);
}
template<typename Functor>
static void For(vtkIdType first, vtkIdType last, vtkIdType grain, Functor const& f)
{
typename vtk::detail::smp::vtkSMPTools_Lookup_For<Functor const>::type fi(f);
fi.For(first, last, grain);
}
///@}
///@{
/**
* Execute a for operation in parallel. First and last
* define the range over which to operate (which is defined
* by the operator). The operation executed is defined by
* operator() of the functor object. The grain gives the parallel
* engine a hint about the coarseness over which to parallelize
* the function (as defined by last-first of each execution of
* operator() ). Uses a default value for the grain.
*/
template<typename Functor>
static void For(vtkIdType first, vtkIdType last, Functor& f)
{
vtkSMPTools::For(first, last, 0, f);
}
template<typename Functor>
static void For(vtkIdType first, vtkIdType last, Functor const& f)
{
vtkSMPTools::For(first, last, 0, f);
}
///@}
///@{
/**
* Execute a for operation in parallel. Begin and end iterators
* define the range over which to operate (which is defined
* by the operator). The operation executed is defined by
* operator() of the functor object. The grain gives the parallel
* engine a hint about the coarseness over which to parallelize
* the function (as defined by last-first of each execution of
* operator() ).
*
* Usage example:
* \code
* template<class IteratorT>
* class ExampleFunctor
* {
* void operator()(IteratorT begin, IteratorT end)
* {
* for (IteratorT it = begin; it != end; ++it)
* {
* // Do stuff
* }
* }
* };
* ExampleFunctor<std::set<int>::iterator> worker;
* vtkSMPTools::For(container.begin(), container.end(), 5, worker);
* \endcode
*
* Lambda are also supported through Functor const&
* function overload:
* \code
* vtkSMPTools::For(container.begin(), container.end(), 5,
* [](std::set<int>::iterator begin, std::set<int>::iterator end) {
* // Do stuff
* });
* \endcode
*/
template<typename Iter, typename Functor>
static vtk::detail::smp::resolvedNotInt<Iter>
For(Iter begin, Iter end, vtkIdType grain, Functor& f)
{
vtkIdType size = std::distance(begin, end);
typename vtk::detail::smp::vtkSMPTools_Lookup_RangeFor<Iter, Functor>::type fi(begin, f);
vtkSMPTools::For(0, size, grain, fi);
}
template<typename Iter, typename Functor>
static vtk::detail::smp::resolvedNotInt<Iter>
For(Iter begin, Iter end, vtkIdType grain, Functor const& f)
{
vtkIdType size = std::distance(begin, end);
typename vtk::detail::smp::vtkSMPTools_Lookup_RangeFor<Iter, Functor const>::type fi(begin,
f);
vtkSMPTools::For(0, size, grain, fi);
}
///@}
///@{
/**
* Execute a for operation in parallel. Begin and end iterators
* define the range over which to operate (which is defined
* by the operator). The operation executed is defined by
* operator() of the functor object. Uses a default value
* for the grain.
*
* Usage example:
* \code
* template<class IteratorT>
* class ExampleFunctor
* {
* void operator()(IteratorT begin, IteratorT end)
* {
* for (IteratorT it = begin; it != end; ++it)
* {
* // Do stuff
* }
* }
* };
* ExampleFunctor<std::set<int>::iterator> worker;
* vtkSMPTools::For(container.begin(), container.end(), worker);
* \endcode
*
* Lambda are also supported through Functor const&
* function overload:
* \code
* vtkSMPTools::For(container.begin(), container.end(),
* [](std::set<int>::iterator begin, std::set<int>::iterator end) {
* // Do stuff
* });
* \endcode
*/
template<typename Iter, typename Functor>
static vtk::detail::smp::resolvedNotInt<Iter> For(Iter begin, Iter end, Functor& f)
{
vtkSMPTools::For(begin, end, 0, f);
}
template<typename Iter, typename Functor>
static vtk::detail::smp::resolvedNotInt<Iter> For(Iter begin, Iter end, Functor const& f)
{
vtkSMPTools::For(begin, end, 0, f);
}
///@}
/**
* Get the backend in use.
*/
static const char* GetBackend();
/**
* /!\ This method is not thread safe.
* Change the backend in use.
* The options can be: "Sequential", "STDThread", "TBB" or "OpenMP"
*
* VTK_SMP_BACKEND_IN_USE env variable can also be used to set the default SMPTools
* backend, in that case SetBackend() doesn't need to be called.
* The backend selected with SetBackend() have the priority over VTK_SMP_BACKEND_IN_USE.
*
* SetBackend() will return true if the backend was found and available.
*/
static bool SetBackend(const char* backend);
/**
* /!\ This method is not thread safe.
* Initialize the underlying libraries for execution. This is
* not required as it is automatically defined by the libaries.
* However, it can be used to control the maximum number of thread used.
* Make sure to call it before the parallel operation.
*
* If Initialize is called without argument it will reset
* to the maximum number of threads or use the VTK_SMP_MAX_THREADS
* env variable if it is defined.
*
* Note: If VTK_SMP_MAX_THREADS env variable is defined the SMPTools will try
* to use it to set the maximum number of threads. Initialize() doesn't
* need to be called.
*/
static void Initialize(int numThreads = 0);
/**
* Get the estimated number of threads being used by the backend.
* This should be used as just an estimate since the number of threads may
* vary dynamically and a particular task may not be executed on all the
* available threads.
*/
static int GetEstimatedNumberOfThreads();
/**
* /!\ This method is not thread safe.
* If true enable nested parallelism for underlying backends.
* When enabled the comportement is different for each backend:
* - TBB support nested parallelism by default.
* - For OpenMP, we set `omp_set_nested` to true so that it is supported.
* - STDThread support nested parallelism by creating new threads pools.
* - For Sequential nothing changes.
*
* Default to true
*/
static void SetNestedParallelism(bool isNested);
/**
* Get true if the nested parallelism is enabled.
*/
static bool GetNestedParallelism();
/**
* Return true if it is called from a parallel scope.
*/
static bool IsParallelScope();
/**
* Structure used to specify configuration for LocalScope() method.
* Several parameters can be configured:
* - MaxNumberOfThreads set the maximum number of threads.
* - Backend set a specific SMPTools backend.
* - NestedParallelism, if true enable nested parallelism.
*/
struct Config
{
int MaxNumberOfThreads = 0;
std::string Backend = vtk::detail::smp::vtkSMPToolsAPI::GetInstance().GetBackend();
bool NestedParallelism = true;
Config()
{}
Config(int maxNumberOfThreads)
: MaxNumberOfThreads(maxNumberOfThreads)
{}
Config(std::string backend)
: Backend(backend)
{}
Config(bool nestedParallelism)
: NestedParallelism(nestedParallelism)
{}
Config(int maxNumberOfThreads, std::string backend, bool nestedParallelism)
: MaxNumberOfThreads(maxNumberOfThreads)
, Backend(backend)
, NestedParallelism(nestedParallelism)
{}
#ifndef DOXYGEN_SHOULD_SKIP_THIS
Config(vtk::detail::smp::vtkSMPToolsAPI& API)
: MaxNumberOfThreads(API.GetInternalDesiredNumberOfThread())
, Backend(API.GetBackend())
, NestedParallelism(API.GetNestedParallelism())
{}
#endif // DOXYGEN_SHOULD_SKIP_THIS
};
/**
* /!\ This method is not thread safe.
* Change the number of threads locally within this scope and call a functor which
* should contains a vtkSMPTools method.
*
* Usage example:
* \code
* vtkSMPTools::LocalScope(
* vtkSMPTools::Config{ 4, "OpenMP", false }, [&]() { vtkSMPTools::For(0, size, worker); });
* \endcode
*/
template<typename T>
static void LocalScope(Config const& config, T&& lambda)
{
auto& SMPToolsAPI = vtk::detail::smp::vtkSMPToolsAPI::GetInstance();
SMPToolsAPI.LocalScope<vtkSMPTools::Config>(config, lambda);
}
/**
* A convenience method for transforming data. It is a drop in replacement for
* std::transform(), it does a unary operation on the input ranges. The data array must have the
* same length. The performed transformation is defined by operator() of the functor object.
*
* Usage example with vtkDataArray:
* \code
* const auto range0 = vtk::DataArrayValueRange<1>(array0);
* auto range1 = vtk::DataArrayValueRange<1>(array1);
* vtkSMPTools::Transform(
* range0.cbegin(), range0.cend(), range1.begin(), [](double x) { return x - 1; });
* \endcode
*
* Please visit vtkDataArrayRange.h documentation for more information and optimisation.
*/
template<typename InputIt, typename OutputIt, typename Functor>
static void Transform(InputIt inBegin, InputIt inEnd, OutputIt outBegin, Functor transform)
{
auto& SMPToolsAPI = vtk::detail::smp::vtkSMPToolsAPI::GetInstance();
SMPToolsAPI.Transform(inBegin, inEnd, outBegin, transform);
}
/**
* A convenience method for transforming data. It is a drop in replacement for
* std::transform(), it does a binary operation on the input ranges. The data array must have
* the same length. The performed transformation is defined by operator() of the functor object.
*
* Usage example with vtkDataArray:
* \code
* const auto range0 = vtk::DataArrayValueRange<1>(array0);
* auto range1 = vtk::DataArrayValueRange<1>(array1);
* vtkSMPTools::Transform(
* range0.cbegin(), range0.cend(), range1.cbegin(), range1.begin(),
* [](double x, double y) { return x * y; });
* \endcode
*
* Please visit vtkDataArrayRange.h documentation for more information and optimisation.
*/
template<typename InputIt1, typename InputIt2, typename OutputIt, typename Functor>
static void Transform(InputIt1 inBegin1,
InputIt1 inEnd,
InputIt2 inBegin2,
OutputIt outBegin,
Functor transform)
{
auto& SMPToolsAPI = vtk::detail::smp::vtkSMPToolsAPI::GetInstance();
SMPToolsAPI.Transform(inBegin1, inEnd, inBegin2, outBegin, transform);
}
/**
* A convenience method for filling data. It is a drop in replacement for std::fill(),
* it assign the given value to the element in ranges.
*
* Usage example with vtkDataArray:
* \code
* // Fill range with its first tuple value
* auto range = vtk::DataArrayTupleRange<1>(array);
* const auto value = *range.begin();
* vtkSMPTools::Fill(range.begin(), range.end(), value);
* \endcode
*
* Please visit vtkDataArrayRange.h documentation for more information and optimisation.
*/
template<typename Iterator, typename T>
static void Fill(Iterator begin, Iterator end, const T& value)
{
auto& SMPToolsAPI = vtk::detail::smp::vtkSMPToolsAPI::GetInstance();
SMPToolsAPI.Fill(begin, end, value);
}
/**
* A convenience method for sorting data. It is a drop in replacement for
* std::sort(). Under the hood different methods are used. For example,
* tbb::parallel_sort is used in TBB.
*/
template<typename RandomAccessIterator>
static void Sort(RandomAccessIterator begin, RandomAccessIterator end)
{
auto& SMPToolsAPI = vtk::detail::smp::vtkSMPToolsAPI::GetInstance();
SMPToolsAPI.Sort(begin, end);
}
/**
* A convenience method for sorting data. It is a drop in replacement for
* std::sort(). Under the hood different methods are used. For example,
* tbb::parallel_sort is used in TBB. This version of Sort() takes a
* comparison class.
*/
template<typename RandomAccessIterator, typename Compare>
static void Sort(RandomAccessIterator begin, RandomAccessIterator end, Compare comp)
{
auto& SMPToolsAPI = vtk::detail::smp::vtkSMPToolsAPI::GetInstance();
SMPToolsAPI.Sort(begin, end, comp);
}
};
#endif
// VTK-HeaderTest-Exclude: vtkSMPTools.h