Pencil2D  ff90c0872e88be3bf81c548cd60f01983012ec49
Pencil2D is an animation software for both bitmap and vector graphics. It is free, multi-platform, and open source.
 All Classes Functions
vectorimage.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 #include <cmath>
18 #include "object.h"
19 #include "vectorimage.h"
20 
21 
22 VectorImage::VectorImage()
23 {
24  deselectAll();
25 }
26 
27 VectorImage::~VectorImage()
28 {
29 }
30 
31 bool VectorImage::read(QString filePath)
32 {
33  QFileInfo fileInfo(filePath);
34  if ( fileInfo.isDir() )
35  {
36  return false;
37  }
38 
39  QFile file{filePath};
40  if (!file.open(QFile::ReadOnly))
41  {
42  //QMessageBox::warning(this, "Warning", "Cannot read file");
43  return false;
44  }
45 
46  QDomDocument doc;
47  if (!doc.setContent(&file)) return false; // this is not a XML file
48  QDomDocumentType type = doc.doctype();
49  if (type.name() != "PencilVectorImage") return false; // this is not a Pencil document
50 
51  QDomElement element = doc.documentElement();
52  if (element.tagName() == "image")
53  {
54  // --- vector image ---
55  if (element.attribute("type") == "vector")
56  {
57  loadDomElement( element );
58  }
59  }
60  return true;
61 }
62 
63 Status VectorImage::write(QString filePath, QString format)
64 {
65  QStringList debugInfo = QStringList() << "VectorImage::write" << QString( "filePath = " ).append( filePath ) << QString( "format = " ).append( format );
66  QFile file{filePath};
67  bool result = file.open(QIODevice::WriteOnly);
68  if (!result)
69  {
70  //QMessageBox::warning(this, "Warning", "Cannot write file");
71  qDebug() << "VectorImage - Cannot write file" << filePath << file.error();
72  return Status( Status::FAIL, debugInfo << QString("file.error() = ").append( file.errorString() ) );
73  }
74 
75  if (format == "VEC")
76  {
77  QXmlStreamWriter xmlStream( &file );
78  xmlStream.setAutoFormatting( true);
79  xmlStream.writeStartDocument();
80  xmlStream.writeDTD( "<!DOCTYPE PencilVectorImage>" );
81 
82  xmlStream.writeStartElement( "image" );
83  xmlStream.writeAttribute( "type", "vector" );
84  Status st = createDomElement( xmlStream );
85  if( !st.ok() )
86  {
87  QStringList xmlDetails = st.detailsList();
88  for ( QString detail : xmlDetails )
89  {
90  detail.prepend( "&nbsp;&nbsp;" );
91  }
92  return Status( Status::FAIL, debugInfo << "- xml creation failed" << xmlDetails );
93  }
94 
95  xmlStream.writeEndElement(); // Close image element
96  xmlStream.writeEndDocument();
97 
98  return Status::OK;
99  }
100  else
101  {
102  qDebug() << "--- Not the VEC format!";
103  return Status( Status::FAIL, debugInfo << "Unrecognized format" );
104  }
105 }
106 
107 Status VectorImage::createDomElement( QXmlStreamWriter& xmlStream )
108 {
109  QStringList debugInfo = QStringList() << "VectorImage::createDomElement";
110  for ( int i = 0; i < m_curves.size(); i++ )
111  {
112  Status st = m_curves[ i ].createDomElement( xmlStream );
113  if ( !st.ok() )
114  {
115  QStringList curveDetails = st.detailsList();
116  for ( QString detail : curveDetails )
117  {
118  detail.prepend( "&nbsp;&nbsp;" );
119  }
120  return Status( Status::FAIL, debugInfo << QString( "- m_curves[%1] failed to write" ).arg( i ) << curveDetails );
121  }
122  }
123  for ( int i = 0; i < area.size(); i++ )
124  {
125  Status st = area[ i ].createDomElement( xmlStream );
126  if ( !st.ok() )
127  {
128  QStringList areaDetails = st.detailsList();
129  for ( QString detail : areaDetails )
130  {
131  detail.prepend( "&nbsp;&nbsp;" );
132  }
133  return Status( Status::FAIL, debugInfo << QString( "- area[%1] failed to write" ).arg( i ) << areaDetails );
134  }
135  }
136  return Status::OK;
137 }
138 
139 void VectorImage::loadDomElement(QDomElement element)
140 {
141  QDomNode atomTag = element.firstChild(); // an atom in a vector picture is a curve or an area
142  while (!atomTag.isNull())
143  {
144  QDomElement atomElement = atomTag.toElement();
145  if (!atomElement.isNull())
146  {
147  if (atomElement.tagName() == "curve")
148  {
149  BezierCurve newCurve = BezierCurve();
150  newCurve.loadDomElement(atomElement);
151  m_curves.append(newCurve);
152  }
153  if (atomElement.tagName() == "area")
154  {
155  BezierArea newArea = BezierArea();
156  newArea.loadDomElement(atomElement);
157  addArea(newArea);
158  }
159  }
160  atomTag = atomTag.nextSibling();
161  }
162  clean();
163  modification();
164 }
165 
166 void VectorImage::addPoint(int curveNumber, int vertexNumber, qreal t)
167 {
168  //curve[curveNumber].addPoint(vertexNumber, point);
169  m_curves[curveNumber].addPoint(vertexNumber, t);
170  // updates the bezierAreas
171  for(int j=0; j < area.size(); j++)
172  {
173  // shift the references of all the points beyond the new point
174  for(int k=0; k< area.at(j).mVertex.size(); k++)
175  {
176  if (area[j].getVertexRef(k).curveNumber == curveNumber)
177  {
178  if (area[j].getVertexRef(k).vertexNumber >= vertexNumber)
179  {
180  area[j].mVertex[k].vertexNumber++;
181  }
182  }
183  }
184  // insert the new point in the area if necessary
185  for(int k=1; k< area.at(j).mVertex.size(); k++)
186  {
187  if ( VertexRef(curveNumber, vertexNumber+1) == area.at(j).mVertex.at(k) ) // area[j].vertex[k] == VertexRef(curveNumber, vertexNumber+1)
188  {
189  if ( VertexRef(curveNumber, vertexNumber-1) == area.at(j).mVertex.at(k-1) )
190  {
191  area[j].mVertex.insert(k, VertexRef(curveNumber, vertexNumber) );
192  }
193  }
194  if ( VertexRef(curveNumber, vertexNumber-1) == area.at(j).mVertex.at(k) )
195  {
196  if ( VertexRef(curveNumber, vertexNumber+1) == area.at(j).mVertex.at(k-1) )
197  {
198  area[j].mVertex.insert(k, VertexRef(curveNumber, vertexNumber) );
199  }
200  }
201  }
202  }
203 }
204 
205 void VectorImage::removeCurveAt(int i)
206 {
207  // first change the curve numbers in the areas
208  for(int j=0; j < area.size(); j++)
209  {
210  for(int k=0; k< area.at(j).mVertex.size(); k++)
211  {
212  if (area.at(j).mVertex[k].curveNumber > i) { area[j].mVertex[k].curveNumber--; }
213  }
214  }
215  // then remove curve
216  m_curves.removeAt(i);
217 }
218 
219 void VectorImage::insertCurve(int position, BezierCurve& newCurve, qreal factor, bool interacts)
220 {
221  if (newCurve.getVertexSize() < 1) {
222 
223  // security - a new curve should have a least 2 vertices
224  return;
225  }
226 
227 
228  // Does the curve should interact with others or with itself?
229  //
230  if (interacts) {
231  // tolerance for taking the intersection as an existing vertex on a curve
232  //
233  qreal tol = qMax( newCurve.getWidth() / factor, 3.0 / factor);
234  //qDebug() << "tolerance" << tol;
235 
236  checkCurveExtremity(newCurve, tol);
237  checkCurveIntersections(newCurve, tol);
238  }
239 
240 
241  // Append or insert the curve in the list
242  //
243  if (position < 0 || position > m_curves.size() - 1) {
244  m_curves.append(newCurve);
245  }
246  else {
247  // If it's an insert we have to shift the curve numbers in the areas
248  //
249  for(int i=0; i < area.size(); i++)
250  {
251  for(int j=0; j< area.at(i).mVertex.size(); j++)
252  {
253  if (area.at(i).mVertex[j].curveNumber >= position) {
254  area[i].mVertex[j].curveNumber++;
255  }
256  }
257  }
258  m_curves.insert(position, newCurve);
259  }
260 
261 
262  updateImageSize(newCurve);
263  modification();
264  //QPainter painter(&image);
265  //painter.setRenderHint(QPainter::Antialiasing, true);
266  //newCurve.drawPath(&painter);
267 }
268 
269 void VectorImage::addCurve(BezierCurve& newCurve, qreal factor, bool interacts)
270 {
271  insertCurve(-1, newCurve, factor, interacts);
272 }
273 
274 void VectorImage::checkCurveExtremity(BezierCurve& newCurve, qreal tolerance)
275 {
276  // finds if the new curve is closed
277  QPointF P = newCurve.getVertex(-1);
278  QPointF Q = newCurve.getVertex(newCurve.getVertexSize()-1);
279  if ( BezierCurve::eLength(P-Q) < tolerance)
280  {
281  newCurve.setVertex(newCurve.getVertexSize()-1, P);
282  }
283  // finds if the first or last point of the new curve is close to other curves
284  for(int i=0; i < m_curves.size(); i++) // for each other curve
285  {
286  for(int j=0; j < m_curves.at(i).getVertexSize(); j++) // for each cubic section of the other curve
287  {
288  QPointF P = newCurve.getVertex(-1);
289  QPointF Q = newCurve.getVertex(newCurve.getVertexSize()-1);
290  QPointF P1 = m_curves.at(i).getVertex(j-1);
291  QPointF P2 = m_curves.at(i).getVertex(j);
292  qreal tol3 = 2.0*sqrt( 0.25*((P1-P2).x()*(P1-P2).x() + (P1-P2).y()*(P1-P2).y()) + tolerance*tolerance );
293  qreal dist1 = BezierCurve::eLength(P-P1);
294  qreal dist2 = BezierCurve::eLength(P-P2);
295  if (dist1 <= 0.2*tolerance)
296  {
297  newCurve.setVertex(-1, P1); //qDebug() << "--b " << P1;
298  }
299  else
300  {
301  if (dist2 <= 0.2*tolerance)
302  {
303  newCurve.setVertex(-1, P2); //qDebug() << "--c " << P2;
304  }
305  else
306  {
307  if ( dist1+dist2 <= 3*tol3 ) // preselection, to speed up
308  {
309  QPointF nearestPoint = P;
310  qreal t = -1.0;
311  qreal distance = BezierCurve::findDistance(m_curves[i], j, P, nearestPoint, t);
312  if (distance < tolerance)
313  {
314  newCurve.setOrigin(nearestPoint); //qDebug() << "--d " << nearestPoint;
315  addPoint(i, j, t);
316  //j++;
317  }
318  }
319  }
320  //qDebug() << "Modif first";
321  }
322 
323  dist1 = BezierCurve::eLength(Q-P1);
324  dist2 = BezierCurve::eLength(Q-P2);
325  if (dist1 <= 0.2*tolerance)
326  {
327  newCurve.setLastVertex(P1); //qDebug() << "--e " << P1;
328  }
329  else
330  {
331  if (dist2 <= 0.2*tolerance)
332  {
333  newCurve.setLastVertex(P2); //qDebug() << "--f " << P2;
334  }
335  else
336  {
337  if ( dist1+dist2 <= 3*tol3 ) // preselection, to speed up
338  {
339  QPointF nearestPoint = Q;
340  qreal t = -1.0;;
341  qreal distance = BezierCurve::findDistance(m_curves[i], j, Q, nearestPoint, t);
342  if (distance < tolerance)
343  {
344  newCurve.setLastVertex(nearestPoint); //qDebug() << "--g " << nearestPoint;
345  addPoint(i, j, t);
346  //j++;
347  }
348  }
349  }
350  //qDebug() << "Modif last";
351  }
352  }
353  }
354 }
355 
356 void VectorImage::checkCurveIntersections(BezierCurve& newCurve, qreal tolerance)
357 {
358 
359  // finds if the new curve interesects itself
360  //
361  for(int k=0; k < newCurve.getVertexSize(); k++) // for each cubic section of the new curve
362  {
363  for(int j=k+1; j < newCurve.getVertexSize(); j++) // for each other cubic section of the new curve
364  {
365  QList<Intersection> intersections;
366  bool intersection = BezierCurve::findIntersection(newCurve, k, newCurve, j, intersections);
367  if (intersection)
368  {
369  //qDebug() << "INTERSECTION" << intersectionPoint << t1 << t2;
370  //newCurve.addPoint(k, intersectionPoint);
371  newCurve.addPoint(k, intersections[0].t1); //qDebug() << "--a " << newCurve.getVertex(k) << newCurve.getVertex(k+1);
372  k++;
373  j++;
374  //newCurve.addPoint(j, intersectionPoint);
375  newCurve.addPoint(j, intersections[0].t2); //qDebug() << "--a " << newCurve.getVertex(j) << newCurve.getVertex(j+1);
376  j++;
377  }
378  }
379  }
380 
381  // finds if the new curve interesects other curves
382  for(int k=0; k < newCurve.getVertexSize(); k++) // for each cubic section of the new curve
383  {
384  //if (k==0) L1 = QLineF(P1 + 1.5*tol*(P1-Q1)/BezierCurve::eLength(P1-Q1), Q1); // we extend slightly the line for the near point
385  //if (k==newCurve.getVertexSize()-1) L1 = QLineF(P1, Q1- 1.5*tol*(P1-Q1)/BezierCurve::eLength(P1-Q1)); // we extend slightly the line for the last point
386  //QPointF extension1 = 1.5*tol*(P1-Q1)/BezierCurve::eLength(P1-Q1);
387  //L1 = QLineF(P1 + extension1, Q1 - extension1);
388  for(int i=0; i < m_curves.size(); i++) // for each other curve // TO DO: should only loop on "nearby" curves instead of all
389  {
390  //BezierCurve otherCurve;
391  //if (i==-1) { otherCurve = newCurve; } else { otherCurve = curve.at(i); }
392 
393  // ---- finds if the first or last point of the other curve is close to the current cubic section of the new curve
394  QPointF P = m_curves.at(i).getVertex(-1);
395  QPointF Q = m_curves.at(i).getVertex(m_curves.at(i).getVertexSize()-1);
396  QPointF P1 = newCurve.getVertex(k-1);
397  QPointF P2 = newCurve.getVertex(k);
398  qreal tol3 = 2.0*sqrt( 0.25*((P1-P2).x()*(P1-P2).x() + (P1-P2).y()*(P1-P2).y()) + tolerance*tolerance );
399  qreal dist1 = BezierCurve::eLength(P-P1);
400  qreal dist2 = BezierCurve::eLength(P-P2);
401 
402  if (dist1 < 0.2*tolerance)
403  {
404  m_curves[i].setVertex(-1, P1); // memo: curve.at(i) is just a copy which can be read, curve[i] is a reference which can be modified
405  }
406  else
407  {
408  if (dist2 < 0.2*tolerance)
409  {
410  m_curves[i].setVertex(-1, P2);
411  }
412  else
413  {
414  if ( dist1+dist2 < 3*tol3 )
415  {
416  // TO DO: find a better intersection point
417  QPointF nearestPoint = P;
418  qreal t = -1.0;
419  qreal distance = BezierCurve::findDistance(newCurve, k, P, nearestPoint, t);
420  //qDebug() << "OK1" << t;
421  if (distance < tolerance)
422  {
423  P = nearestPoint;
424  //m_curves[i].setOrigin(P);
425  //newCurve.addPoint(k, P); //qDebug() << "--i " << P;
426  }
427  }
428  }
429  //qDebug() << "Modif first";
430  }
431  dist1 = BezierCurve::eLength(Q-P1);
432  dist2 = BezierCurve::eLength(Q-P2);
433  if (dist1 < 0.2*tolerance)
434  {
435  m_curves[i].setVertex(m_curves.at(i).getVertexSize()-1, P1);
436  }
437  else
438  {
439  if (dist2 < 0.2*tolerance)
440  {
441  m_curves[i].setVertex(m_curves.at(i).getVertexSize()-1, P2);
442  }
443  else
444  {
445  if ( dist1+dist2 < 3*tol3 )
446  {
447  // TO DO: find a better intersection point
448  QPointF nearestPoint = Q;
449  qreal t = -1.0;;
450  qreal distance = BezierCurve::findDistance(newCurve, k, Q, nearestPoint, t);
451  //qDebug() << "OK2" << t;
452  if (distance < tolerance)
453  {
454  Q = nearestPoint;
455  //m_curves[i].setLastVertex(Q);
456  //newCurve.addPoint(k, Q); //qDebug() << "--j " << Q;
457  }
458  }
459  }
460  //qDebug() << "Modif first";
461  }
462 
463  // ---- finds if any cubic section of the other curve intersects the current cubic section of the new curve
464  for(int j=0; j < m_curves.at(i).getVertexSize(); j++) // for each cubic section of the other curve
465  {
466  QList<Intersection> intersections;
467  bool intersection = BezierCurve::findIntersection(newCurve, k, m_curves.at(i), j, intersections);
468  if (intersection)
469  {
470  //qDebug() << "Found " << intersections.size() << " intersections";
471  QPointF intersectionPoint = intersections[0].point;
472  qreal t1 = intersections[0].t1;
473  qreal t2 = intersections[0].t2;
474  if ( BezierCurve::eLength(intersectionPoint - newCurve.getVertex(k-1)) <= 0.1*tolerance ) // the first point is close to the intersection
475  {
476  newCurve.setVertex(k-1, intersectionPoint); //qDebug() << "--k " << intersectionPoint;
477  //qDebug() << "--------- recal " << k-1 << intersectionPoint;
478  }
479  else
480  {
481  if ( BezierCurve::eLength(intersectionPoint - newCurve.getVertex(k)) <= 0.1*tolerance ) // the second point is close to the intersection
482  {
483  newCurve.setVertex(k, intersectionPoint); //qDebug() << "--l " << intersectionPoint;
484  //qDebug() << "-------- recal " << k << intersectionPoint;
485  }
486  else // none of the point is close to the intersection -> we add a new point
487  {
488  //newCurve.addPoint(k, intersectionPoint);
489  newCurve.addPoint(k, t1); //qDebug() << "--m " << newCurve.getVertex(k);
490  //qDebug() << "----- add " << k << newCurve.getVertex(k);
491  //k++;
492  }
493  }
494  if ( BezierCurve::eLength(intersectionPoint - m_curves.at(i).getVertex(j-1)) <= 0.1*tolerance ) // the first point is close to the intersection
495  {
496  m_curves[i].setVertex(j-1, intersectionPoint); //qDebug() << "--n " << intersectionPoint;
497  //qDebug() << "-------- recal2 " << j-1 << intersectionPoint;
498  }
499  else
500  {
501  if ( BezierCurve::eLength(intersectionPoint - m_curves.at(i).getVertex(j)) <= 0.1*tolerance ) // the second point is close to the intersection
502  {
503  m_curves[i].setVertex(j, intersectionPoint); //qDebug() << "--o " << intersectionPoint;
504  //qDebug() << "-------- recal2 " << j << intersectionPoint;
505  }
506  else // none of the point is close to the intersection -> we add a new point
507  {
508  addPoint(i, j, t2);
509  //qDebug() << "----- add2 " << j << curve[i].getVertex(j);
510  //j++;
511  }
512  }
513  }
514  }
515  }
516  }
517 }
518 
519 void VectorImage::select(QRectF rectangle)
520 {
521  for(int i=0; i< m_curves.size(); i++)
522  {
523  if ( m_curves[i].intersects(rectangle) )
524  {
525  setSelected(i, true);
526  }
527  else
528  {
529  setSelected(i, false);
530  }
531  }
532  for(int i=0; i< area.size(); i++)
533  {
534  if ( rectangle.contains(area[i].mPath.boundingRect()) )
535  {
536  setAreaSelected(i, true);
537  }
538  else
539  {
540  setAreaSelected(i, false);
541  }
542  }
543  modification();
544 }
545 
546 void VectorImage::setSelected(int curveNumber, bool YesOrNo)
547 {
548  m_curves[curveNumber].setSelected(YesOrNo);
549  if (YesOrNo) mSelectionRect |= m_curves[curveNumber].getBoundingRect();
550  modification();
551 }
552 
553 void VectorImage::setSelected(int curveNumber, int vertexNumber, bool YesOrNo)
554 {
555  m_curves[curveNumber].setSelected(vertexNumber, YesOrNo);
556  QPointF vertex = getVertex(curveNumber, vertexNumber);
557  if (YesOrNo) mSelectionRect |= QRectF(vertex.x(), vertex.y(), 0.0, 0.0);
558  modification();
559 }
560 
561 void VectorImage::setSelected(VertexRef vertexRef, bool YesOrNo)
562 {
563  setSelected(vertexRef.curveNumber, vertexRef.vertexNumber, YesOrNo);
564 }
565 
566 void VectorImage::setSelected(QList<int> curveList, bool YesOrNo)
567 {
568  for(int i=0; i<curveList.size(); i++)
569  {
570  setSelected(curveList.at(i), YesOrNo);
571  }
572 }
573 
574 void VectorImage::setSelected(QList<VertexRef> vertexList, bool YesOrNo)
575 {
576  for(int i=0; i<vertexList.size(); i++)
577  {
578  setSelected(vertexList.at(i), YesOrNo);
579  }
580 }
581 
582 void VectorImage::setAreaSelected(int areaNumber, bool YesOrNo)
583 {
584  area[areaNumber].setSelected(YesOrNo);
585  if (YesOrNo) mSelectionRect |= area[areaNumber].mPath.boundingRect();
586  modification();
587 }
588 
589 bool VectorImage::isAreaSelected(int areaNumber)
590 {
591  return area[areaNumber].isSelected();
592 }
593 
594 bool VectorImage::isSelected(int curveNumber)
595 {
596  return m_curves[curveNumber].isSelected();
597 }
598 
599 bool VectorImage::isSelected(int curveNumber, int vertexNumber)
600 {
601  return m_curves[curveNumber].isSelected(vertexNumber);
602 }
603 
604 bool VectorImage::isSelected(VertexRef vertexRef)
605 {
606  return isSelected(vertexRef.curveNumber, vertexRef.vertexNumber);
607 }
608 
609 bool VectorImage::isSelected(QList<int> curveList)
610 {
611  bool result = true;
612  for(int i=0; i<curveList.size(); i++)
613  {
614  result &= isSelected(curveList.at(i));
615  }
616  return result;
617 }
618 
619 bool VectorImage::isSelected(QList<VertexRef> vertexList)
620 {
621  bool result = true;
622  for(int i=0; i<vertexList.size(); i++)
623  {
624  result &= isSelected(vertexList.at(i));
625  }
626  return result;
627 }
628 
629 int VectorImage::getFirstSelectedCurve()
630 {
631  int result = -1;
632  for(int i=0; i<m_curves.size() && result == -1; i++)
633  {
634  if ( isSelected(i) ) result = i;
635  }
636  return result;
637 }
638 
639 int VectorImage::getFirstSelectedArea()
640 {
641  int result = -1;
642  for(int i=0; i<area.size() && result == -1; i++)
643  {
644  if ( isAreaSelected(i) ) result = i;
645  }
646  return result;
647 }
648 
649 void VectorImage::selectAll()
650 {
651  for(int i=0; i< m_curves.size(); i++)
652  {
653  //curve[i].setSelected(true);
654  setSelected(i, true);
655  }
656  mSelectionTransformation.reset();
657  //modification();
658 }
659 
660 void VectorImage::deselectAll()
661 {
662  for(int i=0; i< m_curves.size(); i++)
663  {
664  m_curves[i].setSelected(false);
665  }
666  for(int i=0; i< area.size(); i++)
667  {
668  area[i].setSelected(false);
669  }
670  mSelectionRect = QRectF(0,0,0,0);
671  mSelectionTransformation.reset();
672  modification();
673 }
674 
675 void VectorImage::setSelectionRect(QRectF rectangle)
676 {
677  mSelectionRect = rectangle;
678  select(rectangle);
679 }
680 
681 void VectorImage::calculateSelectionRect()
682 {
683  mSelectionRect = QRectF(0,0,0,0);
684  for(int i=0; i< m_curves.size(); i++)
685  {
686  if ( m_curves.at(i).isPartlySelected()) mSelectionRect |= m_curves[i].getBoundingRect();
687  }
688 }
689 
690 void VectorImage::setSelectionTransformation(QTransform transform)
691 {
692  mSelectionTransformation = transform;
693  modification();
694 }
695 
696 void VectorImage::deleteSelection()
697 {
698  // ---- deletes areas
699  for(int i=0; i< area.size(); i++)
700  {
701  if ( area[i].isSelected())
702  {
703  area.removeAt(i);
704  i--;
705  }
706  }
707  // ---- deletes curves
708  for(int i=0; i< m_curves.size(); i++)
709  {
710  if ( m_curves[i].isSelected())
711  {
712  // eliminates areas which are associated to this curve
713  for(int j=0; j < area.size(); j++)
714  {
715  bool toBeDeleted = false;
716  for(int k=0; k< area.at(j).mVertex.size(); k++)
717  {
718  if (area.at(j).mVertex[k].curveNumber == i) { toBeDeleted = true; }
719  if (area.at(j).mVertex[k].curveNumber > i) { area[j].mVertex[k].curveNumber = area[j].mVertex[k].curveNumber - 1; }
720  }
721  if (toBeDeleted)
722  {
723  area.removeAt(j);
724  j--;
725  }
726  }
727  m_curves.removeAt(i);
728  i--;
729  }
730  else
731  {
732  /*for(int j=-1; j<curve.at(i).size(); j++) {
733  if (curve.at(i).isSelected(j)) {
734  curve.at(i).removeVertex(j);
735  }
736  }*/
737  }
738  }
739  modification();
740 }
741 
742 void VectorImage::removeVertex(int i, int m) // curve number i and vertex number m
743 {
744  // first eliminates areas which are associated to this point
745  for(int j=0; j < area.size(); j++)
746  {
747  bool toBeDeleted = false;
748  for(int k=0; k< area.at(j).mVertex.size(); k++)
749  {
750  if (area.at(j).mVertex[k].curveNumber == i && area.at(j).mVertex[k].vertexNumber == m) { toBeDeleted = true; }
751  //if (area.at(j).vertex[k].curveNumber > i) { area[j].vertex[k].curveNumber = area[j].vertex[k].curveNumber - 1; }
752  }
753  if (toBeDeleted)
754  {
755  area.removeAt(j);
756  j--;
757  }
758  }
759  // then eliminates the point
760  if (m_curves[i].getVertexSize() > 1)
761  {
762  // first possibility: we just remove the point in the curve
763  /*
764  curve[i].removeVertex(m);
765  m--;*/
766  // second possibility: we split the curve into two parts:
767  if ( m == -1 || m == getCurveSize(i) - 1 ) // we just remove the first or last point
768  {
769  m_curves[i].removeVertex(m);
770  m--;
771  // we also need to update the areas
772  for(int j=0; j < area.size(); j++)
773  {
774  for(int k=0; k< area.at(j).mVertex.size(); k++)
775  {
776  if (area.at(j).mVertex[k].curveNumber == i && area.at(j).mVertex[k].vertexNumber > m) { area[j].mVertex[k].vertexNumber--; }
777  }
778  }
779  }
780  else
781  {
782  int n = getCurveSize(i);
783  BezierCurve newCurve = m_curves.at(i); // duplicate curve
784  for(int p = m; p < n; p++) // removes the end of of the curve i (after m, included) -> left part
785  {
786  m_curves[i].removeVertex( getCurveSize(i)-1 );
787  }
788  for(int p=-1; p <= m; p++) // removes the beginning of the new curve (before m, included) -> right part
789  {
790  newCurve.removeVertex(-1);
791  }
792  //if (newCurve.getVertexSize() > 0) curve.insert(i+1, newCurve);
793  if (newCurve.getVertexSize() > 0) m_curves.append( newCurve); // insert the right part if it has more than one point
794  // we also need to update the areas
795  for(int j=0; j < area.size(); j++)
796  {
797  for(int k=0; k< area.at(j).mVertex.size(); k++)
798  {
799  if (area.at(j).mVertex[k].curveNumber == i && area.at(j).mVertex[k].vertexNumber > m)
800  {
801  area[j].mVertex[k].curveNumber = m_curves.size()-1;
802  area[j].mVertex[k].vertexNumber = area[j].mVertex[k].vertexNumber-m-1;
803  }
804  }
805  }
806 
807  if ( getCurveSize(i) < 1) // the left part has less than two points so we remove it
808  {
809  //curve.removeAt(i);
810  removeCurveAt(i);
811  i--;
812  }
813  }
814  }
815  else // there are just two points left, so we remove the whole curve
816  {
817  //curve.removeAt(i);
818  removeCurveAt(i);
819  i--;
820  }
821 }
822 
823 void VectorImage::deleteSelectedPoints()
824 {
825  for(int i=0; i< m_curves.size(); i++)
826  {
827  for(int m=-1; m < getCurveSize(i); m++)
828  {
829  if (m_curves.at(i).isSelected(m)) // point m of curve i is selected
830  {
831  removeVertex(i, m);
832  }
833  }
834  }
835  modification();
836 }
837 
838 void VectorImage::paste(VectorImage& vectorImage)
839 {
840  mSelectionRect = QRect(0,0,0,0);
841  int n = m_curves.size();
842  QList<int> selectedCurves;
843 
844  bool hasSelection = getFirstSelectedCurve() < -1;
845 
846  for(int i=0; i < vectorImage.m_curves.size() ; i++)
847  {
848  // If nothing is selected, paste everything
849  //
850  if ( !hasSelection || vectorImage.m_curves.at(i).isSelected() )
851  {
852  m_curves.append( vectorImage.m_curves.at(i) );
853  selectedCurves << i;
854  mSelectionRect |= vectorImage.m_curves[i].getBoundingRect();
855  }
856  }
857  for(int i=0; i < vectorImage.area.size() ; i++)
858  {
859  BezierArea newArea = vectorImage.area.at(i);
860  bool ok = true;
861  for(int j=0; j < newArea.mVertex.size(); j++)
862  {
863  int curveNumber = newArea.mVertex.at(j).curveNumber;
864  int vertexNumber = newArea.mVertex.at(j).vertexNumber;
865 
866  // If nothing is selected, paste everything
867  //
868  if ( !hasSelection || vectorImage.m_curves.at(curveNumber).isSelected() )
869  {
870  newArea.mVertex[j] = VertexRef( selectedCurves.indexOf(curveNumber) + n, vertexNumber );
871  }
872  else
873  {
874  ok = false;
875  }
876  }
877  if (ok) area.append( newArea );
878  }
879  modification();
880 }
881 
882 QColor VectorImage::getColour(int colourNumber)
883 {
884  return mObject->getColour(colourNumber).colour;
885 }
886 
887 int VectorImage::getColourNumber(QPointF point)
888 {
889  int result = -1;
890  int areaNumber = getLastAreaNumber(point);
891  if (areaNumber != -1)
892  {
893  result = area[areaNumber].mColourNumber;
894  }
895  return result;
896 }
897 
898 bool VectorImage::usesColour(int index)
899 {
900  for(int i=0; i< area.size(); i++)
901  {
902  if (area[i].mColourNumber == index) return true;
903  }
904  for(int i=0; i< m_curves.size(); i++)
905  {
906  if (m_curves[i].getColourNumber() == index) return true;
907  }
908  return false;
909 }
910 
911 void VectorImage::removeColour(int index)
912 {
913  for(int i=0; i< area.size(); i++)
914  {
915  if (area[i].getColourNumber() > index) area[i].decreaseColourNumber();
916  }
917  for(int i=0; i< m_curves.size(); i++)
918  {
919  if (m_curves[i].getColourNumber() > index) m_curves[i].decreaseColourNumber();
920  }
921 }
922 
923 void VectorImage::paintImage(QPainter& painter,
924  bool simplified,
925  bool showThinCurves,
926  bool antialiasing )
927 {
928  painter.setRenderHint(QPainter::Antialiasing, antialiasing);
929 
930  painter.setClipping(false);
931  painter.setOpacity(1.0);
932  QTransform painterMatrix = painter.transform();
933 
934  QRect mappedViewRect = QRect(0,0, painter.device()->width(), painter.device()->height() );
935  painterMatrix.inverted().mapRect( mappedViewRect );
936 
937  // --- draw filled areas ----
938  if (!simplified)
939  {
940  for(int i=0; i< area.size(); i++)
941  {
942  updateArea( area[i] ); // to do: if selected
943 
944  // --- fill areas ---- //
945  QColor colour = getColour(area[i].mColourNumber);
946 
947  painter.save();
948  painter.setWorldMatrixEnabled( false );
949 
950  if (area[i].isSelected())
951  {
952  painter.setBrush( QBrush( QColor(255-colour.red(),255-colour.green(),255-colour.blue()), Qt::Dense6Pattern) );
953  }
954  else {
955  painter.setPen(QPen(QBrush(colour), 1, Qt::NoPen, Qt::RoundCap,Qt::RoundJoin));
956  painter.setBrush( QBrush( colour, Qt::SolidPattern ));
957  }
958 
959  painter.drawPath( painter.transform().map( area[ i ].mPath ) );
960  painter.restore();
961  painter.setWorldMatrixEnabled( true );
962 
963  // --
964  painter.setRenderHint(QPainter::Antialiasing, antialiasing);
965  painter.setClipping(false);
966  }
967  }
968 
969  // ---- draw curves ----
970  //simplified = true;
971  //painter.setClipRect( viewRect );
972  //painter.setClipping(true);
973  for ( BezierCurve curve : m_curves )
974  {
975  curve.drawPath( painter, mObject, mSelectionTransformation, simplified, showThinCurves );
976  painter.setClipping(false);
977  }
978 }
979 
980 void VectorImage::outputImage(QImage* image,
981  QTransform myView,
982  bool simplified,
983  bool showThinCurves,
984  bool antialiasing)
985 {
986  image->fill(qRgba(0,0,0,0));
987  QPainter painter( image );
988  painter.setTransform( myView );
989  paintImage( painter, simplified, showThinCurves, antialiasing );
990 }
991 
992 void VectorImage::clear()
993 {
994  while (m_curves.size() > 0) { m_curves.removeAt(0); }
995  while (area.size() > 0) { area.removeAt(0); }
996  modification();
997 }
998 
999 void VectorImage::clean()
1000 {
1001  for(int i=0; i<m_curves.size(); i++)
1002  {
1003  if (m_curves.at(i).getVertexSize() == 0) { qDebug() << "CLEAN " << i; m_curves.removeAt(i); i--; }
1004  }
1005 }
1006 
1007 void VectorImage::applySelectionTransformation()
1008 {
1009  applySelectionTransformation(mSelectionTransformation);
1010 }
1011 
1012 void VectorImage::applySelectionTransformation(QTransform transf)
1013 {
1014  for(int i=0; i< m_curves.size(); i++)
1015  {
1016  if ( m_curves.at(i).isPartlySelected()) {
1017  m_curves[i].transform(transf);
1018  }
1019  }
1020  calculateSelectionRect();
1021  mSelectionTransformation.reset();
1022  modification();
1023 }
1024 
1025 void VectorImage::applyColourToSelection(int colourNumber)
1026 {
1027  for (int i = 0; i < m_curves.size(); i++)
1028  {
1029  if ( m_curves.at(i).isSelected()) m_curves[i].setColourNumber(colourNumber);
1030  }
1031  for (int i = 0; i < area.size(); i++)
1032  {
1033  if ( area.at(i).isSelected()) area[i].setColourNumber(colourNumber);
1034  }
1035  modification();
1036 }
1037 
1038 void VectorImage::applyWidthToSelection(qreal width)
1039 {
1040  for(int i = 0; i < m_curves.size(); i++)
1041  {
1042  if ( m_curves.at(i).isSelected() ) m_curves[ i ].setWidth( width );
1043  }
1044  modification();
1045 }
1046 
1047 void VectorImage::applyFeatherToSelection(qreal feather)
1048 {
1049  for(int i=0; i< m_curves.size(); i++)
1050  {
1051  if ( m_curves.at(i).isSelected() ) m_curves[i].setFeather(feather);
1052  }
1053  modification();
1054 }
1055 
1056 void VectorImage::applyOpacityToSelection(qreal opacity)
1057 {
1058  Q_UNUSED(opacity);
1059  for(int i=0; i< m_curves.size(); i++)
1060  {
1061  //if ( curve.at(i).isSelected()) curve[i].setOpacity(width);
1062  }
1063  modification();
1064 }
1065 
1066 void VectorImage::applyInvisibilityToSelection(bool YesOrNo)
1067 {
1068  for(int i=0; i< m_curves.size(); i++)
1069  {
1070  if ( m_curves.at(i).isSelected()) m_curves[i].setInvisibility(YesOrNo);
1071  }
1072  modification();
1073 }
1074 
1075 void VectorImage::applyVariableWidthToSelection(bool YesOrNo)
1076 {
1077  for(int i=0; i< m_curves.size(); i++)
1078  {
1079  if ( m_curves.at(i).isSelected()) {
1080  m_curves[i].setVariableWidth(YesOrNo);
1081  }
1082  }
1083  modification();
1084 }
1085 
1086 QList<int> VectorImage::getCurvesCloseTo(QPointF P1, qreal maxDistance)
1087 {
1088  QList<int> result;
1089  for(int j=0; j<m_curves.size(); j++)
1090  {
1091  BezierCurve myCurve;
1092  if (m_curves[j].isPartlySelected()) {
1093  myCurve = m_curves[j].transformed(mSelectionTransformation);
1094  } else {
1095  myCurve = m_curves[j];
1096  }
1097  if ( myCurve.intersects(P1, maxDistance) ) {
1098  result.append( j );
1099  }
1100  }
1101  return result;
1102 }
1103 
1104 
1105 VertexRef VectorImage::getClosestVertexTo(QPointF P1, qreal maxDistance)
1106 {
1107  VertexRef result;
1108  result = VertexRef(-1, -1); // result = [-1, -1]
1109  //qreal distance = image.width()*image.width(); // initial big value
1110  qreal distance = 400.0*400.0; // initial big value
1111  for(int j=0; j<m_curves.size(); j++)
1112  {
1113  for(int k=-1; k<m_curves.at(j).getVertexSize(); k++)
1114  {
1115  //QPointF P2 = selectionTransformation.map( getVertex(j, k) );
1116  QPointF P2 = getVertex(j, k);
1117  qreal distance2 = (P1.x()-P2.x())*(P1.x()-P2.x()) + (P1.y()-P2.y())*(P1.y()-P2.y());
1118  if ( distance2 < distance && distance2 < maxDistance*maxDistance)
1119  {
1120  distance = distance2;
1121  result = VertexRef(j,k);
1122  }
1123  }
1124  }
1125  return result;
1126 }
1127 
1128 QList<VertexRef> VectorImage::getVerticesCloseTo(QPointF P1, qreal maxDistance)
1129 {
1130  QList<VertexRef> result;
1131  for(int j=0; j<m_curves.size(); j++)
1132  {
1133  for(int k=-1; k<m_curves.at(j).getVertexSize(); k++)
1134  {
1135  QPointF P2 = getVertex(j, k);
1136  qreal distance = (P1.x()-P2.x())*(P1.x()-P2.x()) + (P1.y()-P2.y())*(P1.y()-P2.y());
1137  if ( distance < maxDistance*maxDistance )
1138  {
1139  result.append( VertexRef(j,k) );
1140  }
1141  }
1142  }
1143  return result;
1144 }
1145 
1146 QList<VertexRef> VectorImage::getVerticesCloseTo(QPointF P1, qreal maxDistance, QList<VertexRef>* listOfPoints)
1147 {
1148  QList<VertexRef> result;
1149  for(int j=0; j<listOfPoints->size(); j++)
1150  {
1151  QPointF P2 = getVertex(listOfPoints->at(j));
1152  qreal distance = (P1.x()-P2.x())*(P1.x()-P2.x()) + (P1.y()-P2.y())*(P1.y()-P2.y());
1153  if ( distance < maxDistance*maxDistance )
1154  {
1155  result.append(listOfPoints->at(j));
1156  //listOfPoints->removeAt(j);
1157  }
1158  }
1159  return result;
1160 }
1161 
1162 QList<VertexRef> VectorImage::getVerticesCloseTo(VertexRef P1ref, qreal maxDistance)
1163 {
1164  return getVerticesCloseTo( getVertex(P1ref), maxDistance);
1165 }
1166 
1167 QList<VertexRef> VectorImage::getVerticesCloseTo(VertexRef P1ref, qreal maxDistance, QList<VertexRef>* listOfPoints)
1168 {
1169  return getVerticesCloseTo( getVertex(P1ref), maxDistance, listOfPoints);
1170 }
1171 
1172 QList<VertexRef> VectorImage::getAndRemoveVerticesCloseTo(QPointF P1, qreal maxDistance, QList<VertexRef>* listOfPoints)
1173 {
1174  QList<VertexRef> result;
1175  //qreal distance = image.width()*image.width(); // initial big value
1176  for(int j=0; j<listOfPoints->size(); j++)
1177  {
1178  QPointF P2 = getVertex(listOfPoints->at(j));
1179  qreal distance = (P1.x()-P2.x())*(P1.x()-P2.x()) + (P1.y()-P2.y())*(P1.y()-P2.y());
1180  if ( distance < maxDistance*maxDistance )
1181  {
1182  result.append(listOfPoints->at(j));
1183  listOfPoints->removeAt(j);
1184  }
1185  }
1186  return result;
1187 }
1188 
1189 QList<VertexRef> VectorImage::getAndRemoveVerticesCloseTo(VertexRef P1Ref, qreal maxDistance, QList<VertexRef>* listOfPoints)
1190 {
1191  return getAndRemoveVerticesCloseTo(getVertex(P1Ref), maxDistance, listOfPoints);
1192 }
1193 
1194 QPointF VectorImage::getVertex(int curveNumber, int vertexNumber)
1195 {
1196  QPointF result = QPointF(11.11, 11.11); // bogus point
1197  if (curveNumber > -1 && curveNumber < m_curves.size())
1198  {
1199  BezierCurve myCurve = m_curves.at(curveNumber);
1200  if ( myCurve.isPartlySelected() ) myCurve = myCurve.transformed(mSelectionTransformation);
1201  if ( vertexNumber > -2 && vertexNumber < myCurve.getVertexSize())
1202  {
1203  result = myCurve.getVertex(vertexNumber);
1204  }
1205  }
1206  return result;
1207 }
1208 
1209 QPointF VectorImage::getVertex(VertexRef vertexRef)
1210 {
1211  return getVertex(vertexRef.curveNumber, vertexRef.vertexNumber);
1212 }
1213 
1214 QPointF VectorImage::getC1(int curveNumber, int vertexNumber)
1215 {
1216  QPointF result = QPointF(11.11, 11.11); // bogus point
1217  if (curveNumber > -1 && curveNumber < m_curves.size())
1218  {
1219  BezierCurve myCurve = m_curves.at(curveNumber);
1220  if ( myCurve.isPartlySelected() ) myCurve = myCurve.transformed(mSelectionTransformation);
1221  if ( vertexNumber > -1 && vertexNumber < myCurve.getVertexSize())
1222  {
1223  result = myCurve.getC1(vertexNumber);
1224  }
1225  }
1226  return result;
1227 }
1228 
1229 QPointF VectorImage::getC1(VertexRef vertexRef)
1230 {
1231  return getC1(vertexRef.curveNumber, vertexRef.vertexNumber);
1232 }
1233 
1234 QPointF VectorImage::getC2(int curveNumber, int vertexNumber)
1235 {
1236  QPointF result = QPointF(11.11, 11.11); // bogus point
1237  if (curveNumber > -1 && curveNumber < m_curves.size())
1238  {
1239  BezierCurve myCurve = m_curves.at(curveNumber);
1240  if ( myCurve.isPartlySelected() ) myCurve = myCurve.transformed(mSelectionTransformation);
1241  if ( vertexNumber > -1 && vertexNumber < myCurve.getVertexSize())
1242  {
1243  result = myCurve.getC2(vertexNumber);
1244  }
1245  }
1246  return result;
1247 }
1248 
1249 QPointF VectorImage::getC2(VertexRef vertexRef)
1250 {
1251  return getC2(vertexRef.curveNumber, vertexRef.vertexNumber);
1252 }
1253 
1254 QList<VertexRef> VectorImage::getCurveVertices(int curveNumber)
1255 {
1256  QList<VertexRef> result;
1257 
1258  if (curveNumber > -1 && curveNumber < m_curves.size())
1259  {
1260  BezierCurve myCurve = m_curves.at(curveNumber);
1261 
1262  for(int k=-1; k<myCurve.getVertexSize(); k++)
1263  {
1264  VertexRef vertexRef = VertexRef(curveNumber, k);
1265  result.append(vertexRef);
1266  }
1267  }
1268 
1269  return result;
1270 }
1271 
1272 QList<VertexRef> VectorImage::getAllVertices()
1273 {
1274  QList<VertexRef> result;
1275  for(int j=0; j<m_curves.size(); j++)
1276  {
1277  for(int k=-1; k<m_curves.at(j).getVertexSize(); k++)
1278  {
1279  VertexRef vertexRef = VertexRef(j, k);
1280  result.append(vertexRef);
1281  }
1282  }
1283  // Add Area vertices
1284  return result;
1285 }
1286 
1287 int VectorImage::getCurveSize(int curveNumber)
1288 {
1289  if ( curveNumber > -1 && curveNumber < m_curves.size())
1290  {
1291  return m_curves.at(curveNumber).getVertexSize();
1292  }
1293  else
1294  {
1295  return -1;
1296  }
1297 }
1298 
1299 void VectorImage::fillPath(QList<QPointF> contourPath, int colour, float tolerance)
1300 {
1301  QList<VertexRef> vertexPath;
1302 
1303  for (QPointF point : contourPath) {
1304  VertexRef vertex = getClosestVertexTo(point, tolerance);
1305  if (vertex.curveNumber != -1 && !vertexPath.contains(vertex)) {
1306  vertexPath.append(vertex);
1307  }
1308  }
1309 
1310  BezierArea bezierArea(vertexPath, colour);
1311  addArea( bezierArea );
1312  modification();
1313 }
1314 
1315 QList<QPointF> VectorImage::getfillContourPoints(QPoint point)
1316 {
1317  // We get the contour points from a bitmap version of the vector layer as it is much faster to process
1318  QImage* image = new QImage( mSize, QImage::Format_ARGB32_Premultiplied );
1319  image->fill(Qt::white);
1320  QPainter painter( image );
1321 
1322  // Adapt the QWidget view coordinates to the QImage coordinates
1323  QTransform translate;
1324  translate.translate( mSize.width() / 2.f , mSize.height() / 2.f );
1325  painter.setTransform( translate );
1326  paintImage( painter, true, true, false );
1327 
1328  QList<QPoint> queue; // queue all the pixels of the filled area (as they are found)
1329  QList<QPointF> contourPoints; // refs of points near the contour pixels
1330 
1331  qreal maxWidth = mSize.width();
1332  qreal maxHeight = mSize.height();
1333 
1334  // To keep track of the highest y contour point to make sure it is on the main contour and not inside.
1335  int highestY = point.y();
1336 
1337  // Convert point to image coordinates as the image doesn't have the same coordinates origin as the
1338  // QWidget view
1339  QPointF startPoint((maxWidth / 2) + point.x(), (maxHeight / 2) + point.y());
1340  queue.append( startPoint.toPoint() );
1341 
1342  // Check the colour of the clicked point
1343  QRgb colourFrom = image->pixel(startPoint.x(), startPoint.y());
1344  QRgb colourTo = Qt::green;
1345 
1346  QPoint currentPoint;
1347 
1348  int leftX, rightX;
1349  bool foundLeftBound, foundRightBound;
1350 
1351  // ----- flood fill and remember the contour pixels -> contourPixels
1352  // ----- from the standard flood fill algorithm
1353  // ----- http://en.wikipedia.org/wiki/Flood_fill
1354  while ( queue.size() > 0 ) {
1355 
1356  // Get the first point in the queue and remove it afterwards
1357  currentPoint = queue.at(0);
1358  queue.removeAt(0);
1359 
1360  // Inspect a line from edge to edge
1361  if (image->pixel(currentPoint.x(), currentPoint.y()) == colourFrom) {
1362  leftX = currentPoint.x();
1363  rightX = currentPoint.x();
1364 
1365  foundLeftBound = false;
1366  foundRightBound = false;
1367 
1368  while (!foundLeftBound) {
1369  leftX--;
1370 
1371  // Are we getting to the end of the document ?
1372  if ( leftX < 1) {
1373  qWarning() << " Out of bound left ";
1374  QList<QPointF> emptylist;
1375  return emptylist;
1376  }
1377 
1378  QPoint leftPoint = QPoint(leftX, currentPoint.y());
1379 
1380  // Are we getting to a curve ?
1381  if ( image->pixel(leftPoint.x(), leftPoint.y()) != colourFrom &&
1382  image->pixel(leftPoint.x(), leftPoint.y()) != colourTo ) {
1383 
1384  foundLeftBound = true;
1385 
1386  // Convert point to view coordinates
1387  QPointF contourPoint( leftPoint.x() - (maxWidth / 2), leftPoint.y() - (maxHeight / 2));
1388 
1389  // Check if the left bound is just a line crossing the main shape
1390  bool foundFillAfter = false;
1391  int increment = 1;
1392 
1393  while (leftPoint.x() - increment > 0 && increment < 3 && !foundFillAfter) {
1394  QPoint pointAfter = QPoint(leftPoint.x() - increment, leftPoint.y());
1395 
1396  if (image->pixel(pointAfter.x(), pointAfter.y()) == colourTo) {
1397  foundFillAfter = true;
1398  }
1399 
1400  increment++;
1401  }
1402 
1403  // If the bound is not a contour, we must ignore it
1404  if (foundFillAfter) {
1405 
1406  // If the bound is not a contour, we must ignore it
1407  contourPoints.removeOne(contourPoint);
1408  } else {
1409  contourPoints.append(contourPoint);
1410  }
1411  }
1412  }
1413 
1414  while (!foundRightBound) {
1415  rightX++;
1416 
1417  // Are we getting to the end of the document ?
1418  if ( rightX > maxWidth - 1 ) {
1419  qWarning() << " Out of bound right ";
1420  QList<QPointF> emptylist;
1421  return emptylist;
1422  }
1423 
1424  QPoint rightPoint = QPoint(rightX, currentPoint.y());
1425 
1426  // Are we getting to a curve ?
1427  if ( image->pixel(rightPoint.x(), rightPoint.y()) != colourFrom &&
1428  image->pixel(rightPoint.x(), rightPoint.y()) != colourTo) {
1429 
1430  foundRightBound = true;
1431 
1432  // Convert point to view coordinates
1433  QPointF contourPoint( rightPoint.x() - (maxWidth / 2), rightPoint.y() - (maxHeight / 2));
1434 
1435  // Check if the left bound is just a line crossing the main shape
1436  bool foundFillAfter = false;
1437  int increment = 1;
1438 
1439  while (rightPoint.x() + increment < maxWidth && increment < 3 && !foundFillAfter) {
1440  QPoint pointAfter = QPoint(rightPoint.x() + increment, rightPoint.y());
1441 
1442  if (image->pixel(pointAfter.x(), pointAfter.y()) == colourTo) {
1443  foundFillAfter = true;
1444  }
1445 
1446  increment++;
1447  }
1448 
1449  if (foundFillAfter) {
1450 
1451  // If the bound is not a contour, we must ignore it
1452  contourPoints.removeOne(contourPoint);
1453  } else {
1454  contourPoints.append(contourPoint);
1455  }
1456  }
1457  }
1458 
1459  int lineY = currentPoint.y();
1460  int topY = lineY - 1;
1461  int bottomY = lineY + 1;
1462 
1463  if ( topY < 1 || bottomY > maxHeight - 1 ) {
1464  qWarning() << " Out of bound top / bottom ";
1465  QList<QPointF> emptylist;
1466  return emptylist;
1467  }
1468 
1469  for (int x = leftX + 1; x < rightX; x++) {
1470 
1471  // The current line point is checked (Coloured)
1472  QPoint linePoint = QPoint(x, lineY);
1473  image->setPixel(linePoint.x(), linePoint.y(), colourTo);
1474 
1475  QPoint topPoint = QPoint(x, topY);
1476 
1477  if ( image->pixel(topPoint.x(), topPoint.y()) != colourFrom &&
1478  image->pixel(topPoint.x(), topPoint.y()) != colourTo) {
1479 
1480  // Convert point to view coordinates
1481  QPointF contourPoint( topPoint.x() - (maxWidth / 2), topPoint.y() - (maxHeight / 2));
1482 
1483  // Check if the left bound is just a line crossing the main shape
1484  bool foundFillAfter = false;
1485  int increment = 1;
1486 
1487  while (topPoint.y() - increment > 0 && increment < 3 && !foundFillAfter) {
1488  QPoint pointAfter = QPoint(topPoint.x(), topPoint.y() - increment);
1489 
1490  if (image->pixel(pointAfter.x(), pointAfter.y()) == colourTo) {
1491  foundFillAfter = true;
1492  }
1493  increment ++;
1494  }
1495 
1496 
1497  if (foundFillAfter) {
1498 
1499  // If the bound is not a contour, we must ignore it
1500  contourPoints.removeOne(contourPoint);
1501  } else {
1502  contourPoints.append(contourPoint);
1503  }
1504  } else {
1505  queue.append(topPoint);
1506  }
1507 
1508  QPoint bottomPoint = QPoint(x, bottomY);
1509 
1510  if ( image->pixel(bottomPoint.x(), bottomPoint.y()) != colourFrom &&
1511  image->pixel(bottomPoint.x(), bottomPoint.y()) != colourTo ) {
1512 
1513  QPointF contourPoint( bottomPoint.x() - (maxWidth / 2), bottomPoint.y() - (maxHeight / 2));
1514 
1515  // Check if the left bound is just a line crossing the main shape
1516  bool foundFillAfter = false;
1517  int increment = 1;
1518 
1519  while (bottomPoint.y() + increment < maxHeight && increment < 3 && !foundFillAfter) {
1520  QPoint pointAfter = QPoint(bottomPoint.x(), bottomPoint.y() + increment);
1521 
1522  if (image->pixel(pointAfter.x(), pointAfter.y()) == colourTo) {
1523  foundFillAfter = true;
1524  }
1525 
1526  increment++;
1527  }
1528 
1529  if (foundFillAfter) {
1530 
1531  // If the bound is not a contour, we must ignore it
1532  contourPoints.removeOne(contourPoint);
1533  }
1534  else {
1535 
1536  // Keep track of the highest Y position (lowest point) at the beginning of the list
1537  // so that we can parse the list from a point that is a real extremity.
1538  // of the area.
1539  if (highestY < bottomY) {
1540 
1541  highestY = bottomY;
1542  contourPoints.insert(0, contourPoint);
1543  } else {
1544  contourPoints.append(contourPoint);
1545  }
1546  }
1547 
1548  } else {
1549  queue.append(bottomPoint);
1550  }
1551  }
1552  }
1553  }
1554  return contourPoints;
1555 }
1556 
1557 void VectorImage::fill(QPointF point, int colour, float tolerance)
1558 {
1559  // Check if we clicked on a curve. In that case, we change its color.
1560  QList<int> closestCurves = getCurvesCloseTo( point, tolerance );
1561 
1562  if (closestCurves.size() > 0) // the user click on one or more curves
1563  {
1564  // For each clicked curves, we change the color if requiered
1565  for (int i = 0; i < closestCurves.size(); i++) {
1566  int curveNumber = closestCurves[i];
1567  m_curves[curveNumber].setColourNumber(colour);
1568  }
1569 
1570  return;
1571  }
1572 
1573  // Check if we clicked on an area of the same color.
1574  // We don't want to create another area.
1575  int areaNum = getLastAreaNumber(point);
1576  if (areaNum > -1 && area[areaNum].mColourNumber == colour) {
1577  return;
1578  }
1579 
1580  // Get the contour points
1581  QList<QPointF> contourPoints = getfillContourPoints(point.toPoint());
1582 
1583  // Make a path from the external contour points.
1584  // Put the points in the right order.
1585  QList<QPointF> mainContourPath;
1586  QPointF currentPoint;
1587 
1588  if (contourPoints.size() > 0) {
1589  currentPoint = QPointF(contourPoints[0].x(), contourPoints[0].y());
1590  mainContourPath.append(currentPoint);
1591  contourPoints.removeAt(0);
1592  }
1593 
1594  bool completedPath = false;
1595  bool foundError = (contourPoints.size() < 1 && !completedPath);
1596 
1597  int maxDelta = 2;
1598  int minDelta = -2;
1599 
1600  while (!completedPath && !foundError) {
1601 
1602  bool foundNextPoint = false;
1603 
1604  int i = 0;
1605  while (i < contourPoints.size() && !foundNextPoint) {
1606  QPointF point = contourPoints.at(i);
1607 
1608  if (mainContourPath.contains(point)) {
1609  contourPoints.removeAt(i);
1610  }
1611  else {
1612  qreal deltaX = currentPoint.x() - point.x();
1613  qreal deltaY = currentPoint.y() - point.y();
1614 
1615  if ( (deltaX < maxDelta && deltaX > minDelta) &&
1616  (deltaY < maxDelta && deltaY > minDelta)) {
1617 
1618  currentPoint = QPointF(point.x(), point.y());
1619  mainContourPath.append(currentPoint);
1620  contourPoints.removeAt(i);
1621 
1622  foundNextPoint = true;
1623 
1624  maxDelta = 2;
1625  minDelta = -2;
1626  }
1627  i++;
1628  }
1629  }
1630 
1631  // Check if we have looped
1632  if (!foundNextPoint) {
1633 
1634  qreal deltaX = currentPoint.x() - mainContourPath[0].x();
1635  qreal deltaY = currentPoint.y() - mainContourPath[0].y();
1636 
1637  if ( (deltaX < maxDelta && deltaX > minDelta) &&
1638  (deltaY < maxDelta && deltaY > minDelta)) {
1639  completedPath = true;
1640  foundNextPoint = true;
1641  }
1642  else if (maxDelta == 2){
1643  // Check if we can find the point after
1644  //
1645  maxDelta = 3;
1646  minDelta = -3;
1647  foundNextPoint = true;
1648  }
1649  else {
1650  qWarning() << " couldn't find next point after " << currentPoint.x() << ", " << currentPoint.y();
1651  }
1652  }
1653  else if (contourPoints.size() < 1) {
1654  // If we found the next point and we have no more points, it means, we have the end of the path
1655  completedPath = true;
1656  foundNextPoint = true;
1657  }
1658 
1659  foundError = ( (contourPoints.size() < 1 && !completedPath) || !foundNextPoint );
1660  }
1661 
1662  // Add exclude paths
1663 
1664  // Fill the path if we have one.
1665  if (completedPath) {
1666  fillPath(mainContourPath, colour, 10.0);
1667  }
1668  else {
1669  // Check if we clicked on an area in this position and as we couldn't create one,
1670  // we update this one. It may be an area drawn from a stroke path.
1671  int areaNum = getLastAreaNumber(point);
1672  if (areaNum > -1) {
1673 
1674  int clickedColorNum = area[areaNum].getColourNumber();
1675 
1676  if (clickedColorNum != colour) {
1677  area[areaNum].setColourNumber(colour);
1678  }
1679  }
1680  }
1681 }
1682 
1683 void VectorImage::addArea(BezierArea bezierArea)
1684 {
1685  updateArea(bezierArea);
1686  area.append( bezierArea );
1687  modification();
1688 }
1689 
1690 int VectorImage::getFirstAreaNumber(QPointF point)
1691 {
1692  int result = -1;
1693  for(int i=0; i<area.size() && result==-1; i++)
1694  {
1695  if ( area[i].mPath.controlPointRect().contains( point ) )
1696  {
1697  if ( area[i].mPath.contains( point ) )
1698  {
1699  result = i;
1700  }
1701  }
1702  }
1703  return result;
1704 }
1705 
1706 int VectorImage::getLastAreaNumber(QPointF point)
1707 {
1708  return getLastAreaNumber(point, area.size()-1);
1709 }
1710 
1711 int VectorImage::getLastAreaNumber(QPointF point, int maxAreaNumber)
1712 {
1713  int result = -1;
1714  for(int i=maxAreaNumber; i>-1 && result==-1; i--)
1715  {
1716  if ( area[i].mPath.controlPointRect().contains( point ) )
1717  {
1718  if ( area[i].mPath.contains( point ) )
1719  {
1720  result = i;
1721  }
1722  }
1723  }
1724  return result;
1725 }
1726 
1727 void VectorImage::removeArea(QPointF point)
1728 {
1729  int areaNumber = getLastAreaNumber(point);
1730  if ( areaNumber != -1)
1731  {
1732  area.removeAt(areaNumber);
1733  }
1734  modification();
1735 }
1736 
1737 void VectorImage::updateArea(BezierArea& bezierArea)
1738 {
1739  QPainterPath newPath;
1740  for(int i=0; i<bezierArea.mVertex.size(); i++)
1741  {
1742  QPointF myPoint = getVertex(bezierArea.mVertex[i]);
1743  QPointF myC1;
1744  QPointF myC2;
1745 
1746  if (i==0)
1747  {
1748  newPath.moveTo( myPoint );
1749  }
1750  else
1751  {
1752  if (bezierArea.mVertex[i-1].curveNumber == bezierArea.mVertex[i].curveNumber ) // the two points are on the same curve
1753  {
1754  if (bezierArea.mVertex[i-1].vertexNumber < bezierArea.mVertex[i].vertexNumber ) // the points follow the curve progression
1755  {
1756  myC1 = getC1(bezierArea.mVertex[i]);
1757  myC2 = getC2(bezierArea.mVertex[i]);
1758  }
1759  else
1760  {
1761  myC1 = getC2(bezierArea.mVertex[i-1]);
1762  myC2 = getC1(bezierArea.mVertex[i-1]);
1763  }
1764  newPath.cubicTo(myC1, myC2, myPoint);
1765  }
1766  else // the two points are not the same curve
1767  {
1768  if ( bezierArea.mVertex[i].vertexNumber == -1) // the current point is the first point in the new curve
1769  {
1770  newPath.lineTo( myPoint );
1771  }
1772  else
1773  {
1774  newPath.lineTo( myPoint );
1775  }
1776  }
1777  }
1778  }
1779  newPath.closeSubpath();
1780  bezierArea.mPath = newPath;
1781  bezierArea.mPath.setFillRule( Qt::WindingFill );
1782 }
1783 
1784 qreal VectorImage::getDistance(VertexRef r1, VertexRef r2)
1785 {
1786  return BezierCurve::eLength(getVertex(r1)-getVertex(r2));
1787 }
1788 
1789 void VectorImage::updateImageSize(BezierCurve& updatedCurve) {
1790 
1791  // Set the current width of the document based on the extremity of the drawing.
1792  //
1793  // It calculates the size of the document in a way that the center point from
1794  // the view (0, 0) is always the center point of the document.
1795  //
1796  // It adds a point to the 4 sides of the document size in order
1797  // make sure that any curve, any vertex stays within the document.
1798  //
1799 
1800  QRectF rect = updatedCurve.getBoundingRect();
1801 
1802  QPoint topLeft = rect.topLeft().toPoint();
1803  QPoint bottomRight = rect.bottomRight().toPoint();
1804 
1805  int widthFromLeft = ((topLeft.x() * -1) * 2) + 2;
1806 
1807  if (widthFromLeft > mSize.width()) {
1808  mSize.setWidth(widthFromLeft);
1809  }
1810 
1811  int widthFromRight = (bottomRight.x() * 2) + 2;
1812 
1813  if (widthFromRight > mSize.width()) {
1814  mSize.setWidth(widthFromRight);
1815  }
1816 
1817  int heightFromTop = ((topLeft.y() * -1) * 2) + 2;
1818 
1819  if (heightFromTop > mSize.height()) {
1820  mSize.setHeight(heightFromTop);
1821  }
1822 
1823  int heightFromBottom = (bottomRight.y() * 2) + 2;
1824 
1825  if (heightFromBottom > mSize.height()) {
1826  mSize.setHeight(heightFromBottom);
1827  }
1828 }