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
  • managers
selectionmanager.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#include "selectionmanager.h"
18#include "editor.h"
19
20#include "vectorimage.h"
21
22#include "mathutils.h"
23
24#include <QVector2D>
25
26
27SelectionManager::SelectionManager(Editor* editor) : BaseManager(editor, __FUNCTION__)
28{
29}
30
31SelectionManager::~SelectionManager()
32{
33}
34
35bool SelectionManager::init()
36{
37 return true;
38}
39
40Status SelectionManager::load(Object*)
41{
42 resetSelectionProperties();
43 return Status::OK;
44}
45
46Status SelectionManager::save(Object*)
47{
48 return Status::OK;
49}
50
51void SelectionManager::workingLayerChanged(Layer *)
52{
53}
54
55void SelectionManager::resetSelectionTransformProperties()
56{
57 mRotatedAngle = 0;
58 mTranslation = QPointF(0, 0);
59 mScaleX = 1;
60 mScaleY = 1;
61 mAnchorPoint = QPoint();
62 mSelectionTransform.reset();
63}
64
65void SelectionManager::resetSelectionTransform()
66{
67 mSelectionTransform.reset();
68}
69
70bool SelectionManager::isOutsideSelectionArea(const QPointF& point) const
71{
72 return (!mSelectionTransform.map(mSelectionPolygon).containsPoint(point, Qt::WindingFill)) && mMoveMode == MoveMode::NONE;
73}
74
75void SelectionManager::deleteSelection()
76{
77 emit needDeleteSelection();
78}
79
80qreal SelectionManager::selectionTolerance() const
81{
82 return qAbs(mSelectionTolerance * editor()->viewScaleInversed());
83}
84
85QPointF SelectionManager::getSelectionAnchorPoint() const
86{
87 QPointF anchorPoint;
88 if (mSelectionPolygon.count() < 3) { return anchorPoint; }
89
90 if (mMoveMode == MoveMode::BOTTOMRIGHT)
91 {
92 anchorPoint = mSelectionPolygon[0];
93 }
94 else if (mMoveMode == MoveMode::BOTTOMLEFT)
95 {
96 anchorPoint = mSelectionPolygon[1];
97 }
98 else if (mMoveMode == MoveMode::TOPLEFT)
99 {
100 anchorPoint = mSelectionPolygon[2];
101 }
102 else if (mMoveMode == MoveMode::TOPRIGHT)
103 {
104 anchorPoint = mSelectionPolygon[3];
105 } else {
106 anchorPoint = QLineF(mSelectionPolygon[0], mSelectionPolygon[2]).pointAt(.5);
107 }
108 return anchorPoint;
109}
110
111
112void SelectionManager::setMoveModeForAnchorInRange(const QPointF& point)
113{
114 if (mSelectionPolygon.count() < 4)
115 {
116 mMoveMode = MoveMode::NONE;
117 return;
118 }
119
120 QPolygonF projectedPolygon = mapToSelection(mSelectionPolygon);
121
122 const double calculatedSelectionTol = selectionTolerance();
123
124 if (QLineF(point, projectedPolygon[0]).length() < calculatedSelectionTol)
125 {
126 mMoveMode = MoveMode::TOPLEFT;
127 }
128 else if (QLineF(point, projectedPolygon[1]).length() < calculatedSelectionTol)
129 {
130 mMoveMode = MoveMode::TOPRIGHT;
131 }
132 else if (QLineF(point, projectedPolygon[2]).length() < calculatedSelectionTol)
133 {
134 mMoveMode = MoveMode::BOTTOMRIGHT;
135 }
136 else if (QLineF(point, projectedPolygon[3]).length() < calculatedSelectionTol)
137 {
138 mMoveMode = MoveMode::BOTTOMLEFT;
139 }
140 else if (projectedPolygon.containsPoint(point, Qt::WindingFill))
141 {
142 mMoveMode = MoveMode::MIDDLE;
143 }
144 else
145 {
146 mMoveMode = MoveMode::NONE;
147 }
148}
149
150void SelectionManager::adjustSelection(const QPointF& currentPoint, const QPointF& offset, qreal rotationOffset, int rotationIncrement)
151{
152 switch (mMoveMode)
153 {
154 case MoveMode::MIDDLE: {
155 QPointF newOffset = currentPoint - mDragOrigin;
156
157 if (mLockAxis) {
158 mTranslation = offset + alignPositionToAxis(newOffset);
159 } else {
160 mTranslation = offset + newOffset;
161 }
162 break;
163 }
164 case MoveMode::TOPLEFT:
165 case MoveMode::TOPRIGHT:
166 case MoveMode::BOTTOMRIGHT:
167 case MoveMode::BOTTOMLEFT: {
168
169 QPolygonF projectedPolygon = mapToSelection(mSelectionPolygon);
170 QVector2D currentPVec = QVector2D(currentPoint);
171
172 qreal originWidth = mSelectionPolygon[1].x() - mSelectionPolygon[0].x();
173 qreal originHeight = mSelectionPolygon[3].y() - mSelectionPolygon[0].y();
174
175 QVector2D staticXAnchor;
176 QVector2D staticYAnchor;
177 QVector2D movingAnchor;
178 if (mMoveMode == MoveMode::TOPLEFT) {
179 movingAnchor = QVector2D(projectedPolygon[0]);
180 staticXAnchor = QVector2D(projectedPolygon[1]);
181 staticYAnchor = QVector2D(projectedPolygon[3]);
182 } else if (mMoveMode == MoveMode::TOPRIGHT) {
183 movingAnchor = QVector2D(projectedPolygon[1]);
184 staticXAnchor = QVector2D(projectedPolygon[0]);
185 staticYAnchor = QVector2D(projectedPolygon[2]);
186 } else if (mMoveMode == MoveMode::BOTTOMRIGHT) {
187 movingAnchor = QVector2D(projectedPolygon[2]);
188 staticXAnchor = QVector2D(projectedPolygon[3]);
189 staticYAnchor = QVector2D(projectedPolygon[1]);
190 } else {
191 movingAnchor = QVector2D(projectedPolygon[3]);
192 staticXAnchor = QVector2D(projectedPolygon[2]);
193 staticYAnchor = QVector2D(projectedPolygon[0]);
194 }
195
196 QVector2D directionVecX = staticXAnchor - currentPVec;
197 QVector2D directionVecY = staticYAnchor - currentPVec;
198
199 // Calculates the signed distance
200 qreal distanceX = QVector2D::dotProduct(directionVecX, (staticXAnchor - movingAnchor).normalized());
201 qreal distanceY = QVector2D::dotProduct(directionVecY, (staticYAnchor - movingAnchor).normalized());
202
203 qreal scaleX = distanceX / originWidth;
204 qreal scaleY = distanceY / originHeight;
205 if (mAspectRatioFixed) {
206 scaleY = scaleX;
207 }
208
209 scale(scaleX, scaleY);
210
211 break;
212 }
213 case MoveMode::ROTATION: {
214 rotate(rotationOffset, rotationIncrement);
215 break;
216 }
217 default:
218 break;
219 }
220 calculateSelectionTransformation();
221}
222
223void SelectionManager::translate(QPointF newPos)
224{
225 mTranslation += newPos;
226}
227
228void SelectionManager::rotate(qreal angle, qreal lockedAngle)
229{
230 if (lockedAngle > 0) {
231 mRotatedAngle = constrainRotationToAngle(angle, lockedAngle);
232 } else {
233 mRotatedAngle = angle;
234 }
235}
236
237void SelectionManager::scale(qreal sX, qreal sY)
238{
239 // Enforce negative scaling when
240 // deliberately trying to transform in negative space
241 if (mScaleX < 0) {
242 sX = -sX;
243 }
244 if (qFuzzyIsNull(sX)) {
245 // Scale must not become 0
246 sX = 0.0001;
247 }
248
249 // Enforce negative scaling when
250 // deliberately trying to transform in negative space
251 if (mScaleY < 0) {
252 sY = -sY;
253 }
254 if (qFuzzyIsNull(sY)) {
255 // Scale must not become 0
256 sY = 0.0001;
257 }
258
259 mScaleX = sX;
260 mScaleY = sY;
261}
262
263int SelectionManager::constrainRotationToAngle(const qreal rotatedAngle, const int rotationIncrement) const
264{
265 return qRound(rotatedAngle / rotationIncrement) * rotationIncrement;
266}
267
268qreal SelectionManager::angleFromPoint(const QPointF& point, const QPointF& anchorPoint) const
269{
270 return qRadiansToDegrees(MathUtils::getDifferenceAngle(mSelectionTransform.map(anchorPoint), point));
271}
272
273void SelectionManager::setSelection(QRectF rect, bool roundPixels)
274{
275 resetSelectionTransformProperties();
276 if (roundPixels)
277 {
278 rect = rect.toAlignedRect();
279 }
280 mSelectionPolygon = rect;
281 mOriginalRect = rect;
282 mScaleX = 1;
283 mScaleY = 1;
284 mRotatedAngle = 0;
285
286 emit selectionChanged();
287}
288
289void SelectionManager::setTransformAnchor(const QPointF& point)
290{
291 QPointF newPos = mapToSelection(point);
292 QPointF oldPos = mapToSelection(mAnchorPoint);
293
294 // Adjust translation based on anchor point to avoid moving the selection
295 mTranslation = mTranslation - oldPos + newPos;
296 mAnchorPoint = point;
297}
298
299void SelectionManager::calculateSelectionTransformation()
300{
301 QTransform t;
302 t.translate(-mAnchorPoint.x(), -mAnchorPoint.y());
303 QTransform t2;
304 t2.translate(mTranslation.x(), mTranslation.y());
305
306 QTransform r;
307 r.rotate(mRotatedAngle);
308 QTransform s;
309 s.scale(mScaleX, mScaleY);
310 mSelectionTransform = t * s * r * t2;
311}
312
313QPointF SelectionManager::alignPositionToAxis(QPointF currentPoint) const
314{
315 if (qAbs(currentPoint.y()) > qAbs(currentPoint.x())) {
316 // Align to y axis
317 return QPointF(0, currentPoint.y());
318 }
319
320 // Align to x axis
321 return QPointF(currentPoint.x(), 0);
322}
323
328void SelectionManager::flipSelection(bool flipVertical)
329{
330 if (flipVertical)
331 {
332 mScaleY = -mScaleY;
333 }
334 else
335 {
336 mScaleX = -mScaleX;
337 }
338 setTransformAnchor(mOriginalRect.center());
339 calculateSelectionTransformation();
340 emit selectionChanged();
341}
342
343void SelectionManager::resetSelectionProperties()
344{
345 resetSelectionTransformProperties();
346 mSelectionPolygon = QPolygonF();
347 mOriginalRect = QRectF();
348 emit selectionChanged();
349}
350
BaseManager
Definition: basemanager.h:29
Editor
Definition: editor.h:71
Layer
Definition: layer.h:33
Object
Definition: object.h:42
SelectionManager::flipSelection
void flipSelection(bool flipVertical)
ScribbleArea::flipSelection flip selection along the X or Y axis.
Definition: selectionmanager.cpp:328
SelectionManager::resetSelectionTransformProperties
void resetSelectionTransformProperties()
SelectionManager::resetSelectionTransformProperties should be used whenever translate,...
Definition: selectionmanager.cpp:55
SelectionManager::alignPositionToAxis
void alignPositionToAxis(bool state)
Locks movement either horizontally or vertically depending on drag direction.
Definition: selectionmanager.h:55
SelectionManager::calculateSelectionTransformation
void calculateSelectionTransformation()
This should be called to update the selection transform.
Definition: selectionmanager.cpp:299
Status
Definition: pencilerror.h:40
QLineF
QLineF::pointAt
QPointF pointAt(qreal t) const const
QPoint
QPointF
QPointF::x
qreal x() const const
QPointF::y
qreal y() const const
QPolygonF
QPolygonF::containsPoint
bool containsPoint(const QPointF &point, Qt::FillRule fillRule) const const
QRectF
QRectF::center
QPointF center() const const
QRectF::toAlignedRect
QRect toAlignedRect() const const
Qt::WindingFill
WindingFill
QTransform
QTransform::map
QPoint map(const QPoint &point) const const
QTransform::reset
void reset()
QTransform::rotate
QTransform & rotate(qreal angle, Qt::Axis axis)
QTransform::scale
QTransform & scale(qreal sx, qreal sy)
QTransform::translate
QTransform & translate(qreal dx, qreal dy)
QVector2D
QVector2D::dotProduct
float dotProduct(const QVector2D &v1, const QVector2D &v2)
QVector::count
int count(const T &value) const const
Generated on Thu May 8 2025 04:47:53 for Pencil2D by doxygen 1.9.6 based on revision 4513250b1d5b1a3676ec0e67b06b7a885ceaae39