File indexing completed on 2024-04-28 04:32:08

0001 /*
0002  * Copyright (C) 2010-2015 by Stephen Allewell
0003  * steve.allewell@gmail.com
0004  *
0005  * This program is free software; you can redistribute it and/or modify
0006  * it under the terms of the GNU General Public License as published by
0007  * the Free Software Foundation; either version 2 of the License, or
0008  * (at your option) any later version.
0009  */
0010 
0011 #include "StitchData.h"
0012 
0013 #include <KLocalizedString>
0014 
0015 #include "Exceptions.h"
0016 
0017 FlossUsage::FlossUsage()
0018     : backstitchCount(0)
0019     , backstitchLength(0.0)
0020 {
0021 }
0022 
0023 double FlossUsage::totalLength() const
0024 {
0025     return stitchLength() + backstitchLength;
0026 }
0027 
0028 double FlossUsage::stitchLength() const
0029 {
0030     double total = 0;
0031 
0032     foreach (double length, stitchLengths) {
0033         total += length;
0034     }
0035 
0036     return total;
0037 }
0038 
0039 int FlossUsage::totalStitches() const
0040 {
0041     return stitchCount() + backstitchCount;
0042 }
0043 
0044 int FlossUsage::stitchCount() const
0045 {
0046     int total = 0;
0047 
0048     foreach (int count, stitchCounts) {
0049         total += count;
0050     }
0051 
0052     return total;
0053 }
0054 
0055 StitchData::StitchData()
0056     : m_width(0)
0057     , m_height(0)
0058 {
0059 }
0060 
0061 StitchData::~StitchData()
0062 {
0063     clear();
0064 }
0065 
0066 void StitchData::clear()
0067 {
0068     qDeleteAll(m_stitches);
0069     m_stitches.fill(nullptr);
0070 
0071     qDeleteAll(m_backstitches);
0072     m_backstitches.clear();
0073 
0074     qDeleteAll(m_knots);
0075     m_knots.clear();
0076 }
0077 
0078 int StitchData::width() const
0079 {
0080     return m_width;
0081 }
0082 
0083 int StitchData::height() const
0084 {
0085     return m_height;
0086 }
0087 
0088 void StitchData::resize(int width, int height)
0089 {
0090     QVector<StitchQueue *> newVector(width * height);
0091     QRect extentsRect = extents();
0092 
0093     for (int y = extentsRect.top(); y <= extentsRect.bottom(); ++y) {
0094         for (int x = extentsRect.left(); x <= extentsRect.right(); ++x) {
0095             newVector[y * width + x] = takeStitchQueueAt(x, y);
0096         }
0097     }
0098 
0099     m_stitches = newVector;
0100     m_width = width;
0101     m_height = height;
0102 }
0103 
0104 void StitchData::insertColumns(int startColumn, int columns)
0105 {
0106     int originalWidth = m_width;
0107 
0108     resize(originalWidth + columns, m_height);
0109 
0110     for (int y = 0; y < m_height; ++y) {
0111         for (int destinationColumn = m_width - 1, sourceColumn = originalWidth - 1; sourceColumn >= startColumn; --destinationColumn, --sourceColumn) {
0112             m_stitches[index(destinationColumn, y)] = takeStitchQueueAt(sourceColumn, y);
0113         }
0114     }
0115 
0116     startColumn *= 2;
0117     columns *= 2;
0118 
0119     QListIterator<Backstitch *> backstitchIterator(m_backstitches);
0120 
0121     while (backstitchIterator.hasNext()) {
0122         Backstitch *backstitch = backstitchIterator.next();
0123 
0124         if (backstitch->start.x() >= startColumn) {
0125             backstitch->start.setX(backstitch->start.x() + columns);
0126         }
0127 
0128         if (backstitch->end.x() >= startColumn) {
0129             backstitch->end.setX(backstitch->end.x() + columns);
0130         }
0131     }
0132 
0133     QListIterator<Knot *> knotIterator(m_knots);
0134 
0135     while (knotIterator.hasNext()) {
0136         Knot *knot = knotIterator.next();
0137 
0138         if (knot->position.x() >= startColumn) {
0139             knot->position.setX(knot->position.x() + columns);
0140         }
0141     }
0142 }
0143 
0144 void StitchData::insertRows(int startRow, int rows)
0145 {
0146     int originalHeight = m_height;
0147 
0148     resize(m_width, originalHeight + rows);
0149 
0150     for (int destinationRow = m_height - 1, sourceRow = originalHeight - 1; sourceRow >= startRow; --destinationRow, --sourceRow) {
0151         for (int x = 0; x < m_width; ++x) {
0152             m_stitches[index(x, destinationRow)] = takeStitchQueueAt(x, sourceRow);
0153         }
0154     }
0155 
0156     startRow *= 2;
0157     rows *= 2;
0158 
0159     QListIterator<Backstitch *> backstitchIterator(m_backstitches);
0160 
0161     while (backstitchIterator.hasNext()) {
0162         Backstitch *backstitch = backstitchIterator.next();
0163 
0164         if (backstitch->start.y() >= startRow) {
0165             backstitch->start.setY(backstitch->start.y() + rows);
0166         }
0167 
0168         if (backstitch->end.y() >= startRow) {
0169             backstitch->end.setY(backstitch->end.y() + rows);
0170         }
0171     }
0172 
0173     QListIterator<Knot *> knotIterator(m_knots);
0174 
0175     while (knotIterator.hasNext()) {
0176         Knot *knot = knotIterator.next();
0177 
0178         if (knot->position.y() >= startRow) {
0179             knot->position.setY(knot->position.y() + rows);
0180         }
0181     }
0182 }
0183 
0184 void StitchData::removeColumns(int startColumn, int columns)
0185 {
0186     for (int y = 0; y < m_height; ++y) {
0187         for (int destinationColumn = startColumn, sourceColumn = startColumn + columns; sourceColumn < m_width; ++destinationColumn, ++sourceColumn) {
0188             m_stitches[index(destinationColumn, y)] = takeStitchQueueAt(sourceColumn, y);
0189         }
0190     }
0191 
0192     int snapStartColumn = startColumn * 2;
0193     int snapColumns = columns * 2;
0194 
0195     QListIterator<Backstitch *> backstitchIterator(m_backstitches);
0196 
0197     while (backstitchIterator.hasNext()) {
0198         Backstitch *backstitch = backstitchIterator.next();
0199 
0200         if (backstitch->start.x() >= snapStartColumn + snapColumns) {
0201             backstitch->start.setX(backstitch->start.x() - snapColumns);
0202         }
0203 
0204         if (backstitch->end.x() >= snapStartColumn + snapColumns) {
0205             backstitch->end.setX(backstitch->end.x() - snapColumns);
0206         }
0207     }
0208 
0209     QListIterator<Knot *> knotIterator(m_knots);
0210 
0211     while (knotIterator.hasNext()) {
0212         Knot *knot = knotIterator.next();
0213 
0214         if (knot->position.x() >= snapStartColumn + snapColumns) {
0215             knot->position.setX(knot->position.x() - snapColumns);
0216         }
0217     }
0218 
0219     resize(m_width - columns, m_height);
0220 }
0221 
0222 void StitchData::removeRows(int startRow, int rows)
0223 {
0224     for (int destinationRow = startRow, sourceRow = startRow + rows; sourceRow < m_height; ++destinationRow, ++sourceRow) {
0225         for (int x = 0; x < m_width; ++x) {
0226             m_stitches[index(x, destinationRow)] = takeStitchQueueAt(x, sourceRow);
0227         }
0228     }
0229 
0230     int snapStartRow = startRow * 2;
0231     int snapRows = rows * 2;
0232 
0233     QListIterator<Backstitch *> backstitchIterator(m_backstitches);
0234 
0235     while (backstitchIterator.hasNext()) {
0236         Backstitch *backstitch = backstitchIterator.next();
0237 
0238         if (backstitch->start.y() >= snapStartRow + snapRows) {
0239             backstitch->start.setY(backstitch->start.y() - snapRows);
0240         }
0241 
0242         if (backstitch->end.y() >= snapStartRow + snapRows) {
0243             backstitch->end.setY(backstitch->end.y() - snapRows);
0244         }
0245     }
0246 
0247     QListIterator<Knot *> knotIterator(m_knots);
0248 
0249     while (knotIterator.hasNext()) {
0250         Knot *knot = knotIterator.next();
0251 
0252         if (knot->position.y() >= snapStartRow + snapRows) {
0253             knot->position.setY(knot->position.y() - snapRows);
0254         }
0255     }
0256 
0257     resize(m_width, m_height - rows);
0258 }
0259 
0260 QRect StitchData::extents() const
0261 {
0262     QRect extentsRect;
0263 
0264     for (int y = 0; y < m_height; ++y) {
0265         for (int x = 0; x < m_width; ++x) {
0266             if (m_stitches.at(index(x, y))) {
0267                 extentsRect |= QRect(x * 2, y * 2, 2, 2);
0268             }
0269         }
0270     }
0271 
0272     QListIterator<Backstitch *> backstitchIterator(m_backstitches);
0273 
0274     while (backstitchIterator.hasNext()) {
0275         Backstitch *backstitch = backstitchIterator.next();
0276         extentsRect |= QRect(backstitch->start, backstitch->end).normalized();
0277     }
0278 
0279     QListIterator<Knot *> knotIterator(m_knots);
0280 
0281     while (knotIterator.hasNext()) {
0282         Knot *knot = knotIterator.next();
0283         extentsRect |= QRect(knot->position, QSize(0, 0));
0284     }
0285 
0286     extentsRect.adjust(-(extentsRect.left() % 2), -(extentsRect.top() % 2), extentsRect.right() % 2, extentsRect.bottom() % 2);
0287 
0288     if (extentsRect.isValid()) {
0289         extentsRect = QRect(extentsRect.left() / 2, extentsRect.top() / 2, extentsRect.width() / 2, extentsRect.height() / 2);
0290     }
0291 
0292     return extentsRect;
0293 }
0294 
0295 void StitchData::movePattern(int dx, int dy)
0296 {
0297     QRect extentsRect = extents();
0298 
0299     QVector<StitchQueue *> newVector(m_width * m_height);
0300 
0301     for (int y = extentsRect.top(); y <= extentsRect.bottom(); ++y) {
0302         for (int x = extentsRect.left(); x <= extentsRect.right(); ++x) {
0303             newVector[index(x + dx, y + dy)] = takeStitchQueueAt(x, y);
0304         }
0305     }
0306 
0307     m_stitches = newVector;
0308 
0309     dx *= 2;
0310     dy *= 2;
0311 
0312     QListIterator<Backstitch *> backstitchIterator(m_backstitches);
0313 
0314     while (backstitchIterator.hasNext()) {
0315         backstitchIterator.next()->move(dx, dy);
0316     }
0317 
0318     QListIterator<Knot *> knotIterator(m_knots);
0319 
0320     while (knotIterator.hasNext()) {
0321         knotIterator.next()->move(dx, dy);
0322     }
0323 }
0324 
0325 void StitchData::mirror(Qt::Orientation orientation)
0326 {
0327     int rows = m_height;
0328     int cols = m_width;
0329 
0330     if (orientation == Qt::Vertical) {
0331         rows = rows / 2 + rows % 2;
0332     } else {
0333         cols = cols / 2 + cols % 2;
0334     }
0335 
0336     for (int row = 0; row < rows; ++row) {
0337         for (int col = 0; col < cols; ++col) {
0338             QPoint srcCell(col, row);
0339             QPoint dstCell;
0340 
0341             if (orientation == Qt::Vertical) {
0342                 dstCell = QPoint(col, m_height - row - 1);
0343             } else {
0344                 dstCell = QPoint(m_width - col - 1, row);
0345             }
0346 
0347             StitchQueue *src = takeStitchQueueAt(srcCell);
0348             StitchQueue *dst = takeStitchQueueAt(dstCell);
0349 
0350             if (src) {
0351                 invertQueue(orientation, src);
0352                 replaceStitchQueueAt(dstCell, src);
0353             }
0354 
0355             if (dst) {
0356                 invertQueue(orientation, dst);
0357                 replaceStitchQueueAt(srcCell, dst);
0358             }
0359         }
0360     }
0361 
0362     int maxXSnap = m_width * 2;
0363     int maxYSnap = m_height * 2;
0364     QListIterator<Backstitch *> bi(m_backstitches);
0365 
0366     while (bi.hasNext()) {
0367         Backstitch *backstitch = bi.next();
0368 
0369         if (orientation == Qt::Horizontal) {
0370             backstitch->start.setX(maxXSnap - backstitch->start.x());
0371             backstitch->end.setX(maxXSnap - backstitch->end.x());
0372         } else {
0373             backstitch->start.setY(maxYSnap - backstitch->start.y());
0374             backstitch->end.setY(maxYSnap - backstitch->end.y());
0375         }
0376     }
0377 
0378     QListIterator<Knot *> ki(m_knots);
0379 
0380     while (ki.hasNext()) {
0381         Knot *knot = ki.next();
0382 
0383         if (orientation == Qt::Horizontal) {
0384             knot->position.setX(maxXSnap - knot->position.x());
0385         } else {
0386             knot->position.setY(maxYSnap - knot->position.y());
0387         }
0388     }
0389 }
0390 
0391 void StitchData::rotate(Rotation rotation)
0392 {
0393     int rows = m_height;
0394     int cols = m_width;
0395 
0396     QVector<StitchQueue *> rotatedData(m_width * m_height);
0397 
0398     for (int y = 0; y < rows; ++y) {
0399         for (int x = 0; x < cols; ++x) {
0400             StitchQueue *src = takeStitchQueueAt(x, y);
0401             int index = (cols - x - 1) * rows + y; // default to Rotate90
0402 
0403             switch (rotation) {
0404             case Rotate90:
0405                 // index = (cols - x - 1) * rows + y;
0406                 break;
0407 
0408             case Rotate180:
0409                 index = (rows - y - 1) * cols + (cols - x - 1);
0410                 break;
0411 
0412             case Rotate270:
0413                 index = x * rows + (rows - y - 1);
0414                 break;
0415             }
0416 
0417             if (src) {
0418                 rotateQueue(rotation, src);
0419                 rotatedData[index] = src;
0420             }
0421         }
0422     }
0423 
0424     if ((rotation == Rotate90) || (rotation == Rotate270)) {
0425         int height = m_height;
0426         m_height = m_width;
0427         m_width = height;
0428     }
0429 
0430     m_stitches = rotatedData;
0431 
0432     int maxXSnap = m_width * 2;
0433     int maxYSnap = m_height * 2;
0434     QListIterator<Backstitch *> bi(m_backstitches);
0435 
0436     while (bi.hasNext()) {
0437         Backstitch *backstitch = bi.next();
0438 
0439         switch (rotation) {
0440         case Rotate90:
0441             backstitch->start = QPoint(backstitch->start.y(), maxXSnap - backstitch->start.x());
0442             backstitch->end = QPoint(backstitch->end.y(), maxXSnap - backstitch->end.x());
0443             break;
0444 
0445         case Rotate180:
0446             backstitch->start = QPoint(maxXSnap - backstitch->start.x(), maxYSnap - backstitch->start.y());
0447             backstitch->end = QPoint(maxXSnap - backstitch->end.x(), maxYSnap - backstitch->end.y());
0448             break;
0449 
0450         case Rotate270:
0451             backstitch->start = QPoint(maxYSnap - backstitch->start.y(), backstitch->start.x());
0452             backstitch->end = QPoint(maxYSnap - backstitch->end.y(), backstitch->end.x());
0453             break;
0454         }
0455     }
0456 
0457     QListIterator<Knot *> ki(m_knots);
0458 
0459     while (ki.hasNext()) {
0460         Knot *knot = ki.next();
0461 
0462         switch (rotation) {
0463         case Rotate90:
0464             knot->position = QPoint(knot->position.y(), maxXSnap - knot->position.x());
0465             break;
0466 
0467         case Rotate180:
0468             knot->position = QPoint(maxXSnap - knot->position.x(), maxYSnap - knot->position.y());
0469             break;
0470 
0471         case Rotate270:
0472             knot->position = QPoint(maxYSnap - knot->position.y(), knot->position.x());
0473             break;
0474         }
0475     }
0476 }
0477 
0478 void StitchData::invertQueue(Qt::Orientation orientation, StitchQueue *queue)
0479 {
0480     static QMap<Qt::Orientation, QMap<Stitch::Type, Stitch::Type>> mirrorMap;
0481 
0482     if (mirrorMap.isEmpty()) {
0483         mirrorMap[Qt::Horizontal][Stitch::Delete] = Stitch::Delete;
0484         mirrorMap[Qt::Horizontal][Stitch::TLQtr] = Stitch::TRQtr;
0485         mirrorMap[Qt::Horizontal][Stitch::TRQtr] = Stitch::TLQtr;
0486         mirrorMap[Qt::Horizontal][Stitch::BLQtr] = Stitch::BRQtr;
0487         mirrorMap[Qt::Horizontal][Stitch::BRQtr] = Stitch::BLQtr;
0488         mirrorMap[Qt::Horizontal][Stitch::BTHalf] = Stitch::TBHalf;
0489         mirrorMap[Qt::Horizontal][Stitch::TBHalf] = Stitch::BTHalf;
0490         mirrorMap[Qt::Horizontal][Stitch::TL3Qtr] = Stitch::TR3Qtr;
0491         mirrorMap[Qt::Horizontal][Stitch::TR3Qtr] = Stitch::TL3Qtr;
0492         mirrorMap[Qt::Horizontal][Stitch::BL3Qtr] = Stitch::BR3Qtr;
0493         mirrorMap[Qt::Horizontal][Stitch::BR3Qtr] = Stitch::BL3Qtr;
0494         mirrorMap[Qt::Horizontal][Stitch::TLSmallHalf] = Stitch::TRSmallHalf;
0495         mirrorMap[Qt::Horizontal][Stitch::TRSmallHalf] = Stitch::TLSmallHalf;
0496         mirrorMap[Qt::Horizontal][Stitch::BLSmallHalf] = Stitch::BRSmallHalf;
0497         mirrorMap[Qt::Horizontal][Stitch::BRSmallHalf] = Stitch::BLSmallHalf;
0498         mirrorMap[Qt::Horizontal][Stitch::TLSmallFull] = Stitch::TRSmallFull;
0499         mirrorMap[Qt::Horizontal][Stitch::TRSmallFull] = Stitch::TLSmallFull;
0500         mirrorMap[Qt::Horizontal][Stitch::BLSmallFull] = Stitch::BRSmallFull;
0501         mirrorMap[Qt::Horizontal][Stitch::BRSmallFull] = Stitch::BLSmallFull;
0502         mirrorMap[Qt::Horizontal][Stitch::Full] = Stitch::Full;
0503 
0504         mirrorMap[Qt::Vertical][Stitch::Delete] = Stitch::Delete;
0505         mirrorMap[Qt::Vertical][Stitch::TLQtr] = Stitch::BLQtr;
0506         mirrorMap[Qt::Vertical][Stitch::TRQtr] = Stitch::BRQtr;
0507         mirrorMap[Qt::Vertical][Stitch::BLQtr] = Stitch::TLQtr;
0508         mirrorMap[Qt::Vertical][Stitch::BRQtr] = Stitch::TRQtr;
0509         mirrorMap[Qt::Vertical][Stitch::BTHalf] = Stitch::TBHalf;
0510         mirrorMap[Qt::Vertical][Stitch::TBHalf] = Stitch::BTHalf;
0511         mirrorMap[Qt::Vertical][Stitch::TL3Qtr] = Stitch::BL3Qtr;
0512         mirrorMap[Qt::Vertical][Stitch::TR3Qtr] = Stitch::BR3Qtr;
0513         mirrorMap[Qt::Vertical][Stitch::BL3Qtr] = Stitch::TL3Qtr;
0514         mirrorMap[Qt::Vertical][Stitch::BR3Qtr] = Stitch::TR3Qtr;
0515         mirrorMap[Qt::Vertical][Stitch::TLSmallHalf] = Stitch::BLSmallHalf;
0516         mirrorMap[Qt::Vertical][Stitch::TRSmallHalf] = Stitch::BRSmallHalf;
0517         mirrorMap[Qt::Vertical][Stitch::BLSmallHalf] = Stitch::TLSmallHalf;
0518         mirrorMap[Qt::Vertical][Stitch::BRSmallHalf] = Stitch::TRSmallHalf;
0519         mirrorMap[Qt::Vertical][Stitch::TLSmallFull] = Stitch::BLSmallFull;
0520         mirrorMap[Qt::Vertical][Stitch::TRSmallFull] = Stitch::BRSmallFull;
0521         mirrorMap[Qt::Vertical][Stitch::BLSmallFull] = Stitch::TLSmallFull;
0522         mirrorMap[Qt::Vertical][Stitch::BRSmallFull] = Stitch::TRSmallFull;
0523         mirrorMap[Qt::Vertical][Stitch::Full] = Stitch::Full;
0524     }
0525 
0526     QListIterator<Stitch *> i(*queue);
0527 
0528     while (i.hasNext()) {
0529         Stitch *stitch = i.next();
0530         stitch->type = mirrorMap[orientation][stitch->type];
0531     }
0532 }
0533 
0534 void StitchData::rotateQueue(Rotation rotation, StitchQueue *queue)
0535 {
0536     static QMap<Rotation, QMap<Stitch::Type, Stitch::Type>> rotateMap;
0537 
0538     if (rotateMap.isEmpty()) {
0539         rotateMap[Rotate90][Stitch::TLQtr] = Stitch::BLQtr;
0540         rotateMap[Rotate90][Stitch::TRQtr] = Stitch::TLQtr;
0541         rotateMap[Rotate90][Stitch::BLQtr] = Stitch::BRQtr;
0542         rotateMap[Rotate90][Stitch::BRQtr] = Stitch::TRQtr;
0543         rotateMap[Rotate90][Stitch::BTHalf] = Stitch::TBHalf;
0544         rotateMap[Rotate90][Stitch::TBHalf] = Stitch::BTHalf;
0545         rotateMap[Rotate90][Stitch::TL3Qtr] = Stitch::BL3Qtr;
0546         rotateMap[Rotate90][Stitch::TR3Qtr] = Stitch::TL3Qtr;
0547         rotateMap[Rotate90][Stitch::BL3Qtr] = Stitch::BR3Qtr;
0548         rotateMap[Rotate90][Stitch::BR3Qtr] = Stitch::TR3Qtr;
0549         rotateMap[Rotate90][Stitch::TLSmallHalf] = Stitch::BLSmallHalf;
0550         rotateMap[Rotate90][Stitch::TRSmallHalf] = Stitch::TLSmallHalf;
0551         rotateMap[Rotate90][Stitch::BLSmallHalf] = Stitch::BRSmallHalf;
0552         rotateMap[Rotate90][Stitch::BRSmallHalf] = Stitch::TRSmallHalf;
0553         rotateMap[Rotate90][Stitch::TLSmallFull] = Stitch::BLSmallFull;
0554         rotateMap[Rotate90][Stitch::TRSmallFull] = Stitch::TLSmallFull;
0555         rotateMap[Rotate90][Stitch::BLSmallFull] = Stitch::BRSmallFull;
0556         rotateMap[Rotate90][Stitch::BRSmallFull] = Stitch::TRSmallFull;
0557         rotateMap[Rotate90][Stitch::Full] = Stitch::Full;
0558 
0559         rotateMap[Rotate180][Stitch::TLQtr] = Stitch::BRQtr;
0560         rotateMap[Rotate180][Stitch::TRQtr] = Stitch::BLQtr;
0561         rotateMap[Rotate180][Stitch::BLQtr] = Stitch::TRQtr;
0562         rotateMap[Rotate180][Stitch::BRQtr] = Stitch::TLQtr;
0563         rotateMap[Rotate180][Stitch::BTHalf] = Stitch::BTHalf;
0564         rotateMap[Rotate180][Stitch::TBHalf] = Stitch::TBHalf;
0565         rotateMap[Rotate180][Stitch::TL3Qtr] = Stitch::BR3Qtr;
0566         rotateMap[Rotate180][Stitch::TR3Qtr] = Stitch::BL3Qtr;
0567         rotateMap[Rotate180][Stitch::BL3Qtr] = Stitch::TR3Qtr;
0568         rotateMap[Rotate180][Stitch::BR3Qtr] = Stitch::TL3Qtr;
0569         rotateMap[Rotate180][Stitch::TLSmallHalf] = Stitch::BRSmallHalf;
0570         rotateMap[Rotate180][Stitch::TRSmallHalf] = Stitch::BLSmallHalf;
0571         rotateMap[Rotate180][Stitch::BLSmallHalf] = Stitch::TRSmallHalf;
0572         rotateMap[Rotate180][Stitch::BRSmallHalf] = Stitch::TLSmallHalf;
0573         rotateMap[Rotate180][Stitch::TLSmallFull] = Stitch::BRSmallFull;
0574         rotateMap[Rotate180][Stitch::TRSmallFull] = Stitch::BLSmallFull;
0575         rotateMap[Rotate180][Stitch::BLSmallFull] = Stitch::TRSmallFull;
0576         rotateMap[Rotate180][Stitch::BRSmallFull] = Stitch::TLSmallFull;
0577         rotateMap[Rotate180][Stitch::Full] = Stitch::Full;
0578 
0579         rotateMap[Rotate270][Stitch::TLQtr] = Stitch::TRQtr;
0580         rotateMap[Rotate270][Stitch::TRQtr] = Stitch::BRQtr;
0581         rotateMap[Rotate270][Stitch::BLQtr] = Stitch::TLQtr;
0582         rotateMap[Rotate270][Stitch::BRQtr] = Stitch::BLQtr;
0583         rotateMap[Rotate270][Stitch::BTHalf] = Stitch::TBHalf;
0584         rotateMap[Rotate270][Stitch::TBHalf] = Stitch::BTHalf;
0585         rotateMap[Rotate270][Stitch::TL3Qtr] = Stitch::TR3Qtr;
0586         rotateMap[Rotate270][Stitch::TR3Qtr] = Stitch::BR3Qtr;
0587         rotateMap[Rotate270][Stitch::BL3Qtr] = Stitch::TL3Qtr;
0588         rotateMap[Rotate270][Stitch::BR3Qtr] = Stitch::BL3Qtr;
0589         rotateMap[Rotate270][Stitch::TLSmallHalf] = Stitch::TRSmallHalf;
0590         rotateMap[Rotate270][Stitch::TRSmallHalf] = Stitch::BRSmallHalf;
0591         rotateMap[Rotate270][Stitch::BLSmallHalf] = Stitch::TLSmallHalf;
0592         rotateMap[Rotate270][Stitch::BRSmallHalf] = Stitch::BLSmallHalf;
0593         rotateMap[Rotate270][Stitch::TLSmallFull] = Stitch::TRSmallFull;
0594         rotateMap[Rotate270][Stitch::TRSmallFull] = Stitch::BRSmallFull;
0595         rotateMap[Rotate270][Stitch::BLSmallFull] = Stitch::TLSmallFull;
0596         rotateMap[Rotate270][Stitch::BRSmallFull] = Stitch::BLSmallFull;
0597         rotateMap[Rotate270][Stitch::Full] = Stitch::Full;
0598     }
0599 
0600     QListIterator<Stitch *> i(*queue);
0601 
0602     while (i.hasNext()) {
0603         Stitch *stitch = i.next();
0604         stitch->type = rotateMap[rotation][stitch->type];
0605     }
0606 }
0607 
0608 int StitchData::index(int x, int y) const
0609 {
0610     return y * m_width + x;
0611 }
0612 
0613 int StitchData::index(const QPoint &cell) const
0614 {
0615     return index(cell.x(), cell.y());
0616 }
0617 
0618 bool StitchData::isValid(int x, int y) const
0619 {
0620     return ((x >= 0) && (x < m_width) && (y >= 0) && (y < m_height));
0621 }
0622 
0623 void StitchData::addStitch(const QPoint &position, Stitch::Type type, int colorIndex)
0624 {
0625     int i = index(position);
0626     StitchQueue *stitchQueue = m_stitches.at(i);
0627 
0628     if (stitchQueue == nullptr) {
0629         stitchQueue = new StitchQueue;
0630         m_stitches[i] = stitchQueue;
0631     }
0632 
0633     stitchQueue->add(type, colorIndex);
0634 }
0635 
0636 Stitch *StitchData::findStitch(const QPoint &cell, Stitch::Type type, int colorIndex)
0637 {
0638     StitchQueue *stitchQueue = stitchQueueAt(cell);
0639     Stitch *found = nullptr;
0640 
0641     if (stitchQueue) {
0642         if (Stitch *stitch = stitchQueue->find(type, colorIndex)) {
0643             found = stitch;
0644         }
0645     }
0646 
0647     return found;
0648 }
0649 
0650 void StitchData::deleteStitch(const QPoint &position, Stitch::Type type, int colorIndex)
0651 {
0652     int i = index(position);
0653     StitchQueue *stitchQueue = m_stitches.at(i);
0654 
0655     if (stitchQueue) {
0656         if (stitchQueue->remove(type, colorIndex) == 0) {
0657             m_stitches[i] = nullptr;
0658             delete stitchQueue;
0659         }
0660     }
0661 }
0662 
0663 StitchQueue *StitchData::stitchQueueAt(int x, int y)
0664 {
0665     StitchQueue *stitchQueue = nullptr;
0666 
0667     if (isValid(x, y)) {
0668         stitchQueue = m_stitches.at(index(x, y));
0669     }
0670 
0671     return stitchQueue;
0672 }
0673 
0674 StitchQueue *StitchData::stitchQueueAt(const QPoint &position)
0675 {
0676     return stitchQueueAt(position.x(), position.y());
0677 }
0678 
0679 StitchQueue *StitchData::takeStitchQueueAt(int x, int y)
0680 {
0681     StitchQueue *stitchQueue = stitchQueueAt(x, y);
0682 
0683     if (stitchQueue) {
0684         m_stitches[index(x, y)] = nullptr;
0685     }
0686 
0687     return stitchQueue;
0688 }
0689 
0690 StitchQueue *StitchData::takeStitchQueueAt(const QPoint &position)
0691 {
0692     return takeStitchQueueAt(position.x(), position.y());
0693 }
0694 
0695 StitchQueue *StitchData::replaceStitchQueueAt(int x, int y, StitchQueue *stitchQueue)
0696 {
0697     StitchQueue *originalQueue = takeStitchQueueAt(x, y);
0698 
0699     if (isValid(x, y)) {
0700         m_stitches[index(x, y)] = stitchQueue;
0701     }
0702 
0703     return originalQueue;
0704 }
0705 
0706 StitchQueue *StitchData::replaceStitchQueueAt(const QPoint &position, StitchQueue *stitchQueue)
0707 {
0708     return replaceStitchQueueAt(position.x(), position.y(), stitchQueue);
0709 }
0710 
0711 void StitchData::addBackstitch(const QPoint &start, const QPoint &end, int colorIndex)
0712 {
0713     m_backstitches.append(new Backstitch(start, end, colorIndex));
0714 }
0715 
0716 void StitchData::addBackstitch(Backstitch *backstitch)
0717 {
0718     m_backstitches.append(backstitch);
0719 }
0720 
0721 Backstitch *StitchData::findBackstitch(const QPoint &start, const QPoint &end, int colorIndex)
0722 {
0723     Backstitch *found = nullptr;
0724 
0725     foreach (Backstitch *backstitch, m_backstitches) {
0726         if (backstitch->contains(start) && backstitch->contains(end) && ((colorIndex == -1) || backstitch->colorIndex == colorIndex)) {
0727             found = backstitch;
0728             break;
0729         }
0730     }
0731 
0732     return found;
0733 }
0734 
0735 Backstitch *StitchData::takeBackstitch(const QPoint &start, const QPoint &end, int colorIndex)
0736 {
0737     Backstitch *removed = findBackstitch(start, end, colorIndex);
0738     m_backstitches.removeOne(removed);
0739 
0740     return removed;
0741 }
0742 
0743 Backstitch *StitchData::takeBackstitch(Backstitch *backstitch)
0744 {
0745     Backstitch *removed = nullptr;
0746 
0747     if (m_backstitches.removeOne(backstitch)) {
0748         removed = backstitch;
0749     }
0750 
0751     return removed;
0752 }
0753 
0754 void StitchData::addFrenchKnot(const QPoint &position, int colorIndex)
0755 {
0756     m_knots.append(new Knot(position, colorIndex));
0757 }
0758 
0759 void StitchData::addFrenchKnot(Knot *knot)
0760 {
0761     m_knots.append(knot);
0762 }
0763 
0764 Knot *StitchData::findKnot(const QPoint &position, int colorIndex)
0765 {
0766     Knot *found = nullptr;
0767 
0768     foreach (Knot *knot, m_knots) {
0769         if ((knot->position == position) && ((colorIndex == -1) || (knot->colorIndex == colorIndex))) {
0770             found = knot;
0771             break;
0772         }
0773     }
0774 
0775     return found;
0776 }
0777 
0778 Knot *StitchData::takeFrenchKnot(const QPoint &position, int colorIndex)
0779 {
0780     Knot *removed = findKnot(position, colorIndex);
0781 
0782     if (removed) {
0783         m_knots.removeOne(removed);
0784     }
0785 
0786     return removed;
0787 }
0788 
0789 Knot *StitchData::takeFrenchKnot(Knot *knot)
0790 {
0791     Knot *removed = nullptr;
0792 
0793     if (m_knots.removeOne(knot)) {
0794         removed = knot;
0795     }
0796 
0797     return removed;
0798 }
0799 
0800 QList<Backstitch *> &StitchData::backstitches()
0801 {
0802     return m_backstitches;
0803 }
0804 
0805 QList<Knot *> &StitchData::knots()
0806 {
0807     return m_knots;
0808 }
0809 
0810 QListIterator<Backstitch *> StitchData::backstitchIterator()
0811 {
0812     return QListIterator<Backstitch *>(m_backstitches);
0813 }
0814 
0815 QMutableListIterator<Backstitch *> StitchData::mutableBackstitchIterator()
0816 {
0817     return QMutableListIterator<Backstitch *>(m_backstitches);
0818 }
0819 
0820 QListIterator<Knot *> StitchData::knotIterator()
0821 {
0822     return QListIterator<Knot *>(m_knots);
0823 }
0824 
0825 QMutableListIterator<Knot *> StitchData::mutableKnotIterator()
0826 {
0827     return QMutableListIterator<Knot *>(m_knots);
0828 }
0829 
0830 QMap<int, FlossUsage> StitchData::flossUsage()
0831 {
0832     QMap<int, FlossUsage> usage;
0833     static QMap<Stitch::Type, double> lengths;
0834 
0835     if (!lengths.count()) {
0836         lengths.insert(Stitch::Delete, 0.0);
0837         lengths.insert(Stitch::TLQtr, 0.707107 + 0.5);
0838         lengths.insert(Stitch::TRQtr, 0.707107 + 0.5);
0839         lengths.insert(Stitch::BLQtr, 0.707107 + 0.5);
0840         lengths.insert(Stitch::BTHalf, 1.414213 + 1.0);
0841         lengths.insert(Stitch::TL3Qtr, 1.414213 + 0.707107 + 1.0 + 0.5);
0842         lengths.insert(Stitch::BRQtr, 0.707107 + 0.5);
0843         lengths.insert(Stitch::TBHalf, 1.414213 + 1.0);
0844         lengths.insert(Stitch::TR3Qtr, 1.414213 + 0.707107 + 1.0 + 0.5);
0845         lengths.insert(Stitch::BL3Qtr, 1.414213 + 0.707107 + 1.0 + 0.5);
0846         lengths.insert(Stitch::BR3Qtr, 1.414213 + 0.707107 + 1.0 + 0.5);
0847         lengths.insert(Stitch::Full, 1.414213 + 1.414213 + 1.0 + 1.0);
0848         lengths.insert(Stitch::TLSmallHalf, 0.707107 + 0.5);
0849         lengths.insert(Stitch::TRSmallHalf, 0.707107 + 0.5);
0850         lengths.insert(Stitch::BLSmallHalf, 0.707107 + 0.5);
0851         lengths.insert(Stitch::BRSmallHalf, 0.707107 + 0.5);
0852         lengths.insert(Stitch::TLSmallFull, 0.707107 + 0.5 + 0.707107 + 0.5);
0853         lengths.insert(Stitch::TRSmallFull, 0.707107 + 0.5 + 0.707107 + 0.5);
0854         lengths.insert(Stitch::BLSmallFull, 0.707107 + 0.5 + 0.707107 + 0.5);
0855         lengths.insert(Stitch::BRSmallFull, 0.707107 + 0.5 + 0.707107 + 0.5);
0856         lengths.insert(Stitch::FrenchKnot, 2.0);
0857     }
0858 
0859     QVectorIterator<StitchQueue *> stitchesIterator(m_stitches);
0860 
0861     while (stitchesIterator.hasNext()) {
0862         StitchQueue *stitchQueue = stitchesIterator.next();
0863 
0864         if (stitchQueue) {
0865             QListIterator<Stitch *> stitchIterator(*stitchQueue);
0866 
0867             while (stitchIterator.hasNext()) {
0868                 Stitch *stitch = stitchIterator.next();
0869                 usage[stitch->colorIndex].stitchCounts[stitch->type]++;
0870                 usage[stitch->colorIndex].stitchLengths[stitch->type] += lengths[stitch->type];
0871             }
0872         }
0873     }
0874 
0875     QListIterator<Backstitch *> backstitchIterator(m_backstitches);
0876 
0877     while (backstitchIterator.hasNext()) {
0878         Backstitch *backstitch = backstitchIterator.next();
0879         usage[backstitch->colorIndex].backstitchCount++;
0880         usage[backstitch->colorIndex].backstitchLength += QPoint(backstitch->start - backstitch->end).manhattanLength();
0881     }
0882 
0883     QListIterator<Knot *> knotIterator(m_knots);
0884 
0885     while (knotIterator.hasNext()) {
0886         Knot *knot = knotIterator.next();
0887         usage[knot->colorIndex].stitchCounts[Stitch::FrenchKnot]++;
0888         usage[knot->colorIndex].stitchLengths[Stitch::FrenchKnot] += lengths[Stitch::FrenchKnot];
0889     }
0890 
0891     return usage;
0892 }
0893 
0894 QDataStream &operator<<(QDataStream &stream, const StitchData &stitchData)
0895 {
0896     stream << qint32(stitchData.version);
0897     stream << qint32(stitchData.m_width);
0898     stream << qint32(stitchData.m_height);
0899 
0900     QVectorIterator<StitchQueue *> stitchesIterator(stitchData.m_stitches);
0901     int queues = 0;
0902 
0903     while (stitchesIterator.hasNext()) {
0904         if (stitchesIterator.next()) {
0905             ++queues;
0906         }
0907     }
0908 
0909     stream << qint32(queues);
0910 
0911     for (int row = 0; row < stitchData.m_height; ++row) {
0912         for (int column = 0; column < stitchData.m_width; ++column) {
0913             if (StitchQueue *stitchQueue = stitchData.m_stitches[stitchData.index(column, row)]) {
0914                 stream << qint32(column);
0915                 stream << qint32(row);
0916                 stream << *stitchQueue;
0917             }
0918         }
0919     }
0920 
0921     QListIterator<Backstitch *> backstitchIterator(stitchData.m_backstitches);
0922     stream << qint32(stitchData.m_backstitches.count());
0923 
0924     while (backstitchIterator.hasNext()) {
0925         stream << *(backstitchIterator.next());
0926 
0927         if (stream.status() != QDataStream::Ok) {
0928             throw FailedWriteFile(stream.status());
0929         }
0930     }
0931 
0932     QListIterator<Knot *> knotIterator(stitchData.m_knots);
0933     stream << qint32(stitchData.m_knots.count());
0934 
0935     while (knotIterator.hasNext()) {
0936         stream << *(knotIterator.next());
0937 
0938         if (stream.status() != QDataStream::Ok) {
0939             throw FailedWriteFile(stream.status());
0940         }
0941     }
0942 
0943     if (stream.status() != QDataStream::Ok) {
0944         throw FailedWriteFile(stream.status());
0945     }
0946 
0947     return stream;
0948 }
0949 
0950 QDataStream &operator>>(QDataStream &stream, StitchData &stitchData)
0951 {
0952     qint32 version;
0953     qint32 width;
0954     qint32 height;
0955     qint32 layers;
0956     qint32 columns;
0957     qint32 rows;
0958     qint32 count;
0959     QHash<int, QHash<int, StitchQueue *>> stitches;
0960 
0961     stitchData.clear();
0962 
0963     stream >> version;
0964 
0965     switch (version) {
0966     case 103:
0967         stream >> width;
0968         stream >> height;
0969         stitchData.resize(width, height);
0970         stream >> count;
0971 
0972         while (count--) {
0973             stream >> columns;
0974             stream >> rows;
0975             StitchQueue *stitchQueue = new StitchQueue;
0976             stitchData.replaceStitchQueueAt(columns, rows, stitchQueue);
0977             stream >> *stitchQueue;
0978         }
0979 
0980         stream >> count;
0981 
0982         while (count--) {
0983             Backstitch *backstitch = new Backstitch;
0984             stream >> *(backstitch);
0985             stitchData.addBackstitch(backstitch);
0986         }
0987 
0988         stream >> count;
0989 
0990         while (count--) {
0991             Knot *knot = new Knot;
0992             stream >> *knot;
0993             stitchData.addFrenchKnot(knot);
0994         }
0995 
0996         break;
0997 
0998     case 102:
0999         stream >> width;
1000         stream >> height;
1001         stitchData.resize(width, height);
1002 
1003         stream >> columns;
1004 
1005         while (columns--) {
1006             stream >> rows;
1007 
1008             while (rows--) {
1009                 qint32 column;
1010                 qint32 row;
1011 
1012                 stream >> column;
1013                 stream >> row;
1014 
1015                 StitchQueue *stitchQueue = new StitchQueue;
1016                 stitches[column][row] = stitchQueue;
1017                 stream >> *stitchQueue;
1018             }
1019         }
1020 
1021         for (int y = 0; y < height; ++y) {
1022             for (int x = 0; x < width; ++x) {
1023                 if (stitches[x][y]) {
1024                     stitchData.replaceStitchQueueAt(x, y, stitches[x][y]);
1025                 }
1026             }
1027         }
1028 
1029         stream >> count;
1030 
1031         while (count--) {
1032             Backstitch *backstitch = new Backstitch;
1033             stream >> *(backstitch);
1034             stitchData.addBackstitch(backstitch);
1035         }
1036 
1037         stream >> count;
1038 
1039         while (count--) {
1040             Knot *knot = new Knot;
1041             stream >> *knot;
1042             stitchData.addFrenchKnot(knot);
1043         }
1044 
1045         break;
1046 
1047     case 101:
1048         stream >> width;
1049         stream >> height;
1050         stitchData.resize(width, height);
1051 
1052         stream >> columns;
1053 
1054         while (columns--) {
1055             stream >> rows;
1056 
1057             while (rows--) {
1058                 qint32 column;
1059                 qint32 row;
1060 
1061                 stream >> column;
1062                 stream >> row;
1063 
1064                 StitchQueue *stitchQueue = new StitchQueue;
1065                 stitches[column][row] = stitchQueue;
1066                 stream >> *stitchQueue;
1067             }
1068         }
1069 
1070         for (int y = 0; y < height; ++y) {
1071             for (int x = 0; x < width; ++x) {
1072                 if (stitches[x][y]) {
1073                     stitchData.replaceStitchQueueAt(x, y, stitches[x][y]);
1074                 }
1075             }
1076         }
1077 
1078         break;
1079 
1080     case 100:
1081         stream >> width;
1082         stream >> height;
1083         stitchData.m_width = width;
1084         stitchData.m_height = height;
1085 
1086         stream >> layers;
1087 
1088         while (layers--) {
1089             stream >> columns;
1090 
1091             while (columns--) {
1092                 stream >> rows;
1093 
1094                 while (rows--) {
1095                     qint32 layer;
1096                     qint32 column;
1097                     qint32 row;
1098 
1099                     stream >> layer;
1100                     stream >> column;
1101                     stream >> row;
1102 
1103                     StitchQueue *stitchQueue = new StitchQueue;
1104                     stitches[column][row] = stitchQueue;
1105                     stream >> *stitchQueue;
1106                 }
1107             }
1108         }
1109 
1110         for (int y = 0; y < height; ++y) {
1111             for (int x = 0; x < width; ++x) {
1112                 if (stitches[x][y]) {
1113                     stitchData.replaceStitchQueueAt(x, y, stitches[x][y]);
1114                 }
1115             }
1116         }
1117 
1118         break;
1119 
1120     default:
1121         throw InvalidFileVersion(QString(i18n("Stitch data version %1", version)));
1122         break;
1123     }
1124 
1125     if (stream.status() != QDataStream::Ok) {
1126         throw FailedReadFile(QString(i18n("Failed reading stitch data")));
1127     }
1128 
1129     return stream;
1130 }