mirror of
https://github.com/OpenFOAM/ThirdParty-6.git
synced 2025-12-08 06:57:43 +00:00
478 lines
15 KiB
C++
478 lines
15 KiB
C++
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: vtkSpanSpace.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 "vtkSpanSpace.h"
|
|
|
|
#include "vtkCell.h"
|
|
#include "vtkGenericCell.h"
|
|
#include "vtkDataSet.h"
|
|
#include "vtkDataArray.h"
|
|
#include "vtkDoubleArray.h"
|
|
#include "vtkIdList.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkPointData.h"
|
|
#include "vtkSMPTools.h"
|
|
#include "vtkSMPThreadLocalObject.h"
|
|
|
|
#include <algorithm> //std::sort
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// The following tuple is an interface between VTK class and internal class
|
|
struct vtkSpanTuple
|
|
{
|
|
vtkIdType CellId; //originating cellId
|
|
vtkIdType Index; //i-j index into span space (numCells in length)
|
|
//Operator< used to support sorting operation.
|
|
bool operator< (const vtkSpanTuple& tuple) const
|
|
{return Index < tuple.Index;}
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// This class manages the span space, including methods to create, access, and
|
|
// delete it.
|
|
class vtkInternalSpanSpace
|
|
{
|
|
public:
|
|
// Okay the various ivars
|
|
vtkIdType Dim; //the number of rows and number of columns
|
|
double SMin, SMax, Range; //min and max scalar values; range
|
|
vtkSpanTuple *Space; //(cellId,s) span space tuples
|
|
vtkIdType *CellIds; //sorted list of cell ids
|
|
vtkIdType *Offsets; //offset into CellIds for each bucket (Dim*Dim in size)
|
|
vtkIdType NumCells; //total number of cells in span space
|
|
vtkIdType *CandidateCells; //to support parallel computing
|
|
vtkIdType NumCandidates;
|
|
|
|
// Constructor
|
|
vtkInternalSpanSpace(vtkIdType dim, double sMin, double sMax, vtkIdType numCells);
|
|
|
|
// Destructore
|
|
~vtkInternalSpanSpace();
|
|
|
|
// Insert cells with scalar range (smin,smax) in span space
|
|
void SetSpanPoint(vtkIdType id, double sMin, double sMax)
|
|
{
|
|
vtkIdType i = static_cast<vtkIdType>(
|
|
static_cast<double>(this->Dim) * (sMin - this->SMin) / this->Range);
|
|
vtkIdType j = static_cast<vtkIdType>(
|
|
static_cast<double>(this->Dim) * (sMax - this->SMin) / this->Range);
|
|
i = ( i < 0 ? 0 : (i >= this->Dim ? this->Dim-1 : i));
|
|
j = ( j < 0 ? 0 : (j >= this->Dim ? this->Dim-1 : j));
|
|
this->Space[id].CellId = id;
|
|
this->Space[id].Index = i + j*Dim;
|
|
}
|
|
|
|
// Do the hard work of sorting and arranging the span space
|
|
void Build();
|
|
|
|
// Given a scalar value, return a rectangle in span space. This
|
|
// rectangle is used subsequently for extracing individual
|
|
// rows. rMin is the lower (i,j) lower-left corner of the rectangle;
|
|
// rMax is the upper-right corner (i,j) position of the
|
|
// rectangle.
|
|
void GetSpanRectangle(double value, vtkIdType rMin[2], vtkIdType rMax[2])
|
|
{
|
|
vtkIdType i = static_cast<vtkIdType>(
|
|
static_cast<double>(this->Dim) * (value - this->SMin) / this->Range);
|
|
|
|
rMin[0] = 0; //xmin on rectangle left boundary
|
|
rMin[1] = i; //ymin on rectangle bottom
|
|
rMax[0] = i+1; //xmax (non-inclusive interval) on right hand boundary
|
|
rMax[1] = Dim; //ymax (non-inclusive interval) on top boundary of span space
|
|
}
|
|
|
|
// Return an array of cellIds along a prescribed row within the span
|
|
// rectangle. Note that the row should be inside the
|
|
// rectangle. Note that numCells may be zero in which case the
|
|
// pointer returned will not point to valid data.
|
|
vtkIdType *GetCellsInSpan(vtkIdType row, vtkIdType rMin[2], vtkIdType rMax[2],
|
|
vtkIdType& numCells)
|
|
{
|
|
// Find the beginning of some cells on this row.
|
|
vtkIdType startOffset = *(this->Offsets + row*this->Dim + rMin[0]);
|
|
vtkIdType endOffset = *(this->Offsets + row*this->Dim + rMax[0]);
|
|
numCells = endOffset - startOffset;
|
|
return this->CellIds + startOffset;
|
|
}
|
|
|
|
class MapToSpanSpace
|
|
{
|
|
public:
|
|
vtkInternalSpanSpace *SpanSpace;
|
|
vtkDataSet *DataSet;
|
|
vtkDataArray *Scalars;
|
|
vtkSMPThreadLocalObject<vtkIdList> CellPts;
|
|
vtkSMPThreadLocalObject<vtkDoubleArray> CellScalars;
|
|
|
|
MapToSpanSpace(vtkInternalSpanSpace *ss, vtkDataSet *ds, vtkDataArray *s) :
|
|
SpanSpace(ss), DataSet(ds), Scalars(s)
|
|
{
|
|
}
|
|
|
|
void Initialize()
|
|
{
|
|
vtkIdList*& cellPts = this->CellPts.Local();
|
|
cellPts->SetNumberOfIds(12);
|
|
vtkDoubleArray*& cellScalars = this->CellScalars.Local();
|
|
cellScalars->SetNumberOfTuples(12);
|
|
}
|
|
|
|
void operator() (vtkIdType cellId, vtkIdType endCellId)
|
|
{
|
|
vtkIdType j, numScalars;
|
|
double *s, sMin, sMax;
|
|
vtkIdList*& cellPts = this->CellPts.Local();
|
|
vtkDoubleArray*& cellScalars = this->CellScalars.Local();
|
|
|
|
for ( ; cellId < endCellId; ++cellId )
|
|
{
|
|
this->DataSet->GetCellPoints(cellId,cellPts);
|
|
numScalars = cellPts->GetNumberOfIds();
|
|
cellScalars->SetNumberOfTuples(numScalars);
|
|
this->Scalars->GetTuples(cellPts, cellScalars);
|
|
s = cellScalars->GetPointer(0);
|
|
|
|
sMin = VTK_DOUBLE_MAX;
|
|
sMax = VTK_DOUBLE_MIN;
|
|
for ( j=0; j < numScalars; j++ )
|
|
{
|
|
if ( s[j] < sMin )
|
|
{
|
|
sMin = s[j];
|
|
}
|
|
if ( s[j] > sMax )
|
|
{
|
|
sMax = s[j];
|
|
}
|
|
}//for all cell scalars
|
|
// Compute span space id, and prepare to map
|
|
this->SpanSpace->SetSpanPoint(cellId, sMin, sMax);
|
|
}//for all cells in this thread
|
|
}
|
|
|
|
void Reduce()
|
|
{
|
|
}
|
|
};
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
vtkInternalSpanSpace::
|
|
vtkInternalSpanSpace(vtkIdType dim, double sMin, double sMax, vtkIdType numCells)
|
|
{
|
|
this->Dim = (dim > 0 ? dim : 256);
|
|
this->SMin = sMin;
|
|
this->SMax = sMax;
|
|
this->Range = (sMax - sMin);
|
|
this->Offsets = new vtkIdType [dim*dim+1]; //leave one extra for numCells
|
|
std::fill_n(this->Offsets, dim*dim, 0);
|
|
this->NumCells = numCells;
|
|
this->Space = new vtkSpanTuple [numCells];
|
|
this->CellIds = new vtkIdType [numCells];
|
|
this->CandidateCells = NULL;
|
|
this->NumCandidates = 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
vtkInternalSpanSpace::
|
|
~vtkInternalSpanSpace()
|
|
{
|
|
delete [] this->Offsets;
|
|
delete [] this->Space;
|
|
delete [] this->CellIds;
|
|
delete [] this->CandidateCells;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// The heart of the algorithm. The cells are sorted in i-j space into
|
|
// a contiguous array. Then the offsets into the array are built.
|
|
void vtkInternalSpanSpace::
|
|
Build()
|
|
{
|
|
// The first thing to do is to sort the elements across span
|
|
// space. The shape of the span space is upper diagonal (because
|
|
// smax >= smin) but for simplicity sake (for now) we just use a
|
|
// rectangular discretization (of dimensions Dim*Dim).
|
|
vtkSMPTools::Sort(this->Space,this->Space+this->NumCells);
|
|
|
|
// Now that this is done, we create a matrix of offsets into the
|
|
// sorted array. This enables rapid access into the sorted cellIds,
|
|
// including access to span space rows of cells. Also for
|
|
// convenience we replicate the cell ids. This further supports
|
|
// parallel traversal which is a common use case. If I was smarter I
|
|
// could use the CellIds already contained in the tuple and not have
|
|
// to duplicate this, but then sorting requires a custom class with
|
|
// iterators, etc.
|
|
|
|
// First count the number of contributions in each bucket.
|
|
vtkIdType cellId, numElems;
|
|
for ( cellId=0; cellId < this->NumCells; ++cellId )
|
|
{
|
|
this->Offsets[this->Space[cellId].Index]++;
|
|
this->CellIds[cellId] = this->Space[cellId].CellId;
|
|
}
|
|
|
|
// Now accumulate offset array
|
|
vtkIdType i, j, jOffset, idx, currentOffset = 0;
|
|
for (j=0; j < this->Dim; ++j)
|
|
{
|
|
jOffset = j * this->Dim;
|
|
for (i=0; i < this->Dim; ++i)
|
|
{
|
|
idx = i + jOffset;
|
|
numElems = this->Offsets[idx];
|
|
this->Offsets[idx] = currentOffset;
|
|
currentOffset += numElems;
|
|
}
|
|
}
|
|
this->Offsets[this->Dim*this->Dim] = this->NumCells;
|
|
|
|
// We don't need the span space tuple array any more, we have
|
|
// offsets and cell ids computed.
|
|
delete [] this->Space;
|
|
this->Space = NULL;
|
|
|
|
// The candidate cell list can be allocated
|
|
if ( this->CandidateCells )
|
|
{
|
|
delete [] this->CandidateCells;
|
|
this->CandidateCells = NULL;
|
|
}
|
|
this->CandidateCells = new vtkIdType [this->NumCells];
|
|
}
|
|
|
|
|
|
//---The VTK Class proper------------------------------------------------------
|
|
vtkStandardNewMacro(vtkSpanSpace);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Instantiate empty span space object.
|
|
vtkSpanSpace::vtkSpanSpace()
|
|
{
|
|
this->SpanSpace = NULL;
|
|
this->RMin[0] = this->RMin[1] = 0;
|
|
this->RMax[0] = this->RMax[1] = 0;
|
|
this->BatchSize = 10;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
vtkSpanSpace::~vtkSpanSpace()
|
|
{
|
|
this->Initialize();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Frees memory and resets object as appropriate.
|
|
void vtkSpanSpace::Initialize()
|
|
{
|
|
if (this->SpanSpace)
|
|
{
|
|
delete this->SpanSpace;
|
|
this->SpanSpace = NULL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Construct the scalar tree / span space from the dataset
|
|
// provided. Checks build times and modified time from input and
|
|
// reconstructs the tree if necessaery.
|
|
void vtkSpanSpace::BuildTree()
|
|
{
|
|
vtkIdType numCells;
|
|
|
|
// Check input...see whether we have to rebuild
|
|
//
|
|
if ( !this->DataSet || (numCells = this->DataSet->GetNumberOfCells()) < 1 )
|
|
{
|
|
vtkErrorMacro( << "No data to build tree with");
|
|
return;
|
|
}
|
|
|
|
if ( this->BuildTime > this->MTime
|
|
&& this->BuildTime > this->DataSet->GetMTime() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
vtkDebugMacro( << "Building span space..." );
|
|
|
|
// If no scalars set then try and grab them from dataset
|
|
if ( ! this->Scalars )
|
|
{
|
|
this->SetScalars(this->DataSet->GetPointData()->GetScalars());
|
|
}
|
|
if ( ! this->Scalars )
|
|
{
|
|
vtkErrorMacro( << "No scalar data to build trees with");
|
|
return;
|
|
}
|
|
|
|
// We need a range for the scalars
|
|
double range[2];
|
|
this->Scalars->GetRange(range);
|
|
double R = range[1] - range[0];
|
|
if ( R <= 0.0 )
|
|
{
|
|
vtkErrorMacro( << "Bad scalar range");
|
|
return;
|
|
}
|
|
|
|
// Prepare to process scalars
|
|
this->Initialize(); //clears out old span space arrays
|
|
|
|
// The first pass loops over all cells, mapping them into span space
|
|
// (i.e., an integer id into a gridded span space). Later this id will
|
|
// be used to sort the cells across the span space, so that cells
|
|
// can be processed in order by different threads.
|
|
this->SpanSpace = new
|
|
vtkInternalSpanSpace(this->Resolution,range[0],range[1],numCells);
|
|
|
|
// Threaded processing of cells to produce span space
|
|
vtkInternalSpanSpace::
|
|
MapToSpanSpace map(this->SpanSpace,this->DataSet,this->Scalars);
|
|
vtkSMPTools::For(0,numCells,map);
|
|
|
|
// Now sort and build span space
|
|
this->SpanSpace->Build();
|
|
|
|
// Update our build time
|
|
this->BuildTime.Modified();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Begin to traverse the cells based on a scalar value. Returned cells
|
|
// will have scalar values that span the scalar value specified.
|
|
void vtkSpanSpace::InitTraversal(double scalarValue)
|
|
{
|
|
this->BuildTree();
|
|
this->ScalarValue = scalarValue;
|
|
|
|
// Find the rectangle in span space that spans the isovalue
|
|
this->SpanSpace->GetSpanRectangle(scalarValue, this->RMin, this->RMax);
|
|
|
|
// Initiate the serial looping over all span rows
|
|
this->CurrentRow = this->RMin[1];
|
|
this->CurrentSpan = this->SpanSpace->
|
|
GetCellsInSpan(this->CurrentRow, this->RMin, this->RMax,
|
|
this->CurrentNumCells);
|
|
this->CurrentIdx = 0; //beginning of current span row
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Return the next cell that may contain scalar value specified to
|
|
// initialize traversal. The value NULL is returned if the list is
|
|
// exhausted. Make sure that InitTraversal() has been invoked first or
|
|
// you'll get erratic behavior. This is serial traversal.
|
|
vtkCell *vtkSpanSpace::GetNextCell(vtkIdType& cellId, vtkIdList* &cellPts,
|
|
vtkDataArray *cellScalars)
|
|
{
|
|
// Where are we in the current span space row? If at the end, need to get the
|
|
// next row (or return if the last row)
|
|
while ( this->CurrentIdx >= this->CurrentNumCells )
|
|
{
|
|
this->CurrentRow++;
|
|
if (this->CurrentRow >= this->RMax[1])
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
this->CurrentSpan = this->SpanSpace->
|
|
GetCellsInSpan(this->CurrentRow, this->RMin, this->RMax,
|
|
this->CurrentNumCells);
|
|
this->CurrentIdx = 0; //beginning of row
|
|
}
|
|
}
|
|
|
|
// If here then get the next cell
|
|
vtkIdType numScalars;
|
|
vtkCell *cell;
|
|
cellId = this->CurrentSpan[this->CurrentIdx++];
|
|
cell = this->DataSet->GetCell(cellId);
|
|
cellPts = cell->GetPointIds();
|
|
numScalars = cellPts->GetNumberOfIds();
|
|
cellScalars->SetNumberOfTuples(numScalars);
|
|
this->Scalars->GetTuples(cellPts, cellScalars);
|
|
|
|
return cell;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Return the number of cell batches. Here we are going to consider a batch the
|
|
// number of span space buckets included in the current span rectange. Note that
|
|
// InitTraversal() must have been called, which populates the span rectangle.
|
|
vtkIdType vtkSpanSpace::GetNumberOfCellBatches()
|
|
{
|
|
// Basicall just perform a serial traversal and populate candidate list
|
|
this->SpanSpace->NumCandidates = 0;
|
|
|
|
// loop over all rows in span rectangle
|
|
vtkIdType row, *span, idx, numCells;
|
|
for (row=this->RMin[1]; row < this->RMax[1]; ++row)
|
|
{
|
|
span = this->SpanSpace->
|
|
GetCellsInSpan(row, this->RMin, this->RMax, numCells);
|
|
for (idx=0; idx < numCells; ++idx)
|
|
{
|
|
this->SpanSpace->
|
|
CandidateCells[this->SpanSpace->NumCandidates++] = span[idx];
|
|
}
|
|
}//for all rows in span rectangle
|
|
|
|
// Watch for boundary conditions. Return 10 cells to a batch.
|
|
if ( this->SpanSpace->NumCandidates < 1 )
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return ( ((this->SpanSpace->NumCandidates-1)/this->BatchSize) + 1);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Return the next list of cells to process.
|
|
const vtkIdType* vtkSpanSpace::
|
|
GetCellBatch(vtkIdType batchNum, vtkIdType& numCells)
|
|
{
|
|
// Make sure that everything is hunky dory
|
|
vtkIdType pos = batchNum * this->BatchSize;
|
|
if ( this->SpanSpace->NumCells < 1 || ! this->SpanSpace->CandidateCells ||
|
|
pos > this->SpanSpace->NumCandidates )
|
|
{
|
|
numCells = 0;
|
|
return NULL;
|
|
}
|
|
|
|
if ( (this->SpanSpace->NumCandidates - pos) >= this->BatchSize )
|
|
{
|
|
numCells = this->BatchSize;
|
|
}
|
|
else
|
|
{
|
|
numCells = this->SpanSpace->NumCandidates % this->BatchSize;
|
|
}
|
|
|
|
return this->SpanSpace->CandidateCells + pos;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkSpanSpace::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os,indent);
|
|
|
|
os << indent << "Resolution: " << this->Resolution << "\n" ;
|
|
}
|