File indexing completed on 2024-04-21 04:32:12

0001 /*
0002  * Copyright (C) 2012-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 "Pattern.h"
0012 
0013 #include <KLocalizedString>
0014 
0015 #include "Exceptions.h"
0016 
0017 Pattern::Pattern(Document *document)
0018     : m_document(document)
0019 {
0020 }
0021 
0022 void Pattern::clear()
0023 {
0024     m_documentPalette = DocumentPalette();
0025     m_stitchData.clear();
0026 }
0027 
0028 Document *Pattern::document()
0029 {
0030     return m_document;
0031 }
0032 
0033 DocumentPalette &Pattern::palette()
0034 {
0035     return m_documentPalette;
0036 }
0037 
0038 StitchData &Pattern::stitches()
0039 {
0040     return m_stitchData;
0041 }
0042 
0043 void Pattern::constructPalette(Pattern *pattern)
0044 {
0045     pattern->palette().setSchemeName(m_documentPalette.schemeName());
0046     QMap<int, FlossUsage> usage = pattern->stitches().flossUsage();
0047     QMapIterator<int, FlossUsage> flossIterator(usage);
0048 
0049     while (flossIterator.hasNext()) {
0050         flossIterator.next();
0051         int index = flossIterator.key();
0052         double length = flossIterator.value().totalLength();
0053 
0054         if (length > 0) {
0055             pattern->palette().add(index, new DocumentFloss(palette().flosses().value(index)));
0056         }
0057     }
0058 }
0059 
0060 Pattern *Pattern::cut(const QRect &area, int colorMask, const QList<Stitch::Type> &stitchMask, bool excludeBackstitches, bool excludeKnots)
0061 {
0062     Pattern *pattern = new Pattern;
0063     pattern->stitches().resize(area.width(), area.height());
0064 
0065     for (int row = area.top(); row <= area.bottom(); row++) {
0066         for (int column = area.left(); column <= area.right(); ++column) {
0067             QPoint src(column, row);
0068             QPoint dst(src - area.topLeft());
0069             StitchQueue *srcQ = stitches().takeStitchQueueAt(src);
0070 
0071             if (srcQ) {
0072                 StitchQueue *dstQ = new StitchQueue;
0073                 // iterate the queue adding anything that matches the stitch mask or color mask to a new queue
0074                 int count = srcQ->count();
0075 
0076                 while (count--) {
0077                     Stitch *stitch = srcQ->dequeue();
0078 
0079                     if (((colorMask == -1) || (colorMask == stitch->colorIndex)) && (stitchMask.contains(stitch->type))) {
0080                         dstQ->enqueue(stitch);
0081                     } else {
0082                         srcQ->enqueue(stitch);
0083                     }
0084                 }
0085 
0086                 if (srcQ->count()) {
0087                     stitches().replaceStitchQueueAt(src, srcQ);
0088                 } else {
0089                     delete srcQ;
0090                 }
0091 
0092                 if (dstQ->count()) {
0093                     pattern->stitches().replaceStitchQueueAt(dst, dstQ);
0094                 } else {
0095                     delete dstQ;
0096                 }
0097             }
0098         }
0099     }
0100 
0101     QRect snapArea(area.left() * 2, area.top() * 2, area.width() * 2, area.height() * 2);
0102 
0103     if (!excludeBackstitches) {
0104         QMutableListIterator<Backstitch *> mutableListIterator = stitches().mutableBackstitchIterator();
0105 
0106         while (mutableListIterator.hasNext()) {
0107             Backstitch *backstitch = mutableListIterator.next();
0108 
0109             if (((colorMask == -1) || (colorMask == backstitch->colorIndex)) && (snapArea.contains(backstitch->start) && snapArea.contains(backstitch->end))) {
0110                 mutableListIterator.remove();
0111                 backstitch->start -= snapArea.topLeft();
0112                 backstitch->end -= snapArea.topLeft();
0113                 pattern->stitches().addBackstitch(backstitch);
0114             }
0115         }
0116     }
0117 
0118     if (!excludeKnots) {
0119         QMutableListIterator<Knot *> mutableListIterator = stitches().mutableKnotIterator();
0120 
0121         while (mutableListIterator.hasNext()) {
0122             Knot *knot = mutableListIterator.next();
0123 
0124             if (((colorMask == -1) || (colorMask == knot->colorIndex)) && (snapArea.contains(knot->position))) {
0125                 mutableListIterator.remove();
0126                 knot->position -= snapArea.topLeft();
0127                 pattern->stitches().addFrenchKnot(knot);
0128             }
0129         }
0130     }
0131 
0132     constructPalette(pattern);
0133 
0134     return pattern;
0135 }
0136 
0137 Pattern *Pattern::copy(const QRect &area, int colorMask, const QList<Stitch::Type> &stitchMask, bool excludeBackstitches, bool excludeKnots)
0138 {
0139     Pattern *pattern = new Pattern;
0140     pattern->stitches().resize(area.width(), area.height());
0141 
0142     for (int row = area.top(); row <= area.bottom(); row++) {
0143         for (int column = area.left(); column <= area.right(); ++column) {
0144             QPoint src(column, row);
0145             QPoint dst(src - area.topLeft());
0146             StitchQueue *srcQ = stitches().stitchQueueAt(src);
0147 
0148             if (srcQ) {
0149                 StitchQueue *dstQ = new StitchQueue;
0150                 QListIterator<Stitch *> stitchIterator(*srcQ);
0151 
0152                 while (stitchIterator.hasNext()) {
0153                     Stitch *stitch = stitchIterator.next();
0154 
0155                     if (((colorMask == -1) || (colorMask == stitch->colorIndex)) && (stitchMask.contains(stitch->type))) {
0156                         dstQ->add(stitch->type, stitch->colorIndex);
0157                     }
0158                 }
0159 
0160                 if (dstQ->count()) {
0161                     pattern->stitches().replaceStitchQueueAt(dst, dstQ);
0162                 } else {
0163                     delete dstQ;
0164                 }
0165             }
0166         }
0167     }
0168 
0169     QRect snapArea(area.left() * 2, area.top() * 2, area.width() * 2, area.height() * 2);
0170 
0171     if (!excludeBackstitches) {
0172         QListIterator<Backstitch *> backstitchIterator = stitches().backstitchIterator();
0173 
0174         while (backstitchIterator.hasNext()) {
0175             Backstitch *backstitch = backstitchIterator.next();
0176 
0177             if (((colorMask == -1) || (colorMask == backstitch->colorIndex)) && (snapArea.contains(backstitch->start) && snapArea.contains(backstitch->end))) {
0178                 pattern->stitches().addBackstitch(backstitch->start - snapArea.topLeft(), backstitch->end - snapArea.topLeft(), backstitch->colorIndex);
0179             }
0180         }
0181     }
0182 
0183     if (!excludeKnots) {
0184         QListIterator<Knot *> knotIterator = stitches().knotIterator();
0185 
0186         while (knotIterator.hasNext()) {
0187             Knot *knot = knotIterator.next();
0188 
0189             if (((colorMask == -1) || (colorMask == knot->colorIndex)) && (snapArea.contains(knot->position))) {
0190                 pattern->stitches().addFrenchKnot(knot->position - snapArea.topLeft(), knot->colorIndex);
0191             }
0192         }
0193     }
0194 
0195     constructPalette(pattern);
0196 
0197     return pattern;
0198 }
0199 
0200 void Pattern::paste(Pattern *pattern, const QPoint &cell, bool merge)
0201 {
0202     pattern->palette().setSchemeName(palette().schemeName());
0203 
0204     for (int row = 0; row < pattern->stitches().height(); ++row) {
0205         for (int col = 0; col < pattern->stitches().width(); ++col) {
0206             QPoint src(col, row);
0207             QPoint dst(cell + src);
0208 
0209             StitchQueue *srcQ = pattern->stitches().stitchQueueAt(src);
0210             StitchQueue *dstQ = stitches().takeStitchQueueAt(dst);
0211 
0212             if (!merge) {
0213                 delete dstQ;
0214                 dstQ = nullptr;
0215             }
0216 
0217             if (srcQ) {
0218                 if (dstQ == nullptr) {
0219                     dstQ = new StitchQueue();
0220                 }
0221 
0222                 QListIterator<Stitch *> stitchIterator(*srcQ);
0223 
0224                 while (stitchIterator.hasNext()) {
0225                     Stitch *stitch = stitchIterator.next();
0226                     int colorIndex = palette().add(pattern->palette().flosses().value(stitch->colorIndex)->flossColor());
0227                     dstQ->add(stitch->type, colorIndex);
0228                 }
0229             }
0230 
0231             stitches().replaceStitchQueueAt(dst, dstQ);
0232         }
0233     }
0234 
0235     QRect snapArea(0, 0, stitches().width() * 2, stitches().height() * 2);
0236     QPoint targetOffset(cell * 2);
0237 
0238     QListIterator<Backstitch *> backstitchIterator = pattern->stitches().backstitchIterator();
0239 
0240     while (backstitchIterator.hasNext()) {
0241         Backstitch *backstitch = backstitchIterator.next();
0242         int colorIndex = palette().add(pattern->palette().flosses().value(backstitch->colorIndex)->flossColor());
0243 
0244         if (snapArea.contains(backstitch->start + targetOffset) && snapArea.contains(backstitch->end + targetOffset)) {
0245             stitches().addBackstitch(backstitch->start + targetOffset, backstitch->end + targetOffset, colorIndex);
0246         }
0247     }
0248 
0249     QListIterator<Knot *> knotIterator = pattern->stitches().knotIterator();
0250 
0251     while (knotIterator.hasNext()) {
0252         Knot *knot = knotIterator.next();
0253         int colorIndex = palette().add(pattern->palette().flosses().value(knot->colorIndex)->flossColor());
0254 
0255         if (snapArea.contains(knot->position + targetOffset)) {
0256             stitches().addFrenchKnot(knot->position + targetOffset, colorIndex);
0257         }
0258     }
0259 }
0260 
0261 QDataStream &operator<<(QDataStream &stream, const Pattern &pattern)
0262 {
0263     stream << qint32(pattern.version);
0264 
0265     stream << pattern.m_documentPalette;
0266     stream << pattern.m_stitchData;
0267 
0268     return stream;
0269 }
0270 
0271 QDataStream &operator>>(QDataStream &stream, Pattern &pattern)
0272 {
0273     qint32 version;
0274 
0275     stream >> version;
0276 
0277     switch (version) {
0278     case 100:
0279         stream >> pattern.m_documentPalette;
0280         stream >> pattern.m_stitchData;
0281         break;
0282 
0283     default:
0284         throw InvalidFileVersion(QString(i18n("Pattern version %1", version)));
0285         break;
0286     }
0287 
0288     return stream;
0289 }