Pencil2D Animation
Download Community News Docs Contribute
  • Overview
  • Articles
  • Code
  •  
  • Class List
  • Class Index
  • Class Hierarchy
  • Class Members
  • File List
Loading...
Searching...
No Matches
  • core_lib
  • src
  • tool
movetool.cpp
1/*
2
3Pencil2D - Traditional Animation Software
4Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5Copyright (C) 2012-2020 Matthew Chiawen Chang
6
7This program is free software; you can redistribute it and/or
8modify it under the terms of the GNU General Public License
9as published by the Free Software Foundation; version 2 of the License.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16*/
17
18#include "movetool.h"
19
20#include <cassert>
21#include <QMessageBox>
22#include <QSettings>
23
24#include "pointerevent.h"
25#include "editor.h"
26#include "toolmanager.h"
27#include "strokeinterpolator.h"
28#include "selectionmanager.h"
29#include "overlaymanager.h"
30#include "undoredomanager.h"
31#include "scribblearea.h"
32#include "layervector.h"
33#include "layermanager.h"
34#include "layercamera.h"
35#include "mathutils.h"
36#include "vectorimage.h"
37
38MoveTool::MoveTool(QObject* parent) : TransformTool(parent)
39{
40}
41
42ToolType MoveTool::type() const
43{
44 return MOVE;
45}
46
47void MoveTool::loadSettings()
48{
49 mRotationIncrement = mEditor->preference()->getInt(SETTING::ROTATION_INCREMENT);
50 QSettings pencilSettings(PENCIL2D, PENCIL2D);
51
52 mPropertyUsed[TransformToolProperties::SHOWSELECTIONINFO_ENABLED] = { Layer::BITMAP, Layer::VECTOR };
53 mPropertyUsed[TransformToolProperties::ANTI_ALIASING_ENABLED] = { Layer::BITMAP };
54 QHash<int, PropertyInfo> info;
55
56 info[TransformToolProperties::SHOWSELECTIONINFO_ENABLED] = false;
57 info[TransformToolProperties::ANTI_ALIASING_ENABLED] = true;
58
59 toolProperties().insertProperties(info);
60 toolProperties().loadFrom(typeName(), pencilSettings);
61
62 if (toolProperties().requireMigration(pencilSettings, ToolProperties::VERSION_1)) {
63 toolProperties().setBaseValue(TransformToolProperties::SHOWSELECTIONINFO_ENABLED, pencilSettings.value("ShowSelectionInfo", false).toBool());
64 toolProperties().setBaseValue(TransformToolProperties::ANTI_ALIASING_ENABLED, pencilSettings.value("moveAA", true).toBool());
65 }
66
67 connect(mEditor->preference(), &PreferenceManager::optionChanged, this, &MoveTool::updateSettings);
68}
69
70QCursor MoveTool::cursor()
71{
72 MoveMode mode = MoveMode::NONE;
73 SelectionManager* selectMan = mEditor->select();
74 if (selectMan->somethingSelected())
75 {
76 mode = mEditor->select()->getMoveMode();
77 }
78 else if (mEditor->overlays()->anyOverlayEnabled())
79 {
80 mode = mPerspMode;
81 }
82
83 return cursor(mode);
84}
85
86void MoveTool::updateSettings(const SETTING setting)
87{
88 switch (setting)
89 {
90 case SETTING::ROTATION_INCREMENT:
91 mRotationIncrement = mEditor->preference()->getInt(SETTING::ROTATION_INCREMENT);
92 break;
93 case SETTING::OVERLAY_PERSPECTIVE1:
94 mEditor->overlays()->settingsUpdated(setting, mEditor->preference()->isOn(setting));
95 break;
96 case SETTING::OVERLAY_PERSPECTIVE2:
97 mEditor->overlays()->settingsUpdated(setting, mEditor->preference()->isOn(setting));
98 break;
99 case SETTING::OVERLAY_PERSPECTIVE3:
100 mEditor->overlays()->settingsUpdated(setting, mEditor->preference()->isOn(setting));
101 break;
102 default:
103 break;
104 }
105}
106
107void MoveTool::pointerPressEvent(PointerEvent* event)
108{
109 Layer* currentLayer = currentPaintableLayer();
110 if (currentLayer == nullptr) return;
111
112 if (mEditor->select()->somethingSelected())
113 {
114 beginInteraction(event->canvasPos(), event->modifiers(), currentLayer);
115 }
116 else if (mEditor->overlays()->anyOverlayEnabled())
117 {
118 LayerCamera* layerCam = mEditor->layers()->getCameraLayerBelow(mEditor->currentLayerIndex());
119 Q_ASSERT(layerCam);
120
121 mPerspMode = mEditor->overlays()->getMoveModeForPoint(event->canvasPos(), layerCam->getViewAtFrame(mEditor->currentFrame()));
122 mEditor->overlays()->setMoveMode(mPerspMode);
123 QPoint mapped = layerCam->getViewAtFrame(mEditor->currentFrame()).map(event->canvasPos()).toPoint();
124 mEditor->overlays()->updatePerspective(mapped);
125 }
126
127 mEditor->updateFrame();
128}
129
130void MoveTool::pointerMoveEvent(PointerEvent* event)
131{
132 Layer* currentLayer = currentPaintableLayer();
133 if (currentLayer == nullptr) return;
134
135 if (mScribbleArea->isPointerInUse()) // the user is also pressing the mouse (dragging)
136 {
137 transformSelection(event->canvasPos(), event->modifiers());
138
139 if (mEditor->overlays()->anyOverlayEnabled())
140 {
141 LayerCamera* layerCam = mEditor->layers()->getCameraLayerBelow(mEditor->currentLayerIndex());
142 Q_ASSERT(layerCam);
143 mEditor->overlays()->updatePerspective(layerCam->getViewAtFrame(mEditor->currentFrame()).map(event->canvasPos()));
144 }
145 if (mEditor->select()->somethingSelected())
146 {
147 transformSelection(event->canvasPos(), event->modifiers());
148 }
149 }
150 else
151 {
152 // the user is moving the mouse without pressing it
153 // update cursor to reflect selection corner interaction
154 mEditor->select()->setMoveModeForAnchorInRange(event->canvasPos());
155 if (mEditor->overlays()->anyOverlayEnabled())
156 {
157 LayerCamera *layerCam = mEditor->layers()->getCameraLayerBelow(mEditor->currentLayerIndex());
158 Q_ASSERT(layerCam);
159 mPerspMode = mEditor->overlays()->getMoveModeForPoint(event->canvasPos(), layerCam->getViewAtFrame(mEditor->currentFrame()));
160 }
161 mScribbleArea->updateToolCursor();
162
163 if (currentLayer->type() == Layer::VECTOR)
164 {
165 storeClosestVectorCurve(event->canvasPos(), currentLayer);
166 }
167 }
168 mEditor->updateFrame();
169}
170
171void MoveTool::pointerReleaseEvent(PointerEvent*)
172{
173 mEditor->undoRedo()->record(mUndoSaveState, typeName());
174
175 if (mEditor->overlays()->anyOverlayEnabled())
176 {
177 mEditor->overlays()->setMoveMode(MoveMode::NONE);
178 mPerspMode = MoveMode::NONE;
179 }
180
181 auto selectMan = mEditor->select();
182 if (!selectMan->somethingSelected())
183 return;
184
185 mScribbleArea->updateToolCursor();
186 emit mEditor->frameModified(mEditor->currentFrame());
187}
188
189void MoveTool::transformSelection(const QPointF& pos, Qt::KeyboardModifiers keyMod)
190{
191 auto selectMan = mEditor->select();
192 if (selectMan->somethingSelected())
193 {
194 int rotationIncrement = 0;
195 if (selectMan->getMoveMode() == MoveMode::ROTATION && keyMod & Qt::ShiftModifier)
196 {
197 rotationIncrement = mRotationIncrement;
198 }
199
200 selectMan->maintainAspectRatio(keyMod == Qt::ShiftModifier);
201 selectMan->alignPositionToAxis(keyMod == Qt::ShiftModifier);
202
203 qreal newAngle = 0;
204 if (selectMan->getMoveMode() == MoveMode::ROTATION) {
205 QPointF anchorPoint = selectMan->currentTransformAnchor();
206 newAngle = selectMan->angleFromPoint(pos, anchorPoint) - mRotatedAngle;
207 }
208
209 selectMan->adjustSelection(pos, mOffset, newAngle, rotationIncrement);
210 }
211 else // there is nothing selected
212 {
213 selectMan->setMoveMode(MoveMode::NONE);
214 }
215}
216
217void MoveTool::beginInteraction(const QPointF& pos, Qt::KeyboardModifiers keyMod, Layer* layer)
218{
219 auto selectMan = mEditor->select();
220 QRectF selectionRect = selectMan->mySelectionRect();
221 if (!selectionRect.isNull())
222 {
223 mUndoSaveState = mEditor->undoRedo()->state(UndoRedoRecordType::KEYFRAME_MODIFY);
224 mEditor->backup(typeName());
225 }
226
227 if (keyMod != Qt::ShiftModifier)
228 {
229 if (selectMan->isOutsideSelectionArea(pos))
230 {
231 applyTransformation();
232 mEditor->deselectAll();
233 }
234 }
235
236 if (selectMan->getMoveMode() == MoveMode::MIDDLE)
237 {
238 if (keyMod == Qt::ControlModifier) // --- rotation
239 {
240 selectMan->setMoveMode(MoveMode::ROTATION);
241 }
242 }
243
244 if (layer->type() == Layer::VECTOR)
245 {
246 createVectorSelection(pos, keyMod, layer);
247 }
248
249 selectMan->setTransformAnchor(selectMan->getSelectionAnchorPoint());
250 selectMan->setDragOrigin(pos);
251 mOffset = selectMan->myTranslation();
252
253 if(selectMan->getMoveMode() == MoveMode::ROTATION) {
254 mRotatedAngle = selectMan->angleFromPoint(pos, selectMan->currentTransformAnchor()) - selectMan->myRotation();
255 }
256}
257
263void MoveTool::createVectorSelection(const QPointF& pos, Qt::KeyboardModifiers keyMod, Layer* layer)
264{
265 assert(layer->type() == Layer::VECTOR);
266 LayerVector* vecLayer = static_cast<LayerVector*>(layer);
267 VectorImage* vectorImage = vecLayer->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
268 if (vectorImage == nullptr) { return; }
269
270 if (!mEditor->select()->closestCurves().empty()) // the user clicks near a curve
271 {
272 setCurveSelected(vectorImage, keyMod);
273 }
274 else if (vectorImage->getLastAreaNumber(pos) > -1)
275 {
276 setAreaSelected(pos, vectorImage, keyMod);
277 }
278}
279
280void MoveTool::setCurveSelected(VectorImage* vectorImage, Qt::KeyboardModifiers keyMod)
281{
282 auto selectMan = mEditor->select();
283 if (!vectorImage->isSelected(selectMan->closestCurves()))
284 {
285 if (keyMod != Qt::ShiftModifier)
286 {
287 applyTransformation();
288 }
289 vectorImage->setSelected(selectMan->closestCurves(), true);
290 selectMan->setSelection(vectorImage->getSelectionRect(), false);
291 }
292}
293
294void MoveTool::setAreaSelected(const QPointF& pos, VectorImage* vectorImage, Qt::KeyboardModifiers keyMod)
295{
296 int areaNumber = vectorImage->getLastAreaNumber(pos);
297 if (!vectorImage->isAreaSelected(areaNumber))
298 {
299 if (keyMod != Qt::ShiftModifier)
300 {
301 applyTransformation();
302 }
303 vectorImage->setAreaSelected(areaNumber, true);
304 mEditor->select()->setSelection(vectorImage->getSelectionRect(), false);
305 }
306}
307
312void MoveTool::storeClosestVectorCurve(const QPointF& pos, Layer* layer)
313{
314 auto selectMan = mEditor->select();
315 auto layerVector = static_cast<LayerVector*>(layer);
316 VectorImage* pVecImg = layerVector->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
317 if (pVecImg == nullptr) { return; }
318 selectMan->setCurves(pVecImg->getCurvesCloseTo(pos, selectMan->selectionTolerance()));
319}
320
321void MoveTool::applyTransformation()
322{
323 SelectionManager* selectMan = mEditor->select();
324 mScribbleArea->applyTransformedSelection();
325
326 // When the selection has been applied, a new rect is applied based on the bounding box.
327 // This ensures that if the selection has been rotated, it will still fit the bounds of the image.
328 selectMan->setSelection(selectMan->mapToSelection(QPolygonF(selectMan->mySelectionRect())).boundingRect());
329 mRotatedAngle = 0;
330}
331
332bool MoveTool::leavingThisTool()
333{
334 TransformTool::leavingThisTool();
335
336 if (currentPaintableLayer())
337 {
338 applyTransformation();
339 }
340
341 return true;
342}
343
344bool MoveTool::isActive() const {
345 return mScribbleArea->isPointerInUse() &&
346 (mEditor->select()->somethingSelected() || mEditor->overlays()->getMoveMode() != MoveMode::NONE);
347}
348
349Layer* MoveTool::currentPaintableLayer()
350{
351 Layer* layer = mEditor->layers()->currentLayer();
352 if (layer == nullptr)
353 return nullptr;
354 if (!layer->isPaintable())
355 return nullptr;
356 return layer;
357}
358
359QCursor MoveTool::cursor(MoveMode mode) const
360{
361 QPixmap cursorPixmap = QPixmap(24, 24);
362
363 cursorPixmap.fill(QColor(255, 255, 255, 0));
364 QPainter cursorPainter(&cursorPixmap);
365 cursorPainter.setRenderHint(QPainter::Antialiasing);
366
367 switch(mode)
368 {
369 case MoveMode::PERSP_LEFT:
370 case MoveMode::PERSP_RIGHT:
371 case MoveMode::PERSP_MIDDLE:
372 case MoveMode::PERSP_SINGLE:
373 {
374 cursorPainter.drawImage(QPoint(6,6),QImage("://icons/general/cursor-move.svg"));
375 break;
376 }
377 case MoveMode::TOPLEFT:
378 case MoveMode::BOTTOMRIGHT:
379 {
380 cursorPainter.drawImage(QPoint(6,6),QImage("://icons/general/cursor-diagonal-left.svg"));
381 break;
382 }
383 case MoveMode::TOPRIGHT:
384 case MoveMode::BOTTOMLEFT:
385 {
386 cursorPainter.drawImage(QPoint(6,6),QImage("://icons/general/cursor-diagonal-right.svg"));
387 break;
388 }
389 case MoveMode::ROTATIONLEFT:
390 case MoveMode::ROTATIONRIGHT:
391 case MoveMode::ROTATION:
392 {
393 cursorPainter.drawImage(QPoint(6,6),QImage("://icons/general/cursor-rotate.svg"));
394 break;
395 }
396 case MoveMode::MIDDLE:
397 case MoveMode::CENTER:
398 {
399 cursorPainter.drawImage(QPoint(6,6),QImage("://icons/general/cursor-move.svg"));
400 break;
401 }
402 default:
403 return Qt::ArrowCursor;
404 }
405 cursorPainter.end();
406
407 return QCursor(cursorPixmap);
408}
BaseTool::leavingThisTool
virtual bool leavingThisTool()
Will clean up active connections.
Definition: basetool.cpp:91
Editor::frameModified
void frameModified(int frameNumber)
This should be emitted after modifying the frame content.
Editor::updateFrame
void updateFrame()
Will call update() and update the canvas Only call this directly If you need the cache to be intact a...
Definition: editor.cpp:852
LayerCamera
Definition: layercamera.h:30
Layer
Definition: layer.h:33
LayerVector
Definition: layervector.h:26
MoveTool::createVectorSelection
void createVectorSelection(const QPointF &pos, Qt::KeyboardModifiers keyMod, Layer *layer)
MoveTool::createVectorSelection In vector the selection rectangle is based on the bounding box of the...
Definition: movetool.cpp:263
MoveTool::isActive
bool isActive() const override
Check if the tool is active.
Definition: movetool.cpp:344
MoveTool::leavingThisTool
bool leavingThisTool() override
Will clean up active connections.
Definition: movetool.cpp:332
MoveTool::storeClosestVectorCurve
void storeClosestVectorCurve(const QPointF &pos, Layer *layer)
MoveTool::storeClosestVectorCurve stores the curves closest to the mouse position in mClosestCurves.
Definition: movetool.cpp:312
PointerEvent
Definition: pointerevent.h:8
SelectionManager
Definition: selectionmanager.h:33
SelectionManager::setDragOrigin
void setDragOrigin(const QPointF point)
The point from where the dragging will be based of inside the selection area.
Definition: selectionmanager.h:118
SelectionManager::alignPositionToAxis
void alignPositionToAxis(bool state)
Locks movement either horizontally or vertically depending on drag direction.
Definition: selectionmanager.h:55
TransformTool
Definition: transformtool.h:23
UndoRedoManager::record
void record(const UndoSaveState *&undoState, const QString &description)
Records the given save state.
Definition: undoredomanager.cpp:95
UndoRedoManager::state
const UndoSaveState * state(UndoRedoRecordType recordType) const
Prepares and returns a save state with the given scope.
Definition: undoredomanager.cpp:199
VectorImage
Definition: vectorimage.h:32
VectorImage::isAreaSelected
bool isAreaSelected(int areaNumber)
VectorImage::isAreaSelected.
Definition: vectorimage.cpp:696
VectorImage::setSelected
void setSelected(int curveNumber, bool YesOrNo)
VectorImage::setSelected.
Definition: vectorimage.cpp:616
VectorImage::getLastAreaNumber
int getLastAreaNumber(QPointF point)
VectorImage::getLastAreaNumber.
Definition: vectorimage.cpp:2256
VectorImage::setAreaSelected
void setAreaSelected(int areaNumber, bool YesOrNo)
VectorImage::setAreaSelected.
Definition: vectorimage.cpp:684
VectorImage::isSelected
bool isSelected(int curveNumber)
VectorImage::isSelected.
Definition: vectorimage.cpp:726
VectorImage::getCurvesCloseTo
QList< int > getCurvesCloseTo(QPointF thisPoint, qreal maxDistance)
VectorImage::getCurvesCloseTo.
Definition: vectorimage.cpp:1398
QColor
QCursor
QHash
QImage
QList::empty
bool empty() const const
QObject
QObject::connect
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject::event
virtual bool event(QEvent *e)
QPainter
QPainter::Antialiasing
Antialiasing
QPixmap
QPixmap::fill
void fill(const QColor &color)
QPoint
QPointF
QPolygonF
QRectF
QRectF::isNull
bool isNull() const const
QSettings
QSettings::value
QVariant value(const QString &key, const QVariant &defaultValue) const const
Qt::ArrowCursor
ArrowCursor
Qt::KeyboardModifiers
typedef KeyboardModifiers
QTransform::map
QPoint map(const QPoint &point) const const
QVariant::toBool
bool toBool() const const
Generated on Wed Dec 24 2025 23:46:02 for Pencil2D by doxygen 1.9.6 based on revision 1be63043db1f7fc0545528fc3b6d5098a552755b