Now spline can move by mouse.

--HG--
branch : feature
This commit is contained in:
Roman Telezhynskyi 2015-02-12 12:55:09 +02:00
parent fb8dca8452
commit 254d9b7b7a
4 changed files with 208 additions and 1 deletions

View File

@ -744,3 +744,134 @@ void VSpline::SetKcurve(qreal factor)
d->kCurve = factor;
}
}
//---------------------------------------------------------------------------------------------------------------------
int VSpline::Sign(long double ld) const
{
if(qAbs(ld)<0.00000000001)
{
return 0;
}
return (ld>0) ? 1 : -1;
}
//---------------------------------------------------------------------------------------------------------------------
/**
* @brief Cubic Cubic equation solution. Real coefficients case.
*
* This method use method Vieta-Cardano for eval cubic equations.
* Cubic equation write in form x3+a*x2+b*x+c=0.
*
* Output:
* 3 real roots -> then x is filled with them;
* 1 real + 2 complex -> x[0] is real, x[1] is real part of complex roots, x[2] - non-negative imaginary part.
*
* @param x solution array (size 3).
* @param a coefficient
* @param b coefficient
* @param c coefficient
* @return 3 - 3 real roots;
* 1 - 1 real root + 2 complex;
* 2 - 1 real root + complex roots imaginary part is zero (i.e. 2 real roots).
*/
qint32 VSpline::Cubic(QVector<qreal> &x, qreal a, qreal b, qreal c) const
{
//To find cubic equation roots in the case of real coefficients, calculated at the beginning
const qreal q = (pow(a, 2) - 3*b)/9.;
const qreal r = (2*pow(a, 3) - 9*a*b + 27.*c)/54.;
if (pow(r, 2) < pow(q, 3))
{ // equation has three real roots, use formula Vieta
const qreal t = acos(r/sqrt(pow(q, 3)))/3.;
x.insert(0, -2.*sqrt(q)*cos(t)-a/3);
x.insert(1, -2.*sqrt(q)*cos(t + (2*M_2PI/3.)) - a/3.);
x.insert(2, -2.*sqrt(q)*cos(t - (2*M_2PI/3.)) - a/3.);
return(3);
}
else
{ // 1 real root + 2 complex
//Formula Cardano
const qreal aa = -Sign(r)*pow(fabs(r)+sqrt(pow(r, 2)-pow(q, 3)), 1./3.);
const qreal bb = Sign(aa) == 0 ? 0 : q/aa;
x.insert(0, aa+bb-a/3.); // Real root
x.insert(1, (-0.5)*(aa+bb)-a/3.); //Complex root
x.insert(2, (sqrt(3.)*0.5)*fabs(aa-bb)); // Complex root
if (qFuzzyCompare(x.at(2) + 1, 0. + 1))
{
return(2);
}
return(1);
}
}
//---------------------------------------------------------------------------------------------------------------------
QVector<qreal> VSpline::CalcT (qreal curveCoord1, qreal curveCoord2, qreal curveCoord3,
qreal curveCoord4, qreal pointCoord) const
{
const qreal a = -curveCoord1 + 3*curveCoord2 - 3*curveCoord3 + curveCoord4;
const qreal b = 3*curveCoord1 - 6*curveCoord2 + 3*curveCoord3;
const qreal c = -3*curveCoord1 + 3*curveCoord2;
const qreal d = -pointCoord + curveCoord1;
QVector<qreal> t = QVector<qreal>(3, -1);
Cubic(t, b/a, c/a, d/a);
QVector<qreal> retT;
for (int i=0; i < t.size(); ++i)
{
if ( t.at(i) >= 0 && t.at(i) <= 1 )
{
retT.append(t.at(i));
}
}
return retT;
}
//---------------------------------------------------------------------------------------------------------------------
/**
* @brief VSpline::ParamT calculate t coeffient that reprezent point on curve.
*
* Each point that belongs to Cubic Bézier curve can be shown by coefficient in interval [0; 1].
*
* @param pBt point on curve
* @return t coeffient that reprezent this point on curve. Return -1 if point doesn't belongs to curve.
*/
qreal VSpline::ParamT (const QPointF &pBt) const
{
QVector<qreal> ts;
// Calculate t coefficient for each axis
ts += CalcT (GetP1().toQPointF().x(), d->p2.x(), d->p3.x(), GetP4().toQPointF().x(), pBt.x());
ts += CalcT (GetP1().toQPointF().y(), d->p2.y(), d->p3.y(), GetP4().toQPointF().y(), pBt.y());
if(ts.isEmpty())
{
return -1; // We don't have candidates
}
qreal tx = -1;
qreal eps = 3; // Error calculation
// In morst case we will have 6 result in interval [0; 1].
// Here we try find closest to our point.
for (int i=0; i< ts.size(); ++i)
{
const qreal t = ts.at(i);
const QPointF p0 = GetP1().toQPointF();
const QPointF p1 = d->p2;
const QPointF p2 = d->p3;
const QPointF p3 = GetP4().toQPointF();
//The explicit form of the Cubic Bézier curve
const qreal pointX = pow(1-t, 3)*p0.x() + 3*pow(1-t, 2)*t*p1.x() + 3*(1-t)*pow(t, 2)*p2.x() + pow(t, 3)*p3.x();
const qreal pointY = pow(1-t, 3)*p0.y() + 3*pow(1-t, 2)*t*p1.y() + 3*(1-t)*pow(t, 2)*p2.y() + pow(t, 3)*p3.y();
const QLineF line(pBt, QPointF(pointX, pointY));
if (line.length() <= eps)
{
tx = t;
eps = line.length(); //Next point should be even closest
}
}
return tx;
}

View File

@ -71,6 +71,7 @@ public:
// cppcheck-suppress unusedFunction
static QVector<QPointF> SplinePoints(const QPointF &p1, const QPointF &p4, qreal angle1, qreal angle2, qreal kAsm1,
qreal kAsm2, qreal kCurve);
qreal ParamT(const QPointF &pBt) const;
protected:
static QVector<QPointF> GetPoints (const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4 );
private:
@ -80,6 +81,10 @@ private:
qint16 level, QVector<qreal> &px, QVector<qreal> &py);
static qreal CalcSqDistance ( qreal x1, qreal y1, qreal x2, qreal y2);
void CreateName();
QVector<qreal> CalcT(qreal curveCoord1, qreal curveCoord2, qreal curveCoord3, qreal curveCoord4,
qreal pointCoord) const;
qint32 Cubic(QVector<qreal> &x, qreal a, qreal b, qreal c) const;
int Sign(long double ld) const;
};
#endif // VSPLINE_H

View File

@ -31,6 +31,7 @@
#include "../../dialogs/tools/dialogspline.h"
#include "../../undocommands/movespline.h"
#include "../../visualization/vistoolspline.h"
#include <QtMath>
const QString VToolSpline::ToolType = QStringLiteral("simple");
@ -45,7 +46,7 @@ const QString VToolSpline::ToolType = QStringLiteral("simple");
*/
VToolSpline::VToolSpline(VPattern *doc, VContainer *data, quint32 id, const QString color, const Source &typeCreation,
QGraphicsItem *parent)
:VAbstractSpline(doc, data, id, parent)
:VAbstractSpline(doc, data, id, parent), oldPosition()
{
sceneType = SceneObject::Spline;
lineColor = color;
@ -53,6 +54,8 @@ VToolSpline::VToolSpline(VPattern *doc, VContainer *data, quint32 id, const QStr
this->setPen(QPen(Qt::black, qApp->toPixel(qApp->widthHairLine())/factor));
this->setFlag(QGraphicsItem::ItemIsSelectable, true);
this->setFlag(QGraphicsItem::ItemIsFocusable, true);
this->setFlag(QGraphicsItem::ItemIsMovable, true);
//this->setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
this->setAcceptHoverEvents(true);
this->setPath(ToolPath());
@ -355,6 +358,71 @@ void VToolSpline::SaveOptions(QDomElement &tag, QSharedPointer<VGObject> &obj)
doc->SetAttribute(tag, AttrColor, lineColor);
}
//---------------------------------------------------------------------------------------------------------------------
void VToolSpline::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
oldPosition = event->scenePos();
event->accept();
}
VAbstractSpline::mousePressEvent(event);
}
//---------------------------------------------------------------------------------------------------------------------
void VToolSpline::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
// Don't need check if left mouse button was pressed. According to the Qt documentation "If you do receive this
// event, you can be certain that this item also received a mouse press event, and that this item is the current
// mouse grabber.".
// Magic Bezier Drag Equations follow!
// "weight" describes how the influence of the drag should be distributed
// among the handles; 0 = front handle only, 1 = back handle only.
const QSharedPointer<VSpline> spline = VAbstractTool::data.GeometricObject<VSpline>(id);
const qreal t = spline->ParamT(oldPosition);
if (qFloor(t) == -1)
{
return;
}
double weight;
if (t <= 1.0 / 6.0)
{
weight = 0;
}
else if (t <= 0.5)
{
weight = (pow((6 * t - 1) / 2.0, 3)) / 2;
}
else if (t <= 5.0 / 6.0)
{
weight = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
}
else
{
weight = 1;
}
const QPointF delta = event->scenePos() - oldPosition;
const QPointF offset0 = ((1-weight)/(3*t*(1-t)*(1-t))) * delta;
const QPointF offset1 = (weight/(3*t*t*(1-t))) * delta;
const QPointF p2 = spline->GetP2() + offset0;
const QPointF p3 = spline->GetP3() + offset1;
oldPosition = event->scenePos(); // Now mouse here
VSpline spl = VSpline(spline->GetP1(), p2, p3, spline->GetP4(), spline->GetKcurve());
MoveSpline *moveSpl = new MoveSpline(doc, spline.data(), spl, id, this->scene());
connect(moveSpl, &MoveSpline::NeedLiteParsing, doc, &VPattern::LiteParseTree);
qApp->getUndoStack()->push(moveSpl);
}
//---------------------------------------------------------------------------------------------------------------------
/**
* @brief RefreshGeometry refresh item on scene.

View File

@ -63,9 +63,12 @@ protected:
virtual void RemoveReferens();
virtual void SaveDialog(QDomElement &domElement);
virtual void SaveOptions(QDomElement &tag, QSharedPointer<VGObject> &obj);
virtual void mousePressEvent(QGraphicsSceneMouseEvent * event);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent * event);
private:
Q_DISABLE_COPY(VToolSpline)
void RefreshGeometry ();
QPointF oldPosition;
};
#endif // VTOOLSPLINE_H