ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
stereogramDlg.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 "stereogramDlg.h"
9 
10 // Local
11 #include "facetsClassifier.h"
12 
13 // Qt
14 #include <QMainWindow>
15 #include <QMouseEvent>
16 #include <QPainter>
17 #include <QSpacerItem>
18 
19 // ECV_PLUGINS
20 #include <ecvMainAppInterface.h>
21 
22 // CV_DB_LIB
23 #include <ecvColorScaleEditorDlg.h>
24 #include <ecvColorScaleSelector.h>
25 #include <ecvColorScalesManager.h>
26 #include <ecvDisplayTools.h>
27 #include <ecvMesh.h>
28 #include <ecvProgressDialog.h>
29 
32 public:
35  : grid(0), rSteps(0), ddSteps(0), step_deg(0), step_R(0) {
36  minMaxDensity[0] = minMaxDensity[1] = 0;
37  }
38 
41  if (grid) delete[] grid;
42  }
43 
45  double* grid;
46 
48  unsigned rSteps;
50  unsigned ddSteps;
51 
53  double step_R;
55  double step_deg;
56 
58  double minMaxDensity[2];
59 };
60 
62  : QLabel("", parent),
63  m_angularStep_deg(0),
64  m_densityGrid(0),
65  m_meanDipDir_deg(-1.0),
66  m_meanDip_deg(-1.0),
67  m_densityColorScale(0),
68  m_densityColorScaleSteps(
69  ccColorScale::MAX_STEPS < 256
70  ? ccColorScale::MAX_STEPS
71  : 256) // DGM: we can't pass a constant initializer
72  // (MAX_STEPS) by reference
73  ,
74  m_ticksFreq(3),
75  m_showHSVRing(false),
76  m_trackMouseClick(false),
77  m_clickDip_deg(0.0),
78  m_clickDipDir_deg(0.0),
79  m_clickDipSpan_deg(30.0),
80  m_clickDipDirSpan_deg(30.0),
81  m_center(0, 0),
82  m_radius(0) {
83  setVisible(true);
84  QSizePolicy policy = sizePolicy();
85  policy.setHeightForWidth(true);
86  setSizePolicy(policy);
87  // setFixedSize(256,256);
88 }
89 
91  if (m_densityGrid) delete m_densityGrid;
92 }
93 
94 bool StereogramWidget::init(double angularStep_deg,
95  ccHObject* entity,
96  double resolution_deg /*=2.0*/) {
97  m_angularStep_deg = angularStep_deg;
98 
99  if (m_densityGrid) delete m_densityGrid;
100  m_densityGrid = 0;
101 
102  if (!entity) return false;
103 
104  ecvProgressDialog pDlg(true);
105  pDlg.setMethodTitle(QObject::tr("Stereogram"));
106  pDlg.setInfo(QObject::tr("Preparing polar display..."));
107  pDlg.start();
108  QApplication::processEvents();
109 
110  size_t count = 0;
111  ccHObject::Container facets;
112  ccHObject::Container planes;
113  ccPointCloud* cloud = 0;
114 
115  // a set of facets or planes?
116  if (entity->isA(CV_TYPES::HIERARCHY_OBJECT)) {
117  // check for facets first
118  entity->filterChildren(facets, true, CV_TYPES::FACET);
119  count = facets.size();
120  if (count == 0) // if no facets, look for planes instead
121  {
122  entity->filterChildren(planes, true, CV_TYPES::PLANE);
123  count = planes.size();
124  }
125  }
126  // or a cloud?
127  else if (entity->isA(CV_TYPES::POINT_CLOUD)) {
128  cloud = static_cast<ccPointCloud*>(entity);
129  if (cloud->hasNormals()) count = cloud->size();
130  }
131 
132  if (!count) return false;
133 
134  // pDlg.setMaximum(static_cast<int>(count));
136  static_cast<unsigned>(count));
137 
138  // create the density grid
139  FacetDensityGrid* densityGrid = new FacetDensityGrid();
140  densityGrid->step_deg = resolution_deg;
141  densityGrid->step_R = 0.02;
142 
143  // dip steps (dip in [0,90])
144  // densityGrid->dSteps = static_cast<unsigned>(ceil(90.0 /
145  // densityGrid->step_deg)); R steps (R in [0,1])
146  densityGrid->rSteps =
147  static_cast<unsigned>(ceil(1.0 / densityGrid->step_R));
148  // dip direction steps (dip dir. in [0,360])
149  densityGrid->ddSteps =
150  static_cast<unsigned>(ceil(360.0 / densityGrid->step_deg));
151 
152  unsigned cellCount = densityGrid->rSteps * densityGrid->ddSteps;
153  densityGrid->grid = new double[cellCount];
154  if (densityGrid->grid) {
155  memset(densityGrid->grid, 0, sizeof(double) * cellCount);
156 
157  CCVector3d Nmean(0, 0, 0);
158  double surfaceSum = 0.0;
159 
160  for (size_t i = 0; i < count; ++i) {
161  CCVector3 N;
162  double weight = 1.0;
163  if (cloud) // we're using cloud normals
164  {
165  N = cloud->getPointNormal(static_cast<unsigned>(i));
166  } else if (facets.size() != 0) // we're using facets
167  {
168  ccFacet* facet = static_cast<ccFacet*>(facets[i]);
169  N = facet->getNormal();
170  weight = facet->getSurface();
171  } else if (planes.size() != 0) // we're using planes
172  {
173  ccPlane* p = static_cast<ccPlane*>(planes[i]);
174  N = p->getNormal();
175  // n.b. planes all carry equal weight
176  }
177 
178  Nmean.x += static_cast<double>(N.x) * weight;
179  Nmean.y += static_cast<double>(N.y) * weight;
180  Nmean.z += static_cast<double>(N.z) * weight;
181  surfaceSum += weight;
182 
183  PointCoordinateType dipDir = 0, dip = 0;
185 
186  // unsigned iDip =
187  // static_cast<unsigned>(floor(static_cast<double>(dip)/densityGrid->step_deg));
188  // if (iDip == densityGrid->dSteps)
189  // iDip--;
190  unsigned iDipDir = static_cast<unsigned>(
191  floor(static_cast<double>(dipDir) / densityGrid->step_deg));
192  if (iDipDir == densityGrid->ddSteps) iDipDir--;
193 
194  double dip_rad = cloudViewer::DegreesToRadians(dip);
195  double R = sin(dip_rad) / (1.0 + cos(dip_rad));
196 
197  unsigned iR = static_cast<unsigned>(
198  floor(static_cast<double>(R) / densityGrid->step_R));
199  if (iR == densityGrid->rSteps) iR--;
200 
201  densityGrid->grid[iR + iDipDir * densityGrid->rSteps] += weight;
202 
203  // pDlg.setValue(static_cast<int>(i));
204  if (!nProgress.oneStep()) {
205  // process cancelled by user
206  delete densityGrid;
207  return false;
208  }
209  }
210 
211  if (surfaceSum > 0) {
212  Nmean.normalize();
213  CCVector3 N(static_cast<PointCoordinateType>(Nmean.x),
214  static_cast<PointCoordinateType>(Nmean.y),
215  static_cast<PointCoordinateType>(Nmean.z));
216 
217  PointCoordinateType dipDir = 0, dip = 0;
219 
220  m_meanDipDir_deg = static_cast<double>(dipDir);
221  m_meanDip_deg = static_cast<double>(dip);
222 
223  // set same value for the "filter" center
226  }
227 
228  // compute min and max density
229  {
230  // DGM: only supported on C++x11!
231  // std::pair<double*,double*> minmax =
232  // std::minmax_element(densityGrid->grid,densityGrid->grid+cellCount);
233  // densityGrid->minMaxDensity[0] = *minmax.first;
234  // densityGrid->minMaxDensity[1] = *minmax.second;
235 
236  densityGrid->minMaxDensity[0] = densityGrid->grid[0];
237  densityGrid->minMaxDensity[1] = densityGrid->grid[0];
238  for (unsigned j = 1; j < cellCount; ++j) {
239  if (densityGrid->grid[j] < densityGrid->minMaxDensity[0])
240  densityGrid->minMaxDensity[0] = densityGrid->grid[j];
241  else if (densityGrid->grid[j] > densityGrid->minMaxDensity[1])
242  densityGrid->minMaxDensity[1] = densityGrid->grid[j];
243  }
244  }
245 
246  // pDlg.hide();
247  pDlg.stop();
248  QApplication::processEvents();
249  } else {
250  // not enough memory!
251  delete densityGrid;
252  densityGrid = 0;
253  }
254 
255  // replace old grid by new one! (even in case of failure! See below)
256  m_densityGrid = densityGrid;
257 
258  update();
259 
260  return true;
261 }
262 
263 void StereogramWidget::mousePressEvent(QMouseEvent* e) {
264  if (m_trackMouseClick && m_radius > 0) {
265  if (e->button() == Qt::LeftButton) {
266  QRect contentRect = contentsRect();
267  QPoint pos = e->pos() - contentRect.topLeft();
268  QPoint AB = pos - m_center;
269  int squareDistToCenter = AB.x() * AB.x() + AB.y() * AB.y();
270  if (squareDistToCenter <= m_radius * m_radius) {
271  // compute equivalent positions
273  90.0,
274  90.0 * sqrt(static_cast<double>(squareDistToCenter)) /
275  static_cast<double>(m_radius));
277  atan2(static_cast<double>(AB.y()),
278  static_cast<double>(AB.x())));
279  if (m_clickDipDir_deg < 0) m_clickDipDir_deg += 360.0;
281  90.0; // stereogram starts at 12 o'clock (not 3)
282  if (m_clickDipDir_deg >= 360.0) m_clickDipDir_deg -= 360.0;
283 
285  e->accept();
286  return;
287  }
288  }
289  }
290 
291  e->ignore();
292 }
293 
295  double dipSpan_deg /*=30*/,
296  double dipDirSpan_deg /*=30*/) {
297  m_trackMouseClick = state;
298  if (state) {
299  m_clickDipSpan_deg = dipSpan_deg;
300  m_clickDipDirSpan_deg = dipDirSpan_deg;
301  // to give to the parent dialog some feedback about the current marker
302  // position!
304  }
305 }
306 
307 void StereogramWidget::setTrackedCenter(double dip_deg, double dipDir_deg) {
308  m_clickDip_deg = dip_deg;
309  m_clickDipDir_deg = dipDir_deg;
310 }
311 
313  m_radius = 0; // means that nothing has been drawn (yet ;)
314 
315  QLabel::paintEvent(event);
316  QPainter painter(this);
317  painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing,
318  true);
319 
320  // pen
321  QPen pen;
322  pen.setStyle(Qt::SolidLine);
323  pen.setBrush(Qt::black);
324 
325  int diameter = std::min(width(), height());
326  int halfW = width() / 2;
327  int halfH = height() / 2;
328  QPoint center(halfW, halfH);
329 
330  int hsvThickness = 0;
331  if (m_showHSVRing) {
332  int newDiameter = static_cast<int>(ceil(0.9 * diameter));
333  hsvThickness = diameter - newDiameter;
334 
335  // TODO
336  if (hsvThickness > 0) {
337  QRect rectangle(center.x() - diameter / 2 + 1,
338  center.y() - diameter / 2 + 1, diameter - 2,
339  diameter - 2);
340  int angle_span = static_cast<int>(m_angularStep_deg *
341  16.0); // see QPainter::drawPie
342  QBrush brush;
343  brush.setStyle(Qt::SolidPattern);
344  painter.setPen(Qt::NoPen);
345 
346  // dip direction steps (dip dir. in [0,360])
347  unsigned ddSteps = static_cast<unsigned>(
348  ceil(360.0 / std::max(m_angularStep_deg, 1.0)));
349  for (unsigned j = 0; j < ddSteps; ++j) {
350  double dipDir_deg = static_cast<double>(j) * m_angularStep_deg;
351 
352  // set family color
353  ecvColor::Rgb col;
355  col, 90.0, dipDir_deg + 0.5 * m_angularStep_deg, 0, 1);
356  brush.setColor(QColor(static_cast<int>(col.r),
357  static_cast<int>(col.g),
358  static_cast<int>(col.b), 255));
359  painter.setBrush(brush);
360 
361  int angle_start = static_cast<int>(
362  (360.0 - dipDir_deg - m_angularStep_deg + 90.0) *
363  16.0); // see QPainter::drawPie
364  painter.drawPie(rectangle, angle_start, angle_span);
365  }
366  }
367 
368  diameter = newDiameter;
369  }
370 
371  // outer circle
372  pen.setWidth(2);
373  painter.setPen(pen);
374  painter.setBrush(Qt::white);
375  int radius = diameter / 2 - 2;
376  painter.drawEllipse(center, radius, radius);
377  painter.setBrush(Qt::NoBrush);
378 
379  // keep track of the circle position
380  m_radius = radius;
381  m_center = center;
382 
383  // main axes
384  painter.drawLine(center - QPoint(radius, 0), center + QPoint(radius, 0));
385  painter.drawLine(center - QPoint(0, radius), center + QPoint(0, radius));
386 
387  // draw circles
388  if (m_angularStep_deg > 0) {
389  // dip steps (dip in [0,90])
390  unsigned dSteps = static_cast<unsigned>(ceil(90.0 / m_angularStep_deg));
391  // dip direction steps (dip dir. in [0,360])
392  unsigned ddSteps =
393  static_cast<unsigned>(ceil(360.0 / m_angularStep_deg));
394 
395  // draw inner circles
396  pen.setWidth(1);
397  pen.setColor(Qt::gray);
398  painter.setPen(pen);
399  for (unsigned i = 1; i < dSteps; ++i) {
400  double dip_deg = i * m_angularStep_deg;
401  if (dip_deg < 90.0) {
402  int R = static_cast<int>(radius * (dip_deg / 90.0));
403  if (R > 1) painter.drawEllipse(center, R - 1, R - 1);
404  }
405  }
406 
407  // draw rays (+ 'm_ticksFreq' times more ticks)
408  int ticksFreq = std::max(m_ticksFreq, 1);
409  for (unsigned j = 1; j <= ddSteps * ticksFreq; ++j) {
410  double dipDir_deg = j * m_angularStep_deg / ticksFreq;
411  if (dipDir_deg < 360.0) {
412  QPoint X(
413  static_cast<int>(
414  sin(cloudViewer::DegreesToRadians(dipDir_deg)) *
415  radius),
416  -static_cast<int>(
417  cos(cloudViewer::DegreesToRadians(dipDir_deg)) *
418  radius));
419 
420  if ((j % ticksFreq) == 0) // long ticks
421  painter.drawLine(center, center + X);
422  else
423  painter.drawLine(center + X * 0.93, center + X);
424  }
425  }
426  }
427 
428  // draw density map
430  m_densityGrid->minMaxDensity[1] != 0) {
431  assert(m_densityColorScale);
432  assert(m_densityGrid->grid);
433 
434  QBrush brush;
435  brush.setStyle(Qt::SolidPattern);
436  painter.setPen(Qt::NoPen);
437  QPolygon poly(4);
438 
439  const double* d = m_densityGrid->grid;
440  for (unsigned j = 0; j < m_densityGrid->ddSteps; ++j) {
441  double dipDir0_rad =
443  double dipDir1_rad = (j + 1) * cloudViewer::DegreesToRadians(
445  double cos_dipDir0 = cos(dipDir0_rad);
446  double sin_dipDir0 = sin(dipDir0_rad);
447  double cos_dipDir1 = cos(dipDir1_rad);
448  double sin_dipDir1 = sin(dipDir1_rad);
449 
450  for (unsigned i = 0; i < m_densityGrid->rSteps; ++i, ++d) {
451  if (*d != 0) {
452  double relPos = (*d) / m_densityGrid->minMaxDensity[1];
453  const ecvColor::Rgb* col =
454  m_densityColorScale->getColorByRelativePos(
455  relPos, m_densityColorScaleSteps);
456  brush.setColor(QColor(static_cast<int>(col->r),
457  static_cast<int>(col->g),
458  static_cast<int>(col->b), 255));
459  painter.setBrush(brush);
460 
461  // stereographic projection
462  double R0 = radius * ((i)*m_densityGrid->step_R);
463  double R1 = radius * ((i + 1) * m_densityGrid->step_R);
464 
465  poly.setPoint(
466  0,
467  center + QPoint(static_cast<int>(sin_dipDir0 * R0),
468  -static_cast<int>(cos_dipDir0 *
469  R0)));
470  poly.setPoint(
471  1,
472  center + QPoint(static_cast<int>(sin_dipDir0 * R1),
473  -static_cast<int>(cos_dipDir0 *
474  R1)));
475  poly.setPoint(
476  2,
477  center + QPoint(static_cast<int>(sin_dipDir1 * R1),
478  -static_cast<int>(cos_dipDir1 *
479  R1)));
480  poly.setPoint(
481  3,
482  center + QPoint(static_cast<int>(sin_dipDir1 * R0),
483  -static_cast<int>(cos_dipDir1 *
484  R0)));
485 
486  painter.drawPolygon(poly);
487  }
488  }
489  }
490  }
491 
492  // draw main 'dip direction'
493  if (m_meanDipDir_deg >= 0) {
494  pen.setWidth(2);
495  pen.setColor(Qt::red);
496  painter.setPen(pen);
497  // draw main direction
498  QPoint X(static_cast<int>(
500  radius),
501  -static_cast<int>(
503  radius));
504  pen.setStyle(Qt::DashLine);
505  painter.setPen(pen);
506  painter.drawLine(center, center + X);
507 
508  // draw orthogonal to main direction
509  QPoint Y(static_cast<int>(
511  radius),
512  static_cast<int>(
514  radius));
515  pen.setStyle(Qt::SolidLine);
516  painter.setPen(pen);
517  painter.drawLine(center - Y, center + Y);
518  }
519 
520  // draw filter window around last cliked point
521  if (m_trackMouseClick) {
522  pen.setWidth(2);
523  pen.setColor(Qt::magenta);
524  painter.setPen(pen);
525  // QBrush brush;
526  // brush.setStyle(Qt::Dense6Pattern);
527  // brush.setColor(Qt::red);
528  // painter.setBrush(brush);
529  painter.setBrush(Qt::NoBrush);
530 
531  double R0 =
532  radius *
533  (std::max(0.0, m_clickDip_deg - m_clickDipSpan_deg / 2) / 90.0);
534  double R1 = radius *
536  90.0);
537 
538  // draw radial limits
539  {
540  QPoint X0(static_cast<int>(sin(cloudViewer::DegreesToRadians(
542  m_clickDipDirSpan_deg / 2)) *
543  R0),
544  -static_cast<int>(cos(cloudViewer::DegreesToRadians(
546  m_clickDipDirSpan_deg / 2)) *
547  R0));
548  QPoint X1(static_cast<int>(sin(cloudViewer::DegreesToRadians(
550  m_clickDipDirSpan_deg / 2)) *
551  R1),
552  -static_cast<int>(cos(cloudViewer::DegreesToRadians(
554  m_clickDipDirSpan_deg / 2)) *
555  R1));
556  painter.drawLine(center + X0, center + X1);
557  }
558  {
559  QPoint X0(static_cast<int>(sin(cloudViewer::DegreesToRadians(
561  m_clickDipDirSpan_deg / 2)) *
562  R0),
563  -static_cast<int>(cos(cloudViewer::DegreesToRadians(
565  m_clickDipDirSpan_deg / 2)) *
566  R0));
567  QPoint X1(static_cast<int>(sin(cloudViewer::DegreesToRadians(
569  m_clickDipDirSpan_deg / 2)) *
570  R1),
571  -static_cast<int>(cos(cloudViewer::DegreesToRadians(
573  m_clickDipDirSpan_deg / 2)) *
574  R1));
575  painter.drawLine(center + X0, center + X1);
576  }
577 
578  // draw concentric limits
579  {
580  int angle_start =
581  static_cast<int>((360.0 - m_clickDipDir_deg -
582  m_clickDipDirSpan_deg / 2 + 90.0) *
583  16.0); // see QPainter::drawPie
584  int angle_span = static_cast<int>(m_clickDipDirSpan_deg *
585  16.0); // see QPainter::drawPie
586 
587  QRectF rect0(center.x() - R0, center.y() - R0, 2 * R0, 2 * R0);
588  painter.drawArc(rect0, angle_start, angle_span);
589 
590  QRectF rect1(center.x() - R1, center.y() - R1, 2 * R1, 2 * R1);
591  painter.drawArc(rect1, angle_start, angle_span);
592  }
593  }
594 }
595 
597  : QDialog(app ? app->getMainWindow() : 0),
598  Ui::StereogramDialog(),
599  m_classifWidget(0),
600  m_colorScaleSelector(0),
601  m_app(app),
602  m_facetGroupUniqueID(0) {
603  setupUi(this);
604 
605  // create classification widget (stereogram)
606  m_classifWidget = new StereogramWidget(this);
607  {
608  if (!frame->layout()) frame->setLayout(new QHBoxLayout());
609  frame->layout()->addWidget(m_classifWidget);
610  m_classifWidget->setTicksFreq(ticksFreqSpinBox->value());
611  }
612 
613  // add color ramp selector widget
614  if (m_app) {
617  csManager, this,
618  QString::fromUtf8(":/CC/plugin/qFacets/images/gearIcon.png"));
620  ccColorScale::Shared scale =
622  if (scale) {
623  m_colorScaleSelector->setSelectedScale(scale->getUuid());
625  }
626  connect(m_colorScaleSelector, SIGNAL(colorScaleSelected(int)), this,
627  SLOT(colorScaleChanged(int)));
628  connect(m_colorScaleSelector, SIGNAL(colorScaleEditorSummoned()), this,
629  SLOT(spawnColorScaleEditor()));
630  // add selector to group's layout
631  if (!colorRampGroupBox->layout())
632  colorRampGroupBox->setLayout(new QHBoxLayout());
633  colorRampGroupBox->layout()->addItem(new QSpacerItem(
634  20, 20, QSizePolicy::Preferred, QSizePolicy::Minimum));
635  colorRampGroupBox->layout()->addWidget(m_colorScaleSelector);
636  colorScaleStepsSpinBox->setRange(ccColorScale::MIN_STEPS,
638  } else {
641  }
642 
643  connect(colorScaleStepsSpinBox, SIGNAL(valueChanged(int)), this,
644  SLOT(onDensityColorStepsChanged(int)));
645  connect(ticksFreqSpinBox, SIGNAL(valueChanged(int)), this,
646  SLOT(onTicksFreqChanged(int)));
647  connect(showHSVColorsCheckBox, SIGNAL(toggled(bool)), this,
648  SLOT(onHSVColorsToggled(bool)));
649 
650  // interactive filtering mechanism
651  connect(filterFacetsGroupBox, SIGNAL(toggled(bool)), this,
652  SLOT(onFilterEnabled(bool)));
653  connect(dipSpanDoubleSpinBox, SIGNAL(valueChanged(double)), this,
654  SLOT(onFilterSizeChanged(double)));
655  connect(dipDirSpanDoubleSpinBox, SIGNAL(valueChanged(double)), this,
656  SLOT(onFilterSizeChanged(double)));
657  connect(dipDoubleSpinBox, SIGNAL(valueChanged(double)), this,
658  SLOT(onFilterCenterChanged(double)));
659  connect(dipDirDoubleSpinBox, SIGNAL(valueChanged(double)), this,
660  SLOT(onFilterCenterChanged(double)));
661  connect(m_classifWidget, SIGNAL(pointClicked(double, double)), this,
662  SLOT(onPointClicked(double, double)));
663  connect(exportPushButton, SIGNAL(clicked()), this,
664  SLOT(exportCurrentSelection()));
665 }
666 
667 bool StereogramDialog::init(double angularStep_deg,
668  ccHObject* facetGroup,
669  double resolution_deg /*=2.0*/) {
670  if (!m_classifWidget) return false;
671 
672  if (!m_classifWidget->init(angularStep_deg, facetGroup, resolution_deg))
673  return false;
674 
675  double meanDipDir_deg, meanDip_deg;
676  // set stereogram subtitle (i.e. mean direction)
677  m_classifWidget->getMeanDir(meanDip_deg, meanDipDir_deg);
678  meanDirLabel->setText(
679  QString("[Mean] ") +
681  static_cast<PointCoordinateType>(meanDip_deg),
682  static_cast<PointCoordinateType>(meanDipDir_deg)));
683 
684  // restore any old set state before forgetting it!
685  bool filterModeEnabled = filterFacetsGroupBox->isChecked();
686  if (m_facetGroupUniqueID != 0 && filterModeEnabled) {
687  updateFacetsFilter(false);
688  }
689  m_facetGroupUniqueID = facetGroup->getUniqueID();
690  if (filterModeEnabled) {
691  updateFacetsFilter(true);
692  }
693 
694  return true;
695 }
696 
697 void StereogramDialog::closeEvent(QCloseEvent* e) {
698  // filter mode enabled?
699  if (filterFacetsGroupBox->isChecked()) {
700  updateFacetsFilter(false);
701  }
702 }
703 
705  if (m_classifWidget) {
707  dipSpanDoubleSpinBox->value(),
708  dipDirSpanDoubleSpinBox->value());
709  m_classifWidget->update();
710  }
711 
712  updateFacetsFilter(state);
713 }
714 
716  if (m_classifWidget) {
717  m_classifWidget->enableMouseTracking(filterFacetsGroupBox->isChecked(),
718  dipSpanDoubleSpinBox->value(),
719  dipDirSpanDoubleSpinBox->value());
720 
721  updateFacetsFilter(true);
722  }
723 }
724 
726  if (m_classifWidget) {
727  m_classifWidget->setTrackedCenter(dipDoubleSpinBox->value(),
728  dipDirDoubleSpinBox->value());
729  m_classifWidget->update();
730 
731  updateFacetsFilter(true);
732  }
733 }
734 
735 void StereogramDialog::onPointClicked(double dip_deg, double dipDir_deg) {
736  // filter mode enabled?
737  if (filterFacetsGroupBox->isChecked()) {
738  dipDoubleSpinBox->blockSignals(true);
739  dipDirDoubleSpinBox->blockSignals(true);
740 
741  dipDoubleSpinBox->setValue(dip_deg);
742  dipDirDoubleSpinBox->setValue(dipDir_deg);
743 
744  dipDoubleSpinBox->blockSignals(false);
745  dipDirDoubleSpinBox->blockSignals(false);
746 
747  if (m_classifWidget) m_classifWidget->update();
748 
749  updateFacetsFilter(true);
750  }
751 }
752 
754  if (!m_app) return;
755 
756  // try to find the associated entity
757  ccHObject* root = m_app->dbRootObject();
758  ccHObject* entity = (root ? root->find(m_facetGroupUniqueID) : 0);
759  if (!entity) return;
760 
761  PointCoordinateType dipFilter =
762  static_cast<PointCoordinateType>(dipDoubleSpinBox->value());
763  PointCoordinateType dipDirFilter =
764  static_cast<PointCoordinateType>(dipDirDoubleSpinBox->value());
765 
766  PointCoordinateType halfDipSpan =
767  static_cast<PointCoordinateType>(dipSpanDoubleSpinBox->value()) / 2;
768  PointCoordinateType halfDipDirSpan =
769  static_cast<PointCoordinateType>(dipDirSpanDoubleSpinBox->value()) /
770  2;
771 
773  // a set of facets?
774  if (entity->isA(CV_TYPES::HIERARCHY_OBJECT)) {
775  ccHObject::Container facets;
776  entity->filterChildren(facets, true, CV_TYPES::FACET);
777  if (facets.empty()) return;
778 
779  for (size_t i = 0; i < facets.size(); ++i) {
780  ccFacet* facet = static_cast<ccFacet*>(facets[i]);
781 
782  bool visible = true;
783  if (enable) {
784  CCVector3 N = facet->getNormal();
785  PointCoordinateType dip, dipDir;
787 
788  double dDip = fabs(dip - dipFilter);
789  double dDipDir = fabs(dipDir - dipDirFilter);
790 
791  visible =
792  ((dDip <= halfDipSpan || dDip >= 360.0 - halfDipSpan) &&
793  (dDipDir <= halfDipDirSpan ||
794  dDipDir >= 360.0 - halfDipDirSpan));
795  }
796 
797  if (visible) {
799  }
800  // facet->setEnabled(visible);
801  ccMesh* polygon = facet->getPolygon();
802  if (polygon) {
803  polygon->setVisible(visible);
804  }
805  ccPolyline* polyline = facet->getContour();
806  if (polyline) {
807  // polyline->showColors(visible);
808  polyline->setWidth(
809  static_cast<PointCoordinateType>(visible ? 2 : 1));
810  }
811  }
812  }
813  // or a cloud?
814  else if (entity->isA(CV_TYPES::POINT_CLOUD)) {
815  ccPointCloud* cloud = static_cast<ccPointCloud*>(entity);
816  if (!cloud->hasNormals()) return;
817 
818  if (enable) {
819  unsigned count = cloud->size();
820  if (count == 0) return;
821  if (!cloud->resetVisibilityArray()) {
822  m_app->dispToConsole("Not enough memory!");
823  return;
824  }
826  cloud->getTheVisibilityArray();
827  assert(!visTable.empty());
828 
829  for (unsigned i = 0; i < static_cast<unsigned>(count); ++i) {
830  CCVector3 N = cloud->getPointNormal(i);
831  PointCoordinateType dip, dipDir;
833 
834  double dDip = fabs(dip - dipFilter);
835  double dDipDir = fabs(dipDir - dipDirFilter);
836 
837  bool visible =
838  ((dDip <= halfDipSpan || dDip >= 360.0 - halfDipSpan) &&
839  (dDipDir <= halfDipDirSpan ||
840  dDipDir >= 360.0 - halfDipDirSpan));
841 
842  visTable[i] = (visible ? POINT_VISIBLE : POINT_HIDDEN);
843  }
844  } else {
845  cloud->unallocateVisibilityArray();
846  }
847  cloud->setRedraw(true);
848  }
849 
850  if (m_app) m_app->refreshAll();
851 }
852 
854  if (!m_app) return;
855 
856  // try to find the associated entity
857  ccHObject* root = m_app->dbRootObject();
858  ccHObject* entity = (root ? root->find(m_facetGroupUniqueID) : 0);
859  if (!entity) return;
860 
861  PointCoordinateType dipFilter =
862  static_cast<PointCoordinateType>(dipDoubleSpinBox->value());
863  PointCoordinateType dipDirFilter =
864  static_cast<PointCoordinateType>(dipDirDoubleSpinBox->value());
865 
866  PointCoordinateType halfDipSpan =
867  static_cast<PointCoordinateType>(dipSpanDoubleSpinBox->value()) / 2;
868  PointCoordinateType halfDipDirSpan =
869  static_cast<PointCoordinateType>(dipDirSpanDoubleSpinBox->value()) /
870  2;
871 
872  QString selectionSuffix = QString(" [dip=(%1 - %2)][dipDir=(%3 - %4)]")
873  .arg(dipFilter - halfDipSpan)
874  .arg(dipFilter + halfDipSpan)
875  .arg(dipDirFilter - halfDipDirSpan)
876  .arg(dipDirFilter + halfDipDirSpan);
877 
878  // a set of facets?
879  if (entity->isA(CV_TYPES::HIERARCHY_OBJECT)) {
880  ccHObject::Container facets;
881  entity->filterChildren(facets, true, CV_TYPES::FACET);
882  if (facets.empty()) return;
883  ccHObject* newGroup =
884  new ccHObject(entity->getName() + selectionSuffix);
885 
886  for (size_t i = 0; i < facets.size(); ++i) {
887  ccFacet* facet = static_cast<ccFacet*>(facets[i]);
888 
889  CCVector3 N = facet->getNormal();
890  PointCoordinateType dip, dipDir;
892 
893  double dDip = fabs(dip - dipFilter);
894  double dDipDir = fabs(dipDir - dipDirFilter);
895 
896  bool visible =
897  ((dDip <= halfDipSpan || dDip >= 360.0 - halfDipSpan) &&
898  (dDipDir <= halfDipDirSpan ||
899  dDipDir >= 360.0 - halfDipDirSpan));
900 
901  if (visible) {
902  ccFacet* facetClone = facet->clone();
903  if (facetClone) newGroup->addChild(facetClone);
904  }
905  }
906 
907  if (newGroup->getChildrenNumber() != 0) {
908  m_app->addToDB(newGroup);
909  } else {
910  delete newGroup;
911  newGroup = 0;
912  }
913  }
914  // or a cloud?
915  else if (entity->isA(CV_TYPES::POINT_CLOUD)) {
916  ccPointCloud* cloud = static_cast<ccPointCloud*>(entity);
917  if (!cloud->isVisibilityTableInstantiated()) return;
918 
919  ccGenericPointCloud* subset =
921  if (subset) {
922  if (subset->size() != 0) {
923  subset->setName(cloud->getName() + selectionSuffix);
924  m_app->addToDB(subset);
925  } else {
926  delete subset;
927  subset = nullptr;
928  }
929  }
930  }
931 
932  // if (m_app)
933  // m_app->refreshAll();
934 }
935 
937  if (m_classifWidget && value >= ccColorScale::MIN_STEPS) {
939  static_cast<unsigned>(value));
940  m_classifWidget->update();
941  }
942 }
943 
945  if (m_classifWidget) {
947  m_classifWidget->update();
948  }
949 }
950 
952  if (m_classifWidget) {
954  m_classifWidget->update();
955  }
956 }
957 
959  if (!m_classifWidget || !m_colorScaleSelector) return;
960 
962  unsigned steps = static_cast<unsigned>(colorScaleStepsSpinBox->value());
963 
966  m_classifWidget->update();
967 }
968 
970  if (!m_app || !m_app->getColorScalesManager()) return;
971 
972  ccColorScale::Shared colorScale =
978  colorScale, m_app->getMainWindow());
979  if (cseDlg.exec()) {
980  colorScale = cseDlg.getActiveScale();
981  if (colorScale && m_colorScaleSelector) {
982  m_colorScaleSelector->init(); // in fact it's a 're-init'
983  m_colorScaleSelector->setSelectedScale(colorScale->getUuid());
984  }
985 
986  // save current scale manager state to persistent settings
988  }
989 }
Rect frame
MouseEvent event
constexpr unsigned char POINT_VISIBLE
Definition: CVConst.h:92
constexpr unsigned char POINT_HIDDEN
Definition: CVConst.h:94
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
int width
int height
int count
void * X
Definition: SmallVector.cpp:45
boost::geometry::model::polygon< point_xy > polygon
Definition: TreeIso.cpp:37
Density grid.
unsigned rSteps
Dimension (X)
double step_deg
Grid step along Y (in degrees)
double * grid
Grid.
~FacetDensityGrid()
Destructor.
double step_R
Grid step along X (in unit)
FacetDensityGrid()
Default constructor.
double minMaxDensity[2]
Min/max density.
unsigned ddSteps
Dimension (Y)
static void GenerateSubfamilyColor(ecvColor::Rgb &col, double dip, double dipDir, unsigned subFamilyIndex, unsigned subFamilyCount, ecvColor::Rgb *darkCol=0)
Generates a given sub-family color.
Dialog for displaying the angular repartition of facets (qFacets plugin)
void onTicksFreqChanged(int)
ecvMainAppInterface * m_app
Main application interface.
void updateFacetsFilter(bool enable)
ccColorScaleSelector * m_colorScaleSelector
Color scale selector/editor.
void closeEvent(QCloseEvent *e)
void onFilterCenterChanged(double)
StereogramDialog(ecvMainAppInterface *app)
Default constructor.
void onDensityColorStepsChanged(int)
void colorScaleChanged(int)
void onFilterEnabled(bool)
void onPointClicked(double, double)
int m_facetGroupUniqueID
Associated set of facets (unique ID)
void spawnColorScaleEditor()
void exportCurrentSelection()
bool init(double angularStep_deg, ccHObject *facetGroup, double resolution_deg=2.0)
Inits dialog.
void onFilterSizeChanged(double)
void onHSVColorsToggled(bool)
StereogramWidget * m_classifWidget
Associated widget.
Orientation-based classification widget.
Definition: stereogramDlg.h:39
int m_radius
Stereogram radius (pixels)
double m_clickDipDir_deg
Last mouse click equivalent dip direction (in degrees)
void setDensityColorScaleSteps(unsigned steps)
Sets density color scale steps.
Definition: stereogramDlg.h:72
double m_clickDipSpan_deg
Click area span along dip (in degrees)
double m_meanDip_deg
Mean dip (in degrees)
void showHSVRing(bool state)
Whether to show the 'HSV' ring or not.
Definition: stereogramDlg.h:84
bool init(double angularStep_deg, ccHObject *facetGroup, double resolution_deg=2.0)
Sets current parameters.
double m_meanDipDir_deg
Mean dip direction (in degrees)
void setTrackedCenter(double dip_deg, double dipDir_deg)
Sets tracked center position.
int m_ticksFreq
Ticks frequency.
virtual ~StereogramWidget()
Destructor.
StereogramWidget(QWidget *parent=0)
Default constructor.
bool m_trackMouseClick
Mouse tracking.
FacetDensityGrid * m_densityGrid
Density grid.
double m_angularStep_deg
Angular step (in degrees)
void setDensityColorScale(ccColorScale::Shared colorScale)
Sets density color scale.
Definition: stereogramDlg.h:64
double m_clickDipDirSpan_deg
Click area span along dip direction (in degrees)
virtual void paintEvent(QPaintEvent *e)
void pointClicked(double dip_deg, double dipDir_deg)
Signal emitted when the mouse (left) button is clicked.
void enableMouseTracking(bool state, double dipSpan_deg=30, double dipDirSpan_deg=30)
Enables or not the mouse tracking mode.
void getMeanDir(double &meanDip_deg, double &meanDipDir_deg)
Returns the mean dip direction and dip.
Definition: stereogramDlg.h:55
QPoint m_center
Stereogram center (pixels)
double m_clickDip_deg
Last mouse click equivalent dip (in degrees)
bool m_showHSVRing
Whether to show the 'HSV' ring or not.
unsigned m_densityColorScaleSteps
Density color scale steps.
void setTicksFreq(int freq)
Sets the ticks frequency (0 = no ticks)
Definition: stereogramDlg.h:81
virtual void mousePressEvent(QMouseEvent *e)
ccColorScale::Shared m_densityColorScale
Density color scale.
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
void normalize()
Sets vector norm to unity.
Definition: CVGeom.h:428
Dialog to edit/create color scales.
ccColorScale::Shared getActiveScale()
Returns active scale.
Advanced editor for color scales.
void init()
Inits selector with the Color Scales Manager.
void setSelectedScale(QString uuid)
Sets selected combo box item (scale) by UUID.
ccColorScale::Shared getSelectedScale() const
Returns currently selected color scale.
Color scale.
Definition: ecvColorScale.h:71
static const unsigned MIN_STEPS
Minimum number of steps.
Definition: ecvColorScale.h:99
QSharedPointer< ccColorScale > Shared
Shared pointer type.
Definition: ecvColorScale.h:74
static const unsigned MAX_STEPS
Maximum number of steps (internal representation)
Color scales manager/container.
static ccColorScale::Shared GetDefaultScale(DEFAULT_SCALES scale=BGYR)
Returns a pre-defined color scale (static shortcut)
void toPersistentSettings() const
Save custom color scales to persistent settings.
ccColorScale::Shared getDefaultScale(DEFAULT_SCALES scale) const
Returns a pre-defined color scale.
virtual void setVisible(bool state)
Sets entity visibility.
virtual void setRedraw(bool state)
Sets entity redraw mode.
Facet.
Definition: ecvFacet.h:25
ccFacet * clone() const
Clones this facet.
ccMesh * getPolygon()
Returns polygon mesh (if any)
Definition: ecvFacet.h:81
double getSurface() const
Returns associated surface area.
Definition: ecvFacet.h:70
ccPolyline * getContour()
Returns contour polyline (if any)
Definition: ecvFacet.h:86
CCVector3 getNormal() const override
Returns the entity normal.
Definition: ecvFacet.h:63
A 3D cloud interface with associated features (color, normals, octree, etc.)
virtual bool isVisibilityTableInstantiated() const
Returns whether the visibility array is allocated or not.
virtual VisibilityTableType & getTheVisibilityArray()
Returns associated visibility array.
virtual bool resetVisibilityArray()
Resets the associated visibility array.
std::vector< unsigned char > VisibilityTableType
Array of "visibility" information for each point.
virtual void unallocateVisibilityArray()
Erases the points visibility information.
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
ccHObject * find(unsigned uniqueID)
Finds an entity in this object hierarchy.
unsigned getChildrenNumber() const
Returns the number of children.
Definition: ecvHObject.h:312
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a 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
Triangular mesh.
Definition: ecvMesh.h:35
static QString ConvertDipAndDipDirToString(PointCoordinateType dip_deg, PointCoordinateType dipDir_deg)
Converts geological 'dip direction & dip' parameters to a string.
static void ConvertNormalToDipAndDipDir(const CCVector3 &N, PointCoordinateType &dip_deg, PointCoordinateType &dipDir_deg)
Converts a normal vector to geological 'dip direction & dip' parameters.
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
virtual unsigned getUniqueID() const
Returns object unique ID.
Definition: ecvObject.h:86
bool isA(CV_CLASS_ENUM type) const
Definition: ecvObject.h:131
virtual void setName(const QString &name)
Sets object name.
Definition: ecvObject.h:75
Plane (primitive)
Definition: ecvPlane.h:18
CCVector3 getNormal() const override
Returns the entity normal.
Definition: ecvPlane.h:73
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool hasNormals() const override
Returns whether normals are enabled or not.
ccGenericPointCloud * createNewCloudFromVisibilitySelection(bool removeSelectedPoints=false, VisibilityTableType *visTable=nullptr, std::vector< int > *newIndexesOfRemainingPoints=nullptr, bool silent=false, cloudViewer::ReferenceCloud *selection=nullptr) override
const CCVector3 & getPointNormal(unsigned pointIndex) const override
Returns normal corresponding to a given point.
Colored polyline.
Definition: ecvPolyline.h:24
void setWidth(PointCoordinateType width)
Sets the width of the line.
virtual unsigned size() const =0
Returns the number of points.
bool oneStep()
Increments total progress value of a single unit.
unsigned size() const override
Definition: PointCloudTpl.h:38
RGB color structure.
Definition: ecvColorTypes.h:49
static void SetRedrawRecursive(bool redraw=false)
Main application interface (for plugins)
virtual ccHObject * dbRootObject()=0
Returns DB root (as a ccHObject)
virtual QMainWindow * getMainWindow()=0
Returns main window.
virtual void refreshAll(bool only2D=false, bool forceRedraw=true)=0
Redraws all GL windows that have the 'refresh' flag on.
virtual void addToDB(ccHObject *obj, bool updateZoom=false, bool autoExpandDBTree=true, bool checkDimensions=false, bool autoRedraw=true)=0
virtual void dispToConsole(QString message, ConsoleMessageLevel level=STD_CONSOLE_MESSAGE)=0
virtual ccColorScalesManager * getColorScalesManager()=0
Returns color scale manager (unique instance)
Graphical progress indicator (thread-safe)
virtual void stop() override
Notifies the fact that the process has ended.
virtual void start() override
virtual void setInfo(const char *infoStr) override
Notifies some information about the ongoing process.
virtual void setMethodTitle(const char *methodTitle) override
Notifies the algorithm title.
__host__ __device__ float2 fabs(float2 v)
Definition: cutil_math.h:1254
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
@ HIERARCHY_OBJECT
Definition: CVTypes.h:103
@ POINT_CLOUD
Definition: CVTypes.h:104
@ FACET
Definition: CVTypes.h:109
@ PLANE
Definition: CVTypes.h:120
Tensor Minimum(const Tensor &input, const Tensor &other)
Computes the element-wise minimum of input and other. The tensors must have same data type and device...
MiniVec< float, N > floor(const MiniVec< float, N > &a)
Definition: MiniVec.h:75
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89
float RadiansToDegrees(int radians)
Convert radians to degrees.
Definition: CVMath.h:71
float DegreesToRadians(int degrees)
Convert degrees to radians.
Definition: CVMath.h:98
constexpr Rgb black(0, 0, 0)
constexpr Rgb magenta(MAX, 0, MAX)
constexpr Rgb white(MAX, MAX, MAX)
constexpr Rgb red(MAX, 0, 0)
cloudViewer::NormalizedProgress * nProgress