Optimize U-notch shape.

This commit is contained in:
Roman Telezhynskyi 2023-11-22 13:59:50 +02:00
parent ddb921d468
commit aee93b01b9
8 changed files with 277 additions and 6 deletions

View file

@ -57,6 +57,7 @@
- [smart-pattern/valentina#188] Label %mFileName% file name punctuation.
- Adding removing nodes of curved path.
- New tools: Arc start point, Arc end point.
- Optimize U-notch shape.
# Valentina 0.7.52 September 12, 2022
- Fix crash when default locale is ru.

View file

@ -45,6 +45,39 @@
constexpr qreal VAbstractCurve::minLength; // NOLINT(readability-redundant-declaration)
#endif
namespace
{
//---------------------------------------------------------------------------------------------------------------------
double NodeCurvature(const QPointF &p1, const QPointF &p2, const QPointF &p3, double length)
{
QLineF l1(p2, p1);
l1.setAngle(l1.angle() + 180);
QLineF l2(p2, p3);
double angle = qDegreesToRadians(l2.angleTo(l1));
return std::sin(angle / 2.0) / length;
}
//---------------------------------------------------------------------------------------------------------------------
auto MinimalLength(const QVector<QPointF> &points) -> double
{
vsizetype numPoints = points.size();
double smallestDistance = std::numeric_limits<double>::max();
for (int i = 0; i < numPoints - 1; ++i)
{
double distance = QLineF(points[i], points[i + 1]).length();
if (!qFuzzyIsNull(distance))
{
smallestDistance = std::min(smallestDistance, distance);
}
}
return smallestDistance;
}
} // namespace
//---------------------------------------------------------------------------------------------------------------------
VAbstractCurve::VAbstractCurve(const GOType &type, const quint32 &idObject, const Draw &mode)
: VGObject(type, idObject, mode),
@ -687,6 +720,27 @@ void VAbstractCurve::SetAliasSuffix(const QString &aliasSuffix)
CreateAlias();
}
//---------------------------------------------------------------------------------------------------------------------
auto VAbstractCurve::Curvature(const QVector<QPointF> &vertices) -> double
{
vsizetype numVertices = vertices.size();
if (numVertices < 3)
{
// A polygonal chain needs at least 3 vertices
return 0.0;
}
qreal minLength = MinimalLength(vertices);
double sumCurvature = 0.0;
for (vsizetype i = 1; i < vertices.size() - 1; ++i)
{
sumCurvature += NodeCurvature(vertices[i - 1], vertices[i], vertices[i + 1], minLength);
}
return sumCurvature / static_cast<double>(vertices.size() - 2);
}
//---------------------------------------------------------------------------------------------------------------------
auto VAbstractCurve::PathLength(const QVector<QPointF> &path) -> qreal
{

View file

@ -114,6 +114,8 @@ public:
static constexpr qreal minLength = MmToPixel(1.);
static auto Curvature(const QVector<QPointF> &vertices) -> double;
protected:
virtual void CreateName() = 0;
virtual void CreateAlias() = 0;

View file

@ -36,6 +36,7 @@
#include "../ifc/ifcdef.h"
#include "../vmisc/compatibility.h"
#include "../vmisc/def.h"
#include "../vmisc/defglobal.h"
#include "../vmisc/vabstractapplication.h"
#include "../vmisc/vmath.h"
#include "vabstractcurve.h"
@ -405,6 +406,33 @@ auto VArc::CutArc(qreal length, const QString &pointName) const -> QPointF
return CutArc(length, arc1, arc2, pointName);
}
//---------------------------------------------------------------------------------------------------------------------
auto VArc::OptimalApproximationScale(qreal radius, qreal f1, qreal f2, qreal tolerance) -> qreal
{
if (qFuzzyIsNull(radius))
{
return maxCurveApproximationScale;
}
const qreal expectedCurvature = 1 / qAbs(radius);
qreal scale = minCurveApproximationScale;
do
{
VArc arc(VPointF(), radius, f1, f2);
arc.SetApproximationScale(scale);
qreal curvature = Curvature(arc.GetPoints());
if (expectedCurvature - curvature <= expectedCurvature * tolerance)
{
return scale;
}
scale += 0.1;
} while (scale <= maxCurveApproximationScale);
return maxCurveApproximationScale;
}
//---------------------------------------------------------------------------------------------------------------------
void VArc::CreateName()
{

View file

@ -83,6 +83,8 @@ public:
auto CutArc(qreal length, VArc &arc1, VArc &arc2, const QString &pointName) const -> QPointF;
auto CutArc(qreal length, const QString &pointName) const -> QPointF;
static auto OptimalApproximationScale(qreal radius, qreal f1, qreal f2, qreal tolerance) -> qreal;
protected:
void CreateName() override;
void CreateAlias() override;

View file

@ -458,9 +458,10 @@ auto CreateUMarkPassmark(const VPiecePassmarkData &passmarkData, const QLineF &l
points.append(seg.p1());
points.append(seg.p2());
VArc arc(VPointF(baseLine.p2()), radius, QLineF(baseLine.p2(), l1p2).angle(),
QLineF(baseLine.p2(), l2p2).angle());
arc.SetApproximationScale(10);
const qreal f1 = QLineF(baseLine.p2(), l1p2).angle();
const qreal f2 = QLineF(baseLine.p2(), l2p2).angle();
VArc arc(VPointF(baseLine.p2()), radius, f1, f2);
arc.SetApproximationScale(VArc::OptimalApproximationScale(radius, f1, f2, 0.3));
points += arc.GetPoints();
seg = VPassmark::FindIntersection(QLineF(l2p2, l2p1), seamAllowance);
@ -469,9 +470,10 @@ auto CreateUMarkPassmark(const VPiecePassmarkData &passmarkData, const QLineF &l
}
else
{
VArc arc(VPointF(baseLine.p1()), radius, QLineF(baseLine.p1(), l1p1).angle(),
QLineF(baseLine.p1(), l2p1).angle());
arc.SetApproximationScale(10);
const qreal f1 = QLineF(baseLine.p1(), l1p1).angle();
const qreal f2 = QLineF(baseLine.p1(), l2p1).angle();
VArc arc(VPointF(baseLine.p1()), radius, f1, f2);
arc.SetApproximationScale(VArc::OptimalApproximationScale(radius, f1, f2, 0.3));
points += arc.GetPoints();
}

View file

@ -27,6 +27,7 @@
*************************************************************************/
#include "tst_varc.h"
#include "../vgeometry/vabstractcurve.h"
#include "../vgeometry/varc.h"
#include "../vlayout/vabstractpiece.h"
@ -596,3 +597,182 @@ void TST_VArc::EmptyArc()
ComparePathsDistance(empty.GetPoints(), {QPointF()});
QCOMPARE(empty.GetLength(), 0.);
}
//---------------------------------------------------------------------------------------------------------------------
void TST_VArc::TestCurvature_data()
{
QTest::addColumn<qreal>("radius");
QTest::addColumn<qreal>("startAngle");
QTest::addColumn<qreal>("endAngle");
QTest::newRow("Full circle: radius 10") << 10.0 << 0.0 << 360.0;
QTest::newRow("Full circle: radius -10") << -10.0 << 0.0 << 360.0;
QTest::newRow("Full circle: radius 150") << 150.0 << 0.0 << 360.0;
QTest::newRow("Full circle: radius -150") << -150.0 << 0.0 << 360.0;
QTest::newRow("Full circle: radius 1500") << 1500.0 << 0.0 << 360.0;
QTest::newRow("Full circle: radius -1500") << -1500.0 << 0.0 << 360.0;
QTest::newRow("Full circle: radius 50000") << 50000.0 << 0.0 << 360.0;
QTest::newRow("Full circle: radius -50000") << -50000.0 << 0.0 << 360.0;
QTest::newRow("Full circle: radius 90000") << 90000.0 << 0.0 << 360.0;
QTest::newRow("Full circle: radius -90000") << -90000.0 << 0.0 << 360.0;
QTest::newRow("Arc less than 45 degree, radius 100") << 100.0 << 0.0 << 10.5;
QTest::newRow("Arc less than 45 degree, radius -100") << -100.0 << 0.0 << 10.5;
QTest::newRow("Arc less than 45 degree, radius 150") << 150.0 << 0.0 << 10.5;
QTest::newRow("Arc less than 45 degree, radius -150") << -150.0 << 0.0 << 10.5;
QTest::newRow("Arc less than 45 degree, radius 1500") << 1500.0 << 0.0 << 10.5;
QTest::newRow("Arc less than 45 degree, radius -1500") << -1500.0 << 0.0 << 10.5;
QTest::newRow("Arc less than 45 degree, radius 50000") << 50000.0 << 0.0 << 10.5;
QTest::newRow("Arc less than 45 degree, radius -50000") << -50000.0 << 0.0 << 10.5;
QTest::newRow("Arc less than 45 degree, radius 90000") << 90000.0 << 0.0 << 10.5;
QTest::newRow("Arc less than 45 degree, radius -90000") << -90000.0 << 0.0 << 10.5;
QTest::newRow("Arc 45 degree, radius 100") << 100.0 << 0.0 << 45.0;
QTest::newRow("Arc 45 degree, radius -100") << -100.0 << 0.0 << 45.0;
QTest::newRow("Arc 45 degree, radius 150") << 150.0 << 0.0 << 45.0;
QTest::newRow("Arc 45 degree, radius -150") << -150.0 << 0.0 << 45.0;
QTest::newRow("Arc 45 degree, radius 1500") << 1500.0 << 0.0 << 45.0;
QTest::newRow("Arc 45 degree, radius -1500") << -1500.0 << 0.0 << 45.0;
QTest::newRow("Arc 45 degree, radius 50000") << 50000.0 << 0.0 << 45.0;
QTest::newRow("Arc 45 degree, radius -50000") << -50000.0 << 0.0 << 45.0;
QTest::newRow("Arc 45 degree, radius 90000") << 90000.0 << 0.0 << 45.0;
QTest::newRow("Arc 45 degree, radius -90000") << -90000.0 << 0.0 << 45.0;
QTest::newRow("Arc less than 90 degree, radius 100") << 100.0 << 0.0 << 75.0;
QTest::newRow("Arc less than 90 degree, radius -100") << -100.0 << 0.0 << 75.0;
QTest::newRow("Arc less than 90 degree, radius 150") << 150.0 << 0.0 << 75.0;
QTest::newRow("Arc less than 90 degree, radius -150") << -150.0 << 0.0 << 75.0;
QTest::newRow("Arc less than 90 degree, radius 1500") << 1500.0 << 0.0 << 75.0;
QTest::newRow("Arc less than 90 degree, radius -1500") << -1500.0 << 0.0 << 75.0;
QTest::newRow("Arc less than 90 degree, radius 50000") << 50000.0 << 0.0 << 75.0;
QTest::newRow("Arc less than 90 degree, radius -50000") << -50000.0 << 0.0 << 75.0;
QTest::newRow("Arc less than 90 degree, radius 90000") << 90000.0 << 0.0 << 75.0;
QTest::newRow("Arc less than 90 degree, radius -90000") << -90000.0 << 0.0 << 75.0;
QTest::newRow("Arc 90 degree, radius 100") << 100.0 << 0.0 << 90.0;
QTest::newRow("Arc 90 degree, radius -100") << -100.0 << 0.0 << 90.0;
QTest::newRow("Arc 90 degree, radius 150") << 150.0 << 0.0 << 90.0;
QTest::newRow("Arc 90 degree, radius -150") << -150.0 << 0.0 << 90.0;
QTest::newRow("Arc 90 degree, radius 1500") << 1500.0 << 0.0 << 90.0;
QTest::newRow("Arc 90 degree, radius -1500") << -1500.0 << 0.0 << 90.0;
QTest::newRow("Arc 90 degree, radius 50000") << 50000.0 << 0.0 << 90.0;
QTest::newRow("Arc 90 degree, radius -50000") << -50000.0 << 0.0 << 90.0;
QTest::newRow("Arc 90 degree, radius 90000") << 90000.0 << 0.0 << 90.0;
QTest::newRow("Arc 90 degree, radius -90000") << -90000.0 << 0.0 << 90.0;
QTest::newRow("Arc less than 135 degree, radius 100") << 100.0 << 0.0 << 110.6;
QTest::newRow("Arc less than 135 degree, radius -100") << -100.0 << 0.0 << 110.6;
QTest::newRow("Arc less than 135 degree, radius 150") << 150.0 << 0.0 << 110.6;
QTest::newRow("Arc less than 135 degree, radius -150") << -150.0 << 0.0 << 110.6;
QTest::newRow("Arc less than 135 degree, radius 1500") << 1500.0 << 0.0 << 110.6;
QTest::newRow("Arc less than 135 degree, radius -1500") << -1500.0 << 0.0 << 110.6;
QTest::newRow("Arc less than 135 degree, radius 50000") << 50000.0 << 0.0 << 110.6;
QTest::newRow("Arc less than 135 degree, radius -50000") << -50000.0 << 0.0 << 110.6;
QTest::newRow("Arc less than 135 degree, radius 90000") << 90000.0 << 0.0 << 110.6;
QTest::newRow("Arc less than 135 degree, radius -90000") << -90000.0 << 0.0 << 110.6;
QTest::newRow("Arc 135 degree, radius 100") << 100.0 << 0.0 << 135.0;
QTest::newRow("Arc 135 degree, radius -100") << -100.0 << 0.0 << 135.0;
QTest::newRow("Arc 135 degree, radius 150") << 150.0 << 0.0 << 135.0;
QTest::newRow("Arc 135 degree, radius -150") << -150.0 << 0.0 << 135.0;
QTest::newRow("Arc 135 degree, radius 1500") << 1500.0 << 0.0 << 135.0;
QTest::newRow("Arc 135 degree, radius -1500") << -1500.0 << 0.0 << 135.0;
QTest::newRow("Arc 135 degree, radius 50000") << 50000.0 << 0.0 << 135.0;
QTest::newRow("Arc 135 degree, radius -50000") << -50000.0 << 0.0 << 135.0;
QTest::newRow("Arc 135 degree, radius 90000") << 90000.0 << 0.0 << 135.0;
QTest::newRow("Arc 135 degree, radius -90000") << -90000.0 << 0.0 << 135.0;
QTest::newRow("Arc less than 180 degree, radius 100") << 100.0 << 0.0 << 160.7;
QTest::newRow("Arc less than 180 degree, radius -100") << -100.0 << 0.0 << 160.7;
QTest::newRow("Arc less than 180 degree, radius 150") << 150.0 << 0.0 << 160.7;
QTest::newRow("Arc less than 180 degree, radius -150") << -150.0 << 0.0 << 160.7;
QTest::newRow("Arc less than 180 degree, radius 1500") << 1500.0 << 0.0 << 160.7;
QTest::newRow("Arc less than 180 degree, radius -1500") << -1500.0 << 0.0 << 160.7;
QTest::newRow("Arc less than 180 degree, radius 50000") << 50000.0 << 0.0 << 160.7;
QTest::newRow("Arc less than 180 degree, radius -50000") << -50000.0 << 0.0 << 160.7;
QTest::newRow("Arc less than 180 degree, radius 90000") << 90000.0 << 0.0 << 160.7;
QTest::newRow("Arc less than 180 degree, radius -90000") << -90000.0 << 0.0 << 160.7;
QTest::newRow("Arc 180 degree, radius 100") << 100.0 << 0.0 << 180.0;
QTest::newRow("Arc 180 degree, radius -100") << -100.0 << 0.0 << 180.0;
QTest::newRow("Arc 180 degree, radius 150") << 150.0 << 0.0 << 180.0;
QTest::newRow("Arc 180 degree, radius -150") << -150.0 << 0.0 << 180.0;
QTest::newRow("Arc 180 degree, radius 1500") << 1500.0 << 0.0 << 180.0;
QTest::newRow("Arc 180 degree, radius -1500") << -1500.0 << 0.0 << 180.0;
QTest::newRow("Arc 180 degree, radius 50000") << 50000.0 << 0.0 << 180.0;
QTest::newRow("Arc 180 degree, radius -50000") << -50000.0 << 0.0 << 180.0;
QTest::newRow("Arc 180 degree, radius 90000") << 90000.0 << 0.0 << 180.0;
QTest::newRow("Arc 180 degree, radius -90000") << -90000.0 << 0.0 << 180.0;
QTest::newRow("Arc less than 270 degree, radius 100") << 100.0 << 0.0 << 150.3;
QTest::newRow("Arc less than 270 degree, radius -100") << -100.0 << 0.0 << 150.3;
QTest::newRow("Arc less than 270 degree, radius 150") << 150.0 << 0.0 << 150.3;
QTest::newRow("Arc less than 270 degree, radius -150") << -150.0 << 0.0 << 150.3;
QTest::newRow("Arc less than 270 degree, radius 1500") << 1500.0 << 0.0 << 150.3;
QTest::newRow("Arc less than 270 degree, radius -1500") << -1500.0 << 0.0 << 150.3;
QTest::newRow("Arc less than 270 degree, radius 50000") << 50000.0 << 0.0 << 150.3;
QTest::newRow("Arc less than 270 degree, radius -50000") << -50000.0 << 0.0 << 150.3;
QTest::newRow("Arc less than 270 degree, radius 90000") << 90000.0 << 0.0 << 150.3;
QTest::newRow("Arc less than 270 degree, radius -90000") << -90000.0 << 0.0 << 150.3;
QTest::newRow("Arc 270 degree, radius 100") << 100.0 << 0.0 << 270.0;
QTest::newRow("Arc 270 degree, radius -100") << -100.0 << 0.0 << 270.0;
QTest::newRow("Arc 270 degree, radius 150") << 150.0 << 0.0 << 270.0;
QTest::newRow("Arc 270 degree, radius -150") << -150.0 << 0.0 << 270.0;
QTest::newRow("Arc 270 degree, radius 1500") << 1500.0 << 0.0 << 270.0;
QTest::newRow("Arc 270 degree, radius -1500") << -1500.0 << 0.0 << 270.0;
QTest::newRow("Arc 270 degree, radius 50000") << 50000.0 << 0.0 << 270.0;
QTest::newRow("Arc 270 degree, radius -50000") << -50000.0 << 0.0 << 270.0;
QTest::newRow("Arc 270 degree, radius 90000") << 90000.0 << 0.0 << 270.0;
QTest::newRow("Arc 270 degree, radius -90000") << -90000.0 << 0.0 << 270.0;
QTest::newRow("Arc less than 360 degree, radius 100") << 100.0 << 0.0 << 340.0;
QTest::newRow("Arc less than 360 degree, radius -100") << -100.0 << 0.0 << 340.0;
QTest::newRow("Arc less than 360 degree, radius 150") << 150.0 << 0.0 << 340.0;
QTest::newRow("Arc less than 360 degree, radius -150") << -150.0 << 0.0 << 340.0;
QTest::newRow("Arc less than 360 degree, radius 1500") << 1500.0 << 0.0 << 340.0;
QTest::newRow("Arc less than 360 degree, radius -1500") << -1500.0 << 0.0 << 340.0;
QTest::newRow("Arc less than 360 degree, radius 50000") << 50000.0 << 0.0 << 340.0;
QTest::newRow("Arc less than 360 degree, radius -50000") << -50000.0 << 0.0 << 340.0;
QTest::newRow("Arc less than 360 degree, radius 90000") << 90000.0 << 0.0 << 340.0;
QTest::newRow("Arc less than 360 degree, radius -90000") << -90000.0 << 0.0 << 340.0;
QTest::newRow("Arc start 90 degree, angle 45 degree, radius 100") << 100.0 << 90.0 << 135.0;
QTest::newRow("Arc start 90 degree, angle 45 degree, radius -100") << -100.0 << 90.0 << 135.0;
QTest::newRow("Arc start 90 degree, angle 45 degree, radius 150") << 150.0 << 90.0 << 135.0;
QTest::newRow("Arc start 90 degree, angle 45 degree, radius -150") << -150.0 << 90.0 << 135.0;
QTest::newRow("Arc start 90 degree, angle 45 degree, radius 1500") << 1500.0 << 90.0 << 135.0;
QTest::newRow("Arc start 90 degree, angle 45 degree, radius -1500") << -1500.0 << 90.0 << 135.0;
QTest::newRow("Arc start 90 degree, angle 45 degree, radius 50000") << 50000.0 << 90.0 << 135.0;
QTest::newRow("Arc start 90 degree, angle 45 degree, radius -50000") << -50000.0 << 90.0 << 135.0;
QTest::newRow("Arc start 90 degree, angle 45 degree, radius 90000") << 90000.0 << 90.0 << 135.0;
QTest::newRow("Arc start 90 degree, angle 45 degree, radius -90000") << -90000.0 << 90.0 << 135.0;
}
//---------------------------------------------------------------------------------------------------------------------
void TST_VArc::TestCurvature()
{
QFETCH(qreal, radius);
QFETCH(qreal, startAngle);
QFETCH(qreal, endAngle);
const qreal tolerance = 0.1;
qreal expectedCurvature = 1. / qAbs(radius);
qreal scale = VArc::OptimalApproximationScale(radius, startAngle, endAngle, 0.1);
const VPointF center;
VArc arc(center, radius, startAngle, endAngle);
arc.SetApproximationScale(scale);
qreal curvature = VAbstractCurve::Curvature(arc.GetPoints());
if (scale < 10)
{
QVERIFY(expectedCurvature - curvature <= expectedCurvature * tolerance);
QVector<QPointF> p1 = arc.GetPoints();
arc.SetApproximationScale(10);
QVector<QPointF> p2 = arc.GetPoints();
QVERIFY(p1.size() <= p2.size());
}
}

View file

@ -53,6 +53,8 @@ private slots:
void TestCurveIntersectAxis_data();
void TestCurveIntersectAxis();
void EmptyArc();
void TestCurvature_data();
void TestCurvature();
};
#endif // TST_VARC_H