1 /*
2  * Copyright (C) 2012-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 "Pattern.h"
13 
14 #include <KLocalizedString>
15 
16 #include "Exceptions.h"
17 
18 
Pattern(Document * document)19 Pattern::Pattern(Document *document)
20     :   m_document(document)
21 {
22 }
23 
24 
clear()25 void Pattern::clear()
26 {
27     m_documentPalette = DocumentPalette();
28     m_stitchData.clear();
29 }
30 
31 
document()32 Document *Pattern::document()
33 {
34     return m_document;
35 }
36 
37 
palette()38 DocumentPalette &Pattern::palette()
39 {
40     return m_documentPalette;
41 }
42 
43 
stitches()44 StitchData &Pattern::stitches()
45 {
46     return m_stitchData;
47 }
48 
49 
constructPalette(Pattern * pattern)50 void Pattern::constructPalette(Pattern *pattern)
51 {
52     pattern->palette().setSchemeName(m_documentPalette.schemeName());
53     QMap<int, FlossUsage> usage = pattern->stitches().flossUsage();
54     QMapIterator<int, FlossUsage> flossIterator(usage);
55 
56     while (flossIterator.hasNext()) {
57         flossIterator.next();
58         int index = flossIterator.key();
59         double length = flossIterator.value().totalLength();
60 
61         if (length > 0) {
62             pattern->palette().add(index, new DocumentFloss(palette().flosses().value(index)));
63         }
64     }
65 }
66 
67 
cut(const QRect & area,int colorMask,const QList<Stitch::Type> & stitchMask,bool excludeBackstitches,bool excludeKnots)68 Pattern *Pattern::cut(const QRect &area, int colorMask, const QList<Stitch::Type> &stitchMask, bool excludeBackstitches, bool excludeKnots)
69 {
70     Pattern *pattern = new Pattern;
71     pattern->stitches().resize(area.width(), area.height());
72 
73     for (int row = area.top() ; row <= area.bottom() ; row++) {
74         for (int column = area.left() ; column <= area.right() ; ++column) {
75             QPoint src(column, row);
76             QPoint dst(src - area.topLeft());
77             StitchQueue *srcQ = stitches().takeStitchQueueAt(src);
78 
79             if (srcQ) {
80                 StitchQueue *dstQ = new StitchQueue;
81                 // iterate the queue adding anything that matches the stitch mask or color mask to a new queue
82                 int count = srcQ->count();
83 
84                 while (count--) {
85                     Stitch *stitch = srcQ->dequeue();
86 
87                     if (((colorMask == -1) || (colorMask == stitch->colorIndex)) && (stitchMask.contains(stitch->type))) {
88                         dstQ->enqueue(stitch);
89                     } else {
90                         srcQ->enqueue(stitch);
91                     }
92                 }
93 
94                 if (srcQ->count()) {
95                     stitches().replaceStitchQueueAt(src, srcQ);
96                 } else {
97                     delete srcQ;
98                 }
99 
100                 if (dstQ->count()) {
101                     pattern->stitches().replaceStitchQueueAt(dst, dstQ);
102                 } else {
103                     delete dstQ;
104                 }
105             }
106         }
107     }
108 
109     QRect snapArea(area.left() * 2, area.top() * 2, area.width() * 2, area.height() * 2);
110 
111     if (!excludeBackstitches) {
112         QMutableListIterator<Backstitch *> mutableListIterator = stitches().mutableBackstitchIterator();
113 
114         while (mutableListIterator.hasNext()) {
115             Backstitch *backstitch = mutableListIterator.next();
116 
117             if (((colorMask == -1) || (colorMask == backstitch->colorIndex)) && (snapArea.contains(backstitch->start) && snapArea.contains(backstitch->end))) {
118                 mutableListIterator.remove();
119                 backstitch->start -= snapArea.topLeft();
120                 backstitch->end -= snapArea.topLeft();
121                 pattern->stitches().addBackstitch(backstitch);
122             }
123         }
124     }
125 
126     if (!excludeKnots) {
127         QMutableListIterator<Knot *> mutableListIterator = stitches().mutableKnotIterator();
128 
129         while (mutableListIterator.hasNext()) {
130             Knot *knot = mutableListIterator.next();
131 
132             if (((colorMask == -1) || (colorMask == knot->colorIndex)) && (snapArea.contains(knot->position))) {
133                 mutableListIterator.remove();
134                 knot->position -= snapArea.topLeft();
135                 pattern->stitches().addFrenchKnot(knot);
136             }
137         }
138     }
139 
140     constructPalette(pattern);
141 
142     return pattern;
143 }
144 
145 
copy(const QRect & area,int colorMask,const QList<Stitch::Type> & stitchMask,bool excludeBackstitches,bool excludeKnots)146 Pattern *Pattern::copy(const QRect &area,  int colorMask, const QList<Stitch::Type> &stitchMask, bool excludeBackstitches, bool excludeKnots)
147 {
148     Pattern *pattern = new Pattern;
149     pattern->stitches().resize(area.width(), area.height());
150 
151     for (int row = area.top() ; row <= area.bottom() ; row++) {
152         for (int column = area.left() ; column <= area.right() ; ++column) {
153             QPoint src(column, row);
154             QPoint dst(src - area.topLeft());
155             StitchQueue *srcQ = stitches().stitchQueueAt(src);
156 
157             if (srcQ) {
158                 StitchQueue *dstQ = new StitchQueue;
159                 QListIterator<Stitch *> stitchIterator(*srcQ);
160 
161                 while (stitchIterator.hasNext()) {
162                     Stitch *stitch = stitchIterator.next();
163 
164                     if (((colorMask == -1) || (colorMask == stitch->colorIndex)) && (stitchMask.contains(stitch->type))) {
165                         dstQ->add(stitch->type, stitch->colorIndex);
166                     }
167                 }
168 
169                 if (dstQ->count()) {
170                     pattern->stitches().replaceStitchQueueAt(dst, dstQ);
171                 } else {
172                     delete dstQ;
173                 }
174             }
175         }
176     }
177 
178     QRect snapArea(area.left() * 2, area.top() * 2, area.width() * 2, area.height() * 2);
179 
180     if (!excludeBackstitches) {
181         QListIterator<Backstitch *> backstitchIterator = stitches().backstitchIterator();
182 
183         while (backstitchIterator.hasNext()) {
184             Backstitch *backstitch = backstitchIterator.next();
185 
186             if (((colorMask == -1) || (colorMask == backstitch->colorIndex)) && (snapArea.contains(backstitch->start) && snapArea.contains(backstitch->end))) {
187                 pattern->stitches().addBackstitch(backstitch->start - snapArea.topLeft(), backstitch->end - snapArea.topLeft(), backstitch->colorIndex);
188             }
189         }
190     }
191 
192     if (!excludeKnots) {
193         QListIterator<Knot *> knotIterator = stitches().knotIterator();
194 
195         while (knotIterator.hasNext()) {
196             Knot *knot = knotIterator.next();
197 
198             if (((colorMask == -1) || (colorMask == knot->colorIndex)) && (snapArea.contains(knot->position))) {
199                 pattern->stitches().addFrenchKnot(knot->position - snapArea.topLeft(), knot->colorIndex);
200             }
201         }
202     }
203 
204     constructPalette(pattern);
205 
206     return pattern;
207 }
208 
209 
paste(Pattern * pattern,const QPoint & cell,bool merge)210 void Pattern::paste(Pattern *pattern, const QPoint &cell, bool merge)
211 {
212     pattern->palette().setSchemeName(palette().schemeName());
213 
214     for (int row = 0 ; row < pattern->stitches().height() ; ++row) {
215         for (int col = 0 ; col < pattern->stitches().width() ; ++col) {
216             QPoint src(col, row);
217             QPoint dst(cell + src);
218 
219             StitchQueue *srcQ = pattern->stitches().stitchQueueAt(src);
220             StitchQueue *dstQ = stitches().takeStitchQueueAt(dst);
221 
222             if (!merge) {
223                 delete dstQ;
224                 dstQ = nullptr;
225             }
226 
227             if (srcQ) {
228                 if (dstQ == nullptr) {
229                     dstQ = new StitchQueue();
230                 }
231 
232                 QListIterator<Stitch *> stitchIterator(*srcQ);
233 
234                 while (stitchIterator.hasNext()) {
235                     Stitch *stitch = stitchIterator.next();
236                     int colorIndex = palette().add(pattern->palette().flosses().value(stitch->colorIndex)->flossColor());
237                     dstQ->add(stitch->type, colorIndex);
238                 }
239             }
240 
241             stitches().replaceStitchQueueAt(dst, dstQ);
242         }
243     }
244 
245     QRect snapArea(0, 0, stitches().width() * 2, stitches().height() * 2);
246     QPoint targetOffset(cell * 2);
247 
248     QListIterator<Backstitch *> backstitchIterator = pattern->stitches().backstitchIterator();
249 
250     while (backstitchIterator.hasNext()) {
251         Backstitch *backstitch = backstitchIterator.next();
252         int colorIndex = palette().add(pattern->palette().flosses().value(backstitch->colorIndex)->flossColor());
253 
254         if (snapArea.contains(backstitch->start + targetOffset) && snapArea.contains(backstitch->end + targetOffset)) {
255             stitches().addBackstitch(backstitch->start + targetOffset, backstitch->end + targetOffset, colorIndex);
256         }
257     }
258 
259     QListIterator<Knot *> knotIterator = pattern->stitches().knotIterator();
260 
261     while (knotIterator.hasNext()) {
262         Knot *knot = knotIterator.next();
263         int colorIndex = palette().add(pattern->palette().flosses().value(knot->colorIndex)->flossColor());
264 
265         if (snapArea.contains(knot->position + targetOffset)) {
266             stitches().addFrenchKnot(knot->position + targetOffset, colorIndex);
267         }
268     }
269 }
270 
271 
operator <<(QDataStream & stream,const Pattern & pattern)272 QDataStream &operator<<(QDataStream &stream, const Pattern &pattern)
273 {
274     stream << qint32(pattern.version);
275 
276     stream << pattern.m_documentPalette;
277     stream << pattern.m_stitchData;
278 
279     return stream;
280 }
281 
282 
operator >>(QDataStream & stream,Pattern & pattern)283 QDataStream  &operator>>(QDataStream &stream, Pattern &pattern)
284 {
285     qint32 version;
286 
287     stream >> version;
288 
289     switch (version) {
290     case 100:
291         stream >> pattern.m_documentPalette;
292         stream >> pattern.m_stitchData;
293         break;
294 
295     default:
296         throw InvalidFileVersion(QString(i18n("Pattern version %1", version)));
297         break;
298     }
299 
300     return stream;
301 }
302