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 }