/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkTransformToGrid.cxx,v $ 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 "vtkTransformToGrid.h" #include "vtkAbstractTransform.h" #include "vtkIdentityTransform.h" #include "vtkImageData.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkObjectFactory.h" #include "vtkStreamingDemandDrivenPipeline.h" vtkCxxRevisionMacro(vtkTransformToGrid, "$Revision: 1.21 $"); vtkStandardNewMacro(vtkTransformToGrid); vtkCxxSetObjectMacro(vtkTransformToGrid,Input,vtkAbstractTransform); //---------------------------------------------------------------------------- vtkTransformToGrid::vtkTransformToGrid() { this->Input = NULL; this->GridScalarType = VTK_DOUBLE; for (int i = 0; i < 3; i++) { this->GridExtent[2*i] = this->GridExtent[2*i+1] = 0; this->GridOrigin[i] = 0.0; this->GridSpacing[i] = 1.0; } this->DisplacementScale = 1.0; this->DisplacementShift = 0.0; this->SetNumberOfInputPorts(0); this->SetNumberOfOutputPorts(1); } //---------------------------------------------------------------------------- vtkTransformToGrid::~vtkTransformToGrid() { this->SetInput(static_cast(0)); } //---------------------------------------------------------------------------- void vtkTransformToGrid::PrintSelf(ostream& os, vtkIndent indent) { int i; this->Superclass::PrintSelf(os,indent); os << indent << "Input: (" << this->Input << ")\n"; os << indent << "GridSpacing: (" << this->GridSpacing[0]; for (i = 1; i < 3; ++i) { os << ", " << this->GridSpacing[i]; } os << ")\n"; os << indent << "GridOrigin: (" << this->GridOrigin[0]; for (i = 1; i < 3; ++i) { os << ", " << this->GridOrigin[i]; } os << ")\n"; os << indent << "GridExtent: (" << this->GridExtent[0]; for (i = 1; i < 6; ++i) { os << ", " << this->GridExtent[i]; } os << ")\n"; os << indent << "GridScalarType: " << vtkImageScalarTypeNameMacro(this->GridScalarType) << "\n"; this->UpdateShiftScale(); os << indent << "DisplacementScale: " << this->DisplacementScale << "\n"; os << indent << "DisplacementShift: " << this->DisplacementShift << "\n"; } //---------------------------------------------------------------------------- // This method returns the largest data that can be generated. void vtkTransformToGrid::RequestInformation ( vtkInformation * vtkNotUsed(request), vtkInformationVector ** vtkNotUsed( inputVector ), vtkInformationVector *outputVector) { // get the info objects vtkInformation* outInfo = outputVector->GetInformationObject(0); if (this->GetInput() == NULL) { vtkErrorMacro("Missing input"); return; } // update the transform, maybe in the future make transforms part of the // pipeline this->Input->Update(); outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), this->GridExtent,6); outInfo->Set(vtkDataObject::SPACING(),this->GridSpacing,3); outInfo->Set(vtkDataObject::ORIGIN(),this->GridOrigin,3); vtkDataObject::SetPointDataActiveScalarInfo(outInfo, this->GridScalarType, 3); } //---------------------------------------------------------------------------- // Return the maximum absolute displacement of the transform over // the entire grid extent -- this is extremely robust and extremely // inefficient, it should be possible to do much better than this. void vtkTransformToGridMinMax(vtkTransformToGrid *self, int extent[6], double &minDisplacement, double &maxDisplacement) { vtkAbstractTransform *transform = self->GetInput(); transform->Update(); if (!transform) { minDisplacement = -1.0; maxDisplacement = +1.0; return; } double *spacing = self->GetGridSpacing(); double *origin = self->GetGridOrigin(); maxDisplacement = -1e37; minDisplacement = +1e37; double point[3],newPoint[3],displacement; for (int k = extent[4]; k <= extent[5]; k++) { point[2] = k*spacing[2] + origin[2]; for (int j = extent[2]; j <= extent[3]; j++) { point[1] = j*spacing[1] + origin[1]; for (int i = extent[0]; i <= extent[1]; i++) { point[0] = i*spacing[0] + origin[0]; transform->InternalTransformPoint(point,newPoint); for (int l = 0; l < 3; l++) { displacement = newPoint[l] - point[l]; if (displacement > maxDisplacement) { maxDisplacement = displacement; } if (displacement < minDisplacement) { minDisplacement = displacement; } } } } } } //---------------------------------------------------------------------------- void vtkTransformToGrid::UpdateShiftScale() { int gridType = this->GridScalarType; // nothing to do for double or double if (gridType == VTK_DOUBLE || gridType == VTK_DOUBLE) { this->DisplacementShift = 0.0; this->DisplacementScale = 1.0; vtkDebugMacro(<< "displacement (scale, shift) = (" << this->DisplacementScale << ", " << this->DisplacementShift << ")"); return; } // check mtime if (this->ShiftScaleTime.GetMTime() > this->GetMTime()) { return; } // get the maximum displacement double minDisplacement, maxDisplacement; vtkTransformToGridMinMax(this,this->GridExtent, minDisplacement, maxDisplacement); vtkDebugMacro(<< "displacement (min, max) = (" << minDisplacement << ", " << maxDisplacement << ")"); double typeMin,typeMax; switch (gridType) { case VTK_SHORT: typeMin = VTK_SHORT_MIN; typeMax = VTK_SHORT_MAX; break; case VTK_UNSIGNED_SHORT: typeMin = VTK_UNSIGNED_SHORT_MIN; typeMax = VTK_UNSIGNED_SHORT_MAX; break; case VTK_CHAR: typeMin = VTK_CHAR_MIN; typeMax = VTK_CHAR_MAX; break; case VTK_UNSIGNED_CHAR: typeMin = VTK_UNSIGNED_CHAR_MIN; typeMax = VTK_UNSIGNED_CHAR_MAX; break; default: vtkErrorMacro(<< "UpdateShiftScale: Unknown input ScalarType"); return; } this->DisplacementScale = ((maxDisplacement - minDisplacement)/ (typeMax - typeMin)); this->DisplacementShift = ((typeMax*minDisplacement-typeMin*maxDisplacement)/ (typeMax - typeMin)); if (this->DisplacementScale == 0.0) { this->DisplacementScale = 1.0; } vtkDebugMacro(<< "displacement (scale, shift) = (" << this->DisplacementScale << ", " << this->DisplacementShift << ")"); this->ShiftScaleTime.Modified(); } //---------------------------------------------------------------------------- // macros to ensure proper round-to-nearest behaviour inline void vtkGridRound(double val, unsigned char& rnd) { rnd = (unsigned char)(val+0.5f); } inline void vtkGridRound(double val, char& rnd) { rnd = (char)((val+128.5f)-128); } inline void vtkGridRound(double val, short& rnd) { rnd = (short)((int)(val+32768.5f)-32768); } inline void vtkGridRound(double val, unsigned short& rnd) { rnd = (unsigned short)(val+0.5f); } inline void vtkGridRound(double val, double& rnd) { rnd = (double)(val); } //---------------------------------------------------------------------------- template void vtkTransformToGridExecute(vtkTransformToGrid *self, vtkImageData *grid, T *gridPtr, int extent[6], double shift, double scale, int id) { vtkAbstractTransform *transform = self->GetInput(); int isIdentity = 0; if (transform == 0) { transform = vtkIdentityTransform::New(); isIdentity = 1; } double *spacing = grid->GetSpacing(); double *origin = grid->GetOrigin(); vtkIdType *increments = grid->GetIncrements(); double invScale = 1.0/scale; double point[3]; double newPoint[3]; T *gridPtr0 = gridPtr; unsigned long count = 0; unsigned long target = (unsigned long) ((extent[5]-extent[4]+1)*(extent[3]-extent[2]+1)/50.0); target++; for (int k = extent[4]; k <= extent[5]; k++) { point[2] = k*spacing[2] + origin[2]; T *gridPtr1 = gridPtr0; for (int j = extent[2]; j <= extent[3]; j++) { if (id == 0) { if (count % target == 0) { self->UpdateProgress(count/(50.0*target)); } count++; } point[1] = j*spacing[1] + origin[1]; gridPtr = gridPtr1; for (int i = extent[0]; i <= extent[1]; i++) { point[0] = i*spacing[0] + origin[0]; transform->InternalTransformPoint(point,newPoint); vtkGridRound((newPoint[0] - point[0] - shift)*invScale,*gridPtr++); vtkGridRound((newPoint[1] - point[1] - shift)*invScale,*gridPtr++); vtkGridRound((newPoint[2] - point[2] - shift)*invScale,*gridPtr++); } gridPtr1 += increments[1]; } gridPtr0 += increments[2]; } if (isIdentity) { transform->Delete(); } } //---------------------------------------------------------------------------- void vtkTransformToGrid::RequestData( vtkInformation* vtkNotUsed( request ), vtkInformationVector** vtkNotUsed( inputVector ), vtkInformationVector* outputVector) { // get the data object vtkInformation *outInfo = outputVector->GetInformationObject(0); vtkImageData *grid = vtkImageData::SafeDownCast( outInfo->Get(vtkDataObject::DATA_OBJECT())); grid->SetExtent( outInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT())); grid->AllocateScalars(); int *extent = grid->GetExtent(); double *gridPtr = (double *)grid->GetScalarPointerForExtent(extent); int gridType = grid->GetScalarType(); this->UpdateShiftScale(); double scale = this->DisplacementScale; double shift = this->DisplacementShift; int id = 0; switch (gridType) { case VTK_DOUBLE: vtkTransformToGridExecute(this, grid, (double *)(gridPtr), extent, shift,scale,id); break; case VTK_SHORT: vtkTransformToGridExecute(this, grid, (short *)(gridPtr), extent, shift,scale,id); break; case VTK_UNSIGNED_SHORT: vtkTransformToGridExecute(this, grid, (unsigned short *)(gridPtr),extent, shift,scale,id); break; case VTK_CHAR: vtkTransformToGridExecute(this, grid, (char *)(gridPtr), extent, shift,scale,id); break; case VTK_UNSIGNED_CHAR: vtkTransformToGridExecute(this, grid, (unsigned char *)(gridPtr), extent, shift,scale,id); break; default: vtkErrorMacro(<< "Execute: Unknown input ScalarType"); } } //---------------------------------------------------------------------------- unsigned long vtkTransformToGrid::GetMTime() { unsigned long mtime = this->Superclass::GetMTime(); if (this->Input) { unsigned long mtime2 = this->Input->GetMTime(); if (mtime2 > mtime) { mtime = mtime2; } } return mtime; } //---------------------------------------------------------------------------- int vtkTransformToGrid::ProcessRequest(vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector) { // generate the data if(request->Has(vtkDemandDrivenPipeline::REQUEST_DATA())) { this->RequestData(request, inputVector, outputVector); return 1; } // execute information if(request->Has(vtkDemandDrivenPipeline::REQUEST_INFORMATION())) { this->RequestInformation(request, inputVector, outputVector); // after executing set the origin and spacing from the // info int i; for (i = 0; i < this->GetNumberOfOutputPorts(); ++i) { vtkInformation* info = outputVector->GetInformationObject(i); vtkImageData *output = vtkImageData::SafeDownCast(info->Get(vtkDataObject::DATA_OBJECT())); // if execute info didn't set origin and spacing then we set them if (!info->Has(vtkDataObject::ORIGIN())) { info->Set(vtkDataObject::ORIGIN(),0,0,0); info->Set(vtkDataObject::SPACING(),1,1,1); } if (output) { output->SetOrigin(info->Get(vtkDataObject::ORIGIN())); output->SetSpacing(info->Get(vtkDataObject::SPACING())); } } return 1; } return this->Superclass::ProcessRequest(request, inputVector, outputVector); } //---------------------------------------------------------------------------- vtkImageData* vtkTransformToGrid::GetOutput() { return vtkImageData::SafeDownCast(this->GetOutputDataObject(0)); } int vtkTransformToGrid::FillOutputPortInformation( int vtkNotUsed(port), vtkInformation* info) { // now add our info info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkImageData"); return 1; }