14 #include <QLayoutItem>
15 #include <QStringListModel>
33 , m_nameLabel(new QLabel)
34 , m_statusLabel(new QLabel)
36 auto* layout =
new QHBoxLayout;
37 layout->addWidget(m_nameLabel);
38 layout->addWidget(m_statusLabel);
44 m_nameLabel->setText(
name);
49 return m_nameLabel->text();
54 m_statusLabel->setPixmap(QApplication::style()->standardPixmap(QStyle::SP_MessageBoxWarning));
55 m_statusLabel->setToolTip(message);
60 return !m_statusLabel->toolTip().isEmpty();
65 m_statusLabel->setPixmap(QPixmap());
66 m_statusLabel->setToolTip(QString());
71 QLabel* m_statusLabel;
77 , m_scalarFieldsNamesModel(new QStringListModel)
78 , m_extraFieldsDataTypesModel(new QStringListModel)
79 , m_optimalScale(
std::numeric_limits<double>::quiet_NaN(),
80 std::numeric_limits<double>::quiet_NaN(),
81 std::numeric_limits<double>::quiet_NaN())
82 , m_originalScale(
std::numeric_limits<double>::quiet_NaN(),
83 std::numeric_limits<double>::quiet_NaN(),
84 std::numeric_limits<double>::quiet_NaN())
89 QStringList cloudScalarFieldsNames;
90 cloudScalarFieldsNames << QString();
105 m_scalarFieldsNamesModel->setStringList(cloudScalarFieldsNames);
107 bestScaleRadioButton->setChecked(
false);
108 originalScaleRadioButton->setEnabled(
false);
109 customScaleRadioButton->setEnabled(
true);
110 customScaleRadioButton->setChecked(
true);
114 versionComboBox->addItem(versionStr);
116 versionComboBox->setCurrentIndex(0);
118 connect(versionComboBox,
119 (
void(QComboBox::*)(
const QString&))(&QComboBox::currentIndexChanged),
123 connect(pointFormatComboBox,
124 (
void(QComboBox::*)(
int))(&QComboBox::currentIndexChanged),
130 QStringList extraFieldsDataTypeNames{
"uint8",
140 m_extraFieldsDataTypesModel->setStringList(extraFieldsDataTypeNames);
149 pointFormatComboBox->blockSignals(
true);
150 pointFormatComboBox->clear();
155 for (
unsigned fmt : *pointFormats)
157 pointFormatComboBox->addItem(QString::number(
fmt));
159 pointFormatComboBox->setCurrentIndex(0);
165 pointFormatComboBox->blockSignals(
false);
181 QObject* senderObject = sender();
182 size_t senderIndex = std::distance(m_scalarFieldMapping.begin(),
183 std::find_if(m_scalarFieldMapping.begin(),
184 m_scalarFieldMapping.end(),
185 [senderObject](
const std::pair<MappingLabel*, QComboBox*>& pair)
186 { return pair.second == senderObject; }));
188 if (qobject_cast<QComboBox*>(senderObject)->itemText(index).isEmpty())
190 m_scalarFieldMapping[senderIndex].first->clearWarning();
193 const QString scalarFieldName = m_scalarFieldMapping[senderIndex].first->name();
194 const QString ccScalarFieldName = m_scalarFieldMapping[senderIndex].second->currentText();
214 m_scalarFieldMapping[senderIndex].first->setWarning(
"Some values are out of range and will be truncated");
218 m_scalarFieldMapping[senderIndex].first->clearWarning();
221 size_t numWarnings = std::count_if(m_scalarFieldMapping.begin(),
222 m_scalarFieldMapping.end(),
223 [](
const std::pair<MappingLabel*, QComboBox*>& pair)
224 { return pair.first->hasWarning(); });
228 tabWidget->setTabIcon(1, QApplication::style()->standardPixmap(QStyle::SP_MessageBoxWarning));
232 tabWidget->setTabIcon(1, {});
252 Q_ASSERT_X(
false, __func__,
"No point format available for the selected version");
257 for (
int i = 0; i < scalarFieldFormLayout->rowCount(); ++i)
259 auto* label = scalarFieldFormLayout->itemAt(i, QFormLayout::LabelRole);
260 if (label !=
nullptr && label->widget() !=
nullptr)
262 label->widget()->hide();
265 auto* field = scalarFieldFormLayout->itemAt(i, QFormLayout::FieldRole);
266 if (field !=
nullptr && field->widget() !=
nullptr)
268 field->widget()->hide();
275 int numDeltaFields = scalarFieldFormLayout->rowCount() -
static_cast<int>(lasScalarFields.size());
276 if (numDeltaFields < 0)
279 for (
int i = numDeltaFields; i < 0; ++i)
281 auto* box =
new QComboBox(
this);
282 box->setModel(m_scalarFieldsNamesModel);
284 qOverload<int>(&QComboBox::currentIndexChanged),
289 scalarFieldFormLayout->addRow(widget, box);
290 m_scalarFieldMapping.emplace_back(widget, box);
294 assert(lasScalarFields.size() <= scalarFieldFormLayout->rowCount());
296 QStringList cloudScalarFieldsNames = m_scalarFieldsNamesModel->stringList();
297 for (
size_t i = 0; i < lasScalarFields.size(); ++i)
300 m_scalarFieldMapping[i].first->setName(field.
name());
301 m_scalarFieldMapping[i].first->clearWarning();
302 m_scalarFieldMapping[i].second->setCurrentIndex(cloudScalarFieldsNames.indexOf(field.
name()));
304 auto* label = scalarFieldFormLayout->itemAt(
static_cast<int>(i), QFormLayout::LabelRole);
305 if (label !=
nullptr && label->widget() !=
nullptr)
307 label->widget()->show();
310 auto* fieldWidget = scalarFieldFormLayout->itemAt(
static_cast<int>(i), QFormLayout::FieldRole);
311 if (fieldWidget !=
nullptr && fieldWidget->widget() !=
nullptr)
313 fieldWidget->widget()->show();
319 specialScalarFieldFrame->hide();
320 waveformCheckBox->setCheckState(Qt::Unchecked);
321 rgbCheckBox->setCheckState(Qt::Unchecked);
325 specialScalarFieldFrame->show();
329 rgbCheckBox->setEnabled(m_cloud->
hasColors());
330 rgbCheckBox->setChecked(m_cloud->
hasColors());
339 waveformCheckBox->show();
340 waveformCheckBox->setEnabled(m_cloud->
hasFWF());
341 waveformCheckBox->setChecked(m_cloud->
hasFWF());
345 waveformCheckBox->hide();
353 card->typeComboBox->setModel(m_extraFieldsDataTypesModel);
354 card->firstScalarFieldComboBox->setModel(m_scalarFieldsNamesModel);
355 card->secondScalarFieldComboBox->setModel(m_scalarFieldsNamesModel);
356 card->thirdScalarFieldComboBox->setModel(m_scalarFieldsNamesModel);
357 connect(card->removeButton, &QPushButton::clicked, card, &QWidget::hide);
364 #ifdef CC_CORE_LIB_USES_DOUBLE
365 const char* defaultType =
"float64";
367 const char* defaultType =
"float32";
369 int defaultTypeIndex = m_extraFieldsDataTypesModel->stringList().indexOf(defaultType);
370 defaultTypeIndex =
std::max(defaultTypeIndex, 0);
372 int esfCount = extraScalarFieldsLayout->count();
375 for (
int i = 0; i < esfCount; ++i)
377 QLayoutItem* item = extraScalarFieldsLayout->itemAt(i);
378 QWidget* widget = item->widget();
379 if (widget && widget->isHidden())
382 extraScalarFieldsLayout->removeItem(item);
383 extraScalarFieldsLayout->insertItem(extraScalarFieldsLayout->count(), item);
384 auto* card = qobject_cast<LasExtraScalarFieldCard*>(widget);
398 auto* card = createCard();
399 extraScalarFieldsLayout->insertWidget(esfCount, card);
404 const QString versionStr = QString(
"1.%1").arg(versionAndFmt.
minorVersion);
406 int versionIndex = versionComboBox->findText(versionStr);
407 if (versionIndex >= 0)
409 QString fmtStr = QString::number(versionAndFmt.
pointFormat);
410 versionComboBox->setCurrentIndex(versionIndex);
411 int pointFormatIndex = pointFormatComboBox->findText(fmtStr);
412 if (pointFormatIndex >= 0)
414 pointFormatComboBox->setCurrentIndex(pointFormatIndex);
421 m_optimalScale = scale;
423 bestScaleLabel->setText(QString(
"(%1, %2, %3)").arg(scale.
x).arg(scale.
y).arg(scale.
z));
425 bestScaleRadioButton->setEnabled(
true);
428 bestScaleRadioButton->setChecked(
true);
434 m_originalScale = scale;
436 originalScaleLabel->setText(QString(
"(%1, %2, %3)").arg(scale.
x).arg(scale.
y).arg(scale.
z));
438 originalScaleRadioButton->setEnabled(canUseScale);
441 labelOriginal->setText(QObject::tr(
"Original scale is too small for this cloud "));
442 labelOriginal->setStyleSheet(
"color: red;");
443 originalScaleRadioButton->setChecked(
false);
447 originalScaleRadioButton->setChecked(
true);
453 if (extraScalarFields.empty())
460 auto* card = createCard();
461 extraScalarFieldsLayout->insertWidget(extraScalarFieldsLayout->count(), card);
462 card->fillFrom(field);
468 return static_cast<uint8_t
>(
std::min(pointFormatComboBox->currentText().toUInt(), 255u));
476 const QString versionString = versionComboBox->currentText();
478 if (tokens.size() == 2)
480 versionMajor =
static_cast<uint8_t
>(
std::min(tokens[0].toUInt(), 255u));
481 versionMinor =
static_cast<uint8_t
>(
std::min(tokens[1].toUInt(), 255u));
491 return rgbCheckBox->isChecked();
496 return waveformCheckBox->isChecked();
501 if (bestScaleRadioButton->isChecked())
503 assert(std::isfinite(m_optimalScale.
x) && std::isfinite(m_optimalScale.
y) && std::isfinite(m_optimalScale.
z));
504 return m_optimalScale;
506 else if (originalScaleRadioButton->isChecked())
508 assert(std::isfinite(m_originalScale.
x) && std::isfinite(m_originalScale.
y) && std::isfinite(m_originalScale.
z));
509 return m_originalScale;
511 else if (customScaleRadioButton->isChecked())
513 double customScale = customScaleDoubleSpinBox->value();
514 return {customScale, customScale, customScale};
531 std::vector<LasScalarField>
fields;
532 fields.reserve(scalarFieldFormLayout->rowCount());
534 for (
const auto& item : m_scalarFieldMapping)
536 if (item.second->currentIndex() > 0)
547 const std::string
name = item.first->name().toStdString();
565 if (extraScalarFieldsLayout->count() == 0)
569 int esfCount = extraScalarFieldsLayout->count();
571 std::vector<LasExtraScalarField> extraScalarFields;
572 extraScalarFields.reserve(esfCount);
574 for (
int i = 0; i < esfCount; ++i)
576 QLayoutItem* item = extraScalarFieldsLayout->itemAt(i);
577 QWidget* widget = item->widget();
578 if (!widget || widget->isHidden())
582 auto* card = qobject_cast<LasExtraScalarFieldCard*>(widget);
589 if (!card->fillField(field, *m_cloud))
591 CVLog::Error(
"failed to convert scalar field info to something writable");
594 extraScalarFields.push_back(field);
597 extraScalarFields.shrink_to_fit();
599 return extraScalarFields;
std::vector< PCLPointField > fields
static constexpr int ExtraScalarFieldsTabIndex
QtCompatStringRefList qtCompatSplitRefChar(const QString &str, QChar sep)
static bool Error(const char *format,...)
Display an error dialog with formatted message.
void setExtraScalarFields(const std::vector< LasExtraScalarField > &extraScalarFields)
Set the extra LAS scalar fields saved from the original file.
void handleComboBoxChange(int index)
void setOriginalScale(const CCVector3d &scale, bool canUseScale, bool autoCheck=true)
void handleSelectedPointFormatChange(int index)
When the user changes the point format, we need to update the scalar field form.
bool shouldSaveWaveform() const
Returns whether the user wants to save the Waveforms.
LasSaveDialog(ccPointCloud *cloud, QWidget *parent=nullptr)
void selectedVersion(uint8_t &versionMajor, uint8_t &versionMinor) const
Returns the version currently selected.
bool shouldSaveRGB() const
Returns whether the user wants to save RGB.
std::vector< LasScalarField > fieldsToSave() const
void addExtraScalarFieldCard()
uint8_t selectedPointFormat() const
Returns the point format currently selected.
CCVector3d chosenScale() const
Returns the currently selected scale.
std::vector< LasExtraScalarField > extraFieldsToSave() const
void setVersionAndPointFormat(const LasDetails::LasVersion versionAndFmt)
void handleSelectedVersionChange(const QString &)
void setOptimalScale(const CCVector3d &scale, bool autoCheck=false)
Set scale that would offer the user the best precision.
Widget to map a predefined scalar field 'role' with a particular scalar field (combo box)
MappingLabel(QWidget *parent=nullptr)
void setName(const QString &name)
void setWarning(const QString &message)
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool hasFWF() const
Returns whether the cloud has associated Full WaveForm data.
bool hasColors() const override
Returns whether colors are enabled or not.
A scalar field associated to display-related parameters.
int getScalarFieldIndexByName(const char *name) const
Returns the index of a scalar field represented by its name.
ScalarField * getScalarField(int index) const
Returns a pointer to a specific scalar field.
unsigned getNumberOfScalarFields() const
Returns the number of associated (and active) scalar fields.
const char * getScalarFieldName(int index) const
Returns the name of a specific scalar field.
A simple scalar field (to be associated to a point cloud)
ScalarType getMin() const
Returns the minimum value.
ScalarType getMax() const
Returns the maximum value.
const std::array< const char *, 3 > & AvailableVersions()
bool HasRGB(unsigned pointFormatId)
Returns whether the point format supports RGB.
bool HasWaveform(unsigned pointFormatId)
Returns whether the point format supports Waveforms.
const std::vector< unsigned > * PointFormatsAvailableForVersion(QString version)
static std::vector< LasScalarField > ForPointFormat(unsigned pointFormatId)
const char * name() const
static LasScalarField::Range ValueRange(LasScalarField::Id id)
Returns the range of value the given field (ID) supports.
static LasScalarField::Id IdFromName(const char *name, unsigned targetPointFormat)