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 /**
13 @file
14 Implement classes for Stitch, StitchQueue, Backstitch and Knot.
15 */
16
17
18 #include "Stitch.h"
19
20 #include <KLocalizedString>
21
22 #include "Exceptions.h"
23
24
25 /**
26 Constructor.
27 Used when creating instances for streaming.
28 */
Stitch()29 Stitch::Stitch()
30 {
31 }
32
33
34 /**
35 Constructor.
36 @param t stitch type
37 @param i color index
38 */
Stitch(Stitch::Type t,int i)39 Stitch::Stitch(Stitch::Type t, int i)
40 : type(t),
41 colorIndex(i)
42 {
43 }
44
45
operator <<(QDataStream & stream,const Stitch & stitch)46 QDataStream &operator<<(QDataStream &stream, const Stitch &stitch)
47 {
48 stream << qint32(stitch.version);
49 stream << qint32(stitch.type);
50 stream << qint32(stitch.colorIndex);
51 return stream;
52 }
53
54
operator >>(QDataStream & stream,Stitch & stitch)55 QDataStream &operator>>(QDataStream &stream, Stitch &stitch)
56 {
57 qint32 version;
58 qint32 type;
59 qint32 colorIndex;
60
61 stream >> version;
62
63 switch (version) {
64 case 100:
65 stream >> type;
66 stream >> colorIndex;
67 stitch.type = static_cast<Stitch::Type>(type);
68 stitch.colorIndex = colorIndex;
69 break;
70
71 default:
72 throw InvalidFileVersion(QString(i18n("Stitch version %1", version)));
73 break;
74 }
75
76 return stream;
77 }
78
79
80 /**
81 Constructor.
82 */
StitchQueue()83 StitchQueue::StitchQueue()
84 : QQueue<Stitch *>()
85 {
86 }
87
88
~StitchQueue()89 StitchQueue::~StitchQueue()
90 {
91 while (!isEmpty()) {
92 delete dequeue();
93 }
94 }
95
96
StitchQueue(StitchQueue * stitchQueue)97 StitchQueue::StitchQueue(StitchQueue *stitchQueue)
98 {
99 QListIterator<Stitch *> stitchIterator(*stitchQueue);
100
101 while (stitchIterator.hasNext()) {
102 Stitch *stitch = stitchIterator.next();
103 enqueue(new Stitch(stitch->type, stitch->colorIndex));
104 }
105 }
106
107
108 /**
109 Add a stitch to the queue.
110 @param type a Stitch::Type value to be added
111 @param colorIndex the palette index
112 */
add(Stitch::Type type,int colorIndex)113 int StitchQueue::add(Stitch::Type type, int colorIndex)
114 {
115 bool miniStitch = (type & 192);
116 int stitchCount = count();
117 int stitches = stitchCount;
118
119 if (!miniStitch) {
120 // try and merge it with any existing stitches in the queue to update the stitch being added
121 while (stitches--) {
122 Stitch *stitch = dequeue();
123
124 if (!(stitch->type & 192)) { // so we don't try and merge existing mini stitches
125 if (stitch->colorIndex == colorIndex) {
126 type = (Stitch::Type)(type | stitch->type);
127 }
128 }
129
130 enqueue(stitch);
131 }
132 }
133
134 switch (int(type)) { // add the new stitch checking for illegal types
135 case Stitch::TLQtr | Stitch::TRQtr:
136 enqueue(new Stitch(Stitch::TLQtr, colorIndex));
137 enqueue(new Stitch(Stitch::TRQtr, colorIndex));
138 break;
139
140 case Stitch::TLQtr | Stitch::BLQtr:
141 enqueue(new Stitch(Stitch::TLQtr, colorIndex));
142 enqueue(new Stitch(Stitch::BLQtr, colorIndex));
143 break;
144
145 case Stitch::TRQtr | Stitch::BRQtr:
146 enqueue(new Stitch(Stitch::TRQtr, colorIndex));
147 enqueue(new Stitch(Stitch::BRQtr, colorIndex));
148 break;
149
150 case Stitch::BLQtr | Stitch::BRQtr:
151 enqueue(new Stitch(Stitch::BLQtr, colorIndex));
152 enqueue(new Stitch(Stitch::BRQtr, colorIndex));
153 break;
154
155 default: // other values are acceptable as is including mini stitches
156 enqueue(new Stitch(type, colorIndex));
157 break;
158 }
159
160 /** iterate the queue of existing stitches for any that have been overwritten by the new stitch */
161 while (stitchCount--) { // while there are existing stitches
162 Stitch *stitch = dequeue(); // get the stitch at the head of the queue
163 Stitch::Type currentStitchType = (Stitch::Type)(stitch->type); // and find its type
164 int currentColorIndex = stitch->colorIndex; // and color
165 Stitch::Type usageMask = (Stitch::Type)(currentStitchType & 15); // and find which parts of a stitch cell are used
166 Stitch::Type interferenceMask = (Stitch::Type)(usageMask & type);
167
168 // interferenceMask now contains a mask of which bits are affected by new stitch
169 if (interferenceMask) {
170 // Some parts of the current stitch are being overwritten
171 // but a check needs to be made for illegal values
172 Stitch::Type changeMask = (Stitch::Type)(usageMask ^ interferenceMask);
173
174 switch (int(changeMask)) {
175 // changeMask contains what is left of the original stitch after being overwritten
176 // it may contain illegal values, so these are checked for
177 case Stitch::TLQtr | Stitch::TRQtr:
178 enqueue(new Stitch(Stitch::TLQtr, currentColorIndex));
179 enqueue(new Stitch(Stitch::TRQtr, currentColorIndex));
180 changeMask = Stitch::Delete;
181 break;
182
183 case Stitch::TLQtr | Stitch::BLQtr:
184 enqueue(new Stitch(Stitch::TLQtr, currentColorIndex));
185 enqueue(new Stitch(Stitch::BLQtr, currentColorIndex));
186 changeMask = Stitch::Delete;
187 break;
188
189 case Stitch::TRQtr | Stitch::BRQtr:
190 enqueue(new Stitch(Stitch::TRQtr, currentColorIndex));
191 enqueue(new Stitch(Stitch::BRQtr, currentColorIndex));
192 changeMask = Stitch::Delete;
193 break;
194
195 case Stitch::BLQtr | Stitch::BRQtr:
196 enqueue(new Stitch(Stitch::BLQtr, currentColorIndex));
197 enqueue(new Stitch(Stitch::BRQtr, currentColorIndex));
198 changeMask = Stitch::Delete;
199 break;
200
201 default:
202 // other values are acceptable as is
203 break;
204 }
205
206 if (changeMask) { // Check if there is anything left of the original stitch, Stitch::Delete is 0
207 stitch->type = changeMask; // and change stitch type to the changeMask value
208 enqueue(stitch); // and then add it back to the queue
209 }
210 } else {
211 enqueue(stitch);
212 }
213 }
214
215 return count();
216 }
217
218
find(Stitch::Type type,int colorIndex)219 Stitch *StitchQueue::find(Stitch::Type type, int colorIndex)
220 {
221 int stitchCount = count();
222 Stitch *found = nullptr;
223
224 for (int i = 0 ; i < stitchCount ; ++i) {
225 Stitch *stitch = at(i);
226
227 if (((type == Stitch::Delete) || ((stitch->type & type) == type)) && ((colorIndex == -1) || (stitch->colorIndex == colorIndex))) {
228 found = stitch;
229 break;
230 }
231 }
232
233 return found;
234 }
235
236
remove(Stitch::Type type,int colorIndex)237 int StitchQueue::remove(Stitch::Type type, int colorIndex)
238 {
239 int stitchCount = count();
240
241 if (type == Stitch::Delete) {
242 while (stitchCount--) {
243 Stitch *stitch = dequeue();
244
245 if ((colorIndex != -1) && (stitch->colorIndex != colorIndex)) {
246 enqueue(stitch);
247 } else {
248 delete stitch;
249 }
250 }
251 } else {
252 while (stitchCount--) {
253 Stitch *stitch = dequeue();
254
255 if ((stitch->type != type) || ((colorIndex != -1) && (stitch->colorIndex != colorIndex))) {
256 if (((stitch->type & type) == type) && ((colorIndex == -1) || (stitch->colorIndex == colorIndex)) && ((stitch->type & 192) == 0)) {
257 // the mask covers a part of the current stitch and is the correct color or if the color doesn't matter
258 Stitch::Type changeMask = (Stitch::Type)(stitch->type ^ type);
259 stitch->type = Stitch::Delete;
260 int index = stitch->colorIndex;
261
262 switch (int(changeMask)) {
263 // changeMask contains what is left of the original stitch after deleting the maskStitch
264 // it may contain illegal values, so these are checked for
265 case Stitch::TLQtr | Stitch::TRQtr:
266 enqueue(new Stitch(Stitch::TLQtr, index));
267 enqueue(new Stitch(Stitch::TRQtr, index));
268 break;
269
270 case Stitch::TLQtr | Stitch::BLQtr:
271 enqueue(new Stitch(Stitch::TLQtr, index));
272 enqueue(new Stitch(Stitch::BLQtr, index));
273 break;
274
275 case Stitch::TRQtr | Stitch::BRQtr:
276 enqueue(new Stitch(Stitch::TRQtr, index));
277 enqueue(new Stitch(Stitch::BRQtr, index));
278 break;
279
280 case Stitch::BLQtr | Stitch::BRQtr:
281 enqueue(new Stitch(Stitch::BLQtr, index));
282 enqueue(new Stitch(Stitch::BRQtr, index));
283 break;
284
285 default:
286 stitch->type = changeMask;
287 enqueue(stitch);
288 break;
289 }
290
291 if (stitch->type == Stitch::Delete) {
292 delete stitch;
293 }
294 } else {
295 enqueue(stitch);
296 }
297 } else {
298 delete stitch;
299 }
300 }
301 }
302
303 return count();
304 }
305
306
operator <<(QDataStream & stream,const StitchQueue & stitchQueue)307 QDataStream &operator<<(QDataStream &stream, const StitchQueue &stitchQueue)
308 {
309 stream << qint32(stitchQueue.version);
310 stream << qint32(stitchQueue.count());
311 QListIterator<Stitch *> stitchIterator(stitchQueue);
312
313 while (stitchIterator.hasNext()) {
314 stream << *stitchIterator.next();
315 }
316
317 return stream;
318 }
319
320
operator >>(QDataStream & stream,StitchQueue & stitchQueue)321 QDataStream &operator>>(QDataStream &stream, StitchQueue &stitchQueue)
322 {
323 qint32 version;
324 qint32 count;
325
326 stream >> version;
327
328 switch (version) {
329 case 100:
330 stream >> count;
331
332 while (count--) {
333 Stitch *stitch = new Stitch;
334 stitchQueue.enqueue(stitch);
335 stream >> *stitch;
336 }
337
338 break;
339
340 default:
341 throw InvalidFileVersion(QString(i18n("Stitch queue version %1", version)));
342 break;
343 }
344
345 return stream;
346 }
347
348
349 /**
350 Constructor.
351 Used when creating instances for streaming.
352 */
Backstitch()353 Backstitch::Backstitch()
354 {
355 }
356
357
358 /**
359 Constructor.
360 @param s start of backstitch line
361 @param e end of backstitch line
362 @param i palette index
363 */
Backstitch(const QPoint & s,const QPoint & e,int i)364 Backstitch::Backstitch(const QPoint &s, const QPoint &e, int i)
365 : start(s),
366 end(e),
367 colorIndex(i)
368 {
369 }
370
371
372 /**
373 Test if the backstitch start or end is equal to the
374 requested point.
375 @param point point to test
376 @return true if p starts or ends the backstitch line,
377 false other wise.
378 */
contains(const QPoint & point) const379 bool Backstitch::contains(const QPoint &point) const
380 {
381 if (start == point || end == point) {
382 return true;
383 }
384
385 return false;
386 }
387
388
move(int dx,int dy)389 void Backstitch::move(int dx, int dy)
390 {
391 move(QPoint(dx, dy));
392 }
393
394
move(const QPoint & offset)395 void Backstitch::move(const QPoint &offset)
396 {
397 start += offset;
398 end += offset;
399 }
400
401
operator <<(QDataStream & stream,const Backstitch & backstitch)402 QDataStream &operator<<(QDataStream &stream, const Backstitch &backstitch)
403 {
404 stream << qint32(backstitch.version);
405 stream << backstitch.start;
406 stream << backstitch.end;
407 stream << qint32(backstitch.colorIndex);
408 return stream;
409 }
410
411
operator >>(QDataStream & stream,Backstitch & backstitch)412 QDataStream &operator>>(QDataStream &stream, Backstitch &backstitch)
413 {
414 qint32 version;
415 qint32 colorIndex;
416
417 stream >> version;
418
419 switch (version) {
420 case 100:
421 stream >> backstitch.start;
422 stream >> backstitch.end;
423 stream >> colorIndex;
424 backstitch.colorIndex = colorIndex;
425 break;
426
427 default:
428 throw InvalidFileVersion(QString(i18n("Backstitch version %1", version)));
429 break;
430 }
431
432 return stream;
433 }
434
435
436 /**
437 Constructor.
438 Used when creating instances for streaming.
439 */
Knot()440 Knot::Knot()
441 {
442 }
443
444
445 /**
446 Constructor.
447 @param p position for the knot
448 @param i palette index
449 */
Knot(const QPoint & p,int i)450 Knot::Knot(const QPoint &p, int i)
451 : position(p),
452 colorIndex(i)
453 {
454 }
455
456
move(int dx,int dy)457 void Knot::move(int dx, int dy)
458 {
459 move(QPoint(dx, dy));
460 }
461
462
move(const QPoint & offset)463 void Knot::move(const QPoint &offset)
464 {
465 position += offset;
466 }
467
468
operator <<(QDataStream & stream,const Knot & knot)469 QDataStream &operator<<(QDataStream &stream, const Knot &knot)
470 {
471 stream << qint32(knot.version);
472 stream << knot.position;
473 stream << knot.colorIndex;
474 return stream;
475 }
476
477
operator >>(QDataStream & stream,Knot & knot)478 QDataStream &operator>>(QDataStream &stream, Knot &knot)
479 {
480 qint32 version;
481 QPoint position;
482 qint32 colorIndex;
483
484 stream >> version;
485
486 switch (version) {
487 case 100:
488 stream >> knot.position;
489 stream >> colorIndex;
490 knot.colorIndex = colorIndex;
491 break;
492
493 default:
494 throw InvalidFileVersion(QString(i18n("Knot version %1", version)));
495 break;
496 }
497
498 return stream;
499 }
500