ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
PNFilter.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 "PNFilter.h"
9 
10 // CV_DB_LIB
11 #include <CVLog.h>
12 #include <ecvHObjectCaster.h>
13 #include <ecvPointCloud.h>
14 #include <ecvProgressDialog.h>
15 
16 // Qt
17 #include <QFile>
18 
19 // default normal value
20 static const CCVector3 s_defaultNorm(0, 0, 1);
21 
23  : FileIOFilter({"_Point+Normal Filter",
24  DEFAULT_PRIORITY, // priority
25  QStringList{"pn"}, "pn",
26  QStringList{"Point+Normal cloud (*.pn)"},
27  QStringList{"Point+Normal cloud (*.pn)"},
28  Import | Export}) {}
29 
31  bool& multiple,
32  bool& exclusive) const {
33  if (type == CV_TYPES::POINT_CLOUD) {
34  multiple = false;
35  exclusive = true;
36  return true;
37  }
38  return false;
39 }
40 
42  const QString& filename,
43  const SaveParameters& parameters) {
44  if (!entity || filename.isEmpty()) return CC_FERR_BAD_ARGUMENT;
45 
46  // the cloud to save
47  ccGenericPointCloud* theCloud =
49  if (!theCloud) {
50  CVLog::Warning("[PN] This filter can only save one cloud at a time!");
52  }
53  unsigned numberOfPoints = theCloud->size();
54 
55  if (numberOfPoints == 0) {
56  CVLog::Warning("[PN] Input cloud is empty!");
57  return CC_FERR_NO_SAVE;
58  }
59 
60  // open binary file for writing
61  QFile out(filename);
62  if (!out.open(QIODevice::WriteOnly)) return CC_FERR_WRITING;
63 
64  // Has the cloud been recentered?
65  if (theCloud->isShifted())
66  CVLog::Warning(QString("[PNFilter::save] Can't recenter or rescale "
67  "cloud '%1' when saving it in a PN file!")
68  .arg(theCloud->getName()));
69 
70  bool hasNorms = theCloud->hasNormals();
71  if (!hasNorms)
72  CVLog::Warning(QString("[PNFilter::save] Cloud '%1' has no normal (we "
73  "will save points with a default normal)!")
74  .arg(theCloud->getName()));
75  float norm[3] = {static_cast<float>(s_defaultNorm.x),
76  static_cast<float>(s_defaultNorm.y),
77  static_cast<float>(s_defaultNorm.z)};
78 
79  // progress dialog
80  QScopedPointer<ecvProgressDialog> pDlg(0);
81  if (pDlg) {
82  pDlg.reset(new ecvProgressDialog(
83  true, parameters.parentWidget)); // cancel available
84  pDlg->setMethodTitle(QObject::tr("Save PN file"));
85  pDlg->setInfo(QObject::tr("Points: %L1").arg(numberOfPoints));
86  pDlg->start();
87  }
88  cloudViewer::NormalizedProgress nprogress(pDlg.data(), numberOfPoints);
89 
91 
92  for (unsigned i = 0; i < numberOfPoints; i++) {
93  // write point
94  {
95  const CCVector3* P = theCloud->getPoint(i);
96 
97  // conversion to float
98  CCVector3f Pfloat = CCVector3f::fromArray(P->u);
99  if (out.write(reinterpret_cast<const char*>(Pfloat.u),
100  3 * sizeof(float)) < 0) {
102  break;
103  }
104  }
105 
106  // write normal
107  if (hasNorms) {
108  const CCVector3& N = theCloud->getPointNormal(i);
109  // conversion to float
110  norm[0] = static_cast<float>(N.x);
111  norm[1] = static_cast<float>(N.y);
112  norm[2] = static_cast<float>(N.z);
113  }
114  if (out.write(reinterpret_cast<const char*>(norm), 3 * sizeof(float)) <
115  0) {
117  break;
118  }
119 
120  if (pDlg && !nprogress.oneStep()) {
122  break;
123  }
124  }
125 
126  out.close();
127 
128  return result;
129 }
130 
132  ccHObject& container,
133  LoadParameters& parameters) {
134  // opening file
135  QFile in(filename);
136  if (!in.open(QIODevice::ReadOnly)) return CC_FERR_READING;
137 
138  // we deduce the points number from the file size
139  qint64 fileSize = in.size();
140  qint64 singlePointSize = 6 * sizeof(float);
141  // check that size is ok
142  if (fileSize == 0) return CC_FERR_NO_LOAD;
143  if ((fileSize % singlePointSize) != 0) return CC_FERR_MALFORMED_FILE;
144  unsigned numberOfPoints = static_cast<unsigned>(fileSize / singlePointSize);
145 
146  // progress dialog
147  QScopedPointer<ecvProgressDialog> pDlg(0);
148  if (parameters.parentWidget) {
149  pDlg.reset(new ecvProgressDialog(
150  true, parameters.parentWidget)); // cancel available
151  pDlg->setMethodTitle(QObject::tr("Open PN file"));
152  pDlg->setInfo(QObject::tr("Points: %L1").arg(numberOfPoints));
153  pDlg->start();
154  }
155  cloudViewer::NormalizedProgress nprogress(pDlg.data(), numberOfPoints);
156 
157  ccPointCloud* loadedCloud = 0;
158  // if the file is too big, it will be chuncked in multiple parts
159  unsigned chunkIndex = 0;
160  unsigned fileChunkPos = 0;
161  unsigned fileChunkSize = 0;
162  // number of points read for the current cloud part
163  unsigned pointsRead = 0;
165 
166  for (unsigned i = 0; i < numberOfPoints; i++) {
167  // if we reach the max. cloud size limit, we cerate a new chunk
168  if (pointsRead == fileChunkPos + fileChunkSize) {
169  if (loadedCloud) container.addChild(loadedCloud);
170  fileChunkPos = pointsRead;
171  fileChunkSize =
172  std::min<unsigned>(numberOfPoints - pointsRead,
174  loadedCloud = new ccPointCloud(
175  QString("unnamed - Cloud #%1").arg(++chunkIndex));
176  if (!loadedCloud ||
177  !loadedCloud->reserveThePointsTable(fileChunkSize) ||
178  !loadedCloud->reserveTheNormsTable()) {
180  if (loadedCloud) delete loadedCloud;
181  loadedCloud = 0;
182  break;
183  }
184  loadedCloud->showNormals(true);
185  }
186 
187  // we read the 3 coordinates of the point
188  float rBuff[3];
189  if (in.read((char*)rBuff, 3 * sizeof(float)) >= 0) {
190  // conversion to CCVector3
191  CCVector3 P = CCVector3::fromArray(rBuff);
192  loadedCloud->addPoint(P);
193  } else {
195  break;
196  }
197 
198  // then the 3 components of the normal vector
199  if (in.read((char*)rBuff, 3 * sizeof(float)) >= 0) {
200  loadedCloud->addNorm(CCVector3::fromArray(rBuff));
201  } else {
202  // add fake normal for consistency then break
203  loadedCloud->addNorm(s_defaultNorm);
205  break;
206  }
207 
208  ++pointsRead;
209 
210  if (pDlg && !nprogress.oneStep()) {
212  break;
213  }
214  }
215 
216  in.close();
217 
218  if (loadedCloud) {
219  loadedCloud->shrinkToFit();
220  container.addChild(loadedCloud);
221  }
222 
223  return result;
224 }
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_NO_LOAD
Definition: FileIOFilter.h:28
@ CC_FERR_WRITING
Definition: FileIOFilter.h:25
@ CC_FERR_MALFORMED_FILE
Definition: FileIOFilter.h:32
@ CC_FERR_BAD_ARGUMENT
Definition: FileIOFilter.h:22
@ CC_FERR_NO_SAVE
Definition: FileIOFilter.h:27
@ CC_FERR_NO_ERROR
Definition: FileIOFilter.h:21
@ CC_FERR_BAD_ENTITY_TYPE
Definition: FileIOFilter.h:29
@ CC_FERR_READING
Definition: FileIOFilter.h:26
@ CC_FERR_NOT_ENOUGH_MEMORY
Definition: FileIOFilter.h:31
static const CCVector3 s_defaultNorm(0, 0, 1)
core::Tensor result
Definition: VtkUtils.cpp:76
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
Generic file I/O filter.
Definition: FileIOFilter.h:46
static constexpr float DEFAULT_PRIORITY
Definition: FileIOFilter.h:313
PNFilter()
Definition: PNFilter.cpp:22
virtual CC_FILE_ERROR loadFile(const QString &filename, ccHObject &container, LoadParameters &parameters) override
Loads one or more entities from a file.
Definition: PNFilter.cpp:131
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: PNFilter.cpp:41
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: PNFilter.cpp:30
Type y
Definition: CVGeom.h:137
Type u[3]
Definition: CVGeom.h:139
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
virtual bool hasNormals() const
Returns whether normals are enabled or not.
virtual void showNormals(bool state)
Sets normals visibility.
A 3D cloud interface with associated features (color, normals, octree, etc.)
virtual const CCVector3 & getPointNormal(unsigned pointIndex) const =0
Returns normal corresponding to a given point.
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.
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void addNorm(const CCVector3 &N)
Pushes a normal vector on stack (shortcut)
bool reserveTheNormsTable()
Reserves memory to store the compressed normals.
void shrinkToFit()
Removes unused capacity.
bool reserveThePointsTable(unsigned _numberOfPoints)
Reserves memory to store the points coordinates.
bool isShifted() const
Returns whether the cloud is shifted or not.
virtual unsigned size() const =0
Returns the number of points.
virtual const CCVector3 * getPoint(unsigned index) const =0
Returns the ith point.
bool oneStep()
Increments total progress value of a single unit.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
Graphical progress indicator (thread-safe)
const unsigned CC_MAX_NUMBER_OF_POINTS_PER_CLOUD
@ POINT_CLOUD
Definition: CVTypes.h:104
#define fileChunkSize(nChunkSize)
Definition: sqlite3.c:96908
Generic loading parameters.
Definition: FileIOFilter.h:51
QWidget * parentWidget
Parent widget (if any)
Definition: FileIOFilter.h:78
Generic saving parameters.
Definition: FileIOFilter.h:84
QWidget * parentWidget
Parent widget (if any)
Definition: FileIOFilter.h:93