From 9b925dd996367cdffd67bc73403af0b958790dad Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Thu, 23 Mar 2023 08:39:31 +0200 Subject: [PATCH] Fix float-point accuracy issue in multisize measurements dimensions. --- ChangeLog.txt | 1 + .../tape/dialogs/dialogdimensionlabels.cpp | 2 +- .../tape/dialogs/dialogrestrictdimension.cpp | 55 +++++++++-------- src/app/tape/tmainwindow.cpp | 17 +++--- src/app/valentina/mainwindow.cpp | 20 +++---- src/libs/vformat/vdimensions.cpp | 22 ++++--- src/libs/vformat/vdimensions.h | 60 +++++++++++++++++++ 7 files changed, 122 insertions(+), 55 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 0557f3b2c..0c10913ef 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -17,6 +17,7 @@ - Improve labels for V notch. - Fix QT issue on MacOS version 11.0 "Big Sur". - Fix excluding objects in internal path. +- Fix float-point accuracy issue in multisize measurements dimensions. # Valentina 0.7.52 September 12, 2022 - Fix crash when default locale is ru. diff --git a/src/app/tape/dialogs/dialogdimensionlabels.cpp b/src/app/tape/dialogs/dialogdimensionlabels.cpp index 837607d67..cb5d5ee9e 100644 --- a/src/app/tape/dialogs/dialogdimensionlabels.cpp +++ b/src/app/tape/dialogs/dialogdimensionlabels.cpp @@ -184,7 +184,7 @@ void DialogDimensionLabels::InitTable() } { - auto *itemLabel = new QTableWidgetItem(labels.value(base)); + auto *itemLabel = new QTableWidgetItem(VFuzzyValue(labels, base)); itemLabel->setData(Qt::UserRole, base); itemLabel->setTextAlignment(Qt::AlignHCenter | Qt::AlignCenter); diff --git a/src/app/tape/dialogs/dialogrestrictdimension.cpp b/src/app/tape/dialogs/dialogrestrictdimension.cpp index bda648491..c110bad46 100644 --- a/src/app/tape/dialogs/dialogrestrictdimension.cpp +++ b/src/app/tape/dialogs/dialogrestrictdimension.cpp @@ -51,7 +51,7 @@ auto FilterByMinimum(const QVector &base, qreal restriction) -> QVector= restriction) + if (b > restriction || VFuzzyComparePossibleNulls(b, restriction)) { filtered.append(b); } @@ -71,7 +71,7 @@ auto FilterByMaximum(const QVector &base, qreal restriction) -> QVectorMinValue(); - max = bases.indexOf(restriction.GetMax()) != -1 ? restriction.GetMax() : dimension->MaxValue(); + min = VFuzzyIndexOf(bases, restriction.GetMin()) != -1 ? restriction.GetMin() : dimension->MinValue(); + max = VFuzzyIndexOf(bases, restriction.GetMax()) != -1 ? restriction.GetMax() : dimension->MaxValue(); if (max < min) { @@ -100,14 +100,14 @@ void InitMinMax(qreal &min, qreal &max, const MeasurementDimension_p &dimension, void SetCellIcon(QTableWidgetItem *item, const QVector &validRows, qreal rowValue, qreal columnValue, const VDimensionRestriction &restriction, qreal min, qreal max) { - if (validRows.contains(rowValue)) + if (VFuzzyContains(validRows, rowValue)) { - const bool leftRestriction = columnValue >= min; - const bool rightRestriction = columnValue <= max; + const bool leftRestriction = columnValue > min || VFuzzyComparePossibleNulls(columnValue, min); + const bool rightRestriction = columnValue < max || VFuzzyComparePossibleNulls(columnValue, max); if (leftRestriction && rightRestriction) { - item->setIcon(QIcon(restriction.GetExcludeValues().contains(columnValue) + item->setIcon(QIcon(VFuzzyContains(restriction.GetExcludeValues(), columnValue) ? QStringLiteral("://icon/24x24/close.png") : QStringLiteral("://icon/24x24/star.png"))); } @@ -246,7 +246,7 @@ void DialogRestrictDimension::RowSelected() ui->comboBoxMin->blockSignals(true); ui->comboBoxMin->clear(); - QVector filtered = FilterByMaximum(bases, restriction.GetMax()); + QVector filtered = FilterByMinimum(FilterByMaximum(bases, restriction.GetMax()), restriction.GetMin()); FillBases(filtered, dimension, ui->comboBoxMin); int index = ui->comboBoxMin->findData(restriction.GetMin()); ui->comboBoxMin->setCurrentIndex(index != -1 ? index : 0); @@ -254,8 +254,7 @@ void DialogRestrictDimension::RowSelected() ui->comboBoxMax->blockSignals(true); ui->comboBoxMax->clear(); - filtered = FilterByMinimum(bases, restriction.GetMin()); - FillBases(FilterByMinimum(bases, restriction.GetMin()), dimension, ui->comboBoxMax); + FillBases(filtered, dimension, ui->comboBoxMax); index = ui->comboBoxMax->findData(restriction.GetMax()); ui->comboBoxMax->setCurrentIndex(index != -1 ? index : ui->comboBoxMax->count() - 1); ui->comboBoxMax->blockSignals(false); @@ -394,7 +393,7 @@ void DialogRestrictDimension::CellContextMenu(QPoint pos) } VDimensionRestriction restriction = m_restrictions.value(coordinates); - bool exclude = not restriction.GetExcludeValues().contains(columnValue); + bool exclude = not VFuzzyContains(restriction.GetExcludeValues(), columnValue); QScopedPointer menu(new QMenu()); QAction *actionExclude = menu->addAction(exclude ? tr("Exclude") : tr("Include")); @@ -650,7 +649,7 @@ void DialogRestrictDimension::AddCell(int row, int column, qreal rowValue, qreal if (m_restrictionType == RestrictDimension::First) { VDimensionRestriction restriction = m_restrictions.value(QChar('0')); - item->setIcon(QIcon(restriction.GetExcludeValues().contains(columnValue) + item->setIcon(QIcon(VFuzzyContains(restriction.GetExcludeValues(), columnValue) ? QStringLiteral("://icon/24x24/close.png") : QStringLiteral("://icon/24x24/star.png"))); } @@ -732,9 +731,9 @@ void DialogRestrictDimension::FillBases(const QVector &bases, const Measu { for(auto base : bases) { - if (labels.contains(base) && not labels.value(base).isEmpty()) + if (VFuzzyContains(labels, base) && not VFuzzyValue(labels, base).isEmpty()) { - control->addItem(labels.value(base), base); + control->addItem(VFuzzyValue(labels, base), base); } else { @@ -746,9 +745,9 @@ void DialogRestrictDimension::FillBases(const QVector &bases, const Measu { for(auto base : bases) { - if (labels.contains(base) && not labels.value(base).isEmpty()) + if (VFuzzyContains(labels, base) && not VFuzzyValue(labels, base).isEmpty()) { - control->addItem(labels.value(base), base); + control->addItem(VFuzzyValue(labels, base), base); } else { @@ -767,9 +766,9 @@ void DialogRestrictDimension::FillBases(const QVector &bases, const Measu { for(auto base : bases) { - if (labels.contains(base) && not labels.value(base).isEmpty()) + if (VFuzzyContains(labels, base) && not VFuzzyValue(labels, base).isEmpty()) { - control->addItem(labels.value(base), base); + control->addItem(VFuzzyValue(labels, base), base); } else { @@ -791,9 +790,9 @@ auto DialogRestrictDimension::FillDimensionXBases(const QVector &bases, for(auto base : bases) { - if (dimensionLabels.contains(base) && not dimensionLabels.value(base).isEmpty()) + if (VFuzzyContains(dimensionLabels, base) && not VFuzzyValue(dimensionLabels, base).isEmpty()) { - labels.append(dimensionLabels.value(base)); + labels.append(VFuzzyValue(dimensionLabels, base)); } else { @@ -816,9 +815,9 @@ auto DialogRestrictDimension::FillDimensionYBases(const QVector &bases, for(auto base : bases) { - if (dimensionLabels.contains(base) && not dimensionLabels.value(base).isEmpty()) + if (VFuzzyContains(dimensionLabels, base) && not VFuzzyValue(dimensionLabels, base).isEmpty()) { - labels.append(dimensionLabels.value(base)); + labels.append(VFuzzyValue(dimensionLabels, base)); } else { @@ -848,9 +847,9 @@ auto DialogRestrictDimension::FillDimensionWZBases(const QVector &bases, for(auto base : bases) { - if (dimensionLabels.contains(base) && not dimensionLabels.value(base).isEmpty()) + if (VFuzzyContains(dimensionLabels, base) && not VFuzzyValue(dimensionLabels, base).isEmpty()) { - labels.append(dimensionLabels.value(base)); + labels.append(VFuzzyValue(dimensionLabels, base)); } else { @@ -904,8 +903,8 @@ auto DialogRestrictDimension::DimensionRestrictedValues(const MeasurementDimensi const QVector bases = dimension->ValidBases(); - qreal min = bases.indexOf(restriction.GetMin()) != -1 ? restriction.GetMin() : dimension->MinValue(); - qreal max = bases.indexOf(restriction.GetMax()) != -1 ? restriction.GetMax() : dimension->MaxValue(); + qreal min = VFuzzyIndexOf(bases, restriction.GetMin()) != -1 ? restriction.GetMin() : dimension->MinValue(); + qreal max = VFuzzyIndexOf(bases, restriction.GetMax()) != -1 ? restriction.GetMax() : dimension->MaxValue(); if (min > max) { @@ -935,7 +934,7 @@ auto DialogRestrictDimension::StartRow() const -> int for(int i=0; i < basesRow.size(); ++i) { - if (validRows.contains(basesRow.at(i))) + if (VFuzzyContains(validRows, basesRow.at(i))) { return i; } diff --git a/src/app/tape/tmainwindow.cpp b/src/app/tape/tmainwindow.cpp index 0e41d43c0..cc57ae626 100644 --- a/src/app/tape/tmainwindow.cpp +++ b/src/app/tape/tmainwindow.cpp @@ -142,9 +142,9 @@ void InitDimensionXItems(const QVector &bases, const DimesionLabels &labe for(auto base : bases) { - if (labels.contains(base) && not labels.value(base).isEmpty()) + if (VFuzzyContains(labels, base) && not VFuzzyValue(labels, base).isEmpty()) { - control->addItem(labels.value(base), base); + control->addItem(VFuzzyValue(labels, base), base); } else { @@ -159,9 +159,9 @@ void InitDimensionYWZItems(const QVector &bases, const DimesionLabels &la { for(auto base : bases) { - if (labels.contains(base) && not labels.value(base).isEmpty()) + if (VFuzzyContains(labels, base) && not VFuzzyValue(labels, base).isEmpty()) { - control->addItem(labels.value(base), base); + control->addItem(VFuzzyValue(labels, base), base); } else { @@ -2844,9 +2844,10 @@ void TMainWindow::InitDimensionsBaseValue() DimesionLabels labels = dimension->Labels(); - if (labels.contains(dimension->BaseValue()) && not labels.value(dimension->BaseValue()).isEmpty()) + if (VFuzzyContains(labels, dimension->BaseValue()) + && not VFuzzyValue(labels, dimension->BaseValue()).isEmpty()) { - base->setText(labels.value(dimension->BaseValue())); + base->setText(VFuzzyValue(labels, dimension->BaseValue())); } else { @@ -4300,8 +4301,8 @@ auto TMainWindow::DimensionRestrictedValues(int index, const MeasurementDimensio const QVector bases = dimension->ValidBases(); - qreal min = bases.indexOf(restriction.GetMin()) != -1 ? restriction.GetMin() : dimension->MinValue(); - qreal max = bases.indexOf(restriction.GetMax()) != -1 ? restriction.GetMax() : dimension->MaxValue(); + qreal min = VFuzzyIndexOf(bases, restriction.GetMin()) != -1 ? restriction.GetMin() : dimension->MinValue(); + qreal max = VFuzzyIndexOf(bases, restriction.GetMax()) != -1 ? restriction.GetMax() : dimension->MaxValue(); if (min > max) { diff --git a/src/app/valentina/mainwindow.cpp b/src/app/valentina/mainwindow.cpp index 14d375e48..c70a6d4c8 100644 --- a/src/app/valentina/mainwindow.cpp +++ b/src/app/valentina/mainwindow.cpp @@ -2335,14 +2335,14 @@ void MainWindow::StoreMultisizeMDimension(const QList &d case MeasurementDimension::X: VAbstractValApplication::VApp()->SetDimensionHeight(currentBase); VAbstractValApplication::VApp()->SetDimensionHeightLabel( - labels.value(currentBase, QString::number(currentBase))); + VFuzzyValue(labels, currentBase, QString::number(currentBase))); break; case MeasurementDimension::Y: { const bool fc = m_m->IsFullCircumference(); VAbstractValApplication::VApp()->SetDimensionSize(fc ? currentBase*2 : currentBase); VAbstractValApplication::VApp()->SetDimensionSizeLabel( - labels.value(currentBase, QString::number(fc ? currentBase*2 : currentBase))); + VFuzzyValue(labels, currentBase, QString::number(fc ? currentBase*2 : currentBase))); const bool measurement = dimension->IsBodyMeasurement(); VAbstractValApplication::VApp() ->SetDimensionSizeUnits(measurement ? m_m->Units() : Unit::LAST_UNIT_DO_NOT_USE); @@ -2353,7 +2353,7 @@ void MainWindow::StoreMultisizeMDimension(const QList &d const bool fc = m_m->IsFullCircumference(); VAbstractValApplication::VApp()->SetDimensionWaist(fc ? currentBase*2 : currentBase); VAbstractValApplication::VApp()->SetDimensionWaistLabel( - labels.value(currentBase, QString::number(fc ? currentBase*2 : currentBase))); + VFuzzyValue(labels, currentBase, QString::number(fc ? currentBase*2 : currentBase))); break; } case MeasurementDimension::Z: @@ -2361,7 +2361,7 @@ void MainWindow::StoreMultisizeMDimension(const QList &d const bool fc = m_m->IsFullCircumference(); VAbstractValApplication::VApp()->SetDimensionHip(fc ? currentBase*2 : currentBase); VAbstractValApplication::VApp()->SetDimensionHipLabel( - labels.value(currentBase, QString::number(fc ? currentBase*2 : currentBase))); + VFuzzyValue(labels, currentBase, QString::number(fc ? currentBase*2 : currentBase))); break; } default: @@ -2419,8 +2419,8 @@ auto MainWindow::DimensionRestrictedValues(int index, const MeasurementDimension const QVector bases = dimension->ValidBases(); - qreal min = bases.indexOf(restriction.GetMin()) != -1 ? restriction.GetMin() : dimension->MinValue(); - qreal max = bases.indexOf(restriction.GetMax()) != -1 ? restriction.GetMax() : dimension->MaxValue(); + qreal min = VFuzzyIndexOf(bases, restriction.GetMin()) != -1 ? restriction.GetMin() : dimension->MinValue(); + qreal max = VFuzzyIndexOf(bases, restriction.GetMax()) != -1 ? restriction.GetMax() : dimension->MaxValue(); if (min > max) { @@ -4943,9 +4943,9 @@ void MainWindow::InitDimensionXGradation(const QVector &bases, const Dime for(auto base : bases) { - if (labels.contains(base) && not labels.value(base).isEmpty()) + if (VFuzzyContains(labels, base) && not VFuzzyValue(labels, base).isEmpty()) { - control->addItem(labels.value(base), base); + control->addItem(VFuzzyValue(labels, base), base); } else { @@ -4963,9 +4963,9 @@ void MainWindow::InitDimensionYWZGradation(const QVector &bases, const Di for(auto base : bases) { - if (labels.contains(base) && not labels.value(base).isEmpty()) + if (VFuzzyContains(labels, base) && not VFuzzyValue(labels, base).isEmpty()) { - control->addItem(labels.value(base), base); + control->addItem(VFuzzyValue(labels, base), base); } else { diff --git a/src/libs/vformat/vdimensions.cpp b/src/libs/vformat/vdimensions.cpp index babedba7f..75a833a4e 100644 --- a/src/libs/vformat/vdimensions.cpp +++ b/src/libs/vformat/vdimensions.cpp @@ -110,8 +110,8 @@ auto VAbstartMeasurementDimension::Name() const -> QString //--------------------------------------------------------------------------------------------------------------------- auto VAbstartMeasurementDimension::ValidSteps() const -> QVector { - const qreal stepBarrier = 8.5; - const qreal s = 0.5; + const qreal stepBarrier = 50; + const qreal s = 0.1; QVector steps; steps.reserve(qRound((stepBarrier - s) * 2 - 1)); @@ -124,6 +124,7 @@ auto VAbstartMeasurementDimension::ValidSteps() const -> QVector else if (diff > 0) { qreal candidate = 1; + int i = 1; do { const qreal step = (m_units == Unit::Mm ? candidate * 10 : candidate); @@ -132,7 +133,8 @@ auto VAbstartMeasurementDimension::ValidSteps() const -> QVector { steps.append(step); } - candidate += s; + candidate = 1 + s * i; + ++i; } while(candidate < stepBarrier); } @@ -179,23 +181,27 @@ auto VAbstartMeasurementDimension::ValidBases(qreal min, qreal max, qreal step, validBases.reserve(qRound((max - min) / step)); qreal value = min; + int i = 1; do { - if (not exclude.contains(value)) + if (not VFuzzyContains(exclude, value)) { validBases.append(value); } - value += step; + value = min + step * i; + ++i; } while(value < max + step); if (validBases.isEmpty()) { value = min; + int i = 1; do { validBases.append(value); - value += step; + value = min + step * i; + ++i; } while(value < max + step); } @@ -222,7 +228,7 @@ auto VAbstartMeasurementDimension::IsRangeValid() const -> bool //--------------------------------------------------------------------------------------------------------------------- auto VAbstartMeasurementDimension::IsStepValid() const -> bool { - bool valid = ValidSteps().indexOf(m_step) != -1; + bool valid = VFuzzyIndexOf(ValidSteps(), m_step) != -1; if (not valid) { m_error = QCoreApplication::translate("VAbstartMeasurementDimension", "Invalid step"); @@ -234,7 +240,7 @@ auto VAbstartMeasurementDimension::IsStepValid() const -> bool //--------------------------------------------------------------------------------------------------------------------- auto VAbstartMeasurementDimension::IsBaseValid() const -> bool { - bool valid = ValidBases().indexOf(m_baseValue) != -1; + bool valid = VFuzzyIndexOf(ValidBases(), m_baseValue) != -1; if (not valid) { m_error = QCoreApplication::translate("VAbstartMeasurementDimension", "Base value invalid"); diff --git a/src/libs/vformat/vdimensions.h b/src/libs/vformat/vdimensions.h index 7bb23b7ee..ba240ad20 100644 --- a/src/libs/vformat/vdimensions.h +++ b/src/libs/vformat/vdimensions.h @@ -53,6 +53,66 @@ template class QSharedPointer; using MeasurementDimension_p = QSharedPointer; using DimesionLabels = QMap; +//--------------------------------------------------------------------------------------------------------------------- +template +inline bool VFuzzyContains(const QMap &c, qreal value) +{ + auto i = c.constBegin(); + while (i != c.constEnd()) + { + if (VFuzzyComparePossibleNulls(i.key(), value)) + { + return true; + } + ++i; + } + return false; +} + +//--------------------------------------------------------------------------------------------------------------------- +template