Krita Source Code Documentation
Loading...
Searching...
No Matches
SvgMeshPatch.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2007 Jan Hambrecht <jaham@gmx.net>
3 * SPDX-FileCopyrightText: 2020 Sharaf Zaman <sharafzaz121@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7#include "SvgMeshPatch.h"
8
9#include <array>
10#include <math.h>
11#include <QDebug>
12#include <kis_global.h>
13
14
15inline QPointF lerp(const QPointF& p1, const QPointF& p2, qreal t)
16{
17 return (1 - t) * p1 + t * p2;
18}
19
20void deCasteljau(const std::array<QPointF, 4>& points,
21 qreal t, QPointF *p1, QPointF *p2,
22 QPointF *p3, QPointF *p4, QPointF *p5)
23{
24 QPointF q[4];
25
26 q[0] = points[0];
27 q[1] = points[1];
28 q[2] = points[2];
29 q[3] = points[3];
30
31 // points of the new segment after the split point
32 QPointF p[3];
33
34 // the De Casteljau algorithm
35 for (unsigned short j = 1; j <= 3; ++j) {
36 for (unsigned short i = 0; i <= 3 - j; ++i) {
37 q[i] = (1.0 - t) * q[i] + t * q[i + 1];
38 }
39 p[j - 1] = q[0];
40 }
41
42 if (p1)
43 *p1 = p[0];
44 if (p2)
45 *p2 = p[1];
46 if (p3)
47 *p3 = p[2];
48 if (p4)
49 *p4 = q[1];
50 if (p5)
51 *p5 = q[2];
52}
53
54QPair<std::array<QPointF, 4>, std::array<QPointF, 4>> splitAt(const std::array<QPointF, 4>& points, qreal t)
55{
56 QPointF newCP2, newCP1, splitP, splitCP1, splitCP2;
57 deCasteljau(points, t, &newCP2, &splitCP1, &splitP, &splitCP2, &newCP1);
58 return {{points[0], newCP2, splitCP1, splitP},
59 {splitP, splitCP2, newCP1, points[3]}};
60}
61
62SvgMeshPatch::SvgMeshPatch(QPointF startingPoint)
63 : m_newPath(true)
64 , m_startingPoint(startingPoint)
65 , m_parametricCoords({QPointF(0, 0), {1, 0}, {1, 1}, {0, 1}})
66{
67}
68
70 : m_newPath(other.m_newPath)
71 , m_startingPoint(other.m_startingPoint)
72 , m_nodes(other.m_nodes)
73 , controlPoints(other.controlPoints)
74 , m_parametricCoords({QPointF(0, 0), {1, 0}, {1, 1}, {0, 1}})
75{
76}
77
78void SvgMeshPatch::moveTo(const QPointF& p)
79{
81}
82
83void SvgMeshPatch::lineTo(const QPointF& p)
84{
85 controlPoints[counter][1] = lerp(controlPoints[counter][0], p, 1.0 / 3);
86 controlPoints[counter][2] = lerp(controlPoints[counter][0], p, 2.0 / 3);
88 counter++;
89 if (counter < Size)
91}
92
93void SvgMeshPatch::curveTo(const QPointF& c1, const QPointF& c2, const QPointF& p)
94{
95 controlPoints[counter][1] = c1;
96 controlPoints[counter][2] = c2;
98 counter++;
99 if (counter < Size)
100 controlPoints[counter][0] = p;
101}
102
104{
105 return m_nodes[type];
106}
107
108QPointF SvgMeshPatch::segmentPointAt(Type type, qreal t) const
109{
110 QPointF p;
111 deCasteljau(controlPoints[type], t, 0, 0, &p, 0, 0);
112 return p;
113}
114
115QPair<std::array<QPointF, 4>, std::array<QPointF, 4>> SvgMeshPatch::segmentSplitAt(Type type, qreal t) const
116{
117 return splitAt(controlPoints[type], t);
118}
119
120std::array<QPointF, 4> SvgMeshPatch::getSegment(Type type) const
121{
122 return controlPoints[type];
123}
124
125QPainterPath SvgMeshPatch::getPath() const
126{
127 QPainterPath path;
128 path.moveTo(controlPoints[Top][0]);
129 for (const auto& i: controlPoints) {
130 path.cubicTo(i[1], i[2], i[3]);
131 }
132 return path;
133}
134
136{
137 return getPath().boundingRect();
138}
139
140QSizeF SvgMeshPatch::size() const
141{
142 return boundingRect().size();
143}
144
145std::array<QPointF, 4> SvgMeshPatch::getMidCurve(bool isVertical) const
146{
147 std::array<QPointF, 4> p;
148 std::array<QPointF, 4> curvedBoundary0;
149 std::array<QPointF, 4> curvedBoundary1;
150
151 QPointF midpointRuled0;
152 QPointF midpointRuled1;
153
154 if (isVertical) {
155 curvedBoundary0 = getSegment(Right);
156 curvedBoundary1 = getSegment(Left);
157
158 midpointRuled0 = segmentPointAt(Top, 0.5);
159 midpointRuled1 = segmentPointAt(Bottom, 0.5);
160 } else {
161 curvedBoundary0 = getSegment(Top);
162 curvedBoundary1 = getSegment(Bottom);
163
164 midpointRuled0 = segmentPointAt(Left, 0.5);
165 midpointRuled1 = segmentPointAt(Right, 0.5);
166 }
167
168 // we have to reverse it, cB1 & cB2 are in opposite direction
169 std::reverse(curvedBoundary1.begin(), curvedBoundary1.end());
170
171 // Sum of two Bezier curve is a Bezier curve
172 QVector<QPointF> midCurved = {
173 (curvedBoundary0[0] + curvedBoundary1[0]) / 2,
174 (curvedBoundary0[1] + curvedBoundary1[1]) / 2,
175 (curvedBoundary0[2] + curvedBoundary1[2]) / 2,
176 (curvedBoundary0[3] + curvedBoundary1[3]) / 2,
177 };
178
179 // line cutting the bilinear surface in middle
180 QPointF x_2_1 = lerp(midpointRuled0, midpointRuled1, 1.0 / 3);
181 QPointF x_2_2 = lerp(midpointRuled0, midpointRuled1, 2.0 / 3);
182
183 // line cutting rulled surface in middle
184 QPointF x_3_1 = lerp(midCurved[0], midCurved[3], 1.0 / 3);
185 QPointF x_3_2 = lerp(midCurved[0], midCurved[3], 2.0 / 3);
186
187
188 p[0] = midpointRuled0;
189
190 // X_1 = x_1_1 + x_2_1 - x_3_1
191 p[1] = midCurved[1] + x_2_1 - x_3_1;
192
193 // X_2 = x_1_2 + x_2_2 - x_3_2
194 p[2] = midCurved[2] + x_2_2 - x_3_2;
195
196 p[3] = midpointRuled1;
197
198 return p;
199}
200
202 const QVector<QColor>& colors) const
203{
204 const QPair<SvgMeshPath, SvgMeshPath> splitRight = segmentSplitAt(Right, 0.5);
205 const QPair<SvgMeshPath, SvgMeshPath> splitLeft = segmentSplitAt(Left, 0.5);
206
207 SvgMeshPath midHor = getMidCurve(/*isVertical = */ false);
208 SvgMeshPath rMidHor = midHor;
209 std::reverse(rMidHor.begin(), rMidHor.end());
210
211 QColor c1 = getStop(Top).color;
212 QColor c2 = getStop(Right).color;
213 QColor c3 = getStop(Bottom).color;
214 QColor c4 = getStop(Left).color;
215 QColor midc23 = colors[1];
216 QColor midc41 = colors[3];
217
218 QPointF midRightParametric = getMidpointParametric(Right);
219 QPointF midLeftParametric = getMidpointParametric(Left);
220
221 SvgMeshPatch *patch = new SvgMeshPatch(getSegment(Top)[0]);
222 patch->addStop(getSegment(Top), c1, Top);
223 patch->addStop(splitRight.first, c2, Right);
224 patch->addStop(rMidHor, midc23, Bottom);
225 patch->addStop(splitLeft.second, midc41, Left);
226 patch->m_parametricCoords = {
229 midRightParametric,
230 midLeftParametric
231 };
232 subdivided.append(patch);
233
234 patch = new SvgMeshPatch(midHor[0]);
235 patch->addStop(midHor, midc41, Top);
236 patch->addStop(splitRight.second, midc23, Right);
237 patch->addStop(getSegment(Bottom), c3, Bottom);
238 patch->addStop(splitLeft.first,c4, Left);
239 patch->m_parametricCoords = {
240 midLeftParametric,
241 midRightParametric,
244 };
245 subdivided.append(patch);
246}
247
249 const QVector<QColor>& colors) const
250{
251 const QPair<SvgMeshPath, SvgMeshPath> splitTop = segmentSplitAt(Top, 0.5);
252 const QPair<SvgMeshPath, SvgMeshPath> splitBottom = segmentSplitAt(Bottom, 0.5);
253
254 SvgMeshPath midVer = getMidCurve(/*isVertical = */ true);
255 SvgMeshPath rMidVer = midVer;
256 std::reverse(rMidVer.begin(), rMidVer.end());
257
258 QColor c1 = getStop(Top).color;
259 QColor c2 = getStop(Right).color;
260 QColor c3 = getStop(Bottom).color;
261 QColor c4 = getStop(Left).color;
262 QColor midc12 = colors[0];
263 QColor midc34 = colors[2];
264
265 QPointF midTopParametric = getMidpointParametric(Top);
266 QPointF midBottomParametric = getMidpointParametric(Bottom);
267
268 SvgMeshPatch *patch = new SvgMeshPatch(splitTop.first[0]);
269 patch->addStop(splitTop.first, c1, Top);
270 patch->addStop(midVer, midc12, Right);
271 patch->addStop(splitBottom.second, midc34, Bottom);
272 patch->addStop(getSegment(Left), c4, Left);
273 patch->m_parametricCoords = {
275 midTopParametric,
276 midBottomParametric,
278 };
279 subdivided.append(patch);
280
281 patch = new SvgMeshPatch(splitTop.second[0]);
282 patch->addStop(splitTop.second, midc12, Top);
283 patch->addStop(getSegment(Right), c2, Right);
284 patch->addStop(splitBottom.first, c3, Bottom);
285 patch->addStop(rMidVer, midc34, Left);
286 patch->m_parametricCoords = {
287 midTopParametric,
290 midBottomParametric
291 };
292 subdivided.append(patch);
293}
294
296 const QVector<QColor>& colors) const
297{
298 KIS_ASSERT(colors.size() == 5);
299
300 // The orientation is left to right and top to bottom, which means
301 // Eg. the first part of splitTop is TopLeft and the second part is TopRight
302 // Similarly the first part of splitRight is RightTop, but the first part of
303 // splitLeft is splitLeft.second (once again, in Top to Bottom convention)
304 const QPair<std::array<QPointF, 4>, std::array<QPointF, 4>> splitTop = segmentSplitAt(Top, 0.5);
305 const QPair<std::array<QPointF, 4>, std::array<QPointF, 4>> splitRight = segmentSplitAt(Right, 0.5);
306 const QPair<std::array<QPointF, 4>, std::array<QPointF, 4>> splitBottom = segmentSplitAt(Bottom, 0.5);
307 const QPair<std::array<QPointF, 4>, std::array<QPointF, 4>> splitLeft = segmentSplitAt(Left, 0.5);
308
309 // The way the curve and the colors at the corners are arranged before and after subdivision
310 //
311 // midc12
312 // c1 + c2
313 // +---------------+
314 // | | |
315 // | | midVer|
316 // | | < |
317 // midc41 +---------------+ midc23
318 // | ^ | |
319 // | midHor| |
320 // | | |
321 // +---------------+
322 // c4 + c3
323 // midc43
324 //
325 //
326 // midHor --> left to right
327 // midVer --> top to bottom
328
329
330 QPair<std::array<QPointF, 4>, std::array<QPointF, 4>> midHor = splitAt(getMidCurve(/*isVertical = */ false), 0.5);
331 QPair<std::array<QPointF, 4>, std::array<QPointF, 4>> midVer = splitAt(getMidCurve(/*isVertical = */ true), 0.5);
332
333 // middle curve is shared among the two, so we need both directions
334 std::array<QPointF, 4> reversedMidHorFirst = midHor.first;
335 std::reverse(reversedMidHorFirst.begin(), reversedMidHorFirst.end());
336 std::array<QPointF, 4> reversedMidHorSecond = midHor.second;
337 std::reverse(reversedMidHorSecond.begin(), reversedMidHorSecond.end());
338
339 std::array<QPointF, 4> reversedMidVerFirst = midVer.first;
340 std::reverse(reversedMidVerFirst.begin(), reversedMidVerFirst.end());
341 std::array<QPointF, 4> reversedMidVerSecond = midVer.second;
342 std::reverse(reversedMidVerSecond.begin(), reversedMidVerSecond.end());
343
344 QColor c1 = getStop(Top).color;
345 QColor c2 = getStop(Right).color;
346 QColor c3 = getStop(Bottom).color;
347 QColor c4 = getStop(Left).color;
348 QColor midc12 = colors[0];
349 QColor midc23 = colors[1];
350 QColor midc34 = colors[2];
351 QColor midc41 = colors[3];
352 QColor center = colors[4];
353
354 // mid points in parametric space
355 QPointF midTopP = getMidpointParametric(Top);
356 QPointF midRightP = getMidpointParametric(Right);
357 QPointF midBottomP = getMidpointParametric(Bottom);
358 QPointF midLeftP = getMidpointParametric(Left);
359 QPointF centerP = 0.5 * (midTopP + midBottomP);
360
361 // patch 1: TopLeft/NorthWest
362 SvgMeshPatch *patch = new SvgMeshPatch(splitTop.first[0]);
363 patch->addStop(splitTop.first, c1, Top);
364 patch->addStop(midVer.first, midc12, Right);
365 patch->addStop(reversedMidHorFirst, center, Bottom);
366 patch->addStop(splitLeft.second, midc41, Left);
367 patch->m_parametricCoords = {
369 midTopP,
370 centerP,
371 midLeftP
372 };
373 subdivided.append(patch);
374
375 // patch 2: TopRight/NorthRight
376 patch = new SvgMeshPatch(splitTop.second[0]);
377 patch->addStop(splitTop.second, midc12, Top);
378 patch->addStop(splitRight.first, c2, Right);
379 patch->addStop(reversedMidHorSecond, midc23, Bottom);
380 patch->addStop(reversedMidVerFirst, center, Left);
381 patch->m_parametricCoords = {
382 midTopP,
384 midRightP,
385 centerP
386 };
387 subdivided.append(patch);
388
389 // patch 3: BottomLeft/SouthWest
390 patch = new SvgMeshPatch(midHor.first[0]);
391 patch->addStop(midHor.first, midc41, Top);
392 patch->addStop(midVer.second, center, Right);
393 patch->addStop(splitBottom.second, midc34, Bottom);
394 patch->addStop(splitLeft.first, c4, Left);
395 patch->m_parametricCoords = {
396 midLeftP,
397 centerP,
398 midBottomP,
400 };
401 subdivided.append(patch);
402
403 // patch 4: BottomRight/SouthEast
404 patch = new SvgMeshPatch(midHor.second[0]);
405 patch->addStop(midHor.second, center, Top);
406 patch->addStop(splitRight.second, midc23, Right);
407 patch->addStop(splitBottom.first, c3, Bottom);
408 patch->addStop(reversedMidVerSecond, midc34, Left);
409 patch->m_parametricCoords = {
410 centerP,
411 midRightP,
413 midBottomP
414 };
415 subdivided.append(patch);
416}
417
418static qreal controlrectLen(const SvgMeshPath &path) {
419 return QLineF(path[0], path[1]).length() +
420 QLineF(path[1], path[2]).length() +
421 QLineF(path[2], path[3]).length();
422}
423
425{
426 // I arrived at this number by the virtue called trial 'n error
427 const qreal minlength = 1.7;
428 const qreal line1 = QLineF(controlPoints[Top][0], controlPoints[Top][3]).length();
429 const qreal control1 = controlrectLen(getSegment(Top));
430
431 // a decent average, thanks to Khronos's forums
432 if ((line1 + control1 / 2) < minlength) {
433 return false;
434 }
435
436 const qreal line2 = QLineF(controlPoints[Bottom][0], controlPoints[Bottom][3]).length();
437 const qreal control2 = controlrectLen(getSegment(Bottom));
438 if ((line2 + control2 / 2) < minlength) {
439 return false;
440 }
441
442 return true;
443}
444
446{
447 // I arrived at this number by the virtue called trial 'n error
448 const qreal minlength = 1.7;
449
450 // a decent average, thanks to Khronos's forums
451 const qreal line1 = QLineF(controlPoints[Right][0], controlPoints[Right][3]).length();
452 const qreal control1 = controlrectLen(getSegment(Right));
453 if ((line1 + control1 / 2) < minlength) {
454 return false;
455 }
456
457 const qreal line2 = QLineF(controlPoints[Left][0], controlPoints[Left][3]).length();
458 const qreal control2 = controlrectLen(getSegment(Left));
459 if ((line2 + control2 / 2) < minlength) {
460 return false;
461 }
462
463 return true;
464}
465
466void SvgMeshPatch::addStop(const QString& pathStr,
467 QColor color,
468 Type edge,
469 bool pathIncomplete,
470 QPointF lastPoint)
471{
472 SvgMeshStop node(color, m_startingPoint);
473 m_nodes[edge] = node;
474
475 m_startingPoint = parseMeshPath(pathStr, pathIncomplete, lastPoint);
476}
477
478void SvgMeshPatch::addStop(const std::array<QPointF, 4>& pathPoints, QColor color, Type edge)
479{
480 SvgMeshStop stop(color, pathPoints[0]);
481 m_nodes[edge] = stop;
482
483 if (edge == SvgMeshPatch::Top) {
484 moveTo(pathPoints[0]);
485 m_newPath = false;
486 }
487
488 curveTo(pathPoints[1], pathPoints[2], pathPoints[3]);
489 m_startingPoint = pathPoints[3];
490}
491
492
493void SvgMeshPatch::addStopLinear(const std::array<QPointF, 2>& pathPoints, QColor color, Type edge)
494{
495 SvgMeshStop stop(color, pathPoints[0]);
496 m_nodes[edge] = stop;
497
498 if (edge == SvgMeshPatch::Top) {
499 moveTo(pathPoints[0]);
500 m_newPath = false;
501 }
502
503 lineTo(pathPoints[1]);
504 m_startingPoint = pathPoints[1];
505}
506
507void SvgMeshPatch::modifyPath(SvgMeshPatch::Type type, std::array<QPointF, 4> newPath)
508{
509 controlPoints[type] = newPath;
510 m_nodes[type].point = newPath[0];
511}
512
513void SvgMeshPatch::modifyCorner(SvgMeshPatch::Type type, const QPointF &delta)
514{
515 controlPoints[type][0] -= delta;
516 controlPoints[type][1] -= delta;
517 m_nodes[type].point = controlPoints[type][0];
518
519 controlPoints[(Size + type - 1) % Size][3] -= delta;
520 controlPoints[(Size + type - 1) % Size][2] -= delta;
521}
522
523void SvgMeshPatch::setStopColor(SvgMeshPatch::Type type, const QColor &color)
524{
525 m_nodes[type].color = color;
526}
527
528void SvgMeshPatch::setTransform(const QTransform& matrix)
529{
530 m_startingPoint = matrix.map(m_startingPoint);
531 for (int i = 0; i < Size; ++i) {
532 m_nodes[i].point = matrix.map(m_nodes[i].point);
533 for (int j = 0; j < 4; ++j) {
534 controlPoints[i][j] = matrix.map(controlPoints[i][j]);
535 }
536 }
537}
538
540{
541 return m_nodes.size();
542}
543
544
545QPointF SvgMeshPatch::parseMeshPath(const QString& s, bool pathIncomplete, const QPointF lastPoint)
546{
547 // bits and pieces from KoPathShapeLoader, see the copyright above
548 if (!s.isEmpty()) {
549 QString d = s;
550 d.replace(',', ' ');
551 d = d.simplified();
552
553 const QByteArray buffer = d.toLatin1();
554 const char *ptr = buffer.constData();
555 qreal curx = m_startingPoint.x();
556 qreal cury = m_startingPoint.y();
557 qreal tox, toy, x1, y1, x2, y2;
558 bool relative = false;
559 char command = *(ptr++);
560
561 if (m_newPath) {
563 m_newPath = false;
564 }
565
566 while (*ptr == ' ')
567 ++ptr;
568
569 switch (command) {
570 case 'l':
571 relative = true;
572 Q_FALLTHROUGH();
573 case 'L': {
574 ptr = getCoord(ptr, tox);
575 ptr = getCoord(ptr, toy);
576
577 if (relative) {
578 tox = curx + tox;
579 toy = cury + toy;
580 }
581
582 if (pathIncomplete) {
583 tox = lastPoint.x();
584 toy = lastPoint.y();
585 }
586
587 // we convert lines to cubic curve
588 lineTo({tox, toy});
589 break;
590 }
591 case 'c':
592 relative = true;
593 Q_FALLTHROUGH();
594 case 'C': {
595 ptr = getCoord(ptr, x1);
596 ptr = getCoord(ptr, y1);
597 ptr = getCoord(ptr, x2);
598 ptr = getCoord(ptr, y2);
599 ptr = getCoord(ptr, tox);
600 ptr = getCoord(ptr, toy);
601
602 if (relative) {
603 x1 = curx + x1;
604 y1 = cury + y1;
605 x2 = curx + x2;
606 y2 = cury + y2;
607 tox = curx + tox;
608 toy = cury + toy;
609 }
610
611 if (pathIncomplete) {
612 tox = lastPoint.x();
613 toy = lastPoint.y();
614 }
615
616 curveTo(QPointF(x1, y1), QPointF(x2, y2), QPointF(tox, toy));
617 break;
618 }
619
620 default: {
621 qWarning() << "SvgMeshPatch::parseMeshPath: Bad command \"" << command << "\"";
622 return QPointF();
623 }
624 }
625 return {tox, toy};
626 }
627 return QPointF();
628}
629
630const char* SvgMeshPatch::getCoord(const char* ptr, qreal& number)
631{
632 // copied from KoPathShapeLoader, see the copyright above
633 int integer, exponent;
634 qreal decimal, frac;
635 int sign, expsign;
636
637 exponent = 0;
638 integer = 0;
639 frac = 1.0;
640 decimal = 0;
641 sign = 1;
642 expsign = 1;
643
644 // read the sign
645 if (*ptr == '+')
646 ++ptr;
647 else if (*ptr == '-') {
648 ++ptr;
649 sign = -1;
650 }
651
652 // read the integer part
653 while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
654 integer = (integer * 10) + *(ptr++) - '0';
655 if (*ptr == '.') { // read the decimals
656 ++ptr;
657 while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
658 decimal += (*(ptr++) - '0') * (frac *= 0.1);
659 }
660
661 if (*ptr == 'e' || *ptr == 'E') { // read the exponent part
662 ++ptr;
663
664 // read the sign of the exponent
665 if (*ptr == '+')
666 ++ptr;
667 else if (*ptr == '-') {
668 ++ptr;
669 expsign = -1;
670 }
671
672 exponent = 0;
673 while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9') {
674 exponent *= 10;
675 exponent += *ptr - '0';
676 ++ptr;
677 }
678 }
679 number = integer + decimal;
680 number *= sign * pow((qreal)10, qreal(expsign * exponent));
681
682 // skip the following space
683 if (*ptr == ' ')
684 ++ptr;
685
686 return ptr;
687}
const Params2D p
QPointF p2
QPointF p3
QPointF p1
QPair< std::array< QPointF, 4 >, std::array< QPointF, 4 > > splitAt(const std::array< QPointF, 4 > &points, qreal t)
static qreal controlrectLen(const SvgMeshPath &path)
QPointF lerp(const QPointF &p1, const QPointF &p2, qreal t)
void deCasteljau(const std::array< QPointF, 4 > &points, qreal t, QPointF *p1, QPointF *p2, QPointF *p3, QPointF *p4, QPointF *p5)
std::array< QPointF, 4 > SvgMeshPath
QSizeF size() const
Get size swept by mesh in pts.
QPainterPath getPath() const
Get full (closed) meshpath.
SvgMeshPatch(QPointF startingPoint)
Type
Position of stop in the patch.
QRectF boundingRect() const
const char * getCoord(const char *ptr, qreal &number)
QPointF getMidpointParametric(Type type) const
returns the midPoint in parametric space
void subdivideVertically(QVector< SvgMeshPatch * > &subdivided, const QVector< QColor > &colors) const
QPair< std::array< QPointF, 4 >, std::array< QPointF, 4 > > segmentSplitAt(Type type, qreal t) const
split a segment using De Casteljau's algorithm
int countPoints() const
QPointF m_startingPoint
This is the starting point for each path.
std::array< SvgMeshStop, Size > m_nodes
std::array< std::array< QPointF, 4 >, 4 > controlPoints
void subdivideHorizontally(QVector< SvgMeshPatch * > &subdivided, const QVector< QColor > &colors) const
void setTransform(const QTransform &matrix)
std::array< QPointF, 4 > m_parametricCoords
Coordinates in UV space.
void addStop(const QString &pathStr, QColor color, Type edge, bool pathIncomplete=false, QPointF lastPoint=QPointF())
void addStopLinear(const std::array< QPointF, 2 > &pathPoints, QColor color, Type edge)
Adds linear path to the shape.
std::array< QPointF, 4 > getMidCurve(bool isVertical) const
Gets the curve passing through the middle of meshpatch.
QPointF parseMeshPath(const QString &path, bool pathIncomplete=false, const QPointF lastPoint=QPointF())
void moveTo(const QPointF &p)
void modifyPath(SvgMeshPatch::Type type, std::array< QPointF, 4 > newPath)
void setStopColor(SvgMeshPatch::Type type, const QColor &color)
bool isDivisibleHorizontally() const
void subdivide(QVector< SvgMeshPatch * > &subdivided, const QVector< QColor > &colors) const
void curveTo(const QPointF &c1, const QPointF &c2, const QPointF &p)
add points as curve.
SvgMeshStop getStop(Type type) const
returns the starting point of the stop
bool isDivisibleVertically() const
void lineTo(const QPointF &p)
Helper to convert to a cubic curve internally.
QPointF segmentPointAt(Type type, qreal t) const
get the point on a segment using De Casteljau's algorithm
std::array< QPointF, 4 > getSegment(Type type) const
Get a segment of the path in the meshpatch.
void modifyCorner(SvgMeshPatch::Type type, const QPointF &delta)
#define KIS_ASSERT(cond)
Definition kis_assert.h:33