/*========================================================================= Program: Visualization Toolkit Module: vtkImageActor.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 "vtkImageActor.h" #include "vtkDataSetAttributes.h" #include "vtkImageData.h" #include "vtkImageProperty.h" #include "vtkImageSliceMapper.h" #include "vtkInformation.h" #include "vtkMath.h" #include "vtkMatrix4x4.h" #include "vtkObjectFactory.h" #include "vtkRenderer.h" #include "vtkStreamingDemandDrivenPipeline.h" vtkStandardNewMacro(vtkImageActor); //------------------------------------------------------------------------------ vtkImageActor::vtkImageActor() { this->DisplayExtent[0] = 0; this->DisplayExtent[1] = -1; this->DisplayExtent[2] = 0; this->DisplayExtent[3] = -1; this->DisplayExtent[4] = 0; this->DisplayExtent[5] = -1; vtkMath::UninitializeBounds(this->DisplayBounds); this->Property = vtkImageProperty::New(); this->Property->SetInterpolationTypeToLinear(); this->Property->SetAmbient(1.0); this->Property->SetDiffuse(0.0); vtkImageSliceMapper* mapper = vtkImageSliceMapper::New(); this->Mapper = mapper; mapper->BorderOff(); mapper->SliceAtFocalPointOff(); mapper->SliceFacesCameraOff(); mapper->SetOrientationToZ(); // For backwards compabilitity, make Streaming the default behavior mapper->StreamingOn(); // The result of HasTranslucentPolygonalGeometry is cached this->TranslucentCachedResult = false; this->ForceOpaque = false; } //------------------------------------------------------------------------------ vtkImageActor::~vtkImageActor() { if (this->Property) { this->Property->Delete(); this->Property = nullptr; } if (this->Mapper) { this->Mapper->Delete(); this->Mapper = nullptr; } } //------------------------------------------------------------------------------ void vtkImageActor::SetInputData(vtkImageData* input) { if (this->Mapper && input != this->Mapper->GetInput()) { this->Mapper->SetInputData(input); this->Modified(); } } //------------------------------------------------------------------------------ vtkAlgorithm* vtkImageActor::GetInputAlgorithm() { if (!this->Mapper) { return nullptr; } return this->Mapper->GetInputAlgorithm(); } //------------------------------------------------------------------------------ vtkImageData* vtkImageActor::GetInput() { if (!this->Mapper) { return nullptr; } return this->Mapper->GetInput(); } //------------------------------------------------------------------------------ void vtkImageActor::SetInterpolate(vtkTypeBool i) { if (this->Property) { if (i) { if (this->Property->GetInterpolationType() != VTK_LINEAR_INTERPOLATION) { this->Property->SetInterpolationTypeToLinear(); this->Modified(); } } else { if (this->Property->GetInterpolationType() != VTK_NEAREST_INTERPOLATION) { this->Property->SetInterpolationTypeToNearest(); this->Modified(); } } } } //------------------------------------------------------------------------------ vtkTypeBool vtkImageActor::GetInterpolate() { if (this->Property && this->Property->GetInterpolationType() != VTK_NEAREST_INTERPOLATION) { return 1; } return 0; } //------------------------------------------------------------------------------ void vtkImageActor::SetOpacity(double o) { if (this->Property && this->Property->GetOpacity() != o) { this->Property->SetOpacity(o); this->Modified(); } } //------------------------------------------------------------------------------ double vtkImageActor::GetOpacity() { if (this->Property) { return this->Property->GetOpacity(); } return 1.0; } //------------------------------------------------------------------------------ int vtkImageActor::GetSliceNumber() { if (!this->Mapper || !this->Mapper->IsA("vtkImageSliceMapper")) { return 0; } return static_cast(this->Mapper)->GetSliceNumber(); } //------------------------------------------------------------------------------ int vtkImageActor::GetSliceNumberMax() { if (!this->Mapper || !this->Mapper->IsA("vtkImageSliceMapper")) { return 0; } return static_cast(this->Mapper)->GetSliceNumberMaxValue(); } //------------------------------------------------------------------------------ int vtkImageActor::GetSliceNumberMin() { if (!this->Mapper || !this->Mapper->IsA("vtkImageSliceMapper")) { return 0; } return static_cast(this->Mapper)->GetSliceNumberMinValue(); } //------------------------------------------------------------------------------ void vtkImageActor::SetDisplayExtent(const int extent[6]) { int idx, modified = 0; for (idx = 0; idx < 6; ++idx) { if (this->DisplayExtent[idx] != extent[idx]) { this->DisplayExtent[idx] = extent[idx]; modified = 1; } } if (modified) { if (this->Mapper && this->Mapper->IsA("vtkImageSliceMapper")) { if (this->DisplayExtent[0] <= this->DisplayExtent[1]) { static_cast(this->Mapper)->CroppingOn(); static_cast(this->Mapper)->SetCroppingRegion(this->DisplayExtent); static_cast(this->Mapper) ->SetOrientation(vtkImageActor::GetOrientationFromExtent(this->DisplayExtent)); } else { static_cast(this->Mapper)->CroppingOff(); static_cast(this->Mapper)->SetOrientationToZ(); } } this->Modified(); } } //------------------------------------------------------------------------------ void vtkImageActor::SetDisplayExtent(int minX, int maxX, int minY, int maxY, int minZ, int maxZ) { const int extent[6] = { minX, maxX, minY, maxY, minZ, maxZ }; this->SetDisplayExtent(extent); } //------------------------------------------------------------------------------ void vtkImageActor::GetDisplayExtent(int extent[6]) { for (int idx = 0; idx < 6; ++idx) { extent[idx] = this->DisplayExtent[idx]; } } //------------------------------------------------------------------------------ // Get the bounds for this Volume as (Xmin,Xmax,Ymin,Ymax,Zmin,Zmax). double* vtkImageActor::GetDisplayBounds() { vtkAlgorithm* inputAlg = nullptr; if (this->Mapper && this->Mapper->GetNumberOfInputConnections(0) > 0) { inputAlg = this->Mapper->GetInputAlgorithm(); } if (!inputAlg) { return this->DisplayBounds; } inputAlg->UpdateInformation(); int extent[6]; vtkInformation* inputInfo = this->Mapper->GetInputInformation(); inputInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent); double spacing[3] = { 1, 1, 1 }; if (inputInfo->Has(vtkDataObject::SPACING())) { inputInfo->Get(vtkDataObject::SPACING(), spacing); } double origin[3] = { 0, 0, 0 }; if (inputInfo->Has(vtkDataObject::ORIGIN())) { inputInfo->Get(vtkDataObject::ORIGIN(), origin); } // if the display extent has not been set, use first slice extent[5] = extent[4]; if (this->DisplayExtent[0] <= this->DisplayExtent[1]) { extent[0] = this->DisplayExtent[0]; extent[1] = this->DisplayExtent[1]; extent[2] = this->DisplayExtent[2]; extent[3] = this->DisplayExtent[3]; extent[4] = this->DisplayExtent[4]; extent[5] = this->DisplayExtent[5]; } if (spacing[0] >= 0) { this->DisplayBounds[0] = extent[0] * spacing[0] + origin[0]; this->DisplayBounds[1] = extent[1] * spacing[0] + origin[0]; } else { this->DisplayBounds[0] = extent[1] * spacing[0] + origin[0]; this->DisplayBounds[1] = extent[0] * spacing[0] + origin[0]; } if (spacing[1] >= 0) { this->DisplayBounds[2] = extent[2] * spacing[1] + origin[1]; this->DisplayBounds[3] = extent[3] * spacing[1] + origin[1]; } else { this->DisplayBounds[2] = extent[3] * spacing[1] + origin[1]; this->DisplayBounds[3] = extent[2] * spacing[1] + origin[1]; } if (spacing[2] >= 0) { this->DisplayBounds[4] = extent[4] * spacing[2] + origin[2]; this->DisplayBounds[5] = extent[5] * spacing[2] + origin[2]; } else { this->DisplayBounds[4] = extent[5] * spacing[2] + origin[2]; this->DisplayBounds[5] = extent[4] * spacing[2] + origin[2]; } return this->DisplayBounds; } //------------------------------------------------------------------------------ // Get the bounds for the displayed data as (Xmin,Xmax,Ymin,Ymax,Zmin,Zmax). void vtkImageActor::GetDisplayBounds(double bounds[6]) { this->GetDisplayBounds(); for (int i = 0; i < 6; i++) { bounds[i] = this->DisplayBounds[i]; } } //------------------------------------------------------------------------------ // Get the bounds for this Prop3D as (Xmin,Xmax,Ymin,Ymax,Zmin,Zmax). double* vtkImageActor::GetBounds() { int i, n; double *bounds, bbox[24], *fptr; bounds = this->GetDisplayBounds(); // Check for the special case when the data bounds are unknown if (!bounds) { return bounds; } // fill out vertices of a bounding box bbox[0] = bounds[1]; bbox[1] = bounds[3]; bbox[2] = bounds[5]; bbox[3] = bounds[1]; bbox[4] = bounds[2]; bbox[5] = bounds[5]; bbox[6] = bounds[0]; bbox[7] = bounds[2]; bbox[8] = bounds[5]; bbox[9] = bounds[0]; bbox[10] = bounds[3]; bbox[11] = bounds[5]; bbox[12] = bounds[1]; bbox[13] = bounds[3]; bbox[14] = bounds[4]; bbox[15] = bounds[1]; bbox[16] = bounds[2]; bbox[17] = bounds[4]; bbox[18] = bounds[0]; bbox[19] = bounds[2]; bbox[20] = bounds[4]; bbox[21] = bounds[0]; bbox[22] = bounds[3]; bbox[23] = bounds[4]; // make sure matrix (transform) is up-to-date this->ComputeMatrix(); // and transform into actors coordinates fptr = bbox; for (n = 0; n < 8; n++) { double homogeneousPt[4] = { fptr[0], fptr[1], fptr[2], 1.0 }; this->Matrix->MultiplyPoint(homogeneousPt, homogeneousPt); fptr[0] = homogeneousPt[0] / homogeneousPt[3]; fptr[1] = homogeneousPt[1] / homogeneousPt[3]; fptr[2] = homogeneousPt[2] / homogeneousPt[3]; fptr += 3; } // now calc the new bounds this->Bounds[0] = this->Bounds[2] = this->Bounds[4] = VTK_DOUBLE_MAX; this->Bounds[1] = this->Bounds[3] = this->Bounds[5] = -VTK_DOUBLE_MAX; for (i = 0; i < 8; i++) { for (n = 0; n < 3; n++) { if (bbox[i * 3 + n] < this->Bounds[n * 2]) { this->Bounds[n * 2] = bbox[i * 3 + n]; } if (bbox[i * 3 + n] > this->Bounds[n * 2 + 1]) { this->Bounds[n * 2 + 1] = bbox[i * 3 + n]; } } } return this->Bounds; } //------------------------------------------------------------------------------ int vtkImageActor::GetOrientationFromExtent(const int extent[6]) { int orientation = 2; if (extent[4] == extent[5]) { orientation = 2; } else if (extent[2] == extent[3]) { orientation = 1; } else if (extent[0] == extent[1]) { orientation = 0; } return orientation; } //------------------------------------------------------------------------------ void vtkImageActor::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "ForceOpaque: " << (this->ForceOpaque ? "On\n" : "Off\n"); os << indent << "Input: " << this->GetInput() << "\n"; os << indent << "Interpolate: " << (this->GetInterpolate() ? "On\n" : "Off\n"); os << indent << "Opacity: " << this->GetOpacity() << "\n"; os << indent << "DisplayExtent: (" << this->DisplayExtent[0]; for (int idx = 1; idx < 6; ++idx) { os << ", " << this->DisplayExtent[idx]; } os << ")\n"; } //------------------------------------------------------------------------------ int vtkImageActor::GetWholeZMin() { if (!this->GetInputAlgorithm()) { return 0; } this->GetInputAlgorithm()->UpdateInformation(); int* extent = this->Mapper->GetInputInformation()->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()); return extent[4]; } //------------------------------------------------------------------------------ int vtkImageActor::GetWholeZMax() { if (!this->GetInputAlgorithm()) { return 0; } this->GetInputAlgorithm()->UpdateInformation(); int* extent = this->Mapper->GetInputInformation()->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()); return extent[5]; } //------------------------------------------------------------------------------ // Description: // Does this prop have some translucent polygonal geometry? vtkTypeBool vtkImageActor::HasTranslucentPolygonalGeometry() { if (this->ForceOpaque) { return 0; } if (this->ForceTranslucent) { return 1; } // Always consider translucent if opacity is less than unity if (this->GetOpacity() < 1.0) { return 1; } // Otherwise check the scalar information, and if the image has // color scalars (i.e. type is unsigned char) and if it has an // alpha channel (4-component RGBA, or 2-component Luminance + Alpha), // then we "guess" that it is meant to be translucent. This is for // backwards compatibility, note that the newer vtkImageSlice class // does not do this check. if (!this->Mapper || this->Mapper->GetNumberOfInputConnections(0) == 0) { return 0; } vtkAlgorithm* inputAlg = this->Mapper->GetInputAlgorithm(); if (!inputAlg) { return 0; } // This MTime check is the same as done in vtkTexture if (this->GetMTime() < this->TranslucentComputationTime) { vtkImageData* input = this->GetInput(); if (input == nullptr || input->GetMTime() <= this->TranslucentComputationTime) { return this->TranslucentCachedResult; } } int scalarType = VTK_VOID; int numComp = 1; vtkInformation* inputInfo = this->Mapper->GetInputInformation(); inputAlg->UpdateInformation(); // Get the information for the image scalars vtkInformation* scalarInfo = vtkDataObject::GetActiveFieldInformation( inputInfo, vtkDataObject::FIELD_ASSOCIATION_POINTS, vtkDataSetAttributes::SCALARS); if (scalarInfo) { if (scalarInfo->Has(vtkDataObject::FIELD_ARRAY_TYPE())) { scalarType = scalarInfo->Get(vtkDataObject::FIELD_ARRAY_TYPE()); } if (scalarInfo->Has(vtkDataObject::FIELD_NUMBER_OF_COMPONENTS())) { numComp = scalarInfo->Get(vtkDataObject::FIELD_NUMBER_OF_COMPONENTS()); } } this->TranslucentCachedResult = (scalarType == VTK_UNSIGNED_CHAR && numComp % 2 == 0); this->TranslucentComputationTime.Modified(); return this->TranslucentCachedResult; }