/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkTransform.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 "vtkTransform.h" #include "vtkMath.h" #include "vtkObjectFactory.h" #include vtkCxxRevisionMacro(vtkTransform, "$Revision: 1.106 $"); vtkStandardNewMacro(vtkTransform); //---------------------------------------------------------------------------- vtkTransform::vtkTransform() { this->Input = NULL; // most of the functionality is provided by the concatenation this->Concatenation = vtkTransformConcatenation::New(); // the stack will be allocated the first time Push is called this->Stack = NULL; // initialize the legacy 'Point' info this->Point[0] = this->Point[1] = this->Point[2] = this->Point[3] = 0.0; this->DoublePoint[0] = this->DoublePoint[1] = this->DoublePoint[2] = this->DoublePoint[3] = 0.0; // save the original matrix MTime as part of a hack to support legacy code this->MatrixUpdateMTime = this->Matrix->GetMTime(); } //---------------------------------------------------------------------------- vtkTransform::~vtkTransform() { this->SetInput(NULL); if (this->Concatenation) { this->Concatenation->Delete(); } if (this->Stack) { this->Stack->Delete(); } } //---------------------------------------------------------------------------- void vtkTransform::PrintSelf(ostream& os, vtkIndent indent) { this->Update(); this->Superclass::PrintSelf(os, indent); os << indent << "Input: (" << this->Input << ")\n"; os << indent << "InverseFlag: " << this->GetInverseFlag() << "\n"; os << indent << "NumberOfConcatenatedTransforms: " << this->GetNumberOfConcatenatedTransforms() << "\n"; if (this->GetNumberOfConcatenatedTransforms() != 0) { int n = this->GetNumberOfConcatenatedTransforms(); for (int i = 0; i < n; i++) { vtkLinearTransform *t = this->GetConcatenatedTransform(i); os << indent << " " << i << ": " << t->GetClassName() << " at " << t << "\n"; } } os << indent << "DoublePoint: " << "( " << this->DoublePoint[0] << ", " << this->DoublePoint[1] << ", " << this->DoublePoint[2] << ", " << this->DoublePoint[3] << ")\n"; os << indent << "Point: " << "( " << this->Point[0] << ", " << this->Point[1] << ", " << this->Point[2] << ", " << this->Point[3] << ")\n"; } //---------------------------------------------------------------------------- void vtkTransform::Identity() { this->Concatenation->Identity(); this->Modified(); // support for the legacy hack in InternalUpdate if (this->Matrix->GetMTime() > this->MatrixUpdateMTime) { this->Matrix->Identity(); } } //---------------------------------------------------------------------------- void vtkTransform::Inverse() { this->Concatenation->Inverse(); this->Modified(); // for the legacy hack in InternalUpdate if (this->Matrix->GetMTime() > this->MatrixUpdateMTime) { this->Matrix->Invert(); } } //---------------------------------------------------------------------------- void vtkTransform::InternalDeepCopy(vtkAbstractTransform *gtrans) { vtkTransform *transform = (vtkTransform *)gtrans; // copy the input this->SetInput(transform->Input); // copy the concatenation this->Concatenation->DeepCopy(transform->Concatenation); // copy the stack if (transform->Stack) { if (this->Stack == NULL) { this->Stack = vtkTransformConcatenationStack::New(); } this->Stack->DeepCopy(transform->Stack); } else { if (this->Stack) { this->Stack->Delete(); this->Stack = NULL; } } // legacy stuff: copy Point and DoublePoint for (int j = 0; j < 3; j++) { this->Point[j] = transform->Point[j]; this->DoublePoint[j] = transform->DoublePoint[j]; } // to support the legacy hack in InternalUpdate this->Matrix->DeepCopy(transform->Matrix); this->MatrixUpdateMTime = this->Matrix->GetMTime(); #ifndef VTK_LEGACY_REMOVE if (transform->Matrix->GetMTime() > transform->MatrixUpdateMTime) { // this copies the legacy hack flag to the transform vtkWarningMacro(<<"InternalDeepCopy: Legacy Hack deprecated in VTK 4.2. May be removed in a future version."); this->MatrixUpdateMTime--; } #endif } //---------------------------------------------------------------------------- void vtkTransform::InternalUpdate() { int i; int nTransforms = this->Concatenation->GetNumberOfTransforms(); int nPreTransforms = this->Concatenation->GetNumberOfPreTransforms(); // check to see whether someone has been fooling around with our matrix int doTheLegacyHack = 0; if (this->Matrix->GetMTime() > this->MatrixUpdateMTime) { vtkDebugMacro(<<"InternalUpdate: this->Matrix was modified by something other than 'this'"); // check to see if we have any inputs or concatenated transforms int isPipelined = (this->Input != 0); for (i = 0; i < nTransforms && !isPipelined; i++) { // the vtkSimpleTransform is just a matrix placeholder, // it is not a real transform isPipelined = !this->Concatenation->GetTransform(i)->IsA("vtkSimpleTransform"); } // do the legacy hack only if we have no input transforms doTheLegacyHack = !isPipelined; } // copy matrix from input if (this->Input) { this->Matrix->DeepCopy(this->Input->GetMatrix()); // if inverse flag is set, invert the matrix if (this->Concatenation->GetInverseFlag()) { this->Matrix->Invert(); } } else if (doTheLegacyHack) { vtkWarningMacro("InternalUpdate: doing hack to support legacy code. " "This is deprecated in VTK 4.2. May be removed in a " "future version."); // this heuristic works perfectly if GetMatrix() or GetMatrixPointer() // was called immediately prior to the matrix modifications // (fortunately, this is almost always the case) if (this->Matrix->GetMTime() > this->Concatenation->GetMaxMTime()) { // don't apply operations that occurred after matrix modification nPreTransforms = nTransforms = 0; } } else { // otherwise, we start with the identity transform as our base this->Matrix->Identity(); } // concatenate PreTransforms for (i = nPreTransforms-1; i >= 0; i--) { vtkHomogeneousTransform *transform = (vtkHomogeneousTransform *)this->Concatenation->GetTransform(i); vtkMatrix4x4::Multiply4x4(this->Matrix,transform->GetMatrix(), this->Matrix); } // concatenate PostTransforms for (i = nPreTransforms; i < nTransforms; i++) { vtkHomogeneousTransform *transform = (vtkHomogeneousTransform *)this->Concatenation->GetTransform(i); vtkMatrix4x4::Multiply4x4(transform->GetMatrix(),this->Matrix, this->Matrix); } if (doTheLegacyHack) { // the transform operations have been incorporated into the matrix, // so delete them this->Concatenation->Identity(); } else { // having this in the 'else' forces the legacy flag to be sticky this->MatrixUpdateMTime = this->Matrix->GetMTime(); } } //---------------------------------------------------------------------------- void vtkTransform::Concatenate(vtkLinearTransform *transform) { if (transform->CircuitCheck(this)) { vtkErrorMacro("Concatenate: this would create a circular reference."); return; } this->Concatenation->Concatenate(transform); this->Modified(); } //---------------------------------------------------------------------------- void vtkTransform::SetInput(vtkLinearTransform *input) { if (this->Input == input) { return; } if (input && input->CircuitCheck(this)) { vtkErrorMacro("SetInput: this would create a circular reference."); return; } if (this->Input) { this->Input->Delete(); } this->Input = input; if (this->Input) { this->Input->Register(this); } this->Modified(); } //---------------------------------------------------------------------------- int vtkTransform::CircuitCheck(vtkAbstractTransform *transform) { if (this->vtkLinearTransform::CircuitCheck(transform) || this->Input && this->Input->CircuitCheck(transform)) { return 1; } int n = this->Concatenation->GetNumberOfTransforms(); for (int i = 0; i < n; i++) { if (this->Concatenation->GetTransform(i)->CircuitCheck(transform)) { return 1; } } return 0; } //---------------------------------------------------------------------------- vtkAbstractTransform *vtkTransform::MakeTransform() { return vtkTransform::New(); } //---------------------------------------------------------------------------- unsigned long vtkTransform::GetMTime() { unsigned long mtime = this->vtkLinearTransform::GetMTime(); unsigned long mtime2; // checking the matrix MTime is part of the legacy hack in InternalUpdate if ((mtime2 = this->Matrix->GetMTime()) > this->MatrixUpdateMTime) { if (mtime2 > mtime) { mtime = mtime2; } } if (this->Input) { mtime2 = this->Input->GetMTime(); if (mtime2 > mtime) { mtime = mtime2; } } mtime2 = this->Concatenation->GetMaxMTime(); if (mtime2 > mtime) { return mtime2; } return mtime; } //---------------------------------------------------------------------------- // Get the x, y, z orientation angles from the transformation matrix as an // array of three floating point values. void vtkTransform::GetOrientation(double orientation[3], vtkMatrix4x4 *amatrix) { #define VTK_AXIS_EPSILON 0.001 int i; // convenient access to matrix double (*matrix)[4] = amatrix->Element; double ortho[3][3]; for (i = 0; i < 3; i++) { ortho[0][i] = matrix[0][i]; ortho[1][i] = matrix[1][i]; ortho[2][i] = matrix[2][i]; } if (vtkMath::Determinant3x3(ortho) < 0) { ortho[0][2] = -ortho[0][2]; ortho[1][2] = -ortho[1][2]; ortho[2][2] = -ortho[2][2]; } vtkMath::Orthogonalize3x3(ortho, ortho); // first rotate about y axis double x2 = ortho[2][0]; double y2 = ortho[2][1]; double z2 = ortho[2][2]; double x3 = ortho[1][0]; double y3 = ortho[1][1]; double z3 = ortho[1][2]; double d1 = sqrt(x2*x2 + z2*z2); double cosTheta, sinTheta; if (d1 < VTK_AXIS_EPSILON) { cosTheta = 1.0; sinTheta = 0.0; } else { cosTheta = z2/d1; sinTheta = x2/d1; } double theta = atan2(sinTheta, cosTheta); orientation[1] = -theta/vtkMath::DoubleDegreesToRadians(); // now rotate about x axis double d = sqrt(x2*x2 + y2*y2 + z2*z2); double sinPhi, cosPhi; if (d < VTK_AXIS_EPSILON) { sinPhi = 0.0; cosPhi = 1.0; } else if (d1 < VTK_AXIS_EPSILON) { sinPhi = y2/d; cosPhi = z2/d; } else { sinPhi = y2/d; cosPhi = (x2*x2 + z2*z2)/(d1*d); } double phi = atan2(sinPhi, cosPhi); orientation[0] = phi/vtkMath::DoubleDegreesToRadians(); // finally, rotate about z double x3p = x3*cosTheta - z3*sinTheta; double y3p = - sinPhi*sinTheta*x3 + cosPhi*y3 - sinPhi*cosTheta*z3; double d2 = sqrt(x3p*x3p + y3p*y3p); double cosAlpha, sinAlpha; if (d2 < VTK_AXIS_EPSILON) { cosAlpha = 1.0; sinAlpha = 0.0; } else { cosAlpha = y3p/d2; sinAlpha = x3p/d2; } double alpha = atan2(sinAlpha, cosAlpha); orientation[2] = alpha/vtkMath::DoubleDegreesToRadians(); } //---------------------------------------------------------------------------- // Get the x, y, z orientation angles from the transformation matrix as an // array of three floating point values. void vtkTransform::GetOrientation(double orientation[3]) { this->Update(); this->GetOrientation(orientation, this->Matrix); } //---------------------------------------------------------------------------- // vtkTransform::GetOrientationWXYZ void vtkTransform::GetOrientationWXYZ(double wxyz[4]) { int i; this->Update(); // convenient access to matrix double (*matrix)[4] = this->Matrix->Element; double ortho[3][3]; for (i = 0; i < 3; i++) { ortho[0][i] = matrix[0][i]; ortho[1][i] = matrix[1][i]; ortho[2][i] = matrix[2][i]; } if (vtkMath::Determinant3x3(ortho) < 0) { ortho[0][i] = -ortho[0][i]; ortho[1][i] = -ortho[1][i]; ortho[2][i] = -ortho[2][i]; } vtkMath::Matrix3x3ToQuaternion(ortho, wxyz); // calc the return value wxyz double mag = sqrt(wxyz[1]*wxyz[1] + wxyz[2]*wxyz[2] + wxyz[3]*wxyz[3]); if (mag) { wxyz[0] = 2.0*acos(wxyz[0])/vtkMath::DoubleDegreesToRadians(); wxyz[1] /= mag; wxyz[2] /= mag; wxyz[3] /= mag; } else { wxyz[0] = 0.0; wxyz[1] = 0.0; wxyz[2] = 0.0; wxyz[3] = 1.0; } } //---------------------------------------------------------------------------- // Return the position from the current transformation matrix as an array // of three floating point numbers. This is simply returning the translation // component of the 4x4 matrix. void vtkTransform::GetPosition(double position[3]) { this->Update(); position[0] = this->Matrix->Element[0][3]; position[1] = this->Matrix->Element[1][3]; position[2] = this->Matrix->Element[2][3]; } //---------------------------------------------------------------------------- // Return the x, y, z scale factors of the current transformation matrix as // an array of three float numbers. void vtkTransform::GetScale(double scale[3]) { this->Update(); // convenient access to matrix double (*matrix)[4] = this->Matrix->Element; double U[3][3], VT[3][3]; for (int i = 0; i < 3; i++) { U[0][i] = matrix[0][i]; U[1][i] = matrix[1][i]; U[2][i] = matrix[2][i]; } vtkMath::SingularValueDecomposition3x3(U, U, scale, VT); } //---------------------------------------------------------------------------- // Return the inverse of the current transformation matrix. void vtkTransform::GetInverse(vtkMatrix4x4 *inverse) { vtkMatrix4x4::Invert(this->GetMatrix(),inverse); } //---------------------------------------------------------------------------- // Obtain the transpose of the current transformation matrix. void vtkTransform::GetTranspose(vtkMatrix4x4 *transpose) { vtkMatrix4x4::Transpose(this->GetMatrix(),transpose); }