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
erasertool.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 "erasertool.h"
18
19#include <QSettings>
20#include <QPixmap>
21#include <QPainter>
22
23#include "editor.h"
24#include "blitrect.h"
25#include "scribblearea.h"
26#include "layermanager.h"
27#include "viewmanager.h"
28#include "undoredomanager.h"
29#include "layervector.h"
30#include "vectorimage.h"
31#include "pointerevent.h"
32
33
34EraserTool::EraserTool(QObject* parent) : StrokeTool(parent)
35{
36}
37
38ToolType EraserTool::type() const
39{
40 return ERASER;
41}
42
43void EraserTool::loadSettings()
44{
45 StrokeTool::loadSettings();
46
47 QSettings pencilSettings(PENCIL2D, PENCIL2D);
48
49 QHash<int, PropertyInfo> info;
50
51 mPropertyUsed[StrokeToolProperties::WIDTH_VALUE] = { Layer::BITMAP, Layer::VECTOR };
52 mPropertyUsed[StrokeToolProperties::FEATHER_VALUE] = { Layer::BITMAP };
53 mPropertyUsed[StrokeToolProperties::FEATHER_ENABLED] = { Layer::BITMAP };
54 mPropertyUsed[StrokeToolProperties::PRESSURE_ENABLED] = { Layer::BITMAP, Layer::VECTOR };
55 mPropertyUsed[StrokeToolProperties::STABILIZATION_VALUE] = { Layer::BITMAP, Layer::VECTOR };
56 mPropertyUsed[StrokeToolProperties::ANTI_ALIASING_ENABLED] = { Layer::BITMAP };
57
58 info[StrokeToolProperties::WIDTH_VALUE] = { WIDTH_MIN, WIDTH_MAX, 24.0 };
59 info[StrokeToolProperties::FEATHER_VALUE] = { FEATHER_MIN, FEATHER_MAX, 48.0 };
60 info[StrokeToolProperties::FEATHER_ENABLED] = true;
61 info[StrokeToolProperties::PRESSURE_ENABLED] = true;
62 info[StrokeToolProperties::STABILIZATION_VALUE] = { StabilizationLevel::NONE, StabilizationLevel::STRONG, StabilizationLevel::NONE };
63 info[StrokeToolProperties::ANTI_ALIASING_ENABLED] = true;
64
65 toolProperties().insertProperties(info);
66 toolProperties().loadFrom(typeName(), pencilSettings);
67
68 if (toolProperties().requireMigration(pencilSettings, ToolProperties::VERSION_1)) {
69 toolProperties().setBaseValue(StrokeToolProperties::WIDTH_VALUE, pencilSettings.value("eraserWidth", 24.0).toReal());
70 toolProperties().setBaseValue(StrokeToolProperties::FEATHER_VALUE, pencilSettings.value("eraserFeather", 48.0).toReal());
71 toolProperties().setBaseValue(StrokeToolProperties::STABILIZATION_VALUE, pencilSettings.value("stabilizerLevel", StabilizationLevel::NONE).toInt());
72 toolProperties().setBaseValue(StrokeToolProperties::FEATHER_ENABLED, pencilSettings.value("eraserUseFeather", true).toBool());
73 toolProperties().setBaseValue(StrokeToolProperties::PRESSURE_ENABLED, pencilSettings.value("eraserPressure", true).toBool());
74 toolProperties().setBaseValue(StrokeToolProperties::ANTI_ALIASING_ENABLED, pencilSettings.value("eraserAA", true).toBool());
75
76 pencilSettings.remove("eraserWidth");
77 pencilSettings.remove("eraserFeather");
78 pencilSettings.remove("stabilizerLevel");
79 pencilSettings.remove("eraserUseFeather");
80 pencilSettings.remove("eraserPressure");
81 pencilSettings.remove("eraserAA");
82 }
83
84 mQuickSizingProperties.insert(Qt::ShiftModifier, StrokeToolProperties::WIDTH_VALUE);
85 mQuickSizingProperties.insert(Qt::ControlModifier, StrokeToolProperties::FEATHER_VALUE);
86}
87
88QCursor EraserTool::cursor()
89{
90 return QCursor(QPixmap(":icons/general/cross.png"), 10, 10);
91}
92
93void EraserTool::pointerPressEvent(PointerEvent *event)
94{
95 mInterpolator.pointerPressEvent(event);
96 if (handleQuickSizing(event)) {
97 return;
98 }
99
100 startStroke(event->inputType());
101 mLastBrushPoint = getCurrentPoint();
102 mMouseDownPoint = getCurrentPoint();
103
104 StrokeTool::pointerPressEvent(event);
105}
106
107void EraserTool::pointerMoveEvent(PointerEvent* event)
108{
109 mInterpolator.pointerMoveEvent(event);
110 if (handleQuickSizing(event)) {
111 return;
112 }
113
114 if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType)
115 {
116 mCurrentPressure = mInterpolator.getPressure();
117 updateStrokes();
118 if (mSettings.stabilizerLevel() != mInterpolator.getStabilizerLevel())
119 {
120 mInterpolator.setStabilizerLevel(mSettings.stabilizerLevel());
121 }
122 }
123
124 StrokeTool::pointerMoveEvent(event);
125}
126
127void EraserTool::pointerReleaseEvent(PointerEvent *event)
128{
129 mInterpolator.pointerReleaseEvent(event);
130 if (handleQuickSizing(event)) {
131 return;
132 }
133
134 if (event->inputType() != mCurrentInputType) return;
135
136 mEditor->backup(typeName());
137
138 qreal distance = QLineF(getCurrentPoint(), mMouseDownPoint).length();
139 if (distance < 1)
140 {
141 paintAt(mMouseDownPoint);
142 }
143 else
144 {
145 drawStroke();
146 }
147
148 removeVectorPaint();
149 endStroke();
150
151 StrokeTool::pointerReleaseEvent(event);
152}
153
154// draw a single paint dab at the given location
155void EraserTool::paintAt(QPointF point)
156{
157 Layer* layer = mEditor->layers()->currentLayer();
158 if (layer->type() == Layer::BITMAP)
159 {
160 qreal pressure = (mSettings.pressureEnabled()) ? mCurrentPressure : 1.0;
161 qreal opacity = (mSettings.pressureEnabled()) ? (mCurrentPressure * 0.5) : 1.0;
162 qreal brushWidth = mSettings.width() * pressure;
163 mCurrentWidth = brushWidth;
164
165 mScribbleArea->drawBrush(point,
166 brushWidth,
167 mSettings.feather(),
168 QColor(255, 255, 255, 255),
169 QPainter::CompositionMode_SourceOver,
170 opacity,
171 mSettings.featherEnabled(),
172 mSettings.AntiAliasingEnabled() == ON);
173 }
174}
175
176void EraserTool::drawStroke()
177{
178 StrokeTool::drawStroke();
179 QList<QPointF> p = mInterpolator.interpolateStroke();
180
181 Layer* layer = mEditor->layers()->currentLayer();
182
183 if (layer->type() == Layer::BITMAP)
184 {
185 qreal pressure = (mSettings.pressureEnabled()) ? mCurrentPressure : 1.0;
186 qreal opacity = (mSettings.pressureEnabled()) ? (mCurrentPressure * 0.5) : 1.0;
187 qreal brushWidth = mSettings.width() * pressure;
188 mCurrentWidth = brushWidth;
189
190 qreal brushStep = (0.5 * brushWidth);
191 brushStep = qMax(1.0, brushStep);
192
193 BlitRect rect;
194
195 QPointF a = mLastBrushPoint;
196 QPointF b = getCurrentPoint();
197
198 qreal distance = 4 * QLineF(b, a).length();
199 int steps = qRound(distance / brushStep);
200
201 for (int i = 0; i < steps; i++)
202 {
203 QPointF point = mLastBrushPoint + (i + 1) * brushStep * (getCurrentPoint() - mLastBrushPoint) / distance;
204
205 mScribbleArea->drawBrush(point,
206 brushWidth,
207 mSettings.feather(),
208 Qt::white,
209 QPainter::CompositionMode_SourceOver,
210 opacity,
211 mSettings.featherEnabled(),
212 mSettings.AntiAliasingEnabled() == ON);
213 if (i == (steps - 1))
214 {
215 mLastBrushPoint = getCurrentPoint();
216 }
217 }
218 }
219 else if (layer->type() == Layer::VECTOR)
220 {
221 mCurrentWidth = mSettings.width();
222 if (mSettings.pressureEnabled())
223 {
224 mCurrentWidth = (mCurrentWidth + (mInterpolator.getPressure() * mCurrentWidth)) * 0.5;
225 }
226 qreal brushWidth = mCurrentWidth;
227
228 QPen pen(Qt::white, brushWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
229
230 if (p.size() == 4)
231 {
232 QPainterPath path(p[0]);
233 path.cubicTo(p[1],
234 p[2],
235 p[3]);
236 mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source);
237 }
238 }
239}
240
241void EraserTool::removeVectorPaint()
242{
243 Layer* layer = mEditor->layers()->currentLayer();
244 if (layer->type() == Layer::VECTOR)
245 {
246 mScribbleArea->clearDrawingBuffer();
247 VectorImage* vectorImage = static_cast<LayerVector*>(layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
248 if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing
249 // Clear the area containing the last point
250 //vectorImage->removeArea(lastPoint);
251 // Clear the temporary pixel path
252 vectorImage->deleteSelectedPoints();
253
254 mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
255 }
256}
257
258void EraserTool::updateStrokes()
259{
260 Layer* layer = mEditor->layers()->currentLayer();
261 if (layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR)
262 {
263 drawStroke();
264 }
265
266 if (layer->type() == Layer::VECTOR)
267 {
268 qreal radius = mSettings.width() / 2;
269
270 VectorImage* currKey = static_cast<VectorImage*>(layer->getLastKeyFrameAtPosition(mEditor->currentFrame()));
271 QList<VertexRef> nearbyVertices = currKey->getVerticesCloseTo(getCurrentPoint(), radius);
272 for (auto nearbyVertice : nearbyVertices)
273 {
274 currKey->setSelected(nearbyVertice, true);
275 }
276 }
277}
BlitRect
Definition: blitrect.h:25
Layer
Definition: layer.h:33
LayerVector
Definition: layervector.h:26
PointerEvent
Definition: pointerevent.h:8
StrokeTool
Definition: stroketool.h:35
StrokeTool::loadSettings
void loadSettings() override
Definition: stroketool.cpp:61
VectorImage
Definition: vectorimage.h:32
VectorImage::setSelected
void setSelected(int curveNumber, bool YesOrNo)
VectorImage::setSelected.
Definition: vectorimage.cpp:616
VectorImage::deleteSelectedPoints
void deleteSelectedPoints()
VectorImage::deleteSelectedPoints.
Definition: vectorimage.cpp:1030
VectorImage::getVerticesCloseTo
QList< VertexRef > getVerticesCloseTo(QPointF thisPoint, qreal maxDistance)
VectorImage::getVerticesCloseTo.
Definition: vectorimage.cpp:1458
QColor
QCursor
QHash
QHash::insert
QHash::iterator insert(const Key &key, const T &value)
QLineF
QLineF::length
qreal length() const const
QList
QList::size
int size() const const
QObject
QObject::event
virtual bool event(QEvent *e)
QPainter::CompositionMode_SourceOver
CompositionMode_SourceOver
QPainterPath
QPen
QPixmap
QPointF
QSettings
QSettings::remove
void remove(const QString &key)
QSettings::value
QVariant value(const QString &key, const QVariant &defaultValue) const const
Qt::NoBrush
NoBrush
Qt::white
white
Qt::ShiftModifier
ShiftModifier
Qt::LeftButton
LeftButton
Qt::RoundCap
RoundCap
Qt::RoundJoin
RoundJoin
Qt::SolidLine
SolidLine
QVariant::toBool
bool toBool() const const
QVariant::toInt
int toInt(bool *ok) const const
QVariant::toReal
qreal toReal(bool *ok) const const
Generated on Thu Jan 29 2026 05:10:58 for Pencil2D by doxygen 1.9.6 based on revision 2c971b937d0608b05aa496b0f7e9aebcddf8e7fc