Commit bfe06d6d authored by Kenneth Moreland's avatar Kenneth Moreland Committed by Kitware Robot

Merge topic 'point-merge'

6aa99aec Add ability to remove degenerate cells in CleanGrid
5688375c Add point merge capabilities to CleanGrid filter
Acked-by: Kitware Robot's avatarKitware Robot <kwrobot@kitware.com>
Acked-by: Abhishek Yenpure's avatarAbhishek Yenpure <abhishek@uoregon.edu>
Merge-request: !1558
parents 963ccf98 6aa99aec
# Add point merge capabilities to CleanGrid filter
We have added a `PointMerge` worklet that uses a virtual grid approach to
identify nearby points. The worklet works by creating a very fine but
sparsely represented locator grid. It then groups points by grid bins and
finds those within a specified radius.
This functionality has been integrated into the `CleanGrid` filter. The
following flags have been added to `CleanGrid` to modify the behavior of
point merging.
* `Set`/`GetMergePoints` - a flag to turn on/off the merging of
duplicated coincident points. This extra operation will find points
spatially located near each other and merge them together.
* `Set`/`GetTolerance` - Defines the tolerance used when determining
whether two points are considered coincident. If the
`ToleranceIsAbsolute` flag is false (the default), then this tolerance
is scaled by the diagonal of the points. This parameter is only used
when merge points is on.
* `Set`/`GetToleranceIsAbsolute` - When ToleranceIsAbsolute is false (the
default) then the tolerance is scaled by the diagonal of the bounds of
the dataset. If true, then the tolerance is taken as the actual
distance to use. This parameter is only used when merge points is on.
* `Set`/`GetFastMerge` - When FastMerge is true (the default), some
corners are cut when computing coincident points. The point merge will
go faster but the tolerance will not be strictly followed.
......@@ -378,6 +378,13 @@ inline vtkm::cont::DataSet MakeTestDataSet::Make3DUniformDataSet3(const vtkm::Id
vtkm::cont::DataSetFieldAdd dsf;
dsf.AddPointField(dataSet, "pointvar", pointvar);
vtkm::Id numCells = (dims[0] - 1) * (dims[1] - 1) * (dims[2] - 1);
dsf.AddCellField(
dataSet,
"cellvar",
vtkm::cont::make_ArrayHandleCounting(vtkm::Float64(0), vtkm::Float64(1), numCells));
return dataSet;
}
......
......@@ -22,6 +22,8 @@
#include <vtkm/filter/FilterDataSet.h>
#include <vtkm/worklet/PointMerge.h>
#include <vtkm/worklet/RemoveDegenerateCells.h>
#include <vtkm/worklet/RemoveUnusedPoints.h>
namespace vtkm
......@@ -52,10 +54,42 @@ public:
/// When the CompactPointFields flag is true, the filter will identify any
/// points that are not used by the topology. This is on by default.
///
VTKM_CONT
bool GetCompactPointFields() const { return this->CompactPointFields; }
VTKM_CONT
void SetCompactPointFields(bool flag) { this->CompactPointFields = flag; }
VTKM_CONT bool GetCompactPointFields() const { return this->CompactPointFields; }
VTKM_CONT void SetCompactPointFields(bool flag) { this->CompactPointFields = flag; }
/// When the MergePoints flag is true, the filter will identify any coincident
/// points and merge them together. The distance two points can be to considered
/// coincident is set with the tolerance flags. This is on by default.
///
VTKM_CONT bool GetMergePoints() const { return this->MergePoints; }
VTKM_CONT void SetMergePoints(bool flag) { this->MergePoints = flag; }
/// Defines the tolerance used when determining whether two points are considered
/// coincident. If the ToleranceIsAbsolute flag is false (the default), then this
/// tolerance is scaled by the diagonal of the points.
///
VTKM_CONT vtkm::Float64 GetTolerance() const { return this->Tolerance; }
VTKM_CONT void SetTolerance(vtkm::Float64 tolerance) { this->Tolerance = tolerance; }
/// When ToleranceIsAbsolute is false (the default) then the tolerance is scaled
/// by the diagonal of the bounds of the dataset. If true, then the tolerance is
/// taken as the actual distance to use.
///
VTKM_CONT bool GetToleranceIsAbsolute() const { return this->ToleranceIsAbsolute; }
VTKM_CONT void SetToleranceIsAbsolute(bool flag) { this->ToleranceIsAbsolute = flag; }
/// Determine whether a cell is degenerate (that is, has repeated points that drops
/// its dimensionalit) and removes them. This is on by default.
///
VTKM_CONT bool GetRemoveDegenerateCells() const { return this->RemoveDegenerateCells; }
VTKM_CONT void SetRemoveDegenerateCells(bool flag) { this->RemoveDegenerateCells = flag; }
/// When FastMerge is true (the default), some corners are cut when computing
/// coincident points. The point merge will go faster but the tolerance will not
/// be strictly followed.
///
VTKM_CONT bool GetFastMerge() const { return this->FastMerge; }
VTKM_CONT void SetFastMerge(bool flag) { this->FastMerge = flag; }
template <typename Policy>
VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& inData,
......@@ -73,12 +107,21 @@ public:
private:
bool CompactPointFields;
bool MergePoints;
vtkm::Float64 Tolerance;
bool ToleranceIsAbsolute;
bool RemoveDegenerateCells;
bool FastMerge;
vtkm::worklet::RemoveUnusedPoints PointCompactor;
vtkm::worklet::RemoveDegenerateCells CellCompactor;
vtkm::worklet::PointMerge PointMerger;
};
}
} // namespace vtkm::filter
#ifndef vtk_m_filter_CleanGrid_hxx
#include <vtkm/filter/CleanGrid.hxx>
#endif
#endif //vtk_m_filter_CleanGrid_h
......@@ -18,6 +18,11 @@
// this software.
//============================================================================
#ifndef vtk_m_filter_CleanGrid_hxx
#define vtk_m_filter_CleanGrid_hxx
#include <vtkm/filter/CleanGrid.h>
#include <vtkm/worklet/CellDeepCopy.h>
#include <vtkm/worklet/RemoveUnusedPoints.h>
......@@ -30,6 +35,11 @@ namespace filter
inline VTKM_CONT CleanGrid::CleanGrid()
: CompactPointFields(true)
, MergePoints(true)
, Tolerance(1.0e-6)
, ToleranceIsAbsolute(false)
, RemoveDegenerateCells(true)
, FastMerge(true)
{
}
......@@ -41,17 +51,35 @@ inline VTKM_CONT vtkm::cont::DataSet CleanGrid::DoExecute(const vtkm::cont::Data
using VecId = std::vector<CellSetType>::size_type;
VecId numCellSets = static_cast<VecId>(inData.GetNumberOfCellSets());
std::vector<CellSetType> outputCellSets(numCellSets);
VecId activeCoordIndex = static_cast<VecId>(this->GetActiveCoordinateSystemIndex());
// Do a deep copy of the cells to new CellSetExplicit structures
for (VecId cellSetIndex = 0; cellSetIndex < numCellSets; cellSetIndex++)
for (VecId cellSetIndex = 0; cellSetIndex < numCellSets; ++cellSetIndex)
{
vtkm::cont::DynamicCellSet inCellSet =
inData.GetCellSet(static_cast<vtkm::IdComponent>(cellSetIndex));
if (inCellSet.IsType<CellSetType>())
{
// Is expected type, do a shallow copy
outputCellSets[cellSetIndex] = inCellSet.Cast<CellSetType>();
}
else
{
vtkm::worklet::CellDeepCopy::Run(vtkm::filter::ApplyPolicy(inCellSet, policy),
outputCellSets[cellSetIndex]);
}
}
VecId numCoordSystems = static_cast<VecId>(inData.GetNumberOfCoordinateSystems());
std::vector<vtkm::cont::CoordinateSystem> outputCoordinateSystems(numCoordSystems);
vtkm::worklet::CellDeepCopy::Run(vtkm::filter::ApplyPolicy(inCellSet, policy),
outputCellSets[cellSetIndex]);
// Start with a shallow copy of the coordinate systems
for (VecId coordSystemIndex = 0; coordSystemIndex < numCoordSystems; ++coordSystemIndex)
{
outputCoordinateSystems[coordSystemIndex] =
inData.GetCoordinateSystem(static_cast<vtkm::IdComponent>(coordSystemIndex));
}
// Optionally adjust the cell set indices to remove all unused points
......@@ -64,10 +92,61 @@ inline VTKM_CONT vtkm::cont::DataSet CleanGrid::DoExecute(const vtkm::cont::Data
}
this->PointCompactor.FindPointsEnd();
for (VecId cellSetIndex = 0; cellSetIndex < numCellSets; cellSetIndex++)
for (VecId cellSetIndex = 0; cellSetIndex < numCellSets; ++cellSetIndex)
{
outputCellSets[cellSetIndex] = this->PointCompactor.MapCellSet(outputCellSets[cellSetIndex]);
}
for (VecId coordSystemIndex = 0; coordSystemIndex < numCoordSystems; ++coordSystemIndex)
{
outputCoordinateSystems[coordSystemIndex] =
vtkm::cont::CoordinateSystem(outputCoordinateSystems[coordSystemIndex].GetName(),
this->PointCompactor.MapPointFieldDeep(
outputCoordinateSystems[coordSystemIndex].GetData()));
}
}
// Optionally find and merge coincident points
if (this->GetMergePoints())
{
vtkm::cont::CoordinateSystem activeCoordSystem = outputCoordinateSystems[activeCoordIndex];
vtkm::Bounds bounds = activeCoordSystem.GetBounds();
vtkm::Float64 delta = this->GetTolerance();
if (!this->GetToleranceIsAbsolute())
{
delta *=
vtkm::Magnitude(vtkm::make_Vec(bounds.X.Length(), bounds.Y.Length(), bounds.Z.Length()));
}
auto coordArray = activeCoordSystem.GetData();
this->PointMerger.Run(delta, this->GetFastMerge(), bounds, coordArray);
activeCoordSystem = vtkm::cont::CoordinateSystem(activeCoordSystem.GetName(), coordArray);
for (VecId coordSystemIndex = 0; coordSystemIndex < numCoordSystems; ++coordSystemIndex)
{
if (coordSystemIndex == activeCoordIndex)
{
outputCoordinateSystems[coordSystemIndex] = activeCoordSystem;
}
else
{
outputCoordinateSystems[coordSystemIndex] = vtkm::cont::CoordinateSystem(
outputCoordinateSystems[coordSystemIndex].GetName(),
this->PointMerger.MapPointField(outputCoordinateSystems[coordSystemIndex].GetData()));
}
}
for (VecId cellSetIndex = 0; cellSetIndex < numCellSets; ++cellSetIndex)
{
outputCellSets[cellSetIndex] = this->PointMerger.MapCellSet(outputCellSets[cellSetIndex]);
}
}
// Optionally remove degenerate cells
if (this->GetRemoveDegenerateCells())
{
outputCellSets[activeCoordIndex] = this->CellCompactor.Run(outputCellSets[activeCoordIndex]);
}
// Construct resulting data set with new cell sets
......@@ -78,27 +157,9 @@ inline VTKM_CONT vtkm::cont::DataSet CleanGrid::DoExecute(const vtkm::cont::Data
}
// Pass the coordinate systems
// TODO: This is very awkward. First of all, there is no support for dealing
// with coordinate systems at all. That is fine if you are computing a new
// coordinate system, but a pain if you are deriving the coordinate system
// array. Second, why is it that coordinate systems are automatically mapped
// but other fields are not? Why shouldn't the Execute of a filter also set
// up all the fields of the output data set?
for (vtkm::IdComponent coordSystemIndex = 0;
coordSystemIndex < inData.GetNumberOfCoordinateSystems();
coordSystemIndex++)
for (VecId coordSystemIndex = 0; coordSystemIndex < numCoordSystems; ++coordSystemIndex)
{
vtkm::cont::CoordinateSystem coordSystem = inData.GetCoordinateSystem(coordSystemIndex);
if (this->GetCompactPointFields())
{
auto outArray = this->MapPointField(coordSystem.GetData());
outData.AddCoordinateSystem(vtkm::cont::CoordinateSystem(coordSystem.GetName(), outArray));
}
else
{
outData.AddCoordinateSystem(coordSystem);
}
outData.AddCoordinateSystem(outputCoordinateSystems[coordSystemIndex]);
}
return outData;
......@@ -111,11 +172,27 @@ inline VTKM_CONT bool CleanGrid::DoMapField(
const vtkm::filter::FieldMetadata& fieldMeta,
vtkm::filter::PolicyBase<Policy>)
{
if (this->GetCompactPointFields() && fieldMeta.IsPointField())
if (fieldMeta.IsPointField() && (this->GetCompactPointFields() || this->GetMergePoints()))
{
vtkm::cont::ArrayHandle<ValueType> compactedArray = this->MapPointField(input);
vtkm::cont::ArrayHandle<ValueType> compactedArray;
if (this->GetCompactPointFields())
{
compactedArray = this->PointCompactor.MapPointFieldDeep(input);
if (this->GetMergePoints())
{
compactedArray = this->PointMerger.MapPointField(compactedArray);
}
}
else if (this->GetMergePoints())
{
compactedArray = this->PointMerger.MapPointField(input);
}
result.AddField(fieldMeta.AsField(compactedArray));
}
else if (fieldMeta.IsCellField() && this->GetRemoveDegenerateCells())
{
result.AddField(fieldMeta.AsField(this->CellCompactor.ProcessCellField(input)));
}
else
{
result.AddField(fieldMeta.AsField(input));
......@@ -123,14 +200,7 @@ inline VTKM_CONT bool CleanGrid::DoMapField(
return true;
}
template <typename ValueType, typename Storage>
inline VTKM_CONT vtkm::cont::ArrayHandle<ValueType> CleanGrid::MapPointField(
const vtkm::cont::ArrayHandle<ValueType, Storage>& inArray) const
{
VTKM_ASSERT(this->GetCompactPointFields());
return this->PointCompactor.MapPointFieldDeep(inArray);
}
}
}
#endif //vtk_m_filter_CleanGrid_hxx
......@@ -100,6 +100,7 @@ inline VTKM_CONT vtkm::cont::DataSet ExternalFaces::DoExecute(
if (this->CompactPoints)
{
this->Compactor.SetCompactPointFields(true);
this->Compactor.SetMergePoints(false);
return this->Compactor.DoExecute(output, GetCellSetExplicitPolicy(policy));
}
else
......
......@@ -80,6 +80,7 @@ inline vtkm::cont::DataSet ExtractPoints::DoExecute(const vtkm::cont::DataSet& i
if (this->CompactPoints)
{
this->Compactor.SetCompactPointFields(true);
this->Compactor.SetMergePoints(false);
return this->Compactor.DoExecute(output, GetCellSetSingleTypePolicy(policy));
}
else
......
......@@ -65,7 +65,6 @@ public:
private:
vtkm::Id Stride;
bool CompactPoints;
vtkm::filter::CleanGrid Compactor;
vtkm::worklet::Mask Worklet;
};
}
......
......@@ -73,6 +73,7 @@ inline VTKM_CONT vtkm::cont::DataSet MaskPoints::DoExecute(
if (this->CompactPoints)
{
this->Compactor.SetCompactPointFields(true);
this->Compactor.SetMergePoints(false);
return this->Compactor.DoExecute(output, GetCellSetSingleTypePolicy(policy));
}
else
......
......@@ -198,6 +198,7 @@ inline VTKM_CONT vtkm::cont::DataSet ThresholdPoints::DoExecute(
if (this->CompactPoints)
{
this->Compactor.SetCompactPointFields(true);
this->Compactor.SetMergePoints(true);
return this->Compactor.DoExecute(output, GetCellSetSingleTypePolicy(policy));
}
else
......
......@@ -20,6 +20,8 @@
#include <vtkm/filter/CleanGrid.h>
#include <vtkm/filter/MarchingCubes.h>
#include <vtkm/cont/testing/MakeTestDataSet.h>
#include <vtkm/cont/testing/Testing.h>
......@@ -37,46 +39,167 @@ void TestUniformGrid(vtkm::filter::CleanGrid clean)
clean.SetFieldsToPass({ "pointvar", "cellvar" });
vtkm::cont::DataSet outData = clean.Execute(inData);
VTKM_TEST_ASSERT(outData.HasField("pointvar"), "Failed to map point field");
VTKM_TEST_ASSERT(outData.HasField("cellvar"), "Failed to map point field");
VTKM_TEST_ASSERT(outData.HasField("cellvar"), "Failed to map cell field");
vtkm::cont::CellSetExplicit<> outCellSet;
outData.GetCellSet().CopyTo(outCellSet);
VTKM_TEST_ASSERT(outCellSet.GetNumberOfPoints() == 6, "Wrong number of points");
VTKM_TEST_ASSERT(outCellSet.GetNumberOfCells() == 2, "Wrong number of cells");
VTKM_TEST_ASSERT(outCellSet.GetNumberOfPoints() == 6,
"Wrong number of points: ",
outCellSet.GetNumberOfPoints());
VTKM_TEST_ASSERT(
outCellSet.GetNumberOfCells() == 2, "Wrong number of cells: ", outCellSet.GetNumberOfCells());
vtkm::Vec<vtkm::Id, 4> cellIds;
outCellSet.GetIndices(0, cellIds);
VTKM_TEST_ASSERT((cellIds == vtkm::Vec<vtkm::Id, 4>(0, 1, 4, 3)), "Bad cell ids");
VTKM_TEST_ASSERT((cellIds == vtkm::Vec<vtkm::Id, 4>(0, 1, 4, 3)), "Bad cell ids: ", cellIds);
outCellSet.GetIndices(1, cellIds);
VTKM_TEST_ASSERT((cellIds == vtkm::Vec<vtkm::Id, 4>(1, 2, 5, 4)), "Bad cell ids");
VTKM_TEST_ASSERT((cellIds == vtkm::Vec<vtkm::Id, 4>(1, 2, 5, 4)), "Bad cell ids: ", cellIds);
vtkm::cont::ArrayHandle<vtkm::Float32> outPointField;
outData.GetField("pointvar").GetData().CopyTo(outPointField);
VTKM_TEST_ASSERT(outPointField.GetNumberOfValues() == 6, "Wrong point field size.");
VTKM_TEST_ASSERT(outPointField.GetNumberOfValues() == 6,
"Wrong point field size: ",
outPointField.GetNumberOfValues());
VTKM_TEST_ASSERT(test_equal(outPointField.GetPortalConstControl().Get(1), 20.1),
"Bad point field value");
"Bad point field value: ",
outPointField.GetPortalConstControl().Get(1));
VTKM_TEST_ASSERT(test_equal(outPointField.GetPortalConstControl().Get(4), 50.1),
"Bad point field value");
"Bad point field value: ",
outPointField.GetPortalConstControl().Get(1));
vtkm::cont::ArrayHandle<vtkm::Float32> outCellField;
outData.GetField("cellvar").GetData().CopyTo(outCellField);
VTKM_TEST_ASSERT(outCellField.GetNumberOfValues() == 2, "Wrong cell field size.");
VTKM_TEST_ASSERT(test_equal(outCellField.GetPortalConstControl().Get(0), 100.1),
"Bad cell field value");
"Bad cell field value",
outCellField.GetPortalConstControl().Get(0));
VTKM_TEST_ASSERT(test_equal(outCellField.GetPortalConstControl().Get(1), 200.1),
"Bad cell field value");
"Bad cell field value",
outCellField.GetPortalConstControl().Get(0));
}
void TestPointMerging()
{
vtkm::cont::testing::MakeTestDataSet makeDataSet;
vtkm::cont::DataSet baseData = makeDataSet.Make3DUniformDataSet3(vtkm::Id3(4, 4, 4));
vtkm::filter::MarchingCubes marchingCubes;
marchingCubes.SetIsoValue(0.05);
marchingCubes.SetMergeDuplicatePoints(false);
marchingCubes.SetActiveField("pointvar");
vtkm::cont::DataSet inData = marchingCubes.Execute(baseData);
constexpr vtkm::Id originalNumPoints = 228;
constexpr vtkm::Id originalNumCells = 76;
VTKM_TEST_ASSERT(inData.GetCellSet().GetNumberOfPoints() == originalNumPoints);
VTKM_TEST_ASSERT(inData.GetCellSet().GetNumberOfCells() == originalNumCells);
vtkm::filter::CleanGrid cleanGrid;
std::cout << "Clean grid without any merging" << std::endl;
cleanGrid.SetCompactPointFields(false);
cleanGrid.SetMergePoints(false);
cleanGrid.SetRemoveDegenerateCells(false);
vtkm::cont::DataSet noMerging = cleanGrid.Execute(inData);
VTKM_TEST_ASSERT(noMerging.GetCellSet().GetNumberOfCells() == originalNumCells);
VTKM_TEST_ASSERT(noMerging.GetCellSet().GetNumberOfPoints() == originalNumPoints);
VTKM_TEST_ASSERT(noMerging.GetCoordinateSystem().GetData().GetNumberOfValues() ==
originalNumPoints);
VTKM_TEST_ASSERT(noMerging.GetField("pointvar").GetData().GetNumberOfValues() ==
originalNumPoints);
VTKM_TEST_ASSERT(noMerging.GetField("cellvar").GetData().GetNumberOfValues() == originalNumCells);
std::cout << "Clean grid by merging very close points" << std::endl;
cleanGrid.SetMergePoints(true);
cleanGrid.SetFastMerge(false);
vtkm::cont::DataSet closeMerge = cleanGrid.Execute(inData);
constexpr vtkm::Id closeMergeNumPoints = 62;
VTKM_TEST_ASSERT(closeMerge.GetCellSet().GetNumberOfCells() == originalNumCells);
VTKM_TEST_ASSERT(closeMerge.GetCellSet().GetNumberOfPoints() == closeMergeNumPoints);
VTKM_TEST_ASSERT(closeMerge.GetCoordinateSystem().GetData().GetNumberOfValues() ==
closeMergeNumPoints);
VTKM_TEST_ASSERT(closeMerge.GetField("pointvar").GetData().GetNumberOfValues() ==
closeMergeNumPoints);
VTKM_TEST_ASSERT(closeMerge.GetField("cellvar").GetData().GetNumberOfValues() ==
originalNumCells);
std::cout << "Clean grid by merging very close points with fast merge" << std::endl;
cleanGrid.SetFastMerge(true);
vtkm::cont::DataSet closeFastMerge = cleanGrid.Execute(inData);
VTKM_TEST_ASSERT(closeFastMerge.GetCellSet().GetNumberOfCells() == originalNumCells);
VTKM_TEST_ASSERT(closeFastMerge.GetCellSet().GetNumberOfPoints() == closeMergeNumPoints);
VTKM_TEST_ASSERT(closeFastMerge.GetCoordinateSystem().GetData().GetNumberOfValues() ==
closeMergeNumPoints);
VTKM_TEST_ASSERT(closeFastMerge.GetField("pointvar").GetData().GetNumberOfValues() ==
closeMergeNumPoints);
VTKM_TEST_ASSERT(closeFastMerge.GetField("cellvar").GetData().GetNumberOfValues() ==
originalNumCells);
std::cout << "Clean grid with largely separated points" << std::endl;
cleanGrid.SetFastMerge(false);
cleanGrid.SetTolerance(0.1);
vtkm::cont::DataSet farMerge = cleanGrid.Execute(inData);
constexpr vtkm::Id farMergeNumPoints = 36;
VTKM_TEST_ASSERT(farMerge.GetCellSet().GetNumberOfCells() == originalNumCells);
VTKM_TEST_ASSERT(farMerge.GetCellSet().GetNumberOfPoints() == farMergeNumPoints);
VTKM_TEST_ASSERT(farMerge.GetCoordinateSystem().GetData().GetNumberOfValues() ==
farMergeNumPoints);
VTKM_TEST_ASSERT(farMerge.GetField("pointvar").GetData().GetNumberOfValues() ==
farMergeNumPoints);
VTKM_TEST_ASSERT(farMerge.GetField("cellvar").GetData().GetNumberOfValues() == originalNumCells);
std::cout << "Clean grid with largely separated points quickly" << std::endl;
cleanGrid.SetFastMerge(true);
vtkm::cont::DataSet farFastMerge = cleanGrid.Execute(inData);
constexpr vtkm::Id farFastMergeNumPoints = 19;
VTKM_TEST_ASSERT(farFastMerge.GetCellSet().GetNumberOfCells() == originalNumCells);
VTKM_TEST_ASSERT(farFastMerge.GetCellSet().GetNumberOfPoints() == farFastMergeNumPoints);
VTKM_TEST_ASSERT(farFastMerge.GetCoordinateSystem().GetData().GetNumberOfValues() ==
farFastMergeNumPoints);
VTKM_TEST_ASSERT(farFastMerge.GetField("pointvar").GetData().GetNumberOfValues() ==
farFastMergeNumPoints);
VTKM_TEST_ASSERT(farFastMerge.GetField("cellvar").GetData().GetNumberOfValues() ==
originalNumCells);
std::cout << "Clean grid with largely separated points quickly with degenerate cells"
<< std::endl;
cleanGrid.SetRemoveDegenerateCells(true);
vtkm::cont::DataSet noDegenerateCells = cleanGrid.Execute(inData);
constexpr vtkm::Id numNonDegenerateCells = 33;
VTKM_TEST_ASSERT(noDegenerateCells.GetCellSet().GetNumberOfCells() == numNonDegenerateCells);
VTKM_TEST_ASSERT(noDegenerateCells.GetCellSet().GetNumberOfPoints() == farFastMergeNumPoints);
VTKM_TEST_ASSERT(noDegenerateCells.GetCoordinateSystem().GetData().GetNumberOfValues() ==
farFastMergeNumPoints);
VTKM_TEST_ASSERT(noDegenerateCells.GetField("pointvar").GetData().GetNumberOfValues() ==
farFastMergeNumPoints);
VTKM_TEST_ASSERT(noDegenerateCells.GetField("cellvar").GetData().GetNumberOfValues() ==
numNonDegenerateCells);
}
void RunTest()
{
vtkm::filter::CleanGrid clean;
std::cout << "*** Test wqith compact point fields on" << std::endl;
std::cout << "*** Test with compact point fields on merge points off" << std::endl;
clean.SetCompactPointFields(true);
clean.SetMergePoints(false);
TestUniformGrid(clean);
std::cout << "*** Test wqith compact point fields off" << std::endl;
std::cout << "*** Test with compact point fields off merge points off" << std::endl;
clean.SetCompactPointFields(false);
clean.SetMergePoints(false);
TestUniformGrid(clean);
std::cout << "*** Test with compact point fields on merge points on" << std::endl;
clean.SetCompactPointFields(true);
clean.SetMergePoints(true);
TestUniformGrid(clean);
std::cout << "*** Test with compact point fields off merge points on" << std::endl;
clean.SetCompactPointFields(false);
clean.SetMergePoints(true);
TestUniformGrid(clean);
std::cout << "*** Test point merging" << std::endl;
TestPointMerging();
}
} // anonymous namespace
......
......@@ -35,6 +35,8 @@ vtkm::cont::DataSet MakeDataTestSet1()
vtkm::cont::DataSet ds = MakeTestDataSet().Make3DUniformDataSet1();
vtkm::filter::CleanGrid clean;
clean.SetCompactPointFields(false);
clean.SetMergePoints(false);
return clean.Execute(ds);
}
......
......@@ -503,6 +503,7 @@ void TestMarchingCubesNormals()
std::cout << "\tUnstructured dataset\n";
vtkm::filter::CleanGrid makeUnstructured;
makeUnstructured.SetCompactPointFields(false);
makeUnstructured.SetMergePoints(false);
makeUnstructured.SetFieldsToPass("pointvar");
auto result = makeUnstructured.Execute(dataset);
TestNormals(result, false);
......
......@@ -25,7 +25,6 @@
#include <vtkm/cont/DataSetBuilderUniform.h>
#include <vtkm/cont/DataSetFieldAdd.h>
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/filter/CleanGrid.h>
#include <vtkm/filter/ZFPCompressor1D.h>
#include <vtkm/filter/ZFPCompressor2D.h>
......
......@@ -63,7 +63,7 @@ struct ArrayPortalValueReference
VTKM_SUPPRESS_EXEC_WARNINGS
VTKM_EXEC_CONT
operator ValueType(void) const { return this->Portal.Get(this->Index); }
operator ValueType(void) const { return this->Get(); }
// Declaring Set as const seems a little weird because we are changing the value. But remember
// that ArrayPortalReference is only a reference class. The reference itself does not change,
......
......@@ -62,8 +62,10 @@ set(headers
ParticleAdvection.h
PointAverage.h
PointElevation.h
PointMerge.h
PointTransform.h
Probe.h
RemoveDegenerateCells.h
RemoveUnusedPoints.h
ScalarsToColors.h
ScatterCounting.h
......
//============================================================================
// 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 2019 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
// Copyright 2019 UT-Battelle, LLC.
// Copyright 2019 Los Alamos National Security.
//
// Under the terms of Contract DE-NA0003525 with NTESS,
// 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 vtk_m_worklet_PointMerge_h
#define vtk_m_worklet_PointMerge_h
#include <vtkm/worklet/AverageByKey.h>
#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/DispatcherReduceByKey.h>
#include <vtkm/worklet/Invoker.h>
#include <vtkm/worklet/Keys.h>
#include <vtkm/worklet/RemoveUnusedPoints.h>
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/WorkletReduceByKey.h>
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/cont/ArrayHandleIndex.h>
#include <vtkm/cont/ArrayHandlePermutation.h>
#include <vtkm/cont/ArrayHandleVirtual.h>
#include <vtkm/cont/CellSetExplicit.h>
#include <vtkm/cont/ExecutionAndControlObjectBase.h>
#include <vtkm/Bounds.h>
#include <vtkm/Hash.h>
#include <vtkm/Math.h>
#include <vtkm/VectorAnalysis.h>
namespace vtkm
{
namespace worklet
{
class PointMerge
{
public:
// This class can take point worldCoords as inputs and return the bin index of the enclosing bin.
class BinLocator : public vtkm::cont::ExecutionAndControlObjectBase
{
vtkm::Vec<vtkm::Float64, 3> Offset;
vtkm::Vec<vtkm::Float64, 3> Scale;
#ifdef VTKM_USE_64BIT_IDS
// IEEE double precision floating point as 53 bits for the significand, so it would not be
// possible to represent a number with more precision than that. We also back off a few bits to
// avoid potential issues with numerical imprecision in the scaling.
static constexpr vtkm::IdComponent BitsPerDimension = 50;
#else
static constexpr vtkm::IdComponent BitsPerDimension = 31;
#endif
static constexpr vtkm::Id MaxBinsPerDimension =
static_cast<vtkm::Id>((1LL << BitsPerDimension) - 1);
public:
VTKM_CONT BinLocator()
: Offset(0.0)
, Scale(0.0)
{
}
VTKM_CONT
static vtkm::Vec<vtkm::Float64, 3> ComputeBinWidths(const vtkm::Bounds& bounds,
vtkm::Float64 delta)
{
const vtkm::Vec<vtkm::Float64, 3> boundLengths(
bounds.X.Length() + delta, bounds.Y.Length() + delta, bounds.Z.Length() + delta);
vtkm::Vec<vtkm::Float64, 3> binWidths;
for (vtkm::IdComponent dimIndex = 0; dimIndex < 3; ++dimIndex)
{
if (boundLengths[dimIndex] > vtkm::Epsilon64())
{
vtkm::Float64 minBinWidth = boundLengths[dimIndex] / (MaxBinsPerDimension - 1);
if (minBinWidth < (2 * delta))
{
// We can accurately represent delta with the precision of the bin indices. The bin
// size is 2*delta, which means we scale the (offset) point coordinates by 1/delta to
// get the bin index.
binWidths[dimIndex] = 2.0 * delta;
}
else
{
// Scale the (offset) point coordinates by 1/minBinWidth, which will give us bin
// indices between 0 and MaxBinsPerDimension - 1.
binWidths[dimIndex] = minBinWidth;
}
}
else
{
// Bounds are essentially 0 in this dimension. The scale does not matter so much.
binWidths[dimIndex] = 1.0;
}
}
return binWidths;