19 #include <QXmlStreamReader>
27 cmd.
print(
"[CROSS SECTION]");
29 static QString s_xmlACloudViewer =
"ACloudViewer";
30 static QString s_xmlCloudCompare =
"CloudCompare";
31 static QString s_xmlBoxThickness =
"BoxThickness";
32 static QString s_xmlBoxCenter =
"BoxCenter";
33 static QString s_xmlRepeatDim =
"RepeatDim";
34 static QString s_xmlRepeatGap =
"RepeatGap";
35 static QString s_xmlFilePath =
"FilePath";
36 static QString s_outputXmlFilePath =
"OutputFilePath";
41 QString(
"Missing parameter: XML parameters file after \"-%1\"")
43 QString xmlFilename = cmd.
arguments().takeFirst();
48 bool repeatDim[3] = {
false,
false,
false};
49 double repeatGap = 0.0;
51 bool autoCenter =
true;
52 QString inputFilePath;
53 QString outputFilePath;
55 QFile file(xmlFilename);
56 if (!file.open(QFile::ReadOnly | QFile::Text)) {
58 QString(
"Couldn't open XML file '%1'").arg(xmlFilename));
62 QXmlStreamReader stream(&file);
65 if (!stream.readNextStartElement() ||
66 (stream.name() != s_xmlACloudViewer &&
67 stream.name() != s_xmlCloudCompare)) {
69 QString(
"Invalid XML file (should start by '<%1 or %2>')")
70 .arg(s_xmlACloudViewer, s_xmlCloudCompare));
73 unsigned mandatoryCount = 0;
74 while (stream.readNextStartElement())
76 if (stream.name() == s_xmlBoxThickness) {
77 QXmlStreamAttributes attributes = stream.attributes();
78 if (!readVector(attributes, boxThickness, s_xmlBoxThickness,
81 stream.skipCurrentElement();
83 }
else if (stream.name() == s_xmlBoxCenter) {
84 QXmlStreamAttributes attributes = stream.attributes();
85 if (!readVector(attributes, boxCenter, s_xmlBoxCenter, cmd))
87 stream.skipCurrentElement();
89 }
else if (stream.name() == s_xmlRepeatDim) {
90 QString itemValue = stream.readElementText();
92 int dim = itemValue.toInt(&ok);
93 if (!ok || dim < 0 || dim > 2) {
94 return cmd.
error(QString(
"Invalid XML file (invalid value "
96 .arg(s_xmlRepeatDim));
98 repeatDim[dim] =
true;
99 }
else if (stream.name() == s_xmlRepeatGap) {
100 QString itemValue = stream.readElementText();
102 repeatGap = itemValue.toDouble(&ok);
104 return cmd.
error(QString(
"Invalid XML file (invalid value "
106 .arg(s_xmlRepeatGap));
108 }
else if (stream.name() == s_xmlFilePath) {
109 inputFilePath = stream.readElementText();
110 if (!QDir(inputFilePath).exists()) {
111 return cmd.
error(QString(
"Invalid file path (directory "
112 "pointed by '<%1>' doesn't exist)")
113 .arg(s_xmlFilePath));
116 }
else if (stream.name() == s_outputXmlFilePath) {
117 outputFilePath = stream.readElementText();
118 if (!QDir(outputFilePath).exists()) {
120 QString(
"Invalid output file path (directory "
121 "pointed by '<%1>' doesn't exist)")
122 .arg(s_outputXmlFilePath));
126 cmd.
warning(QString(
"Unknown element: %1")
127 .arg(stream.name().toString()));
128 stream.skipCurrentElement();
132 if (mandatoryCount < 1 ||
133 (!repeatDim[0] && !repeatDim[1] && !repeatDim[2])) {
135 QString(
"Some mandatory elements are missing in the XML "
136 "file (see documentation)"));
144 return cmd.
error(QString(
"Invalid box thickness"));
148 boxThickness +
CCVector3(repeatGap, repeatGap, repeatGap);
152 return cmd.
error(QString(
153 "Repeat gap can't be equal or smaller than 'minus' box width"));
156 if (outputFilePath.isEmpty()) {
157 outputFilePath = inputFilePath;
160 int iterationCount = 1;
165 bool fromFiles =
false;
166 if (!inputFilePath.isEmpty()) {
168 dir = QDir(inputFilePath);
169 assert(dir.exists());
170 files = dir.entryList(QDir::Files);
171 iterationCount = files.size();
179 for (
int f = 0; f < iterationCount; ++f) {
183 assert(f < files.size());
184 filename = dir.absoluteFilePath(files[f]);
186 if (!fileinfo.isFile() || fileinfo.suffix().toUpper() ==
"XML") {
191 cmd.
print(QString(
"Processing file: '%1'").arg(files[f]));
197 QStringList realArguments = cmd.
arguments();
199 QStringList loadArguments;
209 cmd.
warning(
"\tFailed to load file!");
213 assert(iterationCount == 1);
221 for (
size_t i = 0; i < cmd.
clouds().
size(); ++i)
222 entities.push_back(cmd.
clouds()[i].pc);
223 for (
size_t j = 0; j < cmd.
meshes().
size(); ++j)
224 entities.push_back(cmd.
meshes()[j].mesh);
225 }
catch (
const std::bad_alloc &) {
226 return cmd.
error(
"Not enough memory!");
229 for (
size_t i = 0; i < entities.size(); ++i) {
235 QString(
"Entity '%1' has an invalid bounding-box!")
244 basename = QFileInfo(
filename).baseName();
246 basename = i < cmd.
clouds().size()
247 ? cmd.
clouds()[i].basename
252 if (entities.size() > 1) basename += QString(
"_%1").arg(i + 1);
254 QDir outputDir(outputFilePath);
255 if (outputFilePath.isEmpty()) {
258 outputDir = QDir::current();
268 assert(outputDir.exists());
269 if (outputDir.cd(basename)) {
271 cmd.
warning(QString(
"Subdirectory '%1' already exists")
273 }
else if (outputDir.mkdir(basename)) {
274 outputDir.cd(basename);
277 QString(
"Failed to create subdirectory '%1' (check "
278 "access rights and base name validity!)")
283 int toto =
ceil(-0.4);
284 int toto2 =
ceil(-0.6);
289 unsigned steps[3] = {1, 1, 1};
290 for (
unsigned d = 0; d < 3; ++d) {
293 boxThickness.
u[d] / 2;
296 int stepsToMinBorder =
static_cast<int>(
297 ceil(distToMinBorder / repeatStep.
u[d]));
298 C0.
u[d] -= stepsToMinBorder * repeatStep.
u[d];
302 int stepsToMaxBoder =
static_cast<int>(
303 ceil(distToMaxBorder / repeatStep.
u[d]) + 1);
304 assert(stepsToMaxBoder >= 0);
305 steps[d] = std::max<unsigned>(stepsToMaxBoder, 1);
309 cmd.
print(QString(
"Will extract up to (%1 x %2 x %3) = %4 "
314 .arg(steps[0] * steps[1] * steps[2]));
317 for (
unsigned dx = 0; dx < steps[0]; ++dx) {
318 for (
unsigned dy = 0; dy < steps[1]; ++dy) {
319 for (
unsigned dz = 0; dz < steps[2]; ++dz) {
323 ccBBox cropBox(C - boxThickness / 2,
324 C + boxThickness / 2);
325 cmd.
print(QString(
"Box (%1;%2;%3) --> (%4;%5;%6)")
335 QString outputBasename =
336 basename + QString(
"_%1_%2_%3")
342 if (i < cmd.
clouds().size()) {
347 outputDir.absolutePath(),
349 ?
static_cast<int>(i)
355 static_cast<ccMesh *
>(croppedEnt),
357 outputDir.absolutePath(),
359 ?
static_cast<int>(i)
365 croppedEnt =
nullptr;
367 if (!errorStr.isEmpty())
368 return cmd.
error(errorStr);
386 bool CommandCrossSection::readVector(
const QXmlStreamAttributes &attributes,
390 if (attributes.size() < 3) {
391 return cmd.
error(QString(
"Invalid XML file (3 attributes expected for "
397 for (
int i = 0; i < attributes.size(); ++i) {
398 QString
name = attributes[i].name().toString().toUpper();
399 QString value = attributes[i].value().toString();
403 P.
x = value.toDouble(&ok);
405 }
else if (
name ==
"Y") {
406 P.
y = value.toDouble(&ok);
408 }
else if (
name ==
"Z") {
409 P.
z = value.toDouble(&ok);
417 QString(
"Invalid XML file (numerical attribute expected "
418 "for attribute '%1' of element '<%2>')")
419 .arg(
name, element));
424 return cmd.
error(QString(
"Invalid XML file (attributes 'X','Y' and 'Z' "
425 "are mandatory for element '<%1>')")
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
float PointCoordinateType
Type of the coordinates of a (N-D) point.
virtual QStringList & arguments()=0
Returns the list of arguments.
virtual void warning(const QString &message) const =0
virtual void print(const QString &message) const =0
virtual bool error(const QString &message) const =0
virtual QString exportEntity(CLEntityDesc &entityDesc, const QString &suffix=QString(), QString *outputFilename=nullptr, ccCommandLineInterface::ExportOptions options=ExportOption::NoOptions)=0
Exports a cloud or a mesh.
virtual std::vector< CLMeshDesc > & meshes()
Currently opened meshes and their filename.
virtual void removeClouds(bool onlyLast=false)=0
Removes all clouds (or only the last one ;)
virtual void removeMeshes(bool onlyLast=false)=0
Removes all meshes (or only the last one ;)
virtual std::vector< CLCloudDesc > & clouds()
Currently opened point clouds and their filename.
Hierarchical CLOUDVIEWER Object.
virtual ccBBox getOwnBB(bool withGLFeatures=false)
Returns the entity's own bounding-box.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
virtual unsigned size() const override
Returns the number of triangles.
virtual QString getName() const
Returns object name.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
Vector3Tpl< T > getCenter() const
Returns center.
const Vector3Tpl< T > & maxCorner() const
Returns max corner (const)
const Vector3Tpl< T > & minCorner() const
Returns min corner (const)
bool isValid() const
Returns whether bounding box is valid or not.
unsigned size() const override
constexpr char COMMAND_CROSS_SECTION[]
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
bool LessThanEpsilon(float x)
Test a floating point number against our epsilon (a very small number).
Loaded cloud description.
bool process(ccCommandLineInterface &cmd) override
Main process.
bool process(ccCommandLineInterface &cmd) override
Main process.