File indexing completed on 2024-05-12 05:40:25
0001 /*************************************************************************** 0002 * Copyright (C) 2019 by Renaud Guezennec * 0003 * http://www.rolisteam.org/contact * 0004 * * 0005 * This software is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, * 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0013 * GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License * 0016 * along with this program; if not, write to the * 0017 * Free Software Foundation, Inc., * 0018 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 0019 ***************************************************************************/ 0020 #include "mindmap/controller/spacingcontroller.h" 0021 0022 #include "mindmap/data/linkcontroller.h" 0023 #include "mindmap/model/minditemmodel.h" 0024 0025 #include <QDebug> 0026 #include <QLineF> 0027 #include <chrono> 0028 #include <cmath> 0029 #include <thread> 0030 0031 namespace mindmap 0032 { 0033 constexpr float k_attraction{0.1f}; 0034 constexpr float k_repulsion{10000.f}; 0035 constexpr float k_defaultDamping{0.5f}; 0036 0037 SpacingController::SpacingController(MindItemModel* data, QObject* parent) : QObject(parent), m_model(data) {} 0038 0039 SpacingController::~SpacingController()= default; 0040 0041 void SpacingController::setRunning(bool run) 0042 { 0043 if(run == m_running) 0044 return; 0045 m_running= run; 0046 emit runningChanged(); 0047 } 0048 0049 bool SpacingController::running() const 0050 { 0051 return m_running; 0052 } 0053 0054 void SpacingController::computeInLoop() 0055 { 0056 while(m_running) 0057 { 0058 auto const& packages= m_model->items(MindItem::PackageType); 0059 QList<PositionedItem*> packagedChildren; 0060 for(auto item : packages) 0061 { 0062 auto pack= dynamic_cast<PackageNode*>(item); 0063 packagedChildren.append(pack->children()); 0064 } 0065 0066 auto const& items= m_model->items(MindItem::NodeType); 0067 0068 std::vector<PositionedItem*> allNodes; 0069 allNodes.reserve(items.size()); 0070 std::transform(std::begin(items), std::end(items), std::back_inserter(allNodes), 0071 [](MindItem* item) { return dynamic_cast<PositionedItem*>(item); }); 0072 0073 allNodes.erase(std::remove_if(std::begin(allNodes), std::end(allNodes), 0074 [packagedChildren](PositionedItem* item) 0075 { return packagedChildren.contains(item); }), 0076 std::end(allNodes)); 0077 0078 for(auto& node : allNodes) 0079 { 0080 applyCoulombsLaw(node, allNodes); 0081 } 0082 auto const allLinks= m_model->items(MindItem::LinkType); 0083 for(auto& item : allLinks) 0084 { 0085 auto link= dynamic_cast<LinkController*>(item); 0086 if(!link) 0087 continue; 0088 0089 if(!link->constraint()) 0090 continue; 0091 0092 applyHookesLaw(link); 0093 } 0094 for(auto& node : allNodes) 0095 { 0096 node->setVelocity(node->getVelocity() * k_defaultDamping); 0097 if(!node->isDragged()) 0098 node->setPosition(node->position() + node->getVelocity().toPointF()); 0099 } 0100 std::this_thread::sleep_for(std::chrono::milliseconds(100)); 0101 } 0102 0103 Q_EMIT finished(); 0104 } 0105 0106 void SpacingController::applyCoulombsLaw(PositionedItem* node, std::vector<PositionedItem*> nodeList) 0107 { 0108 auto globalRepulsionForce= QVector2D(); 0109 for(auto const& otherNode : nodeList) 0110 { 0111 if(node == otherNode) 0112 continue; 0113 0114 auto vect= QVector2D(node->position() - otherNode->position()); 0115 auto length= vect.length(); 0116 auto force= k_repulsion / std::pow(length, 2); 0117 0118 globalRepulsionForce+= vect.normalized() * force; 0119 } 0120 0121 node->setVelocity(node->getVelocity() + globalRepulsionForce); 0122 } 0123 0124 void SpacingController::applyHookesLaw(LinkController* link) 0125 { 0126 if(!link) 0127 return; 0128 0129 auto node1= link->start(); 0130 auto node2= link->end(); 0131 0132 if(node1 == nullptr || node2 == nullptr) 0133 return; 0134 0135 auto p1= node1->position(); 0136 auto p2= node2->position(); 0137 0138 if(qIsNaN(p1.x()) && qIsNaN(p1.y())) 0139 { 0140 p1= {0, 0}; 0141 } 0142 if(qIsNaN(p2.x()) && qIsNaN(p2.y())) 0143 { 0144 p2= {1, 1}; 0145 } 0146 0147 auto vect= QVector2D(p1 - p2); 0148 auto length= vect.length(); 0149 // auto force= k_attraction * std::max(length - k_defaultSpringLength, 0.f); 0150 auto force= k_attraction * std::max(length - link->getLength(), 0.1f); 0151 0152 node1->setVelocity(node1->getVelocity() + vect.normalized() * force * -1); 0153 node2->setVelocity(node2->getVelocity() + vect.normalized() * force); 0154 } 0155 } // namespace mindmap