File indexing completed on 2024-05-19 04:29:03

0001 /*
0002  *  SPDX-FileCopyrightText: 2008 Cyrille Berger <cberger@cberger.net>
0003  *  SPDX-FileCopyrightText: 2017 Scott Petrovic <scottpetrovic@gmail.com>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #ifndef _KIS_PAINTING_ASSISTANT_H_
0009 #define _KIS_PAINTING_ASSISTANT_H_
0010 
0011 #include <QString>
0012 #include <QPointF>
0013 #include <QRect>
0014 #include <QFile>
0015 #include <QObject>
0016 #include <QColor>
0017 #include <QXmlStreamWriter>
0018 #include <QMap>
0019 
0020 #include <kritaui_export.h>
0021 #include <kis_shared.h>
0022 #include <kis_types.h>
0023 
0024 class QPainter;
0025 class QRect;
0026 class QRectF;
0027 class KoStore;
0028 class KisCoordinatesConverter;
0029 class KisCanvas2;
0030 class QDomDocument;
0031 class QDomElement;
0032 
0033 #include <kis_shared_ptr.h>
0034 #include <KoGenericRegistry.h>
0035 
0036 class KisPaintingAssistantHandle;
0037 typedef KisSharedPtr<KisPaintingAssistantHandle> KisPaintingAssistantHandleSP;
0038 class KisPaintingAssistant;
0039 class QPainterPath;
0040 
0041 enum HandleType {
0042     NORMAL,
0043     SIDE,
0044     CORNER,
0045     VANISHING_POINT,
0046     ANCHOR
0047 };
0048 
0049 
0050 /**
0051   * Represent an handle of the assistant, used to edit the parameters
0052   * of an assistants. Handles can be shared between assistants.
0053   */
0054 class KRITAUI_EXPORT KisPaintingAssistantHandle : public QPointF, public KisShared
0055 {
0056     friend class KisPaintingAssistant;
0057 
0058 public:
0059     KisPaintingAssistantHandle(double x, double y);
0060     explicit KisPaintingAssistantHandle(QPointF p);
0061     KisPaintingAssistantHandle(const KisPaintingAssistantHandle&);
0062     ~KisPaintingAssistantHandle();
0063     void mergeWith(KisPaintingAssistantHandleSP);
0064     void uncache();
0065     KisPaintingAssistantHandle& operator=(const QPointF&);
0066     void setType(char type);
0067     char handleType() const;
0068 
0069     /**
0070      * Returns the pointer to the "chief" assistant,
0071      * which is supposed to handle transformations of the
0072      * handle, when all the assistants are transformed
0073      */
0074     KisPaintingAssistant* chiefAssistant() const;
0075 
0076 private:
0077     void registerAssistant(KisPaintingAssistant*);
0078     void unregisterAssistant(KisPaintingAssistant*);
0079     bool containsAssistant(KisPaintingAssistant*) const;
0080 
0081 private:
0082     struct Private;
0083     Private* const d;
0084 };
0085 
0086 /**
0087  * A KisPaintingAssistant is an object that assist the drawing on the canvas.
0088  * With this class you can implement virtual equivalent to ruler or compass.
0089  */
0090 class KRITAUI_EXPORT KisPaintingAssistant
0091 {
0092 public:
0093     KisPaintingAssistant(const QString& id, const QString& name);
0094     virtual ~KisPaintingAssistant();
0095     virtual KisPaintingAssistantSP clone(QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> &handleMap) const = 0;
0096     const QString& id() const;
0097     const QString& name() const;
0098     bool isSnappingActive() const;
0099     void setSnappingActive(bool set);
0100     //copy SharedData from an assistant to this
0101     void copySharedData(KisPaintingAssistantSP assistant);
0102 
0103 
0104     /**
0105      * Adjust the position given in parameter.
0106      * @param point the coordinates in point in the document reference
0107      * @param strokeBegin the coordinates of the beginning of the stroke
0108      * @param snapToAny because now assistants can be composited out of multiple inside assistants.
0109      *         snapToAny true means that you can use any of the inside assistant, while it being false
0110      *         means you should use the last used one. The logic determining when it happens (first stroke etc.)
0111      *         is in the decoration, so those two options are enough.
0112      * @param moveThresholdPt the threshold for the "move" of the cursor measured in pt
0113      *                        (usually equals to 2px in screen coordinates converted to pt)
0114      */
0115     virtual QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin, bool snapToAny, qreal moveThresholdPt) = 0;
0116     virtual void adjustLine(QPointF& point, QPointF& strokeBegin) = 0;
0117     virtual void endStroke();
0118     virtual void setAdjustedBrushPosition(const QPointF position);
0119     virtual void setFollowBrushPosition(bool follow);
0120     virtual QPointF getDefaultEditorPosition() const = 0; // Returns standard editor widget position for this assistant
0121     virtual QPointF getEditorPosition() const; // Returns editor widget position in document-space coordinates.
0122     virtual int numHandles() const = 0;
0123 
0124     /**
0125      * @brief canBeLocal
0126      * @return if the assistant can be potentially a "local assistant" (limited to rectangular area) or not
0127      */
0128     virtual bool canBeLocal() const;
0129     /**
0130      * @brief isLocal
0131      * @return if the assistant is limited to a rectangular area or not
0132      */
0133     bool isLocal() const;
0134     /**
0135      * @brief setLocal
0136      * @param value set the indication if the assistant is limited to a rectangular area or not
0137      */
0138     void setLocal(bool value);
0139 
0140     /**
0141      * @brief isLocked
0142      * @return if the assistant is locked (= cannot be moved, or edited in any way), or not
0143      */
0144     bool isLocked();
0145     /**
0146      * @brief setLocked
0147      * @param value set the indication if the assistant is locked (= cannot be moved, or edited in any way) or not
0148      */
0149     void setLocked(bool value);
0150     /**
0151      * @brief isDuplicating
0152      * @return If the duplication button is pressed
0153      */
0154     /*The duplication button must be depressed when the user clicks it. This getter function indicates to the 
0155     render function when the button is clicked*/
0156     bool isDuplicating();
0157     /**
0158      * @brief setDuplicating
0159      * @param value setter function sets the indication that the duplication button is pressed
0160      */
0161     void setDuplicating(bool value);
0162 
0163     QPointF editorWidgetOffset();
0164     void setEditorWidgetOffset(QPointF offset);
0165 
0166     void replaceHandle(KisPaintingAssistantHandleSP _handle, KisPaintingAssistantHandleSP _with);
0167     void addHandle(KisPaintingAssistantHandleSP handle, HandleType type);
0168 
0169     QPointF viewportConstrainedEditorPosition(const KisCoordinatesConverter* converter, const QSize editorSize);
0170 
0171     QColor effectiveAssistantColor() const;
0172     bool useCustomColor();
0173     void setUseCustomColor(bool useCustomColor);
0174     void setAssistantCustomColor(QColor color);
0175     QColor assistantCustomColor();
0176     void setAssistantGlobalColorCache(const QColor &color);
0177 
0178     virtual void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter, bool cached, KisCanvas2 *canvas=0, bool assistantVisible=true, bool previewVisible=true);
0179     void uncache();
0180     const QList<KisPaintingAssistantHandleSP>& handles() const;
0181     QList<KisPaintingAssistantHandleSP> handles();
0182     const QList<KisPaintingAssistantHandleSP>& sideHandles() const;
0183     QList<KisPaintingAssistantHandleSP> sideHandles();
0184 
0185     QByteArray saveXml( QMap<KisPaintingAssistantHandleSP, int> &handleMap);
0186     virtual void saveCustomXml(QXmlStreamWriter* xml); //in case specific assistants have custom properties (like vanishing point)
0187 
0188     void loadXml(KoStore *store, QMap<int, KisPaintingAssistantHandleSP> &handleMap, QString path);
0189     virtual bool loadCustomXml(QXmlStreamReader* xml);
0190 
0191     void saveXmlList(QDomDocument& doc, QDomElement& assistantsElement, int count);
0192     void findPerspectiveAssistantHandleLocation();
0193     KisPaintingAssistantHandleSP oppHandleOne();
0194 
0195     /**
0196       * Get the topLeft, bottomLeft, topRight and BottomRight corners of the assistant
0197       * Some assistants like the perspective grid have custom logic built around certain handles
0198       */
0199     const KisPaintingAssistantHandleSP topLeft() const;
0200     KisPaintingAssistantHandleSP topLeft();
0201     const KisPaintingAssistantHandleSP topRight() const;
0202     KisPaintingAssistantHandleSP topRight();
0203     const KisPaintingAssistantHandleSP bottomLeft() const;
0204     KisPaintingAssistantHandleSP bottomLeft();
0205     const KisPaintingAssistantHandleSP bottomRight() const;
0206     KisPaintingAssistantHandleSP bottomRight();
0207     const KisPaintingAssistantHandleSP topMiddle() const;
0208     KisPaintingAssistantHandleSP topMiddle();
0209     const KisPaintingAssistantHandleSP rightMiddle() const;
0210     KisPaintingAssistantHandleSP rightMiddle();
0211     const KisPaintingAssistantHandleSP leftMiddle() const;
0212     KisPaintingAssistantHandleSP leftMiddle();
0213     const KisPaintingAssistantHandleSP bottomMiddle() const;
0214     KisPaintingAssistantHandleSP bottomMiddle();
0215 
0216 
0217     // calculates whether a point is near one of the corner points of the assistant
0218     // returns: a corner point from the perspective assistant if the given node is close
0219     // only called once in code when calculating the perspective assistant
0220     KisPaintingAssistantHandleSP closestCornerHandleFromPoint(QPointF point);
0221 
0222     // determines if two points are close to each other
0223     // only used by the nodeNearPoint function (perspective grid assistant).
0224     bool areTwoPointsClose(const QPointF& pointOne, const QPointF& pointTwo);
0225 
0226     /// determines if the assistant has enough handles to be considered created
0227     /// new assistants get in a "creation" phase where they are currently being made on the canvas
0228     /// it will return false if we are in the middle of creating the assistant.
0229     virtual bool isAssistantComplete() const;
0230 
0231     /// Transform the assistant using the given \p transform. Please note that \p transform
0232     /// should be in 'document' coordinate system.
0233     /// Used with image-wide transformations.
0234     virtual void transform(const QTransform &transform);
0235 
0236 public:
0237     /**
0238      * This will render the final output. The drawCache does rendering most of the time so be sure to check that
0239      */
0240     void drawPath(QPainter& painter, const QPainterPath& path, bool drawActive=true);
0241     void drawPreview(QPainter& painter, const QPainterPath& path);
0242     // draw a path in a red color, signalizing incorrect state
0243     void drawError(QPainter& painter, const QPainterPath& path);
0244     // draw a vanishing point marker
0245     void drawX(QPainter& painter, const QPointF& pt);
0246     static double norm2(const QPointF& p);
0247 
0248     void setDecorationThickness(int thickness);
0249 
0250 protected:
0251     explicit KisPaintingAssistant(const KisPaintingAssistant &rhs, QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> &handleMap);
0252 
0253     virtual QRect boundingRect() const;
0254 
0255     /// performance layer where the graphics can be drawn from a cache instead of generated every render update
0256     virtual void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) = 0;
0257 
0258     void initHandles(QList<KisPaintingAssistantHandleSP> _handles);
0259     QList<KisPaintingAssistantHandleSP> m_handles;
0260 
0261     QPointF pixelToView(const QPoint pixelCoords) const;
0262     /**
0263      * @brief Query the effective brush position to be used for preview lines.
0264      * This is inteded to be used for painting the dynamic preview lines for assistants
0265      * that feature them. Affected by setAdjustedBrushPosition() and setFollowBrushPosition().
0266      * @return the effective brush (cursor) position in widget coordinates
0267      */
0268     QPointF effectiveBrushPosition(const KisCoordinatesConverter *converter, KisCanvas2 *canvas) const;
0269 
0270     /**
0271      * @brief firstLocalHandle
0272      * Note: this doesn't guarantee it will be the topleft corner!
0273      * For that, use getLocalRect().topLeft()
0274      * The only purpose of those functions to exist is to be able to
0275      * put getLocalRect() function in the KisPaintingAssistant
0276      * instead of reimplementing it in every specific assistant.
0277      * @return the first handle of the rectangle of the limited area
0278      */
0279     virtual KisPaintingAssistantHandleSP firstLocalHandle() const;
0280     /**
0281      * @brief secondLocalHandle
0282      * Note: this doesn't guarantee it will be the bottomRight corner!
0283      * For that, use getLocalRect().bottomRight()
0284      * (and remember that for QRect bottomRight() works differently than for QRectF,
0285      * so don't convert to QRect before accessing the corner)
0286      * @return
0287      */
0288     virtual KisPaintingAssistantHandleSP secondLocalHandle() const;
0289     /**
0290      * @brief getLocalRect
0291      * The function deals with local handles not being topLeft and bottomRight
0292      * gracefully and returns a correct rectangle.
0293      * Thanks to that the user can place handles in a "wrong" order or move them around
0294      * but the local rectangle will still be correct.
0295      * @return the rectangle of the area that the assistant is limited to
0296      */
0297     QRectF getLocalRect() const;
0298 
0299 
0300 public:
0301     /// clones the list of assistants
0302     /// the originally shared handles will still be shared
0303     /// the cloned assistants do not share any handle with the original assistants
0304     static QList<KisPaintingAssistantSP> cloneAssistantList(const QList<KisPaintingAssistantSP> &list);
0305 
0306 protected:
0307     bool m_hasBeenInsideLocalRect {false};
0308 
0309 private:
0310     struct Private;
0311     Private* const d;
0312 
0313 };
0314 
0315 /**
0316  * Allow to create a painting assistant.
0317  */
0318 class KRITAUI_EXPORT KisPaintingAssistantFactory
0319 {
0320 public:
0321     KisPaintingAssistantFactory();
0322     virtual ~KisPaintingAssistantFactory();
0323     virtual QString id() const = 0;
0324     virtual QString name() const = 0;
0325     virtual KisPaintingAssistant* createPaintingAssistant() const = 0;
0326 
0327 };
0328 
0329 class KRITAUI_EXPORT KisPaintingAssistantFactoryRegistry : public KoGenericRegistry<KisPaintingAssistantFactory*>
0330 {
0331   public:
0332     KisPaintingAssistantFactoryRegistry();
0333     ~KisPaintingAssistantFactoryRegistry() override;
0334 
0335     static KisPaintingAssistantFactoryRegistry* instance();
0336 
0337 };
0338 
0339 #endif