File indexing completed on 2024-05-26 04:32:04

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 "FisheyePointAssistant.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 
0024 #include <math.h>
0025 #include <limits>
0026 
0027 FisheyePointAssistant::FisheyePointAssistant()
0028     : KisPaintingAssistant("fisheye-point", i18n("Fish Eye Point assistant"))
0029 {
0030 }
0031 
0032 FisheyePointAssistant::FisheyePointAssistant(const FisheyePointAssistant &rhs, QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> &handleMap)
0033     : KisPaintingAssistant(rhs, handleMap)
0034     , e(rhs.e)
0035     , extraE(rhs.extraE)
0036 {
0037 }
0038 
0039 KisPaintingAssistantSP FisheyePointAssistant::clone(QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> &handleMap) const
0040 {
0041     return KisPaintingAssistantSP(new FisheyePointAssistant(*this, handleMap));
0042 }
0043 
0044 QPointF FisheyePointAssistant::project(const QPointF& pt, const QPointF& strokeBegin)
0045 {
0046     const static QPointF nullPoint(std::numeric_limits<qreal>::quiet_NaN(), std::numeric_limits<qreal>::quiet_NaN());
0047     Q_ASSERT(isAssistantComplete());
0048     e.set(*handles()[0], *handles()[1], *handles()[2]);
0049 
0050     //set the extrapolation ellipse.
0051     if (e.set(*handles()[0], *handles()[1], *handles()[2])){
0052         QLineF radius(*handles()[1], *handles()[0]);
0053         radius.setAngle(fmod(radius.angle()+180.0,360.0));
0054         QLineF radius2(*handles()[0], *handles()[1]);
0055         radius2.setAngle(fmod(radius2.angle()+180.0,360.0));
0056         if ( extraE.set(*handles()[0], *handles()[1],strokeBegin ) ) {
0057             return extraE.project(pt);
0058         } else if (extraE.set(radius.p1(), radius.p2(),strokeBegin)) {
0059             return extraE.project(pt);
0060         } else if (extraE.set(radius2.p1(), radius2.p2(),strokeBegin)){
0061             return extraE.project(pt);
0062         }
0063     }
0064 
0065     return nullPoint;
0066 
0067 }
0068 
0069 QPointF FisheyePointAssistant::adjustPosition(const QPointF& pt, const QPointF& strokeBegin, const bool /*snapToAny*/, qreal /*moveThresholdPt*/)
0070 {
0071     return project(pt, strokeBegin);
0072 }
0073 
0074 void FisheyePointAssistant::adjustLine(QPointF &point, QPointF &strokeBegin)
0075 {
0076     point = QPointF();
0077     strokeBegin = QPointF();
0078 }
0079 
0080 void FisheyePointAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible)
0081 {
0082     gc.save();
0083     gc.resetTransform();
0084 
0085     if (isSnappingActive() && previewVisible == true ) {
0086 
0087         if (isAssistantComplete()){
0088 
0089             QTransform initialTransform = converter->documentToWidgetTransform();
0090 
0091             if (e.set(*handles()[0], *handles()[1], *handles()[2])) {
0092                 QPointF mousePos = effectiveBrushPosition(converter, canvas);
0093                 if (extraE.set(*handles()[0], *handles()[1], initialTransform.inverted().map(mousePos))){
0094                     gc.setTransform(initialTransform);
0095                     gc.setTransform(e.getInverse(), true);
0096                     QPainterPath path;
0097                     // Draw the ellipse
0098                     path.addEllipse(QPointF(0, 0), extraE.semiMajor(), extraE.semiMinor());
0099                     drawPreview(gc, path);
0100                 }
0101                 QLineF radius(*handles()[1], *handles()[0]);
0102                 radius.setAngle(fmod(radius.angle()+180.0,360.0));
0103                 if (extraE.set(radius.p1(), radius.p2(), initialTransform.inverted().map(mousePos))){
0104                     gc.setTransform(initialTransform);
0105                     gc.setTransform(extraE.getInverse(), true);
0106                     QPainterPath path;
0107                     // Draw the ellipse
0108                     path.addEllipse(QPointF(0, 0), extraE.semiMajor(), extraE.semiMinor());
0109                     drawPreview(gc, path);
0110                 }
0111                 QLineF radius2(*handles()[0], *handles()[1]);
0112                 radius2.setAngle(fmod(radius2.angle()+180.0,360.0));
0113                 if (extraE.set(radius2.p1(), radius2.p2(), initialTransform.inverted().map(mousePos))){
0114                     gc.setTransform(initialTransform);
0115                     gc.setTransform(extraE.getInverse(), true);
0116                     QPainterPath path;
0117                     // Draw the ellipse
0118                     path.addEllipse(QPointF(0, 0), extraE.semiMajor(), extraE.semiMinor());
0119                     drawPreview(gc, path);
0120                 }
0121 
0122             }
0123         }
0124     }
0125     gc.restore();
0126 
0127     KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible);
0128 
0129 }
0130 
0131 void FisheyePointAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible)
0132 {
0133     if (assistantVisible == false){
0134         return;
0135     }
0136 
0137     QTransform initialTransform = converter->documentToWidgetTransform();
0138 
0139     if (handles().size() == 2) {
0140         // just draw the axis
0141         gc.setTransform(initialTransform);
0142         QPainterPath path;
0143         path.moveTo(*handles()[0]);
0144         path.lineTo(*handles()[1]);
0145         drawPath(gc, path, isSnappingActive());
0146         return;
0147     }
0148     if (e.set(*handles()[0], *handles()[1], *handles()[2])) {
0149         // valid ellipse
0150 
0151         gc.setTransform(initialTransform);
0152         gc.setTransform(e.getInverse(), true);
0153         QPainterPath path;
0154         //path.moveTo(QPointF(-e.semiMajor(), -e.semiMinor())); path.lineTo(QPointF(e.semiMajor(), -e.semiMinor()));
0155         path.moveTo(QPointF(-e.semiMajor(), -e.semiMinor())); path.lineTo(QPointF(-e.semiMajor(), e.semiMinor()));
0156         //path.moveTo(QPointF(-e.semiMajor(), e.semiMinor())); path.lineTo(QPointF(e.semiMajor(), e.semiMinor()));
0157         path.moveTo(QPointF(e.semiMajor(), -e.semiMinor())); path.lineTo(QPointF(e.semiMajor(), e.semiMinor()));
0158         path.moveTo(QPointF(-(e.semiMajor()*3), -e.semiMinor())); path.lineTo(QPointF(-(e.semiMajor()*3), e.semiMinor()));
0159         path.moveTo(QPointF((e.semiMajor()*3), -e.semiMinor())); path.lineTo(QPointF((e.semiMajor()*3), e.semiMinor()));
0160         path.moveTo(QPointF(-e.semiMajor(), 0)); path.lineTo(QPointF(e.semiMajor(), 0));
0161         //path.moveTo(QPointF(0, -e.semiMinor())); path.lineTo(QPointF(0, e.semiMinor()));
0162         // Draw the ellipse
0163         path.addEllipse(QPointF(0, 0), e.semiMajor(), e.semiMinor());
0164         drawPath(gc, path, isSnappingActive());
0165     }
0166 
0167 }
0168 
0169 QRect FisheyePointAssistant::boundingRect() const
0170 {
0171     if (!isAssistantComplete()) {
0172         return KisPaintingAssistant::boundingRect();
0173     }
0174 
0175     if (e.set(*handles()[0], *handles()[1], *handles()[2])) {
0176         return e.boundingRect().adjusted(-(e.semiMajor()*2), -2, (e.semiMajor()*2), 2).toAlignedRect();
0177     } else {
0178         return QRect();
0179     }
0180 }
0181 
0182 QPointF FisheyePointAssistant::getDefaultEditorPosition() const
0183 {
0184     return (*handles()[0] + *handles()[1]) * 0.5;
0185 }
0186 
0187 bool FisheyePointAssistant::isAssistantComplete() const
0188 {
0189     return handles().size() >= 3;
0190 }
0191 
0192 
0193 FisheyePointAssistantFactory::FisheyePointAssistantFactory()
0194 {
0195 }
0196 
0197 FisheyePointAssistantFactory::~FisheyePointAssistantFactory()
0198 {
0199 }
0200 
0201 QString FisheyePointAssistantFactory::id() const
0202 {
0203     return "fisheye-point";
0204 }
0205 
0206 QString FisheyePointAssistantFactory::name() const
0207 {
0208     return i18n("Fish Eye Point");
0209 }
0210 
0211 KisPaintingAssistant* FisheyePointAssistantFactory::createPaintingAssistant() const
0212 {
0213     return new FisheyePointAssistant;
0214 }