better state logic, more features, rewritten timing of signals and more crashes
This commit is contained in:
@@ -9,3 +9,9 @@ inline bool ViButton(Vec2D<float> *mouse_pos, float x, float y, float radius, Co
|
||||
DrawCircle(x,y,radius, color);
|
||||
return (Vector2Distance(mp, (Vector2){x,y}) < radius && IsMouseButtonPressed(MOUSE_BUTTON_LEFT));
|
||||
}
|
||||
|
||||
inline bool ViSegButton(Vec2D<float> *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
|
||||
DrawCircleSector((Vector2){x,y},radius, start_angle, end_angle, 5, color);
|
||||
return (Vector2Distance(mp, (Vector2){x,y}) < radius && IsMouseButtonPressed(MOUSE_BUTTON_LEFT));
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ class ConnectionManager{
|
||||
|
||||
std::map<Node*, int> counter;
|
||||
|
||||
void reset(){
|
||||
counter.clear();
|
||||
}
|
||||
|
||||
Node* next(Node* current, Node* last){
|
||||
std::vector<Connection*> paths;
|
||||
std::vector<Node*> nodes;
|
||||
|
||||
+29
-8
@@ -19,6 +19,9 @@ class Connection{
|
||||
bool remove = false;
|
||||
bool config = false;
|
||||
|
||||
float connector_offset = 14.0f;
|
||||
float switches_offset = 25.0f;
|
||||
|
||||
void flip(){
|
||||
Node* vt = v1;
|
||||
v1 = v2;
|
||||
@@ -26,31 +29,49 @@ class Connection{
|
||||
}
|
||||
|
||||
void draw(Vec2D<float> *mouse_pos, Camera2D *cam){
|
||||
Vec2D<float> v1_position = v1->position - ((v1->position - v2->position)).norm() * (v1->radius + 14.0f);
|
||||
Vec2D<float> v2_position = v2->position - ((v2->position - v1->position)).norm() * (v2->radius + 14.0f);
|
||||
Vec2D<float> v1_position = v1->position - ((v1->position - v2->position)).norm() * (v1->radius + connector_offset);
|
||||
Vec2D<float> v2_position = v2->position - ((v2->position - v1->position)).norm() * (v2->radius + connector_offset);
|
||||
float v1_v2_distance = v1_position.dist(&v2_position);
|
||||
|
||||
// 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);
|
||||
DrawCircle(v1_position.x, v1_position.y, 2.0f, c);
|
||||
DrawCircle(v2_position.x, v2_position.y, 2.0f, c);
|
||||
|
||||
Vec2D<float> from_to_handle = v1->position - ((v1->position - v2->position)).norm() * (v1->radius + 35);
|
||||
Vec2D<float> to_from_handle = v2->position - ((v2->position - v1->position)).norm() * (v2->radius + 35);
|
||||
if(v1_v2_distance < 100) switches_offset = v1_v2_distance/2.3f;
|
||||
Vec2D<float> from_to_handle = v1->position - ((v1->position - v2->position)).norm() * (v1->radius + switches_offset);
|
||||
Vec2D<float> to_from_handle = v2->position - ((v2->position - v1->position)).norm() * (v2->radius + switches_offset);
|
||||
Vec2D<float> middle_handle = v2->position - ((v2->position - v1->position))*0.5f;
|
||||
Vec2D<float> config_handle = v2->position - ((v2->position - v1->position))*0.66f;
|
||||
// Vec2D<float> flip_handle = (v1_position - v2_position).norm().orth()*20.0f;
|
||||
// Vec2D<float> config_handle = v2->position - ((v2->position - 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, 120.0f);
|
||||
unsigned char opacity_to = 250.0f - Clamp(to_from_handle.dist(mouse_pos), 0.0f, 120.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(ViButton(mouse_pos, middle_handle.x, middle_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(!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();
|
||||
}
|
||||
};
|
||||
|
||||
+2
-3
@@ -25,7 +25,7 @@ class Vec2D{
|
||||
|
||||
T length() { return (T)sqrt(pow(x, 2) + pow(y, 2));};
|
||||
|
||||
T angle(Vec2D &v){
|
||||
T angle(Vec2D v){
|
||||
T angle = atan2(v.y, v.x) - atan2(y,x);
|
||||
if(angle > M_PI) angle -= M_PI_2;
|
||||
if(angle < -M_PI) angle += M_PI_2;
|
||||
@@ -33,8 +33,7 @@ class Vec2D{
|
||||
//return acos(dot(v));
|
||||
};
|
||||
|
||||
T angleDeg(Vec2D &v){
|
||||
|
||||
T angleDeg(Vec2D v){
|
||||
return angle(v) * 180 / M_PI;
|
||||
};
|
||||
|
||||
|
||||
+242
-258
@@ -19,6 +19,7 @@
|
||||
#include "datatypes.h"
|
||||
#include "signal-manager.h"
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <ranges>
|
||||
#include <raylib.h>
|
||||
@@ -70,12 +71,16 @@ int main(){
|
||||
|
||||
SetWindowMonitor(monitor);
|
||||
|
||||
app.fps = 60.0f;
|
||||
app.fps = 1.0f;
|
||||
app.bpm = 120.0f;
|
||||
app.grid = 120.0f; //round(((app.fps / (app.bpm / 60.0f)) * app.speed) / 4.0f); // from quarter notes to 16th notes = div by 4
|
||||
// app.speed = round((((app.fps / (app.bpm / 60.0f)) / app.grid) / 4.0f) / 10.0f) * 10.0f;
|
||||
|
||||
app.speed = (app.grid * 4.0f) / (app.fps / (app.bpm / 60.0f));
|
||||
// 16th note = 120px (festgelegt)
|
||||
// ganze note = 1920px
|
||||
// ganze note bei 120bpm = 2s
|
||||
// 120bpm = 960px/s
|
||||
app.speed = (app.grid * 8.0f) * (120.0f / app.bpm);
|
||||
|
||||
// 120 fps
|
||||
// 120bpm = 120 1/4 Noten pro Minute = 2 1/4 Noten / s
|
||||
@@ -94,14 +99,14 @@ int main(){
|
||||
app.width = GetMonitorWidth(monitor);
|
||||
app.height = GetMonitorHeight(monitor);
|
||||
|
||||
SetTargetFPS(60.0f);
|
||||
|
||||
Camera2D camera = { {0} };
|
||||
camera.target = (Vector2){ app.width/2.0f, app.height/2.0f };
|
||||
camera.offset = (Vector2){ app.width/2.0f, app.height/2.0f };
|
||||
camera.rotation = 0.0f;
|
||||
camera.zoom = 1.0f;
|
||||
|
||||
SetTargetFPS(app.fps);
|
||||
|
||||
nodes.reserve(1000);
|
||||
|
||||
for(int i = 0; i < 4; ++i){
|
||||
@@ -126,7 +131,8 @@ int main(){
|
||||
|
||||
double drag_timer = GetTime();
|
||||
|
||||
enum InteractionState {HOVER_CANVAS, HOVER_NODE, HOVER_CONNECTOR, DRAG_NODE, CONNECT_NODE};
|
||||
enum InteractionState {HOVER_CANVAS, HOVER_NODE, HOVER_CONNECTOR, DRAG_NODE, CONNECT_NODE, DRAW_SELECTION};
|
||||
InteractionState istate = HOVER_CANVAS;
|
||||
|
||||
Vec2D<float> select_start;
|
||||
Rectangle selection;
|
||||
@@ -135,8 +141,12 @@ int main(){
|
||||
|
||||
while(!WindowShouldClose()){
|
||||
|
||||
BeginDrawing();
|
||||
ClearBackground((Color){46,46,46});
|
||||
BeginMode2D(camera);
|
||||
|
||||
//app.grid = round(((app.fps / (app.bpm / 60.0f)) * app.speed) / 4.0f); // from quarter notes to 16th notes = div by 4
|
||||
app.speed = (app.grid * 4.0f) / (app.fps / (app.bpm / 60.0f));
|
||||
app.speed = (app.grid * 8.0f) * (120.0f / app.bpm);
|
||||
|
||||
Vector2 mp = GetScreenToWorld2D(GetMousePosition(), camera);
|
||||
Vec2D<float> mouse_pos = {(float)mp.x, (float)mp.y};
|
||||
@@ -150,25 +160,114 @@ int main(){
|
||||
bool mouse_right_clicked = IsMouseButtonPressed(MOUSE_BUTTON_RIGHT);
|
||||
bool mouse_right_released = IsMouseButtonReleased(MOUSE_BUTTON_RIGHT);
|
||||
|
||||
for(auto &n : nodes){
|
||||
n.hovering = false;
|
||||
n.hover_connect = false;
|
||||
}
|
||||
Vec2D<float> mouse_delta = {GetMouseDelta().x, GetMouseDelta().y};
|
||||
Vec2D<float> delta = mouse_delta * (-1.0f/camera.zoom);
|
||||
|
||||
auto dragging_nodes = nodes | std::ranges::views::filter([](Node n) { return n.dragging;});
|
||||
auto connecting_nodes = nodes | std::ranges::views::filter([](Node n) { return n.connecting;});
|
||||
auto selected_nodes = nodes | std::ranges::views::filter([](Node n) { return n.selected && !n.dragging;});
|
||||
auto hovering_nodes = nodes | std::ranges::views::filter([&mouse_pos](Node n) { return CheckCollisionPointCircle(mouse_pos, n.position, n.radius);});
|
||||
auto hovering_connectors = nodes | std::ranges::views::filter([&mouse_pos](Node n) { return CheckCollisionPointCircle(mouse_pos, (n.position - ((n.position - mouse_pos)).norm() * (n.radius + 14.0f)), 8.0f);});
|
||||
|
||||
if(!mouse_down && !mouse_right_down && dragging_nodes.empty() && connecting_nodes.empty() && hovering_nodes.empty() && hovering_connectors.empty()) istate = InteractionState::HOVER_CANVAS;
|
||||
if((mouse_clicked || mouse_down || mouse_released) && !mouse_right_down && dragging_nodes.empty() && connecting_nodes.empty() && hovering_nodes.empty() && hovering_connectors.empty()) istate = InteractionState::DRAW_SELECTION;
|
||||
|
||||
if(!hovering_nodes.empty() && !mouse_down && !mouse_right_down) istate = InteractionState::HOVER_NODE;
|
||||
if(!dragging_nodes.empty()) istate = InteractionState::DRAG_NODE;
|
||||
if(!hovering_connectors.empty() && istate == InteractionState::HOVER_CANVAS) istate = InteractionState::HOVER_CONNECTOR;
|
||||
if(!connecting_nodes.empty()) istate = InteractionState::CONNECT_NODE;
|
||||
|
||||
switch(istate){
|
||||
case InteractionState::HOVER_CANVAS:
|
||||
if(mouse_right_down) {
|
||||
camera.target = Vector2Add(camera.target, delta);
|
||||
}
|
||||
break;
|
||||
case InteractionState::DRAW_SELECTION:
|
||||
if(mouse_clicked){
|
||||
select_start = mouse_pos;
|
||||
}
|
||||
if(mouse_down){
|
||||
Vec2D<float> select1 = {mouse_pos.x > select_start.x ? select_start.x : mouse_pos.x, mouse_pos.y > select_start.y ? select_start.y : mouse_pos.y};
|
||||
Vec2D<float> select2 = {mouse_pos.x > select_start.x ? mouse_pos.x - select_start.x : select_start.x - mouse_pos.x, mouse_pos.y > select_start.y ? mouse_pos.y - select_start.y : select_start.y - mouse_pos.y};
|
||||
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 : nodes){
|
||||
if(CheckCollisionPointRec(n.position, selection)) {
|
||||
n.in_rect = true;
|
||||
} else {
|
||||
n.in_rect = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(hovering_nodes.empty() && IsMouseButtonDown(MOUSE_BUTTON_RIGHT)){
|
||||
Vector2 delta = GetMouseDelta();
|
||||
delta = Vector2Scale(delta, -1.0f/camera.zoom);
|
||||
camera.target = Vector2Add(camera.target, delta);
|
||||
}
|
||||
if(mouse_released){
|
||||
for(auto& n : nodes){
|
||||
if(CheckCollisionPointRec(n.position, selection)) {
|
||||
n.selected = true;
|
||||
n.in_rect = false;
|
||||
} else {
|
||||
if(!IsKeyDown(KEY_LEFT_SHIFT)) n.selected = false;
|
||||
}
|
||||
}
|
||||
selection = (Rectangle){0,0,0,0};
|
||||
}
|
||||
break;
|
||||
case InteractionState::HOVER_NODE:
|
||||
if(mouse_clicked) {
|
||||
if(IsKeyDown(KEY_LEFT_SHIFT)) {
|
||||
hovering_nodes.front().selected = true;
|
||||
} else {
|
||||
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;
|
||||
break;
|
||||
case InteractionState::DRAG_NODE:
|
||||
if(IsKeyDown(KEY_LEFT_CONTROL)) {
|
||||
dragging_nodes.front().position = (Vec2D<float>){round((mouse_pos.x+dragging_nodes.front().drag_offset.x)/app.grid)*app.grid, round((mouse_pos.y+dragging_nodes.front().drag_offset.y)/app.grid)*app.grid};
|
||||
} else {
|
||||
dragging_nodes.front().position = mouse_pos + dragging_nodes.front().drag_offset;
|
||||
}
|
||||
|
||||
for(auto &n : selected_nodes){
|
||||
if(!n.picked_up) n.drag_offset = n.position - dragging_nodes.front().position;
|
||||
n.picked_up = true;
|
||||
n.position = dragging_nodes.front().position + n.drag_offset;
|
||||
}
|
||||
|
||||
if(mouse_released) {
|
||||
dragging_nodes.front().dragging = false;
|
||||
for(auto &n : selected_nodes){
|
||||
n.picked_up = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case InteractionState::HOVER_CONNECTOR:
|
||||
if(mouse_clicked) hovering_connectors.front().connecting = true;
|
||||
break;
|
||||
case InteractionState::CONNECT_NODE:
|
||||
Vec2D<float> 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){
|
||||
DrawLine(connector_position.x, connector_position.y, mouse_pos.x, mouse_pos.y, (Color){229, 181, 103, 255});
|
||||
}
|
||||
|
||||
if(mouse_released) {
|
||||
if(!hovering_nodes.empty()){
|
||||
cm.addConnection(&connecting_nodes.front(), &hovering_nodes.front());
|
||||
} else if(!hovering_connectors.empty()){
|
||||
cm.addConnection(&connecting_nodes.front(), &hovering_connectors.front());
|
||||
} else {
|
||||
nodes.emplace_back(mouse_pos.x, mouse_pos.y, 15.0f);
|
||||
cm.addConnection(&connecting_nodes.front(), &nodes.back());
|
||||
}
|
||||
connecting_nodes.front().connecting = false;
|
||||
}
|
||||
break;
|
||||
};
|
||||
|
||||
float wheel = GetMouseWheelMove();
|
||||
if(wheel != 0){
|
||||
@@ -186,269 +285,154 @@ int main(){
|
||||
}
|
||||
}
|
||||
|
||||
if(mouse_clicked){
|
||||
drag_start = mouse_pos;
|
||||
for(int x = -app.grid*100; x < app.grid*100; x+=app.grid){
|
||||
for(int y = -app.grid*100; y < app.grid*100; y+=app.grid){
|
||||
DrawPixel(x, y, (Color){130,130,130,200});
|
||||
}
|
||||
}
|
||||
|
||||
BeginDrawing();
|
||||
DrawFPS(20, app.height - 100);
|
||||
ClearBackground((Color){46,46,46});
|
||||
for(auto& n : nodes){
|
||||
n.draw(&mouse_pos, &camera);
|
||||
}
|
||||
|
||||
BeginMode2D(camera);
|
||||
bool connecting = false;
|
||||
cm.drawConnections(&mouse_pos, &camera);
|
||||
sm.drawSignals(&mouse_pos, &camera);
|
||||
|
||||
for(int x = -app.grid*100; x < app.grid*100; x+=app.grid){
|
||||
for(int y = -app.grid*100; y < app.grid*100; y+=app.grid){
|
||||
DrawPixel(x, y, (Color){130,130,130,200});
|
||||
midi.update();
|
||||
|
||||
EndMode2D();
|
||||
|
||||
// Main Config Window
|
||||
GuiWindowBox((Rectangle){0,0,280,(float)app.height}, "config");
|
||||
{
|
||||
const int s_controls = 55;
|
||||
if(GuiButton((Rectangle){20, (float)s_controls, 60, 30}, "PLAY")) sm.play ? sm.pause() : sm.start();
|
||||
if(GuiButton((Rectangle){100, (float)s_controls, 60, 30}, "STOP")) sm.stop();
|
||||
if(GuiButton((Rectangle){180, (float)s_controls, 60, 30}, "RESET")) sm.reset();
|
||||
|
||||
const int s_bpm = s_controls + 35;
|
||||
GuiLabel((Rectangle){20, (float)s_bpm, 120, 20}, "TIMING");
|
||||
GuiLabel((Rectangle){200, (float)s_bpm, 40, 20}, std::format("{}",app.bpm).c_str());
|
||||
float _bpm = (float)app.bpm;
|
||||
GuiSlider((Rectangle){20, (float)s_bpm + 25, 240, 20}, "", "", &_bpm, 1, 300);
|
||||
app.bpm = _bpm;
|
||||
|
||||
int s_midi = s_bpm + 55;
|
||||
GuiLabel((Rectangle){20, (float)s_midi, 240, 20}, "MIDI OUT");
|
||||
int i = 0;
|
||||
for(auto& mo : midi.midi_out_ports){
|
||||
GuiCheckBox((Rectangle){20, (float)s_midi + 25 + i*20.0f, 20, 20}, mo.port.port_name.c_str(), &mo.enabled);
|
||||
i++;
|
||||
}
|
||||
|
||||
if(GuiButton((Rectangle){20, (float)s_midi + 45 + i*20.0f, 240, 20}, "ENABLE FOR ALL")) {
|
||||
for(auto &midis : midi.midi_out_ports){
|
||||
if(midis.enabled){
|
||||
for(auto &n : nodes){
|
||||
n.midi_outs[midis.number].enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto &h : hovering_connectors){
|
||||
h.hover_connect = true;
|
||||
int s_signal = s_midi + 90 + i*20;
|
||||
GuiLabel((Rectangle){20, (float)s_signal, 240, 20}, "SIGNALS");
|
||||
if(GuiButton((Rectangle){20, (float)s_signal + 35, 240, 20}, "ADD RANDOM SIGNAL")){
|
||||
int ni = rand() % nodes.size();
|
||||
sm.addSignalAtNode(&nodes[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 = app.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, 2);
|
||||
s.multiplier = s_multiplier;
|
||||
signal_configs++;
|
||||
}
|
||||
|
||||
int s_nodes = s_signal + 60 + signal_configs*20;
|
||||
GuiLabel((Rectangle){20, (float)s_nodes + 30, 240, 20}, "NODES");
|
||||
if(GuiButton((Rectangle){20, (float)s_nodes + 60, 240, 20}, "ADD RANDOM NODE")){
|
||||
nodes.emplace_back(200.0f, 200.0f, 15.0f);
|
||||
}
|
||||
|
||||
DrawFPS(20, app.height - 30);
|
||||
}
|
||||
|
||||
// Per Node Config Windows
|
||||
for(auto& n : nodes){
|
||||
Vector2 np = GetWorldToScreen2D((Vector2){n.position.x, n.position.y}, camera);
|
||||
|
||||
for(auto& mo : 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(n.midi_outs.contains(mo.number) && !mo.enabled){
|
||||
n.midi_outs.erase(mo.number);
|
||||
}
|
||||
}
|
||||
|
||||
if(n.config){
|
||||
n.config = !GuiWindowBox((Rectangle){np.x + 10, np.y + 10, 240, 440}, "config");
|
||||
|
||||
int option = n.mode;
|
||||
GuiToggleGroup((Rectangle){ np.x + 20, np.y + 40, 110, 20 }, "Round Robin;Random", &option);
|
||||
n.mode = (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);
|
||||
}
|
||||
|
||||
if(dragging_nodes.empty() && connecting_nodes.empty()) {
|
||||
int y_midi = 120;
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_midi, 220, 20}, "MIDI OUT");
|
||||
|
||||
if(!hovering_connectors.empty()){
|
||||
if(mouse_clicked){
|
||||
hovering_connectors.front().connecting = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(hovering_nodes.empty() || selection.width > 0){
|
||||
if(mouse_clicked){
|
||||
select_start = mouse_pos;
|
||||
for(auto &n : nodes){
|
||||
n.selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(mouse_down){
|
||||
Vec2D<float> select1 = {mouse_pos.x > select_start.x ? select_start.x : mouse_pos.x, mouse_pos.y > select_start.y ? select_start.y : mouse_pos.y};
|
||||
Vec2D<float> select2 = {mouse_pos.x > select_start.x ? mouse_pos.x - select_start.x : select_start.x - mouse_pos.x, mouse_pos.y > select_start.y ? mouse_pos.y - select_start.y : select_start.y - mouse_pos.y};
|
||||
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 : nodes){
|
||||
if(CheckCollisionPointRec(n.position, selection)) {
|
||||
n.in_rect = true;
|
||||
} else {
|
||||
n.in_rect = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(mouse_released){
|
||||
for(auto& n : nodes){
|
||||
if(CheckCollisionPointRec(n.position, selection)) {
|
||||
n.selected = true;
|
||||
}
|
||||
}
|
||||
selection = (Rectangle){0,0,0,0};
|
||||
}
|
||||
}
|
||||
|
||||
if(!hovering_nodes.empty()){
|
||||
hovering_nodes.front().hovering = true;
|
||||
if(selection.width == 0.0f){
|
||||
if(mouse_clicked){
|
||||
drag_timer = GetTime();
|
||||
}
|
||||
if(mouse_right_down && GetTime() - drag_timer > 0.1){
|
||||
hovering_nodes.front().drag_offset = hovering_nodes.front().position - mouse_pos;
|
||||
hovering_nodes.front().dragging = true;
|
||||
}
|
||||
if(mouse_right_released && GetTime() - drag_timer <= 0.1){
|
||||
hovering_nodes.front().config = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if(!dragging_nodes.empty()){
|
||||
|
||||
if(mouse_down){
|
||||
if(IsKeyDown(KEY_LEFT_CONTROL)) {
|
||||
dragging_nodes.front().position = (Vec2D<float>){round((mouse_pos.x+dragging_nodes.front().drag_offset.x)/app.grid)*app.grid, round((mouse_pos.y+dragging_nodes.front().drag_offset.y)/app.grid)*app.grid};
|
||||
} else {
|
||||
dragging_nodes.front().position = mouse_pos + dragging_nodes.front().drag_offset;
|
||||
}
|
||||
}
|
||||
if(mouse_released) {
|
||||
dragging_nodes.front().dragging = false;
|
||||
}
|
||||
|
||||
auto selected_nodes = nodes | std::ranges::views::filter([](Node n) { return n.selected && !n.dragging;});
|
||||
if(!selected_nodes.empty() && mouse_down){
|
||||
for(auto &n : selected_nodes){
|
||||
if(!n.picked_up) n.drag_offset = n.position - dragging_nodes.front().position;
|
||||
n.picked_up = true;
|
||||
n.position = dragging_nodes.front().position + n.drag_offset;
|
||||
}
|
||||
}
|
||||
if(mouse_released) {
|
||||
dragging_nodes.front().dragging = false;
|
||||
for(auto &n : selected_nodes){
|
||||
n.picked_up = false;
|
||||
}
|
||||
}
|
||||
|
||||
} else if(!connecting_nodes.empty() || connecting){
|
||||
|
||||
Vec2D<float> 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){
|
||||
DrawLine(connector_position.x, connector_position.y, mouse_pos.x, mouse_pos.y, (Color){229, 181, 103, 255});
|
||||
}
|
||||
|
||||
if(mouse_released) {
|
||||
if(!hovering_nodes.empty()){
|
||||
cm.addConnection(&connecting_nodes.front(), &hovering_nodes.front());
|
||||
} else if(!hovering_connectors.empty()){
|
||||
cm.addConnection(&connecting_nodes.front(), &hovering_connectors.front());
|
||||
} else {
|
||||
nodes.emplace_back(mouse_pos.x, mouse_pos.y, 15.0f);
|
||||
cm.addConnection(&connecting_nodes.front(), &nodes.back());
|
||||
}
|
||||
connecting_nodes.front().connecting = false;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& n : nodes){
|
||||
n.draw(&mouse_pos, &camera);
|
||||
}
|
||||
|
||||
cm.drawConnections(&mouse_pos, &camera);
|
||||
sm.drawSignals(&mouse_pos, &camera);
|
||||
|
||||
midi.update();
|
||||
EndMode2D();
|
||||
|
||||
// Main Config Window
|
||||
GuiWindowBox((Rectangle){0,0,280,(float)app.height}, "config");
|
||||
{
|
||||
const int s_bpm = 55;
|
||||
GuiLabel((Rectangle){20, (float)s_bpm, 120, 20}, "TIMING");
|
||||
GuiLabel((Rectangle){200, (float)s_bpm, 40, 20}, std::format("{}",app.bpm).c_str());
|
||||
float _bpm = (float)app.bpm;
|
||||
GuiSlider((Rectangle){20, (float)s_bpm + 25, 240, 20}, "", "", &_bpm, 1, 300);
|
||||
app.bpm = _bpm;
|
||||
|
||||
int s_midi = 100;
|
||||
GuiLabel((Rectangle){20, (float)s_midi, 240, 20}, "MIDI OUT");
|
||||
int i = 0;
|
||||
for(auto& mo : midi.midi_out_ports){
|
||||
GuiCheckBox((Rectangle){20, (float)s_midi + 25 + i*20.0f, 20, 20}, mo.port.port_name.c_str(), &mo.enabled);
|
||||
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(GuiButton((Rectangle){20, (float)s_midi + 45 + i*20.0f, 240, 20}, "ENABLE FOR ALL")) {
|
||||
for(auto &midis : midi.midi_out_ports){
|
||||
if(midis.enabled){
|
||||
for(auto &n : nodes){
|
||||
n.midi_outs[midis.number].enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int y_channel = y_midi + 30 + i*20;
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_channel, 220, 20}, "CHANNEL");
|
||||
GuiToggleGroup((Rectangle){np.x + 20, np.y + y_channel + 30, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &n.midi_channel);
|
||||
|
||||
int s_signal = s_midi + 90 + i*20;
|
||||
GuiLabel((Rectangle){20, (float)s_signal, 240, 20}, "SIGNALS");
|
||||
if(GuiButton((Rectangle){20, (float)s_signal + 35, 240, 20}, "ADD RANDOM SIGNAL")){
|
||||
int ni = rand() % nodes.size();
|
||||
sm.addSignalAtNode(&nodes[ni]);
|
||||
}
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_channel + 90, 100, 20}, "NOTE");
|
||||
float v = n.midi_note;
|
||||
GuiLabel((Rectangle){np.x + 210, np.y + y_channel + 90, 20, 20}, midi.midiNotes[(int)round(v)].c_str());
|
||||
GuiLabel((Rectangle){np.x + 160, np.y + y_channel + 90, 40, 20}, std::format("{}",n.midi_note).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_channel + 120, 220, 20}, "", "", &v, 0, 127);
|
||||
n.midi_note = round(v);
|
||||
|
||||
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_speed = round(s.speed*10.0f)/10.0f;
|
||||
float signal_bpm = (app.bpm / app.speed) * s_speed;
|
||||
GuiSlider((Rectangle){70, (float)s_signal+70 + signal_configs*20, 160, 20}, "", std::format("{:.1f}",signal_bpm).c_str(), &s_speed, 0.1, 24);
|
||||
s.speed = s_speed;
|
||||
signal_configs++;
|
||||
}
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_channel + 150, 100, 20}, "VELOCITY");
|
||||
v = n.midi_velocity;
|
||||
GuiLabel((Rectangle){np.x + 210, np.y + y_channel + 150, 20, 20}, std::format("{}",n.midi_velocity).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_channel + 180, 220, 20}, "", "", &v, 0, 127);
|
||||
n.midi_velocity = round(v);
|
||||
|
||||
int s_nodes = s_signal + 60 + signal_configs*20;
|
||||
GuiLabel((Rectangle){20, (float)s_nodes + 30, 240, 20}, "NODES");
|
||||
if(GuiButton((Rectangle){20, (float)s_nodes + 60, 240, 20}, "ADD RANDOM NODE")){
|
||||
nodes.emplace_back(200.0f, 200.0f, 15.0f);
|
||||
}
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_channel + 210, 100, 20}, "GATE");
|
||||
v = n.midi_gate;
|
||||
GuiLabel((Rectangle){np.x + 210, np.y + y_channel + 210, 20, 20}, std::format("{}",(float)n.midi_gate/1000.0f).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_channel + 240, 220, 20}, "", "", &v, 0, 5000);
|
||||
n.midi_gate = round(v);
|
||||
|
||||
//if(GuiValueBox((Rectangle){n.position.x + 100, n.position.y + y_channel + 40, 140, 20}, "VELOCITY", &n.midi_velocity, 0, 127, n.gui_midi_velocity_edit)) n.gui_midi_velocity_edit = !n.gui_midi_velocity_edit;
|
||||
//if(GuiValueBox((Rectangle){n.position.x + 100, n.position.y + y_channel + 60, 140, 20}, "GATE", &n.midi_gate, 0, 5000, n.gui_midi_gate_edit)) n.gui_midi_gate_edit = !n.gui_midi_gate_edit;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& c : cm.connections){
|
||||
if(c.config){
|
||||
Vec2D<float> config_handle = c.v2->position - ((c.v2->position - c.v1->position))*0.75f;
|
||||
c.config = !GuiWindowBox((Rectangle){config_handle.x + 10, config_handle.y + 10, 200, 300}, "connection");
|
||||
GuiToggle((Rectangle){config_handle.x + 10, config_handle.y + 50, 80, 20}, "v1v2", &c.v1_v2);
|
||||
GuiToggle((Rectangle){config_handle.x + 100, config_handle.y + 50, 80, 20}, "v2v1", &c.v2_v1);
|
||||
|
||||
}
|
||||
|
||||
// Per Node Config Windows
|
||||
for(auto& n : nodes){
|
||||
Vector2 np = GetWorldToScreen2D((Vector2){n.position.x, n.position.y}, camera);
|
||||
|
||||
for(auto& mo : 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(n.midi_outs.contains(mo.number) && !mo.enabled){
|
||||
n.midi_outs.erase(mo.number);
|
||||
}
|
||||
}
|
||||
|
||||
if(n.config){
|
||||
n.config = !GuiWindowBox((Rectangle){np.x + 10, np.y + 10, 240, 440}, "config");
|
||||
|
||||
int option = n.mode;
|
||||
GuiToggleGroup((Rectangle){ np.x + 20, np.y + 40, 110, 20 }, "Round Robin;Random", &option);
|
||||
n.mode = (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);
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
int y_channel = y_midi + 30 + i*20;
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_channel, 220, 20}, "CHANNEL");
|
||||
GuiToggleGroup((Rectangle){np.x + 20, np.y + y_channel + 30, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &n.midi_channel);
|
||||
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_channel + 90, 100, 20}, "NOTE");
|
||||
float v = n.midi_note;
|
||||
GuiLabel((Rectangle){np.x + 210, np.y + y_channel + 90, 20, 20}, midi.midiNotes[(int)round(v)].c_str());
|
||||
GuiLabel((Rectangle){np.x + 160, np.y + y_channel + 90, 40, 20}, std::format("{}",n.midi_note).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_channel + 120, 220, 20}, "", "", &v, 0, 127);
|
||||
n.midi_note = round(v);
|
||||
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_channel + 150, 100, 20}, "VELOCITY");
|
||||
v = n.midi_velocity;
|
||||
GuiLabel((Rectangle){np.x + 210, np.y + y_channel + 150, 20, 20}, std::format("{}",n.midi_velocity).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_channel + 180, 220, 20}, "", "", &v, 0, 127);
|
||||
n.midi_velocity = round(v);
|
||||
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_channel + 210, 100, 20}, "GATE");
|
||||
v = n.midi_gate;
|
||||
GuiLabel((Rectangle){np.x + 210, np.y + y_channel + 210, 20, 20}, std::format("{}",(float)n.midi_gate/1000.0f).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_channel + 240, 220, 20}, "", "", &v, 0, 5000);
|
||||
n.midi_gate = round(v);
|
||||
|
||||
//if(GuiValueBox((Rectangle){n.position.x + 100, n.position.y + y_channel + 40, 140, 20}, "VELOCITY", &n.midi_velocity, 0, 127, n.gui_midi_velocity_edit)) n.gui_midi_velocity_edit = !n.gui_midi_velocity_edit;
|
||||
//if(GuiValueBox((Rectangle){n.position.x + 100, n.position.y + y_channel + 60, 140, 20}, "GATE", &n.midi_gate, 0, 5000, n.gui_midi_gate_edit)) n.gui_midi_gate_edit = !n.gui_midi_gate_edit;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& c : cm.connections){
|
||||
if(c.config){
|
||||
Vec2D<float> config_handle = c.v2->position - ((c.v2->position - c.v1->position))*0.75f;
|
||||
c.config = !GuiWindowBox((Rectangle){config_handle.x + 10, config_handle.y + 10, 200, 300}, "connection");
|
||||
GuiToggle((Rectangle){config_handle.x + 10, config_handle.y + 50, 80, 20}, "v1v2", &c.v1_v2);
|
||||
GuiToggle((Rectangle){config_handle.x + 100, config_handle.y + 50, 80, 20}, "v2v1", &c.v2_v1);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EndDrawing();
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ class Movable{
|
||||
Vec2D<float> direction;
|
||||
|
||||
virtual void update(int fps) {};
|
||||
virtual void update() {};
|
||||
virtual void draw(Vec2D<float> *mouse, Camera2D *cam) {};
|
||||
|
||||
};
|
||||
|
||||
+38
-11
@@ -21,6 +21,29 @@ class SignalManager{
|
||||
std::vector<Signal> signals;
|
||||
|
||||
bool loop = true;
|
||||
bool play = false;
|
||||
|
||||
void pause(){
|
||||
play = false;
|
||||
}
|
||||
|
||||
void stop(){
|
||||
play = false;
|
||||
reset();
|
||||
}
|
||||
|
||||
void start(){
|
||||
play = true;
|
||||
}
|
||||
|
||||
void reset(){
|
||||
for(auto &s : signals){
|
||||
s.position = s.start->position + Vec2D<float>{0.1, 0.1};
|
||||
s.next = s.start;
|
||||
s.last = s.start;
|
||||
}
|
||||
cm->reset();
|
||||
}
|
||||
|
||||
void startSignalThread(){
|
||||
std::thread t1(&SignalManager::updateSignals, this);
|
||||
@@ -36,6 +59,7 @@ class SignalManager{
|
||||
signals.emplace(signals.end(), Signal(node->position.x+1, node->position.y+1, 5.0f, app->speed, 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){
|
||||
@@ -45,19 +69,22 @@ class SignalManager{
|
||||
void updateSignals(){
|
||||
auto last = std::chrono::steady_clock::now();
|
||||
while(loop){
|
||||
std::chrono::duration<double, std::milli> diff = std::chrono::steady_clock::now() - last;
|
||||
if(diff.count() > 10){
|
||||
for(auto& s : signals){
|
||||
s.direction = (s.next->position - s.position).norm();
|
||||
if(s.position.dist(&s.next->position) < s.speed * 1.5) {
|
||||
Node* n = cm->next(s.next, s.last);
|
||||
std::chrono::duration<double, std::micro> diff = std::chrono::steady_clock::now() - last;
|
||||
if(diff.count() > 500){
|
||||
if(play){
|
||||
for(auto& s : signals){
|
||||
s.direction = (s.next->position - s.position).norm();
|
||||
s.update(diff.count());
|
||||
if(s.position.dist(&s.next->position) < 1.0f) {
|
||||
Node* n = cm->next(s.next, s.last);
|
||||
|
||||
if(n != s.next && n != nullptr) {
|
||||
for(auto &m : s.next->midi_outs){
|
||||
if(m.second.enabled) midi->note(m.second.i, s.next->midi_channel, s.next->midi_note, s.next->midi_velocity, s.next->midi_gate);
|
||||
if(n != s.next && n != nullptr) {
|
||||
for(auto &m : s.next->midi_outs){
|
||||
if(m.second.enabled) midi->note(m.second.i, s.next->midi_channel, s.next->midi_note, s.next->midi_velocity, s.next->midi_gate);
|
||||
}
|
||||
s.last = s.next;
|
||||
s.next = n;
|
||||
}
|
||||
s.last = s.next;
|
||||
s.next = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-7
@@ -10,7 +10,7 @@ class Signal : public Movable{
|
||||
public:
|
||||
|
||||
Signal();
|
||||
Signal(float x, float y, float r, float s, Color color) : Movable(Vec2D<float>(x, y), Vec2D<float>(0,0), s), radius(r), color(color) {}
|
||||
Signal(float x, float y, float r, float _speed, Color color) : Movable(Vec2D<float>(x, y), Vec2D<float>(0,0), _speed), radius(r), color(color) {}
|
||||
|
||||
int id = 0;
|
||||
|
||||
@@ -25,17 +25,18 @@ class Signal : public Movable{
|
||||
|
||||
float radius;
|
||||
|
||||
float multiplier = 1.0f;
|
||||
|
||||
Color color;
|
||||
|
||||
void update(int fps){
|
||||
|
||||
void update(int t_delta){
|
||||
// 120px / 16th = 1920px / whole note
|
||||
// whole note = 2s at 120bpm
|
||||
// 960px / s
|
||||
position += direction.norm() * speed * multiplier * (float)t_delta / 1000000.0f;
|
||||
}
|
||||
|
||||
void draw(Vec2D<float> *mouse, Camera2D *cam) {
|
||||
|
||||
position += direction.norm() * speed;
|
||||
|
||||
DrawCircle(position.x, position.y, radius, color);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user