vtk-m2/vtkm/worklet/StreamSurface.h
Allison Vacanti 5db762ee71 Refactor topology mappings to clarify meaning.
The `From` and `To` nomenclature for topology mapping has been confusing for
both users and developers, especially at lower levels where the intention of
mapping attributes from one element to another is easily conflated with the
concept of mapping indices (which maps in the exact opposite direction).

These identifiers have been renamed to `VisitTopology` and `IncidentTopology`
to clarify the direction of the mapping. The order in which these template
parameters are specified for `WorkletMapTopology` have also been reversed,
since eventually there may be more than one `IncidentTopology`, and having
`IncidentTopology` at the end will allow us to replace it with a variadic
template parameter pack in the future.

Other implementation details supporting these worklets, include `Fetch` tags,
`Connectivity` classes, and methods on the various `CellSet` classes (such as
`PrepareForInput` have also reversed their template arguments. These will need
to be cautiously updated.

The convenience implementations of `WorkletMapTopology` have been renamed for
clarity as follows:

```
WorkletMapPointToCell --> WorkletVisitCellsWithPoints
WorkletMapCellToPoint --> WorkletVisitPointsWithCells
```

The `ControlSignature` tags have been renamed as follows:

```
FieldInTo --> FieldInVisit
FieldInFrom --> FieldInMap
FromCount --> IncidentElementCount
FromIndices --> IncidentElementIndices
```
2019-08-06 11:27:26 -04:00

283 lines
9.6 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.
//============================================================================
#ifndef vtk_m_worklet_streamsurface_h
#define vtk_m_worklet_streamsurface_h
#include <typeinfo>
#include <vtkm/VectorAnalysis.h>
#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ArrayHandleView.h>
#include <vtkm/cont/CellSetExplicit.h>
#include <vtkm/cont/DataSet.h>
#include <vtkm/worklet/DispatcherMapTopology.h>
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/WorkletMapTopology.h>
namespace vtkm
{
namespace worklet
{
class StreamSurface
{
public:
//Helper worklet to count various things in each polyline.
class CountPolylines : public vtkm::worklet::WorkletVisitCellsWithPoints
{
public:
VTKM_CONT
CountPolylines() {}
using ControlSignature = void(CellSetIn, WholeArrayInOut invalidCell, FieldOut ptsPerPolyline);
using ExecutionSignature = void(CellShape shapeType,
PointCount numPoints,
_2 isValid,
_3 ptsPerPolyline);
using InputDomain = _1;
template <typename CellShapeTag, typename InValidType>
VTKM_EXEC void operator()(const CellShapeTag& shapeType,
const vtkm::IdComponent& numPoints,
InValidType& invalidCell,
vtkm::Id& ptsPerPolyline) const
{
// We only support polylines that contain 2 or more points.
if (shapeType.Id == vtkm::CELL_SHAPE_POLY_LINE && numPoints > 1)
ptsPerPolyline = numPoints;
else
{
invalidCell.Set(0, 1);
ptsPerPolyline = 0;
}
}
private:
};
//Helper worklet to determine number of triangles for each pair of polylines
class CountTriangleConn : public vtkm::worklet::WorkletMapField
{
public:
VTKM_CONT
CountTriangleConn() {}
using ControlSignature = void(FieldIn numPts0, FieldIn numPts1, FieldOut outConnCount);
using ExecutionSignature = void(_1 numPts0, _2 numPts1, _3 outConnCount);
using InputDomain = _1;
VTKM_EXEC void operator()(const vtkm::Id& numPts0,
const vtkm::Id& numPts1,
vtkm::Id& outConnCount) const
{
if (numPts0 == numPts1)
outConnCount = (numPts0 - 1) * 2 * 3;
else if (numPts1 < numPts0)
outConnCount = (numPts0 - 1) * 2 * 3 + (numPts1 - numPts0) * 3;
else
outConnCount = (numPts1 - 1) * 2 * 3 + (numPts0 - numPts1) * 3;
}
private:
};
//Helper worklet to generate the stream surface cells
class GenerateCells : public vtkm::worklet::WorkletMapField
{
public:
VTKM_CONT
GenerateCells() {}
using ControlSignature = void(FieldIn numPts0,
FieldIn numPts1,
FieldIn offset0,
FieldIn offset1,
FieldIn connOffset,
WholeArrayOut outConn);
using ExecutionSignature =
void(_1 numPts0, _2 numPts1, _3 ptOffset0, _4 offset1, _5 connOffset, _6 outConn);
using InputDomain = _1;
template <typename OutConnType>
VTKM_EXEC void operator()(const vtkm::Id& numPts0,
const vtkm::Id& numPts1,
const vtkm::Id& offset0,
const vtkm::Id& offset1,
const vtkm::Id& connOffset,
OutConnType& outConn) const
{
vtkm::Id idx0 = 0, idx1 = 0;
vtkm::Id nextToLastIdx0 = numPts0 - 1;
vtkm::Id nextToLastIdx1 = numPts1 - 1;
vtkm::Id outIdx = connOffset;
//There could be different numbers of points in the pairs of polylines.
//Create pairs of triangles as far as possible.
/* polyline0 polyline1
*
* idx0 + 1 x----------- x idx1 + 1
* | \ |
* | \ Tri2 |
* | \ |
* | \ |
* | Tri1 \ |
* | \|
* idx0 + 0 x ---------- x idx1 + 0
*
*/
while (idx0 < nextToLastIdx0 && idx1 < nextToLastIdx1)
{
//Tri 1
outConn.Set(outIdx + 0, offset0 + idx0 + 0);
outConn.Set(outIdx + 1, offset1 + idx1 + 0);
outConn.Set(outIdx + 2, offset0 + idx0 + 1);
//Tri 2
outConn.Set(outIdx + 3, offset0 + idx0 + 1);
outConn.Set(outIdx + 4, offset1 + idx1 + 0);
outConn.Set(outIdx + 5, offset1 + idx1 + 1);
idx0++;
idx1++;
outIdx += 6;
}
// Same number of points in both polylines. We are done.
if (numPts0 == numPts1)
return;
//If we have more points in one polyline, create a triangle fan
//to complete the triangulation.
//polyline0 is at the end, polyline1 still has more points.
/* polyline0 polyline1
*
* x idx1 + 1
* /|
* / |
* / |
* / |
* / Tri |
* / |
* idx0 + 0 x ---------- x idx1 + 0
*
*/
if (idx0 == nextToLastIdx0 && idx1 < nextToLastIdx1)
{
while (idx1 < nextToLastIdx1)
{
outConn.Set(outIdx + 0, offset0 + idx0 + 0);
outConn.Set(outIdx + 1, offset1 + idx1 + 0);
outConn.Set(outIdx + 2, offset1 + idx1 + 1);
idx1++;
outIdx += 3;
}
}
//polyline1 is at the end, polyline0 still has more points.
/* polyline0 polyline1
*
* idx0 + 1 x
* | \
* | \
* | \
* | \
* | Tri \
* | \
* idx0 + 0 x ---------- x idx1 + 0
*
*/
else
{
while (idx0 < nextToLastIdx0)
{
outConn.Set(outIdx + 0, offset0 + idx0 + 0);
outConn.Set(outIdx + 1, offset1 + idx1 + 0);
outConn.Set(outIdx + 2, offset0 + idx0 + 1);
idx0++;
outIdx += 3;
}
}
}
private:
};
VTKM_CONT
StreamSurface() {}
VTKM_CONT
void Run(const vtkm::cont::CoordinateSystem& coords,
const vtkm::cont::DynamicCellSet& cellset,
vtkm::cont::ArrayHandle<vtkm::Vec3f>& newPoints,
vtkm::cont::CellSetSingleType<>& newCells)
{
using ExplCoordsType = vtkm::cont::ArrayHandle<vtkm::Vec3f>;
if (!(coords.GetData().IsType<ExplCoordsType>() &&
(cellset.IsSameType(vtkm::cont::CellSetExplicit<>()) ||
cellset.IsSameType(vtkm::cont::CellSetSingleType<>()))))
{
throw vtkm::cont::ErrorBadValue("Stream surface requires polyline data.");
}
//Count number of polylines and make sure we ONLY have polylines
vtkm::cont::ArrayHandle<vtkm::Id> ptsPerPolyline, invalidCell;
vtkm::worklet::DispatcherMapTopology<CountPolylines> countInvoker;
//We only care if there are ANY non-polyline cells. So use a one element array.
//Any non-polyline cell will set the value to 1. No need to worry about race conditions
//as the outcasts will all set it to the same value.
invalidCell.Allocate(1);
invalidCell.GetPortalControl().Set(0, 0);
countInvoker.Invoke(cellset, invalidCell, ptsPerPolyline);
if (invalidCell.GetPortalConstControl().Get(0) == 1)
throw vtkm::cont::ErrorBadValue("Stream surface requires only polyline data.");
vtkm::Id numPolylines = cellset.GetNumberOfCells();
//Compute polyline offsets
vtkm::cont::ArrayHandle<vtkm::Id> polylineOffset;
vtkm::cont::Algorithm::ScanExclusive(ptsPerPolyline, polylineOffset);
auto ptsPerPolyline0 = vtkm::cont::make_ArrayHandleView(ptsPerPolyline, 0, numPolylines - 1);
auto ptsPerPolyline1 = vtkm::cont::make_ArrayHandleView(ptsPerPolyline, 1, numPolylines - 1);
//Count the number of triangles to be generated
vtkm::cont::ArrayHandle<vtkm::Id> triangleConnCount, triangleConnOffset;
vtkm::worklet::DispatcherMapField<CountTriangleConn> countTriInvoker;
countTriInvoker.Invoke(ptsPerPolyline0, ptsPerPolyline1, triangleConnCount);
vtkm::cont::Algorithm::ScanExclusive(triangleConnCount, triangleConnOffset);
//Surface points are same as input points.
newPoints = coords.GetData().Cast<ExplCoordsType>();
//Create surface triangles
vtkm::Id numConnIds = vtkm::cont::Algorithm::Reduce(triangleConnCount, vtkm::Id(0));
vtkm::cont::ArrayHandle<vtkm::Id> newConnectivity;
newConnectivity.Allocate(numConnIds);
vtkm::worklet::DispatcherMapField<GenerateCells> genCellsDisp;
genCellsDisp.Invoke(ptsPerPolyline0,
ptsPerPolyline1,
vtkm::cont::make_ArrayHandleView(polylineOffset, 0, numPolylines - 1),
vtkm::cont::make_ArrayHandleView(polylineOffset, 1, numPolylines - 1),
triangleConnOffset,
newConnectivity);
newCells.Fill(numConnIds, vtkm::CELL_SHAPE_TRIANGLE, 3, newConnectivity);
}
private:
};
}
}
#endif // vtk_m_worklet_streamsurface_h