heavy restructuring with smart pointers, not finished

This commit is contained in:
Sebastian
2024-10-31 01:43:00 +01:00
14 changed files with 3317 additions and 390 deletions
+2924
View File
File diff suppressed because it is too large Load Diff
+12 -14
View File
@@ -1,9 +1,8 @@
#pragma once
#include "connection.h"
#include "edge.h"
#include "node-manager.h"
#include "raylib.h"
#include "raymath.h"
#include <map>
#include <vector>
@@ -28,20 +27,13 @@ class ConnectionManager{
std::vector<int> next_node_ids;
for(auto& c : connections){
if((c.v1 == current && c.v1_v2) || (c.v2 == current && c.v2_v1)) {
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.v1 == current ? c.v2 : c.v1 );
next_node_ids.push_back( c.node_id_start == current ? c.node_id_end : c.node_id_start );
}
}
/*if(possible_connections.size() == 2) {*/
/* int next = next_node_ids[0] == last ? next_node_ids[1] : next_node_ids[0];*/
/* if(possible_connections[0]->v1 == next || possible_connections[0]->v2 == next) possible_connections[0]->active = true;*/
/* if(possible_connections[1]->v1 == next || possible_connections[1]->v2 == next) possible_connections[1]->active = true;*/
/* return next;*/
/*}*/
if(possible_connections.empty()) return -1;
int return_path = 0;
@@ -50,7 +42,7 @@ class ConnectionManager{
Vec2D<float> 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->v1 == current ? n->v2 : n->v1;
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;
@@ -74,16 +66,22 @@ class ConnectionManager{
}
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;
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->nodes, from, to);
connections.emplace_back(nm, from, to);
}
void splitConnection(Connection *c){
Vec2D<float> center = c->node_start->position.center(c->node_end->position);
}
void drawConnections(Vec2D<float> *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);
}
}
-88
View File
@@ -1,88 +0,0 @@
#pragma once
#include "button.h"
#include "datatypes.h"
#include "graph-item.h"
#include "node.h"
#include "raylib.h"
class Connection : public GraphItem{
public:
Connection(std::map<int, Node> *nodes, int v1, int v2) : v1(v1), v2(v2), nodes(nodes) {}
int v1;
int v2;
std::map<int, Node> *nodes;
bool v1_v2 = true;
bool v2_v1 = true;
bool active = false;
bool remove = false;
bool config = false;
float connector_offset = 8.0f;
float switches_offset = 25.0f;
void flip(){
int vt = v1;
v1 = v2;
v2 = vt;
}
void draw(Vec2D<float> *mouse_pos, Camera2D *cam){
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);
float midi_gate_v1 = Clamp((4.f / 1000.f) * nodes->at(v1).midi_gate, 1.f, 4.f);
float midi_gate_v2 = Clamp((4.f / 1000.f) * nodes->at(v2).midi_gate, 1.f, 4.f);
// Line with small connector circles
Color c = {229, 181, 103, 255};
if(active) c = {108, 153, 187, 255};
DrawLine(v1_position.x, v1_position.y, v2_position.x, v2_position.y, c);
if(midi_gate_v1 == 4.0f) c = VS_COLOR_RED;
DrawCircle(v1_position.x, v1_position.y, midi_gate_v1, c);
c = {229, 181, 103, 255};
if(midi_gate_v2 == 4.0f) c = VS_COLOR_RED;
DrawCircle(v2_position.x, v2_position.y, midi_gate_v2, c);
if(v1_v2_distance < 100) switches_offset = v1_v2_distance/2.3f;
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);
// Dynamic opacity by distance to mouse
unsigned char opacity = 150.0f - Clamp(middle_handle.dist(mouse_pos), 0.0f, 150.0f);
unsigned char opacity_from = 250.0f - Clamp(from_to_handle.dist(mouse_pos), 0.0f, 220.0f);
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, v1_v2 ? (Color){120, 210, 115, opacity_from} : (Color){232, 50, 32, opacity_from} )) v1_v2 = !v1_v2;
if(ViButton(mouse_pos, to_from_handle.x, to_from_handle.y, 8.0f, v2_v1 ? (Color){120, 210, 115, opacity_to} : (Color){232, 50, 32, opacity_to} )) v2_v1 = !v2_v1;
if(!v1_v2){
Vec2D<float> block_point1 = (v1_position - v2_position).norm().orth()*4.0f + from_to_handle;
Vec2D<float> 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(!v2_v1){
Vec2D<float> block_point1 = (v1_position - v2_position).norm().orth()*4.0f + to_from_handle;
Vec2D<float> 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} )) remove = true;
//if(ViButton(mouse_pos, config_handle.x, config_handle.y, 8.0f, (Color){158, 134, 200, opacity})) config = !config;
//if(ViButton(mouse_pos, config_handle.x + flip_handle.x, config_handle.y + flip_handle.y, 8.0f, (Color){158, 0, 200, opacity})) flip();
}
};
+9 -6
View File
@@ -76,6 +76,9 @@ class Vec2D{
Vec2D norm() { return *this / length();};
Vec2D center(Vec2D v){
return Vec2D<T>{x,y} + (Vec2D<T>{x,y} - v)*0.5f;
}
Vec2D& operator=(const Vec2D& vec){
if (this == &vec){
@@ -125,32 +128,32 @@ class Vec2D{
};
template<typename T>
Vec2D<T> operator+(const Vec2D<T>& left, const Vec2D<T>& right){
Vec2D<T> operator+(const Vec2D<T> left, const Vec2D<T> right){
return Vec2D<T>(left.x + right.x, left.y + right.y);
};
template<typename T>
Vec2D<T> operator-(const Vec2D<T>& left, const Vec2D<T>& right){
Vec2D<T> operator-(const Vec2D<T> left, const Vec2D<T> right){
return Vec2D<T>(left.x - right.x, left.y - right.y);
};
template<typename T>
Vec2D<T> operator*(const Vec2D<T>& left, const Vec2D<T>& right){
Vec2D<T> operator*(const Vec2D<T> left, const Vec2D<T> right){
return Vec2D<T>(left.x * right.x, left.y * right.y);
};
template<typename T>
Vec2D<T> operator/(const Vec2D<T>& left, const Vec2D<T>& right){
Vec2D<T> operator/(const Vec2D<T> left, const Vec2D<T> right){
return Vec2D<T>(left.x / right.x, left.y / right.y);
};
template<typename T>
Vec2D<T> operator/(const Vec2D<T>& left, T scalar){
Vec2D<T> operator/(const Vec2D<T> left, T scalar){
return Vec2D<T>(left.x / scalar, left.y / scalar);
};
template<typename T>
Vec2D<T> operator*(const Vec2D<T>& left, T scalar){
Vec2D<T> operator*(const Vec2D<T> left, T scalar){
return Vec2D<T>(left.x * scalar, left.y * scalar);
};
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "graph-item.h"
#include <memory>
class Node;
class Edge : public GraphItem{
public:
Edge(std::shared_ptr<Node> start, std::shared_ptr<Node> end) : start(start), end(end) {}
std::shared_ptr<Node> start;
std::shared_ptr<Node> end;
bool pass_forward = true;
bool pass_backward = true;
bool active = false;
bool remove = false;
bool config = false;
bool split = false;
float connector_offset = 8.0f;
float switches_offset = 25.0f;
std::shared_ptr<Node> getNextNode(std::shared_ptr<Node> origin){
return start == origin ? end : start;
}
};
+5 -3
View File
@@ -1,13 +1,15 @@
#pragma once
#include "raylib.h"
#include "datatypes.h"
#include <memory>
#include <vector>
class GraphItem{
public:
int id;
GraphItemType type;
std::vector<int> connections;
std::vector<std::shared_ptr<GraphItem>> neighbors;
virtual void draw(Vec2D<float> mouse, Camera2D cam) = 0;
virtual ~GraphItem() = default;
};
+106 -66
View File
@@ -1,11 +1,16 @@
#pragma once
#include "datatypes.h"
#include "graph-item.h"
#include "node.h"
#include "connection.h"
#include "edge.h"
#include <algorithm>
#include <iterator>
#include <map>
#include <memory>
#include <random>
#include <ranges>
#include <vector>
class GraphManager{
@@ -13,89 +18,72 @@ class GraphManager{
GraphManager() {}
std::map<int, Node> nodes;
std::vector<Connection> connections;
std::vector<std::shared_ptr<Node>> nodes;
std::vector<std::shared_ptr<Edge>> edges;
std::vector<int> keys;
int iterator = 0;
std::map<std::shared_ptr<Node>, int> counter;
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();
std::shared_ptr<Node> getRandomNode(){
std::vector<std::shared_ptr<GraphItem>> o;
std::ranges::sample(nodes, std::back_inserter(o), 1, std::mt19937{std::random_device{}()});
return std::dynamic_pointer_cast<Node>(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++;
std::shared_ptr<Edge> getRandomEdge(){
std::vector<std::shared_ptr<GraphItem>> o;
std::ranges::sample(edges, std::back_inserter(o), 1, std::mt19937{std::random_device{}()});
return std::dynamic_pointer_cast<Edge>(o.front());
}
void copyNodes(std::vector<std::shared_ptr<Node>> nodes, Vec2D<float> position){
for(auto n: nodes){
auto nc = std::make_shared<Node>(n);
nc->position = position + nc->drag_offset;
nc->selected = true;
nodes.push_back(nc);
}
}
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 copyNode(std::shared_ptr<Node> n, Vec2D<float> position){
auto nc = std::make_shared<Node>(n);
nc->setPosition(position);
nodes.push_back(nc);
}
void setNodeMode(int mode){
for(auto &n : nodes | std::ranges::views::values){
n.mode = mode;
}
}
int addNode(float x, float y){
std::shared_ptr<Node> 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;
std::shared_ptr<Node> addNode(Vec2D<float> position){
nodes.emplace_back(std::make_shared<Node>(position));
return nodes.back();
}
void triggerNode(int node){
nodes.at(node).trigger();
void setNodeMode(int mode){
for(auto n: nodes){
n->setMode(mode);
}
}
void reset(){
counter.clear();
}
int next(int current, int last){
std::vector<Connection*> possible_connections;
std::vector<int> next_node_ids;
std::shared_ptr<Node> next(std::shared_ptr<Node> current, std::shared_ptr<Node> last){
std::vector<std::shared_ptr<Edge>> possible_connections = current->connections | std::ranges::views::filter([current](std::shared_ptr<Edge> c){
return (c->start == current && c->pass_forward) || (c->end == current && c->pass_backward);
}) | std::ranges::to<std::vector<std::shared_ptr<Edge>>>();
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;
if(possible_connections.empty()) return std::shared_ptr<Node>(nullptr);
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();
if(current->distributionMode == Distribution::GEOMETRIC){
Vec2D<float> coming_from = (current->position - 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());
std::shared_ptr<Node> next_node = n->getNextNode(current);
float angle_v1_v2 = coming_from.norm().dot((next_node->position - current->position).norm());
if(angle_v1_v2 > angle) {
return_path = i;
angle = angle_v1_v2;
@@ -104,7 +92,7 @@ class GraphManager{
}
}
if(nodes.at(current).distributionMode == Distribution::ROUNDROBIN){
if(current->distributionMode == Distribution::ROUNDROBIN){
if(counter.find(current) == counter.end()) {
counter[current] = 0;
} else {
@@ -113,24 +101,76 @@ class GraphManager{
}
}
if(nodes.at(current).distributionMode == Distribution::RANDOM){
if(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;
return possible_connections.at(return_path)->getNextNode(current);
}
void addConnection(int from, int to){
connections.emplace_back(&nodes, from, to);
void addConnection(std::shared_ptr<Node> from, std::shared_ptr<Node> to){
edges.emplace_back(std::make_shared<Edge>(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);
// std::erase_if(connections, [](Connection c){ return c.remove == true;});
for(auto& c : edges){
Vec2D<float> v1_position = c->start->position - ((c->start->position - c->end->position)).norm() * (c->start->radius + c->connector_offset);
Vec2D<float> 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);
// Line with small connector circles
Color col = {229, 181, 103, 255};
if(c.active) 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;
DrawCircle(v1_position.x, v1_position.y, midi_gate_v1, col);
col = {229, 181, 103, 255};
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<float> from_to_handle = c->start->position - ((c->start->position - c->end->position)).norm() * (c->start->radius + c.switches_offset);
Vec2D<float> to_from_handle = c->end->position - ((c->end->position - c->start->position)).norm() * (c->end->radius + c.switches_offset);
Vec2D<float> middle_handle = c->end->position - ((c->end->position - c->start->position))*0.4f;
Vec2D<float> middle_handle_2 = c->end->position - ((c->end->position - c->start->position))*0.6f;
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);
Vec2D<float> flip_handle_2 = middle_handle_2 + (v1_position - v2_position).norm().orth() * (v1_v2_distance < 120.0f ? Clamp((120.0f - v1_v2_distance)*-0.2, -20.f, 0.f) : 1.0f);
// Dynamic opacity by distance to mouse
unsigned char opacity = 150.0f - Clamp(middle_handle.dist(mouse_pos), 0.0f, 150.0f);
unsigned char opacity_from = 250.0f - Clamp(from_to_handle.dist(mouse_pos), 0.0f, 220.0f);
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(!c->start_end){
Vec2D<float> block_point1 = (v1_position - v2_position).norm().orth()*4.0f + from_to_handle;
Vec2D<float> 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){
Vec2D<float> block_point1 = (v1_position - v2_position).norm().orth()*4.0f + to_from_handle;
Vec2D<float> 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;
}
}
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;
+25 -27
View File
@@ -6,6 +6,7 @@
*
*/
#include <memory>
#define RAYGUI_IMPLEMENTATION
#include "datatypes.h"
@@ -67,17 +68,17 @@ int main(){
camera.zoom = 1.0f;
for(int i = 0; i < 4; ++i){
app.nm.addNode((float)i * app.settings.grid*4 + 10 * app.settings.grid, 5 * app.settings.grid*4);
app.gm.addNode((float)i * app.settings.grid*4 + 10 * app.settings.grid, 5 * app.settings.grid*4);
}
for(int i = 3; i >= 0; --i){
app.nm.addNode((float)i * app.settings.grid*4 + 10 * app.settings.grid, 6 * app.settings.grid*4);
app.gm.addNode((float)i * app.settings.grid*4 + 10 * app.settings.grid, 6 * app.settings.grid*4);
}
for(int i = 0; i < 8; ++i){
app.cm.addConnection(app.nm.nodes[i].id, app.nm.nodes[(i+1) % 8].id);
app.gm.addConnection(app.gm.nodes[i].id, app.gm.nodes[(i+1) % 8].id);
}
app.sm.addSignalAtNode((app.nm.nodes | std::views::values).front().id);
app.sm.addSignalAtNode((app.gm.nodes | std::views::values).front().id);
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};
@@ -110,14 +111,15 @@ 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::values | std::ranges::views::filter([](Node n) { return n.dragging;});
auto copied_nodes = app.nm.nodes | std::ranges::views::values | std::ranges::views::filter([](Node n) { return n.copied;});
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);});
std::vector<std::shared_ptr<Node>> copied_nodes;
for(auto &n : app.nm.nodes | std::ranges::views::values){
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;
}
@@ -137,13 +139,13 @@ int main(){
if(IsKeyPressed(KEY_TAB)) {
app.settings.mode = (app.settings.mode + 1) % 3;
app.nm.setNodeMode(app.settings.mode);
app.gm.setNodeMode(app.settings.mode);
}
switch(istate){
case InteractionState::HOVER_CANVAS:
{
for(auto &n : app.nm.nodes | std::ranges::views::values){
for(auto &n : app.gm.nodes | std::ranges::views::values){
n.hover_connect = false;
}
if(mouse_right_down) {
@@ -162,7 +164,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 | std::views::values){
for(auto& n : app.gm.nodes | std::views::values){
if(CheckCollisionPointRec(n.position, selection)) {
n.in_rect = true;
} else {
@@ -172,7 +174,7 @@ int main(){
}
if(mouse_released){
for(auto& n : app.nm.nodes | std::views::values){
for(auto& n : app.gm.nodes | std::views::values){
if(CheckCollisionPointRec(n.position, selection)) {
n.selected = true;
n.in_rect = false;
@@ -259,12 +261,12 @@ 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().id, hovering_nodes.front().id);
app.gm.addConnection(connecting_nodes.front().id, hovering_nodes.front().id);
} else if(!hovering_connectors.empty()){
app.cm.addConnection(connecting_nodes.front().id, hovering_connectors.front().id);
app.gm.addConnection(connecting_nodes.front().id, hovering_connectors.front().id);
} else {
int id = app.nm.addNode(mouse_pos.x, mouse_pos.y);
app.cm.addConnection(connecting_nodes.front().id, id);
int id = app.gm.addNode(mouse_pos.x, mouse_pos.y);
app.gm.addConnection(connecting_nodes.front().id, id);
}
}
connecting_nodes.front().connecting = false;
@@ -282,13 +284,7 @@ int main(){
}
case InteractionState::COPY_SELECTED_NODES:
{
for(auto &n: copied_nodes){
n.copied = false;
}
for(auto &n: selected_nodes){
n.copied = true;
n.selected = false;
}
std::copy(selected_nodes.begin(), selected_nodes.end(), copied_nodes.begin());
break;
}
case InteractionState::PASTE_SELECTED_NODES:
@@ -296,7 +292,9 @@ int main(){
for(auto &n: copied_nodes){
n.drag_offset = (n.position - copied_nodes.front().position);
}
// app.nm.copyNodes(mouse_pos);
// app.gm.copyNodes(mouse_pos);
//app.gm.copyNodes(selected_nodes, mouse_pos);
app.copyNodesAndConnections(mouse_pos);
break;
}
+156
View File
@@ -0,0 +1,156 @@
#pragma once
#include "node.h"
#include "raymath.h"
#include <map>
#define R 14.0f
struct NodeMidiOut{
int i;
std::string name;
bool enabled;
};
class MidiNode : public Node{
public:
bool send_note = true;
bool send_cc = false;
bool send_pc = false;
bool send_pitch_bend = false;
int midi_out = -1;
std::map<int, NodeMidiOut> midi_outs;
bool gui_midi_channel_edit = false;
bool gui_midi_note_edit = false;
bool gui_midi_velocity_edit = false;
bool gui_midi_gate_edit = false;
bool triggered = false;
int mode = 0;
int midi_note_channel = 1;
int midi_note = 48;
int midi_velocity = 60;
int midi_gate = 25; // gate time in milliseconds
int midi_cc_channel = 0;
int midi_cc = 0;
int midi_cc_value = 0;
int midi_pc_channel = 0;
int midi_pc = 0;
int midi_pc_value = 0;
int midi_pitch_bend_channel = 0;
int midi_pitch_bend = 8192;
float start = 0;
float fade = 0;
float time = 0;
float last = 0;
void setMidiPort(int midi_port, bool state){
midi_outs[midi_port].enabled = state;
}
void trigger(){
triggered = true;
start = GetTime();
fade = midi_gate;
}
void setMode(int _mode) override{
mode = _mode;
}
void draw(Vec2D<float> mouse, Camera2D cam) override {
last = time;
time = GetTime();
if(time - start >= (float)midi_gate / 1000.0f){
triggered = false;
fade = 0;
} else {
fade -= (time - last)*1000.0f;
}
DrawRing(position, radius, radius+4, 0, 360, 24, (Color){60, 60, 60, 255});
DrawRing(position, radius, radius+4, 0.0f - 90.0f, (360.0f/127.0f) * midi_velocity - 90.0f, 24, (Color){158, 134, 200, 255});
DrawCircleLinesV(position, radius + 4, (Color){100, 100, 100, 255});
float midi_gate_map = round((255.f / midi_gate) * fade);
Color gate_color = VS_COLOR_GREEN;
switch(mode){
case 0:{
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){180, 210, 115, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, WHITE);
break;}
case 1:{
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){229, 181, 103, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, WHITE);
break;}
case 2:{
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){232, 125, 62, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, WHITE);
break;}
}
if(!dragging && !selected && !in_rect && !hovering) DrawCircle(position.x, position.y, radius, gate_color);
if(selected || dragging || in_rect || hovering) DrawCircle(position.x, position.y, radius, (Color){108, 153, 187, 255});
if(distributionMode == Distribution::RANDOM) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_PINK);
if(distributionMode == Distribution::ROUNDROBIN) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_YELLOW);
if(selected) DrawCircleLinesV(position, radius+8, (Color){158, 255, 210, 255});
if(IsKeyDown(KEY_LEFT_ALT)){
float value = 0;
int offset_gate = 0;
switch(mode){
case 0:
value = (float)midi_gate/1000.0f;
offset_gate = MeasureText(std::format("{:.2f}", value).c_str(), 12);
DrawText(std::format("{:.2f}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, WHITE );
break;
case 1:
value = (float)midi_cc_value;
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, WHITE );
break;
case 2:
value = (float)midi_pitch_bend;
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, WHITE );
break;
}
} else {
switch(mode){
case 0:{
int offset = MeasureText(MIDI::Notes[(int)midi_note].c_str(), 12);
DrawText(MIDI::Notes[midi_note].c_str(), position.x-offset/2.0f, position.y-5, 12, WHITE );
break;}
case 1:{
int offset = MeasureText(std::format("{}", midi_cc).c_str(), 12);
DrawText(std::format("{}", midi_cc).c_str(), position.x-offset/2.0f, position.y-5, 12, WHITE );
break;}
case 2:{
int offset = MeasureText(std::format("{}", midi_pitch_bend).c_str(), 12);
DrawText(std::format("{}", midi_pitch_bend).c_str(), position.x-offset/2.0f, position.y-5, 12, WHITE );
break;}
}
}
Vec2D<float> connector = position - ((position - mouse)).norm() * (radius + R);
float opacity = 150.0f - Clamp(position.dist(mouse), 0.0f, 150.0f);
if(hover_connect){
DrawCircle(connector.x, connector.y, 8.0f, (Color){108, 153, 187, 255});
} else {
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 -4
View File
@@ -1,7 +1,6 @@
#pragma once
#include "datatypes.h"
#include "raylib.h"
class Movable{
@@ -17,9 +16,22 @@ class Movable{
Vec2D<float> position;
Vec2D<float> direction;
virtual void update(int fps) {};
Vec2D<float> drag_offset;
bool in_rect = false;
bool picked_up = false;
bool dragging = false;
bool hover_connect = false;
bool hovering = false;
bool connecting = false;
bool selected = false;
bool highlighted = false;
bool copied = false;
bool erase = false;
virtual void setPosition(Vec2D<float> _position) {position = _position;}
virtual void update(double frametime) {};
virtual void update() {};
virtual void draw(Vec2D<float> *mouse, Camera2D *cam) {};
};
+4 -2
View File
@@ -20,7 +20,9 @@ class NodeManager{
std::vector<int> keys;
int iterator = 0;
std::vector<std::string> fractions = {"1/32","1/16","3/32","1/8","5/32","3/16","7/32","1/4","9/32","5/16","11/32","3/8","13/32","7/16","15/32","1/2","17/32","9/16","19/32","5/8","21/32","11/16","23/32","3/4","25/32","13/16","27/32","7/8","29/32","15/16","31/32","1/1"};
Node* node(int id){
return &nodes.at(id);
}
int getRandomNode(){
std::vector<int> o;
@@ -80,7 +82,7 @@ class NodeManager{
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 = fractions.at((int)Clamp(round(d/60) - 1, 0, 31));
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);
+8 -158
View File
@@ -1,178 +1,28 @@
#pragma once
#include <map>
#include <memory>
#include <raylib.h>
#include "edge.h"
#include "datatypes.h"
#include "graph-item.h"
#include "moveable.h"
#include "raymath.h"
#define R 14.0f
struct NodeMidiOut{
int i;
std::string name;
bool enabled;
};
class Node : public Movable, public GraphItem{
public:
Node() {}
Node(int id, float x, float y, float r) : Movable(Vec2D<float>(x, y), 0.0f), GraphItem(id), radius(r) {}
Node(int id, Vec2D<float> p, float r) : Movable(p, 0.0f), GraphItem(id), radius(r) {}
Node(float x, float y) : Movable(Vec2D<float>(x, y), 0.0f) {}
Node(Vec2D<float> p) : Movable(p, 0.0f) {}
Node *neighbor = nullptr;
float radius;
bool picked_up = false;
bool dragging = false;
bool in_rect = false;
bool hover_connect = false;
bool hovering = false;
bool connecting = false;
bool config = false;
bool selected = false;
bool erase = false;
bool highlighted = false;
bool copied = false;
bool triggered = false;
bool send_note = true;
bool send_cc = false;
bool send_pc = false;
bool send_pitch_bend = false;
int midi_out = -1;
Vec2D<float> drag_offset;
std::map<int, NodeMidiOut> midi_outs;
bool gui_midi_channel_edit = false;
bool gui_midi_note_edit = false;
bool gui_midi_velocity_edit = false;
bool gui_midi_gate_edit = false;
int mode = 0;
int midi_note_channel = 1;
int midi_note = 48;
int midi_velocity = 60;
int midi_gate = 25; // gate time in milliseconds
int midi_cc_channel = 0;
int midi_cc = 0;
int midi_cc_value = 0;
int midi_pc_channel = 0;
int midi_pc = 0;
int midi_pc_value = 0;
int midi_pitch_bend_channel = 0;
int midi_pitch_bend = 8192;
float start = 0;
float fade = 0;
float time = 0;
float last = 0;
std::vector<std::shared_ptr<Edge>> connections;
Distribution distributionMode = Distribution::GEOMETRIC;
void setMidiPort(int midi_port, bool state){
midi_outs[midi_port].enabled = state;
}
float radius = 15.0f;
bool config = false;
void trigger(){
triggered = true;
start = GetTime();
fade = midi_gate;
}
void draw(Vec2D<float> *mouse, Camera2D *cam) {
last = time;
time = GetTime();
if(time - start >= (float)midi_gate / 1000.0f){
triggered = false;
fade = 0;
} else {
fade -= (time - last)*1000.0f;
}
DrawRing(position, radius, radius+4, 0, 360, 24, (Color){60, 60, 60, 255});
DrawRing(position, radius, radius+4, 0.0f - 90.0f, (360.0f/127.0f) * midi_velocity - 90.0f, 24, (Color){158, 134, 200, 255});
DrawCircleLinesV(position, radius + 4, (Color){100, 100, 100, 255});
float midi_gate_map = round((255.f / midi_gate) * fade);
Color gate_color = VS_COLOR_GREEN;
switch(mode){
case 0:{
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){180, 210, 115, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, WHITE);
break;}
case 1:{
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){229, 181, 103, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, WHITE);
break;}
case 2:{
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){232, 125, 62, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, WHITE);
break;}
}
if(!dragging && !selected && !in_rect && !hovering) DrawCircle(position.x, position.y, radius, gate_color);
if(selected || dragging || in_rect || hovering) DrawCircle(position.x, position.y, radius, (Color){108, 153, 187, 255});
if(distributionMode == Distribution::RANDOM) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_PINK);
if(distributionMode == Distribution::ROUNDROBIN) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_YELLOW);
if(selected) DrawCircleLinesV(position, radius+8, (Color){158, 255, 210, 255});
if(IsKeyDown(KEY_LEFT_ALT)){
float value = 0;
int offset_gate = 0;
switch(mode){
case 0:
value = (float)midi_gate/1000.0f;
offset_gate = MeasureText(std::format("{:.2f}", value).c_str(), 12);
DrawText(std::format("{:.2f}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, WHITE );
break;
case 1:
value = (float)midi_cc_value;
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, WHITE );
break;
case 2:
value = (float)midi_pitch_bend;
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, WHITE );
break;
}
} else {
switch(mode){
case 0:{
int offset = MeasureText(MIDI::Notes[(int)midi_note].c_str(), 12);
DrawText(MIDI::Notes[midi_note].c_str(), position.x-offset/2.0f, position.y-5, 12, WHITE );
break;}
case 1:{
int offset = MeasureText(std::format("{}", midi_cc).c_str(), 12);
DrawText(std::format("{}", midi_cc).c_str(), position.x-offset/2.0f, position.y-5, 12, WHITE );
break;}
case 2:{
int offset = MeasureText(std::format("{}", midi_pitch_bend).c_str(), 12);
DrawText(std::format("{}", midi_pitch_bend).c_str(), position.x-offset/2.0f, position.y-5, 12, WHITE );
break;}
}
}
Vec2D<float> connector = position - ((position - *mouse)).norm() * (radius + R);
float opacity = 150.0f - Clamp(position.dist(mouse), 0.0f, 150.0f);
if(hover_connect){
DrawCircle(connector.x, connector.y, 8.0f, (Color){108, 153, 187, 255});
} else {
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});
}
virtual void setMode(int mode) = 0;
};
+9 -9
View File
@@ -12,7 +12,7 @@ class SignalManager{
public:
SignalManager() {}
SignalManager(GraphManager *gm, std::mutex *mutex) : mutex(mutex), gm(gm), nm(nm) {}
SignalManager(GraphManager *gm, std::mutex *mutex) : mutex(mutex), gm(gm) {}
Midi midi;
@@ -39,7 +39,7 @@ class SignalManager{
void reset(){
for(auto &s : signals){
s.position = gm->nodes[s.start].position + Vec2D<float>{0.1, 0.1};
s.position = gm->node(s.start)->position + Vec2D<float>{0.1, 0.1};
s.next = s.start;
s.last = s.start;
}
@@ -57,7 +57,7 @@ class SignalManager{
void addSignalAtNode(int node_id){
Color c = {(unsigned char)(20 + (rand() % 200)), 100, (unsigned char)(20 + (int)(rand() % 200)), 200};
signals.emplace(signals.end(), Signal(gm->nodes[node_id].position.x+1, gm->nodes[node_id].position.y+1, 8.0f, 0, c)); // position + 1 or else it does not render. why? no idea!
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;
@@ -75,18 +75,18 @@ class SignalManager{
mutex->lock();
if(play){
for(auto& s : signals){
s.direction = (gm->node(s.next).position - s.position).norm();
s.direction = (gm->node(s.next)->position - s.position).norm();
s.update(diff.count());
if(s.position.dist(&gm->nodes[s.next].position) < 0.6f) {
if(s.position.dist(&gm->node(s.next)->position) < 0.6f) {
int n = gm->next(s.next, s.last);
gm->triggerNode(s.next);
if(n != s.next && n != -1) {
for(auto &m : gm->nodes[s.next].midi_outs){
for(auto &m : gm->node(s.next)->midi_outs){
if(m.second.enabled) {
if(gm->nodes[s.next].send_note) midi.note(m.second.i, gm->nodes[s.next].midi_note_channel, gm->nodes[s.next].midi_note, gm->nodes[s.next].midi_velocity, gm->nodes[s.next].midi_gate);
if(gm->nodes[s.next].send_cc) midi.cc(m.second.i, gm->nodes[s.next].midi_cc_channel, gm->nodes[s.next].midi_cc, gm->nodes[s.next].midi_cc_value);
if(gm->nodes[s.next].send_pitch_bend) midi.pitch_bend(m.second.i, gm->nodes[s.next].midi_pitch_bend_channel, gm->nodes[s.next].midi_pitch_bend);
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);
}
}
s.last = s.next;
+9 -9
View File
@@ -1,6 +1,6 @@
#pragma once
#include "connection.h"
#include "edge.h"
#include "datatypes.h"
#include "graph-manager.h"
#include "signal-manager.h"
@@ -45,8 +45,8 @@ class AppState{
void copyNodesAndConnections(Vec2D<float> 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<std::vector<int>>();
std::vector<Connection> copy_connections;
std::ranges::copy_if(gm.connections, std::back_inserter(copy_connections), [copy_nodes_ids](Connection c){return std::ranges::contains(copy_nodes_ids, c.v1) && std::ranges::contains(copy_nodes_ids, c.v2);});
std::vector<Edge> 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);});
for(auto &n : copy_nodes_ids){
int old_id = n;
@@ -55,8 +55,8 @@ class AppState{
gm.nodes[new_id].selected = true;
for(auto &c : copy_connections){
if(c.v1 == old_id) c.v1 = new_id;
if(c.v2 == old_id) c.v2 = new_id;
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;
}
}
@@ -76,13 +76,13 @@ class AppState{
erase_nodes_ids.push_back(e.id);
}
auto erase_connections = gm.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);});
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);});
std::vector<int> adjacent_nodes_ids;
for(auto &c : erase_connections){
if(!gm.nodes.at(c.v1).selected) adjacent_nodes_ids.push_back(c.v1);
if(!gm.nodes.at(c.v2).selected) adjacent_nodes_ids.push_back(c.v2);
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){
@@ -111,7 +111,7 @@ class AppState{
}
for(auto &n : erase_nodes){
std::erase_if(gm.connections, [n](Connection c){ return c.v1 == n.id || c.v2 == n.id;});
std::erase_if(gm.connections, [n](Edge c){ return c.node_id_start == n.id || c.node_id_end == n.id;});
}
std::erase_if(gm.nodes, [](std::pair<int, Node> n){ return n.second.erase;});
gm.keys.clear();