You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

537 lines
17 KiB
Plaintext

/*=========================================================================
Program: Visualization Toolkit
Module: vtkStaticCellLinksTemplate.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm 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 "vtkStaticCellLinksTemplate.h"
#ifndef vtkStaticCellLinksTemplate_txx
#define vtkStaticCellLinksTemplate_txx
#include "vtkCellArray.h"
#include "vtkDataArrayRange.h"
#include "vtkDataSet.h"
#include "vtkExplicitStructuredGrid.h"
#include "vtkPolyData.h"
#include "vtkSMPTools.h"
#include "vtkUnstructuredGrid.h"
#include <array>
#include <atomic>
#include <type_traits>
//----------------------------------------------------------------------------
// Note: this class is a faster, threaded version of vtkCellLinks. It uses
// vtkSMPTools and std::atomic.
//----------------------------------------------------------------------------
// Default constructor. BuildLinks() does most of the work.
template <typename TIds>
vtkStaticCellLinksTemplate<TIds>::vtkStaticCellLinksTemplate()
: LinksSize(0)
, NumPts(0)
, NumCells(0)
, Links(nullptr)
, Offsets(nullptr)
{
if (std::is_same<unsigned short, TIds>::value)
{
this->Type = vtkAbstractCellLinks::STATIC_CELL_LINKS_USHORT;
}
else if (std::is_same<unsigned int, TIds>::value)
{
this->Type = vtkAbstractCellLinks::STATIC_CELL_LINKS_UINT;
}
else if (std::is_same<vtkIdType, TIds>::value)
{
this->Type = vtkAbstractCellLinks::STATIC_CELL_LINKS_IDTYPE;
}
else
{
this->Type = vtkAbstractCellLinks::STATIC_CELL_LINKS_SPECIALIZED;
}
this->SequentialProcessing = false;
}
//----------------------------------------------------------------------------
template <typename TIds>
vtkStaticCellLinksTemplate<TIds>::~vtkStaticCellLinksTemplate()
{
this->Initialize();
}
//----------------------------------------------------------------------------
// Clean up any previously allocated memory
template <typename TIds>
void vtkStaticCellLinksTemplate<TIds>::Initialize()
{
if (this->Links)
{
delete[] this->Links;
this->Links = nullptr;
}
if (this->Offsets)
{
delete[] this->Offsets;
this->Offsets = nullptr;
}
}
//----------------------------------------------------------------------------
// Build the link list array for any dataset type. Specialized methods are
// used for dataset types that use vtkCellArrays to represent cells.
template <typename TIds>
void vtkStaticCellLinksTemplate<TIds>::BuildLinks(vtkDataSet* ds)
{
// Use a fast path if polydata or unstructured grid
if (ds->GetDataObjectType() == VTK_POLY_DATA)
{
return this->BuildLinks(static_cast<vtkPolyData*>(ds));
}
else if (ds->GetDataObjectType() == VTK_UNSTRUCTURED_GRID)
{
return this->BuildLinks(static_cast<vtkUnstructuredGrid*>(ds));
}
else if (ds->GetDataObjectType() == VTK_EXPLICIT_STRUCTURED_GRID)
{
return this->BuildLinks(static_cast<vtkExplicitStructuredGrid*>(ds));
}
// Any other type of dataset. Generally this is not called as datasets have
// their own, more efficient ways of getting similar information.
// Make sure that we clear out previous allocation.
this->NumCells = ds->GetNumberOfCells();
this->NumPts = ds->GetNumberOfPoints();
vtkIdType npts, ptId;
vtkIdType cellId, j;
vtkIdList* cellPts = vtkIdList::New();
// Traverse data to determine number of uses of each point. Also count the
// number of links to allocate.
this->Offsets = new TIds[this->NumPts + 1];
std::fill_n(this->Offsets, this->NumPts, 0);
for (this->LinksSize = 0, cellId = 0; cellId < this->NumCells; cellId++)
{
ds->GetCellPoints(cellId, cellPts);
npts = cellPts->GetNumberOfIds();
for (j = 0; j < npts; j++)
{
this->Offsets[cellPts->GetId(j)]++;
this->LinksSize++;
}
}
// Allocate space for links. Perform prefix sum.
this->Links = new TIds[this->LinksSize + 1];
this->Links[this->LinksSize] = this->NumPts;
for (ptId = 0; ptId < this->NumPts; ++ptId)
{
npts = this->Offsets[ptId + 1];
this->Offsets[ptId + 1] = this->Offsets[ptId] + npts;
}
// Now build the links. The summation from the prefix sum indicates where
// the cells are to be inserted. Each time a cell is inserted, the offset
// is decremented. In the end, the offset array is also constructed as it
// points to the beginning of each cell run.
for (cellId = 0; cellId < this->NumCells; ++cellId)
{
ds->GetCellPoints(cellId, cellPts);
npts = cellPts->GetNumberOfIds();
for (j = 0; j < npts; ++j)
{
ptId = cellPts->GetId(j);
this->Offsets[ptId]--;
this->Links[this->Offsets[ptId]] = cellId;
}
}
this->Offsets[this->NumPts] = this->LinksSize;
cellPts->Delete();
}
namespace vtkSCLT_detail
{
struct CountPoints
{
template <typename CellStateT, typename TIds>
void operator()(CellStateT& state,
TIds* linkOffsets, // May be std::atomic<...>
const vtkIdType beginCellId, const vtkIdType endCellId, const vtkIdType idOffset = 0)
{
using ValueType = typename CellStateT::ValueType;
const vtkIdType connBeginId = state.GetBeginOffset(beginCellId);
const vtkIdType connEndId = state.GetEndOffset(endCellId - 1);
auto connRange = vtk::DataArrayValueRange<1>(state.GetConnectivity(), connBeginId, connEndId);
// Count number of point uses
for (const ValueType ptId : connRange)
{
++linkOffsets[static_cast<size_t>(idOffset + ptId)];
}
}
};
// Serial version:
struct BuildLinks
{
template <typename CellStateT, typename TIds>
void operator()(CellStateT& state, TIds* linkOffsets, TIds* links, const vtkIdType idOffset = 0)
{
using ValueType = typename CellStateT::ValueType;
const vtkIdType numCells = state.GetNumberOfCells();
// Now build the links. The summation from the prefix sum indicates where
// the cells are to be inserted. Each time a cell is inserted, the offset
// is decremented. In the end, the offset array is also constructed as it
// points to the beginning of each cell run.
for (vtkIdType cellId = 0; cellId < numCells; ++cellId)
{
const auto cell = state.GetCellRange(cellId);
for (const ValueType cellPtId : cell)
{
const size_t ptId = static_cast<size_t>(cellPtId);
--linkOffsets[ptId];
links[linkOffsets[ptId]] = static_cast<TIds>(idOffset + cellId);
}
}
}
};
// Parallel version:
struct BuildLinksThreaded
{
template <typename CellStateT, typename TIds>
void operator()(CellStateT& state, const TIds* offsets, std::atomic<TIds>* counts, TIds* links,
const vtkIdType beginCellId, const vtkIdType endCellId, const TIds idOffset = 0)
{
using ValueType = typename CellStateT::ValueType;
// Now build the links. The summation from the prefix sum indicates where
// the cells are to be inserted. Each time a cell is inserted, the offset
// is decremented. In the end, the offset array is also constructed as it
// points to the beginning of each cell run.
for (vtkIdType cellId = beginCellId; cellId < endCellId; ++cellId)
{
const auto cell = state.GetCellRange(cellId);
for (const ValueType cellPtId : cell)
{
const size_t ptId = static_cast<size_t>(cellPtId);
// memory_order_relaxed is safe here, since we're not using the atomics
// for synchroniziation.
const TIds offset =
offsets[ptId] + counts[ptId].fetch_sub(1, std::memory_order_relaxed) - 1;
links[offset] = idOffset + cellId;
}
}
}
};
} // end namespace vtkSCLT_detail
//----------------------------------------------------------------------------
// Build the link list array for unstructured grids. Note this is a serial
// implementation: while there is another method (threaded) that is usually
// much faster, in certain pathological situations the serial version can be
// faster.
template <typename TIds>
void vtkStaticCellLinksTemplate<TIds>::SerialBuildLinks(
const vtkIdType numPts, const vtkIdType numCells, vtkCellArray* cellArray)
{
// Basic information about the grid
this->NumPts = numPts;
this->NumCells = numCells;
this->LinksSize = cellArray->GetConnectivityArray()->GetNumberOfValues();
// Extra one allocated to simplify later pointer manipulation
this->Links = new TIds[this->LinksSize + 1];
this->Links[this->LinksSize] = this->NumPts;
this->Offsets = new TIds[numPts + 1];
std::fill_n(this->Offsets, this->NumPts + 1, 0);
// Count how many cells each point appears in:
cellArray->Visit(vtkSCLT_detail::CountPoints{}, this->Offsets, 0, numCells);
// Perform prefix sum (inclusive scan)
for (vtkIdType ptId = 0; ptId < this->NumPts; ++ptId)
{
const vtkIdType npts = this->Offsets[ptId + 1];
this->Offsets[ptId + 1] = this->Offsets[ptId] + npts;
}
// Construct the links table and finalize the offsets:
cellArray->Visit(vtkSCLT_detail::BuildLinks{}, this->Offsets, this->Links);
this->Offsets[numPts] = this->LinksSize;
}
//----------------------------------------------------------------------------
// Threaded implementation of BuildLinks() using vtkSMPTools and std::atomic.
namespace
{ // anonymous
template <typename TIds>
struct CountUses
{
vtkCellArray* CellArray;
std::atomic<TIds>* Counts;
CountUses(vtkCellArray* cellArray, std::atomic<TIds>* counts)
: CellArray(cellArray)
, Counts(counts)
{
}
void operator()(vtkIdType cellId, vtkIdType endCellId)
{
this->CellArray->Visit(vtkSCLT_detail::CountPoints{}, this->Counts, cellId, endCellId);
}
};
template <typename TIds>
struct InsertLinks
{
vtkCellArray* CellArray;
std::atomic<TIds>* Counts;
const TIds* Offsets;
TIds* Links;
InsertLinks(vtkCellArray* cellArray, std::atomic<TIds>* counts, const TIds* offsets, TIds* links)
: CellArray(cellArray)
, Counts(counts)
, Offsets(offsets)
, Links(links)
{
}
void operator()(vtkIdType cellId, vtkIdType endCellId)
{
this->CellArray->Visit(vtkSCLT_detail::BuildLinksThreaded{}, this->Offsets, this->Counts,
this->Links, cellId, endCellId);
}
};
} // anonymous
//----------------------------------------------------------------------------
// Build the link list array for unstructured grids. Note this is a threaded
// implementation: it uses SMPTools and atomics to prevent race situations.
template <typename TIds>
void vtkStaticCellLinksTemplate<TIds>::ThreadedBuildLinks(
const vtkIdType numPts, const vtkIdType numCells, vtkCellArray* cellArray)
{
// Basic information about the grid
this->NumPts = numPts;
this->NumCells = numCells;
// Trick follows: the size of the Links array is equal to
// the size of the cell array, minus the number of cells.
this->LinksSize = cellArray->GetNumberOfConnectivityIds();
// Extra one allocated to simplify later pointer manipulation
this->Links = new TIds[this->LinksSize + 1];
this->Links[this->LinksSize] = this->NumPts;
// Create an array of atomics with initial count=0. This will keep
// track of point uses. Count them in parallel.
std::atomic<TIds>* counts = new std::atomic<TIds>[numPts] {};
CountUses<TIds> count(cellArray, counts);
vtkSMPTools::For(0, numCells, count);
// Perform prefix sum to determine offsets
vtkIdType ptId, npts;
this->Offsets = new TIds[numPts + 1];
this->Offsets[0] = 0;
for (ptId = 1; ptId < numPts; ++ptId)
{
npts = counts[ptId - 1];
this->Offsets[ptId] = this->Offsets[ptId - 1] + npts;
}
this->Offsets[numPts] = this->LinksSize;
// Now insert cell ids into cell links.
InsertLinks<TIds> insertLinks(cellArray, counts, this->Offsets, this->Links);
vtkSMPTools::For(0, numCells, insertLinks);
// Clean up
delete[] counts;
}
//----------------------------------------------------------------------------
// Build the link list array for unstructured grids
template <typename TIds>
void vtkStaticCellLinksTemplate<TIds>::BuildLinks(vtkUnstructuredGrid* ugrid)
{
// Basic information about the grid
vtkIdType numPts = ugrid->GetNumberOfPoints();
vtkIdType numCells = ugrid->GetNumberOfCells();
// We're going to get into the guts of the class
vtkCellArray* cellArray = ugrid->GetCells();
// Use serial or threaded implementations
if (!this->SequentialProcessing)
{
this->ThreadedBuildLinks(numPts, numCells, cellArray);
}
else
{
this->SerialBuildLinks(numPts, numCells, cellArray);
}
}
//----------------------------------------------------------------------------
// Build the link list array for unstructured grids
template <typename TIds>
void vtkStaticCellLinksTemplate<TIds>::BuildLinks(vtkExplicitStructuredGrid* esgrid)
{
// Basic information about the grid
vtkIdType numPts = esgrid->GetNumberOfPoints();
vtkIdType numCells = esgrid->GetNumberOfCells();
// We're going to get into the guts of the class
vtkCellArray* cellArray = esgrid->GetCells();
// Use serial implementation. TODO: add threaded implementation
this->SerialBuildLinks(numPts, numCells, cellArray);
}
//----------------------------------------------------------------------------
// Build the link list array for poly data. This is more complex because there
// are potentially four different cell arrays to contend with.
template <typename TIds>
void vtkStaticCellLinksTemplate<TIds>::BuildLinks(vtkPolyData* pd)
{
// Basic information about the grid
this->NumCells = pd->GetNumberOfCells();
this->NumPts = pd->GetNumberOfPoints();
vtkCellArray* cellArrays[4];
vtkIdType numCells[4];
vtkIdType sizes[4];
int i, j;
cellArrays[0] = pd->GetVerts();
cellArrays[1] = pd->GetLines();
cellArrays[2] = pd->GetPolys();
cellArrays[3] = pd->GetStrips();
for (i = 0; i < 4; ++i)
{
if (cellArrays[i] != nullptr)
{
numCells[i] = cellArrays[i]->GetNumberOfCells();
sizes[i] = cellArrays[i]->GetConnectivityArray()->GetNumberOfValues();
}
else
{
numCells[i] = 0;
sizes[i] = 0;
}
} // for the four polydata arrays
// Allocate
this->LinksSize = sizes[0] + sizes[1] + sizes[2] + sizes[3];
this->Links = new TIds[this->LinksSize + 1];
this->Links[this->LinksSize] = this->NumPts;
this->Offsets = new TIds[this->NumPts + 1];
this->Offsets[this->NumPts] = this->LinksSize;
std::fill_n(this->Offsets, this->NumPts + 1, 0);
// Now create the links.
vtkIdType npts, CellId, ptId;
// Visit the four arrays
for (CellId = 0, j = 0; j < 4; ++j)
{
// Count number of point uses
cellArrays[j]->Visit(vtkSCLT_detail::CountPoints{}, this->Offsets, 0, numCells[j], CellId);
CellId += numCells[j];
} // for each of the four polydata cell arrays
// Perform prefix sum (inclusive scan)
for (ptId = 0; ptId < this->NumPts; ++ptId)
{
npts = this->Offsets[ptId + 1];
this->Offsets[ptId + 1] = this->Offsets[ptId] + npts;
}
// Now build the links. The summation from the prefix sum indicates where
// the cells are to be inserted. Each time a cell is inserted, the offset
// is decremented. In the end, the offset array is also constructed as it
// points to the beginning of each cell run.
for (CellId = 0, j = 0; j < 4; ++j)
{
cellArrays[j]->Visit(vtkSCLT_detail::BuildLinks{}, this->Offsets, this->Links, CellId);
CellId += numCells[j];
} // for each of the four polydata arrays
this->Offsets[this->NumPts] = this->LinksSize;
}
//----------------------------------------------------------------------------
// Satisfy vtkAbstractCellLinks API
template <typename TIds>
unsigned long vtkStaticCellLinksTemplate<TIds>::GetActualMemorySize()
{
unsigned long total = 0;
if (Links != nullptr)
{
total = static_cast<unsigned long>((this->LinksSize + 1) * sizeof(TIds));
total += static_cast<unsigned long>((this->NumPts + 1) * sizeof(TIds));
}
return total;
}
//----------------------------------------------------------------------------
// Satisfy vtkAbstractCellLinks API
template <typename TIds>
void vtkStaticCellLinksTemplate<TIds>::DeepCopy(vtkAbstractCellLinks* src)
{
vtkStaticCellLinksTemplate<TIds>* links = dynamic_cast<vtkStaticCellLinksTemplate<TIds>*>(src);
if (links)
{
this->LinksSize = links->LinksSize;
this->NumPts = links->NumPts;
this->NumCells = links->NumCells;
if (this->Links != nullptr)
{
delete[] this->Links;
}
this->Links = new TIds[this->LinksSize + 1];
std::copy(links->Links, links->Links + (this->LinksSize + 1), this->Links);
if (this->Offsets != nullptr)
{
delete[] this->Offsets;
}
this->Offsets = new TIds[this->NumPts + 1];
std::copy(links->Offsets, links->Offsets + (this->NumPts + 1), this->Offsets);
}
}
#endif