/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkXMLUnstructuredDataReader.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 "vtkXMLUnstructuredDataReader.h" #include "vtkObjectFactory.h" #include "vtkXMLDataElement.h" #include "vtkPoints.h" #include "vtkIdTypeArray.h" #include "vtkUnsignedCharArray.h" #include "vtkCellArray.h" #include "vtkPointSet.h" #include "vtkInformation.h" #include "vtkStreamingDemandDrivenPipeline.h" #include vtkCxxRevisionMacro(vtkXMLUnstructuredDataReader, "$Revision: 1.27 $"); //---------------------------------------------------------------------------- vtkXMLUnstructuredDataReader::vtkXMLUnstructuredDataReader() { this->PointElements = 0; this->NumberOfPoints = 0; this->TotalNumberOfPoints = 0; this->TotalNumberOfCells = 0; this->PointsTimeStep = -1; //invalid state this->PointsOffset = (unsigned long)-1; } //---------------------------------------------------------------------------- vtkXMLUnstructuredDataReader::~vtkXMLUnstructuredDataReader() { if(this->NumberOfPieces) { this->DestroyPieces(); } } //---------------------------------------------------------------------------- void vtkXMLUnstructuredDataReader::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); } //---------------------------------------------------------------------------- vtkPointSet* vtkXMLUnstructuredDataReader::GetOutputAsPointSet() { return vtkPointSet::SafeDownCast( this->GetOutputDataObject(0) ); } //---------------------------------------------------------------------------- vtkXMLDataElement* vtkXMLUnstructuredDataReader ::FindDataArrayWithName(vtkXMLDataElement* eParent, const char* name) { // Find a nested element that represents a data array with the given name. // and proper TimeStep int i; for(i=0;i < eParent->GetNumberOfNestedElements(); ++i) { vtkXMLDataElement* eNested = eParent->GetNestedElement(i); if(strcmp(eNested->GetName(), "DataArray") == 0) { const char* aName = eNested->GetAttribute("Name"); if(aName && (strcmp(aName, name) == 0)) { int numTimeSteps = eNested->GetVectorAttribute("TimeStep", this->NumberOfTimeSteps, this->TimeSteps); assert( numTimeSteps <= this->NumberOfTimeSteps ); // Check if CurrentTimeStep is in the array and particular field is also: int isCurrentTimeInArray = vtkXMLReader::IsTimeStepInArray(this->CurrentTimeStep, this->TimeSteps, numTimeSteps); // If no time is specified or if time is specified and match then read if( !numTimeSteps || isCurrentTimeInArray ) { return eNested; } } } } return 0; } //---------------------------------------------------------------------------- template void vtkXMLUnstructuredDataReaderCopyArray(TIn* in, TOut* out, vtkIdType length) { for(vtkIdType i = 0; i < length; ++i) { out[i] = static_cast(in[i]); } } //---------------------------------------------------------------------------- vtkIdTypeArray* vtkXMLUnstructuredDataReader::ConvertToIdTypeArray(vtkDataArray* a) { // If it is already a vtkIdTypeArray, just return it. vtkIdTypeArray* ida = vtkIdTypeArray::SafeDownCast(a); if(ida) { return ida; } // Need to convert the data. ida = vtkIdTypeArray::New(); ida->SetNumberOfComponents(a->GetNumberOfComponents()); ida->SetNumberOfTuples(a->GetNumberOfTuples()); vtkIdType length = a->GetNumberOfComponents() * a->GetNumberOfTuples(); vtkIdType* idBuffer = ida->GetPointer(0); switch (a->GetDataType()) { vtkTemplateMacro( vtkXMLUnstructuredDataReaderCopyArray( static_cast(a->GetVoidPointer(0)), idBuffer, length)); default: vtkErrorMacro("Cannot convert vtkDataArray of type " << a->GetDataType() << " to vtkIdTypeArray."); ida->Delete(); ida = 0; } a->Delete(); return ida; } //---------------------------------------------------------------------------- vtkUnsignedCharArray* vtkXMLUnstructuredDataReader::ConvertToUnsignedCharArray(vtkDataArray* a) { // If it is already a vtkUnsignedCharArray, just return it. vtkUnsignedCharArray* uca = vtkUnsignedCharArray::SafeDownCast(a); if(uca) { return uca; } // Need to convert the data. uca = vtkUnsignedCharArray::New(); uca->SetNumberOfComponents(a->GetNumberOfComponents()); uca->SetNumberOfTuples(a->GetNumberOfTuples()); vtkIdType length = a->GetNumberOfComponents() * a->GetNumberOfTuples(); unsigned char* ucBuffer = uca->GetPointer(0); switch (a->GetDataType()) { vtkTemplateMacro( vtkXMLUnstructuredDataReaderCopyArray( static_cast(a->GetVoidPointer(0)), ucBuffer, length)); default: vtkErrorMacro("Cannot convert vtkDataArray of type " << a->GetDataType() << " to vtkUnsignedCharArray."); uca->Delete(); uca = 0; } a->Delete(); return uca; } //---------------------------------------------------------------------------- void vtkXMLUnstructuredDataReader::SetupEmptyOutput() { // No pieces means no input. this->GetOutputAsDataSet(0)->SetUpdateExtent(0, 0); } //---------------------------------------------------------------------------- void vtkXMLUnstructuredDataReader::SetupOutputTotals() { this->TotalNumberOfPoints = 0; for(int i=this->StartPiece; i < this->EndPiece; ++i) { this->TotalNumberOfPoints += this->NumberOfPoints[i]; } this->StartPoint = 0; } //---------------------------------------------------------------------------- void vtkXMLUnstructuredDataReader::SetupNextPiece() { this->StartPoint += this->NumberOfPoints[this->Piece]; } //---------------------------------------------------------------------------- void vtkXMLUnstructuredDataReader::SetupUpdateExtent(int piece, int numberOfPieces, int ghostLevel) { this->UpdatePiece = piece; this->UpdateNumberOfPieces = numberOfPieces; this->UpdateGhostLevel = ghostLevel; // If more pieces are requested than available, just return empty // pieces for the extra ones. if(this->UpdateNumberOfPieces > this->NumberOfPieces) { this->UpdateNumberOfPieces = this->NumberOfPieces; } // Find the range of pieces to read. if(this->UpdatePiece < this->UpdateNumberOfPieces) { this->StartPiece = ((this->UpdatePiece*this->NumberOfPieces) / this->UpdateNumberOfPieces); this->EndPiece = (((this->UpdatePiece+1)*this->NumberOfPieces) / this->UpdateNumberOfPieces); } else { this->StartPiece = 0; this->EndPiece = 0; } // Find the total size of the output. this->SetupOutputTotals(); } //---------------------------------------------------------------------------- void vtkXMLUnstructuredDataReader::ReadXMLData() { // Get the update request. int piece; int numberOfPieces; int ghostLevel; this->GetOutputUpdateExtent(piece, numberOfPieces, ghostLevel); vtkDebugMacro("Updating piece " << piece << " of " << numberOfPieces << " with ghost level " << ghostLevel); // Setup the range of pieces that will be read. this->SetupUpdateExtent(piece, numberOfPieces, ghostLevel); // If there are no data to read, stop now. if(this->StartPiece == this->EndPiece) { return; } vtkDebugMacro("Reading piece range [" << this->StartPiece << ", " << this->EndPiece << ") from file."); // Let superclasses read data. This also allocates output data. this->Superclass::ReadXMLData(); // Split current progress range based on fraction contributed by // each piece. float progressRange[2] = {0,0}; this->GetProgressRange(progressRange); // Calculate the cumulative fraction of data contributed by each // piece (for progress). float* fractions = new float[this->EndPiece-this->StartPiece+1]; int i; fractions[0] = 0; for(i=this->StartPiece; i < this->EndPiece; ++i) { int index = i-this->StartPiece; fractions[index+1] = (fractions[index] + this->GetNumberOfPointsInPiece(i) + this->GetNumberOfCellsInPiece(i)); } if(fractions[this->EndPiece-this->StartPiece] == 0) { fractions[this->EndPiece-this->StartPiece] = 1; } for(i=this->StartPiece; i < this->EndPiece; ++i) { int index = i-this->StartPiece; fractions[index+1] = fractions[index+1] / fractions[this->EndPiece-this->StartPiece]; } // Read the data needed from each piece. for(i=this->StartPiece; (i < this->EndPiece && !this->AbortExecute && !this->DataError); ++i) { // Set the range of progress for this piece. this->SetProgressRange(progressRange, i-this->StartPiece, fractions); if(!this->Superclass::ReadPieceData(i)) { // An error occurred while reading the piece. this->DataError = 1; } this->SetupNextPiece(); } delete [] fractions; } //---------------------------------------------------------------------------- void vtkXMLUnstructuredDataReader::SetupPieces(int numPieces) { this->Superclass::SetupPieces(numPieces); this->NumberOfPoints = new vtkIdType[numPieces]; this->PointElements = new vtkXMLDataElement*[numPieces]; for(int i=0;i < numPieces; ++i) { this->PointElements[i] = 0; this->NumberOfPoints[i] = 0; } } //---------------------------------------------------------------------------- void vtkXMLUnstructuredDataReader::DestroyPieces() { delete [] this->PointElements; delete [] this->NumberOfPoints; this->PointElements = 0; this->NumberOfPoints = 0; this->Superclass::DestroyPieces(); } //---------------------------------------------------------------------------- vtkIdType vtkXMLUnstructuredDataReader::GetNumberOfPoints() { return this->TotalNumberOfPoints; } //---------------------------------------------------------------------------- vtkIdType vtkXMLUnstructuredDataReader::GetNumberOfCells() { return this->TotalNumberOfCells; } //---------------------------------------------------------------------------- vtkIdType vtkXMLUnstructuredDataReader::GetNumberOfPointsInPiece(int piece) { return this->NumberOfPoints[piece]; } //---------------------------------------------------------------------------- // Note that any changes (add or removing information) made to this method // should be replicated in CopyOutputInformation void vtkXMLUnstructuredDataReader::SetupOutputInformation(vtkInformation *outInfo) { this->Superclass::SetupOutputInformation(outInfo); // Set the maximum number of pieces that can be provided by this // reader. outInfo->Set(vtkStreamingDemandDrivenPipeline::MAXIMUM_NUMBER_OF_PIECES(), this->NumberOfPieces); } //---------------------------------------------------------------------------- void vtkXMLUnstructuredDataReader::CopyOutputInformation(vtkInformation *outInfo, int port) { this->Superclass::CopyOutputInformation(outInfo, port); outInfo->CopyEntry( this->GetExecutive()->GetOutputInformation( port ), vtkStreamingDemandDrivenPipeline::MAXIMUM_NUMBER_OF_PIECES() ); } //---------------------------------------------------------------------------- void vtkXMLUnstructuredDataReader::SetupOutputData() { this->Superclass::SetupOutputData(); // Create the points array. vtkPoints* points = vtkPoints::New(); // Use the configuration of the first piece since all are the same. vtkXMLDataElement* ePoints = this->PointElements[0]; if (ePoints) { // Non-zero volume. vtkDataArray* a = this->CreateDataArray(ePoints->GetNestedElement(0)); if (a) { // Allocate the points array. a->SetNumberOfTuples( this->GetNumberOfPoints() ); points->SetData(a); a->Delete(); } else { this->DataError = 1; } } this->GetOutputAsPointSet()->SetPoints(points); points->Delete(); } //---------------------------------------------------------------------------- int vtkXMLUnstructuredDataReader::ReadPiece(vtkXMLDataElement* ePiece) { if(!this->Superclass::ReadPiece(ePiece)) { return 0; } if(!ePiece->GetScalarAttribute("NumberOfPoints", this->NumberOfPoints[this->Piece])) { vtkErrorMacro("Piece " << this->Piece << " is missing its NumberOfPoints attribute."); this->NumberOfPoints[this->Piece] = 0; return 0; } // Find the Points element in the piece. int i; this->PointElements[this->Piece] = 0; for(i=0; i < ePiece->GetNumberOfNestedElements(); ++i) { vtkXMLDataElement* eNested = ePiece->GetNestedElement(i); if(strcmp(eNested->GetName(), "Points") == 0) { // make sure the XML file is somehow valid: if( (this->NumberOfTimeSteps > 0 && eNested->GetNumberOfNestedElements() >= 1) || (this->NumberOfTimeSteps == 0 && eNested->GetNumberOfNestedElements() == 1)) { this->PointElements[this->Piece] = eNested; } } } // If there are some points, we require a Points element. if(!this->PointElements[this->Piece] && (this->NumberOfPoints[this->Piece] > 0)) { vtkErrorMacro("A piece is missing its Points element " "or element does not have exactly 1 array."); return 0; } return 1; } //---------------------------------------------------------------------------- int vtkXMLUnstructuredDataReader::ReadPieceData() { // The amount of data read by the superclass's ReadPieceData comes // from point/cell data (we read point specifications here). vtkIdType superclassPieceSize = (this->NumberOfPointArrays*this->GetNumberOfPointsInPiece(this->Piece)+ this->NumberOfCellArrays*this->GetNumberOfCellsInPiece(this->Piece)); // Total amount of data in this piece comes from point/cell data // arrays and the point specifications themselves. vtkIdType totalPieceSize = superclassPieceSize + 1*this->GetNumberOfPointsInPiece(this->Piece); if(totalPieceSize == 0) { totalPieceSize = 1; } // Split the progress range based on the approximate fraction of // data that will be read by each step in this method. float progressRange[2] = {0,0}; this->GetProgressRange(progressRange); float fractions[3] = { 0, float(superclassPieceSize)/totalPieceSize, 1 }; // Set the range of progress for the superclass. this->SetProgressRange(progressRange, 0, fractions); // Let the superclass read its data. if(!this->Superclass::ReadPieceData()) { return 0; } vtkPointSet* output = this->GetOutputAsPointSet(); // Set the range of progress for the Points. this->SetProgressRange(progressRange, 1, fractions); // Read the points array. vtkXMLDataElement* ePoints = this->PointElements[this->Piece]; if(ePoints) { for(int i=0;(i < ePoints->GetNumberOfNestedElements() && !this->AbortExecute);++i) { vtkXMLDataElement* eNested = ePoints->GetNestedElement(i); assert( strcmp(eNested->GetName(), "DataArray") == 0 ); int needToRead = this->PointsNeedToReadTimeStep(eNested); if( needToRead ) { // Read the array. if(!this->ReadArrayForPoints(eNested, output->GetPoints()->GetData())) { vtkErrorMacro("Cannot read points array from " << ePoints->GetName() << " in piece " << this->Piece << ". The data array in the element may be too short."); return 0; } } } } return 1; } //---------------------------------------------------------------------------- int vtkXMLUnstructuredDataReader::ReadCellArray(vtkIdType numberOfCells, vtkIdType totalNumberOfCells, vtkXMLDataElement* eCells, vtkCellArray* outCells) { if(numberOfCells <= 0) { return 1; } else { if(!eCells) { return 0; } } // Split progress range into 1/5 for offsets array and 4/5 for // connectivity array. This assumes an average of 4 points per // cell. Unfortunately, we cannot know the length of the // connectivity array ahead of time to calculate the real fraction. float progressRange[2] = {0,0}; this->GetProgressRange(progressRange); float fractions[3] = {0, 0.2, 1}; // Set range of progress for offsets array. this->SetProgressRange(progressRange, 0, fractions); // Read the cell offsets. vtkXMLDataElement* eOffsets = this->FindDataArrayWithName(eCells, "offsets"); if(!eOffsets) { vtkErrorMacro("Cannot read cell offsets from " << eCells->GetName() << " in piece " << this->Piece << " because the \"offsets\" array could not be found."); return 0; } vtkDataArray* c1 = this->CreateDataArray(eOffsets); if(!c1 || (c1->GetNumberOfComponents() != 1)) { vtkErrorMacro("Cannot read cell offsets from " << eCells->GetName() << " in piece " << this->Piece << " because the \"offsets\" array could not be created" << " with one component."); return 0; } c1->SetNumberOfTuples(numberOfCells); if(!this->ReadData(eOffsets, c1->GetVoidPointer(0), c1->GetDataType(), 0, numberOfCells)) { vtkErrorMacro("Cannot read cell offsets from " << eCells->GetName() << " in piece " << this->Piece << " because the \"offsets\" array is not long enough."); return 0; } vtkIdTypeArray* cellOffsets = this->ConvertToIdTypeArray(c1); if(!cellOffsets) { vtkErrorMacro("Cannot read cell offsets from " << eCells->GetName() << " in piece " << this->Piece << " because the \"offsets\" array could not be" << " converted to a vtkIdTypeArray."); return 0; } // Check the contents of the cell offsets array. vtkIdType* coffset = cellOffsets->GetPointer(0); vtkIdType lastOffset = 0; vtkIdType i; for(i=0; i < numberOfCells; ++i) { if(coffset[i] <= lastOffset) { vtkErrorMacro("Cannot read cell connectivity from " << eCells->GetName() << " in piece " << this->Piece << " because the \"offsets\" array is" << " not monotonically increasing or starts with a" << " value less than 1."); cellOffsets->Delete(); return 0; } lastOffset = coffset[i]; } // Set range of progress for connectivity array. this->SetProgressRange(progressRange, 1, fractions); // Read the cell point connectivity array. vtkIdType cpLength = cellOffsets->GetValue(numberOfCells-1); vtkXMLDataElement* eConn = this->FindDataArrayWithName(eCells, "connectivity"); if(!eConn) { vtkErrorMacro("Cannot read cell connectivity from " << eCells->GetName() << " in piece " << this->Piece << " because the \"connectivity\" array could not be found."); cellOffsets->Delete(); return 0; } vtkDataArray* c0 = this->CreateDataArray(eConn); if(!c0 || (c0->GetNumberOfComponents() != 1)) { vtkErrorMacro("Cannot read cell connectivity from " << eCells->GetName() << " in piece " << this->Piece << " because the \"connectivity\" array could not be created" << " with one component."); cellOffsets->Delete(); return 0; } c0->SetNumberOfTuples(cpLength); if(!this->ReadData(eConn, c0->GetVoidPointer(0), c0->GetDataType(), 0, cpLength)) { vtkErrorMacro("Cannot read cell connectivity from " << eCells->GetName() << " in piece " << this->Piece << " because the \"connectivity\" array is not long enough."); cellOffsets->Delete(); return 0; } vtkIdTypeArray* cellPoints = this->ConvertToIdTypeArray(c0); if(!cellPoints) { vtkErrorMacro("Cannot read cell connectivity from " << eCells->GetName() << " in piece " << this->Piece << " because the \"connectivity\" array could not be" << " converted to a vtkIdTypeArray."); cellOffsets->Delete(); return 0; } // Allocate memory in the output connectivity array. vtkIdType curSize = 0; if(outCells->GetData()) { curSize = outCells->GetData()->GetNumberOfTuples(); } vtkIdType newSize = curSize+numberOfCells+cellPoints->GetNumberOfTuples(); vtkIdType* cptr = outCells->WritePointer(totalNumberOfCells, newSize); cptr += curSize; // Copy the connectivity data. vtkIdType previousOffset = 0; for(i=0; i < numberOfCells; ++i) { vtkIdType length = cellOffsets->GetValue(i)-previousOffset; *cptr++ = length; vtkIdType* sptr = cellPoints->GetPointer(previousOffset); // Copy the point indices, but increment them for the appended // version's index. vtkIdType j; for(j=0;j < length; ++j) { cptr[j] = sptr[j]+this->StartPoint; } cptr += length; previousOffset += length; } cellPoints->Delete(); cellOffsets->Delete(); return 1; } //---------------------------------------------------------------------------- int vtkXMLUnstructuredDataReader::ReadArrayForPoints(vtkXMLDataElement* da, vtkDataArray* outArray) { vtkIdType startPoint = this->StartPoint; vtkIdType numPoints = this->NumberOfPoints[this->Piece]; vtkIdType components = outArray->GetNumberOfComponents(); return this->ReadData(da, outArray->GetVoidPointer(startPoint*components), outArray->GetDataType(), 0, numPoints*components); } //---------------------------------------------------------------------------- int vtkXMLUnstructuredDataReader::PointsNeedToReadTimeStep(vtkXMLDataElement *eNested) { // Easy case no timestep: int numTimeSteps = eNested->GetVectorAttribute("TimeStep", this->NumberOfTimeSteps, this->TimeSteps); assert( numTimeSteps <= this->NumberOfTimeSteps ); if (!numTimeSteps && !this->NumberOfTimeSteps) { assert( this->PointsTimeStep == -1 ); //No timestep in this file return 1; } // else TimeStep was specified but no TimeValues associated were found assert( this->NumberOfTimeSteps ); // case numTimeSteps > 1 int isCurrentTimeInArray = vtkXMLReader::IsTimeStepInArray(this->CurrentTimeStep, this->TimeSteps, numTimeSteps); if( !isCurrentTimeInArray && numTimeSteps) { return 0; } // we know that time steps are specified and that CurrentTimeStep is in the array // we need to figure out if we need to read the array or if it was forwarded // Need to check the current 'offset' unsigned long offset; if( eNested->GetScalarAttribute("offset", offset) ) { if( this->PointsOffset != offset ) { // save the pointsOffset we are about to read assert( this->PointsTimeStep == -1 ); //cannot have mixture of binary and appended this->PointsOffset = offset; return 1; } } else { // No offset is specified this is a binary file // First thing to check if numTimeSteps == 0: if( !numTimeSteps && this->NumberOfTimeSteps && this->PointsTimeStep == -1) { // Update last PointsTimeStep read this->PointsTimeStep = this->CurrentTimeStep; return 1; } int isLastTimeInArray = vtkXMLReader::IsTimeStepInArray(this->PointsTimeStep, this->TimeSteps, numTimeSteps); // If no time is specified or if time is specified and match then read if (isCurrentTimeInArray && !isLastTimeInArray) { // CurrentTimeStep is in TimeSteps but Last is not := need to read // Update last PointsTimeStep read this->PointsTimeStep = this->CurrentTimeStep; return 1; } } // all other cases we don't need to read: return 0; } //---------------------------------------------------------------------------- // Returns true if we need to read the data for the current time step int vtkXMLUnstructuredDataReader::CellsNeedToReadTimeStep(vtkXMLDataElement *eNested, int &cellstimestep, unsigned long &cellsoffset) { // Easy case no timestep: int numTimeSteps = eNested->GetVectorAttribute("TimeStep", this->NumberOfTimeSteps, this->TimeSteps); assert( numTimeSteps <= this->NumberOfTimeSteps ); if (!numTimeSteps && !this->NumberOfTimeSteps) { assert( cellstimestep == -1 ); //No timestep in this file return 1; } // else TimeStep was specified but no TimeValues associated were found assert( !this->NumberOfTimeSteps ); // case numTimeSteps > 1 int isCurrentTimeInArray = vtkXMLReader::IsTimeStepInArray(this->CurrentTimeStep, this->TimeSteps, numTimeSteps); if( !isCurrentTimeInArray && numTimeSteps) { return 0; } // we know that time steps are specified and that CurrentTimeStep is in the array // we need to figure out if we need to read the array or if it was forwarded // Need to check the current 'offset' unsigned long offset; if( eNested->GetScalarAttribute("offset", offset) ) { if( cellsoffset != offset ) { // save the cellsOffset we are about to read assert( cellstimestep == -1 ); //cannot have mixture of binary and appended cellsoffset = offset; return 1; } } else { // No offset is specified this is a binary file // First thing to check if numTimeSteps == 0: if( !numTimeSteps && this->NumberOfTimeSteps && cellstimestep == -1) { // Update last PointsTimeStep read cellstimestep = this->CurrentTimeStep; return 1; } int isLastTimeInArray = vtkXMLReader::IsTimeStepInArray(cellstimestep, this->TimeSteps, numTimeSteps); // If no time is specified or if time is specified and match then read if (isCurrentTimeInArray && !isLastTimeInArray) { // CurrentTimeStep is in TimeSteps but Last is not := need to read // Update last cellstimestep read cellstimestep = this->CurrentTimeStep; return 1; } } // all other cases we don't need to read: return 0; }