mirror of https://gitee.com/openkylin/vtk9.git
3148 lines
90 KiB
C++
3148 lines
90 KiB
C++
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: vtkXMLWriter.cxx
|
|
|
|
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 "vtkXMLWriter.h"
|
|
|
|
#include "vtkAOSDataArrayTemplate.h"
|
|
#include "vtkArrayDispatch.h"
|
|
#include "vtkArrayIteratorIncludes.h"
|
|
#include "vtkBase64OutputStream.h"
|
|
#include "vtkBitArray.h"
|
|
#include "vtkByteSwap.h"
|
|
#include "vtkCellData.h"
|
|
#include "vtkCommand.h"
|
|
#include "vtkDataArray.h"
|
|
#include "vtkDataSet.h"
|
|
#include "vtkDoubleArray.h"
|
|
#include "vtkEndian.h"
|
|
#include "vtkErrorCode.h"
|
|
#include "vtkInformation.h"
|
|
#include "vtkInformationDoubleKey.h"
|
|
#include "vtkInformationDoubleVectorKey.h"
|
|
#include "vtkInformationIdTypeKey.h"
|
|
#include "vtkInformationIntegerKey.h"
|
|
#include "vtkInformationIntegerVectorKey.h"
|
|
#include "vtkInformationIterator.h"
|
|
#include "vtkInformationKeyLookup.h"
|
|
#include "vtkInformationStringKey.h"
|
|
#include "vtkInformationStringVectorKey.h"
|
|
#include "vtkInformationUnsignedLongKey.h"
|
|
#include "vtkInformationVector.h"
|
|
#include "vtkNew.h"
|
|
#include "vtkOutputStream.h"
|
|
#include "vtkPointData.h"
|
|
#include "vtkPoints.h"
|
|
#include "vtkStdString.h"
|
|
#include "vtkStreamingDemandDrivenPipeline.h"
|
|
#include "vtkUnsignedCharArray.h"
|
|
#include "vtkZLibDataCompressor.h"
|
|
#define vtkXMLOffsetsManager_DoNotInclude
|
|
#include "vtkXMLOffsetsManager.h"
|
|
#undef vtkXMLOffsetsManager_DoNotInclude
|
|
#define vtkXMLDataHeaderPrivate_DoNotInclude
|
|
#include "vtkXMLDataHeaderPrivate.h"
|
|
#undef vtkXMLDataHeaderPrivate_DoNotInclude
|
|
#include "vtkInformationQuadratureSchemeDefinitionVectorKey.h"
|
|
#include "vtkInformationStringKey.h"
|
|
#include "vtkNumberToString.h"
|
|
#include "vtkQuadratureSchemeDefinition.h"
|
|
#include "vtkXMLDataElement.h"
|
|
#include "vtkXMLReaderVersion.h"
|
|
#include "vtksys/Encoding.hxx"
|
|
#include "vtksys/FStream.hxx"
|
|
#include <memory>
|
|
|
|
#include <cassert>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
#if !defined(_WIN32) || defined(__CYGWIN__)
|
|
#include <unistd.h> /* unlink */
|
|
#else
|
|
#include <io.h> /* unlink */
|
|
#endif
|
|
|
|
#include <cctype> // for isalnum
|
|
#include <locale> // C++ locale
|
|
|
|
//*****************************************************************************
|
|
// Friend class to enable access for template functions to the protected
|
|
// writer methods.
|
|
class vtkXMLWriterHelper
|
|
{
|
|
public:
|
|
static inline void SetProgressPartial(vtkXMLWriter* writer, double progress)
|
|
{
|
|
writer->SetProgressPartial(progress);
|
|
}
|
|
static inline int WriteBinaryDataBlock(
|
|
vtkXMLWriter* writer, unsigned char* in_data, size_t numWords, int wordType)
|
|
{
|
|
return writer->WriteBinaryDataBlock(in_data, numWords, wordType);
|
|
}
|
|
static inline void* GetInt32IdTypeBuffer(vtkXMLWriter* writer)
|
|
{
|
|
return static_cast<void*>(writer->Int32IdTypeBuffer);
|
|
}
|
|
static inline unsigned char* GetByteSwapBuffer(vtkXMLWriter* writer)
|
|
{
|
|
return writer->ByteSwapBuffer;
|
|
}
|
|
};
|
|
|
|
namespace
|
|
{
|
|
|
|
struct WriteBinaryDataBlockWorker
|
|
{
|
|
vtkXMLWriter* Writer;
|
|
int WordType;
|
|
size_t MemWordSize;
|
|
size_t OutWordSize;
|
|
size_t NumWords;
|
|
bool Result;
|
|
|
|
WriteBinaryDataBlockWorker(
|
|
vtkXMLWriter* writer, int wordType, size_t memWordSize, size_t outWordSize, size_t numWords)
|
|
: Writer(writer)
|
|
, WordType(wordType)
|
|
, MemWordSize(memWordSize)
|
|
, OutWordSize(outWordSize)
|
|
, NumWords(numWords)
|
|
, Result(false)
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Specialize for AoS arrays.
|
|
template <class ValueType>
|
|
void operator()(vtkAOSDataArrayTemplate<ValueType>* array)
|
|
{
|
|
// Get the raw pointer to the array data:
|
|
ValueType* iter = array->GetPointer(0);
|
|
|
|
// generic implementation for fixed component length arrays.
|
|
size_t blockWords = this->Writer->GetBlockSize() / this->OutWordSize;
|
|
size_t memBlockSize = blockWords * this->MemWordSize;
|
|
|
|
// Prepare a pointer and counter to move through the data.
|
|
unsigned char* ptr = reinterpret_cast<unsigned char*>(iter);
|
|
size_t wordsLeft = this->NumWords;
|
|
|
|
// Do the complete blocks.
|
|
vtkXMLWriterHelper::SetProgressPartial(this->Writer, 0);
|
|
this->Result = true;
|
|
while (this->Result && (wordsLeft >= blockWords))
|
|
{
|
|
if (!vtkXMLWriterHelper::WriteBinaryDataBlock(this->Writer, ptr, blockWords, this->WordType))
|
|
{
|
|
this->Result = false;
|
|
}
|
|
ptr += memBlockSize;
|
|
wordsLeft -= blockWords;
|
|
vtkXMLWriterHelper::SetProgressPartial(
|
|
this->Writer, static_cast<float>(this->NumWords - wordsLeft) / this->NumWords);
|
|
}
|
|
|
|
// Do the last partial block if any.
|
|
if (this->Result && (wordsLeft > 0))
|
|
{
|
|
if (!vtkXMLWriterHelper::WriteBinaryDataBlock(this->Writer, ptr, wordsLeft, this->WordType))
|
|
{
|
|
this->Result = false;
|
|
}
|
|
}
|
|
vtkXMLWriterHelper::SetProgressPartial(this->Writer, 1);
|
|
}
|
|
|
|
#if defined(__clang__) && defined(__has_warning)
|
|
#if __has_warning("-Wunused-template")
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wunused-template"
|
|
#endif
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Specialize for vtkBitArray
|
|
void operator()(vtkBitArray* array)
|
|
{
|
|
// Get the raw pointer to the bit array data:
|
|
unsigned char* data = array->GetPointer(0);
|
|
|
|
// generic implementation for fixed component length arrays.
|
|
size_t blockSize = this->Writer->GetBlockSize();
|
|
|
|
// Prepare a pointer and counter to move through the data.
|
|
unsigned char* ptr = reinterpret_cast<unsigned char*>(data);
|
|
size_t totalBytes = (this->NumWords + 7) / 8;
|
|
size_t bytesLeft = totalBytes;
|
|
|
|
// Write out complete blocks.
|
|
vtkXMLWriterHelper::SetProgressPartial(this->Writer, 0);
|
|
this->Result = true;
|
|
while (this->Result && (bytesLeft >= blockSize))
|
|
{
|
|
this->Result =
|
|
vtkXMLWriterHelper::WriteBinaryDataBlock(this->Writer, ptr, blockSize, this->WordType) != 0;
|
|
ptr += blockSize;
|
|
bytesLeft -= blockSize;
|
|
vtkXMLWriterHelper::SetProgressPartial(
|
|
this->Writer, 1.f - static_cast<float>(bytesLeft) / totalBytes);
|
|
}
|
|
|
|
// Do the last partial block if any.
|
|
if (this->Result && (bytesLeft > 0))
|
|
{
|
|
this->Result =
|
|
vtkXMLWriterHelper::WriteBinaryDataBlock(this->Writer, ptr, bytesLeft, this->WordType) != 0;
|
|
}
|
|
vtkXMLWriterHelper::SetProgressPartial(this->Writer, 1);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Specialize for non-AoS generic arrays:
|
|
template <class DerivedType, typename ValueType>
|
|
void operator()(vtkGenericDataArray<DerivedType, ValueType>* array)
|
|
{
|
|
// generic implementation for fixed component length arrays.
|
|
size_t blockWords = this->Writer->GetBlockSize() / this->OutWordSize;
|
|
|
|
// Prepare a buffer to move through the data.
|
|
std::vector<unsigned char> buffer(blockWords * this->MemWordSize);
|
|
size_t wordsLeft = this->NumWords;
|
|
|
|
if (buffer.empty())
|
|
{
|
|
// No data -- bail here, since the calls to buffer[0] below will segfault.
|
|
this->Result = false;
|
|
return;
|
|
}
|
|
|
|
// Do the complete blocks.
|
|
vtkXMLWriterHelper::SetProgressPartial(this->Writer, 0);
|
|
this->Result = true;
|
|
vtkIdType valueIdx = 0;
|
|
while (this->Result && (wordsLeft >= blockWords))
|
|
{
|
|
// Copy data to contiguous buffer:
|
|
ValueType* bufferIter = reinterpret_cast<ValueType*>(&buffer[0]);
|
|
for (size_t i = 0; i < blockWords; ++i, ++valueIdx)
|
|
{
|
|
*bufferIter++ = array->GetValue(valueIdx);
|
|
}
|
|
|
|
if (!vtkXMLWriterHelper::WriteBinaryDataBlock(
|
|
this->Writer, &buffer[0], blockWords, this->WordType))
|
|
{
|
|
this->Result = false;
|
|
}
|
|
wordsLeft -= blockWords;
|
|
vtkXMLWriterHelper::SetProgressPartial(
|
|
this->Writer, static_cast<float>(this->NumWords - wordsLeft) / this->NumWords);
|
|
}
|
|
|
|
// Do the last partial block if any.
|
|
if (this->Result && (wordsLeft > 0))
|
|
{
|
|
ValueType* bufferIter = reinterpret_cast<ValueType*>(&buffer[0]);
|
|
for (size_t i = 0; i < wordsLeft; ++i, ++valueIdx)
|
|
{
|
|
*bufferIter++ = array->GetValue(valueIdx);
|
|
}
|
|
|
|
if (!vtkXMLWriterHelper::WriteBinaryDataBlock(
|
|
this->Writer, &buffer[0], wordsLeft, this->WordType))
|
|
{
|
|
this->Result = false;
|
|
}
|
|
}
|
|
|
|
vtkXMLWriterHelper::SetProgressPartial(this->Writer, 1);
|
|
}
|
|
|
|
// Undo warning suppression.
|
|
#if defined(__clang__) && defined(__has_warning)
|
|
#if __has_warning("-Wunused-template")
|
|
#pragma clang diagnostic pop
|
|
#endif
|
|
#endif
|
|
|
|
}; // End WriteBinaryDataBlockWorker
|
|
|
|
namespace
|
|
{
|
|
//------------------------------------------------------------------------------
|
|
// Specialize for vtkDataArrays, which implicitly cast everything to double:
|
|
template <class ValueType>
|
|
void WriteDataArrayFallback(ValueType*, vtkDataArray* array, WriteBinaryDataBlockWorker& worker)
|
|
{
|
|
// generic implementation for fixed component length arrays.
|
|
size_t blockWords = worker.Writer->GetBlockSize() / worker.OutWordSize;
|
|
|
|
// Prepare a buffer to move through the data.
|
|
std::vector<unsigned char> buffer(blockWords * worker.MemWordSize);
|
|
size_t wordsLeft = worker.NumWords;
|
|
|
|
if (buffer.empty())
|
|
{
|
|
// No data -- bail here, since the calls to buffer[0] below will segfault.
|
|
worker.Result = false;
|
|
return;
|
|
}
|
|
|
|
vtkIdType nComponents = array->GetNumberOfComponents();
|
|
|
|
// Do the complete blocks.
|
|
vtkXMLWriterHelper::SetProgressPartial(worker.Writer, 0);
|
|
worker.Result = true;
|
|
vtkIdType valueIdx = 0;
|
|
while (worker.Result && (wordsLeft >= blockWords))
|
|
{
|
|
// Copy data to contiguous buffer:
|
|
ValueType* bufferIter = reinterpret_cast<ValueType*>(&buffer[0]);
|
|
for (size_t i = 0; i < blockWords; ++i, ++valueIdx)
|
|
{
|
|
*bufferIter++ =
|
|
static_cast<ValueType>(array->GetComponent(valueIdx / nComponents, valueIdx % nComponents));
|
|
}
|
|
|
|
if (!vtkXMLWriterHelper::WriteBinaryDataBlock(
|
|
worker.Writer, &buffer[0], blockWords, worker.WordType))
|
|
{
|
|
worker.Result = false;
|
|
}
|
|
wordsLeft -= blockWords;
|
|
vtkXMLWriterHelper::SetProgressPartial(
|
|
worker.Writer, static_cast<float>(worker.NumWords - wordsLeft) / worker.NumWords);
|
|
}
|
|
|
|
// Do the last partial block if any.
|
|
if (worker.Result && (wordsLeft > 0))
|
|
{
|
|
ValueType* bufferIter = reinterpret_cast<ValueType*>(&buffer[0]);
|
|
for (size_t i = 0; i < wordsLeft; ++i, ++valueIdx)
|
|
{
|
|
*bufferIter++ =
|
|
static_cast<ValueType>(array->GetComponent(valueIdx / nComponents, valueIdx % nComponents));
|
|
}
|
|
|
|
if (!vtkXMLWriterHelper::WriteBinaryDataBlock(
|
|
worker.Writer, &buffer[0], wordsLeft, worker.WordType))
|
|
{
|
|
worker.Result = false;
|
|
}
|
|
}
|
|
|
|
vtkXMLWriterHelper::SetProgressPartial(worker.Writer, 1);
|
|
}
|
|
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Specialize for string arrays:
|
|
int vtkXMLWriterWriteBinaryDataBlocks(vtkXMLWriter* writer,
|
|
vtkArrayIteratorTemplate<vtkStdString>* iter, int wordType, size_t outWordSize, size_t numStrings,
|
|
int)
|
|
{
|
|
vtkXMLWriterHelper::SetProgressPartial(writer, 0);
|
|
vtkStdString::value_type* allocated_buffer = nullptr;
|
|
vtkStdString::value_type* temp_buffer = nullptr;
|
|
if (vtkXMLWriterHelper::GetInt32IdTypeBuffer(writer))
|
|
{
|
|
temp_buffer =
|
|
reinterpret_cast<vtkStdString::value_type*>(vtkXMLWriterHelper::GetInt32IdTypeBuffer(writer));
|
|
}
|
|
else if (vtkXMLWriterHelper::GetByteSwapBuffer(writer))
|
|
{
|
|
temp_buffer =
|
|
reinterpret_cast<vtkStdString::value_type*>(vtkXMLWriterHelper::GetByteSwapBuffer(writer));
|
|
}
|
|
else
|
|
{
|
|
allocated_buffer = new vtkStdString::value_type[writer->GetBlockSize() / outWordSize];
|
|
temp_buffer = allocated_buffer;
|
|
}
|
|
|
|
// For string arrays, writing as binary requires that the strings are written
|
|
// out into a contiguous block. This is essential since the compressor can
|
|
// only compress complete blocks of data.
|
|
size_t maxCharsPerBlock = writer->GetBlockSize() / outWordSize;
|
|
|
|
size_t index = 0; // index in string array.
|
|
int result = 1;
|
|
vtkIdType stringOffset = 0; // num of chars of string written in previous block.
|
|
// this is required since a string may not fit completely in a block.
|
|
|
|
while (result && index < numStrings) // write one block at a time.
|
|
{
|
|
size_t cur_offset = 0; // offset into the temp_buffer.
|
|
while (index < numStrings && cur_offset < maxCharsPerBlock)
|
|
{
|
|
vtkStdString& str = iter->GetValue(static_cast<vtkIdType>(index));
|
|
vtkStdString::size_type length = str.size();
|
|
const char* data = str.c_str();
|
|
data += stringOffset; // advance by the chars already written.
|
|
length -= stringOffset;
|
|
if (length == 0)
|
|
{
|
|
// just write the string termination char.
|
|
temp_buffer[cur_offset++] = 0x0;
|
|
stringOffset = 0;
|
|
index++; // advance to the next string
|
|
}
|
|
else
|
|
{
|
|
size_t new_offset = cur_offset + length + 1; // (+1) for termination char.
|
|
if (new_offset <= maxCharsPerBlock)
|
|
{
|
|
memcpy(&temp_buffer[cur_offset], data, length);
|
|
cur_offset += length;
|
|
temp_buffer[cur_offset++] = 0x0;
|
|
stringOffset = 0;
|
|
index++; // advance to the next string
|
|
}
|
|
else
|
|
{
|
|
size_t bytes_to_copy = (maxCharsPerBlock - cur_offset);
|
|
stringOffset += static_cast<vtkIdType>(bytes_to_copy);
|
|
memcpy(&temp_buffer[cur_offset], data, bytes_to_copy);
|
|
cur_offset += bytes_to_copy;
|
|
// do not advance, only partially written current string
|
|
}
|
|
}
|
|
}
|
|
if (cur_offset > 0)
|
|
{
|
|
// We have a block of data to write.
|
|
result = vtkXMLWriterHelper::WriteBinaryDataBlock(
|
|
writer, reinterpret_cast<unsigned char*>(temp_buffer), cur_offset, wordType);
|
|
vtkXMLWriterHelper::SetProgressPartial(writer, static_cast<float>(index) / numStrings);
|
|
}
|
|
}
|
|
|
|
delete[] allocated_buffer;
|
|
allocated_buffer = nullptr;
|
|
|
|
vtkXMLWriterHelper::SetProgressPartial(writer, 1);
|
|
return result;
|
|
}
|
|
|
|
} // end anon namespace
|
|
//*****************************************************************************
|
|
|
|
//------------------------------------------------------------------------------
|
|
vtkXMLWriter::vtkXMLWriter()
|
|
{
|
|
this->Stream = nullptr;
|
|
|
|
// Default binary data mode is base-64 encoding.
|
|
this->DataStream = vtkBase64OutputStream::New();
|
|
|
|
// Initialize compression data.
|
|
this->CompressionHeader = nullptr;
|
|
this->Int32IdTypeBuffer = nullptr;
|
|
this->ByteSwapBuffer = nullptr;
|
|
|
|
this->AppendedDataPosition = 0;
|
|
this->ProgressRange[0] = 0;
|
|
this->ProgressRange[1] = 1;
|
|
|
|
this->SetNumberOfOutputPorts(0);
|
|
this->SetNumberOfInputPorts(1);
|
|
|
|
this->OutFile = nullptr;
|
|
this->OutStringStream = nullptr;
|
|
|
|
// Time support
|
|
this->NumberOfTimeSteps = 1;
|
|
this->CurrentTimeIndex = 0;
|
|
this->UserContinueExecuting = -1; // invalid state
|
|
this->NumberOfTimeValues = nullptr;
|
|
this->FieldDataOM = new OffsetsManagerGroup;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
vtkXMLWriter::~vtkXMLWriter()
|
|
{
|
|
this->DataStream->Delete();
|
|
delete this->OutFile;
|
|
this->OutFile = nullptr;
|
|
delete this->OutStringStream;
|
|
this->OutStringStream = nullptr;
|
|
delete this->FieldDataOM;
|
|
delete[] this->NumberOfTimeValues;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os, indent);
|
|
if (this->Stream)
|
|
{
|
|
os << indent << "Stream: " << this->Stream << "\n";
|
|
}
|
|
else
|
|
{
|
|
os << indent << "Stream: (none)\n";
|
|
}
|
|
os << indent << "NumberOfTimeSteps:" << this->NumberOfTimeSteps << "\n";
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::SetInputData(vtkDataObject* input)
|
|
{
|
|
this->SetInputData(0, input);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::SetInputData(int index, vtkDataObject* input)
|
|
{
|
|
this->SetInputDataInternal(index, input);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
vtkDataObject* vtkXMLWriter::GetInput(int port)
|
|
{
|
|
if (this->GetNumberOfInputConnections(port) < 1)
|
|
{
|
|
return nullptr;
|
|
}
|
|
return this->GetExecutive()->GetInputData(port, 0);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
vtkTypeBool vtkXMLWriter::ProcessRequest(
|
|
vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
|
|
{
|
|
// generate the data
|
|
if (request->Has(vtkDemandDrivenPipeline::REQUEST_DATA()))
|
|
{
|
|
return this->RequestData(request, inputVector, outputVector);
|
|
}
|
|
|
|
return this->Superclass::ProcessRequest(request, inputVector, outputVector);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::RequestInformation(vtkInformation* vtkNotUsed(request),
|
|
vtkInformationVector** inputVector, vtkInformationVector* vtkNotUsed(outputVector))
|
|
{
|
|
vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
|
|
if (inInfo->Has(vtkStreamingDemandDrivenPipeline::TIME_STEPS()))
|
|
{
|
|
this->NumberOfTimeSteps = inInfo->Length(vtkStreamingDemandDrivenPipeline::TIME_STEPS());
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::RequestData(vtkInformation* vtkNotUsed(request),
|
|
vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* vtkNotUsed(outputVector))
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::NoError);
|
|
|
|
// Make sure we have a file to write.
|
|
if (!this->Stream && !this->FileName && !this->WriteToOutputString)
|
|
{
|
|
vtkErrorMacro("Writer called with no FileName set.");
|
|
this->SetErrorCode(vtkErrorCode::NoFileNameError);
|
|
return 0;
|
|
}
|
|
|
|
// We are just starting to write. Do not call
|
|
// UpdateProgressDiscrete because we want a 0 progress callback the
|
|
// first time.
|
|
this->UpdateProgress(0);
|
|
|
|
// Initialize progress range to entire 0..1 range.
|
|
float wholeProgressRange[2] = { 0.f, 1.f };
|
|
this->SetProgressRange(wholeProgressRange, 0, 1);
|
|
|
|
// Check input validity and call the real writing code.
|
|
int result = this->WriteInternal();
|
|
|
|
// If writing failed, delete the file.
|
|
if (!result)
|
|
{
|
|
vtkErrorMacro("Ran out of disk space; deleting file: " << this->FileName);
|
|
this->DeleteAFile();
|
|
}
|
|
|
|
// We have finished writing.
|
|
this->UpdateProgressDiscrete(1);
|
|
|
|
return result;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::OpenStream()
|
|
{
|
|
if (this->Stream)
|
|
{
|
|
// Rewind stream to the beginning.
|
|
this->Stream->seekp(0);
|
|
}
|
|
else
|
|
{
|
|
if (this->WriteToOutputString)
|
|
{
|
|
if (!this->OpenString())
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!this->OpenFile())
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make sure sufficient precision is used in the ascii
|
|
// representation of data and meta-data.
|
|
this->Stream->precision(11);
|
|
|
|
// Setup the output streams.
|
|
this->DataStream->SetStream(this->Stream);
|
|
|
|
return 1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::OpenFile()
|
|
{
|
|
delete this->OutFile;
|
|
this->OutFile = nullptr;
|
|
|
|
// Strip trailing whitespace from the filename.
|
|
int len = static_cast<int>(strlen(this->FileName));
|
|
for (int i = len - 1; i >= 0; i--)
|
|
{
|
|
if (isalnum(this->FileName[i]))
|
|
{
|
|
break;
|
|
}
|
|
this->FileName[i] = 0;
|
|
}
|
|
|
|
std::ios_base::openmode mode = ios::out;
|
|
#ifdef _WIN32
|
|
mode |= ios::binary;
|
|
#endif
|
|
this->OutFile = new vtksys::ofstream(this->FileName, mode);
|
|
if (!this->OutFile || !*this->OutFile)
|
|
{
|
|
vtkErrorMacro("Error opening output file \"" << this->FileName << "\"");
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
vtkErrorMacro(
|
|
"Error code \"" << vtkErrorCode::GetStringFromErrorCode(this->GetErrorCode()) << "\"");
|
|
return 0;
|
|
}
|
|
this->Stream = this->OutFile;
|
|
|
|
return 1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::OpenString()
|
|
{
|
|
delete this->OutStringStream;
|
|
this->OutStringStream = new std::ostringstream();
|
|
this->Stream = this->OutStringStream;
|
|
|
|
return 1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::CloseStream()
|
|
{
|
|
// Cleanup the output streams.
|
|
this->DataStream->SetStream(nullptr);
|
|
|
|
if (this->WriteToOutputString)
|
|
{
|
|
this->CloseString();
|
|
}
|
|
else
|
|
{
|
|
this->CloseFile();
|
|
}
|
|
|
|
this->Stream = nullptr;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::CloseFile()
|
|
{
|
|
if (this->OutFile)
|
|
{
|
|
// We opened a file. Close it.
|
|
delete this->OutFile;
|
|
this->OutFile = nullptr;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::CloseString()
|
|
{
|
|
if (this->OutStringStream)
|
|
{
|
|
this->OutputString = this->OutStringStream->str();
|
|
delete this->OutStringStream;
|
|
this->OutStringStream = nullptr;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::WriteInternal()
|
|
{
|
|
if (!this->OpenStream())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
(*this->Stream).imbue(std::locale::classic());
|
|
|
|
// Tell the subclass to write the data.
|
|
int result = this->WriteData();
|
|
|
|
// if user manipulate execution don't try closing file
|
|
if (this->UserContinueExecuting != 1)
|
|
{
|
|
this->CloseStream();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
vtkDataSet* vtkXMLWriter::GetInputAsDataSet()
|
|
{
|
|
return static_cast<vtkDataSet*>(this->GetInput());
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::StartFile()
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
|
|
// If this will really be a valid XML file, put the XML header at
|
|
// the top.
|
|
if (this->EncodeAppendedData)
|
|
{
|
|
os << "<?xml version=\"1.0\"?>\n";
|
|
}
|
|
|
|
os.imbue(std::locale::classic());
|
|
|
|
// Open the document-level element. This will contain the rest of
|
|
// the elements.
|
|
os << "<VTKFile";
|
|
this->WriteFileAttributes();
|
|
os << ">\n";
|
|
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteFileAttributes()
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
|
|
// Write the file's type.
|
|
this->WriteStringAttribute("type", this->GetDataSetName());
|
|
|
|
// Write the version number of the file.
|
|
os << " version=\"" << this->GetDataSetMajorVersion() << "." << this->GetDataSetMinorVersion()
|
|
<< "\"";
|
|
|
|
// Write the byte order for the file.
|
|
if (this->ByteOrder == vtkXMLWriter::BigEndian)
|
|
{
|
|
os << " byte_order=\"BigEndian\"";
|
|
}
|
|
else
|
|
{
|
|
os << " byte_order=\"LittleEndian\"";
|
|
}
|
|
|
|
// Write the header type for binary data.
|
|
if (this->HeaderType == vtkXMLWriter::UInt64)
|
|
{
|
|
os << " header_type=\"UInt64\"";
|
|
}
|
|
else
|
|
{
|
|
os << " header_type=\"UInt32\"";
|
|
}
|
|
|
|
// Write the compressor that will be used for the file.
|
|
if (this->Compressor)
|
|
{
|
|
os << " compressor=\"" << this->Compressor->GetClassName() << "\"";
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::EndFile()
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
|
|
// Close the document-level element.
|
|
os << "</VTKFile>\n";
|
|
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::DeleteAFile()
|
|
{
|
|
if (!this->Stream && this->FileName)
|
|
{
|
|
this->DeleteAFile(this->FileName);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::DeleteAFile(const char* name)
|
|
{
|
|
unlink(name);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::StartAppendedData()
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
os << " <AppendedData encoding=\"" << (this->EncodeAppendedData ? "base64" : "raw") << "\">\n";
|
|
os << " _";
|
|
this->AppendedDataPosition = os.tellp();
|
|
|
|
// Setup proper output encoding.
|
|
if (this->EncodeAppendedData)
|
|
{
|
|
vtkBase64OutputStream* base64 = vtkBase64OutputStream::New();
|
|
this->SetDataStream(base64);
|
|
base64->Delete();
|
|
}
|
|
else
|
|
{
|
|
vtkOutputStream* raw = vtkOutputStream::New();
|
|
this->SetDataStream(raw);
|
|
raw->Delete();
|
|
}
|
|
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::EndAppendedData()
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
os << "\n";
|
|
os << " </AppendedData>\n";
|
|
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
vtkTypeInt64 vtkXMLWriter::ReserveAttributeSpace(const char* attr, size_t length)
|
|
{
|
|
// Save the starting stream position.
|
|
ostream& os = *(this->Stream);
|
|
vtkTypeInt64 startPosition = os.tellp();
|
|
|
|
// By default write an empty valid xml: attr="". In most case it
|
|
// will be overwritten but we guarantee that the xml produced will
|
|
// be valid in case we stop writing too early.
|
|
os << " " << attr << "=\"\"";
|
|
|
|
// Now reserve space for the value.
|
|
for (size_t i = 0; i < length; ++i)
|
|
{
|
|
os << " ";
|
|
}
|
|
|
|
// Flush the stream to make sure the system tries to write now and
|
|
// test for a write error reported by the system.
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
|
|
// Return the position at which to write the attribute later.
|
|
return startPosition;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
vtkTypeInt64 vtkXMLWriter::GetAppendedDataOffset()
|
|
{
|
|
vtkTypeInt64 pos = this->Stream->tellp();
|
|
return (pos - this->AppendedDataPosition);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteAppendedDataOffset(
|
|
vtkTypeInt64 streamPos, vtkTypeInt64& lastoffset, const char* attr)
|
|
{
|
|
// Write an XML attribute with the given name. The value is the
|
|
// current appended data offset. Starts writing at the given stream
|
|
// position, and returns the ending position. If attr is 0, writes
|
|
// only the double quotes. In all cases, the final stream position
|
|
// is left the same as before the call.
|
|
ostream& os = *(this->Stream);
|
|
vtkTypeInt64 returnPos = os.tellp();
|
|
vtkTypeInt64 offset = returnPos - this->AppendedDataPosition;
|
|
lastoffset = offset; // saving result
|
|
os.seekp(std::streampos(streamPos));
|
|
if (attr)
|
|
{
|
|
os << " " << attr << "=";
|
|
}
|
|
os << "\"" << offset << "\"";
|
|
os.seekp(std::streampos(returnPos));
|
|
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::ForwardAppendedDataOffset(
|
|
vtkTypeInt64 streamPos, vtkTypeInt64 offset, const char* attr)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
std::streampos returnPos = os.tellp();
|
|
os.seekp(std::streampos(streamPos));
|
|
if (attr)
|
|
{
|
|
os << " " << attr << "=";
|
|
}
|
|
os << "\"" << offset << "\"";
|
|
os.seekp(returnPos);
|
|
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::ForwardAppendedDataDouble(vtkTypeInt64 streamPos, double value, const char* attr)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
std::streampos returnPos = os.tellp();
|
|
os.seekp(std::streampos(streamPos));
|
|
if (attr)
|
|
{
|
|
os << " " << attr << "=";
|
|
}
|
|
os << "\"" << value << "\"";
|
|
os.seekp(returnPos);
|
|
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::WriteBinaryData(vtkAbstractArray* a)
|
|
{
|
|
int wordType = a->GetDataType();
|
|
|
|
size_t dataSize;
|
|
if (wordType != VTK_BIT)
|
|
{
|
|
dataSize = this->GetOutputWordTypeSize(wordType) * static_cast<size_t>(a->GetDataSize());
|
|
}
|
|
else
|
|
{ // vtkBitArray returns 0 for GetDataSize:
|
|
dataSize = (a->GetNumberOfValues() + 7) / 8;
|
|
}
|
|
|
|
if (this->Compressor)
|
|
{
|
|
// Need to compress the data. Create compression header. This
|
|
// reserves enough space in the output.
|
|
if (!this->CreateCompressionHeader(dataSize))
|
|
{
|
|
return 0;
|
|
}
|
|
// Start writing the data.
|
|
int result = this->DataStream->StartWriting();
|
|
|
|
// Process the actual data.
|
|
if (result && !this->WriteBinaryDataInternal(a))
|
|
{
|
|
result = 0;
|
|
}
|
|
|
|
// Finish writing the data.
|
|
if (result && !this->DataStream->EndWriting())
|
|
{
|
|
result = 0;
|
|
}
|
|
|
|
// Go back and write the real compression header in its proper place.
|
|
if (result && !this->WriteCompressionHeader())
|
|
{
|
|
result = 0;
|
|
}
|
|
|
|
// Destroy the compression header if it was used.
|
|
delete this->CompressionHeader;
|
|
this->CompressionHeader = nullptr;
|
|
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
// Start writing the data.
|
|
if (!this->DataStream->StartWriting())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// No data compression. The header is just the length of the data.
|
|
std::unique_ptr<vtkXMLDataHeader> uh(vtkXMLDataHeader::New(this->HeaderType, 1));
|
|
if (!uh->Set(0, dataSize))
|
|
{
|
|
vtkErrorMacro("Array \"" << a->GetName() << "\" is too large. Set HeaderType to UInt64.");
|
|
this->SetErrorCode(vtkErrorCode::FileFormatError);
|
|
return 0;
|
|
}
|
|
this->PerformByteSwap(uh->Data(), uh->WordCount(), uh->WordSize());
|
|
int writeRes = this->DataStream->Write(uh->Data(), uh->DataSize());
|
|
this->Stream->flush();
|
|
if (this->Stream->fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
return 0;
|
|
}
|
|
if (!writeRes)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Process the actual data.
|
|
if (!this->WriteBinaryDataInternal(a))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Finish writing the data.
|
|
if (!this->DataStream->EndWriting())
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::WriteBinaryDataInternal(vtkAbstractArray* a)
|
|
{
|
|
// Break into blocks and handle each one separately. This allows
|
|
// for better random access when reading compressed data and saves
|
|
// memory during writing.
|
|
|
|
// The size of the blocks written (before compression) is
|
|
// this->BlockSize. We need to support the possibility that the
|
|
// size of data in memory and the size on disk are different. This
|
|
// is necessary to allow vtkIdType to be converted to UInt32 for
|
|
// writing.
|
|
|
|
int wordType = a->GetDataType();
|
|
size_t memWordSize = this->GetWordTypeSize(wordType);
|
|
size_t outWordSize = this->GetOutputWordTypeSize(wordType);
|
|
|
|
#ifdef VTK_USE_64BIT_IDS
|
|
// If the type is vtkIdType, it may need to be converted to the type
|
|
// requested for output.
|
|
if ((wordType == VTK_ID_TYPE) && (this->IdType == vtkXMLWriter::Int32))
|
|
{
|
|
size_t blockWordsEstimate = this->BlockSize / outWordSize;
|
|
this->Int32IdTypeBuffer = new Int32IdType[blockWordsEstimate];
|
|
}
|
|
#endif
|
|
|
|
// Decide if we need to byte swap.
|
|
#ifdef VTK_WORDS_BIGENDIAN
|
|
if (outWordSize > 1 && this->ByteOrder != vtkXMLWriter::BigEndian)
|
|
#else
|
|
if (outWordSize > 1 && this->ByteOrder != vtkXMLWriter::LittleEndian)
|
|
#endif
|
|
{
|
|
// We need to byte swap. Prepare a buffer large enough for one
|
|
// block.
|
|
if (this->Int32IdTypeBuffer)
|
|
{
|
|
// Just swap in-place in the converted id-type buffer.
|
|
this->ByteSwapBuffer = reinterpret_cast<unsigned char*>(this->Int32IdTypeBuffer);
|
|
}
|
|
else
|
|
{
|
|
// The maximum nlock size if this->BlockSize. The actual data in the block
|
|
// may be lesser.
|
|
this->ByteSwapBuffer = new unsigned char[this->BlockSize];
|
|
}
|
|
}
|
|
int ret;
|
|
|
|
size_t numValues = static_cast<size_t>(a->GetNumberOfComponents() * a->GetNumberOfTuples());
|
|
|
|
if (wordType == VTK_STRING)
|
|
{
|
|
vtkArrayIterator* aiter = a->NewIterator();
|
|
vtkArrayIteratorTemplate<vtkStdString>* iter =
|
|
vtkArrayIteratorTemplate<vtkStdString>::SafeDownCast(aiter);
|
|
if (iter)
|
|
{
|
|
ret = vtkXMLWriterWriteBinaryDataBlocks(this, iter, wordType, outWordSize, numValues, 1);
|
|
}
|
|
else
|
|
{
|
|
vtkWarningMacro("Unsupported iterator for data type : " << wordType);
|
|
ret = 0;
|
|
}
|
|
aiter->Delete();
|
|
}
|
|
else if (vtkDataArray* da = vtkArrayDownCast<vtkDataArray>(a))
|
|
{
|
|
// Create a dispatcher that also handles vtkBitArray:
|
|
using vtkArrayDispatch::Arrays;
|
|
using XMLArrays = vtkTypeList::Append<Arrays, vtkBitArray>::Result;
|
|
using Dispatcher = vtkArrayDispatch::DispatchByArray<XMLArrays>;
|
|
|
|
WriteBinaryDataBlockWorker worker(this, wordType, memWordSize, outWordSize, numValues);
|
|
if (!Dispatcher::Execute(da, worker))
|
|
{
|
|
switch (wordType)
|
|
{
|
|
case VTK_LONG_LONG:
|
|
case VTK_UNSIGNED_LONG_LONG:
|
|
#ifdef VTK_USE_64BIT_IDS
|
|
case VTK_ID_TYPE:
|
|
#endif
|
|
vtkWarningMacro("Using legacy vtkDataArray API, which may result "
|
|
"in precision loss");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (wordType)
|
|
{
|
|
vtkTemplateMacro(WriteDataArrayFallback(static_cast<VTK_TT*>(nullptr), da, worker));
|
|
default:
|
|
vtkWarningMacro("Unsupported data type: " << wordType);
|
|
break;
|
|
}
|
|
}
|
|
ret = worker.Result ? 1 : 0;
|
|
}
|
|
else
|
|
{
|
|
vtkWarningMacro("Not writing array '" << a->GetName()
|
|
<< "': Unsupported "
|
|
"array type: "
|
|
<< a->GetClassName());
|
|
ret = 0;
|
|
}
|
|
|
|
// Free the byte swap buffer if it was allocated.
|
|
if (!this->Int32IdTypeBuffer)
|
|
{
|
|
delete[] this->ByteSwapBuffer;
|
|
this->ByteSwapBuffer = nullptr;
|
|
}
|
|
|
|
#ifdef VTK_USE_64BIT_IDS
|
|
// Free the id-type conversion buffer if it was allocated.
|
|
delete[] this->Int32IdTypeBuffer;
|
|
this->Int32IdTypeBuffer = nullptr;
|
|
// The swap and ID buffers are shared. Guard against double frees:
|
|
this->ByteSwapBuffer = nullptr;
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::WriteBinaryDataBlock(unsigned char* in_data, size_t numWords, int wordType)
|
|
{
|
|
unsigned char* data = in_data;
|
|
#ifdef VTK_USE_64BIT_IDS
|
|
// If the type is vtkIdType, it may need to be converted to the type
|
|
// requested for output.
|
|
if ((wordType == VTK_ID_TYPE) && (this->IdType == vtkXMLWriter::Int32))
|
|
{
|
|
vtkIdType* idBuffer = reinterpret_cast<vtkIdType*>(in_data);
|
|
|
|
for (size_t i = 0; i < numWords; ++i)
|
|
{
|
|
this->Int32IdTypeBuffer[i] = static_cast<Int32IdType>(idBuffer[i]);
|
|
}
|
|
|
|
data = reinterpret_cast<unsigned char*>(this->Int32IdTypeBuffer);
|
|
}
|
|
#endif
|
|
|
|
// Get the word size of the data buffer. This is now the size that
|
|
// will be written.
|
|
size_t wordSize = this->GetOutputWordTypeSize(wordType);
|
|
|
|
// If we need to byte swap, do it now.
|
|
if (this->ByteSwapBuffer)
|
|
{
|
|
// If we are converting vtkIdType to 32-bit integer data, the data
|
|
// are already in the byte swap buffer because we share the
|
|
// conversion buffer. Otherwise, we need to copy the data before
|
|
// byte swapping.
|
|
if (data != this->ByteSwapBuffer)
|
|
{
|
|
memcpy(this->ByteSwapBuffer, data, numWords * wordSize);
|
|
data = this->ByteSwapBuffer;
|
|
}
|
|
this->PerformByteSwap(this->ByteSwapBuffer, numWords, wordSize);
|
|
}
|
|
|
|
// Now pass the data to the next write phase.
|
|
if (this->Compressor)
|
|
{
|
|
int res = this->WriteCompressionBlock(data, numWords * wordSize);
|
|
this->Stream->flush();
|
|
if (this->Stream->fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
return 0;
|
|
}
|
|
return res;
|
|
}
|
|
else
|
|
{
|
|
int res = this->DataStream->Write(data, numWords * wordSize);
|
|
this->Stream->flush();
|
|
if (this->Stream->fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
return 0;
|
|
}
|
|
return res;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::PerformByteSwap(void* data, size_t numWords, size_t wordSize)
|
|
{
|
|
char* ptr = static_cast<char*>(data);
|
|
if (this->ByteOrder == vtkXMLWriter::BigEndian)
|
|
{
|
|
switch (wordSize)
|
|
{
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
vtkByteSwap::Swap2BERange(ptr, numWords);
|
|
break;
|
|
case 4:
|
|
vtkByteSwap::Swap4BERange(ptr, numWords);
|
|
break;
|
|
case 8:
|
|
vtkByteSwap::Swap8BERange(ptr, numWords);
|
|
break;
|
|
default:
|
|
vtkErrorMacro("Unsupported data type size " << wordSize);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (wordSize)
|
|
{
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
vtkByteSwap::Swap2LERange(ptr, numWords);
|
|
break;
|
|
case 4:
|
|
vtkByteSwap::Swap4LERange(ptr, numWords);
|
|
break;
|
|
case 8:
|
|
vtkByteSwap::Swap8LERange(ptr, numWords);
|
|
break;
|
|
default:
|
|
vtkErrorMacro("Unsupported data type size " << wordSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::SetDataStream(vtkOutputStream* arg)
|
|
{
|
|
if (this->DataStream != arg)
|
|
{
|
|
if (this->DataStream != nullptr)
|
|
{
|
|
this->DataStream->UnRegister(this);
|
|
}
|
|
this->DataStream = arg;
|
|
if (this->DataStream != nullptr)
|
|
{
|
|
this->DataStream->Register(this);
|
|
this->DataStream->SetStream(this->Stream);
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::CreateCompressionHeader(size_t size)
|
|
{
|
|
// Allocate and initialize the compression header.
|
|
// The format is this:
|
|
// struct header {
|
|
// HeaderType number_of_blocks;
|
|
// HeaderType uncompressed_block_size;
|
|
// HeaderType uncompressed_last_block_size;
|
|
// HeaderType compressed_block_sizes[number_of_blocks];
|
|
// }
|
|
|
|
// Find the size and number of blocks.
|
|
size_t numFullBlocks = size / this->BlockSize;
|
|
size_t lastBlockSize = size % this->BlockSize;
|
|
size_t numBlocks = numFullBlocks + (lastBlockSize ? 1 : 0);
|
|
this->CompressionHeader = vtkXMLDataHeader::New(this->HeaderType, 3 + numBlocks);
|
|
|
|
// Write out dummy header data.
|
|
this->CompressionHeaderPosition = this->Stream->tellp();
|
|
int result = (this->DataStream->StartWriting() &&
|
|
this->DataStream->Write(this->CompressionHeader->Data(), this->CompressionHeader->DataSize()) &&
|
|
this->DataStream->EndWriting());
|
|
|
|
this->Stream->flush();
|
|
if (this->Stream->fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
return 0;
|
|
}
|
|
|
|
// Fill in known header data now.
|
|
this->CompressionHeader->Set(0, numBlocks);
|
|
this->CompressionHeader->Set(1, this->BlockSize);
|
|
this->CompressionHeader->Set(2, lastBlockSize);
|
|
|
|
// Initialize counter for block writing.
|
|
this->CompressionBlockNumber = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::WriteCompressionBlock(unsigned char* data, size_t size)
|
|
{
|
|
// Compress the data.
|
|
vtkUnsignedCharArray* outputArray = this->Compressor->Compress(data, size);
|
|
|
|
// Find the compressed size.
|
|
size_t outputSize = outputArray->GetNumberOfTuples();
|
|
unsigned char* outputPointer = outputArray->GetPointer(0);
|
|
|
|
// Write the compressed data.
|
|
int result = this->DataStream->Write(outputPointer, outputSize);
|
|
this->Stream->flush();
|
|
if (this->Stream->fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
|
|
// Store the resulting compressed size in the compression header.
|
|
this->CompressionHeader->Set(3 + this->CompressionBlockNumber++, outputSize);
|
|
|
|
outputArray->Delete();
|
|
|
|
return result;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::WriteCompressionHeader()
|
|
{
|
|
// Write real compression header back into stream.
|
|
std::streampos returnPosition = this->Stream->tellp();
|
|
|
|
// Need to byte-swap header.
|
|
this->PerformByteSwap(this->CompressionHeader->Data(), this->CompressionHeader->WordCount(),
|
|
this->CompressionHeader->WordSize());
|
|
|
|
if (!this->Stream->seekp(std::streampos(this->CompressionHeaderPosition)))
|
|
{
|
|
return 0;
|
|
}
|
|
int result = (this->DataStream->StartWriting() &&
|
|
this->DataStream->Write(this->CompressionHeader->Data(), this->CompressionHeader->DataSize()) &&
|
|
this->DataStream->EndWriting());
|
|
this->Stream->flush();
|
|
if (this->Stream->fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
return 0;
|
|
}
|
|
|
|
if (!this->Stream->seekp(returnPosition))
|
|
{
|
|
return 0;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
size_t vtkXMLWriter::GetOutputWordTypeSize(int dataType)
|
|
{
|
|
#ifdef VTK_USE_64BIT_IDS
|
|
// If the type is vtkIdType, it may need to be converted to the type
|
|
// requested for output.
|
|
if ((dataType == VTK_ID_TYPE) && (this->IdType == vtkXMLWriter::Int32))
|
|
{
|
|
return 4;
|
|
}
|
|
#endif
|
|
return this->GetWordTypeSize(dataType);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <class T>
|
|
size_t vtkXMLWriterGetWordTypeSize(T*)
|
|
{
|
|
return sizeof(T);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
size_t vtkXMLWriter::GetWordTypeSize(int dataType)
|
|
{
|
|
size_t size = 1;
|
|
switch (dataType)
|
|
{
|
|
vtkTemplateMacro(size = vtkXMLWriterGetWordTypeSize(static_cast<VTK_TT*>(nullptr)));
|
|
|
|
case VTK_STRING:
|
|
size = sizeof(vtkStdString::value_type);
|
|
break;
|
|
|
|
case VTK_BIT:
|
|
size = 1;
|
|
break;
|
|
|
|
default:
|
|
vtkWarningMacro("Unsupported data type: " << dataType);
|
|
break;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
const char* vtkXMLWriter::GetWordTypeName(int dataType)
|
|
{
|
|
char isSigned = 0;
|
|
int size = 0;
|
|
|
|
// These string values must match vtkXMLDataElement::GetWordTypeAttribute().
|
|
switch (dataType)
|
|
{
|
|
case VTK_BIT:
|
|
return "Bit";
|
|
case VTK_STRING:
|
|
return "String";
|
|
case VTK_FLOAT:
|
|
return "Float32";
|
|
case VTK_DOUBLE:
|
|
return "Float64";
|
|
case VTK_ID_TYPE:
|
|
{
|
|
switch (this->IdType)
|
|
{
|
|
case vtkXMLWriter::Int32:
|
|
return "Int32";
|
|
case vtkXMLWriter::Int64:
|
|
return "Int64";
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
#if VTK_TYPE_CHAR_IS_SIGNED
|
|
case VTK_CHAR:
|
|
isSigned = 1;
|
|
size = sizeof(char);
|
|
break;
|
|
#else
|
|
case VTK_CHAR:
|
|
isSigned = 0;
|
|
size = sizeof(char);
|
|
break;
|
|
#endif
|
|
case VTK_INT:
|
|
isSigned = 1;
|
|
size = sizeof(int);
|
|
break;
|
|
case VTK_LONG:
|
|
isSigned = 1;
|
|
size = sizeof(long);
|
|
break;
|
|
case VTK_SHORT:
|
|
isSigned = 1;
|
|
size = sizeof(short);
|
|
break;
|
|
case VTK_SIGNED_CHAR:
|
|
isSigned = 1;
|
|
size = sizeof(signed char);
|
|
break;
|
|
case VTK_UNSIGNED_CHAR:
|
|
isSigned = 0;
|
|
size = sizeof(unsigned char);
|
|
break;
|
|
case VTK_UNSIGNED_INT:
|
|
isSigned = 0;
|
|
size = sizeof(unsigned int);
|
|
break;
|
|
case VTK_UNSIGNED_LONG:
|
|
isSigned = 0;
|
|
size = sizeof(unsigned long);
|
|
break;
|
|
case VTK_UNSIGNED_SHORT:
|
|
isSigned = 0;
|
|
size = sizeof(unsigned short);
|
|
break;
|
|
case VTK_LONG_LONG:
|
|
isSigned = 1;
|
|
size = sizeof(long long);
|
|
break;
|
|
case VTK_UNSIGNED_LONG_LONG:
|
|
isSigned = 0;
|
|
size = sizeof(unsigned long long);
|
|
break;
|
|
default:
|
|
{
|
|
vtkWarningMacro("Unsupported data type: " << dataType);
|
|
}
|
|
break;
|
|
}
|
|
const char* type = nullptr;
|
|
switch (size)
|
|
{
|
|
case 1:
|
|
type = isSigned ? "Int8" : "UInt8";
|
|
break;
|
|
case 2:
|
|
type = isSigned ? "Int16" : "UInt16";
|
|
break;
|
|
case 4:
|
|
type = isSigned ? "Int32" : "UInt32";
|
|
break;
|
|
case 8:
|
|
type = isSigned ? "Int64" : "UInt64";
|
|
break;
|
|
default:
|
|
{
|
|
vtkErrorMacro("Data type size " << size << " not supported by VTK XML format.");
|
|
}
|
|
}
|
|
return type;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <class T>
|
|
int vtkXMLWriterWriteVectorAttribute(ostream& os, const char* name, int length, T* data)
|
|
{
|
|
vtkNumberToString convert;
|
|
os << " " << name << "=\"";
|
|
if (length)
|
|
{
|
|
os << convert(data[0]);
|
|
for (int i = 1; i < length; ++i)
|
|
{
|
|
os << " " << convert(data[i]);
|
|
}
|
|
}
|
|
os << "\"";
|
|
return os ? 1 : 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::WriteScalarAttribute(const char* name, int data)
|
|
{
|
|
return this->WriteVectorAttribute(name, 1, &data);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::WriteScalarAttribute(const char* name, float data)
|
|
{
|
|
return this->WriteVectorAttribute(name, 1, &data);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::WriteScalarAttribute(const char* name, double data)
|
|
{
|
|
return this->WriteVectorAttribute(name, 1, &data);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
#ifdef VTK_USE_64BIT_IDS
|
|
int vtkXMLWriter::WriteScalarAttribute(const char* name, vtkIdType data)
|
|
{
|
|
return this->WriteVectorAttribute(name, 1, &data);
|
|
}
|
|
#endif
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::WriteVectorAttribute(const char* name, int length, int* data)
|
|
{
|
|
int res = vtkXMLWriterWriteVectorAttribute(*(this->Stream), name, length, data);
|
|
|
|
this->Stream->flush();
|
|
if (this->Stream->fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
return res;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::WriteVectorAttribute(const char* name, int length, float* data)
|
|
{
|
|
int res = vtkXMLWriterWriteVectorAttribute(*(this->Stream), name, length, data);
|
|
|
|
this->Stream->flush();
|
|
if (this->Stream->fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
return res;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::WriteVectorAttribute(const char* name, int length, double* data)
|
|
{
|
|
int res = vtkXMLWriterWriteVectorAttribute(*(this->Stream), name, length, data);
|
|
|
|
this->Stream->flush();
|
|
if (this->Stream->fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
return res;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
#ifdef VTK_USE_64BIT_IDS
|
|
int vtkXMLWriter::WriteVectorAttribute(const char* name, int length, vtkIdType* data)
|
|
{
|
|
int res = vtkXMLWriterWriteVectorAttribute(*(this->Stream), name, length, data);
|
|
|
|
this->Stream->flush();
|
|
if (this->Stream->fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
return res;
|
|
}
|
|
#endif
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::WriteDataModeAttribute(const char* name)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
os << " " << name << "=\"";
|
|
if (this->DataMode == vtkXMLWriter::Appended)
|
|
{
|
|
os << "appended";
|
|
}
|
|
else if (this->DataMode == vtkXMLWriter::Binary)
|
|
{
|
|
os << "binary";
|
|
}
|
|
else
|
|
{
|
|
os << "ascii";
|
|
}
|
|
os << "\"";
|
|
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
return (os ? 1 : 0);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::WriteWordTypeAttribute(const char* name, int dataType)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
const char* value = this->GetWordTypeName(dataType);
|
|
if (!value)
|
|
{
|
|
return 0;
|
|
}
|
|
os << " " << name << "=\"" << value << "\"";
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
return os ? 1 : 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::WriteStringAttribute(const char* name, const char* value)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
os << " " << name << "=\"" << value << "\"";
|
|
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
return os ? 1 : 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Methods used for serializing vtkInformation. ------------------------------
|
|
namespace
|
|
{
|
|
|
|
// Write generic key information to element.
|
|
void prepElementForInfo(vtkInformationKey* key, vtkXMLDataElement* element)
|
|
{
|
|
element->SetName("InformationKey");
|
|
element->SetAttribute("name", key->GetName());
|
|
element->SetAttribute("location", key->GetLocation());
|
|
}
|
|
|
|
template <class KeyType>
|
|
void writeScalarInfo(KeyType* key, vtkInformation* info, std::ostream& os, vtkIndent indent)
|
|
{
|
|
vtkNew<vtkXMLDataElement> element;
|
|
prepElementForInfo(key, element);
|
|
|
|
std::ostringstream str;
|
|
str.precision(11); // Same used for ASCII array data.
|
|
str << key->Get(info);
|
|
|
|
str.str("");
|
|
str << key->Get(info);
|
|
element->SetCharacterData(str.str().c_str(), static_cast<int>(str.str().size()));
|
|
|
|
element->PrintXML(os, indent);
|
|
}
|
|
|
|
template <class KeyType>
|
|
void writeVectorInfo(KeyType* key, vtkInformation* info, std::ostream& os, vtkIndent indent)
|
|
{
|
|
vtkNew<vtkXMLDataElement> element;
|
|
prepElementForInfo(key, element);
|
|
|
|
std::ostringstream str;
|
|
str.precision(11); // Same used for ASCII array data.
|
|
int length = key->Length(info);
|
|
str << length;
|
|
element->SetAttribute("length", str.str().c_str());
|
|
|
|
for (int i = 0; i < length; ++i)
|
|
{
|
|
vtkNew<vtkXMLDataElement> value;
|
|
value->SetName("Value");
|
|
|
|
str.str("");
|
|
str << i;
|
|
value->SetAttribute("index", str.str().c_str());
|
|
|
|
str.str("");
|
|
str << key->Get(info, i);
|
|
value->SetCharacterData(str.str().c_str(), static_cast<int>(str.str().size()));
|
|
|
|
element->AddNestedElement(value);
|
|
}
|
|
|
|
element->PrintXML(os, indent);
|
|
}
|
|
|
|
} // end anon namespace
|
|
//------------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool vtkXMLWriter::WriteInformation(vtkInformation* info, vtkIndent indent)
|
|
{
|
|
bool result = false;
|
|
vtkNew<vtkInformationIterator> iter;
|
|
iter->SetInformationWeak(info);
|
|
vtkInformationKey* key = nullptr;
|
|
vtkIndent nextIndent = indent.GetNextIndent();
|
|
for (iter->InitTraversal(); (key = iter->GetCurrentKey()); iter->GoToNextItem())
|
|
{
|
|
vtkInformationDoubleKey* dKey = nullptr;
|
|
vtkInformationDoubleVectorKey* dvKey = nullptr;
|
|
vtkInformationIdTypeKey* idKey = nullptr;
|
|
vtkInformationIntegerKey* iKey = nullptr;
|
|
vtkInformationIntegerVectorKey* ivKey = nullptr;
|
|
vtkInformationStringKey* sKey = nullptr;
|
|
vtkInformationStringVectorKey* svKey = nullptr;
|
|
vtkInformationUnsignedLongKey* ulKey = nullptr;
|
|
typedef vtkInformationQuadratureSchemeDefinitionVectorKey QuadDictKey;
|
|
QuadDictKey* qdKey = nullptr;
|
|
if ((dKey = vtkInformationDoubleKey::SafeDownCast(key)))
|
|
{
|
|
writeScalarInfo(dKey, info, *this->Stream, nextIndent);
|
|
result = true;
|
|
}
|
|
else if ((dvKey = vtkInformationDoubleVectorKey::SafeDownCast(key)))
|
|
{
|
|
writeVectorInfo(dvKey, info, *this->Stream, nextIndent);
|
|
result = true;
|
|
}
|
|
else if ((idKey = vtkInformationIdTypeKey::SafeDownCast(key)))
|
|
{
|
|
writeScalarInfo(idKey, info, *this->Stream, nextIndent);
|
|
result = true;
|
|
}
|
|
else if ((iKey = vtkInformationIntegerKey::SafeDownCast(key)))
|
|
{
|
|
writeScalarInfo(iKey, info, *this->Stream, nextIndent);
|
|
result = true;
|
|
}
|
|
else if ((ivKey = vtkInformationIntegerVectorKey::SafeDownCast(key)))
|
|
{
|
|
writeVectorInfo(ivKey, info, *this->Stream, nextIndent);
|
|
result = true;
|
|
}
|
|
else if ((sKey = vtkInformationStringKey::SafeDownCast(key)))
|
|
{
|
|
writeScalarInfo(sKey, info, *this->Stream, nextIndent);
|
|
result = true;
|
|
}
|
|
else if ((svKey = vtkInformationStringVectorKey::SafeDownCast(key)))
|
|
{
|
|
writeVectorInfo(svKey, info, *this->Stream, nextIndent);
|
|
result = true;
|
|
}
|
|
else if ((ulKey = vtkInformationUnsignedLongKey::SafeDownCast(key)))
|
|
{
|
|
writeScalarInfo(ulKey, info, *this->Stream, nextIndent);
|
|
result = true;
|
|
}
|
|
else if ((qdKey = QuadDictKey::SafeDownCast(key)))
|
|
{ // Special case:
|
|
vtkNew<vtkXMLDataElement> element;
|
|
qdKey->SaveState(info, element);
|
|
element->PrintXML(*this->Stream, nextIndent);
|
|
result = true;
|
|
}
|
|
else
|
|
{
|
|
vtkDebugMacro("Could not serialize information with key " << key->GetLocation()
|
|
<< "::" << key->GetName()
|
|
<< ": "
|
|
"Unsupported key type '"
|
|
<< key->GetClassName() << "'.");
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// This method is provided so that the specialization code for certain types
|
|
// can be minimal.
|
|
template <class T>
|
|
inline ostream& vtkXMLWriteAsciiValue(ostream& os, const T& value)
|
|
{
|
|
vtkNumberToString convert;
|
|
os << convert(value);
|
|
return os;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <>
|
|
inline ostream& vtkXMLWriteAsciiValue(ostream& os, const char& c)
|
|
{
|
|
os << short(c);
|
|
return os;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <>
|
|
inline ostream& vtkXMLWriteAsciiValue(ostream& os, const unsigned char& c)
|
|
{
|
|
os << static_cast<unsigned short>(c);
|
|
return os;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <>
|
|
inline ostream& vtkXMLWriteAsciiValue(ostream& os, const signed char& c)
|
|
{
|
|
os << short(c);
|
|
return os;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <>
|
|
inline ostream& vtkXMLWriteAsciiValue(ostream& os, const vtkStdString& str)
|
|
{
|
|
vtkStdString::const_iterator iter;
|
|
for (iter = str.begin(); iter != str.end(); ++iter)
|
|
{
|
|
vtkXMLWriteAsciiValue(os, *iter);
|
|
os << " ";
|
|
}
|
|
char delim = 0x0;
|
|
return vtkXMLWriteAsciiValue(os, delim);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <class iterT>
|
|
int vtkXMLWriteAsciiData(ostream& os, iterT* iter, vtkIndent indent)
|
|
{
|
|
if (!iter)
|
|
{
|
|
return 0;
|
|
}
|
|
size_t columns = 6;
|
|
size_t length = iter->GetNumberOfTuples() * iter->GetNumberOfComponents();
|
|
|
|
size_t rows = length / columns;
|
|
size_t lastRowLength = length % columns;
|
|
vtkIdType index = 0;
|
|
for (size_t r = 0; r < rows; ++r)
|
|
{
|
|
os << indent;
|
|
vtkXMLWriteAsciiValue(os, iter->GetValue(index++));
|
|
for (size_t c = 1; c < columns; ++c)
|
|
{
|
|
os << " ";
|
|
vtkXMLWriteAsciiValue(os, iter->GetValue(index++));
|
|
}
|
|
os << "\n";
|
|
}
|
|
if (lastRowLength > 0)
|
|
{
|
|
os << indent;
|
|
vtkXMLWriteAsciiValue(os, iter->GetValue(index++));
|
|
for (size_t c = 1; c < lastRowLength; ++c)
|
|
{
|
|
os << " ";
|
|
vtkXMLWriteAsciiValue(os, iter->GetValue(index++));
|
|
}
|
|
os << "\n";
|
|
}
|
|
return os ? 1 : 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::WriteAsciiData(vtkAbstractArray* a, vtkIndent indent)
|
|
{
|
|
vtkArrayIterator* iter = a->NewIterator();
|
|
ostream& os = *(this->Stream);
|
|
int ret;
|
|
switch (a->GetDataType())
|
|
{
|
|
vtkArrayIteratorTemplateMacro(
|
|
ret = vtkXMLWriteAsciiData(os, static_cast<VTK_TT*>(iter), indent));
|
|
default:
|
|
ret = 0;
|
|
break;
|
|
}
|
|
iter->Delete();
|
|
return ret;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteArrayAppended(vtkAbstractArray* a, vtkIndent indent, OffsetsManager& offs,
|
|
const char* alternateName, int writeNumTuples, int timestep)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
// Write the header <DataArray or <Array:
|
|
this->WriteArrayHeader(a, indent, alternateName, writeNumTuples, timestep);
|
|
int shortFormatTag = 1; // close with: />
|
|
//
|
|
if (vtkArrayDownCast<vtkDataArray>(a))
|
|
{
|
|
// write the scalar range of this data array, we reserver space because we
|
|
// don't actually have the data at this point
|
|
offs.GetRangeMinPosition(timestep) = this->ReserveAttributeSpace("RangeMin");
|
|
offs.GetRangeMaxPosition(timestep) = this->ReserveAttributeSpace("RangeMax");
|
|
}
|
|
else
|
|
{
|
|
// ranges are not written for non-data arrays.
|
|
offs.GetRangeMinPosition(timestep) = -1;
|
|
offs.GetRangeMaxPosition(timestep) = -1;
|
|
}
|
|
|
|
//
|
|
offs.GetPosition(timestep) = this->ReserveAttributeSpace("offset");
|
|
|
|
// Write information in the recognized keys associated with this array.
|
|
vtkInformation* info = a->GetInformation();
|
|
bool hasInfo = info && info->GetNumberOfKeys() > 0;
|
|
if (hasInfo)
|
|
{
|
|
// close header with </DataArray> or </Array> before writing information:
|
|
os << ">" << endl;
|
|
shortFormatTag = 0; // Tells WriteArrayFooter that the header is closed.
|
|
|
|
this->WriteInformation(info, indent);
|
|
}
|
|
|
|
// Close tag.
|
|
this->WriteArrayFooter(os, indent, a, shortFormatTag);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteArrayAppendedData(
|
|
vtkAbstractArray* a, vtkTypeInt64 pos, vtkTypeInt64& lastoffset)
|
|
{
|
|
this->WriteAppendedDataOffset(pos, lastoffset, "offset");
|
|
this->WriteBinaryData(a);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteArrayHeader(vtkAbstractArray* a, vtkIndent indent,
|
|
const char* alternateName, int writeNumTuples, int timestep)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
if (vtkArrayDownCast<vtkDataArray>(a))
|
|
{
|
|
os << indent << "<DataArray";
|
|
}
|
|
else
|
|
{
|
|
os << indent << "<Array";
|
|
}
|
|
this->WriteWordTypeAttribute("type", a->GetDataType());
|
|
if (a->GetDataType() == VTK_ID_TYPE)
|
|
{
|
|
this->WriteScalarAttribute("IdType", 1);
|
|
}
|
|
if (alternateName)
|
|
{
|
|
this->WriteStringAttribute("Name", alternateName);
|
|
}
|
|
else if (const char* arrayName = a->GetName())
|
|
{
|
|
this->WriteStringAttribute("Name", arrayName);
|
|
}
|
|
else
|
|
{
|
|
// Generate a name for this array.
|
|
std::ostringstream name;
|
|
void* p = a;
|
|
name << "Array " << p;
|
|
this->WriteStringAttribute("Name", name.str().c_str());
|
|
}
|
|
if (a->GetNumberOfComponents() > 1)
|
|
{
|
|
this->WriteScalarAttribute("NumberOfComponents", a->GetNumberOfComponents());
|
|
}
|
|
|
|
// always write out component names, even if only 1 component
|
|
std::ostringstream buff;
|
|
const char* compName = nullptr;
|
|
for (int i = 0; i < a->GetNumberOfComponents(); ++i)
|
|
{
|
|
// get the component names
|
|
buff << "ComponentName" << i;
|
|
compName = a->GetComponentName(i);
|
|
if (compName)
|
|
{
|
|
this->WriteStringAttribute(buff.str().c_str(), compName);
|
|
compName = nullptr;
|
|
}
|
|
buff.str("");
|
|
buff.clear();
|
|
}
|
|
|
|
if (this->NumberOfTimeSteps > 1)
|
|
{
|
|
this->WriteScalarAttribute("TimeStep", timestep);
|
|
}
|
|
else
|
|
{
|
|
// assert(timestep == -1); //FieldData problem
|
|
}
|
|
if (writeNumTuples)
|
|
{
|
|
this->WriteScalarAttribute("NumberOfTuples", a->GetNumberOfTuples());
|
|
}
|
|
|
|
this->WriteDataModeAttribute("format");
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteArrayFooter(
|
|
ostream& os, vtkIndent indent, vtkAbstractArray* a, int shortFormat)
|
|
{
|
|
// Close the tag: </DataArray>, </Array> or />
|
|
if (shortFormat)
|
|
{
|
|
os << "/>" << endl;
|
|
}
|
|
else
|
|
{
|
|
vtkDataArray* da = vtkArrayDownCast<vtkDataArray>(a);
|
|
os << indent << (da ? "</DataArray>" : "</Array>") << "\n";
|
|
}
|
|
// Force write and check for errors.
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteInlineData(vtkAbstractArray* a, vtkIndent indent)
|
|
{
|
|
if (this->DataMode == vtkXMLWriter::Binary)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
os << indent;
|
|
this->WriteBinaryData(a);
|
|
os << "\n";
|
|
}
|
|
else
|
|
{
|
|
this->WriteAsciiData(a, indent);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteArrayInline(
|
|
vtkAbstractArray* a, vtkIndent indent, const char* alternateName, int writeNumTuples)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
// Write the header <DataArray or <Array:
|
|
this->WriteArrayHeader(a, indent, alternateName, writeNumTuples, 0);
|
|
//
|
|
vtkDataArray* da = vtkArrayDownCast<vtkDataArray>(a);
|
|
if (da)
|
|
{
|
|
// write the range
|
|
this->WriteScalarAttribute("RangeMin", da->GetRange(-1)[0]);
|
|
this->WriteScalarAttribute("RangeMax", da->GetRange(-1)[1]);
|
|
}
|
|
// Close the header
|
|
os << ">\n";
|
|
// Write the inline data.
|
|
this->WriteInlineData(a, indent.GetNextIndent());
|
|
// Write information keys associated with this array.
|
|
vtkInformation* info = a->GetInformation();
|
|
if (info && info->GetNumberOfKeys() > 0)
|
|
{
|
|
this->WriteInformation(info, indent);
|
|
}
|
|
// Close tag.
|
|
this->WriteArrayFooter(os, indent, a, 0);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::UpdateFieldData(vtkFieldData* fieldDataCopy)
|
|
{
|
|
vtkDataObject* input = this->GetInput();
|
|
vtkFieldData* fieldData = input->GetFieldData();
|
|
vtkInformation* meta = input->GetInformation();
|
|
bool hasTime = meta->Has(vtkDataObject::DATA_TIME_STEP()) ? true : false;
|
|
if ((!fieldData || !fieldData->GetNumberOfArrays()) && !hasTime)
|
|
{
|
|
fieldDataCopy->Initialize();
|
|
return;
|
|
}
|
|
|
|
fieldDataCopy->ShallowCopy(fieldData);
|
|
if (hasTime)
|
|
{
|
|
vtkNew<vtkDoubleArray> time;
|
|
time->SetNumberOfTuples(1);
|
|
time->SetTypedComponent(0, 0, meta->Get(vtkDataObject::DATA_TIME_STEP()));
|
|
time->SetName("TimeValue");
|
|
fieldDataCopy->AddArray(time);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteFieldData(vtkIndent indent)
|
|
{
|
|
vtkNew<vtkFieldData> fieldDataCopy;
|
|
this->UpdateFieldData(fieldDataCopy);
|
|
|
|
if (!fieldDataCopy->GetNumberOfArrays())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (this->DataMode == vtkXMLWriter::Appended)
|
|
{
|
|
this->WriteFieldDataAppended(fieldDataCopy, indent, this->FieldDataOM);
|
|
}
|
|
else
|
|
{
|
|
// Write the point data arrays.
|
|
this->WriteFieldDataInline(fieldDataCopy, indent);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteFieldDataInline(vtkFieldData* fd, vtkIndent indent)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
char** names = this->CreateStringArray(fd->GetNumberOfArrays());
|
|
|
|
os << indent << "<FieldData>\n";
|
|
|
|
float progressRange[2] = { 0.f, 0.f };
|
|
this->GetProgressRange(progressRange);
|
|
for (int i = 0; i < fd->GetNumberOfArrays(); ++i)
|
|
{
|
|
this->SetProgressRange(progressRange, i, fd->GetNumberOfArrays());
|
|
this->WriteArrayInline(fd->GetAbstractArray(i), indent.GetNextIndent(), names[i], 1);
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
this->DestroyStringArray(fd->GetNumberOfArrays(), names);
|
|
return;
|
|
}
|
|
}
|
|
|
|
os << indent << "</FieldData>\n";
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
this->DestroyStringArray(fd->GetNumberOfArrays(), names);
|
|
return;
|
|
}
|
|
|
|
this->DestroyStringArray(fd->GetNumberOfArrays(), names);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WritePointDataInline(vtkPointData* pd, vtkIndent indent)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
char** names = this->CreateStringArray(pd->GetNumberOfArrays());
|
|
|
|
os << indent << "<PointData";
|
|
this->WriteAttributeIndices(pd, names);
|
|
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
this->DestroyStringArray(pd->GetNumberOfArrays(), names);
|
|
return;
|
|
}
|
|
|
|
os << ">\n";
|
|
|
|
float progressRange[2] = { 0.f, 0.f };
|
|
this->GetProgressRange(progressRange);
|
|
for (int i = 0; i < pd->GetNumberOfArrays(); ++i)
|
|
{
|
|
this->SetProgressRange(progressRange, i, pd->GetNumberOfArrays());
|
|
vtkAbstractArray* a = pd->GetAbstractArray(i);
|
|
this->WriteArrayInline(a, indent.GetNextIndent(), names[i]);
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
this->DestroyStringArray(pd->GetNumberOfArrays(), names);
|
|
return;
|
|
}
|
|
}
|
|
|
|
os << indent << "</PointData>\n";
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
this->DestroyStringArray(pd->GetNumberOfArrays(), names);
|
|
return;
|
|
}
|
|
|
|
this->DestroyStringArray(pd->GetNumberOfArrays(), names);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteCellDataInline(vtkCellData* cd, vtkIndent indent)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
char** names = this->CreateStringArray(cd->GetNumberOfArrays());
|
|
|
|
os << indent << "<CellData";
|
|
this->WriteAttributeIndices(cd, names);
|
|
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
this->DestroyStringArray(cd->GetNumberOfArrays(), names);
|
|
return;
|
|
}
|
|
|
|
os << ">\n";
|
|
|
|
float progressRange[2] = { 0.f, 0.f };
|
|
this->GetProgressRange(progressRange);
|
|
for (int i = 0; i < cd->GetNumberOfArrays(); ++i)
|
|
{
|
|
this->SetProgressRange(progressRange, i, cd->GetNumberOfArrays());
|
|
vtkAbstractArray* a = cd->GetAbstractArray(i);
|
|
this->WriteArrayInline(a, indent.GetNextIndent(), names[i]);
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
this->DestroyStringArray(cd->GetNumberOfArrays(), names);
|
|
return;
|
|
}
|
|
}
|
|
|
|
os << indent << "</CellData>\n";
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
this->DestroyStringArray(cd->GetNumberOfArrays(), names);
|
|
return;
|
|
}
|
|
|
|
this->DestroyStringArray(cd->GetNumberOfArrays(), names);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteFieldDataAppended(
|
|
vtkFieldData* fd, vtkIndent indent, OffsetsManagerGroup* fdManager)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
char** names = this->CreateStringArray(fd->GetNumberOfArrays());
|
|
|
|
os << indent << "<FieldData>\n";
|
|
|
|
// When we want to write index arrays with String Arrays, we will
|
|
// have to determine the actual arrays written out to the file
|
|
// and allocate the fdManager accordingly.
|
|
fdManager->Allocate(fd->GetNumberOfArrays());
|
|
for (int i = 0; i < fd->GetNumberOfArrays(); ++i)
|
|
{
|
|
fdManager->GetElement(i).Allocate(1);
|
|
this->WriteArrayAppended(
|
|
fd->GetAbstractArray(i), indent.GetNextIndent(), fdManager->GetElement(i), names[i], 1, 0);
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
this->DestroyStringArray(fd->GetNumberOfArrays(), names);
|
|
return;
|
|
}
|
|
}
|
|
os << indent << "</FieldData>\n";
|
|
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
this->DestroyStringArray(fd->GetNumberOfArrays(), names);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteFieldDataAppendedData(
|
|
vtkFieldData* fd, int timestep, OffsetsManagerGroup* fdManager)
|
|
{
|
|
float progressRange[2] = { 0.f, 0.f };
|
|
this->GetProgressRange(progressRange);
|
|
fdManager->Allocate(fd->GetNumberOfArrays());
|
|
for (int i = 0; i < fd->GetNumberOfArrays(); ++i)
|
|
{
|
|
fdManager->GetElement(i).Allocate(this->NumberOfTimeSteps);
|
|
this->SetProgressRange(progressRange, i, fd->GetNumberOfArrays());
|
|
this->WriteArrayAppendedData(fd->GetAbstractArray(i),
|
|
fdManager->GetElement(i).GetPosition(timestep),
|
|
fdManager->GetElement(i).GetOffsetValue(timestep));
|
|
vtkDataArray* da = fd->GetArray(i);
|
|
if (da)
|
|
{
|
|
// Write ranges only for data arrays.
|
|
double* range = da->GetRange(-1);
|
|
this->ForwardAppendedDataDouble(
|
|
fdManager->GetElement(i).GetRangeMinPosition(timestep), range[0], "RangeMin");
|
|
this->ForwardAppendedDataDouble(
|
|
fdManager->GetElement(i).GetRangeMaxPosition(timestep), range[1], "RangeMax");
|
|
}
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WritePointDataAppended(
|
|
vtkPointData* pd, vtkIndent indent, OffsetsManagerGroup* pdManager)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
char** names = this->CreateStringArray(pd->GetNumberOfArrays());
|
|
|
|
os << indent << "<PointData";
|
|
this->WriteAttributeIndices(pd, names);
|
|
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
this->DestroyStringArray(pd->GetNumberOfArrays(), names);
|
|
return;
|
|
}
|
|
|
|
os << ">\n";
|
|
|
|
pdManager->Allocate(pd->GetNumberOfArrays());
|
|
for (int i = 0; i < pd->GetNumberOfArrays(); ++i)
|
|
{
|
|
pdManager->GetElement(i).Allocate(this->NumberOfTimeSteps);
|
|
for (int t = 0; t < this->NumberOfTimeSteps; ++t)
|
|
{
|
|
this->WriteArrayAppended(
|
|
pd->GetAbstractArray(i), indent.GetNextIndent(), pdManager->GetElement(i), names[i], 0, t);
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
this->DestroyStringArray(pd->GetNumberOfArrays(), names);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
os << indent << "</PointData>\n";
|
|
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
this->DestroyStringArray(pd->GetNumberOfArrays(), names);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WritePointDataAppendedData(
|
|
vtkPointData* pd, int timestep, OffsetsManagerGroup* pdManager)
|
|
{
|
|
float progressRange[2] = { 0.f, 0.f };
|
|
|
|
this->GetProgressRange(progressRange);
|
|
for (int i = 0; i < pd->GetNumberOfArrays(); ++i)
|
|
{
|
|
this->SetProgressRange(progressRange, i, pd->GetNumberOfArrays());
|
|
vtkMTimeType mtime = pd->GetMTime();
|
|
// Only write pd if MTime has changed
|
|
vtkMTimeType& pdMTime = pdManager->GetElement(i).GetLastMTime();
|
|
vtkAbstractArray* a = pd->GetAbstractArray(i);
|
|
if (pdMTime != mtime || timestep == 0)
|
|
{
|
|
pdMTime = mtime;
|
|
this->WriteArrayAppendedData(a, pdManager->GetElement(i).GetPosition(timestep),
|
|
pdManager->GetElement(i).GetOffsetValue(timestep));
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert(timestep > 0);
|
|
pdManager->GetElement(i).GetOffsetValue(timestep) =
|
|
pdManager->GetElement(i).GetOffsetValue(timestep - 1);
|
|
this->ForwardAppendedDataOffset(pdManager->GetElement(i).GetPosition(timestep),
|
|
pdManager->GetElement(i).GetOffsetValue(timestep), "offset");
|
|
}
|
|
vtkDataArray* d = vtkArrayDownCast<vtkDataArray>(a);
|
|
if (d)
|
|
{
|
|
// ranges are only written in case of Data Arrays.
|
|
double* range = d->GetRange(-1);
|
|
this->ForwardAppendedDataDouble(
|
|
pdManager->GetElement(i).GetRangeMinPosition(timestep), range[0], "RangeMin");
|
|
this->ForwardAppendedDataDouble(
|
|
pdManager->GetElement(i).GetRangeMaxPosition(timestep), range[1], "RangeMax");
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteCellDataAppended(
|
|
vtkCellData* cd, vtkIndent indent, OffsetsManagerGroup* cdManager)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
char** names = this->CreateStringArray(cd->GetNumberOfArrays());
|
|
|
|
os << indent << "<CellData";
|
|
this->WriteAttributeIndices(cd, names);
|
|
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
this->DestroyStringArray(cd->GetNumberOfArrays(), names);
|
|
return;
|
|
}
|
|
|
|
os << ">\n";
|
|
|
|
cdManager->Allocate(cd->GetNumberOfArrays());
|
|
for (int i = 0; i < cd->GetNumberOfArrays(); ++i)
|
|
{
|
|
cdManager->GetElement(i).Allocate(this->NumberOfTimeSteps);
|
|
for (int t = 0; t < this->NumberOfTimeSteps; ++t)
|
|
{
|
|
this->WriteArrayAppended(
|
|
cd->GetAbstractArray(i), indent.GetNextIndent(), cdManager->GetElement(i), names[i], 0, t);
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
this->DestroyStringArray(cd->GetNumberOfArrays(), names);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
os << indent << "</CellData>\n";
|
|
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
this->DestroyStringArray(cd->GetNumberOfArrays(), names);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteCellDataAppendedData(
|
|
vtkCellData* cd, int timestep, OffsetsManagerGroup* cdManager)
|
|
{
|
|
float progressRange[2] = { 0.f, 0.f };
|
|
this->GetProgressRange(progressRange);
|
|
|
|
for (int i = 0; i < cd->GetNumberOfArrays(); ++i)
|
|
{
|
|
this->SetProgressRange(progressRange, i, cd->GetNumberOfArrays());
|
|
vtkMTimeType mtime = cd->GetMTime();
|
|
// Only write pd if MTime has changed
|
|
vtkMTimeType& cdMTime = cdManager->GetElement(i).GetLastMTime();
|
|
vtkAbstractArray* a = cd->GetAbstractArray(i);
|
|
if (cdMTime != mtime)
|
|
{
|
|
cdMTime = mtime;
|
|
this->WriteArrayAppendedData(a, cdManager->GetElement(i).GetPosition(timestep),
|
|
cdManager->GetElement(i).GetOffsetValue(timestep));
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert(timestep > 0);
|
|
cdManager->GetElement(i).GetOffsetValue(timestep) =
|
|
cdManager->GetElement(i).GetOffsetValue(timestep - 1);
|
|
this->ForwardAppendedDataOffset(cdManager->GetElement(i).GetPosition(timestep),
|
|
cdManager->GetElement(i).GetOffsetValue(timestep), "offset");
|
|
}
|
|
vtkDataArray* d = vtkArrayDownCast<vtkDataArray>(a);
|
|
if (d)
|
|
{
|
|
double* range = d->GetRange(-1);
|
|
this->ForwardAppendedDataDouble(
|
|
cdManager->GetElement(i).GetRangeMinPosition(timestep), range[0], "RangeMin");
|
|
this->ForwardAppendedDataDouble(
|
|
cdManager->GetElement(i).GetRangeMaxPosition(timestep), range[1], "RangeMax");
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteAttributeIndices(vtkDataSetAttributes* dsa, char** names)
|
|
{
|
|
int attributeIndices[vtkDataSetAttributes::NUM_ATTRIBUTES];
|
|
dsa->GetAttributeIndices(attributeIndices);
|
|
for (int i = 0; i < vtkDataSetAttributes::NUM_ATTRIBUTES; ++i)
|
|
{
|
|
if (attributeIndices[i] >= 0)
|
|
{
|
|
const char* attrName = vtkDataSetAttributes::GetAttributeTypeAsString(i);
|
|
vtkDataArray* a = dsa->GetArray(attributeIndices[i]);
|
|
const char* arrayName = a->GetName();
|
|
if (!arrayName)
|
|
{
|
|
// Assign a name to the array.
|
|
names[attributeIndices[i]] = new char[strlen(attrName) + 2];
|
|
strcpy(names[attributeIndices[i]], attrName);
|
|
strcat(names[attributeIndices[i]], "_");
|
|
arrayName = names[attributeIndices[i]];
|
|
}
|
|
this->WriteStringAttribute(attrName, arrayName);
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WritePointsAppended(
|
|
vtkPoints* points, vtkIndent indent, OffsetsManager* ptManager)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
|
|
// Only write points if they exist.
|
|
os << indent << "<Points>\n";
|
|
if (points)
|
|
{
|
|
for (int t = 0; t < this->NumberOfTimeSteps; ++t)
|
|
{
|
|
this->WriteArrayAppended(
|
|
points->GetData(), indent.GetNextIndent(), *ptManager, nullptr, 0, t);
|
|
}
|
|
}
|
|
os << indent << "</Points>\n";
|
|
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WritePointsAppendedData(
|
|
vtkPoints* points, int timestep, OffsetsManager* ptManager)
|
|
{
|
|
// Only write points if they exist.
|
|
if (points)
|
|
{
|
|
vtkMTimeType mtime = points->GetMTime();
|
|
// Only write points if MTime has changed
|
|
vtkMTimeType& pointsMTime = ptManager->GetLastMTime();
|
|
// since points->Data is a vtkDataArray.
|
|
vtkDataArray* outPoints = points->GetData();
|
|
if (pointsMTime != mtime || timestep == 0)
|
|
{
|
|
pointsMTime = mtime;
|
|
this->WriteArrayAppendedData(
|
|
outPoints, ptManager->GetPosition(timestep), ptManager->GetOffsetValue(timestep));
|
|
}
|
|
else
|
|
{
|
|
assert(timestep > 0);
|
|
ptManager->GetOffsetValue(timestep) = ptManager->GetOffsetValue(timestep - 1);
|
|
this->ForwardAppendedDataOffset(
|
|
ptManager->GetPosition(timestep), ptManager->GetOffsetValue(timestep), "offset");
|
|
}
|
|
double* range = outPoints->GetRange(-1);
|
|
this->ForwardAppendedDataDouble(ptManager->GetRangeMinPosition(timestep), range[0], "RangeMin");
|
|
this->ForwardAppendedDataDouble(ptManager->GetRangeMaxPosition(timestep), range[1], "RangeMax");
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WritePointsInline(vtkPoints* points, vtkIndent indent)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
// Only write points if they exist.
|
|
os << indent << "<Points>\n";
|
|
if (points)
|
|
{
|
|
vtkAbstractArray* outPoints = points->GetData();
|
|
this->WriteArrayInline(outPoints, indent.GetNextIndent());
|
|
}
|
|
os << indent << "</Points>\n";
|
|
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteCoordinatesInline(
|
|
vtkDataArray* xc, vtkDataArray* yc, vtkDataArray* zc, vtkIndent indent)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
|
|
// Only write coordinates if they exist.
|
|
os << indent << "<Coordinates>\n";
|
|
if (xc && yc && zc)
|
|
{
|
|
|
|
// Split progress over the three coordinates arrays.
|
|
vtkIdType total = (xc->GetNumberOfTuples() + yc->GetNumberOfTuples() + zc->GetNumberOfTuples());
|
|
if (total == 0)
|
|
{
|
|
total = 1;
|
|
}
|
|
float fractions[4] = { 0, float(xc->GetNumberOfTuples()) / total,
|
|
float(xc->GetNumberOfTuples() + yc->GetNumberOfTuples()) / total, 1 };
|
|
float progressRange[2] = { 0.f, 0.f };
|
|
this->GetProgressRange(progressRange);
|
|
|
|
this->SetProgressRange(progressRange, 0, fractions);
|
|
this->WriteArrayInline(xc, indent.GetNextIndent());
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this->SetProgressRange(progressRange, 1, fractions);
|
|
this->WriteArrayInline(yc, indent.GetNextIndent());
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this->SetProgressRange(progressRange, 2, fractions);
|
|
this->WriteArrayInline(zc, indent.GetNextIndent());
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
os << indent << "</Coordinates>\n";
|
|
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
return;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteCoordinatesAppended(vtkDataArray* xc, vtkDataArray* yc, vtkDataArray* zc,
|
|
vtkIndent indent, OffsetsManagerGroup* coordManager)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
|
|
// Helper for the 'for' loop
|
|
vtkDataArray* allcoords[3];
|
|
allcoords[0] = xc;
|
|
allcoords[1] = yc;
|
|
allcoords[2] = zc;
|
|
|
|
// Only write coordinates if they exist.
|
|
os << indent << "<Coordinates>\n";
|
|
coordManager->Allocate(3);
|
|
if (xc && yc && zc)
|
|
{
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
coordManager->GetElement(i).Allocate(this->NumberOfTimeSteps);
|
|
for (int t = 0; t < this->NumberOfTimeSteps; ++t)
|
|
{
|
|
this->WriteArrayAppended(
|
|
allcoords[i], indent.GetNextIndent(), coordManager->GetElement(i), nullptr, 0, t);
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
os << indent << "</Coordinates>\n";
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteCoordinatesAppendedData(vtkDataArray* xc, vtkDataArray* yc,
|
|
vtkDataArray* zc, int timestep, OffsetsManagerGroup* coordManager)
|
|
{
|
|
// Only write coordinates if they exist.
|
|
if (xc && yc && zc)
|
|
{
|
|
// Split progress over the three coordinates arrays.
|
|
vtkIdType total = (xc->GetNumberOfTuples() + yc->GetNumberOfTuples() + zc->GetNumberOfTuples());
|
|
if (total == 0)
|
|
{
|
|
total = 1;
|
|
}
|
|
float fractions[4] = { 0, float(xc->GetNumberOfTuples()) / total,
|
|
float(xc->GetNumberOfTuples() + yc->GetNumberOfTuples()) / total, 1 };
|
|
float progressRange[2] = { 0.f, 0.f };
|
|
this->GetProgressRange(progressRange);
|
|
|
|
// Helper for the 'for' loop
|
|
vtkDataArray* allcoords[3];
|
|
allcoords[0] = xc;
|
|
allcoords[1] = yc;
|
|
allcoords[2] = zc;
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
this->SetProgressRange(progressRange, i, fractions);
|
|
vtkMTimeType mtime = allcoords[i]->GetMTime();
|
|
// Only write pd if MTime has changed
|
|
vtkMTimeType& coordMTime = coordManager->GetElement(i).GetLastMTime();
|
|
if (coordMTime != mtime)
|
|
{
|
|
coordMTime = mtime;
|
|
this->WriteArrayAppendedData(allcoords[i],
|
|
coordManager->GetElement(i).GetPosition(timestep),
|
|
coordManager->GetElement(i).GetOffsetValue(timestep));
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WritePPointData(vtkPointData* pd, vtkIndent indent)
|
|
{
|
|
if (pd->GetNumberOfArrays() == 0)
|
|
{
|
|
return;
|
|
}
|
|
ostream& os = *(this->Stream);
|
|
char** names = this->CreateStringArray(pd->GetNumberOfArrays());
|
|
|
|
os << indent << "<PPointData";
|
|
this->WriteAttributeIndices(pd, names);
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
this->DestroyStringArray(pd->GetNumberOfArrays(), names);
|
|
return;
|
|
}
|
|
os << ">\n";
|
|
|
|
for (int i = 0; i < pd->GetNumberOfArrays(); ++i)
|
|
{
|
|
this->WritePArray(pd->GetAbstractArray(i), indent.GetNextIndent(), names[i]);
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
this->DestroyStringArray(pd->GetNumberOfArrays(), names);
|
|
return;
|
|
}
|
|
}
|
|
|
|
os << indent << "</PPointData>\n";
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
|
|
this->DestroyStringArray(pd->GetNumberOfArrays(), names);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WritePCellData(vtkCellData* cd, vtkIndent indent)
|
|
{
|
|
if (cd->GetNumberOfArrays() == 0)
|
|
{
|
|
return;
|
|
}
|
|
ostream& os = *(this->Stream);
|
|
char** names = this->CreateStringArray(cd->GetNumberOfArrays());
|
|
|
|
os << indent << "<PCellData";
|
|
this->WriteAttributeIndices(cd, names);
|
|
os << ">\n";
|
|
|
|
for (int i = 0; i < cd->GetNumberOfArrays(); ++i)
|
|
{
|
|
this->WritePArray(cd->GetAbstractArray(i), indent.GetNextIndent(), names[i]);
|
|
}
|
|
|
|
os << indent << "</PCellData>\n";
|
|
|
|
this->DestroyStringArray(cd->GetNumberOfArrays(), names);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WritePPoints(vtkPoints* points, vtkIndent indent)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
// Only write points if they exist.
|
|
os << indent << "<PPoints>\n";
|
|
if (points)
|
|
{
|
|
this->WritePArray(points->GetData(), indent.GetNextIndent());
|
|
}
|
|
os << indent << "</PPoints>\n";
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WritePArray(vtkAbstractArray* a, vtkIndent indent, const char* alternateName)
|
|
{
|
|
vtkDataArray* d = vtkArrayDownCast<vtkDataArray>(a);
|
|
ostream& os = *(this->Stream);
|
|
if (d)
|
|
{
|
|
os << indent << "<PDataArray";
|
|
}
|
|
else
|
|
{
|
|
os << indent << "<PArray";
|
|
}
|
|
this->WriteWordTypeAttribute("type", a->GetDataType());
|
|
if (a->GetDataType() == VTK_ID_TYPE)
|
|
{
|
|
this->WriteScalarAttribute("IdType", 1);
|
|
}
|
|
if (alternateName)
|
|
{
|
|
this->WriteStringAttribute("Name", alternateName);
|
|
}
|
|
else
|
|
{
|
|
const char* arrayName = a->GetName();
|
|
if (arrayName)
|
|
{
|
|
this->WriteStringAttribute("Name", arrayName);
|
|
}
|
|
}
|
|
if (a->GetNumberOfComponents() > 1)
|
|
{
|
|
this->WriteScalarAttribute("NumberOfComponents", a->GetNumberOfComponents());
|
|
}
|
|
os << "/>\n";
|
|
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WritePCoordinates(
|
|
vtkDataArray* xc, vtkDataArray* yc, vtkDataArray* zc, vtkIndent indent)
|
|
{
|
|
ostream& os = *(this->Stream);
|
|
|
|
// Only write coordinates if they exist.
|
|
os << indent << "<PCoordinates>\n";
|
|
if (xc && yc && zc)
|
|
{
|
|
this->WritePArray(xc, indent.GetNextIndent());
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
return;
|
|
}
|
|
this->WritePArray(yc, indent.GetNextIndent());
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
return;
|
|
}
|
|
this->WritePArray(zc, indent.GetNextIndent());
|
|
if (this->ErrorCode != vtkErrorCode::NoError)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
os << indent << "</PCoordinates>\n";
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::GetLastSystemError());
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
char** vtkXMLWriter::CreateStringArray(int numStrings)
|
|
{
|
|
char** strings = new char*[numStrings];
|
|
for (int i = 0; i < numStrings; ++i)
|
|
{
|
|
strings[i] = nullptr;
|
|
}
|
|
return strings;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::DestroyStringArray(int numStrings, char** strings)
|
|
{
|
|
for (int i = 0; i < numStrings; ++i)
|
|
{
|
|
delete[] strings[i];
|
|
}
|
|
delete[] strings;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::GetProgressRange(float range[2])
|
|
{
|
|
range[0] = this->ProgressRange[0];
|
|
range[1] = this->ProgressRange[1];
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::SetProgressRange(const float range[2], int curStep, int numSteps)
|
|
{
|
|
float stepSize = (range[1] - range[0]) / numSteps;
|
|
this->ProgressRange[0] = range[0] + stepSize * curStep;
|
|
this->ProgressRange[1] = range[0] + stepSize * (curStep + 1);
|
|
this->UpdateProgressDiscrete(this->ProgressRange[0]);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::SetProgressRange(const float range[2], int curStep, const float* fractions)
|
|
{
|
|
float width = range[1] - range[0];
|
|
this->ProgressRange[0] = range[0] + fractions[curStep] * width;
|
|
this->ProgressRange[1] = range[0] + fractions[curStep + 1] * width;
|
|
this->UpdateProgressDiscrete(this->ProgressRange[0]);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::SetProgressPartial(float fraction)
|
|
{
|
|
float width = this->ProgressRange[1] - this->ProgressRange[0];
|
|
this->UpdateProgressDiscrete(this->ProgressRange[0] + fraction * width);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::UpdateProgressDiscrete(float progress)
|
|
{
|
|
if (!this->AbortExecute)
|
|
{
|
|
// Round progress to nearest 100th.
|
|
float rounded = static_cast<float>(static_cast<int>((progress * 100) + 0.5f)) / 100.f;
|
|
if (this->GetProgress() != rounded)
|
|
{
|
|
this->UpdateProgress(rounded);
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WritePrimaryElementAttributes(ostream& os, vtkIndent indent)
|
|
{
|
|
// Write the time step if any:
|
|
if (this->NumberOfTimeSteps > 1)
|
|
{
|
|
// First thing allocate NumberOfTimeValues
|
|
assert(this->NumberOfTimeValues == nullptr);
|
|
this->NumberOfTimeValues = new vtkTypeInt64[this->NumberOfTimeSteps];
|
|
os << indent << "TimeValues=\"\n";
|
|
|
|
std::string blankline = std::string(40, ' '); // enough room for precision
|
|
for (int i = 0; i < this->NumberOfTimeSteps; i++)
|
|
{
|
|
this->NumberOfTimeValues[i] = os.tellp();
|
|
os << blankline.c_str() << "\n";
|
|
}
|
|
os << "\"";
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkXMLWriter::WritePrimaryElement(ostream& os, vtkIndent indent)
|
|
{
|
|
// Open the primary element.
|
|
os << indent << "<" << this->GetDataSetName();
|
|
|
|
this->WritePrimaryElementAttributes(os, indent);
|
|
|
|
// Close the primary element:
|
|
os << ">\n";
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
this->SetErrorCode(vtkErrorCode::OutOfDiskSpaceError);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// The following function are designed to be called outside of the VTK pipeline
|
|
// typically from a C interface or when ParaView want to control the writing
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::Start()
|
|
{
|
|
// Make sure we have input.
|
|
if (this->GetNumberOfInputConnections(0) < 1)
|
|
{
|
|
vtkErrorMacro("No input provided!");
|
|
return;
|
|
}
|
|
this->UserContinueExecuting = 1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// The function does not make sense in the general case but we need to handle
|
|
// the case where the simulation stop before reaching the number of steps
|
|
// specified by the user. Therefore the CurrentTimeIndex is never equal
|
|
// to NumberOfTimeStep and thus we need to force closing of the xml file
|
|
void vtkXMLWriter::Stop()
|
|
{
|
|
this->UserContinueExecuting = 0;
|
|
this->Modified();
|
|
this->Update();
|
|
this->UserContinueExecuting = -1; // put back the writer in initial state
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkXMLWriter::WriteNextTime(double time)
|
|
{
|
|
this->Modified();
|
|
this->Update();
|
|
|
|
ostream& os = *(this->Stream);
|
|
|
|
if (this->NumberOfTimeValues)
|
|
{
|
|
// Write user specified time value in the TimeValues attribute
|
|
std::streampos returnPos = os.tellp();
|
|
vtkTypeInt64 t = this->NumberOfTimeValues[this->CurrentTimeIndex - 1];
|
|
os.seekp(std::streampos(t));
|
|
os << time;
|
|
os.seekp(returnPos);
|
|
}
|
|
}
|