681 lines
25 KiB
C++
681 lines
25 KiB
C++
// 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();
|
|
}
|
|
}
|
|
}
|