polar snapping (almost bug free)
This commit is contained in:
+21
-1
@@ -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:
|
||||||
{
|
{
|
||||||
|
if(!hovering_connectors.empty()){
|
||||||
hovering_connectors.front().hover_connect = true;
|
hovering_connectors.front().hover_connect = true;
|
||||||
if(mouse_clicked) hovering_connectors.front().connecting = 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()){
|
||||||
|
|||||||
@@ -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++;
|
||||||
|
|||||||
+25
-8
@@ -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);
|
||||||
|
n.neighbor = &nodes.at(neighbors.at(1).id);
|
||||||
|
int i = 0;
|
||||||
|
for(auto it = (neighbors.begin()); it != neighbors.end(); it++, i++){
|
||||||
|
float d = it->position.dist(n.position);
|
||||||
|
if(d > 0 && d < min_d*1.5 && d < 960) {
|
||||||
|
std::string frac = 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 = MeasureTextEx(GetFontDefault(), std::format("{:.1f}",d).c_str(), 10.0f, 2.0f);
|
||||||
Vec2D<float> middle_handle = flat_nodes.at(1).position + (n.position - flat_nodes.at(1).position) * ((d - text_length.x)*0.5f)/d;
|
Vector2 text_length_frac = MeasureTextEx(GetFontDefault(), frac.c_str(), 10.0f, 2.0f);
|
||||||
Vec2D<float> text_position = middle_handle + (flat_nodes.at(1).position - n.position).norm().orth() * 12.0f;
|
|
||||||
float rotation = Vector2Angle((Vector2){1,0}, (n.position - flat_nodes.at(1).position)) * RAD2DEG;
|
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);
|
DrawTextPro(GetFontDefault(), std::format("{:.1f}",d).c_str(), text_position, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK);
|
||||||
DrawLineV(n.position, flat_nodes.at(1).position, 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
@@ -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);
|
||||||
|
|||||||
@@ -28,23 +28,22 @@ 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){
|
||||||
@@ -62,7 +61,7 @@ class AppUI{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int s_signal = s_midi + 90 + i*20;
|
int s_signal = s_midi + 100 + i*20;
|
||||||
GuiLabel((Rectangle){20, (float)s_signal, 240, 20}, "SIGNALS");
|
GuiLabel((Rectangle){20, (float)s_signal, 240, 20}, "SIGNALS");
|
||||||
if(GuiButton((Rectangle){20, (float)s_signal + 35, 240, 20}, "ADD RANDOM SIGNAL")){
|
if(GuiButton((Rectangle){20, (float)s_signal + 35, 240, 20}, "ADD RANDOM SIGNAL")){
|
||||||
int ni = nm->getRandomNode();
|
int ni = nm->getRandomNode();
|
||||||
@@ -81,7 +80,7 @@ class AppUI{
|
|||||||
signal_configs++;
|
signal_configs++;
|
||||||
}
|
}
|
||||||
|
|
||||||
int s_nodes = s_signal + 60 + signal_configs*20;
|
int s_nodes = s_signal + 70 + signal_configs*20;
|
||||||
GuiLabel((Rectangle){20, (float)s_nodes + 30, 240, 20}, "NODES");
|
GuiLabel((Rectangle){20, (float)s_nodes + 30, 240, 20}, "NODES");
|
||||||
if(GuiButton((Rectangle){20, (float)s_nodes + 60, 240, 20}, "ADD RANDOM NODE")){
|
if(GuiButton((Rectangle){20, (float)s_nodes + 60, 240, 20}, "ADD RANDOM NODE")){
|
||||||
nm->addNode(200.0f, 200.0f);
|
nm->addNode(200.0f, 200.0f);
|
||||||
@@ -89,7 +88,6 @@ class AppUI{
|
|||||||
DrawText(std::format("{}", nm->nodes.size()).c_str(), 20, settings->height - 70, 15, VS_COLOR_WHITE);
|
DrawText(std::format("{}", nm->nodes.size()).c_str(), 20, settings->height - 70, 15, VS_COLOR_WHITE);
|
||||||
DrawFPS(20, settings->height - 30);
|
DrawFPS(20, settings->height - 30);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void drawPerNodeConfig(Camera2D *camera){
|
void drawPerNodeConfig(Camera2D *camera){
|
||||||
for(auto& n : nm->nodes | std::ranges::views::values | std::views::filter([](Node n){ return n.config; })){
|
for(auto& n : nm->nodes | std::ranges::views::values | std::views::filter([](Node n){ return n.config; })){
|
||||||
|
|||||||
Reference in New Issue
Block a user