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 }