ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
PovFilter.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "PovFilter.h"
9 
10 // Local
11 #include "BinFilter.h"
12 
13 // CV_CORE_LIB
14 #include <CVLog.h>
15 
16 // CV_DB_LIB
17 #include <ecvGBLSensor.h>
18 #include <ecvHObjectCaster.h>
19 #include <ecvPointCloud.h>
20 
21 // Qt
22 #include <QFileInfo>
23 
24 // System
25 #include <cassert>
26 
27 // Max number of characters per line in an ASCII file
28 // TODO: use QFile instead!
29 const int MAX_ASCII_FILE_LINE_LENGTH = 4096;
30 
31 // Ground based LiDAR sensor mirror and body rotation order
32 // Refer to ccGBLSensor::ROTATION_ORDER
33 const char CC_SENSOR_ROTATION_ORDER_NAMES[][15] = {
34  "YAW_THEN_PITCH", // Rotation: body then mirror
35  "PITCH_THEN_YAW" // Rotation: mirror then body
36 };
37 
38 // same as CC_SENSOR_ROTATION_ORDER_NAMES but with the old names (used in
39 // versions prior to 2.5.6)
41  "THETA_PHI", // Rotation: body then mirror
42  "PHI_THETA" // Rotation: mirror then body
43 };
44 
46  : FileIOFilter({"_POV Filter",
47  DEFAULT_PRIORITY, // priority
48  QStringList{"pov"}, "pov",
49  QStringList{"Clouds + sensor info. [meta][ascii] (*.pov)"},
50  QStringList{"Clouds + sensor info. [meta][ascii] (*.pov)"},
51  Import | Export}) {}
52 
54  bool& multiple,
55  bool& exclusive) const {
56  if (type == CV_TYPES::POINT_CLOUD) {
57  multiple = true;
58  exclusive = true;
59  return true;
60  }
61  return false;
62 }
63 
65  const QString& filename,
66  const SaveParameters& parameters) {
67  Q_UNUSED(parameters);
68 
69  if (!entity || filename.isEmpty()) return CC_FERR_BAD_ARGUMENT;
70 
71  ccHObject::Container hClouds;
72  entity->filterChildren(hClouds, false, CV_TYPES::POINT_CLOUD);
73 
74  if (hClouds.empty()) return CC_FERR_NO_SAVE;
75 
76  std::vector<ccGBLSensor*> sensors;
77  std::vector<ccGenericPointCloud*> clouds;
78  {
79  for (unsigned i = 0; i < hClouds.size(); ++i) {
80  ccHObject::Container cloudSensors;
81  hClouds[i]->filterChildren(cloudSensors, false,
83  if (!cloudSensors.empty()) {
84  clouds.push_back(
86  if (cloudSensors.size() > 1)
87  CVLog::Warning(QString("Found more than one GBL sensor "
88  "associated to entity '%1'. Only "
89  "the first will be saved!")
90  .arg(hClouds[i]->getName()));
91 
92  sensors.push_back(static_cast<ccGBLSensor*>(cloudSensors[0]));
93  }
94  }
95  }
96  assert(sensors.size() == clouds.size());
97 
98  if (sensors.empty()) return CC_FERR_NO_SAVE;
99 
100  // FIXME
101  // the first GLS sensor will be used as reference! (ugly)
102  ccGBLSensor* firstGls = sensors.front();
103  if (sensors.size() > 1)
104  CVLog::Warning("Assuming all sensors are equivalent...");
105 
106  // we extract the body of the filename (without extension)
107  QString fullBaseName = QFileInfo(filename).completeBaseName();
108 
109  // main file (.POV)
110  FILE* mainFile = fopen(qPrintable(filename), "wt");
111  if (!mainFile) return CC_FERR_WRITING;
112 
113  if (fprintf(mainFile, "#CC_POVS_FILE\n") < 0 ||
114  fprintf(mainFile, "SENSOR_TYPE = %s\n",
116  0 ||
117  fprintf(mainFile, "SENSOR_BASE = 0\n") <
118  0 // DGM: sensor base is deprecated
119  || fprintf(mainFile, "UNITS = IGNORED\n") < 0 ||
120  fprintf(mainFile, "#END_HEADER\n") < 0) {
121  fclose(mainFile);
122  return CC_FERR_WRITING;
123  }
124 
125  // save sensor(s) info
126  {
127  for (unsigned i = 0; i < clouds.size(); ++i) {
128  QString thisFilename = fullBaseName + QString("_%1.bin").arg(i);
129 
130  BinFilter::SaveParameters parameters;
131  { parameters.alwaysDisplaySaveDialog = false; }
133  clouds[i], thisFilename, parameters,
135  if (error != CC_FERR_NO_ERROR) {
136  fclose(mainFile);
137  return error;
138  }
139 
140  // il faut ecrire le nom du fichier relatif et non absolu !
141  int result =
142  fprintf(mainFile, "\n#POV %u\nF %s\nT ASC\n", i,
143  qPrintable(QFileInfo(thisFilename).fileName()));
144 
145  if (result > 0) {
146  ccGBLSensor* gls = sensors[i];
147  const float* C = gls->getRigidTransformation().getTranslation();
148  result = fprintf(mainFile, "C %f %f %f\n", C[0], C[1], C[2]);
149 
150  if (result > 0) {
151  const float* mat = gls->getRigidTransformation().data();
152  result = fprintf(mainFile, "X %f %f %f\n", mat[0], mat[1],
153  mat[2]);
154  result = fprintf(mainFile, "Y %f %f %f\n", mat[4], mat[5],
155  mat[6]);
156  result = fprintf(mainFile, "Z %f %f %f\n", mat[8], mat[9],
157  mat[10]);
158  }
159 
160  if (result > 0)
161  result = fprintf(mainFile, "A %f %f\n", gls->getYawStep(),
162  gls->getPitchStep());
163 
164  if (result > 0) result = fprintf(mainFile, "#END_POV\n");
165  }
166 
167  // if (++n == palier)
168  //{
169  // //cancel requested
170  // if (pwin->isCancelRequested())
171  // result = -1;
172 
173  // percent += 1.0;
174  // pwin->update(percent);
175  // n = 0;
176  //}
177  }
178  }
179 
180  // delete pwin;
181 
182  fclose(mainFile);
183 
184  return CC_FERR_NO_ERROR;
185 }
186 
188  ccHObject& container,
189  LoadParameters& parameters) {
190  assert(!filename.isEmpty());
191 
192  // opening file
193  FILE* fp = fopen(qPrintable(filename), "rt");
194  if (!fp) return CC_FERR_READING;
195 
196  // read buffer
197  char line[MAX_ASCII_FILE_LINE_LENGTH];
198 
199  // header
200  if (!fgets(line, MAX_ASCII_FILE_LINE_LENGTH, fp)) {
201  fclose(fp);
202  return CC_FERR_READING;
203  }
204 
205  if (strcmp(line, "#CC_POVS_FILE\n") != 0) {
206  fclose(fp);
207  return CC_FERR_READING;
208  }
209 
210  char sensorType[256];
211  if (fscanf(fp, "SENSOR_TYPE = %s\n", sensorType) < 0) {
212  fclose(fp);
213  return CC_FERR_READING;
214  }
215 
216  ccGBLSensor::ROTATION_ORDER rotationOrder;
217  if (strcmp(sensorType,
219  0 ||
220  strcmp(sensorType, CC_SENSOR_ROTATION_ORDER_OLD_NAMES
221  [ccGBLSensor::YAW_THEN_PITCH]) == 0) {
222  rotationOrder = ccGBLSensor::YAW_THEN_PITCH;
223  } else if (strcmp(sensorType, CC_SENSOR_ROTATION_ORDER_NAMES
224  [ccGBLSensor::PITCH_THEN_YAW]) == 0 ||
225  strcmp(sensorType, CC_SENSOR_ROTATION_ORDER_OLD_NAMES
226  [ccGBLSensor::PITCH_THEN_YAW]) == 0) {
227  rotationOrder = ccGBLSensor::PITCH_THEN_YAW;
228  } else {
230  "[PovFilter::loadFile] Unhandled rotation order description! "
231  "(%s)",
232  sensorType);
233  fclose(fp);
234  return CC_FERR_READING;
235  }
236 
237  float base = 0.0f;
238  char unitsType[3]; // units: ignored in this version
239  if (fscanf(fp, "SENSOR_BASE = %f\n", &base) < 0 ||
240  fscanf(fp, "UNITS = %s\n", unitsType) < 0 ||
241  !fgets(line, MAX_ASCII_FILE_LINE_LENGTH, fp) ||
242  strcmp(line, "#END_HEADER\n") != 0) {
243  fclose(fp);
244  return CC_FERR_READING;
245  }
246 
247  CVLog::Print(
248  "[PovFilter::loadFile] POV FILE [Type %s - base=%f - unit: %s]",
249  sensorType, base, unitsType);
250 
251  // on extrait le chemin relatif
252  QString path = QFileInfo(filename).absolutePath();
253 
254  char subFileName[256];
255  char subFileType[12];
256 
257  while (fgets(line, MAX_ASCII_FILE_LINE_LENGTH, fp)) {
258  if ((line[0] == '#') && (line[1] == 'P')) {
259  CVLog::Print(QString(line).trimmed());
260  if (fscanf(fp, "F %s\n", subFileName) < 0) {
261  CVLog::PrintDebug("[PovFilter::loadFile] Read error (F) !");
262  fclose(fp);
263  return CC_FERR_READING;
264  }
265  if (fscanf(fp, "T %s\n", subFileType) < 0) {
266  CVLog::PrintDebug("[PovFilter::loadFile] Read error (T) !");
267  fclose(fp);
268  return CC_FERR_READING;
269  }
270 
271  // chargement du fichier (potentiellement plusieurs listes)
272  // correspondant au point de vue en cours
273  FileIOFilter::Shared filter =
275  if (!filter) {
276  CVLog::Warning(QString("[POV] No I/O filter found for loading "
277  "file '%1' (type = '%2')")
278  .arg(subFileName, subFileType));
279  fclose(fp);
280  return CC_FERR_UNKNOWN_FILE;
281  }
282 
285  QString("%1/%2").arg(path, subFileName), parameters, filter,
286  result);
287  if (entities) {
288  ccGLMatrix rot;
289  rot.toIdentity();
290  CCVector3 sensorCenter(0, 0, 0);
291  float dPhi = 1.0f;
292  float dTheta = 1.0f;
293 
294  while (fgets(line, MAX_ASCII_FILE_LINE_LENGTH, fp)) {
295  if (line[0] == '#')
296  break;
297  else if (line[0] == 'C') {
298  float C[3];
299  sscanf(line, "C %f %f %f\n", C, C + 1, C + 2);
300  sensorCenter = CCVector3::fromArray(C);
301  } else if (line[0] == 'X' || line[0] == 'Y' ||
302  line[0] == 'Z') {
303  float V[3];
304  sscanf(line + 2, "%f %f %f\n", V, V + 1, V + 2);
305 
306  unsigned char col =
307  static_cast<unsigned char>(line[0]) - 88;
308  float* mat = rot.data();
309  mat[col + 0] = V[0];
310  mat[col + 4] = V[1];
311  mat[col + 8] = V[2];
312  } else if (line[0] == 'A') {
313  sscanf(line, "A %f %f\n", &dTheta, &dPhi);
314  }
315  }
316 
317  ccHObject::Container clouds;
318  if (entities->isKindOf(CV_TYPES::POINT_CLOUD)) {
319  clouds.push_back(entities);
320  } else {
321  entities->filterChildren(clouds, true,
323  entities->detachAllChildren();
324  delete entities;
325  }
326  entities = nullptr;
327 
328  for (size_t i = 0; i < clouds.size(); ++i) {
329  ccGenericPointCloud* theCloud =
331 
332  ccGBLSensor* gls = new ccGBLSensor(rotationOrder);
333  // DGM: the base simply corresponds to a shift of the center
334  // along the X axis!
335  sensorCenter.x -= base;
336  // DGM: sensor center is now integrated in rigid
337  // transformation (= inverse of former rotation matrix +
338  // center as translation)
339  ccGLMatrix trans = rot.inverse();
340  trans.setTranslation(sensorCenter);
341  gls->setRigidTransformation(trans);
342  gls->setYawStep(dTheta);
343  gls->setPitchStep(dPhi);
344  gls->setVisible(true);
345  gls->setEnabled(false);
346 
347  if (gls->computeAutoParameters(theCloud)) {
348  theCloud->addChild(gls);
349  } else {
351  QString("[PovFilter::loadFile] failed to "
352  "create sensor on cloud #%1 (%2)")
353  .arg(i)
354  .arg(theCloud->getName()));
355  delete gls;
356  gls = nullptr;
357  }
358 
359  // theCloud->setName(subFileName);
360  container.addChild(theCloud);
361  }
362  } else {
364  break;
365  } else {
366  CVLog::Print(
367  "[PovFilter::loadFile] File (%s) not found or "
368  "empty!",
369  subFileName);
370  }
371  }
372  }
373  }
374 
375  fclose(fp);
376 
377  return CC_FERR_NO_ERROR;
378 }
int64_t CV_CLASS_ENUM
Type of object type flags (64 bits)
Definition: CVTypes.h:97
std::string filename
char type
CC_FILE_ERROR
Typical I/O filter errors.
Definition: FileIOFilter.h:20
@ CC_FERR_CANCELED_BY_USER
Definition: FileIOFilter.h:30
@ CC_FERR_WRITING
Definition: FileIOFilter.h:25
@ CC_FERR_BAD_ARGUMENT
Definition: FileIOFilter.h:22
@ CC_FERR_UNKNOWN_FILE
Definition: FileIOFilter.h:23
@ CC_FERR_NO_SAVE
Definition: FileIOFilter.h:27
@ CC_FERR_NO_ERROR
Definition: FileIOFilter.h:21
@ CC_FERR_READING
Definition: FileIOFilter.h:26
const int MAX_ASCII_FILE_LINE_LENGTH
Definition: PovFilter.cpp:29
const char CC_SENSOR_ROTATION_ORDER_NAMES[][15]
Definition: PovFilter.cpp:33
const char CC_SENSOR_ROTATION_ORDER_OLD_NAMES[][10]
Definition: PovFilter.cpp:40
core::Tensor result
Definition: VtkUtils.cpp:76
static QString GetFileFilter()
Definition: BinFilter.h:20
static bool PrintDebug(const char *format,...)
Same as Print, but works only in Debug mode.
Definition: CVLog.cpp:153
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
Generic file I/O filter.
Definition: FileIOFilter.h:46
static constexpr float DEFAULT_PRIORITY
Definition: FileIOFilter.h:313
static CC_FILE_ERROR SaveToFile(ccHObject *entities, const QString &filename, const SaveParameters &parameters, Shared filter)
static Shared FindBestFilterForExtension(const QString &ext)
Returns the best filter (presumably) to open a given file extension.
QSharedPointer< FileIOFilter > Shared
Shared type.
Definition: FileIOFilter.h:97
static ccHObject * LoadFromFile(const QString &filename, LoadParameters &parameters, Shared filter, CC_FILE_ERROR &result)
Loads one or more entities from a file with a known filter.
virtual CC_FILE_ERROR loadFile(const QString &filename, ccHObject &container, LoadParameters &parameters) override
Loads one or more entities from a file.
Definition: PovFilter.cpp:187
virtual CC_FILE_ERROR saveToFile(ccHObject *entity, const QString &filename, const SaveParameters &parameters) override
Saves an entity (or a group of) to a file.
Definition: PovFilter.cpp:64
virtual bool canSave(CV_CLASS_ENUM type, bool &multiple, bool &exclusive) const override
Returns whether this I/O filter can save the specified type of entity.
Definition: PovFilter.cpp:53
Type x
Definition: CVGeom.h:137
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
virtual void setVisible(bool state)
Sets entity visibility.
Ground-based Laser sensor.
Definition: ecvGBLSensor.h:26
ROTATION_ORDER
The order of inner-rotations of the sensor (body/mirrors)
Definition: ecvGBLSensor.h:33
bool computeAutoParameters(cloudViewer::GenericCloud *theCloud)
Computes angular parameters automatically (all but the angular steps!)
PointCoordinateType getPitchStep() const
Returns the lateral pitch step (in radians)
Definition: ecvGBLSensor.h:105
void setYawStep(PointCoordinateType dTheta)
Sets the yaw step.
ROTATION_ORDER getRotationOrder() const
Returns the sensor internal rotations order.
Definition: ecvGBLSensor.h:156
PointCoordinateType getYawStep() const
Returns the yaw step (in radians)
Definition: ecvGBLSensor.h:130
void setPitchStep(PointCoordinateType dPhi)
Sets the pitch step.
T * getTranslation()
Retruns a pointer to internal translation.
T * data()
Returns a pointer to internal data.
ccGLMatrixTpl< T > inverse() const
Returns inverse transformation.
void setTranslation(const Vector3Tpl< float > &Tr)
Sets translation (float version)
virtual void toIdentity()
Sets matrix to identity.
Float version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:19
A 3D cloud interface with associated features (color, normals, octree, etc.)
static ccGenericPointCloud * ToGenericPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccGenericPointCloud.
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
void detachAllChildren()
Removes a specific child.
unsigned filterChildren(Container &filteredChildren, bool recursive=false, CV_CLASS_ENUM filter=CV_TYPES::OBJECT, bool strict=false) const
Collects the children corresponding to a certain pattern.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
virtual void setEnabled(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:102
bool isKindOf(CV_CLASS_ENUM type) const
Definition: ecvObject.h:128
virtual ccGLMatrix & getRigidTransformation()
Definition: ecvSensor.h:105
virtual void setRigidTransformation(const ccGLMatrix &mat)
Definition: ecvSensor.h:99
static void error(char *msg)
Definition: lsd.c:159
@ GBL_SENSOR
Definition: CVTypes.h:117
@ POINT_CLOUD
Definition: CVTypes.h:104
static const std::string path
Definition: PointCloud.cpp:59
Generic loading parameters.
Definition: FileIOFilter.h:51
Generic saving parameters.
Definition: FileIOFilter.h:84