Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
CGoGN
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
KennethVanhoey
CGoGN
Commits
9238a53c
Commit
9238a53c
authored
Jan 24, 2014
by
Sylvain Thery
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
binary vtu
parent
2175919d
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
520 additions
and
9 deletions
+520
-9
include/Algo/Export/export.h
include/Algo/Export/export.h
+24
-1
include/Algo/Export/export.hpp
include/Algo/Export/export.hpp
+496
-8
No files found.
include/Algo/Export/export.h
View file @
9238a53c
...
...
@@ -106,6 +106,8 @@ bool exportVTU(typename PFP::MAP& map, const VertexAttribute<typename PFP::VEC3>
template
<
typename
PFP
>
bool
exportVTUCompressed
(
typename
PFP
::
MAP
&
map
,
const
VertexAttribute
<
typename
PFP
::
VEC3
>&
position
,
const
char
*
filename
);
template
<
typename
PFP
>
bool
exportVTUBinary
(
typename
PFP
::
MAP
&
map
,
const
VertexAttribute
<
typename
PFP
::
VEC3
>&
position
,
const
char
*
filename
);
template
<
typename
PFP
>
class
VTUExporter
...
...
@@ -133,6 +135,27 @@ protected:
std
::
vector
<
Dart
>
bufferQuad
;
std
::
vector
<
Dart
>
bufferOther
;
bool
binaryMode
;
std
::
ofstream
f_tempoBin_out
;
template
<
typename
T
>
void
addBinaryVertexAttributeScal
(
const
VertexAttribute
<
T
>&
attrib
,
const
std
::
string
&
vtkType
,
const
std
::
string
&
name
=
""
);
template
<
typename
T
>
void
addBinaryVertexAttributeVect
(
const
VertexAttribute
<
T
>&
attrib
,
const
std
::
string
&
vtkType
,
unsigned
int
nbComp
=
0
,
const
std
::
string
&
name
=
""
);
void
endBinaryVertexAttributes
();
template
<
typename
T
>
void
addBinaryFaceAttributeScal
(
const
FaceAttribute
<
T
>&
attrib
,
const
std
::
string
&
vtkType
,
const
std
::
string
&
name
=
""
);
template
<
typename
T
>
void
addBinaryFaceAttributeVect
(
const
FaceAttribute
<
T
>&
attrib
,
const
std
::
string
&
vtkType
,
unsigned
int
nbComp
=
0
,
const
std
::
string
&
name
=
""
);
void
endBinaryFaceAttributes
();
bool
binaryClose
();
public:
...
...
@@ -145,7 +168,7 @@ public:
* @param filename
* @return true if ok
*/
bool
init
(
const
char
*
filename
);
bool
init
(
const
char
*
filename
,
bool
bin
=
false
);
/**
...
...
include/Algo/Export/export.hpp
View file @
9238a53c
...
...
@@ -1193,6 +1193,210 @@ bool exportVTU(typename PFP::MAP& map, const VertexAttribute<typename PFP::VEC3>
return true;
}
template <typename PFP>
bool exportVTUBinary(typename PFP::MAP& map, const VertexAttribute<typename PFP::VEC3>& position, const char* filename)
{
if (map.dimension() != 2)
{
CGoGNerr << "Surface::Export::exportVTU works only with map of dimension 2"<< CGoGNendl;
return false;
}
typedef typename PFP::MAP MAP;
typedef typename PFP::VEC3 VEC3;
// open file
std::ofstream fout ;
fout.open(filename, std::ios_base::out | std::ios_base::trunc) ;
if (!fout.good())
{
CGoGNerr << "Unable to open file " << filename << CGoGNendl ;
return false ;
}
VertexAutoAttribute<unsigned int> indices(map,"indices_vert");
unsigned int count=0;
for (unsigned int i = position.begin(); i != position.end(); position.next(i))
{
indices[i] = count++;
}
std::vector<unsigned int> triangles;
std::vector<unsigned int> quads;
std::vector<unsigned int> others;
std::vector<unsigned int> others_begin;
triangles.reserve(2048);
quads.reserve(2048);
others.reserve(2048);
others_begin.reserve(2048);
TraversorF<MAP> trav(map) ;
for(Dart d = trav.begin(); d != trav.end(); d = trav.next())
{
unsigned int degree = map.faceDegree(d);
Dart f=d;
switch(degree)
{
case 3:
triangles.push_back(indices[f]); f = map.phi1(f);
triangles.push_back(indices[f]); f = map.phi1(f);
triangles.push_back(indices[f]);
break;
case 4:
quads.push_back(indices[f]); f = map.phi1(f);
quads.push_back(indices[f]); f = map.phi1(f);
quads.push_back(indices[f]); f = map.phi1(f);
quads.push_back(indices[f]);
break;
default:
others_begin.push_back(others.size());
do
{
others.push_back(indices[f]); f = map.phi1(f);
} while (f!=d);
break;
}
}
others_begin.push_back(others.size());
unsigned int nbtotal = triangles.size()/3 + quads.size()/4 + others_begin.size()-1;
fout << "<?xml version=\"1.0\"?>" << std::endl;
fout << "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\" byte_order=\"LittleEndian\">" << std::endl;
fout << "<UnstructuredGrid>" << std::endl;
fout << "<Piece NumberOfPoints=\"" << position.nbElements() << "\" NumberOfCells=\""<< nbtotal << "\">" << std::endl;
fout << "<Points>" << std::endl;
fout << "<DataArray type =\"Float32\" Name =\"Position\" NumberOfComponents =\"3\" format =\"appended\" offset =\"0\"/>" << std::endl;
unsigned int offsetAppend = position.nbElements() * 3 * sizeof(float) + sizeof(unsigned int); // Data + sz of blk
fout << "</Points>" << std::endl;
fout << "<Cells>" << std::endl;
std::vector<int> bufferInt;
bufferInt.reserve(triangles.size()+quads.size()+others.size());
for (unsigned int i=0; i<triangles.size(); i+=3)
{
bufferInt.push_back(triangles[i]);
bufferInt.push_back(triangles[i+1]);
bufferInt.push_back(triangles[i+2]);
}
for (unsigned int i=0; i<quads.size(); i+=4)
{
bufferInt.push_back(quads[i]);
bufferInt.push_back(quads[i+1]);
bufferInt.push_back(quads[i+2]);
bufferInt.push_back(quads[i+3]);
}
for (unsigned int i=1; i<others_begin.size(); ++i)
{
unsigned int beg = others_begin[i-1];
unsigned int end = others_begin[i];
for (unsigned int j=beg; j<end; ++j)
bufferInt.push_back(others[j]);
}
fout << "<DataArray type =\"Int32\" Name =\"connectivity\" format =\"appended\" offset =\""<<offsetAppend<<"\"/>" << std::endl;
offsetAppend +=bufferInt.size() * sizeof(unsigned int) + sizeof(unsigned int);
fout << "<DataArray type =\"Int32\" Name =\"offsets\" format =\"appended\" offset =\""<<offsetAppend<<"\"/>" << std::endl;
offsetAppend += (triangles.size()/3 + quads.size()/4 + others_begin.size()-1) * sizeof(unsigned int) + sizeof(unsigned int);
fout << "<DataArray type =\"UInt8\" Name =\"types\" format =\"appended\" offset =\""<<offsetAppend<<"\"/>" << std::endl;
// offsetAppend += (triangles.size() + quads.size() + others_begin.size()) * sizeof(unsigned char) + sizeof(unsigned int);
fout << "</Cells>" << std::endl;
fout << "</Piece>" << std::endl;
fout << "</UnstructuredGrid>" << std::endl;
fout << "<AppendedData encoding=\"raw\">" << std::endl << "_";
fout.close();
fout.open(filename, std::ios_base::binary | std::ios_base::ate | std::ios_base::app);
unsigned int lengthBuff=0;
// bufferize and save position
{
std::vector<VEC3> bufferV3;
bufferV3.reserve(position.nbElements());
for (unsigned int i = position.begin(); i != position.end(); position.next(i))
bufferV3.push_back(position[i]);
lengthBuff = bufferV3.size()*sizeof(VEC3);
fout.write((char*)&lengthBuff,sizeof(unsigned int));
fout.write((char*)&bufferV3[0],lengthBuff);
}
// save already buffrized indices of primitives
lengthBuff = bufferInt.size()*sizeof(unsigned int);
fout.write((char*)&lengthBuff,sizeof(unsigned int));
fout.write((char*)&(bufferInt[0]),lengthBuff);
// bufferize and save offsets of primitives
bufferInt.clear();
unsigned int offset = 0;
for (unsigned int i=0; i<triangles.size(); i+=3)
{
offset += 3;
bufferInt.push_back(offset);
}
for (unsigned int i=0; i<quads.size(); i+=4)
{
offset += 4;
bufferInt.push_back(offset);
}
for (unsigned int i=1; i<others_begin.size(); ++i)
{
unsigned int length = others_begin[i] - others_begin[i-1];
offset += length;
bufferInt.push_back(offset);
}
lengthBuff = bufferInt.size()*sizeof(unsigned int);
fout.write((char*)&lengthBuff,sizeof(unsigned int));
fout.write((char*)&(bufferInt[0]), lengthBuff);
// bufferize and save types of primitives
std::vector<unsigned char> bufferUC;
bufferUC.reserve(triangles.size()/3 + quads.size()/4 + others_begin.size());
for (unsigned int i=0; i<triangles.size(); i+=3)
bufferUC.push_back((unsigned char)5);
for (unsigned int i=0; i<quads.size(); i+=4)
bufferUC.push_back((unsigned char)9);
for (unsigned int i=1; i<others_begin.size(); ++i)
bufferUC.push_back((unsigned char)7);
lengthBuff = bufferUC.size()*sizeof(unsigned char);
fout.write((char*)&lengthBuff,sizeof(unsigned int));
fout.write((char*)&(bufferUC[0]), lengthBuff);
fout.close();
fout.open(filename, std::ios_base::ate | std::ios_base::app);
fout << std::endl << "</AppendedData>" << std::endl;
fout << "</VTKFile>" << std::endl;
fout.close();
return true;
}
template <typename PFP>
bool exportVTUCompressed(typename PFP::MAP& map, const VertexAttribute<typename PFP::VEC3>& position, const char* filename)
...
...
@@ -1271,7 +1475,7 @@ bool exportVTUCompressed(typename PFP::MAP& map, const VertexAttribute<typename
fout << "<UnstructuredGrid>" << std::endl;
fout << "<Piece NumberOfPoints=\"" << position.nbElements() << "\" NumberOfCells=\""<< nbtotal << "\">" << std::endl;
fout << "<Points>" << std::endl;
fout
<<
"<DataArray type=
\"
Float32
\"
NumberOfComponents=
\"
3
\"
Format=
\"
ascii
\"
>"
<<
std
::
endl
;
fout << "<DataArray type=\"Float32\" NumberOfComponents=\"3\" Format=\"
binary
\">" << std::endl;
{
std::vector<VEC3> bufferV3;
...
...
@@ -1284,7 +1488,7 @@ bool exportVTUCompressed(typename PFP::MAP& map, const VertexAttribute<typename
fout << "</DataArray>" << std::endl;
fout << "</Points>" << std::endl;
fout << "<Cells>" << std::endl;
fout
<<
"<DataArray type=
\"
Int32
\"
Name=
\"
connectivity
\"
Format=
\"
ascii
\"
>"
<<
std
::
endl
;
fout << "<DataArray type=\"Int32\" Name=\"connectivity\" Format=\"
binary
\">" << std::endl;
std::vector<int> bufferInt;
bufferInt.reserve(triangles.size()+quads.size()+others.size());
...
...
@@ -1315,7 +1519,7 @@ bool exportVTUCompressed(typename PFP::MAP& map, const VertexAttribute<typename
Utils::zlibWriteCompressed((unsigned char*)(&bufferInt[0]),bufferInt.size()*sizeof(int), fout);
fout << "</DataArray>" << std::endl;
fout
<<
"<DataArray type=
\"
Int32
\"
Name=
\"
offsets
\"
Format=
\"
ascii
\"
>"
;
fout << "<DataArray type=\"Int32\" Name=\"offsets\" Format=\"
binary
\">" ;
bufferInt.clear();
...
...
@@ -1341,7 +1545,7 @@ bool exportVTUCompressed(typename PFP::MAP& map, const VertexAttribute<typename
Utils::zlibWriteCompressed((unsigned char*)(&bufferInt[0]),bufferInt.size()*sizeof(int), fout);
fout << std::endl << "</DataArray>" << std::endl;
fout
<<
"<DataArray type=
\"
UInt8
\"
Name=
\"
types
\"
Format=
\"
ascii
\"
>"
;
fout << "<DataArray type=\"UInt8\" Name=\"types\" Format=\"
binary
\">";
for (unsigned int i=0; i<triangles.size(); i+=3)
bufferInt.push_back(5);
...
...
@@ -1370,7 +1574,7 @@ bool exportVTUCompressed(typename PFP::MAP& map, const VertexAttribute<typename
template <typename PFP>
VTUExporter<PFP>::VTUExporter(typename PFP::MAP& map, const VertexAttribute<typename PFP::VEC3>& position):
m_map(map),m_position(position),
nbtotal
(
0
),
noPointData
(
true
),
noCellData
(
true
),
closed
(
false
)
nbtotal(0),noPointData(true),noCellData(true),closed(false)
,binaryMode(false)
{
if (map.dimension() != 2)
{
...
...
@@ -1380,7 +1584,7 @@ VTUExporter<PFP>::VTUExporter(typename PFP::MAP& map, const VertexAttribute<type
}
template <typename PFP>
bool
VTUExporter
<
PFP
>::
init
(
const
char
*
filename
)
bool VTUExporter<PFP>::init(const char* filename
, bool bin
)
{
// open file
...
...
@@ -1450,6 +1654,12 @@ bool VTUExporter<PFP>::init(const char* filename)
fout << "<UnstructuredGrid>" << std::endl;
fout << "<Piece NumberOfPoints=\"" << m_position.nbElements() << "\" NumberOfCells=\""<< nbtotal << "\">" << std::endl;
if (bin)
{
binaryMode = true;
f_tempoBin_out.open(filename+"bin_tempo_", std::ios_base::binary);
}
return true;
}
...
...
@@ -1527,8 +1737,6 @@ void VTUExporter<PFP>::endVertexAttributes()
}
template <typename PFP>
template<typename T>
void VTUExporter<PFP>::addFaceAttributeScal(const FaceAttribute<T>& attrib, const std::string& vtkType, const std::string& name)
...
...
@@ -1728,6 +1936,286 @@ bool VTUExporter<PFP>::close()
return true;
}
// BINARY VERSION
template <typename PFP>
template<typename T>
void VTUExporter<PFP>::addBinaryVertexAttributeScal(const VertexAttribute<T>& attrib, const std::string& vtkType, const std::string& name)
{
if (!noCellData)
{
CGoGNerr << "VTUExporter<PFP>::addVertexAttribute: endFaceAttributes before adding VertexAttribute"<< CGoGNendl;
return;
}
if (noPointData)
{
fout << "<PointData Scalars=\"scalars\">" << std::endl;
noPointData = false;
}
if (name.size() != 0)
fout << "<DataArray type=\""<< vtkType <<"\" Name=\""<<name<<"\" Format=\"ascii\">" << std::endl;
else
fout << "<DataArray type=\""<< vtkType <<"\" Name=\""<<attrib.name()<<"\" Format=\"ascii\">" << std::endl;
for (unsigned int i = attrib.begin(); i != attrib.end(); attrib.next(i))
fout << attrib[i] << std::endl;
fout << "</DataArray>" << std::endl;
}
template <typename PFP>
template<typename T>
void VTUExporter<PFP>::addBinaryVertexAttributeVect(const VertexAttribute<T>& attrib, const std::string& vtkType, unsigned int nbComp, const std::string& name)
{
if (!noCellData)
{
CGoGNerr << "VTUExporter<PFP>::addVertexAttribute: endFaceAttributes before adding VertexAttribute"<< CGoGNendl;
return;
}
if (noPointData)
{
fout << "<PointData Scalars=\"scalars\">" << std::endl;
noPointData = false;
}
if (nbComp==0)
nbComp = sizeof(T)/(unsigned int)(2*(vtkType[vtkType.size()-1]-'0')); // Float32 -> 2*2=4 bytes Int64 -> 8 bytes
if (name.size() != 0)
fout << "<DataArray type=\""<< vtkType <<"\" Name=\""<<name<<"\" NumberOfComponents=\""<< nbComp <<"\" Format=\"ascii\">" << std::endl;
else
fout << "<DataArray type=\""<< vtkType <<"\" Name=\""<<attrib.name()<<"\" NumberOfComponents=\""<< nbComp <<"\" Format=\"ascii\">" << std::endl;
for (unsigned int i = attrib.begin(); i != attrib.end(); attrib.next(i))
{
const T& a = attrib[i];
for (unsigned int j=0;j<nbComp;++j)
fout << a[j]<< " ";
fout << std::endl;
}
fout << "</DataArray>" << std::endl;
}
template <typename PFP>
void VTUExporter<PFP>::endBinaryVertexAttributes()
{
if (!noPointData)
fout << "</PointData>" << std::endl;
noPointData = true;
}
template <typename PFP>
template<typename T>
void VTUExporter<PFP>::addBinaryFaceAttributeScal(const FaceAttribute<T>& attrib, const std::string& vtkType, const std::string& name)
{
if (!noPointData)
{
CGoGNerr << "VTUExporter<PFP>::addFaceAttribute: endVertexAttributes before adding FaceAttribute"<< CGoGNendl;
return;
}
if (noCellData)
{
fout << "<CellData Scalars=\"scalars\">" << std::endl;
noCellData = false;
}
if (name.size() != 0)
fout << "<DataArray type=\""<< vtkType <<"\" Name=\""<<name<<"\" Format=\"ascii\">" << std::endl;
else
fout << "<DataArray type=\""<< vtkType <<"\" Name=\""<<attrib.name()<<"\" Format=\"ascii\">" << std::endl;
for (typename std::vector<Dart>::iterator it = bufferTri.begin(); it != bufferTri.end(); ++it)
fout << attrib[*it] << std::endl;
for (typename std::vector<Dart>::iterator it = bufferQuad.begin(); it != bufferQuad.end(); ++it)
fout << attrib[*it] << std::endl;
for (typename std::vector<Dart>::iterator it = bufferOther.begin(); it != bufferOther.end(); ++it)
fout << attrib[*it] << std::endl;
fout << "</DataArray>" << std::endl;
}
template <typename PFP>
template<typename T>
void VTUExporter<PFP>::addBinaryFaceAttributeVect(const FaceAttribute<T>& attrib, const std::string& vtkType, unsigned int nbComp, const std::string& name)
{
if (!noPointData)
{
CGoGNerr << "VTUExporter<PFP>::addFaceAttribute: endVertexAttributes before adding FaceAttribute"<< CGoGNendl;
return;
}
if (noCellData)
{
fout << "<CellData Scalars=\"scalars\">" << std::endl;
noCellData = false;
}
if (nbComp==0)
nbComp = sizeof(T)/(unsigned int)(2*(vtkType[vtkType.size()-1]-'0')); // Float32 -> 2*2=4 bytes Int64 -> 8 bytes
if (name.size() != 0)
fout << "<DataArray type=\""<< vtkType <<"\" Name=\""<<name<<"\" NumberOfComponents=\""<< nbComp <<"\" Format=\"ascii\">" << std::endl;
else
fout << "<DataArray type=\""<< vtkType <<"\" Name=\""<<attrib.name()<<"\" NumberOfComponents=\""<< nbComp <<"\" Format=\"ascii\">" << std::endl;
for (typename std::vector<Dart>::iterator it = bufferTri.begin(); it != bufferTri.end(); ++it)
{
const T& a = attrib[*it];
for (unsigned int j=0;j<nbComp;++j)
fout << a[j]<< " ";
fout << std::endl;
}
for (typename std::vector<Dart>::iterator it = bufferQuad.begin(); it != bufferQuad.end(); ++it)
{
const T& a = attrib[*it];
for (unsigned int j=0;j<nbComp;++j)
fout << a[j]<< " ";
fout << std::endl;
}
for (typename std::vector<Dart>::iterator it = bufferOther.begin(); it != bufferOther.end(); ++it)
{
const T& a = attrib[*it];
for (unsigned int j=0;j<nbComp;++j)
fout << a[j]<< " ";
fout << std::endl;
}
fout << " </DataArray>" << std::endl;
}
template <typename PFP>
void VTUExporter<PFP>::endBinaryFaceAttributes()
{
if (!noCellData)
fout << "</CellData>" << std::endl;
noCellData = true;
}
template <typename PFP>
bool VTUExporter<PFP>::binaryClose()
{
if (!noPointData)
endVertexAttributes();
if (!noCellData)
endFaceAttributes();
fout << "<Points>" << std::endl;
fout << "<DataArray type=\"Float32\" NumberOfComponents=\"3\" Format=\"ascii\">" << std::endl;
for (unsigned int i = m_position.begin(); i != m_position.end(); m_position.next(i))
{
const VEC3& P = m_position[i];
fout << P[0]<< " " << P[1]<< " " << P[2] << std::endl;
}
fout << "</DataArray>" << std::endl;
fout << "</Points>" << std::endl;
fout << "<Cells>" << std::endl;
fout << "<DataArray type=\"Int32\" Name=\"connectivity\" Format=\"ascii\">" << std::endl;
for (unsigned int i=0; i<triangles.size(); i+=3)
{
fout << triangles[i] << " " << triangles[i+1] << " " << triangles[i+2] << std::endl;
}
for (unsigned int i=0; i<quads.size(); i+=4)
{
fout << quads[i] << " " << quads[i+1] << " " << quads[i+2] << " " << quads[i+3]<< std::endl;
}
for (unsigned int i=1; i<others_begin.size(); ++i)
{
unsigned int beg = others_begin[i-1];
unsigned int end = others_begin[i];
for (unsigned int j=beg; j<end; ++j)
{
fout << others[j] << " ";
}
fout << std::endl;
}
fout << "</DataArray>" << std::endl;
fout << "<DataArray type=\"Int32\" Name=\"offsets\" Format=\"ascii\">" ;
unsigned int offset = 0;
for (unsigned int i=0; i<triangles.size(); i+=3)
{
offset += 3;
if (i%60 ==0)
fout << std::endl;
fout << " " << offset;
}
for (unsigned int i=0; i<quads.size(); i+=4)
{
offset += 4;
if (i%80 ==0)
fout << std::endl;
fout << " "<< offset;
}
for (unsigned int i=1; i<others_begin.size(); ++i)
{
unsigned int length = others_begin[i] - others_begin[i-1];
offset += length;
if (i%20 ==0)
fout << std::endl;
fout << " "<< offset;
}
fout << std::endl << "</DataArray>" << std::endl;
fout << "<DataArray type=\"UInt8\" Name=\"types\" Format=\"ascii\">";
for (unsigned int i=0; i<triangles.size(); i+=3)
{
if (i%60 ==0)
fout << std::endl;
fout << " 5";
}
for (unsigned int i=0; i<quads.size(); i+=4)
{
if (i%80 ==0)
fout << std::endl;
fout << " 9";
}
for (unsigned int i=1; i<others_begin.size(); ++i)
{
if (i%20 ==0)
fout << std::endl;
fout << " 7";
}
fout << std::endl << "</DataArray>" << std::endl;
fout << "</Cells>" << std::endl;
fout << "</Piece>" << std::endl;
fout << "</UnstructuredGrid>" << std::endl;
fout << "</VTKFile>" << std::endl;
fout.close();
closed=true;
return true;
}
template <typename PFP>
VTUExporter<PFP>::~VTUExporter()
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment