ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
MascaretFilter.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 "MascaretFilter.h"
9 
10 // local
11 #include "ui_saveMascaretFileDlg.h"
12 
13 // qCC_db
14 #include <CVLog.h>
15 #include <ecvPolyline.h>
16 
17 // Qt
18 #include <QDialog>
19 #include <QFile>
20 
21 // Qt5/Qt6 Compatibility
22 #include <QtCompat.h>
23 // System
24 #include <cstring>
25 
27 class SaveMascaretFileDlg : public QDialog, public Ui::SaveMascaretFileDlg {
28  Q_OBJECT
29 
30 public:
32  SaveMascaretFileDlg(QWidget* parent = nullptr)
33  : QDialog(parent, Qt::Tool), Ui::SaveMascaretFileDlg() {
34  setupUi(this);
35  }
36 };
37 
39  : FileIOFilter({"_Mascaret Filter",
40  DEFAULT_PRIORITY, // priority
41  QStringList(), "georef", QStringList(),
42  QStringList{"(Geo-)Mascaret profile (*.georef)"}, Export}) {
43 }
44 
46  bool& multiple,
47  bool& exclusive) const {
48  if (type == CV_TYPES::POLY_LINE) {
49  multiple = true;
50  exclusive = true;
51  return true;
52  }
53  return false;
54 }
55 
56 QString MakeMascaretName(QString name) {
57  // max 30 characeters
58  name = name.left(30);
59  // no space characters
60  name.replace(' ', '_');
61 
62  return name;
63 }
64 
65 inline void ToLocalAbscissa(const CCVector3& P,
66  const CCVector3& C,
67  const CCVector3& U,
68  unsigned char upDir,
69  CCVector2& localP) {
70  // convert to 'local' coordinate system
71  localP.x = (P - C).dot(U);
72  localP.y = P.u[upDir];
73 }
74 
76  const QString& filename,
77  const SaveParameters& parameters) {
78  Q_UNUSED(parameters);
79 
80  if (!entity || filename.isEmpty()) return CC_FERR_BAD_ARGUMENT;
81 
82  // look for valid profiles
83  std::vector<ccPolyline*> profiles;
84  try {
85  // get all polylines
86  std::vector<ccPolyline*> candidates;
87  if (entity->isA(CV_TYPES::POLY_LINE)) {
88  candidates.push_back(static_cast<ccPolyline*>(entity));
89  } else if (entity->isA(CV_TYPES::HIERARCHY_OBJECT)) {
90  for (unsigned i = 0; i < entity->getChildrenNumber(); ++i)
91  if (entity->getChild(i) &&
92  entity->getChild(i)->isA(CV_TYPES::POLY_LINE))
93  candidates.push_back(
94  static_cast<ccPolyline*>(entity->getChild(i)));
95  }
96 
97  // then keep the valid profiles only
98  for (size_t i = 0; i < candidates.size(); ++i) {
99  ccPolyline* poly = candidates[i];
100  if (!poly->hasMetaData(ccPolyline::MetaKeyUpDir()) ||
102  !poly->hasMetaData(ccPolyline::MetaKeyPrefixCenter() + ".x") ||
103  !poly->hasMetaData(ccPolyline::MetaKeyPrefixCenter() + ".y") ||
104  !poly->hasMetaData(ccPolyline::MetaKeyPrefixCenter() + ".z") ||
106  ".x") ||
108  ".y") ||
110  ".z")) {
111  CVLog::Warning(QString("[Mascaret] Polyline '%1' is not a "
112  "valid profile (missing meta-data)")
113  .arg(poly->getName()));
114  break;
115  } else {
116  profiles.push_back(poly);
117  }
118  }
119  } catch (const std::bad_alloc&) {
121  }
122 
123  if (profiles.empty()) return CC_FERR_NO_SAVE;
124 
125  // open ASCII file for writing
126  QFile file(filename);
127  if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
128  return CC_FERR_WRITING;
129 
130  QTextStream outFile(&file);
131  outFile.setRealNumberNotation(QTextStream::FixedNotation);
132  outFile.setRealNumberPrecision(12);
133 
134  // ask some parameters
135  SaveMascaretFileDlg smfDlg;
136  if (!smfDlg.exec()) return CC_FERR_CANCELED_BY_USER;
137 
138  QString biefName = smfDlg.biefNameLineEdit->text();
139  QString type("T"); // B or T --> ask the user
140  switch (smfDlg.typeComboBox->currentIndex()) {
141  case 0:
142  type = "B"; // bathy
143  break;
144  case 1:
145  type = "T"; // topo
146  break;
147  default:
148  assert(false);
149  }
150 
151  // sanitize the 'bief' (reach) name
152  biefName = MakeMascaretName(biefName);
153 
154  // sort the sections by their abscissa
155  if (profiles.size() > 1) {
156  for (size_t i = 0; i < profiles.size() - 1; ++i) {
157  size_t smallestIndex = i;
158  double smallestAbscissa =
159  profiles[i]
160  ->getMetaData(ccPolyline::MetaKeyAbscissa())
161  .toDouble();
162  for (size_t j = i + 1; j < profiles.size(); ++j) {
163  double a = profiles[j]
164  ->getMetaData(ccPolyline::MetaKeyAbscissa())
165  .toDouble();
166  if (a < smallestAbscissa) {
167  smallestAbscissa = a;
168  smallestIndex = j;
169  }
170  }
171 
172  if (i != smallestIndex) {
173  std::swap(profiles[i], profiles[smallestIndex]);
174  }
175  }
176  }
177 
179 
180  // for each profile
181  for (size_t i = 0; i < profiles.size(); ++i) {
182  ccPolyline* poly = profiles[i];
183  unsigned vertCount = poly ? poly->size() : 0;
184  if (vertCount < 2) {
185  // invalid size
186  CVLog::Warning(QString("[Mascaret] Polyline '%1' does not have "
187  "enough vertices")
188  .arg(poly ? poly->getName()
189  : QStringLiteral("unnamed")));
190  continue;
191  }
192 
193  // decode meta-data
194  bool ok = true;
195  int upDir = 2;
196  double absc = 0.0;
197  CCVector3d Cd(0, 0, 0);
198  CCVector3d Ud(0, 0, 0);
199  while (true) // fake loop for easy break
200  {
201  upDir = poly->getMetaData(ccPolyline::MetaKeyUpDir()).toInt(&ok);
202  if (!ok) break;
204  .toDouble(&ok);
205  if (!ok) break;
206  Cd.x = poly->getMetaData(ccPolyline::MetaKeyPrefixCenter() + ".x")
207  .toDouble(&ok);
208  if (!ok) break;
209  Cd.y = poly->getMetaData(ccPolyline::MetaKeyPrefixCenter() + ".y")
210  .toDouble(&ok);
211  if (!ok) break;
212  Cd.z = poly->getMetaData(ccPolyline::MetaKeyPrefixCenter() + ".z")
213  .toDouble(&ok);
214  if (!ok) break;
216  ".x")
217  .toDouble(&ok);
218  if (!ok) break;
220  ".y")
221  .toDouble(&ok);
222  if (!ok) break;
224  ".z")
225  .toDouble(&ok);
226  break;
227  }
228  if (!ok) {
229  CVLog::Warning(QString("[Mascaret] At least one of the meta-data "
230  "entry of polyline '%1' is invalid?!")
231  .arg(poly->getName()));
232  continue;
233  }
234 
235  QString profileName = poly->getName();
236  profileName = MakeMascaretName(profileName);
237 
240  U.normalize();
241 
242  // write header
243  outFile << "PROFIL " << biefName << " " << profileName << " " << absc;
244 #define SAVE_AS_GEO_MASCARET
245 #ifdef SAVE_AS_GEO_MASCARET
246  int xDir = upDir == 2 ? 0 : upDir + 1;
247  int yDir = xDir == 2 ? 0 : xDir + 1;
248  // for "geo"-mascaret, we add some more information:
249  // - first point
250  {
251  const CCVector3* firstP = poly->getPoint(0);
252  CCVector3d firstPg = poly->toGlobal3d(*firstP);
253  outFile << " ";
254  outFile << firstPg.u[xDir] << " " << firstPg.u[yDir];
255  }
256  // - last point
257  {
258  const CCVector3* lastP = poly->getPoint(vertCount - 1);
259  CCVector3d lastPg = poly->toGlobal3d(*lastP);
260  outFile << " ";
261  outFile << lastPg.u[xDir] << " " << lastPg.u[yDir];
262  }
263  // - profile/path intersection point
264  {
265  outFile << " AXE ";
266  CCVector3d Cdg = poly->toGlobal3d(Cd);
267  outFile << Cdg.u[xDir] << " " << Cdg.u[yDir];
268  }
269 #endif
270  outFile << QtCompat::endl;
271 
272  // check the abscissa values order (must be increasing!)
273  bool inverted = false;
274  {
275  const CCVector3* P0 = poly->getPoint(0);
276  // convert to 'local' coordinate system
277  CCVector2 Q0;
278  ToLocalAbscissa(*P0, C, U, upDir, Q0);
279 
280  const CCVector3* P1 = poly->getPoint(vertCount - 1);
281  // convert to 'local' coordinate system
282  CCVector2 Q1;
283  ToLocalAbscissa(*P1, C, U, upDir, Q1);
284 
285  inverted = (Q1.x < Q0.x);
286  }
287 
288  for (unsigned j = 0; j < vertCount; ++j) {
289  const CCVector3* P =
290  poly->getPoint(inverted ? vertCount - 1 - j : j);
291 
292  // convert to 'local' coordinate system
293  CCVector2 Q;
294  ToLocalAbscissa(*P, C, U, upDir, Q);
295 
296  outFile << Q.x << " " << Q.y << " " << type;
297 #ifdef SAVE_AS_GEO_MASCARET
298  {
299  // for "geo"-mascaret, we add some more information:
300  // - real coordinates of the point
301  outFile << " ";
302  CCVector3d Pg = poly->toGlobal3d(*P);
303  outFile << Pg.u[xDir] << " " << Pg.u[yDir];
304  }
305 #endif
306  outFile << QtCompat::endl;
307  }
308 
310  }
311 
312  file.close();
313 
314  return result;
315 }
316 
317 #include "MascaretFilter.moc"
int64_t CV_CLASS_ENUM
Type of object type flags (64 bits)
Definition: CVTypes.h:97
std::string filename
std::string name
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_NO_SAVE
Definition: FileIOFilter.h:27
@ CC_FERR_NO_ERROR
Definition: FileIOFilter.h:21
@ CC_FERR_NOT_ENOUGH_MEMORY
Definition: FileIOFilter.h:31
QString MakeMascaretName(QString name)
void ToLocalAbscissa(const CCVector3 &P, const CCVector3 &C, const CCVector3 &U, unsigned char upDir, CCVector2 &localP)
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
virtual CC_FILE_ERROR saveToFile(ccHObject *entity, const QString &filename, const SaveParameters &parameters) override
Saves an entity (or a group of) to a file.
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.
Mascaret File Save dialog.
SaveMascaretFileDlg(QWidget *parent=nullptr)
Default constructor.
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
Type x
Definition: CVGeom.h:36
Type y
Definition: CVGeom.h:36
void normalize()
Sets vector norm to unity.
Definition: CVGeom.h:428
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
unsigned getChildrenNumber() const
Returns the number of children.
Definition: ecvHObject.h:312
ccHObject * getChild(unsigned childPos) const
Returns the ith child.
Definition: ecvHObject.h:325
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
bool isA(CV_CLASS_ENUM type) const
Definition: ecvObject.h:131
QVariant getMetaData(const QString &key) const
Returns a given associated meta data.
bool hasMetaData(const QString &key) const
Returns whether a meta-data element with the given key exists or not.
Colored polyline.
Definition: ecvPolyline.h:24
static QString MetaKeyPrefixDirection()
Definition: ecvPolyline.h:238
static QString MetaKeyUpDir()
Definition: ecvPolyline.h:220
static QString MetaKeyAbscissa()
Meta data key: profile abscissa along generatrix.
Definition: ecvPolyline.h:226
static QString MetaKeyPrefixCenter()
Definition: ecvPolyline.h:232
CCVector3d toGlobal3d(const Vector3Tpl< T > &Plocal) const
Returns the point back-projected into the original coordinates system.
unsigned size() const override
Returns the number of points.
const CCVector3 * getPoint(unsigned index) const override
Returns the ith point.
__host__ __device__ float dot(float2 a, float2 b)
Definition: cutil_math.h:1119
@ HIERARCHY_OBJECT
Definition: CVTypes.h:103
@ POLY_LINE
Definition: CVTypes.h:112
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.
Definition: SmallVector.h:1370
Generic saving parameters.
Definition: FileIOFilter.h:84