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());
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
174
175 if (mPoints.size() > 0) {
176 if (mPoints.last() != getCurrentPoint()) {
177 // include the current point before ending the line.
178 mPoints << getCurrentPoint();
179 }
180 SAVESTATE_ID saveStateId = mEditor->undoRedo()->createState(UndoRedoRecordType::KEYFRAME_MODIFY);
181 mEditor->backup(typeName());
182 endPolyline(mPoints);
183 mEditor->undoRedo()->record(saveStateId, typeName());
184 }
185}
186
187void PolylineTool::removeLastPolylineSegment()
188{
189 if (mPoints.size() > 1)
190 {
191 mPoints.removeLast();
192 drawPolyline(mPoints, getCurrentPoint());
193 }
194 else if (mPoints.size() == 1)
195 {
196 cancelPolyline();
197 clearToolData();
198 }
199}
200
201bool PolylineTool::keyPressEvent(QKeyEvent* event)
202{
203 switch (event->key())
204 {
205 case Qt::Key_Control:
206 mClosedPathOverrideEnabled = true;
207 drawPolyline(mPoints, getCurrentPoint());
208 return true;
209 break;
210
211 case Qt::Key_Return:
212 if (mPoints.size() > 0)
213 {
214 // include the current point before ending the line.
215 mPoints << getCurrentPoint();
216 SAVESTATE_ID saveStateId = mEditor->undoRedo()->createState(UndoRedoRecordType::KEYFRAME_MODIFY);
217 endPolyline(mPoints);
218 mEditor->undoRedo()->record(saveStateId, typeName());
219 return true;
220 }
221 break;
222 case Qt::Key_Backspace:
223 if (mPoints.size() > 0)
224 {
225 removeLastPolylineSegment();
226 return true;
227 }
228 case Qt::Key_Escape:
229 if (mPoints.size() > 0)
230 {
231 cancelPolyline();
232 return true;
233 }
234 break;
235
236 default:
237 break;
238 }
239
240 return BaseTool::keyPressEvent(event);
241}
242
243bool PolylineTool::keyReleaseEvent(QKeyEvent* event)
244{
245 switch (event->key())
246 {
247 case Qt::Key_Control:
248 mClosedPathOverrideEnabled = false;
249 drawPolyline(mPoints, getCurrentPoint());
250 return true;
251 break;
252
253 default:
254 break;
255 }
256
257 return BaseTool::keyReleaseEvent(event);
258}
259
260void PolylineTool::drawPolyline(QList<QPointF> points, QPointF endPoint)
261{
262 if (points.size() > 0)
263 {
264 QPen pen(mEditor->color()->frontColor(),
265 mSettings.width(),
266 Qt::SolidLine,
267 Qt::RoundCap,
268 Qt::RoundJoin);
269 Layer* layer = mEditor->layers()->currentLayer();
270
271 // Bitmap by default
272 QPainterPath tempPath;
273 if (mSettings.bezierPathEnabled())
274 {
275 tempPath = BezierCurve(points).getSimplePath();
276 }
277 else
278 {
279 tempPath = BezierCurve(points).getStraightPath();
280 }
281 tempPath.lineTo(endPoint);
282
283 // Ctrl key inverts closed behavior while held (XOR)
284 if ((mSettings.closedPathEnabled() == !mClosedPathOverrideEnabled) && points.size() > 1)
285 {
286 tempPath.closeSubpath();
287 }
288
289 // Vector otherwise
290 if (layer->type() == Layer::VECTOR)
291 {
292 if (mEditor->layers()->currentLayer()->type() == Layer::VECTOR)
293 {
294 if (mScribbleArea->makeInvisible() == true)
295 {
296 pen.setWidth(0);
297 pen.setStyle(Qt::DotLine);
298 }
299 else
300 {
301 pen.setWidth(mSettings.width());
302 }
303 }
304 }
305
306 mScribbleArea->drawPolyline(tempPath, pen, mSettings.AntiAliasingEnabled());
307 }
308}
309
310
311void PolylineTool::cancelPolyline()
312{
313 clearToolData();
314}
315
316void PolylineTool::endPolyline(QList<QPointF> points)
317{
318 Layer* layer = mEditor->layers()->currentLayer();
319
320 if (layer->type() == Layer::VECTOR)
321 {
322 BezierCurve curve = BezierCurve(points, mSettings.bezierPathEnabled());
323 if (mScribbleArea->makeInvisible() == true)
324 {
325 curve.setWidth(0);
326 }
327 else
328 {
329 curve.setWidth(mSettings.width());
330 }
331 curve.setColorNumber(mEditor->color()->frontColorNumber());
332 curve.setVariableWidth(false);
333 curve.setInvisibility(mScribbleArea->makeInvisible());
334
335 VectorImage* vectorImage = static_cast<LayerVector*>(layer)->getLastVectorImageAtFrame(mEditor->currentFrame());
336 if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing
337 vectorImage->addCurve(curve, mEditor->view()->scaling());
338 }
339 if (layer->type() == Layer::BITMAP)
340 {
341 drawPolyline(points, points.last());
342 }
343
344 mScribbleArea->endStroke();
345 mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
346
347 clearToolData();
348}
349
350void PolylineTool::setUseBezier(bool useBezier)
351{
352 toolProperties().setBaseValue(PolylineToolProperties::BEZIERPATH_ENABLED, useBezier);
353 emit bezierPathEnabledChanged(useBezier);
354}
355
356void PolylineTool::setClosePath(bool closePath)
357{
358 toolProperties().setBaseValue(PolylineToolProperties::CLOSEDPATH_ENABLED, closePath);
359 emit closePathChanged(closePath);
360}
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:824
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(SAVESTATE_ID SaveStateId, const QString &description)
Records the given save state.
Definition: undoredomanager.cpp:96
UndoRedoManager::createState
SAVESTATE_ID createState(UndoRedoRecordType recordType)
Prepares and returns an save state with common data.
Definition: undoredomanager.cpp:261
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
Generated on Mon May 4 2026 07:50:47 for Pencil2D by doxygen 1.9.6 based on revision 3ed50cdd696e72315cedf30508d3572536c3876e