File indexing completed on 2024-04-28 04:32:08

0001 /*
0002  * Copyright (C) 2010-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 /**
0012     @file
0013     Implement classes for Stitch, StitchQueue, Backstitch and Knot.
0014     */
0015 
0016 #include "Stitch.h"
0017 
0018 #include <KLocalizedString>
0019 
0020 #include "Exceptions.h"
0021 
0022 /**
0023     Constructor.
0024     Used when creating instances for streaming.
0025     */
0026 Stitch::Stitch()
0027 {
0028 }
0029 
0030 /**
0031     Constructor.
0032     @param t stitch type
0033     @param i color index
0034     */
0035 Stitch::Stitch(Stitch::Type t, int i)
0036     : type(t)
0037     , colorIndex(i)
0038 {
0039 }
0040 
0041 QDataStream &operator<<(QDataStream &stream, const Stitch &stitch)
0042 {
0043     stream << qint32(stitch.version);
0044     stream << qint32(stitch.type);
0045     stream << qint32(stitch.colorIndex);
0046     return stream;
0047 }
0048 
0049 QDataStream &operator>>(QDataStream &stream, Stitch &stitch)
0050 {
0051     qint32 version;
0052     qint32 type;
0053     qint32 colorIndex;
0054 
0055     stream >> version;
0056 
0057     switch (version) {
0058     case 100:
0059         stream >> type;
0060         stream >> colorIndex;
0061         stitch.type = static_cast<Stitch::Type>(type);
0062         stitch.colorIndex = colorIndex;
0063         break;
0064 
0065     default:
0066         throw InvalidFileVersion(QString(i18n("Stitch version %1", version)));
0067         break;
0068     }
0069 
0070     return stream;
0071 }
0072 
0073 /**
0074     Constructor.
0075     */
0076 StitchQueue::StitchQueue()
0077     : QQueue<Stitch *>()
0078 {
0079 }
0080 
0081 StitchQueue::~StitchQueue()
0082 {
0083     while (!isEmpty()) {
0084         delete dequeue();
0085     }
0086 }
0087 
0088 StitchQueue::StitchQueue(StitchQueue *stitchQueue)
0089 {
0090     QListIterator<Stitch *> stitchIterator(*stitchQueue);
0091 
0092     while (stitchIterator.hasNext()) {
0093         Stitch *stitch = stitchIterator.next();
0094         enqueue(new Stitch(stitch->type, stitch->colorIndex));
0095     }
0096 }
0097 
0098 /**
0099     Add a stitch to the queue.
0100     @param type a Stitch::Type value to be added
0101     @param colorIndex the palette index
0102     */
0103 int StitchQueue::add(Stitch::Type type, int colorIndex)
0104 {
0105     bool miniStitch = (type & 192);
0106     int stitchCount = count();
0107     int stitches = stitchCount;
0108 
0109     if (!miniStitch) {
0110         // try and merge it with any existing stitches in the queue to update the stitch being added
0111         while (stitches--) {
0112             Stitch *stitch = dequeue();
0113 
0114             if (!(stitch->type & 192)) { // so we don't try and merge existing mini stitches
0115                 if (stitch->colorIndex == colorIndex) {
0116                     type = (Stitch::Type)(type | stitch->type);
0117                 }
0118             }
0119 
0120             enqueue(stitch);
0121         }
0122     }
0123 
0124     switch (int(type)) { // add the new stitch checking for illegal types
0125     case Stitch::TLQtr | Stitch::TRQtr:
0126         enqueue(new Stitch(Stitch::TLQtr, colorIndex));
0127         enqueue(new Stitch(Stitch::TRQtr, colorIndex));
0128         break;
0129 
0130     case Stitch::TLQtr | Stitch::BLQtr:
0131         enqueue(new Stitch(Stitch::TLQtr, colorIndex));
0132         enqueue(new Stitch(Stitch::BLQtr, colorIndex));
0133         break;
0134 
0135     case Stitch::TRQtr | Stitch::BRQtr:
0136         enqueue(new Stitch(Stitch::TRQtr, colorIndex));
0137         enqueue(new Stitch(Stitch::BRQtr, colorIndex));
0138         break;
0139 
0140     case Stitch::BLQtr | Stitch::BRQtr:
0141         enqueue(new Stitch(Stitch::BLQtr, colorIndex));
0142         enqueue(new Stitch(Stitch::BRQtr, colorIndex));
0143         break;
0144 
0145     default: // other values are acceptable as is including mini stitches
0146         enqueue(new Stitch(type, colorIndex));
0147         break;
0148     }
0149 
0150     /** iterate the queue of existing stitches for any that have been overwritten by the new stitch */
0151     while (stitchCount--) { // while there are existing stitches
0152         Stitch *stitch = dequeue(); // get the stitch at the head of the queue
0153         Stitch::Type currentStitchType = (Stitch::Type)(stitch->type); // and find its type
0154         int currentColorIndex = stitch->colorIndex; // and color
0155         Stitch::Type usageMask = (Stitch::Type)(currentStitchType & 15); // and find which parts of a stitch cell are used
0156         Stitch::Type interferenceMask = (Stitch::Type)(usageMask & type);
0157 
0158         // interferenceMask now contains a mask of which bits are affected by new stitch
0159         if (interferenceMask) {
0160             // Some parts of the current stitch are being overwritten
0161             // but a check needs to be made for illegal values
0162             Stitch::Type changeMask = (Stitch::Type)(usageMask ^ interferenceMask);
0163 
0164             switch (int(changeMask)) {
0165                 // changeMask contains what is left of the original stitch after being overwritten
0166                 // it may contain illegal values, so these are checked for
0167             case Stitch::TLQtr | Stitch::TRQtr:
0168                 enqueue(new Stitch(Stitch::TLQtr, currentColorIndex));
0169                 enqueue(new Stitch(Stitch::TRQtr, currentColorIndex));
0170                 changeMask = Stitch::Delete;
0171                 break;
0172 
0173             case Stitch::TLQtr | Stitch::BLQtr:
0174                 enqueue(new Stitch(Stitch::TLQtr, currentColorIndex));
0175                 enqueue(new Stitch(Stitch::BLQtr, currentColorIndex));
0176                 changeMask = Stitch::Delete;
0177                 break;
0178 
0179             case Stitch::TRQtr | Stitch::BRQtr:
0180                 enqueue(new Stitch(Stitch::TRQtr, currentColorIndex));
0181                 enqueue(new Stitch(Stitch::BRQtr, currentColorIndex));
0182                 changeMask = Stitch::Delete;
0183                 break;
0184 
0185             case Stitch::BLQtr | Stitch::BRQtr:
0186                 enqueue(new Stitch(Stitch::BLQtr, currentColorIndex));
0187                 enqueue(new Stitch(Stitch::BRQtr, currentColorIndex));
0188                 changeMask = Stitch::Delete;
0189                 break;
0190 
0191             default:
0192                 // other values are acceptable as is
0193                 break;
0194             }
0195 
0196             if (changeMask) { // Check if there is anything left of the original stitch, Stitch::Delete is 0
0197                 stitch->type = changeMask; // and change stitch type to the changeMask value
0198                 enqueue(stitch); // and then add it back to the queue
0199             }
0200         } else {
0201             enqueue(stitch);
0202         }
0203     }
0204 
0205     return count();
0206 }
0207 
0208 Stitch *StitchQueue::find(Stitch::Type type, int colorIndex)
0209 {
0210     int stitchCount = count();
0211     Stitch *found = nullptr;
0212 
0213     for (int i = 0; i < stitchCount; ++i) {
0214         Stitch *stitch = at(i);
0215 
0216         if (((type == Stitch::Delete) || ((stitch->type & type) == type)) && ((colorIndex == -1) || (stitch->colorIndex == colorIndex))) {
0217             found = stitch;
0218             break;
0219         }
0220     }
0221 
0222     return found;
0223 }
0224 
0225 int StitchQueue::remove(Stitch::Type type, int colorIndex)
0226 {
0227     int stitchCount = count();
0228 
0229     if (type == Stitch::Delete) {
0230         while (stitchCount--) {
0231             Stitch *stitch = dequeue();
0232 
0233             if ((colorIndex != -1) && (stitch->colorIndex != colorIndex)) {
0234                 enqueue(stitch);
0235             } else {
0236                 delete stitch;
0237             }
0238         }
0239     } else {
0240         while (stitchCount--) {
0241             Stitch *stitch = dequeue();
0242 
0243             if ((stitch->type != type) || ((colorIndex != -1) && (stitch->colorIndex != colorIndex))) {
0244                 if (((stitch->type & type) == type) && ((colorIndex == -1) || (stitch->colorIndex == colorIndex)) && ((stitch->type & 192) == 0)) {
0245                     // the mask covers a part of the current stitch and is the correct color or if the color doesn't matter
0246                     Stitch::Type changeMask = (Stitch::Type)(stitch->type ^ type);
0247                     stitch->type = Stitch::Delete;
0248                     int index = stitch->colorIndex;
0249 
0250                     switch (int(changeMask)) {
0251                         // changeMask contains what is left of the original stitch after deleting the maskStitch
0252                         // it may contain illegal values, so these are checked for
0253                     case Stitch::TLQtr | Stitch::TRQtr:
0254                         enqueue(new Stitch(Stitch::TLQtr, index));
0255                         enqueue(new Stitch(Stitch::TRQtr, index));
0256                         break;
0257 
0258                     case Stitch::TLQtr | Stitch::BLQtr:
0259                         enqueue(new Stitch(Stitch::TLQtr, index));
0260                         enqueue(new Stitch(Stitch::BLQtr, index));
0261                         break;
0262 
0263                     case Stitch::TRQtr | Stitch::BRQtr:
0264                         enqueue(new Stitch(Stitch::TRQtr, index));
0265                         enqueue(new Stitch(Stitch::BRQtr, index));
0266                         break;
0267 
0268                     case Stitch::BLQtr | Stitch::BRQtr:
0269                         enqueue(new Stitch(Stitch::BLQtr, index));
0270                         enqueue(new Stitch(Stitch::BRQtr, index));
0271                         break;
0272 
0273                     default:
0274                         stitch->type = changeMask;
0275                         enqueue(stitch);
0276                         break;
0277                     }
0278 
0279                     if (stitch->type == Stitch::Delete) {
0280                         delete stitch;
0281                     }
0282                 } else {
0283                     enqueue(stitch);
0284                 }
0285             } else {
0286                 delete stitch;
0287             }
0288         }
0289     }
0290 
0291     return count();
0292 }
0293 
0294 QDataStream &operator<<(QDataStream &stream, const StitchQueue &stitchQueue)
0295 {
0296     stream << qint32(stitchQueue.version);
0297     stream << qint32(stitchQueue.count());
0298     QListIterator<Stitch *> stitchIterator(stitchQueue);
0299 
0300     while (stitchIterator.hasNext()) {
0301         stream << *stitchIterator.next();
0302     }
0303 
0304     return stream;
0305 }
0306 
0307 QDataStream &operator>>(QDataStream &stream, StitchQueue &stitchQueue)
0308 {
0309     qint32 version;
0310     qint32 count;
0311 
0312     stream >> version;
0313 
0314     switch (version) {
0315     case 100:
0316         stream >> count;
0317 
0318         while (count--) {
0319             Stitch *stitch = new Stitch;
0320             stitchQueue.enqueue(stitch);
0321             stream >> *stitch;
0322         }
0323 
0324         break;
0325 
0326     default:
0327         throw InvalidFileVersion(QString(i18n("Stitch queue version %1", version)));
0328         break;
0329     }
0330 
0331     return stream;
0332 }
0333 
0334 /**
0335     Constructor.
0336     Used when creating instances for streaming.
0337     */
0338 Backstitch::Backstitch()
0339 {
0340 }
0341 
0342 /**
0343     Constructor.
0344     @param s start of backstitch line
0345     @param e end of backstitch line
0346     @param i palette index
0347     */
0348 Backstitch::Backstitch(const QPoint &s, const QPoint &e, int i)
0349     : start(s)
0350     , end(e)
0351     , colorIndex(i)
0352 {
0353 }
0354 
0355 /**
0356     Test if the backstitch start or end is equal to the
0357     requested point.
0358     @param point point to test
0359     @return true if p starts or ends the backstitch line,
0360     false other wise.
0361     */
0362 bool Backstitch::contains(const QPoint &point) const
0363 {
0364     if (start == point || end == point) {
0365         return true;
0366     }
0367 
0368     return false;
0369 }
0370 
0371 void Backstitch::move(int dx, int dy)
0372 {
0373     move(QPoint(dx, dy));
0374 }
0375 
0376 void Backstitch::move(const QPoint &offset)
0377 {
0378     start += offset;
0379     end += offset;
0380 }
0381 
0382 QDataStream &operator<<(QDataStream &stream, const Backstitch &backstitch)
0383 {
0384     stream << qint32(backstitch.version);
0385     stream << backstitch.start;
0386     stream << backstitch.end;
0387     stream << qint32(backstitch.colorIndex);
0388     return stream;
0389 }
0390 
0391 QDataStream &operator>>(QDataStream &stream, Backstitch &backstitch)
0392 {
0393     qint32 version;
0394     qint32 colorIndex;
0395 
0396     stream >> version;
0397 
0398     switch (version) {
0399     case 100:
0400         stream >> backstitch.start;
0401         stream >> backstitch.end;
0402         stream >> colorIndex;
0403         backstitch.colorIndex = colorIndex;
0404         break;
0405 
0406     default:
0407         throw InvalidFileVersion(QString(i18n("Backstitch version %1", version)));
0408         break;
0409     }
0410 
0411     return stream;
0412 }
0413 
0414 /**
0415     Constructor.
0416     Used when creating instances for streaming.
0417     */
0418 Knot::Knot()
0419 {
0420 }
0421 
0422 /**
0423     Constructor.
0424     @param p position for the knot
0425     @param i palette index
0426     */
0427 Knot::Knot(const QPoint &p, int i)
0428     : position(p)
0429     , colorIndex(i)
0430 {
0431 }
0432 
0433 void Knot::move(int dx, int dy)
0434 {
0435     move(QPoint(dx, dy));
0436 }
0437 
0438 void Knot::move(const QPoint &offset)
0439 {
0440     position += offset;
0441 }
0442 
0443 QDataStream &operator<<(QDataStream &stream, const Knot &knot)
0444 {
0445     stream << qint32(knot.version);
0446     stream << knot.position;
0447     stream << knot.colorIndex;
0448     return stream;
0449 }
0450 
0451 QDataStream &operator>>(QDataStream &stream, Knot &knot)
0452 {
0453     qint32 version;
0454     QPoint position;
0455     qint32 colorIndex;
0456 
0457     stream >> version;
0458 
0459     switch (version) {
0460     case 100:
0461         stream >> knot.position;
0462         stream >> colorIndex;
0463         knot.colorIndex = colorIndex;
0464         break;
0465 
0466     default:
0467         throw InvalidFileVersion(QString(i18n("Knot version %1", version)));
0468         break;
0469     }
0470 
0471     return stream;
0472 }