ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
CSVMatrixFilter.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 "CSVMatrixFilter.h"
9 
10 // Local
11 #include "CSVMatrixOpenDialog.h"
12 
13 // Qt
14 #include <QFile>
15 #include <QFileInfo>
16 
17 // Qt5/Qt6 Compatibility
18 #include <QtCompat.h>
19 
20 // CV_DB_LIB
21 #include <CVLog.h>
22 #include <ecvHObject.h>
23 #include <ecvMesh.h>
24 #include <ecvPointCloud.h>
25 
26 // System
27 #include <assert.h>
28 #include <string.h>
29 
30 // semi-persistent parameters
31 static QChar s_separator(',');
32 static double s_xSpacing = 1.0;
33 static double s_ySpacing = 1.0;
34 static bool s_inverseRows = false;
35 static bool s_loadAsMesh = false;
36 static bool s_useTexture = false;
37 
39  : FileIOFilter({"_CSV Matrix Filter",
40  DEFAULT_PRIORITY, // priority
41  QStringList{"csv"}, "csv",
42  QStringList{"CSV matrix cloud (*.csv)"}, QStringList(),
43  Import}) {}
44 
46  ccHObject& container,
47  LoadParameters& parameters) {
48  CSVMatrixOpenDialog openDlg(nullptr);
49  openDlg.lineEditSeparator->setText(s_separator);
50  openDlg.xDoubleSpinBox->setValue(s_xSpacing);
51  openDlg.yDoubleSpinBox->setValue(s_ySpacing);
52  openDlg.inverseRowCheckBox->setChecked(s_inverseRows);
53  openDlg.loadAsMeshCheckBox->setChecked(s_loadAsMesh);
54  openDlg.useTextureCheckBox->setChecked(s_useTexture);
55 
56  if (!openDlg.exec()) {
58  }
59 
60  if (openDlg.lineEditSeparator->text().isEmpty()) {
61  CVLog::Warning(QString("[CSVMatrixFilter] Invalid separator!"));
62  return CC_FERR_BAD_ARGUMENT;
63  }
64 
65  s_separator = openDlg.lineEditSeparator->text().at(0);
66  s_xSpacing = openDlg.xDoubleSpinBox->value();
67  s_ySpacing = openDlg.yDoubleSpinBox->value();
68  s_inverseRows = openDlg.inverseRowCheckBox->isChecked();
69  s_loadAsMesh = openDlg.loadAsMeshCheckBox->isChecked();
70  s_useTexture = openDlg.useTextureCheckBox->isChecked();
71 
72  QFile file(filename);
73  if (!file.open(QFile::ReadOnly | QFile::Text)) return CC_FERR_READING;
74 
75  QTextStream stream(&file);
76 
77  unsigned lineIndex = 0;
78  int width = -1;
79  int row = 0;
81  ccPointCloud* cloud = new ccPointCloud();
82  while (file.error() == QFile::NoError && !file.atEnd()) {
83  QString line = stream.readLine();
84  ++lineIndex;
85 
86  // skip comments
87  if (line.startsWith("//")) continue;
88 
89  if (line.size() == 0) {
91  QString("[CSVMatrixFilter] Line %1 is empty (ignored)")
92  .arg(lineIndex));
93  continue;
94  }
95 
96  // we split the current line
97  QStringList parts = line.split(s_separator, QtCompat::SkipEmptyParts);
98 
99  // if we have reached the max. number of points per cloud
100  if (width < 0) {
101  width = parts.size();
102  CVLog::Print(
103  QString("[CSVMatrixFilter] Detected width: %1").arg(width));
104  } else if (width != parts.size()) {
105  CVLog::Warning(QString("[CSVMatrixFilter] Line %1 has not the same "
106  "width as the previous ones!")
107  .arg(lineIndex));
109  break;
110  }
111 
112  // reserve memory for next row
113  if (!cloud->reserve(cloud->size() + static_cast<unsigned>(width))) {
115  break;
116  }
117 
118  bool ok = true;
119  CCVector3 P(0,
120  static_cast<PointCoordinateType>(
121  (s_inverseRows ? -row : row) * s_ySpacing),
122  0);
123  for (int i = 0; i < width; ++i) {
124  P.z = parts[i].toDouble(&ok);
125  if (!ok) break;
126  P.x = static_cast<PointCoordinateType>(i * s_xSpacing);
127 
128  cloud->addPoint(P);
129  }
130  ++row;
131  }
132  unsigned rowCount = static_cast<unsigned>(row);
133  unsigned colCount = static_cast<unsigned>(width);
134 
135  file.close();
136 
137  if (result == CC_FERR_NO_ERROR) {
138  // load as mesh
139  ccMesh* mesh = 0;
140  if (s_loadAsMesh) {
141  cloud->setName("vertices");
142  mesh = new ccMesh(cloud);
143 
144  unsigned triCount = (colCount - 1) * (rowCount - 1) * 2;
145  if (!mesh->reserve(triCount)) {
146  delete mesh;
147  mesh = 0;
148  } else {
149  mesh->addChild(cloud);
150  cloud->setEnabled(false);
151  // add triangles
152  for (unsigned j = 0; j < rowCount - 1; ++j) {
153  unsigned index = j * colCount;
154  for (unsigned i = 0; i < colCount - 1; ++i) {
155  mesh->addTriangle(index + i, index + colCount + i,
156  index + i + 1);
157  mesh->addTriangle(index + i + 1, index + colCount + i,
158  index + colCount + i + 1);
159  }
160  }
161  // compute normals
162  if (mesh->computePerVertexNormals()) {
163  mesh->showNormals(true);
164  } else {
166  QString("[CSVMatrixFilter] Failed to compute "
167  "normals on mesh (not enough memory?)"));
168  }
169  }
170  }
171 
172  // texture
173  if (s_useTexture) {
174  QString filename = openDlg.textureFilenameLineEdit->text();
175  QImage texture;
176  if (!texture.load(filename)) {
177  CVLog::Warning(QString("[CSVMatrixFilter] Failed to load "
178  "texture from file '%1'")
179  .arg(filename));
180  } else {
181  if (mesh) {
182  TextureCoordsContainer* texCoords =
184  if (texCoords->reserveSafe(cloud->size()) &&
187  // generate texture coordinates
188  {
189  for (unsigned j = 0; j < rowCount; ++j) {
190  TexCoords2D coord(0, static_cast<float>(j) /
191  (rowCount - 1));
192  for (unsigned i = 0; i < colCount; ++i) {
193  coord.tx = static_cast<float>(i) /
194  (colCount - 1);
195  texCoords->addElement(coord);
196  }
197  }
198  }
199  mesh->setTexCoordinatesTable(texCoords);
200 
201  // create material
202  /*ccMaterial::Shared mat(new ccMaterial("texture"));
203  mat->setTexture(texture,filename,false);
204  ccMaterialSet* matSet = new ccMaterialSet("Materials");
205  matSet->push_back(mat);
206  mesh->setMaterialSet(matSet);*/
207 
208  // assign texture coordinaetes and material to each
209  // triangle
210  for (unsigned i = 0; i < mesh->size(); ++i) {
212  mesh->getTriangleVertIndexes(i);
213  mesh->addTriangleTexCoordIndexes(tsi->i1, tsi->i2,
214  tsi->i3);
215  mesh->addTriangleMtlIndex(0);
216  }
217 
218  mesh->showMaterials(true);
219  } else {
221  "[CSVMatrixFilter] Not enough memory to map "
222  "the texture on the mesh!");
223  texCoords->release();
224  texCoords = 0;
225  }
226  } else {
227  if (texture.width() < static_cast<int>(colCount) ||
228  texture.height() < static_cast<int>(rowCount)) {
230  QString("[CSVMatrixFilter] To map a texture on "
231  "the cloud, the image size should "
232  "equal or greater than the grid size "
233  "(%1x%2)")
234  .arg(colCount)
235  .arg(rowCount));
236  } else if (cloud->reserveTheRGBTable()) {
237  for (unsigned j = 0; j < rowCount; ++j) {
238  for (unsigned i = 0; i < colCount; ++i) {
239  QRgb col = texture.pixel(static_cast<int>(i),
240  static_cast<int>(j));
241  cloud->addRGBColor(ecvColor::FromQRgb(col));
242  }
243  }
244  cloud->showColors(true);
245  } else {
247  "[CSVMatrixFilter] Not enough memory to map "
248  "the texture on the cloud!");
249  }
250  }
251  }
252  }
253 
254  if (s_inverseRows) {
255  CCVector3 T(0,
256  static_cast<PointCoordinateType>((rowCount - 1) *
257  s_ySpacing),
258  0);
259  ccGLMatrix trans;
260  trans.setTranslation(T);
261  cloud->applyGLTransformation_recursive(&trans);
262  // this transformation is of no interest for the user
263  // cloud->resetGLTransformationHistory_recursive();
264  }
265  CVLog::Print(
266  QString("[CSVMatrixFilter] Number of rows: %1").arg(rowCount));
267  cloud->setVisible(true);
268  container.addChild(mesh ? static_cast<ccHObject*>(mesh)
269  : static_cast<ccHObject*>(cloud));
270  } else {
271  delete cloud;
272  cloud = nullptr;
273  }
274 
275  return result;
276 }
static bool s_inverseRows
static double s_ySpacing
static QChar s_separator(',')
static bool s_useTexture
static bool s_loadAsMesh
static double s_xSpacing
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
std::string filename
int width
CC_FILE_ERROR
Typical I/O filter errors.
Definition: FileIOFilter.h:20
@ CC_FERR_CANCELED_BY_USER
Definition: FileIOFilter.h:30
@ CC_FERR_MALFORMED_FILE
Definition: FileIOFilter.h:32
@ CC_FERR_BAD_ARGUMENT
Definition: FileIOFilter.h:22
@ CC_FERR_NO_ERROR
Definition: FileIOFilter.h:21
@ CC_FERR_READING
Definition: FileIOFilter.h:26
@ CC_FERR_NOT_ENOUGH_MEMORY
Definition: FileIOFilter.h:31
core::Tensor result
Definition: VtkUtils.cpp:76
virtual void release()
Decrease counter and deletes object when 0.
Definition: CVShareable.cpp:35
virtual CC_FILE_ERROR loadFile(const QString &filename, ccHObject &container, LoadParameters &parameters)
Loads one or more entities from a file.
CSV Matrix Open dialog.
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
Array of 2D texture coordinates.
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
bool reserveSafe(size_t count)
Reserves memory (no exception thrown)
Definition: ecvArray.h:56
void addElement(const Type &value)
Definition: ecvArray.h:105
virtual void setVisible(bool state)
Sets entity visibility.
virtual void showColors(bool state)
Sets colors visibility.
void setTranslation(const Vector3Tpl< float > &Tr)
Sets translation (float version)
Float version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:19
void showNormals(bool state) override
Sets normals visibility.
virtual void showMaterials(bool state)
Sets whether textures should be displayed or not.
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
void applyGLTransformation_recursive(const ccGLMatrix *trans=nullptr)
Applies the active OpenGL transformation to the entity (recursive)
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
Triangular mesh.
Definition: ecvMesh.h:35
cloudViewer::VerticesIndexes * getTriangleVertIndexes(unsigned triangleIndex) override
Returns the indexes of the vertices of a given triangle.
bool reservePerTriangleMtlIndexes()
Reserves memory to store per-triangle material index.
void addTriangleMtlIndex(int mtlIndex)
Adds triangle material index for next triangle.
bool reserve(std::size_t n)
Reserves the memory to store the vertex indexes (3 per triangle)
void addTriangleTexCoordIndexes(int i1, int i2, int i3)
Adds a triplet of tex coords indexes for next triangle.
void addTriangle(unsigned i1, unsigned i2, unsigned i3)
Adds a triangle to the mesh.
void setTexCoordinatesTable(TextureCoordsContainer *texCoordsTable, bool autoReleaseOldTable=true)
Sets per-triangle texture coordinates array (may be shared)
bool reservePerTriangleTexCoordIndexes()
Reserves memory to store per-triangle triplets of tex coords indexes.
bool computePerVertexNormals()
Computes per-vertex normals.
virtual unsigned size() const override
Returns the number of triangles.
virtual void setName(const QString &name)
Sets object name.
Definition: ecvObject.h:75
virtual void setEnabled(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:102
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
bool reserveTheRGBTable()
Reserves memory to store the RGB colors.
void addRGBColor(const ecvColor::Rgb &C)
Pushes an RGB color on stack.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
unsigned size() const override
Definition: PointCloudTpl.h:38
constexpr Qt::SplitBehavior SkipEmptyParts
Definition: QtCompat.h:302
Rgb FromQRgb(QRgb qColor)
Conversion from QRgb.
Generic loading parameters.
Definition: FileIOFilter.h:51
2D texture coordinates
Triangle described by the indexes of its 3 vertices.