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

0001 /*
0002  * SPDX-FileCopyrightText: 2008 Cyrille Berger <cberger@cberger.net>
0003  * SPDX-FileCopyrightText: 2010 Geoffry Song <goffrie@gmail.com>
0004  * SPDX-FileCopyrightText: 2017 Scott Petrovic <scottpetrovic@gmail.com>
0005  *
0006  *  SPDX-License-Identifier: LGPL-2.0-or-later
0007  */
0008 
0009 #include "EllipseAssistant.h"
0010 
0011 #include <klocalizedstring.h>
0012 #include "kis_debug.h"
0013 #include <QPainter>
0014 #include <QPainterPath>
0015 #include <QLinearGradient>
0016 #include <QTransform>
0017 
0018 #include <kis_canvas2.h>
0019 #include <kis_coordinates_converter.h>
0020 #include "kis_algebra_2d.h"
0021 
0022 #include <math.h>
0023 
0024 EllipseAssistant::EllipseAssistant()
0025         : KisPaintingAssistant("ellipse", i18n("Ellipse assistant"))
0026 {
0027 }
0028 
0029 EllipseAssistant::EllipseAssistant(const EllipseAssistant &rhs, QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> &handleMap)
0030     : KisPaintingAssistant(rhs, handleMap)
0031     , e(rhs.e)
0032 {
0033 }
0034 
0035 KisPaintingAssistantSP EllipseAssistant::clone(QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> &handleMap) const
0036 {
0037     return KisPaintingAssistantSP(new EllipseAssistant(*this, handleMap));
0038 }
0039 
0040 QPointF EllipseAssistant::project(const QPointF& pt) const
0041 {
0042     Q_ASSERT(isAssistantComplete());
0043     e.set(*handles()[0], *handles()[1], *handles()[2]);
0044     return e.project(pt);
0045 }
0046 
0047 QPointF EllipseAssistant::adjustPosition(const QPointF& pt, const QPointF& /*strokeBegin*/, const bool /*snapToAny*/, qreal /*moveThresholdPt*/)
0048 {
0049     return project(pt);
0050 
0051 }
0052 
0053 void EllipseAssistant::adjustLine(QPointF &point, QPointF &strokeBegin)
0054 {
0055     const QPointF p1 = point;
0056     const QPointF p2 = strokeBegin;
0057 
0058     Q_ASSERT(isAssistantComplete());
0059     e.set(*handles()[0], *handles()[1], *handles()[2]);
0060 
0061     QPointF p3 = e.project(p1);
0062     QPointF p4 = e.project(p2);
0063     point = p3;
0064     strokeBegin = p4;
0065 }
0066 
0067 void EllipseAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible)
0068 {
0069     gc.save();
0070     gc.resetTransform();
0071     QPoint mousePos;
0072     
0073     if (canvas){
0074         //simplest, cheapest way to get the mouse-position//
0075         mousePos= canvas->canvasWidget()->mapFromGlobal(QCursor::pos());
0076     }
0077     else {
0078         //...of course, you need to have access to a canvas-widget for that.//
0079         mousePos = QCursor::pos();//this'll give an offset//
0080         dbgFile<<"canvas does not exist in the ellipse assistant, you may have passed arguments incorrectly:"<<canvas;
0081     }
0082 
0083     QTransform initialTransform = converter->documentToWidgetTransform();
0084 
0085     if (isSnappingActive() && boundingRect().contains(initialTransform.inverted().map(mousePos), false) && previewVisible==true){
0086 
0087         if (isAssistantComplete()){
0088             if (e.set(*handles()[0], *handles()[1], *handles()[2])) {
0089                 // valid ellipse
0090                 gc.setTransform(initialTransform);
0091                 gc.setTransform(e.getInverse(), true);
0092                 QPainterPath path;
0093                 //path.moveTo(QPointF(-e.semiMajor(), 0)); path.lineTo(QPointF(e.semiMajor(), 0));
0094                 //path.moveTo(QPointF(0, -e.semiMinor())); path.lineTo(QPointF(0, e.semiMinor()));
0095                 // Draw the ellipse
0096                 path.addEllipse(QPointF(0, 0), e.semiMajor(), e.semiMinor());
0097                 drawPreview(gc, path);
0098             }
0099         }
0100     }
0101     gc.restore();
0102     KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible);
0103 
0104 }
0105 
0106 
0107 void EllipseAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible)
0108 {
0109 
0110     if (assistantVisible == false || handles().size() < 2){
0111         return;
0112     }
0113 
0114     QTransform initialTransform = converter->documentToWidgetTransform();
0115 
0116     if (handles().size() == 2) {
0117         // just draw the axis
0118         gc.setTransform(initialTransform);
0119         QPainterPath path;
0120         path.moveTo(*handles()[0]);
0121         path.lineTo(*handles()[1]);
0122         drawPath(gc, path, isSnappingActive());
0123         return;
0124     }
0125     if (e.set(*handles()[0], *handles()[1], *handles()[2])) {
0126         // valid ellipse
0127 
0128         gc.setTransform(initialTransform);
0129         gc.setTransform(e.getInverse(), true);
0130         QPainterPath path;
0131         path.moveTo(QPointF(-e.semiMajor(), 0)); path.lineTo(QPointF(e.semiMajor(), 0));
0132         path.moveTo(QPointF(0, -e.semiMinor())); path.lineTo(QPointF(0, e.semiMinor()));
0133         // Draw the ellipse
0134         path.addEllipse(QPointF(0, 0), e.semiMajor(), e.semiMinor());
0135         drawPath(gc, path, isSnappingActive());
0136     }
0137 }
0138 
0139 QRect EllipseAssistant::boundingRect() const
0140 {
0141     if (!isAssistantComplete()) {
0142         return KisPaintingAssistant::boundingRect();
0143     }
0144 
0145     if (e.set(*handles()[0], *handles()[1], *handles()[2])) {
0146         return e.boundingRect().adjusted(-2, -2, 2, 2).toAlignedRect();
0147     } else {
0148         return QRect();
0149     }
0150 }
0151 
0152 QPointF EllipseAssistant::getDefaultEditorPosition() const
0153 {
0154     return (*handles()[0] + *handles()[1]) * 0.5;
0155 }
0156 
0157 bool EllipseAssistant::isAssistantComplete() const
0158 {
0159     return handles().size() >= 3;
0160 }
0161 
0162 void EllipseAssistant::transform(const QTransform &transform)
0163 {
0164     e.set(*handles()[0], *handles()[1], *handles()[2]);
0165 
0166     QPointF newAxes;
0167     QTransform newTransform;
0168 
0169     std::tie(newAxes, newTransform) = KisAlgebra2D::transformEllipse(QPointF(e.semiMajor(), e.semiMinor()), e.getInverse() * transform);
0170 
0171     const QPointF p1 = newTransform.map(QPointF(newAxes.x(), 0));
0172     const QPointF p2 = newTransform.map(QPointF(-newAxes.x(), 0));
0173     const QPointF p3 = newTransform.map(QPointF(0, newAxes.y()));
0174 
0175     *handles()[0] = p1;
0176     *handles()[1] = p2;
0177     *handles()[2] = p3;
0178 
0179     uncache();
0180 }
0181 
0182 EllipseAssistantFactory::EllipseAssistantFactory()
0183 {
0184 }
0185 
0186 EllipseAssistantFactory::~EllipseAssistantFactory()
0187 {
0188 }
0189 
0190 QString EllipseAssistantFactory::id() const
0191 {
0192     return "ellipse";
0193 }
0194 
0195 QString EllipseAssistantFactory::name() const
0196 {
0197     return i18n("Ellipse");
0198 }
0199 
0200 KisPaintingAssistant* EllipseAssistantFactory::createPaintingAssistant() const
0201 {
0202     return new EllipseAssistant;
0203 }