File indexing completed on 2025-01-26 04:04:57
0001 /* 0002 * SPDX-FileCopyrightText: 2007 Jan Hambrecht <jaham@gmx.net> 0003 * SPDX-FileCopyrightText: 2020 Sharaf Zaman <sharafzaz121@gmail.com> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 #include "SvgMeshPatch.h" 0008 0009 #include <array> 0010 #include <math.h> 0011 #include <QDebug> 0012 #include <kis_global.h> 0013 0014 0015 inline QPointF lerp(const QPointF& p1, const QPointF& p2, qreal t) 0016 { 0017 return (1 - t) * p1 + t * p2; 0018 } 0019 0020 void deCasteljau(const std::array<QPointF, 4>& points, 0021 qreal t, QPointF *p1, QPointF *p2, 0022 QPointF *p3, QPointF *p4, QPointF *p5) 0023 { 0024 QPointF q[4]; 0025 0026 q[0] = points[0]; 0027 q[1] = points[1]; 0028 q[2] = points[2]; 0029 q[3] = points[3]; 0030 0031 // points of the new segment after the split point 0032 QPointF p[3]; 0033 0034 // the De Casteljau algorithm 0035 for (unsigned short j = 1; j <= 3; ++j) { 0036 for (unsigned short i = 0; i <= 3 - j; ++i) { 0037 q[i] = (1.0 - t) * q[i] + t * q[i + 1]; 0038 } 0039 p[j - 1] = q[0]; 0040 } 0041 0042 if (p1) 0043 *p1 = p[0]; 0044 if (p2) 0045 *p2 = p[1]; 0046 if (p3) 0047 *p3 = p[2]; 0048 if (p4) 0049 *p4 = q[1]; 0050 if (p5) 0051 *p5 = q[2]; 0052 } 0053 0054 QPair<std::array<QPointF, 4>, std::array<QPointF, 4>> splitAt(const std::array<QPointF, 4>& points, qreal t) 0055 { 0056 QPointF newCP2, newCP1, splitP, splitCP1, splitCP2; 0057 deCasteljau(points, t, &newCP2, &splitCP1, &splitP, &splitCP2, &newCP1); 0058 return {{points[0], newCP2, splitCP1, splitP}, 0059 {splitP, splitCP2, newCP1, points[3]}}; 0060 } 0061 0062 SvgMeshPatch::SvgMeshPatch(QPointF startingPoint) 0063 : m_newPath(true) 0064 , m_startingPoint(startingPoint) 0065 , m_parametricCoords({QPointF(0, 0), {1, 0}, {1, 1}, {0, 1}}) 0066 { 0067 } 0068 0069 SvgMeshPatch::SvgMeshPatch(const SvgMeshPatch& other) 0070 : m_newPath(other.m_newPath) 0071 , m_startingPoint(other.m_startingPoint) 0072 , m_nodes(other.m_nodes) 0073 , controlPoints(other.controlPoints) 0074 , m_parametricCoords({QPointF(0, 0), {1, 0}, {1, 1}, {0, 1}}) 0075 { 0076 } 0077 0078 void SvgMeshPatch::moveTo(const QPointF& p) 0079 { 0080 controlPoints[counter][0] = p; 0081 } 0082 0083 void SvgMeshPatch::lineTo(const QPointF& p) 0084 { 0085 controlPoints[counter][1] = lerp(controlPoints[counter][0], p, 1.0 / 3); 0086 controlPoints[counter][2] = lerp(controlPoints[counter][0], p, 2.0 / 3); 0087 controlPoints[counter][3] = p; 0088 counter++; 0089 if (counter < Size) 0090 controlPoints[counter][0] = p; 0091 } 0092 0093 void SvgMeshPatch::curveTo(const QPointF& c1, const QPointF& c2, const QPointF& p) 0094 { 0095 controlPoints[counter][1] = c1; 0096 controlPoints[counter][2] = c2; 0097 controlPoints[counter][3] = p; 0098 counter++; 0099 if (counter < Size) 0100 controlPoints[counter][0] = p; 0101 } 0102 0103 SvgMeshStop SvgMeshPatch::getStop(SvgMeshPatch::Type type) const 0104 { 0105 return m_nodes[type]; 0106 } 0107 0108 QPointF SvgMeshPatch::segmentPointAt(Type type, qreal t) const 0109 { 0110 QPointF p; 0111 deCasteljau(controlPoints[type], t, 0, 0, &p, 0, 0); 0112 return p; 0113 } 0114 0115 QPair<std::array<QPointF, 4>, std::array<QPointF, 4>> SvgMeshPatch::segmentSplitAt(Type type, qreal t) const 0116 { 0117 return splitAt(controlPoints[type], t); 0118 } 0119 0120 std::array<QPointF, 4> SvgMeshPatch::getSegment(Type type) const 0121 { 0122 return controlPoints[type]; 0123 } 0124 0125 QPainterPath SvgMeshPatch::getPath() const 0126 { 0127 QPainterPath path; 0128 path.moveTo(controlPoints[Top][0]); 0129 for (const auto& i: controlPoints) { 0130 path.cubicTo(i[1], i[2], i[3]); 0131 } 0132 return path; 0133 } 0134 0135 QRectF SvgMeshPatch::boundingRect() const 0136 { 0137 return getPath().boundingRect(); 0138 } 0139 0140 QSizeF SvgMeshPatch::size() const 0141 { 0142 return boundingRect().size(); 0143 } 0144 0145 std::array<QPointF, 4> SvgMeshPatch::getMidCurve(bool isVertical) const 0146 { 0147 std::array<QPointF, 4> p; 0148 std::array<QPointF, 4> curvedBoundary0; 0149 std::array<QPointF, 4> curvedBoundary1; 0150 0151 QPointF midpointRuled0; 0152 QPointF midpointRuled1; 0153 0154 if (isVertical) { 0155 curvedBoundary0 = getSegment(Right); 0156 curvedBoundary1 = getSegment(Left); 0157 0158 midpointRuled0 = segmentPointAt(Top, 0.5); 0159 midpointRuled1 = segmentPointAt(Bottom, 0.5); 0160 } else { 0161 curvedBoundary0 = getSegment(Top); 0162 curvedBoundary1 = getSegment(Bottom); 0163 0164 midpointRuled0 = segmentPointAt(Left, 0.5); 0165 midpointRuled1 = segmentPointAt(Right, 0.5); 0166 } 0167 0168 // we have to reverse it, cB1 & cB2 are in opposite direction 0169 std::reverse(curvedBoundary1.begin(), curvedBoundary1.end()); 0170 0171 // Sum of two Bezier curve is a Bezier curve 0172 QVector<QPointF> midCurved = { 0173 (curvedBoundary0[0] + curvedBoundary1[0]) / 2, 0174 (curvedBoundary0[1] + curvedBoundary1[1]) / 2, 0175 (curvedBoundary0[2] + curvedBoundary1[2]) / 2, 0176 (curvedBoundary0[3] + curvedBoundary1[3]) / 2, 0177 }; 0178 0179 // line cutting the bilinear surface in middle 0180 QPointF x_2_1 = lerp(midpointRuled0, midpointRuled1, 1.0 / 3); 0181 QPointF x_2_2 = lerp(midpointRuled0, midpointRuled1, 2.0 / 3); 0182 0183 // line cutting rulled surface in middle 0184 QPointF x_3_1 = lerp(midCurved[0], midCurved[3], 1.0 / 3); 0185 QPointF x_3_2 = lerp(midCurved[0], midCurved[3], 2.0 / 3); 0186 0187 0188 p[0] = midpointRuled0; 0189 0190 // X_1 = x_1_1 + x_2_1 - x_3_1 0191 p[1] = midCurved[1] + x_2_1 - x_3_1; 0192 0193 // X_2 = x_1_2 + x_2_2 - x_3_2 0194 p[2] = midCurved[2] + x_2_2 - x_3_2; 0195 0196 p[3] = midpointRuled1; 0197 0198 return p; 0199 } 0200 0201 void SvgMeshPatch::subdivideHorizontally(QVector<SvgMeshPatch*>& subdivided, 0202 const QVector<QColor>& colors) const 0203 { 0204 const QPair<SvgMeshPath, SvgMeshPath> splitRight = segmentSplitAt(Right, 0.5); 0205 const QPair<SvgMeshPath, SvgMeshPath> splitLeft = segmentSplitAt(Left, 0.5); 0206 0207 SvgMeshPath midHor = getMidCurve(/*isVertical = */ false); 0208 SvgMeshPath rMidHor = midHor; 0209 std::reverse(rMidHor.begin(), rMidHor.end()); 0210 0211 QColor c1 = getStop(Top).color; 0212 QColor c2 = getStop(Right).color; 0213 QColor c3 = getStop(Bottom).color; 0214 QColor c4 = getStop(Left).color; 0215 QColor midc23 = colors[1]; 0216 QColor midc41 = colors[3]; 0217 0218 QPointF midRightParametric = getMidpointParametric(Right); 0219 QPointF midLeftParametric = getMidpointParametric(Left); 0220 0221 SvgMeshPatch *patch = new SvgMeshPatch(getSegment(Top)[0]); 0222 patch->addStop(getSegment(Top), c1, Top); 0223 patch->addStop(splitRight.first, c2, Right); 0224 patch->addStop(rMidHor, midc23, Bottom); 0225 patch->addStop(splitLeft.second, midc41, Left); 0226 patch->m_parametricCoords = { 0227 m_parametricCoords[0], 0228 m_parametricCoords[1], 0229 midRightParametric, 0230 midLeftParametric 0231 }; 0232 subdivided.append(patch); 0233 0234 patch = new SvgMeshPatch(midHor[0]); 0235 patch->addStop(midHor, midc41, Top); 0236 patch->addStop(splitRight.second, midc23, Right); 0237 patch->addStop(getSegment(Bottom), c3, Bottom); 0238 patch->addStop(splitLeft.first,c4, Left); 0239 patch->m_parametricCoords = { 0240 midLeftParametric, 0241 midRightParametric, 0242 m_parametricCoords[2], 0243 m_parametricCoords[3] 0244 }; 0245 subdivided.append(patch); 0246 } 0247 0248 void SvgMeshPatch::subdivideVertically(QVector<SvgMeshPatch*>& subdivided, 0249 const QVector<QColor>& colors) const 0250 { 0251 const QPair<SvgMeshPath, SvgMeshPath> splitTop = segmentSplitAt(Top, 0.5); 0252 const QPair<SvgMeshPath, SvgMeshPath> splitBottom = segmentSplitAt(Bottom, 0.5); 0253 0254 SvgMeshPath midVer = getMidCurve(/*isVertical = */ true); 0255 SvgMeshPath rMidVer = midVer; 0256 std::reverse(rMidVer.begin(), rMidVer.end()); 0257 0258 QColor c1 = getStop(Top).color; 0259 QColor c2 = getStop(Right).color; 0260 QColor c3 = getStop(Bottom).color; 0261 QColor c4 = getStop(Left).color; 0262 QColor midc12 = colors[0]; 0263 QColor midc34 = colors[2]; 0264 0265 QPointF midTopParametric = getMidpointParametric(Top); 0266 QPointF midBottomParametric = getMidpointParametric(Bottom); 0267 0268 SvgMeshPatch *patch = new SvgMeshPatch(splitTop.first[0]); 0269 patch->addStop(splitTop.first, c1, Top); 0270 patch->addStop(midVer, midc12, Right); 0271 patch->addStop(splitBottom.second, midc34, Bottom); 0272 patch->addStop(getSegment(Left), c4, Left); 0273 patch->m_parametricCoords = { 0274 m_parametricCoords[0], 0275 midTopParametric, 0276 midBottomParametric, 0277 m_parametricCoords[3] 0278 }; 0279 subdivided.append(patch); 0280 0281 patch = new SvgMeshPatch(splitTop.second[0]); 0282 patch->addStop(splitTop.second, midc12, Top); 0283 patch->addStop(getSegment(Right), c2, Right); 0284 patch->addStop(splitBottom.first, c3, Bottom); 0285 patch->addStop(rMidVer, midc34, Left); 0286 patch->m_parametricCoords = { 0287 midTopParametric, 0288 m_parametricCoords[1], 0289 m_parametricCoords[2], 0290 midBottomParametric 0291 }; 0292 subdivided.append(patch); 0293 } 0294 0295 void SvgMeshPatch::subdivide(QVector<SvgMeshPatch*>& subdivided, 0296 const QVector<QColor>& colors) const 0297 { 0298 KIS_ASSERT(colors.size() == 5); 0299 0300 // The orientation is left to right and top to bottom, which means 0301 // Eg. the first part of splitTop is TopLeft and the second part is TopRight 0302 // Similarly the first part of splitRight is RightTop, but the first part of 0303 // splitLeft is splitLeft.second (once again, in Top to Bottom convention) 0304 const QPair<std::array<QPointF, 4>, std::array<QPointF, 4>> splitTop = segmentSplitAt(Top, 0.5); 0305 const QPair<std::array<QPointF, 4>, std::array<QPointF, 4>> splitRight = segmentSplitAt(Right, 0.5); 0306 const QPair<std::array<QPointF, 4>, std::array<QPointF, 4>> splitBottom = segmentSplitAt(Bottom, 0.5); 0307 const QPair<std::array<QPointF, 4>, std::array<QPointF, 4>> splitLeft = segmentSplitAt(Left, 0.5); 0308 0309 // The way the curve and the colors at the corners are arranged before and after subdivision 0310 // 0311 // midc12 0312 // c1 + c2 0313 // +---------------+ 0314 // | | | 0315 // | | midVer| 0316 // | | < | 0317 // midc41 +---------------+ midc23 0318 // | ^ | | 0319 // | midHor| | 0320 // | | | 0321 // +---------------+ 0322 // c4 + c3 0323 // midc43 0324 // 0325 // 0326 // midHor --> left to right 0327 // midVer --> top to bottom 0328 0329 0330 QPair<std::array<QPointF, 4>, std::array<QPointF, 4>> midHor = splitAt(getMidCurve(/*isVertical = */ false), 0.5); 0331 QPair<std::array<QPointF, 4>, std::array<QPointF, 4>> midVer = splitAt(getMidCurve(/*isVertical = */ true), 0.5); 0332 0333 // middle curve is shared among the two, so we need both directions 0334 std::array<QPointF, 4> reversedMidHorFirst = midHor.first; 0335 std::reverse(reversedMidHorFirst.begin(), reversedMidHorFirst.end()); 0336 std::array<QPointF, 4> reversedMidHorSecond = midHor.second; 0337 std::reverse(reversedMidHorSecond.begin(), reversedMidHorSecond.end()); 0338 0339 std::array<QPointF, 4> reversedMidVerFirst = midVer.first; 0340 std::reverse(reversedMidVerFirst.begin(), reversedMidVerFirst.end()); 0341 std::array<QPointF, 4> reversedMidVerSecond = midVer.second; 0342 std::reverse(reversedMidVerSecond.begin(), reversedMidVerSecond.end()); 0343 0344 QColor c1 = getStop(Top).color; 0345 QColor c2 = getStop(Right).color; 0346 QColor c3 = getStop(Bottom).color; 0347 QColor c4 = getStop(Left).color; 0348 QColor midc12 = colors[0]; 0349 QColor midc23 = colors[1]; 0350 QColor midc34 = colors[2]; 0351 QColor midc41 = colors[3]; 0352 QColor center = colors[4]; 0353 0354 // mid points in parametric space 0355 QPointF midTopP = getMidpointParametric(Top); 0356 QPointF midRightP = getMidpointParametric(Right); 0357 QPointF midBottomP = getMidpointParametric(Bottom); 0358 QPointF midLeftP = getMidpointParametric(Left); 0359 QPointF centerP = 0.5 * (midTopP + midBottomP); 0360 0361 // patch 1: TopLeft/NorthWest 0362 SvgMeshPatch *patch = new SvgMeshPatch(splitTop.first[0]); 0363 patch->addStop(splitTop.first, c1, Top); 0364 patch->addStop(midVer.first, midc12, Right); 0365 patch->addStop(reversedMidHorFirst, center, Bottom); 0366 patch->addStop(splitLeft.second, midc41, Left); 0367 patch->m_parametricCoords = { 0368 m_parametricCoords[0], 0369 midTopP, 0370 centerP, 0371 midLeftP 0372 }; 0373 subdivided.append(patch); 0374 0375 // patch 2: TopRight/NorthRight 0376 patch = new SvgMeshPatch(splitTop.second[0]); 0377 patch->addStop(splitTop.second, midc12, Top); 0378 patch->addStop(splitRight.first, c2, Right); 0379 patch->addStop(reversedMidHorSecond, midc23, Bottom); 0380 patch->addStop(reversedMidVerFirst, center, Left); 0381 patch->m_parametricCoords = { 0382 midTopP, 0383 m_parametricCoords[1], 0384 midRightP, 0385 centerP 0386 }; 0387 subdivided.append(patch); 0388 0389 // patch 3: BottomLeft/SouthWest 0390 patch = new SvgMeshPatch(midHor.first[0]); 0391 patch->addStop(midHor.first, midc41, Top); 0392 patch->addStop(midVer.second, center, Right); 0393 patch->addStop(splitBottom.second, midc34, Bottom); 0394 patch->addStop(splitLeft.first, c4, Left); 0395 patch->m_parametricCoords = { 0396 midLeftP, 0397 centerP, 0398 midBottomP, 0399 m_parametricCoords[3] 0400 }; 0401 subdivided.append(patch); 0402 0403 // patch 4: BottomRight/SouthEast 0404 patch = new SvgMeshPatch(midHor.second[0]); 0405 patch->addStop(midHor.second, center, Top); 0406 patch->addStop(splitRight.second, midc23, Right); 0407 patch->addStop(splitBottom.first, c3, Bottom); 0408 patch->addStop(reversedMidVerSecond, midc34, Left); 0409 patch->m_parametricCoords = { 0410 centerP, 0411 midRightP, 0412 m_parametricCoords[2], 0413 midBottomP 0414 }; 0415 subdivided.append(patch); 0416 } 0417 0418 static qreal controlrectLen(const SvgMeshPath &path) { 0419 return QLineF(path[0], path[1]).length() + 0420 QLineF(path[1], path[2]).length() + 0421 QLineF(path[2], path[3]).length(); 0422 } 0423 0424 bool SvgMeshPatch::isDivisibleVertically() const 0425 { 0426 // I arrived at this number by the virtue called trial 'n error 0427 const qreal minlength = 1.7; 0428 const qreal line1 = QLineF(controlPoints[Top][0], controlPoints[Top][3]).length(); 0429 const qreal control1 = controlrectLen(getSegment(Top)); 0430 0431 // a decent average, thanks to Khronos's forums 0432 if ((line1 + control1 / 2) < minlength) { 0433 return false; 0434 } 0435 0436 const qreal line2 = QLineF(controlPoints[Bottom][0], controlPoints[Bottom][3]).length(); 0437 const qreal control2 = controlrectLen(getSegment(Bottom)); 0438 if ((line2 + control2 / 2) < minlength) { 0439 return false; 0440 } 0441 0442 return true; 0443 } 0444 0445 bool SvgMeshPatch::isDivisibleHorizontally() const 0446 { 0447 // I arrived at this number by the virtue called trial 'n error 0448 const qreal minlength = 1.7; 0449 0450 // a decent average, thanks to Khronos's forums 0451 const qreal line1 = QLineF(controlPoints[Right][0], controlPoints[Right][3]).length(); 0452 const qreal control1 = controlrectLen(getSegment(Right)); 0453 if ((line1 + control1 / 2) < minlength) { 0454 return false; 0455 } 0456 0457 const qreal line2 = QLineF(controlPoints[Left][0], controlPoints[Left][3]).length(); 0458 const qreal control2 = controlrectLen(getSegment(Left)); 0459 if ((line2 + control2 / 2) < minlength) { 0460 return false; 0461 } 0462 0463 return true; 0464 } 0465 0466 void SvgMeshPatch::addStop(const QString& pathStr, 0467 QColor color, 0468 Type edge, 0469 bool pathIncomplete, 0470 QPointF lastPoint) 0471 { 0472 SvgMeshStop node(color, m_startingPoint); 0473 m_nodes[edge] = node; 0474 0475 m_startingPoint = parseMeshPath(pathStr, pathIncomplete, lastPoint); 0476 } 0477 0478 void SvgMeshPatch::addStop(const std::array<QPointF, 4>& pathPoints, QColor color, Type edge) 0479 { 0480 SvgMeshStop stop(color, pathPoints[0]); 0481 m_nodes[edge] = stop; 0482 0483 if (edge == SvgMeshPatch::Top) { 0484 moveTo(pathPoints[0]); 0485 m_newPath = false; 0486 } 0487 0488 curveTo(pathPoints[1], pathPoints[2], pathPoints[3]); 0489 m_startingPoint = pathPoints[3]; 0490 } 0491 0492 0493 void SvgMeshPatch::addStopLinear(const std::array<QPointF, 2>& pathPoints, QColor color, Type edge) 0494 { 0495 SvgMeshStop stop(color, pathPoints[0]); 0496 m_nodes[edge] = stop; 0497 0498 if (edge == SvgMeshPatch::Top) { 0499 moveTo(pathPoints[0]); 0500 m_newPath = false; 0501 } 0502 0503 lineTo(pathPoints[1]); 0504 m_startingPoint = pathPoints[1]; 0505 } 0506 0507 void SvgMeshPatch::modifyPath(SvgMeshPatch::Type type, std::array<QPointF, 4> newPath) 0508 { 0509 controlPoints[type] = newPath; 0510 m_nodes[type].point = newPath[0]; 0511 } 0512 0513 void SvgMeshPatch::modifyCorner(SvgMeshPatch::Type type, const QPointF &delta) 0514 { 0515 controlPoints[type][0] -= delta; 0516 controlPoints[type][1] -= delta; 0517 m_nodes[type].point = controlPoints[type][0]; 0518 0519 controlPoints[(Size + type - 1) % Size][3] -= delta; 0520 controlPoints[(Size + type - 1) % Size][2] -= delta; 0521 } 0522 0523 void SvgMeshPatch::setStopColor(SvgMeshPatch::Type type, const QColor &color) 0524 { 0525 m_nodes[type].color = color; 0526 } 0527 0528 void SvgMeshPatch::setTransform(const QTransform& matrix) 0529 { 0530 m_startingPoint = matrix.map(m_startingPoint); 0531 for (int i = 0; i < Size; ++i) { 0532 m_nodes[i].point = matrix.map(m_nodes[i].point); 0533 for (int j = 0; j < 4; ++j) { 0534 controlPoints[i][j] = matrix.map(controlPoints[i][j]); 0535 } 0536 } 0537 } 0538 0539 int SvgMeshPatch::countPoints() const 0540 { 0541 return m_nodes.size(); 0542 } 0543 0544 0545 QPointF SvgMeshPatch::parseMeshPath(const QString& s, bool pathIncomplete, const QPointF lastPoint) 0546 { 0547 // bits and pieces from KoPathShapeLoader, see the copyright above 0548 if (!s.isEmpty()) { 0549 QString d = s; 0550 d.replace(',', ' '); 0551 d = d.simplified(); 0552 0553 const QByteArray buffer = d.toLatin1(); 0554 const char *ptr = buffer.constData(); 0555 qreal curx = m_startingPoint.x(); 0556 qreal cury = m_startingPoint.y(); 0557 qreal tox, toy, x1, y1, x2, y2; 0558 bool relative = false; 0559 char command = *(ptr++); 0560 0561 if (m_newPath) { 0562 moveTo(m_startingPoint); 0563 m_newPath = false; 0564 } 0565 0566 while (*ptr == ' ') 0567 ++ptr; 0568 0569 switch (command) { 0570 case 'l': 0571 relative = true; 0572 Q_FALLTHROUGH(); 0573 case 'L': { 0574 ptr = getCoord(ptr, tox); 0575 ptr = getCoord(ptr, toy); 0576 0577 if (relative) { 0578 tox = curx + tox; 0579 toy = cury + toy; 0580 } 0581 0582 if (pathIncomplete) { 0583 tox = lastPoint.x(); 0584 toy = lastPoint.y(); 0585 } 0586 0587 // we convert lines to cubic curve 0588 lineTo({tox, toy}); 0589 break; 0590 } 0591 case 'c': 0592 relative = true; 0593 Q_FALLTHROUGH(); 0594 case 'C': { 0595 ptr = getCoord(ptr, x1); 0596 ptr = getCoord(ptr, y1); 0597 ptr = getCoord(ptr, x2); 0598 ptr = getCoord(ptr, y2); 0599 ptr = getCoord(ptr, tox); 0600 ptr = getCoord(ptr, toy); 0601 0602 if (relative) { 0603 x1 = curx + x1; 0604 y1 = cury + y1; 0605 x2 = curx + x2; 0606 y2 = cury + y2; 0607 tox = curx + tox; 0608 toy = cury + toy; 0609 } 0610 0611 if (pathIncomplete) { 0612 tox = lastPoint.x(); 0613 toy = lastPoint.y(); 0614 } 0615 0616 curveTo(QPointF(x1, y1), QPointF(x2, y2), QPointF(tox, toy)); 0617 break; 0618 } 0619 0620 default: { 0621 qWarning() << "SvgMeshPatch::parseMeshPath: Bad command \"" << command << "\""; 0622 return QPointF(); 0623 } 0624 } 0625 return {tox, toy}; 0626 } 0627 return QPointF(); 0628 } 0629 0630 const char* SvgMeshPatch::getCoord(const char* ptr, qreal& number) 0631 { 0632 // copied from KoPathShapeLoader, see the copyright above 0633 int integer, exponent; 0634 qreal decimal, frac; 0635 int sign, expsign; 0636 0637 exponent = 0; 0638 integer = 0; 0639 frac = 1.0; 0640 decimal = 0; 0641 sign = 1; 0642 expsign = 1; 0643 0644 // read the sign 0645 if (*ptr == '+') 0646 ++ptr; 0647 else if (*ptr == '-') { 0648 ++ptr; 0649 sign = -1; 0650 } 0651 0652 // read the integer part 0653 while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9') 0654 integer = (integer * 10) + *(ptr++) - '0'; 0655 if (*ptr == '.') { // read the decimals 0656 ++ptr; 0657 while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9') 0658 decimal += (*(ptr++) - '0') * (frac *= 0.1); 0659 } 0660 0661 if (*ptr == 'e' || *ptr == 'E') { // read the exponent part 0662 ++ptr; 0663 0664 // read the sign of the exponent 0665 if (*ptr == '+') 0666 ++ptr; 0667 else if (*ptr == '-') { 0668 ++ptr; 0669 expsign = -1; 0670 } 0671 0672 exponent = 0; 0673 while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9') { 0674 exponent *= 10; 0675 exponent += *ptr - '0'; 0676 ++ptr; 0677 } 0678 } 0679 number = integer + decimal; 0680 number *= sign * pow((qreal)10, qreal(expsign * exponent)); 0681 0682 // skip the following space 0683 if (*ptr == ' ') 0684 ++ptr; 0685 0686 return ptr; 0687 }