File indexing completed on 2024-05-12 16:01:37

0001 /*
0002  *  SPDX-FileCopyrightText: 2008, 2011 Cyrille Berger <cberger@cberger.net>
0003  *  SPDX-FileCopyrightText: 2010 Geoffry Song <goffrie@gmail.com>
0004  *  SPDX-FileCopyrightText: 2017 Scott Petrovic <scottpetrovic@gmail.com>
0005  *
0006  *  SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include <QXmlStreamReader>
0010 #include "kis_painting_assistant.h"
0011 #include "kis_coordinates_converter.h"
0012 #include "kis_debug.h"
0013 #include "kis_dom_utils.h"
0014 #include <kis_canvas2.h>
0015 #include "kis_tool.h"
0016 #include "kis_config.h"
0017 
0018 #include <KoStore.h>
0019 
0020 #include <QGlobalStatic>
0021 #include <QPen>
0022 #include <QPainter>
0023 #include <QPixmapCache>
0024 #include <QDomElement>
0025 #include <QDomDocument>
0026 
0027 Q_GLOBAL_STATIC(KisPaintingAssistantFactoryRegistry, s_instance)
0028 
0029 struct KisPaintingAssistantHandle::Private {
0030     QList<KisPaintingAssistant*> assistants;
0031     char handle_type;
0032 };
0033 
0034 KisPaintingAssistantHandle::KisPaintingAssistantHandle(double x, double y) : QPointF(x, y), d(new Private)
0035 {
0036 }
0037 
0038 KisPaintingAssistantHandle::KisPaintingAssistantHandle(QPointF p) : QPointF(p), d(new Private)
0039 {
0040 }
0041 
0042 KisPaintingAssistantHandle::KisPaintingAssistantHandle(const KisPaintingAssistantHandle& rhs)
0043     : QPointF(rhs)
0044     , KisShared()
0045     , d(new Private)
0046 {
0047     dbgUI << "KisPaintingAssistantHandle ctor";
0048 }
0049 
0050 KisPaintingAssistantHandle& KisPaintingAssistantHandle::operator=(const QPointF &  pt)
0051 {
0052     setX(pt.x());
0053     setY(pt.y());
0054     return *this;
0055 }
0056 
0057 void KisPaintingAssistantHandle::setType(char type)
0058 {
0059     d->handle_type = type;
0060 }
0061 
0062 char KisPaintingAssistantHandle::handleType() const
0063 {
0064     return d->handle_type;
0065 }
0066 
0067 KisPaintingAssistant *KisPaintingAssistantHandle::chiefAssistant() const
0068 {
0069     return !d->assistants.isEmpty() ? d->assistants.first() : 0;
0070 }
0071 
0072 KisPaintingAssistantHandle::~KisPaintingAssistantHandle()
0073 {
0074     Q_ASSERT(d->assistants.empty());
0075     delete d;
0076 }
0077 
0078 void KisPaintingAssistantHandle::registerAssistant(KisPaintingAssistant* assistant)
0079 {
0080     Q_ASSERT(!d->assistants.contains(assistant));
0081     d->assistants.append(assistant);
0082 }
0083 
0084 void KisPaintingAssistantHandle::unregisterAssistant(KisPaintingAssistant* assistant)
0085 {
0086     d->assistants.removeOne(assistant);
0087     Q_ASSERT(!d->assistants.contains(assistant));
0088 }
0089 
0090 bool KisPaintingAssistantHandle::containsAssistant(KisPaintingAssistant* assistant) const
0091 {
0092     return d->assistants.contains(assistant);
0093 }
0094 
0095 void KisPaintingAssistantHandle::mergeWith(KisPaintingAssistantHandleSP handle)
0096 {
0097     if(this->handleType()== HandleType::NORMAL || handle.data()->handleType()== HandleType::SIDE) {
0098         return;
0099     }
0100 
0101 
0102     Q_FOREACH (KisPaintingAssistant* assistant, handle->d->assistants) {
0103         if (!assistant->handles().contains(this)) {
0104             assistant->replaceHandle(handle, this);
0105         }
0106     }
0107 }
0108 
0109 void KisPaintingAssistantHandle::uncache()
0110 {
0111     Q_FOREACH (KisPaintingAssistant* assistant, d->assistants) {
0112         assistant->uncache();
0113     }
0114 }
0115 
0116 struct KisPaintingAssistant::Private {
0117     Private();
0118     explicit Private(const Private &rhs);
0119     KisPaintingAssistantHandleSP reuseOrCreateHandle(QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> &handleMap, KisPaintingAssistantHandleSP origHandle, KisPaintingAssistant *q, bool registerAssistant = true);
0120     QList<KisPaintingAssistantHandleSP> handles, sideHandles;
0121 
0122     KisPaintingAssistantHandleSP topLeft, bottomLeft, topRight, bottomRight, topMiddle, bottomMiddle, rightMiddle, leftMiddle;
0123 
0124     // share everything except handles between the clones
0125     struct SharedData {
0126         QString id;
0127         QString name;
0128         bool isSnappingActive {true};
0129         bool outlineVisible {true};
0130         bool isLocal {false};
0131 
0132         KisCanvas2* m_canvas {nullptr};
0133 
0134         QPixmapCache::Key cached;
0135         QRect cachedRect; // relative to boundingRect().topLeft()
0136 
0137         struct TranslationInvariantTransform {
0138             qreal m11 {0.0};
0139             qreal m12 {0.0};
0140             qreal m21 {0.0};
0141             qreal m22 {0.0};
0142 
0143             TranslationInvariantTransform() { }
0144             TranslationInvariantTransform(const QTransform& t) : m11(t.m11()), m12(t.m12()), m21(t.m21()), m22(t.m22()) { }
0145             bool operator==(const TranslationInvariantTransform& b) {
0146                 return m11 == b.m11 && m12 == b.m12 && m21 == b.m21 && m22 == b.m22;
0147             }
0148         } cachedTransform;
0149 
0150         QColor assistantGlobalColorCache = QColor(Qt::red);     // color to paint with if a custom color is not set
0151 
0152         bool useCustomColor {false};
0153         QColor assistantCustomColor {KisConfig(true).defaultAssistantsColor()};
0154     };
0155 
0156     QSharedPointer<SharedData> s;
0157 };
0158 
0159 KisPaintingAssistant::Private::Private()
0160     : s(new SharedData)
0161 {
0162 }
0163 
0164 KisPaintingAssistant::Private::Private(const Private &rhs)
0165     : s(rhs.s)
0166 {
0167 }
0168 
0169 KisPaintingAssistantHandleSP KisPaintingAssistant::Private::reuseOrCreateHandle(QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> &handleMap, KisPaintingAssistantHandleSP origHandle, KisPaintingAssistant *q, bool registerAssistant)
0170 {
0171     KisPaintingAssistantHandleSP mappedHandle = handleMap.value(origHandle);
0172     if (!mappedHandle) {
0173         if (origHandle) {
0174             dbgUI << "handle not found in the map, creating a new one...";
0175             mappedHandle = KisPaintingAssistantHandleSP(new KisPaintingAssistantHandle(*origHandle));
0176             dbgUI << "done";
0177             mappedHandle->setType(origHandle->handleType());
0178             handleMap.insert(origHandle, mappedHandle);
0179         } else {
0180             dbgUI << "orig handle is null, not doing anything";
0181             mappedHandle = KisPaintingAssistantHandleSP();
0182         }
0183     }
0184     if (mappedHandle && registerAssistant) {
0185         mappedHandle->registerAssistant(q);
0186     }
0187     return mappedHandle;
0188 }
0189 
0190 bool KisPaintingAssistant::useCustomColor()
0191 {
0192     return d->s->useCustomColor;
0193 }
0194 
0195 void KisPaintingAssistant::setUseCustomColor(bool useCustomColor)
0196 {
0197     d->s->useCustomColor = useCustomColor;
0198 }
0199 
0200 void KisPaintingAssistant::setAssistantCustomColor(QColor color)
0201 {
0202     d->s->assistantCustomColor = color;
0203 }
0204 
0205 QColor KisPaintingAssistant::assistantCustomColor()
0206 {
0207     return d->s->assistantCustomColor;
0208 }
0209 
0210 void KisPaintingAssistant::setAssistantGlobalColorCache(const QColor &color)
0211 {
0212     d->s->assistantGlobalColorCache = color;
0213 }
0214 
0215 QColor KisPaintingAssistant::effectiveAssistantColor() const
0216 {
0217     return d->s->useCustomColor ? d->s->assistantCustomColor : d->s->assistantGlobalColorCache;
0218 }
0219 
0220 KisPaintingAssistant::KisPaintingAssistant(const QString& id, const QString& name) : d(new Private)
0221 {
0222     d->s->id = id;
0223     d->s->name = name;
0224     d->s->isSnappingActive = true;
0225     d->s->outlineVisible = true;
0226 }
0227 
0228 KisPaintingAssistant::KisPaintingAssistant(const KisPaintingAssistant &rhs, QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> &handleMap)
0229     : d(new Private(*(rhs.d)))
0230 {
0231     dbgUI << "creating handles...";
0232     Q_FOREACH (const KisPaintingAssistantHandleSP origHandle, rhs.d->handles) {
0233         d->handles << d->reuseOrCreateHandle(handleMap, origHandle, this);
0234     }
0235     Q_FOREACH (const KisPaintingAssistantHandleSP origHandle, rhs.d->sideHandles) {
0236         d->sideHandles << d->reuseOrCreateHandle(handleMap, origHandle, this);
0237     }
0238 #define _REUSE_H(name) d->name = d->reuseOrCreateHandle(handleMap, rhs.d->name, this, /* registerAssistant = */ false)
0239     _REUSE_H(topLeft);
0240     _REUSE_H(bottomLeft);
0241     _REUSE_H(topRight);
0242     _REUSE_H(bottomRight);
0243     _REUSE_H(topMiddle);
0244     _REUSE_H(bottomMiddle);
0245     _REUSE_H(rightMiddle);
0246     _REUSE_H(leftMiddle);
0247 #undef _REUSE_H
0248     dbgUI << "done";
0249 }
0250 
0251 bool KisPaintingAssistant::isSnappingActive() const
0252 {
0253     return d->s->isSnappingActive;
0254 }
0255 
0256 void KisPaintingAssistant::setSnappingActive(bool set)
0257 {
0258     d->s->isSnappingActive = set;
0259 }
0260 
0261 bool KisPaintingAssistant::canBeLocal() const
0262 {
0263     return false;
0264 }
0265 
0266 bool KisPaintingAssistant::isLocal() const
0267 {
0268     return d->s->isLocal;
0269 }
0270 
0271 void KisPaintingAssistant::setLocal(bool value)
0272 {
0273     d->s->isLocal = value;
0274 }
0275 
0276 
0277 void KisPaintingAssistant::drawPath(QPainter& painter, const QPainterPath &path, bool isSnappingOn)
0278 {
0279 
0280     QColor paintingColor = effectiveAssistantColor();
0281 
0282     if (!isSnappingOn) {
0283         paintingColor.setAlpha(0.2 * paintingColor.alpha());
0284     }
0285 
0286     painter.save();
0287     QPen pen_a(paintingColor, 2);
0288     pen_a.setCosmetic(true);
0289     painter.setPen(pen_a);
0290     painter.drawPath(path);
0291     painter.restore();
0292 }
0293 
0294 void KisPaintingAssistant::drawPreview(QPainter& painter, const QPainterPath &path)
0295 {
0296     painter.save();
0297     QPen pen_a(effectiveAssistantColor(), 1);
0298     pen_a.setStyle(Qt::SolidLine);
0299     pen_a.setCosmetic(true);
0300     painter.setPen(pen_a);
0301     painter.drawPath(path);
0302     painter.restore();
0303 }
0304 
0305 void KisPaintingAssistant::initHandles(QList<KisPaintingAssistantHandleSP> _handles)
0306 {
0307     Q_ASSERT(d->handles.isEmpty());
0308     d->handles = _handles;
0309     Q_FOREACH (KisPaintingAssistantHandleSP handle, _handles) {
0310         handle->registerAssistant(this);
0311     }
0312 }
0313 
0314 KisPaintingAssistant::~KisPaintingAssistant()
0315 {
0316     Q_FOREACH (KisPaintingAssistantHandleSP handle, d->handles) {
0317         handle->unregisterAssistant(this);
0318     }
0319     if(!d->sideHandles.isEmpty()) {
0320         Q_FOREACH (KisPaintingAssistantHandleSP handle, d->sideHandles) {
0321             handle->unregisterAssistant(this);
0322         }
0323     }
0324     delete d;
0325 }
0326 
0327 const QString& KisPaintingAssistant::id() const
0328 {
0329     return d->s->id;
0330 }
0331 
0332 const QString& KisPaintingAssistant::name() const
0333 {
0334     return d->s->name;
0335 }
0336 
0337 void KisPaintingAssistant::replaceHandle(KisPaintingAssistantHandleSP _handle, KisPaintingAssistantHandleSP _with)
0338 {
0339     Q_ASSERT(d->handles.contains(_handle));
0340     d->handles.replace(d->handles.indexOf(_handle), _with);
0341     Q_ASSERT(!d->handles.contains(_handle));
0342     _handle->unregisterAssistant(this);
0343     _with->registerAssistant(this);
0344 }
0345 
0346 void KisPaintingAssistant::addHandle(KisPaintingAssistantHandleSP handle, HandleType type)
0347 {
0348     Q_ASSERT(!d->handles.contains(handle));
0349     if (HandleType::SIDE == type) {
0350         d->sideHandles.append(handle);
0351     } else {
0352         d->handles.append(handle);
0353     }
0354 
0355     handle->registerAssistant(this);
0356     handle.data()->setType(type);
0357 }
0358 
0359 QPointF KisPaintingAssistant::viewportConstrainedEditorPosition(const KisCoordinatesConverter* converter, const QSize editorSize)
0360 {
0361     QPointF editorDocumentPos = getEditorPosition();
0362     QPointF editorWidgetPos = converter->documentToWidgetTransform().map(editorDocumentPos);
0363     QSizeF canvasSize = converter->getCanvasWidgetSize();
0364     const int padding = 16;
0365 
0366     editorWidgetPos.rx() = qBound(0.0,
0367                                   editorWidgetPos.x(),
0368                                   canvasSize.width() - (editorSize.width() + padding));
0369     editorWidgetPos.ry() = qBound(0.0,
0370                                   editorWidgetPos.y(),
0371                                   canvasSize.height() - (editorSize.height() + padding));
0372 
0373     return converter->widgetToDocument(editorWidgetPos);
0374 }
0375 
0376 void KisPaintingAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool useCache, KisCanvas2* canvas, bool assistantVisible, bool previewVisible)
0377 {
0378     Q_UNUSED(updateRect);
0379 
0380     Q_UNUSED(previewVisible);
0381 
0382     findPerspectiveAssistantHandleLocation();
0383 
0384     if (!useCache) {
0385         gc.save();
0386         drawCache(gc, converter, assistantVisible);
0387         gc.restore();
0388         return;
0389     }
0390 
0391     const QRect bound = boundingRect();
0392     if (bound.isEmpty()) {
0393         return;
0394     }
0395 
0396     const QTransform transform = converter->documentToWidgetTransform();
0397     const QRect widgetBound = transform.mapRect(bound);
0398 
0399     const QRect paintRect = transform.mapRect(bound).intersected(gc.viewport());
0400     if (paintRect.isEmpty()) return;
0401 
0402     QPixmap cached;
0403     bool found = QPixmapCache::find(d->s->cached, &cached);
0404 
0405     if (!(found &&
0406           d->s->cachedTransform == transform &&
0407           d->s->cachedRect.translated(widgetBound.topLeft()).contains(paintRect))) {
0408 
0409         const QRect cacheRect = gc.viewport().adjusted(-100, -100, 100, 100).intersected(widgetBound);
0410         Q_ASSERT(!cacheRect.isEmpty());
0411 
0412         if (cached.isNull() || cached.size() != cacheRect.size()) {
0413             cached = QPixmap(cacheRect.size());
0414         }
0415 
0416         cached.fill(Qt::transparent);
0417         QPainter painter(&cached);
0418         painter.setRenderHint(QPainter::Antialiasing);
0419         painter.setWindow(cacheRect);
0420         drawCache(painter, converter, assistantVisible);
0421         painter.end();
0422         d->s->cachedTransform = transform;
0423         d->s->cachedRect = cacheRect.translated(-widgetBound.topLeft());
0424         d->s->cached = QPixmapCache::insert(cached);
0425     }
0426 
0427     gc.drawPixmap(paintRect, cached, paintRect.translated(-widgetBound.topLeft() - d->s->cachedRect.topLeft()));
0428 
0429 
0430     if (canvas) {
0431         d->s->m_canvas = canvas;
0432     }
0433 }
0434 
0435 void KisPaintingAssistant::uncache()
0436 {
0437     d->s->cached = QPixmapCache::Key();
0438 }
0439 
0440 QRect KisPaintingAssistant::boundingRect() const
0441 {
0442     QRectF r;
0443     Q_FOREACH (KisPaintingAssistantHandleSP h, handles()) {
0444         r = r.united(QRectF(*h, QSizeF(1,1)));
0445     }
0446     return r.adjusted(-2, -2, 2, 2).toAlignedRect();
0447 }
0448 
0449 bool KisPaintingAssistant::isAssistantComplete() const
0450 {
0451     return true;
0452 }
0453 
0454 void KisPaintingAssistant::transform(const QTransform &transform)
0455 {
0456     Q_FOREACH(KisPaintingAssistantHandleSP handle, handles()) {
0457         if (handle->chiefAssistant() != this) continue;
0458 
0459         *handle = transform.map(*handle);
0460     }
0461 
0462     Q_FOREACH(KisPaintingAssistantHandleSP handle, sideHandles()) {
0463         if (handle->chiefAssistant() != this) continue;
0464 
0465         *handle = transform.map(*handle);
0466     }
0467 
0468     uncache();
0469 }
0470 
0471 QByteArray KisPaintingAssistant::saveXml(QMap<KisPaintingAssistantHandleSP, int> &handleMap)
0472 {
0473     QByteArray data;
0474     QXmlStreamWriter xml(&data);
0475     xml.writeStartDocument();
0476     xml.writeStartElement("assistant");
0477     xml.writeAttribute("type",d->s->id);
0478     xml.writeAttribute("active", QString::number(d->s->isSnappingActive));
0479     xml.writeAttribute("useCustomColor", QString::number(d->s->useCustomColor));
0480     xml.writeAttribute("customColor",  KisDomUtils::qColorToQString(d->s->assistantCustomColor));
0481 
0482 
0483 
0484     saveCustomXml(&xml); // if any specific assistants have custom XML data to save to
0485 
0486     // write individual handle data
0487     xml.writeStartElement("handles");
0488     Q_FOREACH (const KisPaintingAssistantHandleSP handle, d->handles) {
0489         int id = handleMap.size();
0490         if (!handleMap.contains(handle)){
0491             handleMap.insert(handle, id);
0492         }
0493         id = handleMap.value(handle);
0494         xml.writeStartElement("handle");
0495         xml.writeAttribute("id", QString::number(id));
0496         xml.writeAttribute("x", QString::number(double(handle->x()), 'f', 3));
0497         xml.writeAttribute("y", QString::number(double(handle->y()), 'f', 3));
0498         xml.writeEndElement();
0499     }
0500     xml.writeEndElement();
0501     if (!d->sideHandles.isEmpty()) { // for vanishing points only
0502     xml.writeStartElement("sidehandles");
0503     QMap<KisPaintingAssistantHandleSP, int> sideHandleMap;
0504     Q_FOREACH (KisPaintingAssistantHandleSP handle, d->sideHandles) {
0505         int id = sideHandleMap.size();
0506         sideHandleMap.insert(handle, id);
0507         xml.writeStartElement("sidehandle");
0508         xml.writeAttribute("id", QString::number(id));
0509         xml.writeAttribute("x", QString::number(double(handle->x()), 'f', 3));
0510         xml.writeAttribute("y", QString::number(double(handle->y()), 'f', 3));
0511         xml.writeEndElement();
0512       }
0513     }
0514 
0515     xml.writeEndElement();
0516     xml.writeEndDocument();
0517     return data;
0518 }
0519 
0520 void KisPaintingAssistant::saveCustomXml(QXmlStreamWriter* xml)
0521 {
0522     Q_UNUSED(xml);
0523 }
0524 
0525 void KisPaintingAssistant::loadXml(KoStore* store, QMap<int, KisPaintingAssistantHandleSP> &handleMap, QString path)
0526 {
0527     int id = 0;
0528     double x = 0.0, y = 0.0;
0529     store->open(path);
0530     QByteArray data = store->read(store->size());
0531     QXmlStreamReader xml(data);
0532     QMap<int, KisPaintingAssistantHandleSP> sideHandleMap;
0533     while (!xml.atEnd()) {
0534         switch (xml.readNext()) {
0535         case QXmlStreamReader::StartElement:
0536             if (xml.name() == "assistant") {
0537 
0538                 QStringRef active = xml.attributes().value("active");
0539                 setSnappingActive( (active != "0")  );
0540 
0541                 // load custom shared assistant properties
0542                 if ( xml.attributes().hasAttribute("useCustomColor")) {
0543                     QStringRef useCustomColor = xml.attributes().value("useCustomColor");
0544 
0545                     bool usingColor = false;
0546                     if (useCustomColor.toString() == "1") {
0547                         usingColor = true;
0548                     }
0549 
0550 
0551                     setUseCustomColor(usingColor);
0552                 }
0553 
0554                 if ( xml.attributes().hasAttribute("customColor")) {
0555                     QStringRef customColor = xml.attributes().value("customColor");
0556                     setAssistantCustomColor( KisDomUtils::qStringToQColor(customColor.toString()) );
0557 
0558                 }
0559 
0560             }
0561 
0562             loadCustomXml(&xml);
0563 
0564             if (xml.name() == "handle") {
0565                 QString strId = xml.attributes().value("id").toString(),
0566                         strX = xml.attributes().value("x").toString(),
0567                         strY = xml.attributes().value("y").toString();
0568                 if (!strId.isEmpty() && !strX.isEmpty() && !strY.isEmpty()) {
0569                     id = strId.toInt();
0570                     x = strX.toDouble();
0571                     y = strY.toDouble();
0572                     if (!handleMap.contains(id)) {
0573                         handleMap.insert(id, new KisPaintingAssistantHandle(x, y));
0574                     }
0575                 }
0576                 addHandle(handleMap.value(id), HandleType::NORMAL);
0577             } else if (xml.name() == "sidehandle") {
0578                 QString strId = xml.attributes().value("id").toString(),
0579                         strX = xml.attributes().value("x").toString(),
0580                         strY = xml.attributes().value("y").toString();
0581                 if (!strId.isEmpty() && !strX.isEmpty() && !strY.isEmpty()) {
0582                     id = strId.toInt();
0583                     x = strX.toDouble();
0584                     y = strY.toDouble();
0585                     if (!sideHandleMap.contains(id)) {
0586                         sideHandleMap.insert(id, new KisPaintingAssistantHandle(x, y));
0587                     }
0588                 }
0589                 addHandle(sideHandleMap.value(id), HandleType::SIDE);
0590 
0591             }
0592             break;
0593         default:
0594             break;
0595         }
0596     }
0597     store->close();
0598 }
0599 
0600 bool KisPaintingAssistant::loadCustomXml(QXmlStreamReader* xml)
0601 {
0602     Q_UNUSED(xml);
0603     return true;
0604 }
0605 
0606 void KisPaintingAssistant::saveXmlList(QDomDocument& doc, QDomElement& assistantsElement,int count)
0607 {
0608     if (d->s->id == "ellipse"){
0609         QDomElement assistantElement = doc.createElement("assistant");
0610         assistantElement.setAttribute("type", "ellipse");
0611         assistantElement.setAttribute("filename", QString("ellipse%1.assistant").arg(count));
0612         assistantsElement.appendChild(assistantElement);
0613     }
0614     else if (d->s->id == "spline"){
0615         QDomElement assistantElement = doc.createElement("assistant");
0616         assistantElement.setAttribute("type", "spline");
0617         assistantElement.setAttribute("filename", QString("spline%1.assistant").arg(count));
0618         assistantsElement.appendChild(assistantElement);
0619     }
0620     else if (d->s->id == "perspective"){
0621         QDomElement assistantElement = doc.createElement("assistant");
0622         assistantElement.setAttribute("type", "perspective");
0623         assistantElement.setAttribute("filename", QString("perspective%1.assistant").arg(count));
0624         assistantsElement.appendChild(assistantElement);
0625     }
0626     else if (d->s->id == "vanishing point"){
0627         QDomElement assistantElement = doc.createElement("assistant");
0628         assistantElement.setAttribute("type", "vanishing point");
0629         assistantElement.setAttribute("filename", QString("vanishing point%1.assistant").arg(count));
0630         assistantsElement.appendChild(assistantElement);
0631     }
0632     else if (d->s->id == "infinite ruler"){
0633         QDomElement assistantElement = doc.createElement("assistant");
0634         assistantElement.setAttribute("type", "infinite ruler");
0635         assistantElement.setAttribute("filename", QString("infinite ruler%1.assistant").arg(count));
0636         assistantsElement.appendChild(assistantElement);
0637     }
0638     else if (d->s->id == "parallel ruler"){
0639         QDomElement assistantElement = doc.createElement("assistant");
0640         assistantElement.setAttribute("type", "parallel ruler");
0641         assistantElement.setAttribute("filename", QString("parallel ruler%1.assistant").arg(count));
0642         assistantsElement.appendChild(assistantElement);
0643     }
0644     else if (d->s->id == "concentric ellipse"){
0645         QDomElement assistantElement = doc.createElement("assistant");
0646         assistantElement.setAttribute("type", "concentric ellipse");
0647         assistantElement.setAttribute("filename", QString("concentric ellipse%1.assistant").arg(count));
0648         assistantsElement.appendChild(assistantElement);
0649     }
0650     else if (d->s->id == "fisheye-point"){
0651         QDomElement assistantElement = doc.createElement("assistant");
0652         assistantElement.setAttribute("type", "fisheye-point");
0653         assistantElement.setAttribute("filename", QString("fisheye-point%1.assistant").arg(count));
0654         assistantsElement.appendChild(assistantElement);
0655     }
0656     else if (d->s->id == "ruler"){
0657         QDomElement assistantElement = doc.createElement("assistant");
0658         assistantElement.setAttribute("type", "ruler");
0659         assistantElement.setAttribute("filename", QString("ruler%1.assistant").arg(count));
0660         assistantsElement.appendChild(assistantElement);
0661     }
0662     else if (d->s->id == "two point"){
0663         QDomElement assistantElement = doc.createElement("assistant");
0664         assistantElement.setAttribute("type", "two point");
0665         assistantElement.setAttribute("filename", QString("two point%1.assistant").arg(count));
0666         assistantsElement.appendChild(assistantElement);
0667     }
0668     else if (d->s->id == "perspective ellipse"){
0669         QDomElement assistantElement = doc.createElement("assistant");
0670         assistantElement.setAttribute("type", "perspective ellipse");
0671         assistantElement.setAttribute("filename", QString("perspective ellipse%1.assistant").arg(count));
0672         assistantsElement.appendChild(assistantElement);
0673     }
0674 }
0675 
0676 void KisPaintingAssistant::findPerspectiveAssistantHandleLocation() {
0677     QList<KisPaintingAssistantHandleSP> hHandlesList;
0678     QList<KisPaintingAssistantHandleSP> vHandlesList;
0679     uint vHole = 0,hHole = 0;
0680     KisPaintingAssistantHandleSP oppHandle;
0681     if (d->handles.size() == 4 && d->s->id == "perspective") {
0682         //get the handle opposite to the first handle
0683         oppHandle = oppHandleOne();
0684         //Sorting handles into two list, X sorted and Y sorted into hHandlesList and vHandlesList respectively.
0685         Q_FOREACH (const KisPaintingAssistantHandleSP handle,d->handles) {
0686             hHandlesList.append(handle);
0687             hHole = hHandlesList.size() - 1;
0688             vHandlesList.append(handle);
0689             vHole = vHandlesList.size() - 1;
0690             /*
0691              sort handles on the basis of X-coordinate
0692              */
0693             while(hHole > 0 && hHandlesList.at(hHole -1).data()->x() > handle.data()->x()) {
0694 #if QT_VERSION >= QT_VERSION_CHECK(5,13,0)
0695                 hHandlesList.swapItemsAt(hHole - 1, hHole);
0696 #else
0697                 hHandlesList.swap(hHole - 1, hHole);
0698 #endif
0699                 hHole = hHole - 1;
0700             }
0701             /*
0702              sort handles on the basis of Y-coordinate
0703              */
0704             while(vHole > 0 && vHandlesList.at(vHole -1).data()->y() > handle.data()->y()) {
0705 #if QT_VERSION >= QT_VERSION_CHECK(5,13,0)
0706                 vHandlesList.swapItemsAt(vHole-1, vHole);
0707 #else
0708                 vHandlesList.swap(vHole-1, vHole);
0709 #endif
0710                 vHole = vHole - 1;
0711             }
0712         }
0713 
0714         /*
0715          give the handles their respective positions
0716          */
0717         if(vHandlesList.at(0).data()->x() > vHandlesList.at(1).data()->x()) {
0718             d->topLeft = vHandlesList.at(1);
0719             d->topRight= vHandlesList.at(0);
0720         }
0721         else {
0722             d->topLeft = vHandlesList.at(0);
0723             d->topRight = vHandlesList.at(1);
0724         }
0725         if(vHandlesList.at(2).data()->x() > vHandlesList.at(3).data()->x()) {
0726             d->bottomLeft = vHandlesList.at(3);
0727             d->bottomRight = vHandlesList.at(2);
0728         }
0729         else {
0730             d->bottomLeft= vHandlesList.at(2);
0731             d->bottomRight = vHandlesList.at(3);
0732         }
0733 
0734         /*
0735          find if the handles that should be opposite are actually oppositely positioned
0736          */
0737         if (( (d->topLeft == d->handles.at(0).data() && d->bottomRight == oppHandle) ||
0738               (d->topLeft == oppHandle && d->bottomRight == d->handles.at(0).data()) ||
0739               (d->topRight == d->handles.at(0).data() && d->bottomLeft == oppHandle) ||
0740               (d->topRight == oppHandle && d->bottomLeft == d->handles.at(0).data()) ) )
0741         {}
0742         else {
0743             if(hHandlesList.at(0).data()->y() > hHandlesList.at(1).data()->y()) {
0744                 d->topLeft = hHandlesList.at(1);
0745                 d->bottomLeft= hHandlesList.at(0);
0746             }
0747             else {
0748                 d->topLeft = hHandlesList.at(0);
0749                 d->bottomLeft = hHandlesList.at(1);
0750             }
0751             if(hHandlesList.at(2).data()->y() > hHandlesList.at(3).data()->y()) {
0752                 d->topRight = hHandlesList.at(3);
0753                 d->bottomRight = hHandlesList.at(2);
0754             }
0755             else {
0756                 d->topRight= hHandlesList.at(2);
0757                 d->bottomRight = hHandlesList.at(3);
0758             }
0759 
0760         }
0761         /*
0762          Setting the middle handles as needed
0763          */
0764         if(!d->bottomMiddle && !d->topMiddle && !d->leftMiddle && !d->rightMiddle) {
0765   
0766             // Before re-adding the handles, clear old ones that have been
0767             // potentially loaded from disk and not re-associated with the
0768             // xxxMiddle pointers in d; otherwise those would stay in place.
0769             if(!d->sideHandles.isEmpty()) {
0770                 Q_FOREACH (KisPaintingAssistantHandleSP handle, d->sideHandles) {
0771                     handle->unregisterAssistant(this);
0772                 }
0773                 d->sideHandles.clear();
0774             }
0775           
0776             d->bottomMiddle = new KisPaintingAssistantHandle((d->bottomLeft.data()->x() + d->bottomRight.data()->x())*0.5,
0777                                                              (d->bottomLeft.data()->y() + d->bottomRight.data()->y())*0.5);
0778             d->topMiddle = new KisPaintingAssistantHandle((d->topLeft.data()->x() + d->topRight.data()->x())*0.5,
0779                                                           (d->topLeft.data()->y() + d->topRight.data()->y())*0.5);
0780             d->rightMiddle= new KisPaintingAssistantHandle((d->topRight.data()->x() + d->bottomRight.data()->x())*0.5,
0781                                                            (d->topRight.data()->y() + d->bottomRight.data()->y())*0.5);
0782             d->leftMiddle= new KisPaintingAssistantHandle((d->bottomLeft.data()->x() + d->topLeft.data()->x())*0.5,
0783                                                           (d->bottomLeft.data()->y() + d->topLeft.data()->y())*0.5);
0784             
0785             addHandle(d->rightMiddle, HandleType::SIDE);
0786             addHandle(d->leftMiddle, HandleType::SIDE);
0787             addHandle(d->bottomMiddle, HandleType::SIDE);
0788             addHandle(d->topMiddle, HandleType::SIDE);
0789         }
0790         else
0791         {
0792             d->bottomMiddle.data()->operator =(QPointF((d->bottomLeft.data()->x() + d->bottomRight.data()->x())*0.5,
0793                                                        (d->bottomLeft.data()->y() + d->bottomRight.data()->y())*0.5));
0794             d->topMiddle.data()->operator =(QPointF((d->topLeft.data()->x() + d->topRight.data()->x())*0.5,
0795                                                     (d->topLeft.data()->y() + d->topRight.data()->y())*0.5));
0796             d->rightMiddle.data()->operator =(QPointF((d->topRight.data()->x() + d->bottomRight.data()->x())*0.5,
0797                                                       (d->topRight.data()->y() + d->bottomRight.data()->y())*0.5));
0798             d->leftMiddle.data()->operator =(QPointF((d->bottomLeft.data()->x() + d->topLeft.data()->x())*0.5,
0799                                                      (d->bottomLeft.data()->y() + d->topLeft.data()->y())*0.5));
0800         }
0801 
0802     }
0803 }
0804 
0805 KisPaintingAssistantHandleSP KisPaintingAssistant::oppHandleOne()
0806 {
0807     QPointF intersection(0,0);
0808     if((QLineF(d->handles.at(0).data()->toPoint(),d->handles.at(1).data()->toPoint()).intersect(QLineF(d->handles.at(2).data()->toPoint(),d->handles.at(3).data()->toPoint()), &intersection) != QLineF::NoIntersection)
0809             && (QLineF(d->handles.at(0).data()->toPoint(),d->handles.at(1).data()->toPoint()).intersect(QLineF(d->handles.at(2).data()->toPoint(),d->handles.at(3).data()->toPoint()), &intersection) != QLineF::UnboundedIntersection))
0810     {
0811         return d->handles.at(1);
0812     }
0813     else if((QLineF(d->handles.at(0).data()->toPoint(),d->handles.at(2).data()->toPoint()).intersect(QLineF(d->handles.at(1).data()->toPoint(),d->handles.at(3).data()->toPoint()), &intersection) != QLineF::NoIntersection)
0814             && (QLineF(d->handles.at(0).data()->toPoint(),d->handles.at(2).data()->toPoint()).intersect(QLineF(d->handles.at(1).data()->toPoint(),d->handles.at(3).data()->toPoint()), &intersection) != QLineF::UnboundedIntersection))
0815     {
0816         return d->handles.at(2);
0817     }
0818     else
0819     {
0820         return d->handles.at(3);
0821     }
0822 }
0823 
0824 KisPaintingAssistantHandleSP KisPaintingAssistant::topLeft()
0825 {
0826     return d->topLeft;
0827 }
0828 
0829 const KisPaintingAssistantHandleSP KisPaintingAssistant::topLeft() const
0830 {
0831     return d->topLeft;
0832 }
0833 
0834 KisPaintingAssistantHandleSP KisPaintingAssistant::bottomLeft()
0835 {
0836     return d->bottomLeft;
0837 }
0838 
0839 const KisPaintingAssistantHandleSP KisPaintingAssistant::bottomLeft() const
0840 {
0841     return d->bottomLeft;
0842 }
0843 
0844 KisPaintingAssistantHandleSP KisPaintingAssistant::topRight()
0845 {
0846     return d->topRight;
0847 }
0848 
0849 const KisPaintingAssistantHandleSP KisPaintingAssistant::topRight() const
0850 {
0851     return d->topRight;
0852 }
0853 
0854 KisPaintingAssistantHandleSP KisPaintingAssistant::bottomRight()
0855 {
0856     return d->bottomRight;
0857 }
0858 
0859 const KisPaintingAssistantHandleSP KisPaintingAssistant::bottomRight() const
0860 {
0861     return d->bottomRight;
0862 }
0863 
0864 KisPaintingAssistantHandleSP KisPaintingAssistant::topMiddle()
0865 {
0866     return d->topMiddle;
0867 }
0868 
0869 const KisPaintingAssistantHandleSP KisPaintingAssistant::topMiddle() const
0870 {
0871     return d->topMiddle;
0872 }
0873 
0874 KisPaintingAssistantHandleSP KisPaintingAssistant::bottomMiddle()
0875 {
0876     return d->bottomMiddle;
0877 }
0878 
0879 const KisPaintingAssistantHandleSP KisPaintingAssistant::bottomMiddle() const
0880 {
0881     return d->bottomMiddle;
0882 }
0883 
0884 KisPaintingAssistantHandleSP KisPaintingAssistant::rightMiddle()
0885 {
0886     return d->rightMiddle;
0887 }
0888 
0889 const KisPaintingAssistantHandleSP KisPaintingAssistant::rightMiddle() const
0890 {
0891     return d->rightMiddle;
0892 }
0893 
0894 KisPaintingAssistantHandleSP KisPaintingAssistant::leftMiddle()
0895 {
0896     return d->leftMiddle;
0897 }
0898 
0899 const KisPaintingAssistantHandleSP KisPaintingAssistant::leftMiddle() const
0900 {
0901     return d->leftMiddle;
0902 }
0903 
0904 const QList<KisPaintingAssistantHandleSP>& KisPaintingAssistant::handles() const
0905 {
0906     return d->handles;
0907 }
0908 
0909 QList<KisPaintingAssistantHandleSP> KisPaintingAssistant::handles()
0910 {
0911     return d->handles;
0912 }
0913 
0914 const QList<KisPaintingAssistantHandleSP>& KisPaintingAssistant::sideHandles() const
0915 {
0916     return d->sideHandles;
0917 }
0918 
0919 QList<KisPaintingAssistantHandleSP> KisPaintingAssistant::sideHandles()
0920 {
0921     return d->sideHandles;
0922 }
0923 
0924 
0925 
0926 bool KisPaintingAssistant::areTwoPointsClose(const QPointF& pointOne, const QPointF& pointTwo)
0927 {
0928     int m_handleSize = 16;
0929 
0930     QRectF handlerect(pointTwo - QPointF(m_handleSize * 0.5, m_handleSize * 0.5), QSizeF(m_handleSize, m_handleSize));
0931     return handlerect.contains(pointOne);
0932 }
0933 
0934 KisPaintingAssistantHandleSP KisPaintingAssistant::closestCornerHandleFromPoint(QPointF point)
0935 {
0936     if (!d->s->m_canvas) {
0937         return 0;
0938     }
0939 
0940 
0941     if (areTwoPointsClose(point, pixelToView(topLeft()->toPoint()))) {
0942         return topLeft();
0943     } else if (areTwoPointsClose(point, pixelToView(topRight()->toPoint()))) {
0944         return topRight();
0945     } else if (areTwoPointsClose(point, pixelToView(bottomLeft()->toPoint()))) {
0946         return bottomLeft();
0947     } else if (areTwoPointsClose(point, pixelToView(bottomRight()->toPoint()))) {
0948         return bottomRight();
0949     }
0950     return 0;
0951 }
0952 
0953 
0954 QPointF KisPaintingAssistant::pixelToView(const QPoint pixelCoords) const
0955 {
0956     QPointF documentCoord = d->s->m_canvas->image()->pixelToDocument(pixelCoords);
0957     return d->s->m_canvas->viewConverter()->documentToView(documentCoord);
0958 }
0959 
0960 KisPaintingAssistantHandleSP KisPaintingAssistant::firstLocalHandle() const
0961 {
0962     return 0;
0963 }
0964 
0965 KisPaintingAssistantHandleSP KisPaintingAssistant::secondLocalHandle() const
0966 {
0967     return 0;
0968 }
0969 
0970 QRectF KisPaintingAssistant::getLocalRect() const
0971 {
0972     if (!isLocal() || !firstLocalHandle() || !secondLocalHandle()) {
0973         return QRectF();
0974     }
0975 
0976     KisPaintingAssistantHandleSP first = firstLocalHandle();
0977     KisPaintingAssistantHandleSP second = secondLocalHandle();
0978 
0979     QPointF topLeft = QPointF(qMin(first->x(), second->x()), qMin(first->y(), second->y()));
0980     QPointF bottomRight = QPointF(qMax(first->x(), second->x()), qMax(first->y(), second->y()));
0981 
0982     QRectF rect(topLeft, bottomRight);
0983     return rect;
0984 }
0985 
0986 double KisPaintingAssistant::norm2(const QPointF& p)
0987 {
0988     return p.x() * p.x() + p.y() * p.y();
0989 }
0990 
0991 QList<KisPaintingAssistantSP> KisPaintingAssistant::cloneAssistantList(const QList<KisPaintingAssistantSP> &list)
0992 {
0993     QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> handleMap;
0994     QList<KisPaintingAssistantSP> clonedList;
0995     for (auto i = list.begin(); i != list.end(); ++i) {
0996         clonedList << (*i)->clone(handleMap);
0997     }
0998     return clonedList;
0999 }
1000 
1001 
1002 
1003 /*
1004  * KisPaintingAssistantFactory classes
1005 */
1006 
1007 KisPaintingAssistantFactory::KisPaintingAssistantFactory()
1008 {
1009 }
1010 
1011 KisPaintingAssistantFactory::~KisPaintingAssistantFactory()
1012 {
1013 }
1014 
1015 KisPaintingAssistantFactoryRegistry::KisPaintingAssistantFactoryRegistry()
1016 {
1017 }
1018 
1019 KisPaintingAssistantFactoryRegistry::~KisPaintingAssistantFactoryRegistry()
1020 {
1021     Q_FOREACH (const QString &id, keys()) {
1022         delete get(id);
1023     }
1024     dbgRegistry << "deleting KisPaintingAssistantFactoryRegistry ";
1025 }
1026 
1027 KisPaintingAssistantFactoryRegistry* KisPaintingAssistantFactoryRegistry::instance()
1028 {
1029     return s_instance;
1030 }
1031