1 /*
2  * Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
3  * Copyright (c) 2010 Geoffry Song <goffrie@gmail.com>
4  * Copyright (c) 2014 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
5  * Copyright (c) 2017 Scott Petrovic <scottpetrovic@gmail.com>
6  *
7  *  This library is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU Lesser General Public License as published by
9  *  the Free Software Foundation; version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 
22 #include "ParallelRulerAssistant.h"
23 
24 #include "kis_debug.h"
25 #include <klocalizedstring.h>
26 
27 #include <QPainter>
28 #include <QPainterPath>
29 #include <QLinearGradient>
30 #include <QTransform>
31 
32 #include <kis_canvas2.h>
33 #include <kis_coordinates_converter.h>
34 #include <kis_algebra_2d.h>
35 
36 #include <math.h>
37 
ParallelRulerAssistant()38 ParallelRulerAssistant::ParallelRulerAssistant()
39     : KisPaintingAssistant("parallel ruler", i18n("Parallel Ruler assistant"))
40     , m_followBrushPosition(false)
41     , m_adjustedPositionValid(false)
42 {
43 }
44 
clone(QMap<KisPaintingAssistantHandleSP,KisPaintingAssistantHandleSP> & handleMap) const45 KisPaintingAssistantSP ParallelRulerAssistant::clone(QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> &handleMap) const
46 {
47     return KisPaintingAssistantSP(new ParallelRulerAssistant(*this, handleMap));
48 }
49 
ParallelRulerAssistant(const ParallelRulerAssistant & rhs,QMap<KisPaintingAssistantHandleSP,KisPaintingAssistantHandleSP> & handleMap)50 ParallelRulerAssistant::ParallelRulerAssistant(const ParallelRulerAssistant &rhs, QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> &handleMap)
51     : KisPaintingAssistant(rhs, handleMap)
52     , m_followBrushPosition(rhs.m_followBrushPosition)
53     , m_adjustedPositionValid(rhs.m_adjustedPositionValid)
54     , m_adjustedBrushPosition(rhs.m_adjustedBrushPosition)
55 {
56 }
57 
setAdjustedBrushPosition(const QPointF position)58 void ParallelRulerAssistant::setAdjustedBrushPosition(const QPointF position)
59 {
60     m_adjustedBrushPosition = position;
61     m_adjustedPositionValid = true;
62 }
63 
endStroke()64 void ParallelRulerAssistant::endStroke()
65 {
66     // Brush stroke ended, guides should follow the brush position again.
67     m_followBrushPosition = false;
68     m_adjustedPositionValid = false;
69 }
70 
71 
setFollowBrushPosition(bool follow)72 void ParallelRulerAssistant::setFollowBrushPosition(bool follow)
73 {
74     m_followBrushPosition = follow;
75 }
76 
project(const QPointF & pt,const QPointF & strokeBegin)77 QPointF ParallelRulerAssistant::project(const QPointF& pt, const QPointF& strokeBegin)
78 {
79     Q_ASSERT(isAssistantComplete());
80 
81     //code nicked from the perspective ruler.
82     qreal dx = pt.x() - strokeBegin.x();
83     qreal dy = pt.y() - strokeBegin.y();
84 
85     if (dx * dx + dy * dy < 4.0) {
86         return strokeBegin; // allow some movement before snapping
87     }
88 
89     //dbgKrita<<strokeBegin<< ", " <<*handles()[0];
90     QLineF snapLine = QLineF(*handles()[0], *handles()[1]);
91     QPointF translation = (*handles()[0]-strokeBegin)*-1.0;
92     snapLine = snapLine.translated(translation);
93 
94     dx = snapLine.dx();
95     dy = snapLine.dy();
96 
97     const qreal
98             dx2 = dx * dx,
99             dy2 = dy * dy,
100             invsqrlen = 1.0 / (dx2 + dy2);
101     QPointF r(dx2 * pt.x() + dy2 * snapLine.x1() + dx * dy * (pt.y() - snapLine.y1()),
102               dx2 * snapLine.y1() + dy2 * pt.y() + dx * dy * (pt.x() - snapLine.x1()));
103     r *= invsqrlen;
104     return r;
105     //return pt;
106 }
107 
adjustPosition(const QPointF & pt,const QPointF & strokeBegin)108 QPointF ParallelRulerAssistant::adjustPosition(const QPointF& pt, const QPointF& strokeBegin)
109 {
110     return project(pt, strokeBegin);
111 }
112 
drawAssistant(QPainter & gc,const QRectF & updateRect,const KisCoordinatesConverter * converter,bool cached,KisCanvas2 * canvas,bool assistantVisible,bool previewVisible)113 void ParallelRulerAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible)
114 {
115     gc.save();
116     gc.resetTransform();
117     QPointF mousePos(0,0);
118 
119     if (canvas){
120         //simplest, cheapest way to get the mouse-position//
121         mousePos= canvas->canvasWidget()->mapFromGlobal(QCursor::pos());
122     }
123     else {
124         //...of course, you need to have access to a canvas-widget for that.//
125         mousePos = QCursor::pos();//this'll give an offset//
126         dbgFile<<"canvas does not exist in ruler, you may have passed arguments incorrectly:"<<canvas;
127     }
128 
129     if (isAssistantComplete() && isSnappingActive() && previewVisible==true) {
130         //don't draw if invalid.
131         QTransform initialTransform = converter->documentToWidgetTransform();
132         QLineF snapLine= QLineF(initialTransform.map(*handles()[0]), initialTransform.map(*handles()[1]));
133 
134         if (m_followBrushPosition && m_adjustedPositionValid) {
135             mousePos = initialTransform.map(m_adjustedBrushPosition);
136         }
137 
138         QPointF translation = (initialTransform.map(*handles()[0])-mousePos)*-1.0;
139         snapLine= snapLine.translated(translation);
140 
141         QRect viewport= gc.viewport();
142         KisAlgebra2D::intersectLineRect(snapLine, viewport);
143 
144 
145         QPainterPath path;
146         path.moveTo(snapLine.p1());
147         path.lineTo(snapLine.p2());
148 
149         drawPreview(gc, path);//and we draw the preview.
150     }
151     gc.restore();
152 
153     KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible);
154 
155 }
156 
drawCache(QPainter & gc,const KisCoordinatesConverter * converter,bool assistantVisible)157 void ParallelRulerAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible)
158 {
159     if (assistantVisible == false || !isAssistantComplete()){
160         return;
161     }
162 
163     QTransform initialTransform = converter->documentToWidgetTransform();
164 
165     // Draw the line
166     QPointF p1 = *handles()[0];
167     QPointF p2 = *handles()[1];
168 
169     gc.setTransform(initialTransform);
170     QPainterPath path;
171     path.moveTo(p1);
172     path.lineTo(p2);
173     drawPath(gc, path, isSnappingActive());
174 
175 }
176 
getEditorPosition() const177 QPointF ParallelRulerAssistant::getEditorPosition() const
178 {
179     return (*handles()[0] + *handles()[1]) * 0.5;
180 }
181 
isAssistantComplete() const182 bool ParallelRulerAssistant::isAssistantComplete() const
183 {
184     return handles().size() >= 2;
185 }
186 
ParallelRulerAssistantFactory()187 ParallelRulerAssistantFactory::ParallelRulerAssistantFactory()
188 {
189 }
190 
~ParallelRulerAssistantFactory()191 ParallelRulerAssistantFactory::~ParallelRulerAssistantFactory()
192 {
193 }
194 
id() const195 QString ParallelRulerAssistantFactory::id() const
196 {
197     return "parallel ruler";
198 }
199 
name() const200 QString ParallelRulerAssistantFactory::name() const
201 {
202     return i18n("Parallel Ruler");
203 }
204 
createPaintingAssistant() const205 KisPaintingAssistant* ParallelRulerAssistantFactory::createPaintingAssistant() const
206 {
207     return new ParallelRulerAssistant;
208 }
209