/*========================================================================= Program: Visualization Toolkit Module: vtkFastSplatter.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. =========================================================================*/ /*---------------------------------------------------------------------------- Copyright (c) Sandia Corporation See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details. ----------------------------------------------------------------------------*/ #include "vtkFastSplatter.h" #include "vtkGraph.h" #include "vtkImageData.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkObjectFactory.h" #include "vtkPointSet.h" #include "vtkPoints.h" #include "vtkStreamingDemandDrivenPipeline.h" #include "vtkUnsignedIntArray.h" #include vtkStandardNewMacro(vtkFastSplatter); //------------------------------------------------------------------------------ vtkFastSplatter::vtkFastSplatter() { this->SetNumberOfInputPorts(2); this->SetNumberOfOutputPorts(1); this->ModelBounds[0] = this->ModelBounds[2] = this->ModelBounds[4] = 0; this->ModelBounds[1] = this->ModelBounds[3] = this->ModelBounds[5] = -1; this->OutputDimensions[0] = 100; this->OutputDimensions[1] = 100; this->OutputDimensions[2] = 1; this->LimitMode = NoneLimit; this->MinValue = 0.0; this->MaxValue = 1.0; this->Buckets = vtkImageData::New(); this->NumberOfPointsSplatted = 0; this->LastDataMinValue = 0.0; this->LastDataMaxValue = 1.0; } vtkFastSplatter::~vtkFastSplatter() { this->Buckets->Delete(); } void vtkFastSplatter::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "ModelBounds: " << this->ModelBounds[0] << ", " << this->ModelBounds[1] << ", " << this->ModelBounds[2] << ", " << this->ModelBounds[3] << ", " << this->ModelBounds[4] << ", " << this->ModelBounds[5] << endl; os << indent << "OutputDimensions: " << this->OutputDimensions[0] << ", " << this->OutputDimensions[1] << ", " << this->OutputDimensions[2] << endl; os << indent << "LimitMode: " << this->LimitMode << endl; os << indent << "MinValue: " << this->MinValue << endl; os << indent << "MaxValue: " << this->MaxValue << endl; os << indent << "NumberOfPointsSplatted: " << this->NumberOfPointsSplatted << endl; } //------------------------------------------------------------------------------ int vtkFastSplatter::FillInputPortInformation(int port, vtkInformation* info) { switch (port) { case 0: info->Remove(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE()); info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPointSet"); info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkGraph"); break; case 1: info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData"); break; } return 1; } //------------------------------------------------------------------------------ // For those familiar with the old pipeline, this is equivalent to the // ExecuteInformation method. int vtkFastSplatter::RequestInformation(vtkInformation* vtkNotUsed(request), vtkInformationVector** 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->OutputDimensions[0] == 1)) && ((this->ModelBounds[2] < this->ModelBounds[3]) || (this->OutputDimensions[1] == 1)) && ((this->ModelBounds[4] < this->ModelBounds[5]) || (this->OutputDimensions[2] == 1))) { 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++) { if (this->OutputDimensions[i] > 1) { this->Spacing[i] = ((this->ModelBounds[2 * i + 1] - this->ModelBounds[2 * i]) / (this->OutputDimensions[i] - 1)); } else { this->Spacing[i] = 1.0; } 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->OutputDimensions[0] - 1, 0, this->OutputDimensions[1] - 1, 0, this->OutputDimensions[2] - 1); vtkInformation* splatInfo = inputVector[1]->GetInformationObject(0); vtkImageData::SetScalarType(vtkImageData::GetScalarType(splatInfo), outInfo); // if (splatInfo->Has(vtkDataObject::SCALAR_NUMBER_OF_COMPONENTS())) // { // outInfo->Set(vtkDataObject::SCALAR_NUMBER_OF_COMPONENTS(), // splatInfo->Get(vtkDataObject::SCALAR_NUMBER_OF_COMPONENTS())); // } return 1; } //------------------------------------------------------------------------------ int vtkFastSplatter::RequestUpdateExtent(vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector, vtkInformationVector* outputVector) { // get the info objects vtkInformation* outInfo = outputVector->GetInformationObject(0); vtkInformation* inInfo = inputVector[0]->GetInformationObject(0); vtkInformation* splatInfo = inputVector[1]->GetInformationObject(0); splatInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), splatInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()), 6); int numPieces = 1; int piece = 0; int ghostLevel = 0; // Use the output piece request to break up the input. // If not specified, use defaults. if (outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES())) { numPieces = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES()); } if (outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER())) { piece = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER()); } if (outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS())) { ghostLevel = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS()); } inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES(), numPieces); inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER(), piece); inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS(), ghostLevel); vtkDataObject* data = inInfo->Get(vtkDataObject::DATA_OBJECT()); if (data->GetExtentType() == VTK_3D_EXTENT) { int* inWholeExtent = inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()); inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), inWholeExtent, 6); } return 1; } //------------------------------------------------------------------------------ template void vtkFastSplatterBucketPoints(const T* points, vtkIdType numPoints, unsigned int* buckets, const int dimensions[3], const double origin[3], const double spacing[3]) { // Clear out the buckets. std::fill_n(buckets, dimensions[0] * dimensions[1] * dimensions[2], 0); // Iterate over all the points. for (vtkIdType i = 0; i < numPoints; i++) { const T* p = points + 3 * i; // Find the bucket. vtkIdType loc[3]; loc[0] = static_cast(((p[0] - origin[0]) / spacing[0]) + 0.5); loc[1] = static_cast(((p[1] - origin[1]) / spacing[1]) + 0.5); loc[2] = static_cast(((p[2] - origin[2]) / spacing[2]) + 0.5); if ((loc[0] < 0) || (loc[0] >= dimensions[0]) || (loc[1] < 0) || (loc[1] >= dimensions[1]) || (loc[2] < 0) || (loc[2] >= dimensions[2])) { // Point outside of splatting region. continue; } vtkIdType bucketId = (loc[2] * dimensions[0] * dimensions[1] + loc[1] * dimensions[0] + loc[0]); // Increment the bucket. buckets[bucketId]++; } } //------------------------------------------------------------------------------ template void vtkFastSplatterConvolve(T* splat, const int splatDims[3], unsigned int* buckets, T* output, int* numPointsSplatted, const int imageDims[3]) { int numPoints = 0; // First, clear out the output image. std::fill_n(output, imageDims[0] * imageDims[1] * imageDims[2], static_cast(0)); int splatCenter[3]; splatCenter[0] = splatDims[0] / 2; splatCenter[1] = splatDims[1] / 2; splatCenter[2] = splatDims[2] / 2; // Iterate over all entries in buckets and splat anything that is nonzero. unsigned int* b = buckets; for (int k = 0; k < imageDims[2]; k++) { // Figure out how splat projects on image in this slab, taking into // account overlap. int splatProjMinZ = k - splatCenter[2]; int splatProjMaxZ = splatProjMinZ + splatDims[2]; if (splatProjMinZ < 0) splatProjMinZ = 0; if (splatProjMaxZ > imageDims[2]) splatProjMaxZ = imageDims[2]; for (int j = 0; j < imageDims[1]; j++) { // Figure out how splat projects on image in this slab, taking into // account overlap. int splatProjMinY = j - splatCenter[1]; int splatProjMaxY = splatProjMinY + splatDims[1]; if (splatProjMinY < 0) splatProjMinY = 0; if (splatProjMaxY > imageDims[1]) splatProjMaxY = imageDims[1]; for (int i = 0; i < imageDims[0]; i++) { // No need to splat 0. if (*b == 0) { b++; continue; } T value = static_cast(*b); numPoints += static_cast(*b); b++; // Figure out how splat projects on image in this pixel, taking into // account overlap. int splatProjMinX = i - splatCenter[0]; int splatProjMaxX = splatProjMinX + splatDims[0]; if (splatProjMinX < 0) splatProjMinX = 0; if (splatProjMaxX > imageDims[0]) splatProjMaxX = imageDims[0]; // Do the splat. for (int imageZ = splatProjMinZ; imageZ < splatProjMaxZ; imageZ++) { int imageZOffset = imageZ * imageDims[0] * imageDims[1]; int splatZ = imageZ - k + splatCenter[2]; int splatZOffset = splatZ * splatDims[0] * splatDims[1]; for (int imageY = splatProjMinY; imageY < splatProjMaxY; imageY++) { int imageYOffset = imageZOffset + imageY * imageDims[0]; int splatY = imageY - j + splatCenter[1]; int splatYOffset = splatZOffset + splatY * splatDims[0]; for (int imageX = splatProjMinX; imageX < splatProjMaxX; imageX++) { int imageOffset = imageYOffset + imageX; int splatX = imageX - i + splatCenter[0]; int splatOffset = splatYOffset + splatX; output[imageOffset] += value * splat[splatOffset]; } } } } } } *numPointsSplatted = numPoints; } //------------------------------------------------------------------------------ // For those of you familiar with the old pipeline, this is equivalent to the // Execute method. int vtkFastSplatter::RequestData(vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector, vtkInformationVector* outputVector) { this->NumberOfPointsSplatted = 0; // Get the input and output objects. vtkInformation* inInfo = inputVector[0]->GetInformationObject(0); vtkPoints* points = nullptr; if (vtkPointSet* const input = vtkPointSet::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()))) { points = input->GetPoints(); } else if (vtkGraph* const graph = vtkGraph::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()))) { points = graph->GetPoints(); } vtkInformation* splatInfo = inputVector[1]->GetInformationObject(0); vtkImageData* splatImage = vtkImageData::SafeDownCast(splatInfo->Get(vtkDataObject::DATA_OBJECT())); vtkInformation* outInfo = outputVector->GetInformationObject(0); vtkImageData* output = vtkImageData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); // Figure out the real bounds to use. const double* bounds; if (((this->ModelBounds[0] < this->ModelBounds[1]) || (this->OutputDimensions[0] == 1)) && ((this->ModelBounds[2] < this->ModelBounds[3]) || (this->OutputDimensions[1] == 1)) && ((this->ModelBounds[4] < this->ModelBounds[5]) || (this->OutputDimensions[2] == 1))) { bounds = this->ModelBounds; } else { bounds = points->GetBounds(); } // Compute origin and spacing from bounds for (int i = 0; i < 3; i++) { this->Origin[i] = bounds[2 * i]; if (this->OutputDimensions[i] > 1) { this->Spacing[i] = ((bounds[2 * i + 1] - bounds[2 * i]) / (this->OutputDimensions[i] - 1)); } else { this->Spacing[i] = 2.0 * (bounds[2 * i + 1] - bounds[2 * i]); } if (this->Spacing[i] <= 0.0) { this->Spacing[i] = 1.0; } } // Set up output. output->SetDimensions(this->OutputDimensions); outInfo->Set(vtkDataObject::ORIGIN(), this->Origin, 3); output->SetOrigin(this->Origin); outInfo->Set(vtkDataObject::SPACING(), this->Spacing, 3); output->SetSpacing(this->Spacing); output->SetExtent(0, this->OutputDimensions[0] - 1, 0, this->OutputDimensions[1] - 1, 0, this->OutputDimensions[2] - 1); output->AllocateScalars(splatImage->GetScalarType(), splatImage->GetNumberOfScalarComponents()); // Set up intermediate buckets image. this->Buckets->SetDimensions(this->OutputDimensions); this->Buckets->SetOrigin(this->Origin); this->Buckets->SetSpacing(this->Spacing); this->Buckets->SetExtent(0, this->OutputDimensions[0] - 1, 0, this->OutputDimensions[1] - 1, 0, this->OutputDimensions[2] - 1); this->Buckets->AllocateScalars(VTK_UNSIGNED_INT, 1); // Get array for buckets. unsigned int* buckets = static_cast(this->Buckets->GetScalarPointer()); // Count how many points in the input lie in each pixel of the output image. void* p = points->GetVoidPointer(0); switch (points->GetDataType()) { vtkTemplateMacro(vtkFastSplatterBucketPoints(static_cast(p), points->GetNumberOfPoints(), buckets, this->OutputDimensions, this->Origin, this->Spacing)); } // Now convolve the splat image with the bucket image. void* splat = splatImage->GetScalarPointer(); void* o = output->GetScalarPointer(); switch (output->GetScalarType()) { vtkTemplateMacro( vtkFastSplatterConvolve(static_cast(splat), splatImage->GetDimensions(), buckets, static_cast(o), &(this->NumberOfPointsSplatted), this->OutputDimensions)); } // Do any appropriate limiting. switch (this->LimitMode) { case NoneLimit: break; case ClampLimit: switch (output->GetScalarType()) { vtkTemplateMacro(vtkFastSplatterClamp(static_cast(o), output->GetNumberOfPoints() * output->GetNumberOfScalarComponents(), static_cast(this->MinValue), static_cast(this->MaxValue))); } break; case FreezeScaleLimit: switch (output->GetScalarType()) { vtkTemplateMacro( vtkFastSplatterFrozenScale(static_cast(o), output->GetNumberOfScalarComponents(), output->GetNumberOfPoints(), static_cast(this->MinValue), static_cast(this->MaxValue), this->LastDataMinValue, this->LastDataMaxValue)); } break; case ScaleLimit: switch (output->GetScalarType()) { vtkTemplateMacro( vtkFastSplatterScale(static_cast(o), output->GetNumberOfScalarComponents(), output->GetNumberOfPoints(), static_cast(this->MinValue), static_cast(this->MaxValue), &this->LastDataMinValue, &this->LastDataMaxValue)); } break; } return 1; } void vtkFastSplatter::SetSplatConnection(vtkAlgorithmOutput* input) { this->SetInputConnection(1, input); }