polar snapping (almost bug free)

This commit is contained in:
Sebastian
2024-10-29 00:50:39 +01:00
parent fc48c2e7ed
commit c63eeed1b4
5 changed files with 106 additions and 67 deletions
+23 -3
View File
@@ -16,6 +16,7 @@
/*Pink: (176, 82, 121); #b05279*/ /*Pink: (176, 82, 121); #b05279*/
/*Blue: (108, 153, 187); #6c99bb*/ /*Blue: (108, 153, 187); #6c99bb*/
#include <algorithm>
#define RAYGUI_IMPLEMENTATION #define RAYGUI_IMPLEMENTATION
#include "datatypes.h" #include "datatypes.h"
@@ -127,12 +128,17 @@ int main(){
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_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);}); 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);});
for(auto &n : app.nm.nodes | std::ranges::views::values){
if(hovering_nodes.empty()) n.hovering = false;
if(hovering_connectors.empty()) n.hover_connect = false;
}
if(!mouse_down && !mouse_right_down && dragging_nodes.empty() && connecting_nodes.empty() && hovering_nodes.empty() && hovering_connectors.empty()) istate = InteractionState::HOVER_CANVAS; 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((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(!hovering_nodes.empty() && !mouse_down && !mouse_right_down) istate = InteractionState::HOVER_NODE;
if(!dragging_nodes.empty()) istate = InteractionState::DRAG_NODE; if(!dragging_nodes.empty()) istate = InteractionState::DRAG_NODE;
if(!hovering_connectors.empty() && istate == InteractionState::HOVER_CANVAS) istate = InteractionState::HOVER_CONNECTOR; if(!hovering_connectors.empty() && !mouse_right_down && istate == InteractionState::HOVER_CANVAS) istate = InteractionState::HOVER_CONNECTOR;
if(!connecting_nodes.empty()) istate = InteractionState::CONNECT_NODE; if(!connecting_nodes.empty()) istate = InteractionState::CONNECT_NODE;
if(!selected_nodes.empty() && IsKeyPressed(KEY_DELETE)) istate = InteractionState::DELETE_SELECTED_NODES; if(!selected_nodes.empty() && IsKeyPressed(KEY_DELETE)) istate = InteractionState::DELETE_SELECTED_NODES;
@@ -207,6 +213,10 @@ int main(){
if(IsKeyDown(KEY_LEFT_CONTROL)) { if(IsKeyDown(KEY_LEFT_CONTROL)) {
dragging_nodes.front().position = (Vec2D<float>){round((mouse_pos.x+dragging_nodes.front().drag_offset.x)/app.settings.grid)*app.settings.grid, round((mouse_pos.y+dragging_nodes.front().drag_offset.y)/app.settings.grid)*app.settings.grid}; dragging_nodes.front().position = (Vec2D<float>){round((mouse_pos.x+dragging_nodes.front().drag_offset.x)/app.settings.grid)*app.settings.grid, round((mouse_pos.y+dragging_nodes.front().drag_offset.y)/app.settings.grid)*app.settings.grid};
} else if(IsKeyDown(KEY_LEFT_SHIFT)){
float steps = round(dragging_nodes.front().neighbor->position.dist(mouse_pos) / 60.0f) * 60.0f;
Vec2D<float> polar = dragging_nodes.front().neighbor->position - (dragging_nodes.front().neighbor->position - (mouse_pos + dragging_nodes.front().drag_offset)).norm() * steps;
dragging_nodes.front().position = polar;
} else { } else {
dragging_nodes.front().position = mouse_pos + dragging_nodes.front().drag_offset; dragging_nodes.front().position = mouse_pos + dragging_nodes.front().drag_offset;
} }
@@ -230,8 +240,10 @@ int main(){
} }
case InteractionState::HOVER_CONNECTOR: case InteractionState::HOVER_CONNECTOR:
{ {
hovering_connectors.front().hover_connect = true; if(!hovering_connectors.empty()){
if(mouse_clicked) hovering_connectors.front().connecting = true; hovering_connectors.front().hover_connect = true;
if(mouse_clicked) hovering_connectors.front().connecting = true;
}
break; break;
} }
case InteractionState::CONNECT_NODE: case InteractionState::CONNECT_NODE:
@@ -240,8 +252,16 @@ int main(){
if(connecting_nodes.front().connecting && mouse_down){ if(connecting_nodes.front().connecting && mouse_down){
DrawLine(connector_position.x, connector_position.y, mouse_pos.x, mouse_pos.y, (Color){229, 181, 103, 255}); DrawLine(connector_position.x, connector_position.y, mouse_pos.x, mouse_pos.y, (Color){229, 181, 103, 255});
connecting_nodes.front().hovering = true;
connecting_nodes.front().hover_connect = true;
} }
if(!hovering_connectors.empty()) {
hovering_connectors.front().hovering = true;
hovering_connectors.front().hover_connect = true;
}
if(!hovering_nodes.empty()) hovering_nodes.front().hovering = true;
if(mouse_released) { if(mouse_released) {
if(&hovering_nodes.front() != &connecting_nodes.front() && &connecting_nodes.front() != &hovering_connectors.front()){ if(&hovering_nodes.front() != &connecting_nodes.front() && &connecting_nodes.front() != &hovering_connectors.front()){
if(!hovering_nodes.empty()){ if(!hovering_nodes.empty()){
+1
View File
@@ -60,6 +60,7 @@ class Midi {
void enumerate(){ void enumerate(){
std::cout << (int)obs.get_current_api() << std::endl; std::cout << (int)obs.get_current_api() << std::endl;
int i = 0; int i = 0;
midi_out_ports.clear();
for(const libremidi::output_port& port : obs.get_output_ports()){ for(const libremidi::output_port& port : obs.get_output_ports()){
midi_out_ports.push_back({port, i, false}); midi_out_ports.push_back({port, i, false});
i++; i++;
+27 -10
View File
@@ -20,6 +20,8 @@ class NodeManager{
std::vector<int> keys; std::vector<int> keys;
int iterator = 0; 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"};
int getRandomNode(){ int getRandomNode(){
std::vector<int> o; std::vector<int> o;
std::ranges::sample(keys, std::back_inserter(o), 1, std::mt19937{std::random_device{}()}); std::ranges::sample(keys, std::back_inserter(o), 1, std::mt19937{std::random_device{}()});
@@ -60,18 +62,33 @@ class NodeManager{
} }
void drawNodes(Vec2D<float> *mouse_pos, Camera2D *camera){ void drawNodes(Vec2D<float> *mouse_pos, Camera2D *camera){
auto flat_nodes = nodes | std::ranges::views::values | std::ranges::to<std::vector<Node>>(); 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;
for(auto& n : nodes | std::ranges::views::values ){ for(auto& n : nodes | std::ranges::views::values ){
if(n.dragging){ if(n.dragging){
std::sort(flat_nodes.begin(), flat_nodes.end(), [n](Node na, Node nb){ return na.position.dist(n.position) < nb.position.dist(n.position);}); std::sort(neighbors.begin(), neighbors.end(), [n](Node na, Node nb){ return na.position.dist(n.position) < nb.position.dist(n.position);});
float d = flat_nodes.at(1).position.dist(n.position); if(neighbors.size() > 1){
if(d > 0 && d < 240) { min_d = neighbors.front().position.dist(n.position);
Vector2 text_length = MeasureTextEx(GetFontDefault(), std::format("{:.1f}",d).c_str(), 10.0f, 2.0f); n.neighbor = &nodes.at(neighbors.at(1).id);
Vec2D<float> middle_handle = flat_nodes.at(1).position + (n.position - flat_nodes.at(1).position) * ((d - text_length.x)*0.5f)/d; int i = 0;
Vec2D<float> text_position = middle_handle + (flat_nodes.at(1).position - n.position).norm().orth() * 12.0f; for(auto it = (neighbors.begin()); it != neighbors.end(); it++, i++){
float rotation = Vector2Angle((Vector2){1,0}, (n.position - flat_nodes.at(1).position)) * RAD2DEG; float d = it->position.dist(n.position);
DrawTextPro(GetFontDefault(), std::format("{:.1f}",d).c_str(), text_position, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK); if(d > 0 && d < min_d*1.5 && d < 960) {
DrawLineV(n.position, flat_nodes.at(1).position, VS_COLOR_PINK); std::string frac = fractions.at((int)Clamp(round(d/60) - 1, 0, 31));
Vector2 text_length = MeasureTextEx(GetFontDefault(), std::format("{:.1f}",d).c_str(), 10.0f, 2.0f);
Vector2 text_length_frac = MeasureTextEx(GetFontDefault(), frac.c_str(), 10.0f, 2.0f);
Vec2D<float> middle_handle = it->position + (n.position - it->position) * ((d - text_length.x)*0.5f)/d;
Vec2D<float> middle_handle_frac = it->position + (n.position - it->position) * ((d - text_length_frac.x)*0.5f)/d;
Vec2D<float> text_position = middle_handle + (it->position - n.position).norm().orth() * 12.0f;
Vec2D<float> text_position_frac = middle_handle_frac - (it->position - n.position).norm().orth() * 4.0f;
float rotation = Vector2Angle((Vector2){1,0}, (n.position - it->position)) * RAD2DEG;
DrawTextPro(GetFontDefault(), std::format("{:.1f}",d).c_str(), text_position, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK);
if(i == 0 && IsKeyDown(KEY_LEFT_SHIFT)) DrawTextPro(GetFontDefault(), frac.c_str(), text_position_frac, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK);
DrawLineV(n.position, it->position, VS_COLOR_PINK);
}
}
} }
} }
n.draw(mouse_pos, camera); n.draw(mouse_pos, camera);
+4 -1
View File
@@ -22,6 +22,9 @@ class Node : public Movable{
Node(int id, float x, float y, float r) : Movable(Vec2D<float>(x, y), 0.0f), radius(r), id(id) {} Node(int id, float x, float y, float r) : Movable(Vec2D<float>(x, y), 0.0f), radius(r), id(id) {}
Node(int id, Vec2D<float> p, float r) : Movable(p, 0.0f), radius(r), id(id) {} Node(int id, Vec2D<float> p, float r) : Movable(p, 0.0f), radius(r), id(id) {}
Node *neighbor = nullptr;
float radius; float radius;
int id; int id;
@@ -86,6 +89,7 @@ class Node : public Movable{
DrawRing(position, radius, radius+4, 0, 360, 24, (Color){60, 60, 60, 255}); 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}); 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}); DrawCircleLinesV(position, radius + 4, (Color){100, 100, 100, 255});
if(mode == Distribution::RANDOM) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_PINK);
float midi_gate_map = round((255.f / midi_gate) * fade); float midi_gate_map = round((255.f / midi_gate) * fade);
Color gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){180, 210, 115, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, WHITE); Color gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){180, 210, 115, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, WHITE);
@@ -102,7 +106,6 @@ class Node : public Movable{
int offset = MeasureText(MIDI::Notes[midi_note].c_str(), 12); int offset = MeasureText(MIDI::Notes[midi_note].c_str(), 12);
DrawText(MIDI::Notes[midi_note].c_str(), position.x-offset/2.0f, position.y-5, 12, WHITE ); DrawText(MIDI::Notes[midi_note].c_str(), position.x-offset/2.0f, position.y-5, 12, WHITE );
} }
if(mode == Distribution::RANDOM) DrawCircle(position.x, position.y, radius/2.0f, (Color){229, 181, 103, 255});
Vec2D<float> connector = position - ((position - *mouse)).norm() * (radius + R); Vec2D<float> connector = position - ((position - *mouse)).norm() * (radius + R);
float opacity = 150.0f - Clamp(position.dist(mouse), 0.0f, 150.0f); float opacity = 150.0f - Clamp(position.dist(mouse), 0.0f, 150.0f);
+51 -53
View File
@@ -28,67 +28,65 @@ class AppUI{
void drawSideBar(){ void drawSideBar(){
GuiPanel((Rectangle){-1,-30,280,(float)settings->height + 31}, ""); GuiPanel((Rectangle){-1,-30,280,(float)settings->height + 31}, "");
{ const int s_controls = 25;
const int s_controls = 20; //GuiSetStyle(BUTTON, STATE_FOCUSED, 0xFF0000FF);
//GuiSetStyle(BUTTON, STATE_FOCUSED, 0xFF0000FF); if(GuiButton((Rectangle){20, (float)s_controls, 80, 30}, "PLAY")) sm->play ? sm->pause() : sm->start();
if(GuiButton((Rectangle){20, (float)s_controls, 80, 30}, "PLAY")) sm->play ? sm->pause() : sm->start(); if(GuiButton((Rectangle){120, (float)s_controls, 60, 30}, "STOP")) sm->stop();
if(GuiButton((Rectangle){120, (float)s_controls, 60, 30}, "STOP")) sm->stop(); if(GuiButton((Rectangle){200, (float)s_controls, 60, 30}, "RESET")) sm->reset();
if(GuiButton((Rectangle){200, (float)s_controls, 60, 30}, "RESET")) sm->reset();
const int s_bpm = s_controls + 35; const int s_bpm = s_controls + 45;
GuiLabel((Rectangle){20, (float)s_bpm, 120, 20}, "TIMING"); GuiLabel((Rectangle){20, (float)s_bpm, 120, 20}, "TIMING");
GuiLabel((Rectangle){200, (float)s_bpm, 40, 20}, std::format("{}",settings->bpm).c_str()); GuiLabel((Rectangle){200, (float)s_bpm, 40, 20}, std::format("{}",settings->bpm).c_str());
float _bpm = (float)settings->bpm; float _bpm = (float)settings->bpm;
GuiSlider((Rectangle){20, (float)s_bpm + 25, 240, 20}, "", "", &_bpm, 1, 300); GuiSlider((Rectangle){20, (float)s_bpm + 25, 240, 20}, "", "", &_bpm, 1, 300);
settings->bpm = _bpm; settings->bpm = _bpm;
int s_midi = s_bpm + 55; int s_midi = s_bpm + 75;
GuiLabel((Rectangle){20, (float)s_midi, 240, 20}, "MIDI OUT"); GuiLabel((Rectangle){20, (float)s_midi, 240, 20}, "MIDI OUT");
if(GuiButton((Rectangle){20, (float)s_midi, 60, 30}, "REload")) sm->midi.enumerate(); if(GuiButton((Rectangle){200, (float)s_midi, 60, 20}, "RELOAD")) sm->midi.enumerate();
int i = 0; int i = 0;
for(auto& mo : sm->midi.midi_out_ports){ for(auto& mo : sm->midi.midi_out_ports){
GuiCheckBox((Rectangle){20, (float)s_midi + 25 + i*20.0f, 20, 20}, mo.port.port_name.substr(0,32).c_str(), &mo.enabled); GuiCheckBox((Rectangle){20, (float)s_midi + 25 + i*20.0f, 20, 20}, mo.port.port_name.substr(0,32).c_str(), &mo.enabled);
i++; i++;
} }
if(GuiButton((Rectangle){20, (float)s_midi + 45 + i*20.0f, 240, 20}, "ENABLE FOR ALL")) { if(GuiButton((Rectangle){20, (float)s_midi + 45 + i*20.0f, 240, 20}, "ENABLE FOR ALL")) {
for(auto &midis : sm->midi.midi_out_ports){ for(auto &midis : sm->midi.midi_out_ports){
if(midis.enabled){ if(midis.enabled){
for(auto &n : nm->nodes){ for(auto &n : nm->nodes){
n.second.setMidiPort(midis.number, true); n.second.setMidiPort(midis.number, 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 = nm->getRandomNode();
sm->addSignalAtNode(ni);
}
int signal_configs = 0;
for(auto &s : sm->signals){
if(GuiButton((Rectangle){20,(float)s_signal+70 + signal_configs*20, 20, 20},"X")) sm->removeSignal(signal_configs);
DrawCircle(55, s_signal+80 + signal_configs*20, 6.0F, s.color);
float s_multiplier = round(s.multiplier*10.0f)/10.0f;
float signal_bpm = settings->bpm * s.multiplier;
GuiSlider((Rectangle){70, (float)s_signal+70 + signal_configs*20, 160, 20}, "", std::format("{:.1f}",signal_bpm).c_str(), &s_multiplier, 0.1, 10);
s.multiplier = s_multiplier;
s.speed = settings->speed;
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")){
nm->addNode(200.0f, 200.0f);
}
DrawText(std::format("{}", nm->nodes.size()).c_str(), 20, settings->height - 70, 15, VS_COLOR_WHITE);
DrawFPS(20, settings->height - 30);
} }
int s_signal = s_midi + 100 + i*20;
GuiLabel((Rectangle){20, (float)s_signal, 240, 20}, "SIGNALS");
if(GuiButton((Rectangle){20, (float)s_signal + 35, 240, 20}, "ADD RANDOM SIGNAL")){
int ni = nm->getRandomNode();
sm->addSignalAtNode(ni);
}
int signal_configs = 0;
for(auto &s : sm->signals){
if(GuiButton((Rectangle){20,(float)s_signal+70 + signal_configs*20, 20, 20},"X")) sm->removeSignal(signal_configs);
DrawCircle(55, s_signal+80 + signal_configs*20, 6.0F, s.color);
float s_multiplier = round(s.multiplier*10.0f)/10.0f;
float signal_bpm = settings->bpm * s.multiplier;
GuiSlider((Rectangle){70, (float)s_signal+70 + signal_configs*20, 160, 20}, "", std::format("{:.1f}",signal_bpm).c_str(), &s_multiplier, 0.1, 10);
s.multiplier = s_multiplier;
s.speed = settings->speed;
signal_configs++;
}
int s_nodes = s_signal + 70 + signal_configs*20;
GuiLabel((Rectangle){20, (float)s_nodes + 30, 240, 20}, "NODES");
if(GuiButton((Rectangle){20, (float)s_nodes + 60, 240, 20}, "ADD RANDOM NODE")){
nm->addNode(200.0f, 200.0f);
}
DrawText(std::format("{}", nm->nodes.size()).c_str(), 20, settings->height - 70, 15, VS_COLOR_WHITE);
DrawFPS(20, settings->height - 30);
} }
void drawPerNodeConfig(Camera2D *camera){ void drawPerNodeConfig(Camera2D *camera){