Pencil2D  ff90c0872e88be3bf81c548cd60f01983012ec49
Pencil2D is an animation software for both bitmap and vector graphics. It is free, multi-platform, and open source.
 All Classes Functions
beziercurve.cpp
1 /*
2 
3 Pencil - Traditional Animation Software
4 Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5 Copyright (C) 2012-2017 Matthew Chiawen Chang
6 
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; version 2 of the License.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15 
16 */
17 
18 #include <cmath>
19 #include <QList>
20 #include "beziercurve.h"
21 #include "object.h"
22 #include "pencilerror.h"
23 
24 BezierCurve::BezierCurve()
25 {
26 }
27 
28 BezierCurve::BezierCurve(QList<QPointF> pointList)
29 {
30  QList<qreal> pressureList;
31  for (int i = 0; i < pointList.size(); i++)
32  {
33  pressureList << 0.5; // default pressure
34  }
35  createCurve(pointList, pressureList);
36 }
37 
38 BezierCurve::BezierCurve(QList<QPointF> pointList, QList<qreal> pressureList, double tol)
39 {
40  int n = pointList.size();
41 
42  // Simplify path
43  QList<bool> markList;
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);
48 
49  QList<QPointF> simplifiedPointList;
50  QList<qreal> simplifiedPressureList;
51  for(int i=0; i<n; i++)
52  {
53  if (markList.at(i)==true)
54  {
55  simplifiedPointList.append(pointList.at(i));
56  if (pressureList.size() > i)
57  {
58  // Make sure that the stroke point always has a pressure (and a width)
59  //
60  qreal currentPressure = pressureList.at(i);
61  if (currentPressure < 0.1) {
62  currentPressure = 0.1;
63  }
64  simplifiedPressureList.append(currentPressure);
65  }
66  else
67  {
68  simplifiedPressureList.append(0.5); // default pressure
69  }
70  }
71  }
72 
73  // Create curve from the simplified path
74  createCurve(simplifiedPointList, simplifiedPressureList);
75 }
76 
77 
78 Status BezierCurve::createDomElement( QXmlStreamWriter& xmlStream )
79 {
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) ) );
89 
90  int errorLocation = -1;
91  for ( int i = 0; i < c1.size() ; i++ )
92  {
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() )
102  {
103  errorLocation = i;
104  }
105  }
106 
107  xmlStream.writeEndElement(); // Close curve element
108 
109  if ( xmlStream.hasError() && errorLocation >= 0 )
110  {
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( "&nbsp;&nbsp;c1x = %1" ).arg( c1.at( errorLocation ).x() )
122  << QString( "&nbsp;&nbsp;c1y = %1" ).arg( c1.at( errorLocation ).y() )
123  << QString( "&nbsp;&nbsp;c2x = %1" ).arg( c2.at( errorLocation ).x() )
124  << QString( "&nbsp;&nbsp;c2y = %1" ).arg( c2.at( errorLocation ).y() )
125  << QString( "&nbsp;&nbsp;vx = %1" ).arg( vertex.at( errorLocation ).x() )
126  << QString( "&nbsp;&nbsp;vy = %1" ).arg( vertex.at( errorLocation ).y() )
127  << QString( "&nbsp;&nbsp;pressure = %1" ).arg( pressure.at( errorLocation + 1 ) );
128 
129  return Status( Status::FAIL, debugInfo );
130  }
131 
132  return Status::OK;
133 }
134 
135 void BezierCurve::loadDomElement(QDomElement element)
136 {
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);
146 
147  QDomNode segmentTag = element.firstChild();
148  while (!segmentTag.isNull())
149  {
150  QDomElement segmentElement = segmentTag.toElement();
151  if (!segmentElement.isNull())
152  {
153  if (segmentElement.tagName() == "segment")
154  {
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);
160  }
161  }
162  segmentTag = segmentTag.nextSibling();
163  }
164 }
165 
166 
167 void BezierCurve::setOrigin(const QPointF& point)
168 {
169  origin = point;
170 }
171 
172 void BezierCurve::setOrigin(const QPointF& point, const qreal& pressureValue, const bool& trueOrFalse)
173 {
174  origin = point;
175  pressure[0] = pressureValue;
176  selected[0] = trueOrFalse;
177 }
178 
179 void BezierCurve::setC1(int i, const QPointF& point)
180 {
181  if ( i >= 0 || i < c1.size() )
182  {
183  c1[i] = point;
184  }
185  else
186  {
187  qDebug() << "BezierCurve::setC1! index out of bounds:" << i;
188  }
189 }
190 
191 void BezierCurve::setC2(int i, const QPointF& point)
192 {
193  if ( i >= 0 || i < c2.size() )
194  {
195  c2[i] = point;
196  }
197  else
198  {
199  qDebug() << "BezierCurve::setC2! index out of bounds:" << i;
200  }
201 }
202 
203 void BezierCurve::setVertex(int i, const QPointF& point)
204 {
205  if (i==-1) { origin = point; }
206  else
207  {
208  if ( i >= 0 || i < vertex.size() )
209  {
210  vertex[i] = point;
211  }
212  else
213  {
214  qDebug() << "BezierCurve::setVertex! index out of bounds:" << i;
215  }
216  }
217 }
218 
219 void BezierCurve::setLastVertex(const QPointF& point)
220 {
221  if (vertex.size()>0)
222  {
223  vertex[vertex.size()-1] = point;
224  }
225  else
226  {
227  qDebug() << "BezierCurve::setLastVertex! curve has less than 2 vertices";
228  }
229 }
230 
231 
232 void BezierCurve::setWidth(qreal desiredWidth)
233 {
234  width = desiredWidth;
235 }
236 
237 void BezierCurve::setFeather(qreal desiredFeather)
238 {
239  feather = desiredFeather;
240 }
241 
242 void BezierCurve::setVariableWidth(bool YesOrNo)
243 {
244  variableWidth = YesOrNo;
245 }
246 
247 void BezierCurve::setInvisibility(bool YesOrNo)
248 {
249  invisible = YesOrNo;
250 }
251 
252 void BezierCurve::setSelected(int i, bool YesOrNo)
253 {
254  selected[i+1] = YesOrNo;
255 }
256 
257 BezierCurve BezierCurve::transformed(QTransform transformation)
258 {
259  BezierCurve transformedCurve = *this; // copy the curve
260  if (isSelected(-1)) { transformedCurve.setOrigin(transformation.map(origin)); }
261  for(int i=0; i< vertex.size(); i++)
262  {
263  if (isSelected(i-1)) { transformedCurve.setC1(i, transformation.map(c1.at(i))); }
264  if (isSelected(i))
265  {
266  transformedCurve.setC2(i, transformation.map(c2.at(i)));
267  transformedCurve.setVertex(i, transformation.map(vertex.at(i)));
268  }
269  }
270  //transformedCurve.smoothCurve();
271  /*QPointF newOrigin = origin;
272  if (isSelected(-1)) { newOrigin = transformation.map(newOrigin); }
273  transformedCurve.setOrigin( newOrigin );
274  for(int i=0; i< vertex.size(); i++) {
275  QPointF newC1 = c1.at(i);
276  QPointF newC2 = c2.at(i);
277  QPointF newVertex = vertex.at(i);
278  if (isSelected(i-1)) { newC1 = transformation.map(newC1); }
279  if (isSelected(i)) { newC2 = transformation.map(newC2); newVertex = transformation.map(newVertex); }
280  transformedCurve.appendCubic( newC1, newC2, newVertex, pressure.at(i) );
281  if (isSelected(i)) { transformedCurve.setSelected(i, true); }
282  }
283  transformedCurve.setWidth( width);
284  transformedCurve.setVariableWidth( variableWidth );
285  //transformedCurve.setSelected(true); // or select only the selected elements of the orginal curve?
286  */
287  return transformedCurve;
288 }
289 
290 void BezierCurve::transform(QTransform transformation)
291 {
292  if (isSelected(-1)) setOrigin( transformation.map(origin) );
293  for(int i=0; i< vertex.size(); i++)
294  {
295  if (isSelected(i-1)) c1[i] = transformation.map(c1.at(i));
296  if (isSelected(i))
297  {
298  c2[i] = transformation.map(c2.at(i));
299  vertex[i] = transformation.map(vertex.at(i));
300  }
301  }
302  //smoothCurve();
303 }
304 
305 void BezierCurve::appendCubic(const QPointF& c1Point, const QPointF& c2Point, const QPointF& vertexPoint, qreal pressureValue)
306 {
307  c1.append(c1Point);
308  c2.append(c2Point);
309  vertex.append(vertexPoint);
310  pressure.append(pressureValue);
311  selected.append(false);
312 }
313 
314 void BezierCurve::addPoint(int position, const QPointF point)
315 {
316  if ( position > -1 && position < getVertexSize() )
317  {
318  QPointF v1 = getVertex(position-1);
319  QPointF v2 = getVertex(position);
320  QPointF c1o = getC1(position);
321  QPointF c2o = getC2(position);
322 
323  c1[position] = point + 0.2*(v2-v1);
324  c2[position] = v2 + (c2o-v2)*(0.5);
325 
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));
331 
332  //smoothCurve();
333  }
334  else
335  {
336  qDebug() << "Error BezierCurve::addPoint(int, QPointF)";
337  }
338 }
339 
340 void BezierCurve::addPoint(int position, const qreal t) // t is the fraction where to split the bezier curve (ex: t=0.5)
341 {
342  // de Casteljau's method is used
343  // http://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm
344  // http://www.damtp.cam.ac.uk/user/na/PartIII/cagd2002/halve.ps
345  if ( position > -1 && position < getVertexSize() )
346  {
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;
357 
358  setC1(position, cB1);
359  setC2(position, cB2);
360 
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));
366 
367  //smoothCurve();
368  }
369  else
370  {
371  qDebug() << "Error BezierCurve::addPoint(int, qreal)";
372  }
373 }
374 
375 void BezierCurve::removeVertex(int i)
376 {
377  int n = vertex.size();
378  if (i>-2 && i< n)
379  {
380  if (i== -1)
381  {
382  origin = vertex.at(0);
383  vertex.removeAt(0);
384  c1.removeAt(0);
385  c2.removeAt(0);
386  pressure.removeAt(0);
387  selected.removeAt(0);
388  }
389  else
390  {
391  vertex.removeAt(i);
392  c2.removeAt(i);
393  pressure.removeAt(i+1);
394  selected.removeAt(i+1);
395  if ( i != n-1 )
396  {
397  c1.removeAt(i+1);
398  }
399  else
400  {
401  c1.removeAt(i);
402  }
403  }
404  }
405 }
406 
407 void BezierCurve::drawPath(QPainter& painter, Object* object, QTransform transformation, bool simplified, bool showThinLines )
408 {
409  QColor colour = object->getColour(colourNumber).colour;
410 
411  BezierCurve myCurve;
412  if (isPartlySelected()) { myCurve = (transformed(transformation)); }
413  else { myCurve = *this; }
414 
415  if ( variableWidth && !simplified && !invisible)
416  {
417  painter.setPen(QPen(QBrush(colour), 1, Qt::NoPen, Qt::RoundCap,Qt::RoundJoin));
418  painter.setBrush(colour);
419  painter.drawPath(myCurve.getStrokedPath());
420  }
421  else
422  {
423  qreal renderedWidth = width;
424  if (simplified)
425  {
426  renderedWidth = 1.0/painter.matrix().m11();
427  }
428  painter.setBrush(Qt::NoBrush);
429  if ( invisible )
430  {
431  if (showThinLines)
432  {
433  if (simplified)
434  {
435  // Set simplified lines to black for the fill function to define contours.
436  painter.setPen(QPen(QBrush(Qt::black), renderedWidth, Qt::SolidLine, Qt::RoundCap,Qt::RoundJoin));
437  }
438  else
439  {
440  painter.setPen(QPen(QBrush(colour), 0, Qt::DotLine, Qt::RoundCap,Qt::RoundJoin));
441  }
442  }
443  else
444  {
445  painter.setPen(Qt::NoPen);
446  }
447  }
448  else
449  {
450  painter.setPen( QPen( QBrush( colour ), renderedWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) );
451  //painter.setPen( QPen( Qt::darkYellow , 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) );
452  }
453  QPainterPath path = myCurve.getSimplePath();
454  painter.drawPath( path );
455  }
456 
457  if (!simplified)
458  {
459  // highlight the selected elements
460  colour = QColor(100,100,255); // highlight colour
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());
465 
466 
467  for(int i=-1; i< vertex.size(); i++)
468  {
469  if (isSelected(i))
470  {
471  //painter.fillRect(myCurve.getVertex(i).x()-0.5*squareWidth, myCurve.getVertex(i).y()-0.5*squareWidth, squareWidth, squareWidth, colour);
472 
473  //painter.fillRect(QRectF(myCurve.getVertex(i).x()-0.5*squareWidth, myCurve.getVertex(i).y()-0.5*squareWidth, squareWidth, squareWidth), colour);
474 
475  /*painter.setFont( QFont("Arial", floor(12.0/painter.matrix().m11()), -1, false) );
476  //painter.drawText(myCurve.getVertex(i)+QPointF(4.0,0.0), QString::number(i)+"-"+QString::number(myCurve.getVertex(i).x())+","+QString::number(myCurve.getVertex(i).y()));
477  QPointF normale = QPointF(4.0, 0.0);
478  if (i>-1) { normale = (myCurve.getVertex(i)-myCurve.getC2(i)); } else { normale = (myCurve.getC1(i+1)-myCurve.getVertex(i)); }
479  normale = QPointF(-normale.y(), normale.x());
480  normale = 8.0*normale/eLength(normale)/painter.matrix().m11();
481  painter.drawLine(myCurve.getVertex(i), myCurve.getVertex(i)+normale);
482  painter.drawText(myCurve.getVertex(i)+2*normale, QString::number(i));*/
483  }
484  }
485  }
486 }
487 
488 // Without curve fitting
489 QPainterPath BezierCurve::getStraightPath()
490 {
491  QPainterPath path;
492  path.moveTo(origin);
493  for(int i=0; i<vertex.size(); i++)
494  {
495  path.lineTo(vertex[i]);
496  }
497  return path;
498 }
499 
500 // With bezier curve fitting
501 QPainterPath BezierCurve::getSimplePath()
502 {
503  QPainterPath path;
504  path.moveTo(origin);
505  for(int i=0; i<vertex.size(); i++)
506  {
507  path.cubicTo(c1.at(i), c2.at(i), vertex.at(i));
508  }
509  return path;
510 }
511 
512 QPainterPath BezierCurve::getStrokedPath()
513 {
514  return getStrokedPath( width );
515 }
516 
517 QPainterPath BezierCurve::getStrokedPath(qreal width)
518 {
519  return getStrokedPath(width, true);
520 }
521 
522 QPainterPath BezierCurve::getStrokedPath(qreal width, bool usePressure)
523 {
524  QPainterPath path;
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++)
535  {
536  if (i==n-1)
537  {
538  normalVec2 = QPointF(-(vertex.at(i) - c2.at(i)).y(), (vertex.at(i) - c2.at(i)).x());
539  }
540  else
541  {
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;
547  }
548  normalise(normalVec2);
549  if (usePressure) width2 = width * 0.5 * pressure.at(i);
550  if (n==1 && width2 == 0.0) width2 = 0.15 * width;
551  //if (i==n-1) width2 = 0.0;
552  path.cubicTo(c1.at(i) + width2*normalVec, c2.at(i) + width2*normalVec2, vertex.at(i) + width2*normalVec2);
553  //path.moveTo(vertex.at(i) + width*normalVec2);
554  //path.lineTo(vertex.at(i) - width*normalVec2);
555  normalVec = normalVec2;
556  }
557  if (usePressure) width2 = width * 0.5 * pressure.at(n-1);
558  if (n==1 && width2 == 0.0) width2 = 0.15 * width;
559 
560  //path.lineTo(vertex.at(n-1) - width2*normalVec);
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);
564 
565  for(int i=n-2; i>=0; i--)
566  {
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;
577  }
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);
583 
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);
587 
588  path.closeSubpath();
589  return path;
590 }
591 
592 QRectF BezierCurve::getBoundingRect()
593 {
594  return getSimplePath().boundingRect();
595 }
596 
597 void BezierCurve::createCurve(QList<QPointF>& pointList, QList<qreal>& pressureList )
598 {
599  int p = 0;
600  int n = pointList.size();
601  // generate the Bezier (cubic) curve from the simplified path and mouse pressure
602  // first, empty everything
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);
608 
609  setOrigin( pointList.at(0) );
610  selected.append(false);
611  pressure.append(pressureList.at(0));
612 
613  for(p=1; p<n; p++)
614  {
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);
620 
621  }
622  smoothCurve();
623  //colourNumber = 0;
624  feather = 0;
625 }
626 
627 
628 void BezierCurve::smoothCurve()
629 {
630  QPointF c1, c2, c2old, tangentVec, normalVec;
631  int n = vertex.size();
632  c2old = QPointF(-100,-100); // bogus point
633  for(int p=0; p<n-1; p++)
634  {
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);
640 
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 )
644  {
645  // smooth point
646  c1 = D - tangentVec*(L1+0.0)/(L1+L2);
647  c2 = D + tangentVec*(L2+0.0)/(L1+L2);
648  }
649  else
650  {
651  // sharp point
652  c1 = 0.6*D + 0.4*Dprev;
653  c2 = 0.6*D + 0.4*Dnext;
654  }
655 
656  if (p==0)
657  {
658  c2old = 0.5*(vertex.at(0)+c1);
659  }
660 
661  this->c1[p] = c2old;
662  this->c2[p] = c1;
663  //appendCubic(c2old, c1, D, pressureList->at(p));
664  c2old = c2;
665  }
666  if (n>2)
667  {
668  this->c1[n-1] = c2old;
669  this->c2[n-1] = 0.5*(c2old+vertex.at(n-1));
670  }
671 }
672 
673 void BezierCurve::simplify(double tol, QList<QPointF>& inputList, int j, int k, QList<bool>& markList)
674 {
675  // -- Douglas-Peucker simplification algorithm
676  // from http://geometryalgorithms.com/Archive/algorithm_0205/
677  if (k <= j+1) //there is nothing to simplify
678  {
679  // return immediately
680  }
681  else
682  {
683  // test distance of intermediate vertices from segment Vj to Vk
684  double maxd = 0.0; //is the distance of farthest vertex from segment jk
685  int maxi = j; //is the index of the vertex farthest from segement jk
686  for(int i=j+1; i<k-1; i++) // each intermediate vertex Vi
687  {
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);
695  if ( dv != 0.0)
696  {
697  dv = sqrt( Vijx*Vijx+Vijy*Vijy - pow(Vijx*Vjkx+Vijy*Vjky,2)/dv );
698  }
699  //qDebug() << "distance = "+QString::number(dv);
700  if (dv < maxd)
701  {
702  //Vi is not farther away, so continue to the next vertex
703  }
704  else //Vi is a new max vertex
705  {
706  maxd = dv;
707  maxi = i; //to remember the farthest vertex
708  }
709  }
710  if (maxd >= tol) //a vertex is farther than tol from Sjk
711  {
712  // split the polyline at the farthest vertex
713  //Mark Vmaxi as part of the simplified polyline
714  markList.replace(maxi, true);
715  //and recursively simplify the two subpolylines
716  simplify(tol, inputList, j, maxi, markList);
717  simplify(tol, inputList, maxi, k, markList);
718  }
719  }
720 }
721 
722 // general useful functions -> to be placed elsewhere
723 qreal BezierCurve::eLength(const QPointF point) // calculates the Euclidean Length (of a point seen as a vector)
724 {
725  qreal result = sqrt( point.x()*point.x() + point.y()*point.y() ); // could also use QLine.length()... is it any faster?
726  //if (result == 0.0) result = 1.0;
727  return result;
728 }
729 
730 qreal BezierCurve::mLength(const QPointF point) // calculates the Manhattan Length (of a point seen as a vector)
731 {
732  qreal result = qAbs(point.x()) + qAbs(point.y());
733  if (result == 0.0) result = 1.0;
734  return result;
735 }
736 
737 void BezierCurve::normalise(QPointF& point)
738 {
739  qreal length = eLength(point);
740  if (length > 1.0e-6)
741  {
742  point = point/length;
743  }
744 }
745 
746 qreal BezierCurve::findDistance(BezierCurve curve, int i, QPointF P, QPointF& nearestPoint, qreal& t) //finds the distance between a cubic section and a point
747 {
748  //qDebug() << "---- INTER CUBIC SEGMENT";
749  int nSteps = 24;
750  QPointF Q;
751  Q = curve.getVertex(i-1);
752  qreal distMin = eLength(Q-P);
753  nearestPoint = Q;
754  t = 0;
755  for(int k=1; k<=nSteps; k++)
756  {
757  qreal s = (k+0.0)/nSteps;
758  Q = curve.getPointOnCubic(i, s);
759  qreal dist = eLength(Q-P);
760  if (dist <= distMin)
761  {
762  distMin = dist;
763  nearestPoint = Q;
764  t = s;
765  }
766  }
767  //QPointF Q1 = curve.getPointOnCubic(i, t);
768  return distMin;
769 }
770 
771 QPointF BezierCurve::getPointOnCubic(int i, qreal t)
772 {
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);
777 }
778 
779 
780 bool BezierCurve::intersects(QPointF point, qreal distance)
781 {
782  bool result = false;
783  if ( getStrokedPath(distance, false).contains(point) )
784  {
785  //if ( getSimplePath().controlPointRect().contains(point)) {
786  result = true;
787  }
788  return result;
789 }
790 
791 bool BezierCurve::intersects(QRectF rectangle)
792 {
793  bool result = false;
794  if ( getSimplePath().controlPointRect().intersects(rectangle))
795  {
796  for(int i=0; i<vertex.size(); i++)
797  {
798  if ( rectangle.contains( getVertex(i) ) ) return true;
799  }
800  }
801  return result;
802 }
803 
804 bool BezierCurve::findIntersection(BezierCurve curve1, int i1, BezierCurve curve2, int i2, QList<Intersection>& intersections) //finds the intersection between two cubic sections
805 {
806  bool result = false;
807  //qDebug() << "---- INTER CUBIC CUBIC" << i1 << i2;
808  QPointF P1, Q1, P2, Q2;
809  QLineF L1, L2;
810  QRectF R1;
811  QRectF R2;
812 
813  P1 = curve1.getVertex(i1-1);
814  Q1 = curve1.getVertex(i1);
815  P2 = curve2.getVertex(i2-1);
816  Q2 = curve2.getVertex(i2);
817  L1 = QLineF(P1, Q1);
818  L2 = QLineF(P2, Q2);
819 
820  //qDebug() << "-------------------- ";
821 
822  R1.setTopLeft(P1);
823  R1.setBottomRight(Q1);
824  R2.setTopLeft(P2);
825  R2.setBottomRight(Q2);
826 
827  QPointF intersectionPoint = QPointF(50.0, 50.0); // bogus point
828  QPointF* cubicIntersection = &intersectionPoint;
829  if ( R1.intersects(R2) || L2.intersect(L1, cubicIntersection) == QLineF::BoundedIntersection )
830  {
831  //if (L2.intersect(L1, intersection) == QLineF::BoundedIntersection) {
832  //qDebug() << " FOUND rectangle intersection ";
833  //if (intersectionPoint != curve1.getVertex(i1-1) && intersectionPoint != curve1.getVertex(i1)) {
834  // qDebug() << " it's not one of the points ";
835  // find the cubic intersection
836  int nSteps = 24;
837  P1 = curve1.getVertex(i1-1);
838  for(int i=1; i<=nSteps; i++)
839  {
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++)
844  {
845  qreal t = (j+0.0)/nSteps;
846  Q2 = curve2.getPointOnCubic(i2, t);
847  L1 = QLineF(P1, Q1);
848  L2 = QLineF(P2, Q2);
849  if (L2.intersect(L1, cubicIntersection) == QLineF::BoundedIntersection)
850  {
851  QPointF intersectionPoint = *cubicIntersection;
852  if (intersectionPoint != curve1.getVertex(i1-1) && intersectionPoint != curve1.getVertex(i1))
853  {
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;
858  Intersection intersection;
859  intersection.point = intersectionPoint;
860  intersection.t1 = t1;
861  intersection.t2 = t2;
862  intersections.append( intersection );
863  result = true;
864  //qDebug() << "FOUND cubic interesection " << intersectionPoint << i << j;
865  }
866  }
867  P2 = Q2;
868  }
869  P1 = Q1;
870  }
871  }
872  else
873  {
874  //return false; // approximation to speed up the calculation
875  }
876  //qDebug() << "------";
877  return result;
878 }
Definition: object.h:71