/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkTensorGlyph.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 "vtkTensorGlyph.h" #include "vtkCell.h" #include "vtkCellArray.h" #include "vtkDataSet.h" #include "vtkExecutive.h" #include "vtkFloatArray.h" #include "vtkMath.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkPolyData.h" #include "vtkTransform.h" vtkCxxRevisionMacro(vtkTensorGlyph, "$Revision: 1.57.12.1 $"); vtkStandardNewMacro(vtkTensorGlyph); // Construct object with scaling on and scale factor 1.0. Eigenvalues are // extracted, glyphs are colored with input scalar data, and logarithmic // scaling is turned off. vtkTensorGlyph::vtkTensorGlyph() { this->Scaling = 1; this->ScaleFactor = 1.0; this->ExtractEigenvalues = 1; this->ColorGlyphs = 1; this->ColorMode = COLOR_BY_SCALARS; this->ClampScaling = 0; this->MaxScaleFactor = 100; this->ThreeGlyphs = 0; this->Symmetric = 0; this->Length = 1.0; this->SetNumberOfInputPorts(2); } vtkTensorGlyph::~vtkTensorGlyph() { } int vtkTensorGlyph::RequestData( vtkInformation *vtkNotUsed(request), vtkInformationVector **inputVector, vtkInformationVector *outputVector) { // get the info objects vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); vtkInformation *sourceInfo = inputVector[1]->GetInformationObject(0); vtkInformation *outInfo = outputVector->GetInformationObject(0); // get the input and ouptut vtkDataSet *input = vtkDataSet::SafeDownCast( inInfo->Get(vtkDataObject::DATA_OBJECT())); vtkPolyData *source = vtkPolyData::SafeDownCast( sourceInfo->Get(vtkDataObject::DATA_OBJECT())); vtkPolyData *output = vtkPolyData::SafeDownCast( outInfo->Get(vtkDataObject::DATA_OBJECT())); vtkDataArray *inTensors; double tensor[9]; vtkDataArray *inScalars; vtkIdType numPts, numSourcePts, numSourceCells, inPtId, i; int j; vtkPoints *sourcePts; vtkDataArray *sourceNormals; vtkCellArray *sourceCells, *cells; vtkPoints *newPts; vtkFloatArray *newScalars=NULL; vtkFloatArray *newNormals=NULL; double x[3], s; vtkTransform *trans; vtkCell *cell; vtkIdList *cellPts; int npts; vtkIdType *pts; vtkIdType ptIncr, cellId; vtkIdType subIncr; int numDirs, dir, eigen_dir, symmetric_dir; vtkMatrix4x4 *matrix; double *m[3], w[3], *v[3]; double m0[3], m1[3], m2[3]; double v0[3], v1[3], v2[3]; double xv[3], yv[3], zv[3]; double maxScale; vtkPointData *pd, *outPD; numDirs = (this->ThreeGlyphs?3:1)*(this->Symmetric+1); pts = new vtkIdType[source->GetMaxCellSize()]; trans = vtkTransform::New(); matrix = vtkMatrix4x4::New(); // set up working matrices m[0] = m0; m[1] = m1; m[2] = m2; v[0] = v0; v[1] = v1; v[2] = v2; vtkDebugMacro(<<"Generating tensor glyphs"); pd = input->GetPointData(); outPD = output->GetPointData(); inTensors = pd->GetTensors(); inScalars = pd->GetScalars(); numPts = input->GetNumberOfPoints(); if ( !inTensors || numPts < 1 ) { vtkErrorMacro(<<"No data to glyph!"); return 1; } // // Allocate storage for output PolyData // sourcePts = source->GetPoints(); numSourcePts = sourcePts->GetNumberOfPoints(); numSourceCells = source->GetNumberOfCells(); newPts = vtkPoints::New(); newPts->Allocate(numDirs*numPts*numSourcePts); // Setting up for calls to PolyData::InsertNextCell() if ( (sourceCells=source->GetVerts())->GetNumberOfCells() > 0 ) { cells = vtkCellArray::New(); cells->Allocate(numDirs*numPts*sourceCells->GetSize()); output->SetVerts(cells); cells->Delete(); } if ( (sourceCells=this->GetSource()->GetLines())->GetNumberOfCells() > 0 ) { cells = vtkCellArray::New(); cells->Allocate(numDirs*numPts*sourceCells->GetSize()); output->SetLines(cells); cells->Delete(); } if ( (sourceCells=this->GetSource()->GetPolys())->GetNumberOfCells() > 0 ) { cells = vtkCellArray::New(); cells->Allocate(numDirs*numPts*sourceCells->GetSize()); output->SetPolys(cells); cells->Delete(); } if ( (sourceCells=this->GetSource()->GetStrips())->GetNumberOfCells() > 0 ) { cells = vtkCellArray::New(); cells->Allocate(numDirs*numPts*sourceCells->GetSize()); output->SetStrips(cells); cells->Delete(); } // only copy scalar data through pd = this->GetSource()->GetPointData(); // generate scalars if eigenvalues are chosen or if scalars exist. if (this->ColorGlyphs && ((this->ColorMode == COLOR_BY_EIGENVALUES) || (inScalars && (this->ColorMode == COLOR_BY_SCALARS)) ) ) { newScalars = vtkFloatArray::New(); newScalars->Allocate(numDirs*numPts*numSourcePts); } else { outPD->CopyAllOff(); outPD->CopyScalarsOn(); outPD->CopyAllocate(pd,numDirs*numPts*numSourcePts); } if ( (sourceNormals = pd->GetNormals()) ) { newNormals = vtkFloatArray::New(); newNormals->SetNumberOfComponents(3); newNormals->Allocate(numDirs*3*numPts*numSourcePts); } // // First copy all topology (transformation independent) // for (inPtId=0; inPtId < numPts; inPtId++) { ptIncr = numDirs * inPtId * numSourcePts; for (cellId=0; cellId < numSourceCells; cellId++) { cell = this->GetSource()->GetCell(cellId); cellPts = cell->GetPointIds(); npts = cellPts->GetNumberOfIds(); for (dir=0; dir < numDirs; dir++) { // This variable may be removed, but that // will not improve readability subIncr = ptIncr + dir*numSourcePts; for (i=0; i < npts; i++) { pts[i] = cellPts->GetId(i) + subIncr; } output->InsertNextCell(cell->GetCellType(),npts,pts); } } } // // Traverse all Input points, transforming glyph at Source points // trans->PreMultiply(); for (inPtId=0; inPtId < numPts; inPtId++) { ptIncr = numDirs * inPtId * numSourcePts; // Translation is postponed inTensors->GetTuple(inPtId, tensor); // compute orientation vectors and scale factors from tensor if ( this->ExtractEigenvalues ) // extract appropriate eigenfunctions { for (j=0; j<3; j++) { for (i=0; i<3; i++) { m[i][j] = tensor[i+3*j]; } } vtkMath::Jacobi(m, w, v); //copy eigenvectors xv[0] = v[0][0]; xv[1] = v[1][0]; xv[2] = v[2][0]; yv[0] = v[0][1]; yv[1] = v[1][1]; yv[2] = v[2][1]; zv[0] = v[0][2]; zv[1] = v[1][2]; zv[2] = v[2][2]; } else //use tensor columns as eigenvectors { for (i=0; i<3; i++) { xv[i] = tensor[i]; yv[i] = tensor[i+3]; zv[i] = tensor[i+6]; } w[0] = vtkMath::Normalize(xv); w[1] = vtkMath::Normalize(yv); w[2] = vtkMath::Normalize(zv); } // compute scale factors w[0] *= this->ScaleFactor; w[1] *= this->ScaleFactor; w[2] *= this->ScaleFactor; if ( this->ClampScaling ) { for (maxScale=0.0, i=0; i<3; i++) { if ( maxScale < fabs(w[i]) ) { maxScale = fabs(w[i]); } } if ( maxScale > this->MaxScaleFactor ) { maxScale = this->MaxScaleFactor / maxScale; for (i=0; i<3; i++) { w[i] *= maxScale; //preserve overall shape of glyph } } } // normalization is postponed // make sure scale is okay (non-zero) and scale data for (maxScale=0.0, i=0; i<3; i++) { if ( w[i] > maxScale ) { maxScale = w[i]; } } if ( maxScale == 0.0 ) { maxScale = 1.0; } for (i=0; i<3; i++) { if ( w[i] == 0.0 ) { w[i] = maxScale * 1.0e-06; } } // Now do the real work for each "direction" for (dir=0; dir < numDirs; dir++) { eigen_dir = dir%(this->ThreeGlyphs?3:1); symmetric_dir = dir/(this->ThreeGlyphs?3:1); // Remove previous scales ... trans->Identity(); // translate Source to Input point input->GetPoint(inPtId, x); trans->Translate(x[0], x[1], x[2]); // normalized eigenvectors rotate object for eigen direction 0 matrix->Element[0][0] = xv[0]; matrix->Element[0][1] = yv[0]; matrix->Element[0][2] = zv[0]; matrix->Element[1][0] = xv[1]; matrix->Element[1][1] = yv[1]; matrix->Element[1][2] = zv[1]; matrix->Element[2][0] = xv[2]; matrix->Element[2][1] = yv[2]; matrix->Element[2][2] = zv[2]; trans->Concatenate(matrix); if (eigen_dir == 1) { trans->RotateZ(90.0); } if (eigen_dir == 2) { trans->RotateY(-90.0); } if (this->ThreeGlyphs) { trans->Scale(w[eigen_dir], this->ScaleFactor, this->ScaleFactor); } else { trans->Scale(w[0], w[1], w[2]); } // Mirror second set to the symmetric position if (symmetric_dir == 1) { trans->Scale(-1.,1.,1.); } // if the eigenvalue is negative, shift to reverse direction. // The && is there to ensure that we do not change the // old behaviour of vtkTensorGlyphs (which only used one dir), // in case there is an oriented glyph, e.g. an arrow. if (w[eigen_dir] < 0 && numDirs > 1) { trans->Translate(-this->Length, 0., 0.); } // multiply points (and normals if available) by resulting // matrix trans->TransformPoints(sourcePts,newPts); // Apply the transformation to a series of points, // and append the results to outPts. if ( newNormals ) { trans->TransformNormals(sourceNormals,newNormals); } // Copy point data from source if ( this->ColorGlyphs && inScalars && (this->ColorMode == COLOR_BY_SCALARS) ) { s = inScalars->GetComponent(inPtId, 0); for (i=0; i < numSourcePts; i++) { newScalars->InsertTuple(ptIncr+i, &s); } } else if (this->ColorGlyphs && (this->ColorMode == COLOR_BY_EIGENVALUES) ) { // If ThreeGlyphs is false we use the first (largest) // eigenvalue as scalar. s = w[eigen_dir]; for (i=0; i < numSourcePts; i++) { newScalars->InsertTuple(ptIncr+i, &s); } } else { for (i=0; i < numSourcePts; i++) { outPD->CopyData(pd,i,ptIncr+i); } } ptIncr += numSourcePts; } } vtkDebugMacro(<<"Generated " << numPts <<" tensor glyphs"); // // Update output and release memory // delete [] pts; output->SetPoints(newPts); newPts->Delete(); if ( newScalars ) { int idx = outPD->AddArray(newScalars); outPD->SetActiveAttribute(idx, vtkDataSetAttributes::SCALARS); newScalars->Delete(); } if ( newNormals ) { outPD->SetNormals(newNormals); newNormals->Delete(); } output->Squeeze(); trans->Delete(); matrix->Delete(); return 1; } void vtkTensorGlyph::SetSourceConnection(int id, vtkAlgorithmOutput* algOutput) { if (id < 0) { vtkErrorMacro("Bad index " << id << " for source."); return; } int numConnections = this->GetNumberOfInputConnections(1); if (id < numConnections) { this->SetNthInputConnection(1, id, algOutput); } else if (id == numConnections && algOutput) { this->AddInputConnection(1, algOutput); } else if (algOutput) { vtkWarningMacro("The source id provided is larger than the maximum " "source id, using " << numConnections << " instead."); this->AddInputConnection(1, algOutput); } } void vtkTensorGlyph::SetSource(vtkPolyData *source) { this->SetInput(1, source); } vtkPolyData *vtkTensorGlyph::GetSource() { if (this->GetNumberOfInputConnections(1) < 1) { return NULL; } return vtkPolyData::SafeDownCast(this->GetExecutive()->GetInputData(1, 0)); } int vtkTensorGlyph::FillInputPortInformation(int port, vtkInformation *info) { if (port == 1) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData"); return 1; } info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet"); return 1; } void vtkTensorGlyph::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); os << indent << "Source: " << this->GetSource() << "\n"; os << indent << "Scaling: " << (this->Scaling ? "On\n" : "Off\n"); os << indent << "Scale Factor: " << this->ScaleFactor << "\n"; os << indent << "Extract Eigenvalues: " << (this->ExtractEigenvalues ? "On\n" : "Off\n"); os << indent << "Color Glyphs: " << (this->ColorGlyphs ? "On\n" : "Off\n"); os << indent << "Color Mode: " << this->ColorMode << endl; os << indent << "Clamp Scaling: " << (this->ClampScaling ? "On\n" : "Off\n"); os << indent << "Max Scale Factor: " << this->MaxScaleFactor << "\n"; os << indent << "Three Glyphs: " << (this->ThreeGlyphs ? "On\n" : "Off\n"); os << indent << "Symmetric: " << (this->Symmetric ? "On\n" : "Off\n"); os << indent << "Length: " << this->Length << "\n"; }