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 QSettings pencilSettings(PENCIL2D, PENCIL2D);
44
45 mPropertyUsed[StrokeToolProperties::WIDTH_VALUE] = { Layer::BITMAP, Layer::VECTOR };
46 mPropertyUsed[StrokeToolProperties::PRESSURE_ENABLED] = { Layer::BITMAP, Layer::VECTOR };
47 mPropertyUsed[StrokeToolProperties::ANTI_ALIASING_ENABLED] = { Layer::BITMAP };
48 mPropertyUsed[StrokeToolProperties::STABILIZATION_VALUE] = { Layer::BITMAP, Layer::VECTOR };
49
50 QHash<int, PropertyInfo> info;
51
52 info[StrokeToolProperties::WIDTH_VALUE] = { WIDTH_MIN, WIDTH_MAX, 12.0 };
53 info[StrokeToolProperties::PRESSURE_ENABLED] = true;
54 info[StrokeToolProperties::ANTI_ALIASING_ENABLED] = true;
55 info[StrokeToolProperties::STABILIZATION_VALUE] = { StabilizationLevel::NONE, StabilizationLevel::STRONG, StabilizationLevel::STRONG };
56
57 toolProperties().insertProperties(info);
58 toolProperties().loadFrom(typeName(), pencilSettings);
59
60 if (toolProperties().requireMigration(pencilSettings, ToolProperties::VERSION_1)) {
61 toolProperties().setBaseValue(StrokeToolProperties::WIDTH_VALUE, pencilSettings.value("penWidth", 12.0).toReal());
62 toolProperties().setBaseValue(StrokeToolProperties::PRESSURE_ENABLED, pencilSettings.value("penPressure", true).toBool());
63 toolProperties().setBaseValue(StrokeToolProperties::ANTI_ALIASING_ENABLED, pencilSettings.value("penAA", true).toBool());
64 toolProperties().setBaseValue(StrokeToolProperties::STABILIZATION_VALUE, pencilSettings.value("penLineStablization", StabilizationLevel::STRONG).toInt());
65
66 pencilSettings.remove("penWidth");
67 pencilSettings.remove("penPressure");
68 pencilSettings.remove("penAA");
69 pencilSettings.remove("penLineStablization");
70 }
71
72 mQuickSizingProperties.insert(Qt::ShiftModifier, StrokeToolProperties::WIDTH_VALUE);
73}
74
75QCursor PenTool::cursor()
76{
77 if (mEditor->preference()->isOn(SETTING::TOOL_CURSOR))
78 {
79 return QCursor(QPixmap(":icons/general/cursor-pen.svg"), 5, 14);
80 }
81 return QCursor(QPixmap(":icons/general/cross.png"), 10, 10);
82}
83
84void PenTool::pointerPressEvent(PointerEvent *event)
85{
86 mInterpolator.pointerPressEvent(event);
87 if (handleQuickSizing(event)) {
88 return;
89 }
90
91 mMouseDownPoint = getCurrentPoint();
92 mLastBrushPoint = getCurrentPoint();
93
94 startStroke(event->inputType());
95
96 StrokeTool::pointerPressEvent(event);
97}
98
99void PenTool::pointerMoveEvent(PointerEvent* event)
100{
101 mInterpolator.pointerMoveEvent(event);
102 if (handleQuickSizing(event)) {
103 return;
104 }
105
106 if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType)
107 {
108 mCurrentPressure = mInterpolator.getPressure();
109 drawStroke();
110 if (mSettings.stabilizerLevel() != mInterpolator.getStabilizerLevel())
111 {
112 mInterpolator.setStabilizerLevel(mSettings.stabilizerLevel());
113 }
114 }
115
116 StrokeTool::pointerMoveEvent(event);
117}
118
119void PenTool::pointerReleaseEvent(PointerEvent *event)
120{
121 mInterpolator.pointerReleaseEvent(event);
122 if (handleQuickSizing(event)) {
123 return;
124 }
125
126 if (event->inputType() != mCurrentInputType) return;
127
128 mEditor->backup(typeName());
129
130 Layer* layer = mEditor->layers()->currentLayer();
131
132 qreal distance = QLineF(getCurrentPoint(), mMouseDownPoint).length();
133 if (distance < 1)
134 {
135 paintAt(mMouseDownPoint);
136 }
137 else
138 {
139 drawStroke();
140 }
141
142 if (layer->type() == Layer::VECTOR) {
143 paintVectorStroke(layer);
144 }
145 endStroke();
146
147 StrokeTool::pointerReleaseEvent(event);
148}
149
150// draw a single paint dab at the given location
151void PenTool::paintAt(QPointF point)
152{
153 Layer* layer = mEditor->layers()->currentLayer();
154 if (layer->type() == Layer::BITMAP)
155 {
156 qreal pressure = (mSettings.pressureEnabled()) ? mCurrentPressure : 1.0;
157 qreal brushWidth = mSettings.width() * pressure;
158 mCurrentWidth = brushWidth;
159
160 mScribbleArea->drawPen(point,
161 brushWidth,
162 mEditor->color()->frontColor(),
163 mSettings.AntiAliasingEnabled());
164 }
165}
166
167void PenTool::drawStroke()
168{
169 StrokeTool::drawStroke();
170 QList<QPointF> p = mInterpolator.interpolateStroke();
171
172 Layer* layer = mEditor->layers()->currentLayer();
173
174 if (layer->type() == Layer::BITMAP)
175 {
176 qreal pressure = (mSettings.pressureEnabled()) ? mCurrentPressure : 1.0;
177 qreal brushWidth = mSettings.width() * pressure;
178 mCurrentWidth = brushWidth;
179
180 // TODO: Make popup widget for less important properties,
181 // Eg. stepsize should be a slider.. will have fixed (0.3) value for now.
182 qreal brushStep = (0.5 * brushWidth);
183 brushStep = qMax(1.0, brushStep);
184
185 QPointF a = mLastBrushPoint;
186 QPointF b = getCurrentPoint();
187
188 qreal distance = 4 * QLineF(b, a).length();
189 int steps = qRound(distance / brushStep);
190
191 for (int i = 0; i < steps; i++)
192 {
193 QPointF point = mLastBrushPoint + (i + 1) * brushStep * (getCurrentPoint() - mLastBrushPoint) / distance;
194 mScribbleArea->drawPen(point,
195 brushWidth,
196 mEditor->color()->frontColor(),
197 mSettings.AntiAliasingEnabled());
198
199 if (i == (steps - 1))
200 {
201 mLastBrushPoint = getCurrentPoint();
202 }
203 }
204 }
205 else if (layer->type() == Layer::VECTOR)
206 {
207 qreal pressure = (mSettings.pressureEnabled()) ? mCurrentPressure : 1.0;
208 qreal brushWidth = mSettings.width() * pressure;
209
210 QPen pen(mEditor->color()->frontColor(),
211 brushWidth,
212 Qt::SolidLine,
213 Qt::RoundCap,
214 Qt::RoundJoin);
215
216 if (p.size() == 4)
217 {
218 QPainterPath path(p[0]);
219 path.cubicTo(p[1], p[2], p[3]);
220 mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source);
221 }
222 }
223}
224
225void PenTool::paintVectorStroke(Layer* layer)
226{
227 if (mStrokePoints.empty())
228 return;
229
230 // Clear the temporary pixel path
231 mScribbleArea->clearDrawingBuffer();
232 qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling();
233
234 BezierCurve curve(mStrokePoints, mStrokePressures, tol);
235 curve.setWidth(mSettings.width());
236 curve.setFeather(mSettings.feather());
237 curve.setFilled(false);
238 curve.setInvisibility(mSettings.invisibilityEnabled());
239 curve.setVariableWidth(mSettings.pressureEnabled());
240 curve.setColorNumber(mEditor->color()->frontColorNumber());
241
242 auto pLayerVector = static_cast<LayerVector*>(layer);
243 VectorImage* vectorImage = pLayerVector->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
244 if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing
245 vectorImage->addCurve(curve, mEditor->view()->scaling(), false);
246
247 if (vectorImage->isAnyCurveSelected() || mEditor->select()->somethingSelected())
248 {
249 mEditor->deselectAll();
250 }
251
252 vectorImage->setSelected(vectorImage->getLastCurveNumber(), true);
253
254 mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
255}
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:61
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
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
QSettings::remove
void remove(const QString &key)
QSettings::value
QVariant value(const QString &key, const QVariant &defaultValue) const const
Qt::NoBrush
NoBrush
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 Wed Dec 24 2025 23:46:02 for Pencil2D by doxygen 1.9.6 based on revision 1be63043db1f7fc0545528fc3b6d5098a552755b