Fix handling of cell fields in Tube filter

The `Tube` filter wraps a tube of polygons around poly line cells.
During this process it had a strange (and wrong) handling of cell data.
It assumed that each line had an independent field entry for each
segment of each line. It thus had lots of extra code to find the length
and offsets of the segment data in the cell data.

This is simply not how cell fields work in VTK-m. In VTK-m, each cell
has exactly one entry in the cell field array. Even if a polyline has
100 segments, it only gets one cell field value. This behavior is
consistent with how VTK treats cell field arrays.

The behavior the `Tube` filter was trying to implement was closer to an
"edge" field. However, edge fields are currently not supported in VTK-m.
The proper implementation would be to add edge fields to VTK-m. (This
would also get around some problems with the implementation that was
removed here when mixing polylines with other cell types and degenerate
lines.)
This commit is contained in:
Kenneth Moreland 2022-10-31 14:38:05 -06:00
parent ef3c4c65c9
commit 2bcc9aa708
3 changed files with 34 additions and 28 deletions

@ -0,0 +1,19 @@
# Fix handling of cell fields in Tube filter
The `Tube` filter wraps a tube of polygons around poly line cells.
During this process it had a strange (and wrong) handling of cell data.
It assumed that each line had an independent field entry for each
segment of each line. It thus had lots of extra code to find the length
and offsets of the segment data in the cell data.
This is simply not how cell fields work in VTK-m. In VTK-m, each cell
has exactly one entry in the cell field array. Even if a polyline has
100 segments, it only gets one cell field value. This behavior is
consistent with how VTK treats cell field arrays.
The behavior the `Tube` filter was trying to implement was closer to an
"edge" field. However, edge fields are currently not supported in VTK-m.
The proper implementation would be to add edge fields to VTK-m. (This
would also get around some problems with the implementation that was
removed here when mixing polylines with other cell types and degenerate
lines.)

@ -62,14 +62,12 @@ void TestTubeFilters()
ptVar.push_back(1); ptVar.push_back(1);
ptVar.push_back(2); ptVar.push_back(2);
cellVar.push_back(100); cellVar.push_back(100);
cellVar.push_back(101);
//Polyline 2. //Polyline 2.
ptVar.push_back(10); ptVar.push_back(10);
ptVar.push_back(11); ptVar.push_back(11);
ptVar.push_back(12); ptVar.push_back(12);
cellVar.push_back(110); cellVar.push_back(110);
cellVar.push_back(111);
//Add some degenerate polylines. //Add some degenerate polylines.
//Polyline 3: (only 1 point) //Polyline 3: (only 1 point)
@ -79,7 +77,6 @@ void TestTubeFilters()
ptVar.push_back(-1); ptVar.push_back(-1);
ptVar.push_back(-1); ptVar.push_back(-1);
cellVar.push_back(-1); cellVar.push_back(-1);
cellVar.push_back(-1);
ds.AddPointField("pointVar", ptVar); ds.AddPointField("pointVar", ptVar);
ds.AddCellField("cellVar", cellVar); ds.AddCellField("cellVar", cellVar);
@ -117,10 +114,10 @@ void TestTubeFilters()
vtkm::cont::ArrayHandle<vtkm::FloatDefault> cellArr; vtkm::cont::ArrayHandle<vtkm::FloatDefault> cellArr;
output.GetField("cellVar").GetData().AsArrayHandle(cellArr); output.GetField("cellVar").GetData().AsArrayHandle(cellArr);
VTKM_TEST_ASSERT(cellArr.GetNumberOfValues() == 36, "Wrong number of values in cell field"); VTKM_TEST_ASSERT(cellArr.GetNumberOfValues() == 36, "Wrong number of values in cell field");
std::vector<vtkm::FloatDefault> cellVals = { 100, 100, 100, 100, 100, 100, 101, 101, 101, std::vector<vtkm::FloatDefault> cellVals = { 100, 100, 100, 100, 100, 100, 100, 100, 100,
101, 101, 101, 100, 100, 100, 101, 101, 101, 100, 100, 100, 100, 100, 100, 100, 100, 100,
110, 110, 110, 110, 110, 110, 111, 111, 111, 110, 110, 110, 110, 110, 110, 110, 110, 110,
111, 111, 111, 110, 110, 110, 111, 111, 111 }; 110, 110, 110, 110, 110, 110, 110, 110, 110 };
portal = cellArr.ReadPortal(); portal = cellArr.ReadPortal();
for (vtkm::Id i = 0; i < 36; i++) for (vtkm::Id i = 0; i < 36; i++)
VTKM_TEST_ASSERT(portal.Get(i) == cellVals[static_cast<std::size_t>(i)], VTKM_TEST_ASSERT(portal.Get(i) == cellVals[static_cast<std::size_t>(i)],

@ -48,7 +48,6 @@ public:
FieldOut ptsPerPolyline, FieldOut ptsPerPolyline,
FieldOut ptsPerTube, FieldOut ptsPerTube,
FieldOut numTubeConnIds, FieldOut numTubeConnIds,
FieldOut linesPerPolyline,
FieldOut validCell); FieldOut validCell);
using ExecutionSignature = void(CellShape shapeType, using ExecutionSignature = void(CellShape shapeType,
PointCount numPoints, PointCount numPoints,
@ -58,8 +57,7 @@ public:
_4 ptsPerPolyline, _4 ptsPerPolyline,
_5 ptsPerTube, _5 ptsPerTube,
_6 numTubeConnIds, _6 numTubeConnIds,
_7 linesPerPolyline, _7 validCell);
_8 validCell);
using InputDomain = _1; using InputDomain = _1;
template <typename CellShapeTag, typename PointIndexType, typename InPointsType> template <typename CellShapeTag, typename PointIndexType, typename InPointsType>
@ -71,7 +69,6 @@ public:
vtkm::Id& ptsPerPolyline, vtkm::Id& ptsPerPolyline,
vtkm::Id& ptsPerTube, vtkm::Id& ptsPerTube,
vtkm::Id& numTubeConnIds, vtkm::Id& numTubeConnIds,
vtkm::Id& linesPerPolyline,
vtkm::Id& validCell) const vtkm::Id& validCell) const
{ {
// We only support polylines that contain 2 or more points. // We only support polylines that contain 2 or more points.
@ -97,7 +94,6 @@ public:
ptsPerTube = this->NumSides * numNonCoincidentPoints; ptsPerTube = this->NumSides * numNonCoincidentPoints;
// (two tris per segment) X (numSides) X numVertsPerCell // (two tris per segment) X (numSides) X numVertsPerCell
numTubeConnIds = (numNonCoincidentPoints - 1) * 2 * this->NumSides * this->NumVertsPerCell; numTubeConnIds = (numNonCoincidentPoints - 1) * 2 * this->NumSides * this->NumVertsPerCell;
linesPerPolyline = numNonCoincidentPoints - 1;
//Capping adds center vertex in middle of cap, plus NumSides triangles for cap. //Capping adds center vertex in middle of cap, plus NumSides triangles for cap.
if (this->Capping) if (this->Capping)
@ -113,7 +109,6 @@ public:
nonIncidentPtsPerPolyline = 0; nonIncidentPtsPerPolyline = 0;
ptsPerTube = 0; ptsPerTube = 0;
numTubeConnIds = 0; numTubeConnIds = 0;
linesPerPolyline = 0;
} }
} }
@ -481,24 +476,23 @@ public:
FieldInCell ptsPerPolyline, FieldInCell ptsPerPolyline,
FieldInCell tubePointOffsets, FieldInCell tubePointOffsets,
FieldInCell tubeConnOffsets, FieldInCell tubeConnOffsets,
FieldInCell segOffset,
WholeArrayOut outConnectivity, WholeArrayOut outConnectivity,
WholeArrayOut outCellSrcIdx); WholeArrayOut outCellSrcIdx);
using ExecutionSignature = void(CellShape shapeType, using ExecutionSignature = void(CellShape shapeType,
InputIndex inCellIndex,
_2 ptsPerPolyline, _2 ptsPerPolyline,
_3 tubePointOffset, _3 tubePointOffset,
_4 tubeConnOffsets, _4 tubeConnOffsets,
_5 segOffset, _5 outConn,
_6 outConn, _6 outCellSrcIdx);
_7 outCellSrcIdx);
using InputDomain = _1; using InputDomain = _1;
template <typename CellShapeTag, typename OutConnType, typename OutCellSrcIdxType> template <typename CellShapeTag, typename OutConnType, typename OutCellSrcIdxType>
VTKM_EXEC void operator()(const CellShapeTag& shapeType, VTKM_EXEC void operator()(const CellShapeTag& shapeType,
vtkm::Id inCellIndex,
const vtkm::IdComponent& numPoints, const vtkm::IdComponent& numPoints,
const vtkm::Id& tubePointOffset, const vtkm::Id& tubePointOffset,
const vtkm::Id& tubeConnOffset, const vtkm::Id& tubeConnOffset,
const vtkm::Id& segOffset,
OutConnType& outConn, OutConnType& outConn,
OutCellSrcIdxType& outCellSrcIdx) const OutCellSrcIdxType& outCellSrcIdx) const
{ {
@ -517,7 +511,7 @@ public:
outConn.Set(outIdx + 1, tubePtOffset + i * this->NumSides + (j + 1) % this->NumSides); outConn.Set(outIdx + 1, tubePtOffset + i * this->NumSides + (j + 1) % this->NumSides);
outConn.Set(outIdx + 2, outConn.Set(outIdx + 2,
tubePtOffset + (i + 1) * this->NumSides + (j + 1) % this->NumSides); tubePtOffset + (i + 1) * this->NumSides + (j + 1) % this->NumSides);
outCellSrcIdx.Set(outIdx / 3, segOffset + static_cast<vtkm::Id>(i)); outCellSrcIdx.Set(outIdx / 3, inCellIndex);
outIdx += 3; outIdx += 3;
//Triangle 2: verts 0,2,3 //Triangle 2: verts 0,2,3
@ -525,7 +519,7 @@ public:
outConn.Set(outIdx + 1, outConn.Set(outIdx + 1,
tubePtOffset + (i + 1) * this->NumSides + (j + 1) % this->NumSides); tubePtOffset + (i + 1) * this->NumSides + (j + 1) % this->NumSides);
outConn.Set(outIdx + 2, tubePtOffset + (i + 1) * this->NumSides + j); outConn.Set(outIdx + 2, tubePtOffset + (i + 1) * this->NumSides + j);
outCellSrcIdx.Set(outIdx / 3, segOffset + static_cast<vtkm::Id>(i)); outCellSrcIdx.Set(outIdx / 3, inCellIndex);
outIdx += 3; outIdx += 3;
} }
} }
@ -539,7 +533,7 @@ public:
outConn.Set(outIdx + 0, startCenterPt); outConn.Set(outIdx + 0, startCenterPt);
outConn.Set(outIdx + 1, startCenterPt + 1 + j); outConn.Set(outIdx + 1, startCenterPt + 1 + j);
outConn.Set(outIdx + 2, startCenterPt + 1 + ((j + 1) % this->NumSides)); outConn.Set(outIdx + 2, startCenterPt + 1 + ((j + 1) % this->NumSides));
outCellSrcIdx.Set(outIdx / 3, segOffset); outCellSrcIdx.Set(outIdx / 3, inCellIndex);
outIdx += 3; outIdx += 3;
} }
@ -552,7 +546,7 @@ public:
outConn.Set(outIdx + 0, endCenterPt); outConn.Set(outIdx + 0, endCenterPt);
outConn.Set(outIdx + 1, endOffsetPt + j); outConn.Set(outIdx + 1, endOffsetPt + j);
outConn.Set(outIdx + 2, endOffsetPt + ((j + 1) % this->NumSides)); outConn.Set(outIdx + 2, endOffsetPt + ((j + 1) % this->NumSides));
outCellSrcIdx.Set(outIdx / 3, segOffset + static_cast<vtkm::Id>(numPoints - 2)); outCellSrcIdx.Set(outIdx / 3, inCellIndex);
outIdx += 3; outIdx += 3;
} }
} }
@ -622,8 +616,7 @@ public:
} }
//Count number of polyline pts, tube pts and tube cells //Count number of polyline pts, tube pts and tube cells
vtkm::cont::ArrayHandle<vtkm::Id> ptsPerPolyline, ptsPerTube, numTubeConnIds, segPerPolyline, vtkm::cont::ArrayHandle<vtkm::Id> ptsPerPolyline, ptsPerTube, numTubeConnIds, validCell;
validCell;
vtkm::cont::ArrayHandle<vtkm::IdComponent> nonIncidentPtsPerPolyline; vtkm::cont::ArrayHandle<vtkm::IdComponent> nonIncidentPtsPerPolyline;
CountSegments countSegs(this->Capping, this->NumSides); CountSegments countSegs(this->Capping, this->NumSides);
vtkm::worklet::DispatcherMapTopology<CountSegments> countInvoker(countSegs); vtkm::worklet::DispatcherMapTopology<CountSegments> countInvoker(countSegs);
@ -633,7 +626,6 @@ public:
ptsPerPolyline, ptsPerPolyline,
ptsPerTube, ptsPerTube,
numTubeConnIds, numTubeConnIds,
segPerPolyline,
validCell); validCell);
vtkm::Id totalPolylinePts = vtkm::cont::Algorithm::Reduce(ptsPerPolyline, vtkm::Id(0)); vtkm::Id totalPolylinePts = vtkm::cont::Algorithm::Reduce(ptsPerPolyline, vtkm::Id(0));
@ -645,14 +637,13 @@ public:
vtkm::Id totalTubeCells = totalTubeConnIds / 3; vtkm::Id totalTubeCells = totalTubeConnIds / 3;
vtkm::cont::ArrayHandle<vtkm::Id> polylinePtOffset, nonIncidentPolylinePtOffset, vtkm::cont::ArrayHandle<vtkm::Id> polylinePtOffset, nonIncidentPolylinePtOffset,
tubePointOffsets, tubeConnOffsets, segOffset; tubePointOffsets, tubeConnOffsets;
vtkm::cont::Algorithm::ScanExclusive(ptsPerPolyline, polylinePtOffset); vtkm::cont::Algorithm::ScanExclusive(ptsPerPolyline, polylinePtOffset);
vtkm::cont::Algorithm::ScanExclusive( vtkm::cont::Algorithm::ScanExclusive(
vtkm::cont::make_ArrayHandleCast<vtkm::Id>(nonIncidentPtsPerPolyline), vtkm::cont::make_ArrayHandleCast<vtkm::Id>(nonIncidentPtsPerPolyline),
nonIncidentPolylinePtOffset); nonIncidentPolylinePtOffset);
vtkm::cont::Algorithm::ScanExclusive(ptsPerTube, tubePointOffsets); vtkm::cont::Algorithm::ScanExclusive(ptsPerTube, tubePointOffsets);
vtkm::cont::Algorithm::ScanExclusive(numTubeConnIds, tubeConnOffsets); vtkm::cont::Algorithm::ScanExclusive(numTubeConnIds, tubeConnOffsets);
vtkm::cont::Algorithm::ScanExclusive(segPerPolyline, segOffset);
//Generate normals at each point on all polylines //Generate normals at each point on all polylines
NormalsType normals; NormalsType normals;
@ -686,7 +677,6 @@ public:
nonIncidentPtsPerPolyline, nonIncidentPtsPerPolyline,
tubePointOffsets, tubePointOffsets,
tubeConnOffsets, tubeConnOffsets,
segOffset,
newConnectivity, newConnectivity,
this->OutputCellSourceIndex); this->OutputCellSourceIndex);
newCells.Fill(totalTubePts, vtkm::CELL_SHAPE_TRIANGLE, 3, newConnectivity); newCells.Fill(totalTubePts, vtkm::CELL_SHAPE_TRIANGLE, 3, newConnectivity);