Krita Source Code Documentation
Loading...
Searching...
No Matches
KoPathPoint.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 SPDX-FileCopyrightText: 2006 Thorsten Zachmann <zachmann@kde.org>
3 SPDX-FileCopyrightText: 2006-2008 Jan Hambrecht <jaham@gmx.net>
4 SPDX-FileCopyrightText: 2007 Thomas Zander <zander@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "KoPathPoint.h"
10#include "KoPathShape.h"
11
12#include <FlakeDebug.h>
13#include <QPainter>
14#include <QPointF>
16
17#include <math.h>
18
19#include <qnumeric.h> // for qIsNaN
20static bool qIsNaNPoint(const QPointF &p) {
21 return qIsNaN(p.x()) || qIsNaN(p.y());
22}
23
24class Q_DECL_HIDDEN KoPathPoint::Private
25{
26public:
28 : shape(0), properties(Normal)
29 , activeControlPoint1(false), activeControlPoint2(false) {}
31 QPointF point;
34 PointProperties properties;
37};
38
40 : d(new Private())
41{
42 d->shape = 0;
43 d->point = pathPoint.d->point;
44 d->controlPoint1 = pathPoint.d->controlPoint1;
45 d->controlPoint2 = pathPoint.d->controlPoint2;
46 d->properties = pathPoint.d->properties;
47 d->activeControlPoint1 = pathPoint.d->activeControlPoint1;
48 d->activeControlPoint2 = pathPoint.d->activeControlPoint2;
49}
50
52 : KoPathPoint(pathPoint)
53{
54 d->shape = newParent;
55}
56
58 : d(new Private())
59{
60}
61
62KoPathPoint::KoPathPoint(KoPathShape * path, const QPointF &point, PointProperties properties)
63 : d(new Private())
64{
65 d->shape = path;
66 d->point = point;
67 d->controlPoint1 = point;
68 d->controlPoint2 = point;
69 d->properties = properties;
70}
71
73{
74 delete d;
75}
76
78{
79 if (this == &rhs)
80 return (*this);
81
82 d->shape = rhs.d->shape;
83 d->point = rhs.d->point;
84 d->controlPoint1 = rhs.d->controlPoint1;
85 d->controlPoint2 = rhs.d->controlPoint2;
86 d->properties = rhs.d->properties;
87 d->activeControlPoint1 = rhs.d->activeControlPoint1;
88 d->activeControlPoint2 = rhs.d->activeControlPoint2;
89
90 return (*this);
91}
92
94{
95 if (d->point != rhs.d->point)
96 return false;
97 if (d->controlPoint1 != rhs.d->controlPoint1)
98 return false;
99 if (d->controlPoint2 != rhs.d->controlPoint2)
100 return false;
101 if (d->properties != rhs.d->properties)
102 return false;
103 if (d->activeControlPoint1 != rhs.d->activeControlPoint1)
104 return false;
105 if (d->activeControlPoint2 != rhs.d->activeControlPoint2)
106 return false;
107 return true;
108}
109
110void KoPathPoint::setPoint(const QPointF &point)
111{
112 d->point = point;
113 if (d->shape)
114 d->shape->notifyChanged();
115}
116
117void KoPathPoint::setControlPoint1(const QPointF &point)
118{
119 if (qIsNaNPoint(point)) return;
120
121 d->controlPoint1 = point;
122 d->activeControlPoint1 = true;
123 if (d->shape)
124 d->shape->notifyChanged();
125}
126
127void KoPathPoint::setControlPoint2(const QPointF &point)
128{
129 if (qIsNaNPoint(point)) return;
130
131 d->controlPoint2 = point;
132 d->activeControlPoint2 = true;
133 if (d->shape)
134 d->shape->notifyChanged();
135}
136
138{
139 d->activeControlPoint1 = false;
140 d->properties &= ~IsSmooth;
141 d->properties &= ~IsSymmetric;
142 if (d->shape)
143 d->shape->notifyChanged();
144}
145
147{
148 d->activeControlPoint2 = false;
149 d->properties &= ~IsSmooth;
150 d->properties &= ~IsSymmetric;
151 if (d->shape)
152 d->shape->notifyChanged();
153}
154
155void KoPathPoint::setProperties(PointProperties properties)
156{
157 d->properties = properties;
158 // CloseSubpath only allowed with StartSubpath or StopSubpath
159 if ((d->properties & StartSubpath) == 0 && (d->properties & StopSubpath) == 0)
160 d->properties &= ~CloseSubpath;
161
163 // strip smooth and symmetric flags if point has not two control points
164 d->properties &= ~IsSmooth;
165 d->properties &= ~IsSymmetric;
166 }
167
168 if (d->shape)
169 d->shape->notifyChanged();
170}
171
173{
174 switch (property) {
175 case StartSubpath:
176 case StopSubpath:
177 case CloseSubpath:
178 // nothing special to do here
179 break;
180 case IsSmooth:
181 d->properties &= ~IsSymmetric;
182 break;
183 case IsSymmetric:
184 d->properties &= ~IsSmooth;
185 break;
186 default: return;
187 }
188
189 d->properties |= property;
190
192 // strip smooth and symmetric flags if point has not two control points
193 d->properties &= ~IsSymmetric;
194 d->properties &= ~IsSmooth;
195 }
196}
197
199{
200 switch (property) {
201 case StartSubpath:
202 if (d->properties & StartSubpath && (d->properties & StopSubpath) == 0)
203 d->properties &= ~CloseSubpath;
204 break;
205 case StopSubpath:
206 if (d->properties & StopSubpath && (d->properties & StartSubpath) == 0)
207 d->properties &= ~CloseSubpath;
208 break;
209 case CloseSubpath:
210 if (d->properties & StartSubpath || d->properties & StopSubpath) {
211 d->properties &= ~IsSmooth;
212 d->properties &= ~IsSymmetric;
213 }
214 break;
215 case IsSmooth:
216 case IsSymmetric:
217 // no others depend on these
218 break;
219 default: return;
220 }
221 d->properties &= ~property;
222}
223
225{
226 // only start point on closed subpaths can have a controlPoint1
227 if ((d->properties & StartSubpath) && (d->properties & CloseSubpath) == 0)
228 return false;
229
230 return d->activeControlPoint1;
231}
232
234{
235 // only end point on closed subpaths can have a controlPoint2
236 if ((d->properties & StopSubpath) && (d->properties & CloseSubpath) == 0)
237 return false;
238
239 return d->activeControlPoint2;
240}
241
242void KoPathPoint::map(const QTransform &matrix)
243{
244 d->point = matrix.map(d->point);
245 d->controlPoint1 = matrix.map(d->controlPoint1);
246 d->controlPoint2 = matrix.map(d->controlPoint2);
247
248 if (d->shape)
249 d->shape->notifyChanged();
250}
251
252void KoPathPoint::paint(KisHandlePainterHelper &handlesHelper, PointTypes types, bool active)
253{
254 bool drawControlPoint1 = types & ControlPoint1 && (!active || activeControlPoint1());
255 bool drawControlPoint2 = types & ControlPoint2 && (!active || activeControlPoint2());
256
257 // draw lines at the bottom
258 if (drawControlPoint2) {
259 handlesHelper.drawConnectionLine(point(), controlPoint2());
260 }
261
262 if (drawControlPoint1) {
263 handlesHelper.drawConnectionLine(point(), controlPoint1());
264 }
265
266 // the point is lowest
267 if (types & Node) {
268 if (properties() & IsSmooth) {
269 handlesHelper.drawHandleCircle(point());
270 } else if (properties() & IsSymmetric) {
271 handlesHelper.drawHandleRect(point());
272 } else {
273 handlesHelper.drawGradientHandle(point());
274 }
275 }
276
277 // then comes control point 2
278 if (drawControlPoint2) {
279 handlesHelper.drawHandleSmallCircle(controlPoint2());
280 }
281
282 // then comes control point 1
283 if (drawControlPoint1) {
284 handlesHelper.drawHandleSmallCircle(controlPoint1());
285 }
286}
287
289{
290 // don't set to zero
291 //Q_ASSERT(parent);
292 d->shape = parent;
293}
294
295QRectF KoPathPoint::boundingRect(bool active) const
296{
297 QRectF rect(d->point, QSize(1, 1));
298 if (!active && activeControlPoint1()) {
299 QRectF r1(d->point, QSize(1, 1));
300 r1.setBottomRight(d->controlPoint1);
301 rect = rect.united(r1);
302 }
303 if (!active && activeControlPoint2()) {
304 QRectF r2(d->point, QSize(1, 1));
305 r2.setBottomRight(d->controlPoint2);
306 rect = rect.united(r2);
307 }
308 if (d->shape)
309 return d->shape->shapeToDocument(rect);
310 else
311 return rect;
312}
313
315{
316 std::swap(d->controlPoint1, d->controlPoint2);
317 std::swap(d->activeControlPoint1, d->activeControlPoint2);
318 PointProperties newProps = Normal;
319 newProps |= d->properties & IsSmooth;
320 newProps |= d->properties & IsSymmetric;
321 newProps |= d->properties & StartSubpath;
322 newProps |= d->properties & StopSubpath;
323 newProps |= d->properties & CloseSubpath;
324 d->properties = newProps;
325}
326
328{
329 QPointF t1, t2;
330
331 if (activeControlPoint1()) {
332 t1 = point() - controlPoint1();
333 } else {
334 // we need the previous path point but there is none provided
335 if (! prev)
336 return false;
337 if (prev->activeControlPoint2())
338 t1 = point() - prev->controlPoint2();
339 else
340 t1 = point() - prev->point();
341 }
342
343 if (activeControlPoint2()) {
344 t2 = controlPoint2() - point();
345 } else {
346 // we need the next path point but there is none provided
347 if (! next)
348 return false;
349 if (next->activeControlPoint1())
350 t2 = next->controlPoint1() - point();
351 else
352 t2 = next->point() - point();
353 }
354
355 // normalize tangent vectors
356 qreal l1 = sqrt(t1.x() * t1.x() + t1.y() * t1.y());
357 qreal l2 = sqrt(t2.x() * t2.x() + t2.y() * t2.y());
358 if (qFuzzyCompare(l1 + 1, qreal(1.0)) || qFuzzyCompare(l2 + 1, qreal(1.0)))
359 return true;
360
361 t1 /= l1;
362 t2 /= l2;
363
364 qreal scalar = t1.x() * t2.x() + t1.y() * t2.y();
365 // tangents are parallel if t1*t2 = |t1|*|t2|
366 return qFuzzyCompare(scalar, qreal(1.0));
367}
368
369KoPathPoint::PointProperties KoPathPoint::properties() const
370{
371 return d->properties;
372}
373
374QPointF KoPathPoint::point() const
375{
376 return d->point;
377}
378
379QPointF KoPathPoint::controlPoint1() const
380{
381 return d->controlPoint1;
382}
383
384QPointF KoPathPoint::controlPoint2() const
385{
386 return d->controlPoint2;
387}
388
390{
391 return d->shape;
392}
QPointF r2
const Params2D p
QPointF r1
static bool qIsNaNPoint(const QPointF &p)
The KisHandlePainterHelper class is a special helper for painting handles around objects....
void drawHandleSmallCircle(const QPointF &center)
void drawHandleRect(const QPointF &center, qreal radius)
void drawConnectionLine(const QLineF &line)
void drawGradientHandle(const QPointF &center, qreal radius)
void drawHandleCircle(const QPointF &center, qreal radius)
A KoPathPoint represents a point in a path.
PointProperties properties
void setProperties(PointProperties properties)
Set the properties of a point.
QRectF boundingRect(bool active=true) const
Get the bounding rect of the point.
void reverse()
Reverses the path point.
void setControlPoint1(const QPointF &point)
Set the control point 1.
QPointF point
void setProperty(PointProperty property)
Sets a single property of a point.
KoPathPoint & operator=(const KoPathPoint &other)
Assignment operator.
bool operator==(const KoPathPoint &other) const
Compare operator.
void setControlPoint2(const QPointF &point)
Set the control point 2.
void removeControlPoint1()
Removes the first control point.
KoPathPoint()
Default constructor.
bool isSmooth(KoPathPoint *previous, KoPathPoint *next) const
QPointF controlPoint1
void map(const QTransform &matrix)
apply matrix on the point
KoPathShape * parent() const
Get the path shape the point belongs to.
PointProperty
property enum
Definition KoPathPoint.h:36
@ IsSmooth
it is smooth, both control points on a line through the point
Definition KoPathPoint.h:41
@ StartSubpath
it starts a new subpath by a moveTo command
Definition KoPathPoint.h:38
@ Normal
it has no control points
Definition KoPathPoint.h:37
@ IsSymmetric
it is symmetric, like smooth but control points have same distance to point
Definition KoPathPoint.h:42
@ CloseSubpath
it closes a subpath (only applicable on StartSubpath and StopSubpath)
Definition KoPathPoint.h:40
@ StopSubpath
it stops a subpath (last point of subpath)
Definition KoPathPoint.h:39
void setPoint(const QPointF &point)
alter the point
@ ControlPoint2
the second control point
Definition KoPathPoint.h:51
@ ControlPoint1
the first control point
Definition KoPathPoint.h:50
void unsetProperty(PointProperty property)
Removes a property from the point.
bool activeControlPoint1
Private *const d
~KoPathPoint()
Destructor.
void removeControlPoint2()
Removes the second control point.
KoPathShape * shape
void paint(KisHandlePainterHelper &handlesHelper, PointTypes types, bool active=true)
bool activeControlPoint2
void setParent(KoPathShape *parent)
Sets the parent path shape.
QPointF controlPoint2
The position of a path point within a path shape.
Definition KoPathShape.h:63
Definition Node.h:24
static bool qFuzzyCompare(half p1, half p2)