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.
924 lines
30 KiB
C++
924 lines
30 KiB
C++
/*=========================================================================
|
|
|
|
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 <algorithm>
|
|
#include <cmath>
|
|
|
|
vtkStandardNewMacro(vtkCheckerboardSplatter);
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Algorithm and integration with vtkSMPTools
|
|
template <typename TPoints, typename TScalars>
|
|
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 <typename TTPoints> 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<unsigned char>(
|
|
(x[0] - this->Algo->CBOrigin[0]) / this->Algo->CBSpacing[0]);
|
|
j = static_cast<unsigned char>(
|
|
(x[1] - this->Algo->CBOrigin[1]) / this->Algo->CBSpacing[1]);
|
|
k = static_cast<unsigned char>(
|
|
(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 <typename TTPoints> 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; i<npts; ++i)
|
|
{
|
|
this->Algo->SplatPoint(this->Algo->SPts[pts+i].PtId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Do the actual work of splatting the point
|
|
void SplatPoint(vtkIdType ptId);
|
|
template <typename TTPoints> 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<double>(p[0]);
|
|
this->PD[1] = static_cast<double>(p[1]);
|
|
this->PD[2] = static_cast<double>(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<double>
|
|
(this->ExponentFactor*(dist2)/(this->R2)));
|
|
|
|
TScalars Tv = static_cast<TScalars>(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 <typename TPoints, typename TScalars>
|
|
void vtkCheckerboardSplatterAlgorithm<TPoints,TScalars>::
|
|
SplatPoint(vtkIdType ptId)
|
|
{
|
|
// Configure the parallel splat
|
|
Splat<TPoints> 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<vtkIdType>(floor(static_cast<double>(loc[i]) -
|
|
this->Footprint));
|
|
max[i] = static_cast<vtkIdType>(ceil(static_cast<double>(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 <typename TPoints, typename TScalars>
|
|
void vtkCheckerboardSplatterAlgorithm<TPoints,TScalars>::
|
|
Cap(TScalars *s, TScalars capValue)
|
|
{
|
|
vtkIdType i, j, k, jOffset, kOffset;
|
|
|
|
// i-j planes
|
|
//k = 0;
|
|
for (j=0; j<this->Dims[1]; j++)
|
|
{
|
|
jOffset = j*this->Dims[0];
|
|
for (i=0; i<this->Dims[0]; i++)
|
|
{
|
|
s[i+jOffset] = capValue;
|
|
}
|
|
}
|
|
kOffset = (this->Dims[2] - 1) * this->SliceSize;
|
|
for (j=0; j<this->Dims[1]; j++)
|
|
{
|
|
jOffset = j*this->Dims[0];
|
|
for (i=0; i<this->Dims[0]; i++)
|
|
{
|
|
s[i+jOffset+kOffset] = capValue;
|
|
}
|
|
}
|
|
|
|
// j-k planes
|
|
//i = 0;
|
|
for (k=0; k<this->Dims[2]; k++)
|
|
{
|
|
kOffset = k*this->SliceSize;
|
|
for (j=0; j<this->Dims[1]; j++)
|
|
{
|
|
s[j*this->Dims[0]+kOffset] = capValue;
|
|
}
|
|
}
|
|
i = this->Dims[0] - 1;
|
|
for (k=0; k<this->Dims[2]; k++)
|
|
{
|
|
kOffset = k*this->SliceSize;
|
|
for (j=0; j<this->Dims[1]; j++)
|
|
{
|
|
s[i+j*this->Dims[0]+kOffset] = capValue;
|
|
}
|
|
}
|
|
|
|
// i-k planes
|
|
//j = 0;
|
|
for (k=0; k<this->Dims[2]; k++)
|
|
{
|
|
kOffset = k*this->SliceSize;
|
|
for (i=0; i<this->Dims[0]; i++)
|
|
{
|
|
s[i+kOffset] = capValue;
|
|
}
|
|
}
|
|
jOffset = (this->Dims[1] - 1) * this->Dims[0];
|
|
for (k=0; k<this->Dims[2]; k++)
|
|
{
|
|
kOffset = k*this->SliceSize;
|
|
for (i=0; i<this->Dims[0]; i++)
|
|
{
|
|
s[i+jOffset+kOffset] = capValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// The algorithm driver method.
|
|
template <typename TPoints, typename TScalars>
|
|
void vtkCheckerboardSplatterAlgorithm<TPoints,TScalars>::
|
|
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<TPoints,TScalars> 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<TScalars>(self->GetNullValue());
|
|
algo.ParallelSplatCrossover = self->GetParallelSplatCrossover();
|
|
|
|
// Set up function pointers to sample functions
|
|
if ( self->GetNormalWarping() && (algo.InNormals != NULL) )
|
|
{
|
|
algo.Sample = &vtkCheckerboardSplatterAlgorithm::EccentricGaussian;
|
|
}
|
|
else
|
|
{
|
|
algo.Sample = &vtkCheckerboardSplatterAlgorithm::Gaussian;
|
|
}
|
|
|
|
if ( self->GetScalarWarping() && algo.InScalars != NULL )
|
|
{
|
|
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<vtkIdType>( ceil(
|
|
static_cast<double>(algo.Dims[i]-1) / static_cast<double>(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
|
|
// determing 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<TPoints> 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<TPoints> 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<TScalars>(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<double,float>::
|
|
SplatPoints(this, npts, static_cast<double*>(ptsPtr), inScalars, inNormals,
|
|
output, extent, static_cast<float*>(scalarPtr));
|
|
break;
|
|
case VTK_FLOAT:
|
|
vtkCheckerboardSplatterAlgorithm<float,float>::
|
|
SplatPoints(this, npts, static_cast<float*>(ptsPtr), inScalars, inNormals,
|
|
output, extent, static_cast<float*>(scalarPtr));
|
|
break;
|
|
default:
|
|
vtkWarningMacro(<<"Undefined input point type");
|
|
}
|
|
}
|
|
else if ( this->OutputScalarType == VTK_DOUBLE )
|
|
{
|
|
switch (points->GetDataType())
|
|
{
|
|
case VTK_DOUBLE:
|
|
vtkCheckerboardSplatterAlgorithm<double,double>::
|
|
SplatPoints(this, npts, static_cast<double*>(ptsPtr), inScalars, inNormals,
|
|
output, extent, static_cast<double*>(scalarPtr));
|
|
break;
|
|
case VTK_FLOAT:
|
|
vtkCheckerboardSplatterAlgorithm<float,double>::
|
|
SplatPoints(this, npts, static_cast<float*>(ptsPtr), inScalars, inNormals,
|
|
output, extent, static_cast<double*>(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)
|
|
{
|
|
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";
|
|
}
|