File indexing completed on 2024-05-12 15:57:00

0001 /*
0002  *  SPDX-FileCopyrightText: 2020 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "KisBezierPatch.h"
0008 
0009 #include <QtMath>
0010 #include <kis_algebra_2d.h>
0011 #include "KisBezierUtils.h"
0012 
0013 #include "kis_debug.h"
0014 
0015 QRectF KisBezierPatch::dstBoundingRect() const {
0016     QRectF result;
0017 
0018     for (auto it = points.begin(); it != points.end(); ++it) {
0019         KisAlgebra2D::accumulateBounds(*it, &result);
0020     }
0021 
0022     return result;
0023 }
0024 
0025 QRectF KisBezierPatch::srcBoundingRect() const {
0026     return originalRect;
0027 }
0028 
0029 QPointF KisBezierPatch::localToGlobal(const QPointF &pt) const
0030 {
0031     return KisBezierUtils::calculateGlobalPos(points, pt);
0032 }
0033 
0034 QPointF KisBezierPatch::globalToLocal(const QPointF &pt) const
0035 {
0036     return KisBezierUtils::calculateLocalPos(points, pt);
0037 }
0038 
0039 void KisBezierPatch::sampleRegularGrid(QSize &gridSize, QVector<QPointF> &origPoints, QVector<QPointF> &transfPoints, const QPointF &dstStep) const
0040 {
0041     using KisAlgebra2D::lerp;
0042     using KisBezierUtils::bezierCurve;
0043 
0044     const QRectF bounds = dstBoundingRect();
0045     gridSize.rwidth() = qCeil(bounds.width() / dstStep.x());
0046     gridSize.rheight() = qCeil(bounds.height() / dstStep.y());
0047 
0048     const qreal topLength = KisBezierUtils::curveLength(points[TL], points[TL_HC], points[TR_HC], points[TR], 0.01);
0049     const qreal bottomLength = KisBezierUtils::curveLength(points[BL], points[BL_HC], points[BR_HC], points[BR], 0.01);
0050 
0051     const qreal leftLength = KisBezierUtils::curveLength(points[TL], points[TL_VC], points[BL_VC], points[BL], 0.01);
0052     const qreal rightLength = KisBezierUtils::curveLength(points[TR], points[TR_VC], points[BR_VC], points[BR], 0.01);
0053 
0054     struct Split {
0055         QPointF p0;
0056         QPointF relP1;
0057         QPointF relP2;
0058         QPointF p3;
0059         qreal coord1;
0060         qreal coord2;
0061         qreal proportion;
0062     };
0063 
0064     std::vector<Split> verticalSplits;
0065     std::vector<Split> horizontalSplits;
0066 
0067     for (int y = 0; y < gridSize.height(); y++) {
0068         const qreal yProportion = qreal(y) / (gridSize.height() - 1);
0069 
0070         const qreal yCoord1 = KisBezierUtils::curveLengthAtPoint(points[TL], points[TL_VC], points[BL_VC], points[BL], yProportion, 0.01) / leftLength;
0071         const qreal yCoord2 = KisBezierUtils::curveLengthAtPoint(points[TR], points[TR_VC], points[BR_VC], points[BR], yProportion, 0.01) / rightLength;
0072 
0073         const QPointF p0 = bezierCurve(points[TL], points[TL_VC], points[BL_VC], points[BL], yProportion);
0074 
0075         const QPointF relP1 = lerp(points[TL_HC] - points[TL], points[BL_HC] - points[BL], yProportion);
0076         const QPointF relP2 = lerp(points[TR_HC] - points[TR], points[BR_HC] - points[BR], yProportion);
0077 
0078         const QPointF p3 = bezierCurve(points[TR], points[TR_VC], points[BR_VC], points[BR], yProportion);
0079 
0080         verticalSplits.push_back({p0, relP1, relP2, p3, yCoord1, yCoord2, yProportion});
0081     }
0082 
0083     for (int x = 0; x < gridSize.width(); x++) {
0084         const qreal xProportion = qreal(x) / (gridSize.width() - 1);
0085 
0086         const qreal xCoord1 = KisBezierUtils::curveLengthAtPoint(points[TL], points[TL_HC], points[TR_HC], points[TR], xProportion, 0.01) / topLength;
0087         const qreal xCoord2 = KisBezierUtils::curveLengthAtPoint(points[BL], points[BL_HC], points[BR_HC], points[BR], xProportion, 0.01) / bottomLength;
0088 
0089         const QPointF q0 = bezierCurve(points[TL], points[TL_HC], points[TR_HC], points[TR], xProportion);
0090 
0091         const QPointF relQ1 = lerp(points[TL_VC] - points[TL], points[TR_VC] - points[TR], xProportion);
0092         const QPointF relQ2 = lerp(points[BL_VC] - points[BL], points[BR_VC] - points[BR], xProportion);
0093 
0094         const QPointF q3 = bezierCurve(points[BL], points[BL_HC], points[BR_HC], points[BR], xProportion);
0095 
0096         horizontalSplits.push_back({q0, relQ1, relQ2, q3, xCoord1, xCoord2, xProportion});
0097     }
0098 
0099     for (int y = 0; y < gridSize.height(); y++) {
0100         for (int x = 0; x < gridSize.width(); x++) {
0101             const Split &ySplit = verticalSplits[y];
0102             const Split &xSplit = horizontalSplits[x];
0103 
0104             const QPointF transf1 = bezierCurve(ySplit.p0,
0105                                                 ySplit.p0 + ySplit.relP1,
0106                                                 ySplit.p3 + ySplit.relP2,
0107                                                 ySplit.p3,
0108                                                 xSplit.proportion);
0109 
0110 
0111             const QPointF transf2 = bezierCurve(xSplit.p0,
0112                                                 xSplit.p0 + xSplit.relP1,
0113                                                 xSplit.p3 + xSplit.relP2,
0114                                                 xSplit.p3,
0115                                                 ySplit.proportion);
0116 
0117 
0118             const QPointF transf = 0.5 * (transf1 + transf2);
0119 
0120             const QPointF localPt(lerp(xSplit.coord1, xSplit.coord2, ySplit.proportion),
0121                                   lerp(ySplit.coord1, ySplit.coord2, xSplit.proportion));
0122             const QPointF orig = KisAlgebra2D::relativeToAbsolute(localPt, originalRect);
0123 
0124             origPoints.append(orig);
0125             transfPoints.append(transf);
0126         }
0127     }
0128 }
0129 
0130 void KisBezierPatch::sampleRegularGridSVG2(QSize &gridSize, QVector<QPointF> &origPoints, QVector<QPointF> &transfPoints, const QPointF &dstStep) const
0131 {
0132     using KisAlgebra2D::lerp;
0133     using KisBezierUtils::bezierCurve;
0134 
0135     const QRectF bounds = dstBoundingRect();
0136     gridSize.rwidth() = qCeil(bounds.width() / dstStep.x());
0137     gridSize.rheight() = qCeil(bounds.height() / dstStep.y());
0138 
0139     const qreal topLength = KisBezierUtils::curveLength(points[TL], points[TL_HC], points[TR_HC], points[TR], 0.01);
0140     const qreal bottomLength = KisBezierUtils::curveLength(points[BL], points[BL_HC], points[BR_HC], points[BR], 0.01);
0141 
0142     const qreal leftLength = KisBezierUtils::curveLength(points[TL], points[TL_VC], points[BL_VC], points[BL], 0.01);
0143     const qreal rightLength = KisBezierUtils::curveLength(points[TR], points[TR_VC], points[BR_VC], points[BR], 0.01);
0144 
0145 
0146     struct Split {
0147         QPointF p0;
0148         QPointF p3;
0149         qreal coord1;
0150         qreal coord2;
0151         qreal proportion;
0152     };
0153 
0154     std::vector<Split> verticalSplits;
0155     std::vector<Split> horizontalSplits;
0156 
0157     for (int y = 0; y < gridSize.height(); y++) {
0158         const qreal yProportion = qreal(y) / (gridSize.height() - 1);
0159 
0160         const qreal yCoord1 = KisBezierUtils::curveLengthAtPoint(points[TL], points[TL_VC], points[BL_VC], points[BL], yProportion, 0.01) / leftLength;
0161         const qreal yCoord2 = KisBezierUtils::curveLengthAtPoint(points[TR], points[TR_VC], points[BR_VC], points[BR], yProportion, 0.01) / rightLength;
0162 
0163         const QPointF p0 = bezierCurve(points[TL], points[TL_VC], points[BL_VC], points[BL], yProportion);
0164         const QPointF p3 = bezierCurve(points[TR], points[TR_VC], points[BR_VC], points[BR], yProportion);
0165 
0166         verticalSplits.push_back({p0, p3, yCoord1, yCoord2, yProportion});
0167     }
0168 
0169     for (int x = 0; x < gridSize.width(); x++) {
0170         const qreal xProportion = qreal(x) / (gridSize.width() - 1);
0171 
0172         const qreal xCoord1 = KisBezierUtils::curveLengthAtPoint(points[TL], points[TL_HC], points[TR_HC], points[TR], xProportion, 0.01) / topLength;
0173         const qreal xCoord2 = KisBezierUtils::curveLengthAtPoint(points[BL], points[BL_HC], points[BR_HC], points[BR], xProportion, 0.01) / bottomLength;
0174 
0175         const QPointF q0 = bezierCurve(points[TL], points[TL_HC], points[TR_HC], points[TR], xProportion);
0176         const QPointF q3 = bezierCurve(points[BL], points[BL_HC], points[BR_HC], points[BR], xProportion);
0177 
0178         horizontalSplits.push_back({q0, q3, xCoord1, xCoord2, xProportion});
0179     }
0180 
0181     for (int y = 0; y < gridSize.height(); y++) {
0182         for (int x = 0; x < gridSize.width(); x++) {
0183             const Split &ySplit = verticalSplits[y];
0184             const Split &xSplit = horizontalSplits[x];
0185 
0186             const QPointF Sc = lerp(xSplit.p0, xSplit.p3, ySplit.proportion);
0187             const QPointF Sd = lerp(ySplit.p0, ySplit.p3, xSplit.proportion);
0188 
0189             const QPointF Sb =
0190                     lerp(lerp(points[TL], points[TR], xSplit.proportion),
0191                          lerp(points[BL], points[BR], xSplit.proportion),
0192                          ySplit.proportion);
0193 
0194             const QPointF transf = Sc + Sd - Sb;
0195 
0196             const QPointF localPt(lerp(xSplit.coord1, xSplit.coord2, ySplit.proportion),
0197                                   lerp(ySplit.coord1, ySplit.coord2, xSplit.proportion));
0198             const QPointF orig = KisAlgebra2D::relativeToAbsolute(localPt, originalRect);
0199 
0200             origPoints.append(orig);
0201             transfPoints.append(transf);
0202         }
0203     }
0204 }
0205 
0206 QDebug operator<<(QDebug dbg, const KisBezierPatch &p) {
0207     dbg.nospace() << "Patch " << p.srcBoundingRect() << " -> " << p.dstBoundingRect() << "\n";
0208     dbg.nospace() << "  ( " << p.points[KisBezierPatch::TL] << " "<< p.points[KisBezierPatch::TR] << " " << p.points[KisBezierPatch::BL] << " " << p.points[KisBezierPatch::BR] << ") ";
0209     return dbg.nospace();
0210 }