Commit 91ae34bd authored by Andrew Maclean's avatar Andrew Maclean
Browse files

ENH: The functionality of vktParticleReader has been extended as follows:

1) Either ASCII text files or binary data files of particles can be read.
2) The file type is automatically detected (this can be overridden by the user).
3) A particle’s scalar value is now optional.
4) The data types that can be read are now float or double.
5) Progress updates are provided.
6) vtkSmartPointer is used.
parent 395383c1
......@@ -14,40 +14,109 @@
=========================================================================*/
#include "vtkParticleReader.h"
#include "vtkByteSwap.h"
#include "vtkCellArray.h"
#include "vtkFloatArray.h"
#include <sstream>
#include <algorithm>
#include <vector>
#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 "vtkStreamingDemandDrivenPipeline.h"
#include "vtkCellArray.h"
vtkCxxRevisionMacro(vtkParticleReader, "1.21");
#include "vtkDataArray.h"
#include "vtkDoubleArray.h"
#include "vtkFloatArray.h"
vtkCxxRevisionMacro(vtkParticleReader, "1.22");
vtkStandardNewMacro(vtkParticleReader);
// These are copied right from vtkImageReader.
// I do not know what they do.
#ifdef read
#undef read
#endif
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.
vtkstd::istringstream is(s);// is.str(s);
is >> val[0] >> val[1] >> val[2] >> val[3];
is.clear();
return 1;
}
#ifdef close
#undef close
#endif
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()
vtkParticleReader::vtkParticleReader() :
FileName(NULL)
, File(NULL)
, HasScalar(1)
, FileType(FILE_TYPE_IS_UNKNOWN)
, DataType(VTK_FLOAT)
, alliquot(0)
, count(0)
{
this->FileName = NULL;
this->File = NULL;
this->SwapBytes = 0;
this->SetDataByteOrderToBigEndian();
this->NumberOfPoints = 0;
this->SetNumberOfInputPorts(0);
}
......@@ -77,7 +146,7 @@ void vtkParticleReader::OpenFile()
return;
}
// Close file from any previous image
// If the file was open close it.
if (this->File)
{
this->File->close();
......@@ -85,7 +154,7 @@ void vtkParticleReader::OpenFile()
this->File = NULL;
}
// Open the new file
// Open the new file.
vtkDebugMacro(<< "Initialize: opening file " << this->FileName);
#ifdef _WIN32
this->File = new ifstream(this->FileName, ios::in | ios::binary);
......@@ -101,7 +170,6 @@ void vtkParticleReader::OpenFile()
}
//----------------------------------------------------------------------------
// This method returns the largest data that can be generated.
int vtkParticleReader::RequestInformation(
vtkInformation *vtkNotUsed(request),
vtkInformationVector **vtkNotUsed(inputVector),
......@@ -117,26 +185,321 @@ int vtkParticleReader::RequestInformation(
}
//----------------------------------------------------------------------------
// This method returns the largest data that can be generated.
int vtkParticleReader::RequestData(
vtkInformation *vtkNotUsed(request),
vtkInformationVector **vtkNotUsed(inputVector),
vtkInformationVector *outputVector)
{
// get the info object
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 <unsigned char> 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 charsCounted = 0;
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++;
charsCounted++;
continue;
}
// Conventional ASCII characters.
if ( s[j] > 0x1f && s[j] < 0x80 )
{
conventionalASCII++;
charsCounted++;
continue;
}
// Extended ASCII characters may have been used.
if ( s[j] > 0x7f )
{
extendedASCII++;
charsCounted++;
continue;
}
// Control characters.
if ( s[j] == '\n' || s[j] == '\r' || s[j] == '\t' || s[j] == '\f' )
{
controlASCII++;
charsCounted++;
continue;
}
otherASCII++;
charsCounted++;
}
// cout << "Zero: " << zero << " Conventional ASCII:" << conventionalASCII <<
// " Extended ASCII: " << extendedASCII << " Control ASCII: " << controlASCII <<
// " Other characters: " << otherASCII <<
// " Total counted: " << charsCounted << endl;
// 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;
else
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<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
points->SetDataTypeToDouble();
points->Reset();
vtkSmartPointer<vtkDoubleArray> scalars = vtkSmartPointer<vtkDoubleArray>::New();
scalars->Reset();
scalars->SetName("Scalar");
this->File->seekg(0,ios::beg);
this->alliquot = fileLength / quantum;
this->count = 1;
ParseLine<double> pl;
while ( vtkstd::getline(*this->File,s,'\n') )
{
if ( s.size() != 0 )
{
bytesRead += s.size();
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<vtkCellArray> vertices = vtkSmartPointer<vtkCellArray>::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<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
points->SetDataTypeToFloat();
points->Reset();
vtkSmartPointer<vtkFloatArray> scalars = vtkSmartPointer<vtkFloatArray>::New();
scalars->Reset();
scalars->SetName("Scalar");
this->File->seekg(0,ios::beg);
this->alliquot = fileLength / quantum;
this->count = 1;
ParseLine<float> pl;
while ( vtkstd::getline(*this->File,s,'\n') )
{
if ( s.size() != 0 )
{
bytesRead += s.size();
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<vtkCellArray> vertices = vtkSmartPointer<vtkCellArray>::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)
{
vtkPoints *points;
vtkFloatArray *array;
vtkCellArray *verts;
unsigned long fileLength, start, next, length, ptIdx, cellPtIdx;
unsigned long cellLength;
int piece, numPieces;
float *data, *ptr;
double *data, *ptr;
if (!this->FileName)
{
......@@ -155,31 +518,44 @@ int vtkParticleReader::RequestData(
}
fileLength = (unsigned long)this->File->tellg();
this->NumberOfPoints = fileLength / (4 * sizeof(float));
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)
if ((unsigned long)numPieces > this->numberOfPoints)
{
numPieces = (int)(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;
start = piece * this->numberOfPoints / numPieces;
next = (piece+1) * this->numberOfPoints / numPieces;
length = next - start;
data = new float[length * 4];
if ( this->HasScalar )
data = new double[length * 4];
else
data = new double[length * 3];
// Seek to the first point in the file.
this->File->seekg(start*4*sizeof(float), ios::beg);
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);
......@@ -188,41 +564,250 @@ int vtkParticleReader::RequestData(
}
// Read the data.
this->File->read((char *)data, length*4*sizeof(float));
if ( static_cast<unsigned long>(this->File->gcount()) !=
static_cast<unsigned long>(length*4*sizeof(float))
// On apple read to eof returns fail
if ( this->HasScalar )
{
this->File->read((char *)data, length*4*sizeof(double));
if ( static_cast<unsigned long>(this->File->gcount()) !=
static_cast<unsigned long>(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<unsigned long>(this->File->gcount()) !=
static_cast<unsigned long>(length*3*sizeof(double))
// On apple read to eof returns fail
#ifndef __APPLE_CC__
|| this->File->fail()
|| 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<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
points->SetNumberOfPoints(length);
vtkSmartPointer<vtkFloatArray> array = vtkSmartPointer<vtkFloatArray>::New();
array->SetName("Scalar");
vtkSmartPointer<vtkCellArray> verts = vtkSmartPointer<vtkCellArray>::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 count = 1;
double len = length;
while (length > 0)
{
vtkErrorMacro("Could not read points: " << start
<< " to " << next-1);
if ( count % 10 == 0 )
this->UpdateProgress(0.5+((count * 1000.0)/len)/2.0);
count++;
cellLength = 1000;
if (cellLength > length)
{
cellLength = length;
}
length = length - cellLength;
verts->InsertNextCell((int)cellLength);