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
pentool.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 "pentool.h"
18
19#include <QPixmap>
20#include <QSettings>
21
22#include "vectorimage.h"
23#include "layervector.h"
24#include "colormanager.h"
25#include "layermanager.h"
26#include "viewmanager.h"
27#include "undoredomanager.h"
28#include "selectionmanager.h"
29#include "editor.h"
30#include "scribblearea.h"
31#include "blitrect.h"
32#include "pointerevent.h"
33
34
35PenTool::PenTool(QObject* parent) : StrokeTool(parent)
36{
37}
38
39void PenTool::loadSettings()
40{
41 StrokeTool::loadSettings();
42
43 mPropertyEnabled[WIDTH] = true;
44 mPropertyEnabled[PRESSURE] = true;
45 mPropertyEnabled[VECTORMERGE] = true;
46 mPropertyEnabled[ANTI_ALIASING] = true;
47 mPropertyEnabled[STABILIZATION] = true;
48
49 QSettings settings(PENCIL2D, PENCIL2D);
50
51 properties.width = settings.value("penWidth", 12.0).toDouble();
52 properties.pressure = settings.value("penPressure", true).toBool();
53 properties.invisibility = OFF;
54 properties.preserveAlpha = OFF;
55 properties.useAA = settings.value("penAA", true).toBool();
56 properties.stabilizerLevel = settings.value("penLineStabilization", StabilizationLevel::STRONG).toInt();
57 properties.useFeather = false;
58
59 mQuickSizingProperties.insert(Qt::ShiftModifier, WIDTH);
60}
61
62void PenTool::saveSettings()
63{
64 QSettings settings(PENCIL2D, PENCIL2D);
65
66 settings.setValue("penWidth", properties.width);
67 settings.setValue("penPressure", properties.pressure);
68 settings.setValue("penAA", properties.useAA);
69 settings.setValue("penLineStabilization", properties.stabilizerLevel);
70
71 settings.sync();
72}
73
74void PenTool::resetToDefault()
75{
76 setWidth(12.0);
77 setUseFeather(false);
78 setPressure(true);
79 setStabilizerLevel(StabilizationLevel::STRONG);
80 setAA(1);
81}
82
83void PenTool::setWidth(const qreal width)
84{
85 // Set current property
86 properties.width = width;
87}
88
89void PenTool::setPressure(const bool pressure)
90{
91 // Set current property
92 properties.pressure = pressure;
93}
94
95void PenTool::setAA(const int AA)
96{
97 // Set current property
98 properties.useAA = AA;
99
100}
101
102void PenTool::setStabilizerLevel(const int level)
103{
104 properties.stabilizerLevel = level;
105}
106
107QCursor PenTool::cursor()
108{
109 if (mEditor->preference()->isOn(SETTING::TOOL_CURSOR))
110 {
111 return QCursor(QPixmap(":icons/general/cursor-pen.svg"), 5, 14);
112 }
113 return QCursor(QPixmap(":icons/general/cross.png"), 10, 10);
114}
115
116void PenTool::pointerPressEvent(PointerEvent *event)
117{
118 mInterpolator.pointerPressEvent(event);
119 if (handleQuickSizing(event)) {
120 return;
121 }
122
123 mMouseDownPoint = getCurrentPoint();
124 mLastBrushPoint = getCurrentPoint();
125
126 startStroke(event->inputType());
127
128 StrokeTool::pointerPressEvent(event);
129}
130
131void PenTool::pointerMoveEvent(PointerEvent* event)
132{
133 mInterpolator.pointerMoveEvent(event);
134 if (handleQuickSizing(event)) {
135 return;
136 }
137
138 if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType)
139 {
140 mCurrentPressure = mInterpolator.getPressure();
141 drawStroke();
142 if (properties.stabilizerLevel != mInterpolator.getStabilizerLevel())
143 {
144 mInterpolator.setStabilizerLevel(properties.stabilizerLevel);
145 }
146 }
147
148 StrokeTool::pointerMoveEvent(event);
149}
150
151void PenTool::pointerReleaseEvent(PointerEvent *event)
152{
153 mInterpolator.pointerReleaseEvent(event);
154 if (handleQuickSizing(event)) {
155 return;
156 }
157
158 if (event->inputType() != mCurrentInputType) return;
159
160 mEditor->backup(typeName());
161
162 Layer* layer = mEditor->layers()->currentLayer();
163
164 qreal distance = QLineF(getCurrentPoint(), mMouseDownPoint).length();
165 if (distance < 1)
166 {
167 paintAt(mMouseDownPoint);
168 }
169 else
170 {
171 drawStroke();
172 }
173
174 if (layer->type() == Layer::VECTOR) {
175 paintVectorStroke(layer);
176 }
177 endStroke();
178
179 StrokeTool::pointerReleaseEvent(event);
180}
181
182// draw a single paint dab at the given location
183void PenTool::paintAt(QPointF point)
184{
185 Layer* layer = mEditor->layers()->currentLayer();
186 if (layer->type() == Layer::BITMAP)
187 {
188 qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
189 qreal brushWidth = properties.width * pressure;
190 mCurrentWidth = brushWidth;
191
192 mScribbleArea->drawPen(point,
193 brushWidth,
194 mEditor->color()->frontColor(),
195 properties.useAA);
196 }
197}
198
199void PenTool::drawStroke()
200{
201 StrokeTool::drawStroke();
202 QList<QPointF> p = mInterpolator.interpolateStroke();
203
204 Layer* layer = mEditor->layers()->currentLayer();
205
206 if (layer->type() == Layer::BITMAP)
207 {
208 qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
209 qreal brushWidth = properties.width * pressure;
210 mCurrentWidth = brushWidth;
211
212 // TODO: Make popup widget for less important properties,
213 // Eg. stepsize should be a slider.. will have fixed (0.3) value for now.
214 qreal brushStep = (0.5 * brushWidth);
215 brushStep = qMax(1.0, brushStep);
216
217 QPointF a = mLastBrushPoint;
218 QPointF b = getCurrentPoint();
219
220 qreal distance = 4 * QLineF(b, a).length();
221 int steps = qRound(distance / brushStep);
222
223 for (int i = 0; i < steps; i++)
224 {
225 QPointF point = mLastBrushPoint + (i + 1) * brushStep * (getCurrentPoint() - mLastBrushPoint) / distance;
226 mScribbleArea->drawPen(point,
227 brushWidth,
228 mEditor->color()->frontColor(),
229 properties.useAA);
230
231 if (i == (steps - 1))
232 {
233 mLastBrushPoint = getCurrentPoint();
234 }
235 }
236 }
237 else if (layer->type() == Layer::VECTOR)
238 {
239 qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0;
240 qreal brushWidth = properties.width * pressure;
241
242 QPen pen(mEditor->color()->frontColor(),
243 brushWidth,
244 Qt::SolidLine,
245 Qt::RoundCap,
246 Qt::RoundJoin);
247
248 if (p.size() == 4)
249 {
250 QPainterPath path(p[0]);
251 path.cubicTo(p[1], p[2], p[3]);
252 mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source);
253 }
254 }
255}
256
257void PenTool::paintVectorStroke(Layer* layer)
258{
259 if (mStrokePoints.empty())
260 return;
261
262 // Clear the temporary pixel path
263 mScribbleArea->clearDrawingBuffer();
264 qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling();
265
266 BezierCurve curve(mStrokePoints, mStrokePressures, tol);
267 curve.setWidth(properties.width);
268 curve.setFeather(properties.feather);
269 curve.setFilled(false);
270 curve.setInvisibility(properties.invisibility);
271 curve.setVariableWidth(properties.pressure);
272 curve.setColorNumber(mEditor->color()->frontColorNumber());
273
274 auto pLayerVector = static_cast<LayerVector*>(layer);
275 VectorImage* vectorImage = pLayerVector->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
276 if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing
277 vectorImage->addCurve(curve, mEditor->view()->scaling(), false);
278
279 if (vectorImage->isAnyCurveSelected() || mEditor->select()->somethingSelected())
280 {
281 mEditor->deselectAll();
282 }
283
284 vectorImage->setSelected(vectorImage->getLastCurveNumber(), true);
285
286 mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
287}
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::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::SolidLine
SolidLine
Generated on Mon Dec 15 2025 03:41:35 for Pencil2D by doxygen 1.9.6 based on revision 9bfef078cfa681fa5250352bbcb5a69281765ae9