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