/*========================================================================= medInria Copyright (c) INRIA 2013 - 2020. All rights reserved. See LICENSE.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =========================================================================*/ #include #include #include #include #include #include #include #include #include #include "vtkSphericalHarmonicGlyph.h" vtkStandardNewMacro(vtkSphericalHarmonicGlyph) // Function taken from 3D Slicer, SuperquadricTensorGlyph // // This is sort of the inverse of code from Gordon Kindlmann for mapping // the mode (index value) to RGB. See vtkTensorMathematics for that code. // There may be a simpler way to do this but this works. // Note this expects a "0 1" Hue Range in the vtkLookupTable used to // display the glyphs. static void RGBToIndex(double R,double G,double B,double &index) { // remove the gray part of the color. // this is so we can use the model where either R,G, or B is 0. // then we scale so that the max of the other two is one. double min = R; int minIdx = 0; if (Gmax) { max = G; maxIdx = 1; } if (B>max) { max = B; maxIdx = 2; } if (max!=0.0) { R /= max; G /= max; B /= max; } int sextant = 0; if (maxIdx==0 && minIdx==2) sextant = 0; if (maxIdx==1 && minIdx==2) sextant = 1; if (maxIdx==1 && minIdx==0) sextant = 2; if (maxIdx==2 && minIdx==0) sextant = 3; if (maxIdx==2 && minIdx==1) sextant = 4; if (maxIdx==0 && minIdx==1) sextant = 5; const double offset = 256/6; switch (sextant) { case 0: { index = G*offset; break; } case 1: { index = offset+(1-R)*offset; break; } case 2: { index = offset*2+B*offset; break; } case 3: { index = offset*3+(1-G)*offset; break; } case 4: { index = offset*4+R*offset; break; } case 5: { index = offset*5+(1-B)*offset; break; } } } vtkSphericalHarmonicGlyph::vtkSphericalHarmonicGlyph() { ScaleFactor = 1.0; ColorGlyphs = 1; ColorMode = COLOR_BY_SCALARS; SetNumberOfInputPorts(2); SphericalHarmonicSource = nullptr; TMatrix = nullptr; } vtkSphericalHarmonicGlyph::~vtkSphericalHarmonicGlyph() { if(TMatrix) TMatrix->Delete(); if (SphericalHarmonicSource) SphericalHarmonicSource->UnRegister(this); } int vtkSphericalHarmonicGlyph::RequestData(vtkInformation*,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); const int numDirs = 1; // Get the input. vtkDataSet* input = vtkDataSet::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT())); const vtkIdType numPts = input->GetNumberOfPoints(); //number of points in the data if (!numPts || numPts < 0) { vtkErrorMacro(<<"No data to glyph!"); return 1; } vtkDebugMacro(<<"Generating spherical harmonic glyphs"); vtkPointData* pd = input->GetPointData(); vtkDataArray* inScalars = pd->GetScalars(GetSphericalHarmonicCoefficientsArrayName()); vtkDataArray* inAniso = pd->GetScalars(GetAnisotropyMeasureArrayName()); // Allocate storage for output PolyData vtkPolyData* source = vtkPolyData::SafeDownCast(sourceInfo->Get(vtkDataObject::DATA_OBJECT())); vtkPoints* sourcePts = source->GetPoints(); //shell // Number of points on the shell const vtkIdType numSourcePts = sourcePts->GetNumberOfPoints(); if (!numSourcePts || numSourcePts < 0) { vtkErrorMacro(<<"No data to glyph!"); return 1; } // Number of cells is the number of triangles in shell const vtkIdType numSourceCells = source->GetNumberOfCells(); const unsigned newpts_sz = numDirs*numPts*numSourcePts; vtkPoints* newPts = vtkPoints::New(); newPts->Allocate(newpts_sz); vtkPointData* newPointData = vtkPointData::New(); newPointData->Allocate(newpts_sz); // Setting up for calls to PolyData::InsertNextCell() vtkPolyData* output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); vtkPointData* outPD = output->GetPointData(); vtkCellArray* sourceCells; if ((sourceCells=source->GetVerts())->GetNumberOfCells()>0) { vtkCellArray* cells = vtkCellArray::New(); cells->Allocate(numDirs*numPts*sourceCells->GetSize()); output->SetVerts(cells); cells->Delete(); } if ((sourceCells=GetSource()->GetLines())->GetNumberOfCells()>0) { vtkCellArray* cells = vtkCellArray::New(); cells->Allocate(numDirs*numPts*sourceCells->GetSize()); output->SetLines(cells); cells->Delete(); } if ((sourceCells=GetSource()->GetPolys())->GetNumberOfCells()>0) { vtkCellArray* cells = vtkCellArray::New(); cells->Allocate(numDirs*numPts*sourceCells->GetSize()); output->SetPolys(cells); cells->Delete(); } if ((sourceCells=GetSource()->GetStrips())->GetNumberOfCells()>0) { vtkCellArray* cells = vtkCellArray::New(); cells->Allocate(numDirs*numPts*sourceCells->GetSize()); output->SetStrips(cells); cells->Delete(); } vtkFloatArray* newPointScalars = vtkFloatArray::New(); newPointScalars->Allocate(numDirs*numPts*numSourcePts); // Only copy scalar data through. pd = GetSource()->GetPointData(); vtkFloatArray* newScalars = 0; if (ColorGlyphs && (ColorMode==COLOR_BY_DIRECTIONS || (inAniso && ColorMode==COLOR_BY_SCALARS))) { newScalars = vtkFloatArray::New(); newScalars->Allocate(numDirs*numPts*numSourcePts); } else { outPD->CopyAllOff(); outPD->CopyScalarsOn(); outPD->CopyAllocate(pd,numDirs*numPts*numSourcePts); } if (pd->GetNormals()) { vtkFloatArray* newNormals = vtkFloatArray::New(); newNormals->SetNumberOfComponents(3); newNormals->Allocate(numDirs*3*numPts*numSourcePts); newNormals->Delete(); } // First copy all topology (transformation independent) for (vtkIdType inPtId=0,inPtIdReal=0;inPtIdGetComponent(inPtId,0)!=0) { const vtkIdType ptIncr = numDirs*inPtIdReal*numSourcePts; for (vtkIdType cellId=0;cellIdGetCell(cellId); vtkIdList* cellPts = cell->GetPointIds(); const int npts = cellPts->GetNumberOfIds(); vtkIdType* pts = new vtkIdType[npts]; for (int dir=0;dirGetId(i)+subIncr; output->InsertNextCell(cell->GetCellType(),npts,pts); } delete[] pts; } inPtIdReal++; } // Traverse all Input points, transforming glyph at Source points vtkTransform* trans = vtkTransform::New(); trans->PreMultiply(); double x[4]; for (vtkIdType inPtId=0,inPtIdReal=0;inPtId(inPtId)/numPts); if (GetAbortExecute()) break; } if (inAniso->GetComponent(inPtId,0)) { const vtkIdType ptIncr = numDirs*inPtIdReal*numSourcePts; // Get the Spherical Harmonic vector. double* sh = new double[inScalars->GetNumberOfComponents()]; inScalars->GetTuple(inPtId,sh); // Set harmonics and compute spherical function. SphericalHarmonicSource->SetSphericalHarmonics(sh); SphericalHarmonicSource->Update(); vtkPointData* sourcePointData = SphericalHarmonicSource->GetOutput()->GetPointData(); vtkPoints* deformPts = SphericalHarmonicSource->GetOutput()->GetPoints(); trans->SetMatrix( TMatrix); input->GetPoint(inPtId,x); trans->Translate(x[0],x[1],x[2]); trans->Scale(ScaleFactor,ScaleFactor,ScaleFactor); // Translate the deform pt to the correct x,y,z location trans->TransformPoints(deformPts,newPts); // This is for spherical values coloring // Need to set them at the right place in the list of all points for (int z=0;zInsertTuple(ptIncr+z,sourcePointData->GetScalars()->GetTuple(z)); if (ColorGlyphs) { // Scalar color for anisotropy : one color per shell if (inAniso && ColorMode==COLOR_BY_SCALARS) { const double s = inAniso->GetComponent(inPtId,0); for (vtkIdType i=0;iInsertTuple(ptIncr+i,&s); } // RGB color : color at every point of the spherical function if (ColorMode==COLOR_BY_DIRECTIONS) { for (vtkIdType i=0;iGetOutput()->GetPoints()->GetPoint(i,vect); RGBToIndex(fabs(vect[0]),fabs(vect[1]),fabs(vect[2]),s); newScalars->InsertTuple(ptIncr+i,&s); } } } inPtIdReal++; delete[] sh; } } output->SetPoints(newPts); // Assigning color to PointData if (newScalars) { newScalars->SetName((ColorMode==COLOR_BY_SCALARS) ? GetAnisotropyMeasureArrayName() : GetRGBArrayName()); const int idx = outPD->AddArray(newScalars); outPD->SetActiveAttribute(idx,vtkDataSetAttributes::SCALARS); newScalars->Delete(); } // Assigning spherical values if (newPointScalars) { newPointScalars->SetName(GetSphericalHarmonicValuesArrayName()); newPointScalars->Delete(); } newPts->Delete(); newPointData->Delete(); trans->Delete(); return 1; } void vtkSphericalHarmonicGlyph::SetSourceConnection(int id,vtkAlgorithmOutput* algOutput) { if (id<0) { vtkErrorMacro("Bad index " << id << " for source."); return; } const int numConnections = GetNumberOfInputConnections(1); if (idGetInputData(1,0)); } int vtkSphericalHarmonicGlyph::FillInputPortInformation(int port,vtkInformation* info) { const char* type = (port==1) ? "vtkPolyData" : "vtkDataSet"; info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(),type); return 1; } void vtkSphericalHarmonicGlyph::PrintSelf(ostream& os,vtkIndent indent) { Superclass::PrintSelf(os,indent); os << indent << "Source: " << GetSource() << "\n"; os << indent << "Scale Factor: " << ScaleFactor << "\n"; os << indent << "Color Glyphs: " << (ColorGlyphs ? "On\n" : "Off\n"); os << indent << "Color Mode: " << ColorMode << endl; }