/*========================================================================= Program: Visualization Toolkit Module: vtkCheckerboardSplatter.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 "vtkCheckerboardSplatter.h" #include "vtkCompositeDataIterator.h" #include "vtkCompositeDataSet.h" #include "vtkDoubleArray.h" #include "vtkImageData.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkMultiBlockDataSet.h" #include "vtkNew.h" #include "vtkObjectFactory.h" #include "vtkStreamingDemandDrivenPipeline.h" #include "vtkPointData.h" #include "vtkPointSet.h" #include "vtkSMPTools.h" #include #include vtkStandardNewMacro(vtkCheckerboardSplatter); //---------------------------------------------------------------------------- // Algorithm and integration with vtkSMPTools template class vtkCheckerboardSplatterAlgorithm { public: // Pointers to functions which are selected based on user input double (vtkCheckerboardSplatterAlgorithm::*Sample)( vtkIdType ptId, double x[3], double p[3]); double (vtkCheckerboardSplatterAlgorithm::*SampleFactor)(vtkIdType ptId); // Information from the VTK class vtkCheckerboardSplatter *Splatter; vtkIdType NPts; TPoints *Pts; TScalars *Scalars; vtkDataArray *InScalars, *InNormals; vtkIdType Dims[3], SliceSize; double *Origin, *Spacing; double R2, E2; //radius squared, eccentricity squared double ExponentFactor; //scale the gaussian exponent double ScaleFactor; //scale the gaussian int AccumulationMode; // how to combine scalar values TScalars InitialValue; // initial value of scalars before splatting int ParallelSplatCrossover; //at which point to parallelize splatting // Points are grouped according to their checkerboard square address struct SortedPoints { vtkIdType PtId; vtkIdType Addr; //Operator< used to support sorting operation. bool operator<(const SortedPoints& spts) const {return Addr < spts.Addr;} }; SortedPoints *SPts; //sorted points array // Checkerboard squares refer to the points inside of them. struct Squares { vtkIdType NPts; //the number of points in this square vtkIdType Pts; //the list of points in this square Squares():NPts(0),Pts(0) {} }; Squares *CBoard; // the actual 3D checkerboard // Checkerboard information: number and spacing of squares in // each direction. vtkIdType CBWidth, CBDims[3]; //checkerboard information double CBOrigin[3], CBSpacing[3]; int Footprint; //the footprint radius of a splat measured in voxels unsigned char MaxDim; //max number of squares in any of the i-j-k dirs vtkIdType BDims[3], BSliceSize; //8-way checkerboard blocks/groups // The addresses of the eight colors / groups. The ninth value is // added to simplify looping later on. Note that this can be thought of // as eight separate volumes, one for each checkerboard square color. vtkIdType NSquares; //number of squares of a particular color, 8 colors total vtkIdType Offsets[9]; // Construct the algorithm; initialize key data members. vtkCheckerboardSplatterAlgorithm() {} // Integration between VTK and templated algorithm static void SplatPoints(vtkCheckerboardSplatter *self, vtkIdType npts, TPoints *points, vtkDataArray *inScalars, vtkDataArray *inNormals, vtkImageData *output, int extent[6], TScalars *scalars); // Various sampling functions centered around point p. These returns a // distance value (depending on eccentricity). Eccentric splats are available // when normals are available, and NormalWarping is enabled. double Gaussian (vtkIdType, double x[3], double p[3]) { return ((x[0]-p[0])*(x[0]-p[0]) + (x[1]-p[1])*(x[1]-p[1]) + (x[2]-p[2])*(x[2]-p[2]) ); } double EccentricGaussian (vtkIdType ptId, double x[3], double p[3]) { double v[3], r2, z2, rxy2, mag, n[3]; this->InNormals->GetTuple(ptId,n); v[0] = x[0] - p[0]; v[1] = x[1] - p[1]; v[2] = x[2] - p[2]; r2 = v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; if ( (mag=n[0]*n[0] + n[1]*n[1] + n[2]*n[2]) != 1.0 ) { mag = (mag == 0.0 ? 1.0 : sqrt(mag)); } z2 = (v[0]*n[0] + v[1]*n[1] + v[2]*n[2])/mag; z2 = z2*z2; rxy2 = r2 - z2; return (rxy2/this->E2 + z2); } // Different ways of affecting scale from scalar value. The scalar value is // used when scalars are available and ScalarWarping is enabled. double ScalarSampling(vtkIdType ptId) { return this->ScaleFactor * this->InScalars->GetComponent(ptId,0); } double PositionSampling(vtkIdType) {return this->ScaleFactor;} // Assign membership of points to checkerboard squares template class AssignSquares { public: vtkCheckerboardSplatterAlgorithm *Algo; AssignSquares(vtkCheckerboardSplatterAlgorithm *algo) {this->Algo = algo;} void operator()(vtkIdType ptId, vtkIdType end) { vtkIdType addr; unsigned char i, j, k, oct; TPoints *x; for ( ; ptId < end; ++ptId ) { // First, map the point prior to sorting this->Algo->SPts[ptId].PtId = ptId; // Determine the square that the point is in x = this->Algo->Pts + 3*ptId; i = static_cast( (x[0] - this->Algo->CBOrigin[0]) / this->Algo->CBSpacing[0]); j = static_cast( (x[1] - this->Algo->CBOrigin[1]) / this->Algo->CBSpacing[1]); k = static_cast( (x[2] - this->Algo->CBOrigin[2]) / this->Algo->CBSpacing[2]); oct = (i%2) | ((j%2)<<1) | ((k%2)<<2); // Compute the address based on the particular color / block addr = this->Algo->Offsets[oct] + (i/2) + (j/2)*this->Algo->BDims[0] + (k/2)*this->Algo->BSliceSize; this->Algo->SPts[ptId].Addr = addr; }//over all points in given range } }; // Process all points in given range of checkerboard squares template class SplatSquares { public: vtkCheckerboardSplatterAlgorithm *Algo; SplatSquares(vtkCheckerboardSplatterAlgorithm *algo) {this->Algo = algo;} void operator()(vtkIdType sqNum, vtkIdType end) { vtkIdType npts, pts; for ( ; sqNum < end; ++sqNum ) { if (this->Algo->CBoard[sqNum].NPts > 0) { npts = this->Algo->CBoard[sqNum].NPts; pts = this->Algo->CBoard[sqNum].Pts; for (int i=0; iAlgo->SplatPoint(this->Algo->SPts[pts+i].PtId); } } } } }; // Do the actual work of splatting the point void SplatPoint(vtkIdType ptId); template class Splat { public: vtkCheckerboardSplatterAlgorithm *Algo; vtkIdType XMin, XMax, YMin, YMax, PtId; double PD[3]; Splat(vtkCheckerboardSplatterAlgorithm *algo) {this->Algo = algo;} void SetSliceBounds(vtkIdType min[3], vtkIdType max[3]) { this->XMin = min[0]; this->XMax = max[0]; this->YMin = min[1]; this->YMax = max[1]; } void SetSplatPoint(vtkIdType ptId, TTPoints p[3]) { this->PtId = ptId; this->PD[0] = static_cast(p[0]); this->PD[1] = static_cast(p[1]); this->PD[2] = static_cast(p[2]); } void operator()(vtkIdType slice, vtkIdType end) { vtkIdType i, j, jOffset, kOffset, idx; double x[3]; for ( ; slice < end; ++slice ) { // Loop over all sample points in volume within footprint and // evaluate the splat x[2] = this->Algo->Origin[2] + this->Algo->Spacing[2]*slice; kOffset = slice*this->Algo->SliceSize; for (j=YMin; j<=YMax; ++j) { x[1] = this->Algo->Origin[1] + this->Algo->Spacing[1]*j; jOffset = j*this->Algo->Dims[0]; for (i=XMin; i<=XMax; ++i) { x[0] = this->Algo->Origin[0] + this->Algo->Spacing[0]*i; idx = i + jOffset + kOffset; this->Algo->SetScalar(this->PtId, this->PD, x, this->Algo->Scalars+idx); }//i }//j }//k within splat footprint } }; // Accumlate scalar values as appropriate void SetScalar(vtkIdType ptId, double pd[3], double x[3], TScalars *sPtr) { double dist2 = (this->*Sample)(ptId,x,pd); double v = (this->*SampleFactor)(ptId) * exp(static_cast (this->ExponentFactor*(dist2)/(this->R2))); TScalars Tv = static_cast(v); switch (this->AccumulationMode) { case VTK_ACCUMULATION_MODE_MIN: if ( Tv < *sPtr ) { *sPtr = Tv; } break; case VTK_ACCUMULATION_MODE_MAX: if ( Tv > *sPtr ) { *sPtr = Tv; } break; case VTK_ACCUMULATION_MODE_SUM: *sPtr += Tv; break; } } // Cap the boundary if requested. void Cap(TScalars *s, TScalars capValue); }; //---------------------------------------------------------------------------- // This is where the work is actually done and the points are splatted. Note // that splatting is only parallelized when the splat footprint is large // enough (to avoid multithreading overhead). template void vtkCheckerboardSplatterAlgorithm:: SplatPoint(vtkIdType ptId) { // Configure the parallel splat Splat splat(this); TPoints *p = this->Pts + 3*ptId; splat.SetSplatPoint(ptId,p); //casts the point into double precision // Determine which voxel the point lies in vtkIdType loc[3], min[3], max[3]; loc[0] = (p[0]-this->Origin[0]) / this->Spacing[0]; loc[1] = (p[1]-this->Origin[1]) / this->Spacing[1]; loc[2] = (p[2]-this->Origin[2]) / this->Spacing[2]; // Determine the splat footprint vtkIdType i; for (i=0; i<3; i++) { min[i] = static_cast(floor(static_cast(loc[i]) - this->Footprint)); max[i] = static_cast(ceil(static_cast(loc[i]) + this->Footprint)); if ( min[i] < 0 ) { min[i] = 0; } if ( max[i] >= this->Dims[i] ) { max[i] = this->Dims[i] - 1; } } // The parallel splat across the splat footprint. If the footprint is too // small then use serial processing to avoid thread inefficiency. Note that // empirically the crossover point seems to be a footprint=1 (e.g., 3x3x3 // splat footprint and smaller is processed in serial). splat.SetSliceBounds(min,max); if ( this->Footprint < this->ParallelSplatCrossover ) { splat(min[2],max[2]+1); } else {//parallelize splat vtkSMPTools::For(min[2],max[2]+1, splat); } } //---------------------------------------------------------------------------- // Cap the boundaries with a specific value (the capValue). template void vtkCheckerboardSplatterAlgorithm:: Cap(TScalars *s, TScalars capValue) { vtkIdType i, j, k, jOffset, kOffset; // i-j planes //k = 0; for (j=0; jDims[1]; j++) { jOffset = j*this->Dims[0]; for (i=0; iDims[0]; i++) { s[i+jOffset] = capValue; } } kOffset = (this->Dims[2] - 1) * this->SliceSize; for (j=0; jDims[1]; j++) { jOffset = j*this->Dims[0]; for (i=0; iDims[0]; i++) { s[i+jOffset+kOffset] = capValue; } } // j-k planes //i = 0; for (k=0; kDims[2]; k++) { kOffset = k*this->SliceSize; for (j=0; jDims[1]; j++) { s[j*this->Dims[0]+kOffset] = capValue; } } i = this->Dims[0] - 1; for (k=0; kDims[2]; k++) { kOffset = k*this->SliceSize; for (j=0; jDims[1]; j++) { s[i+j*this->Dims[0]+kOffset] = capValue; } } // i-k planes //j = 0; for (k=0; kDims[2]; k++) { kOffset = k*this->SliceSize; for (i=0; iDims[0]; i++) { s[i+kOffset] = capValue; } } jOffset = (this->Dims[1] - 1) * this->Dims[0]; for (k=0; kDims[2]; k++) { kOffset = k*this->SliceSize; for (i=0; iDims[0]; i++) { s[i+jOffset+kOffset] = capValue; } } } //---------------------------------------------------------------------------- // The algorithm driver method. template void vtkCheckerboardSplatterAlgorithm:: SplatPoints(vtkCheckerboardSplatter *self, vtkIdType npts, TPoints *pts, vtkDataArray *inScalars, vtkDataArray *inNormals, vtkImageData *output, int extent[6], TScalars *scalars) { int i; // Populate the algorithm with relevant information from the VTK class vtkCheckerboardSplatterAlgorithm algo; algo.Splatter = self; algo.NPts = npts; algo.Pts = pts; algo.Scalars = scalars; algo.InScalars = inScalars; algo.InNormals = inNormals; algo.Origin = output->GetOrigin(); algo.Spacing = output->GetSpacing(); for (i=0; i<3; ++i) //dimensions expressed in voxel cells { algo.Dims[i] = extent[2*i+1] - extent[2*i] + 1; } algo.SliceSize = algo.Dims[0]*algo.Dims[1]; if ( self->GetRadius() <= 0.0 ) { algo.R2 = algo.Spacing[0]*algo.Spacing[0] + algo.Spacing[1]*algo.Spacing[1] + algo.Spacing[2]*algo.Spacing[2]; } else { algo.R2 = self->GetRadius()*self->GetRadius(); } algo.E2 = self->GetEccentricity()*self->GetEccentricity(); algo.ScaleFactor = self->GetScaleFactor(); algo.ExponentFactor = self->GetExponentFactor(); algo.AccumulationMode = self->GetAccumulationMode(); algo.InitialValue = static_cast(self->GetNullValue()); algo.ParallelSplatCrossover = self->GetParallelSplatCrossover(); // Set up function pointers to sample functions if ( self->GetNormalWarping() && (algo.InNormals != nullptr) ) { algo.Sample = &vtkCheckerboardSplatterAlgorithm::EccentricGaussian; } else { algo.Sample = &vtkCheckerboardSplatterAlgorithm::Gaussian; } if ( self->GetScalarWarping() && algo.InScalars != nullptr ) { algo.SampleFactor = &vtkCheckerboardSplatterAlgorithm::ScalarSampling; } else { algo.SampleFactor = &vtkCheckerboardSplatterAlgorithm::PositionSampling; } // Okay now setup the checkerboard. It overlays the volume (note that some // of the checkerboard squares will be empty, and/or partially cover the // volume). Rectangular groups of 8 checkerboard squares are arranged into // blocks (like an octree) corresponding to the eight square colors. These // eight colors (or groups) are processed in parallel, Note that the splat // footprint is carefully designed to avoid write contention during // parallel splatting, thus the width of each checkerboard square is a // function of the splat footprint. algo.MaxDim = self->GetMaximumDimension(); algo.Footprint = self->GetFootprint(); algo.CBWidth = 2*algo.Footprint + 1; // Set up dimensions for the checkerboard and the grouping block // structure. Ensure that the checkerboard dimensions are evenly divisible // by two. for (i=0; i<3; ++i) { algo.CBDims[i] = static_cast( ceil( static_cast(algo.Dims[i]-1) / static_cast(algo.CBWidth) )); algo.CBDims[i] = (algo.CBDims[i] > algo.MaxDim ? algo.MaxDim : algo.CBDims[i]); algo.CBDims[i] = ((algo.CBDims[i] % 2) ? algo.CBDims[i]+1 : algo.CBDims[i]); algo.CBOrigin[i] = algo.Origin[i]; algo.CBSpacing[i] = algo.CBWidth * algo.Spacing[i]; algo.BDims[i] = algo.CBDims[i] / 2; } algo.BSliceSize = algo.BDims[0] * algo.BDims[1]; // The NSquares is the number of squares of a given color (there are eight // total colors / groups). Because the checkerboard dimensions are a // multiple of two, the total number of all colors of squares is divisible // by 8. Also set up offsets for each color / group which is used in // determining addresses and later processing. algo.NSquares = algo.BDims[0]*algo.BDims[1]*algo.BDims[2]; for (i=0; i<9; ++i) { algo.Offsets[i] = i*algo.NSquares; } // The checkerboard tracks (npts,pts) for each square, where npts is the // number of points in each square, and pts is a location into the sorted // points array. algo.CBoard = new Squares [algo.NSquares*8]; // The sorted points array contains the offset into the original points array // and a checkerboard address. algo.SPts = new SortedPoints [algo.NPts]; // Loop over all points, computing address into checkerboard. This consists // of (octNum,i,j,k) where the checkerboard square number is a value // (0<=octNum<8) indicating which of the eight octants/squares the point // belongs to (i.e., each point is associated with one of eight spatially // distinct groups). The (i,j,k) indicate which checkerboard square the // point is contained. AssignSquares assign(&algo); vtkSMPTools::For(0,npts, assign); // Now sort points based on checkerboard address. This will separate // points into squares which will be processed in parallel. vtkSMPTools::Sort(algo.SPts, algo.SPts+npts); // Okay now run through the sorted points and build pointers to // each checkerboard square (and associated points, if any). This could be // parallelized but it may not be worth it. vtkIdType currentAddr, pStart, pEnd=0; while ( pEnd < npts ) { currentAddr = algo.SPts[pEnd].Addr; pStart = pEnd; while ( pEnd < npts && currentAddr == algo.SPts[pEnd].Addr ) { pEnd++; } algo.CBoard[currentAddr].NPts = pEnd - pStart; algo.CBoard[currentAddr].Pts = pStart; } // Finally we can process the 8-way checkerboard, where we process in // parallel all squares in a particular color/group. Need to initialize the // output with the fill operation. std::fill_n(scalars, algo.Dims[0]*algo.Dims[1]*algo.Dims[2], algo.InitialValue); SplatSquares splatSquares(&algo); for (i=0; i < 8; ++i) //loop over all eight checkerboard colors { vtkSMPTools::For(algo.Offsets[i], algo.Offsets[i+1], splatSquares); } // Cap the boundary if requested if ( self->GetCapping() ) { algo.Cap(algo.Scalars,static_cast(self->GetCapValue())); } // Free up memory delete [] algo.CBoard; delete [] algo.SPts; } //---------------------------------------------------------------------------- // Create the VTK class proper. Construct object with dimensions=(50,50,50); // automatic computation of bounds; a splat radius of 0.1; an exponent factor // of -5; and normal and scalar warping turned on. vtkCheckerboardSplatter::vtkCheckerboardSplatter() { this->OutputScalarType = VTK_FLOAT; this->SampleDimensions[0] = 50; this->SampleDimensions[1] = 50; this->SampleDimensions[2] = 50; this->Footprint = 2; this->Radius = 0.0; //automatically compute this->ExponentFactor = -5.0; this->ModelBounds[0] = 0.0; this->ModelBounds[1] = 0.0; this->ModelBounds[2] = 0.0; this->ModelBounds[3] = 0.0; this->ModelBounds[4] = 0.0; this->ModelBounds[5] = 0.0; this->NormalWarping = 1; this->Eccentricity = 2.5; this->ScalarWarping = 1; this->ScaleFactor = 1.0; this->Capping = 1; this->CapValue = 0.0; this->AccumulationMode = VTK_ACCUMULATION_MODE_MAX; this->NullValue = 0.0; this->MaximumDimension = 50; this->ParallelSplatCrossover = 2; // Splat point scalars by default: this->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, vtkDataSetAttributes::SCALARS); } //---------------------------------------------------------------------------- int vtkCheckerboardSplatter::FillInputPortInformation( int vtkNotUsed(port), vtkInformation* info) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPointSet"); return 1; } //---------------------------------------------------------------------------- int vtkCheckerboardSplatter::RequestInformation ( vtkInformation * vtkNotUsed(request), vtkInformationVector ** vtkNotUsed( inputVector ), vtkInformationVector *outputVector) { // get the info objects vtkInformation* outInfo = outputVector->GetInformationObject(0); // use model bounds if set this->Origin[0] = 0; this->Origin[1] = 0; this->Origin[2] = 0; if ( this->ModelBounds[0] < this->ModelBounds[1] && this->ModelBounds[2] < this->ModelBounds[3] && this->ModelBounds[4] < this->ModelBounds[5] ) { this->Origin[0] = this->ModelBounds[0]; this->Origin[1] = this->ModelBounds[2]; this->Origin[2] = this->ModelBounds[4]; } outInfo->Set(vtkDataObject::ORIGIN(), this->Origin, 3); int i; for (i=0; i<3; i++) { this->Spacing[i] = (this->ModelBounds[2*i+1] - this->ModelBounds[2*i]) / (this->SampleDimensions[i] - 1); if ( this->Spacing[i] <= 0.0 ) { this->Spacing[i] = 1.0; } } outInfo->Set(vtkDataObject::SPACING(),this->Spacing,3); outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), 0, this->SampleDimensions[0] - 1, 0, this->SampleDimensions[1] - 1, 0, this->SampleDimensions[2] - 1); vtkDataObject:: SetPointDataActiveScalarInfo(outInfo, this->OutputScalarType, 1); return 1; } //---------------------------------------------------------------------------- int vtkCheckerboardSplatter::RequestData( vtkInformation* vtkNotUsed( request ), vtkInformationVector** inputVector, vtkInformationVector* outputVector) { // get the data object vtkInformation *outInfo = outputVector->GetInformationObject(0); vtkImageData *output = vtkImageData::GetData(outputVector,0); vtkPointSet *input = vtkPointSet::GetData(inputVector[0]); vtkPoints *points = input->GetPoints(); output->SetExtent( outInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT())); output->AllocateScalars(outInfo); int* extent = this->GetExecutive()->GetOutputInformation(0)->Get( vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()); vtkDataArray *outScalars = output->GetPointData()->GetScalars(); // Configure the output output->SetDimensions(this->GetSampleDimensions()); this->ComputeModelBounds(input, output, outInfo); // Make sure points are available vtkIdType npts = input->GetNumberOfPoints(); if ( npts == 0 ) { vtkDebugMacro(<<"No points to splat!"); vtkWarningMacro(<<"No POINTS to splat!!"); return 1; } else { vtkDebugMacro(<< "Splatting data, total of: " << npts << " points."); } // Grab relevant attribute data vtkDataArray *inScalars = this->GetInputArrayToProcess(0, inputVector); vtkDataArray *inNormals = input->GetPointData()->GetNormals(); // Okay actually execute the algorithm. Manage all the crazy template // stuff. Note that the output types are currently limitied to // (float,double) to manage precision. The point type is also limited // to real types but could be easily extended to other types. void *ptsPtr = points->GetVoidPointer(0); void *scalarPtr = output->GetArrayPointerForExtent(outScalars, extent); if ( this->OutputScalarType == VTK_FLOAT ) { switch (points->GetDataType()) { case VTK_DOUBLE: vtkCheckerboardSplatterAlgorithm:: SplatPoints(this, npts, static_cast(ptsPtr), inScalars, inNormals, output, extent, static_cast(scalarPtr)); break; case VTK_FLOAT: vtkCheckerboardSplatterAlgorithm:: SplatPoints(this, npts, static_cast(ptsPtr), inScalars, inNormals, output, extent, static_cast(scalarPtr)); break; default: vtkWarningMacro(<<"Undefined input point type"); } } else if ( this->OutputScalarType == VTK_DOUBLE ) { switch (points->GetDataType()) { case VTK_DOUBLE: vtkCheckerboardSplatterAlgorithm:: SplatPoints(this, npts, static_cast(ptsPtr), inScalars, inNormals, output, extent, static_cast(scalarPtr)); break; case VTK_FLOAT: vtkCheckerboardSplatterAlgorithm:: SplatPoints(this, npts, static_cast(ptsPtr), inScalars, inNormals, output, extent, static_cast(scalarPtr)); break; default: vtkWarningMacro(<<"Undefined input point type"); } } else //warning output type not supported { vtkWarningMacro(<<"Only FLOAT or DOUBLE output scalar type is supported"); } return 1; } //---------------------------------------------------------------------------- // Compute the size of the sample bounding box automatically from the // input data. void vtkCheckerboardSplatter::ComputeModelBounds(vtkDataSet *input, vtkImageData *output, vtkInformation *outInfo) { const double *bounds; int i, adjustBounds=0; // compute model bounds if not set previously if ( this->ModelBounds[0] >= this->ModelBounds[1] || this->ModelBounds[2] >= this->ModelBounds[3] || this->ModelBounds[4] >= this->ModelBounds[5] ) { adjustBounds = 1; bounds = input->GetBounds(); } else { bounds = this->ModelBounds; } // Adjust bounds so model fits strictly inside (only if not set previously) if ( adjustBounds ) { for (i=0; i<3; i++) { this->ModelBounds[2*i] = bounds[2*i]; this->ModelBounds[2*i+1] = bounds[2*i+1]; } } // Set volume origin and data spacing outInfo->Set(vtkDataObject::ORIGIN(), this->ModelBounds[0],this->ModelBounds[2], this->ModelBounds[4]); memcpy(this->Origin,outInfo->Get(vtkDataObject::ORIGIN()), sizeof(double)*3); output->SetOrigin(this->Origin); for (i=0; i<3; i++) { this->Spacing[i] = (this->ModelBounds[2*i+1] - this->ModelBounds[2*i]) / (this->SampleDimensions[i] - 1); if ( this->Spacing[i] <= 0.0 ) { this->Spacing[i] = 1.0; } } outInfo->Set(vtkDataObject::SPACING(),this->Spacing,3); output->SetSpacing(this->Spacing); } //---------------------------------------------------------------------------- // Set the dimensions of the sampling structured point set. void vtkCheckerboardSplatter::SetSampleDimensions(int i, int j, int k) { int dim[3]; dim[0] = i; dim[1] = j; dim[2] = k; this->SetSampleDimensions(dim); } //---------------------------------------------------------------------------- void vtkCheckerboardSplatter::SetSampleDimensions(int dim[3]) { int dataDim, i; vtkDebugMacro(<< " setting SampleDimensions to (" << dim[0] << "," << dim[1] << "," << dim[2] << ")"); if (dim[0] != this->SampleDimensions[0] || dim[1] != this->SampleDimensions[1] || dim[2] != this->SampleDimensions[2] ) { if ( dim[0]<1 || dim[1]<1 || dim[2]<1 ) { vtkErrorMacro (<< "Bad Sample Dimensions, retaining previous values"); return; } for (dataDim=0, i=0; i<3 ; i++) { if (dim[i] > 1) { dataDim++; } } if ( dataDim < 3 ) { vtkErrorMacro(<<"Sample dimensions must define a volume!"); return; } for ( i=0; i<3; i++) { this->SampleDimensions[i] = dim[i]; } this->Modified(); } } //---------------------------------------------------------------------------- const char *vtkCheckerboardSplatter::GetAccumulationModeAsString() { if ( this->AccumulationMode == VTK_ACCUMULATION_MODE_MIN ) { return "Minimum"; } else if ( this->AccumulationMode == VTK_ACCUMULATION_MODE_MAX ) { return "Maximum"; } else //if ( this->AccumulationMode == VTK_ACCUMULATION_MODE_SUM ) { return "Sum"; } } //---------------------------------------------------------------------------- void vtkCheckerboardSplatter::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); os << indent << "Sample Dimensions: (" << this->SampleDimensions[0] << ", " << this->SampleDimensions[1] << ", " << this->SampleDimensions[2] << ")\n"; os << indent << "Footprint: " << this->Footprint << "\n"; os << indent << "Radius: " << this->Radius << "\n"; os << indent << "Exponent Factor: " << this->ExponentFactor << "\n"; os << indent << "ModelBounds: \n"; os << indent << " Xmin,Xmax: (" << this->ModelBounds[0] << ", " << this->ModelBounds[1] << ")\n"; os << indent << " Ymin,Ymax: (" << this->ModelBounds[2] << ", " << this->ModelBounds[3] << ")\n"; os << indent << " Zmin,Zmax: (" << this->ModelBounds[4] << ", " << this->ModelBounds[5] << ")\n"; os << indent << "Scalar Warping: " << (this->ScalarWarping ? "On\n" : "Off\n"); os << indent << "Scale Factor: " << this->ScaleFactor << "\n"; os << indent << "Normal Warping: " << (this->NormalWarping ? "On\n" : "Off\n"); os << indent << "Eccentricity: " << this->Eccentricity << "\n"; os << indent << "Capping: " << (this->Capping ? "On\n" : "Off\n"); os << indent << "Cap Value: " << this->CapValue << "\n"; os << indent << "Accumulation Mode: " << this->GetAccumulationModeAsString() << "\n"; os << indent << "Null Value: " << this->NullValue << "\n"; os << indent << "Maximum Dimension: " << this->MaximumDimension << "\n"; os << indent << "Parallel Splat Crossover: " << this->ParallelSplatCrossover << "\n"; }