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
polylinetool.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
18#include "polylinetool.h"
19
20#include <QSettings>
21#include "editor.h"
22#include "scribblearea.h"
23
24#include "layermanager.h"
25#include "colormanager.h"
26#include "viewmanager.h"
27#include "undoredomanager.h"
28#include "pointerevent.h"
29#include "layervector.h"
30#include "layerbitmap.h"
31#include "vectorimage.h"
32
33
34PolylineTool::PolylineTool(QObject* parent) : StrokeTool(parent)
35{
36}
37
38ToolType PolylineTool::type() const
39{
40 return POLYLINE;
41}
42
43void PolylineTool::loadSettings()
44{
45 StrokeTool::loadSettings();
46
47 mPropertyUsed[StrokeToolProperties::WIDTH_VALUE] = { Layer::BITMAP, Layer::VECTOR };
48 mPropertyUsed[PolylineToolProperties::CLOSEDPATH_ENABLED] = { Layer::BITMAP, Layer::VECTOR };
49 mPropertyUsed[PolylineToolProperties::BEZIERPATH_ENABLED] = { Layer::BITMAP };
50 mPropertyUsed[StrokeToolProperties::ANTI_ALIASING_ENABLED] = { Layer::BITMAP };
51
52 QSettings pencilSettings(PENCIL2D, PENCIL2D);
53
54 QHash<int, PropertyInfo> info;
55
56 info[StrokeToolProperties::WIDTH_VALUE] = { WIDTH_MIN, WIDTH_MAX, 8.0 };
57 info[PolylineToolProperties::CLOSEDPATH_ENABLED] = false;
58 info[PolylineToolProperties::BEZIERPATH_ENABLED] = false;
59 info[StrokeToolProperties::ANTI_ALIASING_ENABLED] = true;
60
61 toolProperties().insertProperties(info);
62 toolProperties().loadFrom(typeName(), pencilSettings);
63
64 if (toolProperties().requireMigration(pencilSettings, ToolProperties::VERSION_1)) {
65 toolProperties().setBaseValue(StrokeToolProperties::WIDTH_VALUE, pencilSettings.value("polylineWidth", 8.0).toReal());
66 toolProperties().setBaseValue(StrokeToolProperties::ANTI_ALIASING_ENABLED, pencilSettings.value("brushAA", true).toBool());
67 toolProperties().setBaseValue(PolylineToolProperties::CLOSEDPATH_ENABLED, pencilSettings.value("closedPolylinePath", false).toBool());
68
69 pencilSettings.remove("polylineWidth");
70 pencilSettings.remove("brushAA");
71 pencilSettings.remove("closedPolylinePath");
72 }
73
74 mQuickSizingProperties.insert(Qt::ShiftModifier, StrokeToolProperties::WIDTH_VALUE);
75}
76
77bool PolylineTool::leavingThisTool()
78{
79 StrokeTool::leavingThisTool();
80 if (mPoints.size() > 0)
81 {
82 cancelPolyline();
83 }
84 return true;
85}
86
87bool PolylineTool::isActive() const
88{
89 return !mPoints.isEmpty();
90}
91
92QCursor PolylineTool::cursor()
93{
94 return QCursor(QPixmap(":icons/general/cross.png"), 10, 10);
95}
96
97void PolylineTool::clearToolData()
98{
99 if (mPoints.empty()) {
100 return;
101 }
102
103 mPoints.clear();
104 emit isActiveChanged(POLYLINE, false);
105
106 // Clear the in-progress polyline from the bitmap buffer.
107 mScribbleArea->clearDrawingBuffer();
108 mScribbleArea->updateFrame();
109}
110
111void PolylineTool::pointerPressEvent(PointerEvent* event)
112{
113 mInterpolator.pointerPressEvent(event);
114 if (handleQuickSizing(event)) {
115 return;
116 }
117
118 Layer* layer = mEditor->layers()->currentLayer();
119
120 if (event->button() == Qt::LeftButton)
121 {
122 if (layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR)
123 {
124 mScribbleArea->handleDrawingOnEmptyFrame();
125
126 if (layer->type() == Layer::VECTOR)
127 {
128 VectorImage* vectorImage = static_cast<LayerVector*>(layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
129 Q_CHECK_PTR(vectorImage);
130 vectorImage->deselectAll();
131 if (mScribbleArea->makeInvisible() && !mEditor->preference()->isOn(SETTING::INVISIBLE_LINES))
132 {
133 mScribbleArea->toggleThinLines();
134 }
135 }
136 mPoints << getCurrentPoint();
137 emit isActiveChanged(POLYLINE, true);
138 }
139 }
140
141 StrokeTool::pointerPressEvent(event);
142}
143
144void PolylineTool::pointerMoveEvent(PointerEvent* event)
145{
146 mInterpolator.pointerMoveEvent(event);
147 if (handleQuickSizing(event)) {
148 return;
149 }
150
151 Layer* layer = mEditor->layers()->currentLayer();
152 if (layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR)
153 {
154 drawPolyline(mPoints, getCurrentPoint());
155 }
156
157 StrokeTool::pointerMoveEvent(event);
158}
159
160void PolylineTool::pointerReleaseEvent(PointerEvent* event)
161{
162 mInterpolator.pointerReleaseEvent(event);
163 if (handleQuickSizing(event)) {
164 return;
165 }
166
167 StrokeTool::pointerReleaseEvent(event);
168}
169
170void PolylineTool::pointerDoubleClickEvent(PointerEvent* event)
171{
172 mInterpolator.pointerPressEvent(event);
173 // include the current point before ending the line.
174 mPoints << getCurrentPoint();
175
176 const UndoSaveState* saveState = mEditor->undoRedo()->state(UndoRedoRecordType::KEYFRAME_MODIFY);
177 mEditor->backup(typeName());
178
179 endPolyline(mPoints);
180 mEditor->undoRedo()->record(saveState, typeName());
181}
182
183void PolylineTool::removeLastPolylineSegment()
184{
185 if (mPoints.size() > 1)
186 {
187 mPoints.removeLast();
188 drawPolyline(mPoints, getCurrentPoint());
189 }
190 else if (mPoints.size() == 1)
191 {
192 cancelPolyline();
193 clearToolData();
194 }
195}
196
197bool PolylineTool::keyPressEvent(QKeyEvent* event)
198{
199 switch (event->key())
200 {
201 case Qt::Key_Control:
202 mClosedPathOverrideEnabled = true;
203 drawPolyline(mPoints, getCurrentPoint());
204 return true;
205 break;
206
207 case Qt::Key_Return:
208 if (mPoints.size() > 0)
209 {
210 const UndoSaveState* saveState = mEditor->undoRedo()->state(UndoRedoRecordType::KEYFRAME_MODIFY);
211 endPolyline(mPoints);
212 mEditor->undoRedo()->record(saveState, typeName());
213 return true;
214 }
215 break;
216 case Qt::Key_Backspace:
217 if (mPoints.size() > 0)
218 {
219 removeLastPolylineSegment();
220 return true;
221 }
222 case Qt::Key_Escape:
223 if (mPoints.size() > 0)
224 {
225 cancelPolyline();
226 return true;
227 }
228 break;
229
230 default:
231 break;
232 }
233
234 return BaseTool::keyPressEvent(event);
235}
236
237bool PolylineTool::keyReleaseEvent(QKeyEvent* event)
238{
239 switch (event->key())
240 {
241 case Qt::Key_Control:
242 mClosedPathOverrideEnabled = false;
243 drawPolyline(mPoints, getCurrentPoint());
244 return true;
245 break;
246
247 default:
248 break;
249 }
250
251 return BaseTool::keyReleaseEvent(event);
252}
253
254void PolylineTool::drawPolyline(QList<QPointF> points, QPointF endPoint)
255{
256 if (points.size() > 0)
257 {
258 QPen pen(mEditor->color()->frontColor(),
259 mSettings.width(),
260 Qt::SolidLine,
261 Qt::RoundCap,
262 Qt::RoundJoin);
263 Layer* layer = mEditor->layers()->currentLayer();
264
265 // Bitmap by default
266 QPainterPath tempPath;
267 if (mSettings.bezierPathEnabled())
268 {
269 tempPath = BezierCurve(points).getSimplePath();
270 }
271 else
272 {
273 tempPath = BezierCurve(points).getStraightPath();
274 }
275 tempPath.lineTo(endPoint);
276
277 // Ctrl key inverts closed behavior while held (XOR)
278 if ((mSettings.closedPathEnabled() == !mClosedPathOverrideEnabled) && points.size() > 1)
279 {
280 tempPath.closeSubpath();
281 }
282
283 // Vector otherwise
284 if (layer->type() == Layer::VECTOR)
285 {
286 if (mEditor->layers()->currentLayer()->type() == Layer::VECTOR)
287 {
288 if (mScribbleArea->makeInvisible() == true)
289 {
290 pen.setWidth(0);
291 pen.setStyle(Qt::DotLine);
292 }
293 else
294 {
295 pen.setWidth(mSettings.width());
296 }
297 }
298 }
299
300 mScribbleArea->drawPolyline(tempPath, pen, mSettings.AntiAliasingEnabled());
301 }
302}
303
304
305void PolylineTool::cancelPolyline()
306{
307 clearToolData();
308}
309
310void PolylineTool::endPolyline(QList<QPointF> points)
311{
312 Layer* layer = mEditor->layers()->currentLayer();
313
314 if (layer->type() == Layer::VECTOR)
315 {
316 BezierCurve curve = BezierCurve(points, mSettings.bezierPathEnabled());
317 if (mScribbleArea->makeInvisible() == true)
318 {
319 curve.setWidth(0);
320 }
321 else
322 {
323 curve.setWidth(mSettings.width());
324 }
325 curve.setColorNumber(mEditor->color()->frontColorNumber());
326 curve.setVariableWidth(false);
327 curve.setInvisibility(mScribbleArea->makeInvisible());
328
329 VectorImage* vectorImage = static_cast<LayerVector*>(layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
330 if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing
331 vectorImage->addCurve(curve, mEditor->view()->scaling());
332 }
333 if (layer->type() == Layer::BITMAP)
334 {
335 drawPolyline(points, points.last());
336 }
337
338 mScribbleArea->endStroke();
339 mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
340
341 clearToolData();
342}
343
344void PolylineTool::setUseBezier(bool useBezier)
345{
346 toolProperties().setBaseValue(PolylineToolProperties::BEZIERPATH_ENABLED, useBezier);
347 emit bezierPathEnabledChanged(useBezier);
348}
349
350void PolylineTool::setClosePath(bool closePath)
351{
352 toolProperties().setBaseValue(PolylineToolProperties::CLOSEDPATH_ENABLED, closePath);
353 emit closePathChanged(closePath);
354}
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
PolylineTool::isActive
bool isActive() const override
Check if the tool is active.
Definition: polylinetool.cpp:87
PolylineTool::leavingThisTool
bool leavingThisTool() override
Will clean up active connections.
Definition: polylinetool.cpp:77
ScribbleArea::updateFrame
void updateFrame()
Update frame.
Definition: scribblearea.cpp:200
ScribbleArea::handleDrawingOnEmptyFrame
void handleDrawingOnEmptyFrame()
Call this when starting to use a paint tool.
Definition: scribblearea.cpp:812
StrokeTool
Definition: stroketool.h:35
StrokeTool::leavingThisTool
bool leavingThisTool() override
Will clean up active connections.
Definition: stroketool.cpp:107
StrokeTool::loadSettings
void loadSettings() override
Definition: stroketool.cpp:61
UndoRedoManager::record
void record(const UndoSaveState *&undoState, const QString &description)
Records the given save state.
Definition: undoredomanager.cpp:95
UndoRedoManager::state
const UndoSaveState * state(UndoRedoRecordType recordType) const
Prepares and returns a save state with the given scope.
Definition: undoredomanager.cpp:199
VectorImage
Definition: vectorimage.h:32
VectorImage::deselectAll
void deselectAll()
VectorImage::deselectAll.
Definition: vectorimage.cpp:839
VectorImage::addCurve
void addCurve(BezierCurve &newCurve, qreal factor, bool interacts=true)
VectorImage::addCurve.
Definition: vectorimage.cpp:341
QCursor
QHash
QHash::insert
QHash::iterator insert(const Key &key, const T &value)
QKeyEvent
QList
QList::clear
void clear()
QList::empty
bool empty() const const
QList::isEmpty
bool isEmpty() const const
QList::last
T & last()
QList::removeLast
void removeLast()
QList::size
int size() const const
QObject
QObject::event
virtual bool event(QEvent *e)
QPainterPath
QPainterPath::closeSubpath
void closeSubpath()
QPainterPath::lineTo
void lineTo(const QPointF &endPoint)
QPen
QPixmap
QPointF
QSettings
QSettings::remove
void remove(const QString &key)
QSettings::value
QVariant value(const QString &key, const QVariant &defaultValue) const const
Qt::Key_Control
Key_Control
Qt::ShiftModifier
ShiftModifier
Qt::LeftButton
LeftButton
Qt::RoundCap
RoundCap
Qt::RoundJoin
RoundJoin
Qt::SolidLine
SolidLine
QVariant::toBool
bool toBool() const const
QVariant::toReal
qreal toReal(bool *ok) const const
UndoSaveState
This is the main undo/redo state structure which is meant to populate whatever states that needs to b...
Definition: undoredomanager.h:80
Generated on Wed Dec 24 2025 23:46:02 for Pencil2D by doxygen 1.9.6 based on revision 1be63043db1f7fc0545528fc3b6d5098a552755b