All Classes Namespaces Functions Variables Enumerations Properties Pages
basetool.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 
18 #include "basetool.h"
19 
20 #include <array>
21 #include <QtMath>
22 #include <QPixmap>
23 #include "editor.h"
24 #include "viewmanager.h"
25 #include "toolmanager.h"
26 #include "scribblearea.h"
27 #include "strokemanager.h"
28 #include "pointerevent.h"
29 
30 // ---- shared static variables ---- ( only one instance for all the tools )
31 qreal BaseTool::msOriginalPropertyValue; // start value (width, feather ..)
32 bool BaseTool::msIsAdjusting = false;
33 
34 
35 QString BaseTool::TypeName(ToolType type)
36 {
37  static std::array<QString, TOOL_TYPE_COUNT> map;
38 
39  if (map[0].isEmpty())
40  {
41  map[PENCIL] = tr("Pencil");
42  map[ERASER] = tr("Eraser");
43  map[SELECT] = tr("Select");
44  map[MOVE] = tr("Move");
45  map[HAND] = tr("Hand");
46  map[SMUDGE] = tr("Smudge");
47  map[PEN] = tr("Pen");
48  map[POLYLINE] = tr("Polyline");
49  map[BUCKET] = tr("Bucket");
50  map[EYEDROPPER] = tr("Eyedropper");
51  map[BRUSH] = tr("Brush");
52  }
53  return map.at(type);
54 }
55 
56 BaseTool::BaseTool(QObject* parent) : QObject(parent)
57 {
58  mPropertyEnabled.insert(WIDTH, false);
59  mPropertyEnabled.insert(FEATHER, false);
60  mPropertyEnabled.insert(USEFEATHER, false);
61  mPropertyEnabled.insert(PRESSURE, false);
62  mPropertyEnabled.insert(INVISIBILITY, false);
63  mPropertyEnabled.insert(PRESERVEALPHA, false);
64  mPropertyEnabled.insert(BEZIER, false);
65  mPropertyEnabled.insert(ANTI_ALIASING, false);
66  mPropertyEnabled.insert(FILL_MODE, false);
67  mPropertyEnabled.insert(STABILIZATION, false);
68 }
69 
70 QCursor BaseTool::cursor()
71 {
72  return Qt::ArrowCursor;
73 }
74 
75 void BaseTool::initialize(Editor* editor)
76 {
77  Q_ASSERT(editor);
78  mEditor = editor;
79  mScribbleArea = editor->getScribbleArea();
80  Q_ASSERT(mScribbleArea);
81 
82  mStrokeManager = mEditor->getScribbleArea()->getStrokeManager();
83  loadSettings();
84 }
85 
86 void BaseTool::pointerPressEvent(PointerEvent* event)
87 {
88  event->accept();
89 }
90 
91 void BaseTool::pointerMoveEvent(PointerEvent* event)
92 {
93  event->accept();
94 }
95 
96 void BaseTool::pointerReleaseEvent(PointerEvent* event)
97 {
98  event->accept();
99 }
100 
101 void BaseTool::pointerDoubleClickEvent(PointerEvent* event)
102 {
103  pointerPressEvent(event);
104 }
105 
112 {
113  if (type() == ToolType::HAND || type() == ToolType::MOVE || type() == ToolType::SELECT )
114  {
115  return false;
116  }
117  return true;
118 }
119 
124 QPixmap BaseTool::canvasCursor(float width, float feather, bool useFeather, float scalingFac, int windowWidth)
125 {
126  float propWidth = width * scalingFac;
127  float propFeather = feather * scalingFac;
128 
129  float cursorWidth = 0.0f;
130  float xyA = 0.0f;
131  float xyB = 0.0f;
132  float whA = 0.0f;
133  float whB = 0.0f;
134 
135  if (useFeather)
136  {
137  cursorWidth = propWidth + 0.5 * propFeather;
138  xyA = 1 + propFeather / 2;
139  xyB = 1 + propFeather / 8;
140  whA = qMax<float>(0, propWidth - xyA - 1);
141  whB = qMax<float>(0, cursorWidth - propFeather / 4 - 2);
142  }
143  else
144  {
145  cursorWidth = (propWidth + 0.5);
146  whA = qMax<float>(0, propWidth - 1);
147  whB = qMax<float>(0, cursorWidth / 4 - 2);
148  }
149 
150  float radius = cursorWidth / 2;
151 
152  // deallocate when cursor width gets some value larger than the widget
153  if (cursorWidth > windowWidth * 2)
154  {
155  return QPixmap(0, 0);
156  }
157 
158  if (cursorWidth < 1) { cursorWidth = 1; }
159 
160  QPixmap cursorPixmap = QPixmap(cursorWidth, cursorWidth);
161  if (!cursorPixmap.isNull())
162  {
163  cursorPixmap.fill(QColor(255, 255, 255, 0));
164  QPainter cursorPainter(&cursorPixmap);
165  QPen cursorPen = cursorPainter.pen();
167 
168  // Draw cross in center
169  cursorPen.setStyle(Qt::SolidLine);
170  cursorPen.setColor(QColor(0, 0, 0, 127));
171  cursorPainter.setPen(cursorPen);
172  cursorPainter.drawLine(QPointF(radius - 2, radius), QPointF(radius + 2, radius));
173  cursorPainter.drawLine(QPointF(radius, radius - 2), QPointF(radius, radius + 2));
174 
175  // Draw outer circle
176  if (useFeather)
177  {
178  cursorPen.setStyle(Qt::DotLine);
179  cursorPen.setColor(QColor(0, 0, 0, 255));
180  cursorPainter.setPen(cursorPen);
181  cursorPainter.drawEllipse(QRectF(xyB, xyB, whB, whB));
182  cursorPen.setDashOffset(4);
183  cursorPen.setColor(QColor(255, 255, 255, 255));
184  cursorPainter.setPen(cursorPen);
185  cursorPainter.drawEllipse(QRectF(xyB, xyB, whB, whB));
186  }
187 
188  // Draw inner circle
189  cursorPen.setStyle(Qt::DotLine);
190  cursorPen.setColor(QColor(0, 0, 0, 255));
191  cursorPainter.setPen(cursorPen);
192  cursorPainter.drawEllipse(QRectF(xyA, xyA, whA, whA));
193  cursorPen.setDashOffset(4);
194  cursorPen.setColor(QColor(255, 255, 255, 255));
195  cursorPainter.setPen(cursorPen);
196  cursorPainter.drawEllipse(QRectF(xyA, xyA, whA, whA));
197 
198  cursorPainter.end();
199  }
200  return cursorPixmap;
201 }
202 
203 QCursor BaseTool::selectMoveCursor(MoveMode mode, ToolType type)
204 {
205  QPixmap cursorPixmap = QPixmap(24, 24);
206  if (!cursorPixmap.isNull())
207  {
208  cursorPixmap.fill(QColor(255, 255, 255, 0));
209  QPainter cursorPainter(&cursorPixmap);
210  cursorPainter.setRenderHint(QPainter::HighQualityAntialiasing);
211 
212  switch(mode)
213  {
214  case MoveMode::PERSP_LEFT:
215  case MoveMode::PERSP_RIGHT:
216  case MoveMode::PERSP_MIDDLE:
217  case MoveMode::PERSP_SINGLE:
218  {
219  cursorPainter.drawImage(QPoint(6,6),QImage("://icons/new/arrow-selectmove.png"));
220  break;
221  }
222  case MoveMode::MIDDLE:
223  {
224  if (type == SELECT) {
225  cursorPainter.drawImage(QPoint(6,6),QImage("://icons/new/arrow-selectmove.png"));
226  } else {
227  return Qt::ArrowCursor;
228  }
229  break;
230  }
231  case MoveMode::TOPLEFT:
232  case MoveMode::BOTTOMRIGHT:
233  {
234  cursorPainter.drawImage(QPoint(6,6),QImage("://icons/new/arrow-diagonalleft.png"));
235  break;
236  }
237  case MoveMode::TOPRIGHT:
238  case MoveMode::BOTTOMLEFT:
239  {
240  cursorPainter.drawImage(QPoint(6,6),QImage("://icons/new/arrow-diagonalright.png"));
241  break;
242  }
243 
244  default:
245  return (type == SELECT) ? Qt::CrossCursor : Qt::ArrowCursor;
246  break;
247  }
248  cursorPainter.end();
249  }
250  return QCursor(cursorPixmap);
251 }
252 
254 {
255  return strokeManager()->isActive();
256 }
257 
263 {
264  qreal propSize = qMax(0., properties.width) * scalingFac;
265  qreal propFeather = qMax(0., properties.feather) * scalingFac;
266  QRectF cursorRect(0, 0, propSize+2, propSize+2);
267 
268  QRectF sizeRect = cursorRect.adjusted(1, 1, -1, -1);
269  qreal featherRadius = (1 - propFeather / 100) * propSize / 2.;
270 
271  QPixmap cursorPixmap = QPixmap(cursorRect.size().toSize());
272  if (!cursorPixmap.isNull())
273  {
274  cursorPixmap.fill(QColor(255, 255, 255, 0));
275  QPainter cursorPainter(&cursorPixmap);
276  cursorPainter.setRenderHints(QPainter::Antialiasing, true);
277 
278  // Draw width (outside circle)
279  cursorPainter.setPen(QColor(255, 127, 127, 127));
280  cursorPainter.setBrush(QColor(0, 255, 127, 127));
281  cursorPainter.drawEllipse(sizeRect);
282 
283  // Draw feather (inside circle)
285  cursorPainter.setPen(QColor(0, 0, 0, 0));
286  cursorPainter.setBrush(QColor(0, 191, 95, 127));
287  cursorPainter.drawEllipse(cursorRect.center(), featherRadius, featherRadius);
288 
289  // Draw cursor in center
290  cursorPainter.setRenderHints(QPainter::Antialiasing, false);
291  cursorPainter.setPen(QColor(0, 0, 0, 255));
292  cursorPainter.drawLine(cursorRect.center() - QPoint(2, 0), cursorRect.center() + QPoint(2, 0));
293  cursorPainter.drawLine(cursorRect.center() - QPoint(0, 2), cursorRect.center() + QPoint(0, 2));
294 
295  cursorPainter.end();
296  }
297  return cursorPixmap;
298 }
299 
300 bool BaseTool::startAdjusting(Qt::KeyboardModifiers modifiers, qreal step)
301 {
302  if (mQuickSizingProperties.contains(modifiers))
303  {
304  switch (mQuickSizingProperties.value(modifiers)) {
305  case WIDTH:
306  msOriginalPropertyValue = properties.width;
307  break;
308  case FEATHER:
309  msOriginalPropertyValue = properties.feather;
310  break;
311  case TOLERANCE:
312  msOriginalPropertyValue = properties.tolerance;
313  break;
314  default:
315  qDebug() << "Unhandled quick sizing property for tool" << typeName();
316  Q_ASSERT(false);
317  return false;
318  }
319 
320  msIsAdjusting = true;
321  mAdjustmentStep = step;
322  mScribbleArea->updateCanvasCursor();
323  return true;
324  }
325  return false;
326 }
327 
328 void BaseTool::stopAdjusting()
329 {
330  msIsAdjusting = false;
331  mAdjustmentStep = 0;
332  msOriginalPropertyValue = 0;
333  mEditor->getScribbleArea()->updateCanvasCursor();
334 }
335 
336 void BaseTool::adjustCursor(Qt::KeyboardModifiers modifiers)
337 {
338  qreal inc = qPow(msOriginalPropertyValue * 100, 0.5);
339  qreal newValue = inc + getCurrentPoint().x();
340 
341  if (newValue < 0)
342  {
343  newValue = 0;
344  }
345 
346  newValue = qPow(newValue, 2) / 100;
347  if (mAdjustmentStep > 0)
348  {
349  int tempValue = static_cast<int>(newValue / mAdjustmentStep); // + 0.5 ?
350  newValue = tempValue * mAdjustmentStep;
351  }
352 
353  switch (mQuickSizingProperties.value(modifiers))
354  {
355  case WIDTH:
356  mEditor->tools()->setWidth(qBound(1., newValue, 200.));
357  break;
358  case FEATHER:
359  mEditor->tools()->setFeather(qBound(2., newValue, 200.));
360  break;
361  case TOLERANCE:
362  mEditor->tools()->setTolerance(qBound(0., newValue, 100.));
363  break;
364  default:
365  qDebug() << "Unhandled quick sizing property for tool" << typeName();
366  Q_ASSERT(false);
367  break;
368  }
369 }
370 
371 QPointF BaseTool::getCurrentPressPixel()
372 {
373  return strokeManager()->getCurrentPressPixel();
374 }
375 
376 QPointF BaseTool::getCurrentPressPoint()
377 {
378  return mEditor->view()->mapScreenToCanvas(strokeManager()->getCurrentPressPixel());
379 }
380 
381 QPointF BaseTool::getCurrentPixel()
382 {
383  return strokeManager()->getCurrentPixel();
384 }
385 
386 QPointF BaseTool::getCurrentPoint()
387 {
388  return mEditor->view()->mapScreenToCanvas(getCurrentPixel());
389 }
390 
391 QPointF BaseTool::getLastPixel()
392 {
393  return strokeManager()->getLastPixel();
394 }
395 
396 QPointF BaseTool::getLastPoint()
397 {
398  return mEditor->view()->mapScreenToCanvas(getLastPixel());
399 }
400 
401 QPointF BaseTool::getLastPressPixel()
402 {
403  return strokeManager()->getLastPressPixel();
404 }
405 
406 QPointF BaseTool::getLastPressPoint()
407 {
408  return mEditor->view()->mapScreenToCanvas(getLastPressPixel());
409 }
410 
411 void BaseTool::setWidth(const qreal width)
412 {
413  properties.width = width;
414 }
415 
416 void BaseTool::setFeather(const qreal feather)
417 {
418  properties.feather = feather;
419 }
420 
421 void BaseTool::setUseFeather(const bool usingFeather)
422 {
423  properties.useFeather = usingFeather;
424 }
425 
426 void BaseTool::setInvisibility(const bool invisibility)
427 {
428  properties.invisibility = invisibility;
429 }
430 
431 void BaseTool::setBezier(const bool _bezier_state)
432 {
433  properties.bezier_state = _bezier_state;
434 }
435 
436 void BaseTool::setPressure(const bool pressure)
437 {
438  properties.pressure = pressure;
439 }
440 
441 void BaseTool::setPreserveAlpha(const bool preserveAlpha)
442 {
443  properties.preserveAlpha = preserveAlpha;
444 }
445 
446 void BaseTool::setVectorMergeEnabled(const bool vectorMergeEnabled)
447 {
448  properties.vectorMergeEnabled = vectorMergeEnabled;
449 }
450 
451 void BaseTool::setAA(const int useAA)
452 {
453  properties.useAA = useAA;
454 }
455 
456 void BaseTool::setFillMode(const int mode)
457 {
458  properties.fillMode = mode;
459 }
460 
461 void BaseTool::setStabilizerLevel(const int level)
462 {
463  properties.stabilizerLevel = level;
464 }
465 
466 void BaseTool::setTolerance(const int tolerance)
467 {
468  properties.tolerance = tolerance;
469 }
470 
471 void BaseTool::setToleranceEnabled(const bool enabled)
472 {
473  properties.toleranceEnabled = enabled;
474 }
475 
476 void BaseTool::setFillExpand(const int fillExpandValue)
477 {
478  properties.bucketFillExpand = fillExpandValue;
479 }
480 
481 void BaseTool::setFillToLayer(int layerMode)
482 {
483  properties.bucketFillToLayerMode = layerMode;
484 }
485 
486 void BaseTool::setFillReferenceMode(int referenceMode)
487 {
488  properties.bucketFillReferenceMode = referenceMode;
489 }
490 
491 void BaseTool::setFillExpandEnabled(const bool enabled)
492 {
493  properties.bucketFillExpandEnabled = enabled;
494 }
495 
496 void BaseTool::setUseFillContour(const bool useFillContour)
497 {
498  properties.useFillContour = useFillContour;
499 }
500 
501 void BaseTool::setShowSelectionInfo(const bool b)
502 {
503  properties.showSelectionInfo = b;
504 }
505 
506 
static QPixmap canvasCursor(float brushWidth, float brushFeather, bool useFeather, float scalingFac, int windowWidth)
precision circular cursor: used for drawing a cursor within scribble area.
Definition: basetool.cpp:124
typedef KeyboardModifiers
void setStyle(Qt::PenStyle style)
bool end()
void setCompositionMode(QPainter::CompositionMode mode)
QPixmap quickSizeCursor(qreal scalingFac)
precision circular cursor: used for drawing stroke size while adjusting
Definition: basetool.cpp:262
void setRenderHint(QPainter::RenderHint hint, bool on)
SolidLine
void fill(const QColor &color)
QSizeF size() const const
void drawLine(const QLineF &line)
QString tr(const char *sourceText, const char *disambiguation, int n)
bool isDrawingTool()
BaseTool::isDrawingTool - A drawing tool is anything that applies something to the canvas...
Definition: basetool.cpp:111
qreal x() const const
void setPen(const QColor &color)
void drawEllipse(const QRectF &rectangle)
QSize toSize() const const
void setBrush(const QBrush &brush)
ArrowCursor
QPointF center() const const
void setColor(const QColor &color)
const T value(const Key &key) const const
CompositionMode_Darken
bool isNull() const const
void setRenderHints(QPainter::RenderHints hints, bool on)
QRectF adjusted(qreal dx1, qreal dy1, qreal dx2, qreal dy2) const const
bool contains(const Key &key) const const
Definition: editor.h:55
HighQualityAntialiasing
const QPen & pen() const const
virtual bool isActive()
Check if the tool is active.
Definition: basetool.cpp:253
void setDashOffset(qreal offset)