18#include "beziercurve.h"
22#include <QXmlStreamWriter>
25#include <QPainterPath>
27#include "pencilerror.h"
30BezierCurve::BezierCurve()
34BezierCurve::BezierCurve(
const QList<QPointF>& pointList,
bool smooth)
37 for (
int i = 0; i < pointList.
size(); i++)
41 createCurve(pointList, pressureList, smooth);
47 int n = pointList.
size();
53 for (
int i=0; i<n; i++) { markList.
append(
false); }
56 BezierCurve::simplify(tol, pointList, 0, n-1, markList);
60 for(
int i=0; i<n; i++)
62 if (markList.
at(i) ==
true)
64 simplifiedPointList.
append(pointList.
at(i));
65 if (pressureList.
size() > i)
69 qreal currentPressure = pressureList.
at(i);
70 if (currentPressure < 0.1) {
71 currentPressure = 0.1;
73 simplifiedPressureList.
append(currentPressure);
77 simplifiedPressureList.
append(0.5);
83 createCurve(simplifiedPointList, simplifiedPressureList, smooth);
91 xmlStream.
writeAttribute(
"variableWidth", variableWidth ?
"true" :
"false" );
93 xmlStream.
writeAttribute(
"invisible", invisible ?
"true" :
"false" );
100 int errorLocation = -1;
101 for (
int i = 0; i < c1.
size() ; i++ )
111 if ( errorLocation < 0 && xmlStream.
hasError() )
119 if ( xmlStream.
hasError() && errorLocation >= 0 )
122 debugInfo <<
"BezierCurve::createDomElement";
123 debugInfo <<
QString(
"width = %1").
arg(width);
124 debugInfo <<
QString(
"variableWidth = %1").
arg(
"variableWidth");
125 debugInfo <<
QString(
"feather = %1").
arg(feather);
126 debugInfo <<
QString(
"invisible = %1").
arg(invisible);
127 debugInfo <<
QString(
"filled = %1").
arg(mFilled);
128 debugInfo <<
QString(
"colorNumber = %1").
arg(colorNumber);
129 debugInfo <<
QString(
"originX = %1").
arg(origin.
x());
130 debugInfo <<
QString(
"originY = %1").
arg(origin.
y());
131 debugInfo <<
QString(
"originPressure = %1").
arg(pressure.
at(0));
132 debugInfo <<
QString(
"- segmentTag[%1] has failed to write").
arg(errorLocation);
133 debugInfo <<
QString(
" c1x = %1").
arg(c1.
at(errorLocation).
x());
134 debugInfo <<
QString(
" c1y = %1").
arg(c1.
at(errorLocation).
y());
135 debugInfo <<
QString(
" c2x = %1").
arg(c2.
at(errorLocation).
x());
136 debugInfo <<
QString(
" c2y = %1").
arg(c2.
at(errorLocation).
y());
137 debugInfo <<
QString(
" vx = %1").
arg(vertex.
at(errorLocation).
x());
138 debugInfo <<
QString(
" vy = %1").
arg(vertex.
at(errorLocation).
y());
139 debugInfo <<
QString(
" pressure = %1").
arg(pressure.
at(errorLocation + 1));
141 return Status(Status::FAIL, debugInfo);
147void BezierCurve::loadDomElement(
const QDomElement& element)
150 variableWidth = (element.
attribute(
"variableWidth") ==
"1") || (element.
attribute(
"variableWidth") ==
"true");
152 invisible = (element.
attribute(
"invisible") ==
"1") || (element.
attribute(
"invisible") ==
"true");
153 mFilled = (element.
attribute(
"filled") ==
"1") || (element.
attribute(
"filled") ==
"true");
154 if (width == 0) invisible =
true;
162 while (!segmentTag.
isNull())
165 if (!segmentElement.
isNull())
167 if (segmentElement.
tagName() ==
"segment")
173 appendCubic(c1Point, c2Point, vertexPoint, pressureValue);
181void BezierCurve::setOrigin(
const QPointF& point)
186void BezierCurve::setOrigin(
const QPointF& point,
const qreal& pressureValue,
const bool& trueOrFalse)
189 pressure[0] = pressureValue;
190 selected[0] = trueOrFalse;
193void BezierCurve::setC1(
int i,
const QPointF& point)
195 if ( i >= 0 || i < c1.
size() )
201 qDebug() <<
"BezierCurve::setC1! index out of bounds:" << i;
205void BezierCurve::setC2(
int i,
const QPointF& point)
207 if ( i >= 0 || i < c2.
size() )
213 qDebug() <<
"BezierCurve::setC2! index out of bounds:" << i;
217void BezierCurve::setVertex(
int i,
const QPointF& point)
223 else if (i >= 0 && i < vertex.
size())
229 qDebug() <<
"BezierCurve::setVertex! index out of bounds:" << i;
233void BezierCurve::setLastVertex(
const QPointF& point)
235 if (vertex.
size() > 0)
237 vertex[vertex.
size()-1] = point;
241 qDebug() <<
"BezierCurve::setLastVertex! curve has less than 2 vertices";
246void BezierCurve::setWidth(qreal desiredWidth)
248 width = desiredWidth;
251void BezierCurve::setFeather(qreal desiredFeather)
253 feather = desiredFeather;
256void BezierCurve::setVariableWidth(
bool YesOrNo)
258 variableWidth = YesOrNo;
261void BezierCurve::setInvisibility(
bool YesOrNo)
266void BezierCurve::setSelected(
int i,
bool YesOrNo)
268 selected[i+1] = YesOrNo;
285 if (isSelected(-1)) { transformedCurve.setOrigin(transformation.
map(origin)); }
286 for(
int i=0; i< vertex.
size(); i++)
288 if (isSelected(i-1)) { transformedCurve.setC1(i, transformation.
map(c1.
at(i))); }
291 transformedCurve.setC2(i, transformation.
map(c2.
at(i)));
292 transformedCurve.setVertex(i, transformation.
map(vertex.
at(i)));
312 return transformedCurve;
315void BezierCurve::transform(
QTransform transformation)
317 if (isSelected(-1)) setOrigin( transformation.
map(origin) );
318 for(
int i=0; i< vertex.
size(); i++)
320 if (isSelected(i-1)) c1[i] = transformation.
map(c1.
at(i));
323 c2[i] = transformation.
map(c2.
at(i));
324 vertex[i] = transformation.
map(vertex.
at(i));
330void BezierCurve::appendCubic(
const QPointF& c1Point,
const QPointF& c2Point,
const QPointF& vertexPoint, qreal pressureValue)
334 vertex.
append(vertexPoint);
335 pressure.
append(pressureValue);
339void BezierCurve::addPoint(
int position,
const QPointF point)
341 if ( position > -1 && position < getVertexSize() )
343 QPointF v1 = getVertex(position-1);
344 QPointF v2 = getVertex(position);
348 c1[position] = point + 0.2*(v2-v1);
349 c2[position] = v2 + (c2o-v2)*(0.5);
351 c1.
insert(position, v1 + (c1o-v1)*(0.5) );
352 c2.insert(position, point - 0.2*(v2-v1));
353 vertex.
insert(position, point);
354 pressure.
insert(position, getPressure(position));
355 selected.
insert(position, isSelected(position) && isSelected(position-1));
361 qDebug() <<
"Error BezierCurve::addPoint(int, QPointF)";
365void BezierCurve::addPoint(
int position,
const qreal fraction)
370 if ( position > -1 && position < getVertexSize() )
372 QPointF vA = getVertex(position-1);
373 QPointF vB = getVertex(position);
376 QPointF c12 = (1-fraction)*c1o + fraction*c2o;
377 QPointF cA1 = (1-fraction)*vA + fraction*c1o;
378 QPointF cB2 = (1-fraction)*c2o + fraction*vB;
379 QPointF cA2 = (1-fraction)*cA1 + fraction*c12;
380 QPointF cB1 = (1-fraction)*c12 + fraction*cB2;
381 QPointF vM = (1-fraction)*cA2 + fraction*cB1;
383 setC1(position, cB1);
384 setC2(position, cB2);
387 c2.insert(position, cA2);
388 vertex.
insert(position, vM);
389 pressure.
insert(position, getPressure(position));
390 selected.
insert(position, isSelected(position) && isSelected(position-1));
396 qDebug() <<
"Error BezierCurve::addPoint(int, qreal)";
400void BezierCurve::removeVertex(
int i)
402 int n = vertex.
size();
407 origin = vertex.
at(0);
432void BezierCurve::drawPath(
QPainter& painter,
const Object&
object,
QTransform transformation,
bool simplified,
bool showThinLines )
434 QColor color =
object.getColor(colorNumber).color;
437 if (isPartlySelected()) { myCurve = (transformed(transformation)); }
438 else { myCurve = *
this; }
440 if ( variableWidth && !simplified && !invisible)
444 painter.
drawPath(myCurve.getStrokedPath());
448 qreal renderedWidth = width;
454 renderedWidth = fabs(renderedWidth);
488 color =
QColor(100,150,255);
491 lineWidth = fabs(lineWidth);
493 if (isSelected()) painter.
drawPath(myCurve.getSimplePath());
496 for(
int i=-1; i< vertex.
size(); i++)
521 for(
int i=0; i<vertex.
size(); i++)
533 for(
int i=0; i<vertex.
size(); i++)
542 return getStrokedPath( width );
547 return getStrokedPath(width,
true);
551QPainterPath BezierCurve::getStrokedPath(qreal width,
bool usePressure)
554 QPointF tangentVec, normalVec, normalVec2, normalVec2_1, normalVec2_2;
555 qreal width2 = width;
556 int n = vertex.
size();
559 normalVec =
QPointF(-(c1.
at(0) - origin).y(), (c1.
at(0) - origin).x());
560 normalise(normalVec);
561 if (usePressure) width2 = width * 0.5 * pressure.
at(0);
562 if (n==1 && width2 == 0.0) width2 = 0.15 * width;
563 path.
moveTo(origin + width2*normalVec);
564 for(
int i=0; i<n; i++)
568 normalVec2 =
QPointF(-(vertex.
at(i) - c2.at(i)).y(), (vertex.
at(i) - c2.at(i)).x());
572 normalVec2_1 =
QPointF(-(vertex.
at(i) - c2.at(i)).y(), (vertex.
at(i) - c2.at(i)).x());
573 normalise(normalVec2_1);
574 normalVec2_2 =
QPointF(-(c1.
at(i+1) - vertex.
at(i)).y(), (c1.
at(i+1) - vertex.
at(i)).x());
575 normalise(normalVec2_2);
576 normalVec2 = normalVec2_1 + normalVec2_2;
578 normalise(normalVec2);
579 if (usePressure) width2 = width * 0.5 * pressure.
at(i);
580 if (n==1 && width2 == 0.0) width2 = 0.15 * width;
582 path.
cubicTo(c1.
at(i) + width2*normalVec, c2.at(i) + width2*normalVec2, vertex.
at(i) + width2*normalVec2);
585 normalVec = normalVec2;
587 if (usePressure) width2 = width * 0.5 * pressure.
at(n-1);
588 if (n==1 && width2 == 0.0) width2 = 0.15 * width;
591 tangentVec = (vertex.
at(n-1)-c2.at(n-1));
592 normalise(tangentVec);
593 path.
cubicTo(vertex.
at(n-1) + width2*(normalVec+1.8*tangentVec), vertex.
at(n-1) + width2*(-normalVec+1.8*tangentVec), vertex.
at(n-1) - width2*normalVec);
595 for(
int i=n-2; i>=0; i--)
597 normalVec2_1 =
QPointF((vertex.
at(i) - c1.
at(i+1)).y(), -(vertex.
at(i) - c1.
at(i+1)).x());
598 normalise(normalVec2_1);
599 normalVec2_2 =
QPointF((c2.at(i) - vertex.
at(i)).y(), -(c2.at(i) - vertex.
at(i)).x());
600 normalise(normalVec2_2);
601 normalVec2 = normalVec2_1 + normalVec2_2;
602 normalise(normalVec2);
603 if (usePressure) width2 = width * 0.5 * pressure.
at(i);
604 if (n==1 && width2 == 0.0) width2 = 0.15 * width;
605 path.
cubicTo(c2.at(i+1) - width2*normalVec, c1.
at(i+1) - width2*normalVec2, vertex.
at(i) - width2*normalVec2);
606 normalVec = normalVec2;
608 normalVec2 =
QPointF((origin - c1.
at(0)).y(), -(origin - c1.
at(0)).x());
609 normalise(normalVec2);
610 if (usePressure) width2 = width * 0.5 * pressure.
at(0);
611 if (n==1 && width2 == 0.0) width2 = 0.15 * width;
612 path.
cubicTo(c2.at(0) - width2*normalVec, c1.
at(0) - width2*normalVec2, origin - width2*normalVec2);
614 tangentVec = (origin-c1.
at(0));
615 normalise(tangentVec);
616 path.
cubicTo(origin + width2*(-normalVec+1.8*tangentVec), origin + width2*(normalVec+1.8*tangentVec), origin + width2*normalVec);
622QRectF BezierCurve::getBoundingRect()
624 qreal radius = getWidth() / 2;
631 int n = pointList.
size();
635 while (c2.size()>0) c2.removeAt(0);
640 setOrigin( pointList.
at(0) );
642 pressure.
append(pressureList.
at(0));
647 c2.append(pointList.
at(p));
649 pressure.
append(pressureList.
at(p));
662void BezierCurve::smoothCurve()
664 QPointF c1, c2, c2old, tangentVec, normalVec;
665 int n = vertex.
size();
667 for(
int p=0; p<n-1; p++)
670 QPointF Dprev = getVertex(p-1);
671 QPointF Dnext = getVertex(p+1);
672 qreal L1 = mLength(D-Dprev);
673 qreal L2 = mLength(D-Dnext);
675 tangentVec = 0.4*(Dnext - Dprev);
676 normalVec =
QPointF(-tangentVec.
y(), tangentVec.
x())/eLength(tangentVec);
677 if ( ((D-Dprev).x()*(D-Dnext).x()+(D-Dprev).y()*(D-Dnext).y())/(1.0*L1*L2) < 0 )
680 c1 = D - tangentVec*(L1+0.0)/(L1+L2);
681 c2 = D + tangentVec*(L2+0.0)/(L1+L2);
686 c1 = 0.6*D + 0.4*Dprev;
687 c2 = 0.6*D + 0.4*Dnext;
692 c2old = 0.5*(vertex.
at(0)+c1);
702 this->c1[n-1] = c2old;
703 this->c2[n-1] = 0.5*(c2old+vertex.
at(n-1));
720 for (
int i = j + 1; i < k - 1; i++)
724 double Vijx = Vij.
x();
725 double Vijy = Vij.
y();
726 double Vjkx = Vjk.
x();
727 double Vjky = Vjk.
y();
728 double dv = (Vjkx * Vjkx + Vjky * Vjky);
731 dv = sqrt( Vijx*Vijx+Vijy*Vijy - pow(Vijx*Vjkx+Vijy*Vjky,2)/dv );
750 simplify(tol, inputList, j, maxi, markList);
751 simplify(tol, inputList, maxi, k, markList);
757qreal BezierCurve::eLength(
const QPointF point)
759 qreal result = sqrt( point.
x()*point.
x() + point.
y()*point.
y() );
764qreal BezierCurve::mLength(
const QPointF point)
766 qreal result = qAbs(point.
x()) + qAbs(point.
y());
767 if (result == 0.0) result = 1.0;
771void BezierCurve::normalise(
QPointF& point)
773 qreal length = eLength(point);
776 point = point/length;
785 Q = curve.getVertex(i-1);
786 qreal distMin = eLength(Q-P);
789 for(
int k=1; k<=nSteps; k++)
791 qreal s = (k+0.0)/nSteps;
792 Q = curve.getPointOnCubic(i, s);
793 qreal dist = eLength(Q-P);
805QPointF BezierCurve::getPointOnCubic(
int i, qreal t)
807 return (1.0-t)*(1.0-t)*(1.0-t)*getVertex(i-1)
808 + 3*t*(1.0-t)*(1.0-t)*getC1(i)
809 + 3*t*t*(1.0-t)*getC2(i)
810 + t*t*t*getVertex(i);
814bool BezierCurve::intersects(
QPointF point, qreal distance)
817 if ( getStrokedPath(distance,
false).contains(point) )
825bool BezierCurve::intersects(
QRectF rectangle)
828 if ( getSimplePath().controlPointRect().intersects(rectangle))
830 for(
int i=0; i<vertex.
size(); i++)
832 if ( rectangle.
contains( getVertex(i) ) )
return true;
847 P1 = curve1.getVertex(i1-1);
848 Q1 = curve1.getVertex(i1);
849 P2 = curve2.getVertex(i2-1);
850 Q2 = curve2.getVertex(i2);
862 QPointF* cubicIntersection = &intersectionPoint;
863#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
875 P1 = curve1.getVertex(i1-1);
876 for(
int i=1; i<=nSteps; i++)
878 qreal s = (i+0.0)/nSteps;
879 Q1 = curve1.getPointOnCubic(i1, s);
880 P2 = curve2.getVertex(i2-1);
881 for(
int j=1; j<=nSteps; j++)
883 qreal t = (j+0.0)/nSteps;
884 Q2 = curve2.getPointOnCubic(i2, t);
887#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
893 QPointF intersectionPoint = *cubicIntersection;
894 if (intersectionPoint != curve1.getVertex(i1-1) && intersectionPoint != curve1.getVertex(i1))
896 qreal fraction1 = eLength(intersectionPoint-Q1)/(0.0+eLength(Q1-P1));
897 qreal fraction2 = eLength(intersectionPoint-Q2)/(0.0+eLength(Q2-P2));
898 qreal t1 = (i - fraction1)/nSteps;
899 qreal t2 = (j - fraction2)/nSteps;
901 intersection.point = intersectionPoint;
902 intersection.t1 = t1;
903 intersection.t2 = t2;
904 intersections.
append( intersection );
void setFilled(bool yesOrNo)
BezierCurve::setFilled.
QString attribute(const QString &name, const QString &defValue) const const
QString tagName() const const
QDomNode firstChild() const const
bool isNull() const const
QDomNode nextSibling() const const
QDomElement toElement() const const
QLineF::IntersectType intersect(const QLineF &line, QPointF *intersectionPoint) const const
QLineF::IntersectionType intersects(const QLineF &line, QPointF *intersectionPoint) const const
void append(const T &value)
const T & at(int i) const const
void insert(int i, const T &value)
void replace(int i, const T &value)
void drawPath(const QPainterPath &path)
void setBrush(const QBrush &brush)
void setPen(const QColor &color)
QRectF boundingRect() const const
void cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &endPoint)
void lineTo(const QPointF &endPoint)
void moveTo(const QPointF &point)
void setFillRule(Qt::FillRule fillRule)
QRectF adjusted(qreal dx1, qreal dy1, qreal dx2, qreal dy2) const const
bool contains(const QRectF &rectangle) const const
bool intersects(const QRectF &rectangle) const const
void setBottomRight(const QPointF &position)
void setTopLeft(const QPointF &position)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString number(int n, int base)
double toDouble(bool *ok) const const
float toFloat(bool *ok) const const
int toInt(bool *ok, int base) const const
bool hasError() const const
void writeAttribute(const QString &qualifiedName, const QString &value)
void writeEmptyElement(const QString &qualifiedName)
void writeStartElement(const QString &qualifiedName)