170 lines
5.5 KiB
C++
170 lines
5.5 KiB
C++
|
|
#pragma once
|
||
|
|
|
||
|
|
#include "datatypes.h"
|
||
|
|
#include "node.h"
|
||
|
|
#include "connection.h"
|
||
|
|
#include <map>
|
||
|
|
#include <random>
|
||
|
|
#include <ranges>
|
||
|
|
|
||
|
|
class GraphManager{
|
||
|
|
|
||
|
|
public:
|
||
|
|
|
||
|
|
GraphManager() {}
|
||
|
|
|
||
|
|
std::map<int, Node> nodes;
|
||
|
|
std::vector<Connection> connections;
|
||
|
|
|
||
|
|
std::vector<int> keys;
|
||
|
|
int iterator = 0;
|
||
|
|
|
||
|
|
std::map<int, int> counter;
|
||
|
|
|
||
|
|
int getRandomNode(){
|
||
|
|
std::vector<int> o;
|
||
|
|
std::ranges::sample(keys, std::back_inserter(o), 1, std::mt19937{std::random_device{}()});
|
||
|
|
return o.front();
|
||
|
|
}
|
||
|
|
|
||
|
|
void copyNodes(Vec2D<float> position){
|
||
|
|
for(auto n: nodes | std::ranges::views::filter([](std::pair<int, Node> n){ return n.second.copied;})){
|
||
|
|
n.second.position = position + n.second.drag_offset;
|
||
|
|
n.second.id = iterator;
|
||
|
|
n.second.copied = false;
|
||
|
|
n.second.selected = true;
|
||
|
|
nodes.insert(std::make_pair(iterator, n.second));
|
||
|
|
keys.push_back(iterator);
|
||
|
|
iterator++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int copyNode(int id, Vec2D<float> position){
|
||
|
|
Node n = nodes.at(id);
|
||
|
|
n.position = position;
|
||
|
|
n.id = iterator;
|
||
|
|
nodes.insert(std::make_pair(iterator, n));
|
||
|
|
keys.push_back(iterator);
|
||
|
|
iterator++;
|
||
|
|
return iterator - 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
void setNodeMode(int mode){
|
||
|
|
for(auto &n : nodes | std::ranges::views::values){
|
||
|
|
n.mode = mode;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int addNode(float x, float y){
|
||
|
|
return addNode(Vec2D<float>(x,y));
|
||
|
|
}
|
||
|
|
|
||
|
|
int addNode(Vec2D<float> position){
|
||
|
|
nodes.emplace(std::make_pair(iterator, Node(iterator, position, 15.0f)));
|
||
|
|
keys.push_back(iterator);
|
||
|
|
iterator++;
|
||
|
|
return iterator - 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
void triggerNode(int node){
|
||
|
|
nodes.at(node).trigger();
|
||
|
|
}
|
||
|
|
|
||
|
|
void reset(){
|
||
|
|
counter.clear();
|
||
|
|
}
|
||
|
|
|
||
|
|
int next(int current, int last){
|
||
|
|
std::vector<Connection*> possible_connections;
|
||
|
|
std::vector<int> next_node_ids;
|
||
|
|
|
||
|
|
for(auto& c : connections){
|
||
|
|
if((c.v1 == current && c.v1_v2) || (c.v2 == current && c.v2_v1)) {
|
||
|
|
c.active = false;
|
||
|
|
possible_connections.push_back(&c);
|
||
|
|
next_node_ids.push_back( c.v1 == current ? c.v2 : c.v1 );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if(possible_connections.empty()) return -1;
|
||
|
|
|
||
|
|
int return_path = 0;
|
||
|
|
float angle = -1;
|
||
|
|
if(nodes.at(current).distributionMode == Distribution::GEOMETRIC){
|
||
|
|
Vec2D<float> coming_from = (nodes.at(current).position - nodes.at(last).position).norm();
|
||
|
|
int i = 0;
|
||
|
|
for(auto n : possible_connections){
|
||
|
|
int next_position = n->v1 == current ? n->v2 : n->v1;
|
||
|
|
float angle_v1_v2 = coming_from.norm().dot((nodes.at(next_position).position - nodes.at(current).position).norm());
|
||
|
|
if(angle_v1_v2 > angle) {
|
||
|
|
return_path = i;
|
||
|
|
angle = angle_v1_v2;
|
||
|
|
}
|
||
|
|
++i;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if(nodes.at(current).distributionMode == Distribution::ROUNDROBIN){
|
||
|
|
if(counter.find(current) == counter.end()) {
|
||
|
|
counter[current] = 0;
|
||
|
|
} else {
|
||
|
|
counter[current] = (counter[current] + 1) % possible_connections.size();
|
||
|
|
return_path = counter[current];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if(nodes.at(current).distributionMode == Distribution::RANDOM){
|
||
|
|
return_path = rand() % possible_connections.size();
|
||
|
|
}
|
||
|
|
|
||
|
|
possible_connections.at(return_path)->active = true;
|
||
|
|
return possible_connections.at(return_path)->v1 == current ? possible_connections.at(return_path)->v2 : possible_connections.at(return_path)->v1;
|
||
|
|
}
|
||
|
|
|
||
|
|
void addConnection(int from, int to){
|
||
|
|
connections.emplace_back(&nodes, from, to);
|
||
|
|
}
|
||
|
|
|
||
|
|
void drawConnections(Vec2D<float> *mouse_pos, Camera2D *cam){
|
||
|
|
std::erase_if(connections, [](Connection c){ return c.remove == true;});
|
||
|
|
for(auto& c : connections){
|
||
|
|
c.draw(mouse_pos, cam);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
void drawNodes(Vec2D<float> *mouse_pos, Camera2D *camera){
|
||
|
|
auto neighbors = nodes | std::ranges::views::values | std::ranges::views::filter([](Node n){ return !n.dragging && !n.selected;}) | std::ranges::to<std::vector<Node>>();
|
||
|
|
float min_d = 480;
|
||
|
|
for(auto& n : nodes | std::ranges::views::values ){
|
||
|
|
if(n.dragging){
|
||
|
|
std::sort(neighbors.begin(), neighbors.end(), [n](Node na, Node nb){ return na.position.dist(n.position) < nb.position.dist(n.position);});
|
||
|
|
if(neighbors.size() > 1){
|
||
|
|
min_d = neighbors.front().position.dist(n.position);
|
||
|
|
n.neighbor = &nodes.at(neighbors.at(1).id);
|
||
|
|
int i = 0;
|
||
|
|
for(auto it = (neighbors.begin()); it != neighbors.end(); it++, i++){
|
||
|
|
float d = it->position.dist(n.position);
|
||
|
|
if(d > 0 && d < min_d*1.5 && d < 960) {
|
||
|
|
std::string frac = MIDI::Fractions.at((int)Clamp(round(d/60) - 1, 0, 31));
|
||
|
|
Vector2 text_length = MeasureTextEx(GetFontDefault(), std::format("{:.1f}",d).c_str(), 10.0f, 2.0f);
|
||
|
|
Vector2 text_length_frac = MeasureTextEx(GetFontDefault(), frac.c_str(), 10.0f, 2.0f);
|
||
|
|
|
||
|
|
Vec2D<float> middle_handle = it->position + (n.position - it->position) * ((d - text_length.x)*0.5f)/d;
|
||
|
|
Vec2D<float> middle_handle_frac = it->position + (n.position - it->position) * ((d - text_length_frac.x)*0.5f)/d;
|
||
|
|
Vec2D<float> text_position = middle_handle + (it->position - n.position).norm().orth() * 12.0f;
|
||
|
|
Vec2D<float> text_position_frac = middle_handle_frac - (it->position - n.position).norm().orth() * 4.0f;
|
||
|
|
|
||
|
|
float rotation = Vector2Angle((Vector2){1,0}, (n.position - it->position)) * RAD2DEG;
|
||
|
|
DrawTextPro(GetFontDefault(), std::format("{:.1f}",d).c_str(), text_position, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK);
|
||
|
|
if(i == 0 && IsKeyDown(KEY_LEFT_SHIFT)) DrawTextPro(GetFontDefault(), frac.c_str(), text_position_frac, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK);
|
||
|
|
DrawLineV(n.position, it->position, VS_COLOR_PINK);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
n.draw(mouse_pos, camera);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
};
|
||
|
|
|