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 }