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 #ifndef KISBEZIERMESH_H 0008 #define KISBEZIERMESH_H 0009 0010 #include <kritaglobal_export.h> 0011 0012 #include <QDebug> 0013 0014 #include <KisBezierUtils.h> 0015 #include <KisBezierPatch.h> 0016 0017 #include <boost/iterator/iterator_facade.hpp> 0018 #include <boost/operators.hpp> 0019 0020 #include <functional> 0021 #include "KisCppQuirks.h" 0022 0023 #include "kis_debug.h" 0024 0025 class QDomElement; 0026 0027 namespace KisBezierMeshDetails { 0028 0029 struct BaseMeshNode : public boost::equality_comparable<BaseMeshNode> { 0030 BaseMeshNode() = default; 0031 0032 BaseMeshNode(const QPointF &_node) 0033 : leftControl(_node), 0034 topControl(_node), 0035 node(_node), 0036 rightControl(_node), 0037 bottomControl(_node) 0038 { 0039 } 0040 0041 bool operator==(const BaseMeshNode &rhs) const { 0042 return leftControl == rhs.leftControl && 0043 topControl == rhs.topControl && 0044 node == rhs.node && 0045 rightControl == rhs.rightControl && 0046 bottomControl == rhs.bottomControl; 0047 } 0048 0049 void setLeftControlRelative(const QPointF &value) { 0050 leftControl = value + node; 0051 } 0052 0053 QPointF leftControlRelative() const { 0054 return leftControl - node; 0055 } 0056 0057 void setRightControlRelative(const QPointF &value) { 0058 rightControl = value + node; 0059 } 0060 0061 QPointF rightControlRelative() const { 0062 return rightControl - node; 0063 } 0064 0065 void setTopControlRelative(const QPointF &value) { 0066 topControl = value + node; 0067 } 0068 0069 QPointF topControlRelative() const { 0070 return topControl - node; 0071 } 0072 0073 void setBottomControlRelative(const QPointF &value) { 0074 bottomControl = value + node; 0075 } 0076 0077 QPointF bottomControlRelative() const { 0078 return bottomControl - node; 0079 } 0080 0081 void translate(const QPointF &offset) { 0082 leftControl += offset; 0083 topControl += offset; 0084 node += offset; 0085 rightControl += offset; 0086 bottomControl += offset; 0087 } 0088 0089 void transform(const QTransform &t) { 0090 leftControl = t.map(leftControl); 0091 topControl = t.map(topControl); 0092 node = t.map(node); 0093 rightControl = t.map(rightControl); 0094 bottomControl = t.map(bottomControl); 0095 } 0096 0097 QPointF leftControl; 0098 QPointF topControl; 0099 QPointF node; 0100 QPointF rightControl; 0101 QPointF bottomControl; 0102 }; 0103 0104 inline void lerpNodeData(const BaseMeshNode &left, const BaseMeshNode &right, qreal t, BaseMeshNode &dst) 0105 { 0106 Q_UNUSED(left); 0107 Q_UNUSED(right); 0108 Q_UNUSED(t); 0109 Q_UNUSED(dst); 0110 } 0111 0112 inline void assignPatchData(KisBezierPatch *patch, 0113 const QRectF &srcRect, 0114 const BaseMeshNode &tl, 0115 const BaseMeshNode &tr, 0116 const BaseMeshNode &bl, 0117 const BaseMeshNode &br) 0118 { 0119 patch->originalRect = srcRect; 0120 Q_UNUSED(tl); 0121 Q_UNUSED(tr); 0122 Q_UNUSED(bl); 0123 Q_UNUSED(br); 0124 } 0125 0126 template<typename NodeArg = BaseMeshNode, 0127 typename PatchArg = KisBezierPatch> 0128 class Mesh : public boost::equality_comparable<Mesh<NodeArg, PatchArg>> 0129 { 0130 public: 0131 using Node = NodeArg; 0132 using Patch = PatchArg; 0133 0134 struct PatchIndex : public QPoint, boost::additive<PatchIndex, QPoint> 0135 { 0136 using QPoint::QPoint; 0137 PatchIndex& operator+=(const QPoint &rhs) { 0138 QPoint::operator+=(rhs); 0139 return *this; 0140 } 0141 PatchIndex& operator-=(const QPoint &rhs) { 0142 QPoint::operator-=(rhs); 0143 return *this; 0144 } 0145 }; 0146 0147 struct NodeIndex : public QPoint, boost::additive<NodeIndex, QPoint> 0148 { 0149 using QPoint::QPoint; 0150 NodeIndex& operator+=(const QPoint &rhs) { 0151 QPoint::operator+=(rhs); 0152 return *this; 0153 } 0154 NodeIndex& operator-=(const QPoint &rhs) { 0155 QPoint::operator-=(rhs); 0156 return *this; 0157 } 0158 }; 0159 0160 using SegmentIndex = std::pair<NodeIndex, int>; 0161 0162 struct ControlPointIndex : public boost::equality_comparable<ControlPointIndex> 0163 { 0164 enum ControlType { 0165 LeftControl = 0, 0166 TopControl, 0167 RightControl, 0168 BottomControl, 0169 Node 0170 }; 0171 0172 ControlPointIndex() = default; 0173 0174 ControlPointIndex(NodeIndex _nodeIndex, ControlType _controlType) 0175 : nodeIndex(_nodeIndex), 0176 controlType(_controlType) 0177 { 0178 } 0179 0180 NodeIndex nodeIndex; 0181 ControlType controlType; 0182 0183 inline bool isNode() const { 0184 return controlType == Node; 0185 } 0186 0187 inline bool isControlPoint() const { 0188 return controlType != Node; 0189 } 0190 0191 template <class NodeType, 0192 class PointType = std::add_const_if_t<std::is_const<NodeType>::value, QPointF>> 0193 static 0194 PointType& controlPoint(NodeType &node, ControlType controlType) { 0195 return 0196 controlType == LeftControl ? node.leftControl : 0197 controlType == RightControl ? node.rightControl : 0198 controlType == TopControl ? node.topControl : 0199 controlType == BottomControl ? node.bottomControl : 0200 node.node; 0201 } 0202 0203 QPointF& controlPoint(Mesh::Node &node) { 0204 return controlPoint(node, controlType); 0205 } 0206 0207 friend bool operator==(const ControlPointIndex &lhs, const ControlPointIndex &rhs) { 0208 return lhs.nodeIndex == rhs.nodeIndex && lhs.controlType == rhs.controlType; 0209 } 0210 0211 friend QDebug operator<<(QDebug dbg, const Mesh::ControlPointIndex &index) { 0212 dbg.nospace() << "ControlPointIndex (" 0213 << index.nodeIndex.x() << ", " << index.nodeIndex.x() << ", "; 0214 0215 switch (index.controlType) { 0216 case Mesh::ControlType::Node: 0217 dbg.nospace() << "Node"; 0218 break; 0219 case Mesh::ControlType::LeftControl: 0220 dbg.nospace() << "LeftControl"; 0221 break; 0222 case Mesh::ControlType::RightControl: 0223 dbg.nospace() << "RightControl"; 0224 break; 0225 case Mesh::ControlType::TopControl: 0226 dbg.nospace() << "TopControl"; 0227 break; 0228 case Mesh::ControlType::BottomControl: 0229 dbg.nospace() << "BottomControl"; 0230 break; 0231 } 0232 0233 dbg.nospace() << ")"; 0234 return dbg.space(); 0235 } 0236 }; 0237 0238 using ControlType = typename ControlPointIndex::ControlType; 0239 0240 private: 0241 template<bool is_const> 0242 class segment_iterator_impl; 0243 0244 template<bool is_const> 0245 class control_point_iterator_impl; 0246 0247 template<bool is_const> 0248 class patch_iterator_impl : 0249 public boost::iterator_facade <patch_iterator_impl<is_const>, 0250 Patch, 0251 boost::random_access_traversal_tag, 0252 Patch> 0253 { 0254 using PointType = std::add_const_if_t<is_const, QPointF>; 0255 using MeshType = std::add_const_if_t<is_const, Mesh>; 0256 using SegmentIteratorType = segment_iterator_impl<is_const>; 0257 using ControlPointIteratorType = control_point_iterator_impl<is_const>; 0258 0259 public: 0260 patch_iterator_impl() 0261 : m_mesh(0), 0262 m_col(0), 0263 m_row(0) {} 0264 0265 patch_iterator_impl(MeshType* mesh, int col, int row) 0266 : m_mesh(mesh), 0267 m_col(col), 0268 m_row(row) 0269 { 0270 } 0271 0272 Mesh::PatchIndex patchIndex() const { 0273 return {m_col, m_row}; 0274 } 0275 0276 SegmentIteratorType segmentP() const; 0277 SegmentIteratorType segmentQ() const; 0278 SegmentIteratorType segmentR() const; 0279 SegmentIteratorType segmentS() const; 0280 0281 ControlPointIteratorType nodeTopLeft() const; 0282 ControlPointIteratorType nodeTopRight() const; 0283 ControlPointIteratorType nodeBottomLeft() const; 0284 ControlPointIteratorType nodeBottomRight() const; 0285 0286 bool isValid() const { 0287 return 0288 m_col >= 0 && 0289 m_col < m_mesh->size().width() - 1 && 0290 m_row >= 0 && 0291 m_row < m_mesh->size().height() - 1; 0292 } 0293 0294 private: 0295 friend class boost::iterator_core_access; 0296 0297 void increment() { 0298 m_col++; 0299 if (m_col >= m_mesh->m_size.width() - 1) { 0300 m_col = 0; 0301 m_row++; 0302 } 0303 } 0304 0305 void decrement() { 0306 m_col--; 0307 if (m_col < 0) { 0308 m_col = m_mesh->m_size.width() - 2; 0309 m_row--; 0310 } 0311 } 0312 0313 void advance(int n) { 0314 const int index = m_row * (m_mesh->m_size.width() - 1) + m_col + n; 0315 0316 m_row = index / (m_mesh->m_size.width() - 1); 0317 m_col = index % (m_mesh->m_size.width() - 1); 0318 0319 KIS_SAFE_ASSERT_RECOVER_NOOP(m_row < m_mesh->m_size.height() - 1); 0320 } 0321 0322 int distance_to(const patch_iterator_impl &z) const { 0323 const int index = m_row * (m_mesh->m_size.width() - 1) + m_col; 0324 const int otherIndex = z.m_row * (m_mesh->m_size.width() - 1) + z.m_col; 0325 0326 return otherIndex - index; 0327 } 0328 0329 bool equal(patch_iterator_impl const& other) const { 0330 return m_row == other.m_row && 0331 m_col == other.m_col && 0332 m_mesh == other.m_mesh; 0333 } 0334 0335 Patch dereference() const { 0336 return m_mesh->makePatch(m_col, m_row); 0337 } 0338 0339 private: 0340 0341 MeshType *m_mesh; 0342 int m_col; 0343 int m_row; 0344 }; 0345 0346 template<bool is_const> 0347 class control_point_iterator_impl : 0348 public boost::iterator_facade <control_point_iterator_impl<is_const>, 0349 std::add_const_if_t<is_const, QPointF>, 0350 boost::bidirectional_traversal_tag> 0351 { 0352 using PointType = std::add_const_if_t<is_const, QPointF>; 0353 using NodeType = std::add_const_if_t<is_const, Node>; 0354 using MeshType = std::add_const_if_t<is_const, Mesh>; 0355 using SegmentIteratorType = segment_iterator_impl<is_const>; 0356 0357 public: 0358 control_point_iterator_impl() 0359 : m_mesh(0), 0360 m_col(0), 0361 m_row(0), 0362 m_controlIndex(0) 0363 {} 0364 0365 control_point_iterator_impl(MeshType* mesh, int col, int row, int controlIndex) 0366 : m_mesh(mesh), 0367 m_col(col), 0368 m_row(row), 0369 m_controlIndex(controlIndex) 0370 { 0371 } 0372 0373 Mesh::ControlType type() const { 0374 return Mesh::ControlType(m_controlIndex); 0375 } 0376 0377 int col() const { 0378 return m_col; 0379 } 0380 0381 int row() const { 0382 return m_row; 0383 } 0384 0385 Mesh::ControlPointIndex controlIndex() const { 0386 return Mesh::ControlPointIndex(nodeIndex(), type()); 0387 } 0388 0389 Mesh::NodeIndex nodeIndex() const { 0390 return Mesh::NodeIndex(m_col, m_row); 0391 } 0392 0393 NodeType& node() const { 0394 return m_mesh->node(m_col, m_row); 0395 } 0396 0397 bool isLeftBorder() const { 0398 return m_col == 0; 0399 } 0400 0401 bool isRightBorder() const { 0402 return m_col == m_mesh->size().width() - 1; 0403 } 0404 0405 bool isTopBorder() const { 0406 return m_row == 0; 0407 } 0408 0409 bool isBottomBorder() const { 0410 return m_row == m_mesh->size().height() - 1; 0411 } 0412 0413 0414 bool isBorderNode() const { 0415 return isLeftBorder() || isRightBorder() || isTopBorder() || isBottomBorder(); 0416 } 0417 0418 bool isCornerNode() const { 0419 return (isLeftBorder() + isRightBorder() + isTopBorder() + isBottomBorder()) > 1; 0420 } 0421 0422 bool isNode() const { 0423 return type() == Mesh::ControlPointIndex::Node; 0424 } 0425 0426 control_point_iterator_impl symmetricControl() const { 0427 typename Mesh::ControlPointIndex::ControlType newIndex = 0428 Mesh::ControlPointIndex::Node; 0429 0430 switch (type()) { 0431 case Mesh::ControlPointIndex::Node: 0432 newIndex = Mesh::ControlPointIndex::Node; 0433 break; 0434 case Mesh::ControlPointIndex::LeftControl: 0435 newIndex = Mesh::ControlPointIndex::RightControl; 0436 break; 0437 case Mesh::ControlPointIndex::RightControl: 0438 newIndex = Mesh::ControlPointIndex::LeftControl; 0439 break; 0440 case Mesh::ControlPointIndex::TopControl: 0441 newIndex = Mesh::ControlPointIndex::BottomControl; 0442 break; 0443 case Mesh::ControlPointIndex::BottomControl: 0444 newIndex = Mesh::ControlPointIndex::TopControl; 0445 break; 0446 } 0447 0448 control_point_iterator_impl it(m_mesh, m_col, m_row, newIndex); 0449 0450 if (!it.controlIsValid()) { 0451 it = m_mesh->endControlPoints(); 0452 } 0453 0454 return it; 0455 } 0456 0457 SegmentIteratorType topSegment() const; 0458 SegmentIteratorType bottomSegment() const; 0459 SegmentIteratorType leftSegment() const; 0460 SegmentIteratorType rightSegment() const; 0461 0462 bool isValid() const { 0463 return nodeIsValid() && controlIsValid(); 0464 } 0465 0466 private: 0467 friend class boost::iterator_core_access; 0468 0469 bool nodeIsValid() const { 0470 return m_col >= 0 && m_row >= 0 && m_col < m_mesh->size().width() && m_row < m_mesh->size().height(); 0471 } 0472 0473 bool controlIsValid() const { 0474 if (m_col == 0 && m_controlIndex == Mesh::ControlType::LeftControl) { 0475 return false; 0476 } 0477 0478 if (m_col == m_mesh->m_size.width() - 1 && m_controlIndex == Mesh::ControlType::RightControl) { 0479 return false; 0480 } 0481 0482 if (m_row == 0 && m_controlIndex == Mesh::ControlType::TopControl) { 0483 return false; 0484 } 0485 0486 if (m_row == m_mesh->m_size.height() - 1 && m_controlIndex == Mesh::ControlType::BottomControl) { 0487 return false; 0488 } 0489 0490 return true; 0491 } 0492 0493 void increment() { 0494 do { 0495 m_controlIndex++; 0496 if (m_controlIndex > 4) { 0497 m_controlIndex = 0; 0498 m_col++; 0499 if (m_col >= m_mesh->m_size.width()) { 0500 m_col = 0; 0501 m_row++; 0502 } 0503 } 0504 } while (nodeIsValid() && !controlIsValid()); 0505 } 0506 0507 void decrement() { 0508 do { 0509 m_controlIndex--; 0510 if (m_controlIndex < 0) { 0511 m_controlIndex = 4; 0512 m_col--; 0513 if (m_col < 0) { 0514 m_col = m_mesh->m_size.width() - 1; 0515 m_row--; 0516 } 0517 } 0518 } while (nodeIsValid() && !controlIsValid()); 0519 } 0520 0521 0522 bool equal(control_point_iterator_impl const& other) const { 0523 return m_controlIndex == other.m_controlIndex && 0524 m_row == other.m_row && 0525 m_col == other.m_col && 0526 m_mesh == other.m_mesh; 0527 } 0528 0529 PointType& dereference() const { 0530 return Mesh::ControlPointIndex::controlPoint(m_mesh->node(m_col, m_row), Mesh::ControlType(m_controlIndex)); 0531 } 0532 0533 private: 0534 MeshType* m_mesh; 0535 int m_col; 0536 int m_row; 0537 int m_controlIndex; 0538 }; 0539 0540 template<bool is_const> 0541 class segment_iterator_impl : 0542 public boost::iterator_facade <segment_iterator_impl<is_const>, 0543 Mesh::SegmentIndex, 0544 boost::bidirectional_traversal_tag, 0545 Mesh::SegmentIndex> 0546 { 0547 using PointType = std::add_const_if_t<is_const, QPointF>; 0548 using NodeType = std::add_const_if_t<is_const, Node>; 0549 using MeshType = std::add_const_if_t<is_const, Mesh>; 0550 using ControlPointIteratorType = control_point_iterator_impl<is_const>; 0551 0552 public: 0553 segment_iterator_impl() 0554 : m_mesh(0), 0555 m_col(0), 0556 m_row(0), 0557 m_isHorizontal(0) 0558 {} 0559 0560 segment_iterator_impl(MeshType* mesh, int col, int row, int isHorizontal) 0561 : m_mesh(mesh), 0562 m_col(col), 0563 m_row(row), 0564 m_isHorizontal(isHorizontal) 0565 { 0566 } 0567 0568 Mesh::SegmentIndex segmentIndex() const { 0569 return 0570 { Mesh::NodeIndex(m_col, m_row), 0571 m_isHorizontal}; 0572 } 0573 0574 Mesh::NodeIndex firstNodeIndex() const { 0575 return Mesh::NodeIndex(m_col, m_row); 0576 } 0577 0578 Mesh::NodeIndex secondNodeIndex() const { 0579 return m_isHorizontal ? Mesh::NodeIndex(m_col + 1, m_row) : Mesh::NodeIndex(m_col, m_row + 1); 0580 } 0581 0582 NodeType& firstNode() const { 0583 return m_mesh->node(firstNodeIndex()); 0584 } 0585 0586 NodeType& secondNode() const { 0587 return m_mesh->node(secondNodeIndex()); 0588 } 0589 0590 PointType& p0() const { 0591 return firstNode().node; 0592 } 0593 0594 PointType& p1() const { 0595 return m_isHorizontal ? firstNode().rightControl : firstNode().bottomControl; 0596 } 0597 0598 PointType& p2() const { 0599 return m_isHorizontal ? secondNode().leftControl : secondNode().topControl; 0600 } 0601 0602 PointType& p3() const { 0603 return secondNode().node; 0604 } 0605 0606 ControlPointIteratorType itP0() const { 0607 return m_mesh->find(ControlPointIndex(firstNodeIndex(), Mesh::ControlType::Node)); 0608 } 0609 0610 ControlPointIteratorType itP1() const { 0611 return m_mesh->find(ControlPointIndex(firstNodeIndex(), 0612 m_isHorizontal ? 0613 Mesh::ControlType::RightControl : 0614 Mesh::ControlType::BottomControl)); 0615 } 0616 0617 ControlPointIteratorType itP2() const { 0618 return m_mesh->find(ControlPointIndex(secondNodeIndex(), 0619 m_isHorizontal ? 0620 Mesh::ControlType::LeftControl : 0621 Mesh::ControlType::TopControl)); 0622 } 0623 0624 ControlPointIteratorType itP3() const { 0625 return m_mesh->find(ControlPointIndex(secondNodeIndex(), Mesh::ControlType::Node)); 0626 } 0627 0628 QPointF pointAtParam(qreal t) const { 0629 return KisBezierUtils::bezierCurve(p0(), p1(), p2(), p3(), t); 0630 } 0631 0632 qreal length() const { 0633 const qreal eps = 1e-3; 0634 return KisBezierUtils::curveLength(p0(), p1(), p2(), p3(), eps); 0635 } 0636 0637 int degree() const { 0638 return KisBezierUtils::bezierDegree(p0(), p1(), p2(), p3()); 0639 } 0640 0641 bool isHorizontal() const { 0642 return m_isHorizontal; 0643 } 0644 0645 bool isValid() const { 0646 return nodeIsValid() && controlIsValid(); 0647 } 0648 0649 private: 0650 friend class boost::iterator_core_access; 0651 0652 bool nodeIsValid() const { 0653 return m_col >= 0 && m_row >= 0 && m_col < m_mesh->size().width() && m_row < m_mesh->size().height(); 0654 } 0655 0656 bool controlIsValid() const { 0657 if (m_col == m_mesh->m_size.width() - 1 && m_isHorizontal) { 0658 return false; 0659 } 0660 0661 if (m_row == m_mesh->m_size.height() - 1 && !m_isHorizontal) { 0662 return false; 0663 } 0664 0665 return true; 0666 } 0667 0668 void increment() { 0669 do { 0670 m_isHorizontal++; 0671 if (m_isHorizontal > 1) { 0672 m_isHorizontal = 0; 0673 m_col++; 0674 if (m_col >= m_mesh->m_size.width()) { 0675 m_col = 0; 0676 m_row++; 0677 } 0678 } 0679 } while (nodeIsValid() && !controlIsValid()); 0680 } 0681 0682 void decrement() { 0683 do { 0684 m_isHorizontal--; 0685 if (m_isHorizontal < 0) { 0686 m_isHorizontal = 1; 0687 m_col--; 0688 if (m_col < 0) { 0689 m_col = m_mesh->m_size.width() - 1; 0690 m_row--; 0691 } 0692 } 0693 } while (nodeIsValid() && !controlIsValid()); 0694 } 0695 0696 0697 bool equal(segment_iterator_impl const& other) const { 0698 return m_isHorizontal == other.m_isHorizontal && 0699 m_row == other.m_row && 0700 m_col == other.m_col && 0701 m_mesh == other.m_mesh; 0702 } 0703 0704 Mesh::SegmentIndex dereference() const { 0705 return segmentIndex(); 0706 } 0707 0708 private: 0709 0710 MeshType* m_mesh; 0711 int m_col; 0712 int m_row; 0713 int m_isHorizontal; 0714 }; 0715 0716 public: 0717 Mesh() 0718 : Mesh(QRectF(0.0, 0.0, 1.0, 1.0)) 0719 { 0720 } 0721 0722 Mesh(const QRectF &mapRect, const QSize &size = QSize(2,2)) 0723 : m_size(size), 0724 m_originalRect(mapRect) 0725 { 0726 const qreal xControlOffset = 0.2 * (mapRect.width() / size.width()); 0727 const qreal yControlOffset = 0.2 * (mapRect.height() / size.height()); 0728 0729 for (int row = 0; row < m_size.height(); row++) { 0730 const qreal yPos = qreal(row) / (size.height() - 1) * mapRect.height() + mapRect.y(); 0731 0732 for (int col = 0; col < m_size.width(); col++) { 0733 const qreal xPos = qreal(col) / (size.width() - 1) * mapRect.width() + mapRect.x(); 0734 0735 Node node(QPointF(xPos, yPos)); 0736 node.setLeftControlRelative(QPointF(-xControlOffset, 0)); 0737 node.setRightControlRelative(QPointF(xControlOffset, 0)); 0738 node.setTopControlRelative(QPointF(0, -yControlOffset)); 0739 node.setBottomControlRelative(QPointF(0, yControlOffset)); 0740 0741 m_nodes.push_back(node); 0742 } 0743 } 0744 0745 for (int col = 0; col < m_size.width(); col++) { 0746 m_columns.push_back(qreal(col) / (size.width() - 1)); 0747 } 0748 0749 for (int row = 0; row < m_size.height(); row++) { 0750 m_rows.push_back(qreal(row) / (size.height() - 1)); 0751 } 0752 } 0753 0754 bool operator==(const Mesh &rhs) const { 0755 return m_size == rhs.m_size && 0756 m_rows == rhs.m_rows && 0757 m_columns == rhs.m_columns && 0758 m_originalRect == rhs.m_originalRect && 0759 m_nodes == rhs.m_nodes; 0760 } 0761 0762 Node& node(int col, int row) { 0763 KIS_ASSERT(col >= 0 && col < m_size.width() && row >= 0 && row < m_size.height()); 0764 return m_nodes[row * m_size.width() + col]; 0765 } 0766 0767 const Node& node(int col, int row) const { 0768 KIS_ASSERT(col >= 0 && col < m_size.width() && row >= 0 && row < m_size.height()); 0769 return m_nodes[row * m_size.width() + col]; 0770 } 0771 0772 Node& node(const NodeIndex &index) { 0773 return node(index.x(), index.y()); 0774 } 0775 0776 const Node& node(const NodeIndex &index) const { 0777 return node(index.x(), index.y()); 0778 } 0779 0780 0781 int subdivideRow(qreal proportionalT) { 0782 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(proportionalT >= 0.0 && proportionalT <= 1.0, -1); 0783 0784 { 0785 auto existing = std::find(m_rows.begin(), m_rows.end(), proportionalT); 0786 if (existing != m_rows.end()) { 0787 return distance(m_rows.begin(), existing); 0788 } 0789 } 0790 0791 const auto it = prev(upper_bound(m_rows.begin(), m_rows.end(), proportionalT)); 0792 const int topRow = distance(m_rows.begin(), it); 0793 const qreal relT = (proportionalT - *it) / (*next(it) - *it); 0794 0795 return subdivideRow(topRow, relT); 0796 } 0797 0798 int subdivideRow(int topRow, qreal relProportionalT) { 0799 const auto it = m_rows.begin() + topRow; 0800 const int bottomRow = topRow + 1; 0801 const qreal absProportionalT = KisAlgebra2D::lerp(*it, *next(it), relProportionalT); 0802 0803 std::vector<Node> newRow; 0804 newRow.resize(m_size.width()); 0805 for (int col = 0; col < m_size.width(); col++) { 0806 const qreal t = 0807 KisBezierUtils::curveParamByProportion(node(col, topRow).node, 0808 node(col, topRow).bottomControl, 0809 node(col, bottomRow).topControl, 0810 node(col, bottomRow).node, 0811 relProportionalT, 0812 0.01); 0813 0814 splitCurveVertically(node(col, topRow), node(col, bottomRow), t, newRow[col]); 0815 } 0816 0817 m_nodes.insert(m_nodes.begin() + bottomRow * m_size.width(), 0818 newRow.begin(), newRow.end()); 0819 0820 m_size.rheight()++; 0821 auto insertedIt = m_rows.insert(next(it), absProportionalT); 0822 return distance(m_rows.begin(), insertedIt); 0823 } 0824 0825 int subdivideColumn(qreal proportionalT) { 0826 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(proportionalT >= 0.0 && proportionalT <= 1.0, -1); 0827 0828 { 0829 auto existing = std::find(m_columns.begin(), m_columns.end(), proportionalT); 0830 if (existing != m_columns.end()) { 0831 return distance(m_columns.begin(), existing); 0832 } 0833 } 0834 0835 const auto it = prev(upper_bound(m_columns.begin(), m_columns.end(), proportionalT)); 0836 const int leftColumn = distance(m_columns.begin(), it); 0837 0838 const qreal relT = (proportionalT - *it) / (*next(it) - *it); 0839 0840 return subdivideColumn(leftColumn, relT); 0841 } 0842 0843 int subdivideColumn(int leftColumn, qreal relProportionalT) { 0844 const auto it = m_columns.begin() + leftColumn; 0845 const int rightColumn = leftColumn + 1; 0846 const qreal absProportinalT = KisAlgebra2D::lerp(*it, *next(it), relProportionalT); 0847 0848 std::vector<Node> newColumn; 0849 newColumn.resize(m_size.height()); 0850 for (int row = 0; row < m_size.height(); row++) { 0851 const qreal t = 0852 KisBezierUtils::curveParamByProportion(node(leftColumn, row).node, 0853 node(leftColumn, row).rightControl, 0854 node(rightColumn, row).leftControl, 0855 node(rightColumn, row).node, 0856 relProportionalT, 0857 0.01); 0858 0859 splitCurveHorizontally(node(leftColumn, row), node(rightColumn, row), t, newColumn[row]); 0860 } 0861 0862 auto dstIt = m_nodes.begin() + rightColumn; 0863 for (auto columnIt = newColumn.begin(); columnIt != newColumn.end(); ++columnIt) { 0864 dstIt = m_nodes.insert(dstIt, *columnIt); 0865 dstIt += m_size.width() + 1; 0866 } 0867 0868 m_size.rwidth()++; 0869 auto insertedIt = m_columns.insert(next(it), absProportinalT); 0870 return distance(m_columns.begin(), insertedIt); 0871 } 0872 0873 void removeColumn(int column) { 0874 const bool hasNeighbours = column > 0 || column < m_size.width() - 1; 0875 0876 if (hasNeighbours) { 0877 for (int row = 0; row < m_size.height(); row++) { 0878 unlinkNodeHorizontally(node(column - 1, row), node(column, row), node(column + 1, row)); 0879 } 0880 } 0881 0882 auto it = m_nodes.begin() + column; 0883 for (int row = 0; row < m_size.height(); row++) { 0884 it = m_nodes.erase(it); 0885 it += m_size.width() - 1; 0886 } 0887 0888 m_size.rwidth()--; 0889 m_columns.erase(m_columns.begin() + column); 0890 } 0891 0892 void removeRow(int row) { 0893 const bool hasNeighbours = row > 0 || row < m_size.height() - 1; 0894 0895 if (hasNeighbours) { 0896 for (int column = 0; column < m_size.width(); column++) { 0897 unlinkNodeVertically(node(column, row - 1), node(column, row), node(column, row + 1)); 0898 } 0899 } 0900 0901 auto it = m_nodes.begin() + row * m_size.width(); 0902 m_nodes.erase(it, it + m_size.width()); 0903 0904 m_size.rheight()--; 0905 m_rows.erase(m_rows.begin() + row); 0906 } 0907 0908 void removeColumnOrRow(NodeIndex index, bool row) { 0909 if (row) { 0910 removeRow(index.y()); 0911 } else { 0912 removeColumn(index.x()); 0913 } 0914 } 0915 0916 void subdivideSegment(SegmentIndex index, qreal proportionalT) { 0917 auto it = find(index); 0918 KIS_SAFE_ASSERT_RECOVER_RETURN(it != endSegments()); 0919 0920 if (it.isHorizontal()) { 0921 subdivideColumn(it.firstNodeIndex().x(), proportionalT); 0922 } else { 0923 subdivideRow(it.firstNodeIndex().y(), proportionalT); 0924 } 0925 } 0926 0927 Patch makePatch(const PatchIndex &index) const { 0928 return makePatch(index.x(), index.y()); 0929 } 0930 0931 Patch makePatch(int col, int row) const 0932 { 0933 const Node &tl = node(col, row); 0934 const Node &tr = node(col + 1, row); 0935 const Node &bl = node(col, row + 1); 0936 const Node &br = node(col + 1, row + 1); 0937 0938 Patch patch; 0939 0940 patch.points[Patch::TL] = tl.node; 0941 patch.points[Patch::TL_HC] = tl.rightControl; 0942 patch.points[Patch::TL_VC] = tl.bottomControl; 0943 0944 patch.points[Patch::TR] = tr.node; 0945 patch.points[Patch::TR_HC] = tr.leftControl; 0946 patch.points[Patch::TR_VC] = tr.bottomControl; 0947 0948 patch.points[Patch::BL] = bl.node; 0949 patch.points[Patch::BL_HC] = bl.rightControl; 0950 patch.points[Patch::BL_VC] = bl.topControl; 0951 0952 patch.points[Patch::BR] = br.node; 0953 patch.points[Patch::BR_HC] = br.leftControl; 0954 patch.points[Patch::BR_VC] = br.topControl; 0955 0956 const QRectF relRect(m_columns[col], 0957 m_rows[row], 0958 m_columns[col + 1] - m_columns[col], 0959 m_rows[row + 1] - m_rows[row]); 0960 0961 assignPatchData(&patch, KisAlgebra2D::relativeToAbsolute(relRect, m_originalRect), 0962 tl, tr, bl, br); 0963 0964 return patch; 0965 } 0966 0967 using patch_iterator = patch_iterator_impl<false>; 0968 using patch_const_iterator = patch_iterator_impl<true>; 0969 0970 using control_point_iterator = control_point_iterator_impl<false>; 0971 using control_point_const_iterator = control_point_iterator_impl<true>; 0972 0973 using segment_iterator = segment_iterator_impl<false>; 0974 using segment_const_iterator = segment_iterator_impl<true>; 0975 0976 patch_iterator beginPatches() { return beginPatches(*this); } 0977 patch_const_iterator beginPatches() const { return beginPatches(*this); } 0978 patch_const_iterator constBeginPatches() const { return beginPatches(*this); } 0979 0980 patch_iterator endPatches() { return endPatches(*this); } 0981 patch_const_iterator endPatches() const { return endPatches(*this); } 0982 patch_const_iterator constEndPatches() const { return endPatches(*this); } 0983 0984 control_point_iterator beginControlPoints() { return beginControlPoints(*this); } 0985 control_point_const_iterator beginControlPoints() const { return beginControlPoints(*this); } 0986 control_point_const_iterator constBeginControlPoints() const { return beginControlPoints(*this); } 0987 0988 control_point_iterator endControlPoints() { return endControlPoints(*this); } 0989 control_point_const_iterator endControlPoints() const { return endControlPoints(*this); } 0990 control_point_const_iterator constEndControlPoints() const { return endControlPoints(*this); } 0991 0992 segment_iterator beginSegments() { return beginSegments(*this); } 0993 segment_const_iterator beginSegments() const { return beginSegments(*this); } 0994 segment_const_iterator constBeginSegments() const { return beginSegments(*this); } 0995 0996 segment_iterator endSegments() { return endSegments(*this); } 0997 segment_const_iterator endSegments() const { return endSegments(*this); } 0998 segment_const_iterator constEndSegments() const { return endSegments(*this); } 0999 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 1129 private: 1130 void splitCurveHorizontally(Node &left, Node &right, qreal t, Node &newNode) { 1131 using KisBezierUtils::deCasteljau; 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) { 1152 using KisBezierUtils::deCasteljau; 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 1172 void unlinkNodeHorizontally(Mesh::Node &left, const Mesh::Node &node, Mesh::Node &right) 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 1180 void unlinkNodeVertically(Mesh::Node &top, const Mesh::Node &node, Mesh::Node &bottom) 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 1207 private: 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 1282 protected: 1283 1284 std::vector<Node> m_nodes; 1285 std::vector<qreal> m_rows; 1286 std::vector<qreal> m_columns; 1287 1288 QSize m_size; 1289 QRectF m_originalRect; 1290 }; 1291 1292 template<typename Node, typename Patch> 1293 QDebug 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 1305 template<typename NodeArg, typename PatchArg> 1306 template<bool is_const> 1307 typename Mesh<NodeArg, PatchArg>::template patch_iterator_impl<is_const>::SegmentIteratorType 1308 Mesh<NodeArg, PatchArg>::patch_iterator_impl<is_const>::segmentP() const 1309 { 1310 using SegmentIteratorType = typename Mesh<NodeArg, PatchArg>::template patch_iterator_impl<is_const>::SegmentIteratorType; 1311 return SegmentIteratorType(m_mesh, m_col, m_row, 1); 1312 } 1313 1314 template<typename NodeArg, typename PatchArg> 1315 template<bool is_const> 1316 typename Mesh<NodeArg, PatchArg>::template patch_iterator_impl<is_const>::SegmentIteratorType 1317 Mesh<NodeArg, PatchArg>::patch_iterator_impl<is_const>::segmentQ() const 1318 { 1319 using SegmentIteratorType = typename Mesh<NodeArg, PatchArg>::template patch_iterator_impl<is_const>::SegmentIteratorType; 1320 return SegmentIteratorType(const_cast<Mesh<NodeArg, PatchArg>*>(m_mesh), m_col, m_row + 1, 1); 1321 } 1322 1323 template<typename NodeArg, typename PatchArg> 1324 template<bool is_const> 1325 typename Mesh<NodeArg, PatchArg>::template patch_iterator_impl<is_const>::SegmentIteratorType 1326 Mesh<NodeArg, PatchArg>::patch_iterator_impl<is_const>::segmentR() const 1327 { 1328 using SegmentIteratorType = typename Mesh<NodeArg, PatchArg>::template patch_iterator_impl<is_const>::SegmentIteratorType; 1329 return SegmentIteratorType(const_cast<Mesh<NodeArg, PatchArg>*>(m_mesh), m_col, m_row, 0); 1330 } 1331 1332 template<typename NodeArg, typename PatchArg> 1333 template<bool is_const> 1334 typename Mesh<NodeArg, PatchArg>::template patch_iterator_impl<is_const>::SegmentIteratorType 1335 Mesh<NodeArg, PatchArg>::patch_iterator_impl<is_const>::segmentS() const 1336 { 1337 using SegmentIteratorType = typename Mesh<NodeArg, PatchArg>::template patch_iterator_impl<is_const>::SegmentIteratorType; 1338 return SegmentIteratorType(const_cast<Mesh<NodeArg, PatchArg>*>(m_mesh), m_col + 1, m_row, 0); 1339 } 1340 1341 template<typename NodeArg, typename PatchArg> 1342 template<bool is_const> 1343 typename Mesh<NodeArg, PatchArg>::template patch_iterator_impl<is_const>::ControlPointIteratorType 1344 Mesh<NodeArg, PatchArg>::patch_iterator_impl<is_const>::nodeTopLeft() const 1345 { 1346 using ControlPointIteratorType = typename Mesh<NodeArg, PatchArg>::template patch_iterator_impl<is_const>::ControlPointIteratorType; 1347 return ControlPointIteratorType(const_cast<Mesh<NodeArg, PatchArg>*>(m_mesh), m_col, m_row, Mesh::ControlType::Node); 1348 } 1349 1350 template<typename NodeArg, typename PatchArg> 1351 template<bool is_const> 1352 typename Mesh<NodeArg, PatchArg>::template patch_iterator_impl<is_const>::ControlPointIteratorType 1353 Mesh<NodeArg, PatchArg>::patch_iterator_impl<is_const>::nodeTopRight() const 1354 { 1355 using ControlPointIteratorType = typename Mesh<NodeArg, PatchArg>::template patch_iterator_impl<is_const>::ControlPointIteratorType; 1356 return ControlPointIteratorType(const_cast<Mesh<NodeArg, PatchArg>*>(m_mesh), m_col + 1, m_row, Mesh::ControlType::Node); 1357 } 1358 1359 template<typename NodeArg, typename PatchArg> 1360 template<bool is_const> 1361 typename Mesh<NodeArg, PatchArg>::template patch_iterator_impl<is_const>::ControlPointIteratorType 1362 Mesh<NodeArg, PatchArg>::patch_iterator_impl<is_const>::nodeBottomLeft() const 1363 { 1364 using ControlPointIteratorType = typename Mesh<NodeArg, PatchArg>::template patch_iterator_impl<is_const>::ControlPointIteratorType; 1365 return ControlPointIteratorType(const_cast<Mesh<NodeArg, PatchArg>*>(m_mesh), m_col, m_row + 1, Mesh::ControlType::Node); 1366 } 1367 1368 template<typename NodeArg, typename PatchArg> 1369 template<bool is_const> 1370 typename Mesh<NodeArg, PatchArg>::template patch_iterator_impl<is_const>::ControlPointIteratorType 1371 Mesh<NodeArg, PatchArg>::patch_iterator_impl<is_const>::nodeBottomRight() const 1372 { 1373 using ControlPointIteratorType = typename Mesh<NodeArg, PatchArg>::template patch_iterator_impl<is_const>::ControlPointIteratorType; 1374 return ControlPointIteratorType(const_cast<Mesh<NodeArg, PatchArg>*>(m_mesh), m_col + 1, m_row + 1, Mesh::ControlType::Node); 1375 } 1376 1377 1378 template<typename NodeArg, typename PatchArg> 1379 template<bool is_const> 1380 typename Mesh<NodeArg, PatchArg>::template control_point_iterator_impl<is_const>::SegmentIteratorType 1381 Mesh<NodeArg, PatchArg>::control_point_iterator_impl<is_const>::topSegment() const 1382 { 1383 using SegmentIteratorType = typename Mesh<NodeArg, PatchArg>::template control_point_iterator_impl<is_const>::SegmentIteratorType; 1384 1385 if (isTopBorder()) { 1386 return m_mesh->endSegments(); 1387 } 1388 1389 return SegmentIteratorType(m_mesh, m_col, m_row - 1, false); 1390 } 1391 1392 template<typename NodeArg, typename PatchArg> 1393 template<bool is_const> 1394 typename Mesh<NodeArg, PatchArg>::template control_point_iterator_impl<is_const>::SegmentIteratorType 1395 Mesh<NodeArg, PatchArg>::control_point_iterator_impl<is_const>::bottomSegment() const 1396 { 1397 using SegmentIteratorType = typename Mesh<NodeArg, PatchArg>::template control_point_iterator_impl<is_const>::SegmentIteratorType; 1398 1399 if (isBottomBorder()) { 1400 return m_mesh->endSegments(); 1401 } 1402 1403 return SegmentIteratorType(m_mesh, m_col, m_row, false); 1404 } 1405 1406 template<typename NodeArg, typename PatchArg> 1407 template<bool is_const> 1408 typename Mesh<NodeArg, PatchArg>::template control_point_iterator_impl<is_const>::SegmentIteratorType 1409 Mesh<NodeArg, PatchArg>::control_point_iterator_impl<is_const>::leftSegment() const 1410 { 1411 using SegmentIteratorType = typename Mesh<NodeArg, PatchArg>::template control_point_iterator_impl<is_const>::SegmentIteratorType; 1412 1413 if (isLeftBorder()) { 1414 return m_mesh->endSegments(); 1415 } 1416 1417 return SegmentIteratorType(m_mesh, m_col - 1, m_row, true); 1418 } 1419 1420 template<typename NodeArg, typename PatchArg> 1421 template<bool is_const> 1422 typename Mesh<NodeArg, PatchArg>::template control_point_iterator_impl<is_const>::SegmentIteratorType 1423 Mesh<NodeArg, PatchArg>::control_point_iterator_impl<is_const>::rightSegment() const 1424 { 1425 using SegmentIteratorType = typename Mesh<NodeArg, PatchArg>::template control_point_iterator_impl<is_const>::SegmentIteratorType; 1426 1427 if (isRightBorder()) { 1428 return m_mesh->endSegments(); 1429 } 1430 1431 return SegmentIteratorType(m_mesh, m_col, m_row, true); 1432 } 1433 1434 enum SmartMoveMeshControlMode { 1435 MoveFree, 1436 MoveSymmetricLock, 1437 MoveRotationLock 1438 }; 1439 1440 template<typename NodeArg, typename PatchArg> 1441 void smartMoveControl(Mesh<NodeArg, PatchArg> &mesh, 1442 typename Mesh<NodeArg, PatchArg>::ControlPointIndex index, 1443 const QPointF &move, 1444 SmartMoveMeshControlMode mode, 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); 1453 KIS_SAFE_ASSERT_RECOVER_RETURN(it != mesh.endControlPoints()); 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 1533 KRITAGLOBAL_EXPORT 1534 QDebug operator<<(QDebug dbg, const BaseMeshNode &n); 1535 1536 KRITAGLOBAL_EXPORT 1537 void saveValue(QDomElement *parent, const QString &tag, const BaseMeshNode &node); 1538 1539 KRITAGLOBAL_EXPORT 1540 bool loadValue(const QDomElement &parent, BaseMeshNode *node); 1541 1542 } 1543 1544 namespace KisDomUtils { 1545 using KisBezierMeshDetails::loadValue; 1546 using KisBezierMeshDetails::saveValue; 1547 } 1548 1549 template <typename Node, typename Patch> 1550 using KisBezierMeshBase = KisBezierMeshDetails::Mesh<Node, Patch>; 1551 1552 using KisSmartMoveMeshControlMode = KisBezierMeshDetails::SmartMoveMeshControlMode; 1553 using KisBezierMesh = KisBezierMeshDetails::Mesh<KisBezierMeshDetails::BaseMeshNode, KisBezierPatch>; 1554 1555 1556 #endif // KISBEZIERMESH_H