1 /*
2  * Copyright (C) 2010-2015 by Stephen Allewell
3  * steve.allewell@gmail.com
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  */
10 
11 
12 #include "StitchData.h"
13 
14 #include <KLocalizedString>
15 
16 #include "Exceptions.h"
17 
18 
FlossUsage()19 FlossUsage::FlossUsage()
20     :   backstitchCount(0),
21         backstitchLength(0.0)
22 {
23 }
24 
25 
totalLength() const26 double FlossUsage::totalLength() const
27 {
28     return stitchLength() + backstitchLength;
29 }
30 
31 
stitchLength() const32 double FlossUsage::stitchLength() const
33 {
34     double total = 0;
35 
36     foreach (double length, stitchLengths) {
37         total += length;
38     }
39 
40     return total;
41 }
42 
43 
totalStitches() const44 int FlossUsage::totalStitches() const
45 {
46     return stitchCount() + backstitchCount;
47 }
48 
49 
stitchCount() const50 int FlossUsage::stitchCount() const
51 {
52     int total = 0;
53 
54     foreach (int count, stitchCounts) {
55         total += count;
56     }
57 
58     return total;
59 }
60 
61 
StitchData()62 StitchData::StitchData()
63     :   m_width(0),
64         m_height(0)
65 {
66 }
67 
68 
~StitchData()69 StitchData::~StitchData()
70 {
71     clear();
72 }
73 
74 
clear()75 void StitchData::clear()
76 {
77     qDeleteAll(m_stitches);
78     m_stitches.fill(nullptr);
79 
80     qDeleteAll(m_backstitches);
81     m_backstitches.clear();
82 
83     qDeleteAll(m_knots);
84     m_knots.clear();
85 }
86 
87 
width() const88 int StitchData::width() const
89 {
90     return m_width;
91 }
92 
93 
height() const94 int StitchData::height() const
95 {
96     return m_height;
97 }
98 
99 
resize(int width,int height)100 void StitchData::resize(int width, int height)
101 {
102     QVector<StitchQueue *> newVector(width * height);
103     QRect extentsRect = extents();
104 
105     for (int y = extentsRect.top() ; y <= extentsRect.bottom() ; ++y) {
106         for (int x = extentsRect.left() ; x <= extentsRect.right() ; ++x) {
107             newVector[y * width + x] = takeStitchQueueAt(x, y);
108         }
109     }
110 
111     m_stitches = newVector;
112     m_width = width;
113     m_height = height;
114 }
115 
116 
insertColumns(int startColumn,int columns)117 void StitchData::insertColumns(int startColumn, int columns)
118 {
119     int originalWidth = m_width;
120 
121     resize(originalWidth + columns, m_height);
122 
123     for (int y = 0 ; y < m_height ; ++y) {
124         for (int destinationColumn = m_width - 1, sourceColumn = originalWidth - 1 ; sourceColumn >= startColumn ; --destinationColumn, --sourceColumn) {
125             m_stitches[index(destinationColumn, y)] = takeStitchQueueAt(sourceColumn, y);
126         }
127     }
128 
129     startColumn *= 2;
130     columns *= 2;
131 
132     QListIterator<Backstitch *> backstitchIterator(m_backstitches);
133 
134     while (backstitchIterator.hasNext()) {
135         Backstitch *backstitch = backstitchIterator.next();
136 
137         if (backstitch->start.x() >= startColumn) {
138             backstitch->start.setX(backstitch->start.x() + columns);
139         }
140 
141         if (backstitch->end.x() >= startColumn) {
142             backstitch->end.setX(backstitch->end.x() + columns);
143         }
144     }
145 
146     QListIterator<Knot *> knotIterator(m_knots);
147 
148     while (knotIterator.hasNext()) {
149         Knot *knot = knotIterator.next();
150 
151         if (knot->position.x() >= startColumn) {
152             knot->position.setX(knot->position.x() + columns);
153         }
154     }
155 }
156 
157 
insertRows(int startRow,int rows)158 void StitchData::insertRows(int startRow, int rows)
159 {
160     int originalHeight = m_height;
161 
162     resize(m_width, originalHeight + rows);
163 
164     for (int destinationRow = m_height - 1, sourceRow = originalHeight - 1; sourceRow >= startRow ; --destinationRow, --sourceRow) {
165         for (int x = 0 ; x < m_width ; ++x) {
166             m_stitches[index(x, destinationRow)] = takeStitchQueueAt(x, sourceRow);
167         }
168     }
169 
170     startRow *= 2;
171     rows *= 2;
172 
173     QListIterator<Backstitch *> backstitchIterator(m_backstitches);
174 
175     while (backstitchIterator.hasNext()) {
176         Backstitch *backstitch = backstitchIterator.next();
177 
178         if (backstitch->start.y() >= startRow) {
179             backstitch->start.setY(backstitch->start.y() + rows);
180         }
181 
182         if (backstitch->end.y() >= startRow) {
183             backstitch->end.setY(backstitch->end.y() + rows);
184         }
185     }
186 
187     QListIterator<Knot *> knotIterator(m_knots);
188 
189     while (knotIterator.hasNext()) {
190         Knot *knot = knotIterator.next();
191 
192         if (knot->position.y() >= startRow) {
193             knot->position.setY(knot->position.y() + rows);
194         }
195     }
196 }
197 
198 
removeColumns(int startColumn,int columns)199 void StitchData::removeColumns(int startColumn, int columns)
200 {
201     for (int y = 0 ; y < m_height ; ++y) {
202         for (int destinationColumn = startColumn, sourceColumn = startColumn + columns ; sourceColumn < m_width ; ++destinationColumn, ++sourceColumn) {
203             m_stitches[index(destinationColumn, y)] = takeStitchQueueAt(sourceColumn, y);
204         }
205     }
206 
207     int snapStartColumn = startColumn * 2;
208     int snapColumns = columns * 2;
209 
210     QListIterator<Backstitch *> backstitchIterator(m_backstitches);
211 
212     while (backstitchIterator.hasNext()) {
213         Backstitch *backstitch = backstitchIterator.next();
214 
215         if (backstitch->start.x() >= snapStartColumn + snapColumns) {
216             backstitch->start.setX(backstitch->start.x() - snapColumns);
217         }
218 
219         if (backstitch->end.x() >= snapStartColumn + snapColumns) {
220             backstitch->end.setX(backstitch->end.x() - snapColumns);
221         }
222     }
223 
224     QListIterator<Knot *> knotIterator(m_knots);
225 
226     while (knotIterator.hasNext()) {
227         Knot *knot = knotIterator.next();
228 
229         if (knot->position.x() >= snapStartColumn + snapColumns) {
230             knot->position.setX(knot->position.x() - snapColumns);
231         }
232     }
233 
234     resize(m_width - columns, m_height);
235 }
236 
237 
removeRows(int startRow,int rows)238 void StitchData::removeRows(int startRow, int rows)
239 {
240     for (int destinationRow = startRow, sourceRow = startRow + rows ; sourceRow < m_height ; ++destinationRow, ++sourceRow) {
241         for (int x = 0 ; x < m_width ; ++x) {
242             m_stitches[index(x, destinationRow)] = takeStitchQueueAt(x, sourceRow);
243         }
244     }
245 
246     int snapStartRow = startRow * 2;
247     int snapRows = rows * 2;
248 
249     QListIterator<Backstitch *> backstitchIterator(m_backstitches);
250 
251     while (backstitchIterator.hasNext()) {
252         Backstitch *backstitch = backstitchIterator.next();
253 
254         if (backstitch->start.y() >= snapStartRow + snapRows) {
255             backstitch->start.setY(backstitch->start.y() - snapRows);
256         }
257 
258         if (backstitch->end.y() >= snapStartRow + snapRows) {
259             backstitch->end.setY(backstitch->end.y() - snapRows);
260         }
261     }
262 
263     QListIterator<Knot *> knotIterator(m_knots);
264 
265     while (knotIterator.hasNext()) {
266         Knot *knot = knotIterator.next();
267 
268         if (knot->position.y() >= snapStartRow + snapRows) {
269             knot->position.setY(knot->position.y() - snapRows);
270         }
271     }
272 
273     resize(m_width, m_height - rows);
274 }
275 
276 
extents() const277 QRect StitchData::extents() const
278 {
279     QRect extentsRect;
280 
281     for (int y = 0 ; y < m_height ; ++y) {
282         for (int x = 0 ; x < m_width ; ++x) {
283             if (m_stitches.at(index(x, y))) {
284                 extentsRect |= QRect(x * 2, y * 2, 2, 2);
285             }
286         }
287     }
288 
289     QListIterator<Backstitch *> backstitchIterator(m_backstitches);
290 
291     while (backstitchIterator.hasNext()) {
292         Backstitch *backstitch = backstitchIterator.next();
293         extentsRect |= QRect(backstitch->start, backstitch->end).normalized();
294     }
295 
296     QListIterator<Knot *> knotIterator(m_knots);
297 
298     while (knotIterator.hasNext()) {
299         Knot *knot = knotIterator.next();
300         extentsRect |= QRect(knot->position, QSize(0, 0));
301     }
302 
303     extentsRect.adjust(-(extentsRect.left() % 2), -(extentsRect.top() % 2), extentsRect.right() % 2, extentsRect.bottom() % 2);
304 
305     if (extentsRect.isValid()) {
306         extentsRect = QRect(extentsRect.left() / 2, extentsRect.top() / 2, extentsRect.width() / 2, extentsRect.height() / 2);
307     }
308 
309     return extentsRect;
310 }
311 
312 
movePattern(int dx,int dy)313 void StitchData::movePattern(int dx, int dy)
314 {
315     QRect extentsRect = extents();
316 
317     QVector<StitchQueue *> newVector(m_width * m_height);
318 
319     for (int y = extentsRect.top() ; y <= extentsRect.bottom() ; ++y) {
320         for (int x = extentsRect.left() ; x <= extentsRect.right() ; ++x) {
321             newVector[index(x + dx, y + dy)] = takeStitchQueueAt(x, y);
322         }
323     }
324 
325     m_stitches = newVector;
326 
327     dx *= 2;
328     dy *= 2;
329 
330     QListIterator<Backstitch *> backstitchIterator(m_backstitches);
331 
332     while (backstitchIterator.hasNext()) {
333         backstitchIterator.next()->move(dx, dy);
334     }
335 
336     QListIterator<Knot *> knotIterator(m_knots);
337 
338     while (knotIterator.hasNext()) {
339         knotIterator.next()->move(dx, dy);
340     }
341 }
342 
343 
mirror(Qt::Orientation orientation)344 void StitchData::mirror(Qt::Orientation orientation)
345 {
346     int rows = m_height;
347     int cols = m_width;
348 
349     if (orientation == Qt::Vertical) {
350         rows = rows / 2 + rows % 2;
351     } else {
352         cols = cols / 2 + cols % 2;
353     }
354 
355     for (int row = 0 ; row < rows ; ++row) {
356         for (int col = 0 ; col < cols ; ++col) {
357             QPoint srcCell(col, row);
358             QPoint dstCell;
359 
360             if (orientation == Qt::Vertical) {
361                 dstCell = QPoint(col, m_height - row - 1);
362             } else {
363                 dstCell = QPoint(m_width - col - 1, row);
364             }
365 
366             StitchQueue *src = takeStitchQueueAt(srcCell);
367             StitchQueue *dst = takeStitchQueueAt(dstCell);
368 
369             if (src) {
370                 invertQueue(orientation, src);
371                 replaceStitchQueueAt(dstCell, src);
372             }
373 
374             if (dst) {
375                 invertQueue(orientation, dst);
376                 replaceStitchQueueAt(srcCell, dst);
377             }
378         }
379     }
380 
381     int maxXSnap = m_width * 2;
382     int maxYSnap = m_height * 2;
383     QListIterator<Backstitch *> bi(m_backstitches);
384 
385     while (bi.hasNext()) {
386         Backstitch *backstitch = bi.next();
387 
388         if (orientation == Qt::Horizontal) {
389             backstitch->start.setX(maxXSnap - backstitch->start.x());
390             backstitch->end.setX(maxXSnap - backstitch->end.x());
391         } else {
392             backstitch->start.setY(maxYSnap - backstitch->start.y());
393             backstitch->end.setY(maxYSnap - backstitch->end.y());
394         }
395     }
396 
397     QListIterator<Knot *> ki(m_knots);
398 
399     while (ki.hasNext()) {
400         Knot *knot = ki.next();
401 
402         if (orientation == Qt::Horizontal) {
403             knot->position.setX(maxXSnap - knot->position.x());
404         } else {
405             knot->position.setY(maxYSnap - knot->position.y());
406         }
407     }
408 }
409 
410 
rotate(Rotation rotation)411 void StitchData::rotate(Rotation rotation)
412 {
413     int rows = m_height;
414     int cols = m_width;
415 
416     QVector<StitchQueue *> rotatedData(m_width * m_height);
417 
418     for (int y = 0 ; y < rows ; ++y) {
419         for (int x = 0 ; x < cols ; ++x) {
420             StitchQueue *src = takeStitchQueueAt(x, y);
421             int index = (cols - x - 1) * rows + y; // default to Rotate90
422 
423             switch (rotation) {
424             case Rotate90:
425                 // index = (cols - x - 1) * rows + y;
426                 break;
427 
428             case Rotate180:
429                 index = (rows - y - 1) * cols + (cols - x - 1);
430                 break;
431 
432             case Rotate270:
433                 index = x * rows + (rows - y - 1);
434                 break;
435             }
436 
437             if (src) {
438                 rotateQueue(rotation, src);
439                 rotatedData[index] = src;
440             }
441         }
442     }
443 
444     if ((rotation == Rotate90) || (rotation == Rotate270)) {
445         int height = m_height;
446         m_height = m_width;
447         m_width = height;
448     }
449 
450     m_stitches = rotatedData;
451 
452     int maxXSnap = m_width * 2;
453     int maxYSnap = m_height * 2;
454     QListIterator<Backstitch *> bi(m_backstitches);
455 
456     while (bi.hasNext()) {
457         Backstitch *backstitch = bi.next();
458 
459         switch (rotation) {
460         case Rotate90:
461             backstitch->start = QPoint(backstitch->start.y(), maxXSnap - backstitch->start.x());
462             backstitch->end = QPoint(backstitch->end.y(), maxXSnap - backstitch->end.x());
463             break;
464 
465         case Rotate180:
466             backstitch->start = QPoint(maxXSnap - backstitch->start.x(), maxYSnap - backstitch->start.y());
467             backstitch->end = QPoint(maxXSnap - backstitch->end.x(), maxYSnap - backstitch->end.y());
468             break;
469 
470         case Rotate270:
471             backstitch->start = QPoint(maxYSnap - backstitch->start.y(), backstitch->start.x());
472             backstitch->end = QPoint(maxYSnap - backstitch->end.y(), backstitch->end.x());
473             break;
474         }
475     }
476 
477     QListIterator<Knot *> ki(m_knots);
478 
479     while (ki.hasNext()) {
480         Knot *knot = ki.next();
481 
482         switch (rotation) {
483         case Rotate90:
484             knot->position = QPoint(knot->position.y(), maxXSnap - knot->position.x());
485             break;
486 
487         case Rotate180:
488             knot->position = QPoint(maxXSnap - knot->position.x(), maxYSnap - knot->position.y());
489             break;
490 
491         case Rotate270:
492             knot->position = QPoint(maxYSnap - knot->position.y(), knot->position.x());
493             break;
494         }
495     }
496 }
497 
498 
invertQueue(Qt::Orientation orientation,StitchQueue * queue)499 void StitchData::invertQueue(Qt::Orientation orientation, StitchQueue *queue)
500 {
501     static QMap<Qt::Orientation, QMap<Stitch::Type, Stitch::Type> > mirrorMap;
502 
503     if (mirrorMap.isEmpty()) {
504         mirrorMap[Qt::Horizontal][Stitch::Delete] = Stitch::Delete;
505         mirrorMap[Qt::Horizontal][Stitch::TLQtr] = Stitch::TRQtr;
506         mirrorMap[Qt::Horizontal][Stitch::TRQtr] = Stitch::TLQtr;
507         mirrorMap[Qt::Horizontal][Stitch::BLQtr] = Stitch::BRQtr;
508         mirrorMap[Qt::Horizontal][Stitch::BRQtr] = Stitch::BLQtr;
509         mirrorMap[Qt::Horizontal][Stitch::BTHalf] = Stitch::TBHalf;
510         mirrorMap[Qt::Horizontal][Stitch::TBHalf] = Stitch::BTHalf;
511         mirrorMap[Qt::Horizontal][Stitch::TL3Qtr] = Stitch::TR3Qtr;
512         mirrorMap[Qt::Horizontal][Stitch::TR3Qtr] = Stitch::TL3Qtr;
513         mirrorMap[Qt::Horizontal][Stitch::BL3Qtr] = Stitch::BR3Qtr;
514         mirrorMap[Qt::Horizontal][Stitch::BR3Qtr] = Stitch::BL3Qtr;
515         mirrorMap[Qt::Horizontal][Stitch::TLSmallHalf] = Stitch::TRSmallHalf;
516         mirrorMap[Qt::Horizontal][Stitch::TRSmallHalf] = Stitch::TLSmallHalf;
517         mirrorMap[Qt::Horizontal][Stitch::BLSmallHalf] = Stitch::BRSmallHalf;
518         mirrorMap[Qt::Horizontal][Stitch::BRSmallHalf] = Stitch::BLSmallHalf;
519         mirrorMap[Qt::Horizontal][Stitch::TLSmallFull] = Stitch::TRSmallFull;
520         mirrorMap[Qt::Horizontal][Stitch::TRSmallFull] = Stitch::TLSmallFull;
521         mirrorMap[Qt::Horizontal][Stitch::BLSmallFull] = Stitch::BRSmallFull;
522         mirrorMap[Qt::Horizontal][Stitch::BRSmallFull] = Stitch::BLSmallFull;
523         mirrorMap[Qt::Horizontal][Stitch::Full] = Stitch::Full;
524 
525         mirrorMap[Qt::Vertical][Stitch::Delete] = Stitch::Delete;
526         mirrorMap[Qt::Vertical][Stitch::TLQtr] = Stitch::BLQtr;
527         mirrorMap[Qt::Vertical][Stitch::TRQtr] = Stitch::BRQtr;
528         mirrorMap[Qt::Vertical][Stitch::BLQtr] = Stitch::TLQtr;
529         mirrorMap[Qt::Vertical][Stitch::BRQtr] = Stitch::TRQtr;
530         mirrorMap[Qt::Vertical][Stitch::BTHalf] = Stitch::TBHalf;
531         mirrorMap[Qt::Vertical][Stitch::TBHalf] = Stitch::BTHalf;
532         mirrorMap[Qt::Vertical][Stitch::TL3Qtr] = Stitch::BL3Qtr;
533         mirrorMap[Qt::Vertical][Stitch::TR3Qtr] = Stitch::BR3Qtr;
534         mirrorMap[Qt::Vertical][Stitch::BL3Qtr] = Stitch::TL3Qtr;
535         mirrorMap[Qt::Vertical][Stitch::BR3Qtr] = Stitch::TR3Qtr;
536         mirrorMap[Qt::Vertical][Stitch::TLSmallHalf] = Stitch::BLSmallHalf;
537         mirrorMap[Qt::Vertical][Stitch::TRSmallHalf] = Stitch::BRSmallHalf;
538         mirrorMap[Qt::Vertical][Stitch::BLSmallHalf] = Stitch::TLSmallHalf;
539         mirrorMap[Qt::Vertical][Stitch::BRSmallHalf] = Stitch::TRSmallHalf;
540         mirrorMap[Qt::Vertical][Stitch::TLSmallFull] = Stitch::BLSmallFull;
541         mirrorMap[Qt::Vertical][Stitch::TRSmallFull] = Stitch::BRSmallFull;
542         mirrorMap[Qt::Vertical][Stitch::BLSmallFull] = Stitch::TLSmallFull;
543         mirrorMap[Qt::Vertical][Stitch::BRSmallFull] = Stitch::TRSmallFull;
544         mirrorMap[Qt::Vertical][Stitch::Full] = Stitch::Full;
545     }
546 
547     QListIterator<Stitch *> i(*queue);
548 
549     while (i.hasNext()) {
550         Stitch *stitch = i.next();
551         stitch->type = mirrorMap[orientation][stitch->type];
552     }
553 }
554 
555 
rotateQueue(Rotation rotation,StitchQueue * queue)556 void StitchData::rotateQueue(Rotation rotation, StitchQueue *queue)
557 {
558     static QMap<Rotation, QMap<Stitch::Type, Stitch::Type> > rotateMap;
559 
560     if (rotateMap.isEmpty()) {
561         rotateMap[Rotate90][Stitch::TLQtr] = Stitch::BLQtr;
562         rotateMap[Rotate90][Stitch::TRQtr] = Stitch::TLQtr;
563         rotateMap[Rotate90][Stitch::BLQtr] = Stitch::BRQtr;
564         rotateMap[Rotate90][Stitch::BRQtr] = Stitch::TRQtr;
565         rotateMap[Rotate90][Stitch::BTHalf] = Stitch::TBHalf;
566         rotateMap[Rotate90][Stitch::TBHalf] = Stitch::BTHalf;
567         rotateMap[Rotate90][Stitch::TL3Qtr] = Stitch::BL3Qtr;
568         rotateMap[Rotate90][Stitch::TR3Qtr] = Stitch::TL3Qtr;
569         rotateMap[Rotate90][Stitch::BL3Qtr] = Stitch::BR3Qtr;
570         rotateMap[Rotate90][Stitch::BR3Qtr] = Stitch::TR3Qtr;
571         rotateMap[Rotate90][Stitch::TLSmallHalf] = Stitch::BLSmallHalf;
572         rotateMap[Rotate90][Stitch::TRSmallHalf] = Stitch::TLSmallHalf;
573         rotateMap[Rotate90][Stitch::BLSmallHalf] = Stitch::BRSmallHalf;
574         rotateMap[Rotate90][Stitch::BRSmallHalf] = Stitch::TRSmallHalf;
575         rotateMap[Rotate90][Stitch::TLSmallFull] = Stitch::BLSmallFull;
576         rotateMap[Rotate90][Stitch::TRSmallFull] = Stitch::TLSmallFull;
577         rotateMap[Rotate90][Stitch::BLSmallFull] = Stitch::BRSmallFull;
578         rotateMap[Rotate90][Stitch::BRSmallFull] = Stitch::TRSmallFull;
579         rotateMap[Rotate90][Stitch::Full] = Stitch::Full;
580 
581         rotateMap[Rotate180][Stitch::TLQtr] = Stitch::BRQtr;
582         rotateMap[Rotate180][Stitch::TRQtr] = Stitch::BLQtr;
583         rotateMap[Rotate180][Stitch::BLQtr] = Stitch::TRQtr;
584         rotateMap[Rotate180][Stitch::BRQtr] = Stitch::TLQtr;
585         rotateMap[Rotate180][Stitch::BTHalf] = Stitch::BTHalf;
586         rotateMap[Rotate180][Stitch::TBHalf] = Stitch::TBHalf;
587         rotateMap[Rotate180][Stitch::TL3Qtr] = Stitch::BR3Qtr;
588         rotateMap[Rotate180][Stitch::TR3Qtr] = Stitch::BL3Qtr;
589         rotateMap[Rotate180][Stitch::BL3Qtr] = Stitch::TR3Qtr;
590         rotateMap[Rotate180][Stitch::BR3Qtr] = Stitch::TL3Qtr;
591         rotateMap[Rotate180][Stitch::TLSmallHalf] = Stitch::BRSmallHalf;
592         rotateMap[Rotate180][Stitch::TRSmallHalf] = Stitch::BLSmallHalf;
593         rotateMap[Rotate180][Stitch::BLSmallHalf] = Stitch::TRSmallHalf;
594         rotateMap[Rotate180][Stitch::BRSmallHalf] = Stitch::TLSmallHalf;
595         rotateMap[Rotate180][Stitch::TLSmallFull] = Stitch::BRSmallFull;
596         rotateMap[Rotate180][Stitch::TRSmallFull] = Stitch::BLSmallFull;
597         rotateMap[Rotate180][Stitch::BLSmallFull] = Stitch::TRSmallFull;
598         rotateMap[Rotate180][Stitch::BRSmallFull] = Stitch::TLSmallFull;
599         rotateMap[Rotate180][Stitch::Full] = Stitch::Full;
600 
601         rotateMap[Rotate270][Stitch::TLQtr] = Stitch::TRQtr;
602         rotateMap[Rotate270][Stitch::TRQtr] = Stitch::BRQtr;
603         rotateMap[Rotate270][Stitch::BLQtr] = Stitch::TLQtr;
604         rotateMap[Rotate270][Stitch::BRQtr] = Stitch::BLQtr;
605         rotateMap[Rotate270][Stitch::BTHalf] = Stitch::TBHalf;
606         rotateMap[Rotate270][Stitch::TBHalf] = Stitch::BTHalf;
607         rotateMap[Rotate270][Stitch::TL3Qtr] = Stitch::TR3Qtr;
608         rotateMap[Rotate270][Stitch::TR3Qtr] = Stitch::BR3Qtr;
609         rotateMap[Rotate270][Stitch::BL3Qtr] = Stitch::TL3Qtr;
610         rotateMap[Rotate270][Stitch::BR3Qtr] = Stitch::BL3Qtr;
611         rotateMap[Rotate270][Stitch::TLSmallHalf] = Stitch::TRSmallHalf;
612         rotateMap[Rotate270][Stitch::TRSmallHalf] = Stitch::BRSmallHalf;
613         rotateMap[Rotate270][Stitch::BLSmallHalf] = Stitch::TLSmallHalf;
614         rotateMap[Rotate270][Stitch::BRSmallHalf] = Stitch::BLSmallHalf;
615         rotateMap[Rotate270][Stitch::TLSmallFull] = Stitch::TRSmallFull;
616         rotateMap[Rotate270][Stitch::TRSmallFull] = Stitch::BRSmallFull;
617         rotateMap[Rotate270][Stitch::BLSmallFull] = Stitch::TLSmallFull;
618         rotateMap[Rotate270][Stitch::BRSmallFull] = Stitch::BLSmallFull;
619         rotateMap[Rotate270][Stitch::Full] = Stitch::Full;
620     }
621 
622     QListIterator<Stitch *> i(*queue);
623 
624     while (i.hasNext()) {
625         Stitch *stitch = i.next();
626         stitch->type = rotateMap[rotation][stitch->type];
627     }
628 }
629 
630 
index(int x,int y) const631 int StitchData::index(int x, int y) const
632 {
633     return y * m_width + x;
634 }
635 
636 
index(const QPoint & cell) const637 int StitchData::index(const QPoint &cell) const
638 {
639     return index(cell.x(), cell.y());
640 }
641 
642 
isValid(int x,int y) const643 bool StitchData::isValid(int x, int y) const
644 {
645     return ((x >= 0) && (x < m_width) && (y >= 0) && (y < m_height));
646 }
647 
648 
addStitch(const QPoint & position,Stitch::Type type,int colorIndex)649 void StitchData::addStitch(const QPoint &position, Stitch::Type type, int colorIndex)
650 {
651     int i = index(position);
652     StitchQueue *stitchQueue = m_stitches.at(i);
653 
654     if (stitchQueue == nullptr) {
655         stitchQueue = new StitchQueue;
656         m_stitches[i] = stitchQueue;
657     }
658 
659     stitchQueue->add(type, colorIndex);
660 }
661 
662 
findStitch(const QPoint & cell,Stitch::Type type,int colorIndex)663 Stitch *StitchData::findStitch(const QPoint &cell, Stitch::Type type, int colorIndex)
664 {
665     StitchQueue *stitchQueue = stitchQueueAt(cell);
666     Stitch *found = nullptr;
667 
668     if (stitchQueue) {
669         if (Stitch *stitch = stitchQueue->find(type, colorIndex)) {
670             found = stitch;
671         }
672     }
673 
674     return found;
675 }
676 
677 
deleteStitch(const QPoint & position,Stitch::Type type,int colorIndex)678 void StitchData::deleteStitch(const QPoint &position, Stitch::Type type, int colorIndex)
679 {
680     int i = index(position);
681     StitchQueue *stitchQueue = m_stitches.at(i);
682 
683     if (stitchQueue) {
684         if (stitchQueue->remove(type, colorIndex) == 0) {
685             m_stitches[i] = nullptr;
686             delete stitchQueue;
687         }
688     }
689 }
690 
691 
stitchQueueAt(int x,int y)692 StitchQueue *StitchData::stitchQueueAt(int x, int y)
693 {
694     StitchQueue *stitchQueue = nullptr;
695 
696     if (isValid(x, y)) {
697         stitchQueue = m_stitches.at(index(x, y));
698     }
699 
700     return stitchQueue;
701 }
702 
703 
stitchQueueAt(const QPoint & position)704 StitchQueue *StitchData::stitchQueueAt(const QPoint &position)
705 {
706     return stitchQueueAt(position.x(), position.y());
707 }
708 
709 
takeStitchQueueAt(int x,int y)710 StitchQueue *StitchData::takeStitchQueueAt(int x, int y)
711 {
712     StitchQueue *stitchQueue = stitchQueueAt(x, y);
713 
714     if (stitchQueue) {
715         m_stitches[index(x, y)] = nullptr;
716     }
717 
718     return stitchQueue;
719 }
720 
721 
takeStitchQueueAt(const QPoint & position)722 StitchQueue *StitchData::takeStitchQueueAt(const QPoint &position)
723 {
724     return takeStitchQueueAt(position.x(), position.y());
725 }
726 
727 
replaceStitchQueueAt(int x,int y,StitchQueue * stitchQueue)728 StitchQueue *StitchData::replaceStitchQueueAt(int x, int y, StitchQueue *stitchQueue)
729 {
730     StitchQueue *originalQueue = takeStitchQueueAt(x, y);
731 
732     if (isValid(x, y)) {
733         m_stitches[index(x, y)] = stitchQueue;
734     }
735 
736     return originalQueue;
737 }
738 
739 
replaceStitchQueueAt(const QPoint & position,StitchQueue * stitchQueue)740 StitchQueue *StitchData::replaceStitchQueueAt(const QPoint &position, StitchQueue *stitchQueue)
741 {
742     return replaceStitchQueueAt(position.x(), position.y(), stitchQueue);
743 }
744 
745 
addBackstitch(const QPoint & start,const QPoint & end,int colorIndex)746 void StitchData::addBackstitch(const QPoint &start, const QPoint &end, int colorIndex)
747 {
748     m_backstitches.append(new Backstitch(start, end, colorIndex));
749 }
750 
751 
addBackstitch(Backstitch * backstitch)752 void StitchData::addBackstitch(Backstitch *backstitch)
753 {
754     m_backstitches.append(backstitch);
755 }
756 
757 
findBackstitch(const QPoint & start,const QPoint & end,int colorIndex)758 Backstitch *StitchData::findBackstitch(const QPoint &start, const QPoint &end, int colorIndex)
759 {
760     Backstitch *found = nullptr;
761 
762     foreach (Backstitch *backstitch, m_backstitches) {
763         if (backstitch->contains(start) && backstitch->contains(end) && ((colorIndex == -1) || backstitch->colorIndex == colorIndex)) {
764             found = backstitch;
765             break;
766         }
767     }
768 
769     return found;
770 }
771 
772 
takeBackstitch(const QPoint & start,const QPoint & end,int colorIndex)773 Backstitch *StitchData::takeBackstitch(const QPoint &start, const QPoint &end, int colorIndex)
774 {
775     Backstitch *removed = findBackstitch(start, end, colorIndex);
776     m_backstitches.removeOne(removed);
777 
778     return removed;
779 }
780 
781 
takeBackstitch(Backstitch * backstitch)782 Backstitch *StitchData::takeBackstitch(Backstitch *backstitch)
783 {
784     Backstitch *removed = nullptr;
785 
786     if (m_backstitches.removeOne(backstitch)) {
787         removed = backstitch;
788     }
789 
790     return removed;
791 }
792 
793 
addFrenchKnot(const QPoint & position,int colorIndex)794 void StitchData::addFrenchKnot(const QPoint &position, int colorIndex)
795 {
796     m_knots.append(new Knot(position, colorIndex));
797 }
798 
799 
addFrenchKnot(Knot * knot)800 void StitchData::addFrenchKnot(Knot *knot)
801 {
802     m_knots.append(knot);
803 }
804 
805 
findKnot(const QPoint & position,int colorIndex)806 Knot *StitchData::findKnot(const QPoint &position, int colorIndex)
807 {
808     Knot *found = nullptr;
809 
810     foreach (Knot *knot, m_knots) {
811         if ((knot->position == position) && ((colorIndex == -1) || (knot->colorIndex == colorIndex))) {
812             found = knot;
813             break;
814         }
815     }
816 
817     return found;
818 }
819 
820 
takeFrenchKnot(const QPoint & position,int colorIndex)821 Knot *StitchData::takeFrenchKnot(const QPoint &position, int colorIndex)
822 {
823     Knot *removed = findKnot(position, colorIndex);
824 
825     if (removed) {
826         m_knots.removeOne(removed);
827     }
828 
829     return removed;
830 }
831 
832 
takeFrenchKnot(Knot * knot)833 Knot *StitchData::takeFrenchKnot(Knot *knot)
834 {
835     Knot *removed = nullptr;
836 
837     if (m_knots.removeOne(knot)) {
838         removed = knot;
839     }
840 
841     return removed;
842 }
843 
844 
backstitches()845 QList<Backstitch *> &StitchData::backstitches()
846 {
847     return m_backstitches;
848 }
849 
850 
knots()851 QList<Knot *> &StitchData::knots()
852 {
853     return m_knots;
854 }
855 
856 
backstitchIterator()857 QListIterator<Backstitch *> StitchData::backstitchIterator()
858 {
859     return QListIterator<Backstitch *>(m_backstitches);
860 }
861 
862 
mutableBackstitchIterator()863 QMutableListIterator<Backstitch *> StitchData::mutableBackstitchIterator()
864 {
865     return QMutableListIterator<Backstitch *>(m_backstitches);
866 }
867 
868 
knotIterator()869 QListIterator<Knot *> StitchData::knotIterator()
870 {
871     return QListIterator<Knot *>(m_knots);
872 }
873 
874 
mutableKnotIterator()875 QMutableListIterator<Knot *> StitchData::mutableKnotIterator()
876 {
877     return QMutableListIterator<Knot *>(m_knots);
878 }
879 
880 
flossUsage()881 QMap<int, FlossUsage> StitchData::flossUsage()
882 {
883     QMap<int, FlossUsage> usage;
884     static QMap<Stitch::Type, double> lengths;
885 
886     if (!lengths.count()) {
887         lengths.insert(Stitch::Delete, 0.0);
888         lengths.insert(Stitch::TLQtr, 0.707107 + 0.5);
889         lengths.insert(Stitch::TRQtr, 0.707107 + 0.5);
890         lengths.insert(Stitch::BLQtr, 0.707107 + 0.5);
891         lengths.insert(Stitch::BTHalf, 1.414213 + 1.0);
892         lengths.insert(Stitch::TL3Qtr, 1.414213 + 0.707107 + 1.0 + 0.5);
893         lengths.insert(Stitch::BRQtr, 0.707107 + 0.5);
894         lengths.insert(Stitch::TBHalf, 1.414213 + 1.0);
895         lengths.insert(Stitch::TR3Qtr, 1.414213 + 0.707107 + 1.0 + 0.5);
896         lengths.insert(Stitch::BL3Qtr, 1.414213 + 0.707107 + 1.0 + 0.5);
897         lengths.insert(Stitch::BR3Qtr, 1.414213 + 0.707107 + 1.0 + 0.5);
898         lengths.insert(Stitch::Full, 1.414213 + 1.414213 + 1.0 + 1.0);
899         lengths.insert(Stitch::TLSmallHalf, 0.707107 + 0.5);
900         lengths.insert(Stitch::TRSmallHalf, 0.707107 + 0.5);
901         lengths.insert(Stitch::BLSmallHalf, 0.707107 + 0.5);
902         lengths.insert(Stitch::BRSmallHalf, 0.707107 + 0.5);
903         lengths.insert(Stitch::TLSmallFull, 0.707107 + 0.5 + 0.707107 + 0.5);
904         lengths.insert(Stitch::TRSmallFull, 0.707107 + 0.5 + 0.707107 + 0.5);
905         lengths.insert(Stitch::BLSmallFull, 0.707107 + 0.5 + 0.707107 + 0.5);
906         lengths.insert(Stitch::BRSmallFull, 0.707107 + 0.5 + 0.707107 + 0.5);
907         lengths.insert(Stitch::FrenchKnot, 2.0);
908     }
909 
910     QVectorIterator<StitchQueue *> stitchesIterator(m_stitches);
911 
912     while (stitchesIterator.hasNext()) {
913         StitchQueue *stitchQueue = stitchesIterator.next();
914 
915         if (stitchQueue) {
916             QListIterator<Stitch *> stitchIterator(*stitchQueue);
917 
918             while (stitchIterator.hasNext()) {
919                 Stitch *stitch = stitchIterator.next();
920                 usage[stitch->colorIndex].stitchCounts[stitch->type]++;
921                 usage[stitch->colorIndex].stitchLengths[stitch->type] += lengths[stitch->type];
922             }
923         }
924     }
925 
926 
927     QListIterator<Backstitch *> backstitchIterator(m_backstitches);
928 
929     while (backstitchIterator.hasNext()) {
930         Backstitch *backstitch = backstitchIterator.next();
931         usage[backstitch->colorIndex].backstitchCount++;
932         usage[backstitch->colorIndex].backstitchLength += QPoint(backstitch->start - backstitch->end).manhattanLength();
933     }
934 
935     QListIterator<Knot *> knotIterator(m_knots);
936 
937     while (knotIterator.hasNext()) {
938         Knot *knot = knotIterator.next();
939         usage[knot->colorIndex].stitchCounts[Stitch::FrenchKnot]++;
940         usage[knot->colorIndex].stitchLengths[Stitch::FrenchKnot] += lengths[Stitch::FrenchKnot];
941     }
942 
943     return usage;
944 }
945 
946 
operator <<(QDataStream & stream,const StitchData & stitchData)947 QDataStream &operator<<(QDataStream &stream, const StitchData &stitchData)
948 {
949     stream << qint32(stitchData.version);
950     stream << qint32(stitchData.m_width);
951     stream << qint32(stitchData.m_height);
952 
953     QVectorIterator<StitchQueue *> stitchesIterator(stitchData.m_stitches);
954     int queues = 0;
955 
956     while (stitchesIterator.hasNext()) {
957         if (stitchesIterator.next()) {
958             ++queues;
959         }
960     }
961 
962     stream << qint32(queues);
963 
964     for (int row = 0 ; row < stitchData.m_height ; ++row) {
965         for (int column = 0 ; column < stitchData.m_width ; ++column) {
966             if (StitchQueue *stitchQueue = stitchData.m_stitches[stitchData.index(column, row)]) {
967                 stream << qint32(column);
968                 stream << qint32(row);
969                 stream << *stitchQueue;
970             }
971         }
972     }
973 
974     QListIterator<Backstitch *> backstitchIterator(stitchData.m_backstitches);
975     stream << qint32(stitchData.m_backstitches.count());
976 
977     while (backstitchIterator.hasNext()) {
978         stream << *(backstitchIterator.next());
979 
980         if (stream.status() != QDataStream::Ok) {
981             throw FailedWriteFile(stream.status());
982         }
983     }
984 
985     QListIterator<Knot *> knotIterator(stitchData.m_knots);
986     stream << qint32(stitchData.m_knots.count());
987 
988     while (knotIterator.hasNext()) {
989         stream << *(knotIterator.next());
990 
991         if (stream.status() != QDataStream::Ok) {
992             throw FailedWriteFile(stream.status());
993         }
994     }
995 
996     if (stream.status() != QDataStream::Ok) {
997         throw FailedWriteFile(stream.status());
998     }
999 
1000     return stream;
1001 }
1002 
1003 
operator >>(QDataStream & stream,StitchData & stitchData)1004 QDataStream &operator>>(QDataStream &stream, StitchData &stitchData)
1005 {
1006     qint32 version;
1007     qint32 width;
1008     qint32 height;
1009     qint32 layers;
1010     qint32 columns;
1011     qint32 rows;
1012     qint32 count;
1013     QHash<int, QHash<int, StitchQueue *> > stitches;
1014 
1015     stitchData.clear();
1016 
1017     stream >> version;
1018 
1019     switch (version) {
1020     case 103:
1021         stream >> width;
1022         stream >> height;
1023         stitchData.resize(width, height);
1024         stream >> count;
1025 
1026         while (count--) {
1027             stream >> columns;
1028             stream >> rows;
1029             StitchQueue *stitchQueue = new StitchQueue;
1030             stitchData.replaceStitchQueueAt(columns, rows, stitchQueue);
1031             stream >> *stitchQueue;
1032         }
1033 
1034         stream >> count;
1035 
1036         while (count--) {
1037             Backstitch *backstitch = new Backstitch;
1038             stream >> *(backstitch);
1039             stitchData.addBackstitch(backstitch);
1040         }
1041 
1042         stream >> count;
1043 
1044         while (count--) {
1045             Knot *knot = new Knot;
1046             stream >> *knot;
1047             stitchData.addFrenchKnot(knot);
1048         }
1049 
1050         break;
1051 
1052     case 102:
1053         stream >> width;
1054         stream >> height;
1055         stitchData.resize(width, height);
1056 
1057         stream >> columns;
1058 
1059         while (columns--) {
1060             stream >> rows;
1061 
1062             while (rows--) {
1063                 qint32 column;
1064                 qint32 row;
1065 
1066                 stream >> column;
1067                 stream >> row;
1068 
1069                 StitchQueue *stitchQueue = new StitchQueue;
1070                 stitches[column][row] = stitchQueue;
1071                 stream >> *stitchQueue;
1072             }
1073         }
1074 
1075         for (int y = 0 ; y < height ; ++y) {
1076             for (int x = 0 ; x < width ; ++x) {
1077                 if (stitches[x][y]) {
1078                     stitchData.replaceStitchQueueAt(x, y, stitches[x][y]);
1079                 }
1080             }
1081         }
1082 
1083         stream >> count;
1084 
1085         while (count--) {
1086             Backstitch *backstitch = new Backstitch;
1087             stream >> *(backstitch);
1088             stitchData.addBackstitch(backstitch);
1089         }
1090 
1091         stream >> count;
1092 
1093         while (count--) {
1094             Knot *knot = new Knot;
1095             stream >> *knot;
1096             stitchData.addFrenchKnot(knot);
1097         }
1098 
1099         break;
1100 
1101     case 101:
1102         stream >> width;
1103         stream >> height;
1104         stitchData.resize(width, height);
1105 
1106         stream >> columns;
1107 
1108         while (columns--) {
1109             stream >> rows;
1110 
1111             while (rows--) {
1112                 qint32 column;
1113                 qint32 row;
1114 
1115                 stream >> column;
1116                 stream >> row;
1117 
1118                 StitchQueue *stitchQueue = new StitchQueue;
1119                 stitches[column][row] = stitchQueue;
1120                 stream >> *stitchQueue;
1121             }
1122         }
1123 
1124         for (int y = 0 ; y < height ; ++y) {
1125             for (int x = 0 ; x < width ; ++x) {
1126                 if (stitches[x][y]) {
1127                     stitchData.replaceStitchQueueAt(x, y, stitches[x][y]);
1128                 }
1129             }
1130         }
1131 
1132         break;
1133 
1134     case 100:
1135         stream >> width;
1136         stream >> height;
1137         stitchData.m_width = width;
1138         stitchData.m_height = height;
1139 
1140         stream >> layers;
1141 
1142         while (layers--) {
1143             stream >> columns;
1144 
1145             while (columns--) {
1146                 stream >> rows;
1147 
1148                 while (rows--) {
1149                     qint32 layer;
1150                     qint32 column;
1151                     qint32 row;
1152 
1153                     stream >> layer;
1154                     stream >> column;
1155                     stream >> row;
1156 
1157                     StitchQueue *stitchQueue = new StitchQueue;
1158                     stitches[column][row] = stitchQueue;
1159                     stream >> *stitchQueue;
1160                 }
1161             }
1162         }
1163 
1164         for (int y = 0 ; y < height ; ++y) {
1165             for (int x = 0 ; x < width ; ++x) {
1166                 if (stitches[x][y]) {
1167                     stitchData.replaceStitchQueueAt(x, y, stitches[x][y]);
1168                 }
1169             }
1170         }
1171 
1172         break;
1173 
1174     default:
1175         throw InvalidFileVersion(QString(i18n("Stitch data version %1", version)));
1176         break;
1177     }
1178 
1179     if (stream.status() != QDataStream::Ok) {
1180         throw FailedReadFile(QString(i18n("Failed reading stitch data")));
1181     }
1182 
1183     return stream;
1184 }
1185