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