/* * * VISEQ - Visual Sequencer * 2024 Sebastian Kujas * */ #define RAYGUI_IMPLEMENTATION #include "datatypes.h" #include #include #include #include #include #include #include "state-manager.h" AppState app; Vec2D mouse_pos; Vec2D drag_start; Camera2D camera = { {0} }; bool mouse_clicked = false; bool mouse_touched = false; bool mouse_dragging = false; int main(){ srand(time(NULL)); InitWindow(1920, 1080, "VISEQ"); int current_monitor = GetCurrentMonitor(); int monitor_width = GetMonitorWidth(current_monitor); int monitor_height = GetMonitorHeight(current_monitor); SetWindowMonitor(current_monitor); SetWindowState(FLAG_WINDOW_RESIZABLE); SetWindowSize(monitor_width - 300, monitor_height - 300); //ToggleFullscreen(); app.ui.init(); app.settings.fps = 60.0f; app.settings.bpm = 120.0f; app.settings.grid = 60.0f; // 120px for a 16th note app.settings.speed = (app.settings.grid * 16.0f) * (app.settings.bpm / 120.f); app.settings.width = monitor_width - 300; app.settings.height = monitor_height - 300; SetTargetFPS(app.settings.fps); camera.target = (Vector2){ app.settings.width/2.0f, app.settings.height/2.0f }; camera.offset = (Vector2){ app.settings.width/2.0f, app.settings.height/2.0f }; camera.rotation = 0.0f; 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); } 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); } for(int i = 0; i < 8; ++i){ app.cm.addConnection(app.nm.nodes[i].id, app.nm.nodes[(i+1) % 8].id); } app.sm.addSignalAtNode((app.nm.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}; InteractionState istate = HOVER_CANVAS; Vec2D select_start; Rectangle selection; while(!WindowShouldClose()){ BeginDrawing(); ClearBackground((Color){46,46,46}); BeginMode2D(camera); app.settings.speed = (app.settings.grid * 16.0f) * (app.settings.bpm / 120.f); Vector2 mp = GetScreenToWorld2D(GetMousePosition(), camera); Vec2D mouse_pos = {(float)mp.x, (float)mp.y}; Vec2D cam_pos = {camera.offset.x, camera.offset.y}; bool mouse_down = IsMouseButtonDown(MOUSE_BUTTON_LEFT); bool mouse_clicked = IsMouseButtonPressed(MOUSE_BUTTON_LEFT); bool mouse_released = IsMouseButtonReleased(MOUSE_BUTTON_LEFT); bool mouse_right_down = IsMouseButtonDown(MOUSE_BUTTON_RIGHT); bool mouse_right_clicked = IsMouseButtonPressed(MOUSE_BUTTON_RIGHT); bool mouse_right_released = IsMouseButtonReleased(MOUSE_BUTTON_RIGHT); Vec2D mouse_delta = {GetMouseDelta().x, GetMouseDelta().y}; Vec2D 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);}); 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_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() && !mouse_right_down && istate == InteractionState::HOVER_CANVAS) istate = InteractionState::HOVER_CONNECTOR; if(!connecting_nodes.empty()) istate = InteractionState::CONNECT_NODE; if(!selected_nodes.empty() && IsKeyPressed(KEY_DELETE)) istate = InteractionState::DELETE_SELECTED_NODES; if(!selected_nodes.empty() && IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_C)) istate = InteractionState::COPY_SELECTED_NODES; if(!copied_nodes.empty() && IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_V)) istate = InteractionState::PASTE_SELECTED_NODES; if(IsKeyPressed(KEY_TAB)) { app.settings.mode = (app.settings.mode + 1) % 3; app.nm.setNodeMode(app.settings.mode); } switch(istate){ case InteractionState::HOVER_CANVAS: { for(auto &n : app.nm.nodes | std::ranges::views::values){ n.hover_connect = false; } 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 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 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 : app.nm.nodes | std::views::values){ if(CheckCollisionPointRec(n.position, selection)) { n.in_rect = true; } else { n.in_rect = false; } } } if(mouse_released){ for(auto& n : app.nm.nodes | std::views::values){ 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(!dragging_nodes.empty()){ for(auto &n : selected_nodes){ if(!n.picked_up) n.drag_offset = n.position - dragging_nodes.front().position; n.picked_up = true; } if(IsKeyDown(KEY_LEFT_CONTROL)) { dragging_nodes.front().position = (Vec2D){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) / app.settings.grid) * app.settings.grid; Vec2D 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 { dragging_nodes.front().position = mouse_pos + dragging_nodes.front().drag_offset; } for(auto &n : selected_nodes){ n.position = dragging_nodes.front().position + n.drag_offset; } if(mouse_released) { for(auto &n : dragging_nodes){ n.dragging = false; n.picked_up = false; } for(auto &n : selected_nodes){ n.picked_up = false; n.dragging = false; } } } break; } case InteractionState::HOVER_CONNECTOR: { if(!hovering_connectors.empty()){ hovering_connectors.front().hover_connect = true; if(mouse_clicked) hovering_connectors.front().connecting = true; } break; } case InteractionState::CONNECT_NODE: { Vec2D 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}); 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(&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); } else if(!hovering_connectors.empty()){ app.cm.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); } } connecting_nodes.front().connecting = false; istate = InteractionState::HOVER_CANVAS; } break; } case InteractionState::DELETE_SELECTED_NODES: { for(auto &n : selected_nodes){ n.erase = true; } app.removeNodes(); break; } case InteractionState::COPY_SELECTED_NODES: { for(auto &n: copied_nodes){ n.copied = false; } for(auto &n: selected_nodes){ n.copied = true; n.selected = false; } break; } case InteractionState::PASTE_SELECTED_NODES: { for(auto &n: copied_nodes){ n.drag_offset = (n.position - copied_nodes.front().position); } // app.nm.copyNodes(mouse_pos); app.copyNodesAndConnections(mouse_pos); break; } }; float wheel = GetMouseWheelMove(); if(wheel != 0){ if(!hovering_nodes.empty() && IsKeyDown(KEY_LEFT_CONTROL) && !IsKeyDown(KEY_LEFT_ALT)){ switch(app.settings.mode){ case 0:{ hovering_nodes.front().midi_note += wheel; break;} case 1:{ hovering_nodes.front().midi_cc += wheel; break;} case 2:{ hovering_nodes.front().midi_pitch_bend += wheel; break;} }; if(hovering_nodes.front().midi_note < 0 ) hovering_nodes.front().midi_note = app.sm.midi.midiNotes.size()-1; if(hovering_nodes.front().midi_note >= app.sm.midi.midiNotes.size() ) hovering_nodes.front().midi_note = 0; if(hovering_nodes.front().midi_cc < 0 ) hovering_nodes.front().midi_cc = 127; if(hovering_nodes.front().midi_cc > 127 ) hovering_nodes.front().midi_cc = 0; if(hovering_nodes.front().midi_pitch_bend < 0 ) hovering_nodes.front().midi_pitch_bend = 0; if(hovering_nodes.front().midi_pitch_bend > 16383 ) hovering_nodes.front().midi_pitch_bend = 16383; } else if(!hovering_nodes.empty() && IsKeyDown(KEY_LEFT_SHIFT) && !IsKeyDown(KEY_LEFT_ALT)) { hovering_nodes.front().midi_velocity += wheel; if(hovering_nodes.front().midi_velocity < 0 ) hovering_nodes.front().midi_velocity = 127; if(hovering_nodes.front().midi_velocity > 127 ) hovering_nodes.front().midi_velocity = 0; } else if(!hovering_nodes.empty() && IsKeyDown(KEY_LEFT_ALT)) { switch(app.settings.mode){ case 0:{ float incr = (float)wheel * (IsKeyDown(KEY_LEFT_SHIFT) ? 100.0f : IsKeyDown(KEY_LEFT_CONTROL) ? 1000.0f : 10.0f); hovering_nodes.front().midi_gate += incr; if(hovering_nodes.front().midi_gate < 0 ) hovering_nodes.front().midi_gate = 0; break;} case 1:{ hovering_nodes.front().midi_cc_value += wheel; if(hovering_nodes.front().midi_cc_value < 0 ) hovering_nodes.front().midi_cc_value = 0; if(hovering_nodes.front().midi_cc_value > 127 ) hovering_nodes.front().midi_cc_value = 127; break;} case 2:{ float incr = (float)wheel * (IsKeyDown(KEY_LEFT_SHIFT) ? 100.0f : IsKeyDown(KEY_LEFT_CONTROL) ? 1000.0f : 10.0f); hovering_nodes.front().midi_pitch_bend += incr; if(hovering_nodes.front().midi_pitch_bend < 0 ) hovering_nodes.front().midi_pitch_bend = 0; if(hovering_nodes.front().midi_pitch_bend > 16383 ) hovering_nodes.front().midi_pitch_bend = 16383; break;} }; } else { Vector2 mouseWorldPos = GetScreenToWorld2D(GetMousePosition(), camera); camera.offset = GetMousePosition(); camera.target = mouseWorldPos; float scaleFactor = 1.0f + (0.25f*fabsf(wheel)); if (wheel < 0) scaleFactor = 1.0f/scaleFactor; camera.zoom = Clamp(camera.zoom*scaleFactor, 0.125f, 64.0f); } } for(int x = -app.settings.grid*100; x < app.settings.grid*100; x+=app.settings.grid){ for(int y = -app.settings.grid*100; y < app.settings.grid*100; y+=app.settings.grid){ DrawPixel(x, y, (Color){130,130,130,200}); } } app.draw(&mouse_pos, &camera); EndMode2D(); app.drawUI(&mouse_pos, &camera); EndDrawing(); } CloseWindow(); return 0; }