File indexing completed on 2024-12-22 04:14:25

0001 /*
0002  * SPDX-FileCopyrightText: 2008 Cyrille Berger <cberger@cberger.net>
0003  * SPDX-FileCopyrightText: 2010 Geoffry Song <goffrie@gmail.com>
0004  * SPDX-FileCopyrightText: 2014 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
0005  * SPDX-FileCopyrightText: 2017 Scott Petrovic <scottpetrovic@gmail.com>
0006  *
0007  *  SPDX-License-Identifier: LGPL-2.0-or-later
0008  */
0009 
0010 #include "ParallelRulerAssistant.h"
0011 
0012 #include "kis_debug.h"
0013 #include <klocalizedstring.h>
0014 
0015 #include <QPainter>
0016 #include <QPainterPath>
0017 #include <QLinearGradient>
0018 #include <QTransform>
0019 
0020 #include <kis_canvas2.h>
0021 #include <kis_coordinates_converter.h>
0022 #include <kis_algebra_2d.h>
0023 #include <kis_dom_utils.h>
0024 
0025 #include <math.h>
0026 
0027 ParallelRulerAssistant::ParallelRulerAssistant()
0028     : KisPaintingAssistant("parallel ruler", i18n("Parallel Ruler assistant"))
0029 {
0030 }
0031 
0032 KisPaintingAssistantSP ParallelRulerAssistant::clone(QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> &handleMap) const
0033 {
0034     return KisPaintingAssistantSP(new ParallelRulerAssistant(*this, handleMap));
0035 }
0036 
0037 ParallelRulerAssistant::ParallelRulerAssistant(const ParallelRulerAssistant &rhs, QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> &handleMap)
0038     : KisPaintingAssistant(rhs, handleMap)
0039 {
0040 }
0041 
0042 QPointF ParallelRulerAssistant::project(const QPointF& pt, const QPointF& strokeBegin, qreal /*moveThresholdPt*/)
0043 {
0044     Q_ASSERT(isAssistantComplete());
0045 
0046     if (isLocal() && isAssistantComplete()) {
0047         if (getLocalRect().contains(pt)) {
0048             m_hasBeenInsideLocalRect = true;
0049         } else if (isLocal() && !m_hasBeenInsideLocalRect) {
0050             return QPointF(qQNaN(), qQNaN());
0051         }
0052     }
0053 
0054     //dbgKrita<<strokeBegin<< ", " <<*handles()[0];
0055     QLineF snapLine = QLineF(*handles()[0], *handles()[1]);
0056     QPointF translation = (*handles()[0]-strokeBegin)*-1.0;
0057     snapLine = snapLine.translated(translation);
0058 
0059     qreal dx = snapLine.dx();
0060     qreal dy = snapLine.dy();
0061 
0062     const qreal
0063             dx2 = dx * dx,
0064             dy2 = dy * dy,
0065             invsqrlen = 1.0 / (dx2 + dy2);
0066     QPointF r(dx2 * pt.x() + dy2 * snapLine.x1() + dx * dy * (pt.y() - snapLine.y1()),
0067               dx2 * snapLine.y1() + dy2 * pt.y() + dx * dy * (pt.x() - snapLine.x1()));
0068     r *= invsqrlen;
0069     return r;
0070 }
0071 
0072 QPointF ParallelRulerAssistant::adjustPosition(const QPointF& pt, const QPointF& strokeBegin, const bool /*snapToAny*/, qreal moveThresholdPt)
0073 {
0074     return project(pt, strokeBegin, moveThresholdPt);
0075 }
0076 
0077 void ParallelRulerAssistant::adjustLine(QPointF &point, QPointF &strokeBegin)
0078 {
0079     point = project(point, strokeBegin, 0.0);
0080 }
0081 
0082 void ParallelRulerAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible)
0083 {
0084     gc.save();
0085     gc.resetTransform();
0086 
0087     QTransform initialTransform = converter->documentToWidgetTransform();
0088     QRectF local = getLocalRect();
0089     QRectF localTransformed = initialTransform.mapRect(local);
0090     const QRect viewport= gc.viewport();
0091     QPolygonF viewportAndLocal = !localTransformed.isEmpty() ? QPolygonF(QRectF(viewport)).intersected(localTransformed) : QRectF(viewport);
0092 
0093 
0094 
0095     if (assistantVisible && isLocal() && isAssistantComplete()) {
0096         QPainterPath path;
0097         // note: be careful; bottom and right only work with RectF, not Rect
0098         path.moveTo(initialTransform.map(local.topLeft()));
0099 
0100         path.lineTo(initialTransform.map(local.topRight()));
0101         path.lineTo(initialTransform.map(local.bottomRight()));
0102         path.lineTo(initialTransform.map(local.bottomLeft()));
0103         path.lineTo(initialTransform.map(local.topLeft()));
0104         drawPath(gc, path, isSnappingActive());//and we draw the preview.
0105     }
0106 
0107 
0108     if (isAssistantComplete() && isSnappingActive() && previewVisible==true) {
0109         //don't draw if invalid.
0110         QLineF snapLine= QLineF(initialTransform.map(*handles()[0]), initialTransform.map(*handles()[1]));
0111 
0112         QPointF mousePos = effectiveBrushPosition(converter, canvas);
0113 
0114         QPointF translation = (initialTransform.map(*handles()[0])-mousePos)*-1.0;
0115         snapLine= snapLine.translated(translation);
0116 
0117         KisAlgebra2D::cropLineToConvexPolygon(snapLine, viewportAndLocal, true, true);
0118 
0119 
0120         QPainterPath path;
0121         path.moveTo(snapLine.p1());
0122         path.lineTo(snapLine.p2());
0123 
0124         drawPreview(gc, path);//and we draw the preview.
0125     }
0126     gc.restore();
0127 
0128     KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible);
0129 
0130 }
0131 
0132 void ParallelRulerAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible)
0133 {
0134     if (assistantVisible == false || handles().size() < 2) {
0135         return;
0136     }
0137 
0138     QTransform initialTransform = converter->documentToWidgetTransform();
0139 
0140     // Draw the line
0141     QPointF p1 = *handles()[0];
0142     QPointF p2 = *handles()[1];
0143 
0144     gc.setTransform(initialTransform);
0145     QPainterPath path;
0146     path.moveTo(p1);
0147     path.lineTo(p2);
0148     drawPath(gc, path, isSnappingActive());
0149 
0150 }
0151 
0152 KisPaintingAssistantHandleSP ParallelRulerAssistant::firstLocalHandle() const
0153 {
0154     return handles().size() > 2 ? handles()[2] : 0;
0155 }
0156 
0157 KisPaintingAssistantHandleSP ParallelRulerAssistant::secondLocalHandle() const
0158 {
0159     return handles().size() > 3 ? handles()[3] : 0;
0160 }
0161 
0162 QPointF ParallelRulerAssistant::getDefaultEditorPosition() const
0163 {
0164     if (handles().size() > 1) {
0165         return (*handles()[0] + *handles()[1]) * 0.5;
0166     } else if (handles().size() > 0) {
0167         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(false, *handles()[0]);
0168         return *handles()[0];
0169     } else {
0170         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(false, QPointF(0, 0));
0171         return QPointF(0, 0);
0172     }
0173 }
0174 
0175 bool ParallelRulerAssistant::isAssistantComplete() const
0176 {
0177     return handles().size() >= numHandles();
0178 }
0179 
0180 bool ParallelRulerAssistant::canBeLocal() const
0181 {
0182     return true;
0183 }
0184 
0185 void ParallelRulerAssistant::saveCustomXml(QXmlStreamWriter *xml)
0186 {
0187     xml->writeStartElement("isLocal");
0188     xml->writeAttribute("value", KisDomUtils::toString( (int)this->isLocal()));
0189     xml->writeEndElement();
0190 }
0191 
0192 bool ParallelRulerAssistant::loadCustomXml(QXmlStreamReader *xml)
0193 {
0194     if (xml && xml->name() == "isLocal") {
0195         this->setLocal((bool)KisDomUtils::toInt(xml->attributes().value("value").toString()));
0196     }
0197     return true;
0198 }
0199 
0200 ParallelRulerAssistantFactory::ParallelRulerAssistantFactory()
0201 {
0202 }
0203 
0204 ParallelRulerAssistantFactory::~ParallelRulerAssistantFactory()
0205 {
0206 }
0207 
0208 QString ParallelRulerAssistantFactory::id() const
0209 {
0210     return "parallel ruler";
0211 }
0212 
0213 QString ParallelRulerAssistantFactory::name() const
0214 {
0215     return i18n("Parallel Ruler");
0216 }
0217 
0218 KisPaintingAssistant* ParallelRulerAssistantFactory::createPaintingAssistant() const
0219 {
0220     return new ParallelRulerAssistant;
0221 }