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 }