All Classes Namespaces Functions Variables Enumerations Properties Pages
vectorimage.cpp
1 /*
2 
3 Pencil2D - Traditional Animation Software
4 Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5 Copyright (C) 2012-2020 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 "vectorimage.h"
18 
19 #include <cmath>
20 #include <QImage>
21 #include <QFile>
22 #include <QFileInfo>
23 #include <QDebug>
24 #include <QXmlStreamWriter>
25 #include "object.h"
26 
27 
28 VectorImage::VectorImage()
29 {
30  deselectAll();
31 }
32 
33 VectorImage::VectorImage(const VectorImage& v2) : KeyFrame(v2)
34 {
35  deselectAll();
36  mObject = v2.mObject;
37  mCurves = v2.mCurves;
38  mArea = v2.mArea;
39 }
40 
41 VectorImage::~VectorImage()
42 {
43 }
44 
45 VectorImage& VectorImage::operator=(const VectorImage& a) {
46  if (this != &a) {
47  deselectAll();
48  mObject = a.mObject;
49  mCurves = a.mCurves;
50  mArea = a.mArea;
51  modification();
52  }
53  return *this;
54 }
55 
56 VectorImage* VectorImage::clone()
57 {
58  return new VectorImage(*this);
59 }
60 
65 bool VectorImage::read(QString filePath)
66 {
67  QFileInfo fileInfo(filePath);
68  if (fileInfo.isDir())
69  {
70  return false;
71  }
72 
73  QFile file(filePath);
74  if (!file.open(QFile::ReadOnly))
75  {
76  return false;
77  }
78 
79  QDomDocument doc;
80  if (!doc.setContent(&file)) return false; // this is not a XML file
81  QDomDocumentType type = doc.doctype();
82  if (type.name() != "PencilVectorImage") return false; // this is not a Pencil document
83 
84  QDomElement element = doc.documentElement();
85  if (element.tagName() == "image")
86  {
87  if (element.attribute("type") == "vector")
88  {
89  loadDomElement(element);
90  }
91  }
92 
93  setFileName(filePath);
94  setModified(false);
95  return true;
96 }
97 
105 {
106  DebugDetails debugInfo;
107  debugInfo << "VectorImage::write";
108  debugInfo << QString("filePath = ").append(filePath);
109  debugInfo << QString("format = ").append(format);
110 
111  QFile file(filePath);
112  bool result = file.open(QIODevice::WriteOnly);
113  if (!result)
114  {
115  qDebug() << "VectorImage - Cannot write file" << filePath << file.error();
116  debugInfo << ("file.error() = " + file.errorString());
117  return Status(Status::FAIL, debugInfo);
118  }
119 
120  if (format != "VEC")
121  {
122  debugInfo << "Unrecognized format";
123  return Status(Status::FAIL, debugInfo);
124  }
125 
126  QXmlStreamWriter xmlStream(&file);
127  xmlStream.setAutoFormatting(true);
128  xmlStream.writeStartDocument();
129  xmlStream.writeDTD("<!DOCTYPE PencilVectorImage>");
130 
131  xmlStream.writeStartElement("image");
132  xmlStream.writeAttribute("type", "vector");
133 
134  Status st = createDomElement(xmlStream);
135  if (!st.ok())
136  {
137  debugInfo.collect(st.details());
138  debugInfo << "- xml creation failed";
139  return Status(Status::FAIL, debugInfo);
140  }
141  xmlStream.writeEndElement(); // Close image element
142  xmlStream.writeEndDocument();
143 
144  setFileName(filePath);
145  return Status::OK;
146 }
147 
154 {
155  DebugDetails debugInfo;
156  debugInfo << "VectorImage::createDomElement";
157 
158  for (int i = 0; i < mCurves.size(); i++)
159  {
160  Status st = mCurves[i].createDomElement(xmlStream);
161  if (!st.ok())
162  {
163  debugInfo.collect(st.details());
164  debugInfo << QString("- m_curves[%1] failed to write").arg(i);
165  return Status(Status::FAIL, debugInfo);
166  }
167  }
168  for (int i = 0; i < mArea.size(); i++)
169  {
170  Status st = mArea[i].createDomElement(xmlStream);
171  if (!st.ok())
172  {
173  debugInfo.collect(st.details());
174  debugInfo << QString("- area[%1] failed to write").arg(i);
175  return Status(Status::FAIL, debugInfo);
176  }
177  }
178  return Status::OK;
179 }
180 
186 {
187  QDomNode atomTag = element.firstChild(); // an atom in a vector picture is a curve or an area
188  while (!atomTag.isNull())
189  {
190  QDomElement atomElement = atomTag.toElement();
191  if (!atomElement.isNull())
192  {
193  if (atomElement.tagName() == "curve")
194  {
195  BezierCurve newCurve;
196  newCurve.loadDomElement(atomElement);
197  mCurves.append(newCurve);
198  }
199  if (atomElement.tagName() == "area")
200  {
201  BezierArea newArea;
202  newArea.loadDomElement(atomElement);
203  addArea(newArea);
204  }
205  }
206  atomTag = atomTag.nextSibling();
207  }
208  clean();
209 }
210 
211 BezierCurve& VectorImage::curve(int i)
212 {
213  return mCurves[i];
214 }
215 
222 void VectorImage::addPoint(int curveNumber, int vertexNumber, qreal fraction)
223 {
224  mCurves[curveNumber].addPoint(vertexNumber, fraction);
225  // updates the bezierAreas
226  for (int j = 0; j < mArea.size(); j++)
227  {
228  // shift the references of all the points beyond the new point
229  for (int k = 0; k < mArea.at(j).mVertex.size(); k++)
230  {
231  if (mArea[j].getVertexRef(k).curveNumber == curveNumber)
232  {
233  if (mArea[j].getVertexRef(k).vertexNumber >= vertexNumber)
234  {
235  mArea[j].mVertex[k].vertexNumber++;
236  }
237  }
238  }
239  // insert the new point in the area if necessary
240  for (int k = 1; k < mArea.at(j).mVertex.size(); k++)
241  {
242  if (VertexRef(curveNumber, vertexNumber + 1) == mArea.at(j).mVertex.at(k)) // area[j].vertex[k] == VertexRef(curveNumber, vertexNumber+1)
243  {
244  if (VertexRef(curveNumber, vertexNumber - 1) == mArea.at(j).mVertex.at(k - 1))
245  {
246  mArea[j].mVertex.insert(k, VertexRef(curveNumber, vertexNumber));
247  }
248  }
249  if (VertexRef(curveNumber, vertexNumber - 1) == mArea.at(j).mVertex.at(k))
250  {
251  if (VertexRef(curveNumber, vertexNumber + 1) == mArea.at(j).mVertex.at(k - 1))
252  {
253  mArea[j].mVertex.insert(k, VertexRef(curveNumber, vertexNumber));
254  }
255  }
256  }
257  }
258  modification();
259 }
260 
266 {
267  // first change the curve numbers in the areas
268  for (int j = 0; j < mArea.size(); j++)
269  {
270  for (int k = 0; k < mArea.at(j).mVertex.size(); k++)
271  {
272  if (mArea.at(j).mVertex[k].curveNumber > i) { mArea[j].mVertex[k].curveNumber--; }
273  }
274  }
275  // then remove curve
276  mCurves.removeAt(i);
277  modification();
278 }
279 
287 void VectorImage::insertCurve(int position, BezierCurve& newCurve, qreal factor, bool interacts)
288 {
289  if (newCurve.getVertexSize() < 1) // security - a new curve should have a least 2 vertices
290  return;
291 
292  // Does the curve interact with others or with itself?
293  if (interacts)
294  {
295  // tolerance for taking the intersection as an existing vertex on a curve
296  qreal tol = qMax(newCurve.getWidth() / factor, 3.0 / factor);
297  //qDebug() << "tolerance" << tol;
298 
299  checkCurveExtremity(newCurve, tol);
300  checkCurveIntersections(newCurve, tol);
301  }
302 
303 
304  // Append or insert the curve in the list
305  if (position < 0 || position > mCurves.size() - 1)
306  {
307  mCurves.append(newCurve);
308  }
309  else
310  {
311  // If it's an insert we have to shift the curve numbers in the areas
312  for (int i = 0; i < mArea.size(); i++)
313  {
314  for (int j = 0; j < mArea.at(i).mVertex.size(); j++)
315  {
316  if (mArea.at(i).mVertex[j].curveNumber >= position) {
317  mArea[i].mVertex[j].curveNumber++;
318  }
319  }
320  }
321  mCurves.insert(position, newCurve);
322  }
323  updateImageSize(newCurve);
324  modification();
325 }
326 
333 void VectorImage::addCurve(BezierCurve& newCurve, qreal factor, bool interacts)
334 {
335  insertCurve(-1, newCurve, factor, interacts);
336 }
337 
343 void VectorImage::checkCurveExtremity(BezierCurve& newCurve, qreal tolerance)
344 {
345  // finds if the new curve is closed
346  QPointF P = newCurve.getVertex(-1);
347  QPointF Q = newCurve.getVertex(newCurve.getVertexSize() - 1);
348  if (BezierCurve::eLength(P - Q) < tolerance)
349  {
350  newCurve.setVertex(newCurve.getVertexSize() - 1, P);
351  }
352 
353  // finds if the first or last point of the new curve is close to other curves
354  for (int i = 0; i < mCurves.size(); i++) // for each other curve
355  {
356  for (int j = 0; j < mCurves.at(i).getVertexSize(); j++) // for each cubic section of the other curve
357  {
358  QPointF P = newCurve.getVertex(-1);
359  QPointF Q = newCurve.getVertex(newCurve.getVertexSize() - 1);
360  QPointF P1 = mCurves.at(i).getVertex(j - 1);
361  QPointF P2 = mCurves.at(i).getVertex(j);
362  qreal tol3 = 2.0*sqrt(0.25*((P1 - P2).x()*(P1 - P2).x() + (P1 - P2).y()*(P1 - P2).y()) + tolerance*tolerance);
363  qreal dist1 = BezierCurve::eLength(P - P1);
364  qreal dist2 = BezierCurve::eLength(P - P2);
365  if (dist1 <= 0.2*tolerance)
366  {
367  newCurve.setVertex(-1, P1); //qDebug() << "--b " << P1;
368  }
369  else
370  {
371  if (dist2 <= 0.2*tolerance)
372  {
373  newCurve.setVertex(-1, P2); //qDebug() << "--c " << P2;
374  }
375  else
376  {
377  if (dist1 + dist2 <= 3 * tol3) // preselection, to speed up
378  {
379  QPointF nearestPoint = P;
380  qreal t = -1.0;
381  qreal distance = BezierCurve::findDistance(mCurves[i], j, P, nearestPoint, t);
382  if (distance < tolerance)
383  {
384  newCurve.setOrigin(nearestPoint); //qDebug() << "--d " << nearestPoint;
385  addPoint(i, j, t);
386  }
387  }
388  }
389  }
390 
391  dist1 = BezierCurve::eLength(Q - P1);
392  dist2 = BezierCurve::eLength(Q - P2);
393  if (dist1 <= 0.2*tolerance)
394  {
395  newCurve.setLastVertex(P1); //qDebug() << "--e " << P1;
396  }
397  else
398  {
399  if (dist2 <= 0.2*tolerance)
400  {
401  newCurve.setLastVertex(P2); //qDebug() << "--f " << P2;
402  }
403  else
404  {
405  if (dist1 + dist2 <= 3 * tol3) // pre-selection, to speed up
406  {
407  QPointF nearestPoint = Q;
408  qreal t = -1.0;;
409  qreal distance = BezierCurve::findDistance(mCurves[i], j, Q, nearestPoint, t);
410  if (distance < tolerance)
411  {
412  newCurve.setLastVertex(nearestPoint); //qDebug() << "--g " << nearestPoint;
413  addPoint(i, j, t);
414  }
415  }
416  }
417  //qDebug() << "Modif last";
418  }
419  }
420  }
421  modification();
422 }
423 
429 void VectorImage::checkCurveIntersections(BezierCurve& newCurve, qreal tolerance)
430 {
431  // finds if the new curve intersects itself
432  for (int k = 0; k < newCurve.getVertexSize(); k++) // for each cubic section of the new curve
433  {
434  for (int j = k + 1; j < newCurve.getVertexSize(); j++) // for each other cubic section of the new curve
435  {
436  QList<Intersection> intersections;
437  bool intersection = BezierCurve::findIntersection(newCurve, k, newCurve, j, intersections);
438  if (intersection)
439  {
440  //qDebug() << "INTERSECTION" << intersectionPoint << t1 << t2;
441  //newCurve.addPoint(k, intersectionPoint);
442  newCurve.addPoint(k, intersections[0].t1); //qDebug() << "--a " << newCurve.getVertex(k) << newCurve.getVertex(k+1);
443  k++;
444  j++;
445  //newCurve.addPoint(j, intersectionPoint);
446  newCurve.addPoint(j, intersections[0].t2); //qDebug() << "--a " << newCurve.getVertex(j) << newCurve.getVertex(j+1);
447  j++;
448  }
449  }
450  }
451 
452  // finds if the new curve interesects other curves
453  for (int k = 0; k < newCurve.getVertexSize(); k++) // for each cubic section of the new curve
454  {
455  //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
456  //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
457  //QPointF extension1 = 1.5*tol*(P1-Q1)/BezierCurve::eLength(P1-Q1);
458  //L1 = QLineF(P1 + extension1, Q1 - extension1);
459  for (int i = 0; i < mCurves.size(); i++) // for each other curve // TO DO: should only loop on "nearby" curves instead of all
460  {
461  // ---- finds if the first or last point of the other curve is close to the current cubic section of the new curve
462  QPointF P = mCurves.at(i).getVertex(-1);
463  QPointF Q = mCurves.at(i).getVertex(mCurves.at(i).getVertexSize() - 1);
464  QPointF P1 = newCurve.getVertex(k - 1);
465  QPointF P2 = newCurve.getVertex(k);
466  qreal tol3 = 2.0*sqrt(0.25*((P1 - P2).x()*(P1 - P2).x() + (P1 - P2).y()*(P1 - P2).y()) + tolerance*tolerance);
467  qreal dist1 = BezierCurve::eLength(P - P1);
468  qreal dist2 = BezierCurve::eLength(P - P2);
469 
470  if (dist1 < 0.2*tolerance)
471  {
472  mCurves[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
473  }
474  else
475  {
476  if (dist2 < 0.2*tolerance)
477  {
478  mCurves[i].setVertex(-1, P2);
479  }
480  else
481  {
482  if (dist1 + dist2 < 3 * tol3)
483  {
484  // TO DO: find a better intersection point
485  QPointF nearestPoint = P;
486  qreal t = -1.0;
487  qreal distance = BezierCurve::findDistance(newCurve, k, P, nearestPoint, t);
488  //qDebug() << "OK1" << t;
489  if (distance < tolerance)
490  {
491  P = nearestPoint;
492  //m_curves[i].setOrigin(P);
493  //newCurve.addPoint(k, P); //qDebug() << "--i " << P;
494  }
495  }
496  }
497  //qDebug() << "Modif first";
498  }
499  dist1 = BezierCurve::eLength(Q - P1);
500  dist2 = BezierCurve::eLength(Q - P2);
501  if (dist1 < 0.2*tolerance)
502  {
503  mCurves[i].setVertex(mCurves.at(i).getVertexSize() - 1, P1);
504  }
505  else
506  {
507  if (dist2 < 0.2*tolerance)
508  {
509  mCurves[i].setVertex(mCurves.at(i).getVertexSize() - 1, P2);
510  }
511  else
512  {
513  if (dist1 + dist2 < 3 * tol3)
514  {
515  // TO DO: find a better intersection point
516  QPointF nearestPoint = Q;
517  qreal t = -1.0;;
518  qreal distance = BezierCurve::findDistance(newCurve, k, Q, nearestPoint, t);
519  //qDebug() << "OK2" << t;
520  if (distance < tolerance)
521  {
522  Q = nearestPoint;
523  //m_curves[i].setLastVertex(Q);
524  //newCurve.addPoint(k, Q); //qDebug() << "--j " << Q;
525  }
526  }
527  }
528  //qDebug() << "Modif first";
529  }
530 
531  // ---- finds if any cubic section of the other curve intersects the current cubic section of the new curve
532  for (int j = 0; j < mCurves.at(i).getVertexSize(); j++) // for each cubic section of the other curve
533  {
534  QList<Intersection> intersections;
535  bool intersection = BezierCurve::findIntersection(newCurve, k, mCurves.at(i), j, intersections);
536  if (intersection)
537  {
538  //qDebug() << "Found " << intersections.size() << " intersections";
539  QPointF intersectionPoint = intersections[0].point;
540  qreal t1 = intersections[0].t1;
541  qreal t2 = intersections[0].t2;
542  if (BezierCurve::eLength(intersectionPoint - newCurve.getVertex(k - 1)) <= 0.1*tolerance) // the first point is close to the intersection
543  {
544  newCurve.setVertex(k - 1, intersectionPoint); //qDebug() << "--k " << intersectionPoint;
545  //qDebug() << "--------- recal " << k-1 << intersectionPoint;
546  }
547  else
548  {
549  if (BezierCurve::eLength(intersectionPoint - newCurve.getVertex(k)) <= 0.1*tolerance) // the second point is close to the intersection
550  {
551  newCurve.setVertex(k, intersectionPoint); //qDebug() << "--l " << intersectionPoint;
552  //qDebug() << "-------- recal " << k << intersectionPoint;
553  }
554  else // none of the point is close to the intersection -> we add a new point
555  {
556  //newCurve.addPoint(k, intersectionPoint);
557  newCurve.addPoint(k, t1); //qDebug() << "--m " << newCurve.getVertex(k);
558  //qDebug() << "----- add " << k << newCurve.getVertex(k);
559  //k++;
560  }
561  }
562  if (BezierCurve::eLength(intersectionPoint - mCurves.at(i).getVertex(j - 1)) <= 0.1*tolerance) // the first point is close to the intersection
563  {
564  mCurves[i].setVertex(j - 1, intersectionPoint); //qDebug() << "--n " << intersectionPoint;
565  //qDebug() << "-------- recal2 " << j-1 << intersectionPoint;
566  }
567  else
568  {
569  if (BezierCurve::eLength(intersectionPoint - mCurves.at(i).getVertex(j)) <= 0.1*tolerance) // the second point is close to the intersection
570  {
571  mCurves[i].setVertex(j, intersectionPoint); //qDebug() << "--o " << intersectionPoint;
572  //qDebug() << "-------- recal2 " << j << intersectionPoint;
573  }
574  else // none of the point is close to the intersection -> we add a new point
575  {
576  addPoint(i, j, t2);
577  //qDebug() << "----- add2 " << j << curve[i].getVertex(j);
578  //j++;
579  }
580  }
581  }
582  }
583  }
584  }
585 }
586 
587 void VectorImage::select(QRectF rectangle)
588 {
589  for (int i = 0; i < mCurves.size(); i++)
590  {
591  bool bSelected = mCurves[i].intersects(rectangle);
592  setSelected(i, bSelected);
593  }
594 
595  for (int i = 0; i < mArea.size(); i++)
596  {
597  bool b = rectangle.contains(mArea[i].mPath.boundingRect());
598  setAreaSelected(i, b);
599  }
600  modification();
601 }
602 
608 void VectorImage::setSelected(int curveNumber, bool YesOrNo)
609 {
610  if (mCurves.isEmpty()) return;
611 
612  mCurves[curveNumber].setSelected(YesOrNo);
613 
614  if (YesOrNo)
615  mSelectionRect |= mCurves[curveNumber].getBoundingRect();
616  modification();
617 }
618 
625 void VectorImage::setSelected(int curveNumber, int vertexNumber, bool YesOrNo)
626 {
627  if (mCurves.isEmpty()) return;
628  mCurves[curveNumber].setSelected(vertexNumber, YesOrNo);
629  QPointF vertex = getVertex(curveNumber, vertexNumber);
630  if (YesOrNo) mSelectionRect |= QRectF(vertex.x(), vertex.y(), 0.0, 0.0);
631 
632  modification();
633 }
634 
640 void VectorImage::setSelected(VertexRef vertexRef, bool YesOrNo)
641 {
642  setSelected(vertexRef.curveNumber, vertexRef.vertexNumber, YesOrNo);
643 }
644 
650 void VectorImage::setSelected(QList<int> curveList, bool YesOrNo)
651 {
652  for (int i = 0; i < curveList.size(); i++)
653  {
654  setSelected(curveList.at(i), YesOrNo);
655  }
656 }
657 
663 void VectorImage::setSelected(QList<VertexRef> vertexList, bool YesOrNo)
664 {
665  for (int i = 0; i < vertexList.size(); i++)
666  {
667  setSelected(vertexList.at(i), YesOrNo);
668  }
669 }
670 
676 void VectorImage::setAreaSelected(int areaNumber, bool YesOrNo)
677 {
678  mArea[areaNumber].setSelected(YesOrNo);
679  if (YesOrNo) mSelectionRect |= mArea[areaNumber].mPath.boundingRect();
680  modification();
681 }
682 
688 bool VectorImage::isAreaSelected(int areaNumber)
689 {
690  return mArea[areaNumber].isSelected();
691 }
692 
698 {
699  bool filled = false;
700  QList<int> curveNumbers = getSelectedCurveNumbers();
701  for (int curveNum : curveNumbers)
702  {
703  //qDebug() << mCurves[curveNum].isFilled();
704  if (mCurves[curveNum].isSelected())
705  {
706  // FIXME: something wrong here.
707  filled = mCurves[curveNum].isFilled();
708  }
709  }
710  return filled;
711 }
712 
718 bool VectorImage::isSelected(int curveNumber)
719 {
720  return mCurves[curveNumber].isSelected();
721 }
722 
729 bool VectorImage::isSelected(int curveNumber, int vertexNumber)
730 {
731  return mCurves[curveNumber].isSelected(vertexNumber);
732 }
733 
740 {
741  return isSelected(vertexRef.curveNumber, vertexRef.vertexNumber);
742 }
743 
750 {
751  bool result = true;
752  for (int i = 0; i < curveList.size(); i++)
753  {
754  result &= isSelected(curveList.at(i));
755  }
756  return result;
757 }
758 
765 {
766  bool result = true;
767  for (int i = 0; i < vertexList.size(); i++)
768  {
769  result &= isSelected(vertexList.at(i));
770  }
771  return result;
772 }
773 
779 {
780  int result = -1;
781  for (int i = 0; i < mCurves.size() && result == -1; i++)
782  {
783  if (isSelected(i)) result = i;
784  }
785  return result;
786 }
787 
793 {
794  int result = -1;
795  for (int i = 0; i < mArea.size() && result == -1; i++)
796  {
797  if (isAreaSelected(i)) result = i;
798  }
799  return result;
800 }
801 
806 {
807  for (int i = 0; i < mCurves.size(); i++)
808  {
809  setSelected(i, true);
810  }
811  mSelectionTransformation.reset();
812 }
813 
819 {
820  if (mCurves.isEmpty()) return false;
821  for (int curve = 0; curve < mCurves.size(); curve++)
822  {
823  if (mCurves[curve].isSelected()) return true;
824  }
825  return false;
826 }
827 
832 {
833  if (mCurves.empty()) return;
834  for (int i = 0; i < mCurves.size(); i++)
835  {
836  mCurves[i].setSelected(false);
837  }
838  for (int i = 0; i < mArea.size(); i++)
839  {
840  mArea[i].setSelected(false);
841  }
842  mSelectionRect = QRectF(0, 0, 0, 0);
843  mSelectionTransformation.reset();
844  modification();
845 }
846 
852 {
853  mSelectionRect = rectangle;
854  select(rectangle);
855 }
856 
861 {
862  mSelectionRect = QRectF(0, 0, 0, 0);
863  for (int i = 0; i < mCurves.size(); i++)
864  {
865  if (mCurves.at(i).isPartlySelected())
866  mSelectionRect |= mCurves[i].getBoundingRect();
867  }
868 }
869 
875 {
876  mSelectionTransformation = transform;
877  modification();
878 }
879 
884 {
885  // ---- deletes areas
886  for (int i = 0; i < mArea.size(); i++)
887  {
888  if (mArea[i].isSelected())
889  {
890  mArea.removeAt(i);
891  i--;
892  }
893  }
894  // ---- deletes curves
895  for (int i = 0; i < mCurves.size(); i++)
896  {
897  if (mCurves[i].isSelected())
898  {
899  // eliminates areas which are associated to this curve
900  for (int j = 0; j < mArea.size(); j++)
901  {
902  bool toBeDeleted = false;
903  for (int k = 0; k < mArea.at(j).mVertex.size(); k++)
904  {
905  if (mArea.at(j).mVertex[k].curveNumber == i) { toBeDeleted = true; }
906  if (mArea.at(j).mVertex[k].curveNumber > i)
907  {
908  mArea[j].mVertex[k].curveNumber = mArea[j].mVertex[k].curveNumber - 1;
909  }
910  }
911  if (toBeDeleted)
912  {
913  mArea.removeAt(j);
914  j--;
915  }
916  }
917  mCurves.removeAt(i);
918  i--;
919  }
920  }
921  modification();
922 }
923 
929 void VectorImage::removeVertex(int curve, int vertex)
930 {
931  // first eliminates areas which are associated to this point
932  for (int j = 0; j < mArea.size(); j++)
933  {
934  bool toBeDeleted = false;
935  for (int k = 0; k < mArea.at(j).mVertex.size(); k++)
936  {
937  if (mArea.at(j).mVertex[k].curveNumber == curve && mArea.at(j).mVertex[k].vertexNumber == vertex) { toBeDeleted = true; }
938  //if (area.at(j).vertex[k].curveNumber > i) { area[j].vertex[k].curveNumber = area[j].vertex[k].curveNumber - 1; }
939  }
940  if (toBeDeleted)
941  {
942  mArea.removeAt(j);
943  j--;
944  }
945  }
946  // then eliminates the point
947  if (mCurves[curve].getVertexSize() > 1)
948  {
949  // second possibility: we split the curve into two parts:
950  if (vertex == -1 || vertex == getCurveSize(curve) - 1) // we just remove the first or last point
951  {
952  mCurves[curve].removeVertex(vertex);
953  vertex--;
954  // we also need to update the areas
955  for (int j = 0; j < mArea.size(); j++)
956  {
957  for (int k = 0; k < mArea.at(j).mVertex.size(); k++)
958  {
959  if (mArea.at(j).mVertex[k].curveNumber == curve && mArea.at(j).mVertex[k].vertexNumber > vertex) { mArea[j].mVertex[k].vertexNumber--; }
960  }
961  }
962  }
963  else
964  {
965  int n = getCurveSize(curve);
966  BezierCurve newCurve = mCurves.at(curve); // duplicate curve
967  for (int p = vertex; p < n; p++) // removes the end of of the curve i (after m, included) -> left part
968  {
969  mCurves[curve].removeVertex(getCurveSize(curve) - 1);
970  }
971  for (int p = -1; p <= vertex; p++) // removes the beginning of the new curve (before m, included) -> right part
972  {
973  newCurve.removeVertex(-1);
974  }
975  //if (newCurve.getVertexSize() > 0) curve.insert(i+1, newCurve);
976  if (newCurve.getVertexSize() > 0) mCurves.append(newCurve); // insert the right part if it has more than one point
977  // we also need to update the areas
978  for (int j = 0; j < mArea.size(); j++)
979  {
980  for (int k = 0; k < mArea.at(j).mVertex.size(); k++)
981  {
982  if (mArea.at(j).mVertex[k].curveNumber == curve && mArea.at(j).mVertex[k].vertexNumber > vertex)
983  {
984  mArea[j].mVertex[k].curveNumber = mCurves.size() - 1;
985  mArea[j].mVertex[k].vertexNumber = mArea[j].mVertex[k].vertexNumber - vertex - 1;
986  }
987  }
988  }
989 
990  if (getCurveSize(curve) < 1) // the left part has less than two points so we remove it
991  {
992  removeCurveAt(curve);
993  curve--;
994  }
995  }
996  }
997  else // there are just two points left, so we remove the whole curve
998  {
999  removeCurveAt(curve);
1000  curve--;
1001  }
1002 }
1003 
1008 {
1009  for (int i = 0; i < mCurves.size(); i++)
1010  {
1011  for (int m = -1; m < getCurveSize(i); m++)
1012  {
1013  if (mCurves.at(i).isSelected(m)) // point m of curve i is selected
1014  {
1015  removeVertex(i, m);
1016  }
1017  }
1018  }
1019  modification();
1020 }
1021 
1027 {
1028  mSelectionRect = QRect(0, 0, 0, 0);
1029  int n = mCurves.size();
1030  QList<int> selectedCurves;
1031 
1032  bool hasSelection = getFirstSelectedCurve() < -1;
1033 
1034  for (int i = 0; i < vectorImage.mCurves.size(); i++)
1035  {
1036  // If nothing is selected, paste everything
1037  if (!hasSelection || vectorImage.mCurves.at(i).isSelected())
1038  {
1039  mCurves.append(vectorImage.mCurves.at(i));
1040  selectedCurves << i;
1041  mSelectionRect |= vectorImage.mCurves[i].getBoundingRect();
1042  }
1043  }
1044  for (int i = 0; i < vectorImage.mArea.size(); i++)
1045  {
1046  BezierArea newArea = vectorImage.mArea.at(i);
1047  bool ok = true;
1048  for (int j = 0; j < newArea.mVertex.size(); j++)
1049  {
1050  int curveNumber = newArea.mVertex.at(j).curveNumber;
1051  int vertexNumber = newArea.mVertex.at(j).vertexNumber;
1052 
1053  // If nothing is selected, paste everything
1054  //
1055  if (!hasSelection || vectorImage.mCurves.at(curveNumber).isSelected())
1056  {
1057  newArea.mVertex[j] = VertexRef(selectedCurves.indexOf(curveNumber) + n, vertexNumber);
1058  }
1059  else
1060  {
1061  ok = false;
1062  }
1063  }
1064  if (ok) mArea.append(newArea);
1065  }
1066  modification();
1067 }
1068 
1075 {
1076  return mObject->getColor(colorNumber).color;
1077 }
1078 
1085 {
1086  int result = -1;
1087  int areaNumber = getLastAreaNumber(point);
1088  if (areaNumber != -1)
1089  {
1090  result = mArea[areaNumber].mColorNumber;
1091  }
1092  return result;
1093 }
1100 {
1101  int result = -1;
1102  if (curve > -1)
1103  {
1104  result = mCurves[curve].getColorNumber();
1105  }
1106  return result;
1107 }
1108 
1109 bool VectorImage::isCurveVisible(int curve)
1110 {
1111  if (curve > -1 && curve < mCurves.length())
1112  {
1113  return !mCurves[curve].isInvisible();
1114  }
1115  return false;
1116 }
1117 
1123 bool VectorImage::usesColor(int index)
1124 {
1125  for (int i = 0; i < mArea.size(); i++)
1126  {
1127  if (mArea[i].mColorNumber == index) return true;
1128  }
1129  for (int i = 0; i < mCurves.size(); i++)
1130  {
1131  if (mCurves[i].getColorNumber() == index) return true;
1132  }
1133  return false;
1134 }
1135 
1141 {
1142  for (int i = 0; i < mArea.size(); i++)
1143  {
1144  if (mArea[i].getColorNumber() > index) mArea[i].decreaseColorNumber();
1145  }
1146  for (int i = 0; i < mCurves.size(); i++)
1147  {
1148  if (mCurves[i].getColorNumber() > index) mCurves[i].decreaseColorNumber();
1149  }
1150 }
1151 
1152 void VectorImage::moveColor(int start, int end)
1153 {
1154  for(int i=0; i< mArea.size(); i++)
1155  {
1156  if (mArea[i].getColorNumber() == start) mArea[i].setColorNumber(end);
1157  }
1158  for(int i=0; i< mCurves.size(); i++)
1159  {
1160  if (mCurves[i].getColorNumber() == start) mCurves[i].setColorNumber(end);
1161  }
1162 }
1163 
1172  bool simplified,
1173  bool showThinCurves,
1174  bool antialiasing)
1175 {
1176  painter.setRenderHint(QPainter::Antialiasing, antialiasing);
1177 
1178  painter.setClipping(false);
1179  painter.setOpacity(1.0);
1180  QTransform painterMatrix = painter.transform();
1181 
1182  QRect mappedViewRect = QRect(0, 0, painter.device()->width(), painter.device()->height());
1183  painterMatrix.inverted().mapRect(mappedViewRect);
1184 
1185  // --- draw filled areas ----
1186  if (!simplified)
1187  {
1188  for (int i = 0; i < mArea.size(); i++)
1189  {
1190  updateArea(mArea[i]); // to do: if selected
1191 
1192  // --- fill areas ---- //
1193  QColor color = getColor(mArea[i].mColorNumber);
1194 
1195  painter.save();
1196  painter.setWorldMatrixEnabled(false);
1197 
1198  if (mArea[i].isSelected())
1199  {
1200  painter.setBrush(QBrush(qPremultiply(color.rgba()), Qt::Dense2Pattern));
1201  }
1202  else
1203  {
1204  painter.setPen(QPen(QBrush(color), 1, Qt::NoPen, Qt::RoundCap, Qt::RoundJoin));
1205  painter.setBrush(QBrush(color, Qt::SolidPattern));
1206  }
1207 
1208  painter.drawPath(painter.transform().map(mArea[i].mPath));
1209  painter.restore();
1210  painter.setWorldMatrixEnabled(true);
1211  painter.setRenderHint(QPainter::Antialiasing, antialiasing);
1212  painter.setClipping(false);
1213  }
1214  }
1215 
1216  // ---- draw curves ----
1217  for (BezierCurve curve : mCurves)
1218  {
1219  curve.drawPath(painter, mObject, mSelectionTransformation, simplified, showThinCurves);
1220  painter.setClipping(false);
1221  }
1222 }
1223 
1233  QTransform myView,
1234  bool simplified,
1235  bool showThinCurves,
1236  bool antialiasing)
1237 {
1238  image->fill(qRgba(0, 0, 0, 0));
1239  QPainter painter(image);
1240  painter.setTransform(myView);
1241  paintImage(painter, simplified, showThinCurves, antialiasing);
1242 }
1243 
1248 {
1249  while (mCurves.size() > 0) { mCurves.removeAt(0); }
1250  while (mArea.size() > 0) { mArea.removeAt(0); }
1251  modification();
1252 }
1253 
1258 {
1259  for (int i = 0; i < mCurves.size(); i++)
1260  {
1261  if (mCurves.at(i).getVertexSize() == 0)
1262  {
1263  mCurves.removeAt(i);
1264  i--;
1265  }
1266  }
1267 }
1268 
1273 {
1274  applySelectionTransformation(mSelectionTransformation);
1275 }
1276 
1282 {
1283  for (int i = 0; i < mCurves.size(); i++)
1284  {
1285  if (mCurves.at(i).isPartlySelected())
1286  {
1287  mCurves[i].transform(transf);
1288  }
1289  }
1291  mSelectionTransformation.reset();
1292  modification();
1293 }
1294 
1301 {
1302  for (int i = 0; i < mCurves.size(); i++)
1303  {
1304  if (mCurves.at(i).isSelected()) mCurves[i].setColorNumber(colorNumber);
1305  }
1306  modification();
1307 }
1308 
1314 {
1315  for (int i = 0; i < mArea.size(); i++)
1316  {
1317  if (mArea.at(i).isSelected()) mArea[i].setColorNumber(colorNumber);
1318  }
1319  modification();
1320 }
1321 
1327 {
1328  for (int i = 0; i < mCurves.size(); i++)
1329  {
1330  if (mCurves.at(i).isSelected()) mCurves[i].setWidth(width);
1331  }
1332  modification();
1333 }
1334 
1340 {
1341  for (int i = 0; i < mCurves.size(); i++)
1342  {
1343  if (mCurves.at(i).isSelected()) mCurves[i].setFeather(feather);
1344  }
1345  modification();
1346 }
1347 
1353 {
1354  Q_UNUSED(opacity);
1355  for (int i = 0; i < mCurves.size(); i++)
1356  {
1357  //if ( curve.at(i).isSelected()) curve[i].setOpacity(width);
1358  }
1359  modification();
1360 }
1361 
1367 {
1368  for (int i = 0; i < mCurves.size(); i++)
1369  {
1370  if (mCurves.at(i).isSelected()) mCurves[i].setInvisibility(YesOrNo);
1371  }
1372  modification();
1373 }
1374 
1380 {
1381  for (int i = 0; i < mCurves.size(); i++)
1382  {
1383  if (mCurves.at(i).isSelected()) {
1384  mCurves[i].setVariableWidth(YesOrNo);
1385  }
1386  }
1387  modification();
1388 }
1389 
1397 {
1398  QList<int> result;
1399  for (int j = 0; j < mCurves.size(); j++)
1400  {
1401  BezierCurve myCurve;
1402  if (mCurves[j].isPartlySelected())
1403  {
1404  myCurve = mCurves[j].transformed(mSelectionTransformation);
1405  }
1406  else
1407  {
1408  myCurve = mCurves[j];
1409  }
1410  if (myCurve.intersects(P1, maxDistance))
1411  {
1412  result.append(j);
1413  // store stroke for later use.
1414  mGetStrokedPath = myCurve.getStrokedPath(1.0, true);
1415  }
1416  }
1417  return result;
1418 }
1419 
1427 VertexRef VectorImage::getClosestVertexTo(const BezierCurve& curve, int curveNum, QPointF thePoint)
1428 {
1429  VertexRef result;
1430  result = VertexRef(-1, -1); // result = [-1, -1]
1431 
1432  // distance of the closest point
1433  QPointF P2 = getVertex(0, 0);
1434  qreal minDistance = thePoint.dotProduct(QPointF(thePoint - P2), QPointF(thePoint - P2));
1435 
1436  for (int vertexPoint = -1; vertexPoint < curve.getVertexSize(); vertexPoint++)
1437  {
1438  P2 = curve.getVertex(vertexPoint);
1439  qreal distance = thePoint.dotProduct(QPointF(thePoint - P2), QPointF(thePoint - P2));
1440 
1441  if (distance < minDistance)
1442  {
1443  minDistance = distance;
1444  result = VertexRef(curveNum, vertexPoint);
1445  }
1446  }
1447  return result;
1448 }
1449 
1457 {
1458  QList<VertexRef> result;
1459 
1460  // Square maxDistance rather than taking the square root for each distance
1461  maxDistance *= maxDistance;
1462 
1463  for (int curve = 0; curve < mCurves.size(); curve++)
1464  {
1465  for (int vertex = -1; vertex < mCurves.at(curve).getVertexSize(); vertex++)
1466  {
1467  QPointF P2 = getVertex(curve, vertex);
1468  qreal distance = P1.dotProduct(QPointF(P1 - P2), QPointF(P1 - P2));
1469  if (distance < maxDistance)
1470  {
1471  result.append(VertexRef(curve, vertex));
1472  }
1473  }
1474  }
1475  return result;
1476 }
1477 
1486 {
1487  QList<VertexRef> result;
1488  for (int j = 0; j < listOfPoints->size(); j++)
1489  {
1490  QPointF P2 = getVertex(listOfPoints->at(j));
1491  qreal distance = P1.dotProduct(QPointF(P1 - P2), QPointF(P1 - P2));
1492  if (distance < maxDistance)
1493  {
1494  result.append(listOfPoints->at(j));
1495  }
1496  }
1497  return result;
1498 }
1499 
1507 {
1508  return getVerticesCloseTo(getVertex(P1ref), maxDistance);
1509 }
1510 
1519 {
1520  return getVerticesCloseTo(getVertex(P1ref), maxDistance, listOfPoints);
1521 }
1522 
1531 {
1532  QList<VertexRef> result;
1533  for (int j = 0; j < listOfPoints->size(); j++)
1534  {
1535  QPointF P2 = getVertex(listOfPoints->at(j));
1536  qreal distance = P1.dotProduct(QPointF(P1 - P2), QPointF(P1 - P2));
1537  if (distance < maxDistance)
1538  {
1539  result.append(listOfPoints->at(j));
1540  listOfPoints->removeAt(j);
1541  }
1542  }
1543  return result;
1544 }
1545 
1554 {
1555  return getAndRemoveVerticesCloseTo(getVertex(P1Ref), maxDistance, listOfPoints);
1556 }
1557 
1564 QPointF VectorImage::getVertex(int curveNumber, int vertexNumber)
1565 {
1566  QPointF result = QPointF(0, 0);
1567  if (curveNumber > -1 && curveNumber < mCurves.size())
1568  {
1569  BezierCurve myCurve = mCurves.at(curveNumber);
1570  if (myCurve.isPartlySelected())
1571  {
1572  myCurve = myCurve.transformed(mSelectionTransformation);
1573  }
1574 
1575  if (vertexNumber > -2 && vertexNumber < myCurve.getVertexSize())
1576  {
1577  result = myCurve.getVertex(vertexNumber);
1578  }
1579  }
1580  return result;
1581 }
1582 
1589 {
1590  return getVertex(vertexRef.curveNumber, vertexRef.vertexNumber);
1591 }
1592 
1599 QPointF VectorImage::getC1(int curveNumber, int vertexNumber)
1600 {
1601  QPointF result = QPointF(0, 0);
1602  if (curveNumber > -1 && curveNumber < mCurves.size())
1603  {
1604  BezierCurve myCurve = mCurves.at(curveNumber);
1605  if (myCurve.isPartlySelected()) myCurve = myCurve.transformed(mSelectionTransformation);
1606  if (vertexNumber > -1 && vertexNumber < myCurve.getVertexSize())
1607  {
1608  result = myCurve.getC1(vertexNumber);
1609  }
1610  }
1611  return result;
1612 }
1613 
1620 {
1621  return getC1(vertexRef.curveNumber, vertexRef.vertexNumber);
1622 }
1623 
1630 QPointF VectorImage::getC2(int curveNumber, int vertexNumber)
1631 {
1632  QPointF result = QPointF(0, 0);
1633  if (curveNumber > -1 && curveNumber < mCurves.size())
1634  {
1635  BezierCurve myCurve = mCurves.at(curveNumber);
1636  if (myCurve.isPartlySelected()) myCurve = myCurve.transformed(mSelectionTransformation);
1637  if (vertexNumber > -1 && vertexNumber < myCurve.getVertexSize())
1638  {
1639  result = myCurve.getC2(vertexNumber);
1640  }
1641  }
1642  return result;
1643 }
1644 
1651 {
1652  return getC2(vertexRef.curveNumber, vertexRef.vertexNumber);
1653 }
1654 
1661 {
1662  QList<VertexRef> result;
1663 
1664  if (curveNumber > -1 && curveNumber < mCurves.size())
1665  {
1666  BezierCurve myCurve = mCurves[curveNumber];
1667 
1668  for (int k = -1; k < myCurve.getVertexSize(); k++)
1669  {
1670  VertexRef vertexRef = VertexRef(curveNumber, k);
1671  result.append(vertexRef);
1672  }
1673  }
1674 
1675  return result;
1676 }
1677 
1683 {
1684  QList<VertexRef> result;
1685  for (int j = 0; j < mCurves.size(); j++)
1686  {
1687  for (int k = -1; k < mCurves.at(j).getVertexSize(); k++)
1688  {
1689  VertexRef vertexRef = VertexRef(j, k);
1690  result.append(vertexRef);
1691  }
1692  }
1693  // Add Area vertices
1694  return result;
1695 }
1696 
1702 int VectorImage::getCurveSize(int curveNumber)
1703 {
1704  if (curveNumber > -1 && curveNumber < mCurves.size())
1705  {
1706  return mCurves.at(curveNumber).getVertexSize();
1707  }
1708  else
1709  {
1710  return -1;
1711  }
1712 }
1713 
1719 {
1720  QList<BezierCurve> curves;
1721  for (int curve = 0; curve < mCurves.size(); curve++)
1722  {
1723  if (mCurves[curve].isSelected())
1724  {
1725  curves.append(mCurves[curve]);
1726  }
1727  }
1728  return curves;
1729 }
1730 
1736 {
1737  QList<int> result;
1738  for (int curve = 0; curve < mCurves.size(); curve++)
1739  {
1740  if (mCurves[curve].isSelected())
1741  {
1742  result.append(curve);
1743  }
1744  }
1745  return result;
1746 }
1747 
1753 {
1754  int count = 0;
1755  for (int curve = 0; curve < mCurves.size(); curve++)
1756  {
1757  if (mCurves[curve].isSelected())
1758  {
1759  count++;
1760  }
1761  }
1762  return count;
1763 }
1764 
1771 {
1772  for (int i = 0; i < mArea.size(); i++)
1773  {
1774  if (mArea[i].mPath.controlPointRect().contains(currentPoint))
1775  {
1776  return mArea[i];
1777  }
1778  }
1779  return BezierArea();
1780 }
1781 
1788 {
1789  QList<VertexRef> vertexPath;
1790  QList<int> curveNumbers = getSelectedCurveNumbers();
1792  QList<VertexRef> vertexList;
1793  VertexRef vertex;
1794  for (int curve = 0; curve < getNumOfCurvesSelected(); curve++)
1795  {
1796  vertexList = getCurveVertices(curveNumbers[curve]);
1797  for (int i = 0; i < vertexList.size(); i++)
1798  {
1799  QPointF point = getVertex(vertexList[i]);
1800  vertex = getClosestVertexTo(curves[curve], curveNumbers[curve], point);
1801 
1802  if (vertex.curveNumber != -1 && !vertexPath.contains(vertex))
1803  {
1804  vertexPath.append(vertex);
1805  }
1806  }
1807 
1808  BezierArea bezierArea(vertexPath, color);
1809  addArea(bezierArea);
1810 
1811  // set selected curves as filled
1812  mCurves[curveNumbers[curve]].setFilled(true);
1813 
1814  // clear path for next area
1815  vertexPath.clear();
1816  }
1817 
1818  modification();
1819 }
1820 
1827 void VectorImage::fillContour(QList<QPointF> contourPath, int color)
1828 {
1829  QList<VertexRef> vertexPath;
1830  VertexRef vertex;
1831 
1832  BezierCurve lastCurve = getLastCurve();
1833  int lastCurveNum = getLastCurveNumber();
1834 
1835  for (QPointF point : contourPath) {
1836  vertex = getClosestVertexTo(lastCurve, lastCurveNum, point);
1837 
1838  if (vertex.curveNumber != -1 && !vertexPath.contains(vertex)) {
1839  vertexPath.append(vertex);
1840  }
1841  }
1842 
1843  BezierArea bezierArea(vertexPath, color);
1844 
1845  addArea(bezierArea);
1846  modification();
1847 }
1848 
1849 //QList<QPointF> VectorImage::getfillContourPoints(QPoint point)
1850 //{
1851 // // We get the contour points from a bitmap version of the vector layer as it is much faster to process
1852 // QImage* image = new QImage( mSize, QImage::Format_ARGB32_Premultiplied );
1853 // image->fill(Qt::white);
1854 // QPainter painter( image );
1855 
1856 // // Adapt the QWidget view coordinates to the QImage coordinates
1857 // QTransform translate;
1858 // translate.translate( mSize.width() / 2.f , mSize.height() / 2.f );
1859 // painter.setTransform( translate );
1860 // paintImage( painter, true, true, false );
1861 
1862 // QList<QPoint> queue; // queue all the pixels of the filled area (as they are found)
1863 // QList<QPointF> contourPoints; // refs of points near the contour pixels
1864 
1865 // qreal maxWidth = mSize.width();
1866 // qreal maxHeight = mSize.height();
1867 
1868 // // To keep track of the highest y contour point to make sure it is on the main contour and not inside.
1869 // int highestY = point.y();
1870 
1871 // // Convert point to image coordinates as the image doesn't have the same coordinates origin as the
1872 // // QWidget view
1873 // QPointF startPoint((maxWidth / 2) + point.x(), (maxHeight / 2) + point.y());
1874 // queue.append( startPoint.toPoint() );
1875 
1876 // // Check the color of the clicked point
1877 // QRgb colorFrom = image->pixel(startPoint.x(), startPoint.y());
1878 // QRgb colorTo = Qt::green;
1879 
1880 // QPoint currentPoint;
1881 
1882 // int leftX, rightX;
1883 // bool foundLeftBound, foundRightBound;
1884 
1885 // // ----- flood fill and remember the contour pixels -> contourPixels
1886 // // ----- from the standard flood fill algorithm
1887 // // ----- http://en.wikipedia.org/wiki/Flood_fill
1888 // while ( queue.size() > 0 ) {
1889 
1890 // // Get the first point in the queue and remove it afterwards
1891 // currentPoint = queue.at(0);
1892 // queue.removeAt(0);
1893 
1894 // // Inspect a line from edge to edge
1895 // if (image->pixel(currentPoint.x(), currentPoint.y()) == colorFrom) {
1896 // leftX = currentPoint.x();
1897 // rightX = currentPoint.x();
1898 
1899 // foundLeftBound = false;
1900 // foundRightBound = false;
1901 
1902 // while (!foundLeftBound) {
1903 // leftX--;
1904 
1905 // // Are we getting to the end of the document ?
1906 // if ( leftX < 1) {
1907 // qWarning() << " Out of bound left ";
1908 // QList<QPointF> emptylist;
1909 // return emptylist;
1910 // }
1911 
1912 // QPoint leftPoint = QPoint(leftX, currentPoint.y());
1913 
1914 // // Are we getting to a curve ?
1915 // if ( image->pixel(leftPoint.x(), leftPoint.y()) != colorFrom &&
1916 // image->pixel(leftPoint.x(), leftPoint.y()) != colorTo ) {
1917 
1918 // foundLeftBound = true;
1919 
1920 // // Convert point to view coordinates
1921 // QPointF contourPoint( leftPoint.x() - (maxWidth / 2), leftPoint.y() - (maxHeight / 2));
1922 
1923 // // Check if the left bound is just a line crossing the main shape
1924 // bool foundFillAfter = false;
1925 // int increment = 1;
1926 
1927 // while (leftPoint.x() - increment > 0 && increment < 3 && !foundFillAfter) {
1928 // QPoint pointAfter = QPoint(leftPoint.x() - increment, leftPoint.y());
1929 
1930 // if (image->pixel(pointAfter.x(), pointAfter.y()) == colorTo) {
1931 // foundFillAfter = true;
1932 // }
1933 
1934 // increment++;
1935 // }
1936 
1937 // // If the bound is not a contour, we must ignore it
1938 // if (foundFillAfter) {
1939 
1940 // // If the bound is not a contour, we must ignore it
1941 // contourPoints.removeOne(contourPoint);
1942 // } else {
1943 // contourPoints.append(contourPoint);
1944 // }
1945 // }
1946 // }
1947 
1948 // while (!foundRightBound) {
1949 // rightX++;
1950 
1951 // // Are we getting to the end of the document ?
1952 // if ( rightX > maxWidth - 1 ) {
1953 // qWarning() << " Out of bound right ";
1954 // QList<QPointF> emptylist;
1955 // return emptylist;
1956 // }
1957 
1958 // QPoint rightPoint = QPoint(rightX, currentPoint.y());
1959 
1960 // // Are we getting to a curve ?
1961 // if ( image->pixel(rightPoint.x(), rightPoint.y()) != colorFrom &&
1962 // image->pixel(rightPoint.x(), rightPoint.y()) != colorTo) {
1963 
1964 // foundRightBound = true;
1965 
1966 // // Convert point to view coordinates
1967 // QPointF contourPoint( rightPoint.x() - (maxWidth / 2), rightPoint.y() - (maxHeight / 2));
1968 
1969 // // Check if the left bound is just a line crossing the main shape
1970 // bool foundFillAfter = false;
1971 // int increment = 1;
1972 
1973 // while (rightPoint.x() + increment < maxWidth && increment < 3 && !foundFillAfter) {
1974 // QPoint pointAfter = QPoint(rightPoint.x() + increment, rightPoint.y());
1975 
1976 // if (image->pixel(pointAfter.x(), pointAfter.y()) == colorTo) {
1977 // foundFillAfter = true;
1978 // }
1979 
1980 // increment++;
1981 // }
1982 
1983 // if (foundFillAfter) {
1984 
1985 // // If the bound is not a contour, we must ignore it
1986 // contourPoints.removeOne(contourPoint);
1987 // } else {
1988 // contourPoints.append(contourPoint);
1989 // }
1990 // }
1991 // }
1992 
1993 // int lineY = currentPoint.y();
1994 // int topY = lineY - 1;
1995 // int bottomY = lineY + 1;
1996 
1997 // if ( topY < 1 || bottomY > maxHeight - 1 ) {
1998 // qWarning() << " Out of bound top / bottom ";
1999 // QList<QPointF> emptylist;
2000 // return emptylist;
2001 // }
2002 
2003 // for (int x = leftX + 1; x < rightX; x++) {
2004 
2005 // // The current line point is checked (Colored)
2006 // QPoint linePoint = QPoint(x, lineY);
2007 // image->setPixel(linePoint.x(), linePoint.y(), colorTo);
2008 
2009 // QPoint topPoint = QPoint(x, topY);
2010 
2011 // if ( image->pixel(topPoint.x(), topPoint.y()) != colorFrom &&
2012 // image->pixel(topPoint.x(), topPoint.y()) != colorTo) {
2013 
2014 // // Convert point to view coordinates
2015 // QPointF contourPoint( topPoint.x() - (maxWidth / 2), topPoint.y() - (maxHeight / 2));
2016 
2017 // // Check if the left bound is just a line crossing the main shape
2018 // bool foundFillAfter = false;
2019 // int increment = 1;
2020 
2021 // while (topPoint.y() - increment > 0 && increment < 3 && !foundFillAfter) {
2022 // QPoint pointAfter = QPoint(topPoint.x(), topPoint.y() - increment);
2023 
2024 // if (image->pixel(pointAfter.x(), pointAfter.y()) == colorTo) {
2025 // foundFillAfter = true;
2026 // }
2027 // increment ++;
2028 // }
2029 
2030 
2031 // if (foundFillAfter) {
2032 
2033 // // If the bound is not a contour, we must ignore it
2034 // contourPoints.removeOne(contourPoint);
2035 // } else {
2036 // contourPoints.append(contourPoint);
2037 // }
2038 // } else {
2039 // queue.append(topPoint);
2040 // }
2041 
2042 // QPoint bottomPoint = QPoint(x, bottomY);
2043 
2044 // if ( image->pixel(bottomPoint.x(), bottomPoint.y()) != colorFrom &&
2045 // image->pixel(bottomPoint.x(), bottomPoint.y()) != colorTo ) {
2046 
2047 // QPointF contourPoint( bottomPoint.x() - (maxWidth / 2), bottomPoint.y() - (maxHeight / 2));
2048 
2049 // // Check if the left bound is just a line crossing the main shape
2050 // bool foundFillAfter = false;
2051 // int increment = 1;
2052 
2053 // while (bottomPoint.y() + increment < maxHeight && increment < 3 && !foundFillAfter) {
2054 // QPoint pointAfter = QPoint(bottomPoint.x(), bottomPoint.y() + increment);
2055 
2056 // if (image->pixel(pointAfter.x(), pointAfter.y()) == colorTo) {
2057 // foundFillAfter = true;
2058 // }
2059 
2060 // increment++;
2061 // }
2062 
2063 // if (foundFillAfter) {
2064 
2065 // // If the bound is not a contour, we must ignore it
2066 // contourPoints.removeOne(contourPoint);
2067 // }
2068 // else {
2069 
2070 // // Keep track of the highest Y position (lowest point) at the beginning of the list
2071 // // so that we can parse the list from a point that is a real extremity.
2072 // // of the area.
2073 // if (highestY < bottomY) {
2074 
2075 // highestY = bottomY;
2076 // contourPoints.insert(0, contourPoint);
2077 // } else {
2078 // contourPoints.append(contourPoint);
2079 // }
2080 // }
2081 
2082 // } else {
2083 // queue.append(bottomPoint);
2084 // }
2085 // }
2086 // }
2087 // }
2088 // return contourPoints;
2089 //}
2090 
2091 //void VectorImage::fill(QPointF point, int color, float tolerance)
2092 //{
2093 // // Check if we clicked on a curve. In that case, we change its color.
2094 // QList<int> closestCurves = getCurvesCloseTo( point, tolerance );
2095 
2096 // if (closestCurves.size() > 0) // the user click on one or more curves
2097 // {
2098 // // For each clicked curves, we change the color if requiered
2099 // for (int i = 0; i < closestCurves.size(); i++) {
2100 // int curveNumber = closestCurves[i];
2101 // m_curves[curveNumber].setColorNumber(color);
2102 // }
2103 
2104 // return;
2105 // }
2106 
2107 // // Check if we clicked on an area of the same color.
2108 // // We don't want to create another area.
2109 // int areaNum = getLastAreaNumber(point);
2110 // if (areaNum > -1 && area[areaNum].mColorNumber == color) {
2111 // return;
2112 // }
2113 
2114 // // Get the contour points
2115 // QList<QPointF> contourPoints = getfillContourPoints(point.toPoint());
2116 
2117 // // Make a path from the external contour points.
2118 // // Put the points in the right order.
2119 // QList<QPointF> mainContourPath;
2120 // QPointF currentPoint;
2121 
2122 // if (contourPoints.size() > 0) {
2123 // currentPoint = QPointF(contourPoints[0].x(), contourPoints[0].y());
2124 // mainContourPath.append(currentPoint);
2125 // contourPoints.removeAt(0);
2126 // }
2127 
2128 // bool completedPath = false;
2129 // bool foundError = (contourPoints.size() < 1 && !completedPath);
2130 
2131 // int maxDelta = 2;
2132 // int minDelta = -2;
2133 
2134 // while (!completedPath && !foundError) {
2135 
2136 // bool foundNextPoint = false;
2137 
2138 // int i = 0;
2139 // while (i < contourPoints.size() && !foundNextPoint) {
2140 // QPointF point = contourPoints.at(i);
2141 
2142 // if (mainContourPath.contains(point)) {
2143 // contourPoints.removeAt(i);
2144 // }
2145 // else {
2146 // qreal deltaX = currentPoint.x() - point.x();
2147 // qreal deltaY = currentPoint.y() - point.y();
2148 
2149 // if ( (deltaX < maxDelta && deltaX > minDelta) &&
2150 // (deltaY < maxDelta && deltaY > minDelta)) {
2151 
2152 // currentPoint = QPointF(point.x(), point.y());
2153 // mainContourPath.append(currentPoint);
2154 // contourPoints.removeAt(i);
2155 
2156 // foundNextPoint = true;
2157 
2158 // maxDelta = 2;
2159 // minDelta = -2;
2160 // }
2161 // i++;
2162 // }
2163 // }
2164 
2165 // // Check if we have looped
2166 // if (!foundNextPoint) {
2167 
2168 // qreal deltaX = currentPoint.x() - mainContourPath[0].x();
2169 // qreal deltaY = currentPoint.y() - mainContourPath[0].y();
2170 
2171 // if ( (deltaX < maxDelta && deltaX > minDelta) &&
2172 // (deltaY < maxDelta && deltaY > minDelta)) {
2173 // completedPath = true;
2174 // foundNextPoint = true;
2175 // }
2176 // else if (maxDelta == 2){
2177 // // Check if we can find the point after
2178 // //
2179 // maxDelta = 3;
2180 // minDelta = -3;
2181 // foundNextPoint = true;
2182 // }
2183 // else {
2184 // qWarning() << " couldn't find next point after " << currentPoint.x() << ", " << currentPoint.y();
2185 // }
2186 // }
2187 // else if (contourPoints.size() < 1) {
2188 // // If we found the next point and we have no more points, it means, we have the end of the path
2189 // completedPath = true;
2190 // foundNextPoint = true;
2191 // }
2192 
2193 // foundError = ( (contourPoints.size() < 1 && !completedPath) || !foundNextPoint );
2194 // }
2195 
2196 // // Add exclude paths
2197 
2198 // // Fill the path if we have one.
2199 // if (completedPath) {
2200 // fillSelectedPath(mainContourPath, color);
2201 // }
2202 // else {
2203 // // Check if we clicked on an area in this position and as we couldn't create one,
2204 // // we update this one. It may be an area drawn from a stroke path.
2205 // int areaNum = getLastAreaNumber(point);
2206 // if (areaNum > -1) {
2207 
2208 // int clickedColorNum = area[areaNum].getColorNumber();
2209 
2210 // if (clickedColorNum != color) {
2211 // area[areaNum].setColorNumber(color);
2212 // }
2213 // }
2214 // }
2215 //}
2216 
2222 {
2223  updateArea(bezierArea);
2224  mArea.append(bezierArea);
2225  modification();
2226 }
2227 
2234 {
2235  int result = -1;
2236  for (int i = 0; i < mArea.size() && result == -1; i++)
2237  {
2238  if (mArea[i].mPath.controlPointRect().contains(point))
2239  {
2240  if (mArea[i].mPath.contains(point))
2241  {
2242  result = i;
2243  }
2244  }
2245  }
2246  return result;
2247 }
2248 
2255 {
2256  return getLastAreaNumber(point, mArea.size() - 1);
2257 }
2263 {
2264  return !mCurves.isEmpty() ? mCurves.size() - 1 : 0;
2265 }
2266 
2272 {
2273  return !mCurves.isEmpty() ? mCurves[mCurves.size() - 1] : BezierCurve();
2274 }
2275 
2282 int VectorImage::getLastAreaNumber(QPointF point, int maxAreaNumber)
2283 {
2284  int result = -1;
2285  for (int i = maxAreaNumber; i > -1 && result == -1; i--)
2286  {
2287  if (mArea[i].mPath.controlPointRect().contains(point))
2288  {
2289  if (mArea[i].mPath.contains(point))
2290  {
2291  result = i;
2292  }
2293  }
2294  }
2295  return result;
2296 }
2297 
2304 {
2305  int areaNumber = getLastAreaNumber(point);
2306  if (areaNumber != -1)
2307  {
2308  mArea.removeAt(areaNumber);
2309  }
2310  modification();
2311 }
2312 
2319 void VectorImage::removeAreaInCurve(int curve, int areaNumber)
2320 {
2321  QPointF areaPoint = getVertex(curve, areaNumber);
2322  removeArea(areaPoint);
2323 }
2324 
2330 {
2331  QPainterPath newPath;
2332  for (int i = 0; i < bezierArea.mVertex.size(); i++)
2333  {
2334  QPointF myPoint = getVertex(bezierArea.mVertex[i]);
2335  QPointF myC1;
2336  QPointF myC2;
2337 
2338  if (i == 0)
2339  {
2340  newPath.moveTo(myPoint);
2341  }
2342  else
2343  {
2344  if (bezierArea.mVertex[i - 1].curveNumber == bezierArea.mVertex[i].curveNumber) // the two points are on the same curve
2345  {
2346  if (bezierArea.mVertex[i - 1].vertexNumber < bezierArea.mVertex[i].vertexNumber) // the points follow the curve progression
2347  {
2348  myC1 = getC1(bezierArea.mVertex[i]);
2349  myC2 = getC2(bezierArea.mVertex[i]);
2350  }
2351  else
2352  {
2353  myC1 = getC2(bezierArea.mVertex[i - 1]);
2354  myC2 = getC1(bezierArea.mVertex[i - 1]);
2355  }
2356  newPath.cubicTo(myC1, myC2, myPoint);
2357  }
2358  else // the two points are not the same curve
2359  {
2360  if (bezierArea.mVertex[i].vertexNumber == -1) // the current point is the first point in the new curve
2361  {
2362  newPath.lineTo(myPoint);
2363  }
2364  else
2365  {
2366  newPath.lineTo(myPoint);
2367  }
2368  }
2369  }
2370  }
2371  newPath.closeSubpath();
2372  bezierArea.mPath = newPath;
2373  bezierArea.mPath.setFillRule(Qt::WindingFill);
2374 }
2375 
2383 {
2384  return BezierCurve::eLength(getVertex(r1) - getVertex(r2));
2385 }
2386 
2392 
2393  // Set the current width of the document based on the extremity of the drawing.
2394  //
2395  // It calculates the size of the document in a way that the center point from
2396  // the view (0, 0) is always the center point of the document.
2397  //
2398  // It adds a point to the 4 sides of the document size in order
2399  // make sure that any curve, any vertex stays within the document.
2400  //
2401 
2402  QRectF rect = updatedCurve.getBoundingRect();
2403 
2404  QPoint topLeft = rect.topLeft().toPoint();
2405  QPoint bottomRight = rect.bottomRight().toPoint();
2406 
2407  int widthFromLeft = ((topLeft.x() * -1) * 2) + 2;
2408 
2409  if (widthFromLeft > mSize.width())
2410  {
2411  mSize.setWidth(widthFromLeft);
2412  }
2413 
2414  int widthFromRight = (bottomRight.x() * 2) + 2;
2415 
2416  if (widthFromRight > mSize.width())
2417  {
2418  mSize.setWidth(widthFromRight);
2419  }
2420 
2421  int heightFromTop = ((topLeft.y() * -1) * 2) + 2;
2422 
2423  if (heightFromTop > mSize.height())
2424  {
2425  mSize.setHeight(heightFromTop);
2426  }
2427 
2428  int heightFromBottom = (bottomRight.y() * 2) + 2;
2429 
2430  if (heightFromBottom > mSize.height())
2431  {
2432  mSize.setHeight(heightFromBottom);
2433  }
2434 }
QList< VertexRef > getAllVertices()
VectorImage::getAllVertices.
void clear()
void insertCurve(int position, BezierCurve &newCurve, qreal factor, bool interacts)
VectorImage::insertCurve.
void setTransform(const QTransform &transform, bool combine)
void setOpacity(qreal opacity)
void updateArea(BezierArea &bezierArea)
VectorImage::updateArea.
void paintImage(QPainter &painter, bool simplified, bool showThinCurves, bool antialiasing)
VectorImage::paintImage.
void setHeight(int height)
int getFirstSelectedCurve()
VectorImage::getFirstSelectedCurve.
QString & append(QChar ch)
qreal getDistance(VertexRef r1, VertexRef r2)
VectorImage::getDistance.
int getNumOfCurvesSelected()
VectorImage::numOfCurvesSelected.
void writeDTD(const QString &dtd)
void applyColorToSelectedArea(int colorNumber)
VectorImage::applyColorToSelectedArea.
int width() const const
void checkCurveIntersections(BezierCurve &newCurve, qreal tolerance)
VectorImage::checkCurveIntersections.
Dense2Pattern
void applyInvisibilityToSelection(bool YesOrNo)
VectorImage::applyInvisibilityToSelection.
void reset()
void setRenderHint(QPainter::RenderHint hint, bool on)
void checkCurveExtremity(BezierCurve &newCurve, qreal tolerance)
VectorImage::checkCurveExtremity.
int getCurveSize(int curveNumber)
VectorImage::getCurveSize.
int getLastAreaNumber(QPointF point)
VectorImage::getLastAreaNumber.
QString attribute(const QString &name, const QString &defValue) const const
void closeSubpath()
int length() const const
void clear()
VectorImage::clear.
QString errorString() const const
void calculateSelectionRect()
VectorImage::calculateSelectionRect.
bool isPathFilled()
VectorImage::isPathFilled.
QList< VertexRef > getVerticesCloseTo(QPointF thisPoint, qreal maxDistance)
VectorImage::getVerticesCloseTo.
QPoint map(const QPoint &point) const const
void setAutoFormatting(bool enable)
bool read(QString filePath)
VectorImage::read.
Definition: vectorimage.cpp:65
WindingFill
void setClipping(bool enable)
QFileDevice::FileError error() const const
int getCurvesColor(int curve)
VectorImage::getCurvesColor.
QList< int > getCurvesCloseTo(QPointF thisPoint, qreal maxDistance)
VectorImage::getCurvesCloseTo.
const T & at(int i) const const
void removeAt(int i)
void loadDomElement(QDomElement element)
VectorImage::loadDomElement.
void fillSelectedPath(int color)
VectorImage::fillSelectedPath.
bool contains(const QRectF &rectangle) const const
void save()
void cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &endPoint)
const QTransform & transform() const const
void moveTo(const QPointF &point)
void fillContour(QList< QPointF > contourPath, int color)
VectorImage::fillContour.
void deselectAll()
VectorImage::deselectAll.
QDomElement documentElement() const const
void applyVariableWidthToSelection(bool YesOrNo)
VectorImage::applyVariableWidthToSelection.
QPointF getC1(int curveNumber, int vertexNumber)
VectorImage::getC1.
BezierCurve getLastCurve()
VectorImage::getLastCurve.
QDomDocumentType doctype() const const
QList< BezierCurve > getSelectedCurves()
VectorImage::getSelectedCurve.
RoundCap
QString name() const const
QTransform inverted(bool *invertible) const const
int x() const const
int y() const const
int size() const const
QDomNode nextSibling() const const
int indexOf(const T &value, int from) const const
int width() const const
QDomElement toElement() const const
void removeAreaInCurve(int curve, int areaNumber)
VectorImage::removeAreaInCurve.
int getColorNumber(QPointF point)
VectorImage::getColorNumber.
qreal x() const const
qreal y() const const
void append(const T &value)
void selectAll()
VectorImage::selectAll.
bool empty() const const
int getFirstSelectedArea()
VectorImage::getFirstSelectedArea.
void addPoint(int curveNumber, int vertexNumber, qreal fraction)
VectorImage::addPoint.
void fill(uint pixelValue)
void setFillRule(Qt::FillRule fillRule)
void removeVertex(int curve, int vertex)
VectorImage::removeVertex.
bool isDir() const const
void setPen(const QColor &color)
void lineTo(const QPointF &endPoint)
bool isAnyCurveSelected()
VectorImage::isAnyCurveSelected.
void setWidth(int width)
bool isEmpty() const const
QPointF topLeft() const const
QPointF getVertex(int curveNumber, int vertexNumber)
VectorImage::getVertex.
bool isSelected(int curveNumber)
VectorImage::isSelected.
QList< VertexRef > getCurveVertices(int curveNumber)
VectorImage::getCurveVertices.
QPaintDevice * device() const const
bool usesColor(int index)
VectorImage::usesColor.
void setBrush(const QBrush &brush)
void addCurve(BezierCurve &newCurve, qreal factor, bool interacts=true)
VectorImage::addCurve.
QColor getColor(int i)
VectorImage::getColor.
void clean()
VectorImage::clean.
virtual bool open(QIODevice::OpenMode mode) override
void deleteSelectedPoints()
VectorImage::deleteSelectedPoints.
void applyWidthToSelection(qreal width)
VectorImage::applyWidthToSelection.
RoundJoin
bool contains(const T &value) const const
BezierArea getSelectedArea(QPointF currentPoint)
VectorImage::getSelectedArea.
bool isNull() const const
void removeArea(QPointF point)
VectorImage::removeArea.
int getFirstAreaNumber(QPointF point)
VectorImage::getFirstAreaNumber.
void writeAttribute(const QString &qualifiedName, const QString &value)
void restore()
Status write(QString filePath, QString format)
VectorImage::write.
void applyColorToSelectedCurve(int colorNumber)
VectorImage::applyColorToSelectedCurve.
QPointF getC2(int curveNumber, int vertexNumber)
VectorImage::getC2.
void setSelectionTransformation(QTransform transform)
VectorImage::setSelectionTransformation.
QDomNode firstChild() const const
void drawPath(const QPainterPath &path)
bool isAreaSelected(int areaNumber)
VectorImage::isAreaSelected.
QPoint toPoint() const const
void insert(int i, const T &value)
void updateImageSize(BezierCurve &updatedCurve)
VectorImage::updateImageSize.
void paste(VectorImage &)
VectorImage::paste.
void writeEndDocument()
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
void setWorldMatrixEnabled(bool enable)
void setAreaSelected(int areaNumber, bool YesOrNo)
VectorImage::setAreaSelected.
void addArea(BezierArea bezierArea)
VectorImage::addArea.
int height() const const
void applyFeatherToSelection(qreal feather)
VectorImage::applyFeatherToSelection.
QList< VertexRef > getAndRemoveVerticesCloseTo(QPointF thisPoint, qreal maxDistance, QList< VertexRef > *listOfPoints)
VectorImage::getAndRemoveVerticesCloseTo.
void removeCurveAt(int i)
VectorImage::removeCurveAt.
Status createDomElement(QXmlStreamWriter &doc)
VectorImage::createDomElement.
QList< int > getSelectedCurveNumbers()
VectorImage::getSelectedCurveNumber.
void applySelectionTransformation()
VectorImage::applySelectionTransformation.
qreal dotProduct(const QPointF &p1, const QPointF &p2)
int getLastCurveNumber()
VectorImage::getLastCurveNumber.
void removeColor(int index)
VectorImage::removeColor.
QString tagName() const const
void writeStartDocument()
QPointF bottomRight() const const
int height() const const
void setSelected(int curveNumber, bool YesOrNo)
VectorImage::setSelected.
void applyOpacityToSelection(qreal opacity)
VectorImage::applyOpacityToSelection.
QRect mapRect(const QRect &rectangle) const const
void outputImage(QImage *image, QTransform myView, bool simplified, bool showThinCurves, bool antialiasing)
VectorImage::outputImage.
QRgb rgba() const const
void deleteSelection()
VectorImage::deleteSelection.
void writeEndElement()
void writeStartElement(const QString &qualifiedName)
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
VertexRef getClosestVertexTo(const BezierCurve &curve, int curveNum, QPointF thisPoint)
VectorImage::getClosestVertexTo.
void setSelectionRect(QRectF rectange)
VectorImage::setSelectionRect.