File indexing completed on 2024-06-23 04:28:29

0001 /*
0002  *  kis_tool_transform.h - part of Krita
0003  *
0004  *  SPDX-FileCopyrightText: 2004 Boudewijn Rempt <boud@valdyas.org>
0005  *  SPDX-FileCopyrightText: 2005 C. Boemann <cbo@boemann.dk>
0006  *  SPDX-FileCopyrightText: 2010 Marc Pegon <pe.marc@free.fr>
0007  *
0008  *  SPDX-License-Identifier: GPL-2.0-or-later
0009  */
0010 
0011 #ifndef KIS_TOOL_TRANSFORM_H_
0012 #define KIS_TOOL_TRANSFORM_H_
0013 
0014 #include <kis_icon.h>
0015 
0016 #include <QPoint>
0017 #include <QPointF>
0018 #include <QVector2D>
0019 #include <QVector3D>
0020 #include <QButtonGroup>
0021 #include <QPointer>
0022 
0023 #include <QKeySequence>
0024 
0025 #include <KoToolFactoryBase.h>
0026 
0027 #include <kis_shape_selection.h>
0028 #include <kis_undo_adapter.h>
0029 #include <kis_types.h>
0030 #include <flake/kis_node_shape.h>
0031 #include <kis_tool.h>
0032 #include <kis_canvas2.h>
0033 #include <kis_action.h>
0034 
0035 #include <KisToolPaintFactoryBase.h>
0036 
0037 
0038 #include "tool_transform_args.h"
0039 #include "KisToolChangesTracker.h"
0040 #include "kis_tool_transform_config_widget.h"
0041 #include "transform_transaction_properties.h"
0042 #include "kis_signal_auto_connection.h"
0043 
0044 #include "strokes/inplace_transform_stroke_strategy.h"
0045 
0046 class QTouchEvent;
0047 class KisTransformStrategyBase;
0048 class KisWarpTransformStrategy;
0049 class KisCageTransformStrategy;
0050 class KisLiquifyTransformStrategy;
0051 class KisFreeTransformStrategy;
0052 class KisPerspectiveTransformStrategy;
0053 class KisMeshTransformStrategy;
0054 
0055 
0056 /**
0057  * Transform tool
0058  * This tool offers several modes.
0059  * - Free Transform mode allows the user to translate, scale, shear, rotate and
0060  *   apply a perspective transformation to a selection or the whole canvas.
0061  * - Warp mode allows the user to warp the selection of the canvas by grabbing
0062  *   and moving control points placed on the image. The user can either work
0063  *   with default control points, like a grid whose density can be modified, or
0064  *   place the control points manually. The modifications made on the selected
0065  *   pixels are applied only when the user clicks the Apply button : the
0066  *   semi-transparent image displayed until the user click that button is only a
0067  *   preview.
0068  * - Cage transform is similar to warp transform with control points exactly
0069  *   placed on the outer boundary. The user draws a boundary polygon, the
0070  *   vertices of which become control points.
0071  * - Perspective transform applies a two-point perspective transformation. The
0072  *   user can manipulate the corners of the selection. If the vanishing points
0073  *   of the resulting quadrilateral are on screen, the user can manipulate those
0074  *   as well.
0075  * - Liquify transform transforms the selection by painting motions, as if the
0076  *   user was finger painting.
0077  */
0078 class KisToolTransform : public KisTool
0079 {
0080 
0081     Q_OBJECT
0082 
0083     Q_PROPERTY(TransformToolMode transformMode READ transformMode WRITE setTransformMode NOTIFY transformModeChanged)
0084 
0085     Q_PROPERTY(double translateX READ translateX WRITE setTranslateX NOTIFY freeTransformChanged)
0086     Q_PROPERTY(double translateY READ translateY WRITE setTranslateY NOTIFY freeTransformChanged)
0087 
0088     Q_PROPERTY(double rotateX READ rotateX WRITE setRotateX NOTIFY freeTransformChanged)
0089     Q_PROPERTY(double rotateY READ rotateY WRITE setRotateY NOTIFY freeTransformChanged)
0090     Q_PROPERTY(double rotateZ READ rotateZ WRITE setRotateZ NOTIFY freeTransformChanged)
0091 
0092     Q_PROPERTY(double scaleX READ scaleX WRITE setScaleX NOTIFY freeTransformChanged)
0093     Q_PROPERTY(double scaleY READ scaleY WRITE setScaleY NOTIFY freeTransformChanged)
0094 
0095     Q_PROPERTY(double shearX READ shearX WRITE setShearX NOTIFY freeTransformChanged)
0096     Q_PROPERTY(double shearY READ shearY WRITE setShearY NOTIFY freeTransformChanged)
0097 
0098     Q_PROPERTY(WarpType warpType READ warpType WRITE setWarpType NOTIFY warpTransformChanged)
0099     Q_PROPERTY(double warpFlexibility READ warpFlexibility WRITE setWarpFlexibility NOTIFY warpTransformChanged)
0100     Q_PROPERTY(int warpPointDensity READ warpPointDensity WRITE setWarpPointDensity NOTIFY warpTransformChanged)
0101 
0102 
0103 
0104 public:
0105     enum TransformToolMode {
0106         FreeTransformMode,
0107         WarpTransformMode,
0108         CageTransformMode,
0109         LiquifyTransformMode,
0110         PerspectiveTransformMode,
0111         MeshTransformMode
0112     };
0113     Q_ENUMS(TransformToolMode)
0114 
0115     enum WarpType {
0116         RigidWarpType,
0117         AffineWarpType,
0118         SimilitudeWarpType
0119     };
0120     Q_ENUMS(WarpType)
0121 
0122     KisToolTransform(KoCanvasBase * canvas);
0123     ~KisToolTransform() override;
0124 
0125     /**
0126      * @brief wantsAutoScroll
0127      * reimplemented from KoToolBase
0128      * there's an issue where autoscrolling with this tool never makes the
0129      * stroke end, so we return false here so that users don't get stuck with
0130      * the tool. See bug 362659
0131      * @return false
0132      */
0133     bool wantsAutoScroll() const override {
0134         return false;
0135     }
0136 
0137     QWidget* createOptionWidget() override;
0138 
0139     void mousePressEvent(KoPointerEvent *e) override;
0140     void mouseMoveEvent(KoPointerEvent *e) override;
0141     void mouseReleaseEvent(KoPointerEvent *e) override;
0142     void beginActionImpl(KoPointerEvent *event, bool usePrimaryAction, KisTool::AlternateAction action);
0143     void continueActionImpl(KoPointerEvent *event, bool usePrimaryAction, KisTool::AlternateAction action);
0144     void endActionImpl(KoPointerEvent *event, bool usePrimaryAction, KisTool::AlternateAction action);
0145     QMenu* popupActionsMenu() override;
0146 
0147     void activatePrimaryAction() override;
0148     void deactivatePrimaryAction() override;
0149     void beginPrimaryAction(KoPointerEvent *event) override;
0150     void continuePrimaryAction(KoPointerEvent *event) override;
0151     void endPrimaryAction(KoPointerEvent *event) override;
0152 
0153     void activateAlternateAction(AlternateAction action) override;
0154     void deactivateAlternateAction(AlternateAction action) override;
0155     void beginAlternateAction(KoPointerEvent *event, AlternateAction action) override;
0156     void continueAlternateAction(KoPointerEvent *event, AlternateAction action) override;
0157     void endAlternateAction(KoPointerEvent *event, AlternateAction action) override;
0158 
0159     void paint(QPainter& gc, const KoViewConverter &converter) override;
0160 
0161     void newActivationWithExternalSource(KisPaintDeviceSP externalSource) override;
0162 
0163     TransformToolMode transformMode() const;
0164 
0165     double translateX() const;
0166     double translateY() const;
0167 
0168     double rotateX() const;
0169     double rotateY() const;
0170     double rotateZ() const;
0171 
0172     double scaleX() const;
0173     double scaleY() const;
0174 
0175     double shearX() const;
0176     double shearY() const;
0177 
0178     WarpType warpType() const;
0179     double warpFlexibility() const;
0180     int warpPointDensity() const;
0181 
0182 public Q_SLOTS:
0183     void activate(const QSet<KoShape*> &shapes) override;
0184     void deactivate() override;
0185     // Applies the current transformation to the original paint device and commits it to the undo stack
0186     void applyTransform();
0187 
0188     void requestImageRecalculation();
0189 
0190     void setTransformMode( KisToolTransform::TransformToolMode newMode );
0191 
0192     void setTranslateX(double translateX);
0193     void setTranslateY(double translateY);
0194 
0195     void setRotateX(double rotation);
0196     void setRotateY(double rotation);
0197     void setRotateZ(double rotation);
0198 
0199     void setScaleX(double scaleX);
0200     void setScaleY(double scaleY);
0201 
0202     void setShearX(double shearX);
0203     void setShearY(double shearY);
0204 
0205     void setWarpType(WarpType type);
0206     void setWarpFlexibility(double flexibility);
0207     void setWarpPointDensity(int density);
0208 
0209 protected Q_SLOTS:
0210     void resetCursorStyle() override;
0211     void slotGlobalConfigChanged();
0212 
0213 Q_SIGNALS:
0214     void transformModeChanged();
0215     void freeTransformChanged();
0216     void warpTransformChanged();
0217 
0218 public Q_SLOTS:
0219     void requestUndoDuringStroke() override;
0220     void requestRedoDuringStroke() override;
0221     void requestStrokeEnd() override;
0222     void requestStrokeCancellation() override;
0223     void canvasUpdateRequested();
0224     void cursorOutlineUpdateRequested(const QPointF &imagePos);
0225 
0226     // Update the widget according to m_currentArgs
0227     void updateOptionWidget();
0228 
0229     void resetRotationCenterButtonsRequested();
0230     void imageTooBigRequested(bool value);
0231 
0232 private:
0233     QList<KisNodeSP> fetchNodesList(ToolTransformArgs::TransformMode mode, KisNodeSP root, bool isExternalSourcePresent);
0234     QScopedPointer<QMenu> m_contextMenu;
0235 
0236     void startStroke(ToolTransformArgs::TransformMode mode, bool forceReset);
0237     void endStroke();
0238     void cancelStroke();
0239 
0240 private:
0241     void outlineChanged();
0242     // Sets the cursor according to mouse position (doesn't take shearing into account well yet)
0243     void setFunctionalCursor();
0244     // Sets m_function according to mouse position and modifier
0245     void setTransformFunction(QPointF mousePos, Qt::KeyboardModifiers modifiers);
0246 
0247     void commitChanges();
0248 
0249     void initTransformMode(ToolTransformArgs::TransformMode mode);
0250     void initGuiAfterTransformMode();
0251 
0252     void initThumbnailImage(KisPaintDeviceSP previewDevice);
0253     void updateApplyResetAvailability();
0254 
0255 private:
0256     ToolTransformArgs m_currentArgs;
0257 
0258     // Set by newActivationWithExternalSource before starting a new stroke.
0259     // The source pixels for the next transform will be read from this device.
0260     KisPaintDeviceSP m_externalSourceForNextActivation;
0261 
0262     bool m_actuallyMoveWhileSelected {false}; // true <=> selection has been moved while clicked
0263 
0264     KisPaintDeviceSP m_selectedPortionCache;
0265     KisStrokeId m_strokeId;
0266     void *m_strokeStrategyCookie {0};
0267     bool m_currentlyUsingOverlayPreviewStyle {false};
0268     bool m_preferOverlayPreviewStyle {false};
0269     bool m_forceLodMode {false};
0270 
0271 
0272     QPainterPath m_selectionPath; // original (unscaled) selection outline, used for painting decorations
0273 
0274     KisToolTransformConfigWidget *m_optionsWidget {0};
0275     QPointer<KisCanvas2> m_canvas;
0276 
0277     TransformTransactionProperties m_transaction;
0278     KisToolChangesTracker m_changesTracker;
0279     KisSignalAutoConnectionsStore m_actionConnections;
0280 
0281 
0282     /// actions for the context click menu
0283     KisAction* warpAction {0};
0284     KisAction* meshAction {0};
0285     KisAction* liquifyAction {0};
0286     KisAction* cageAction {0};
0287     KisAction* freeTransformAction {0};
0288     KisAction* perspectiveAction {0};
0289     KisAction* applyTransformation {0};
0290     KisAction* resetTransformation {0};
0291 
0292     // a few extra context click options if free transform is active
0293     KisAction* mirrorHorizontalAction {0};
0294     KisAction* mirrorVerticalAction {0};
0295     KisAction* rotateNinetyCWAction {0};
0296     KisAction* rotateNinetyCCWAction {0};
0297 
0298 
0299 
0300 
0301     /**
0302      * This artificial rect is used to store the image to flake
0303      * transformation. We check against this rect to get to know
0304      * whether zoom has changed.
0305      */
0306     QRectF m_refRect;
0307 
0308     QScopedPointer<KisWarpTransformStrategy> m_warpStrategy;
0309     QScopedPointer<KisCageTransformStrategy> m_cageStrategy;
0310     QScopedPointer<KisLiquifyTransformStrategy> m_liquifyStrategy;
0311     QScopedPointer<KisMeshTransformStrategy> m_meshStrategy;
0312     QScopedPointer<KisFreeTransformStrategy> m_freeStrategy;
0313     QScopedPointer<KisPerspectiveTransformStrategy> m_perspectiveStrategy;
0314     KisTransformStrategyBase* currentStrategy() const;
0315 
0316     QPainterPath m_cursorOutline;
0317 
0318     KisAsynchronousStrokeUpdateHelper m_asyncUpdateHelper;
0319 
0320 private Q_SLOTS:
0321     void slotTrackerChangedConfig(KisToolChangesTrackerDataSP status);
0322     void slotUiChangedConfig(bool needsPreviewRecalculation);
0323     void slotApplyTransform();
0324     void slotResetTransform(ToolTransformArgs::TransformMode mode);
0325     void slotCancelTransform();
0326     void slotRestartTransform();
0327     void slotRestartAndContinueTransform();
0328     void slotEditingFinished();
0329 
0330     void slotMoveDiscreteUp();
0331     void slotMoveDiscreteUpMore();
0332     void slotMoveDiscreteDown();
0333     void slotMoveDiscreteDownMore();
0334     void slotMoveDiscreteLeft();
0335     void slotMoveDiscreteLeftMore();
0336     void slotMoveDiscreteRight();
0337     void slotMoveDiscreteRightMore();
0338 
0339     void slotTransactionGenerated(TransformTransactionProperties transaction, ToolTransformArgs args, void *strokeStrategyCookie);
0340     void slotPreviewDeviceGenerated(KisPaintDeviceSP device);
0341 
0342     // context menu options for updating the transform type
0343     // this is to help with discoverability since come people can't find the tool options
0344     void slotUpdateToWarpType();
0345     void slotUpdateToPerspectiveType();
0346     void slotUpdateToFreeTransformType();
0347     void slotUpdateToLiquifyType();
0348     void slotUpdateToMeshType();
0349     void slotUpdateToCageType();
0350 };
0351 
0352 class KisToolTransformFactory : public KisToolPaintFactoryBase
0353 {
0354 public:
0355 
0356     KisToolTransformFactory()
0357             : KisToolPaintFactoryBase("KisToolTransform") {
0358         setToolTip(i18n("Transform a layer or a selection"));
0359         setSection(ToolBoxSection::Transform);
0360         setIconName(koIconNameCStr("krita_tool_transform"));
0361         setShortcut(QKeySequence(Qt::CTRL + Qt::Key_T));
0362         setPriority(2);
0363         setActivationShapeId(KRITA_TOOL_ACTIVATION_ID);
0364     }
0365 
0366     ~KisToolTransformFactory() override {}
0367 
0368     KoToolBase * createTool(KoCanvasBase *canvas) override {
0369         return new KisToolTransform(canvas);
0370     }
0371 
0372     QList<QAction *> createActionsImpl() override;
0373 };
0374 
0375 
0376 
0377 #endif // KIS_TOOL_TRANSFORM_H_
0378