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