Files
viseq/src/main.cpp
T

481 lines
17 KiB
C++
Raw Normal View History

2024-10-09 02:01:23 +02:00
/*
*
2024-11-04 19:28:12 +01:00
* VISEQ - The Visual Sequencer
2024-10-30 02:03:03 +01:00
* 2024 Sebastian Kujas
2024-10-09 02:01:23 +02:00
*
*/
2024-11-03 03:48:49 +01:00
#include "graph-manager.h"
#include "node.h"
2024-11-03 03:48:49 +01:00
#include "vmath.h"
#include <filesystem>
#include <fstream>
2024-10-25 16:41:37 +02:00
#define RAYGUI_IMPLEMENTATION
#include <raylib.h>
#include <raymath.h>
#include "state-manager.h"
#include "datatypes.h"
#include "midi-node.h"
#include "nlohmann/json.hpp"
2024-10-09 02:01:23 +02:00
#include <cstdlib>
#include <stdio.h>
#include <cmath>
#include <ranges>
#include <algorithm>
#include <iterator>
#include <memory>
2024-10-09 02:01:23 +02:00
2024-11-03 03:48:49 +01:00
#include "state-io.h"
using json = nlohmann::json;
2024-11-03 03:48:49 +01:00
VISEQ::APP::AppState app;
2024-10-09 02:01:23 +02:00
Vec2Df mouse_pos;
Vec2Df drag_start;
2024-10-09 02:01:23 +02:00
2024-10-25 16:41:37 +02:00
Camera2D camera = { {0} };
2024-10-09 02:01:23 +02:00
bool mouse_clicked = false;
bool mouse_touched = false;
bool mouse_dragging = false;
2024-10-09 02:01:23 +02:00
int main(){
srand(time(NULL));
2024-11-01 14:08:27 +01:00
InitWindow(1920, 1080, "VISEQ - The Visual Sequencer");
2024-10-26 16:31:23 +02:00
int current_monitor = GetCurrentMonitor();
int monitor_width = GetMonitorWidth(current_monitor);
int monitor_height = GetMonitorHeight(current_monitor);
2024-10-25 16:41:37 +02:00
2024-10-26 16:31:23 +02:00
SetWindowMonitor(current_monitor);
SetWindowState(FLAG_WINDOW_RESIZABLE);
SetWindowSize(monitor_width - 300, monitor_height - 300);
2024-10-26 16:31:23 +02:00
//ToggleFullscreen();
2024-10-26 16:31:23 +02:00
app.ui.init();
2024-10-26 16:31:23 +02:00
app.settings.fps = 60.0f;
2024-10-25 16:41:37 +02:00
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);
2024-10-25 16:41:37 +02:00
app.settings.width = monitor_width - 300;
app.settings.height = monitor_height - 300;
2024-10-26 16:31:23 +02:00
SetTargetFPS(app.settings.fps);
2024-10-25 16:41:37 +02:00
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;
2024-10-25 16:41:37 +02:00
camera.zoom = 1.0f;
2024-10-09 02:01:23 +02:00
2024-11-03 03:48:49 +01:00
if(std::filesystem::exists("state.json")){
std::ifstream f("state.json");
json graph = json::parse(f);
app.settings = graph.at("settings");
// app.gm = graph["graphmanager"].get<VISEQ::GRAPH::GraphManager>();
2024-11-04 09:53:08 +01:00
int highest_id = 0;
std::shared_ptr<Node> hid;
for(auto n : graph.at("graphmanager").at("nodes")){
std::shared_ptr<MidiNode> mn = n;
app.gm.nodes.push_back(mn);
2024-11-04 09:53:08 +01:00
if(mn->id > highest_id) {
highest_id = mn->id;
hid = mn;
}
std::cout << std::format("adding node {}", mn->id) << std::endl;
}
for(auto e : graph.at("graphmanager").at("edges")){
int start_id = e.at("start").get<int>();
int end_id = e.at("end").get<int>();
VEC_SPTR_Node start = app.gm.nodes | RANGE_FILTER([start_id](SPTR_Node n){return n->id == start_id;}) | std::ranges::to<VEC_SPTR_Node>();
VEC_SPTR_Node end = app.gm.nodes | RANGE_FILTER([end_id](SPTR_Node n){return n->id == end_id;}) | std::ranges::to<VEC_SPTR_Node>();
if(!start.empty() && !end.empty()) {
std::cout << std::format("connection {} to {}", start_id, end_id) << std::endl;
app.gm.addConnection(start.front(), end.front());
}
}
2024-11-03 03:48:49 +01:00
for(auto s : graph.at("signalmanager").at("signals")){
int start_id = s.at("start").get<int>();
int next_id = s.at("next").get<int>();
int last_id = s.at("last").get<int>();
std::shared_ptr<VISEQ::SIGNAL::Signal> ms = s;
VEC_SPTR_Node start = app.gm.nodes | RANGE_FILTER([start_id](SPTR_Node n){return n->id == start_id;}) | std::ranges::to<VEC_SPTR_Node>();
VEC_SPTR_Node last = app.gm.nodes | RANGE_FILTER([last_id](SPTR_Node n){return n->id == last_id;}) | std::ranges::to<VEC_SPTR_Node>();
VEC_SPTR_Node next = app.gm.nodes | RANGE_FILTER([next_id](SPTR_Node n){return n->id == next_id;}) | std::ranges::to<VEC_SPTR_Node>();
ms->start = start.front();
ms->next = next.front();
ms->last = last.front();
app.sm.signals.push_back(ms);
std::cout << std::format("adding signal with start node {}", start_id) << std::endl;
}
2024-11-03 03:48:49 +01:00
} else {
for(int i = 0; i < 4; ++i){
app.gm.addMidiNode(Vec2Df{(float)i * app.settings.grid*4 + 7 * app.settings.grid, 2 * app.settings.grid*4});
}
for(int i = 3; i >= 0; --i){
app.gm.addMidiNode(Vec2Df{(float)i * app.settings.grid*4 + 7 * app.settings.grid, 3 * app.settings.grid*4});
}
2024-11-03 03:48:49 +01:00
for(int i = 0; i < 8; ++i){
app.gm.addConnection(app.gm.nodes[i], app.gm.nodes[(i+1) % 8]);
}
app.sm.addSignalAtNode(app.gm.nodes.front());
}
2024-10-25 16:41:37 +02:00
app.sm.startSignalThread();
2024-10-26 16:31:23 +02:00
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;
2024-10-24 21:05:05 +02:00
Vec2Df select_start;
Rectangle selection;
2024-11-01 14:08:27 +01:00
VEC_SPTR_Node copied_nodes;
while(!WindowShouldClose()){
2024-10-09 02:01:23 +02:00
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);
Vec2Df mouse_pos = {(float)mp.x, (float)mp.y};
Vec2Df cam_pos = {camera.offset.x, camera.offset.y};
2024-10-20 23:42:38 +02:00
bool mouse_down = IsMouseButtonDown(MOUSE_BUTTON_LEFT);
bool mouse_clicked = IsMouseButtonPressed(MOUSE_BUTTON_LEFT);
bool mouse_released = IsMouseButtonReleased(MOUSE_BUTTON_LEFT);
2024-10-24 21:05:05 +02:00
bool mouse_right_down = IsMouseButtonDown(MOUSE_BUTTON_RIGHT);
bool mouse_right_clicked = IsMouseButtonPressed(MOUSE_BUTTON_RIGHT);
bool mouse_right_released = IsMouseButtonReleased(MOUSE_BUTTON_RIGHT);
2024-10-10 02:59:27 +02:00
Vec2Df mouse_delta = {GetMouseDelta().x, GetMouseDelta().y};
Vec2Df delta = mouse_delta * (-1.0f/camera.zoom);
2024-11-01 14:08:27 +01:00
auto dragging_nodes = app.gm.nodes | RANGE_FILTER([](SPTR_Node n) { return n->dragging;});
auto connecting_nodes = app.gm.nodes | RANGE_FILTER([](SPTR_Node n) { return n->connecting;});
auto selected_nodes = app.gm.nodes | RANGE_FILTER([](SPTR_Node n) { return n->selected && !n->dragging;}) | std::ranges::to<VEC_SPTR_Node>();
auto hovering_nodes = app.gm.nodes | RANGE_FILTER([&mouse_pos](SPTR_Node n) { return CheckCollisionPointCircle(mouse_pos, n->position, n->radius);});
auto hovering_connectors = app.gm.nodes | RANGE_FILTER([&mouse_pos](SPTR_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 ){
if(hovering_nodes.empty()) n->hovering = false;
if(hovering_connectors.empty()) n->hover_connect = false;
2024-10-29 00:50:39 +01:00
}
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;
2024-10-24 21:05:05 +02:00
if(!hovering_nodes.empty() && !mouse_down && !mouse_right_down) istate = InteractionState::HOVER_NODE;
if(!dragging_nodes.empty()) istate = InteractionState::DRAG_NODE;
2024-10-29 00:50:39 +01:00
if(!hovering_connectors.empty() && !mouse_right_down && istate == InteractionState::HOVER_CANVAS) istate = InteractionState::HOVER_CONNECTOR;
if(!connecting_nodes.empty()) istate = InteractionState::CONNECT_NODE;
2024-10-24 21:05:05 +02:00
if(!selected_nodes.empty() && IsKeyPressed(KEY_DELETE)) istate = InteractionState::DELETE_SELECTED_NODES;
2024-10-26 16:31:23 +02:00
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.gm.setNodeMode(app.settings.mode);
}
switch(istate){
case InteractionState::HOVER_CANVAS:
{
for(auto &n : app.gm.nodes){
n->hover_connect = false;
2024-10-27 00:40:57 +02:00
}
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){
Vec2Df 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};
Vec2Df 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.gm.nodes){
if(CheckCollisionPointRec(n->position, selection)) {
n->in_rect = true;
} else {
n->in_rect = false;
}
}
}
2024-10-24 21:05:05 +02:00
if(mouse_released){
int n_selected = 0;
for(auto& n : app.gm.nodes){
if(CheckCollisionPointRec(n->position, selection)) {
n->selected = true;
n->in_rect = false;
n_selected++;
} else {
if(!IsKeyDown(KEY_LEFT_SHIFT)) n->selected = false;
}
}
if(n_selected > 0) copied_nodes.clear();
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:
{
2024-10-26 16:31:23 +02:00
if(!dragging_nodes.empty()){
2024-11-01 14:08:27 +01:00
SPTR_Node dragNode = dragging_nodes.front();
for(auto &n : selected_nodes){
2024-11-01 14:08:27 +01:00
if(!n->picked_up) n->drag_offset = n->position - dragNode->position;
n->picked_up = true;
2024-10-26 16:31:23 +02:00
}
if(IsKeyDown(KEY_LEFT_CONTROL)) {
2024-11-01 14:08:27 +01:00
dragNode->position.x = round((mouse_pos.x + dragNode->drag_offset.x) / app.settings.grid) * app.settings.grid;
dragNode->position.y = round((mouse_pos.y + dragNode->drag_offset.y) / app.settings.grid) * app.settings.grid;
2024-10-29 00:50:39 +01:00
} else if(IsKeyDown(KEY_LEFT_SHIFT)){
2024-11-01 14:08:27 +01:00
float steps = round((dragNode->neighbor->position.dist(mouse_pos)) / app.settings.grid) * app.settings.grid;
float pol_r = steps;
float pol_theta = Vector2Angle(Vec2Df{1,0},(mouse_pos - dragNode->neighbor->position));
2024-11-01 14:08:27 +01:00
2024-11-03 03:48:49 +01:00
Vec2Df polar = VISEQ::VMATH::polarToCartesian(pol_r, pol_theta);
2024-11-01 14:08:27 +01:00
dragNode->position = dragNode->neighbor->position + polar;
2024-10-26 16:31:23 +02:00
} else {
2024-11-01 14:08:27 +01:00
dragNode->position = mouse_pos + dragNode->drag_offset;
2024-10-26 16:31:23 +02:00
}
for(auto &n : selected_nodes){
2024-11-01 14:08:27 +01:00
n->position = dragNode->position + n->drag_offset;
2024-10-26 16:31:23 +02:00
}
if(mouse_released) {
for(auto &n : dragging_nodes){
n->dragging = false;
n->picked_up = false;
2024-10-26 16:31:23 +02:00
}
for(auto &n : selected_nodes){
n->picked_up = false;
n->dragging = false;
2024-10-26 16:31:23 +02:00
}
}
}
break;
}
case InteractionState::HOVER_CONNECTOR:
{
2024-10-29 00:50:39 +01:00
if(!hovering_connectors.empty()){
hovering_connectors.front()->hover_connect = true;
if(mouse_clicked) hovering_connectors.front()->connecting = true;
2024-10-29 00:50:39 +01:00
}
break;
}
case InteractionState::CONNECT_NODE:
{
Vec2Df connector_position = connecting_nodes.front()->position - ((connecting_nodes.front()->position - mouse_pos)).norm() * (connecting_nodes.front()->radius + 14.0f);
2024-11-04 09:53:08 +01:00
Vec2Df target_position = mouse_pos;
if(IsKeyDown(KEY_LEFT_CONTROL)) {
target_position.x = round((mouse_pos.x) / app.settings.grid) * app.settings.grid;
target_position.y = round((mouse_pos.y) / app.settings.grid) * app.settings.grid;
} else if(IsKeyDown(KEY_LEFT_SHIFT)){
std::shared_ptr<Node> closest = app.gm.getClosestNode(mouse_pos);
float steps = round((closest->position.dist(mouse_pos)) / app.settings.grid) * app.settings.grid;
float pol_r = steps;
float pol_theta = Vector2Angle(Vec2Df{1,0},(mouse_pos - closest->position));
Vec2Df polar = VISEQ::VMATH::polarToCartesian(pol_r, pol_theta);
target_position = closest->position + polar;
} else {
target_position = mouse_pos;
}
if(connecting_nodes.front()->connecting && mouse_down){
2024-11-04 09:53:08 +01:00
DrawLine(connector_position.x, connector_position.y, target_position.x, target_position.y, (Color){229, 181, 103, 255});
connecting_nodes.front()->hovering = true;
connecting_nodes.front()->hover_connect = true;
2024-10-29 00:50:39 +01:00
}
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) {
2024-10-25 16:41:37 +02:00
if(&hovering_nodes.front() != &connecting_nodes.front() && &connecting_nodes.front() != &hovering_connectors.front()){
if(!hovering_nodes.empty()){
app.gm.addConnection(connecting_nodes.front(), hovering_nodes.front());
2024-10-25 16:41:37 +02:00
} else if(!hovering_connectors.empty()){
app.gm.addConnection(connecting_nodes.front(), hovering_connectors.front());
2024-10-25 16:41:37 +02:00
} else {
2024-11-04 09:53:08 +01:00
SPTR_Node new_node = app.gm.copyNode(std::dynamic_pointer_cast<MidiNode>(connecting_nodes.front()), target_position);
new_node->connecting = false;
app.gm.addConnection(connecting_nodes.front(), new_node);
2024-10-25 16:41:37 +02:00
}
}
connecting_nodes.front()->connecting = false;
istate = InteractionState::HOVER_CANVAS;
}
break;
}
case InteractionState::DELETE_SELECTED_NODES:
{
2024-11-01 14:08:27 +01:00
app.removeNodes(selected_nodes | std::ranges::to<VEC_SPTR_Node>());
break;
}
2024-10-26 16:31:23 +02:00
case InteractionState::COPY_SELECTED_NODES:
{
std::ranges::copy(selected_nodes.begin(), selected_nodes.end(), std::back_inserter(copied_nodes));
std::ranges::for_each(selected_nodes.begin(), selected_nodes.end(), [](SPTR_Node n){n->selected = false;});
selected_nodes.clear();
2024-10-26 16:31:23 +02:00
break;
}
case InteractionState::PASTE_SELECTED_NODES:
{
for(auto n: copied_nodes){
n->drag_offset = (n->position - copied_nodes.front()->position);
2024-10-26 16:31:23 +02:00
}
app.copyNodesAndConnections(copied_nodes, mouse_pos);
//copied_nodes.clear();
2024-10-26 16:31:23 +02:00
break;
}
};
2024-10-20 23:42:38 +02:00
float wheel = GetMouseWheelMove();
if(wheel != 0){
if(!hovering_nodes.empty() && hovering_nodes.front()->getNodeType() == NodeType::MIDINODE) {
VEC_SPTR_Node modify_nodes;
std::copy_if(selected_nodes.begin(), selected_nodes.end(), std::back_inserter(modify_nodes), [](SPTR_Node n){ return n->getNodeType() == NodeType::MIDINODE;});
if(selected_nodes.empty()) std::copy_if(hovering_nodes.begin(), hovering_nodes.end(), std::back_inserter(modify_nodes), [modify_nodes](SPTR_Node n){ return n->getNodeType() == NodeType::MIDINODE && !std::ranges::contains(modify_nodes, n);});
for(auto n : modify_nodes){
std::shared_ptr<MidiNode> midiNode = std::dynamic_pointer_cast<MidiNode>(n);
if(IsKeyDown(KEY_LEFT_CONTROL) && !IsKeyDown(KEY_LEFT_ALT)){
switch(app.settings.mode){
case 0:{
midiNode->midi_note += wheel;
break;}
case 1:{
midiNode->midi_cc += wheel;
break;}
case 2:{
midiNode->midi_pitch_bend += wheel;
break;}
};
2024-11-03 03:48:49 +01:00
if(midiNode->midi_note < 0 ) midiNode->midi_note = VISEQ::MIDI::Notes.size()-1;
if(midiNode->midi_note >= VISEQ::MIDI::Notes.size() ) midiNode->midi_note = 0;
if(midiNode->midi_cc < 0 ) midiNode->midi_cc = 127;
if(midiNode->midi_cc > 127 ) midiNode->midi_cc = 0;
if(midiNode->midi_pitch_bend < 0 ) midiNode->midi_pitch_bend = 0;
if(midiNode->midi_pitch_bend > 16383 ) midiNode->midi_pitch_bend = 16383;
} else if(IsKeyDown(KEY_LEFT_SHIFT) && !IsKeyDown(KEY_LEFT_ALT)) {
midiNode->midi_velocity += wheel;
if(midiNode->midi_velocity < 0 ) midiNode->midi_velocity = 127;
if(midiNode->midi_velocity > 127 ) midiNode->midi_velocity = 0;
} else if(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);
midiNode->midi_gate += incr;
if(midiNode->midi_gate < 0 ) midiNode->midi_gate = 0;
break;}
case 1:{
midiNode->midi_cc_value += wheel;
if(midiNode->midi_cc_value < 0 ) midiNode->midi_cc_value = 0;
if(midiNode->midi_cc_value > 127 ) midiNode->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);
midiNode->midi_pitch_bend += incr;
if(midiNode->midi_pitch_bend < 0 ) midiNode->midi_pitch_bend = 0;
if(midiNode->midi_pitch_bend > 16383 ) midiNode->midi_pitch_bend = 16383;
break;}
};
}
}
}
2024-11-04 09:53:08 +01:00
if(!IsKeyDown(KEY_LEFT_SHIFT) && !IsKeyDown(KEY_LEFT_CONTROL) && !IsKeyDown(KEY_LEFT_ALT)){
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);
}
2024-10-20 23:42:38 +02:00
}
2024-10-25 16:41:37 +02:00
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});
}
2024-10-10 02:59:27 +02:00
}
app.draw(mouse_pos, camera);
EndMode2D();
2024-10-25 16:41:37 +02:00
app.drawUI(&mouse_pos, &camera);
EndDrawing();
2024-10-09 02:01:23 +02:00
}
std::ofstream file("state.json");
file << json(app);
2024-11-03 03:48:49 +01:00
std::cout << std::setw(4) << json(app) << std::endl;
CloseWindow();
return 0;
2024-10-09 02:01:23 +02:00
}