GrpcPrint/PrintS/external/vl/include/vlX/VisitorExportToVLB.hpp
2024-03-19 17:45:12 +08:00

397 lines
12 KiB
C++

/**************************************************************************************/
/* */
/* Visualization Library */
/* http://visualizationlibrary.org */
/* */
/* Copyright (c) 2005-2020, Michele Bosi */
/* All rights reserved. */
/* */
/* Redistribution and use in source and binary forms, with or without modification, */
/* are permitted provided that the following conditions are met: */
/* */
/* - Redistributions of source code must retain the above copyright notice, this */
/* list of conditions and the following disclaimer. */
/* */
/* - Redistributions in binary form must reproduce the above copyright notice, this */
/* list of conditions and the following disclaimer in the documentation and/or */
/* other materials provided with the distribution. */
/* */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND */
/* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */
/* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */
/* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR */
/* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */
/* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */
/* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */
/* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
/* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/* */
/**************************************************************************************/
#ifndef VLXVisitorExportToVLB_INCLUDE_ONCE
#define VLXVisitorExportToVLB_INCLUDE_ONCE
#include <vlX/Visitor.hpp>
#include <vlX/Value.hpp>
#include <vlX/BinaryDefs.hpp>
#include <vlCore/VirtualFile.hpp>
namespace vlX
{
/** Translates a VLX hierarchy into VLB format writing to the provided VirtualFile. */
class VisitorExportToVLB: public Visitor
{
VL_INSTRUMENT_CLASS(vlX::VisitorExportToVLB, Visitor)
public:
VisitorExportToVLB(vl::VirtualFile* file = NULL)
{
mIDSet = NULL;
setOutputFile(file);
}
bool isUsed(const std::string& uid)
{
if (mIDSet)
{
std::map< std::string, int >::iterator it = mIDSet->find(uid);
if (it != mIDSet->end())
return it->second > 1;
else
{
// should not happen
VL_TRAP()
return false;
}
}
else
return true;
}
void writeValue(VLXValue& value)
{
switch(value.type())
{
case VLXValue::Structure:
value.getStructure()->acceptVisitor(this);
break;
case VLXValue::List:
value.getList()->acceptVisitor(this);
break;
/*
case VLXValue::ArrayString:
break;
case VLXValue::ArrayIdentifier:
break;
case VLXValue::ArrayID:
break;
*/
case VLXValue::ArrayInteger:
value.getArrayInteger()->acceptVisitor(this);
break;
case VLXValue::ArrayReal:
value.getArrayReal()->acceptVisitor(this);
break;
case VLXValue::RawtextBlock:
{
VLXRawtextBlock* fblock = value.getRawtextBlock();
// header
mOutputFile->writeUInt8( VLB_ChunkRawtext );
// tag
writeString( fblock->tag().c_str() );
// value
writeString( fblock->value().c_str() ); // no decoding needed
}
break;
case VLXValue::String:
// header
mOutputFile->writeUInt8( VLB_ChunkString );
// value
writeString( value.getString().c_str() );
break;
case VLXValue::Identifier:
// header
mOutputFile->writeUInt8( VLB_ChunkIdentifier );
// value
writeString( value.getIdentifier().c_str() );
break;
case VLXValue::ID:
// header
mOutputFile->writeUInt8( VLB_ChunkID );
// value
writeString( value.getID().c_str() );
break;
case VLXValue::Bool:
// header
mOutputFile->writeUInt8( VLB_ChunkBool );
// value
mOutputFile->writeUInt8( value.getBool() );
break;
case VLXValue::Integer:
// header
mOutputFile->writeUInt8( VLB_ChunkInteger);
// value
writeInteger( value.getInteger() );
break;
case VLXValue::Real:
// header
mOutputFile->writeUInt8( VLB_ChunkRealDouble);
// value
mOutputFile->writeDouble( value.getReal() );
break;
}
}
virtual void visitStructure(VLXStructure* obj)
{
if (isVisited(obj))
{
mOutputFile->writeUInt8( VLB_ChunkID );
writeString( obj->uid().c_str() );
return;
}
// header
mOutputFile->writeUInt8( VLB_ChunkStructure );
// tag
writeString( obj->tag().c_str() );
// ID
writeString( obj->uid().c_str() );
// key/value count
writeInteger( obj->value().size() );
// values
for(size_t i=0; i<obj->value().size(); ++i)
{
// key
writeString(obj->value()[i].key().c_str());
// value
writeValue(obj->value()[i].value());
}
}
virtual void visitList(VLXList* list)
{
// this should happen only if the user manually creates loops
if (isVisited(list))
{
vl::Log::warning("VisitorExportToVLT: cycle detected on VLXList.\n");
return;
}
// header
mOutputFile->writeUInt8( VLB_ChunkList );
// tag
writeString( list->tag().c_str() );
// value count
writeInteger( list->value().size() );
// values
for(size_t i=0; i<list->value().size(); ++i)
writeValue(list->value()[i]);
}
virtual void visitArray(VLXArrayInteger* arr)
{
// header
mOutputFile->writeUInt8( VLB_ChunkArrayInteger );
// tag
writeString(arr->tag().c_str());
// value count
writeInteger(arr->value().size());
// value
if (arr->value().size() > 0)
{
std::vector<unsigned char> encoded;
encodeIntegers(&arr->value()[0], (int)arr->value().size(), encoded); VL_CHECK(encoded.size())
writeInteger(encoded.size());
mOutputFile->writeUInt8(&encoded[0], encoded.size());
}
}
bool needsDoublePrecision(const double* in, size_t count)
{
for(size_t i=0; i<count; ++i)
{
float f = (float)in[i];
if ((double)f != in[i])
return true;
}
return false;
}
virtual void visitArray(VLXArrayReal* arr)
{
bool needs_double = arr->value().empty() ? false : needsDoublePrecision(&arr->value()[0], arr->value().size());
// header
mOutputFile->writeUInt8( (unsigned char)(needs_double ? VLB_ChunkArrayRealDouble : VLB_ChunkArrayRealFloat) );
// tag
writeString(arr->tag().c_str());
// count
writeInteger(arr->value().size());
// value
if (arr->value().size())
{
#if 1
if (needs_double)
mOutputFile->writeDouble(&arr->value().front(), arr->value().size());
else
{
std::vector<float> floats;
floats.resize(arr->value().size());
for(size_t i=0; i<arr->value().size(); ++i)
floats[i] = (float)arr->value()[i];
mOutputFile->writeFloat(&floats[0], floats.size());
}
#else
std::vector<unsigned char> zipped;
compress( &arr->value()[0], arr->value().size() * sizeof(arr->value()[0]), zipped, 1 );
writeInteger( zipped.size() );
mOutputFile->write(&zipped[0], zipped.size());
#endif
}
}
/*
virtual void visitArray(VLXArrayString* arr)
{
}
virtual void visitArray(VLXArrayIdentifier* arr)
{
}
virtual void visitArray(VLXArrayID* arr)
{
}
*/
void writeHeader()
{
// see http://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ for more info on why I choose these characters.
unsigned char vlx_identifier[] = { 0xAB, 'V', 'L', 'X', 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
mOutputFile->write(vlx_identifier, sizeof(vlx_identifier));
mOutputFile->writeUInt16(VL_SERIALIZER_VERSION); // "version" (16 bits uint)
mOutputFile->write("ascii", 5+1); // "encoding" (zero terminated string)
mOutputFile->writeUInt32(0); // "flags" (reserved for the future)
}
void writeString(const char* str)
{
size_t len = strlen(str);
writeInteger(len);
mOutputFile->write(str, len);
}
void writeInteger(long long n)
{
#if 0
mOutputFile->writeSInt64(n);
#else
const unsigned char nxt_flag = 0x80;
const unsigned char neg_flag = 0x40;
unsigned char bytes[12]; memset(bytes, 0, sizeof(bytes)); // should take maximum 10 bytes
unsigned char* byte = bytes;
if (n < 0)
{
n = -n;
*byte = neg_flag;
}
// lower 6 bits
*byte |= n & 0x3F; n >>= 6;
*byte |= n ? nxt_flag : 0;
++byte; // --> output
// rest of the bytes
while (n)
{
*byte = n & 0x7F; n >>= 7;
*byte |= n ? nxt_flag : 0;
++byte; // --> output
}
mOutputFile->write(bytes, byte - bytes);
#endif
}
void encodeIntegers(long long* val, int count, std::vector<unsigned char>& out)
{
const unsigned char nxt_flag = 0x80;
const unsigned char neg_flag = 0x40;
out.reserve(count);
for( int i=0; i<count; ++i)
{
unsigned char byte = 0;
long long n = val[i];
if (n < 0)
{
n = -n;
byte = neg_flag;
}
// lower 6 bits
byte |= n & 0x3F; n >>= 6;
byte |= n ? nxt_flag : 0;
out.push_back(byte);
// rest of the bytes
while (n)
{
byte = n & 0x7F; n >>= 7;
byte |= n ? nxt_flag : 0;
out.push_back(byte);
}
}
}
void setIDSet(std::map< std::string, int >* uids) { mIDSet = uids; }
std::map< std::string, int >* uidSet() { return mIDSet; }
const std::map< std::string, int >* uidSet() const { return mIDSet; }
void setOutputFile(vl::VirtualFile* file)
{
mOutputFile = file;
if (file)
{
file->close();
file->open(vl::OM_WriteOnly);
}
}
vl::VirtualFile* outputFile() { return mOutputFile.get(); }
const vl::VirtualFile* outputFile() const { return mOutputFile.get(); }
private:
std::map< std::string, int >* mIDSet;
vl::ref<vl::VirtualFile> mOutputFile;
};
}
#endif