diff --git a/libs/ui/kis_painting_assistant.cc b/libs/ui/kis_painting_assistant.cc index d0b3d95d75..a093e681c7 100644 --- a/libs/ui/kis_painting_assistant.cc +++ b/libs/ui/kis_painting_assistant.cc @@ -1,916 +1,912 @@ /* * Copyright (c) 2008,2011 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "kis_painting_assistant.h" #include "kis_coordinates_converter.h" #include "kis_debug.h" #include "kis_dom_utils.h" #include #include "kis_tool.h" #include "kis_config.h" #include #include #include #include #include #include #include Q_GLOBAL_STATIC(KisPaintingAssistantFactoryRegistry, s_instance) struct KisPaintingAssistantHandle::Private { QList assistants; char handle_type; }; KisPaintingAssistantHandle::KisPaintingAssistantHandle(double x, double y) : QPointF(x, y), d(new Private) { } KisPaintingAssistantHandle::KisPaintingAssistantHandle(QPointF p) : QPointF(p), d(new Private) { } KisPaintingAssistantHandle::KisPaintingAssistantHandle(const KisPaintingAssistantHandle& rhs) : QPointF(rhs) , KisShared() , d(new Private) { dbgUI << "KisPaintingAssistantHandle ctor"; } KisPaintingAssistantHandle& KisPaintingAssistantHandle::operator=(const QPointF & pt) { setX(pt.x()); setY(pt.y()); return *this; } void KisPaintingAssistantHandle::setType(char type) { d->handle_type = type; } char KisPaintingAssistantHandle::handleType() const { return d->handle_type; } KisPaintingAssistantHandle::~KisPaintingAssistantHandle() { Q_ASSERT(d->assistants.empty()); delete d; } void KisPaintingAssistantHandle::registerAssistant(KisPaintingAssistant* assistant) { Q_ASSERT(!d->assistants.contains(assistant)); d->assistants.append(assistant); } void KisPaintingAssistantHandle::unregisterAssistant(KisPaintingAssistant* assistant) { d->assistants.removeOne(assistant); Q_ASSERT(!d->assistants.contains(assistant)); } bool KisPaintingAssistantHandle::containsAssistant(KisPaintingAssistant* assistant) const { return d->assistants.contains(assistant); } void KisPaintingAssistantHandle::mergeWith(KisPaintingAssistantHandleSP handle) { if(this->handleType()== HandleType::NORMAL || handle.data()->handleType()== HandleType::SIDE) { return; } Q_FOREACH (KisPaintingAssistant* assistant, handle->d->assistants) { if (!assistant->handles().contains(this)) { assistant->replaceHandle(handle, this); } } } void KisPaintingAssistantHandle::uncache() { Q_FOREACH (KisPaintingAssistant* assistant, d->assistants) { assistant->uncache(); } } struct KisPaintingAssistant::Private { Private(); explicit Private(const Private &rhs); KisPaintingAssistantHandleSP reuseOrCreateHandle(QMap &handleMap, KisPaintingAssistantHandleSP origHandle, KisPaintingAssistant *q, bool registerAssistant = true); QList handles, sideHandles; KisPaintingAssistantHandleSP topLeft, bottomLeft, topRight, bottomRight, topMiddle, bottomMiddle, rightMiddle, leftMiddle; // share everything except handles between the clones struct SharedData { QString id; QString name; bool isSnappingActive; bool outlineVisible; KisCanvas2* m_canvas = 0; QPixmapCache::Key cached; QRect cachedRect; // relative to boundingRect().topLeft() struct TranslationInvariantTransform { qreal m11, m12, m21, m22; TranslationInvariantTransform() { } TranslationInvariantTransform(const QTransform& t) : m11(t.m11()), m12(t.m12()), m21(t.m21()), m22(t.m22()) { } bool operator==(const TranslationInvariantTransform& b) { return m11 == b.m11 && m12 == b.m12 && m21 == b.m21 && m22 == b.m22; } } cachedTransform; QColor assistantGlobalColorCache = QColor(Qt::red); // color to paint with if a custom color is not set bool useCustomColor = false; QColor assistantCustomColor = KisConfig(true).defaultAssistantsColor(); }; QSharedPointer s; }; KisPaintingAssistant::Private::Private() : s(new SharedData) { } KisPaintingAssistant::Private::Private(const Private &rhs) : s(rhs.s) { } KisPaintingAssistantHandleSP KisPaintingAssistant::Private::reuseOrCreateHandle(QMap &handleMap, KisPaintingAssistantHandleSP origHandle, KisPaintingAssistant *q, bool registerAssistant) { KisPaintingAssistantHandleSP mappedHandle = handleMap.value(origHandle); if (!mappedHandle) { if (origHandle) { dbgUI << "handle not found in the map, creating a new one..."; mappedHandle = KisPaintingAssistantHandleSP(new KisPaintingAssistantHandle(*origHandle)); dbgUI << "done"; mappedHandle->setType(origHandle->handleType()); handleMap.insert(origHandle, mappedHandle); } else { dbgUI << "orig handle is null, not doing anything"; mappedHandle = KisPaintingAssistantHandleSP(); } } if (mappedHandle && registerAssistant) { mappedHandle->registerAssistant(q); } return mappedHandle; } bool KisPaintingAssistant::useCustomColor() { return d->s->useCustomColor; } void KisPaintingAssistant::setUseCustomColor(bool useCustomColor) { d->s->useCustomColor = useCustomColor; } void KisPaintingAssistant::setAssistantCustomColor(QColor color) { d->s->assistantCustomColor = color; } QColor KisPaintingAssistant::assistantCustomColor() { return d->s->assistantCustomColor; } void KisPaintingAssistant::setAssistantGlobalColorCache(const QColor &color) { d->s->assistantGlobalColorCache = color; } QColor KisPaintingAssistant::effectiveAssistantColor() const { return d->s->useCustomColor ? d->s->assistantCustomColor : d->s->assistantGlobalColorCache; } KisPaintingAssistant::KisPaintingAssistant(const QString& id, const QString& name) : d(new Private) { d->s->id = id; d->s->name = name; d->s->isSnappingActive = true; d->s->outlineVisible = true; } KisPaintingAssistant::KisPaintingAssistant(const KisPaintingAssistant &rhs, QMap &handleMap) : d(new Private(*(rhs.d))) { dbgUI << "creating handles..."; Q_FOREACH (const KisPaintingAssistantHandleSP origHandle, rhs.d->handles) { d->handles << d->reuseOrCreateHandle(handleMap, origHandle, this); } Q_FOREACH (const KisPaintingAssistantHandleSP origHandle, rhs.d->sideHandles) { d->sideHandles << d->reuseOrCreateHandle(handleMap, origHandle, this); } #define _REUSE_H(name) d->name = d->reuseOrCreateHandle(handleMap, rhs.d->name, this, /* registerAssistant = */ false) _REUSE_H(topLeft); _REUSE_H(bottomLeft); _REUSE_H(topRight); _REUSE_H(bottomRight); _REUSE_H(topMiddle); _REUSE_H(bottomMiddle); _REUSE_H(rightMiddle); _REUSE_H(leftMiddle); #undef _REUSE_H dbgUI << "done"; } bool KisPaintingAssistant::isSnappingActive() const { return d->s->isSnappingActive; } void KisPaintingAssistant::setSnappingActive(bool set) { d->s->isSnappingActive = set; } void KisPaintingAssistant::drawPath(QPainter& painter, const QPainterPath &path, bool isSnappingOn) { QColor paintingColor = effectiveAssistantColor(); if (!isSnappingOn) { paintingColor.setAlpha(0.2 * paintingColor.alpha()); } painter.save(); QPen pen_a(paintingColor, 2); pen_a.setCosmetic(true); painter.setPen(pen_a); painter.drawPath(path); painter.restore(); } void KisPaintingAssistant::drawPreview(QPainter& painter, const QPainterPath &path) { painter.save(); QPen pen_a(effectiveAssistantColor(), 1); pen_a.setStyle(Qt::SolidLine); pen_a.setCosmetic(true); painter.setPen(pen_a); painter.drawPath(path); painter.restore(); } void KisPaintingAssistant::initHandles(QList _handles) { Q_ASSERT(d->handles.isEmpty()); d->handles = _handles; Q_FOREACH (KisPaintingAssistantHandleSP handle, _handles) { handle->registerAssistant(this); } } KisPaintingAssistant::~KisPaintingAssistant() { Q_FOREACH (KisPaintingAssistantHandleSP handle, d->handles) { handle->unregisterAssistant(this); } if(!d->sideHandles.isEmpty()) { Q_FOREACH (KisPaintingAssistantHandleSP handle, d->sideHandles) { handle->unregisterAssistant(this); } } delete d; } const QString& KisPaintingAssistant::id() const { return d->s->id; } const QString& KisPaintingAssistant::name() const { return d->s->name; } void KisPaintingAssistant::replaceHandle(KisPaintingAssistantHandleSP _handle, KisPaintingAssistantHandleSP _with) { Q_ASSERT(d->handles.contains(_handle)); d->handles.replace(d->handles.indexOf(_handle), _with); Q_ASSERT(!d->handles.contains(_handle)); _handle->unregisterAssistant(this); _with->registerAssistant(this); } void KisPaintingAssistant::addHandle(KisPaintingAssistantHandleSP handle, HandleType type) { Q_ASSERT(!d->handles.contains(handle)); if (HandleType::SIDE == type) { d->sideHandles.append(handle); } else { d->handles.append(handle); } handle->registerAssistant(this); handle.data()->setType(type); } -QPointF KisPaintingAssistant::viewportConstrainedEditorWidgetPosition(const KisCoordinatesConverter* converter, const QSize editorWidgetSize) +QPointF KisPaintingAssistant::viewportConstrainedEditorPosition(const KisCoordinatesConverter* converter, const QSize editorSize) { - QPointF editorPos = editorWidgetPosition(); - QPointF viewportPos = converter->documentToViewportTransform().map(editorPos); - - int xOffset = qMin(0, converter->documentOffset().x()); - int yOffset = qMin(0, converter->documentOffset().y()); - - QSizeF canvasWidgetSize = converter->getCanvasWidgetSize(); + QPointF editorDocumentPos = getEditorPosition(); + QPointF editorWidgetPos = converter->documentToWidgetTransform().map(editorDocumentPos); + QSizeF canvasSize = converter->getCanvasWidgetSize(); const int padding = 16; - viewportPos.rx() = qBound(qreal(0.f) + xOffset, - viewportPos.x(), - (canvasWidgetSize.width() - (editorWidgetSize.width() + padding)) + xOffset); - viewportPos.ry() = qBound(qreal(0.f) + yOffset, - viewportPos.y(), - (canvasWidgetSize.height() - (editorWidgetSize.height() + padding)) + yOffset); + editorWidgetPos.rx() = qBound(0.0, + editorWidgetPos.x(), + canvasSize.width() - (editorSize.width() + padding)); + editorWidgetPos.ry() = qBound(0.0, + editorWidgetPos.y(), + canvasSize.height() - (editorSize.height() + padding)); - return converter->documentToViewportTransform().inverted().map(viewportPos); + return converter->widgetToDocument(editorWidgetPos); } void KisPaintingAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool useCache, KisCanvas2* canvas, bool assistantVisible, bool previewVisible) { Q_UNUSED(updateRect); Q_UNUSED(previewVisible); findPerspectiveAssistantHandleLocation(); if (!useCache) { gc.save(); drawCache(gc, converter, assistantVisible); gc.restore(); return; } const QRect bound = boundingRect(); if (bound.isEmpty()) { return; } const QTransform transform = converter->documentToWidgetTransform(); const QRect widgetBound = transform.mapRect(bound); const QRect paintRect = transform.mapRect(bound).intersected(gc.viewport()); if (paintRect.isEmpty()) return; QPixmap cached; bool found = QPixmapCache::find(d->s->cached, &cached); if (!(found && d->s->cachedTransform == transform && d->s->cachedRect.translated(widgetBound.topLeft()).contains(paintRect))) { const QRect cacheRect = gc.viewport().adjusted(-100, -100, 100, 100).intersected(widgetBound); Q_ASSERT(!cacheRect.isEmpty()); if (cached.isNull() || cached.size() != cacheRect.size()) { cached = QPixmap(cacheRect.size()); } cached.fill(Qt::transparent); QPainter painter(&cached); painter.setRenderHint(QPainter::Antialiasing); painter.setWindow(cacheRect); drawCache(painter, converter, assistantVisible); painter.end(); d->s->cachedTransform = transform; d->s->cachedRect = cacheRect.translated(-widgetBound.topLeft()); d->s->cached = QPixmapCache::insert(cached); } gc.drawPixmap(paintRect, cached, paintRect.translated(-widgetBound.topLeft() - d->s->cachedRect.topLeft())); if (canvas) { d->s->m_canvas = canvas; } } void KisPaintingAssistant::uncache() { d->s->cached = QPixmapCache::Key(); } QRect KisPaintingAssistant::boundingRect() const { QRectF r; Q_FOREACH (KisPaintingAssistantHandleSP h, handles()) { r = r.united(QRectF(*h, QSizeF(1,1))); } return r.adjusted(-2, -2, 2, 2).toAlignedRect(); } bool KisPaintingAssistant::isAssistantComplete() const { return true; } QByteArray KisPaintingAssistant::saveXml(QMap &handleMap) { QByteArray data; QXmlStreamWriter xml(&data); xml.writeStartDocument(); xml.writeStartElement("assistant"); xml.writeAttribute("type",d->s->id); xml.writeAttribute("active", QString::number(d->s->isSnappingActive)); xml.writeAttribute("useCustomColor", QString::number(d->s->useCustomColor)); xml.writeAttribute("customColor", KisDomUtils::qColorToQString(d->s->assistantCustomColor)); saveCustomXml(&xml); // if any specific assistants have custom XML data to save to // write individual handle data xml.writeStartElement("handles"); Q_FOREACH (const KisPaintingAssistantHandleSP handle, d->handles) { int id = handleMap.size(); if (!handleMap.contains(handle)){ handleMap.insert(handle, id); } id = handleMap.value(handle); xml.writeStartElement("handle"); xml.writeAttribute("id", QString::number(id)); xml.writeAttribute("x", QString::number(double(handle->x()), 'f', 3)); xml.writeAttribute("y", QString::number(double(handle->y()), 'f', 3)); xml.writeEndElement(); } xml.writeEndElement(); xml.writeEndElement(); xml.writeEndDocument(); return data; } void KisPaintingAssistant::saveCustomXml(QXmlStreamWriter* xml) { Q_UNUSED(xml); } void KisPaintingAssistant::loadXml(KoStore* store, QMap &handleMap, QString path) { int id = 0; double x = 0.0, y = 0.0; store->open(path); QByteArray data = store->read(store->size()); QXmlStreamReader xml(data); while (!xml.atEnd()) { switch (xml.readNext()) { case QXmlStreamReader::StartElement: if (xml.name() == "assistant") { QStringRef active = xml.attributes().value("active"); setSnappingActive( (active != "0") ); // load custom shared assistant properties if ( xml.attributes().hasAttribute("useCustomColor")) { QStringRef useCustomColor = xml.attributes().value("useCustomColor"); bool usingColor = false; if (useCustomColor.toString() == "1") { usingColor = true; } setUseCustomColor(usingColor); } if ( xml.attributes().hasAttribute("customColor")) { QStringRef customColor = xml.attributes().value("customColor"); setAssistantCustomColor( KisDomUtils::qStringToQColor(customColor.toString()) ); } } loadCustomXml(&xml); if (xml.name() == "handle") { QString strId = xml.attributes().value("id").toString(), strX = xml.attributes().value("x").toString(), strY = xml.attributes().value("y").toString(); if (!strId.isEmpty() && !strX.isEmpty() && !strY.isEmpty()) { id = strId.toInt(); x = strX.toDouble(); y = strY.toDouble(); if (!handleMap.contains(id)) { handleMap.insert(id, new KisPaintingAssistantHandle(x, y)); } } addHandle(handleMap.value(id), HandleType::NORMAL); } break; default: break; } } store->close(); } bool KisPaintingAssistant::loadCustomXml(QXmlStreamReader* xml) { Q_UNUSED(xml); return true; } void KisPaintingAssistant::saveXmlList(QDomDocument& doc, QDomElement& assistantsElement,int count) { if (d->s->id == "ellipse"){ QDomElement assistantElement = doc.createElement("assistant"); assistantElement.setAttribute("type", "ellipse"); assistantElement.setAttribute("filename", QString("ellipse%1.assistant").arg(count)); assistantsElement.appendChild(assistantElement); } else if (d->s->id == "spline"){ QDomElement assistantElement = doc.createElement("assistant"); assistantElement.setAttribute("type", "spline"); assistantElement.setAttribute("filename", QString("spline%1.assistant").arg(count)); assistantsElement.appendChild(assistantElement); } else if (d->s->id == "perspective"){ QDomElement assistantElement = doc.createElement("assistant"); assistantElement.setAttribute("type", "perspective"); assistantElement.setAttribute("filename", QString("perspective%1.assistant").arg(count)); assistantsElement.appendChild(assistantElement); } else if (d->s->id == "vanishing point"){ QDomElement assistantElement = doc.createElement("assistant"); assistantElement.setAttribute("type", "vanishing point"); assistantElement.setAttribute("filename", QString("vanishing point%1.assistant").arg(count)); assistantsElement.appendChild(assistantElement); } else if (d->s->id == "infinite ruler"){ QDomElement assistantElement = doc.createElement("assistant"); assistantElement.setAttribute("type", "infinite ruler"); assistantElement.setAttribute("filename", QString("infinite ruler%1.assistant").arg(count)); assistantsElement.appendChild(assistantElement); } else if (d->s->id == "parallel ruler"){ QDomElement assistantElement = doc.createElement("assistant"); assistantElement.setAttribute("type", "parallel ruler"); assistantElement.setAttribute("filename", QString("parallel ruler%1.assistant").arg(count)); assistantsElement.appendChild(assistantElement); } else if (d->s->id == "concentric ellipse"){ QDomElement assistantElement = doc.createElement("assistant"); assistantElement.setAttribute("type", "concentric ellipse"); assistantElement.setAttribute("filename", QString("concentric ellipse%1.assistant").arg(count)); assistantsElement.appendChild(assistantElement); } else if (d->s->id == "fisheye-point"){ QDomElement assistantElement = doc.createElement("assistant"); assistantElement.setAttribute("type", "fisheye-point"); assistantElement.setAttribute("filename", QString("fisheye-point%1.assistant").arg(count)); assistantsElement.appendChild(assistantElement); } else if (d->s->id == "ruler"){ QDomElement assistantElement = doc.createElement("assistant"); assistantElement.setAttribute("type", "ruler"); assistantElement.setAttribute("filename", QString("ruler%1.assistant").arg(count)); assistantsElement.appendChild(assistantElement); } } void KisPaintingAssistant::findPerspectiveAssistantHandleLocation() { QList hHandlesList; QList vHandlesList; uint vHole = 0,hHole = 0; KisPaintingAssistantHandleSP oppHandle; if (d->handles.size() == 4 && d->s->id == "perspective") { //get the handle opposite to the first handle oppHandle = oppHandleOne(); //Sorting handles into two list, X sorted and Y sorted into hHandlesList and vHandlesList respectively. Q_FOREACH (const KisPaintingAssistantHandleSP handle,d->handles) { hHandlesList.append(handle); hHole = hHandlesList.size() - 1; vHandlesList.append(handle); vHole = vHandlesList.size() - 1; /* sort handles on the basis of X-coordinate */ while(hHole > 0 && hHandlesList.at(hHole -1).data()->x() > handle.data()->x()) { hHandlesList.swap(hHole-1, hHole); hHole = hHole - 1; } /* sort handles on the basis of Y-coordinate */ while(vHole > 0 && vHandlesList.at(vHole -1).data()->y() > handle.data()->y()) { vHandlesList.swap(vHole-1, vHole); vHole = vHole - 1; } } /* give the handles their respective positions */ if(vHandlesList.at(0).data()->x() > vHandlesList.at(1).data()->x()) { d->topLeft = vHandlesList.at(1); d->topRight= vHandlesList.at(0); } else { d->topLeft = vHandlesList.at(0); d->topRight = vHandlesList.at(1); } if(vHandlesList.at(2).data()->x() > vHandlesList.at(3).data()->x()) { d->bottomLeft = vHandlesList.at(3); d->bottomRight = vHandlesList.at(2); } else { d->bottomLeft= vHandlesList.at(2); d->bottomRight = vHandlesList.at(3); } /* find if the handles that should be opposite are actually oppositely positioned */ if (( (d->topLeft == d->handles.at(0).data() && d->bottomRight == oppHandle) || (d->topLeft == oppHandle && d->bottomRight == d->handles.at(0).data()) || (d->topRight == d->handles.at(0).data() && d->bottomLeft == oppHandle) || (d->topRight == oppHandle && d->bottomLeft == d->handles.at(0).data()) ) ) {} else { if(hHandlesList.at(0).data()->y() > hHandlesList.at(1).data()->y()) { d->topLeft = hHandlesList.at(1); d->bottomLeft= hHandlesList.at(0); } else { d->topLeft = hHandlesList.at(0); d->bottomLeft = hHandlesList.at(1); } if(hHandlesList.at(2).data()->y() > hHandlesList.at(3).data()->y()) { d->topRight = hHandlesList.at(3); d->bottomRight = hHandlesList.at(2); } else { d->topRight= hHandlesList.at(2); d->bottomRight = hHandlesList.at(3); } } /* Setting the middle handles as needed */ if(!d->bottomMiddle && !d->topMiddle && !d->leftMiddle && !d->rightMiddle) { d->bottomMiddle = new KisPaintingAssistantHandle((d->bottomLeft.data()->x() + d->bottomRight.data()->x())*0.5, (d->bottomLeft.data()->y() + d->bottomRight.data()->y())*0.5); d->topMiddle = new KisPaintingAssistantHandle((d->topLeft.data()->x() + d->topRight.data()->x())*0.5, (d->topLeft.data()->y() + d->topRight.data()->y())*0.5); d->rightMiddle= new KisPaintingAssistantHandle((d->topRight.data()->x() + d->bottomRight.data()->x())*0.5, (d->topRight.data()->y() + d->bottomRight.data()->y())*0.5); d->leftMiddle= new KisPaintingAssistantHandle((d->bottomLeft.data()->x() + d->topLeft.data()->x())*0.5, (d->bottomLeft.data()->y() + d->topLeft.data()->y())*0.5); addHandle(d->rightMiddle.data(), HandleType::SIDE); addHandle(d->leftMiddle.data(), HandleType::SIDE); addHandle(d->bottomMiddle.data(), HandleType::SIDE); addHandle(d->topMiddle.data(), HandleType::SIDE); } else { d->bottomMiddle.data()->operator =(QPointF((d->bottomLeft.data()->x() + d->bottomRight.data()->x())*0.5, (d->bottomLeft.data()->y() + d->bottomRight.data()->y())*0.5)); d->topMiddle.data()->operator =(QPointF((d->topLeft.data()->x() + d->topRight.data()->x())*0.5, (d->topLeft.data()->y() + d->topRight.data()->y())*0.5)); d->rightMiddle.data()->operator =(QPointF((d->topRight.data()->x() + d->bottomRight.data()->x())*0.5, (d->topRight.data()->y() + d->bottomRight.data()->y())*0.5)); d->leftMiddle.data()->operator =(QPointF((d->bottomLeft.data()->x() + d->topLeft.data()->x())*0.5, (d->bottomLeft.data()->y() + d->topLeft.data()->y())*0.5)); } } } KisPaintingAssistantHandleSP KisPaintingAssistant::oppHandleOne() { QPointF intersection(0,0); 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) && (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)) { return d->handles.at(1); } 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) && (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)) { return d->handles.at(2); } else { return d->handles.at(3); } } KisPaintingAssistantHandleSP KisPaintingAssistant::topLeft() { return d->topLeft; } const KisPaintingAssistantHandleSP KisPaintingAssistant::topLeft() const { return d->topLeft; } KisPaintingAssistantHandleSP KisPaintingAssistant::bottomLeft() { return d->bottomLeft; } const KisPaintingAssistantHandleSP KisPaintingAssistant::bottomLeft() const { return d->bottomLeft; } KisPaintingAssistantHandleSP KisPaintingAssistant::topRight() { return d->topRight; } const KisPaintingAssistantHandleSP KisPaintingAssistant::topRight() const { return d->topRight; } KisPaintingAssistantHandleSP KisPaintingAssistant::bottomRight() { return d->bottomRight; } const KisPaintingAssistantHandleSP KisPaintingAssistant::bottomRight() const { return d->bottomRight; } KisPaintingAssistantHandleSP KisPaintingAssistant::topMiddle() { return d->topMiddle; } const KisPaintingAssistantHandleSP KisPaintingAssistant::topMiddle() const { return d->topMiddle; } KisPaintingAssistantHandleSP KisPaintingAssistant::bottomMiddle() { return d->bottomMiddle; } const KisPaintingAssistantHandleSP KisPaintingAssistant::bottomMiddle() const { return d->bottomMiddle; } KisPaintingAssistantHandleSP KisPaintingAssistant::rightMiddle() { return d->rightMiddle; } const KisPaintingAssistantHandleSP KisPaintingAssistant::rightMiddle() const { return d->rightMiddle; } KisPaintingAssistantHandleSP KisPaintingAssistant::leftMiddle() { return d->leftMiddle; } const KisPaintingAssistantHandleSP KisPaintingAssistant::leftMiddle() const { return d->leftMiddle; } const QList& KisPaintingAssistant::handles() const { return d->handles; } QList KisPaintingAssistant::handles() { return d->handles; } const QList& KisPaintingAssistant::sideHandles() const { return d->sideHandles; } QList KisPaintingAssistant::sideHandles() { return d->sideHandles; } bool KisPaintingAssistant::areTwoPointsClose(const QPointF& pointOne, const QPointF& pointTwo) { int m_handleSize = 16; QRectF handlerect(pointTwo - QPointF(m_handleSize * 0.5, m_handleSize * 0.5), QSizeF(m_handleSize, m_handleSize)); return handlerect.contains(pointOne); } KisPaintingAssistantHandleSP KisPaintingAssistant::closestCornerHandleFromPoint(QPointF point) { if (!d->s->m_canvas) { return 0; } if (areTwoPointsClose(point, pixelToView(topLeft()->toPoint()))) { return topLeft(); } else if (areTwoPointsClose(point, pixelToView(topRight()->toPoint()))) { return topRight(); } else if (areTwoPointsClose(point, pixelToView(bottomLeft()->toPoint()))) { return bottomLeft(); } else if (areTwoPointsClose(point, pixelToView(bottomRight()->toPoint()))) { return bottomRight(); } return 0; } QPointF KisPaintingAssistant::pixelToView(const QPoint pixelCoords) const { QPointF documentCoord = d->s->m_canvas->image()->pixelToDocument(pixelCoords); return d->s->m_canvas->viewConverter()->documentToView(documentCoord); } double KisPaintingAssistant::norm2(const QPointF& p) { return p.x() * p.x() + p.y() * p.y(); } QList KisPaintingAssistant::cloneAssistantList(const QList &list) { QMap handleMap; QList clonedList; for (auto i = list.begin(); i != list.end(); ++i) { clonedList << (*i)->clone(handleMap); } return clonedList; } /* * KisPaintingAssistantFactory classes */ KisPaintingAssistantFactory::KisPaintingAssistantFactory() { } KisPaintingAssistantFactory::~KisPaintingAssistantFactory() { } KisPaintingAssistantFactoryRegistry::KisPaintingAssistantFactoryRegistry() { } KisPaintingAssistantFactoryRegistry::~KisPaintingAssistantFactoryRegistry() { Q_FOREACH (const QString &id, keys()) { delete get(id); } dbgRegistry << "deleting KisPaintingAssistantFactoryRegistry "; } KisPaintingAssistantFactoryRegistry* KisPaintingAssistantFactoryRegistry::instance() { return s_instance; } diff --git a/libs/ui/kis_painting_assistant.h b/libs/ui/kis_painting_assistant.h index f938451524..cd8e54b291 100644 --- a/libs/ui/kis_painting_assistant.h +++ b/libs/ui/kis_painting_assistant.h @@ -1,238 +1,238 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2017 Scott Petrovic * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_PAINTING_ASSISTANT_H_ #define _KIS_PAINTING_ASSISTANT_H_ #include #include #include #include #include #include #include #include #include #include #include class QPainter; class QRect; class QRectF; class KoStore; class KisCoordinatesConverter; class KisCanvas2; class QDomDocument; class QDomElement; #include #include class KisPaintingAssistantHandle; typedef KisSharedPtr KisPaintingAssistantHandleSP; class KisPaintingAssistant; class QPainterPath; enum HandleType { NORMAL, SIDE, CORNER, VANISHING_POINT, ANCHOR }; /** * Represent an handle of the assistant, used to edit the parameters * of an assistants. Handles can be shared between assistants. */ class KRITAUI_EXPORT KisPaintingAssistantHandle : public QPointF, public KisShared { friend class KisPaintingAssistant; public: KisPaintingAssistantHandle(double x, double y); explicit KisPaintingAssistantHandle(QPointF p); KisPaintingAssistantHandle(const KisPaintingAssistantHandle&); ~KisPaintingAssistantHandle(); void mergeWith(KisPaintingAssistantHandleSP); void uncache(); KisPaintingAssistantHandle& operator=(const QPointF&); void setType(char type); char handleType() const; private: void registerAssistant(KisPaintingAssistant*); void unregisterAssistant(KisPaintingAssistant*); bool containsAssistant(KisPaintingAssistant*) const; private: struct Private; Private* const d; }; /** * A KisPaintingAssistant is an object that assist the drawing on the canvas. * With this class you can implement virtual equivalent to ruler or compas. */ class KRITAUI_EXPORT KisPaintingAssistant { public: KisPaintingAssistant(const QString& id, const QString& name); virtual ~KisPaintingAssistant(); virtual KisPaintingAssistantSP clone(QMap &handleMap) const = 0; const QString& id() const; const QString& name() const; bool isSnappingActive() const; void setSnappingActive(bool set); /** * Adjust the position given in parameter. * @param point the coordinates in point in the document reference * @param strokeBegin the coordinates of the beginning of the stroke */ virtual QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) = 0; virtual void endStroke() { } - virtual QPointF editorWidgetPosition() const = 0; + virtual QPointF getEditorPosition() const = 0; // Returns editor widget position in document-space coordinates. virtual int numHandles() const = 0; void replaceHandle(KisPaintingAssistantHandleSP _handle, KisPaintingAssistantHandleSP _with); void addHandle(KisPaintingAssistantHandleSP handle, HandleType type); - QPointF viewportConstrainedEditorWidgetPosition(const KisCoordinatesConverter* converter, const QSize editorWidgetSize); + QPointF viewportConstrainedEditorPosition(const KisCoordinatesConverter* converter, const QSize editorSize); QColor effectiveAssistantColor() const; bool useCustomColor(); void setUseCustomColor(bool useCustomColor); void setAssistantCustomColor(QColor color); QColor assistantCustomColor(); void setAssistantGlobalColorCache(const QColor &color); virtual void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter, bool cached = true,KisCanvas2 *canvas=0, bool assistantVisible=true, bool previewVisible=true); void uncache(); const QList& handles() const; QList handles(); const QList& sideHandles() const; QList sideHandles(); QByteArray saveXml( QMap &handleMap); virtual void saveCustomXml(QXmlStreamWriter* xml); //in case specific assistants have custom properties (like vanishing point) void loadXml(KoStore *store, QMap &handleMap, QString path); virtual bool loadCustomXml(QXmlStreamReader* xml); void saveXmlList(QDomDocument& doc, QDomElement& ssistantsElement, int count); void findPerspectiveAssistantHandleLocation(); KisPaintingAssistantHandleSP oppHandleOne(); /** * Get the topLeft, bottomLeft, topRight and BottomRight corners of the assistant * Some assistants like the perspective grid have custom logic built around certain handles */ const KisPaintingAssistantHandleSP topLeft() const; KisPaintingAssistantHandleSP topLeft(); const KisPaintingAssistantHandleSP topRight() const; KisPaintingAssistantHandleSP topRight(); const KisPaintingAssistantHandleSP bottomLeft() const; KisPaintingAssistantHandleSP bottomLeft(); const KisPaintingAssistantHandleSP bottomRight() const; KisPaintingAssistantHandleSP bottomRight(); const KisPaintingAssistantHandleSP topMiddle() const; KisPaintingAssistantHandleSP topMiddle(); const KisPaintingAssistantHandleSP rightMiddle() const; KisPaintingAssistantHandleSP rightMiddle(); const KisPaintingAssistantHandleSP leftMiddle() const; KisPaintingAssistantHandleSP leftMiddle(); const KisPaintingAssistantHandleSP bottomMiddle() const; KisPaintingAssistantHandleSP bottomMiddle(); // calculates whether a point is near one of the corner points of the assistant // returns: a corner point from the perspective assistant if the given node is close // only called once in code when calculating the perspective assistant KisPaintingAssistantHandleSP closestCornerHandleFromPoint(QPointF point); // determines if two points are close to each other // only used by the nodeNearPoint function (perspective grid assistant). bool areTwoPointsClose(const QPointF& pointOne, const QPointF& pointTwo); /// determines if the assistant has enough handles to be considered created /// new assistants get in a "creation" phase where they are currently being made on the canvas /// it will return false if we are in the middle of creating the assistant. virtual bool isAssistantComplete() const; public: /** * This will render the final output. The drawCache does rendering most of the time so be sure to check that */ void drawPath(QPainter& painter, const QPainterPath& path, bool drawActive=true); void drawPreview(QPainter& painter, const QPainterPath& path); static double norm2(const QPointF& p); protected: explicit KisPaintingAssistant(const KisPaintingAssistant &rhs, QMap &handleMap); virtual QRect boundingRect() const; /// performance layer where the graphics can be drawn from a cache instead of generated every render update virtual void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) = 0; void initHandles(QList _handles); QList m_handles; QPointF pixelToView(const QPoint pixelCoords) const; public: /// clones the list of assistants /// the originally shared handles will still be shared /// the cloned assistants do not share any handle with the original assistants static QList cloneAssistantList(const QList &list); private: struct Private; Private* const d; }; /** * Allow to create a painting assistant. */ class KRITAUI_EXPORT KisPaintingAssistantFactory { public: KisPaintingAssistantFactory(); virtual ~KisPaintingAssistantFactory(); virtual QString id() const = 0; virtual QString name() const = 0; virtual KisPaintingAssistant* createPaintingAssistant() const = 0; }; class KRITAUI_EXPORT KisPaintingAssistantFactoryRegistry : public KoGenericRegistry { public: KisPaintingAssistantFactoryRegistry(); ~KisPaintingAssistantFactoryRegistry() override; static KisPaintingAssistantFactoryRegistry* instance(); }; #endif diff --git a/libs/ui/kis_painting_assistants_decoration.cpp b/libs/ui/kis_painting_assistants_decoration.cpp index d1a4b4a159..11838e6079 100644 --- a/libs/ui/kis_painting_assistants_decoration.cpp +++ b/libs/ui/kis_painting_assistants_decoration.cpp @@ -1,498 +1,498 @@ /* * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2017 Scott Petrovic * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_painting_assistants_decoration.h" #include #include #include #include #include #include #include "kis_debug.h" #include "KisDocument.h" #include "kis_canvas2.h" #include "kis_icon_utils.h" #include "KisViewManager.h" #include #include struct KisPaintingAssistantsDecoration::Private { Private() : assistantVisible(false) , outlineVisible(false) , snapOnlyOneAssistant(true) , firstAssistant(0) , aFirstStroke(false) , m_handleSize(14) {} bool assistantVisible; bool outlineVisible; bool snapOnlyOneAssistant; KisPaintingAssistantSP firstAssistant; KisPaintingAssistantSP selectedAssistant; bool aFirstStroke; bool m_isEditingAssistants = false; bool m_outlineVisible = false; int m_handleSize; // size of editor handles on assistants // move, visibility, delete icons for each assistant. These only display while the assistant tool is active // these icons will be covered by the kis_paintint_assistant_decoration with things like the perspective assistant AssistantEditorData toolData; QPixmap m_iconDelete = KisIconUtils::loadIcon("dialog-cancel").pixmap(toolData.deleteIconSize, toolData.deleteIconSize); QPixmap m_iconSnapOn = KisIconUtils::loadIcon("visible").pixmap(toolData.snapIconSize, toolData.snapIconSize); QPixmap m_iconSnapOff = KisIconUtils::loadIcon("novisible").pixmap(toolData.snapIconSize, toolData.snapIconSize); QPixmap m_iconMove = KisIconUtils::loadIcon("transform-move").pixmap(toolData.moveIconSize, toolData.moveIconSize); KisCanvas2 * m_canvas = 0; }; KisPaintingAssistantsDecoration::KisPaintingAssistantsDecoration(QPointer parent) : KisCanvasDecoration("paintingAssistantsDecoration", parent), d(new Private) { setAssistantVisible(true); setOutlineVisible(true); setPriority(95); d->snapOnlyOneAssistant = true; //turn on by default. } KisPaintingAssistantsDecoration::~KisPaintingAssistantsDecoration() { delete d; } void KisPaintingAssistantsDecoration::addAssistant(KisPaintingAssistantSP assistant) { QList assistants = view()->document()->assistants(); if (assistants.contains(assistant)) return; assistants.append(assistant); assistant->setAssistantGlobalColorCache(view()->document()->assistantsGlobalColor()); view()->document()->setAssistants(assistants); setVisible(!assistants.isEmpty()); emit assistantChanged(); } void KisPaintingAssistantsDecoration::removeAssistant(KisPaintingAssistantSP assistant) { QList assistants = view()->document()->assistants(); KIS_ASSERT_RECOVER_NOOP(assistants.contains(assistant)); if (assistants.removeAll(assistant)) { view()->document()->setAssistants(assistants); setVisible(!assistants.isEmpty()); emit assistantChanged(); } } void KisPaintingAssistantsDecoration::removeAll() { QList assistants = view()->document()->assistants(); assistants.clear(); view()->document()->setAssistants(assistants); setVisible(!assistants.isEmpty()); emit assistantChanged(); } void KisPaintingAssistantsDecoration::setAssistants(const QList &assistants) { Q_FOREACH (KisPaintingAssistantSP assistant, assistants) { assistant->setAssistantGlobalColorCache(view()->document()->assistantsGlobalColor()); } view()->document()->setAssistants(assistants); setVisible(!assistants.isEmpty()); emit assistantChanged(); } QPointF KisPaintingAssistantsDecoration::adjustPosition(const QPointF& point, const QPointF& strokeBegin) { if (assistants().empty()) { return point; } if (assistants().count() == 1) { if(assistants().first()->isSnappingActive() == true){ QPointF newpoint = assistants().first()->adjustPosition(point, strokeBegin); // check for NaN if (newpoint.x() != newpoint.x()) return point; return newpoint; } } QPointF best = point; double distance = DBL_MAX; //the following tries to find the closest point to stroke-begin. It checks all assistants for the closest point// if(!d->snapOnlyOneAssistant){ Q_FOREACH (KisPaintingAssistantSP assistant, assistants()) { if(assistant->isSnappingActive() == true){//this checks if the assistant in question has it's snapping boolean turned on// QPointF pt = assistant->adjustPosition(point, strokeBegin); if (pt.x() != pt.x()) continue; double dist = qAbs(pt.x() - point.x()) + qAbs(pt.y() - point.y()); if (dist < distance) { best = pt; distance = dist; } } } } else if (d->aFirstStroke==false) { Q_FOREACH (KisPaintingAssistantSP assistant, assistants()) { if(assistant->isSnappingActive() == true){//this checks if the assistant in question has it's snapping boolean turned on// QPointF pt = assistant->adjustPosition(point, strokeBegin); if (pt.x() != pt.x()) continue; double dist = qAbs(pt.x() - point.x()) + qAbs(pt.y() - point.y()); if (dist < distance) { best = pt; distance = dist; d->firstAssistant = assistant; } } } } else if(d->firstAssistant) { //make sure there's a first assistant to begin with.// QPointF newpoint = d->firstAssistant->adjustPosition(point, strokeBegin); // BUGFIX: 402535 // assistants might return (NaN,NaN), must always check for that if (newpoint.x() == newpoint.x()) { // not a NaN best = newpoint; } } else { d->aFirstStroke=false; } //this is here to be compatible with the movement in the perspective tool. qreal dx = point.x() - strokeBegin.x(), dy = point.y() - strokeBegin.y(); if (dx * dx + dy * dy >= 4.0) { // allow some movement before snapping d->aFirstStroke=true; } return best; } void KisPaintingAssistantsDecoration::endStroke() { d->aFirstStroke = false; Q_FOREACH (KisPaintingAssistantSP assistant, assistants()) { assistant->endStroke(); } } void KisPaintingAssistantsDecoration::drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter,KisCanvas2* canvas) { if(assistants().length() == 0) { return; // no assistants to worry about, ok to exit } if (!canvas) { dbgFile<<"canvas does not exist in painting assistant decoration, you may have passed arguments incorrectly:"<m_canvas = canvas; } // the preview functionality for assistants. do not show while editing if (d->m_isEditingAssistants) { d->m_outlineVisible = false; } else { d->m_outlineVisible = outlineVisibility(); } Q_FOREACH (KisPaintingAssistantSP assistant, assistants()) { assistant->drawAssistant(gc, updateRect, converter, true, canvas, assistantVisibility(), d->m_outlineVisible); if (isEditingAssistants()) { drawHandles(assistant, gc, converter); } } // draw editor controls on top of all assistant lines (why this code is last) if (isEditingAssistants()) { Q_FOREACH (KisPaintingAssistantSP assistant, assistants()) { drawEditorWidget(assistant, gc, converter); } } } void KisPaintingAssistantsDecoration::drawHandles(KisPaintingAssistantSP assistant, QPainter& gc, const KisCoordinatesConverter *converter) { QTransform initialTransform = converter->documentToWidgetTransform(); QColor colorToPaint = assistant->effectiveAssistantColor(); Q_FOREACH (const KisPaintingAssistantHandleSP handle, assistant->handles()) { QPointF transformedHandle = initialTransform.map(*handle); QRectF ellipse(transformedHandle - QPointF(handleSize() * 0.5, handleSize() * 0.5), QSizeF(handleSize(), handleSize())); QPainterPath path; path.addEllipse(ellipse); gc.save(); gc.setPen(Qt::NoPen); gc.setBrush(colorToPaint); gc.drawPath(path); gc.restore(); } // some assistants have side handles like the vanishing point assistant Q_FOREACH (const KisPaintingAssistantHandleSP handle, assistant->sideHandles()) { QPointF transformedHandle = initialTransform.map(*handle); QRectF ellipse(transformedHandle - QPointF(handleSize() * 0.5, handleSize() * 0.5), QSizeF(handleSize(), handleSize())); QPainterPath path; path.addEllipse(ellipse); gc.save(); gc.setPen(Qt::NoPen); gc.setBrush(colorToPaint); gc.drawPath(path); gc.restore(); } } int KisPaintingAssistantsDecoration::handleSize() { return d->m_handleSize; } void KisPaintingAssistantsDecoration::setHandleSize(int handleSize) { d->m_handleSize = handleSize; } QList KisPaintingAssistantsDecoration::handles() { QList hs; Q_FOREACH (KisPaintingAssistantSP assistant, assistants()) { Q_FOREACH (const KisPaintingAssistantHandleSP handle, assistant->handles()) { if (!hs.contains(handle)) { hs.push_back(handle); } } Q_FOREACH (const KisPaintingAssistantHandleSP handle, assistant->sideHandles()) { if (!hs.contains(handle)) { hs.push_back(handle); } } } return hs; } QList KisPaintingAssistantsDecoration::assistants() const { QList assistants = view()->document()->assistants(); return assistants; } KisPaintingAssistantSP KisPaintingAssistantsDecoration::selectedAssistant() { return d->selectedAssistant; } void KisPaintingAssistantsDecoration::setSelectedAssistant(KisPaintingAssistantSP assistant) { d->selectedAssistant = assistant; emit selectedAssistantChanged(); } void KisPaintingAssistantsDecoration::deselectAssistant() { d->selectedAssistant.clear(); } void KisPaintingAssistantsDecoration::setAssistantVisible(bool set) { d->assistantVisible=set; } void KisPaintingAssistantsDecoration::setOutlineVisible(bool set) { d->outlineVisible=set; } void KisPaintingAssistantsDecoration::setOnlyOneAssistantSnap(bool assistant) { d->snapOnlyOneAssistant = assistant; } bool KisPaintingAssistantsDecoration::assistantVisibility() { return d->assistantVisible; } bool KisPaintingAssistantsDecoration::outlineVisibility() { return d->outlineVisible; } void KisPaintingAssistantsDecoration::uncache() { Q_FOREACH (KisPaintingAssistantSP assistant, assistants()) { assistant->uncache(); } } void KisPaintingAssistantsDecoration::toggleAssistantVisible() { setAssistantVisible(!assistantVisibility()); uncache(); } void KisPaintingAssistantsDecoration::toggleOutlineVisible() { setOutlineVisible(!outlineVisibility()); } QColor KisPaintingAssistantsDecoration::globalAssistantsColor() { return view()->document()->assistantsGlobalColor(); } void KisPaintingAssistantsDecoration::setGlobalAssistantsColor(QColor color) { // view()->document() is referenced multiple times in this class // it is used to later store things in the KRA file when saving. view()->document()->setAssistantsGlobalColor(color); Q_FOREACH (KisPaintingAssistantSP assistant, assistants()) { assistant->setAssistantGlobalColorCache(color); } uncache(); } void KisPaintingAssistantsDecoration::activateAssistantsEditor() { setVisible(true); // this turns on the decorations in general. we leave it on at this point d->m_isEditingAssistants = true; uncache(); // updates visuals when editing } void KisPaintingAssistantsDecoration::deactivateAssistantsEditor() { if (!d->m_canvas) { return; } d->m_isEditingAssistants = false; // some elements are hidden when we aren't editing uncache(); // updates visuals when not editing } bool KisPaintingAssistantsDecoration::isEditingAssistants() { return d->m_isEditingAssistants; } QPointF KisPaintingAssistantsDecoration::snapToGuide(KoPointerEvent *e, const QPointF &offset, bool useModifiers) { if (!d->m_canvas || !d->m_canvas->currentImage()) { return e->point; } KoSnapGuide *snapGuide = d->m_canvas->snapGuide(); QPointF pos = snapGuide->snap(e->point, offset, useModifiers ? e->modifiers() : Qt::NoModifier); return pos; } QPointF KisPaintingAssistantsDecoration::snapToGuide(const QPointF& pt, const QPointF &offset) { if (!d->m_canvas) { return pt; } KoSnapGuide *snapGuide = d->m_canvas->snapGuide(); QPointF pos = snapGuide->snap(pt, offset, Qt::NoModifier); return pos; } /* * functions only used internally in this class * we potentially could make some of these inline to speed up performance */ void KisPaintingAssistantsDecoration::drawEditorWidget(KisPaintingAssistantSP assistant, QPainter& gc, const KisCoordinatesConverter *converter) { if (!assistant->isAssistantComplete()) { return; } AssistantEditorData toolData; // shared const data for positioning and sizing QTransform initialTransform = converter->documentToWidgetTransform(); - QPointF actionsPosition = initialTransform.map(assistant->viewportConstrainedEditorWidgetPosition(converter, toolData.boundingSize)); + QPointF actionsPosition = initialTransform.map(assistant->viewportConstrainedEditorPosition(converter, toolData.boundingSize)); QPointF iconMovePosition(actionsPosition + toolData.moveIconPosition); QPointF iconSnapPosition(actionsPosition + toolData.snapIconPosition); QPointF iconDeletePosition(actionsPosition + toolData.deleteIconPosition); // Background container for helpers QBrush backgroundColor = d->m_canvas->viewManager()->mainWindow()->palette().window(); QPointF actionsBGRectangle(actionsPosition + QPointF(10, 10)); gc.setRenderHint(QPainter::Antialiasing); QPainterPath bgPath; bgPath.addRoundedRect(QRectF(actionsBGRectangle.x(), actionsBGRectangle.y(), toolData.boundingSize.width(), toolData.boundingSize.height()), 6, 6); QPen stroke(QColor(60, 60, 60, 80), 2); // if the assistant is selected, make outline stroke fatter and use theme's highlight color // for better visual feedback if (selectedAssistant()) { // there might not be a selected assistant, so do not seg fault - if (assistant->editorWidgetPosition() == selectedAssistant()->editorWidgetPosition()) { + if (assistant->getEditorPosition() == selectedAssistant()->getEditorPosition()) { stroke.setWidth(4); stroke.setColor(qApp->palette().color(QPalette::Highlight)); } } // draw the final result gc.setPen(stroke); gc.fillPath(bgPath, backgroundColor); gc.drawPath(bgPath); // Move Assistant Tool helper gc.drawPixmap(iconMovePosition, d->m_iconMove); // active toggle if (assistant->isSnappingActive() == true) { gc.drawPixmap(iconSnapPosition, d->m_iconSnapOn); } else { gc.drawPixmap(iconSnapPosition, d->m_iconSnapOff); } gc.drawPixmap(iconDeletePosition, d->m_iconDelete); } diff --git a/plugins/assistants/Assistants/ConcentricEllipseAssistant.cc b/plugins/assistants/Assistants/ConcentricEllipseAssistant.cc index 2033a44a35..8c14b969bf 100644 --- a/plugins/assistants/Assistants/ConcentricEllipseAssistant.cc +++ b/plugins/assistants/Assistants/ConcentricEllipseAssistant.cc @@ -1,208 +1,208 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ConcentricEllipseAssistant.h" #include #include "kis_debug.h" #include #include #include #include #include #include #include ConcentricEllipseAssistant::ConcentricEllipseAssistant() : KisPaintingAssistant("concentric ellipse", i18n("Concentric Ellipse assistant")) { } KisPaintingAssistantSP ConcentricEllipseAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new ConcentricEllipseAssistant(*this, handleMap)); } ConcentricEllipseAssistant::ConcentricEllipseAssistant(const ConcentricEllipseAssistant &rhs, QMap &handleMap) : KisPaintingAssistant(rhs, handleMap) , m_ellipse(rhs.m_ellipse) , m_extraEllipse(rhs.m_extraEllipse) { } QPointF ConcentricEllipseAssistant::project(const QPointF& pt, const QPointF& strokeBegin) const { Q_ASSERT(isAssistantComplete()); m_ellipse.set(*handles()[0], *handles()[1], *handles()[2]); qreal dx = pt.x() - strokeBegin.x(), dy = pt.y() - strokeBegin.y(); if (dx * dx + dy * dy < 4.0) { // allow some movement before snapping return strokeBegin; } //calculate ratio QPointF initial = m_ellipse.project(strokeBegin); QPointF center = m_ellipse.boundingRect().center(); qreal Ratio = QLineF(center, strokeBegin).length() /QLineF(center, initial).length(); //calculate the points of the extrapolated ellipse. QLineF extrapolate0 = QLineF(center, *handles()[0]); extrapolate0.setLength(extrapolate0.length()*Ratio); QLineF extrapolate1 = QLineF(center, *handles()[1]); extrapolate1.setLength(extrapolate1.length()*Ratio); QLineF extrapolate2 = QLineF(center, *handles()[2]); extrapolate2.setLength(extrapolate2.length()*Ratio); //set the extrapolation ellipse. m_extraEllipse.set(extrapolate0.p2(), extrapolate1.p2(), extrapolate2.p2()); return m_extraEllipse.project(pt); } QPointF ConcentricEllipseAssistant::adjustPosition(const QPointF& pt, const QPointF& strokeBegin) { return project(pt, strokeBegin); } void ConcentricEllipseAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible) { gc.save(); gc.resetTransform(); QPointF mousePos; if (canvas){ //simplest, cheapest way to get the mouse-position// mousePos= canvas->canvasWidget()->mapFromGlobal(QCursor::pos()); } else { //...of course, you need to have access to a canvas-widget for that.// mousePos = QCursor::pos();//this'll give an offset// dbgFile<<"canvas does not exist in the ellipse assistant, you may have passed arguments incorrectly:"<documentToWidgetTransform(); if (isSnappingActive() && previewVisible == true){ if (isAssistantComplete()){ if (m_ellipse.set(*handles()[0], *handles()[1], *handles()[2])) { QPointF initial = m_ellipse.project(initialTransform.inverted().map(mousePos)); QPointF center = m_ellipse.boundingRect().center(); qreal Ratio = QLineF(center, initialTransform.inverted().map(mousePos)).length() /QLineF(center, initial).length(); //line from center to handle 1 * difference. //set handle1 translated to // valid ellipse gc.setTransform(initialTransform); gc.setTransform(m_ellipse.getInverse(), true); QPainterPath path; // Draw the ellipse path.addEllipse(QPointF(0, 0), m_ellipse.semiMajor()*Ratio, m_ellipse.semiMinor()*Ratio); drawPreview(gc, path); } } } gc.restore(); KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); } void ConcentricEllipseAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (assistantVisible == false || handles().size() < 2) { // 2 points means a line, so we can continue after 1 point return; } QTransform initialTransform = converter->documentToWidgetTransform(); if (handles().size() == 2) { // just draw the axis gc.setTransform(initialTransform); QPainterPath path; path.moveTo(*handles()[0]); path.lineTo(*handles()[1]); drawPath(gc, path, isSnappingActive()); return; } if (m_ellipse.set(*handles()[0], *handles()[1], *handles()[2])) { // valid ellipse gc.setTransform(initialTransform); gc.setTransform(m_ellipse.getInverse(), true); QPainterPath path; path.moveTo(QPointF(-m_ellipse.semiMajor(), 0)); path.lineTo(QPointF(m_ellipse.semiMajor(), 0)); path.moveTo(QPointF(0, -m_ellipse.semiMinor())); path.lineTo(QPointF(0, m_ellipse.semiMinor())); // Draw the ellipse path.addEllipse(QPointF(0, 0), m_ellipse.semiMajor(), m_ellipse.semiMinor()); drawPath(gc, path, isSnappingActive()); } } QRect ConcentricEllipseAssistant::boundingRect() const { if (!isAssistantComplete()) { return KisPaintingAssistant::boundingRect(); } if (m_ellipse.set(*handles()[0], *handles()[1], *handles()[2])) { return m_ellipse.boundingRect().adjusted(-2, -2, 2, 2).toAlignedRect(); } else { return QRect(); } } -QPointF ConcentricEllipseAssistant::editorWidgetPosition() const +QPointF ConcentricEllipseAssistant::getEditorPosition() const { return (*handles()[0] + *handles()[1]) * 0.5; } bool ConcentricEllipseAssistant::isAssistantComplete() const { return handles().size() >= 3; } ConcentricEllipseAssistantFactory::ConcentricEllipseAssistantFactory() { } ConcentricEllipseAssistantFactory::~ConcentricEllipseAssistantFactory() { } QString ConcentricEllipseAssistantFactory::id() const { return "concentric ellipse"; } QString ConcentricEllipseAssistantFactory::name() const { return i18n("Concentric Ellipse"); } KisPaintingAssistant* ConcentricEllipseAssistantFactory::createPaintingAssistant() const { return new ConcentricEllipseAssistant; } diff --git a/plugins/assistants/Assistants/ConcentricEllipseAssistant.h b/plugins/assistants/Assistants/ConcentricEllipseAssistant.h index 625b0f2122..3d972ef98c 100644 --- a/plugins/assistants/Assistants/ConcentricEllipseAssistant.h +++ b/plugins/assistants/Assistants/ConcentricEllipseAssistant.h @@ -1,60 +1,60 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _CONCENTRIC_ELLIPSE_ASSISTANT_H_ #define _CONCENTRIC_ELLIPSE_ASSISTANT_H_ #include "kis_painting_assistant.h" #include "Ellipse.h" #include #include class ConcentricEllipseAssistant : public KisPaintingAssistant { public: ConcentricEllipseAssistant(); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; - QPointF editorWidgetPosition() const override; + QPointF getEditorPosition() const override; int numHandles() const override { return 3; } bool isAssistantComplete() const override; protected: QRect boundingRect() const override; void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible=true, bool previewVisible=true) override; void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt, const QPointF& strokeBegin) const; mutable Ellipse m_ellipse; mutable Ellipse m_extraEllipse; explicit ConcentricEllipseAssistant(const ConcentricEllipseAssistant &rhs, QMap &handleMap); }; class ConcentricEllipseAssistantFactory : public KisPaintingAssistantFactory { public: ConcentricEllipseAssistantFactory(); ~ConcentricEllipseAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/EllipseAssistant.cc b/plugins/assistants/Assistants/EllipseAssistant.cc index 78ae7452da..8b3b16c64c 100644 --- a/plugins/assistants/Assistants/EllipseAssistant.cc +++ b/plugins/assistants/Assistants/EllipseAssistant.cc @@ -1,181 +1,181 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "EllipseAssistant.h" #include #include "kis_debug.h" #include #include #include #include #include #include EllipseAssistant::EllipseAssistant() : KisPaintingAssistant("ellipse", i18n("Ellipse assistant")) { } EllipseAssistant::EllipseAssistant(const EllipseAssistant &rhs, QMap &handleMap) : KisPaintingAssistant(rhs, handleMap) , e(rhs.e) { } KisPaintingAssistantSP EllipseAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new EllipseAssistant(*this, handleMap)); } QPointF EllipseAssistant::project(const QPointF& pt) const { Q_ASSERT(isAssistantComplete()); e.set(*handles()[0], *handles()[1], *handles()[2]); return e.project(pt); } QPointF EllipseAssistant::adjustPosition(const QPointF& pt, const QPointF& /*strokeBegin*/) { return project(pt); } void EllipseAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible) { gc.save(); gc.resetTransform(); QPoint mousePos; if (canvas){ //simplest, cheapest way to get the mouse-position// mousePos= canvas->canvasWidget()->mapFromGlobal(QCursor::pos()); } else { //...of course, you need to have access to a canvas-widget for that.// mousePos = QCursor::pos();//this'll give an offset// dbgFile<<"canvas does not exist in the ellipse assistant, you may have passed arguments incorrectly:"<documentToWidgetTransform(); if (isSnappingActive() && boundingRect().contains(initialTransform.inverted().map(mousePos), false) && previewVisible==true){ if (isAssistantComplete()){ if (e.set(*handles()[0], *handles()[1], *handles()[2])) { // valid ellipse gc.setTransform(initialTransform); gc.setTransform(e.getInverse(), true); QPainterPath path; //path.moveTo(QPointF(-e.semiMajor(), 0)); path.lineTo(QPointF(e.semiMajor(), 0)); //path.moveTo(QPointF(0, -e.semiMinor())); path.lineTo(QPointF(0, e.semiMinor())); // Draw the ellipse path.addEllipse(QPointF(0, 0), e.semiMajor(), e.semiMinor()); drawPreview(gc, path); } } } gc.restore(); KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); } void EllipseAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (assistantVisible == false || handles().size() < 2){ return; } QTransform initialTransform = converter->documentToWidgetTransform(); if (handles().size() == 2) { // just draw the axis gc.setTransform(initialTransform); QPainterPath path; path.moveTo(*handles()[0]); path.lineTo(*handles()[1]); drawPath(gc, path, isSnappingActive()); return; } if (e.set(*handles()[0], *handles()[1], *handles()[2])) { // valid ellipse gc.setTransform(initialTransform); gc.setTransform(e.getInverse(), true); QPainterPath path; path.moveTo(QPointF(-e.semiMajor(), 0)); path.lineTo(QPointF(e.semiMajor(), 0)); path.moveTo(QPointF(0, -e.semiMinor())); path.lineTo(QPointF(0, e.semiMinor())); // Draw the ellipse path.addEllipse(QPointF(0, 0), e.semiMajor(), e.semiMinor()); drawPath(gc, path, isSnappingActive()); } } QRect EllipseAssistant::boundingRect() const { if (!isAssistantComplete()) { return KisPaintingAssistant::boundingRect(); } if (e.set(*handles()[0], *handles()[1], *handles()[2])) { return e.boundingRect().adjusted(-2, -2, 2, 2).toAlignedRect(); } else { return QRect(); } } -QPointF EllipseAssistant::editorWidgetPosition() const +QPointF EllipseAssistant::getEditorPosition() const { return (*handles()[0] + *handles()[1]) * 0.5; } bool EllipseAssistant::isAssistantComplete() const { return handles().size() >= 3; } EllipseAssistantFactory::EllipseAssistantFactory() { } EllipseAssistantFactory::~EllipseAssistantFactory() { } QString EllipseAssistantFactory::id() const { return "ellipse"; } QString EllipseAssistantFactory::name() const { return i18n("Ellipse"); } KisPaintingAssistant* EllipseAssistantFactory::createPaintingAssistant() const { return new EllipseAssistant; } diff --git a/plugins/assistants/Assistants/EllipseAssistant.h b/plugins/assistants/Assistants/EllipseAssistant.h index 43f969509d..b66b206244 100644 --- a/plugins/assistants/Assistants/EllipseAssistant.h +++ b/plugins/assistants/Assistants/EllipseAssistant.h @@ -1,58 +1,58 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _ELLIPSE_ASSISTANT_H_ #define _ELLIPSE_ASSISTANT_H_ #include "kis_painting_assistant.h" #include "Ellipse.h" #include class EllipseAssistant : public KisPaintingAssistant { public: EllipseAssistant(); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; - QPointF editorWidgetPosition() const override; + QPointF getEditorPosition() const override; int numHandles() const override { return 3; } bool isAssistantComplete() const override; protected: QRect boundingRect() const override; void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible=true, bool previewVisible=true) override; void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt) const; explicit EllipseAssistant(const EllipseAssistant &rhs, QMap &handleMap); mutable Ellipse e; }; class EllipseAssistantFactory : public KisPaintingAssistantFactory { public: EllipseAssistantFactory(); ~EllipseAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/FisheyePointAssistant.cc b/plugins/assistants/Assistants/FisheyePointAssistant.cc index 4f5d72b223..8338f4b4ab 100644 --- a/plugins/assistants/Assistants/FisheyePointAssistant.cc +++ b/plugins/assistants/Assistants/FisheyePointAssistant.cc @@ -1,238 +1,238 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2014 Wolthera van Hövell tot Westerflier * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "FisheyePointAssistant.h" #include "kis_debug.h" #include #include #include #include #include #include #include #include #include FisheyePointAssistant::FisheyePointAssistant() : KisPaintingAssistant("fisheye-point", i18n("Fish Eye Point assistant")) { } FisheyePointAssistant::FisheyePointAssistant(const FisheyePointAssistant &rhs, QMap &handleMap) : KisPaintingAssistant(rhs, handleMap) , e(rhs.e) , extraE(rhs.extraE) { } KisPaintingAssistantSP FisheyePointAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new FisheyePointAssistant(*this, handleMap)); } QPointF FisheyePointAssistant::project(const QPointF& pt, const QPointF& strokeBegin) { const static QPointF nullPoint(std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); Q_ASSERT(isAssistantComplete()); e.set(*handles()[0], *handles()[1], *handles()[2]); qreal dx = pt.x() - strokeBegin.x(), dy = pt.y() - strokeBegin.y(); if (dx * dx + dy * dy < 4.0) { // allow some movement before snapping return strokeBegin; } //set the extrapolation ellipse. if (e.set(*handles()[0], *handles()[1], *handles()[2])){ QLineF radius(*handles()[1], *handles()[0]); radius.setAngle(fmod(radius.angle()+180.0,360.0)); QLineF radius2(*handles()[0], *handles()[1]); radius2.setAngle(fmod(radius2.angle()+180.0,360.0)); if ( extraE.set(*handles()[0], *handles()[1],strokeBegin ) ) { return extraE.project(pt); } else if (extraE.set(radius.p1(), radius.p2(),strokeBegin)) { return extraE.project(pt); } else if (extraE.set(radius2.p1(), radius2.p2(),strokeBegin)){ return extraE.project(pt); } } return nullPoint; } QPointF FisheyePointAssistant::adjustPosition(const QPointF& pt, const QPointF& strokeBegin) { return project(pt, strokeBegin); } void FisheyePointAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible) { gc.save(); gc.resetTransform(); QPointF mousePos(0,0); if (canvas){ //simplest, cheapest way to get the mouse-position// mousePos= canvas->canvasWidget()->mapFromGlobal(QCursor::pos()); } else { //...of course, you need to have access to a canvas-widget for that.// mousePos = QCursor::pos();//this'll give an offset// dbgFile<<"canvas does not exist in ruler, you may have passed arguments incorrectly:"<documentToWidgetTransform(); if (isSnappingActive() && previewVisible == true ) { if (isAssistantComplete()){ if (e.set(*handles()[0], *handles()[1], *handles()[2])) { if (extraE.set(*handles()[0], *handles()[1], initialTransform.inverted().map(mousePos))){ gc.setTransform(initialTransform); gc.setTransform(e.getInverse(), true); QPainterPath path; // Draw the ellipse path.addEllipse(QPointF(0, 0), extraE.semiMajor(), extraE.semiMinor()); drawPreview(gc, path); } QLineF radius(*handles()[1], *handles()[0]); radius.setAngle(fmod(radius.angle()+180.0,360.0)); if (extraE.set(radius.p1(), radius.p2(), initialTransform.inverted().map(mousePos))){ gc.setTransform(initialTransform); gc.setTransform(extraE.getInverse(), true); QPainterPath path; // Draw the ellipse path.addEllipse(QPointF(0, 0), extraE.semiMajor(), extraE.semiMinor()); drawPreview(gc, path); } QLineF radius2(*handles()[0], *handles()[1]); radius2.setAngle(fmod(radius2.angle()+180.0,360.0)); if (extraE.set(radius2.p1(), radius2.p2(), initialTransform.inverted().map(mousePos))){ gc.setTransform(initialTransform); gc.setTransform(extraE.getInverse(), true); QPainterPath path; // Draw the ellipse path.addEllipse(QPointF(0, 0), extraE.semiMajor(), extraE.semiMinor()); drawPreview(gc, path); } } } } gc.restore(); KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); } void FisheyePointAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (assistantVisible == false){ return; } QTransform initialTransform = converter->documentToWidgetTransform(); if (handles().size() == 2) { // just draw the axis gc.setTransform(initialTransform); QPainterPath path; path.moveTo(*handles()[0]); path.lineTo(*handles()[1]); drawPath(gc, path, isSnappingActive()); return; } if (e.set(*handles()[0], *handles()[1], *handles()[2])) { // valid ellipse gc.setTransform(initialTransform); gc.setTransform(e.getInverse(), true); QPainterPath path; //path.moveTo(QPointF(-e.semiMajor(), -e.semiMinor())); path.lineTo(QPointF(e.semiMajor(), -e.semiMinor())); path.moveTo(QPointF(-e.semiMajor(), -e.semiMinor())); path.lineTo(QPointF(-e.semiMajor(), e.semiMinor())); //path.moveTo(QPointF(-e.semiMajor(), e.semiMinor())); path.lineTo(QPointF(e.semiMajor(), e.semiMinor())); path.moveTo(QPointF(e.semiMajor(), -e.semiMinor())); path.lineTo(QPointF(e.semiMajor(), e.semiMinor())); path.moveTo(QPointF(-(e.semiMajor()*3), -e.semiMinor())); path.lineTo(QPointF(-(e.semiMajor()*3), e.semiMinor())); path.moveTo(QPointF((e.semiMajor()*3), -e.semiMinor())); path.lineTo(QPointF((e.semiMajor()*3), e.semiMinor())); path.moveTo(QPointF(-e.semiMajor(), 0)); path.lineTo(QPointF(e.semiMajor(), 0)); //path.moveTo(QPointF(0, -e.semiMinor())); path.lineTo(QPointF(0, e.semiMinor())); // Draw the ellipse path.addEllipse(QPointF(0, 0), e.semiMajor(), e.semiMinor()); drawPath(gc, path, isSnappingActive()); } } QRect FisheyePointAssistant::boundingRect() const { if (!isAssistantComplete()) { return KisPaintingAssistant::boundingRect(); } if (e.set(*handles()[0], *handles()[1], *handles()[2])) { return e.boundingRect().adjusted(-(e.semiMajor()*2), -2, (e.semiMajor()*2), 2).toAlignedRect(); } else { return QRect(); } } -QPointF FisheyePointAssistant::editorWidgetPosition() const +QPointF FisheyePointAssistant::getEditorPosition() const { return (*handles()[0] + *handles()[1]) * 0.5; } bool FisheyePointAssistant::isAssistantComplete() const { return handles().size() >= 3; } FisheyePointAssistantFactory::FisheyePointAssistantFactory() { } FisheyePointAssistantFactory::~FisheyePointAssistantFactory() { } QString FisheyePointAssistantFactory::id() const { return "fisheye-point"; } QString FisheyePointAssistantFactory::name() const { return i18n("Fish Eye Point"); } KisPaintingAssistant* FisheyePointAssistantFactory::createPaintingAssistant() const { return new FisheyePointAssistant; } diff --git a/plugins/assistants/Assistants/FisheyePointAssistant.h b/plugins/assistants/Assistants/FisheyePointAssistant.h index 7035a7e71d..db661db29f 100644 --- a/plugins/assistants/Assistants/FisheyePointAssistant.h +++ b/plugins/assistants/Assistants/FisheyePointAssistant.h @@ -1,66 +1,66 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2014 Wolthera van Hövell tot Westerflier * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _FISHEYEPOINT_ASSISTANT_H_ #define _FISHEYEPOINT_ASSISTANT_H_ #include "kis_painting_assistant.h" #include "Ellipse.h" #include #include #include #include //class FisheyePoint; class FisheyePointAssistant : public KisPaintingAssistant { public: FisheyePointAssistant(); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; //virtual void endStroke(); - QPointF editorWidgetPosition() const override; + QPointF getEditorPosition() const override; int numHandles() const override { return 3; } bool isAssistantComplete() const override; protected: QRect boundingRect() const override; void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached = true,KisCanvas2* canvas=0, bool assistantVisible=true, bool previewVisible=true) override; void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt, const QPointF& strokeBegin); explicit FisheyePointAssistant(const FisheyePointAssistant &rhs, QMap &handleMap); mutable Ellipse e; mutable Ellipse extraE; }; class FisheyePointAssistantFactory : public KisPaintingAssistantFactory { public: FisheyePointAssistantFactory(); ~FisheyePointAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/InfiniteRulerAssistant.cc b/plugins/assistants/Assistants/InfiniteRulerAssistant.cc index d629bd26df..4fe1907137 100644 --- a/plugins/assistants/Assistants/InfiniteRulerAssistant.cc +++ b/plugins/assistants/Assistants/InfiniteRulerAssistant.cc @@ -1,171 +1,171 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2014 Wolthera van Hövell tot Westerflier * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "InfiniteRulerAssistant.h" #include "kis_debug.h" #include #include #include #include #include #include #include #include InfiniteRulerAssistant::InfiniteRulerAssistant() : KisPaintingAssistant("infinite ruler", i18n("Infinite Ruler assistant")) { } InfiniteRulerAssistant::InfiniteRulerAssistant(const InfiniteRulerAssistant &rhs, QMap &handleMap) : KisPaintingAssistant(rhs, handleMap) { } KisPaintingAssistantSP InfiniteRulerAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new InfiniteRulerAssistant(*this, handleMap)); } QPointF InfiniteRulerAssistant::project(const QPointF& pt, const QPointF& strokeBegin) { Q_ASSERT(isAssistantComplete()); //code nicked from the perspective ruler. qreal dx = pt.x() - strokeBegin.x(), dy = pt.y() - strokeBegin.y(); if (dx * dx + dy * dy < 4.0) { // allow some movement before snapping return strokeBegin; } //dbgKrita<canvasWidget()->mapFromGlobal(QCursor::pos()); } else { //...of course, you need to have access to a canvas-widget for that.// mousePos = QCursor::pos();//this'll give an offset// dbgFile<<"canvas does not exist in ruler, you may have passed arguments incorrectly:"<documentToWidgetTransform(); QLineF snapLine= QLineF(initialTransform.map(*handles()[0]), initialTransform.map(*handles()[1])); QRect viewport= gc.viewport(); KisAlgebra2D::intersectLineRect(snapLine, viewport); QPainterPath path; path.moveTo(snapLine.p1()); path.lineTo(snapLine.p2()); drawPreview(gc, path);//and we draw the preview. } gc.restore(); KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); } void InfiniteRulerAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (assistantVisible == false || !isAssistantComplete()){ return; } QTransform initialTransform = converter->documentToWidgetTransform(); // Draw the line QPointF p1 = *handles()[0]; QPointF p2 = *handles()[1]; gc.setTransform(initialTransform); QPainterPath path; path.moveTo(p1); path.lineTo(p2); drawPath(gc, path, isSnappingActive()); } -QPointF InfiniteRulerAssistant::editorWidgetPosition() const +QPointF InfiniteRulerAssistant::getEditorPosition() const { return (*handles()[0]); } bool InfiniteRulerAssistant::isAssistantComplete() const { return handles().size() >= 2; } InfiniteRulerAssistantFactory::InfiniteRulerAssistantFactory() { } InfiniteRulerAssistantFactory::~InfiniteRulerAssistantFactory() { } QString InfiniteRulerAssistantFactory::id() const { return "infinite ruler"; } QString InfiniteRulerAssistantFactory::name() const { return i18n("Infinite Ruler"); } KisPaintingAssistant* InfiniteRulerAssistantFactory::createPaintingAssistant() const { return new InfiniteRulerAssistant; } diff --git a/plugins/assistants/Assistants/InfiniteRulerAssistant.h b/plugins/assistants/Assistants/InfiniteRulerAssistant.h index fbc10521d9..94384e2a0e 100644 --- a/plugins/assistants/Assistants/InfiniteRulerAssistant.h +++ b/plugins/assistants/Assistants/InfiniteRulerAssistant.h @@ -1,63 +1,63 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2014 Wolthera van Hövell tot Westerflier * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _INFINITERULER_ASSISTANT_H_ #define _INFINITERULER_ASSISTANT_H_ #include "kis_painting_assistant.h" #include #include #include #include /* Design: */ class InfiniteRuler; class InfiniteRulerAssistant : public KisPaintingAssistant { public: InfiniteRulerAssistant(); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; //virtual void endStroke(); - QPointF editorWidgetPosition() const override; + QPointF getEditorPosition() const override; int numHandles() const override { return 2; } bool isAssistantComplete() const override; protected: void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached = true,KisCanvas2* canvas=0, bool assistantVisible=true, bool previewVisible=true) override; void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt, const QPointF& strokeBegin); explicit InfiniteRulerAssistant(const InfiniteRulerAssistant &rhs, QMap &handleMap); }; class InfiniteRulerAssistantFactory : public KisPaintingAssistantFactory { public: InfiniteRulerAssistantFactory(); ~InfiniteRulerAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/ParallelRulerAssistant.cc b/plugins/assistants/Assistants/ParallelRulerAssistant.cc index f3c558f38c..2deb3d3ab2 100644 --- a/plugins/assistants/Assistants/ParallelRulerAssistant.cc +++ b/plugins/assistants/Assistants/ParallelRulerAssistant.cc @@ -1,178 +1,178 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2014 Wolthera van Hövell tot Westerflier * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ParallelRulerAssistant.h" #include "kis_debug.h" #include #include #include #include #include #include #include #include ParallelRulerAssistant::ParallelRulerAssistant() : KisPaintingAssistant("parallel ruler", i18n("Parallel Ruler assistant")) { } KisPaintingAssistantSP ParallelRulerAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new ParallelRulerAssistant(*this, handleMap)); } ParallelRulerAssistant::ParallelRulerAssistant(const ParallelRulerAssistant &rhs, QMap &handleMap) : KisPaintingAssistant(rhs, handleMap) { } QPointF ParallelRulerAssistant::project(const QPointF& pt, const QPointF& strokeBegin) { Q_ASSERT(isAssistantComplete()); //code nicked from the perspective ruler. qreal dx = pt.x() - strokeBegin.x(); qreal dy = pt.y() - strokeBegin.y(); if (dx * dx + dy * dy < 4.0) { return strokeBegin; // allow some movement before snapping } //dbgKrita<canvasWidget()->mapFromGlobal(QCursor::pos()); } else { //...of course, you need to have access to a canvas-widget for that.// mousePos = QCursor::pos();//this'll give an offset// dbgFile<<"canvas does not exist in ruler, you may have passed arguments incorrectly:"<documentToWidgetTransform(); QLineF snapLine= QLineF(initialTransform.map(*handles()[0]), initialTransform.map(*handles()[1])); QPointF translation = (initialTransform.map(*handles()[0])-mousePos)*-1.0; snapLine= snapLine.translated(translation); QRect viewport= gc.viewport(); KisAlgebra2D::intersectLineRect(snapLine, viewport); QPainterPath path; path.moveTo(snapLine.p1()); path.lineTo(snapLine.p2()); drawPreview(gc, path);//and we draw the preview. } gc.restore(); KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); } void ParallelRulerAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (assistantVisible == false || !isAssistantComplete()){ return; } QTransform initialTransform = converter->documentToWidgetTransform(); // Draw the line QPointF p1 = *handles()[0]; QPointF p2 = *handles()[1]; gc.setTransform(initialTransform); QPainterPath path; path.moveTo(p1); path.lineTo(p2); drawPath(gc, path, isSnappingActive()); } -QPointF ParallelRulerAssistant::editorWidgetPosition() const +QPointF ParallelRulerAssistant::getEditorPosition() const { return (*handles()[0] + *handles()[1]) * 0.5; } bool ParallelRulerAssistant::isAssistantComplete() const { return handles().size() >= 2; } ParallelRulerAssistantFactory::ParallelRulerAssistantFactory() { } ParallelRulerAssistantFactory::~ParallelRulerAssistantFactory() { } QString ParallelRulerAssistantFactory::id() const { return "parallel ruler"; } QString ParallelRulerAssistantFactory::name() const { return i18n("Parallel Ruler"); } KisPaintingAssistant* ParallelRulerAssistantFactory::createPaintingAssistant() const { return new ParallelRulerAssistant; } diff --git a/plugins/assistants/Assistants/ParallelRulerAssistant.h b/plugins/assistants/Assistants/ParallelRulerAssistant.h index b178269f8c..dec5f54c97 100644 --- a/plugins/assistants/Assistants/ParallelRulerAssistant.h +++ b/plugins/assistants/Assistants/ParallelRulerAssistant.h @@ -1,63 +1,63 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2014 Wolthera van Hövell tot Westerflier * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _PARALLELRULER_ASSISTANT_H_ #define _PARALLELRULER_ASSISTANT_H_ #include "kis_painting_assistant.h" #include #include #include #include /* Design: */ class ParallelRuler; class ParallelRulerAssistant : public KisPaintingAssistant { public: ParallelRulerAssistant(); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; //virtual void endStroke(); - QPointF editorWidgetPosition() const override; + QPointF getEditorPosition() const override; int numHandles() const override { return 2; } bool isAssistantComplete() const override; protected: void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached = true,KisCanvas2* canvas=0, bool assistantVisible=true, bool previewVisible=true) override; void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt, const QPointF& strokeBegin); explicit ParallelRulerAssistant(const ParallelRulerAssistant &rhs, QMap &handleMap); }; class ParallelRulerAssistantFactory : public KisPaintingAssistantFactory { public: ParallelRulerAssistantFactory(); ~ParallelRulerAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/PerspectiveAssistant.cc b/plugins/assistants/Assistants/PerspectiveAssistant.cc index 0bfdb9765e..46007b5e3c 100644 --- a/plugins/assistants/Assistants/PerspectiveAssistant.cc +++ b/plugins/assistants/Assistants/PerspectiveAssistant.cc @@ -1,481 +1,481 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "PerspectiveAssistant.h" #include "kis_debug.h" #include #include #include #include #include #include #include #include #include PerspectiveAssistant::PerspectiveAssistant(QObject *parent) : KisAbstractPerspectiveGrid(parent) , KisPaintingAssistant("perspective", i18n("Perspective assistant")) { } PerspectiveAssistant::PerspectiveAssistant(const PerspectiveAssistant &rhs, QMap &handleMap) : KisAbstractPerspectiveGrid(rhs.parent()) , KisPaintingAssistant(rhs, handleMap) , m_snapLine(rhs.m_snapLine) , m_cachedTransform(rhs.m_cachedTransform) , m_cachedPolygon(rhs.m_cachedPolygon) , m_cacheValid(rhs.m_cacheValid) { for (int i = 0; i < 4; ++i) { m_cachedPoints[i] = rhs.m_cachedPoints[i]; } } KisPaintingAssistantSP PerspectiveAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new PerspectiveAssistant(*this, handleMap)); } // squared distance from a point to a line inline qreal distsqr(const QPointF& pt, const QLineF& line) { // distance = |(p2 - p1) x (p1 - pt)| / |p2 - p1| // magnitude of (p2 - p1) x (p1 - pt) const qreal cross = (line.dx() * (line.y1() - pt.y()) - line.dy() * (line.x1() - pt.x())); return cross * cross / (line.dx() * line.dx() + line.dy() * line.dy()); } QPointF PerspectiveAssistant::project(const QPointF& pt, const QPointF& strokeBegin) { const static QPointF nullPoint(std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); Q_ASSERT(isAssistantComplete()); if (m_snapLine.isNull()) { QPolygonF poly; QTransform transform; if (!getTransform(poly, transform)) { return nullPoint; } if (!poly.containsPoint(strokeBegin, Qt::OddEvenFill)) { return nullPoint; // avoid problems with multiple assistants: only snap if starting in the grid } const qreal dx = pt.x() - strokeBegin.x(); const qreal dy = pt.y() - strokeBegin.y(); if (dx * dx + dy * dy < 4.0) { return strokeBegin; // allow some movement before snapping } // construct transformation bool invertible; const QTransform inverse = transform.inverted(&invertible); if (!invertible) { return nullPoint; // shouldn't happen } // figure out which direction to go const QPointF start = inverse.map(strokeBegin); const QLineF verticalLine = QLineF(strokeBegin, transform.map(start + QPointF(0, 1))); const QLineF horizontalLine = QLineF(strokeBegin, transform.map(start + QPointF(1, 0))); // determine whether the horizontal or vertical line is closer to the point m_snapLine = distsqr(pt, verticalLine) < distsqr(pt, horizontalLine) ? verticalLine : horizontalLine; } // snap to line const qreal dx = m_snapLine.dx(), dy = m_snapLine.dy(), dx2 = dx * dx, dy2 = dy * dy, invsqrlen = 1.0 / (dx2 + dy2); QPointF r(dx2 * pt.x() + dy2 * m_snapLine.x1() + dx * dy * (pt.y() - m_snapLine.y1()), dx2 * m_snapLine.y1() + dy2 * pt.y() + dx * dy * (pt.x() - m_snapLine.x1())); r *= invsqrlen; return r; } QPointF PerspectiveAssistant::adjustPosition(const QPointF& pt, const QPointF& strokeBegin) { return project(pt, strokeBegin); } void PerspectiveAssistant::endStroke() { m_snapLine = QLineF(); } bool PerspectiveAssistant::contains(const QPointF& pt) const { QPolygonF poly; if (!quad(poly)) return false; return poly.containsPoint(pt, Qt::OddEvenFill); } inline qreal lengthSquared(const QPointF& vector) { return vector.x() * vector.x() + vector.y() * vector.y(); } inline qreal localScale(const QTransform& transform, QPointF pt) { // const qreal epsilon = 1e-5, epsilonSquared = epsilon * epsilon; // qreal xSizeSquared = lengthSquared(transform.map(pt + QPointF(epsilon, 0.0)) - orig) / epsilonSquared; // qreal ySizeSquared = lengthSquared(transform.map(pt + QPointF(0.0, epsilon)) - orig) / epsilonSquared; // xSizeSquared /= lengthSquared(transform.map(QPointF(0.0, pt.y())) - transform.map(QPointF(1.0, pt.y()))); // ySizeSquared /= lengthSquared(transform.map(QPointF(pt.x(), 0.0)) - transform.map(QPointF(pt.x(), 1.0))); // when taking the limit epsilon->0: // xSizeSquared=((m23*y+m33)^2*(m23*y+m33+m13)^2)/(m23*y+m13*x+m33)^4 // ySizeSquared=((m23*y+m33)^2*(m23*y+m33+m13)^2)/(m23*y+m13*x+m33)^4 // xSize*ySize=(abs(m13*x+m33)*abs(m13*x+m33+m23)*abs(m23*y+m33)*abs(m23*y+m33+m13))/(m23*y+m13*x+m33)^4 const qreal x = transform.m13() * pt.x(), y = transform.m23() * pt.y(), a = x + transform.m33(), b = y + transform.m33(), c = x + y + transform.m33(), d = c * c; return fabs(a*(a + transform.m23())*b*(b + transform.m13()))/(d * d); } // returns the reciprocal of the maximum local scale at the points (0,0),(0,1),(1,0),(1,1) inline qreal inverseMaxLocalScale(const QTransform& transform) { const qreal a = fabs((transform.m33() + transform.m13()) * (transform.m33() + transform.m23())), b = fabs((transform.m33()) * (transform.m13() + transform.m33() + transform.m23())), d00 = transform.m33() * transform.m33(), d11 = (transform.m33() + transform.m23() + transform.m13())*(transform.m33() + transform.m23() + transform.m13()), s0011 = qMin(d00, d11) / a, d10 = (transform.m33() + transform.m13()) * (transform.m33() + transform.m13()), d01 = (transform.m33() + transform.m23()) * (transform.m33() + transform.m23()), s1001 = qMin(d10, d01) / b; return qMin(s0011, s1001); } qreal PerspectiveAssistant::distance(const QPointF& pt) const { QPolygonF poly; QTransform transform; if (!getTransform(poly, transform)) { return 1.0; } bool invertible; QTransform inverse = transform.inverted(&invertible); if (!invertible) { return 1.0; } if (inverse.m13() * pt.x() + inverse.m23() * pt.y() + inverse.m33() == 0.0) { return 0.0; // point at infinity } return localScale(transform, inverse.map(pt)) * inverseMaxLocalScale(transform); } // draw a vanishing point marker inline QPainterPath drawX(const QPointF& pt) { QPainterPath path; path.moveTo(QPointF(pt.x() - 5.0, pt.y() - 5.0)); path.lineTo(QPointF(pt.x() + 5.0, pt.y() + 5.0)); path.moveTo(QPointF(pt.x() - 5.0, pt.y() + 5.0)); path.lineTo(QPointF(pt.x() + 5.0, pt.y() - 5.0)); return path; } void PerspectiveAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible) { gc.save(); gc.resetTransform(); QTransform initialTransform = converter->documentToWidgetTransform(); //QTransform reverseTransform = converter->widgetToDocument(); QPolygonF poly; QTransform transform; // unused, but computed for caching purposes if (getTransform(poly, transform) && assistantVisible==true) { // draw vanishing points QPointF intersection(0, 0); if (fmod(QLineF(poly[0], poly[1]).angle(), 180.0)>=fmod(QLineF(poly[2], poly[3]).angle(), 180.0)+2.0 || fmod(QLineF(poly[0], poly[1]).angle(), 180.0)<=fmod(QLineF(poly[2], poly[3]).angle(), 180.0)-2.0) { if (QLineF(poly[0], poly[1]).intersect(QLineF(poly[2], poly[3]), &intersection) != QLineF::NoIntersection) { drawPath(gc, drawX(initialTransform.map(intersection))); } } if (fmod(QLineF(poly[1], poly[2]).angle(), 180.0)>=fmod(QLineF(poly[3], poly[0]).angle(), 180.0)+2.0 || fmod(QLineF(poly[1], poly[2]).angle(), 180.0)<=fmod(QLineF(poly[3], poly[0]).angle(), 180.0)-2.0){ if (QLineF(poly[1], poly[2]).intersect(QLineF(poly[3], poly[0]), &intersection) != QLineF::NoIntersection) { drawPath(gc, drawX(initialTransform.map(intersection))); } } } if (isSnappingActive() && getTransform(poly, transform) && previewVisible==true){ //find vanishing point, find mouse, draw line between both. QPainterPath path2; QPointF intersection(0, 0);//this is the position of the vanishing point. QPointF mousePos(0,0); QLineF snapLine; QRect viewport= gc.viewport(); QRect bounds; if (canvas){ //simplest, cheapest way to get the mouse-position mousePos= canvas->canvasWidget()->mapFromGlobal(QCursor::pos()); } else { //...of course, you need to have access to a canvas-widget for that. mousePos = QCursor::pos(); // this'll give an offset dbgFile<<"canvas does not exist, you may have passed arguments incorrectly:"<=fmod(QLineF(poly[2], poly[3]).angle(), 180.0)+2.0 || fmod(QLineF(poly[0], poly[1]).angle(), 180.0)<=fmod(QLineF(poly[2], poly[3]).angle(), 180.0)-2.0) { if (QLineF(poly[0], poly[1]).intersect(QLineF(poly[2], poly[3]), &intersection) != QLineF::NoIntersection) { intersectTransformed = initialTransform.map(intersection); snapLine = QLineF(intersectTransformed, mousePos); KisAlgebra2D::intersectLineRect(snapLine, viewport); bounds= QRect(snapLine.p1().toPoint(), snapLine.p2().toPoint()); QPainterPath path; if (bounds.contains(intersectTransformed.toPoint())){ path2.moveTo(intersectTransformed); path2.lineTo(snapLine.p1()); } else { path2.moveTo(snapLine.p1()); path2.lineTo(snapLine.p2()); } } } if (fmod(QLineF(poly[1], poly[2]).angle(), 180.0)>=fmod(QLineF(poly[3], poly[0]).angle(), 180.0)+2.0 || fmod(QLineF(poly[1], poly[2]).angle(), 180.0)<=fmod(QLineF(poly[3], poly[0]).angle(), 180.0)-2.0){ if (QLineF(poly[1], poly[2]).intersect(QLineF(poly[3], poly[0]), &intersection) != QLineF::NoIntersection) { intersectTransformed = initialTransform.map(intersection); snapLine = QLineF(intersectTransformed, mousePos); KisAlgebra2D::intersectLineRect(snapLine, viewport); bounds= QRect(snapLine.p1().toPoint(), snapLine.p2().toPoint()); QPainterPath path; if (bounds.contains(intersectTransformed.toPoint())){ path2.moveTo(intersectTransformed); path2.lineTo(snapLine.p1()); } else { path2.moveTo(snapLine.p1()); path2.lineTo(snapLine.p2()); } } } drawPreview(gc, path2); } } gc.restore(); KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached,canvas, assistantVisible, previewVisible); } void PerspectiveAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (assistantVisible == false) { return; } gc.setTransform(converter->documentToWidgetTransform()); QPolygonF poly; QTransform transform; if (!getTransform(poly, transform)) { // color red for an invalid transform, but not for an incomplete one if(isAssistantComplete()) { gc.setPen(QColor(255, 0, 0, 125)); gc.drawPolygon(poly); } else { QPainterPath path; path.addPolygon(poly); drawPath(gc, path, isSnappingActive()); } } else { gc.setPen(QColor(0, 0, 0, 125)); gc.setTransform(transform, true); QPainterPath path; for (int y = 0; y <= 8; ++y) { path.moveTo(QPointF(0.0, y * 0.125)); path.lineTo(QPointF(1.0, y * 0.125)); } for (int x = 0; x <= 8; ++x) { path.moveTo(QPointF(x * 0.125, 0.0)); path.lineTo(QPointF(x * 0.125, 1.0)); } drawPath(gc, path, isSnappingActive()); } } -QPointF PerspectiveAssistant::editorWidgetPosition() const +QPointF PerspectiveAssistant::getEditorPosition() const { QPointF centroid(0, 0); for (int i = 0; i < 4; ++i) { centroid += *handles()[i]; } return centroid * 0.25; } template int sign(T a) { return (a > 0) - (a < 0); } // perpendicular dot product inline qreal pdot(const QPointF& a, const QPointF& b) { return a.x() * b.y() - a.y() * b.x(); } bool PerspectiveAssistant::quad(QPolygonF& poly) const { for (int i = 0; i < handles().size(); ++i) { poly.push_back(*handles()[i]); } if (!isAssistantComplete()) { return false; } int sum = 0; int signs[4]; for (int i = 0; i < 4; ++i) { int j = (i == 3) ? 0 : (i + 1); int k = (j == 3) ? 0 : (j + 1); signs[i] = sign(pdot(poly[j] - poly[i], poly[k] - poly[j])); sum += signs[i]; } if (sum == 0) { // complex (crossed) for (int i = 0; i < 4; ++i) { int j = (i == 3) ? 0 : (i + 1); if (signs[i] * signs[j] == -1) { // opposite signs: uncross std::swap(poly[i], poly[j]); return true; } } // okay, maybe it's just a line return false; } else if (sum != 4 && sum != -4) { // concave, or a triangle if (sum == 2 || sum == -2) { // concave, let's return a triangle instead for (int i = 0; i < 4; ++i) { int j = (i == 3) ? 0 : (i + 1); if (signs[i] != sign(sum)) { // wrong sign: drop the inside node poly.remove(j); return false; } } } return false; } // convex return true; } bool PerspectiveAssistant::getTransform(QPolygonF& poly, QTransform& transform) const { if (m_cachedPolygon.size() != 0 && isAssistantComplete()) { for (int i = 0; i <= 4; ++i) { if (i == 4) { poly = m_cachedPolygon; transform = m_cachedTransform; return m_cacheValid; } if (m_cachedPoints[i] != *handles()[i]) break; } } m_cachedPolygon.clear(); m_cacheValid = false; if (!quad(poly)) { m_cachedPolygon = poly; return false; } if (!QTransform::squareToQuad(poly, transform)) { qWarning("Failed to create perspective mapping"); return false; } for (int i = 0; i < 4; ++i) { m_cachedPoints[i] = *handles()[i]; } m_cachedPolygon = poly; m_cachedTransform = transform; m_cacheValid = true; return true; } bool PerspectiveAssistant::isAssistantComplete() const { return handles().size() >= 4; // specify 4 corners to make assistant complete } PerspectiveAssistantFactory::PerspectiveAssistantFactory() { } PerspectiveAssistantFactory::~PerspectiveAssistantFactory() { } QString PerspectiveAssistantFactory::id() const { return "perspective"; } QString PerspectiveAssistantFactory::name() const { return i18n("Perspective"); } KisPaintingAssistant* PerspectiveAssistantFactory::createPaintingAssistant() const { return new PerspectiveAssistant; } diff --git a/plugins/assistants/Assistants/PerspectiveAssistant.h b/plugins/assistants/Assistants/PerspectiveAssistant.h index b8cbbcb110..7c5c79a13a 100644 --- a/plugins/assistants/Assistants/PerspectiveAssistant.h +++ b/plugins/assistants/Assistants/PerspectiveAssistant.h @@ -1,78 +1,78 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _PERSPECTIVE_ASSISTANT_H_ #define _PERSPECTIVE_ASSISTANT_H_ #include "kis_abstract_perspective_grid.h" #include "kis_painting_assistant.h" #include #include #include #include class PerspectiveAssistant : public KisAbstractPerspectiveGrid, public KisPaintingAssistant { Q_OBJECT public: PerspectiveAssistant(QObject * parent = 0); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; void endStroke() override; - QPointF editorWidgetPosition() const override; + QPointF getEditorPosition() const override; int numHandles() const override { return 4; } void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached = true,KisCanvas2* canvas=0, bool assistantVisible=true, bool previewVisible=true) override; bool contains(const QPointF& point) const override; qreal distance(const QPointF& point) const override; bool isAssistantComplete() const override; protected: void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt, const QPointF& strokeBegin); // creates the convex hull, returns false if it's not a quadrilateral bool quad(QPolygonF& out) const; // finds the transform from perspective coordinates (a unit square) to the document bool getTransform(QPolygonF& polyOut, QTransform& transformOut) const; explicit PerspectiveAssistant(const PerspectiveAssistant &rhs, QMap &handleMap); // which direction to snap to (in transformed coordinates) QLineF m_snapLine; // cached information mutable QTransform m_cachedTransform; mutable QPolygonF m_cachedPolygon; mutable QPointF m_cachedPoints[4]; mutable bool m_cacheValid; }; class PerspectiveAssistantFactory : public KisPaintingAssistantFactory { public: PerspectiveAssistantFactory(); ~PerspectiveAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/RulerAssistant.cc b/plugins/assistants/Assistants/RulerAssistant.cc index 5005e6804e..48cc15e6a2 100644 --- a/plugins/assistants/Assistants/RulerAssistant.cc +++ b/plugins/assistants/Assistants/RulerAssistant.cc @@ -1,177 +1,177 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "RulerAssistant.h" #include "kis_debug.h" #include #include #include #include #include #include #include RulerAssistant::RulerAssistant() : KisPaintingAssistant("ruler", i18n("Ruler assistant")) { } KisPaintingAssistantSP RulerAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new RulerAssistant(*this, handleMap)); } RulerAssistant::RulerAssistant(const RulerAssistant &rhs, QMap &handleMap) : KisPaintingAssistant(rhs, handleMap) { } QPointF RulerAssistant::project(const QPointF& pt) const { Q_ASSERT(isAssistantComplete()); QPointF pt1 = *handles()[0]; QPointF pt2 = *handles()[1]; QPointF a = pt - pt1; QPointF u = pt2 - pt1; qreal u_norm = sqrt(u.x() * u.x() + u.y() * u.y()); if(u_norm == 0) return pt; u /= u_norm; double t = a.x() * u.x() + a.y() * u.y(); if(t < 0.0) return pt1; if(t > u_norm) return pt2; return t * u + pt1; } QPointF RulerAssistant::adjustPosition(const QPointF& pt, const QPointF& /*strokeBegin*/) { return project(pt); } inline double angle(const QPointF& p1, const QPointF& p2) { return atan2(p2.y() - p1.y(), p2.x() - p1.x()); } inline double norm2(const QPointF& p) { return sqrt(p.x() * p.x() + p.y() * p.y()); } void RulerAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible) { gc.save(); gc.resetTransform(); QPointF mousePos; if (canvas){ //simplest, cheapest way to get the mouse-position// mousePos= canvas->canvasWidget()->mapFromGlobal(QCursor::pos()); } else { //...of course, you need to have access to a canvas-widget for that.// mousePos = QCursor::pos();//this'll give an offset// dbgFile<<"canvas does not exist in ruler, you may have passed arguments incorrectly:"<documentToWidgetTransform(); // first we find the path that our point create. QPointF p1 = *handles()[0]; QPointF p2 = *handles()[1]; gc.setTransform(initialTransform); QPainterPath path; path.moveTo(p1); path.lineTo(p2); //then we use this path to check the bounding rectangle// if (isSnappingActive() && path.boundingRect().contains(initialTransform.inverted().map(mousePos)) && previewVisible==true){ drawPreview(gc, path);//and we draw the preview. } } gc.restore(); KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); } void RulerAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (assistantVisible == false || !isAssistantComplete()){ return; } QTransform initialTransform = converter->documentToWidgetTransform(); // Draw the line QPointF p1 = *handles()[0]; QPointF p2 = *handles()[1]; gc.setTransform(initialTransform); QPainterPath path; path.moveTo(p1); path.lineTo(p2); drawPath(gc, path, isSnappingActive()); } -QPointF RulerAssistant::editorWidgetPosition() const +QPointF RulerAssistant::getEditorPosition() const { return (*handles()[0] + *handles()[1]) * 0.5; } bool RulerAssistant::isAssistantComplete() const { return handles().size() >= 2; } RulerAssistantFactory::RulerAssistantFactory() { } RulerAssistantFactory::~RulerAssistantFactory() { } QString RulerAssistantFactory::id() const { return "ruler"; } QString RulerAssistantFactory::name() const { return i18n("Ruler"); } KisPaintingAssistant* RulerAssistantFactory::createPaintingAssistant() const { return new RulerAssistant; } diff --git a/plugins/assistants/Assistants/RulerAssistant.h b/plugins/assistants/Assistants/RulerAssistant.h index d52d74bef0..f6692fd620 100644 --- a/plugins/assistants/Assistants/RulerAssistant.h +++ b/plugins/assistants/Assistants/RulerAssistant.h @@ -1,57 +1,57 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _RULER_ASSISTANT_H_ #define _RULER_ASSISTANT_H_ #include #include "kis_painting_assistant.h" class Ruler; class RulerAssistant : public KisPaintingAssistant { public: RulerAssistant(); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; - QPointF editorWidgetPosition() const override; + QPointF getEditorPosition() const override; int numHandles() const override { return 2; } bool isAssistantComplete() const override; protected: void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible=true, bool previewVisible=true) override; void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt) const; explicit RulerAssistant(const RulerAssistant &rhs, QMap &handleMap); }; class RulerAssistantFactory : public KisPaintingAssistantFactory { public: RulerAssistantFactory(); ~RulerAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/SplineAssistant.cc b/plugins/assistants/Assistants/SplineAssistant.cc index 5db8b8626e..20b19dda6e 100644 --- a/plugins/assistants/Assistants/SplineAssistant.cc +++ b/plugins/assistants/Assistants/SplineAssistant.cc @@ -1,239 +1,239 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "SplineAssistant.h" #include #include #include #include #include #include #include "kis_debug.h" #include #include #include SplineAssistant::SplineAssistant() : KisPaintingAssistant("spline", i18n("Spline assistant")) { } SplineAssistant::SplineAssistant(const SplineAssistant &rhs, QMap &handleMap) : KisPaintingAssistant(rhs, handleMap) , m_canvas(rhs.m_canvas) { } KisPaintingAssistantSP SplineAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new SplineAssistant(*this, handleMap)); } // parametric form of a cubic spline (B(t) = (1-t)^3 P0 + 3 (1-t)^2 t P1 + 3 (1-t) t^2 P2 + t^3 P3) inline QPointF B(qreal t, const QPointF& P0, const QPointF& P1, const QPointF& P2, const QPointF& P3) { const qreal tp = 1 - t; const qreal tp2 = tp * tp; const qreal t2 = t * t; return ( tp2 * tp) * P0 + (3 * tp2 * t ) * P1 + (3 * tp * t2) * P2 + ( t * t2) * P3; } // squared distance from a point on the spline to given point: we want to minimize this inline qreal D(qreal t, const QPointF& P0, const QPointF& P1, const QPointF& P2, const QPointF& P3, const QPointF& p) { const qreal tp = 1 - t, tp2 = tp * tp, t2 = t * t, a = tp2 * tp, b = 3 * tp2 * t, c = 3 * tp * t2, d = t * t2, x_dist = a*P0.x() + b*P1.x() + c*P2.x() + d*P3.x() - p.x(), y_dist = a*P0.y() + b*P1.y() + c*P2.y() + d*P3.y() - p.y(); return x_dist * x_dist + y_dist * y_dist; } QPointF SplineAssistant::project(const QPointF& pt) const { Q_ASSERT(isAssistantComplete()); // minimize d(t), but keep t in the same neighbourhood as before (unless starting a new stroke) // (this is a rather inefficient method) qreal min_t = std::numeric_limits::max(); qreal d_min_t = std::numeric_limits::max(); for (qreal t = 0; t <= 1; t += 1e-3) { qreal d_t = D(t, *handles()[0], *handles()[2], *handles()[3], *handles()[1], pt); if (d_t < d_min_t) { d_min_t = d_t; min_t = t; } } return B(min_t, *handles()[0], *handles()[2], *handles()[3], *handles()[1]); } QPointF SplineAssistant::adjustPosition(const QPointF& pt, const QPointF& /*strokeBegin*/) { return project(pt); } void SplineAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible) { gc.save(); gc.resetTransform(); QPoint mousePos; if (canvas){ //simplest, cheapest way to get the mouse-position// mousePos= canvas->canvasWidget()->mapFromGlobal(QCursor::pos()); m_canvas = canvas; } else { //...of course, you need to have access to a canvas-widget for that.// mousePos = QCursor::pos();//this'll give an offset// dbgFile<<"canvas does not exist in spline, you may have passed arguments incorrectly:"< 1) { QTransform initialTransform = converter->documentToWidgetTransform(); // first we find the path that our point create. QPointF pts[4]; pts[0] = *handles()[0]; pts[1] = *handles()[1]; pts[2] = (handles().size() >= 3) ? (*handles()[2]) : (*handles()[0]); pts[3] = (handles().size() >= 4) ? (*handles()[3]) : (handles().size() >= 3) ? (*handles()[2]) : (*handles()[1]); gc.setTransform(initialTransform); // Draw the spline QPainterPath path; path.moveTo(pts[0]); path.cubicTo(pts[2], pts[3], pts[1]); //then we use this path to check the bounding rectangle// if (isSnappingActive() && path.boundingRect().contains(initialTransform.inverted().map(mousePos)) && previewVisible==true){ drawPreview(gc, path);//and we draw the preview. } } gc.restore(); // there is some odd rectangle that is getting rendered when there is only one point, so don't start rendering the line until after 2 // this issue only exists with this spline assistant...none of the others if (handles().size() > 2) { KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); } } void SplineAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (assistantVisible == false || handles().size() < 2 ){ return; } QTransform initialTransform = converter->documentToWidgetTransform(); QPointF pts[4]; pts[0] = *handles()[0]; pts[1] = *handles()[1]; pts[2] = (handles().size() >= 3) ? (*handles()[2]) : (*handles()[0]); pts[3] = (handles().size() >= 4) ? (*handles()[3]) : (handles().size() >= 3) ? (*handles()[2]) : (*handles()[1]); gc.setTransform(initialTransform); { // Draw bezier handles control lines only if we are editing the assistant gc.save(); QColor assistantColor = effectiveAssistantColor(); QPen bezierlinePen(assistantColor); bezierlinePen.setStyle(Qt::DotLine); bezierlinePen.setWidth(1); if (m_canvas->paintingAssistantsDecoration()->isEditingAssistants()) { if (!isSnappingActive()) { QColor snappingColor = assistantColor; snappingColor.setAlpha(snappingColor.alpha() * 0.2); bezierlinePen.setColor(snappingColor); } gc.setPen(bezierlinePen); gc.drawLine(pts[0], pts[2]); if (isAssistantComplete()) { gc.drawLine(pts[1], pts[3]); } gc.setPen(QColor(0, 0, 0, 125)); } gc.restore(); } // Draw the spline QPainterPath path; path.moveTo(pts[0]); path.cubicTo(pts[2], pts[3], pts[1]); drawPath(gc, path, isSnappingActive()); } -QPointF SplineAssistant::editorWidgetPosition() const +QPointF SplineAssistant::getEditorPosition() const { return B(0.5, *handles()[0], *handles()[2], *handles()[3], *handles()[1]); } bool SplineAssistant::isAssistantComplete() const { return handles().size() >= 4; // specify 4 corners to make assistant complete } SplineAssistantFactory::SplineAssistantFactory() { } SplineAssistantFactory::~SplineAssistantFactory() { } QString SplineAssistantFactory::id() const { return "spline"; } QString SplineAssistantFactory::name() const { return i18n("Spline"); } KisPaintingAssistant* SplineAssistantFactory::createPaintingAssistant() const { return new SplineAssistant; } diff --git a/plugins/assistants/Assistants/SplineAssistant.h b/plugins/assistants/Assistants/SplineAssistant.h index 43cad961a3..f74ca50725 100644 --- a/plugins/assistants/Assistants/SplineAssistant.h +++ b/plugins/assistants/Assistants/SplineAssistant.h @@ -1,58 +1,58 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _SPLINE_ASSISTANT_H_ #define _SPLINE_ASSISTANT_H_ #include "kis_painting_assistant.h" #include class SplineAssistant : public KisPaintingAssistant { public: SplineAssistant(); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; - QPointF editorWidgetPosition() const override; + QPointF getEditorPosition() const override; int numHandles() const override { return 4; } bool isAssistantComplete() const override; protected: void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible=true, bool previewVisible=true) override; void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt) const; explicit SplineAssistant(const SplineAssistant &rhs, QMap &handleMap); /// used for getting the decoration so the bezier handles aren't drawn while editing KisCanvas2* m_canvas; }; class SplineAssistantFactory : public KisPaintingAssistantFactory { public: SplineAssistantFactory(); ~SplineAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/VanishingPointAssistant.cc b/plugins/assistants/Assistants/VanishingPointAssistant.cc index 18a2415076..3d11d2539d 100644 --- a/plugins/assistants/Assistants/VanishingPointAssistant.cc +++ b/plugins/assistants/Assistants/VanishingPointAssistant.cc @@ -1,318 +1,318 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2014 Wolthera van Hövell tot Westerflier * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "VanishingPointAssistant.h" #include "kis_debug.h" #include #include #include #include #include #include #include #include #include VanishingPointAssistant::VanishingPointAssistant() : KisPaintingAssistant("vanishing point", i18n("Vanishing Point assistant")) { } VanishingPointAssistant::VanishingPointAssistant(const VanishingPointAssistant &rhs, QMap &handleMap) : KisPaintingAssistant(rhs, handleMap) , m_canvas(rhs.m_canvas) , m_referenceLineDensity(rhs.m_referenceLineDensity) { } KisPaintingAssistantSP VanishingPointAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new VanishingPointAssistant(*this, handleMap)); } QPointF VanishingPointAssistant::project(const QPointF& pt, const QPointF& strokeBegin) { //Q_ASSERT(handles().size() == 1 || handles().size() == 5); //code nicked from the perspective ruler. qreal dx = pt.x() - strokeBegin.x(); qreal dy = pt.y() - strokeBegin.y(); if (dx * dx + dy * dy < 4.0) { // allow some movement before snapping return strokeBegin; } //dbgKrita<canvasWidget()->mapFromGlobal(QCursor::pos()); m_canvas = canvas; } else { //...of course, you need to have access to a canvas-widget for that.// mousePos = QCursor::pos();//this'll give an offset// dbgFile<<"canvas does not exist in ruler, you may have passed arguments incorrectly:"<paintingAssistantsDecoration()->isEditingAssistants() == false && isAssistantComplete()) { if (isSnappingActive() && previewVisible == true) { //don't draw if invalid. QTransform initialTransform = converter->documentToWidgetTransform(); QPointF startPoint = initialTransform.map(*handles()[0]); QLineF snapLine= QLineF(startPoint, mousePos); QRect viewport= gc.viewport(); KisAlgebra2D::intersectLineRect(snapLine, viewport); QRect bounds= QRect(snapLine.p1().toPoint(), snapLine.p2().toPoint()); QPainterPath path; if (bounds.contains(startPoint.toPoint())){ path.moveTo(startPoint); path.lineTo(snapLine.p1()); } else { path.moveTo(snapLine.p1()); path.lineTo(snapLine.p2()); } drawPreview(gc, path);//and we draw the preview. } } // editor specific controls display if (canvas->paintingAssistantsDecoration()->isEditingAssistants()) { // draws a circle around the vanishing point node while editing QTransform initialTransform = converter->documentToWidgetTransform(); QPointF p0 = initialTransform.map(*handles()[0]); // main vanishing point QPointF p1 = initialTransform.map(*sideHandles()[0]); QPointF p2 = initialTransform.map(*sideHandles()[1]); QPointF p3 = initialTransform.map(*sideHandles()[2]); QPointF p4 = initialTransform.map(*sideHandles()[3]); QRectF ellipse = QRectF(QPointF(p0.x() -15, p0.y() -15), QSizeF(30, 30)); QPainterPath pathCenter; pathCenter.addEllipse(ellipse); drawPath(gc, pathCenter, isSnappingActive()); QColor paintingColor = effectiveAssistantColor(); // draw the lines connecting the different nodes QPen penStyle(paintingColor, 2.0, Qt::SolidLine); if (!isSnappingActive()) { QColor snappingColor = paintingColor; snappingColor.setAlpha(snappingColor.alpha() * 0.2); penStyle.setColor(snappingColor); } gc.save(); gc.setPen(penStyle); gc.drawLine(p0, p1); gc.drawLine(p0, p3); gc.drawLine(p1, p2); gc.drawLine(p3, p4); gc.restore(); } // draw references guide for vanishing points at specified density // this is shown as part of the preview, so don't show if preview is off if ( (assistantVisible && canvas->paintingAssistantsDecoration()->outlineVisibility()) && this->isSnappingActive() ) { // cycle through degrees from 0 to 180. We are doing an infinite line, so we don't need to go 360 QTransform initialTransform = converter->documentToWidgetTransform(); QPointF p0 = initialTransform.map(*handles()[0]); // main vanishing point for (int currentAngle=0; currentAngle <= 180; currentAngle = currentAngle + m_referenceLineDensity ) { // determine the correct angle based on the iteration float xPos = cos(currentAngle * M_PI / 180); float yPos = sin(currentAngle * M_PI / 180); QPointF unitAngle; unitAngle.setX(p0.x() + xPos); unitAngle.setY(p0.y() + yPos); // find point QLineF snapLine= QLineF(p0, unitAngle); QRect viewport= gc.viewport(); KisAlgebra2D::intersectLineRect(snapLine, viewport); // make a line from VP center to edge of canvas with that angle QPainterPath path; path.moveTo(snapLine.p1()); path.lineTo(snapLine.p2()); drawPreview(gc, path);//and we draw the preview. } } gc.restore(); KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); } void VanishingPointAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (!m_canvas || !isAssistantComplete()) { return; } if (assistantVisible == false || m_canvas->paintingAssistantsDecoration()->isEditingAssistants()) { return; } QTransform initialTransform = converter->documentToWidgetTransform(); QPointF p0 = initialTransform.map(*handles()[0]); // draws an "X" QPainterPath path; path.moveTo(QPointF(p0.x() - 10.0, p0.y() - 10.0)); path.lineTo(QPointF(p0.x() + 10.0, p0.y() + 10.0)); path.moveTo(QPointF(p0.x() - 10.0, p0.y() + 10.0)); path.lineTo(QPointF(p0.x() + 10.0, p0.y() - 10.0)); drawPath(gc, path, isSnappingActive()); } -QPointF VanishingPointAssistant::editorWidgetPosition() const +QPointF VanishingPointAssistant::getEditorPosition() const { return (*handles()[0]); } void VanishingPointAssistant::setReferenceLineDensity(float value) { // cannot have less than 1 degree value if (value < 1.0) { value = 1.0; } m_referenceLineDensity = value; } float VanishingPointAssistant::referenceLineDensity() { return m_referenceLineDensity; } bool VanishingPointAssistant::isAssistantComplete() const { return handles().size() > 0; // only need one point to be ready } void VanishingPointAssistant::saveCustomXml(QXmlStreamWriter* xml) { xml->writeStartElement("angleDensity"); xml->writeAttribute("value", KisDomUtils::toString( this->referenceLineDensity())); xml->writeEndElement(); } bool VanishingPointAssistant::loadCustomXml(QXmlStreamReader* xml) { if (xml && xml->name() == "angleDensity") { this->setReferenceLineDensity((float)KisDomUtils::toDouble(xml->attributes().value("value").toString())); } return true; } VanishingPointAssistantFactory::VanishingPointAssistantFactory() { } VanishingPointAssistantFactory::~VanishingPointAssistantFactory() { } QString VanishingPointAssistantFactory::id() const { return "vanishing point"; } QString VanishingPointAssistantFactory::name() const { return i18n("Vanishing Point"); } KisPaintingAssistant* VanishingPointAssistantFactory::createPaintingAssistant() const { return new VanishingPointAssistant; } diff --git a/plugins/assistants/Assistants/VanishingPointAssistant.h b/plugins/assistants/Assistants/VanishingPointAssistant.h index 6748785310..8c790161ad 100644 --- a/plugins/assistants/Assistants/VanishingPointAssistant.h +++ b/plugins/assistants/Assistants/VanishingPointAssistant.h @@ -1,85 +1,85 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2014 Wolthera van Hövell tot Westerflier * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _VANISHINGPOINT_ASSISTANT_H_ #define _VANISHINGPOINT_ASSISTANT_H_ #include "kis_painting_assistant.h" #include #include #include #include /* Design: *The idea behind the vanishing point ruler is that in a perspective deformed landscape, a set of parallel *lines al share a single vanishing point. *Therefore, a perspective can contain an theoretical infinite of vanishing points. *It's a pity if we only allowed an artist to access 1, 2 or 3 of these at any given time, as other *solutions for perspective tools do. *Hence a vanishing point ruler. * *This ruler is relatively simple compared to the other perspective ruler: *It has only one vanishing point that is required to draw. *However, it does have it's own weaknesses in how to determine onto which of these infinite rulers to snap. *Furthermore, it has four extra handles for adding a perspective ruler to a preexisting perspective. */ //class VanishingPoint; class VanishingPointAssistant : public KisPaintingAssistant { public: VanishingPointAssistant(); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; //virtual void endStroke(); - QPointF editorWidgetPosition() const override; + QPointF getEditorPosition() const override; int numHandles() const override { return 1; } float referenceLineDensity(); void setReferenceLineDensity(float value); bool isAssistantComplete() const override; void saveCustomXml(QXmlStreamWriter* xml) override; bool loadCustomXml(QXmlStreamReader* xml) override; protected: void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached = true,KisCanvas2* canvas=0, bool assistantVisible=true, bool previewVisible=true) override; void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt, const QPointF& strokeBegin); explicit VanishingPointAssistant(const VanishingPointAssistant &rhs, QMap &handleMap); KisCanvas2 *m_canvas; float m_referenceLineDensity = 15.0; }; class VanishingPointAssistantFactory : public KisPaintingAssistantFactory { public: VanishingPointAssistantFactory(); ~VanishingPointAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/kis_assistant_tool.cc b/plugins/assistants/Assistants/kis_assistant_tool.cc index 755f6b3cda..a9af2756e8 100644 --- a/plugins/assistants/Assistants/kis_assistant_tool.cc +++ b/plugins/assistants/Assistants/kis_assistant_tool.cc @@ -1,1061 +1,1061 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include "kis_dom_utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_global.h" #include "VanishingPointAssistant.h" #include "EditAssistantsCommand.h" #include #include KisAssistantTool::KisAssistantTool(KoCanvasBase * canvas) : KisTool(canvas, KisCursor::arrowCursor()), m_canvas(dynamic_cast(canvas)), m_assistantDrag(0), m_newAssistant(0), m_optionsWidget(0) { Q_ASSERT(m_canvas); setObjectName("tool_assistanttool"); } KisAssistantTool::~KisAssistantTool() { } void KisAssistantTool::activate(ToolActivation toolActivation, const QSet &shapes) { KisTool::activate(toolActivation, shapes); m_canvas->paintingAssistantsDecoration()->activateAssistantsEditor(); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); m_handleDrag = 0; m_internalMode = MODE_CREATION; m_assistantHelperYOffset = 10; m_handleSize = 17; m_canvas->paintingAssistantsDecoration()->setHandleSize(m_handleSize); if (m_optionsWidget) { m_canvas->paintingAssistantsDecoration()->deselectAssistant(); updateToolOptionsUI(); } m_canvas->updateCanvas(); } void KisAssistantTool::deactivate() { m_canvas->paintingAssistantsDecoration()->deactivateAssistantsEditor(); m_canvas->updateCanvas(); KisTool::deactivate(); } void KisAssistantTool::beginPrimaryAction(KoPointerEvent *event) { setMode(KisTool::PAINT_MODE); m_origAssistantList = KisPaintingAssistant::cloneAssistantList(m_canvas->paintingAssistantsDecoration()->assistants()); bool newAssistantAllowed = true; KisPaintingAssistantsDecorationSP canvasDecoration = m_canvas->paintingAssistantsDecoration(); if (m_newAssistant) { m_internalMode = MODE_CREATION; *m_newAssistant->handles().back() = canvasDecoration->snapToGuide(event, QPointF(), false); if (m_newAssistant->handles().size() == m_newAssistant->numHandles()) { addAssistant(); } else { m_newAssistant->addHandle(new KisPaintingAssistantHandle(canvasDecoration->snapToGuide(event, QPointF(), false)), HandleType::NORMAL); } m_canvas->updateCanvas(); return; } m_handleDrag = 0; double minDist = 81.0; QPointF mousePos = m_canvas->viewConverter()->documentToView(canvasDecoration->snapToGuide(event, QPointF(), false));//m_canvas->viewConverter()->documentToView(event->point); // syncs the assistant handles to the handles reference we store in this tool // they can get out of sync with the way the actions and paintevents occur // we probably need to stop storing a reference in m_handles and call the assistants directly m_handles = m_canvas->paintingAssistantsDecoration()->handles(); Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { // find out which handle on all assistants is closest to the mouse position // vanishing points have "side handles", so make sure to include that { QList allAssistantHandles; allAssistantHandles.append(assistant->handles()); allAssistantHandles.append(assistant->sideHandles()); Q_FOREACH (const KisPaintingAssistantHandleSP handle, allAssistantHandles) { double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*handle)); if (dist < minDist) { minDist = dist; m_handleDrag = handle; assistantSelected(assistant); // whatever handle is the closest contains the selected assistant } } } if(m_handleDrag && assistant->id() == "perspective") { // Look for the handle which was pressed if (m_handleDrag == assistant->topLeft()) { double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*m_handleDrag)); if (dist < minDist) { minDist = dist; } m_dragStart = QPointF(assistant->topRight().data()->x(),assistant->topRight().data()->y()); m_internalMode = MODE_DRAGGING_NODE; } else if (m_handleDrag == assistant->topRight()) { double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*m_handleDrag)); if (dist < minDist) { minDist = dist; } m_internalMode = MODE_DRAGGING_NODE; m_dragStart = QPointF(assistant->topLeft().data()->x(),assistant->topLeft().data()->y()); } else if (m_handleDrag == assistant->bottomLeft()) { double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*m_handleDrag)); if (dist < minDist) { minDist = dist; } m_internalMode = MODE_DRAGGING_NODE; m_dragStart = QPointF(assistant->bottomRight().data()->x(),assistant->bottomRight().data()->y()); } else if (m_handleDrag == assistant->bottomRight()) { double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*m_handleDrag)); if (dist < minDist) { minDist = dist; } m_internalMode = MODE_DRAGGING_NODE; m_dragStart = QPointF(assistant->bottomLeft().data()->x(),assistant->bottomLeft().data()->y()); } else if (m_handleDrag == assistant->leftMiddle()) { m_internalMode = MODE_DRAGGING_TRANSLATING_TWONODES; m_dragStart = QPointF((assistant->bottomLeft().data()->x()+assistant->topLeft().data()->x())*0.5, (assistant->bottomLeft().data()->y()+assistant->topLeft().data()->y())*0.5); m_selectedNode1 = new KisPaintingAssistantHandle(assistant->topLeft().data()->x(),assistant->topLeft().data()->y()); m_selectedNode2 = new KisPaintingAssistantHandle(assistant->bottomLeft().data()->x(),assistant->bottomLeft().data()->y()); m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get("perspective")->createPaintingAssistant()); m_newAssistant->addHandle(assistant->topLeft(), HandleType::NORMAL ); m_newAssistant->addHandle(m_selectedNode1, HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode2, HandleType::NORMAL); m_newAssistant->addHandle(assistant->bottomLeft(), HandleType::NORMAL); m_dragEnd = event->point; m_handleDrag = 0; m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas return; } else if (m_handleDrag == assistant->rightMiddle()) { m_dragStart = QPointF((assistant->topRight().data()->x()+assistant->bottomRight().data()->x())*0.5, (assistant->topRight().data()->y()+assistant->bottomRight().data()->y())*0.5); m_internalMode = MODE_DRAGGING_TRANSLATING_TWONODES; m_selectedNode1 = new KisPaintingAssistantHandle(assistant->topRight().data()->x(),assistant->topRight().data()->y()); m_selectedNode2 = new KisPaintingAssistantHandle(assistant->bottomRight().data()->x(),assistant->bottomRight().data()->y()); m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get("perspective")->createPaintingAssistant()); m_newAssistant->addHandle(assistant->topRight(), HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode1, HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode2, HandleType::NORMAL); m_newAssistant->addHandle(assistant->bottomRight(), HandleType::NORMAL); m_dragEnd = event->point; m_handleDrag = 0; m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas return; } else if (m_handleDrag == assistant->topMiddle()) { m_dragStart = QPointF((assistant->topLeft().data()->x()+assistant->topRight().data()->x())*0.5, (assistant->topLeft().data()->y()+assistant->topRight().data()->y())*0.5); m_internalMode = MODE_DRAGGING_TRANSLATING_TWONODES; m_selectedNode1 = new KisPaintingAssistantHandle(assistant->topLeft().data()->x(),assistant->topLeft().data()->y()); m_selectedNode2 = new KisPaintingAssistantHandle(assistant->topRight().data()->x(),assistant->topRight().data()->y()); m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get("perspective")->createPaintingAssistant()); m_newAssistant->addHandle(m_selectedNode1, HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode2, HandleType::NORMAL); m_newAssistant->addHandle(assistant->topRight(), HandleType::NORMAL); m_newAssistant->addHandle(assistant->topLeft(), HandleType::NORMAL); m_dragEnd = event->point; m_handleDrag = 0; m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas return; } else if (m_handleDrag == assistant->bottomMiddle()) { m_dragStart = QPointF((assistant->bottomLeft().data()->x()+assistant->bottomRight().data()->x())*0.5, (assistant->bottomLeft().data()->y()+assistant->bottomRight().data()->y())*0.5); m_internalMode = MODE_DRAGGING_TRANSLATING_TWONODES; m_selectedNode1 = new KisPaintingAssistantHandle(assistant->bottomLeft().data()->x(),assistant->bottomLeft().data()->y()); m_selectedNode2 = new KisPaintingAssistantHandle(assistant->bottomRight().data()->x(),assistant->bottomRight().data()->y()); m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get("perspective")->createPaintingAssistant()); m_newAssistant->addHandle(assistant->bottomLeft(), HandleType::NORMAL); m_newAssistant->addHandle(assistant->bottomRight(), HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode2, HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode1, HandleType::NORMAL); m_dragEnd = event->point; m_handleDrag = 0; m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas return; } m_snapIsRadial = false; } else if (m_handleDrag && assistant->handles().size()>1 && (assistant->id() == "ruler" || assistant->id() == "parallel ruler" || assistant->id() == "infinite ruler" || assistant->id() == "spline")){ if (m_handleDrag == assistant->handles()[0]) { m_dragStart = *assistant->handles()[1]; } else if (m_handleDrag == assistant->handles()[1]) { m_dragStart = *assistant->handles()[0]; } else if(assistant->handles().size()==4){ if (m_handleDrag == assistant->handles()[2]) { m_dragStart = *assistant->handles()[0]; } else if (m_handleDrag == assistant->handles()[3]) { m_dragStart = *assistant->handles()[1]; } } m_snapIsRadial = false; } else if (m_handleDrag && assistant->handles().size()>2 && (assistant->id() == "ellipse" || assistant->id() == "concentric ellipse" || assistant->id() == "fisheye-point")){ m_snapIsRadial = false; if (m_handleDrag == assistant->handles()[0]) { m_dragStart = *assistant->handles()[1]; } else if (m_handleDrag == assistant->handles()[1]) { m_dragStart = *assistant->handles()[0]; } else if (m_handleDrag == assistant->handles()[2]) { - m_dragStart = assistant->editorWidgetPosition(); + m_dragStart = assistant->getEditorPosition(); m_radius = QLineF(m_dragStart, *assistant->handles()[0]); m_snapIsRadial = true; } } else { - m_dragStart = assistant->editorWidgetPosition(); + m_dragStart = assistant->getEditorPosition(); m_snapIsRadial = false; } } if (m_handleDrag) { // TODO: Shift-press should now be handled using the alternate actions // if (event->modifiers() & Qt::ShiftModifier) { // m_handleDrag->uncache(); // m_handleDrag = m_handleDrag->split()[0]; // m_handles = m_canvas->view()->paintingAssistantsDecoration()->handles(); // } m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas return; } m_assistantDrag.clear(); Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { AssistantEditorData editorShared; // shared position data between assistant tool and decoration const KisCoordinatesConverter *converter = m_canvas->coordinatesConverter(); // This code contains the click event behavior. QTransform initialTransform = converter->documentToWidgetTransform(); - QPointF actionsPosition = initialTransform.map(assistant->viewportConstrainedEditorWidgetPosition(converter, editorShared.boundingSize)); + QPointF actionsPosition = initialTransform.map(assistant->viewportConstrainedEditorPosition(converter, editorShared.boundingSize)); // for UI editor widget controls with move, show, and delete -- disregard document transforms like rotating and mirroring. // otherwise the UI controls get awkward to use when they are at 45 degree angles or the order of controls gets flipped backwards QPointF uiMousePosition = initialTransform.map(canvasDecoration->snapToGuide(event, QPointF(), false)); QPointF iconMovePosition(actionsPosition + editorShared.moveIconPosition); QPointF iconSnapPosition(actionsPosition + editorShared.snapIconPosition); QPointF iconDeletePosition(actionsPosition + editorShared.deleteIconPosition); QRectF deleteRect(iconDeletePosition, QSizeF(editorShared.deleteIconSize, editorShared.deleteIconSize)); QRectF visibleRect(iconSnapPosition, QSizeF(editorShared.snapIconSize, editorShared.snapIconSize)); QRectF moveRect(iconMovePosition, QSizeF(editorShared.moveIconSize, editorShared.moveIconSize)); if (moveRect.contains(uiMousePosition)) { m_assistantDrag = assistant; m_cursorStart = event->point; m_currentAdjustment = QPointF(); m_internalMode = MODE_EDITING; assistantSelected(assistant); // whatever handle is the closest contains the selected assistant return; } if (deleteRect.contains(uiMousePosition)) { removeAssistant(assistant); if(m_canvas->paintingAssistantsDecoration()->assistants().isEmpty()) { m_internalMode = MODE_CREATION; } else m_internalMode = MODE_EDITING; m_canvas->updateCanvas(); return; } if (visibleRect.contains(uiMousePosition)) { newAssistantAllowed = false; assistant->setSnappingActive(!assistant->isSnappingActive()); // toggle assistant->uncache();//this updates the chache of the assistant, very important. assistantSelected(assistant); // whatever handle is the closest contains the selected assistant } } if (newAssistantAllowed==true){//don't make a new assistant when I'm just toogling visibility// QString key = m_options.availableAssistantsComboBox->model()->index( m_options.availableAssistantsComboBox->currentIndex(), 0 ).data(Qt::UserRole).toString(); m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get(key)->createPaintingAssistant()); m_internalMode = MODE_CREATION; m_newAssistant->addHandle(new KisPaintingAssistantHandle(canvasDecoration->snapToGuide(event, QPointF(), false)), HandleType::NORMAL); if (m_newAssistant->numHandles() <= 1) { addAssistant(); } else { m_newAssistant->addHandle(new KisPaintingAssistantHandle(canvasDecoration->snapToGuide(event, QPointF(), false)), HandleType::NORMAL); } } if (m_newAssistant) { m_newAssistant->setAssistantGlobalColorCache(m_canvas->paintingAssistantsDecoration()->globalAssistantsColor()); } m_canvas->updateCanvas(); } void KisAssistantTool::continuePrimaryAction(KoPointerEvent *event) { KisPaintingAssistantsDecorationSP canvasDecoration = m_canvas->paintingAssistantsDecoration(); if (m_handleDrag) { *m_handleDrag = event->point; //ported from the gradient tool... we need to think about this more in the future. if (event->modifiers() == Qt::ShiftModifier && m_snapIsRadial) { QLineF dragRadius = QLineF(m_dragStart, event->point); dragRadius.setLength(m_radius.length()); *m_handleDrag = dragRadius.p2(); } else if (event->modifiers() == Qt::ShiftModifier ) { QPointF move = snapToClosestAxis(event->point - m_dragStart); *m_handleDrag = m_dragStart + move; } else { *m_handleDrag = canvasDecoration->snapToGuide(event, QPointF(), false); } m_handleDrag->uncache(); m_handleCombine = 0; if (!(event->modifiers() & Qt::ShiftModifier)) { double minDist = 49.0; QPointF mousePos = m_canvas->viewConverter()->documentToView(event->point); Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_handles) { if (handle == m_handleDrag) continue; double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*handle)); if (dist < minDist) { minDist = dist; m_handleCombine = handle; } } } m_canvas->updateCanvas(); } else if (m_assistantDrag) { QPointF newAdjustment = canvasDecoration->snapToGuide(event, QPointF(), false) - m_cursorStart; if (event->modifiers() == Qt::ShiftModifier ) { newAdjustment = snapToClosestAxis(newAdjustment); } Q_FOREACH (KisPaintingAssistantHandleSP handle, m_assistantDrag->handles()) { *handle += (newAdjustment - m_currentAdjustment); } if (m_assistantDrag->id()== "vanishing point"){ Q_FOREACH (KisPaintingAssistantHandleSP handle, m_assistantDrag->sideHandles()) { *handle += (newAdjustment - m_currentAdjustment); } } m_currentAdjustment = newAdjustment; m_canvas->updateCanvas(); } else { event->ignore(); } bool wasHiglightedNode = m_higlightedNode != 0; QPointF mousep = m_canvas->viewConverter()->documentToView(event->point); QList pAssistant= m_canvas->paintingAssistantsDecoration()->assistants(); Q_FOREACH (KisPaintingAssistantSP assistant, pAssistant) { if(assistant->id() == "perspective") { if ((m_higlightedNode = assistant->closestCornerHandleFromPoint(mousep))) { if (m_higlightedNode == m_selectedNode1 || m_higlightedNode == m_selectedNode2) { m_higlightedNode = 0; } else { m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas break; } } } //this following bit sets the translations for the vanishing-point handles. if(m_handleDrag && assistant->id() == "vanishing point" && assistant->sideHandles().size()==4) { //for inner handles, the outer handle gets translated. if (m_handleDrag == assistant->sideHandles()[0]) { QLineF perspectiveline = QLineF(*assistant->handles()[0], *assistant->sideHandles()[0]); qreal length = QLineF(*assistant->sideHandles()[0], *assistant->sideHandles()[1]).length(); if (length < 2.0){ length = 2.0; } length += perspectiveline.length(); perspectiveline.setLength(length); *assistant->sideHandles()[1] = perspectiveline.p2(); } else if (m_handleDrag == assistant->sideHandles()[2]){ QLineF perspectiveline = QLineF(*assistant->handles()[0], *assistant->sideHandles()[2]); qreal length = QLineF(*assistant->sideHandles()[2], *assistant->sideHandles()[3]).length(); if (length<2.0){ length=2.0; } length += perspectiveline.length(); perspectiveline.setLength(length); *assistant->sideHandles()[3] = perspectiveline.p2(); } // for outer handles, only the vanishing point is translated, but only if there's an intersection. else if (m_handleDrag == assistant->sideHandles()[1]|| m_handleDrag == assistant->sideHandles()[3]){ QPointF vanishingpoint(0,0); QLineF perspectiveline = QLineF(*assistant->sideHandles()[0], *assistant->sideHandles()[1]); QLineF perspectiveline2 = QLineF(*assistant->sideHandles()[2], *assistant->sideHandles()[3]); if (QLineF(perspectiveline2).intersect(QLineF(perspectiveline), &vanishingpoint) != QLineF::NoIntersection){ *assistant->handles()[0] = vanishingpoint; } }// and for the vanishing point itself, only the outer handles get translated. else if (m_handleDrag == assistant->handles()[0]){ QLineF perspectiveline = QLineF(*assistant->handles()[0], *assistant->sideHandles()[0]); QLineF perspectiveline2 = QLineF(*assistant->handles()[0], *assistant->sideHandles()[2]); qreal length = QLineF(*assistant->sideHandles()[0], *assistant->sideHandles()[1]).length(); qreal length2 = QLineF(*assistant->sideHandles()[2], *assistant->sideHandles()[3]).length(); if (length < 2.0) { length = 2.0; } if (length2 < 2.0) { length2=2.0; } length += perspectiveline.length(); length2 += perspectiveline2.length(); perspectiveline.setLength(length); perspectiveline2.setLength(length2); *assistant->sideHandles()[1] = perspectiveline.p2(); *assistant->sideHandles()[3] = perspectiveline2.p2(); } } } if (wasHiglightedNode && !m_higlightedNode) { m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas } } void KisAssistantTool::endPrimaryAction(KoPointerEvent *event) { setMode(KisTool::HOVER_MODE); if (m_handleDrag || m_assistantDrag) { if (m_handleDrag) { if (!(event->modifiers() & Qt::ShiftModifier) && m_handleCombine) { m_handleCombine->mergeWith(m_handleDrag); m_handleCombine->uncache(); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); } m_handleDrag = m_handleCombine = 0; } else { m_assistantDrag.clear(); } dbgUI << "creating undo command..."; KUndo2Command *command = new EditAssistantsCommand(m_canvas, m_origAssistantList, KisPaintingAssistant::cloneAssistantList(m_canvas->paintingAssistantsDecoration()->assistants())); m_canvas->viewManager()->undoAdapter()->addCommand(command); dbgUI << "done"; } else if(m_internalMode == MODE_DRAGGING_TRANSLATING_TWONODES) { addAssistant(); m_internalMode = MODE_CREATION; } else { event->ignore(); } m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas } void KisAssistantTool::addAssistant() { m_canvas->paintingAssistantsDecoration()->addAssistant(m_newAssistant); KisAbstractPerspectiveGrid* grid = dynamic_cast(m_newAssistant.data()); if (grid) { m_canvas->viewManager()->canvasResourceProvider()->addPerspectiveGrid(grid); } QList assistants = m_canvas->paintingAssistantsDecoration()->assistants(); KUndo2Command *addAssistantCmd = new EditAssistantsCommand(m_canvas, m_origAssistantList, KisPaintingAssistant::cloneAssistantList(assistants), EditAssistantsCommand::ADD, assistants.indexOf(m_newAssistant)); m_canvas->viewManager()->undoAdapter()->addCommand(addAssistantCmd); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); m_canvas->paintingAssistantsDecoration()->setSelectedAssistant(m_newAssistant); updateToolOptionsUI(); // vanishing point assistant will get an extra option m_newAssistant.clear(); } void KisAssistantTool::removeAssistant(KisPaintingAssistantSP assistant) { QList assistants = m_canvas->paintingAssistantsDecoration()->assistants(); KisAbstractPerspectiveGrid* grid = dynamic_cast(assistant.data()); if (grid) { m_canvas->viewManager()->canvasResourceProvider()->removePerspectiveGrid(grid); } m_canvas->paintingAssistantsDecoration()->removeAssistant(assistant); KUndo2Command *removeAssistantCmd = new EditAssistantsCommand(m_canvas, m_origAssistantList, KisPaintingAssistant::cloneAssistantList(m_canvas->paintingAssistantsDecoration()->assistants()), EditAssistantsCommand::REMOVE, assistants.indexOf(assistant)); m_canvas->viewManager()->undoAdapter()->addCommand(removeAssistantCmd); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); m_canvas->paintingAssistantsDecoration()->deselectAssistant(); updateToolOptionsUI(); } void KisAssistantTool::assistantSelected(KisPaintingAssistantSP assistant) { m_canvas->paintingAssistantsDecoration()->setSelectedAssistant(assistant); updateToolOptionsUI(); } void KisAssistantTool::updateToolOptionsUI() { KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant(); bool hasActiveAssistant = m_selectedAssistant ? true : false; if (m_selectedAssistant) { bool isVanishingPointAssistant = m_selectedAssistant->id() == "vanishing point"; m_options.vanishingPointAngleSpinbox->setVisible(isVanishingPointAssistant); if (isVanishingPointAssistant) { QSharedPointer assis = qSharedPointerCast(m_selectedAssistant); m_options.vanishingPointAngleSpinbox->setValue(assis->referenceLineDensity()); } // load custom color settings from assistant (this happens when changing assistant m_options.useCustomAssistantColor->setChecked(m_selectedAssistant->useCustomColor()); m_options.customAssistantColorButton->setColor(m_selectedAssistant->assistantCustomColor()); float opacity = (float)m_selectedAssistant->assistantCustomColor().alpha()/255.0 * 100.0 ; m_options.customColorOpacitySlider->setValue(opacity); } else { m_options.vanishingPointAngleSpinbox->setVisible(false); // } // show/hide elements if an assistant is selected or not m_options.assistantsGlobalOpacitySlider->setVisible(hasActiveAssistant); m_options.assistantsColor->setVisible(hasActiveAssistant); m_options.globalColorLabel->setVisible(hasActiveAssistant); m_options.useCustomAssistantColor->setVisible(hasActiveAssistant); // hide custom color options if use custom color is not selected bool showCustomColorSettings = m_options.useCustomAssistantColor->isChecked() && hasActiveAssistant; m_options.customColorOpacitySlider->setVisible(showCustomColorSettings); m_options.customAssistantColorButton->setVisible(showCustomColorSettings); // disable global color settings if we are using the custom color m_options.assistantsGlobalOpacitySlider->setEnabled(!showCustomColorSettings); m_options.assistantsColor->setEnabled(!showCustomColorSettings); m_options.globalColorLabel->setEnabled(!showCustomColorSettings); } void KisAssistantTool::slotChangeVanishingPointAngle(double value) { if ( m_canvas->paintingAssistantsDecoration()->assistants().length() == 0) { return; } // get the selected assistant and change the angle value KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant(); if (m_selectedAssistant) { bool isVanishingPointAssistant = m_selectedAssistant->id() == "vanishing point"; if (isVanishingPointAssistant) { QSharedPointer assis = qSharedPointerCast(m_selectedAssistant); assis->setReferenceLineDensity((float)value); } } m_canvas->canvasWidget()->update(); } void KisAssistantTool::mouseMoveEvent(KoPointerEvent *event) { if (m_newAssistant && m_internalMode == MODE_CREATION) { *m_newAssistant->handles().back() = event->point; } else if (m_newAssistant && m_internalMode == MODE_DRAGGING_TRANSLATING_TWONODES) { QPointF translate = event->point - m_dragEnd; m_dragEnd = event->point; m_selectedNode1.data()->operator = (QPointF(m_selectedNode1.data()->x(),m_selectedNode1.data()->y()) + translate); m_selectedNode2.data()->operator = (QPointF(m_selectedNode2.data()->x(),m_selectedNode2.data()->y()) + translate); } m_canvas->updateCanvas(); } void KisAssistantTool::paint(QPainter& _gc, const KoViewConverter &_converter) { QRectF canvasSize = QRectF(QPointF(0, 0), QSizeF(m_canvas->image()->size())); // show special display while a new assistant is in the process of being created if (m_newAssistant) { QColor assistantColor = m_newAssistant->effectiveAssistantColor(); assistantColor.setAlpha(80); m_newAssistant->drawAssistant(_gc, canvasSize, m_canvas->coordinatesConverter(), false, m_canvas, true, false); Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_newAssistant->handles()) { QPainterPath path; path.addEllipse(QRectF(_converter.documentToView(*handle) - QPointF(m_handleSize * 0.5, m_handleSize * 0.5), QSizeF(m_handleSize, m_handleSize))); _gc.save(); _gc.setPen(Qt::NoPen); _gc.setBrush(assistantColor); _gc.drawPath(path); _gc.restore(); } } Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { QColor assistantColor = assistant->effectiveAssistantColor(); assistantColor.setAlpha(80); Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_handles) { QRectF ellipse(_converter.documentToView(*handle) - QPointF(m_handleSize * 0.5, m_handleSize * 0.5), QSizeF(m_handleSize, m_handleSize)); // render handles differently if it is the one being dragged. if (handle == m_handleDrag || handle == m_handleCombine) { QPen stroke(assistantColor, 4); _gc.save(); _gc.setPen(stroke); _gc.setBrush(Qt::NoBrush); _gc.drawEllipse(ellipse); _gc.restore(); } } } } void KisAssistantTool::removeAllAssistants() { m_canvas->viewManager()->canvasResourceProvider()->clearPerspectiveGrids(); m_canvas->paintingAssistantsDecoration()->removeAll(); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); m_canvas->updateCanvas(); m_canvas->paintingAssistantsDecoration()->deselectAssistant(); updateToolOptionsUI(); } void KisAssistantTool::loadAssistants() { KoFileDialog dialog(m_canvas->viewManager()->mainWindow(), KoFileDialog::OpenFile, "OpenAssistant"); dialog.setCaption(i18n("Select an Assistant")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(QStringList() << "application/x-krita-assistant", "application/x-krita-assistant"); QString filename = dialog.filename(); if (filename.isEmpty()) return; if (!QFileInfo(filename).exists()) return; QFile file(filename); file.open(QIODevice::ReadOnly); QByteArray data = file.readAll(); QXmlStreamReader xml(data); QMap handleMap; KisPaintingAssistantSP assistant; bool errors = false; while (!xml.atEnd()) { switch (xml.readNext()) { case QXmlStreamReader::StartElement: if (xml.name() == "handle") { if (assistant && !xml.attributes().value("ref").isEmpty()) { KisPaintingAssistantHandleSP handle = handleMap.value(xml.attributes().value("ref").toString().toInt()); if (handle) { assistant->addHandle(handle, HandleType::NORMAL); } else { errors = true; } } else { QString strId = xml.attributes().value("id").toString(), strX = xml.attributes().value("x").toString(), strY = xml.attributes().value("y").toString(); if (!strId.isEmpty() && !strX.isEmpty() && !strY.isEmpty()) { int id = strId.toInt(); double x = strX.toDouble(), y = strY.toDouble(); if (!handleMap.contains(id)) { handleMap.insert(id, new KisPaintingAssistantHandle(x, y)); } else { errors = true; } } else { errors = true; } } } else if (xml.name() == "assistant") { const KisPaintingAssistantFactory* factory = KisPaintingAssistantFactoryRegistry::instance()->get(xml.attributes().value("type").toString()); if (factory) { if (assistant) { errors = true; assistant.clear(); } assistant = toQShared(factory->createPaintingAssistant()); } else { errors = true; } // load custom shared assistant properties if ( xml.attributes().hasAttribute("useCustomColor")) { QStringRef useCustomColor = xml.attributes().value("useCustomColor"); bool usingColor = false; if (useCustomColor.toString() == "1") { usingColor = true; } assistant->setUseCustomColor(usingColor); } if ( xml.attributes().hasAttribute("useCustomColor")) { QStringRef customColor = xml.attributes().value("customColor"); assistant->setAssistantCustomColor( KisDomUtils::qStringToQColor(customColor.toString()) ); } } if (assistant) { assistant->loadCustomXml(&xml); } break; case QXmlStreamReader::EndElement: if (xml.name() == "assistant") { if (assistant) { if (assistant->handles().size() == assistant->numHandles()) { if (assistant->id() == "vanishing point"){ //ideally we'd save and load side-handles as well, but this is all I've got// QPointF pos = *assistant->handles()[0]; assistant->addHandle(new KisPaintingAssistantHandle(pos+QPointF(-70,0)), HandleType::SIDE); assistant->addHandle(new KisPaintingAssistantHandle(pos+QPointF(-140,0)), HandleType::SIDE); assistant->addHandle(new KisPaintingAssistantHandle(pos+QPointF(70,0)), HandleType::SIDE); assistant->addHandle(new KisPaintingAssistantHandle(pos+QPointF(140,0)), HandleType::SIDE); } m_canvas->paintingAssistantsDecoration()->addAssistant(assistant); KisAbstractPerspectiveGrid* grid = dynamic_cast(assistant.data()); if (grid) { m_canvas->viewManager()->canvasResourceProvider()->addPerspectiveGrid(grid); } } else { errors = true; } assistant.clear(); } } break; default: break; } } if (assistant) { errors = true; assistant.clear(); } if (xml.hasError()) { QMessageBox::warning(0, i18nc("@title:window", "Krita"), xml.errorString()); } if (errors) { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Errors were encountered. Not all assistants were successfully loaded.")); } m_handles = m_canvas->paintingAssistantsDecoration()->handles(); m_canvas->updateCanvas(); } void KisAssistantTool::saveAssistants() { if (m_handles.isEmpty()) return; QByteArray data; QXmlStreamWriter xml(&data); xml.writeStartDocument(); xml.writeStartElement("paintingassistant"); xml.writeAttribute("color", KisDomUtils::qColorToQString( m_canvas->paintingAssistantsDecoration()->globalAssistantsColor())); // global color if no custom color used xml.writeStartElement("handles"); QMap handleMap; Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_handles) { int id = handleMap.size(); handleMap.insert(handle, id); xml.writeStartElement("handle"); //xml.writeAttribute("type", handle->handleType()); xml.writeAttribute("id", QString::number(id)); xml.writeAttribute("x", QString::number(double(handle->x()), 'f', 3)); xml.writeAttribute("y", QString::number(double(handle->y()), 'f', 3)); xml.writeEndElement(); } xml.writeEndElement(); xml.writeStartElement("assistants"); Q_FOREACH (const KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { xml.writeStartElement("assistant"); xml.writeAttribute("type", assistant->id()); xml.writeAttribute("useCustomColor", QString::number(assistant->useCustomColor())); xml.writeAttribute("customColor", KisDomUtils::qColorToQString(assistant->assistantCustomColor())); // custom assistant properties like angle density on vanishing point assistant->saveCustomXml(&xml); // handle information xml.writeStartElement("handles"); Q_FOREACH (const KisPaintingAssistantHandleSP handle, assistant->handles()) { xml.writeStartElement("handle"); xml.writeAttribute("ref", QString::number(handleMap.value(handle))); xml.writeEndElement(); } xml.writeEndElement(); xml.writeEndElement(); } xml.writeEndElement(); xml.writeEndElement(); xml.writeEndDocument(); KoFileDialog dialog(m_canvas->viewManager()->mainWindow(), KoFileDialog::SaveFile, "OpenAssistant"); dialog.setCaption(i18n("Save Assistant")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(QStringList() << "application/x-krita-assistant", "application/x-krita-assistant"); QString filename = dialog.filename(); if (filename.isEmpty()) return; QFile file(filename); file.open(QIODevice::WriteOnly); file.write(data); } QWidget *KisAssistantTool::createOptionWidget() { if (!m_optionsWidget) { m_optionsWidget = new QWidget; m_options.setupUi(m_optionsWidget); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(m_optionsWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); m_optionsWidget->layout()->addWidget(specialSpacer); m_options.loadAssistantButton->setIcon(KisIconUtils::loadIcon("document-open")); m_options.saveAssistantButton->setIcon(KisIconUtils::loadIcon("document-save")); m_options.deleteAllAssistantsButton->setIcon(KisIconUtils::loadIcon("edit-delete")); QList assistants; Q_FOREACH (const QString& key, KisPaintingAssistantFactoryRegistry::instance()->keys()) { QString name = KisPaintingAssistantFactoryRegistry::instance()->get(key)->name(); assistants << KoID(key, name); } std::sort(assistants.begin(), assistants.end(), KoID::compareNames); Q_FOREACH(const KoID &id, assistants) { m_options.availableAssistantsComboBox->addItem(id.name(), id.id()); } connect(m_options.saveAssistantButton, SIGNAL(clicked()), SLOT(saveAssistants())); connect(m_options.loadAssistantButton, SIGNAL(clicked()), SLOT(loadAssistants())); connect(m_options.deleteAllAssistantsButton, SIGNAL(clicked()), SLOT(removeAllAssistants())); connect(m_options.assistantsColor, SIGNAL(changed(QColor)), SLOT(slotGlobalAssistantsColorChanged(QColor))); connect(m_options.assistantsGlobalOpacitySlider, SIGNAL(valueChanged(int)), SLOT(slotGlobalAssistantOpacityChanged())); connect(m_options.vanishingPointAngleSpinbox, SIGNAL(valueChanged(double)), this, SLOT(slotChangeVanishingPointAngle(double))); //ENTER_FUNCTION() << ppVar(m_canvas) << ppVar(m_canvas && m_canvas->paintingAssistantsDecoration()); // initialize UI elements with existing data if possible if (m_canvas && m_canvas->paintingAssistantsDecoration()) { const QColor color = m_canvas->paintingAssistantsDecoration()->globalAssistantsColor(); QColor opaqueColor = color; opaqueColor.setAlpha(255); //ENTER_FUNCTION() << ppVar(opaqueColor); m_options.assistantsColor->setColor(opaqueColor); m_options.customAssistantColorButton->setColor(opaqueColor); m_options.assistantsGlobalOpacitySlider->setValue(color.alphaF() * 100.0); } else { m_options.assistantsColor->setColor(QColor(176, 176, 176, 255)); // grey default for all assistants m_options.assistantsGlobalOpacitySlider->setValue(100); // 100% } m_options.assistantsGlobalOpacitySlider->setPrefix(i18n("Opacity: ")); m_options.assistantsGlobalOpacitySlider->setSuffix(" %"); // custom color of selected assistant m_options.customColorOpacitySlider->setValue(100); // 100% m_options.customColorOpacitySlider->setPrefix(i18n("Opacity: ")); m_options.customColorOpacitySlider->setSuffix(" %"); connect(m_options.useCustomAssistantColor, SIGNAL(clicked(bool)), this, SLOT(slotUpdateCustomColor())); connect(m_options.customAssistantColorButton, SIGNAL(changed(QColor)), this, SLOT(slotUpdateCustomColor())); connect(m_options.customColorOpacitySlider, SIGNAL(valueChanged(int)), SLOT(slotCustomOpacityChanged())); m_options.vanishingPointAngleSpinbox->setPrefix(i18n("Density: ")); m_options.vanishingPointAngleSpinbox->setSuffix(QChar(Qt::Key_degree)); m_options.vanishingPointAngleSpinbox->setRange(1.0, 180.0); m_options.vanishingPointAngleSpinbox->setSingleStep(0.5); m_options.vanishingPointAngleSpinbox->setVisible(false); } updateToolOptionsUI(); return m_optionsWidget; } void KisAssistantTool::slotGlobalAssistantsColorChanged(const QColor& setColor) { // color and alpha are stored separately, so we need to merge the values before sending it on int oldAlpha = m_canvas->paintingAssistantsDecoration()->globalAssistantsColor().alpha(); QColor newColor = setColor; newColor.setAlpha(oldAlpha); m_canvas->paintingAssistantsDecoration()->setGlobalAssistantsColor(newColor); m_canvas->paintingAssistantsDecoration()->uncache(); m_canvas->canvasWidget()->update(); } void KisAssistantTool::slotGlobalAssistantOpacityChanged() { QColor newColor = m_canvas->paintingAssistantsDecoration()->globalAssistantsColor(); qreal newOpacity = m_options.assistantsGlobalOpacitySlider->value() * 0.01 * 255.0; newColor.setAlpha(int(newOpacity)); m_canvas->paintingAssistantsDecoration()->setGlobalAssistantsColor(newColor); m_canvas->paintingAssistantsDecoration()->uncache(); m_canvas->canvasWidget()->update(); } void KisAssistantTool::slotUpdateCustomColor() { // get the selected assistant and change the angle value KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant(); if (m_selectedAssistant) { m_selectedAssistant->setUseCustomColor(m_options.useCustomAssistantColor->isChecked()); // changing color doesn't keep alpha, so update that before we send it on QColor newColor = m_options.customAssistantColorButton->color(); newColor.setAlpha(m_selectedAssistant->assistantCustomColor().alpha()); m_selectedAssistant->setAssistantCustomColor(newColor); m_selectedAssistant->uncache(); } updateToolOptionsUI(); m_canvas->canvasWidget()->update(); } void KisAssistantTool::slotCustomOpacityChanged() { KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant(); if (m_selectedAssistant) { QColor newColor = m_selectedAssistant->assistantCustomColor(); qreal newOpacity = m_options.customColorOpacitySlider->value() * 0.01 * 255.0; newColor.setAlpha(int(newOpacity)); m_selectedAssistant->setAssistantCustomColor(newColor); m_selectedAssistant->uncache(); } // this forces the canvas to refresh to see the changes immediately m_canvas->paintingAssistantsDecoration()->uncache(); m_canvas->canvasWidget()->update(); }