/*========================================================================= Program: Visualization Toolkit Module: vtkSampleFunction.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 "vtkSampleFunction.h" #include "vtkDoubleArray.h" #include "vtkFloatArray.h" #include "vtkGarbageCollector.h" #include "vtkImageData.h" #include "vtkImplicitFunction.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkMath.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkSMPTools.h" #include "vtkStreamingDemandDrivenPipeline.h" vtkStandardNewMacro(vtkSampleFunction); vtkCxxSetObjectMacro(vtkSampleFunction, ImplicitFunction, vtkImplicitFunction); // The heart of the algorithm plus interface to the SMP tools. template class vtkSampleFunctionAlgorithm { public: vtkImplicitFunction* ImplicitFunction; T* Scalars; float* Normals; vtkIdType Extent[6]; vtkIdType Dims[3]; vtkIdType SliceSize; double Origin[3]; double Spacing[3]; double CapValue; // Constructor vtkSampleFunctionAlgorithm(); // Interface between VTK and templated functions static void SampleAcrossImage( vtkSampleFunction* self, vtkImageData* output, int extent[6], T* scalars, float* normals); // Cap the boundaries with the specified cap value (if requested). void Cap(); // Interface implicit function computation to SMP tools. template class FunctionValueOp { public: FunctionValueOp(vtkSampleFunctionAlgorithm* algo) { this->Algo = algo; } vtkSampleFunctionAlgorithm* Algo; void operator()(vtkIdType k, vtkIdType end) { double x[3]; vtkIdType* extent = this->Algo->Extent; vtkIdType i, j, jOffset, kOffset; for (; k < end; ++k) { x[2] = this->Algo->Origin[2] + k * this->Algo->Spacing[2]; kOffset = (k - extent[4]) * this->Algo->SliceSize; for (j = extent[2]; j <= extent[3]; ++j) { x[1] = this->Algo->Origin[1] + j * this->Algo->Spacing[1]; jOffset = (j - extent[2]) * this->Algo->Dims[0]; for (i = extent[0]; i <= extent[1]; ++i) { x[0] = this->Algo->Origin[0] + i * this->Algo->Spacing[0]; this->Algo->Scalars[(i - extent[0]) + jOffset + kOffset] = static_cast(this->Algo->ImplicitFunction->FunctionValue(x)); } } } } }; // Interface implicit function graadient computation to SMP tools. template class FunctionGradientOp { public: FunctionGradientOp(vtkSampleFunctionAlgorithm* algo) { this->Algo = algo; } vtkSampleFunctionAlgorithm* Algo; void operator()(vtkIdType k, vtkIdType end) { double x[3], n[3]; float* nPtr; vtkIdType* extent = this->Algo->Extent; vtkIdType i, j, jOffset, kOffset; for (; k < end; ++k) { x[2] = this->Algo->Origin[2] + k * this->Algo->Spacing[2]; kOffset = (k - extent[4]) * this->Algo->SliceSize; for (j = extent[2]; j <= extent[3]; ++j) { x[1] = this->Algo->Origin[1] + j * this->Algo->Spacing[1]; jOffset = (j - extent[2]) * this->Algo->Dims[0]; for (i = extent[0]; i <= extent[1]; ++i) { x[0] = this->Algo->Origin[0] + i * this->Algo->Spacing[0]; this->Algo->ImplicitFunction->FunctionGradient(x, n); vtkMath::Normalize(n); nPtr = this->Algo->Normals + 3 * ((i - extent[0]) + jOffset + kOffset); nPtr[0] = static_cast(-n[0]); nPtr[1] = static_cast(-n[1]); nPtr[2] = static_cast(-n[2]); } // i } // j } // k } }; }; //------------------------------------------------------------------------------ // Initialized mainly to eliminate compiler warnings. template vtkSampleFunctionAlgorithm::vtkSampleFunctionAlgorithm() : Scalars(nullptr) , Normals(nullptr) { for (int i = 0; i < 3; ++i) { this->Extent[2 * i] = this->Extent[2 * i + 1] = 0; this->Dims[i] = 0; this->Origin[i] = this->Spacing[i] = 0.0; } this->SliceSize = 0; this->CapValue = 0.0; } //------------------------------------------------------------------------------ // Templated class is glue between VTK and templated algorithms. template void vtkSampleFunctionAlgorithm::SampleAcrossImage( vtkSampleFunction* self, vtkImageData* output, int extent[6], T* scalars, float* normals) { // Populate data into local storage vtkSampleFunctionAlgorithm algo; algo.ImplicitFunction = self->GetImplicitFunction(); algo.Scalars = scalars; algo.Normals = normals; for (int i = 0; i < 3; ++i) { algo.Extent[2 * i] = extent[2 * i]; algo.Extent[2 * i + 1] = extent[2 * i + 1]; algo.Dims[i] = extent[2 * i + 1] - extent[2 * i] + 1; } algo.SliceSize = algo.Dims[0] * algo.Dims[1]; output->GetOrigin(algo.Origin); output->GetSpacing(algo.Spacing); algo.CapValue = self->GetCapValue(); // Okay now generate samples using SMP tools FunctionValueOp values(&algo); vtkSMPTools::For(extent[4], extent[5] + 1, values); // If requested, generate normals if (algo.Normals) { FunctionGradientOp gradient(&algo); vtkSMPTools::For(extent[4], extent[5] + 1, gradient); } // If requested, cap boundaries if (self->GetCapping()) { algo.Cap(); } } //------------------------------------------------------------------------------ // Cap the boundaries of the volume if requested. template void vtkSampleFunctionAlgorithm::Cap() { vtkIdType i, j, k; vtkIdType idx; // i-j planes // k = this->Extent[4]; for (j = this->Extent[2]; j <= this->Extent[3]; j++) { for (i = this->Extent[0]; i <= this->Extent[1]; i++) { this->Scalars[i + j * this->Dims[0]] = this->CapValue; } } idx = this->Extent[5] * this->SliceSize; for (j = this->Extent[2]; j <= this->Extent[3]; j++) { for (i = this->Extent[0]; i <= this->Extent[1]; i++) { this->Scalars[idx + i + j * this->Dims[0]] = this->CapValue; } } // j-k planes // i = this->Extent[0]; for (k = this->Extent[4]; k <= this->Extent[5]; k++) { for (j = this->Extent[2]; j <= this->Extent[3]; j++) { this->Scalars[j * this->Dims[0] + k * this->SliceSize] = this->CapValue; } } i = this->Extent[1]; for (k = this->Extent[4]; k <= this->Extent[5]; k++) { for (j = this->Extent[2]; j <= this->Extent[3]; j++) { this->Scalars[i + j * this->Dims[0] + k * this->SliceSize] = this->CapValue; } } // i-k planes // j = this->Extent[2]; for (k = this->Extent[4]; k <= this->Extent[5]; k++) { for (i = this->Extent[0]; i <= this->Extent[1]; i++) { this->Scalars[i + k * this->SliceSize] = this->CapValue; } } j = this->Extent[3]; idx = j * this->Dims[0]; for (k = this->Extent[4]; k <= this->Extent[5]; k++) { for (i = this->Extent[0]; i <= this->Extent[1]; i++) { this->Scalars[idx + i + k * this->SliceSize] = this->CapValue; } } } //------------------------------------------------------------------------------ // Okay define the VTK class proper vtkSampleFunction::vtkSampleFunction() { this->ModelBounds[0] = -1.0; this->ModelBounds[1] = 1.0; this->ModelBounds[2] = -1.0; this->ModelBounds[3] = 1.0; this->ModelBounds[4] = -1.0; this->ModelBounds[5] = 1.0; this->SampleDimensions[0] = 50; this->SampleDimensions[1] = 50; this->SampleDimensions[2] = 50; this->Capping = 0; this->CapValue = VTK_DOUBLE_MAX; this->ImplicitFunction = nullptr; this->ComputeNormals = 1; this->OutputScalarType = VTK_DOUBLE; this->ScalarArrayName = nullptr; this->SetScalarArrayName("scalars"); this->NormalArrayName = nullptr; this->SetNormalArrayName("normals"); this->SetNumberOfInputPorts(0); } //------------------------------------------------------------------------------ vtkSampleFunction::~vtkSampleFunction() { this->SetImplicitFunction(nullptr); this->SetScalarArrayName(nullptr); this->SetNormalArrayName(nullptr); } //------------------------------------------------------------------------------ // Specify the dimensions of the data on which to sample. void vtkSampleFunction::SetSampleDimensions(int i, int j, int k) { int dim[3]; dim[0] = i; dim[1] = j; dim[2] = k; this->SetSampleDimensions(dim); } //------------------------------------------------------------------------------ // Specify the dimensions of the data on which to sample. void vtkSampleFunction::SetSampleDimensions(int dim[3]) { 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]) { for (int i = 0; i < 3; i++) { this->SampleDimensions[i] = (dim[i] > 0 ? dim[i] : 1); } this->Modified(); } } //------------------------------------------------------------------------------ // Set the bounds of the model. void vtkSampleFunction::SetModelBounds(const double bounds[6]) { this->SetModelBounds(bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5]); } //------------------------------------------------------------------------------ void vtkSampleFunction::SetModelBounds( double xMin, double xMax, double yMin, double yMax, double zMin, double zMax) { vtkDebugMacro(<< " setting ModelBounds to (" << "(" << xMin << "," << xMax << "), " << "(" << yMin << "," << yMax << "), " << "(" << zMin << "," << zMax << "), "); if ((xMin > xMax) || (yMin > yMax) || (zMin > zMax)) { vtkErrorMacro("Invalid bounds: " << "(" << xMin << "," << xMax << "), " << "(" << yMin << "," << yMax << "), " << "(" << zMin << "," << zMax << ")" << " Bound mins cannot be larger that bound maxs"); return; } if (xMin != this->ModelBounds[0] || xMax != this->ModelBounds[1] || yMin != this->ModelBounds[2] || yMax != this->ModelBounds[3] || zMin != this->ModelBounds[4] || zMax != this->ModelBounds[5]) { this->ModelBounds[0] = xMin; this->ModelBounds[1] = xMax; this->ModelBounds[2] = yMin; this->ModelBounds[3] = yMax; this->ModelBounds[4] = zMin; this->ModelBounds[5] = zMax; this->Modified(); } } //------------------------------------------------------------------------------ int vtkSampleFunction::RequestInformation(vtkInformation* vtkNotUsed(request), vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* outputVector) { // get the info objects vtkInformation* outInfo = outputVector->GetInformationObject(0); int i; double ar[3], origin[3]; int wExt[6]; wExt[0] = 0; wExt[2] = 0; wExt[4] = 0; wExt[1] = this->SampleDimensions[0] - 1; wExt[3] = this->SampleDimensions[1] - 1; wExt[5] = this->SampleDimensions[2] - 1; outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), wExt, 6); for (i = 0; i < 3; i++) { origin[i] = this->ModelBounds[2 * i]; if (this->SampleDimensions[i] <= 1) { ar[i] = 1; } else { ar[i] = (this->ModelBounds[2 * i + 1] - this->ModelBounds[2 * i]) / (this->SampleDimensions[i] - 1); } } outInfo->Set(vtkDataObject::ORIGIN(), origin, 3); outInfo->Set(vtkDataObject::SPACING(), ar, 3); vtkDataObject::SetPointDataActiveScalarInfo(outInfo, this->OutputScalarType, 1); outInfo->Set(vtkAlgorithm::CAN_PRODUCE_SUB_EXTENT(), 1); return 1; } //------------------------------------------------------------------------------ // Produce the data. void vtkSampleFunction::ExecuteDataWithInformation(vtkDataObject* outp, vtkInformation* outInfo) { vtkFloatArray* newNormals = nullptr; float* normals = nullptr; vtkImageData* output = this->GetOutput(); int* extent = this->GetExecutive()->GetOutputInformation(0)->Get( vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT()); output->SetExtent(extent); output = this->AllocateOutputData(outp, outInfo); vtkDataArray* newScalars = output->GetPointData()->GetScalars(); vtkIdType numPts = newScalars->GetNumberOfTuples(); vtkDebugMacro(<< "Sampling implicit function"); // Initialize self; create output objects // if (!this->ImplicitFunction) { vtkErrorMacro(<< "No implicit function specified"); return; } if (this->ComputeNormals) { newNormals = vtkFloatArray::New(); newNormals->SetNumberOfComponents(3); newNormals->SetNumberOfTuples(numPts); normals = newNormals->WritePointer(0, numPts); } void* ptr = output->GetArrayPointerForExtent(newScalars, extent); switch (newScalars->GetDataType()) { vtkTemplateMacro(vtkSampleFunctionAlgorithm::SampleAcrossImage( this, output, extent, (VTK_TT*)ptr, normals)); } newScalars->SetName(this->ScalarArrayName); // Update self // if (newNormals) { // For an unknown reason yet, if the following line is not commented out, // it will make ImplicitSum, TestBoxFunction and TestDiscreteMarchingCubes // to fail. newNormals->SetName(this->NormalArrayName); output->GetPointData()->SetNormals(newNormals); newNormals->Delete(); } } //------------------------------------------------------------------------------ vtkMTimeType vtkSampleFunction::GetMTime() { vtkMTimeType mTime = this->Superclass::GetMTime(); vtkMTimeType impFuncMTime; if (this->ImplicitFunction != nullptr) { impFuncMTime = this->ImplicitFunction->GetMTime(); mTime = (impFuncMTime > mTime ? impFuncMTime : mTime); } return mTime; } //------------------------------------------------------------------------------ void vtkSampleFunction::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 << "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 << "OutputScalarType: " << this->OutputScalarType << "\n"; if (this->ImplicitFunction) { os << indent << "Implicit Function: " << this->ImplicitFunction << "\n"; } else { os << indent << "No Implicit function defined\n"; } os << indent << "Capping: " << (this->Capping ? "On\n" : "Off\n"); os << indent << "Cap Value: " << this->CapValue << "\n"; os << indent << "Compute Normals: " << (this->ComputeNormals ? "On\n" : "Off\n"); os << indent << "ScalarArrayName: "; if (this->ScalarArrayName != nullptr) { os << this->ScalarArrayName << endl; } else { os << "(none)" << endl; } os << indent << "NormalArrayName: "; if (this->NormalArrayName != nullptr) { os << this->NormalArrayName << endl; } else { os << "(none)" << endl; } } //------------------------------------------------------------------------------ void vtkSampleFunction::ReportReferences(vtkGarbageCollector* collector) { this->Superclass::ReportReferences(collector); vtkGarbageCollectorReport(collector, this->ImplicitFunction, "ImplicitFunction"); }