All Classes Namespaces Functions Variables Enumerations Properties Pages
selectionmanager.cpp
1 /*
2 
3 Pencil2D - Traditional Animation Software
4 Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5 Copyright (C) 2012-2020 Matthew Chiawen Chang
6 
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; version 2 of the License.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15 
16 */
17 #include "selectionmanager.h"
18 #include "editor.h"
19 
20 #include "layerbitmap.h"
21 #include "vectorimage.h"
22 #include "bitmapimage.h"
23 
24 #include "layervector.h"
25 #include "mathutils.h"
26 
27 //#ifdef QT_DEBUG
28 #include <QDebug>
29 //#endif
30 
31 
32 SelectionManager::SelectionManager(Editor* editor) : BaseManager(editor)
33 {
34 }
35 
36 SelectionManager::~SelectionManager()
37 {
38 }
39 
40 bool SelectionManager::init()
41 {
42  return true;
43 }
44 
45 Status SelectionManager::load(Object*)
46 {
47  resetSelectionProperties();
48  return Status::OK;
49 }
50 
51 Status SelectionManager::save(Object*)
52 {
53  return Status::OK;
54 }
55 
56 void SelectionManager::workingLayerChanged(Layer *)
57 {
58 }
59 
61 {
62  mOffset = QPointF(0, 0);
63  mRotatedAngle = 0;
64  mSelectionTransform.reset();
65 }
66 
67 void SelectionManager::updatePolygons()
68 {
69  mCurrentSelectionPolygonF = mTempTransformedSelection;
70  mLastSelectionPolygonF = mTransformedSelection;
71 }
72 
73 void SelectionManager::resetSelectionTransform()
74 {
75  mSelectionTransform.reset();
76 }
77 
78 bool SelectionManager::isOutsideSelectionArea(QPointF point)
79 {
80  return (!mTransformedSelection.contains(point)
81  && validateMoveMode(point) == MoveMode::NONE);
82 }
83 
84 bool SelectionManager::transformHasBeenModified()
85 {
86  return (mSelection != mTempTransformedSelection) || rotationHasBeenModified();
87 }
88 
89 bool SelectionManager::rotationHasBeenModified()
90 {
91  return !qFuzzyCompare(mRotatedAngle,0);
92 }
93 
94 void SelectionManager::deleteSelection()
95 {
96  emit needDeleteSelection();
97 }
98 
99 void SelectionManager::clearCurves()
100 {
101  mClosestCurves.clear();
102 }
103 
104 void SelectionManager::clearVertices()
105 {
106  mClosestVertices.clear();
107 }
108 
109 qreal SelectionManager::selectionTolerance() const
110 {
111  return qAbs(mSelectionTolerance * editor()->viewScaleInversed());
112 }
113 
114 MoveMode SelectionManager::validateMoveMode(QPointF pos)
115 {
116  return moveModeForAnchorInRange(pos);
117 }
118 
119 MoveMode SelectionManager::moveModeForAnchorInRange(QPointF lastPos)
120 {
121  QRectF transformRect = mTempTransformedSelection;
122  QPointF lastPoint = lastPos;
123 
124  const double calculatedSelectionTol = selectionTolerance();
125 
126  MoveMode mode;
127  if (QLineF(lastPoint, transformRect.topLeft()).length() < calculatedSelectionTol)
128  {
129  mode = MoveMode::TOPLEFT;
130  }
131  else if (QLineF(lastPoint, transformRect.topRight()).length() < calculatedSelectionTol)
132  {
133  mode = MoveMode::TOPRIGHT;
134  }
135  else if (QLineF(lastPoint, transformRect.bottomLeft()).length() < calculatedSelectionTol)
136  {
137  mode = MoveMode::BOTTOMLEFT;
138 
139  }
140  else if (QLineF(lastPoint, transformRect.bottomRight()).length() < calculatedSelectionTol)
141  {
142  mode = MoveMode::BOTTOMRIGHT;
143  }
144  else if (mTransformedSelection.contains(lastPoint))
145  {
146  mode = MoveMode::MIDDLE;
147  }
148  else {
149  mode = MoveMode::NONE;
150  }
151  mMoveMode = mode;
152  return mode;
153 }
154 
155 MoveMode SelectionManager::getMoveModeForSelectionAnchor(QPointF pos)
156 {
157  const double calculatedSelectionTol = selectionTolerance();
158 
159  if (mCurrentSelectionPolygonF.count() < 4) { return MoveMode::NONE; }
160 
161  QPointF topLeftCorner = mCurrentSelectionPolygonF[0];
162 
163  QPointF topRightCorner = mCurrentSelectionPolygonF[1];
164 
165  QPointF bottomRightCorner = mCurrentSelectionPolygonF[2];
166 
167  QPointF bottomLeftCorner = mCurrentSelectionPolygonF[3];
168 
169  QPointF currentPos = pos;
170 
171  if (QLineF(currentPos, topLeftCorner).length() < calculatedSelectionTol)
172  {
173  return MoveMode::TOPLEFT;
174  }
175  else if (QLineF(currentPos, topRightCorner).length() < calculatedSelectionTol)
176  {
177  return MoveMode::TOPRIGHT;
178  }
179  else if (QLineF(currentPos, bottomLeftCorner).length() < calculatedSelectionTol)
180  {
181  return MoveMode::BOTTOMLEFT;
182 
183  }
184  else if (QLineF(currentPos, bottomRightCorner).length() < calculatedSelectionTol)
185  {
186  return MoveMode::BOTTOMRIGHT;
187  }
188  else if (mTempTransformedSelection.contains(currentPos))
189  {
190  return MoveMode::MIDDLE;
191  }
192 
193  return MoveMode::NONE;
194 }
195 
196 QPointF SelectionManager::whichAnchorPoint(QPointF currentPoint)
197 {
198  QPointF anchorPoint;
199  MoveMode mode = getMoveModeForSelectionAnchor(currentPoint);
200  if (mode == MoveMode::TOPLEFT)
201  {
202  anchorPoint = mSelection.bottomRight();
203  }
204  else if (mode == MoveMode::TOPRIGHT)
205  {
206  anchorPoint = mSelection.bottomLeft();
207  }
208  else if (mode == MoveMode::BOTTOMLEFT)
209  {
210  anchorPoint = mSelection.topRight();
211  }
212  else if (mode == MoveMode::BOTTOMRIGHT)
213  {
214  anchorPoint = mSelection.topLeft();
215  }
216  return anchorPoint;
217 }
218 
219 void SelectionManager::adjustSelection(const QPointF& currentPoint, qreal offsetX, qreal offsetY, qreal rotationOffset, int rotationIncrement)
220 {
221  offsetX = qRound(offsetX);
222  offsetY = qRound(offsetY);
223  QRectF& transformedSelection = mTransformedSelection;
224 
225  switch (mMoveMode)
226  {
227  case MoveMode::MIDDLE:
228  {
229  mTempTransformedSelection = transformedSelection.translated(QPointF(offsetX, offsetY));
230  break;
231  }
232  case MoveMode::TOPRIGHT:
233  {
234  mTempTransformedSelection = transformedSelection.adjusted(0, offsetY, offsetX, 0);
235  break;
236  }
237  case MoveMode::TOPLEFT:
238  {
239  mTempTransformedSelection = transformedSelection.adjusted(offsetX, offsetY, 0, 0);
240  break;
241  }
242  case MoveMode::BOTTOMLEFT:
243  {
244  mTempTransformedSelection = transformedSelection.adjusted(offsetX, 0, 0, offsetY);
245  break;
246  }
247  case MoveMode::BOTTOMRIGHT:
248  {
249  mTempTransformedSelection = transformedSelection.adjusted(0, 0, offsetX, offsetY);
250  break;
251 
252  }
253  case MoveMode::ROTATION:
254  {
255  mTempTransformedSelection = transformedSelection;
256  QPointF anchorPoint = transformedSelection.center();
257  qreal rotatedAngle = qRadiansToDegrees(MathUtils::getDifferenceAngle(anchorPoint, currentPoint)) - rotationOffset;
258  if (rotationIncrement > 0) {
259  mRotatedAngle = constrainRotationToAngle(rotatedAngle, rotationIncrement);
260  } else {
261  mRotatedAngle = rotatedAngle;
262  }
263  break;
264  }
265  default:
266  break;
267  }
268 }
269 
270 int SelectionManager::constrainRotationToAngle(const qreal& rotatedAngle, const int& rotationIncrement) const
271 {
272  return qRound(rotatedAngle / rotationIncrement) * rotationIncrement;
273 }
274 
275 void SelectionManager::setSelection(QRectF rect, bool roundPixels)
276 {
278  if (roundPixels)
279  {
280  rect = QRect(rect.topLeft().toPoint(), rect.bottomRight().toPoint() - QPoint(1,1));
281  }
282  mSelection = rect;
283  mTransformedSelection = rect;
284  mTempTransformedSelection = rect;
285  mSomethingSelected = (mSelection.isNull() ? false : true);
286 
287  emit selectionChanged();
288 }
289 
290 void SelectionManager::calculateSelectionTransformation()
291 {
292  QVector<QPointF> centerPoints = calcSelectionCenterPoints();
293 
294  mSelectionTransform.reset();
295 
296  mSelectionTransform.translate(centerPoints[0].x(), centerPoints[0].y());
297  mSelectionTransform.rotate(mRotatedAngle);
298 
299  if (mSelection.width() > 0 && mSelection.height() > 0) // can't divide by 0
300  {
301  qreal scaleX = mTempTransformedSelection.width() / mSelection.width();
302  qreal scaleY = mTempTransformedSelection.height() / mSelection.height();
303  mSelectionTransform.scale(scaleX, scaleY);
304  }
305  mSelectionTransform.translate(-centerPoints[1].x(), -centerPoints[1].y());
306 }
307 
308 QVector<QPointF> SelectionManager::calcSelectionCenterPoints()
309 {
310  QVector<QPointF> centerPoints;
311  qreal selectionCenterX,
312  selectionCenterY,
313  tempSelectionCenterX,
314  tempSelectionCenterY;
315 
316  tempSelectionCenterX = mTempTransformedSelection.center().x();
317  tempSelectionCenterY = mTempTransformedSelection.center().y();
318  selectionCenterX = mSelection.center().x();
319  selectionCenterY = mSelection.center().y();
320  centerPoints.append(QPointF(tempSelectionCenterX, tempSelectionCenterY));
321  centerPoints.append(QPointF(selectionCenterX, selectionCenterY));
322  return centerPoints;
323 }
324 
325 
326 QPointF SelectionManager::offsetFromAspectRatio(qreal offsetX, qreal offsetY)
327 {
328  qreal factor = mTransformedSelection.width() / mTransformedSelection.height();
329 
330  if (mMoveMode == MoveMode::TOPLEFT || mMoveMode == MoveMode::BOTTOMRIGHT)
331  {
332  offsetY = offsetX / factor;
333  }
334  else if (mMoveMode == MoveMode::TOPRIGHT || mMoveMode == MoveMode::BOTTOMLEFT)
335  {
336  offsetY = -(offsetX / factor);
337  }
338  else if (mMoveMode == MoveMode::MIDDLE)
339  {
340  qreal absX = offsetX;
341  if (absX < 0) { absX = -absX; }
342 
343  qreal absY = offsetY;
344  if (absY < 0) { absY = -absY; }
345 
346  if (absX > absY) { offsetY = 0; }
347  if (absY > absX) { offsetX = 0; }
348  }
349  return QPointF(offsetX, offsetY);
350 }
351 
356 void SelectionManager::flipSelection(bool flipVertical)
357 {
358  if (flipVertical)
359  {
360  editor()->backup(tr("Flip selection vertically"));
361  }
362  else
363  {
364  editor()->backup(tr("Flip selection horizontally"));
365  }
366 
367  qreal scaleX = mTempTransformedSelection.width() / mSelection.width();
368  qreal scaleY = mTempTransformedSelection.height() / mSelection.height();
369  QVector<QPointF> centerPoints = calcSelectionCenterPoints();
370 
371  QTransform translate = QTransform::fromTranslate(centerPoints[0].x(), centerPoints[0].y());
372  QTransform _translate = QTransform::fromTranslate(-centerPoints[1].x(), -centerPoints[1].y());
373  QTransform scale = QTransform::fromScale(-scaleX, scaleY);
374 
375  if (flipVertical)
376  {
377  scale = QTransform::fromScale(scaleX, -scaleY);
378  }
379 
380  // reset transformation for vector selections
381  mSelectionTransform.reset();
382  mSelectionTransform *= _translate * scale * translate;
383 
384  emit needPaintAndApply();
385 }
386 
387 void SelectionManager::translate(QPointF point)
388 {
389  mTempTransformedSelection.translate(point);
390  mTransformedSelection = mTempTransformedSelection;
391  calculateSelectionTransformation();
392 }
393 
394 void SelectionManager::resetSelectionProperties()
395 {
397  mSelection = QRectF();
398  mTransformedSelection = QRectF();
399  mTempTransformedSelection = QRectF();
400  mCurrentSelectionPolygonF = QPolygonF();
401  mLastSelectionPolygonF = QPolygonF();
402 
403  mSomethingSelected = false;
404  vectorSelection.clear();
405 
406  emit selectionChanged();
407 }
408 
QTransform fromTranslate(qreal dx, qreal dy)
void clear()
QTransform fromScale(qreal sx, qreal sy)
void reset()
void append(const T &value)
bool contains(const QRectF &rectangle) const const
void resetSelectionTransformProperties()
SelectionManager::resetSelectionTransformProperties should be used whenever translate, rotate, transform, scale has been applied to a selection, but don't want to reset size nor position.
QString tr(const char *sourceText, const char *disambiguation, int n)
qreal length() const const
QTransform & translate(qreal dx, qreal dy)
qreal x() const const
qreal y() const const
void translate(qreal dx, qreal dy)
QTransform & scale(qreal sx, qreal sy)
QPointF topLeft() const const
Definition: layer.h:39
QPointF center() const const
QPointF topRight() const const
bool isNull() const const
QTransform & rotate(qreal angle, Qt::Axis axis)
qreal width() const const
QPoint toPoint() const const
QRectF translated(qreal dx, qreal dy) const const
QPointF bottomLeft() const const
int count(const T &value) const const
QRectF adjusted(qreal dx1, qreal dy1, qreal dx2, qreal dy2) const const
qreal height() const const
Definition: object.h:54
void flipSelection(bool flipVertical)
ScribbleArea::flipSelection flip selection along the X or Y axis.
QPointF bottomRight() const const
Definition: editor.h:51