Krita Source Code Documentation
Loading...
Searching...
No Matches
KisBezierMesh.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2020 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#ifndef KISBEZIERMESH_H
8#define KISBEZIERMESH_H
9
10#include <kritaglobal_export.h>
11
12#include <QDebug>
13
14#include <KisBezierUtils.h>
15#include <KisBezierPatch.h>
16
17#include <boost/iterator/iterator_facade.hpp>
18#include <boost/operators.hpp>
19
20#include <functional>
21#include "KisCppQuirks.h"
22
23#include "kis_debug.h"
24
25class QDomElement;
26
28
29struct BaseMeshNode : public boost::equality_comparable<BaseMeshNode> {
30 BaseMeshNode() = default;
31
32 BaseMeshNode(const QPointF &_node)
33 : leftControl(_node),
34 topControl(_node),
35 node(_node),
36 rightControl(_node),
37 bottomControl(_node)
38 {
39 }
40
41 bool operator==(const BaseMeshNode &rhs) const {
42 return leftControl == rhs.leftControl &&
43 topControl == rhs.topControl &&
44 node == rhs.node &&
47 }
48
49 void setLeftControlRelative(const QPointF &value) {
51 }
52
53 QPointF leftControlRelative() const {
54 return leftControl - node;
55 }
56
57 void setRightControlRelative(const QPointF &value) {
59 }
60
61 QPointF rightControlRelative() const {
62 return rightControl - node;
63 }
64
65 void setTopControlRelative(const QPointF &value) {
67 }
68
69 QPointF topControlRelative() const {
70 return topControl - node;
71 }
72
73 void setBottomControlRelative(const QPointF &value) {
75 }
76
77 QPointF bottomControlRelative() const {
78 return bottomControl - node;
79 }
80
81 void translate(const QPointF &offset) {
82 leftControl += offset;
83 topControl += offset;
84 node += offset;
85 rightControl += offset;
86 bottomControl += offset;
87 }
88
89 void transform(const QTransform &t) {
90 leftControl = t.map(leftControl);
91 topControl = t.map(topControl);
92 node = t.map(node);
95 }
96
97 QPointF leftControl;
98 QPointF topControl;
99 QPointF node;
102};
103
104inline void lerpNodeData(const BaseMeshNode &left, const BaseMeshNode &right, qreal t, BaseMeshNode &dst)
105{
106 Q_UNUSED(left);
107 Q_UNUSED(right);
108 Q_UNUSED(t);
109 Q_UNUSED(dst);
110}
111
113 const QRectF &srcRect,
114 const BaseMeshNode &tl,
115 const BaseMeshNode &tr,
116 const BaseMeshNode &bl,
117 const BaseMeshNode &br)
118{
119 patch->originalRect = srcRect;
120 Q_UNUSED(tl);
121 Q_UNUSED(tr);
122 Q_UNUSED(bl);
123 Q_UNUSED(br);
124}
125
126template<typename NodeArg = BaseMeshNode,
127 typename PatchArg = KisBezierPatch>
128class Mesh : public boost::equality_comparable<Mesh<NodeArg, PatchArg>>
129{
130public:
131 using Node = NodeArg;
132 using Patch = PatchArg;
133
134 struct PatchIndex : public QPoint, boost::additive<PatchIndex, QPoint>
135 {
136 using QPoint::QPoint;
137 PatchIndex& operator+=(const QPoint &rhs) {
138 QPoint::operator+=(rhs);
139 return *this;
140 }
141 PatchIndex& operator-=(const QPoint &rhs) {
142 QPoint::operator-=(rhs);
143 return *this;
144 }
145 };
146
147 struct NodeIndex : public QPoint, boost::additive<NodeIndex, QPoint>
148 {
149 using QPoint::QPoint;
150 NodeIndex& operator+=(const QPoint &rhs) {
151 QPoint::operator+=(rhs);
152 return *this;
153 }
154 NodeIndex& operator-=(const QPoint &rhs) {
155 QPoint::operator-=(rhs);
156 return *this;
157 }
158 };
159
160 using SegmentIndex = std::pair<NodeIndex, int>;
161
162 struct ControlPointIndex : public boost::equality_comparable<ControlPointIndex>
163 {
171
172 ControlPointIndex() = default;
173
174 ControlPointIndex(NodeIndex _nodeIndex, ControlType _controlType)
175 : nodeIndex(_nodeIndex),
176 controlType(_controlType)
177 {
178 }
179
182
183 inline bool isNode() const {
184 return controlType == Node;
185 }
186
187 inline bool isControlPoint() const {
188 return controlType != Node;
189 }
190
191 template <class NodeType,
192 class PointType = std::copy_const_t<NodeType, QPointF>>
193 static
194 PointType& controlPoint(NodeType &node, ControlType controlType) {
195 return
196 controlType == LeftControl ? node.leftControl :
197 controlType == RightControl ? node.rightControl :
198 controlType == TopControl ? node.topControl :
199 controlType == BottomControl ? node.bottomControl :
200 node.node;
201 }
202
205 }
206
207 friend bool operator==(const ControlPointIndex &lhs, const ControlPointIndex &rhs) {
208 return lhs.nodeIndex == rhs.nodeIndex && lhs.controlType == rhs.controlType;
209 }
210
211 friend QDebug operator<<(QDebug dbg, const Mesh::ControlPointIndex &index) {
212 dbg.nospace() << "ControlPointIndex ("
213 << index.nodeIndex.x() << ", " << index.nodeIndex.x() << ", ";
214
215 switch (index.controlType) {
216 case Mesh::ControlType::Node:
217 dbg.nospace() << "Node";
218 break;
219 case Mesh::ControlType::LeftControl:
220 dbg.nospace() << "LeftControl";
221 break;
222 case Mesh::ControlType::RightControl:
223 dbg.nospace() << "RightControl";
224 break;
225 case Mesh::ControlType::TopControl:
226 dbg.nospace() << "TopControl";
227 break;
228 case Mesh::ControlType::BottomControl:
229 dbg.nospace() << "BottomControl";
230 break;
231 }
232
233 dbg.nospace() << ")";
234 return dbg.space();
235 }
236 };
237
239
240private:
241 template<bool is_const>
243
244 template<bool is_const>
246
247 template<bool is_const>
249 public boost::iterator_facade <patch_iterator_impl<is_const>,
250 Patch,
251 boost::random_access_traversal_tag,
252 Patch>
253 {
258
259 public:
261 : m_mesh(0),
262 m_col(0),
263 m_row(0) {}
264
265 patch_iterator_impl(MeshType* mesh, int col, int row)
266 : m_mesh(mesh),
267 m_col(col),
268 m_row(row)
269 {
270 }
271
273 return {m_col, m_row};
274 }
275
280
285
286 bool isValid() const {
287 return
288 m_col >= 0 &&
289 m_col < m_mesh->size().width() - 1 &&
290 m_row >= 0 &&
291 m_row < m_mesh->size().height() - 1;
292 }
293
294 private:
296
297 void increment() {
298 m_col++;
299 if (m_col >= m_mesh->m_size.width() - 1) {
300 m_col = 0;
301 m_row++;
302 }
303 }
304
305 void decrement() {
306 m_col--;
307 if (m_col < 0) {
308 m_col = m_mesh->m_size.width() - 2;
309 m_row--;
310 }
311 }
312
313 void advance(int n) {
314 const int index = m_row * (m_mesh->m_size.width() - 1) + m_col + n;
315
316 m_row = index / (m_mesh->m_size.width() - 1);
317 m_col = index % (m_mesh->m_size.width() - 1);
318
319 KIS_SAFE_ASSERT_RECOVER_NOOP(m_row < m_mesh->m_size.height() - 1);
320 }
321
322 int distance_to(const patch_iterator_impl &z) const {
323 const int index = m_row * (m_mesh->m_size.width() - 1) + m_col;
324 const int otherIndex = z.m_row * (m_mesh->m_size.width() - 1) + z.m_col;
325
326 return otherIndex - index;
327 }
328
329 bool equal(patch_iterator_impl const& other) const {
330 return m_row == other.m_row &&
331 m_col == other.m_col &&
332 m_mesh == other.m_mesh;
333 }
334
336 return m_mesh->makePatch(m_col, m_row);
337 }
338
339 private:
340
342 int m_col;
343 int m_row;
344 };
345
346 template<bool is_const>
348 public boost::iterator_facade <control_point_iterator_impl<is_const>,
349 std::add_const_if_t<is_const, QPointF>,
350 boost::bidirectional_traversal_tag>
351 {
356
357 public:
359 : m_mesh(0),
360 m_col(0),
361 m_row(0),
363 {}
364
366 : m_mesh(mesh),
367 m_col(col),
368 m_row(row),
370 {
371 }
372
376
377 int col() const {
378 return m_col;
379 }
380
381 int row() const {
382 return m_row;
383 }
384
388
390 return Mesh::NodeIndex(m_col, m_row);
391 }
392
393 NodeType& node() const {
394 return m_mesh->node(m_col, m_row);
395 }
396
397 bool isLeftBorder() const {
398 return m_col == 0;
399 }
400
401 bool isRightBorder() const {
402 return m_col == m_mesh->size().width() - 1;
403 }
404
405 bool isTopBorder() const {
406 return m_row == 0;
407 }
408
409 bool isBottomBorder() const {
410 return m_row == m_mesh->size().height() - 1;
411 }
412
413
414 bool isBorderNode() const {
416 }
417
418 bool isCornerNode() const {
419 return (isLeftBorder() + isRightBorder() + isTopBorder() + isBottomBorder()) > 1;
420 }
421
422 bool isNode() const {
424 }
425
427 typename Mesh::ControlPointIndex::ControlType newIndex =
429
430 switch (type()) {
433 break;
436 break;
439 break;
442 break;
445 break;
446 }
447
449
450 if (!it.controlIsValid()) {
451 it = m_mesh->endControlPoints();
452 }
453
454 return it;
455 }
456
461
462 bool isValid() const {
463 return nodeIsValid() && controlIsValid();
464 }
465
466 private:
468
469 bool nodeIsValid() const {
470 return m_col >= 0 && m_row >= 0 && m_col < m_mesh->size().width() && m_row < m_mesh->size().height();
471 }
472
473 bool controlIsValid() const {
474 if (m_col == 0 && m_controlIndex == Mesh::ControlType::LeftControl) {
475 return false;
476 }
477
478 if (m_col == m_mesh->m_size.width() - 1 && m_controlIndex == Mesh::ControlType::RightControl) {
479 return false;
480 }
481
482 if (m_row == 0 && m_controlIndex == Mesh::ControlType::TopControl) {
483 return false;
484 }
485
486 if (m_row == m_mesh->m_size.height() - 1 && m_controlIndex == Mesh::ControlType::BottomControl) {
487 return false;
488 }
489
490 return true;
491 }
492
493 void increment() {
494 do {
496 if (m_controlIndex > 4) {
497 m_controlIndex = 0;
498 m_col++;
499 if (m_col >= m_mesh->m_size.width()) {
500 m_col = 0;
501 m_row++;
502 }
503 }
504 } while (nodeIsValid() && !controlIsValid());
505 }
506
507 void decrement() {
508 do {
510 if (m_controlIndex < 0) {
511 m_controlIndex = 4;
512 m_col--;
513 if (m_col < 0) {
514 m_col = m_mesh->m_size.width() - 1;
515 m_row--;
516 }
517 }
518 } while (nodeIsValid() && !controlIsValid());
519 }
520
521
522 bool equal(control_point_iterator_impl const& other) const {
523 return m_controlIndex == other.m_controlIndex &&
524 m_row == other.m_row &&
525 m_col == other.m_col &&
526 m_mesh == other.m_mesh;
527 }
528
532
533 private:
535 int m_col;
536 int m_row;
538 };
539
540 template<bool is_const>
542 public boost::iterator_facade <segment_iterator_impl<is_const>,
543 Mesh::SegmentIndex,
544 boost::bidirectional_traversal_tag,
545 Mesh::SegmentIndex>
546 {
551
552 public:
554 : m_mesh(0),
555 m_col(0),
556 m_row(0),
558 {}
559
560 segment_iterator_impl(MeshType* mesh, int col, int row, int isHorizontal)
561 : m_mesh(mesh),
562 m_col(col),
563 m_row(row),
565 {
566 }
567
569 return
572 }
573
577
581
583 return m_mesh->node(firstNodeIndex());
584 }
585
587 return m_mesh->node(secondNodeIndex());
588 }
589
590 PointType& p0() const {
591 return firstNode().node;
592 }
593
594 PointType& p1() const {
595 return m_isHorizontal ? firstNode().rightControl : firstNode().bottomControl;
596 }
597
598 PointType& p2() const {
599 return m_isHorizontal ? secondNode().leftControl : secondNode().topControl;
600 }
601
602 PointType& p3() const {
603 return secondNode().node;
604 }
605
607 return m_mesh->find(ControlPointIndex(firstNodeIndex(), Mesh::ControlType::Node));
608 }
609
613 Mesh::ControlType::RightControl :
614 Mesh::ControlType::BottomControl));
615 }
616
620 Mesh::ControlType::LeftControl :
621 Mesh::ControlType::TopControl));
622 }
623
625 return m_mesh->find(ControlPointIndex(secondNodeIndex(), Mesh::ControlType::Node));
626 }
627
628 QPointF pointAtParam(qreal t) const {
629 return KisBezierUtils::bezierCurve(p0(), p1(), p2(), p3(), t);
630 }
631
632 qreal length() const {
633 const qreal eps = 1e-3;
634 return KisBezierUtils::curveLength(p0(), p1(), p2(), p3(), eps);
635 }
636
637 int degree() const {
638 return KisBezierUtils::bezierDegree(p0(), p1(), p2(), p3());
639 }
640
641 bool isHorizontal() const {
642 return m_isHorizontal;
643 }
644
645 bool isValid() const {
646 return nodeIsValid() && controlIsValid();
647 }
648
649 private:
651
652 bool nodeIsValid() const {
653 return m_col >= 0 && m_row >= 0 && m_col < m_mesh->size().width() && m_row < m_mesh->size().height();
654 }
655
656 bool controlIsValid() const {
657 if (m_col == m_mesh->m_size.width() - 1 && m_isHorizontal) {
658 return false;
659 }
660
661 if (m_row == m_mesh->m_size.height() - 1 && !m_isHorizontal) {
662 return false;
663 }
664
665 return true;
666 }
667
668 void increment() {
669 do {
671 if (m_isHorizontal > 1) {
672 m_isHorizontal = 0;
673 m_col++;
674 if (m_col >= m_mesh->m_size.width()) {
675 m_col = 0;
676 m_row++;
677 }
678 }
679 } while (nodeIsValid() && !controlIsValid());
680 }
681
682 void decrement() {
683 do {
685 if (m_isHorizontal < 0) {
686 m_isHorizontal = 1;
687 m_col--;
688 if (m_col < 0) {
689 m_col = m_mesh->m_size.width() - 1;
690 m_row--;
691 }
692 }
693 } while (nodeIsValid() && !controlIsValid());
694 }
695
696
697 bool equal(segment_iterator_impl const& other) const {
698 return m_isHorizontal == other.m_isHorizontal &&
699 m_row == other.m_row &&
700 m_col == other.m_col &&
701 m_mesh == other.m_mesh;
702 }
703
705 return segmentIndex();
706 }
707
708 private:
709
711 int m_col;
712 int m_row;
714 };
715
716public:
718 : Mesh(QRectF(0.0, 0.0, 1.0, 1.0))
719 {
720 }
721
722 Mesh(const QRectF &mapRect, const QSize &size = QSize(2,2))
723 : m_size(size),
724 m_originalRect(mapRect)
725 {
726 const qreal xControlOffset = 0.2 * (mapRect.width() / size.width());
727 const qreal yControlOffset = 0.2 * (mapRect.height() / size.height());
728
729 for (int row = 0; row < m_size.height(); row++) {
730 const qreal yPos = qreal(row) / (size.height() - 1) * mapRect.height() + mapRect.y();
731
732 for (int col = 0; col < m_size.width(); col++) {
733 const qreal xPos = qreal(col) / (size.width() - 1) * mapRect.width() + mapRect.x();
734
735 Node node(QPointF(xPos, yPos));
736 node.setLeftControlRelative(QPointF(-xControlOffset, 0));
737 node.setRightControlRelative(QPointF(xControlOffset, 0));
738 node.setTopControlRelative(QPointF(0, -yControlOffset));
739 node.setBottomControlRelative(QPointF(0, yControlOffset));
740
741 m_nodes.push_back(node);
742 }
743 }
744
745 for (int col = 0; col < m_size.width(); col++) {
746 m_columns.push_back(qreal(col) / (size.width() - 1));
747 }
748
749 for (int row = 0; row < m_size.height(); row++) {
750 m_rows.push_back(qreal(row) / (size.height() - 1));
751 }
752 }
753
754 bool operator==(const Mesh &rhs) const {
755 return m_size == rhs.m_size &&
756 m_rows == rhs.m_rows &&
757 m_columns == rhs.m_columns &&
759 m_nodes == rhs.m_nodes;
760 }
761
762 Node& node(int col, int row) {
763 KIS_ASSERT(col >= 0 && col < m_size.width() && row >= 0 && row < m_size.height());
764 return m_nodes[row * m_size.width() + col];
765 }
766
767 const Node& node(int col, int row) const {
768 KIS_ASSERT(col >= 0 && col < m_size.width() && row >= 0 && row < m_size.height());
769 return m_nodes[row * m_size.width() + col];
770 }
771
772 Node& node(const NodeIndex &index) {
773 return node(index.x(), index.y());
774 }
775
776 const Node& node(const NodeIndex &index) const {
777 return node(index.x(), index.y());
778 }
779
780
781 int subdivideRow(qreal proportionalT) {
782 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(proportionalT >= 0.0 && proportionalT <= 1.0, -1);
783
784 {
785 auto existing = std::find(m_rows.begin(), m_rows.end(), proportionalT);
786 if (existing != m_rows.end()) {
787 return distance(m_rows.begin(), existing);
788 }
789 }
790
791 const auto it = prev(upper_bound(m_rows.begin(), m_rows.end(), proportionalT));
792 const int topRow = distance(m_rows.begin(), it);
793 const qreal relT = (proportionalT - *it) / (*next(it) - *it);
794
795 return subdivideRow(topRow, relT);
796 }
797
798 int subdivideRow(int topRow, qreal relProportionalT) {
799 const auto it = m_rows.begin() + topRow;
800 const int bottomRow = topRow + 1;
801 const qreal absProportionalT = KisAlgebra2D::lerp(*it, *next(it), relProportionalT);
802
803 std::vector<Node> newRow;
804 newRow.resize(m_size.width());
805 for (int col = 0; col < m_size.width(); col++) {
806 const qreal t =
808 node(col, topRow).bottomControl,
809 node(col, bottomRow).topControl,
810 node(col, bottomRow).node,
811 relProportionalT,
812 0.01);
813
814 splitCurveVertically(node(col, topRow), node(col, bottomRow), t, newRow[col]);
815 }
816
817 m_nodes.insert(m_nodes.begin() + bottomRow * m_size.width(),
818 newRow.begin(), newRow.end());
819
820 m_size.rheight()++;
821 auto insertedIt = m_rows.insert(next(it), absProportionalT);
822 return distance(m_rows.begin(), insertedIt);
823 }
824
825 int subdivideColumn(qreal proportionalT) {
826 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(proportionalT >= 0.0 && proportionalT <= 1.0, -1);
827
828 {
829 auto existing = std::find(m_columns.begin(), m_columns.end(), proportionalT);
830 if (existing != m_columns.end()) {
831 return distance(m_columns.begin(), existing);
832 }
833 }
834
835 const auto it = prev(upper_bound(m_columns.begin(), m_columns.end(), proportionalT));
836 const int leftColumn = distance(m_columns.begin(), it);
837
838 const qreal relT = (proportionalT - *it) / (*next(it) - *it);
839
840 return subdivideColumn(leftColumn, relT);
841 }
842
843 int subdivideColumn(int leftColumn, qreal relProportionalT) {
844 const auto it = m_columns.begin() + leftColumn;
845 const int rightColumn = leftColumn + 1;
846 const qreal absProportionalT = KisAlgebra2D::lerp(*it, *next(it), relProportionalT);
847
848 std::vector<Node> newColumn;
849 newColumn.resize(m_size.height());
850 for (int row = 0; row < m_size.height(); row++) {
851 const qreal t =
853 node(leftColumn, row).rightControl,
854 node(rightColumn, row).leftControl,
855 node(rightColumn, row).node,
856 relProportionalT,
857 0.01);
858
859 splitCurveHorizontally(node(leftColumn, row), node(rightColumn, row), t, newColumn[row]);
860 }
861
862 auto dstIt = m_nodes.begin() + rightColumn;
863 for (auto columnIt = newColumn.begin(); columnIt != newColumn.end(); ++columnIt) {
864 dstIt = m_nodes.insert(dstIt, *columnIt);
865 dstIt += m_size.width() + 1;
866 }
867
868 m_size.rwidth()++;
869 auto insertedIt = m_columns.insert(next(it), absProportionalT);
870 return distance(m_columns.begin(), insertedIt);
871 }
872
873 void removeColumn(int column) {
874 const bool hasNeighbours = column > 0 || column < m_size.width() - 1;
875
876 if (hasNeighbours) {
877 for (int row = 0; row < m_size.height(); row++) {
878 unlinkNodeHorizontally(node(column - 1, row), node(column, row), node(column + 1, row));
879 }
880 }
881
882 auto it = m_nodes.begin() + column;
883 for (int row = 0; row < m_size.height(); row++) {
884 it = m_nodes.erase(it);
885 it += m_size.width() - 1;
886 }
887
888 m_size.rwidth()--;
889 m_columns.erase(m_columns.begin() + column);
890 }
891
892 void removeRow(int row) {
893 const bool hasNeighbours = row > 0 || row < m_size.height() - 1;
894
895 if (hasNeighbours) {
896 for (int column = 0; column < m_size.width(); column++) {
897 unlinkNodeVertically(node(column, row - 1), node(column, row), node(column, row + 1));
898 }
899 }
900
901 auto it = m_nodes.begin() + row * m_size.width();
902 m_nodes.erase(it, it + m_size.width());
903
904 m_size.rheight()--;
905 m_rows.erase(m_rows.begin() + row);
906 }
907
908 void removeColumnOrRow(NodeIndex index, bool row) {
909 if (row) {
910 removeRow(index.y());
911 } else {
912 removeColumn(index.x());
913 }
914 }
915
916 void subdivideSegment(SegmentIndex index, qreal proportionalT) {
917 auto it = find(index);
919
920 if (it.isHorizontal()) {
921 subdivideColumn(it.firstNodeIndex().x(), proportionalT);
922 } else {
923 subdivideRow(it.firstNodeIndex().y(), proportionalT);
924 }
925 }
926
927 Patch makePatch(const PatchIndex &index) const {
928 return makePatch(index.x(), index.y());
929 }
930
931 Patch makePatch(int col, int row) const
932 {
933 const Node &tl = node(col, row);
934 const Node &tr = node(col + 1, row);
935 const Node &bl = node(col, row + 1);
936 const Node &br = node(col + 1, row + 1);
937
938 Patch patch;
939
940 patch.points[Patch::TL] = tl.node;
941 patch.points[Patch::TL_HC] = tl.rightControl;
942 patch.points[Patch::TL_VC] = tl.bottomControl;
943
944 patch.points[Patch::TR] = tr.node;
945 patch.points[Patch::TR_HC] = tr.leftControl;
946 patch.points[Patch::TR_VC] = tr.bottomControl;
947
948 patch.points[Patch::BL] = bl.node;
949 patch.points[Patch::BL_HC] = bl.rightControl;
950 patch.points[Patch::BL_VC] = bl.topControl;
951
952 patch.points[Patch::BR] = br.node;
953 patch.points[Patch::BR_HC] = br.leftControl;
954 patch.points[Patch::BR_VC] = br.topControl;
955
956 const QRectF relRect(m_columns[col],
957 m_rows[row],
958 m_columns[col + 1] - m_columns[col],
959 m_rows[row + 1] - m_rows[row]);
960
962 tl, tr, bl, br);
963
964 return patch;
965 }
966
969
972
975
979
981 patch_const_iterator endPatches() const { return endPatches(*this); }
983
987
991
995
999
1000 control_point_iterator find(const ControlPointIndex &index) { return find(*this, index); }
1001 control_point_const_iterator find(const ControlPointIndex &index) const { return find(*this, index); }
1002 control_point_const_iterator constFind(const ControlPointIndex &index) const { return find(*this, index); }
1003
1004 control_point_iterator find(const NodeIndex &index) { return find(*this, index); }
1005 control_point_const_iterator find(const NodeIndex &index) const { return find(*this, index); }
1006 control_point_const_iterator constFind(const NodeIndex &index) const { return find(*this, index); }
1007
1008 segment_iterator find(const SegmentIndex &index) { return find(*this, index); }
1009 segment_const_iterator find(const SegmentIndex &index) const { return find(*this, index); }
1010 segment_const_iterator constFind(const SegmentIndex &index) const { return find(*this, index); }
1011
1012 patch_iterator find(const PatchIndex &index) { return find(*this, index); }
1013 patch_const_iterator find(const PatchIndex &index) const { return find(*this, index); }
1014 patch_const_iterator constFind(const PatchIndex &index) const { return find(*this, index); }
1015
1016 QSize size() const {
1017 return m_size;
1018 }
1019
1020 QRectF originalRect() const {
1021 return m_originalRect;
1022 }
1023
1024 QRectF dstBoundingRect() const {
1025 QRectF result;
1026 for (auto it = beginPatches(); it != endPatches(); ++it) {
1027 result |= it->dstBoundingRect();
1028 }
1029 return result;
1030 }
1031
1032 bool isIdentity() const {
1033 Mesh identityMesh(m_originalRect, m_size);
1034 return *this == identityMesh;
1035 }
1036
1037 void translate(const QPointF &offset) {
1038 for (auto it = m_nodes.begin(); it != m_nodes.end(); ++it) {
1039 it->translate(offset);
1040 }
1041 }
1042
1043 void transform(const QTransform &t) {
1044 for (auto it = m_nodes.begin(); it != m_nodes.end(); ++it) {
1045 it->transform(t);
1046 }
1047 }
1048
1049 void transformSrcAndDst(const QTransform &t) {
1050 KIS_SAFE_ASSERT_RECOVER_RETURN(t.type() <= QTransform::TxScale);
1051 transform(t);
1052 m_originalRect = t.mapRect(m_originalRect);
1053 }
1054
1055 ControlPointIndex hitTestNode(const QPointF &pt, qreal distanceThreshold) const {
1056 return hitTestPointImpl(pt, distanceThreshold, true);
1057 }
1058
1059 ControlPointIndex hitTestControlPoint(const QPointF &pt, qreal distanceThreshold) const {
1060 return hitTestPointImpl(pt, distanceThreshold, false);
1061 }
1062
1063 SegmentIndex hitTestSegment(const QPointF &pt, qreal distanceThreshold, qreal *t = 0) const {
1064 auto result = endSegments();
1065 qreal minDistance = std::numeric_limits<qreal>::max();
1066
1067 for (auto it = beginSegments(); it != endSegments(); ++it) {
1068
1069 qreal foundDistance = 0.0;
1070 const qreal foundT = KisBezierUtils::nearestPoint({it.p0(), it.p1(), it.p2(), it.p3()}, pt, &foundDistance);
1071
1072 if (foundDistance < minDistance && foundDistance < distanceThreshold) {
1073 result = it;
1074 minDistance = foundDistance;
1075 if (t) {
1076 *t = foundT;
1077 }
1078 }
1079 }
1080
1081 return result.segmentIndex();
1082 }
1083
1084 template <typename T>
1085 bool isIndexValid(const T &index) const {
1086 return find(index).isValid();
1087 }
1088
1089 void reshapeMeshHorizontally(int numColumns) {
1090 KIS_SAFE_ASSERT_RECOVER_RETURN(numColumns >= 2);
1091
1092 std::vector<int> insertedIndexes;
1093
1094 for (int i = 1; i < numColumns - 1; i++) {
1095 const qreal pos = qreal(i) / (numColumns - 1);
1096 int inserted = subdivideColumn(pos);
1097 KIS_SAFE_ASSERT_RECOVER(inserted >= 0) { continue; }
1098
1099 insertedIndexes.push_back(inserted);
1100 }
1101
1102 for (int i = m_columns.size() - 2; i >= 1; i--) {
1103 if (std::find(insertedIndexes.begin(), insertedIndexes.end(), i) == insertedIndexes.end()) {
1104 removeColumn(i);
1105 }
1106 }
1107 }
1108
1109 void reshapeMeshVertically(int numRows) {
1110 KIS_SAFE_ASSERT_RECOVER_RETURN(numRows >= 2);
1111
1112 std::vector<int> insertedIndexes;
1113
1114 for (int i = 1; i < numRows - 1; i++) {
1115 const qreal pos = qreal(i) / (numRows - 1);
1116 int inserted = subdivideRow(pos);
1117 KIS_SAFE_ASSERT_RECOVER(inserted >= 0) { continue; }
1118
1119 insertedIndexes.push_back(inserted);
1120 }
1121
1122 for (int i = m_rows.size() - 2; i >= 1; i--) {
1123 if (std::find(insertedIndexes.begin(), insertedIndexes.end(), i) == insertedIndexes.end()) {
1124 removeRow(i);
1125 }
1126 }
1127 }
1128
1129private:
1130 void splitCurveHorizontally(Node &left, Node &right, qreal t, Node &newNode) {
1132 using KisAlgebra2D::lerp;
1133
1134 QPointF p1, p2, p3, q1, q2;
1135
1136 deCasteljau(left.node, left.rightControl, right.leftControl, right.node, t,
1137 &p1, &p2, &p3, &q1, &q2);
1138
1139 left.rightControl = p1;
1140 newNode.leftControl = p2;
1141 newNode.node = p3;
1142 newNode.rightControl = q1;
1143 right.leftControl = q2;
1144
1145 newNode.topControl = newNode.node + lerp(left.topControl - left.node, right.topControl - right.node, t);
1146 newNode.bottomControl = newNode.node + lerp(left.bottomControl - left.node, right.bottomControl - right.node, t);
1147
1148 lerpNodeData(left, right, t, newNode);
1149 }
1150
1151 void splitCurveVertically(Node &top, Node &bottom, qreal t, Node &newNode) {
1153 using KisAlgebra2D::lerp;
1154
1155 QPointF p1, p2, p3, q1, q2;
1156
1157 deCasteljau(top.node, top.bottomControl, bottom.topControl, bottom.node, t,
1158 &p1, &p2, &p3, &q1, &q2);
1159
1160 top.bottomControl = p1;
1161 newNode.topControl = p2;
1162 newNode.node = p3;
1163 newNode.bottomControl = q1;
1164 bottom.topControl = q2;
1165
1166 newNode.leftControl = newNode.node + lerp(top.leftControl - top.node, bottom.leftControl - bottom.node, t);
1167 newNode.rightControl = newNode.node + lerp(top.rightControl - top.node, bottom.rightControl - bottom.node, t);
1168
1169 lerpNodeData(top, bottom, t, newNode);
1170 }
1171
1173 {
1174 std::tie(left.rightControl, right.leftControl) =
1175 KisBezierUtils::removeBezierNode(left.node, left.rightControl,
1176 node.leftControl, node.node, node.rightControl,
1177 right.leftControl, right.node);
1178 }
1179
1181 {
1182 std::tie(top.bottomControl, bottom.topControl) =
1183 KisBezierUtils::removeBezierNode(top.node, top.bottomControl,
1184 node.topControl, node.node, node.bottomControl,
1185 bottom.topControl, bottom.node);
1186 }
1187
1188 ControlPointIndex hitTestPointImpl(const QPointF &pt, qreal distanceThreshold, bool onlyNodeMode) const {
1189 const qreal distanceThresholdSq = pow2(distanceThreshold);
1190
1191 auto result = endControlPoints();
1192 qreal minDistanceSq = std::numeric_limits<qreal>::max();
1193
1194 for (auto it = beginControlPoints(); it != endControlPoints(); ++it) {
1195 if (onlyNodeMode != (it.type() == ControlType::Node)) continue;
1196
1197 const qreal distSq = kisSquareDistance(*it, pt);
1198 if (distSq < minDistanceSq && distSq < distanceThresholdSq) {
1199 result = it;
1200 minDistanceSq = distSq;
1201 }
1202 }
1203
1204 return result.controlIndex();
1205 }
1206
1207private:
1208 template <class MeshType,
1209 class IteratorType = control_point_iterator_impl<std::is_const<MeshType>::value>>
1210 static
1211 IteratorType find(MeshType &mesh, const ControlPointIndex &index) {
1212 IteratorType it(&mesh, index.nodeIndex.x(), index.nodeIndex.y(), index.controlType);
1213 return it.isValid() ? it : mesh.endControlPoints();
1214 }
1215
1216 template <class MeshType,
1217 class IteratorType = control_point_iterator_impl<std::is_const<MeshType>::value>>
1218 static
1219 IteratorType find(MeshType &mesh, const NodeIndex &index) {
1220 IteratorType it(&mesh, index.x(), index.y(), Mesh::ControlType::Node);
1221 return it.isValid() ? it : mesh.endControlPoints();
1222 }
1223
1224 template <class MeshType,
1225 class IteratorType = segment_iterator_impl<std::is_const<MeshType>::value>>
1226 static
1227 IteratorType find(MeshType &mesh, const SegmentIndex &index) {
1228 IteratorType it(&mesh, index.first.x(), index.first.y(), index.second);
1229 return it.isValid() ? it : mesh.endSegments();
1230 }
1231
1232 template <class MeshType,
1233 class IteratorType = patch_iterator_impl<std::is_const<MeshType>::value>>
1234 static
1235 IteratorType find(MeshType &mesh, const PatchIndex &index) {
1236 IteratorType it(&mesh, index.x(), index.y());
1237 return it.isValid() ? it : mesh.endPatches();
1238 }
1239
1240 template <class MeshType,
1241 class IteratorType = patch_iterator_impl<std::is_const<MeshType>::value>>
1242 static
1243 IteratorType beginPatches(MeshType &mesh) {
1244 return IteratorType(&mesh, 0, 0);
1245 }
1246
1247 template <class MeshType,
1248 class IteratorType = patch_iterator_impl<std::is_const<MeshType>::value>>
1249 static
1250 IteratorType endPatches(MeshType &mesh) {
1251 return IteratorType(&mesh, 0, mesh.m_size.height() - 1);
1252 }
1253
1254 template <class MeshType,
1255 class IteratorType = control_point_iterator_impl<std::is_const<MeshType>::value>>
1256 static
1257 IteratorType beginControlPoints(MeshType &mesh) {
1258 return IteratorType(&mesh, 0, 0, ControlType::RightControl);
1259 }
1260
1261 template <class MeshType,
1262 class IteratorType = control_point_iterator_impl<std::is_const<MeshType>::value>>
1263 static
1264 IteratorType endControlPoints(MeshType &mesh) {
1265 return IteratorType(&mesh, 0, mesh.m_size.height(), 0);
1266 }
1267
1268 template <class MeshType,
1269 class IteratorType = segment_iterator_impl<std::is_const<MeshType>::value>>
1270 static
1271 IteratorType beginSegments(MeshType &mesh) {
1272 return IteratorType(&mesh, 0, 0, 0);
1273 }
1274
1275 template <class MeshType,
1276 class IteratorType = segment_iterator_impl<std::is_const<MeshType>::value>>
1277 static
1278 IteratorType endSegments(MeshType &mesh) {
1279 return IteratorType(&mesh, 0, mesh.m_size.height(), 0);
1280 }
1281
1282protected:
1283
1284 std::vector<Node> m_nodes;
1285 std::vector<qreal> m_rows;
1286 std::vector<qreal> m_columns;
1287
1288 QSize m_size;
1290};
1291
1292template<typename Node, typename Patch>
1293QDebug operator<<(QDebug dbg, const Mesh<Node, Patch> &mesh)
1294{
1295 dbg.nospace() << "Mesh " << mesh.size() << "\n";
1296
1297 for (int y = 0; y < mesh.size().height(); y++) {
1298 for (int x = 0; x < mesh.size().width(); x++) {
1299 dbg.nospace() << " node(" << x << ", " << y << ") " << mesh.node(x, y) << "\n";
1300 }
1301 }
1302 return dbg.space();
1303}
1304
1305template<typename NodeArg, typename PatchArg>
1306template<bool is_const>
1307typename Mesh<NodeArg, PatchArg>::template patch_iterator_impl<is_const>::SegmentIteratorType
1313
1314template<typename NodeArg, typename PatchArg>
1315template<bool is_const>
1322
1323template<typename NodeArg, typename PatchArg>
1324template<bool is_const>
1331
1332template<typename NodeArg, typename PatchArg>
1333template<bool is_const>
1340
1341template<typename NodeArg, typename PatchArg>
1342template<bool is_const>
1349
1350template<typename NodeArg, typename PatchArg>
1351template<bool is_const>
1358
1359template<typename NodeArg, typename PatchArg>
1360template<bool is_const>
1367
1368template<typename NodeArg, typename PatchArg>
1369template<bool is_const>
1376
1377
1378template<typename NodeArg, typename PatchArg>
1379template<bool is_const>
1382{
1384
1385 if (isTopBorder()) {
1386 return m_mesh->endSegments();
1387 }
1388
1389 return SegmentIteratorType(m_mesh, m_col, m_row - 1, false);
1390}
1391
1392template<typename NodeArg, typename PatchArg>
1393template<bool is_const>
1396{
1398
1399 if (isBottomBorder()) {
1400 return m_mesh->endSegments();
1401 }
1402
1403 return SegmentIteratorType(m_mesh, m_col, m_row, false);
1404}
1405
1406template<typename NodeArg, typename PatchArg>
1407template<bool is_const>
1410{
1412
1413 if (isLeftBorder()) {
1414 return m_mesh->endSegments();
1415 }
1416
1417 return SegmentIteratorType(m_mesh, m_col - 1, m_row, true);
1418}
1419
1420template<typename NodeArg, typename PatchArg>
1421template<bool is_const>
1424{
1426
1427 if (isRightBorder()) {
1428 return m_mesh->endSegments();
1429 }
1430
1431 return SegmentIteratorType(m_mesh, m_col, m_row, true);
1432}
1433
1439
1440template<typename NodeArg, typename PatchArg>
1443 const QPointF &move,
1445 bool scaleNodeMoves)
1446{
1447 using ControlType = typename Mesh<NodeArg, PatchArg>::ControlType;
1448 using ControlPointIndex = typename Mesh<NodeArg, PatchArg>::ControlPointIndex;
1449// using ControlPointIterator = typename Mesh<NodeArg, PatchArg>::control_point_iterator;
1450 using SegmentIterator = typename Mesh<NodeArg, PatchArg>::segment_iterator;
1451
1452 auto it = mesh.find(index);
1454
1455 if (it.isNode()) {
1456 auto preAdjustSegment = [] (Mesh<NodeArg, PatchArg> &mesh,
1457 SegmentIterator it,
1458 const QPointF &normalizedOffset) {
1459
1460 if (it == mesh.endSegments()) return;
1461
1462 const QPointF base1 = it.p3() - it.p0();
1463 const QPointF base2 = it.p3() - it.p0() - normalizedOffset;
1464
1465 {
1466 const QPointF control = it.p1() - it.p0();
1467 const qreal dist0 = KisAlgebra2D::norm(base1);
1468 const qreal dist1 = KisAlgebra2D::dotProduct(base2, base1) / dist0;
1469 const qreal coeff = dist1 / dist0;
1470
1471 it.p1() = it.p0() + coeff * (control);
1472 }
1473 {
1474 const QPointF control = it.p2() - it.p3();
1475 const qreal dist0 = KisAlgebra2D::norm(base1);
1476 const qreal dist1 = KisAlgebra2D::dotProduct(base2, base1) / dist0;
1477 const qreal coeff = dist1 / dist0;
1478
1479 it.p2() = it.p3() + coeff * (control);
1480 }
1481 };
1482
1483 if (scaleNodeMoves) {
1484 preAdjustSegment(mesh, it.topSegment(), -move);
1485 preAdjustSegment(mesh, it.leftSegment(), -move);
1486 preAdjustSegment(mesh, it.bottomSegment(), move);
1487 preAdjustSegment(mesh, it.rightSegment(), move);
1488 }
1489
1490 it.node().translate(move);
1491
1492 } else {
1493 const QPointF newPos = *it + move;
1494
1495 if (mode == MoveRotationLock || mode == MoveSymmetricLock) {
1496 const qreal rotation = KisAlgebra2D::angleBetweenVectors(*it - it.node().node,
1497 newPos - it.node().node);
1498 QTransform R;
1499 R.rotateRadians(rotation);
1500
1501 const QTransform t =
1502 QTransform::fromTranslate(-it.node().node.x(), -it.node().node.y()) *
1503 R *
1504 QTransform::fromTranslate(it.node().node.x(), it.node().node.y());
1505
1506 if (mode == MoveRotationLock) {
1507 for (int intType = 0; intType < 4; intType++) {
1508 ControlType type = static_cast<ControlType>(intType);
1509
1510 if (type == ControlType::Node ||
1511 type == index.controlType) {
1512
1513 continue;
1514 }
1515
1516 auto neighbourIt = mesh.find(ControlPointIndex(index.nodeIndex, type));
1517 if (neighbourIt == mesh.endControlPoints()) continue;
1518
1519 *neighbourIt = t.map(*neighbourIt);
1520 }
1521 } else {
1522 auto neighbourIt = it.symmetricControl();
1523 if (neighbourIt != mesh.endControlPoints()) {
1524 *neighbourIt = t.map(*neighbourIt);
1525 }
1526 }
1527 }
1528
1529 *it = newPos;
1530 }
1531}
1532
1533KRITAGLOBAL_EXPORT
1534QDebug operator<<(QDebug dbg, const BaseMeshNode &n);
1535
1536KRITAGLOBAL_EXPORT
1537void saveValue(QDomElement *parent, const QString &tag, const BaseMeshNode &node);
1538
1539KRITAGLOBAL_EXPORT
1540bool loadValue(const QDomElement &parent, BaseMeshNode *node);
1541
1542}
1543
1544namespace KisDomUtils {
1547}
1548
1549template <typename Node, typename Patch>
1551
1554
1555
1556#endif // KISBEZIERMESH_H
float value(const T *src, size_t ch)
QPointF q1
Eigen::Matrix< double, 4, 2 > R
QPointF p2
QPointF q2
QPointF p3
QPointF p1
qreal distance(const QPointF &p1, const QPointF &p2)
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::add_const_if_t< is_const, Node > NodeType
bool equal(control_point_iterator_impl const &other) const
segment_iterator_impl< is_const > SegmentIteratorType
control_point_iterator_impl(MeshType *mesh, int col, int row, int controlIndex)
std::add_const_if_t< is_const, Mesh > MeshType
control_point_iterator_impl symmetricControl() const
std::add_const_if_t< is_const, QPointF > PointType
patch_iterator_impl(MeshType *mesh, int col, int row)
ControlPointIteratorType nodeTopLeft() const
std::add_const_if_t< is_const, QPointF > PointType
segment_iterator_impl< is_const > SegmentIteratorType
ControlPointIteratorType nodeBottomLeft() const
control_point_iterator_impl< is_const > ControlPointIteratorType
int distance_to(const patch_iterator_impl &z) const
ControlPointIteratorType nodeBottomRight() const
ControlPointIteratorType nodeTopRight() const
bool equal(patch_iterator_impl const &other) const
std::add_const_if_t< is_const, Mesh > MeshType
segment_iterator_impl(MeshType *mesh, int col, int row, int isHorizontal)
std::add_const_if_t< is_const, Mesh > MeshType
bool equal(segment_iterator_impl const &other) const
std::add_const_if_t< is_const, QPointF > PointType
std::add_const_if_t< is_const, Node > NodeType
control_point_const_iterator find(const NodeIndex &index) const
segment_const_iterator find(const SegmentIndex &index) const
typename ControlPointIndex::ControlType ControlType
void removeColumnOrRow(NodeIndex index, bool row)
Patch makePatch(int col, int row) const
const Node & node(int col, int row) const
patch_const_iterator constBeginPatches() const
Mesh(const QRectF &mapRect, const QSize &size=QSize(2, 2))
control_point_iterator find(const NodeIndex &index)
segment_iterator find(const SegmentIndex &index)
patch_const_iterator endPatches() const
static IteratorType beginPatches(MeshType &mesh)
control_point_const_iterator constFind(const ControlPointIndex &index) const
segment_const_iterator constFind(const SegmentIndex &index) const
void unlinkNodeVertically(Mesh::Node &top, const Mesh::Node &node, Mesh::Node &bottom)
segment_const_iterator constEndSegments() const
std::vector< qreal > m_rows
patch_iterator find(const PatchIndex &index)
static IteratorType find(MeshType &mesh, const ControlPointIndex &index)
std::pair< NodeIndex, int > SegmentIndex
patch_iterator endPatches()
void reshapeMeshVertically(int numRows)
control_point_const_iterator constEndControlPoints() const
void splitCurveHorizontally(Node &left, Node &right, qreal t, Node &newNode)
void subdivideSegment(SegmentIndex index, qreal proportionalT)
std::vector< qreal > m_columns
ControlPointIndex hitTestControlPoint(const QPointF &pt, qreal distanceThreshold) const
segment_const_iterator endSegments() const
void transform(const QTransform &t)
void unlinkNodeHorizontally(Mesh::Node &left, const Mesh::Node &node, Mesh::Node &right)
patch_iterator beginPatches()
static IteratorType endPatches(MeshType &mesh)
ControlPointIndex hitTestNode(const QPointF &pt, qreal distanceThreshold) const
segment_const_iterator beginSegments() const
control_point_iterator find(const ControlPointIndex &index)
control_point_const_iterator endControlPoints() const
control_point_const_iterator beginControlPoints() const
segment_const_iterator constBeginSegments() const
bool isIndexValid(const T &index) const
patch_const_iterator constEndPatches() const
static IteratorType find(MeshType &mesh, const PatchIndex &index)
bool operator==(const Mesh &rhs) const
SegmentIndex hitTestSegment(const QPointF &pt, qreal distanceThreshold, qreal *t=0) const
int subdivideRow(int topRow, qreal relProportionalT)
static IteratorType beginSegments(MeshType &mesh)
control_point_const_iterator constBeginControlPoints() const
void transformSrcAndDst(const QTransform &t)
control_point_iterator beginControlPoints()
std::vector< Node > m_nodes
int subdivideColumn(int leftColumn, qreal relProportionalT)
patch_const_iterator beginPatches() const
void translate(const QPointF &offset)
const Node & node(const NodeIndex &index) const
void removeColumn(int column)
static IteratorType endControlPoints(MeshType &mesh)
patch_const_iterator constFind(const PatchIndex &index) const
Patch makePatch(const PatchIndex &index) const
control_point_const_iterator find(const ControlPointIndex &index) const
control_point_iterator endControlPoints()
control_point_const_iterator constFind(const NodeIndex &index) const
ControlPointIndex hitTestPointImpl(const QPointF &pt, qreal distanceThreshold, bool onlyNodeMode) const
void reshapeMeshHorizontally(int numColumns)
static IteratorType beginControlPoints(MeshType &mesh)
void splitCurveVertically(Node &top, Node &bottom, qreal t, Node &newNode)
Node & node(int col, int row)
segment_iterator endSegments()
segment_iterator beginSegments()
Node & node(const NodeIndex &index)
patch_const_iterator find(const PatchIndex &index) const
static IteratorType endSegments(MeshType &mesh)
int subdivideRow(qreal proportionalT)
static IteratorType find(MeshType &mesh, const SegmentIndex &index)
int subdivideColumn(qreal proportionalT)
static IteratorType find(MeshType &mesh, const NodeIndex &index)
Definition Node.h:24
KisNodeSP node() const
Definition Node.cpp:827
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
const qreal eps
T pow2(const T &x)
Definition kis_global.h:166
qreal kisSquareDistance(const QPointF &pt1, const QPointF &pt2)
Definition kis_global.h:194
QPointF relativeToAbsolute(const QPointF &pt, const QRectF &rc)
Point lerp(const Point &pt1, const Point &pt2, qreal t)
qreal angleBetweenVectors(const QPointF &v1, const QPointF &v2)
qreal norm(const T &a)
PointTypeTraits< T >::value_type dotProduct(const T &a, const T &b)
void smartMoveControl(Mesh< NodeArg, PatchArg > &mesh, typename Mesh< NodeArg, PatchArg >::ControlPointIndex index, const QPointF &move, SmartMoveMeshControlMode mode, bool scaleNodeMoves)
KRITAGLOBAL_EXPORT void saveValue(QDomElement *parent, const QString &tag, const BaseMeshNode &node)
QDebug operator<<(QDebug dbg, const Mesh< Node, Patch > &mesh)
void assignPatchData(KisBezierPatch *patch, const QRectF &srcRect, const BaseMeshNode &tl, const BaseMeshNode &tr, const BaseMeshNode &bl, const BaseMeshNode &br)
void lerpNodeData(const BaseMeshNode &left, const BaseMeshNode &right, qreal t, BaseMeshNode &dst)
KRITAGLOBAL_EXPORT bool loadValue(const QDomElement &parent, BaseMeshNode *node)
qreal curveParamByProportion(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal proportion, const qreal error)
void deCasteljau(const QPointF &q0, const QPointF &q1, const QPointF &q2, const QPointF &q3, qreal t, QPointF *p0, QPointF *p1, QPointF *p2, QPointF *p3, QPointF *p4)
QPointF bezierCurve(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal t)
qreal nearestPoint(const QList< QPointF > controlPoints, const QPointF &point, qreal *resultDistance, QPointF *resultPoint)
std::pair< QPointF, QPointF > removeBezierNode(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &q1, const QPointF &q2, const QPointF &q3)
Adjusts position for the bezier control points after removing a node.
qreal curveLength(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, const qreal error)
int bezierDegree(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3)
typename copy_const< Src, Dst >::type copy_const_t
typename add_const_if< is_const, T >::type add_const_if_t
void setRightControlRelative(const QPointF &value)
void translate(const QPointF &offset)
bool operator==(const BaseMeshNode &rhs) const
void setBottomControlRelative(const QPointF &value)
void setTopControlRelative(const QPointF &value)
void transform(const QTransform &t)
void setLeftControlRelative(const QPointF &value)
BaseMeshNode(const QPointF &_node)
ControlPointIndex(NodeIndex _nodeIndex, ControlType _controlType)
static PointType & controlPoint(NodeType &node, ControlType controlType)
friend bool operator==(const ControlPointIndex &lhs, const ControlPointIndex &rhs)
friend QDebug operator<<(QDebug dbg, const Mesh::ControlPointIndex &index)
NodeIndex & operator+=(const QPoint &rhs)
NodeIndex & operator-=(const QPoint &rhs)
PatchIndex & operator-=(const QPoint &rhs)
PatchIndex & operator+=(const QPoint &rhs)