From 19b2c9549fd949b1a8d5aa3d1ece1cf7861d043a Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 1 Nov 2024 02:38:47 +0100 Subject: [PATCH] conversion to smart pointers done but still buggy --- src/button.h | 6 +- src/connection-manager.h | 88 --------------- src/datatypes.h | 5 +- src/edge.h | 7 ++ src/graph-item.h | 3 + src/graph-manager.h | 112 +++++++++++++------- src/main.cpp | 223 ++++++++++++++++++++------------------- src/midi-node.h | 16 ++- src/moveable.h | 1 + src/node-manager.h | 106 ------------------- src/node.h | 14 ++- src/signal-manager.h | 53 +++++----- src/signal.h | 17 ++- src/state-manager.h | 103 +++++++++--------- src/ui.h | 159 ++++++++++++++-------------- 15 files changed, 389 insertions(+), 524 deletions(-) delete mode 100644 src/connection-manager.h delete mode 100644 src/node-manager.h diff --git a/src/button.h b/src/button.h index c86fcfa..1d1bb84 100644 --- a/src/button.h +++ b/src/button.h @@ -4,14 +4,12 @@ #include "raylib.h" #include "raymath.h" -inline bool ViButton(Vec2D *mouse_pos, float x, float y, float radius, Color color){ - Vector2 mp = {mouse_pos->x, mouse_pos->y}; //GetMousePosition(); TODO: Refactor my own vectors to raylib vectors +inline bool ViButton(Vec2D mp, float x, float y, float radius, Color color){ DrawCircle(x,y,radius, color); return (Vector2Distance(mp, (Vector2){x,y}) < radius && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)); } -inline bool ViSegButton(Vec2D *mouse_pos, float x, float y, float start_angle, float end_angle, float radius, Color color){ - Vector2 mp = {mouse_pos->x, mouse_pos->y}; //GetMousePosition(); TODO: Refactor my own vectors to raylib vectors +inline bool ViSegButton(Vec2D mp, float x, float y, float start_angle, float end_angle, float radius, Color color){ DrawCircleSector((Vector2){x,y},radius, start_angle, end_angle, 5, color); return (Vector2Distance(mp, (Vector2){x,y}) < radius && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)); } diff --git a/src/connection-manager.h b/src/connection-manager.h deleted file mode 100644 index da8f052..0000000 --- a/src/connection-manager.h +++ /dev/null @@ -1,88 +0,0 @@ -#pragma once - -#include "edge.h" -#include "node-manager.h" -#include "raylib.h" -#include -#include - -class ConnectionManager{ - - public: - - ConnectionManager(NodeManager *nm) : nm(nm) {} - - NodeManager *nm; - - std::vector connections; - - std::map counter; - - void reset(){ - counter.clear(); - } - - int next(int current, int last){ - std::vector possible_connections; - std::vector next_node_ids; - - for(auto& c : connections){ - if((c.node_id_start == current && c.start_end) || (c.node_id_end == current && c.end_start)) { - c.active = false; - possible_connections.push_back(&c); - next_node_ids.push_back( c.node_id_start == current ? c.node_id_end : c.node_id_start ); - } - } - - if(possible_connections.empty()) return -1; - - int return_path = 0; - float angle = -1; - if(nm->nodes.at(current).distributionMode == Distribution::GEOMETRIC){ - Vec2D coming_from = (nm->nodes.at(current).position - nm->nodes.at(last).position).norm(); - int i = 0; - for(auto n : possible_connections){ - int next_position = n->node_id_start == current ? n->node_id_end : n->node_id_start; - float angle_v1_v2 = coming_from.norm().dot((nm->nodes.at(next_position).position - nm->nodes.at(current).position).norm()); - if(angle_v1_v2 > angle) { - return_path = i; - angle = angle_v1_v2; - } - ++i; - } - } - - if(nm->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(nm->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)->node_id_start == current ? possible_connections.at(return_path)->node_id_end : possible_connections.at(return_path)->node_id_start; - } - - void addConnection(int from, int to){ - connections.emplace_back(nm, from, to); - } - - void splitConnection(Connection *c){ - Vec2D center = c->node_start->position.center(c->node_end->position); - - } - - void drawConnections(Vec2D *mouse_pos, Camera2D *cam){ - std::erase_if(connections, [](Connection c){ return c.remove == true;}); - for(auto& c : connections){ - if(c.split) splitConnection(&c); - c.draw(mouse_pos, cam); - } - } -}; diff --git a/src/datatypes.h b/src/datatypes.h index 4509e32..4ad058c 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -1,7 +1,5 @@ #pragma once -//#include "connection-manager.h" -//#include "signal-manager.h" #ifdef _WIN32 #define _USE_MATH_DEFINES #endif @@ -172,7 +170,8 @@ struct AppSettings{ // Graph -inline enum GraphItemType {NODE, EDGE} type; +enum GraphItemType {NODE, EDGE}; +enum NodeType {OSCNODE, MIDINODE}; // MIDI diff --git a/src/edge.h b/src/edge.h index 0eade80..91926a8 100644 --- a/src/edge.h +++ b/src/edge.h @@ -8,6 +8,7 @@ class Node; class Edge : public GraphItem{ public: + Edge() {} Edge(std::shared_ptr start, std::shared_ptr end) : start(start), end(end) {} std::shared_ptr start; @@ -24,7 +25,13 @@ class Edge : public GraphItem{ float connector_offset = 8.0f; float switches_offset = 25.0f; + virtual bool GActive() {return active;} + std::shared_ptr getNextNode(std::shared_ptr origin){ return start == origin ? end : start; } + + void draw(Vec2D mouse_pos, Camera2D cam) { + + } }; diff --git a/src/graph-item.h b/src/graph-item.h index d554de1..22222d3 100644 --- a/src/graph-item.h +++ b/src/graph-item.h @@ -10,6 +10,9 @@ class GraphItem{ public: std::vector> neighbors; + GraphItemType graphItemType; + virtual void draw(Vec2D mouse, Camera2D cam) = 0; + virtual GraphItemType getType() {return graphItemType;} virtual ~GraphItem() = default; }; diff --git a/src/graph-manager.h b/src/graph-manager.h index 0d34118..fee8692 100644 --- a/src/graph-manager.h +++ b/src/graph-manager.h @@ -1,10 +1,14 @@ #pragma once +#include "button.h" #include "datatypes.h" #include "graph-item.h" +#include "midi-node.h" #include "node.h" #include "edge.h" +#include "raymath.h" #include +#include #include #include #include @@ -37,31 +41,53 @@ class GraphManager{ void copyNodes(std::vector> nodes, Vec2D position){ for(auto n: nodes){ - auto nc = std::make_shared(n); + auto nc = std::make_shared(*n); nc->position = position + nc->drag_offset; nc->selected = true; nodes.push_back(nc); } } - void copyNode(std::shared_ptr n, Vec2D position){ - auto nc = std::make_shared(n); - nc->setPosition(position); - nodes.push_back(nc); + void copyNodes(std::vector> nodes, Vec2D position){ + for(auto n: nodes){ + std::shared_ptr nc = std::make_shared(*n); + nc->position = position + nc->drag_offset; + nc->selected = true; + nodes.push_back(nc); + } } - std::shared_ptr addNode(float x, float y){ - return addNode(Vec2D(x,y)); + std::shared_ptr copyNode(std::shared_ptr n, Vec2D position){ + auto nc = std::make_shared(*n); + nc->setPosition(position); + nodes.push_back(nc); + return nc; } - - std::shared_ptr addNode(Vec2D position){ - nodes.emplace_back(std::make_shared(position)); + + std::shared_ptr copyNode(std::shared_ptr n, Vec2D position){ + auto nc = std::make_shared(*n); + nc->setPosition(position); + nodes.push_back(nc); + return nc; + } + + /*std::shared_ptr addNode(float x, float y){*/ + /* return addNode(Vec2D(x,y));*/ + /*}*/ + /**/ + /*std::shared_ptr addNode(Vec2D position){*/ + /* nodes.emplace_back(std::make_shared(position));*/ + /* return nodes.back();*/ + /*}*/ + + std::shared_ptr addMidiNode(Vec2D position){ + nodes.emplace_back(std::make_shared(position)); return nodes.back(); } void setNodeMode(int mode){ for(auto n: nodes){ - n->setMode(mode); + n->SMode(mode); } } @@ -110,22 +136,26 @@ class GraphManager{ } void addConnection(std::shared_ptr from, std::shared_ptr to){ - edges.emplace_back(std::make_shared(from, to)); + edges.emplace_back(std::make_shared()); + edges.back()->start = from; + edges.back()->end = to; + from->connections.push_back(edges.back()); + to->connections.push_back(edges.back()); } - void drawConnections(Vec2D *mouse_pos, Camera2D *cam){ + void drawEdges(Vec2D mouse_pos, Camera2D cam){ // std::erase_if(connections, [](Connection c){ return c.remove == true;}); for(auto& c : edges){ Vec2D v1_position = c->start->position - ((c->start->position - c->end->position)).norm() * (c->start->radius + c->connector_offset); Vec2D v2_position = c->end->position - ((c->end->position - c->start->position)).norm() * (c->end->radius + c->connector_offset); float v1_v2_distance = v1_position.dist(&v2_position); - float midi_gate_v1 = Clamp((4.f / 1000.f) * c->start->midi_gate, 1.f, 4.f); - float midi_gate_v2 = Clamp((4.f / 1000.f) * c->end->midi_gate, 1.f, 4.f); + float midi_gate_v1 = Clamp((4.f / 1000.f) * c->start->GGate(), 1.f, 4.f); + float midi_gate_v2 = Clamp((4.f / 1000.f) * c->end->GGate(), 1.f, 4.f); // Line with small connector circles Color col = {229, 181, 103, 255}; - if(c.active) col = {108, 153, 187, 255}; + if(c->GActive()) col = {108, 153, 187, 255}; DrawLine(v1_position.x, v1_position.y, v2_position.x, v2_position.y, col); if(midi_gate_v1 == 4.0f) col = VS_COLOR_RED; @@ -135,9 +165,9 @@ class GraphManager{ if(midi_gate_v2 == 4.0f) col = VS_COLOR_RED; DrawCircle(v2_position.x, v2_position.y, midi_gate_v2, col); - if(v1_v2_distance < 100) c.switches_offset = v1_v2_distance/2.3f; - Vec2D from_to_handle = c->start->position - ((c->start->position - c->end->position)).norm() * (c->start->radius + c.switches_offset); - Vec2D to_from_handle = c->end->position - ((c->end->position - c->start->position)).norm() * (c->end->radius + c.switches_offset); + if(v1_v2_distance < 100) c->switches_offset = v1_v2_distance/2.3f; + Vec2D from_to_handle = c->start->position - ((c->start->position - c->end->position)).norm() * (c->start->radius + c->switches_offset); + Vec2D to_from_handle = c->end->position - ((c->end->position - c->start->position)).norm() * (c->end->radius + c->switches_offset); Vec2D middle_handle = c->end->position - ((c->end->position - c->start->position))*0.4f; Vec2D middle_handle_2 = c->end->position - ((c->end->position - c->start->position))*0.6f; Vec2D 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); @@ -150,58 +180,58 @@ class GraphManager{ unsigned char opacity_to = 250.0f - Clamp(to_from_handle.dist(mouse_pos), 0.0f, 220.0f); // On/Off switches on both sides - if(ViButton(mouse_pos, from_to_handle.x, from_to_handle.y, 8.0f, c->start_end ? (Color){120, 210, 115, opacity_from} : (Color){232, 50, 32, opacity_from} )) c->start_end = !c->start_end; - if(ViButton(mouse_pos, to_from_handle.x, to_from_handle.y, 8.0f, c.end_start ? (Color){120, 210, 115, opacity_to} : (Color){232, 50, 32, opacity_to} )) c.end_start = !c.end_start; + if(ViButton(mouse_pos, from_to_handle.x, from_to_handle.y, 8.0f, c->pass_forward ? (Color){120, 210, 115, opacity_from} : (Color){232, 50, 32, opacity_from} )) c->pass_forward = !c->pass_forward; + if(ViButton(mouse_pos, to_from_handle.x, to_from_handle.y, 8.0f, c->pass_backward ? (Color){120, 210, 115, opacity_to} : (Color){232, 50, 32, opacity_to} )) c->pass_backward = !c->pass_backward; - if(!c->start_end){ + if(!c->pass_forward){ Vec2D block_point1 = (v1_position - v2_position).norm().orth()*4.0f + from_to_handle; Vec2D block_point2 = (v1_position - v2_position).norm().orth()*-4.0f + from_to_handle; DrawLineEx(block_point1, block_point2, 2.0f, (Color){232,232,232,opacity_from}); } - if(!c.end_start){ + if(!c->pass_backward){ Vec2D block_point1 = (v1_position - v2_position).norm().orth()*4.0f + to_from_handle; Vec2D block_point2 = (v1_position - v2_position).norm().orth()*-4.0f + to_from_handle; DrawLineEx(block_point1, block_point2, 2.0f, (Color){232,232,232,opacity_to}); } - if(ViButton(mouse_pos, flip_handle.x, flip_handle.y, 8.0f, (Color){232, 50, 32, opacity} )) c.remove = true; - if(ViButton(mouse_pos, flip_handle_2.x, flip_handle_2.y, 8.0f, (Color){232, 50, 32, opacity} )) c.split = true; + if(ViButton(mouse_pos, flip_handle.x, flip_handle.y, 8.0f, (Color){232, 50, 32, opacity} )) c->remove = true; + if(ViButton(mouse_pos, flip_handle_2.x, flip_handle_2.y, 8.0f, (Color){232, 50, 32, opacity} )) c->split = true; } } - void drawNodes(Vec2D *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>(); + void drawNodes(Vec2D mouse_pos, Camera2D camera){ + auto neighbors = nodes | std::ranges::views::filter([](std::shared_ptr n){ return !n->dragging && !n->selected;}) | std::ranges::to>>(); 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);}); + for(auto n : nodes){ + if(n->dragging){ + std::sort(neighbors.begin(), neighbors.end(), [n](std::shared_ptr na, std::shared_ptr 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); + min_d = neighbors.front()->position.dist(n->position); + n->neighbor = neighbors.at(1); int i = 0; - for(auto it = (neighbors.begin()); it != neighbors.end(); it++, i++){ - float d = it->position.dist(n.position); + for(auto nb : neighbors){ + float d = nb->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 middle_handle = it->position + (n.position - it->position) * ((d - text_length.x)*0.5f)/d; - Vec2D middle_handle_frac = it->position + (n.position - it->position) * ((d - text_length_frac.x)*0.5f)/d; - Vec2D text_position = middle_handle + (it->position - n.position).norm().orth() * 12.0f; - Vec2D text_position_frac = middle_handle_frac - (it->position - n.position).norm().orth() * 4.0f; + Vec2D middle_handle = nb->position + (n->position - nb->position) * ((d - text_length.x)*0.5f)/d; + Vec2D middle_handle_frac = nb->position + (n->position - nb->position) * ((d - text_length_frac.x)*0.5f)/d; + Vec2D text_position = middle_handle + (nb->position - n->position).norm().orth() * 12.0f; + Vec2D text_position_frac = middle_handle_frac - (nb->position - n->position).norm().orth() * 4.0f; - float rotation = Vector2Angle((Vector2){1,0}, (n.position - it->position)) * RAD2DEG; + float rotation = Vector2Angle((Vector2){1,0}, (n->position - nb->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); + DrawLineV(n->position, nb->position, VS_COLOR_PINK); } } } } - n.draw(mouse_pos, camera); + n->draw(mouse_pos, camera); } } diff --git a/src/main.cpp b/src/main.cpp index 02153b6..974cbe1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,7 +6,10 @@ * */ +#include "midi-node.h" +#include #include +#include #define RAYGUI_IMPLEMENTATION #include "datatypes.h" @@ -68,17 +71,19 @@ int main(){ camera.zoom = 1.0f; for(int i = 0; i < 4; ++i){ - app.gm.addNode((float)i * app.settings.grid*4 + 10 * app.settings.grid, 5 * app.settings.grid*4); + app.gm.addMidiNode(Vec2D{(float)i * app.settings.grid*4 + 5 * app.settings.grid, 5 * app.settings.grid*4}); } for(int i = 3; i >= 0; --i){ - app.gm.addNode((float)i * app.settings.grid*4 + 10 * app.settings.grid, 6 * app.settings.grid*4); + app.gm.addMidiNode(Vec2D{(float)i * app.settings.grid*4 + 5 * app.settings.grid, 6 * app.settings.grid*4}); } for(int i = 0; i < 8; ++i){ - app.gm.addConnection(app.gm.nodes[i].id, app.gm.nodes[(i+1) % 8].id); + app.gm.addConnection(app.gm.nodes[i], app.gm.nodes[(i+1) % 8]); } - - app.sm.addSignalAtNode((app.gm.nodes | std::views::values).front().id); + + std::cout << app.gm.nodes[0]->getNodeType() << std::endl; + + app.sm.addSignalAtNode(app.gm.nodes.front()); app.sm.startSignalThread(); enum InteractionState {HOVER_CANVAS, HOVER_NODE, HOVER_CONNECTOR, DRAG_NODE, CONNECT_NODE, DRAW_SELECTION, DELETE_SELECTED_NODES, COPY_SELECTED_NODES, PASTE_SELECTED_NODES}; @@ -87,6 +92,7 @@ int main(){ Vec2D select_start; Rectangle selection; + std::vector> copied_nodes; while(!WindowShouldClose()){ @@ -111,17 +117,15 @@ int main(){ Vec2D mouse_delta = {GetMouseDelta().x, GetMouseDelta().y}; Vec2D delta = mouse_delta * (-1.0f/camera.zoom); - std::vector> copied_nodes; + auto dragging_nodes = app.gm.nodes | std::ranges::views::filter([](std::shared_ptr n) { return n->dragging;}); + auto connecting_nodes = app.gm.nodes | std::ranges::views::filter([](std::shared_ptr n) { return n->connecting;}); + auto selected_nodes = app.gm.nodes | std::ranges::views::filter([](std::shared_ptr n) { return n->selected && !n->dragging;}) | std::ranges::to>>(); + auto hovering_nodes = app.gm.nodes | std::ranges::views::filter([&mouse_pos](std::shared_ptr n) { return CheckCollisionPointCircle(mouse_pos, n->position, n->radius);}); + auto hovering_connectors = app.gm.nodes | std::ranges::views::filter([&mouse_pos](std::shared_ptr n) { return CheckCollisionPointCircle(mouse_pos, (n->position - ((n->position - mouse_pos)).norm() * (n->radius + 14.0f)), 8.0f);}); - auto dragging_nodes = app.gm.nodes | std::ranges::views::values | std::ranges::views::filter([](Node n) { return n.dragging;}); - auto connecting_nodes = app.gm.nodes | std::ranges::views::values| std::ranges::views::filter([](Node n) { return n.connecting;}); - auto selected_nodes = app.gm.nodes | std::ranges::views::values| std::ranges::views::filter([](Node n) { return n.selected && !n.dragging;}); - auto hovering_nodes = app.gm.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.gm.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);}); - - for(auto &n : app.gm.nodes | std::ranges::views::values){ - if(hovering_nodes.empty()) n.hovering = false; - if(hovering_connectors.empty()) n.hover_connect = false; + for(auto &n : app.gm.nodes ){ + if(hovering_nodes.empty()) n->hovering = false; + if(hovering_connectors.empty()) n->hover_connect = false; } if(!mouse_down && !mouse_right_down && dragging_nodes.empty() && connecting_nodes.empty() && hovering_nodes.empty() && hovering_connectors.empty()) istate = InteractionState::HOVER_CANVAS; @@ -145,8 +149,8 @@ int main(){ switch(istate){ case InteractionState::HOVER_CANVAS: { - for(auto &n : app.gm.nodes | std::ranges::views::values){ - n.hover_connect = false; + for(auto &n : app.gm.nodes){ + n->hover_connect = false; } if(mouse_right_down) { camera.target = Vector2Add(camera.target, delta); @@ -164,22 +168,22 @@ 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.gm.nodes | std::views::values){ - if(CheckCollisionPointRec(n.position, selection)) { - n.in_rect = true; + for(auto& n : app.gm.nodes){ + if(CheckCollisionPointRec(n->position, selection)) { + n->in_rect = true; } else { - n.in_rect = false; + n->in_rect = false; } } } if(mouse_released){ - for(auto& n : app.gm.nodes | std::views::values){ - if(CheckCollisionPointRec(n.position, selection)) { - n.selected = true; - n.in_rect = false; + for(auto& n : app.gm.nodes){ + if(CheckCollisionPointRec(n->position, selection)) { + n->selected = true; + n->in_rect = false; } else { - if(!IsKeyDown(KEY_LEFT_SHIFT)) n.selected = false; + if(!IsKeyDown(KEY_LEFT_SHIFT)) n->selected = false; } } selection = (Rectangle){0,0,0,0}; @@ -190,45 +194,45 @@ int main(){ { if(mouse_clicked) { if(IsKeyDown(KEY_LEFT_SHIFT)) { - hovering_nodes.front().selected = true; + hovering_nodes.front()->selected = true; } else { - hovering_nodes.front().dragging = true; - hovering_nodes.front().drag_offset = hovering_nodes.front().position - mouse_pos; + hovering_nodes.front()->dragging = true; + hovering_nodes.front()->drag_offset = hovering_nodes.front()->position - mouse_pos; } } - if(mouse_right_clicked) hovering_nodes.front().config = true; + if(mouse_right_clicked) hovering_nodes.front()->config = true; break; } case InteractionState::DRAG_NODE: { if(!dragging_nodes.empty()){ for(auto &n : selected_nodes){ - if(!n.picked_up) n.drag_offset = n.position - dragging_nodes.front().position; - n.picked_up = true; + if(!n->picked_up) n->drag_offset = n->position - dragging_nodes.front()->position; + n->picked_up = true; } if(IsKeyDown(KEY_LEFT_CONTROL)) { - dragging_nodes.front().position = (Vec2D){round((mouse_pos.x+dragging_nodes.front().drag_offset.x)/app.settings.grid)*app.settings.grid, round((mouse_pos.y+dragging_nodes.front().drag_offset.y)/app.settings.grid)*app.settings.grid}; + dragging_nodes.front()->position = (Vec2D){round((mouse_pos.x+dragging_nodes.front()->drag_offset.x)/app.settings.grid)*app.settings.grid, round((mouse_pos.y+dragging_nodes.front()->drag_offset.y)/app.settings.grid)*app.settings.grid}; } else if(IsKeyDown(KEY_LEFT_SHIFT)){ - float steps = round(dragging_nodes.front().neighbor->position.dist(mouse_pos) / app.settings.grid) * app.settings.grid; - Vec2D polar = dragging_nodes.front().neighbor->position - (dragging_nodes.front().neighbor->position - (mouse_pos + dragging_nodes.front().drag_offset)).norm() * steps; - dragging_nodes.front().position = polar; + float steps = round(dragging_nodes.front()->neighbor->position.dist(mouse_pos) / app.settings.grid) * app.settings.grid; + Vec2D polar = dragging_nodes.front()->neighbor->position - (dragging_nodes.front()->neighbor->position - (mouse_pos + dragging_nodes.front()->drag_offset)).norm() * steps; + dragging_nodes.front()->position = polar; } else { - dragging_nodes.front().position = mouse_pos + dragging_nodes.front().drag_offset; + dragging_nodes.front()->position = mouse_pos + dragging_nodes.front()->drag_offset; } for(auto &n : selected_nodes){ - n.position = dragging_nodes.front().position + n.drag_offset; + n->position = dragging_nodes.front()->position + n->drag_offset; } if(mouse_released) { for(auto &n : dragging_nodes){ - n.dragging = false; - n.picked_up = false; + n->dragging = false; + n->picked_up = false; } for(auto &n : selected_nodes){ - n.picked_up = false; - n.dragging = false; + n->picked_up = false; + n->dragging = false; } } } @@ -237,116 +241,115 @@ int main(){ case InteractionState::HOVER_CONNECTOR: { if(!hovering_connectors.empty()){ - hovering_connectors.front().hover_connect = true; - if(mouse_clicked) hovering_connectors.front().connecting = true; + hovering_connectors.front()->hover_connect = true; + if(mouse_clicked) hovering_connectors.front()->connecting = true; } break; } case InteractionState::CONNECT_NODE: { - Vec2D connector_position = connecting_nodes.front().position - ((connecting_nodes.front().position - mouse_pos)).norm() * (connecting_nodes.front().radius + 14.0f); + Vec2D connector_position = connecting_nodes.front()->position - ((connecting_nodes.front()->position - mouse_pos)).norm() * (connecting_nodes.front()->radius + 14.0f); - if(connecting_nodes.front().connecting && mouse_down){ + if(connecting_nodes.front()->connecting && mouse_down){ DrawLine(connector_position.x, connector_position.y, mouse_pos.x, mouse_pos.y, (Color){229, 181, 103, 255}); - connecting_nodes.front().hovering = true; - connecting_nodes.front().hover_connect = true; + connecting_nodes.front()->hovering = true; + connecting_nodes.front()->hover_connect = true; } if(!hovering_connectors.empty()) { - hovering_connectors.front().hovering = true; - hovering_connectors.front().hover_connect = true; + hovering_connectors.front()->hovering = true; + hovering_connectors.front()->hover_connect = true; } - if(!hovering_nodes.empty()) hovering_nodes.front().hovering = true; + if(!hovering_nodes.empty()) hovering_nodes.front()->hovering = true; if(mouse_released) { if(&hovering_nodes.front() != &connecting_nodes.front() && &connecting_nodes.front() != &hovering_connectors.front()){ if(!hovering_nodes.empty()){ - app.gm.addConnection(connecting_nodes.front().id, hovering_nodes.front().id); + app.gm.addConnection(connecting_nodes.front(), hovering_nodes.front()); } else if(!hovering_connectors.empty()){ - app.gm.addConnection(connecting_nodes.front().id, hovering_connectors.front().id); + app.gm.addConnection(connecting_nodes.front(), hovering_connectors.front()); } else { - int id = app.gm.addNode(mouse_pos.x, mouse_pos.y); - app.gm.addConnection(connecting_nodes.front().id, id); + std::shared_ptr new_node = app.gm.addMidiNode(mouse_pos); + app.gm.addConnection(connecting_nodes.front(), new_node); } } - connecting_nodes.front().connecting = false; + connecting_nodes.front()->connecting = false; istate = InteractionState::HOVER_CANVAS; } break; } case InteractionState::DELETE_SELECTED_NODES: { - for(auto &n : selected_nodes){ - n.erase = true; - } - app.removeNodes(); + app.removeNodes(selected_nodes | std::ranges::to>>()); break; } case InteractionState::COPY_SELECTED_NODES: { - std::copy(selected_nodes.begin(), selected_nodes.end(), copied_nodes.begin()); + std::ranges::copy(selected_nodes.begin(), selected_nodes.end(), std::back_inserter(copied_nodes)); break; } case InteractionState::PASTE_SELECTED_NODES: { for(auto &n: copied_nodes){ - n.drag_offset = (n.position - copied_nodes.front().position); + n->drag_offset = (n->position - copied_nodes.front()->position); } -// app.gm.copyNodes(mouse_pos); - //app.gm.copyNodes(selected_nodes, mouse_pos); - - app.copyNodesAndConnections(mouse_pos); + app.copyNodesAndConnections(copied_nodes, mouse_pos); + copied_nodes.clear(); break; } }; float wheel = GetMouseWheelMove(); if(wheel != 0){ - if(!hovering_nodes.empty() && IsKeyDown(KEY_LEFT_CONTROL) && !IsKeyDown(KEY_LEFT_ALT)){ - switch(app.settings.mode){ - case 0:{ - hovering_nodes.front().midi_note += wheel; - break;} - case 1:{ - hovering_nodes.front().midi_cc += wheel; - break;} - case 2:{ - hovering_nodes.front().midi_pitch_bend += wheel; - break;} - }; - if(hovering_nodes.front().midi_note < 0 ) hovering_nodes.front().midi_note = app.sm.midi.midiNotes.size()-1; - if(hovering_nodes.front().midi_note >= app.sm.midi.midiNotes.size() ) hovering_nodes.front().midi_note = 0; + if(!hovering_nodes.empty() && hovering_nodes.front()->getNodeType() == NodeType::MIDINODE) { + std::shared_ptr midiNode = std::dynamic_pointer_cast(hovering_nodes.front()); + if(IsKeyDown(KEY_LEFT_CONTROL) && !IsKeyDown(KEY_LEFT_ALT)){ + switch(app.settings.mode){ + case 0:{ + midiNode->midi_note += wheel; + break;} + case 1:{ + midiNode->midi_cc += wheel; + break;} + case 2:{ + midiNode->midi_pitch_bend += wheel; + break;} + }; + if(midiNode->midi_note < 0 ) midiNode->midi_note = app.sm.midi.midiNotes.size()-1; + if(midiNode->midi_note >= app.sm.midi.midiNotes.size() ) midiNode->midi_note = 0; + + if(midiNode->midi_cc < 0 ) midiNode->midi_cc = 127; + if(midiNode->midi_cc > 127 ) midiNode->midi_cc = 0; - if(hovering_nodes.front().midi_cc < 0 ) hovering_nodes.front().midi_cc = 127; - if(hovering_nodes.front().midi_cc > 127 ) hovering_nodes.front().midi_cc = 0; - - if(hovering_nodes.front().midi_pitch_bend < 0 ) hovering_nodes.front().midi_pitch_bend = 0; - if(hovering_nodes.front().midi_pitch_bend > 16383 ) hovering_nodes.front().midi_pitch_bend = 16383; + if(midiNode->midi_pitch_bend < 0 ) midiNode->midi_pitch_bend = 0; + if(midiNode->midi_pitch_bend > 16383 ) midiNode->midi_pitch_bend = 16383; - } else if(!hovering_nodes.empty() && IsKeyDown(KEY_LEFT_SHIFT) && !IsKeyDown(KEY_LEFT_ALT)) { - hovering_nodes.front().midi_velocity += wheel; - if(hovering_nodes.front().midi_velocity < 0 ) hovering_nodes.front().midi_velocity = 127; - if(hovering_nodes.front().midi_velocity > 127 ) hovering_nodes.front().midi_velocity = 0; - } else if(!hovering_nodes.empty() && IsKeyDown(KEY_LEFT_ALT)) { - switch(app.settings.mode){ - case 0:{ - float incr = (float)wheel * (IsKeyDown(KEY_LEFT_SHIFT) ? 100.0f : IsKeyDown(KEY_LEFT_CONTROL) ? 1000.0f : 10.0f); - hovering_nodes.front().midi_gate += incr; - if(hovering_nodes.front().midi_gate < 0 ) hovering_nodes.front().midi_gate = 0; - break;} - case 1:{ - hovering_nodes.front().midi_cc_value += wheel; - if(hovering_nodes.front().midi_cc_value < 0 ) hovering_nodes.front().midi_cc_value = 0; - if(hovering_nodes.front().midi_cc_value > 127 ) hovering_nodes.front().midi_cc_value = 127; - break;} - case 2:{ - float incr = (float)wheel * (IsKeyDown(KEY_LEFT_SHIFT) ? 100.0f : IsKeyDown(KEY_LEFT_CONTROL) ? 1000.0f : 10.0f); - hovering_nodes.front().midi_pitch_bend += incr; - if(hovering_nodes.front().midi_pitch_bend < 0 ) hovering_nodes.front().midi_pitch_bend = 0; - if(hovering_nodes.front().midi_pitch_bend > 16383 ) hovering_nodes.front().midi_pitch_bend = 16383; - break;} - }; - } else { + } else if(!hovering_nodes.empty() && IsKeyDown(KEY_LEFT_SHIFT) && !IsKeyDown(KEY_LEFT_ALT)) { + midiNode->midi_velocity += wheel; + if(midiNode->midi_velocity < 0 ) midiNode->midi_velocity = 127; + if(midiNode->midi_velocity > 127 ) midiNode->midi_velocity = 0; + } else if(!hovering_nodes.empty() && IsKeyDown(KEY_LEFT_ALT)) { + switch(app.settings.mode){ + case 0:{ + float incr = (float)wheel * (IsKeyDown(KEY_LEFT_SHIFT) ? 100.0f : IsKeyDown(KEY_LEFT_CONTROL) ? 1000.0f : 10.0f); + midiNode->midi_gate += incr; + if(midiNode->midi_gate < 0 ) midiNode->midi_gate = 0; + break;} + case 1:{ + midiNode->midi_cc_value += wheel; + if(midiNode->midi_cc_value < 0 ) midiNode->midi_cc_value = 0; + if(midiNode->midi_cc_value > 127 ) midiNode->midi_cc_value = 127; + break;} + case 2:{ + float incr = (float)wheel * (IsKeyDown(KEY_LEFT_SHIFT) ? 100.0f : IsKeyDown(KEY_LEFT_CONTROL) ? 1000.0f : 10.0f); + midiNode->midi_pitch_bend += incr; + if(midiNode->midi_pitch_bend < 0 ) midiNode->midi_pitch_bend = 0; + if(midiNode->midi_pitch_bend > 16383 ) midiNode->midi_pitch_bend = 16383; + break;} + }; + } + } + if(hovering_nodes.empty()){ Vector2 mouseWorldPos = GetScreenToWorld2D(GetMousePosition(), camera); camera.offset = GetMousePosition(); camera.target = mouseWorldPos; @@ -362,7 +365,7 @@ int main(){ } } - app.draw(&mouse_pos, &camera); + app.draw(mouse_pos, camera); EndMode2D(); diff --git a/src/midi-node.h b/src/midi-node.h index 8474fe0..2002ce6 100644 --- a/src/midi-node.h +++ b/src/midi-node.h @@ -1,5 +1,6 @@ #pragma once +#include "datatypes.h" #include "node.h" #include "raymath.h" #include @@ -16,6 +17,11 @@ class MidiNode : public Node{ public: + MidiNode() {} + MidiNode(Vec2D position) : Node(position) {} + + NodeType nodeType = NodeType::MIDINODE; + bool send_note = true; bool send_cc = false; bool send_pc = false; @@ -55,20 +61,26 @@ class MidiNode : public Node{ float time = 0; float last = 0; + virtual NodeType getNodeType() override {return nodeType;} + void setMidiPort(int midi_port, bool state){ midi_outs[midi_port].enabled = state; } - void trigger(){ + void trigger() override { triggered = true; start = GetTime(); fade = midi_gate; } - void setMode(int _mode) override{ + void SMode(int _mode) override{ mode = _mode; } + int Ggate(){ + return midi_gate; + } + void draw(Vec2D mouse, Camera2D cam) override { last = time; time = GetTime(); diff --git a/src/moveable.h b/src/moveable.h index aecc1f3..8aadde1 100644 --- a/src/moveable.h +++ b/src/moveable.h @@ -32,6 +32,7 @@ class Movable{ bool erase = false; virtual void setPosition(Vec2D _position) {position = _position;} + virtual Vec2D getPosition() {return position;}; virtual void update(double frametime) {}; virtual void update() {}; }; diff --git a/src/node-manager.h b/src/node-manager.h deleted file mode 100644 index e105211..0000000 --- a/src/node-manager.h +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -#include "datatypes.h" -#include "node.h" -#include "raylib.h" -#include "raymath.h" -#include -#include -#include -#include -#include -#include - -class NodeManager{ - - public: - NodeManager() {} - - std::map nodes; - std::vector keys; - int iterator = 0; - - Node* node(int id){ - return &nodes.at(id); - } - - int getRandomNode(){ - std::vector o; - std::ranges::sample(keys, std::back_inserter(o), 1, std::mt19937{std::random_device{}()}); - return o.front(); - } - - void copyNodes(Vec2D position){ - for(auto n: nodes | std::ranges::views::filter([](std::pair 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 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(x,y)); - } - - int addNode(Vec2D position){ - nodes.emplace(std::make_pair(iterator, Node(iterator, position, 15.0f))); - keys.push_back(iterator); - iterator++; - return iterator - 1; - } - - void drawNodes(Vec2D *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>(); - 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 middle_handle = it->position + (n.position - it->position) * ((d - text_length.x)*0.5f)/d; - Vec2D middle_handle_frac = it->position + (n.position - it->position) * ((d - text_length_frac.x)*0.5f)/d; - Vec2D text_position = middle_handle + (it->position - n.position).norm().orth() * 12.0f; - Vec2D 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); - } - } - -}; diff --git a/src/node.h b/src/node.h index 9fe29cf..6952e7f 100644 --- a/src/node.h +++ b/src/node.h @@ -14,8 +14,10 @@ class Node : public Movable, public GraphItem{ Node() {} Node(float x, float y) : Movable(Vec2D(x, y), 0.0f) {} Node(Vec2D p) : Movable(p, 0.0f) {} - - Node *neighbor = nullptr; + + NodeType nodeType; + + std::shared_ptr neighbor; std::vector> connections; @@ -24,5 +26,11 @@ class Node : public Movable, public GraphItem{ float radius = 15.0f; bool config = false; - virtual void setMode(int mode) = 0; + virtual NodeType getNodeType() {return nodeType;} + + virtual void SMode(int mode) {}; + virtual int GGate() {return 0;}; + virtual int GActive() {return 0;}; + virtual void trigger() {}; + void draw(Vec2D mouse_pos, Camera2D cam) {} }; diff --git a/src/signal-manager.h b/src/signal-manager.h index 2eda2b4..aa2ada5 100644 --- a/src/signal-manager.h +++ b/src/signal-manager.h @@ -2,10 +2,12 @@ #include "datatypes.h" #include "graph-manager.h" +#include "midi-node.h" #include "signal.h" #include "midi.h" #include +#include #include class SignalManager{ @@ -19,11 +21,11 @@ class SignalManager{ std::mutex *mutex; GraphManager *gm; - std::vector signals; + std::vector> signals; bool loop = true; bool play = false; - + void pause(){ play = false; } @@ -39,9 +41,9 @@ class SignalManager{ void reset(){ for(auto &s : signals){ - s.position = gm->node(s.start)->position + Vec2D{0.1, 0.1}; - s.next = s.start; - s.last = s.start; + s->position = s->start->position + Vec2D{0.1, 0.1}; + s->next = s->start; + s->last = s->start; } gm->reset(); } @@ -55,12 +57,12 @@ class SignalManager{ loop = false; } - void addSignalAtNode(int node_id){ + void addSignalAtNode(std::shared_ptr node){ Color c = {(unsigned char)(20 + (rand() % 200)), 100, (unsigned char)(20 + (int)(rand() % 200)), 200}; - signals.emplace(signals.end(), Signal(gm->node(node_id)->position.x+1, gm->node(node_id)->position.y+1, 8.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; + signals.emplace_back(std::make_shared(node->position, 8.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 removeSignal(int n){ @@ -75,22 +77,23 @@ class SignalManager{ mutex->lock(); if(play){ for(auto& s : signals){ - s.direction = (gm->node(s.next)->position - s.position).norm(); - s.update(diff.count()); - if(s.position.dist(&gm->node(s.next)->position) < 0.6f) { - int n = gm->next(s.next, s.last); - gm->triggerNode(s.next); + s->direction = (s->next->position - s->position).norm(); + s->update(diff.count()); + if(s->position.dist(s->next->position) < 0.6f) { + std::shared_ptr next_node = gm->next(s->next, s->last); + s->next->trigger(); - if(n != s.next && n != -1) { - for(auto &m : gm->node(s.next)->midi_outs){ + if(next_node != s->next && next_node != std::shared_ptr(nullptr) && s->next->getNodeType() == NodeType::MIDINODE) { + auto midinode = std::dynamic_pointer_cast(s->next); + for(auto &m : midinode->midi_outs){ if(m.second.enabled) { - if(gm->node(s.next)->send_note) midi.note(m.second.i, gm->node(s.next)->midi_note_channel, gm->node(s.next)->midi_note, gm->node(s.next)->midi_velocity, gm->node(s.next)->midi_gate); - if(gm->node(s.next)->send_cc) midi.cc(m.second.i, gm->node(s.next)->midi_cc_channel, gm->node(s.next)->midi_cc, gm->node(s.next)->midi_cc_value); - if(gm->node(s.next)->send_pitch_bend) midi.pitch_bend(m.second.i, gm->node(s.next)->midi_pitch_bend_channel, gm->node(s.next)->midi_pitch_bend); + if(midinode->send_note) midi.note(m.second.i, midinode->midi_note_channel, midinode->midi_note, midinode->midi_velocity, midinode->midi_gate); + if(midinode->send_cc) midi.cc(m.second.i, midinode->midi_cc_channel, midinode->midi_cc, midinode->midi_cc_value); + if(midinode->send_pitch_bend) midi.pitch_bend(m.second.i, midinode->midi_pitch_bend_channel, midinode->midi_pitch_bend); } } - s.last = s.next; - s.next = n; + s->last = s->next; + s->next = next_node; } } } @@ -102,10 +105,10 @@ class SignalManager{ } } - void drawSignals(Vec2D *mouse_pos, Camera2D *camera){ - for(auto& s : signals){ + void drawSignals(Vec2D mouse_pos, Camera2D camera){ + for(auto s : signals){ mutex->lock(); - s.draw(mouse_pos, camera); + s->draw(mouse_pos, camera); mutex->unlock(); } } diff --git a/src/signal.h b/src/signal.h index 1d02219..29397f3 100644 --- a/src/signal.h +++ b/src/signal.h @@ -1,26 +1,25 @@ #pragma once +#include #include #include "datatypes.h" #include "moveable.h" +#include "node.h" class Signal : public Movable{ public: Signal(); - Signal(float x, float y, float r, float _speed, Color color) : Movable(Vec2D(x, y), Vec2D(0,0), _speed), radius(r), color(color) {} + Signal(Vec2D position, float r, float _speed, Color color) : Movable(position, Vec2D(0,0), _speed), radius(r), color(color) {} - int id = 0; + std::shared_ptr next; + std::shared_ptr last; + std::shared_ptr edge; - int next; - int last; - - int start; + std::shared_ptr start; bool at_target = false; - int target_node_id = 1; - int source_node_id = 0; float radius; @@ -35,7 +34,7 @@ class Signal : public Movable{ position += direction.norm() * speed * multiplier * (float)t_delta / 1000000.0f; } - void draw(Vec2D *mouse, Camera2D *cam) { + void draw(Vec2D mouse, Camera2D cam) { DrawCircle(position.x, position.y, radius, color); } }; diff --git a/src/state-manager.h b/src/state-manager.h index 2026c56..416af1c 100644 --- a/src/state-manager.h +++ b/src/state-manager.h @@ -3,11 +3,13 @@ #include "edge.h" #include "datatypes.h" #include "graph-manager.h" +#include "midi-node.h" #include "signal-manager.h" #include "ui.h" #include #include +#include #include #include @@ -30,100 +32,89 @@ class AppState{ AppUI ui; void updateMidiPorts(){ - for(auto&n : gm.nodes | std::views::values){ + for(auto&n : gm.nodes | std::ranges::views::filter([](std::shared_ptr n){return n->getNodeType() == NodeType::MIDINODE;})){ + auto nc = std::dynamic_pointer_cast(n); 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}; + if(!nc->midi_outs.contains(mo.number) && mo.enabled){ + nc->midi_outs[mo.number] = {mo.number, mo.port.port_name, false}; } - if(n.midi_outs.contains(mo.number) && !mo.enabled){ - n.midi_outs.erase(mo.number); + if(nc->midi_outs.contains(mo.number) && !mo.enabled){ + nc->midi_outs.erase(mo.number); } } } } - void copyNodesAndConnections(Vec2D position){ - auto copy_nodes_ids = gm.nodes | std::ranges::views::values | std::ranges::views::filter([](Node n){return n.copied;}) | std::views::transform([](Node n){return n.id;}) | std::ranges::to>(); + void copyNodesAndConnections(std::vector> nodes, Vec2D position){ - std::vector copy_connections; - std::ranges::copy_if(gm.connections, std::back_inserter(copy_connections), [copy_nodes_ids](Edge c){return std::ranges::contains(copy_nodes_ids, c.node_id_start) && std::ranges::contains(copy_nodes_ids, c.node_id_end);}); + std::vector> copy_connections; + for(auto n : nodes){ + std::ranges::copy_if(n->connections.begin(), n->connections.end(), std::back_inserter(copy_connections), [nodes](std::shared_ptr e){ return std::ranges::contains(nodes, e->start) && std::ranges::contains(nodes, e->end);}); + } - for(auto &n : copy_nodes_ids){ - int old_id = n; - int new_id = gm.copyNode(n, position + gm.nodes[n].drag_offset); - gm.nodes[new_id].copied = false; - gm.nodes[new_id].selected = true; + for(auto n : nodes){ + std::shared_ptr old_node = n; + std::shared_ptr new_node = gm.copyNode(n, position + n->drag_offset); + new_node->copied = false; + new_node->selected = true; for(auto &c : copy_connections){ - if(c.node_id_start == old_id) c.node_id_start = new_id; - if(c.node_id_end == old_id) c.node_id_end = new_id; + if(c->start == old_node) c->start = new_node; + if(c->end == old_node) c->end = new_node; } } - gm.connections.insert(gm.connections.end(), copy_connections.cbegin(), copy_connections.cend()); + gm.edges.insert(gm.edges.end(), copy_connections.cbegin(), copy_connections.cend()); } - void removeNodes(){ + void removeNodes(std::vector> nodes){ - for(auto &n : gm.nodes){ - n.second.highlighted = false; + for(auto n : gm.nodes){ + n->highlighted = false; } - auto erase_nodes = gm.nodes | std::ranges::views::values | std::ranges::views::filter([](Node n){return n.erase;}); + auto erase_connections = gm.edges | std::ranges::views::filter([nodes](std::shared_ptr c){ return std::ranges::contains(nodes, c->start) || std::ranges::contains(nodes, c->end);}); - std::vector erase_nodes_ids; - for(auto &e : erase_nodes){ - erase_nodes_ids.push_back(e.id); + std::vector> adjacent_nodes; + + for(auto c : erase_connections){ + if(!c->start->selected) adjacent_nodes.push_back(c->start); + if(!c->end->selected) adjacent_nodes.push_back(c->end); } - auto erase_connections = gm.connections | std::ranges::views::filter([erase_nodes_ids](Edge c){ return std::ranges::contains(erase_nodes_ids, c.node_id_start) || std::ranges::contains(erase_nodes_ids, c.node_id_end);}); + auto reroute_signals = sm.signals | std::ranges::views::filter([nodes](std::shared_ptr s){return std::ranges::contains(nodes, s->next);}); - std::vector adjacent_nodes_ids; - - for(auto &c : erase_connections){ - if(!gm.nodes.at(c.node_id_start).selected) adjacent_nodes_ids.push_back(c.node_id_start); - if(!gm.nodes.at(c.node_id_end).selected) adjacent_nodes_ids.push_back(c.node_id_end); - } - - 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){ + for(auto s : reroute_signals){ float dist = 10000000; - int next_id = -1; - for(int i : adjacent_nodes_ids){ - if(gm.nodes.at(i).position.dist(&s.position) < dist) { - dist = gm.nodes.at(i).position.dist(&s.position); - next_id = i; + std::shared_ptr next_node; + for(auto n : adjacent_nodes){ + if(n->position.dist(s->position) < dist) { + dist = n->position.dist(s->position); + next_node = n; } } - s.next = next_id; - s.last = next_id; + s->next = next_node; + s->last = next_node; } - auto rebase_signals = sm.signals | std::ranges::views::filter([erase_nodes_ids](Signal s){ - return std::ranges::contains(erase_nodes_ids, s.start); - }); + auto rebase_signals = sm.signals | std::ranges::views::filter([nodes](std::shared_ptr s){return std::ranges::contains(nodes, s->start);}); - for(auto &s : rebase_signals){ - s.start = s.next; + for(auto s : rebase_signals){ + s->start = s->next; } - for(auto &n : erase_nodes){ - std::erase_if(gm.connections, [n](Edge c){ return c.node_id_start == n.id || c.node_id_end == n.id;}); + for(auto n : nodes){ + std::erase_if(gm.edges, [n](std::shared_ptr c){ return c->start == n || c->end == n;}); } - std::erase_if(gm.nodes, [](std::pair n){ return n.second.erase;}); - gm.keys.clear(); - std::ranges::copy(gm.nodes | std::ranges::views::keys, std::back_inserter(gm.keys)); + std::erase_if(gm.nodes, [nodes](std::shared_ptr n){ return std::ranges::contains(nodes, n);}); } - void draw(Vec2D *mouse_pos, Camera2D *camera){ + void draw(Vec2D mouse_pos, Camera2D camera){ mutex.lock(); updateMidiPorts(); mutex.unlock(); gm.drawNodes(mouse_pos, camera); - gm.drawConnections(mouse_pos, camera); + gm.drawEdges(mouse_pos, camera); sm.drawSignals(mouse_pos, camera); } diff --git a/src/ui.h b/src/ui.h index 78cb75f..332c192 100644 --- a/src/ui.h +++ b/src/ui.h @@ -1,5 +1,6 @@ #pragma once +#include "midi-node.h" #include "raylib.h" #include "raygui/raygui.h" #include "raygui/dark/style_dark.h" @@ -8,6 +9,7 @@ #include "graph-manager.h" #include "signal-manager.h" +#include #include class AppUI{ @@ -52,8 +54,8 @@ class AppUI{ if(GuiButton((Rectangle){20, (float)s_midi + 45 + i*20.0f, 240, 20}, "ENABLE FOR ALL")) { for(auto &midis : sm->midi.midi_out_ports){ if(midis.enabled){ - for(auto &n : gm->nodes){ - n.second.setMidiPort(midis.number, true); + for(auto n : gm->nodes){ + if(n->getNodeType() == NodeType::MIDINODE) std::dynamic_pointer_cast(n)->setMidiPort(midis.number, true); } } } @@ -62,114 +64,117 @@ class AppUI{ int s_signal = s_midi + 100 + 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 = gm->getRandomNode(); + std::shared_ptr ni = gm->getRandomNode(); sm->addSignalAtNode(ni); } int signal_configs = 0; for(auto &s : sm->signals){ if(GuiButton((Rectangle){20,(float)s_signal+70 + signal_configs*20, 20, 20},"X")) sm->removeSignal(signal_configs); - DrawCircle(55, s_signal+80 + signal_configs*20, 6.0F, s.color); - float s_multiplier = round(s.multiplier*10.0f)/10.0f; - float signal_bpm = settings->bpm * s.multiplier; + DrawCircle(55, s_signal+80 + signal_configs*20, 6.0F, s->color); + float s_multiplier = round(s->multiplier*10.0f)/10.0f; + float signal_bpm = settings->bpm * s->multiplier; GuiSlider((Rectangle){70, (float)s_signal+70 + signal_configs*20, 160, 20}, "", std::format("{:.1f}",signal_bpm).c_str(), &s_multiplier, 0.1, 10); - s.multiplier = s_multiplier; - s.speed = settings->speed; + s->multiplier = s_multiplier; + s->speed = settings->speed; signal_configs++; } int s_nodes = s_signal + 70 + 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")){ - gm->addNode(200.0f, 200.0f); + gm->addMidiNode(Vec2D{200.0f, 200.0f}); } DrawText(std::format("{}", gm->nodes.size()).c_str(), 20, settings->height - 70, 15, VS_COLOR_WHITE); DrawFPS(20, settings->height - 30); } void drawPerNodeConfig(Camera2D *camera){ - for(auto& n : gm->nodes | std::ranges::views::values | std::views::filter([](Node n){ return n.config; })){ - Vector2 np = GetWorldToScreen2D((Vector2){n.position.x, n.position.y}, *camera); + for(auto& n : gm->nodes | std::views::filter([](std::shared_ptr 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"); + n->config = !GuiWindowBox((Rectangle){np.x + 10, np.y + 10, 240, 440}, "config"); - int option = n.distributionMode; + int option = n->distributionMode; GuiToggleGroup((Rectangle){ np.x + 20, np.y + 40, 73, 20 }, "GEO;RR;RAND", &option); - n.distributionMode = (Distribution)option; + n->distributionMode = (Distribution)option; 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.id); + sm->addSignalAtNode(n); } int y_midi = 120; GuiLabel((Rectangle){np.x + 20, np.y + y_midi, 220, 20}, "MIDI OUT"); int i = 0; - for(auto& mo : n.midi_outs){ - GuiCheckBox((Rectangle){np.x + 20, np.y + y_midi + 20 + i*20.0f, 20, 20}, mo.second.name.c_str(), &mo.second.enabled); - i++; - } + if(n->getNodeType() == NodeType::MIDINODE){ + std::shared_ptr mn = std::dynamic_pointer_cast(n); + for(auto& mo : mn->midi_outs){ + GuiCheckBox((Rectangle){np.x + 20, np.y + y_midi + 20 + i*20.0f, 20, 20}, mo.second.name.c_str(), &mo.second.enabled); + i++; + } + + int y_channel = y_midi + 30 + i*20; + GuiCheckBox((Rectangle){np.x + 20, np.y + y_channel, 20, 20}, "NOTE", &mn->send_note); + GuiCheckBox((Rectangle){np.x + 90, np.y + y_channel, 20, 20}, "CC", &mn->send_cc); + GuiCheckBox((Rectangle){np.x + 160, np.y + y_channel, 20, 20}, "PITCH", &mn->send_pitch_bend); + + int y_cc_note = y_channel + 120; + switch(settings->mode){ + case 0:{ + GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note - 75, 220, 20}, "NOTE CHANNEL"); + GuiToggleGroup((Rectangle){np.x + 20, np.y + y_cc_note - 50, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &mn->midi_note_channel); - int y_channel = y_midi + 30 + i*20; - GuiCheckBox((Rectangle){np.x + 20, np.y + y_channel, 20, 20}, "NOTE", &n.send_note); - GuiCheckBox((Rectangle){np.x + 90, np.y + y_channel, 20, 20}, "CC", &n.send_cc); - GuiCheckBox((Rectangle){np.x + 160, np.y + y_channel, 20, 20}, "PITCH", &n.send_pitch_bend); - - int y_cc_note = y_channel + 120; - switch(settings->mode){ - case 0:{ - GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note - 75, 220, 20}, "NOTE CHANNEL"); - GuiToggleGroup((Rectangle){np.x + 20, np.y + y_cc_note - 50, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &n.midi_note_channel); - - GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "NOTE"); - float v = n.midi_note; - GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note , 20, 20}, sm->midi.midiNotes[(int)round(v)].c_str()); - GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",n.midi_note).c_str()); - GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 127); - n.midi_note = round(v); - - GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 60, 100, 20}, "VELOCITY"); - v = n.midi_velocity; - GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 60, 20, 20}, std::format("{}",n.midi_velocity).c_str()); - GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 90, 220, 20}, "", "", &v, 0, 127); - n.midi_velocity = round(v); - - GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 120, 100, 20}, "GATE"); - v = n.midi_gate; - GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 120, 20, 20}, std::format("{}",(float)n.midi_gate/1000.0f).c_str()); - GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 150, 220, 20}, "", "", &v, 0, 30000); - n.midi_gate = round(v); - break;} - case 1: - { - GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note - 75, 220, 20}, "CC CHANNEL"); - GuiToggleGroup((Rectangle){np.x + 20, np.y + y_cc_note - 50, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &n.midi_cc_channel); - - GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "CC"); - float v = n.midi_cc; - GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",n.midi_cc).c_str()); - GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 127); - n.midi_cc = round(v); - - GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 60, 100, 20}, "CC VALUE"); - v = n.midi_cc_value; - GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 60, 20, 20}, std::format("{}",n.midi_cc_value).c_str()); - GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 90, 220, 20}, "", "", &v, 0, 127); - n.midi_cc_value = round(v); - - break;} - case 2: - { - GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note - 75, 220, 20}, "PITCH BEND CHANNEL"); - GuiToggleGroup((Rectangle){np.x + 20, np.y + y_cc_note - 50, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &n.midi_pitch_bend_channel); - - GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "CC"); - float v = n.midi_pitch_bend; - GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",n.midi_pitch_bend).c_str()); - GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 16383); - n.midi_pitch_bend = round(v); + GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "NOTE"); + float v = mn->midi_note; + GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note , 20, 20}, sm->midi.midiNotes[(int)round(v)].c_str()); + GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",mn->midi_note).c_str()); + GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 127); + mn->midi_note = round(v); + + GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 60, 100, 20}, "VELOCITY"); + v = mn->midi_velocity; + GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 60, 20, 20}, std::format("{}",mn->midi_velocity).c_str()); + GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 90, 220, 20}, "", "", &v, 0, 127); + mn->midi_velocity = round(v); + + GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 120, 100, 20}, "GATE"); + v = mn->midi_gate; + GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 120, 20, 20}, std::format("{}",(float)mn->midi_gate/1000.0f).c_str()); + GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 150, 220, 20}, "", "", &v, 0, 30000); + mn->midi_gate = round(v); break;} + case 1: + { + GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note - 75, 220, 20}, "CC CHANNEL"); + GuiToggleGroup((Rectangle){np.x + 20, np.y + y_cc_note - 50, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &mn->midi_cc_channel); + + GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "CC"); + float v = mn->midi_cc; + GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",mn->midi_cc).c_str()); + GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 127); + mn->midi_cc = round(v); + + GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 60, 100, 20}, "CC VALUE"); + v = mn->midi_cc_value; + GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 60, 20, 20}, std::format("{}",mn->midi_cc_value).c_str()); + GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 90, 220, 20}, "", "", &v, 0, 127); + mn->midi_cc_value = round(v); + + break;} + case 2: + { + GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note - 75, 220, 20}, "PITCH BEND CHANNEL"); + GuiToggleGroup((Rectangle){np.x + 20, np.y + y_cc_note - 50, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &mn->midi_pitch_bend_channel); + + GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "CC"); + float v = mn->midi_pitch_bend; + GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",mn->midi_pitch_bend).c_str()); + GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 16383); + mn->midi_pitch_bend = round(v); + break;} + } } } }