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
penciltool.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 "penciltool.h"
18
19#include <QSettings>
20#include <QPixmap>
21#include "pointerevent.h"
22
23#include "layermanager.h"
24#include "colormanager.h"
25#include "viewmanager.h"
26#include "preferencemanager.h"
27#include "selectionmanager.h"
28#include "undoredomanager.h"
29
30#include "editor.h"
31#include "scribblearea.h"
32#include "layervector.h"
33#include "vectorimage.h"
34
35
36PencilTool::PencilTool(QObject* parent) : StrokeTool(parent)
37{
38}
39
40void PencilTool::loadSettings()
41{
42 StrokeTool::loadSettings();
43
44 mPropertyEnabled[WIDTH] = true;
45 mPropertyEnabled[PRESSURE] = true;
46 mPropertyEnabled[VECTORMERGE] = false;
47 mPropertyEnabled[STABILIZATION] = true;
48 mPropertyEnabled[FILLCONTOUR] = true;
49
50 QSettings settings(PENCIL2D, PENCIL2D);
51 properties.width = settings.value("pencilWidth", 4).toDouble();
52 properties.feather = 50;
53 properties.pressure = settings.value("pencilPressure", true).toBool();
54 properties.stabilizerLevel = settings.value("pencilLineStabilization", StabilizationLevel::STRONG).toInt();
55 properties.useAA = DISABLED;
56 properties.useFillContour = false;
57 properties.useFeather = false;
58
59 mQuickSizingProperties.insert(Qt::ShiftModifier, WIDTH);
60}
61
62void PencilTool::saveSettings()
63{
64 QSettings settings(PENCIL2D, PENCIL2D);
65
66 settings.setValue("pencilWidth", properties.width);
67 settings.setValue("pencilPressure", properties.pressure);
68 settings.setValue("brushUseFeather", properties.useFeather);
69 settings.setValue("pencilLineStabilization", properties.stabilizerLevel);
70 settings.setValue("FillContour", properties.useFillContour);
71
72 settings.sync();
73}
74
75void PencilTool::resetToDefault()
76{
77 setWidth(4.0);
78 setFeather(50);
79 setUseFeather(false);
80 setStabilizerLevel(StabilizationLevel::STRONG);
81}
82
83void PencilTool::setWidth(const qreal width)
84{
85 // Set current property
86 properties.width = width;
87}
88
89void PencilTool::setFeather(const qreal feather)
90{
91 properties.feather = feather;
92}
93
94void PencilTool::setUseFeather(const bool usingFeather)
95{
96 // Set current property
97 properties.useFeather = usingFeather;
98
99}
100
101void PencilTool::setInvisibility(const bool)
102{
103 // force value
104 properties.invisibility = 1;
105}
106
107void PencilTool::setPressure(const bool pressure)
108{
109 // Set current property
110 properties.pressure = pressure;
111}
112
113void PencilTool::setPreserveAlpha(const bool preserveAlpha)
114{
115 // force value
116 Q_UNUSED( preserveAlpha )
117 properties.preserveAlpha = 0;
118}
119
120void PencilTool::setStabilizerLevel(const int level)
121{
122 properties.stabilizerLevel = level;
123}
124
125void PencilTool::setUseFillContour(const bool useFillContour)
126{
127 properties.useFillContour = useFillContour;
128}
129
130QCursor PencilTool::cursor()
131{
132 if (mEditor->preference()->isOn(SETTING::TOOL_CURSOR))
133 {
134 return QCursor(QPixmap(":icons/general/cursor-pencil.svg"), 4, 14);
135 }
136 return QCursor(QPixmap(":icons/general/cross.png"), 10, 10);
137}
138
139void PencilTool::pointerPressEvent(PointerEvent *event)
140{
141 mInterpolator.pointerPressEvent(event);
142 if (handleQuickSizing(event)) {
143 return;
144 }
145
146 mMouseDownPoint = getCurrentPoint();
147 mLastBrushPoint = getCurrentPoint();
148
149 startStroke(event->inputType());
150
151 // note: why are we doing this on device press event?
152 if (mEditor->layers()->currentLayer()->type() == Layer::VECTOR && !mEditor->preference()->isOn(SETTING::INVISIBLE_LINES))
153 {
154 mScribbleArea->toggleThinLines();
155 }
156
157 StrokeTool::pointerPressEvent(event);
158}
159
160void PencilTool::pointerMoveEvent(PointerEvent* event)
161{
162 mInterpolator.pointerMoveEvent(event);
163 if (handleQuickSizing(event)) {
164 return;
165 }
166
167 if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType)
168 {
169 mCurrentPressure = mInterpolator.getPressure();
170 drawStroke();
171 if (properties.stabilizerLevel != mInterpolator.getStabilizerLevel())
172 {
173 mInterpolator.setStabilizerLevel(properties.stabilizerLevel);
174 }
175 }
176 StrokeTool::pointerMoveEvent(event);
177}
178
179void PencilTool::pointerReleaseEvent(PointerEvent *event)
180{
181 mInterpolator.pointerReleaseEvent(event);
182 if (handleQuickSizing(event)) {
183 return;
184 }
185
186 if (event->inputType() != mCurrentInputType) return;
187
188 mEditor->backup(typeName());
189 qreal distance = QLineF(getCurrentPoint(), mMouseDownPoint).length();
190 if (distance < 1)
191 {
192 paintAt(mMouseDownPoint);
193 }
194 else
195 {
196 drawStroke();
197 }
198
199 Layer* layer = mEditor->layers()->currentLayer();
200 if (layer->type() == Layer::VECTOR) {
201 paintVectorStroke(layer);
202 }
203 endStroke();
204
205 StrokeTool::pointerReleaseEvent(event);
206}
207
208// draw a single paint dab at the given location
209void PencilTool::paintAt(QPointF point)
210{
211 //qDebug() << "Made a single dab at " << point;
212 Layer* layer = mEditor->layers()->currentLayer();
213 if (layer->type() == Layer::BITMAP)
214 {
215 qreal opacity = (properties.pressure) ? (mCurrentPressure * 0.5) : 1.0;
216 qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
217 qreal brushWidth = properties.width * pressure;
218 qreal fixedBrushFeather = properties.feather;
219
220 mCurrentWidth = brushWidth;
221 mScribbleArea->drawPencil(point,
222 brushWidth,
223 fixedBrushFeather,
224 mEditor->color()->frontColor(),
225 opacity);
226 }
227}
228
229
230void PencilTool::drawStroke()
231{
232 StrokeTool::drawStroke();
233 QList<QPointF> p = mInterpolator.interpolateStroke();
234
235 Layer* layer = mEditor->layers()->currentLayer();
236
237 if (layer->type() == Layer::BITMAP)
238 {
239 qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
240 qreal opacity = (properties.pressure) ? (mCurrentPressure * 0.5) : 1.0;
241 qreal brushWidth = properties.width * pressure;
242 mCurrentWidth = brushWidth;
243
244 qreal fixedBrushFeather = properties.feather;
245 qreal brushStep = qMax(1.0, (0.5 * brushWidth));
246
247 QPointF a = mLastBrushPoint;
248 QPointF b = getCurrentPoint();
249
250 qreal distance = 4 * QLineF(b, a).length();
251 int steps = qRound(distance / brushStep);
252
253 for (int i = 0; i < steps; i++)
254 {
255 QPointF point = mLastBrushPoint + (i + 1) * brushStep * (getCurrentPoint() - mLastBrushPoint) / distance;
256 mScribbleArea->drawPencil(point,
257 brushWidth,
258 fixedBrushFeather,
259 mEditor->color()->frontColor(),
260 opacity);
261
262 if (i == (steps - 1))
263 {
264 mLastBrushPoint = getCurrentPoint();
265 }
266 }
267 }
268 else if (layer->type() == Layer::VECTOR)
269 {
270 mCurrentWidth = 0; // FIXME: WTF?
271 QPen pen(mEditor->color()->frontColor(),
272 1,
273 Qt::DotLine,
274 Qt::RoundCap,
275 Qt::RoundJoin);
276
277 if (p.size() == 4)
278 {
279 QPainterPath path(p[0]);
280 path.cubicTo(p[1],
281 p[2],
282 p[3]);
283 mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source);
284 }
285 }
286}
287
288void PencilTool::paintVectorStroke(Layer* layer)
289{
290 if (mStrokePoints.empty())
291 return;
292
293 // Clear the temporary pixel path
294 mScribbleArea->clearDrawingBuffer();
295 qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling();
296
297 BezierCurve curve(mStrokePoints, mStrokePressures, tol);
298 curve.setWidth(0);
299 curve.setFeather(0);
300 curve.setFilled(false);
301 curve.setInvisibility(true);
302 curve.setVariableWidth(false);
303 curve.setColorNumber(mEditor->color()->frontColorNumber());
304 VectorImage* vectorImage = static_cast<LayerVector*>(layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
305 if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing
306 vectorImage->addCurve(curve, qAbs(mEditor->view()->scaling()), properties.vectorMergeEnabled);
307
308 if (properties.useFillContour)
309 {
310 vectorImage->fillContour(mStrokePoints,
311 mEditor->color()->frontColorNumber());
312 }
313
314 if (vectorImage->isAnyCurveSelected() || mEditor->select()->somethingSelected())
315 {
316 mEditor->deselectAll();
317 }
318
319 // select last/newest curve
320 vectorImage->setSelected(vectorImage->getLastCurveNumber(), true);
321
322 // TODO: selection doesn't apply on enter
323
324 mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
325}
BezierCurve
Definition: beziercurve.h:34
ColorManager::frontColor
QColor frontColor(bool useIndexedColor=true)
frontColor
Definition: colormanager.cpp:61
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:57
VectorImage
Definition: vectorimage.h:32
VectorImage::setSelected
void setSelected(int curveNumber, bool YesOrNo)
VectorImage::setSelected.
Definition: vectorimage.cpp:616
VectorImage::getLastCurveNumber
int getLastCurveNumber()
VectorImage::getLastCurveNumber.
Definition: vectorimage.cpp:2264
VectorImage::addCurve
void addCurve(BezierCurve &newCurve, qreal factor, bool interacts=true)
VectorImage::addCurve.
Definition: vectorimage.cpp:341
VectorImage::fillContour
void fillContour(QList< QPointF > contourPath, int color)
VectorImage::fillContour.
Definition: vectorimage.cpp:1829
VectorImage::isAnyCurveSelected
bool isAnyCurveSelected()
VectorImage::isAnyCurveSelected.
Definition: vectorimage.cpp:826
QCursor
QHash::insert
QHash::iterator insert(const Key &key, const T &value)
QLineF
QLineF::length
qreal length() const const
QList
QList::empty
bool empty() const const
QList::size
int size() const const
QObject
QObject::event
virtual bool event(QEvent *e)
QPainter::CompositionMode_Source
CompositionMode_Source
QPainterPath
QPen
QPixmap
QPointF
QSettings
Qt::NoBrush
NoBrush
Qt::ShiftModifier
ShiftModifier
Qt::LeftButton
LeftButton
Qt::RoundCap
RoundCap
Qt::RoundJoin
RoundJoin
Qt::DotLine
DotLine
Generated on Fri Dec 19 2025 07:54:21 for Pencil2D by doxygen 1.9.6 based on revision 7fd8cd9e03f2d31750e199ecec202e5c0f30e532