node deletion with connection deletion and signal redistribution implemented

This commit is contained in:
Sebastian
2024-10-26 02:14:41 +02:00
parent 658bc118ef
commit c25d5b2a1a
10 changed files with 167 additions and 84 deletions
+14 -11
View File
@@ -1,6 +1,7 @@
#pragma once
#include "connection.h"
#include "node-manager.h"
#include "raylib.h"
#include <map>
#include <vector>
@@ -9,40 +10,42 @@ class ConnectionManager{
public:
ConnectionManager() {}
ConnectionManager(NodeManager *nm) : nm(nm) {}
NodeManager *nm;
std::vector<Connection> connections;
std::map<Node*, int> counter;
std::map<int, int> counter;
void reset(){
counter.clear();
}
Node* next(Node* current, Node* last){
int next(int current, int last){
std::vector<Connection*> paths;
std::vector<Node*> nodes;
std::vector<int> node_ids;
for(auto& p : connections){
if((p.v1 == current && p.v1_v2) || (p.v2 == current && p.v2_v1)) {
p.active = false;
paths.push_back(&p);
nodes.push_back( p.v1 == current ? p.v2 : p.v1 );
node_ids.push_back( p.v1 == current ? p.v2 : p.v1 );
}
}
if(paths.size() == 2) {
Node* next = nodes[0] == last ? nodes[1] : nodes[0];
int next = node_ids[0] == last ? node_ids[1] : node_ids[0];
if(paths[0]->v1 == next || paths[0]->v2 == next) paths[0]->active = true;
if(paths[1]->v1 == next || paths[1]->v2 == next) paths[1]->active = true;
return next;
}
if(paths.empty()) return nullptr;
if(paths.empty()) return -1;
int return_path = 0;
if(current->mode == Distribution::ROUNDROBIN){
if(nm->nodes.at(current).mode == Distribution::ROUNDROBIN){
if(counter.find(current) == counter.end()) {
counter[current] = 0;
} else {
@@ -50,15 +53,15 @@ class ConnectionManager{
return_path = counter[current];
}
}
if(current->mode == Distribution::RANDOM){
if(nm->nodes.at(current).mode == Distribution::RANDOM){
return_path = rand() % paths.size();
}
paths.at(return_path)->active = true;
return paths.at(return_path)->v1 == current ? paths.at(return_path)->v2 : paths.at(return_path)->v1;
}
void addConnection(Node *from, Node *to){
connections.emplace_back(from, to);
void addConnection(int from, int to){
connections.emplace_back(&nm->nodes, from, to);
}
void drawConnections(Vec2D<float> *mouse_pos, Camera2D *cam){
+12 -11
View File
@@ -7,11 +7,12 @@
class Connection{
public:
Connection() {}
Connection(Node* v1, Node* v2) : v1(v1), v2(v2) {}
Connection(std::map<int, Node> *nodes, int v1, int v2) : v1(v1), v2(v2), nodes(nodes) {}
Node* v1;
Node* v2;
int v1;
int v2;
std::map<int, Node> *nodes;
bool v1_v2 = true;
bool v2_v1 = true;
@@ -23,14 +24,14 @@ class Connection{
float switches_offset = 25.0f;
void flip(){
Node* vt = v1;
int vt = v1;
v1 = v2;
v2 = vt;
}
void draw(Vec2D<float> *mouse_pos, Camera2D *cam){
Vec2D<float> v1_position = v1->position - ((v1->position - v2->position)).norm() * (v1->radius + connector_offset);
Vec2D<float> v2_position = v2->position - ((v2->position - v1->position)).norm() * (v2->radius + connector_offset);
Vec2D<float> v1_position = nodes->at(v1).position - ((nodes->at(v1).position - nodes->at(v2).position)).norm() * (nodes->at(v1).radius + connector_offset);
Vec2D<float> v2_position = nodes->at(v2).position - ((nodes->at(v2).position - nodes->at(v1).position)).norm() * (nodes->at(v2).radius + connector_offset);
float v1_v2_distance = v1_position.dist(&v2_position);
// Line with small connector circles
@@ -41,10 +42,10 @@ class Connection{
DrawCircle(v2_position.x, v2_position.y, 2.0f, c);
if(v1_v2_distance < 100) switches_offset = v1_v2_distance/2.3f;
Vec2D<float> from_to_handle = v1->position - ((v1->position - v2->position)).norm() * (v1->radius + switches_offset);
Vec2D<float> to_from_handle = v2->position - ((v2->position - v1->position)).norm() * (v2->radius + switches_offset);
Vec2D<float> middle_handle = v2->position - ((v2->position - v1->position))*0.5f;
// Vec2D<float> config_handle = v2->position - ((v2->position - v1->position))*0.66f;
Vec2D<float> from_to_handle = nodes->at(v1).position - ((nodes->at(v1).position - nodes->at(v2).position)).norm() * (nodes->at(v1).radius + switches_offset);
Vec2D<float> to_from_handle = nodes->at(v2).position - ((nodes->at(v2).position - nodes->at(v1).position)).norm() * (nodes->at(v2).radius + switches_offset);
Vec2D<float> middle_handle = nodes->at(v2).position - ((nodes->at(v2).position - nodes->at(v1).position))*0.5f;
// Vec2D<float> config_handle = nodes.at(v2)->position - ((nodes.at(v2)->position - nodes.at(v1)->position))*0.66f;
Vec2D<float> flip_handle = middle_handle + (v1_position - v2_position).norm().orth() * (v1_v2_distance < 120.0f ? Clamp((120.0f - v1_v2_distance)*0.2, 0.f, 20.f) : 1.0f);
+19 -19
View File
@@ -50,7 +50,7 @@ int main(){
app.ui.init();
int monitor = 0; //GetCurrentMonitor();
int monitor = 1; //GetCurrentMonitor();
SetWindowMonitor(monitor);
@@ -83,10 +83,10 @@ int main(){
}
for(int i = 0; i < 8; ++i){
app.cm.addConnection(&app.nm.nodes[i], &app.nm.nodes[(i+1) % 8]);
app.cm.addConnection(app.nm.nodes[i].id, app.nm.nodes[(i+1) % 8].id);
}
app.sm.addSignalAtNode(&app.nm.nodes.front());
app.sm.addSignalAtNode((app.nm.nodes | std::views::values).front().id);
app.sm.startSignalThread();
@@ -110,7 +110,7 @@ int main(){
BeginDrawing();
ClearBackground((Color){46,46,46});
BeginMode2D(camera);
//app.settings.mutex.lock();
app.settings.speed = (app.settings.grid * 8.0f) * (app.settings.bpm / 120.f);
Vector2 mp = GetScreenToWorld2D(GetMousePosition(), camera);
@@ -128,11 +128,11 @@ int main(){
Vec2D<float> mouse_delta = {GetMouseDelta().x, GetMouseDelta().y};
Vec2D<float> delta = mouse_delta * (-1.0f/camera.zoom);
auto dragging_nodes = app.nm.nodes | std::ranges::views::filter([](Node n) { return n.dragging;});
auto connecting_nodes = app.nm.nodes | std::ranges::views::filter([](Node n) { return n.connecting;});
auto selected_nodes = app.nm.nodes | std::ranges::views::filter([](Node n) { return n.selected && !n.dragging;});
auto hovering_nodes = app.nm.nodes | std::ranges::views::filter([&mouse_pos](Node n) { return CheckCollisionPointCircle(mouse_pos, n.position, n.radius);});
auto hovering_connectors = app.nm.nodes | std::ranges::views::filter([&mouse_pos](Node n) { return CheckCollisionPointCircle(mouse_pos, (n.position - ((n.position - mouse_pos)).norm() * (n.radius + 14.0f)), 8.0f);});
auto dragging_nodes = app.nm.nodes | std::ranges::views::values | std::ranges::views::filter([](Node n) { return n.dragging;});
auto connecting_nodes = app.nm.nodes | std::ranges::views::values| std::ranges::views::filter([](Node n) { return n.connecting;});
auto selected_nodes = app.nm.nodes | std::ranges::views::values| std::ranges::views::filter([](Node n) { return n.selected && !n.dragging;});
auto hovering_nodes = app.nm.nodes | std::ranges::views::values| std::ranges::views::filter([&mouse_pos](Node n) { return CheckCollisionPointCircle(mouse_pos, n.position, n.radius);});
auto hovering_connectors = app.nm.nodes | std::ranges::views::values| std::ranges::views::filter([&mouse_pos](Node n) { return CheckCollisionPointCircle(mouse_pos, (n.position - ((n.position - mouse_pos)).norm() * (n.radius + 14.0f)), 8.0f);});
if(!mouse_down && !mouse_right_down && dragging_nodes.empty() && connecting_nodes.empty() && hovering_nodes.empty() && hovering_connectors.empty()) istate = InteractionState::HOVER_CANVAS;
if((mouse_clicked || mouse_down || mouse_released) && !mouse_right_down && dragging_nodes.empty() && connecting_nodes.empty() && hovering_nodes.empty() && hovering_connectors.empty()) istate = InteractionState::DRAW_SELECTION;
@@ -163,7 +163,7 @@ int main(){
DrawRectangleLines(select1.x, select1.y, select2.x, select2.y, (Color){150,150,150,150});
selection = (Rectangle){select1.x, select1.y, select2.x, select2.y};
for(auto& n : app.nm.nodes){
for(auto& n : app.nm.nodes | std::views::values){
if(CheckCollisionPointRec(n.position, selection)) {
n.in_rect = true;
} else {
@@ -173,7 +173,7 @@ int main(){
}
if(mouse_released){
for(auto& n : app.nm.nodes){
for(auto& n : app.nm.nodes | std::views::values){
if(CheckCollisionPointRec(n.position, selection)) {
n.selected = true;
n.in_rect = false;
@@ -236,24 +236,25 @@ int main(){
if(mouse_released) {
if(&hovering_nodes.front() != &connecting_nodes.front() && &connecting_nodes.front() != &hovering_connectors.front()){
if(!hovering_nodes.empty()){
app.cm.addConnection(&connecting_nodes.front(), &hovering_nodes.front());
app.cm.addConnection(connecting_nodes.front().id, hovering_nodes.front().id);
} else if(!hovering_connectors.empty()){
app.cm.addConnection(&connecting_nodes.front(), &hovering_connectors.front());
app.cm.addConnection(connecting_nodes.front().id, hovering_connectors.front().id);
} else {
app.nm.addNode(mouse_pos.x, mouse_pos.y);
app.cm.addConnection(&connecting_nodes.front(), &app.nm.nodes.back());
int id = app.nm.addNode(mouse_pos.x, mouse_pos.y);
app.cm.addConnection(connecting_nodes.front().id, id);
}
}
connecting_nodes.front().connecting = false;
istate = InteractionState::HOVER_CANVAS;
}
break;
}
case InteractionState::DELETE_SELECTED_NODES:
{
for(auto &n : app.nm.nodes){
for(auto &n : selected_nodes){
n.erase = true;
}
app.nm.removeNodes();
app.removeNodes();
break;
}
};
@@ -279,8 +280,7 @@ int main(){
DrawPixel(x, y, (Color){130,130,130,200});
}
}
app.draw(&mouse_pos, &camera);
EndMode2D();
+21 -13
View File
@@ -1,31 +1,39 @@
#pragma once
#include "node.h"
#include <iterator>
#include <random>
#include <ranges>
#include <vector>
class NodeManager{
public:
NodeManager() {
nodes.reserve(1000);
NodeManager() {}
std::map<int, Node> nodes;
std::vector<int> keys;
int iterator = 0;
int getRandomNode(){
std::vector<int> o;
std::ranges::sample(keys, std::back_inserter(o), 1, std::mt19937{std::random_device{}()});
return o.front();
}
std::vector<Node> nodes;
void addNode(float x, float y){
addNode(Vec2D<float>(x,y));
int addNode(float x, float y){
return addNode(Vec2D<float>(x,y));
}
void addNode(Vec2D<float> position){
nodes.emplace_back(position, 15.0f);
}
void removeNodes(){
std::erase_if(nodes, [](Node n){ return n.erase;});
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 drawNodes(Vec2D<float> *mouse_pos, Camera2D *camera){
for(auto& n : nodes){
for(auto& n : nodes | std::ranges::views::values ){
n.draw(mouse_pos, camera);
}
}
+9 -3
View File
@@ -19,11 +19,13 @@ class Node : public Movable{
public:
Node() {}
Node(float x, float y, float r) : Movable(Vec2D<float>(x, y), 0.0f), radius(r) {}
Node(Vec2D<float> p, float r) : Movable(p, 0.0f), radius(r) {}
Node(int id, float x, float y, float r) : Movable(Vec2D<float>(x, y), 0.0f), radius(r), id(id) {}
Node(int id, Vec2D<float> p, float r) : Movable(p, 0.0f), radius(r), id(id) {}
float radius;
int id;
bool picked_up = false;
bool dragging = false;
bool in_rect = false;
@@ -33,6 +35,7 @@ class Node : public Movable{
bool config = false;
bool selected = false;
bool erase = false;
bool highlighted = false;
int midi_out = -1;
@@ -60,7 +63,9 @@ class Node : public Movable{
void draw(Vec2D<float> *mouse, Camera2D *cam) {
if(!dragging && !selected && !in_rect && !hovering) DrawCircle(position.x, position.y, radius, (Color){180, 210, 115, 255});
if(dragging || selected || in_rect || hovering) DrawCircle(position.x, position.y, radius, (Color){108, 153, 187, 255});
if(selected || dragging || in_rect || hovering) DrawCircle(position.x, position.y, radius, (Color){108, 153, 187, 255});
if(selected) DrawCircleLinesV(position, radius+1, (Color){158, 255, 210, 255});
int offset = MeasureText(MIDI::Notes[midi_note].c_str(), 10);
DrawText(MIDI::Notes[midi_note].c_str(), position.x-offset/2.0f, position.y-5, 10, WHITE );
@@ -76,5 +81,6 @@ class Node : public Movable{
DrawCircleLines(position.x, position.y, radius + R, (Color){108, 153, 187, (unsigned char)opacity});
DrawCircle(connector.x, connector.y, 8.0f, (Color){108, 153, 187, (unsigned char)opacity});
}
if(highlighted) DrawCircleLinesV(position, radius+2, (Color){255, 0, 0, 200});
}
};
+16 -14
View File
@@ -2,6 +2,7 @@
#include "connection-manager.h"
#include "datatypes.h"
#include "node-manager.h"
#include "signal.h"
#include "node.h"
#include "midi.h"
@@ -13,12 +14,13 @@ class SignalManager{
public:
SignalManager() {}
SignalManager(ConnectionManager *cm, std::mutex *mutex) : mutex(mutex), cm(cm) {}
SignalManager(NodeManager *nm, ConnectionManager *cm, std::mutex *mutex) : mutex(mutex), cm(cm), nm(nm) {}
Midi midi;
std::mutex *mutex;
ConnectionManager *cm;
NodeManager *nm;
std::vector<Signal> signals;
@@ -40,7 +42,7 @@ class SignalManager{
void reset(){
for(auto &s : signals){
s.position = s.start->position + Vec2D<float>{0.1, 0.1};
s.position = nm->nodes[s.start].position + Vec2D<float>{0.1, 0.1};
s.next = s.start;
s.last = s.start;
}
@@ -56,12 +58,12 @@ class SignalManager{
loop = false;
}
void addSignalAtNode(Node *node){
Color c = {(unsigned char)(232 - (rand() % 120)), (unsigned char)(125 + (int)(rand() % 120)), 62, 255};
signals.emplace(signals.end(), Signal(node->position.x+1, node->position.y+1, 5.0f, 0, c)); // position + 1 or else it does not render. why? no idea!
signals.back().next = node;
signals.back().last = node;
signals.back().start = node;
void addSignalAtNode(int node_id){
Color c = {(unsigned char)(232 - (rand() % 120)), 50, (unsigned char)(20 + (int)(rand() % 120)), 255};
signals.emplace(signals.end(), Signal(nm->nodes[node_id].position.x+1, nm->nodes[node_id].position.y+1, 5.0f, 0, c)); // position + 1 or else it does not render. why? no idea!
signals.back().next = node_id;
signals.back().last = node_id;
signals.back().start = node_id;
}
void removeSignal(int n){
@@ -76,14 +78,14 @@ class SignalManager{
mutex->lock();
if(play){
for(auto& s : signals){
s.direction = (s.next->position - s.position).norm();
s.direction = (nm->nodes[s.next].position - s.position).norm();
s.update(diff.count());
if(s.position.dist(&s.next->position) < 1.0f) {
Node* n = cm->next(s.next, s.last);
if(s.position.dist(&nm->nodes[s.next].position) < 1.0f) {
int n = cm->next(s.next, s.last);
if(n != s.next && n != nullptr) {
for(auto &m : s.next->midi_outs){
if(m.second.enabled) midi.note(m.second.i, s.next->midi_channel, s.next->midi_note, s.next->midi_velocity, s.next->midi_gate);
if(n != s.next && n != -1) {
for(auto &m : nm->nodes[s.next].midi_outs){
if(m.second.enabled) midi.note(m.second.i, nm->nodes[s.next].midi_channel, nm->nodes[s.next].midi_note, nm->nodes[s.next].midi_velocity, nm->nodes[s.next].midi_gate);
}
s.last = s.next;
s.next = n;
+3 -4
View File
@@ -4,7 +4,6 @@
#include "datatypes.h"
#include "moveable.h"
#include "node.h"
class Signal : public Movable{
public:
@@ -14,10 +13,10 @@ class Signal : public Movable{
int id = 0;
Node *next;
Node *last;
int next;
int last;
Node *start;
int start;
bool at_target = false;
int target_node_id = 1;
+65 -2
View File
@@ -1,15 +1,21 @@
#pragma once
#include "connection.h"
#include "datatypes.h"
#include "node-manager.h"
#include "connection-manager.h"
#include "signal-manager.h"
#include "ui.h"
#include <algorithm>
#include <iostream>
#include <iterator>
#include <ranges>
#include <vector>
class AppState{
public:
AppState() : sm(&cm, &mutex), ui(&settings, &nm, &cm, &sm) {}
AppState() : cm(&nm), sm(&nm, &cm, &mutex), ui(&settings, &nm, &cm, &sm) {}
enum States {IDLE, CONFIG};
@@ -26,7 +32,7 @@ class AppState{
AppUI ui;
void updateMidiPorts(){
for(auto&n : nm.nodes){
for(auto&n : nm.nodes | std::views::values){
for(auto& mo : sm.midi.midi_out_ports){
if(!n.midi_outs.contains(mo.number) && mo.enabled){
n.midi_outs[mo.number] = {mo.number, mo.port.port_name, false};
@@ -38,6 +44,63 @@ class AppState{
}
}
void removeNodes(){
for(auto &n : nm.nodes){
n.second.highlighted = false;
}
auto erase_nodes = nm.nodes | std::ranges::views::values | std::ranges::views::filter([](Node n){return n.erase;});
std::vector<int> erase_nodes_ids;
for(auto &e : erase_nodes){
erase_nodes_ids.push_back(e.id);
}
auto erase_connections = cm.connections | std::ranges::views::filter([erase_nodes_ids](Connection c){ return std::ranges::contains(erase_nodes_ids, c.v1) || std::ranges::contains(erase_nodes_ids, c.v2);});
std::vector<int> adjacent_nodes_ids;
for(auto &c : erase_connections){
if(!nm.nodes.at(c.v1).selected) adjacent_nodes_ids.push_back(c.v1);
if(!nm.nodes.at(c.v2).selected) adjacent_nodes_ids.push_back(c.v2);
}
// std::ranges::for_each(adjacent_nodes_ids, [this](int id){ nm.nodes.at(id).highlighted = true;} );
auto reroute_signals = sm.signals | std::ranges::views::filter([erase_nodes_ids](Signal s){
return std::ranges::contains(erase_nodes_ids, s.next);
});
for(auto &s : reroute_signals){
float dist = 10000000;
int next_id = -1;
for(int i : adjacent_nodes_ids){
if(nm.nodes.at(i).position.dist(&s.position) < dist) {
dist = nm.nodes.at(i).position.dist(&s.position);
next_id = i;
}
}
s.next = next_id;
s.last = next_id;
}
auto rebase_signals = sm.signals | std::ranges::views::filter([erase_nodes_ids](Signal s){
return std::ranges::contains(erase_nodes_ids, s.start);
});
for(auto &s : rebase_signals){
s.start = s.next;
}
for(auto &n : erase_nodes){
std::erase_if(cm.connections, [n](Connection c){ return c.v1 == n.id || c.v2 == n.id;});
}
std::erase_if(nm.nodes, [](std::pair<int, Node> n){ return n.second.erase;});
nm.keys.clear();
std::ranges::copy(nm.nodes | std::ranges::views::keys, std::back_inserter(nm.keys));
}
void draw(Vec2D<float> *mouse_pos, Camera2D *camera){
mutex.lock();
updateMidiPorts();
+7 -6
View File
@@ -53,7 +53,7 @@ class AppUI{
for(auto &midis : sm->midi.midi_out_ports){
if(midis.enabled){
for(auto &n : nm->nodes){
n.setMidiPort(midis.number, true);
n.second.setMidiPort(midis.number, true);
}
}
}
@@ -62,8 +62,8 @@ class AppUI{
int s_signal = s_midi + 90 + i*20;
GuiLabel((Rectangle){20, (float)s_signal, 240, 20}, "SIGNALS");
if(GuiButton((Rectangle){20, (float)s_signal + 35, 240, 20}, "ADD RANDOM SIGNAL")){
int ni = rand() % nm->nodes.size();
sm->addSignalAtNode(&nm->nodes[ni]);
int ni = nm->getRandomNode();
sm->addSignalAtNode(ni);
}
int signal_configs = 0;
@@ -81,7 +81,8 @@ class AppUI{
int s_nodes = s_signal + 60 + signal_configs*20;
GuiLabel((Rectangle){20, (float)s_nodes + 30, 240, 20}, "NODES");
if(GuiButton((Rectangle){20, (float)s_nodes + 60, 240, 20}, "ADD RANDOM NODE")){
nm->nodes.emplace_back(200.0f, 200.0f, 15.0f);
nm->addNode(200.0f, 200.0f);
// nm->nodes.emplace_back(200.0f, 200.0f, 15.0f);
}
DrawFPS(20, settings->height - 30);
@@ -89,7 +90,7 @@ class AppUI{
}
void drawPerNodeConfig(Camera2D *camera){
for(auto& n : nm->nodes | std::ranges::views::filter([](Node n){ return n.config; })){
for(auto& n : nm->nodes | std::ranges::views::values | std::views::filter([](Node n){ return n.config; })){
Vector2 np = GetWorldToScreen2D((Vector2){n.position.x, n.position.y}, *camera);
n.config = !GuiWindowBox((Rectangle){np.x + 10, np.y + 10, 240, 440}, "config");
@@ -100,7 +101,7 @@ class AppUI{
GuiLabel((Rectangle){np.x + 20, np.y + 65, 220, 20}, "SIGNALS");
if(GuiButton((Rectangle){np.x + 20, np.y + 90, 220, 20}, "ADD SIGNAL")){
sm->addSignalAtNode(&n);
sm->addSignalAtNode(n.id);
}
int y_midi = 120;