/*========================================================================= 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 //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( static_cast(this->Dim) * (sMin - this->SMin) / this->Range); vtkIdType j = static_cast( static_cast(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 extracting 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( static_cast(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 CellPts; vtkSMPThreadLocalObject 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 = nullptr; 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 = nullptr; // The candidate cell list can be allocated if ( this->CandidateCells ) { delete [] this->CandidateCells; this->CandidateCells = nullptr; } this->CandidateCells = new vtkIdType [this->NumCells]; } //---The VTK Class proper------------------------------------------------------ vtkStandardNewMacro(vtkSpanSpace); //----------------------------------------------------------------------------- // Instantiate empty span space object. vtkSpanSpace::vtkSpanSpace() { this->SpanSpace = nullptr; this->RMin[0] = this->RMin[1] = 0; this->RMax[0] = this->RMax[1] = 0; this->BatchSize = 10; this->Resolution = 100; } //----------------------------------------------------------------------------- vtkSpanSpace::~vtkSpanSpace() { this->Initialize(); } //----------------------------------------------------------------------------- // Frees memory and resets object as appropriate. void vtkSpanSpace::Initialize() { if (this->SpanSpace) { delete this->SpanSpace; this->SpanSpace = nullptr; } } //----------------------------------------------------------------------------- // 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 nullptr 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 nullptr; } 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 nullptr; } 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" ; }