File indexing completed on 2024-06-23 04:28:11
0001 /* 0002 * SPDX-FileCopyrightText: 2020 Sharaf Zaman <sharafzaz121@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "KoShapeMeshGradientHandles.h" 0008 0009 #include <QVector> 0010 0011 #include <KoShapeFillWrapper.h> 0012 #include <kis_algebra_2d.h> 0013 0014 KoShapeMeshGradientHandles::KoShapeMeshGradientHandles(KoFlake::FillVariant fillVariant, 0015 KoShape *shape) 0016 : m_fillVariant(fillVariant) 0017 , m_shape(shape) 0018 { 0019 } 0020 0021 QVector<KoShapeMeshGradientHandles::Handle> KoShapeMeshGradientHandles::handles() const 0022 { 0023 QVector<Handle> result; 0024 0025 const SvgMeshGradient *g = gradient(); 0026 if (!g) return result; 0027 0028 SvgMeshArray const *mesharray = g->getMeshArray().data(); 0029 0030 for (int irow = 0; irow < mesharray->numRows(); ++irow) { 0031 for (int icol = 0; icol < mesharray->numColumns(); ++icol) { 0032 // add corners as well 0033 result << getHandles(mesharray, SvgMeshPatch::Top, irow, icol); 0034 0035 result << getBezierHandles(mesharray, SvgMeshPatch::Left, irow, icol); 0036 0037 if (irow == mesharray->numRows() - 1) { 0038 result << getHandles(mesharray, SvgMeshPatch::Left, irow, icol); 0039 0040 if (icol == mesharray->numColumns() - 1) { 0041 result << getHandles(mesharray, SvgMeshPatch::Bottom, irow, icol); 0042 } else { 0043 result << getBezierHandles(mesharray, SvgMeshPatch::Bottom, irow, icol); 0044 } 0045 } 0046 0047 if (icol == mesharray->numColumns() - 1) { 0048 result << getHandles(mesharray, SvgMeshPatch::Right, irow, icol); 0049 } 0050 } 0051 } 0052 0053 // we get pointer events in points (pts, not logical), so we transform these now 0054 // and then invert them while drawing handles (see SelectionDecorator). 0055 QTransform t = absoluteTransformation(g->gradientUnits()); 0056 for (auto &handle: result) { 0057 handle.pos = t.map(handle.pos); 0058 } 0059 0060 return result; 0061 } 0062 0063 KoShapeMeshGradientHandles::Handle KoShapeMeshGradientHandles::getHandle(SvgMeshPosition position) const 0064 { 0065 const SvgMeshGradient *g = gradient(); 0066 if (!g) return Handle(); 0067 0068 Handle handle = getHandles(g->getMeshArray().data(), position.segmentType, position.row, position.col)[0]; 0069 0070 QTransform t = absoluteTransformation(g->gradientUnits()); 0071 handle.pos = t.map(handle.pos); 0072 0073 return handle; 0074 } 0075 0076 KUndo2Command* KoShapeMeshGradientHandles::moveGradientHandle(const Handle &handle, 0077 const QPointF &newPos) 0078 { 0079 KoShapeFillWrapper wrapper(m_shape, m_fillVariant); 0080 QScopedPointer<SvgMeshGradient> newGradient(new SvgMeshGradient(*wrapper.meshgradient())); 0081 SvgMeshArray *mesharray = newGradient->getMeshArray().data(); 0082 SvgMeshPatch *patch = newGradient->getMeshArray()->getPatch(handle.row, handle.col); 0083 std::array<QPointF, 4> path = patch->getSegment(handle.segmentType); 0084 0085 QTransform t = absoluteTransformation(newGradient->gradientUnits()).inverted(); 0086 0087 if (handle.type == Handle::BezierHandle) { 0088 path[handle.index] = t.map(newPos); 0089 mesharray->modifyHandle(SvgMeshPosition {handle.row, handle.col, handle.segmentType}, path); 0090 0091 } else if (handle.type == Handle::Corner) { 0092 mesharray->modifyCorner(SvgMeshPosition {handle.row, handle.col, handle.segmentType}, t.map(newPos)); 0093 } 0094 0095 return wrapper.setMeshGradient(newGradient.data(), QTransform()); 0096 } 0097 0098 QPainterPath KoShapeMeshGradientHandles::path() const 0099 { 0100 QPainterPath painterPath; 0101 0102 if (!gradient()) 0103 return painterPath; 0104 0105 QScopedPointer<SvgMeshGradient> g(new SvgMeshGradient(*gradient())); 0106 if (g->gradientUnits() == KoFlake::ObjectBoundingBox) { 0107 const QTransform gradientToUser = KisAlgebra2D::mapToRect(m_shape->outlineRect()); 0108 g->setTransform(gradientToUser); 0109 } 0110 0111 SvgMeshArray *mesharray = g->getMeshArray().data(); 0112 0113 for (int i = 0; i < mesharray->numRows(); ++i) { 0114 for (int j = 0; j < mesharray->numColumns(); ++j) { 0115 painterPath.addPath(mesharray->getPatch(i, j)->getPath()); 0116 } 0117 } 0118 0119 return painterPath; 0120 } 0121 0122 QVector<QPainterPath> KoShapeMeshGradientHandles::getConnectedPath(const Handle &handle) const 0123 { 0124 KIS_ASSERT(handle.type != Handle::None); 0125 0126 QVector<QPainterPath> result; 0127 0128 // TODO(sh_zam): Handle OBB and user mode in and only in SvgMeshPatch 0129 const QTransform t = (gradient()->gradientUnits() == KoFlake::ObjectBoundingBox) 0130 ? KisAlgebra2D::mapToRect(m_shape->outlineRect()) 0131 : QTransform(); 0132 const SvgMeshArray *mesharray = gradient()->getMeshArray().data(); 0133 QPainterPath painterPath; 0134 0135 if (handle.type == Handle::BezierHandle) { 0136 SvgMeshPath path = mesharray->getPath(handle.getPosition()); 0137 std::transform(path.begin(), path.end(), path.begin(), [&t](QPointF &point) { return t.map(point); }); 0138 painterPath.moveTo(path[0]); 0139 painterPath.cubicTo(path[1], path[2], path[3]); 0140 result << painterPath; 0141 } else { 0142 QVector<SvgMeshPosition> positions = mesharray->getConnectedPaths(handle.getPosition()); 0143 for (const auto &position: positions) { 0144 SvgMeshPath path = mesharray->getPath(position); 0145 std::transform(path.begin(), path.end(), path.begin(), [&t](QPointF &point) { return t.map(point); }); 0146 painterPath = QPainterPath(); 0147 painterPath.moveTo(path[0]); 0148 painterPath.cubicTo(path[1], path[2], path[3]); 0149 result << painterPath; 0150 } 0151 } 0152 0153 return result; 0154 } 0155 0156 QPointF KoShapeMeshGradientHandles::getAttachedCorner(const Handle &bezierHandle) const 0157 { 0158 KIS_ASSERT(bezierHandle.type == Handle::BezierHandle); 0159 0160 const SvgMeshArray *mesharray = gradient()->getMeshArray().data(); 0161 const SvgMeshPath path = mesharray->getPath(bezierHandle.getPosition()); 0162 const QTransform t = (gradient()->gradientUnits() == KoFlake::ObjectBoundingBox) 0163 ? KisAlgebra2D::mapToRect(m_shape->outlineRect()) 0164 : QTransform(); 0165 if (bezierHandle.index == Handle::First) { 0166 return t.map(path[bezierHandle.index - 1]); 0167 } else { 0168 return t.map(path[bezierHandle.index + 1]); 0169 } 0170 } 0171 0172 const SvgMeshGradient* KoShapeMeshGradientHandles::gradient() const 0173 { 0174 KoShapeFillWrapper wrapper(m_shape, m_fillVariant); 0175 return wrapper.meshgradient(); 0176 } 0177 0178 QVector<KoShapeMeshGradientHandles::Handle> KoShapeMeshGradientHandles::getHandles(const SvgMeshArray *mesharray, 0179 SvgMeshPatch::Type type, 0180 int row, 0181 int col) const 0182 { 0183 QVector<Handle> buffer; 0184 std::array<QPointF, 4> path = mesharray->getPath(type, row, col); 0185 buffer << Handle(Handle::Corner, path[0], row, col, type); 0186 buffer << Handle(Handle::BezierHandle, path[1], row, col, type, Handle::First); 0187 buffer << Handle(Handle::BezierHandle, path[2], row, col, type, Handle::Second); 0188 0189 return buffer; 0190 } 0191 0192 QVector<KoShapeMeshGradientHandles::Handle> KoShapeMeshGradientHandles::getBezierHandles(const SvgMeshArray *mesharray, 0193 SvgMeshPatch::Type type, 0194 int row, 0195 int col) const 0196 { 0197 QVector<Handle> buffer; 0198 std::array<QPointF, 4> path = mesharray->getPath(type, row, col); 0199 buffer << Handle(Handle::BezierHandle, path[1], row, col, type, Handle::First); 0200 buffer << Handle(Handle::BezierHandle, path[2], row, col, type, Handle::Second); 0201 0202 return buffer; 0203 } 0204 0205 QTransform KoShapeMeshGradientHandles::absoluteTransformation(KoFlake::CoordinateSystem system) const 0206 { 0207 QTransform t; 0208 if (system == KoFlake::UserSpaceOnUse) { 0209 t = m_shape->absoluteTransformation(); 0210 } else { 0211 const QTransform gradientToUser = KisAlgebra2D::mapToRect(m_shape->outlineRect()); 0212 t = gradientToUser * m_shape->absoluteTransformation(); 0213 } 0214 return t; 0215 }