20 #include "beziercurve.h"
22 #include "pencilerror.h"
24 BezierCurve::BezierCurve()
31 for (
int i = 0; i < pointList.size(); i++)
35 createCurve(pointList, pressureList);
40 int n = pointList.size();
44 for(
int i=0; i<n; i++) { markList.append(
false); }
45 markList.replace(0,
true);
46 markList.replace(n-1,
true);
47 BezierCurve::simplify(tol, pointList, 0, n-1, markList);
51 for(
int i=0; i<n; i++)
53 if (markList.at(i)==
true)
55 simplifiedPointList.append(pointList.at(i));
56 if (pressureList.size() > i)
60 qreal currentPressure = pressureList.at(i);
61 if (currentPressure < 0.1) {
62 currentPressure = 0.1;
64 simplifiedPressureList.append(currentPressure);
68 simplifiedPressureList.append(0.5);
74 createCurve(simplifiedPointList, simplifiedPressureList);
78 Status BezierCurve::createDomElement( QXmlStreamWriter& xmlStream )
80 xmlStream.writeStartElement(
"curve" );
81 xmlStream.writeAttribute(
"width", QString::number( width ) );
82 xmlStream.writeAttribute(
"variableWidth", variableWidth ?
"true" :
"false" );
83 if (feather>0) xmlStream.writeAttribute(
"feather", QString::number( feather ) );
84 xmlStream.writeAttribute(
"invisible", invisible ?
"true" :
"false" );
85 xmlStream.writeAttribute(
"colourNumber", QString::number( colourNumber ) );
86 xmlStream.writeAttribute(
"originX", QString::number( origin.x() ) );
87 xmlStream.writeAttribute(
"originY", QString::number( origin.y() ) );
88 xmlStream.writeAttribute(
"originPressure", QString::number( pressure.at(0) ) );
90 int errorLocation = -1;
91 for (
int i = 0; i < c1.size() ; i++ )
93 xmlStream.writeEmptyElement(
"segment" );
94 xmlStream.writeAttribute(
"c1x", QString::number( c1.at( i ).x() ) );
95 xmlStream.writeAttribute(
"c1y", QString::number( c1.at( i ).y() ) );
96 xmlStream.writeAttribute(
"c2x", QString::number( c2.at( i ).x() ) );
97 xmlStream.writeAttribute(
"c2y", QString::number( c2.at( i ).y() ) );
98 xmlStream.writeAttribute(
"vx", QString::number( vertex.at( i ).x() ) );
99 xmlStream.writeAttribute(
"vy", QString::number( vertex.at( i ).y() ) );
100 xmlStream.writeAttribute(
"pressure", QString::number( pressure.at( i + 1 ) ) );
101 if ( errorLocation < 0 && xmlStream.hasError() )
107 xmlStream.writeEndElement();
109 if ( xmlStream.hasError() && errorLocation >= 0 )
111 QStringList debugInfo = QStringList() <<
"BezierCurve::createDomElement"
112 << QString(
"width = %1" ).arg( width )
113 << QString(
"variableWidth = %1" ).arg(
"variableWidth" )
114 << QString(
"feather = %1" ).arg( feather )
115 << QString(
"invisible = %1" ).arg( invisible )
116 << QString(
"colourNumber = %1" ).arg( colourNumber )
117 << QString(
"originX = %1" ).arg( origin.x() )
118 << QString(
"originY = %1" ).arg( origin.y() )
119 << QString(
"originPressure = %1" ).arg( pressure.at( 0 ) )
120 << QString(
"- segmentTag[%1] has failed to write" ).arg( errorLocation )
121 << QString(
" c1x = %1" ).arg( c1.at( errorLocation ).x() )
122 << QString(
" c1y = %1" ).arg( c1.at( errorLocation ).y() )
123 << QString(
" c2x = %1" ).arg( c2.at( errorLocation ).x() )
124 << QString(
" c2y = %1" ).arg( c2.at( errorLocation ).y() )
125 << QString(
" vx = %1" ).arg( vertex.at( errorLocation ).x() )
126 << QString(
" vy = %1" ).arg( vertex.at( errorLocation ).y() )
127 << QString(
" pressure = %1" ).arg( pressure.at( errorLocation + 1 ) );
129 return Status( Status::FAIL, debugInfo );
135 void BezierCurve::loadDomElement(QDomElement element)
137 width = element.attribute(
"width").toDouble();
138 variableWidth = (element.attribute(
"variableWidth") ==
"1");
139 feather = element.attribute(
"feather").toDouble();
140 invisible = (element.attribute(
"invisible") ==
"1");
141 if (width == 0) invisible =
true;
142 colourNumber = element.attribute(
"colourNumber").toInt();
143 origin = QPointF( element.attribute(
"originX").toFloat(), element.attribute(
"originY").toFloat() );
144 pressure.append( element.attribute(
"originPressure").toFloat() );
145 selected.append(
false);
147 QDomNode segmentTag = element.firstChild();
148 while (!segmentTag.isNull())
150 QDomElement segmentElement = segmentTag.toElement();
151 if (!segmentElement.isNull())
153 if (segmentElement.tagName() ==
"segment")
155 QPointF c1Point = QPointF(segmentElement.attribute(
"c1x").toFloat(), segmentElement.attribute(
"c1y").toFloat());
156 QPointF c2Point = QPointF(segmentElement.attribute(
"c2x").toFloat(), segmentElement.attribute(
"c2y").toFloat());
157 QPointF vertexPoint = QPointF(segmentElement.attribute(
"vx").toFloat(), segmentElement.attribute(
"vy").toFloat());
158 qreal pressureValue = segmentElement.attribute(
"pressure").toFloat();
159 appendCubic(c1Point, c2Point, vertexPoint, pressureValue);
162 segmentTag = segmentTag.nextSibling();
167 void BezierCurve::setOrigin(
const QPointF& point)
172 void BezierCurve::setOrigin(
const QPointF& point,
const qreal& pressureValue,
const bool& trueOrFalse)
175 pressure[0] = pressureValue;
176 selected[0] = trueOrFalse;
179 void BezierCurve::setC1(
int i,
const QPointF& point)
181 if ( i >= 0 || i < c1.size() )
187 qDebug() <<
"BezierCurve::setC1! index out of bounds:" << i;
191 void BezierCurve::setC2(
int i,
const QPointF& point)
193 if ( i >= 0 || i < c2.size() )
199 qDebug() <<
"BezierCurve::setC2! index out of bounds:" << i;
203 void BezierCurve::setVertex(
int i,
const QPointF& point)
205 if (i==-1) { origin = point; }
208 if ( i >= 0 || i < vertex.size() )
214 qDebug() <<
"BezierCurve::setVertex! index out of bounds:" << i;
219 void BezierCurve::setLastVertex(
const QPointF& point)
223 vertex[vertex.size()-1] = point;
227 qDebug() <<
"BezierCurve::setLastVertex! curve has less than 2 vertices";
232 void BezierCurve::setWidth(qreal desiredWidth)
234 width = desiredWidth;
237 void BezierCurve::setFeather(qreal desiredFeather)
239 feather = desiredFeather;
242 void BezierCurve::setVariableWidth(
bool YesOrNo)
244 variableWidth = YesOrNo;
247 void BezierCurve::setInvisibility(
bool YesOrNo)
252 void BezierCurve::setSelected(
int i,
bool YesOrNo)
254 selected[i+1] = YesOrNo;
257 BezierCurve BezierCurve::transformed(QTransform transformation)
260 if (isSelected(-1)) { transformedCurve.setOrigin(transformation.map(origin)); }
261 for(
int i=0; i< vertex.size(); i++)
263 if (isSelected(i-1)) { transformedCurve.setC1(i, transformation.map(c1.at(i))); }
266 transformedCurve.setC2(i, transformation.map(c2.at(i)));
267 transformedCurve.setVertex(i, transformation.map(vertex.at(i)));
287 return transformedCurve;
290 void BezierCurve::transform(QTransform transformation)
292 if (isSelected(-1)) setOrigin( transformation.map(origin) );
293 for(
int i=0; i< vertex.size(); i++)
295 if (isSelected(i-1)) c1[i] = transformation.map(c1.at(i));
298 c2[i] = transformation.map(c2.at(i));
299 vertex[i] = transformation.map(vertex.at(i));
305 void BezierCurve::appendCubic(
const QPointF& c1Point,
const QPointF& c2Point,
const QPointF& vertexPoint, qreal pressureValue)
309 vertex.append(vertexPoint);
310 pressure.append(pressureValue);
311 selected.append(
false);
314 void BezierCurve::addPoint(
int position,
const QPointF point)
316 if ( position > -1 && position < getVertexSize() )
318 QPointF v1 = getVertex(position-1);
319 QPointF v2 = getVertex(position);
320 QPointF c1o = getC1(position);
321 QPointF c2o = getC2(position);
323 c1[position] = point + 0.2*(v2-v1);
324 c2[position] = v2 + (c2o-v2)*(0.5);
326 c1.insert(position, v1 + (c1o-v1)*(0.5) );
327 c2.insert(position, point - 0.2*(v2-v1));
328 vertex.insert(position, point);
329 pressure.insert(position, getPressure(position));
330 selected.insert(position, isSelected(position) && isSelected(position-1));
336 qDebug() <<
"Error BezierCurve::addPoint(int, QPointF)";
340 void BezierCurve::addPoint(
int position,
const qreal t)
345 if ( position > -1 && position < getVertexSize() )
347 QPointF vA = getVertex(position-1);
348 QPointF vB = getVertex(position);
349 QPointF c1o = getC1(position);
350 QPointF c2o = getC2(position);
351 QPointF c12 = (1-t)*c1o + t*c2o;
352 QPointF cA1 = (1-t)*vA + t*c1o;
353 QPointF cB2 = (1-t)*c2o + t*vB;
354 QPointF cA2 = (1-t)*cA1 + t*c12;
355 QPointF cB1 = (1-t)*c12 + t*cB2;
356 QPointF vM = (1-t)*cA2 + t*cB1;
358 setC1(position, cB1);
359 setC2(position, cB2);
361 c1.insert(position, cA1);
362 c2.insert(position, cA2);
363 vertex.insert(position, vM);
364 pressure.insert(position, getPressure(position));
365 selected.insert(position, isSelected(position) && isSelected(position-1));
371 qDebug() <<
"Error BezierCurve::addPoint(int, qreal)";
375 void BezierCurve::removeVertex(
int i)
377 int n = vertex.size();
382 origin = vertex.at(0);
386 pressure.removeAt(0);
387 selected.removeAt(0);
393 pressure.removeAt(i+1);
394 selected.removeAt(i+1);
407 void BezierCurve::drawPath(QPainter& painter,
Object*
object, QTransform transformation,
bool simplified,
bool showThinLines )
409 QColor colour =
object->getColour(colourNumber).colour;
412 if (isPartlySelected()) { myCurve = (transformed(transformation)); }
413 else { myCurve = *
this; }
415 if ( variableWidth && !simplified && !invisible)
417 painter.setPen(QPen(QBrush(colour), 1, Qt::NoPen, Qt::RoundCap,Qt::RoundJoin));
418 painter.setBrush(colour);
419 painter.drawPath(myCurve.getStrokedPath());
423 qreal renderedWidth = width;
426 renderedWidth = 1.0/painter.matrix().m11();
428 painter.setBrush(Qt::NoBrush);
436 painter.setPen(QPen(QBrush(Qt::black), renderedWidth, Qt::SolidLine, Qt::RoundCap,Qt::RoundJoin));
440 painter.setPen(QPen(QBrush(colour), 0, Qt::DotLine, Qt::RoundCap,Qt::RoundJoin));
445 painter.setPen(Qt::NoPen);
450 painter.setPen( QPen( QBrush( colour ), renderedWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) );
453 QPainterPath path = myCurve.getSimplePath();
454 painter.drawPath( path );
460 colour = QColor(100,100,255);
461 painter.setBrush(Qt::NoBrush);
462 qreal lineWidth = 1.5/painter.matrix().m11();
463 painter.setPen(QPen(QBrush(colour), lineWidth, Qt::SolidLine, Qt::RoundCap,Qt::RoundJoin));
464 if (isSelected()) painter.drawPath(myCurve.getSimplePath());
467 for(
int i=-1; i< vertex.size(); i++)
489 QPainterPath BezierCurve::getStraightPath()
493 for(
int i=0; i<vertex.size(); i++)
495 path.lineTo(vertex[i]);
501 QPainterPath BezierCurve::getSimplePath()
505 for(
int i=0; i<vertex.size(); i++)
507 path.cubicTo(c1.at(i), c2.at(i), vertex.at(i));
512 QPainterPath BezierCurve::getStrokedPath()
514 return getStrokedPath( width );
517 QPainterPath BezierCurve::getStrokedPath(qreal width)
519 return getStrokedPath(width,
true);
522 QPainterPath BezierCurve::getStrokedPath(qreal width,
bool usePressure)
525 QPointF tangentVec, normalVec, normalVec2, normalVec2_1, normalVec2_2;
526 qreal width2 = width;
527 path.setFillRule(Qt::WindingFill);
528 int n = vertex.size();
529 normalVec = QPointF(-(c1.at(0) - origin).y(), (c1.at(0) - origin).x());
530 normalise(normalVec);
531 if (usePressure) width2 = width * 0.5 * pressure.at(0);
532 if (n==1 && width2 == 0.0) width2 = 0.15 * width;
533 path.moveTo(origin + width2*normalVec);
534 for(
int i=0; i<n; i++)
538 normalVec2 = QPointF(-(vertex.at(i) - c2.at(i)).y(), (vertex.at(i) - c2.at(i)).x());
542 normalVec2_1 = QPointF(-(vertex.at(i) - c2.at(i)).y(), (vertex.at(i) - c2.at(i)).x());
543 normalise(normalVec2_1);
544 normalVec2_2 = QPointF(-(c1.at(i+1) - vertex.at(i)).y(), (c1.at(i+1) - vertex.at(i)).x());
545 normalise(normalVec2_2);
546 normalVec2 = normalVec2_1 + normalVec2_2;
548 normalise(normalVec2);
549 if (usePressure) width2 = width * 0.5 * pressure.at(i);
550 if (n==1 && width2 == 0.0) width2 = 0.15 * width;
552 path.cubicTo(c1.at(i) + width2*normalVec, c2.at(i) + width2*normalVec2, vertex.at(i) + width2*normalVec2);
555 normalVec = normalVec2;
557 if (usePressure) width2 = width * 0.5 * pressure.at(n-1);
558 if (n==1 && width2 == 0.0) width2 = 0.15 * width;
561 tangentVec = (vertex.at(n-1)-c2.at(n-1));
562 normalise(tangentVec);
563 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);
565 for(
int i=n-2; i>=0; i--)
567 normalVec2_1 = QPointF((vertex.at(i) - c1.at(i+1)).y(), -(vertex.at(i) - c1.at(i+1)).x());
568 normalise(normalVec2_1);
569 normalVec2_2 = QPointF((c2.at(i) - vertex.at(i)).y(), -(c2.at(i) - vertex.at(i)).x());
570 normalise(normalVec2_2);
571 normalVec2 = normalVec2_1 + normalVec2_2;
572 normalise(normalVec2);
573 if (usePressure) width2 = width * 0.5 * pressure.at(i);
574 if (n==1 && width2 == 0.0) width2 = 0.15 * width;
575 path.cubicTo(c2.at(i+1) - width2*normalVec, c1.at(i+1) - width2*normalVec2, vertex.at(i) - width2*normalVec2);
576 normalVec = normalVec2;
578 normalVec2 = QPointF((origin - c1.at(0)).y(), -(origin - c1.at(0)).x());
579 normalise(normalVec2);
580 if (usePressure) width2 = width * 0.5 * pressure.at(0);
581 if (n==1 && width2 == 0.0) width2 = 0.15 * width;
582 path.cubicTo(c2.at(0) - width2*normalVec, c1.at(0) - width2*normalVec2, origin - width2*normalVec2);
584 tangentVec = (origin-c1.at(0));
585 normalise(tangentVec);
586 path.cubicTo(origin + width2*(-normalVec+1.8*tangentVec), origin + width2*(normalVec+1.8*tangentVec), origin + width2*normalVec);
592 QRectF BezierCurve::getBoundingRect()
594 return getSimplePath().boundingRect();
600 int n = pointList.size();
603 while (c1.size()>0) c1.removeAt(0);
604 while (c2.size()>0) c2.removeAt(0);
605 while (vertex.size()>0) vertex.removeAt(0);
606 while (selected.size()>0) selected.removeAt(0);
607 while (pressure.size()>0) pressure.removeAt(0);
609 setOrigin( pointList.at(0) );
610 selected.append(
false);
611 pressure.append(pressureList.at(0));
615 c1.append(pointList.at(p));
616 c2.append(pointList.at(p));
617 vertex.append(pointList.at(p));
618 pressure.append(pressureList.at(p));
619 selected.append(
false);
628 void BezierCurve::smoothCurve()
630 QPointF c1, c2, c2old, tangentVec, normalVec;
631 int n = vertex.size();
632 c2old = QPointF(-100,-100);
633 for(
int p=0; p<n-1; p++)
635 QPointF D = getVertex(p);
636 QPointF Dprev = getVertex(p-1);
637 QPointF Dnext = getVertex(p+1);
638 qreal L1 = mLength(D-Dprev);
639 qreal L2 = mLength(D-Dnext);
641 tangentVec = 0.4*(Dnext - Dprev);
642 normalVec = QPointF(-tangentVec.y(), tangentVec.x())/eLength(tangentVec);
643 if ( ((D-Dprev).x()*(D-Dnext).x()+(D-Dprev).y()*(D-Dnext).y())/(1.0*L1*L2) < 0 )
646 c1 = D - tangentVec*(L1+0.0)/(L1+L2);
647 c2 = D + tangentVec*(L2+0.0)/(L1+L2);
652 c1 = 0.6*D + 0.4*Dprev;
653 c2 = 0.6*D + 0.4*Dnext;
658 c2old = 0.5*(vertex.at(0)+c1);
668 this->c1[n-1] = c2old;
669 this->c2[n-1] = 0.5*(c2old+vertex.at(n-1));
686 for(
int i=j+1; i<k-1; i++)
688 QPointF Vij = inputList.at(i)-inputList.at(j);
689 QPointF Vjk = inputList.at(j)-inputList.at(k);
690 double Vijx = Vij.x();
691 double Vijy = Vij.y();
692 double Vjkx = Vjk.x();
693 double Vjky = Vjk.y();
694 double dv = (Vjkx*Vjkx+Vjky*Vjky);
697 dv = sqrt( Vijx*Vijx+Vijy*Vijy - pow(Vijx*Vjkx+Vijy*Vjky,2)/dv );
714 markList.replace(maxi,
true);
716 simplify(tol, inputList, j, maxi, markList);
717 simplify(tol, inputList, maxi, k, markList);
723 qreal BezierCurve::eLength(
const QPointF point)
725 qreal result = sqrt( point.x()*point.x() + point.y()*point.y() );
730 qreal BezierCurve::mLength(
const QPointF point)
732 qreal result = qAbs(point.x()) + qAbs(point.y());
733 if (result == 0.0) result = 1.0;
737 void BezierCurve::normalise(QPointF& point)
739 qreal length = eLength(point);
742 point = point/length;
746 qreal BezierCurve::findDistance(
BezierCurve curve,
int i, QPointF P, QPointF& nearestPoint, qreal& t)
751 Q = curve.getVertex(i-1);
752 qreal distMin = eLength(Q-P);
755 for(
int k=1; k<=nSteps; k++)
757 qreal s = (k+0.0)/nSteps;
758 Q = curve.getPointOnCubic(i, s);
759 qreal dist = eLength(Q-P);
771 QPointF BezierCurve::getPointOnCubic(
int i, qreal t)
773 return (1.0-t)*(1.0-t)*(1.0-t)*getVertex(i-1)
774 + 3*t*(1.0-t)*(1.0-t)*getC1(i)
775 + 3*t*t*(1.0-t)*getC2(i)
776 + t*t*t*getVertex(i);
780 bool BezierCurve::intersects(QPointF point, qreal distance)
783 if ( getStrokedPath(distance,
false).contains(point) )
791 bool BezierCurve::intersects(QRectF rectangle)
794 if ( getSimplePath().controlPointRect().intersects(rectangle))
796 for(
int i=0; i<vertex.size(); i++)
798 if ( rectangle.contains( getVertex(i) ) )
return true;
808 QPointF P1, Q1, P2, Q2;
813 P1 = curve1.getVertex(i1-1);
814 Q1 = curve1.getVertex(i1);
815 P2 = curve2.getVertex(i2-1);
816 Q2 = curve2.getVertex(i2);
823 R1.setBottomRight(Q1);
825 R2.setBottomRight(Q2);
827 QPointF intersectionPoint = QPointF(50.0, 50.0);
828 QPointF* cubicIntersection = &intersectionPoint;
829 if ( R1.intersects(R2) || L2.intersect(L1, cubicIntersection) == QLineF::BoundedIntersection )
837 P1 = curve1.getVertex(i1-1);
838 for(
int i=1; i<=nSteps; i++)
840 qreal s = (i+0.0)/nSteps;
841 Q1 = curve1.getPointOnCubic(i1, s);
842 P2 = curve2.getVertex(i2-1);
843 for(
int j=1; j<=nSteps; j++)
845 qreal t = (j+0.0)/nSteps;
846 Q2 = curve2.getPointOnCubic(i2, t);
849 if (L2.intersect(L1, cubicIntersection) == QLineF::BoundedIntersection)
851 QPointF intersectionPoint = *cubicIntersection;
852 if (intersectionPoint != curve1.getVertex(i1-1) && intersectionPoint != curve1.getVertex(i1))
854 qreal fraction1 = eLength(intersectionPoint-Q1)/(0.0+eLength(Q1-P1));
855 qreal fraction2 = eLength(intersectionPoint-Q2)/(0.0+eLength(Q2-P2));
856 qreal t1 = (i - fraction1)/nSteps;
857 qreal t2 = (j - fraction2)/nSteps;
859 intersection.point = intersectionPoint;
860 intersection.t1 = t1;
861 intersection.t2 = t2;
862 intersections.append( intersection );