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