heavy restructuring with smart pointers, not finished
This commit is contained in:
+12
-14
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user