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) : BaseTool(parent)
39{
40}
41
42ToolType MoveTool::type()
43{
44 return MOVE;
45}
46
47void MoveTool::loadSettings()
48{
49 QSettings settings(PENCIL2D, PENCIL2D);
50
51 properties.width = -1;
52 properties.feather = -1;
53 properties.useFeather = false;
54 properties.stabilizerLevel = -1;
55 properties.useAA = settings.value("moveAA").toBool();
56 mRotationIncrement = mEditor->preference()->getInt(SETTING::ROTATION_INCREMENT);
57 properties.showSelectionInfo = settings.value("ShowSelectionInfo").toBool();
58 mPropertyEnabled[SHOWSELECTIONINFO] = true;
59 mPropertyEnabled[ANTI_ALIASING] = true;
60
61 connect(mEditor->preference(), &PreferenceManager::optionChanged, this, &MoveTool::updateSettings);
62}
63
64void MoveTool::saveSettings()
65{
66 QSettings settings(PENCIL2D, PENCIL2D);
67
68 settings.setValue("ShowSelectionInfo", properties.showSelectionInfo);
69 settings.setValue("moveAA", properties.useAA);
70
71 settings.sync();
72}
73
74QCursor MoveTool::cursor()
75{
76 MoveMode mode = MoveMode::NONE;
77 SelectionManager* selectMan = mEditor->select();
78 if (selectMan->somethingSelected())
79 {
80 mode = mEditor->select()->getMoveMode();
81 }
82 else if (mEditor->overlays()->anyOverlayEnabled())
83 {
84 mode = mPerspMode;
85 }
86
87 return cursor(mode);
88}
89
90void MoveTool::updateSettings(const SETTING setting)
91{
92 switch (setting)
93 {
94 case SETTING::ROTATION_INCREMENT:
95 mRotationIncrement = mEditor->preference()->getInt(SETTING::ROTATION_INCREMENT);
96 break;
97 case SETTING::OVERLAY_PERSPECTIVE1:
98 mEditor->overlays()->settingsUpdated(setting, mEditor->preference()->isOn(setting));
99 break;
100 case SETTING::OVERLAY_PERSPECTIVE2:
101 mEditor->overlays()->settingsUpdated(setting, mEditor->preference()->isOn(setting));
102 break;
103 case SETTING::OVERLAY_PERSPECTIVE3:
104 mEditor->overlays()->settingsUpdated(setting, mEditor->preference()->isOn(setting));
105 break;
106 default:
107 break;
108 }
109}
110
111void MoveTool::pointerPressEvent(PointerEvent* event)
112{
113 Layer* currentLayer = currentPaintableLayer();
114 if (currentLayer == nullptr) return;
115
116 if (mEditor->select()->somethingSelected())
117 {
118 beginInteraction(event->canvasPos(), event->modifiers(), currentLayer);
119 }
120 else if (mEditor->overlays()->anyOverlayEnabled())
121 {
122 LayerCamera* layerCam = mEditor->layers()->getCameraLayerBelow(mEditor->currentLayerIndex());
123 Q_ASSERT(layerCam);
124
125 mPerspMode = mEditor->overlays()->getMoveModeForPoint(event->canvasPos(), layerCam->getViewAtFrame(mEditor->currentFrame()));
126 mEditor->overlays()->setMoveMode(mPerspMode);
127 QPoint mapped = layerCam->getViewAtFrame(mEditor->currentFrame()).map(event->canvasPos()).toPoint();
128 mEditor->overlays()->updatePerspective(mapped);
129 }
130
131 mEditor->updateFrame();
132}
133
134void MoveTool::pointerMoveEvent(PointerEvent* event)
135{
136 Layer* currentLayer = currentPaintableLayer();
137 if (currentLayer == nullptr) return;
138
139 if (mScribbleArea->isPointerInUse()) // the user is also pressing the mouse (dragging)
140 {
141 transformSelection(event->canvasPos(), event->modifiers());
142
143 if (mEditor->overlays()->anyOverlayEnabled())
144 {
145 LayerCamera* layerCam = mEditor->layers()->getCameraLayerBelow(mEditor->currentLayerIndex());
146 Q_ASSERT(layerCam);
147 mEditor->overlays()->updatePerspective(layerCam->getViewAtFrame(mEditor->currentFrame()).map(event->canvasPos()));
148 }
149 if (mEditor->select()->somethingSelected())
150 {
151 transformSelection(event->canvasPos(), event->modifiers());
152 }
153 }
154 else
155 {
156 // the user is moving the mouse without pressing it
157 // update cursor to reflect selection corner interaction
158 mEditor->select()->setMoveModeForAnchorInRange(event->canvasPos());
159 if (mEditor->overlays()->anyOverlayEnabled())
160 {
161 LayerCamera *layerCam = mEditor->layers()->getCameraLayerBelow(mEditor->currentLayerIndex());
162 Q_ASSERT(layerCam);
163 mPerspMode = mEditor->overlays()->getMoveModeForPoint(event->canvasPos(), layerCam->getViewAtFrame(mEditor->currentFrame()));
164 }
165 mScribbleArea->updateToolCursor();
166
167 if (currentLayer->type() == Layer::VECTOR)
168 {
169 storeClosestVectorCurve(event->canvasPos(), currentLayer);
170 }
171 }
172 mEditor->updateFrame();
173}
174
175void MoveTool::pointerReleaseEvent(PointerEvent*)
176{
177 mEditor->undoRedo()->record(mUndoSaveState, typeName());
178
179 if (mEditor->overlays()->anyOverlayEnabled())
180 {
181 mEditor->overlays()->setMoveMode(MoveMode::NONE);
182 mPerspMode = MoveMode::NONE;
183 }
184
185 auto selectMan = mEditor->select();
186 if (!selectMan->somethingSelected())
187 return;
188
189 mScribbleArea->updateToolCursor();
190 emit mEditor->frameModified(mEditor->currentFrame());
191}
192
193void MoveTool::transformSelection(const QPointF& pos, Qt::KeyboardModifiers keyMod)
194{
195 auto selectMan = mEditor->select();
196 if (selectMan->somethingSelected())
197 {
198 int rotationIncrement = 0;
199 if (selectMan->getMoveMode() == MoveMode::ROTATION && keyMod & Qt::ShiftModifier)
200 {
201 rotationIncrement = mRotationIncrement;
202 }
203
204 selectMan->maintainAspectRatio(keyMod == Qt::ShiftModifier);
205 selectMan->alignPositionToAxis(keyMod == Qt::ShiftModifier);
206
207 qreal newAngle = 0;
208 if (selectMan->getMoveMode() == MoveMode::ROTATION) {
209 QPointF anchorPoint = selectMan->currentTransformAnchor();
210 newAngle = selectMan->angleFromPoint(pos, anchorPoint) - mRotatedAngle;
211 }
212
213 selectMan->adjustSelection(pos, mOffset, newAngle, rotationIncrement);
214 }
215 else // there is nothing selected
216 {
217 selectMan->setMoveMode(MoveMode::NONE);
218 }
219}
220
221void MoveTool::beginInteraction(const QPointF& pos, Qt::KeyboardModifiers keyMod, Layer* layer)
222{
223 auto selectMan = mEditor->select();
224 QRectF selectionRect = selectMan->mySelectionRect();
225 if (!selectionRect.isNull())
226 {
227 mUndoSaveState = mEditor->undoRedo()->state(UndoRedoRecordType::KEYFRAME_MODIFY);
228 mEditor->backup(typeName());
229 }
230
231 if (keyMod != Qt::ShiftModifier)
232 {
233 if (selectMan->isOutsideSelectionArea(pos))
234 {
235 applyTransformation();
236 mEditor->deselectAll();
237 }
238 }
239
240 if (selectMan->getMoveMode() == MoveMode::MIDDLE)
241 {
242 if (keyMod == Qt::ControlModifier) // --- rotation
243 {
244 selectMan->setMoveMode(MoveMode::ROTATION);
245 }
246 }
247
248 if (layer->type() == Layer::VECTOR)
249 {
250 createVectorSelection(pos, keyMod, layer);
251 }
252
253 selectMan->setTransformAnchor(selectMan->getSelectionAnchorPoint());
254 selectMan->setDragOrigin(pos);
255 mOffset = selectMan->myTranslation();
256
257 if(selectMan->getMoveMode() == MoveMode::ROTATION) {
258 mRotatedAngle = selectMan->angleFromPoint(pos, selectMan->currentTransformAnchor()) - selectMan->myRotation();
259 }
260}
261
267void MoveTool::createVectorSelection(const QPointF& pos, Qt::KeyboardModifiers keyMod, Layer* layer)
268{
269 assert(layer->type() == Layer::VECTOR);
270 LayerVector* vecLayer = static_cast<LayerVector*>(layer);
271 VectorImage* vectorImage = vecLayer->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
272 if (vectorImage == nullptr) { return; }
273
274 if (!mEditor->select()->closestCurves().empty()) // the user clicks near a curve
275 {
276 setCurveSelected(vectorImage, keyMod);
277 }
278 else if (vectorImage->getLastAreaNumber(pos) > -1)
279 {
280 setAreaSelected(pos, vectorImage, keyMod);
281 }
282}
283
284void MoveTool::setCurveSelected(VectorImage* vectorImage, Qt::KeyboardModifiers keyMod)
285{
286 auto selectMan = mEditor->select();
287 if (!vectorImage->isSelected(selectMan->closestCurves()))
288 {
289 if (keyMod != Qt::ShiftModifier)
290 {
291 applyTransformation();
292 }
293 vectorImage->setSelected(selectMan->closestCurves(), true);
294 selectMan->setSelection(vectorImage->getSelectionRect(), false);
295 }
296}
297
298void MoveTool::setAreaSelected(const QPointF& pos, VectorImage* vectorImage, Qt::KeyboardModifiers keyMod)
299{
300 int areaNumber = vectorImage->getLastAreaNumber(pos);
301 if (!vectorImage->isAreaSelected(areaNumber))
302 {
303 if (keyMod != Qt::ShiftModifier)
304 {
305 applyTransformation();
306 }
307 vectorImage->setAreaSelected(areaNumber, true);
308 mEditor->select()->setSelection(vectorImage->getSelectionRect(), false);
309 }
310}
311
316void MoveTool::storeClosestVectorCurve(const QPointF& pos, Layer* layer)
317{
318 auto selectMan = mEditor->select();
319 auto layerVector = static_cast<LayerVector*>(layer);
320 VectorImage* pVecImg = layerVector->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
321 if (pVecImg == nullptr) { return; }
322 selectMan->setCurves(pVecImg->getCurvesCloseTo(pos, selectMan->selectionTolerance()));
323}
324
325void MoveTool::applyTransformation()
326{
327 SelectionManager* selectMan = mEditor->select();
328 mScribbleArea->applyTransformedSelection();
329
330 // When the selection has been applied, a new rect is applied based on the bounding box.
331 // This ensures that if the selection has been rotated, it will still fit the bounds of the image.
332 selectMan->setSelection(selectMan->mapToSelection(QPolygonF(selectMan->mySelectionRect())).boundingRect());
333 mRotatedAngle = 0;
334}
335
336bool MoveTool::leavingThisTool()
337{
338 BaseTool::leavingThisTool();
339
340 if (currentPaintableLayer())
341 {
342 applyTransformation();
343 }
344
345 saveSettings();
346
347 return true;
348}
349
350bool MoveTool::isActive() const {
351 return mScribbleArea->isPointerInUse() &&
352 (mEditor->select()->somethingSelected() || mEditor->overlays()->getMoveMode() != MoveMode::NONE);
353}
354
355void MoveTool::resetToDefault()
356{
357 setShowSelectionInfo(false);
358 setAA(true);
359}
360
361void MoveTool::setShowSelectionInfo(const bool b)
362{
363 properties.showSelectionInfo = b;
364
365 QSettings settings(PENCIL2D, PENCIL2D);
366 settings.setValue("ShowSelectionInfo", b);
367
368}
369
370Layer* MoveTool::currentPaintableLayer()
371{
372 Layer* layer = mEditor->layers()->currentLayer();
373 if (layer == nullptr)
374 return nullptr;
375 if (!layer->isPaintable())
376 return nullptr;
377 return layer;
378}
379
380QCursor MoveTool::cursor(MoveMode mode) const
381{
382 QPixmap cursorPixmap = QPixmap(24, 24);
383
384 cursorPixmap.fill(QColor(255, 255, 255, 0));
385 QPainter cursorPainter(&cursorPixmap);
386 cursorPainter.setRenderHint(QPainter::Antialiasing);
387
388 switch(mode)
389 {
390 case MoveMode::PERSP_LEFT:
391 case MoveMode::PERSP_RIGHT:
392 case MoveMode::PERSP_MIDDLE:
393 case MoveMode::PERSP_SINGLE:
394 {
395 cursorPainter.drawImage(QPoint(6,6),QImage("://icons/general/cursor-move.svg"));
396 break;
397 }
398 case MoveMode::TOPLEFT:
399 case MoveMode::BOTTOMRIGHT:
400 {
401 cursorPainter.drawImage(QPoint(6,6),QImage("://icons/general/cursor-diagonal-left.svg"));
402 break;
403 }
404 case MoveMode::TOPRIGHT:
405 case MoveMode::BOTTOMLEFT:
406 {
407 cursorPainter.drawImage(QPoint(6,6),QImage("://icons/general/cursor-diagonal-right.svg"));
408 break;
409 }
410 case MoveMode::ROTATIONLEFT:
411 case MoveMode::ROTATIONRIGHT:
412 case MoveMode::ROTATION:
413 {
414 cursorPainter.drawImage(QPoint(6,6),QImage("://icons/general/cursor-rotate.svg"));
415 break;
416 }
417 case MoveMode::MIDDLE:
418 case MoveMode::CENTER:
419 {
420 cursorPainter.drawImage(QPoint(6,6),QImage("://icons/general/cursor-move.svg"));
421 break;
422 }
423 default:
424 return Qt::ArrowCursor;
425 }
426 cursorPainter.end();
427
428 return QCursor(cursorPixmap);
429}
BaseTool
Definition: basetool.h:70
BaseTool::leavingThisTool
virtual bool leavingThisTool()
Will clean up active connections.
Definition: basetool.cpp:69
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:850
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:267
MoveTool::isActive
bool isActive() const override
Check if the tool is active.
Definition: movetool.cpp:350
MoveTool::leavingThisTool
bool leavingThisTool() override
Will clean up active connections.
Definition: movetool.cpp:336
MoveTool::storeClosestVectorCurve
void storeClosestVectorCurve(const QPointF &pos, Layer *layer)
MoveTool::storeClosestVectorCurve stores the curves closest to the mouse position in mClosestCurves.
Definition: movetool.cpp:316
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
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
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
Qt::ArrowCursor
ArrowCursor
Qt::KeyboardModifiers
typedef KeyboardModifiers
QTransform::map
QPoint map(const QPoint &point) const const
Generated on Thu May 8 2025 04:47:53 for Pencil2D by doxygen 1.9.6 based on revision 4513250b1d5b1a3676ec0e67b06b7a885ceaae39