// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkImageData.h" #include "vtkCellData.h" #include "vtkConstantArray.h" #include "vtkDataArray.h" #include "vtkDoubleArray.h" #include "vtkGenericCell.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkMath.h" #include "vtkMatrix3x3.h" #include "vtkMatrix4x4.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkPoints.h" #include "vtkStructuredCellArray.h" #include "vtkStructuredPointArray.h" #include "vtkUnsignedCharArray.h" #include "vtkVoxel.h" VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkImageData); vtkStandardExtendedNewMacro(vtkImageData); //------------------------------------------------------------------------------ vtkImageData::vtkImageData() { for (int idx = 0; idx < 3; ++idx) { this->Increments[idx] = 0; this->Origin[idx] = 0.0; this->Spacing[idx] = 1.0; } this->DirectionMatrix = vtkMatrix3x3::New(); this->DirectionMatrixIsIdentity = true; this->IndexToPhysicalMatrix = vtkMatrix4x4::New(); this->PhysicalToIndexMatrix = vtkMatrix4x4::New(); this->DirectionMatrix->Identity(); this->ComputeTransforms(); } //------------------------------------------------------------------------------ vtkImageData::~vtkImageData() { if (this->DirectionMatrix) { this->DirectionMatrix->Delete(); } if (this->IndexToPhysicalMatrix) { this->IndexToPhysicalMatrix->Delete(); } if (this->PhysicalToIndexMatrix) { this->PhysicalToIndexMatrix->Delete(); } } //------------------------------------------------------------------------------ // Copy the geometric and topological structure of an input structured points // object. void vtkImageData::CopyStructure(vtkDataSet* ds) { vtkImageData* sPts = static_cast(ds); this->Initialize(); for (int i = 0; i < 3; i++) { this->Spacing[i] = sPts->Spacing[i]; this->Origin[i] = sPts->Origin[i]; } // set extent sets, extent, dimensions, and data description this->DirectionMatrix->DeepCopy(sPts->GetDirectionMatrix()); this->ComputeTransforms(); this->Superclass::CopyStructure(ds); if (ds->HasAnyBlankPoints()) { // there is blanking this->GetPointData()->AddArray(ds->GetPointGhostArray()); } if (ds->HasAnyBlankCells()) { // there is blanking this->GetCellData()->AddArray(ds->GetCellGhostArray()); } } //------------------------------------------------------------------------------ void vtkImageData::CopyInformationFromPipeline(vtkInformation* information) { // Let the superclass copy whatever it wants. this->Superclass::CopyInformationFromPipeline(information); // Copy origin and spacing from pipeline information to the internal // copies. if (information->Has(SPACING())) { this->SetSpacing(information->Get(SPACING())); } if (information->Has(ORIGIN())) { this->SetOrigin(information->Get(ORIGIN())); } if (information->Has(DIRECTION())) { this->SetDirectionMatrix(information->Get(DIRECTION())); } } //------------------------------------------------------------------------------ void vtkImageData::CopyInformationToPipeline(vtkInformation* info) { // Let the superclass copy information to the pipeline. this->Superclass::CopyInformationToPipeline(info); // Copy the spacing, origin, direction, and scalar info info->Set(vtkDataObject::SPACING(), this->Spacing, 3); info->Set(vtkDataObject::ORIGIN(), this->Origin, 3); info->Set(vtkDataObject::DIRECTION(), this->DirectionMatrix->GetData(), 9); vtkDataObject::SetPointDataActiveScalarInfo( info, this->GetScalarType(), this->GetNumberOfScalarComponents()); } //------------------------------------------------------------------------------ // Graphics filters reallocate every execute. Image filters try to reuse // the scalars. void vtkImageData::PrepareForNewData() { // free everything but the scalars vtkDataArray* scalars = this->GetPointData()->GetScalars(); if (scalars) { scalars->Register(this); } this->Initialize(); if (scalars) { this->GetPointData()->SetScalars(scalars); scalars->UnRegister(this); } } //------------------------------------------------------------------------------ void vtkImageData::BuildPoints() { vtkNew xCoords; vtkNew yCoords; vtkNew zCoords; vtkDoubleArray* axisCoords[3] = { xCoords.Get(), yCoords.Get(), zCoords.Get() }; int dims[3]; this->GetDimensions(dims); int extent[6]; this->GetExtent(extent); int loc[3]; int ijk[3]; double point[3]; for (int i = 0; i < 3; ++i) { if (this->DirectionMatrixIsIdentity) { axisCoords[i]->SetNumberOfValues(dims[i]); for (loc[i] = 0, ijk[i] = extent[2 * i]; loc[i] < dims[i]; ++loc[i], ++ijk[i]) { point[i] = this->Origin[i] + this->Spacing[i] * ijk[i]; axisCoords[i]->SetValue(loc[i], point[i]); } } else { // axis coords will be used to extract spacing and origin, so we use loc instead of ijk axisCoords[i]->SetNumberOfValues(2); axisCoords[i]->SetValue(0, this->Origin[i]); axisCoords[i]->SetValue(1, this->Origin[i] + this->Spacing[i]); } } this->SetStructuredPoints(vtkStructuredData::GetPoints( xCoords, yCoords, zCoords, extent, this->DirectionMatrix->GetData())); } //------------------------------------------------------------------------------ void vtkImageData::GetCell(vtkIdType cellId, vtkGenericCell* cell) { // see whether the cell is blanked if (!this->IsCellVisible(cellId)) { cell->SetCellTypeToEmptyCell(); return; } // set cell type cell->SetCellType(this->GetCellTypes()->GetValue(cellId)); // get min max ijk int ijkMin[3], ijkMax[3]; vtkStructuredData::ComputeCellStructuredMinMaxCoords( cellId, this->GetDimensions(), ijkMin, ijkMax, this->GetDataDescription()); // set cell point ids vtkIdType cellSize; this->GetCells()->GetCellAtId(ijkMin, cellSize, cell->PointIds->GetPointer(0)); // set cell points vtkPoints* points = this->GetPoints(); const auto pointsBackend = static_cast*>(points->GetData())->GetBackend(); int loc[3], npts = 0; double point[3]; if (this->DirectionMatrixIsIdentity) { for (loc[2] = ijkMin[2]; loc[2] <= ijkMax[2]; loc[2]++) { point[2] = pointsBackend->mapStructuredZComponent(loc[2]); for (loc[1] = ijkMin[1]; loc[1] <= ijkMax[1]; loc[1]++) { point[1] = pointsBackend->mapStructuredYComponent(loc[1]); for (loc[0] = ijkMin[0]; loc[0] <= ijkMax[0]; loc[0]++) { point[0] = pointsBackend->mapStructuredXComponent(loc[0]); cell->Points->SetPoint(npts++, point); } } } } else { for (loc[2] = ijkMin[2]; loc[2] <= ijkMax[2]; loc[2]++) { for (loc[1] = ijkMin[1]; loc[1] <= ijkMax[1]; loc[1]++) { for (loc[0] = ijkMin[0]; loc[0] <= ijkMax[0]; loc[0]++) { pointsBackend->mapStructuredTuple(loc, point); cell->Points->SetPoint(npts++, point); } } } } } //------------------------------------------------------------------------------ // Fast implementation of GetCellBounds(). Bounds are calculated without // constructing a cell. void vtkImageData::GetCellBounds(vtkIdType cellId, double bounds[6]) { if (this->GetCells()->GetCellSize(cellId) == 0) { bounds[0] = bounds[1] = bounds[2] = bounds[3] = bounds[4] = bounds[5] = 0.0; return; } int ijkMin[3], ijkMax[3]; vtkStructuredData::ComputeCellStructuredMinMaxCoords( cellId, this->GetDimensions(), ijkMin, ijkMax, this->GetDataDescription()); vtkPoints* points = this->GetPoints(); const auto pointsBackend = static_cast*>(points->GetData())->GetBackend(); int loc[3]; double point[3]; bounds[0] = bounds[2] = bounds[4] = VTK_DOUBLE_MAX; bounds[1] = bounds[3] = bounds[5] = VTK_DOUBLE_MIN; if (this->DirectionMatrixIsIdentity) { for (loc[2] = ijkMin[2]; loc[2] <= ijkMax[2]; loc[2]++) { point[2] = pointsBackend->mapStructuredZComponent(loc[2]); bounds[4] = std::min(bounds[4], point[2]); bounds[5] = std::max(bounds[5], point[2]); } for (loc[1] = ijkMin[1]; loc[1] <= ijkMax[1]; loc[1]++) { point[1] = pointsBackend->mapStructuredYComponent(loc[1]); bounds[2] = std::min(bounds[2], point[1]); bounds[3] = std::max(bounds[3], point[1]); } for (loc[0] = ijkMin[0]; loc[0] <= ijkMax[0]; loc[0]++) { point[0] = pointsBackend->mapStructuredXComponent(loc[0]); bounds[0] = std::min(bounds[0], point[0]); bounds[1] = std::max(bounds[1], point[0]); } } else { for (loc[2] = ijkMin[2]; loc[2] <= ijkMax[2]; loc[2]++) { for (loc[1] = ijkMin[1]; loc[1] <= ijkMax[1]; loc[1]++) { for (loc[0] = ijkMin[0]; loc[0] <= ijkMax[0]; loc[0]++) { pointsBackend->mapStructuredTuple(loc, point); bounds[0] = std::min(bounds[0], point[0]); bounds[1] = std::max(bounds[1], point[0]); bounds[2] = std::min(bounds[2], point[1]); bounds[3] = std::max(bounds[3], point[1]); bounds[4] = std::min(bounds[4], point[2]); bounds[5] = std::max(bounds[5], point[2]); } } } } } //------------------------------------------------------------------------------ vtkIdType vtkImageData::FindPoint(double x[3]) { // // Ensure valid spacing // const double* spacing = this->Spacing; vtkIdType dims[3]; this->GetDimensions(dims); std::string ijkLabels[3] = { "I", "J", "K" }; for (int i = 0; i < 3; i++) { if (spacing[i] == 0.0 && dims[i] > 1) { vtkWarningMacro("Spacing along the " << ijkLabels[i] << " axis is 0."); return -1; } } // // Compute the ijk location // const int* extent = this->GetExtent(); int loc[3]; double ijk[3]; this->TransformPhysicalPointToContinuousIndex(x, ijk); loc[0] = vtkMath::Floor(ijk[0] + 0.5); loc[1] = vtkMath::Floor(ijk[1] + 0.5); loc[2] = vtkMath::Floor(ijk[2] + 0.5); if (loc[0] < extent[0] || loc[0] > extent[1] || loc[1] < extent[2] || loc[1] > extent[3] || loc[2] < extent[4] || loc[2] > extent[5]) { return -1; } // since point id is relative to the first point actually stored loc[0] -= extent[0]; loc[1] -= extent[2]; loc[2] -= extent[4]; // // From this location get the point id // return loc[2] * dims[0] * dims[1] + loc[1] * dims[0] + loc[0]; } //------------------------------------------------------------------------------ vtkIdType vtkImageData::FindCell(double x[3], vtkCell* vtkNotUsed(cell), vtkIdType vtkNotUsed(cellId), double tol2, int& subId, double pcoords[3], double* weights) { int idx[3]; // Compute the voxel index if (this->ComputeStructuredCoordinates(x, idx, pcoords) == 0) { // If voxel index is out of bounds, check point "x" against the // bounds to see if within tolerance of the bounds. const int* extent = this->GetExtent(); const double* spacing = this->Spacing; // Compute squared distance of point x from the boundary double dist2 = 0.0; for (int i = 0; i < 3; i++) { int minIdx = extent[i * 2]; int maxIdx = extent[i * 2 + 1]; if (idx[i] < minIdx) { double dist = (idx[i] + pcoords[i] - minIdx) * spacing[i]; idx[i] = minIdx; pcoords[i] = 0.0; dist2 += dist * dist; } else if (idx[i] >= maxIdx) { double dist = (idx[i] + pcoords[i] - maxIdx) * spacing[i]; if (maxIdx == minIdx) { idx[i] = minIdx; pcoords[i] = 0.0; } else { idx[i] = maxIdx - 1; pcoords[i] = 1.0; } dist2 += dist * dist; } } // Check squared distance against the tolerance if (dist2 > tol2) { return -1; } } if (weights) { // Shift parametric coordinates for XZ/YZ planes int descr = this->GetDataDescription(); if (descr == vtkStructuredData::VTK_STRUCTURED_XZ_PLANE) { pcoords[1] = pcoords[2]; pcoords[2] = 0.0; } else if (descr == vtkStructuredData::VTK_STRUCTURED_YZ_PLANE) { pcoords[0] = pcoords[1]; pcoords[1] = pcoords[2]; pcoords[2] = 0.0; } else if (descr == vtkStructuredData::VTK_STRUCTURED_XY_PLANE) { pcoords[2] = 0.0; } vtkVoxel::InterpolationFunctions(pcoords, weights); } // // From this location get the cell id // subId = 0; const vtkIdType cellId = this->ComputeCellId(idx); if (!this->IsCellVisible(cellId)) { return -1; } return cellId; } //------------------------------------------------------------------------------ void vtkImageData::ComputeBounds() { if (this->GetMTime() <= this->ComputeTime) { return; } const int* extent = this->GetExtent(); if (extent[0] > extent[1] || extent[2] > extent[3] || extent[4] > extent[5]) { vtkMath::UninitializeBounds(this->Bounds); } else { if (this->DirectionMatrixIsIdentity) { // Direction is identity: bounds are easy to compute // with only origin and spacing const double* origin = this->Origin; const double* spacing = this->Spacing; int swapXBounds = (spacing[0] < 0); // 1 if true, 0 if false int swapYBounds = (spacing[1] < 0); // 1 if true, 0 if false int swapZBounds = (spacing[2] < 0); // 1 if true, 0 if false this->Bounds[0] = origin[0] + (extent[0 + swapXBounds] * spacing[0]); this->Bounds[2] = origin[1] + (extent[2 + swapYBounds] * spacing[1]); this->Bounds[4] = origin[2] + (extent[4 + swapZBounds] * spacing[2]); this->Bounds[1] = origin[0] + (extent[1 - swapXBounds] * spacing[0]); this->Bounds[3] = origin[1] + (extent[3 - swapYBounds] * spacing[1]); this->Bounds[5] = origin[2] + (extent[5 - swapZBounds] * spacing[2]); } else { // Direction isn't identity: use IndexToPhysical matrix // to determine the position of the dataset corners int iMin, iMax, jMin, jMax, kMin, kMax; iMin = extent[0]; iMax = extent[1]; jMin = extent[2]; jMax = extent[3]; kMin = extent[4]; kMax = extent[5]; int ijkCorners[8][3] = { { iMin, jMin, kMin }, { iMax, jMin, kMin }, { iMin, jMax, kMin }, { iMax, jMax, kMin }, { iMin, jMin, kMax }, { iMax, jMin, kMax }, { iMin, jMax, kMax }, { iMax, jMax, kMax }, }; double xyz[3]; double xMin, xMax, yMin, yMax, zMin, zMax; xMin = yMin = zMin = VTK_DOUBLE_MAX; xMax = yMax = zMax = VTK_DOUBLE_MIN; for (int* ijkCorner : ijkCorners) { this->TransformIndexToPhysicalPoint(ijkCorner, xyz); xMin = std::min(xyz[0], xMin); xMax = std::max(xyz[0], xMax); yMin = std::min(xyz[1], yMin); yMax = std::max(xyz[1], yMax); zMin = std::min(xyz[2], zMin); zMax = std::max(xyz[2], zMax); } this->Bounds[0] = xMin; this->Bounds[1] = xMax; this->Bounds[2] = yMin; this->Bounds[3] = yMax; this->Bounds[4] = zMin; this->Bounds[5] = zMax; } } this->ComputeTime.Modified(); } namespace { class CellVisibility { public: CellVisibility(vtkImageData* input) : Input(input) { } bool operator()(const vtkIdType id) { return !Input->IsCellVisible(id); } private: vtkImageData* Input; }; } // anonymous namespace //------------------------------------------------------------------------------ // Given structured coordinates (i,j,k) for a voxel cell, compute the eight // gradient values for the voxel corners. The order in which the gradient // vectors are arranged corresponds to the ordering of the voxel points. // Gradient vector is computed by central differences (except on edges of // volume where forward difference is used). The scalars s are the scalars // from which the gradient is to be computed. This method will treat // only 3D structured point datasets (i.e., volumes). void vtkImageData::GetVoxelGradient(int i, int j, int k, vtkDataArray* s, vtkDataArray* g) { double gv[3]; int ii, jj, kk, idx = 0; for (kk = 0; kk < 2; kk++) { for (jj = 0; jj < 2; jj++) { for (ii = 0; ii < 2; ii++) { this->GetPointGradient(i + ii, j + jj, k + kk, s, gv); g->SetTuple(idx++, gv); } } } } //------------------------------------------------------------------------------ // Given structured coordinates (i,j,k) for a point in a structured point // dataset, compute the gradient vector from the scalar data at that point. // The scalars s are the scalars from which the gradient is to be computed. // This method will treat structured point datasets of any dimension. void vtkImageData::GetPointGradient(int i, int j, int k, vtkDataArray* s, double g[3]) { const double* ar = this->Spacing; double sp, sm; const int* extent = this->GetExtent(); vtkIdType dims[3]; this->GetDimensions(dims); vtkIdType ijsize = dims[0] * dims[1]; // Adjust i,j,k to the start of the extent i -= extent[0]; j -= extent[2]; k -= extent[4]; // Check for out-of-bounds if (i < 0 || i >= dims[0] || j < 0 || j >= dims[1] || k < 0 || k >= dims[2]) { g[0] = g[1] = g[2] = 0.0; return; } // i-axis if (dims[0] == 1) { g[0] = 0.0; } else if (i == 0) { sp = s->GetComponent(i + 1 + j * dims[0] + k * ijsize, 0); sm = s->GetComponent(i + j * dims[0] + k * ijsize, 0); g[0] = (sm - sp) / ar[0]; } else if (i == (dims[0] - 1)) { sp = s->GetComponent(i + j * dims[0] + k * ijsize, 0); sm = s->GetComponent(i - 1 + j * dims[0] + k * ijsize, 0); g[0] = (sm - sp) / ar[0]; } else { sp = s->GetComponent(i + 1 + j * dims[0] + k * ijsize, 0); sm = s->GetComponent(i - 1 + j * dims[0] + k * ijsize, 0); g[0] = 0.5 * (sm - sp) / ar[0]; } // j-axis if (dims[1] == 1) { g[1] = 0.0; } else if (j == 0) { sp = s->GetComponent(i + (j + 1) * dims[0] + k * ijsize, 0); sm = s->GetComponent(i + j * dims[0] + k * ijsize, 0); g[1] = (sm - sp) / ar[1]; } else if (j == (dims[1] - 1)) { sp = s->GetComponent(i + j * dims[0] + k * ijsize, 0); sm = s->GetComponent(i + (j - 1) * dims[0] + k * ijsize, 0); g[1] = (sm - sp) / ar[1]; } else { sp = s->GetComponent(i + (j + 1) * dims[0] + k * ijsize, 0); sm = s->GetComponent(i + (j - 1) * dims[0] + k * ijsize, 0); g[1] = 0.5 * (sm - sp) / ar[1]; } // k-axis if (dims[2] == 1) { g[2] = 0.0; } else if (k == 0) { sp = s->GetComponent(i + j * dims[0] + (k + 1) * ijsize, 0); sm = s->GetComponent(i + j * dims[0] + k * ijsize, 0); g[2] = (sm - sp) / ar[2]; } else if (k == (dims[2] - 1)) { sp = s->GetComponent(i + j * dims[0] + k * ijsize, 0); sm = s->GetComponent(i + j * dims[0] + (k - 1) * ijsize, 0); g[2] = (sm - sp) / ar[2]; } else { sp = s->GetComponent(i + j * dims[0] + (k + 1) * ijsize, 0); sm = s->GetComponent(i + j * dims[0] + (k - 1) * ijsize, 0); g[2] = 0.5 * (sm - sp) / ar[2]; } // Apply direction transform to get in xyz coordinate system // Note: we already applied the spacing when handling the ijk // axis above, and do not need to translate by the origin // since this is a gradient computation this->DirectionMatrix->MultiplyPoint(g, g); } //------------------------------------------------------------------------------ int vtkImageData::ComputeStructuredCoordinates(const double x[3], int ijk[3], double pcoords[3]) { return this->ComputeStructuredCoordinates(x, ijk, pcoords, 1e-12); } //------------------------------------------------------------------------------ int vtkImageData::ComputeStructuredCoordinates( const double x[3], int ijk[3], double pcoords[3], double tol2) { // // Compute the ijk location // double doubleLoc[3]; this->TransformPhysicalPointToContinuousIndex(x, doubleLoc); const int* extent = this->GetExtent(); int isInBounds = 1; for (int i = 0; i < 3; i++) { // Floor for negative indexes. ijk[i] = vtkMath::Floor(doubleLoc[i]); // integer pcoords[i] = doubleLoc[i] - ijk[i]; // >= 0 and < 1 int tmpInBounds = 0; int minExt = extent[i * 2]; int maxExt = extent[i * 2 + 1]; // check if data is one pixel thick as well as // low boundary check if (minExt == maxExt || ijk[i] < minExt) { double dist = doubleLoc[i] - minExt; if (dist * dist <= tol2) { pcoords[i] = 0.0; ijk[i] = minExt; tmpInBounds = 1; } } // high boundary check else if (ijk[i] >= maxExt) { double dist = doubleLoc[i] - maxExt; if (dist * dist <= tol2) { // make sure index is within the allowed cell index range pcoords[i] = 1.0; ijk[i] = maxExt - 1; tmpInBounds = 1; } } // else index is definitely within bounds else { tmpInBounds = 1; } // clear isInBounds if out of bounds for this dimension isInBounds = (isInBounds & tmpInBounds); } return isInBounds; } //------------------------------------------------------------------------------ void vtkImageData::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); int idx; const double* direction = this->GetDirectionMatrix()->GetData(); os << indent << "Spacing: (" << this->Spacing[0] << ", " << this->Spacing[1] << ", " << this->Spacing[2] << ")\n"; os << indent << "Origin: (" << this->Origin[0] << ", " << this->Origin[1] << ", " << this->Origin[2] << ")\n"; os << indent << "Direction: (" << direction[0]; for (idx = 1; idx < 9; ++idx) { os << ", " << direction[idx]; } os << ")\n"; os << indent << "Increments: (" << this->Increments[0] << ", " << this->Increments[1] << ", " << this->Increments[2] << ")\n"; os << ")\n"; } //------------------------------------------------------------------------------ vtkIdType* vtkImageData::GetIncrements() { // Make sure the increments are up to date. The filter bypass and update // mechanism make it tricky to update the increments anywhere other than here this->ComputeIncrements(); return this->Increments; } //------------------------------------------------------------------------------ vtkIdType* vtkImageData::GetIncrements(vtkDataArray* scalars) { // Make sure the increments are up to date. The filter bypass and update // mechanism make it tricky to update the increments anywhere other than here this->ComputeIncrements(scalars); return this->Increments; } //------------------------------------------------------------------------------ void vtkImageData::GetIncrements(vtkIdType& incX, vtkIdType& incY, vtkIdType& incZ) { vtkIdType inc[3]; this->ComputeIncrements(inc); incX = inc[0]; incY = inc[1]; incZ = inc[2]; } //------------------------------------------------------------------------------ void vtkImageData::GetIncrements( vtkDataArray* scalars, vtkIdType& incX, vtkIdType& incY, vtkIdType& incZ) { vtkIdType inc[3]; this->ComputeIncrements(scalars, inc); incX = inc[0]; incY = inc[1]; incZ = inc[2]; } //------------------------------------------------------------------------------ void vtkImageData::GetIncrements(vtkIdType inc[3]) { this->ComputeIncrements(inc); } //------------------------------------------------------------------------------ void vtkImageData::GetIncrements(vtkDataArray* scalars, vtkIdType inc[3]) { this->ComputeIncrements(scalars, inc); } //------------------------------------------------------------------------------ void vtkImageData::GetContinuousIncrements( int extent[6], vtkIdType& incX, vtkIdType& incY, vtkIdType& incZ) { this->GetContinuousIncrements(this->GetPointData()->GetScalars(), extent, incX, incY, incZ); } //------------------------------------------------------------------------------ void vtkImageData::GetContinuousIncrements( vtkDataArray* scalars, int extent[6], vtkIdType& incX, vtkIdType& incY, vtkIdType& incZ) { int e0, e1, e2, e3; incX = 0; const int* selfExtent = this->GetExtent(); e0 = std::max(extent[0], selfExtent[0]); e1 = std::min(extent[1], selfExtent[1]); e2 = std::max(extent[2], selfExtent[2]); e3 = std::min(extent[3], selfExtent[3]); // Make sure the increments are up to date vtkIdType inc[3]; this->ComputeIncrements(scalars, inc); incY = inc[1] - (e1 - e0 + 1) * inc[0]; incZ = inc[2] - (e3 - e2 + 1) * inc[1]; } //------------------------------------------------------------------------------ // This method computes the increments from the MemoryOrder and the extent. // This version assumes we are using the Active Scalars void vtkImageData::ComputeIncrements(vtkIdType inc[3]) { this->ComputeIncrements(this->GetPointData()->GetScalars(), inc); } //------------------------------------------------------------------------------ // This method computes the increments from the MemoryOrder and the extent. void vtkImageData::ComputeIncrements(vtkDataArray* scalars, vtkIdType inc[3]) { if (!scalars) { vtkErrorMacro("No Scalar Field has been specified - assuming 1 component!"); this->ComputeIncrements(1, inc); } else { this->ComputeIncrements(scalars->GetNumberOfComponents(), inc); } } //------------------------------------------------------------------------------ // This method computes the increments from the MemoryOrder and the extent. void vtkImageData::ComputeIncrements(int numberOfComponents, vtkIdType inc[3]) { int idx; vtkIdType incr = numberOfComponents; const int* extent = this->GetExtent(); for (idx = 0; idx < 3; ++idx) { inc[idx] = incr; incr *= (extent[idx * 2 + 1] - extent[idx * 2] + 1); } } //------------------------------------------------------------------------------ template void vtkImageDataConvertScalar(TIn* in, TOut* out) { *out = static_cast(*in); } //------------------------------------------------------------------------------ double vtkImageData::GetScalarComponentAsDouble(int x, int y, int z, int comp) { // Check the component index. if (comp < 0 || comp >= this->GetNumberOfScalarComponents()) { vtkErrorMacro("Bad component index " << comp); return 0.0; } vtkIdType index = this->GetScalarIndex(x, y, z); if (index < 0) { // An error message was already generated by GetScalarIndex. return 0.0; } vtkDataArray* scalars = this->GetPointData()->GetScalars(); return scalars->GetComponent(index, comp); } //------------------------------------------------------------------------------ void vtkImageData::SetScalarComponentFromDouble(int x, int y, int z, int comp, double value) { // Check the component index. if (comp < 0 || comp >= this->GetNumberOfScalarComponents()) { vtkErrorMacro("Bad component index " << comp); return; } vtkIdType index = this->GetScalarIndex(x, y, z); if (index < 0) { // An error message was already generated by GetScalarIndex. return; } vtkDataArray* scalars = this->GetPointData()->GetScalars(); scalars->SetComponent(index, comp, value); } //------------------------------------------------------------------------------ float vtkImageData::GetScalarComponentAsFloat(int x, int y, int z, int comp) { return this->GetScalarComponentAsDouble(x, y, z, comp); } //------------------------------------------------------------------------------ void vtkImageData::SetScalarComponentFromFloat(int x, int y, int z, int comp, float value) { this->SetScalarComponentFromDouble(x, y, z, comp, value); } //------------------------------------------------------------------------------ // This Method returns a pointer to a location in the vtkImageData. // Coordinates are in pixel units and are relative to the whole // image origin. void* vtkImageData::GetScalarPointer(int x, int y, int z) { return this->GetArrayPointer(this->GetPointData()->GetScalars(), x, y, z); } //------------------------------------------------------------------------------ // This Method returns a pointer to a location in the vtkImageData. // Coordinates are in pixel units and are relative to the whole // image origin. void* vtkImageData::GetScalarPointerForExtent(int extent[6]) { return this->GetArrayPointerForExtent(this->GetPointData()->GetScalars(), extent); } //------------------------------------------------------------------------------ void* vtkImageData::GetScalarPointer(int coordinate[3]) { return this->GetArrayPointer(this->GetPointData()->GetScalars(), coordinate); } //------------------------------------------------------------------------------ // This method returns a pointer to the origin of the vtkImageData. void* vtkImageData::GetScalarPointer() { auto array = this->GetPointData()->GetScalars(); return array ? array->GetVoidPointer(0) : nullptr; } //------------------------------------------------------------------------------ // This Method returns an index to a location in the vtkImageData. // Coordinates are in pixel units and are relative to the whole // image origin. vtkIdType vtkImageData::GetScalarIndex(int x, int y, int z) { return this->GetTupleIndex(this->GetPointData()->GetScalars(), x, y, z); } //------------------------------------------------------------------------------ // This Method returns an index to a location in the vtkImageData. // Coordinates are in pixel units and are relative to the whole // image origin. vtkIdType vtkImageData::GetScalarIndexForExtent(int extent[6]) { return this->GetTupleIndexForExtent(this->GetPointData()->GetScalars(), extent); } //------------------------------------------------------------------------------ vtkIdType vtkImageData::GetScalarIndex(int coordinate[3]) { return this->GetTupleIndex(this->GetPointData()->GetScalars(), coordinate); } //------------------------------------------------------------------------------ void vtkImageData::AllocateScalars(vtkInformation* pipeline_info) { auto mkhold = vtkMemkindRAII(this->GetIsInMemkind()); int newType = VTK_DOUBLE; int newNumComp = 1; if (pipeline_info) { vtkInformation* scalarInfo = vtkDataObject::GetActiveFieldInformation( pipeline_info, FIELD_ASSOCIATION_POINTS, vtkDataSetAttributes::SCALARS); if (scalarInfo) { newType = scalarInfo->Get(FIELD_ARRAY_TYPE()); if (scalarInfo->Has(FIELD_NUMBER_OF_COMPONENTS())) { newNumComp = scalarInfo->Get(FIELD_NUMBER_OF_COMPONENTS()); } } } this->AllocateScalars(newType, newNumComp); } //------------------------------------------------------------------------------ void vtkImageData::AllocateScalars(int dataType, int numComponents) { auto mkhold = vtkMemkindRAII(this->GetIsInMemkind()); vtkDataArray* scalars; // if the scalar type has not been set then we have a problem if (dataType == VTK_VOID) { vtkErrorMacro("Attempt to allocate scalars before scalar type was set!."); return; } const int* extent = this->GetExtent(); // Use vtkIdType to avoid overflow on large images vtkIdType dims[3]; dims[0] = extent[1] - extent[0] + 1; dims[1] = extent[3] - extent[2] + 1; dims[2] = extent[5] - extent[4] + 1; vtkIdType imageSize = dims[0] * dims[1] * dims[2]; // if we currently have scalars then just adjust the size scalars = this->PointData->GetScalars(); if (scalars && scalars->GetDataType() == dataType && scalars->GetReferenceCount() == 1) { scalars->SetNumberOfComponents(numComponents); scalars->SetNumberOfTuples(imageSize); // Since the execute method will be modifying the scalars // directly. scalars->Modified(); return; } // allocate the new scalars scalars = vtkDataArray::CreateDataArray(dataType); scalars->SetNumberOfComponents(numComponents); scalars->SetName("ImageScalars"); // allocate enough memory scalars->SetNumberOfTuples(imageSize); this->PointData->SetScalars(scalars); scalars->Delete(); } //------------------------------------------------------------------------------ int vtkImageData::GetScalarSize(vtkInformation* meta_data) { return vtkDataArray::GetDataTypeSize(this->GetScalarType(meta_data)); } int vtkImageData::GetScalarSize() { vtkDataArray* scalars = this->GetPointData()->GetScalars(); if (!scalars) { return vtkDataArray::GetDataTypeSize(VTK_DOUBLE); } return vtkDataArray::GetDataTypeSize(scalars->GetDataType()); } //------------------------------------------------------------------------------ // This templated function executes the filter for any type of data. template void vtkImageDataCastExecute( vtkImageData* inData, IT* inPtr, vtkImageData* outData, OT* outPtr, int outExt[6]) { int idxR, idxY, idxZ; int maxY, maxZ; vtkIdType inIncX, inIncY, inIncZ; vtkIdType outIncX, outIncY, outIncZ; int rowLength; // find the region to loop over rowLength = (outExt[1] - outExt[0] + 1) * inData->GetNumberOfScalarComponents(); maxY = outExt[3] - outExt[2]; maxZ = outExt[5] - outExt[4]; // Get increments to march through data inData->GetContinuousIncrements(outExt, inIncX, inIncY, inIncZ); outData->GetContinuousIncrements(outExt, outIncX, outIncY, outIncZ); // Loop through output pixels for (idxZ = 0; idxZ <= maxZ; idxZ++) { for (idxY = 0; idxY <= maxY; idxY++) { for (idxR = 0; idxR < rowLength; idxR++) { // Pixel operation *outPtr = static_cast(*inPtr); outPtr++; inPtr++; } outPtr += outIncY; inPtr += inIncY; } outPtr += outIncZ; inPtr += inIncZ; } } //------------------------------------------------------------------------------ template void vtkImageDataCastExecute(vtkImageData* inData, T* inPtr, vtkImageData* outData, int outExt[6]) { void* outPtr = outData->GetScalarPointerForExtent(outExt); if (outPtr == nullptr) { vtkGenericWarningMacro("Scalars not allocated."); return; } int scalarType = outData->GetPointData()->GetScalars()->GetDataType(); switch (scalarType) { vtkTemplateMacro(vtkImageDataCastExecute( inData, static_cast(inPtr), outData, static_cast(outPtr), outExt)); default: vtkGenericWarningMacro("Execute: Unknown output ScalarType"); return; } } //------------------------------------------------------------------------------ // This method is passed a input and output region, and executes the filter // algorithm to fill the output from the input. // It just executes a switch statement to call the correct function for // the regions data types. void vtkImageData::CopyAndCastFrom(vtkImageData* inData, int extent[6]) { void* inPtr = inData->GetScalarPointerForExtent(extent); if (inPtr == nullptr) { vtkErrorMacro("Scalars not allocated."); return; } int scalarType = inData->GetPointData()->GetScalars()->GetDataType(); switch (scalarType) { vtkTemplateMacro(vtkImageDataCastExecute(inData, static_cast(inPtr), this, extent)); default: vtkErrorMacro(<< "Execute: Unknown input ScalarType"); return; } } //------------------------------------------------------------------------------ void vtkImageData::Crop(const int* updateExtent) { const int* extent = this->GetExtent(); // Do nothing for empty datasets: for (int dim = 0; dim < 3; ++dim) { if (extent[2 * dim] > extent[2 * dim + 1]) { vtkDebugMacro(<< "Refusing to crop empty dataset."); return; } } int nExt[6]; int idxX, idxY, idxZ; int maxX, maxY, maxZ; vtkIdType outId, inId, inIdY, inIdZ, incZ, incY; vtkImageData* newImage; vtkIdType numPts, numCells, tmp; // If extents already match, then we need to do nothing. if (extent[0] == updateExtent[0] && extent[1] == updateExtent[1] && extent[2] == updateExtent[2] && extent[3] == updateExtent[3] && extent[4] == updateExtent[4] && extent[5] == updateExtent[5]) { return; } // Take the intersection of the two extent so that // we are not asking for more than the extent. memcpy(nExt, updateExtent, 6 * sizeof(int)); nExt[0] = std::max(nExt[0], extent[0]); nExt[1] = std::min(nExt[1], extent[1]); nExt[2] = std::max(nExt[2], extent[2]); nExt[3] = std::min(nExt[3], extent[3]); nExt[4] = std::max(nExt[4], extent[4]); nExt[5] = std::min(nExt[5], extent[5]); // If the extents are the same just return. if (extent[0] == nExt[0] && extent[1] == nExt[1] && extent[2] == nExt[2] && extent[3] == nExt[3] && extent[4] == nExt[4] && extent[5] == nExt[5]) { vtkDebugMacro("Extents already match."); return; } // How many point/cells. numPts = (nExt[1] - nExt[0] + 1) * (nExt[3] - nExt[2] + 1) * (nExt[5] - nExt[4] + 1); // Conditional are to handle 3d, 2d, and even 1d images. tmp = nExt[1] - nExt[0]; if (tmp <= 0) { tmp = 1; } numCells = tmp; tmp = nExt[3] - nExt[2]; if (tmp <= 0) { tmp = 1; } numCells *= tmp; tmp = nExt[5] - nExt[4]; if (tmp <= 0) { tmp = 1; } numCells *= tmp; // Create a new temporary image. newImage = vtkImageData::New(); newImage->SetExtent(nExt); vtkPointData* npd = newImage->GetPointData(); vtkCellData* ncd = newImage->GetCellData(); npd->CopyAllocate(this->PointData, numPts); ncd->CopyAllocate(this->CellData, numCells); // Loop through outData points incY = extent[1] - extent[0] + 1; incZ = (extent[3] - extent[2] + 1) * incY; outId = 0; inIdZ = incZ * (nExt[4] - extent[4]) + incY * (nExt[2] - extent[2]) + (nExt[0] - extent[0]); for (idxZ = nExt[4]; idxZ <= nExt[5]; idxZ++) { inIdY = inIdZ; for (idxY = nExt[2]; idxY <= nExt[3]; idxY++) { inId = inIdY; for (idxX = nExt[0]; idxX <= nExt[1]; idxX++) { npd->CopyData(this->PointData, inId, outId); ++inId; ++outId; } inIdY += incY; } inIdZ += incZ; } // Loop through outData cells // Have to handle the 2d and 1d cases. maxX = nExt[1]; maxY = nExt[3]; maxZ = nExt[5]; if (maxX == nExt[0]) { ++maxX; } if (maxY == nExt[2]) { ++maxY; } if (maxZ == nExt[4]) { ++maxZ; } incY = extent[1] - extent[0]; incZ = (extent[3] - extent[2]) * incY; outId = 0; inIdZ = incZ * (nExt[4] - extent[4]) + incY * (nExt[2] - extent[2]) + (nExt[0] - extent[0]); for (idxZ = nExt[4]; idxZ < maxZ; idxZ++) { inIdY = inIdZ; for (idxY = nExt[2]; idxY < maxY; idxY++) { inId = inIdY; for (idxX = nExt[0]; idxX < maxX; idxX++) { ncd->CopyData(this->CellData, inId, outId); ++inId; ++outId; } inIdY += incY; } inIdZ += incZ; } this->PointData->ShallowCopy(npd); this->CellData->ShallowCopy(ncd); this->SetExtent(nExt); newImage->Delete(); } //------------------------------------------------------------------------------ double vtkImageData::GetScalarTypeMin(vtkInformation* meta_data) { return vtkDataArray::GetDataTypeMin(this->GetScalarType(meta_data)); } //------------------------------------------------------------------------------ double vtkImageData::GetScalarTypeMin() { return vtkDataArray::GetDataTypeMin(this->GetScalarType()); } //------------------------------------------------------------------------------ double vtkImageData::GetScalarTypeMax(vtkInformation* meta_data) { return vtkDataArray::GetDataTypeMax(this->GetScalarType(meta_data)); } //------------------------------------------------------------------------------ double vtkImageData::GetScalarTypeMax() { return vtkDataArray::GetDataTypeMax(this->GetScalarType()); } //------------------------------------------------------------------------------ void vtkImageData::SetAxisUpdateExtent( int idx, int min, int max, const int* updateExtent, int* axisUpdateExtent) { if (idx > 2) { vtkWarningMacro("illegal axis!"); return; } memcpy(axisUpdateExtent, updateExtent, 6 * sizeof(int)); if (axisUpdateExtent[idx * 2] != min) { axisUpdateExtent[idx * 2] = min; } if (axisUpdateExtent[idx * 2 + 1] != max) { axisUpdateExtent[idx * 2 + 1] = max; } } //------------------------------------------------------------------------------ void vtkImageData::GetAxisUpdateExtent(int idx, int& min, int& max, const int* updateExtent) { if (idx > 2) { vtkWarningMacro("illegal axis!"); return; } min = updateExtent[idx * 2]; max = updateExtent[idx * 2 + 1]; } //------------------------------------------------------------------------------ unsigned long vtkImageData::GetActualMemorySize() { return this->Superclass::GetActualMemorySize(); } //------------------------------------------------------------------------------ void vtkImageData::ShallowCopy(vtkDataObject* dataObject) { vtkImageData* imageData = vtkImageData::SafeDownCast(dataObject); if (imageData != nullptr) { this->InternalImageDataCopy(imageData); } // Do superclass this->Superclass::ShallowCopy(dataObject); } //------------------------------------------------------------------------------ void vtkImageData::DeepCopy(vtkDataObject* dataObject) { auto mkhold = vtkMemkindRAII(this->GetIsInMemkind()); vtkImageData* imageData = vtkImageData::SafeDownCast(dataObject); if (imageData != nullptr) { this->InternalImageDataCopy(imageData); } // Do superclass this->Superclass::DeepCopy(dataObject); } //------------------------------------------------------------------------------ // This copies all the local variables (but not objects). void vtkImageData::InternalImageDataCopy(vtkImageData* src) { for (int idx = 0; idx < 3; ++idx) { this->Increments[idx] = src->Increments[idx]; this->Origin[idx] = src->Origin[idx]; this->Spacing[idx] = src->Spacing[idx]; } this->DirectionMatrix->DeepCopy(src->DirectionMatrix); this->ComputeTransforms(); // set extent sets, extent, dimensions, and data description this->SetExtent(src->GetExtent()); } //============================================================================ // Starting to make some more general methods that deal with any array // (not just scalars). //============================================================================ //------------------------------------------------------------------------------ // This Method returns a pointer to a location in the vtkImageData. // Coordinates are in pixel units and are relative to the whole // image origin. void vtkImageData::GetArrayIncrements(vtkDataArray* array, vtkIdType increments[3]) { const int* extent = this->GetExtent(); // We could store tuple increments and just // multiply by the number of components... increments[0] = array->GetNumberOfComponents(); increments[1] = increments[0] * (extent[1] - extent[0] + 1); increments[2] = increments[1] * (extent[3] - extent[2] + 1); } //------------------------------------------------------------------------------ void* vtkImageData::GetArrayPointerForExtent(vtkDataArray* array, int extent[6]) { int tmp[3] = { extent[0], extent[2], extent[4] }; return this->GetArrayPointer(array, tmp); } //------------------------------------------------------------------------------ // This Method returns a pointer to a location in the vtkImageData. // Coordinates are in pixel units and are relative to the whole // image origin. void* vtkImageData::GetArrayPointer(vtkDataArray* array, int coordinate[3]) { vtkIdType valueIndex = this->GetValueIndex(array, coordinate); return valueIndex >= 0 ? array->GetVoidPointer(valueIndex) : nullptr; } //------------------------------------------------------------------------------ void* vtkImageData::GetArrayPointer(vtkDataArray* array, int x, int y, int z) { int temp[3] = { x, y, z }; return this->GetArrayPointer(array, temp); } //------------------------------------------------------------------------------ void vtkImageData::ComputeInternalExtent(int* intExt, int* tgtExt, int* bnds) { int i; const int* extent = this->GetExtent(); for (i = 0; i < 3; ++i) { intExt[i * 2] = tgtExt[i * 2]; if (intExt[i * 2] - bnds[i * 2] < extent[i * 2]) { intExt[i * 2] = extent[i * 2] + bnds[i * 2]; } intExt[i * 2 + 1] = tgtExt[i * 2 + 1]; if (intExt[i * 2 + 1] + bnds[i * 2 + 1] > extent[i * 2 + 1]) { intExt[i * 2 + 1] = extent[i * 2 + 1] - bnds[i * 2 + 1]; } } } //------------------------------------------------------------------------------ vtkImageData* vtkImageData::GetData(vtkInformation* info) { return info ? vtkImageData::SafeDownCast(info->Get(DATA_OBJECT())) : nullptr; } //------------------------------------------------------------------------------ vtkImageData* vtkImageData::GetData(vtkInformationVector* v, int i) { return vtkImageData::GetData(v->GetInformationObject(i)); } //------------------------------------------------------------------------------ void vtkImageData::SetSpacing(double i, double j, double k) { vtkDebugMacro(<< this->GetClassName() << " (" << this << "): setting Spacing to (" << i << "," << j << "," << k << ")"); if ((this->Spacing[0] != i) || (this->Spacing[1] != j) || (this->Spacing[2] != k)) { this->Spacing[0] = i; this->Spacing[1] = j; this->Spacing[2] = k; this->ComputeTransforms(); this->BuildPoints(); this->Modified(); } } //------------------------------------------------------------------------------ void vtkImageData::SetSpacing(const double ijk[3]) { this->SetSpacing(ijk[0], ijk[1], ijk[2]); } //------------------------------------------------------------------------------ void vtkImageData::SetOrigin(double i, double j, double k) { vtkDebugMacro(<< this->GetClassName() << " (" << this << "): setting Origin to (" << i << "," << j << "," << k << ")"); if ((this->Origin[0] != i) || (this->Origin[1] != j) || (this->Origin[2] != k)) { this->Origin[0] = i; this->Origin[1] = j; this->Origin[2] = k; this->ComputeTransforms(); this->BuildPoints(); this->Modified(); } } //------------------------------------------------------------------------------ void vtkImageData::SetOrigin(const double ijk[3]) { this->SetOrigin(ijk[0], ijk[1], ijk[2]); } //------------------------------------------------------------------------------ void vtkImageData::SetDirectionMatrix(vtkMatrix3x3* m) { vtkMTimeType lastModified = this->GetMTime(); vtkSetObjectBodyMacro(DirectionMatrix, vtkMatrix3x3, m); if (lastModified < this->GetMTime()) { this->ComputeTransforms(); this->BuildPoints(); this->Modified(); } } //------------------------------------------------------------------------------ void vtkImageData::SetDirectionMatrix(const double elements[9]) { this->SetDirectionMatrix(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8]); } //------------------------------------------------------------------------------ void vtkImageData::SetDirectionMatrix(double e00, double e01, double e02, double e10, double e11, double e12, double e20, double e21, double e22) { vtkMatrix3x3* m3 = this->DirectionMatrix; vtkMTimeType lastModified = m3->GetMTime(); m3->SetElement(0, 0, e00); m3->SetElement(0, 1, e01); m3->SetElement(0, 2, e02); m3->SetElement(1, 0, e10); m3->SetElement(1, 1, e11); m3->SetElement(1, 2, e12); m3->SetElement(2, 0, e20); m3->SetElement(2, 1, e21); m3->SetElement(2, 2, e22); if (lastModified < m3->GetMTime()) { this->ComputeTransforms(); this->BuildPoints(); this->Modified(); } } //------------------------------------------------------------------------------ template inline static void TransformCoordinates( T1 input0, T1 input1, T1 input2, T2 output[3], vtkMatrix4x4* m4) { double* mdata = m4->GetData(); output[0] = mdata[0] * input0 + mdata[1] * input1 + mdata[2] * input2 + mdata[3]; output[1] = mdata[4] * input0 + mdata[5] * input1 + mdata[6] * input2 + mdata[7]; output[2] = mdata[8] * input0 + mdata[9] * input1 + mdata[10] * input2 + mdata[11]; } // must pass the inverse matrix template inline static void TransformNormal(T1 input0, T1 input1, T1 input2, T2 output[3], vtkMatrix4x4* m4) { double* mdata = m4->GetData(); output[0] = mdata[0] * input0 + mdata[4] * input1 + mdata[8] * input2; output[1] = mdata[1] * input0 + mdata[5] * input1 + mdata[9] * input2; output[2] = mdata[2] * input0 + mdata[6] * input1 + mdata[10] * input2; } // useful for when the ImageData is not available but the information // spacing, origin, direction are void vtkImageData::TransformContinuousIndexToPhysicalPoint(double i, double j, double k, double const origin[3], double const spacing[3], double const direction[9], double xyz[3]) { for (int c = 0; c < 3; ++c) { xyz[c] = i * spacing[0] * direction[c * 3] + j * spacing[1] * direction[c * 3 + 1] + k * spacing[2] * direction[c * 3 + 2] + origin[c]; } } //------------------------------------------------------------------------------ void vtkImageData::TransformContinuousIndexToPhysicalPoint( double i, double j, double k, double xyz[3]) { TransformCoordinates(i, j, k, xyz, this->IndexToPhysicalMatrix); } //------------------------------------------------------------------------------ void vtkImageData::TransformContinuousIndexToPhysicalPoint(const double ijk[3], double xyz[3]) { TransformCoordinates(ijk[0], ijk[1], ijk[2], xyz, this->IndexToPhysicalMatrix); } //------------------------------------------------------------------------------ void vtkImageData::TransformIndexToPhysicalPoint(int i, int j, int k, double xyz[3]) { TransformCoordinates(i, j, k, xyz, this->IndexToPhysicalMatrix); } //------------------------------------------------------------------------------ void vtkImageData::TransformIndexToPhysicalPoint(const int ijk[3], double xyz[3]) { TransformCoordinates(ijk[0], ijk[1], ijk[2], xyz, this->IndexToPhysicalMatrix); } //------------------------------------------------------------------------------ void vtkImageData::TransformPhysicalPointToContinuousIndex( double x, double y, double z, double ijk[3]) { TransformCoordinates(x, y, z, ijk, this->PhysicalToIndexMatrix); } //------------------------------------------------------------------------------ void vtkImageData::TransformPhysicalPointToContinuousIndex(const double xyz[3], double ijk[3]) { TransformCoordinates(xyz[0], xyz[1], xyz[2], ijk, this->PhysicalToIndexMatrix); } //------------------------------------------------------------------------------ void vtkImageData::TransformPhysicalNormalToContinuousIndex(const double xyz[3], double ijk[3]) { TransformNormal(xyz[0], xyz[1], xyz[2], ijk, this->IndexToPhysicalMatrix); } //------------------------------------------------------------------------------ void vtkImageData::TransformPhysicalPlaneToContinuousIndex( double const normal[4], double xnormal[4]) { // transform the normal, note the inverse matrix is passed in TransformNormal( normal[0], normal[1], normal[2], xnormal, this->IndexToPhysicalMatrix); vtkMath::Normalize(xnormal); // transform the point double newPt[3]; TransformCoordinates(-normal[3] * normal[0], -normal[3] * normal[1], -normal[3] * normal[2], newPt, this->PhysicalToIndexMatrix); // recompute plane eqn xnormal[3] = -xnormal[0] * newPt[0] - xnormal[1] * newPt[1] - xnormal[2] * newPt[2]; } //------------------------------------------------------------------------------ void vtkImageData::ComputeTransforms() { this->DirectionMatrixIsIdentity = this->DirectionMatrix->IsIdentity(); vtkImageData::ComputeIndexToPhysicalMatrix(this->Origin, this->Spacing, this->DirectionMatrix->GetData(), this->IndexToPhysicalMatrix->GetData()); this->IndexToPhysicalMatrix->Modified(); vtkImageData::ComputePhysicalToIndexMatrix(this->Origin, this->Spacing, this->DirectionMatrix->GetData(), this->PhysicalToIndexMatrix->GetData()); this->PhysicalToIndexMatrix->Modified(); } //------------------------------------------------------------------------------ void vtkImageData::ComputeIndexToPhysicalMatrix( double const origin[3], double const spacing[3], double const direction[9], double result[16]) { for (int i = 0; i < 3; ++i) { result[i * 4] = direction[i * 3] * spacing[0]; result[i * 4 + 1] = direction[i * 3 + 1] * spacing[1]; result[i * 4 + 2] = direction[i * 3 + 2] * spacing[2]; } result[3] = origin[0]; result[7] = origin[1]; result[11] = origin[2]; result[12] = 0.0; result[13] = 0.0; result[14] = 0.0; result[15] = 1.0; } //------------------------------------------------------------------------------ void vtkImageData::ComputePhysicalToIndexMatrix( double const origin[3], double const spacing[3], double const direction[9], double result[16]) { double invDirection[9]; vtkMatrix3x3::Invert(direction, invDirection); double invOrigin[3] = { -origin[0], -origin[1], -origin[2] }; vtkMatrix3x3::MultiplyPoint(invDirection, invOrigin, invOrigin); for (int i = 0; i < 3; ++i) { if (spacing[i] != 0.0) { result[i * 4] = invDirection[i * 3] / spacing[i]; result[i * 4 + 1] = invDirection[i * 3 + 1] / spacing[i]; result[i * 4 + 2] = invDirection[i * 3 + 2] / spacing[i]; result[i * 4 + 3] = invOrigin[i] / spacing[i]; } else { // if spacing is zero, result is pseudoinverse of IndexToPhysicalMatrix result[i * 4] = 0.0; result[i * 4 + 1] = 0.0; result[i * 4 + 2] = 0.0; result[i * 4 + 3] = 0.0; } } result[12] = 0.0; result[13] = 0.0; result[14] = 0.0; result[15] = 1.0; } //------------------------------------------------------------------------------ void vtkImageData::ApplyIndexToPhysicalMatrix(vtkMatrix4x4* sourceIndexToPhysicalMatrix) { if (sourceIndexToPhysicalMatrix == nullptr) { vtkErrorMacro("Source IndexToPhysicalMatrix matrix is null"); return; } // Get origin, spacing, and direction from the source matrix double origin[3] = { sourceIndexToPhysicalMatrix->GetElement(0, 3), sourceIndexToPhysicalMatrix->GetElement(1, 3), sourceIndexToPhysicalMatrix->GetElement(2, 3) }; double directionMatrixElements[9]; double spacing[3]; for (int i = 0; i < 3; i++) { double direction[3] = { sourceIndexToPhysicalMatrix->GetElement(0, i), sourceIndexToPhysicalMatrix->GetElement(1, i), sourceIndexToPhysicalMatrix->GetElement(2, i) }; spacing[i] = vtkMath::Normalize(direction); directionMatrixElements[i] = direction[0]; directionMatrixElements[3 + i] = direction[1]; directionMatrixElements[6 + i] = direction[2]; } bool modified = false; if ((this->Origin[0] != origin[0]) || (this->Origin[1] != origin[1]) || (this->Origin[2] != origin[2])) { this->Origin[0] = origin[0]; this->Origin[1] = origin[1]; this->Origin[2] = origin[2]; modified = true; } if ((this->Spacing[0] != spacing[0]) || (this->Spacing[1] != spacing[1]) || (this->Spacing[2] != spacing[2])) { this->Spacing[0] = spacing[0]; this->Spacing[1] = spacing[1]; this->Spacing[2] = spacing[2]; modified = true; } bool directionMatrixModified = false; double* currentDirectionMatrixElements = this->DirectionMatrix->GetData(); for (int i = 0; i < 9; i++) { if (currentDirectionMatrixElements[i] != directionMatrixElements[i]) { currentDirectionMatrixElements[i] = directionMatrixElements[i]; directionMatrixModified = true; } } if (directionMatrixModified) { this->DirectionMatrix->Modified(); modified = true; } // Update everything with a single Modified() event if (modified) { this->ComputeTransforms(); this->BuildPoints(); this->Modified(); } } //------------------------------------------------------------------------------ void vtkImageData::ApplyPhysicalToIndexMatrix(vtkMatrix4x4* sourcePhysicalToIndexMatrix) { if (sourcePhysicalToIndexMatrix == nullptr) { vtkErrorMacro("Source PhysicalToIndexMatrix matrix is null"); return; } vtkNew indexToPhysicalMatrix; vtkMatrix4x4::Invert(sourcePhysicalToIndexMatrix, indexToPhysicalMatrix); this->ApplyIndexToPhysicalMatrix(indexToPhysicalMatrix); } //------------------------------------------------------------------------------ // Override this method because of blanking void vtkImageData::ComputeScalarRange() { if (this->GetMTime() > this->ScalarRangeComputeTime) { vtkDataArray* ptScalars = this->PointData->GetScalars(); vtkDataArray* cellScalars = this->CellData->GetScalars(); double ptRange[2]; double cellRange[2]; double s; ptRange[0] = VTK_DOUBLE_MAX; ptRange[1] = VTK_DOUBLE_MIN; if (ptScalars) { vtkIdType num = this->GetNumberOfPoints(); for (vtkIdType id = 0; id < num; ++id) { if (this->IsPointVisible(id)) { s = ptScalars->GetComponent(id, 0); if (!std::isnan(s)) { ptRange[0] = std::min(s, ptRange[0]); ptRange[1] = std::max(s, ptRange[1]); } } } } cellRange[0] = ptRange[0]; cellRange[1] = ptRange[1]; if (cellScalars) { vtkIdType num = this->GetNumberOfCells(); for (vtkIdType id = 0; id < num; ++id) { if (this->IsCellVisible(id)) { s = cellScalars->GetComponent(id, 0); if (!std::isnan(s)) { cellRange[0] = std::min(s, cellRange[0]); cellRange[1] = std::max(s, cellRange[1]); } } } } this->ScalarRange[0] = (cellRange[0] >= VTK_DOUBLE_MAX ? 0.0 : cellRange[0]); this->ScalarRange[1] = (cellRange[1] <= VTK_DOUBLE_MIN ? 1.0 : cellRange[1]); this->ScalarRangeComputeTime.Modified(); } } VTK_ABI_NAMESPACE_END