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()
39{
40 return POLYLINE;
41}
42
43void PolylineTool::loadSettings()
44{
45 StrokeTool::loadSettings();
46
47 mPropertyEnabled[WIDTH] = true;
48 mPropertyEnabled[BEZIER] = true;
49 mPropertyEnabled[CLOSEDPATH] = true;
50 mPropertyEnabled[ANTI_ALIASING] = true;
51
52 QSettings settings(PENCIL2D, PENCIL2D);
53
54 properties.width = settings.value("polyLineWidth", 8.0).toDouble();
55 properties.feather = -1;
56 properties.useFeather = false;
57 properties.pressure = false;
58 properties.invisibility = OFF;
59 properties.preserveAlpha = OFF;
60 properties.closedPolylinePath = settings.value("closedPolylinePath").toBool();
61 properties.useAA = settings.value("brushAA").toBool();
62 properties.stabilizerLevel = -1;
63
64 mQuickSizingProperties.insert(Qt::ShiftModifier, WIDTH);
65}
66
67void PolylineTool::saveSettings()
68{
69 QSettings settings(PENCIL2D, PENCIL2D);
70
71 settings.setValue("polyLineWidth", properties.width);
72 settings.setValue("brushAA", properties.useAA);
73 settings.setValue("closedPolylinePath", properties.closedPolylinePath);
74
75 settings.sync();
76}
77
78void PolylineTool::resetToDefault()
79{
80 setWidth(8.0);
81 setBezier(false);
82 setClosedPath(false);
83}
84
85void PolylineTool::setWidth(const qreal width)
86{
87 // Set current property
88 properties.width = width;
89}
90
91void PolylineTool::setFeather(const qreal feather)
92{
93 Q_UNUSED(feather);
94 properties.feather = -1;
95}
96
97void PolylineTool::setAA(const int AA)
98{
99 // Set current property
100 properties.useAA = AA;
101}
102
103void PolylineTool::setClosedPath(const bool closed)
104{
105 BaseTool::setClosedPath(closed);
106}
107
108bool PolylineTool::leavingThisTool()
109{
110 StrokeTool::leavingThisTool();
111 if (mPoints.size() > 0)
112 {
113 cancelPolyline();
114 }
115 return true;
116}
117
118bool PolylineTool::isActive() const
119{
120 return !mPoints.isEmpty();
121}
122
123QCursor PolylineTool::cursor()
124{
125 return QCursor(QPixmap(":icons/general/cross.png"), 10, 10);
126}
127
128void PolylineTool::clearToolData()
129{
130 if (mPoints.empty()) {
131 return;
132 }
133
134 mPoints.clear();
135 emit isActiveChanged(POLYLINE, false);
136
137 // Clear the in-progress polyline from the bitmap buffer.
138 mScribbleArea->clearDrawingBuffer();
139 mScribbleArea->updateFrame();
140}
141
142void PolylineTool::pointerPressEvent(PointerEvent* event)
143{
144 mInterpolator.pointerPressEvent(event);
145 if (handleQuickSizing(event)) {
146 return;
147 }
148
149 Layer* layer = mEditor->layers()->currentLayer();
150
151 if (event->button() == Qt::LeftButton)
152 {
153 if (layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR)
154 {
155 mScribbleArea->handleDrawingOnEmptyFrame();
156
157 if (layer->type() == Layer::VECTOR)
158 {
159 VectorImage* vectorImage = static_cast<LayerVector*>(layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
160 Q_CHECK_PTR(vectorImage);
161 vectorImage->deselectAll();
162 if (mScribbleArea->makeInvisible() && !mEditor->preference()->isOn(SETTING::INVISIBLE_LINES))
163 {
164 mScribbleArea->toggleThinLines();
165 }
166 }
167 mPoints << getCurrentPoint();
168 emit isActiveChanged(POLYLINE, true);
169 }
170 }
171
172 StrokeTool::pointerPressEvent(event);
173}
174
175void PolylineTool::pointerMoveEvent(PointerEvent* event)
176{
177 mInterpolator.pointerMoveEvent(event);
178 if (handleQuickSizing(event)) {
179 return;
180 }
181
182 Layer* layer = mEditor->layers()->currentLayer();
183 if (layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR)
184 {
185 drawPolyline(mPoints, getCurrentPoint());
186 }
187
188 StrokeTool::pointerMoveEvent(event);
189}
190
191void PolylineTool::pointerReleaseEvent(PointerEvent* event)
192{
193 mInterpolator.pointerReleaseEvent(event);
194 if (handleQuickSizing(event)) {
195 return;
196 }
197
198 StrokeTool::pointerReleaseEvent(event);
199}
200
201void PolylineTool::pointerDoubleClickEvent(PointerEvent* event)
202{
203 mInterpolator.pointerPressEvent(event);
204 // include the current point before ending the line.
205 mPoints << getCurrentPoint();
206
207 const UndoSaveState* saveState = mEditor->undoRedo()->state(UndoRedoRecordType::KEYFRAME_MODIFY);
208 mEditor->backup(typeName());
209
210 endPolyline(mPoints);
211 mEditor->undoRedo()->record(saveState, typeName());
212}
213
214void PolylineTool::removeLastPolylineSegment()
215{
216 if (mPoints.size() > 1)
217 {
218 mPoints.removeLast();
219 drawPolyline(mPoints, getCurrentPoint());
220 }
221 else if (mPoints.size() == 1)
222 {
223 cancelPolyline();
224 clearToolData();
225 }
226}
227
228bool PolylineTool::keyPressEvent(QKeyEvent* event)
229{
230 switch (event->key())
231 {
232 case Qt::Key_Control:
233 mClosedPathOverrideEnabled = true;
234 drawPolyline(mPoints, getCurrentPoint());
235 return true;
236 break;
237
238 case Qt::Key_Return:
239 if (mPoints.size() > 0)
240 {
241 const UndoSaveState* saveState = mEditor->undoRedo()->state(UndoRedoRecordType::KEYFRAME_MODIFY);
242 endPolyline(mPoints);
243 mEditor->undoRedo()->record(saveState, typeName());
244 return true;
245 }
246 break;
247 case Qt::Key_Backspace:
248 if (mPoints.size() > 0)
249 {
250 removeLastPolylineSegment();
251 return true;
252 }
253 case Qt::Key_Escape:
254 if (mPoints.size() > 0)
255 {
256 cancelPolyline();
257 return true;
258 }
259 break;
260
261 default:
262 break;
263 }
264
265 return BaseTool::keyPressEvent(event);
266}
267
268bool PolylineTool::keyReleaseEvent(QKeyEvent* event)
269{
270 switch (event->key())
271 {
272 case Qt::Key_Control:
273 mClosedPathOverrideEnabled = false;
274 drawPolyline(mPoints, getCurrentPoint());
275 return true;
276 break;
277
278 default:
279 break;
280 }
281
282 return BaseTool::keyReleaseEvent(event);
283}
284
285void PolylineTool::drawPolyline(QList<QPointF> points, QPointF endPoint)
286{
287 if (points.size() > 0)
288 {
289 QPen pen(mEditor->color()->frontColor(),
290 properties.width,
291 Qt::SolidLine,
292 Qt::RoundCap,
293 Qt::RoundJoin);
294 Layer* layer = mEditor->layers()->currentLayer();
295
296 // Bitmap by default
297 QPainterPath tempPath;
298 if (properties.bezier_state)
299 {
300 tempPath = BezierCurve(points).getSimplePath();
301 }
302 else
303 {
304 tempPath = BezierCurve(points).getStraightPath();
305 }
306 tempPath.lineTo(endPoint);
307
308 // Ctrl key inverts closed behavior while held (XOR)
309 if ((properties.closedPolylinePath == !mClosedPathOverrideEnabled) && points.size() > 1)
310 {
311 tempPath.closeSubpath();
312 }
313
314 // Vector otherwise
315 if (layer->type() == Layer::VECTOR)
316 {
317 if (mEditor->layers()->currentLayer()->type() == Layer::VECTOR)
318 {
319 if (mScribbleArea->makeInvisible() == true)
320 {
321 pen.setWidth(0);
322 pen.setStyle(Qt::DotLine);
323 }
324 else
325 {
326 pen.setWidth(properties.width);
327 }
328 }
329 }
330
331 mScribbleArea->drawPolyline(tempPath, pen, properties.useAA);
332 }
333}
334
335
336void PolylineTool::cancelPolyline()
337{
338 clearToolData();
339}
340
341void PolylineTool::endPolyline(QList<QPointF> points)
342{
343 Layer* layer = mEditor->layers()->currentLayer();
344
345 if (layer->type() == Layer::VECTOR)
346 {
347 BezierCurve curve = BezierCurve(points, properties.bezier_state);
348 if (mScribbleArea->makeInvisible() == true)
349 {
350 curve.setWidth(0);
351 }
352 else
353 {
354 curve.setWidth(properties.width);
355 }
356 curve.setColorNumber(mEditor->color()->frontColorNumber());
357 curve.setVariableWidth(false);
358 curve.setInvisibility(mScribbleArea->makeInvisible());
359
360 VectorImage* vectorImage = static_cast<LayerVector*>(layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
361 if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing
362 vectorImage->addCurve(curve, mEditor->view()->scaling());
363 }
364 if (layer->type() == Layer::BITMAP)
365 {
366 drawPolyline(points, points.last());
367 }
368
369 mScribbleArea->endStroke();
370 mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
371
372 clearToolData();
373}
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:118
PolylineTool::leavingThisTool
bool leavingThisTool() override
Will clean up active connections.
Definition: polylinetool.cpp:108
ScribbleArea::updateFrame
void updateFrame()
Update frame.
Definition: scribblearea.cpp:199
ScribbleArea::handleDrawingOnEmptyFrame
void handleDrawingOnEmptyFrame()
Call this when starting to use a paint tool.
Definition: scribblearea.cpp:827
StrokeTool
Definition: stroketool.h:35
StrokeTool::leavingThisTool
bool leavingThisTool() override
Will clean up active connections.
Definition: stroketool.cpp:89
StrokeTool::loadSettings
void loadSettings() override
Definition: stroketool.cpp:57
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::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
Qt::Key_Control
Key_Control
Qt::ShiftModifier
ShiftModifier
Qt::LeftButton
LeftButton
Qt::RoundCap
RoundCap
Qt::RoundJoin
RoundJoin
Qt::SolidLine
SolidLine
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 Fri Dec 19 2025 07:54:21 for Pencil2D by doxygen 1.9.6 based on revision 7fd8cd9e03f2d31750e199ecec202e5c0f30e532