/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkParticleReader.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 "vtkParticleReader.h" #include "vtkSmartPointer.h" #include "vtkStreamingDemandDrivenPipeline.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkObjectFactory.h" #include "vtkByteSwap.h" #include "vtkPointData.h" #include "vtkPoints.h" #include "vtkPolyData.h" #include "vtkCellArray.h" #include "vtkDataArray.h" #include "vtkDoubleArray.h" #include "vtkFloatArray.h" #include #include #include vtkCxxRevisionMacro(vtkParticleReader, "$Revision: 1.28 $"); vtkStandardNewMacro(vtkParticleReader); namespace { // .NAME ParseLine - Read a line of four values of type T filtering out comments. // .SECTION Description // The ParseLine operator scans a string and returns 0 if it finds a // comment symbol. Otherwise it returns 1 and four values corresponding // to the position of the particle (x,y,z) and a scalar s asociated with // the particle. It is a good idea to set the values of val to some // predefined value before calling the operator. template < typename T > class ParseLine { public: ParseLine (): LookForEndString(false) {}; int operator () (vtkstd::string &s, T val[4]) { // Skip over comment lines. vtkstd::string::iterator itr; vtkstd::string tgt("/*"); itr = vtkstd::search(s.begin(),s.end(),tgt.begin(),tgt.end()); if ( itr != s.end() ) { LookForEndString = true; //continue; } if ( LookForEndString ) { tgt = "*/"; itr = vtkstd::search(s.begin(),s.end(),tgt.begin(),tgt.end()); if ( itr != s.end() ) { LookForEndString = false; } //continue; return 0; } tgt = "//"; itr = vtkstd::search(s.begin(),s.end(),tgt.begin(),tgt.end()); if ( itr != s.end() ) { return 0; } tgt = "%"; itr = vtkstd::search(s.begin(),s.end(),tgt.begin(),tgt.end()); if ( itr != s.end() ) { return 0; } tgt = "#"; itr = vtkstd::search(s.begin(),s.end(),tgt.begin(),tgt.end()); if ( itr != s.end() ) { return 0; } // If comma delimited, replace with tab vtkstd::replace(s.begin(),s.end(),',','\t'); // We have data. strstream is; is << s.c_str() << ends;// no istringstream in VTK is >> val[0] >> val[1] >> val[2] >> val[3]; return 1; } private: bool LookForEndString; }; // The number of times we output a progress message. int const quantum = 20; // The ratio of high ASCII characters to low ASCII characeters. double hiToLowASCII = 0.1; } //---------------------------------------------------------------------------- vtkParticleReader::vtkParticleReader() : FileName(NULL) , File(NULL) , HasScalar(1) , FileType(FILE_TYPE_IS_UNKNOWN) , DataType(VTK_FLOAT) , Alliquot(0) , Count(0) , SwapBytes(0) , NumberOfPoints(0) { this->SetNumberOfInputPorts(0); } //---------------------------------------------------------------------------- vtkParticleReader::~vtkParticleReader() { if (this->File) { this->File->close(); delete this->File; this->File = NULL; } if (this->FileName) { delete [] this->FileName; this->FileName = NULL; } } //---------------------------------------------------------------------------- void vtkParticleReader::OpenFile() { if (!this->FileName) { vtkErrorMacro(<<"FileName must be specified."); return; } // If the file was open close it. if (this->File) { this->File->close(); delete this->File; this->File = NULL; } // Open the new file. vtkDebugMacro(<< "Initialize: opening file " << this->FileName); #ifdef _WIN32 this->File = new ifstream(this->FileName, ios::in | ios::binary); #else this->File = new ifstream(this->FileName, ios::in); #endif if (! this->File || this->File->fail()) { vtkErrorMacro(<< "Initialize: Could not open file " << this->FileName); return; } } //---------------------------------------------------------------------------- int vtkParticleReader::RequestInformation( vtkInformation *vtkNotUsed(request), vtkInformationVector **vtkNotUsed(inputVector), vtkInformationVector *outputVector) { // get the info object vtkInformation *outInfo = outputVector->GetInformationObject(0); outInfo->Set(vtkStreamingDemandDrivenPipeline::MAXIMUM_NUMBER_OF_PIECES(), -1); return 1; } //---------------------------------------------------------------------------- int vtkParticleReader::RequestData( vtkInformation *vtkNotUsed(request), vtkInformationVector **vtkNotUsed(inputVector), vtkInformationVector *outputVector) { if (!this->FileName) { vtkErrorMacro(<<"FileName must be specified."); return 0; } this->OpenFile(); int ft = this->FileType; if ( ft == FILE_TYPE_IS_UNKNOWN ) { ft = DetermineFileType(); if ( ft == FILE_TYPE_IS_UNKNOWN ) { vtkErrorMacro(<< "File type cannot be determined."); return 0; } } switch ( ft ) { case FILE_TYPE_IS_TEXT: switch ( this->DataType ) { case VTK_FLOAT: return ProduceOutputFromTextFileFloat(outputVector); break; case VTK_DOUBLE: return ProduceOutputFromTextFileDouble(outputVector); break; default: { vtkErrorMacro(<<"Only float or double data can be processed."); return 0; } } case FILE_TYPE_IS_BINARY: switch ( this->DataType ) { case VTK_FLOAT: return ProduceOutputFromBinaryFileFloat(outputVector); break; case VTK_DOUBLE: return ProduceOutputFromBinaryFileDouble(outputVector); break; default: { vtkErrorMacro(<<"Only float or double data can be processed."); return 0; } } default: { vtkErrorMacro(<<"The file type was not able to be determined."); return 0; } } } //---------------------------------------------------------------------------- int vtkParticleReader::DetermineFileType() { // This function assumes that the file has been opened. this->File->seekg(0,ios::end); if (this->File->fail()) { vtkErrorMacro("Could not seek to end of file."); return FILE_TYPE_IS_UNKNOWN; } size_t fileLength = this->File->tellg(); if ( fileLength == 0 ) { vtkErrorMacro("File is empty."); return FILE_TYPE_IS_UNKNOWN; } this->File->seekg(0,ios::beg); if (this->File->fail()) { vtkErrorMacro("Could not seek to start of file."); return FILE_TYPE_IS_UNKNOWN; } size_t sampleSize = fileLength < 5000 ? fileLength: 5000; // cout << "File length: " << fileLength << " Sample size: " << sampleSize << endl; vtkstd::vector s; for ( size_t i = 0; i < sampleSize; ++i ) { char c; this->File->read(&c,sizeof(char)); s.push_back(c); } // If read terminated prematurely then it may have detected // a premature EOF character in the data. // Assume that the file type is undetermined in this case. if ( s.size() != sampleSize ) { // cout << "Premature termination" << endl; return FILE_TYPE_IS_UNKNOWN; } size_t zero = 0; size_t conventionalASCII = 0; size_t extendedASCII = 0; size_t controlASCII = 0; size_t otherASCII = 0; for ( size_t j = 0; j < s.size(); ++j ) { if ( s[j] == '\0' ) { zero++; continue; } // Conventional ASCII characters. if ( s[j] > 0x1f && s[j] < 0x80 ) { conventionalASCII++; continue; } // Extended ASCII characters may have been used. if ( s[j] > 0x7f ) { extendedASCII++; continue; } // Control characters. if ( s[j] == '\n' || s[j] == '\r' || s[j] == '\t' || s[j] == '\f' ) { controlASCII++; continue; } otherASCII++; } // NULL shouldn't ever appear in a text file. if ( zero != 0 || otherASCII > 0 || conventionalASCII == 0 ) { return FILE_TYPE_IS_BINARY; } if ( (double)extendedASCII / (double) conventionalASCII < hiToLowASCII ) { return FILE_TYPE_IS_TEXT; } return FILE_TYPE_IS_BINARY; } //---------------------------------------------------------------------------- int vtkParticleReader::ProduceOutputFromTextFileDouble(vtkInformationVector *outputVector) { // Get the size of the file. this->File->seekg(0,ios::end); if (this->File->fail()) { vtkErrorMacro("Could not seek to end of file."); return 0; } size_t fileLength = (unsigned long)this->File->tellg(); size_t bytesRead = 0; vtkstd::string s; vtkSmartPointer points = vtkSmartPointer::New(); points->SetDataTypeToDouble(); points->Reset(); vtkSmartPointer scalars = vtkSmartPointer::New(); scalars->Reset(); scalars->SetName("Scalar"); this->File->seekg(0,ios::beg); this->Alliquot = fileLength / quantum; this->Count = 1; ParseLine pl; char buffer[256]; while ( this->File->getline(buffer,256,'\n') ) { s = buffer; if ( s.size() != 0 ) { bytesRead += s.size(); this->DoProgressUpdate( bytesRead, fileLength ); double val[4]; val[0]=val[1]=val[2]=val[3]=0; if ( pl(s,val) ) { points->InsertNextPoint(val[0], val[1], val[2]); if ( this->HasScalar) { scalars->InsertNextValue(val[3]); } } } } vtkSmartPointer vertices = vtkSmartPointer::New(); vertices->Reset(); this->NumberOfPoints = points->GetNumberOfPoints(); for( vtkIdType j = 0; j < (vtkIdType)this->NumberOfPoints; ++j ) { vertices->InsertNextCell( 1 ); vertices->InsertCellPoint( j ); } // get the info object vtkInformation *outInfo = outputVector->GetInformationObject(0); // get the ouptut vtkPolyData *output = vtkPolyData::SafeDownCast( outInfo->Get(vtkDataObject::DATA_OBJECT())); output->SetPoints(points); output->SetVerts(vertices); if ( this->HasScalar ) output->GetPointData()->SetScalars(scalars); output->Modified(); return 1; } //---------------------------------------------------------------------------- int vtkParticleReader::ProduceOutputFromTextFileFloat(vtkInformationVector *outputVector) { // Get the size of the file. this->File->seekg(0,ios::end); if (this->File->fail()) { vtkErrorMacro("Could not seek to end of file."); return 0; } size_t fileLength = (unsigned long)this->File->tellg(); size_t bytesRead = 0; vtkstd::string s; vtkSmartPointer points = vtkSmartPointer::New(); points->SetDataTypeToFloat(); points->Reset(); vtkSmartPointer scalars = vtkSmartPointer::New(); scalars->Reset(); scalars->SetName("Scalar"); this->File->seekg(0,ios::beg); this->Alliquot = fileLength / quantum; this->Count = 1; ParseLine pl; char buffer[256]; while ( this->File->getline(buffer,256,'\n') ) { s = buffer; if ( s.size() != 0 ) { bytesRead += s.size(); this->DoProgressUpdate( bytesRead, fileLength ); float val[4]; val[0]=val[1]=val[2]=val[3]=0; if ( pl(s,val) ) { points->InsertNextPoint(val[0], val[1], val[2]); if ( this->HasScalar) { scalars->InsertNextValue(val[3]); } } } } vtkSmartPointer vertices = vtkSmartPointer::New(); vertices->Reset(); this->NumberOfPoints = points->GetNumberOfPoints(); for( vtkIdType j = 0; j < (vtkIdType)this->NumberOfPoints; ++j ) { vertices->InsertNextCell( 1 ); vertices->InsertCellPoint( j ); } // get the info object vtkInformation *outInfo = outputVector->GetInformationObject(0); // get the ouptut vtkPolyData *output = vtkPolyData::SafeDownCast( outInfo->Get(vtkDataObject::DATA_OBJECT())); output->SetPoints(points); output->SetVerts(vertices); if ( this->HasScalar ) { output->GetPointData()->SetScalars(scalars); } output->Modified(); return 1; } //---------------------------------------------------------------------------- int vtkParticleReader::ProduceOutputFromBinaryFileDouble(vtkInformationVector *outputVector) { unsigned long fileLength, start, next, length, ptIdx, cellPtIdx; unsigned long cellLength; int piece, numPieces; double *data, *ptr; if (!this->FileName) { vtkErrorMacro(<<"FileName must be specified."); return 0; } this->OpenFile(); // Get the size of the header from the size of the image this->File->seekg(0,ios::end); if (this->File->fail()) { vtkErrorMacro("Could not seek to end of file."); return 0; } fileLength = (unsigned long)this->File->tellg(); if ( this->HasScalar ) { this->NumberOfPoints = fileLength / (4 * sizeof(double)); } else { this->NumberOfPoints = fileLength / (3 * sizeof(double)); } // get the info object vtkInformation *outInfo = outputVector->GetInformationObject(0); piece = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER()); numPieces = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES()); if ((unsigned long)numPieces > this->NumberOfPoints) { numPieces = (int)(this->NumberOfPoints); } if (numPieces <= 0 || piece < 0 || piece >= numPieces) { return 0; } start = piece * this->NumberOfPoints / numPieces; next = (piece+1) * this->NumberOfPoints / numPieces; length = next - start; if ( this->HasScalar ) { data = new double[length * 4]; } else { data = new double[length * 3]; } // Seek to the first point in the file. if ( this->HasScalar ) { this->File->seekg(start*4*sizeof(double), ios::beg); } else { this->File->seekg(start*3*sizeof(double), ios::beg); } if (this->File->fail()) { vtkErrorMacro(<< "File operation failed: Seeking to " << start*4); delete [] data; return 0; } // Read the data. if ( this->HasScalar ) { this->File->read((char *)data, length*4*sizeof(double)); if ( static_cast(this->File->gcount()) != static_cast(length*4*sizeof(double)) // On apple read to eof returns fail #ifndef __APPLE_CC__ || this->File->fail() #endif // __APPLE_CC__ ) { vtkErrorMacro("Could not read points: " << start << " to " << next-1); delete [] data; return 0; } } else { this->File->read((char *)data, length*3*sizeof(double)); if ( static_cast(this->File->gcount()) != static_cast(length*3*sizeof(double)) // On apple read to eof returns fail #ifndef __APPLE_CC__ || this->File->fail() #endif // __APPLE_CC__ ) { vtkErrorMacro("Could not read points: " << start << " to " << next-1); delete [] data; return 0; } } // Swap bytes if necessary. if (this->GetSwapBytes()) { if ( this->HasScalar ) { vtkByteSwap::SwapVoidRange(data, length*4, sizeof(double)); } else { vtkByteSwap::SwapVoidRange(data, length*3, sizeof(double)); } } this->UpdateProgress(0.5); ptr = data; vtkSmartPointer points = vtkSmartPointer::New(); points->SetNumberOfPoints(length); vtkSmartPointer array = vtkSmartPointer::New(); array->SetName("Scalar"); vtkSmartPointer verts = vtkSmartPointer::New(); // Each cell will have 1000 points. Leave a little extra space just in case. // We break up the cell this way so that the render will check for aborts // at a reasonable rate. verts->Allocate((int)((float)length * 1.002)); // Keep adding cells until we run out of points. ptIdx = 0; int cnt = 1; double len = length; while (length > 0) { if ( cnt % 10 == 0 ) { this->UpdateProgress(0.5+((cnt * 1000.0)/len)/2.0); } cnt++; cellLength = 1000; if (cellLength > length) { cellLength = length; } length = length - cellLength; verts->InsertNextCell((int)cellLength); for (cellPtIdx = 0; cellPtIdx < cellLength; ++cellPtIdx) { points->SetPoint(ptIdx, ptr[0], ptr[1], ptr[2]); if ( this->HasScalar ) { array->InsertNextValue(ptr[3]); ptr += 4; } else { ptr += 3; } verts->InsertCellPoint(ptIdx); ++ptIdx; } } delete [] data; // get the ouptut vtkPolyData *output = vtkPolyData::SafeDownCast( outInfo->Get(vtkDataObject::DATA_OBJECT())); output->SetPoints(points); output->SetVerts(verts); if ( this->HasScalar ) { output->GetPointData()->SetScalars(array); } return 1; } //---------------------------------------------------------------------------- int vtkParticleReader::ProduceOutputFromBinaryFileFloat(vtkInformationVector *outputVector) { unsigned long fileLength, start, next, length, ptIdx, cellPtIdx; unsigned long cellLength; int piece, numPieces; float *data, *ptr; if (!this->FileName) { vtkErrorMacro(<<"FileName must be specified."); return 0; } this->OpenFile(); // Get the size of the header from the size of the image this->File->seekg(0,ios::end); if (this->File->fail()) { vtkErrorMacro("Could not seek to end of file."); return 0; } fileLength = (unsigned long)this->File->tellg(); if ( this->HasScalar ) { this->NumberOfPoints = fileLength / (4 * sizeof(float)); } else { this->NumberOfPoints = fileLength / (3 * sizeof(float)); } // get the info object vtkInformation *outInfo = outputVector->GetInformationObject(0); piece = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER()); numPieces = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES()); if ((unsigned long)numPieces > this->NumberOfPoints) { numPieces = (int)(this->NumberOfPoints); } if (numPieces <= 0 || piece < 0 || piece >= numPieces) { return 0; } start = piece * this->NumberOfPoints / numPieces; next = (piece+1) * this->NumberOfPoints / numPieces; length = next - start; if ( this->HasScalar ) { data = new float[length * 4]; } else { data = new float[length * 3]; } // Seek to the first point in the file. if ( this->HasScalar ) { this->File->seekg(start*4*sizeof(float), ios::beg); } else { this->File->seekg(start*3*sizeof(float), ios::beg); } if (this->File->fail()) { vtkErrorMacro(<< "File operation failed: Seeking to " << start*4); delete [] data; return 0; } // Read the data. if ( this->HasScalar ) { this->File->read((char *)data, length*4*sizeof(float)); if ( static_cast(this->File->gcount()) != static_cast(length*4*sizeof(float)) // On apple read to eof returns fail #ifndef __APPLE_CC__ || this->File->fail() #endif // __APPLE_CC__ ) { vtkErrorMacro("Could not read points: " << start << " to " << next-1); delete [] data; return 0; } } else { this->File->read((char *)data, length*3*sizeof(float)); if ( static_cast(this->File->gcount()) != static_cast(length*3*sizeof(float)) // On apple read to eof returns fail #ifndef __APPLE_CC__ || this->File->fail() #endif // __APPLE_CC__ ) { vtkErrorMacro("Could not read points: " << start << " to " << next-1); delete [] data; return 0; } } // Swap bytes if necessary. if (this->GetSwapBytes()) { if ( this->HasScalar ) { vtkByteSwap::SwapVoidRange(data, length*4, sizeof(float)); } else { vtkByteSwap::SwapVoidRange(data, length*3, sizeof(float)); } } this->UpdateProgress(0.5); ptr = data; vtkSmartPointer points = vtkSmartPointer::New(); points->SetNumberOfPoints(length); vtkSmartPointer array = vtkSmartPointer::New(); array->SetName("Scalar"); vtkSmartPointer verts = vtkSmartPointer::New(); // Each cell will have 1000 points. Leave a little extra space just in case. // We break up the cell this way so that the render will check for aborts // at a reasonable rate. verts->Allocate((int)((float)length * 1.002)); // Keep adding cells until we run out of points. ptIdx = 0; int cnt = 1; double len = length; while (length > 0) { if ( cnt % 10 == 0 ) { this->UpdateProgress(0.5+((cnt * 1000.0)/len)/2.0); } cnt++; cellLength = 1000; if (cellLength > length) { cellLength = length; } length = length - cellLength; verts->InsertNextCell((int)cellLength); for (cellPtIdx = 0; cellPtIdx < cellLength; ++cellPtIdx) { points->SetPoint(ptIdx, ptr[0], ptr[1], ptr[2]); if ( this->HasScalar ) { array->InsertNextValue(ptr[3]); ptr += 4; } else { ptr += 3; } verts->InsertCellPoint(ptIdx); ++ptIdx; } } delete [] data; // get the ouptut vtkPolyData *output = vtkPolyData::SafeDownCast( outInfo->Get(vtkDataObject::DATA_OBJECT())); output->SetPoints(points); output->SetVerts(verts); if ( this->HasScalar ) { output->GetPointData()->SetScalars(array); } return 1; } //---------------------------------------------------------------------------- void vtkParticleReader::DoProgressUpdate( size_t & bytesRead, size_t & fileLength ) { if ( bytesRead > this->Alliquot ) { this->UpdateProgress( bytesRead/(double)fileLength ); this->Count++; this->Alliquot = fileLength / quantum * this->Count; } } //---------------------------------------------------------------------------- void vtkParticleReader::SetDataByteOrderToBigEndian() { #ifndef VTK_WORDS_BIGENDIAN this->SwapBytesOn(); #else this->SwapBytesOff(); #endif } //---------------------------------------------------------------------------- void vtkParticleReader::SetDataByteOrderToLittleEndian() { #ifdef VTK_WORDS_BIGENDIAN this->SwapBytesOn(); #else this->SwapBytesOff(); #endif } //---------------------------------------------------------------------------- void vtkParticleReader::SetDataByteOrder(int byteOrder) { if ( byteOrder == VTK_FILE_BYTE_ORDER_BIG_ENDIAN ) { this->SetDataByteOrderToBigEndian(); } else { this->SetDataByteOrderToLittleEndian(); } } //---------------------------------------------------------------------------- int vtkParticleReader::GetDataByteOrder() { #ifdef VTK_WORDS_BIGENDIAN if ( this->SwapBytes ) { return VTK_FILE_BYTE_ORDER_LITTLE_ENDIAN; } else { return VTK_FILE_BYTE_ORDER_BIG_ENDIAN; } #else if ( this->SwapBytes ) { return VTK_FILE_BYTE_ORDER_BIG_ENDIAN; } else { return VTK_FILE_BYTE_ORDER_LITTLE_ENDIAN; } #endif } //---------------------------------------------------------------------------- const char *vtkParticleReader::GetDataByteOrderAsString() { #ifdef VTK_WORDS_BIGENDIAN if ( this->SwapBytes ) { return "LittleEndian"; } else { return "BigEndian"; } #else if ( this->SwapBytes ) { return "BigEndian"; } else { return "LittleEndian"; } #endif } //---------------------------------------------------------------------------- void vtkParticleReader::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); os << indent << "FileName: " << (this->FileName ? this->FileName : "(none)") << "\n"; os << indent << "Swap Bytes: " << (this->SwapBytes ? "On\n" : "Off\n"); os << indent << "Has Scalar: " << (this->HasScalar ? "On\n" : "Off\n"); switch ( this->FileType ) { case FILE_TYPE_IS_UNKNOWN: os << indent << "File type is unknown (The class automatically determines the file type).\n"; break; case FILE_TYPE_IS_TEXT: os << indent << "File type is text.\n"; break; case FILE_TYPE_IS_BINARY: os << indent << "File type is binary.\n"; break; default: os << indent << "File type should never have this value: " << this->FileType << "\n"; break; } switch ( this->DataType ) { case VTK_FLOAT: os << indent << "Data type is float.\n"; break; case VTK_DOUBLE: os << indent << "Data type is double.\n"; break; default: os << indent << "Data type should never have this value: " << this->DataType << "\n"; break; } os << indent << "NumberOfPoints: " << this->NumberOfPoints << "\n"; os << indent << "Alliquot: " << (unsigned int)this->Alliquot << "\n"; os << indent << "Count: " << (unsigned int)this->Count << "\n"; }