vtk-m/vtkm/filter/clean_grid/CleanGrid.cxx
Kenneth Moreland 19f752960f Remove Filter::CreateResult that takes a vector of CoordinateSystems
This method is principally obsolete and should have been removed when
coordinate systems were converted into fields. I noticed it was still
there when updating the documentation.

This removal required some changes to the CleanGrid filter, but this
actually simplified the code.
2022-11-30 14:02:49 -07:00

204 lines
7.2 KiB
C++

//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/filter/MapFieldMergeAverage.h>
#include <vtkm/filter/MapFieldPermutation.h>
#include <vtkm/filter/clean_grid/CleanGrid.h>
#include <vtkm/filter/clean_grid/worklet/PointMerge.h>
#include <vtkm/filter/clean_grid/worklet/RemoveDegenerateCells.h>
#include <vtkm/filter/clean_grid/worklet/RemoveUnusedPoints.h>
namespace vtkm
{
namespace filter
{
namespace clean_grid
{
struct SharedStates
{
vtkm::worklet::RemoveUnusedPoints PointCompactor;
vtkm::worklet::RemoveDegenerateCells CellCompactor;
vtkm::worklet::PointMerge PointMerger;
};
}
}
}
// New Filter Design: DoMapField is now a free function in an anonymous namespace. It should be
// considered as a convenience/extension to the lambda passed to CreateResult.
// Being a free function discourages the developer to "pass" mutable states from DoExecute phase
// to DoMapField phase via data member. However, there is nothing to prevent developer doing
// stupid thing to circumvent the protection. One example here is that the developer could
// always pass a mutable reference/pointer to the filter instance and thus pass mutable state
// across the DoExecute and DoMapField boundary. We need to explicitly discourage developer
// trying to do such a thing in the manual.
namespace
{
bool DoMapField(vtkm::cont::DataSet& result,
const vtkm::cont::Field& field,
const vtkm::filter::clean_grid::CleanGrid& self,
vtkm::filter::clean_grid::SharedStates& worklets)
{
if (field.IsPointField() && (self.GetCompactPointFields() || self.GetMergePoints()))
{
vtkm::cont::Field compactedField;
if (self.GetCompactPointFields())
{
bool success = vtkm::filter::MapFieldPermutation(
field, worklets.PointCompactor.GetPointScatter().GetOutputToInputMap(), compactedField);
if (!success)
{
return false;
}
}
else
{
compactedField = field;
}
if (self.GetMergePoints())
{
return vtkm::filter::MapFieldMergeAverage(
compactedField, worklets.PointMerger.GetMergeKeys(), result);
}
else
{
result.AddField(compactedField);
return true;
}
}
else if (field.IsCellField() && self.GetRemoveDegenerateCells())
{
return vtkm::filter::MapFieldPermutation(
field, worklets.CellCompactor.GetValidCellIds(), result);
}
else
{
result.AddField(field);
return true;
}
}
} // anonymous namespace
namespace vtkm
{
namespace filter
{
namespace clean_grid
{
//-----------------------------------------------------------------------------
vtkm::cont::DataSet CleanGrid::GenerateOutput(const vtkm::cont::DataSet& inData,
vtkm::cont::CellSetExplicit<>& outputCellSet,
clean_grid::SharedStates& worklets)
{
using VecId = std::size_t;
const auto activeCoordIndex = static_cast<VecId>(this->GetActiveCoordinateSystemIndex());
// Start with a shallow copy of the coordinate systems
vtkm::cont::CoordinateSystem activeCoordSystem = inData.GetCoordinateSystem(activeCoordIndex);
// Optionally adjust the cell set indices to remove all unused points
if (this->GetCompactPointFields())
{
worklets.PointCompactor.FindPointsStart();
worklets.PointCompactor.FindPoints(outputCellSet);
worklets.PointCompactor.FindPointsEnd();
outputCellSet = worklets.PointCompactor.MapCellSet(outputCellSet);
activeCoordSystem = vtkm::cont::CoordinateSystem(
activeCoordSystem.GetName(),
worklets.PointCompactor.MapPointFieldDeep(activeCoordSystem.GetData()));
}
// Optionally find and merge coincident points
if (this->GetMergePoints())
{
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();
worklets.PointMerger.Run(delta, this->GetFastMerge(), bounds, coordArray);
activeCoordSystem = vtkm::cont::CoordinateSystem(activeCoordSystem.GetName(), coordArray);
outputCellSet = worklets.PointMerger.MapCellSet(outputCellSet);
}
// Optionally remove degenerate cells
if (this->GetRemoveDegenerateCells())
{
outputCellSet = worklets.CellCompactor.Run(outputCellSet);
}
// New Filter Design: We pass the actions needed to be done as a lambda to the generic
// CreateResult method. CreateResult now acts as thrust::transform_if on the
// Fields. Shared mutable state is captured by the lambda. We could also put all the logic
// of field mapping in the lambda. However, it is cleaner to put it in the filter specific
// implementation of DoMapField which takes mutable state as an extra parameter.
//
// For filters that do not need to do interpolation for mapping fields, we provide an overload
// that does not take the extra arguments and just AddField.
auto mapper = [&](auto& outDataSet, const auto& f) {
DoMapField(outDataSet, f, *this, worklets);
};
return this->CreateResultCoordinateSystem(inData, outputCellSet, activeCoordSystem, mapper);
}
vtkm::cont::DataSet CleanGrid::DoExecute(const vtkm::cont::DataSet& inData)
{
// New Filter Design: mutable states that was a data member of the filter is now a local
// variable. Each invocation of DoExecute() in the std::async will have a copy of worklets
// thus making it thread-safe.
clean_grid::SharedStates worklets;
using CellSetType = vtkm::cont::CellSetExplicit<>;
CellSetType outputCellSet;
vtkm::cont::UnknownCellSet inCellSet = inData.GetCellSet();
if (inCellSet.IsType<CellSetType>())
{
// Is expected type, do a shallow copy
outputCellSet = inCellSet.AsCellSet<CellSetType>();
}
else
{ // Clean the grid
vtkm::cont::ArrayHandle<vtkm::IdComponent> numIndices;
this->Invoke(worklet::CellDeepCopy::CountCellPoints{}, inCellSet, numIndices);
vtkm::cont::ArrayHandle<vtkm::UInt8> shapes;
vtkm::cont::ArrayHandle<vtkm::Id> offsets;
vtkm::Id connectivitySize;
vtkm::cont::ConvertNumComponentsToOffsets(numIndices, offsets, connectivitySize);
numIndices.ReleaseResourcesExecution();
vtkm::cont::ArrayHandle<vtkm::Id> connectivity;
connectivity.Allocate(connectivitySize);
this->Invoke(worklet::CellDeepCopy::PassCellStructure{},
inCellSet,
shapes,
vtkm::cont::make_ArrayHandleGroupVecVariable(connectivity, offsets));
outputCellSet.Fill(inCellSet.GetNumberOfPoints(), shapes, connectivity, offsets);
}
// New Filter Design: The share, mutable state is pass to other methods via parameter, not as
// a data member.
return this->GenerateOutput(inData, outputCellSet, worklets);
}
} //namespace clean_grid
} //namespace filter
} //namespace vtkm