Improve cutting spline and curved path.

Loose cutting restrictions.
This commit is contained in:
Roman Telezhynskyi 2023-11-22 14:58:00 +02:00
parent aee93b01b9
commit c7379122a8
9 changed files with 132 additions and 132 deletions

View file

@ -32,14 +32,14 @@
#include <QLineF> #include <QLineF>
#include <QMessageLogger> #include <QMessageLogger>
#include <QPoint> #include <QPoint>
#include <QtDebug>
#include <QtConcurrent> #include <QtConcurrent>
#include <QtDebug>
#include "../vmisc/def.h"
#include "../vmisc/vmath.h"
#include "../vgeometry/vpointf.h"
#include "../vmisc/vabstractapplication.h"
#include "../ifc/exception/vexception.h" #include "../ifc/exception/vexception.h"
#include "../vgeometry/vpointf.h"
#include "../vmisc/def.h"
#include "../vmisc/vabstractapplication.h"
#include "../vmisc/vmath.h"
namespace namespace
{ {
@ -74,8 +74,8 @@ inline auto CalcSqDistance(qreal x1, qreal y1, qreal x2, qreal y2) -> qreal
* @param points spline points coordinates. * @param points spline points coordinates.
* @param approximationScale curve approximation scale. * @param approximationScale curve approximation scale.
*/ */
auto PointBezier_r(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4, auto PointBezier_r(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4, qint16 level,
qint16 level, QVector<QPointF> points, qreal approximationScale) -> QVector<QPointF> QVector<QPointF> points, qreal approximationScale) -> QVector<QPointF>
{ {
if (points.size() >= 2) if (points.size() >= 2)
{ {
@ -91,7 +91,10 @@ auto PointBezier_r(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, q
const double curve_collinearity_epsilon = 1e-30; const double curve_collinearity_epsilon = 1e-30;
const double curve_angle_tolerance_epsilon = 0.01; const double curve_angle_tolerance_epsilon = 0.01;
const double m_angle_tolerance = 0.0; const double m_angle_tolerance = 0.0;
enum curve_recursion_limit_e { curve_recursion_limit = 32 }; enum curve_recursion_limit_e
{
curve_recursion_limit = 32
};
const double m_cusp_limit = 0.0; const double m_cusp_limit = 0.0;
double m_approximation_scale = approximationScale; double m_approximation_scale = approximationScale;
@ -373,7 +376,8 @@ auto PointBezier_r(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, q
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
VAbstractCubicBezier::VAbstractCubicBezier(const GOType &type, const quint32 &idObject, const Draw &mode) VAbstractCubicBezier::VAbstractCubicBezier(const GOType &type, const quint32 &idObject, const Draw &mode)
: VAbstractBezier(type, idObject, mode) : VAbstractBezier(type, idObject, mode)
{} {
}
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
auto VAbstractCubicBezier::operator=(const VAbstractCubicBezier &curve) -> VAbstractCubicBezier & auto VAbstractCubicBezier::operator=(const VAbstractCubicBezier &curve) -> VAbstractCubicBezier &
@ -397,47 +401,44 @@ auto VAbstractCubicBezier::operator=(const VAbstractCubicBezier &curve) -> VAbst
* @param pointName cutting point name. * @param pointName cutting point name.
* @return point of cutting. This point is forth point of first spline and first point of second spline. * @return point of cutting. This point is forth point of first spline and first point of second spline.
*/ */
auto VAbstractCubicBezier::CutSpline(qreal length, QPointF &spl1p2, QPointF &spl1p3, QPointF &spl2p2, auto VAbstractCubicBezier::CutSpline(qreal length, QPointF &spl1p2, QPointF &spl1p3, QPointF &spl2p2, QPointF &spl2p3,
QPointF &spl2p3, const QString &pointName) const -> QPointF const QString &pointName) const -> QPointF
{ {
// Always need return two splines, so we must correct wrong length. // Always need return two splines, so we must correct wrong length.
const qreal fullLength = GetLength(); const qreal fullLength = GetLength();
if (fullLength <= minLength) if (qFuzzyIsNull(fullLength))
{ {
spl1p2 = spl1p3 = spl2p2 = spl2p3 = QPointF(); spl1p2 = spl1p3 = spl2p2 = spl2p3 = static_cast<QPointF>(GetP1());
const QString errorMsg = QObject::tr("Unable to cut curve '%1'. The curve is too short.").arg(name()); return static_cast<QPointF>(GetP1());
VAbstractApplication::VApp()->IsPedantic() ? throw VException(errorMsg) :
qWarning() << VAbstractApplication::warningMessageSignature + errorMsg;
return {};
} }
const qreal maxLength = fullLength - minLength; if (length < 0)
if (length < minLength)
{ {
length = minLength; length = fullLength + length;
}
if (length < 0)
{
QString errorMsg; QString errorMsg;
if (not pointName.isEmpty()) if (not pointName.isEmpty())
{ {
errorMsg = QObject::tr("Curve '%1'. Length of a cut segment (%2) is too small. Optimize it to minimal " errorMsg = QObject::tr("Curve '%1'. Length of a cut segment (%2) is too small. Optimize it to minimal "
"value.").arg(name(), pointName); "value.")
.arg(name(), pointName);
} }
else else
{ {
errorMsg = QObject::tr("Curve '%1'. Length of a cut segment is too small. Optimize it to minimal value.") errorMsg = QObject::tr("Curve '%1'. Length of a cut segment is too small. Optimize it to minimal value.")
.arg(name()); .arg(name());
} }
VAbstractApplication::VApp()->IsPedantic() ? throw VException(errorMsg) : VAbstractApplication::VApp()->IsPedantic()
qWarning() << VAbstractApplication::warningMessageSignature + errorMsg; ? throw VException(errorMsg)
: qWarning() << VAbstractApplication::warningMessageSignature + errorMsg;
} }
else if (length > maxLength) else if (length > fullLength)
{ {
length = maxLength;
QString errorMsg; QString errorMsg;
if (not pointName.isEmpty()) if (not pointName.isEmpty())
{ {
@ -449,10 +450,13 @@ auto VAbstractCubicBezier::CutSpline(qreal length, QPointF &spl1p2, QPointF &spl
errorMsg = QObject::tr("Curve '%1'. Length of a cut segment is too big. Optimize it to maximal value.") errorMsg = QObject::tr("Curve '%1'. Length of a cut segment is too big. Optimize it to maximal value.")
.arg(name()); .arg(name());
} }
VAbstractApplication::VApp()->IsPedantic() ? throw VException(errorMsg) : VAbstractApplication::VApp()->IsPedantic()
qWarning() << VAbstractApplication::warningMessageSignature + errorMsg; ? throw VException(errorMsg)
: qWarning() << VAbstractApplication::warningMessageSignature + errorMsg;
} }
length = qBound(0.0, length, fullLength);
const qreal parT = GetParmT(length); const qreal parT = GetParmT(length);
QLineF seg1_2(static_cast<QPointF>(GetP1()), GetControlPoint1()); QLineF seg1_2(static_cast<QPointF>(GetP1()), GetControlPoint1());
@ -535,8 +539,7 @@ auto VAbstractCubicBezier::GetParmT(qreal length) const -> qreal
} }
splLength > length ? parT -= step : parT += step; splLength > length ? parT -= step : parT += step;
} } while (qAbs(splLength - length) > eps);
while (qAbs(splLength - length) > eps);
return parT; return parT;
} }
@ -581,8 +584,8 @@ auto VAbstractCubicBezier::GetCubicBezierPoints(const QPointF &p1, const QPointF
{ {
QVector<QPointF> pvector; QVector<QPointF> pvector;
pvector.append(p1); pvector.append(p1);
pvector = PointBezier_r(p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y(), p4.x(), p4.y(), 0, pvector, pvector =
approximationScale); PointBezier_r(p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y(), p4.x(), p4.y(), 0, pvector, approximationScale);
pvector.append(p4); pvector.append(p4);
return pvector; return pvector;
} }

View file

@ -179,25 +179,20 @@ auto VAbstractCubicBezierPath::CutSplinePath(qreal length, qint32 &p1, qint32 &p
// Always need return two spline paths, so we must correct wrong length. // Always need return two spline paths, so we must correct wrong length.
qreal fullLength = GetLength(); qreal fullLength = GetLength();
if (fullLength <= minLength) if (qFuzzyIsNull(fullLength))
{ {
p1 = p2 = -1;
spl1p2 = spl1p3 = spl2p2 = spl2p3 = QPointF(); spl1p2 = spl1p3 = spl2p2 = spl2p3 = QPointF();
const QString errorMsg = tr("Unable to cut curve '%1'. The curve is too short.").arg(name());
VAbstractApplication::VApp()->IsPedantic()
? throw VException(errorMsg)
: qWarning() << VAbstractApplication::warningMessageSignature + errorMsg;
return {}; return {};
} }
const qreal maxLength = fullLength - minLength; if (length < 0)
if (length < minLength)
{ {
length = minLength; length = fullLength + length;
}
if (length < 0)
{
QString errorMsg; QString errorMsg;
if (not pointName.isEmpty()) if (not pointName.isEmpty())
{ {
@ -213,10 +208,8 @@ auto VAbstractCubicBezierPath::CutSplinePath(qreal length, qint32 &p1, qint32 &p
? throw VException(errorMsg) ? throw VException(errorMsg)
: qWarning() << VAbstractApplication::warningMessageSignature + errorMsg; : qWarning() << VAbstractApplication::warningMessageSignature + errorMsg;
} }
else if (length > maxLength) else if (length > fullLength)
{ {
length = maxLength;
QString errorMsg; QString errorMsg;
if (not pointName.isEmpty()) if (not pointName.isEmpty())
{ {
@ -232,13 +225,15 @@ auto VAbstractCubicBezierPath::CutSplinePath(qreal length, qint32 &p1, qint32 &p
: qWarning() << VAbstractApplication::warningMessageSignature + errorMsg; : qWarning() << VAbstractApplication::warningMessageSignature + errorMsg;
} }
length = qBound(0.0, length, fullLength);
fullLength = 0; fullLength = 0;
for (qint32 i = 1; i <= CountSubSpl(); ++i) for (qint32 i = 1; i <= CountSubSpl(); ++i)
{ {
const VSpline spl = GetSpline(i); const VSpline spl = GetSpline(i);
const qreal splLength = spl.GetLength(); const qreal splLength = spl.GetLength();
fullLength += splLength; fullLength += splLength;
if (fullLength > length) if (fullLength >= length)
{ {
p1 = i - 1; p1 = i - 1;
p2 = i; p2 = i;

View file

@ -112,8 +112,6 @@ public:
void SetAliasSuffix(const QString &aliasSuffix) override; void SetAliasSuffix(const QString &aliasSuffix) override;
static constexpr qreal minLength = MmToPixel(1.);
static auto Curvature(const QVector<QPointF> &vertices) -> double; static auto Curvature(const QVector<QPointF> &vertices) -> double;
protected: protected:

View file

@ -426,11 +426,8 @@ void DialogCutSpline::ShowDialog(bool click)
const QSharedPointer<VAbstractCubicBezier> spl = data->GeometricObject<VAbstractCubicBezier>(getSplineId()); const QSharedPointer<VAbstractCubicBezier> spl = data->GeometricObject<VAbstractCubicBezier>(getSplineId());
QPointF p = spl->ClosestPoint(scene->getScenePos()); QPointF p = spl->ClosestPoint(scene->getScenePos());
qreal len = spl->GetLengthByPoint(p); qreal len = spl->GetLengthByPoint(p);
if (len > 0)
{
SetFormula(QString::number(FromPixel(len, *data->GetPatternUnit()))); SetFormula(QString::number(FromPixel(len, *data->GetPatternUnit())));
} }
}
FinishCreating(); FinishCreating();
} }

View file

@ -427,11 +427,8 @@ void DialogCutSplinePath::ShowDialog(bool click)
data->GeometricObject<VAbstractCubicBezierPath>(getSplinePathId()); data->GeometricObject<VAbstractCubicBezierPath>(getSplinePathId());
QPointF p = curve->ClosestPoint(scene->getScenePos()); QPointF p = curve->ClosestPoint(scene->getScenePos());
qreal len = curve->GetLengthByPoint(p); qreal len = curve->GetLengthByPoint(p);
if (len > 0)
{
SetFormula(QString::number(FromPixel(len, *data->GetPatternUnit()))); SetFormula(QString::number(FromPixel(len, *data->GetPatternUnit())));
} }
}
FinishCreating(); FinishCreating();
} }

View file

@ -69,7 +69,7 @@ void VisToolCutSpline::RefreshGeometry()
const auto spl = GetData()->GeometricObject<VAbstractCubicBezier>(m_splineId); const auto spl = GetData()->GeometricObject<VAbstractCubicBezier>(m_splineId);
DrawPath(this, spl->GetPath(), spl->DirectionArrows(), LineStyle(), Qt::RoundCap); DrawPath(this, spl->GetPath(), spl->DirectionArrows(), LineStyle(), Qt::RoundCap);
if (not qFuzzyIsNull(m_length)) if (!qIsInf(m_length))
{ {
QPointF spl1p2; QPointF spl1p2;
QPointF spl1p3; QPointF spl1p3;

View file

@ -29,12 +29,12 @@
#ifndef VISTOOLCUTSPLINE_H #ifndef VISTOOLCUTSPLINE_H
#define VISTOOLCUTSPLINE_H #define VISTOOLCUTSPLINE_H
#include <QGraphicsItem> #include <QGraphicsItem>
#include <QMetaObject> #include <QMetaObject>
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QtGlobal> #include <QtGlobal>
#include <limits>
#include "../vmisc/def.h" #include "../vmisc/def.h"
#include "vispath.h" #include "vispath.h"
@ -42,6 +42,7 @@
class VisToolCutSpline final : public VisPath class VisToolCutSpline final : public VisPath
{ {
Q_OBJECT // NOLINT Q_OBJECT // NOLINT
public: public:
explicit VisToolCutSpline(const VContainer *data, QGraphicsItem *parent = nullptr); explicit VisToolCutSpline(const VContainer *data, QGraphicsItem *parent = nullptr);
~VisToolCutSpline() override = default; ~VisToolCutSpline() override = default;
@ -53,13 +54,17 @@ public:
void SetLength(const QString &expression); void SetLength(const QString &expression);
auto type() const -> int override { return Type; } auto type() const -> int override { return Type; }
enum {Type = UserType + static_cast<int>(Vis::ToolCutSpline)}; enum
{
Type = UserType + static_cast<int>(Vis::ToolCutSpline)
};
private: private:
Q_DISABLE_COPY_MOVE(VisToolCutSpline) // NOLINT Q_DISABLE_COPY_MOVE(VisToolCutSpline) // NOLINT
VScaledEllipse *m_point{nullptr}; VScaledEllipse *m_point{nullptr};
VCurvePathItem *m_spl1{nullptr}; VCurvePathItem *m_spl1{nullptr};
VCurvePathItem *m_spl2{nullptr}; VCurvePathItem *m_spl2{nullptr};
qreal m_length{0}; qreal m_length{std::numeric_limits<qreal>::infinity()};
quint32 m_splineId{NULL_ID}; quint32 m_splineId{NULL_ID};
}; };

View file

@ -68,7 +68,7 @@ void VisToolCutSplinePath::RefreshGeometry()
const auto splPath = GetData()->GeometricObject<VAbstractCubicBezierPath>(m_splinePathId); const auto splPath = GetData()->GeometricObject<VAbstractCubicBezierPath>(m_splinePathId);
DrawPath(this, splPath->GetPath(), splPath->DirectionArrows(), LineStyle(), Qt::RoundCap); DrawPath(this, splPath->GetPath(), splPath->DirectionArrows(), LineStyle(), Qt::RoundCap);
if (not qFuzzyIsNull(m_length)) if (!qIsInf(m_length))
{ {
VSplinePath *spPath1 = nullptr; VSplinePath *spPath1 = nullptr;
VSplinePath *spPath2 = nullptr; VSplinePath *spPath2 = nullptr;

View file

@ -29,12 +29,12 @@
#ifndef VISTOOLCUTSPLINEPATH_H #ifndef VISTOOLCUTSPLINEPATH_H
#define VISTOOLCUTSPLINEPATH_H #define VISTOOLCUTSPLINEPATH_H
#include <QGraphicsItem> #include <QGraphicsItem>
#include <QMetaObject> #include <QMetaObject>
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QtGlobal> #include <QtGlobal>
#include <limits>
#include "../vmisc/def.h" #include "../vmisc/def.h"
#include "vispath.h" #include "vispath.h"
@ -42,6 +42,7 @@
class VisToolCutSplinePath final : public VisPath class VisToolCutSplinePath final : public VisPath
{ {
Q_OBJECT // NOLINT Q_OBJECT // NOLINT
public: public:
explicit VisToolCutSplinePath(const VContainer *data, QGraphicsItem *parent = nullptr); explicit VisToolCutSplinePath(const VContainer *data, QGraphicsItem *parent = nullptr);
~VisToolCutSplinePath() override = default; ~VisToolCutSplinePath() override = default;
@ -53,13 +54,17 @@ public:
void SetLength(const QString &expression); void SetLength(const QString &expression);
auto type() const -> int override { return Type; } auto type() const -> int override { return Type; }
enum {Type = UserType + static_cast<int>(Vis::ToolCutSpline)}; enum
{
Type = UserType + static_cast<int>(Vis::ToolCutSpline)
};
private: private:
Q_DISABLE_COPY_MOVE(VisToolCutSplinePath) // NOLINT Q_DISABLE_COPY_MOVE(VisToolCutSplinePath) // NOLINT
VScaledEllipse *m_point{nullptr}; VScaledEllipse *m_point{nullptr};
VCurvePathItem *m_splPath1{nullptr}; VCurvePathItem *m_splPath1{nullptr};
VCurvePathItem *m_splPath2{nullptr}; VCurvePathItem *m_splPath2{nullptr};
qreal m_length{0}; qreal m_length{std::numeric_limits<qreal>::infinity()};
quint32 m_splinePathId{NULL_ID}; quint32 m_splinePathId{NULL_ID};
}; };