Krita Source Code Documentation
Loading...
Searching...
No Matches
KoShapeMeshGradientHandles.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2020 Sharaf Zaman <sharafzaz121@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <QVector>
10
11#include <KoShapeFillWrapper.h>
12#include <kis_algebra_2d.h>
13
15 KoShape *shape)
16 : m_fillVariant(fillVariant)
17 , m_shape(shape)
18{
19}
20
22{
23 QVector<Handle> result;
24
25 const SvgMeshGradient *g = gradient();
26 if (!g) return result;
27
28 SvgMeshArray const *mesharray = g->getMeshArray().data();
29
30 for (int irow = 0; irow < mesharray->numRows(); ++irow) {
31 for (int icol = 0; icol < mesharray->numColumns(); ++icol) {
32 // add corners as well
33 result << getHandles(mesharray, SvgMeshPatch::Top, irow, icol);
34
35 result << getBezierHandles(mesharray, SvgMeshPatch::Left, irow, icol);
36
37 if (irow == mesharray->numRows() - 1) {
38 result << getHandles(mesharray, SvgMeshPatch::Left, irow, icol);
39
40 if (icol == mesharray->numColumns() - 1) {
41 result << getHandles(mesharray, SvgMeshPatch::Bottom, irow, icol);
42 } else {
43 result << getBezierHandles(mesharray, SvgMeshPatch::Bottom, irow, icol);
44 }
45 }
46
47 if (icol == mesharray->numColumns() - 1) {
48 result << getHandles(mesharray, SvgMeshPatch::Right, irow, icol);
49 }
50 }
51 }
52
53 // we get pointer events in points (pts, not logical), so we transform these now
54 // and then invert them while drawing handles (see SelectionDecorator).
55 QTransform t = absoluteTransformation(g->gradientUnits());
56 for (auto &handle: result) {
57 handle.pos = t.map(handle.pos);
58 }
59
60 return result;
61}
62
64{
65 const SvgMeshGradient *g = gradient();
66 if (!g) return Handle();
67
68 Handle handle = getHandles(g->getMeshArray().data(), position.segmentType, position.row, position.col)[0];
69
70 QTransform t = absoluteTransformation(g->gradientUnits());
71 handle.pos = t.map(handle.pos);
72
73 return handle;
74}
75
77 const QPointF &newPos)
78{
80 QScopedPointer<SvgMeshGradient> newGradient(new SvgMeshGradient(*wrapper.meshgradient()));
81 SvgMeshArray *mesharray = newGradient->getMeshArray().data();
82 SvgMeshPatch *patch = newGradient->getMeshArray()->getPatch(handle.row, handle.col);
83 std::array<QPointF, 4> path = patch->getSegment(handle.segmentType);
84
85 QTransform t = absoluteTransformation(newGradient->gradientUnits()).inverted();
86
87 if (handle.type == Handle::BezierHandle) {
88 path[handle.index] = t.map(newPos);
89 mesharray->modifyHandle(SvgMeshPosition {handle.row, handle.col, handle.segmentType}, path);
90
91 } else if (handle.type == Handle::Corner) {
92 mesharray->modifyCorner(SvgMeshPosition {handle.row, handle.col, handle.segmentType}, t.map(newPos));
93 }
94
95 return wrapper.setMeshGradient(newGradient.data(), QTransform());
96}
97
99{
100 QPainterPath painterPath;
101
102 if (!gradient())
103 return painterPath;
104
105 QScopedPointer<SvgMeshGradient> g(new SvgMeshGradient(*gradient()));
106 if (g->gradientUnits() == KoFlake::ObjectBoundingBox) {
107 const QTransform gradientToUser = KisAlgebra2D::mapToRect(m_shape->outlineRect());
108 g->setTransform(gradientToUser);
109 }
110
111 SvgMeshArray *mesharray = g->getMeshArray().data();
112
113 for (int i = 0; i < mesharray->numRows(); ++i) {
114 for (int j = 0; j < mesharray->numColumns(); ++j) {
115 painterPath.addPath(mesharray->getPatch(i, j)->getPath());
116 }
117 }
118
119 return painterPath;
120}
121
123{
124 KIS_ASSERT(handle.type != Handle::None);
125
127
128 // TODO(sh_zam): Handle OBB and user mode in and only in SvgMeshPatch
129 const QTransform t = (gradient()->gradientUnits() == KoFlake::ObjectBoundingBox)
131 : QTransform();
132 const SvgMeshArray *mesharray = gradient()->getMeshArray().data();
133 QPainterPath painterPath;
134
135 if (handle.type == Handle::BezierHandle) {
136 SvgMeshPath path = mesharray->getPath(handle.getPosition());
137 std::transform(path.begin(), path.end(), path.begin(), [&t](QPointF &point) { return t.map(point); });
138 painterPath.moveTo(path[0]);
139 painterPath.cubicTo(path[1], path[2], path[3]);
140 result << painterPath;
141 } else {
142 QVector<SvgMeshPosition> positions = mesharray->getConnectedPaths(handle.getPosition());
143 for (const auto &position: positions) {
144 SvgMeshPath path = mesharray->getPath(position);
145 std::transform(path.begin(), path.end(), path.begin(), [&t](QPointF &point) { return t.map(point); });
146 painterPath = QPainterPath();
147 painterPath.moveTo(path[0]);
148 painterPath.cubicTo(path[1], path[2], path[3]);
149 result << painterPath;
150 }
151 }
152
153 return result;
154}
155
157{
158 KIS_ASSERT(bezierHandle.type == Handle::BezierHandle);
159
160 const SvgMeshArray *mesharray = gradient()->getMeshArray().data();
161 const SvgMeshPath path = mesharray->getPath(bezierHandle.getPosition());
162 const QTransform t = (gradient()->gradientUnits() == KoFlake::ObjectBoundingBox)
164 : QTransform();
165 if (bezierHandle.index == Handle::First) {
166 return t.map(path[bezierHandle.index - 1]);
167 } else {
168 return t.map(path[bezierHandle.index + 1]);
169 }
170}
171
177
180 int row,
181 int col) const
182{
183 QVector<Handle> buffer;
184 std::array<QPointF, 4> path = mesharray->getPath(type, row, col);
185 buffer << Handle(Handle::Corner, path[0], row, col, type);
186 buffer << Handle(Handle::BezierHandle, path[1], row, col, type, Handle::First);
187 buffer << Handle(Handle::BezierHandle, path[2], row, col, type, Handle::Second);
188
189 return buffer;
190}
191
194 int row,
195 int col) const
196{
197 QVector<Handle> buffer;
198 std::array<QPointF, 4> path = mesharray->getPath(type, row, col);
199 buffer << Handle(Handle::BezierHandle, path[1], row, col, type, Handle::First);
200 buffer << Handle(Handle::BezierHandle, path[2], row, col, type, Handle::Second);
201
202 return buffer;
203}
204
206{
207 QTransform t;
208 if (system == KoFlake::UserSpaceOnUse) {
210 } else {
211 const QTransform gradientToUser = KisAlgebra2D::mapToRect(m_shape->outlineRect());
212 t = gradientToUser * m_shape->absoluteTransformation();
213 }
214 return t;
215}
std::array< QPointF, 4 > SvgMeshPath
const SvgMeshGradient * meshgradient() const
KUndo2Command * setMeshGradient(const SvgMeshGradient *gradient, const QTransform &transform)
QVector< Handle > getHandles(const SvgMeshArray *mesharray, SvgMeshPatch::Type type, int row, int col) const
get handles including the corner
QVector< QPainterPath > getConnectedPath(const Handle &handle) const
Handle getHandle(SvgMeshPosition position) const
convenience method to get a handle by its position in the mesharray
const SvgMeshGradient * gradient() const
KUndo2Command * moveGradientHandle(const Handle &handle, const QPointF &newPos)
KoShapeMeshGradientHandles(KoFlake::FillVariant fillVariant, KoShape *shape)
QTransform absoluteTransformation(KoFlake::CoordinateSystem system) const
QVector< Handle > getBezierHandles(const SvgMeshArray *mesharray, SvgMeshPatch::Type type, int row, int col) const
QPointF getAttachedCorner(const Handle &bezierHandle) const
get the attached corner node of the bezierHandle
QVector< Handle > handles() const
get all nodes in the mesh, don't use this for drawing the path but use path()
virtual QRectF outlineRect() const
Definition KoShape.cpp:637
QTransform absoluteTransformation() const
Definition KoShape.cpp:382
int numRows() const
QVector< SvgMeshPosition > getConnectedPaths(const SvgMeshPosition &position) const
Return the paths connected to the corner. Can be thought of as edges connected to a vertex.
void modifyCorner(const SvgMeshPosition &position, const QPointF &newPos)
void modifyHandle(const SvgMeshPosition &position, const std::array< QPointF, 4 > &newPath)
std::array< QPointF, 4 > getPath(const SvgMeshPatch::Type edge, const int row, const int col) const
Get the Path Points for a segment of the meshpatch.
int numColumns() const
SvgMeshPatch * getPatch(const int row, const int col) const
const QScopedPointer< SvgMeshArray > & getMeshArray() const
KoFlake::CoordinateSystem gradientUnits() const
QPainterPath getPath() const
Get full (closed) meshpath.
Type
Position of stop in the patch.
std::array< QPointF, 4 > getSegment(Type type) const
Get a segment of the path in the meshpatch.
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
QTransform mapToRect(const QRectF &rect)
FillVariant
Definition KoFlake.h:28
SvgMeshPatch::Type segmentType