2024-10-25 16:41:37 +02:00
|
|
|
#pragma once
|
|
|
|
|
|
2024-11-26 01:58:09 +01:00
|
|
|
#include <filesystem>
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include "nlohmann/json.hpp"
|
2024-11-19 01:04:06 +01:00
|
|
|
#include "clipboard.h"
|
2024-10-25 16:41:37 +02:00
|
|
|
#include "datatypes.h"
|
2024-11-26 01:58:09 +01:00
|
|
|
#include "canvas.h"
|
2024-10-30 14:55:24 +01:00
|
|
|
#include "graph-manager.h"
|
2024-11-25 00:53:10 +01:00
|
|
|
#include "interaction-event.h"
|
2024-11-01 02:38:47 +01:00
|
|
|
#include "midi-node.h"
|
2024-11-21 23:58:35 +01:00
|
|
|
#include "mouse.h"
|
2024-11-21 02:03:32 +01:00
|
|
|
#include "node.h"
|
|
|
|
|
#include "raylib.h"
|
2024-10-25 16:41:37 +02:00
|
|
|
#include "signal-manager.h"
|
2024-11-21 02:03:32 +01:00
|
|
|
#include "state.h"
|
2024-10-25 16:41:37 +02:00
|
|
|
#include "ui.h"
|
2024-10-27 00:40:57 +02:00
|
|
|
|
2024-11-01 02:38:47 +01:00
|
|
|
#include <memory>
|
2024-10-26 02:14:41 +02:00
|
|
|
#include <ranges>
|
2024-10-25 16:41:37 +02:00
|
|
|
|
2024-11-26 01:58:09 +01:00
|
|
|
using json = nlohmann::json;
|
|
|
|
|
|
2024-11-03 03:48:49 +01:00
|
|
|
namespace VISEQ::APP {
|
2024-11-26 01:58:09 +01:00
|
|
|
class AppManager;
|
|
|
|
|
void to_json(json& j, const AppManager& A);
|
|
|
|
|
void from_json(const json& j, AppManager& A);
|
2024-10-25 16:41:37 +02:00
|
|
|
|
2024-11-14 00:55:09 +01:00
|
|
|
class AppManager{
|
2024-11-03 03:48:49 +01:00
|
|
|
public:
|
2024-11-26 01:58:09 +01:00
|
|
|
|
2024-11-22 00:24:31 +01:00
|
|
|
AppManager() : sm(&gm, &mutex), mouse(&gm), ui(&sm, &gm) {}
|
2024-10-25 16:41:37 +02:00
|
|
|
|
2024-11-03 03:48:49 +01:00
|
|
|
std::mutex mutex;
|
2024-10-25 16:41:37 +02:00
|
|
|
|
2024-11-26 01:58:09 +01:00
|
|
|
std::shared_ptr<CANVAS::Canvas> canvas = std::make_shared<CANVAS::Canvas>();
|
2024-11-03 03:48:49 +01:00
|
|
|
VISEQ::GRAPH::GraphManager gm;
|
|
|
|
|
VISEQ::SIGNAL::SignalManager sm;
|
2024-11-19 01:04:06 +01:00
|
|
|
VISEQ::APP::Clipboard cb;
|
2024-11-21 23:58:35 +01:00
|
|
|
VISEQ::APP::Mouse mouse;
|
2024-10-25 16:41:37 +02:00
|
|
|
|
2024-11-21 02:03:32 +01:00
|
|
|
std::shared_ptr<APP::State> state = APP::State::get();
|
|
|
|
|
|
2024-11-03 03:48:49 +01:00
|
|
|
UI::AppUI ui;
|
2024-10-25 16:41:37 +02:00
|
|
|
|
2024-11-26 01:58:09 +01:00
|
|
|
void init(){
|
|
|
|
|
canvas->camera.target = (Vector2){ state->settings.width/2.0f, state->settings.height/2.0f };
|
|
|
|
|
canvas->camera.offset = (Vector2){ state->settings.width/2.0f, state->settings.height/2.0f };
|
|
|
|
|
canvas->camera.rotation = 0.0f;
|
|
|
|
|
canvas->camera.zoom = 1.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void close(){
|
|
|
|
|
std::ofstream file("state.json");
|
|
|
|
|
file << json(*this);
|
|
|
|
|
std::cout << std::setw(4) << json(*this) << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void startSignals(){
|
|
|
|
|
sm.startSignalThread();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void loadState(){
|
|
|
|
|
if(std::filesystem::exists("state.json")){
|
|
|
|
|
std::ifstream f("state.json");
|
|
|
|
|
json graph = json::parse(f);
|
|
|
|
|
|
|
|
|
|
int highest_id = 0;
|
|
|
|
|
std::shared_ptr<Node> hid;
|
|
|
|
|
for(auto n : graph.at("graphmanager").at("nodes")){
|
|
|
|
|
std::shared_ptr<MidiNode> mn = n;
|
|
|
|
|
if(mn->id > highest_id) {
|
|
|
|
|
highest_id = mn->id;
|
|
|
|
|
hid = mn;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(auto e : graph.at("graphmanager").at("edges")){
|
|
|
|
|
if(e.at("id") > highest_id){
|
|
|
|
|
highest_id = e.at("id");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(auto n : graph.at("graphmanager").at("nodes")){
|
|
|
|
|
std::shared_ptr<MidiNode> mn = n;
|
|
|
|
|
gm.addNode(mn);
|
|
|
|
|
//app.gm.nodes.push_back(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 = gm.nodes | RANGE_FILTER([start_id](SPTR_Node n){return n->id == start_id;}) | std::ranges::to<VEC_SPTR_Node>();
|
|
|
|
|
VEC_SPTR_Node end = 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;
|
|
|
|
|
gm.addConnection(start.front(), end.front());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gm.edges.back()->newId(highest_id); // set static id counter in base class to highest value from json file
|
|
|
|
|
|
|
|
|
|
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 = gm.nodes | RANGE_FILTER([start_id](SPTR_Node n){return n->id == start_id;}) | std::ranges::to<VEC_SPTR_Node>();
|
|
|
|
|
VEC_SPTR_Node last = gm.nodes | RANGE_FILTER([last_id](SPTR_Node n){return n->id == last_id;}) | std::ranges::to<VEC_SPTR_Node>();
|
|
|
|
|
VEC_SPTR_Node next = 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();
|
|
|
|
|
sm.signals.push_back(ms);
|
|
|
|
|
|
|
|
|
|
std::cout << std::format("adding signal with start node {}", start_id) << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
for(int i = 0; i < 4; ++i){
|
|
|
|
|
gm.addMidiNode(Vec2Df{(float)i * state->settings.grid*4 + 7 * state->settings.grid, 2 * state->settings.grid*4});
|
|
|
|
|
}
|
|
|
|
|
for(int i = 3; i >= 0; --i){
|
|
|
|
|
gm.addMidiNode(Vec2Df{(float)i * state->settings.grid*4 + 7 * state->settings.grid, 3 * state->settings.grid*4});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < 8; ++i){
|
|
|
|
|
gm.addConnection(gm.nodes[i], gm.nodes[(i+1) % 8]);
|
|
|
|
|
}
|
|
|
|
|
sm.addSignalAtNode(gm.nodes.front());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-03 03:48:49 +01:00
|
|
|
void updateMidiPorts(){
|
|
|
|
|
for(auto&n : gm.nodes | RANGE_FILTER([](VISEQ::GRAPH::NODES::SPTR_Node n){return n->getNodeType() == NodeType::MIDINODE;})){
|
|
|
|
|
auto nc = std::dynamic_pointer_cast<MidiNode>(n);
|
|
|
|
|
for(auto& mo : sm.midi.midi_out_ports){
|
|
|
|
|
if(!nc->midi_outs.contains(mo.number) && mo.enabled){
|
|
|
|
|
nc->midi_outs[mo.number] = {mo.number, mo.port.port_name, false};
|
|
|
|
|
}
|
|
|
|
|
if(nc->midi_outs.contains(mo.number) && !mo.enabled){
|
|
|
|
|
nc->midi_outs.erase(mo.number);
|
|
|
|
|
}
|
2024-10-25 16:41:37 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-03 03:48:49 +01:00
|
|
|
void removeNodes(VEC_SPTR_Node nodes){
|
2024-10-26 02:14:41 +02:00
|
|
|
|
2024-11-21 02:03:32 +01:00
|
|
|
VEC_SPTR_Node adjacent_nodes = gm.getAdjacentNodes(nodes);
|
2024-10-26 02:14:41 +02:00
|
|
|
|
2024-11-21 02:03:32 +01:00
|
|
|
sm.rerouteSignal(nodes, adjacent_nodes);
|
|
|
|
|
sm.rebaseSignal(nodes);
|
2024-10-26 02:14:41 +02:00
|
|
|
|
2024-11-21 02:03:32 +01:00
|
|
|
gm.removeNodesAndEdges(nodes);
|
2024-10-26 02:14:41 +02:00
|
|
|
}
|
2024-11-17 02:40:31 +01:00
|
|
|
|
2024-11-26 01:58:09 +01:00
|
|
|
void doInteraction(){
|
|
|
|
|
bool exit = false;
|
|
|
|
|
while(event_queue.size() > 0 && !exit){
|
2024-11-24 03:08:06 +01:00
|
|
|
auto e = event_queue.front();
|
2024-11-26 01:58:09 +01:00
|
|
|
std::cout << e.item->getType() << " " << e.action << std::endl;
|
|
|
|
|
|
|
|
|
|
if(e.item->getType() == GraphItemType::CANVAS) {
|
|
|
|
|
if(e.action == Interaction::SELECT) {
|
|
|
|
|
gm.select();
|
|
|
|
|
std::cout << gm.selected_nodes.size() << std::endl;
|
|
|
|
|
canvas->clearSelectionRect();
|
|
|
|
|
}
|
|
|
|
|
if(e.action == Interaction::DESELECT) gm.deselect();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(e.item->getType() == GraphItemType::NODE){
|
|
|
|
|
if(e.action == Interaction::PICKUP) {
|
|
|
|
|
e.item->pickUp();
|
|
|
|
|
if(std::dynamic_pointer_cast<Node>(e.item)->selected){
|
|
|
|
|
for(auto s : gm.selected_nodes){
|
|
|
|
|
s->pickUp(std::dynamic_pointer_cast<Node>(e.item)->position);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
exit = true;
|
|
|
|
|
}
|
|
|
|
|
if(e.action == Interaction::DRAG) {
|
|
|
|
|
e.item->drag();
|
|
|
|
|
if(std::dynamic_pointer_cast<Node>(e.item)->selected){
|
|
|
|
|
for(auto s : gm.selected_nodes){
|
|
|
|
|
s->drag();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
exit = true;
|
|
|
|
|
}
|
|
|
|
|
if(e.action == Interaction::PUTDOWN) {
|
|
|
|
|
e.item->putDown();
|
|
|
|
|
if(std::dynamic_pointer_cast<Node>(e.item)->selected){
|
|
|
|
|
for(auto s : gm.selected_nodes){
|
|
|
|
|
s->putDown();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(exit) break;
|
|
|
|
|
|
|
|
|
|
if(e.item->getType() == GraphItemType::CONNECTOR || e.item->getType() == GraphItemType::CANVAS) {
|
2024-11-24 03:08:06 +01:00
|
|
|
switch(e.action){
|
|
|
|
|
case VISEQ::APP::Interaction::PICKUP:
|
|
|
|
|
{
|
|
|
|
|
e.item->pickUp();
|
2024-11-26 01:58:09 +01:00
|
|
|
exit = true;
|
2024-11-24 03:08:06 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case VISEQ::APP::Interaction::DRAG:
|
|
|
|
|
{
|
|
|
|
|
e.item->drag();
|
2024-11-26 01:58:09 +01:00
|
|
|
exit = true;
|
2024-11-24 03:08:06 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case VISEQ::APP::Interaction::PUTDOWN:
|
|
|
|
|
{
|
|
|
|
|
e.item->putDown();
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-11-25 00:53:10 +01:00
|
|
|
default:
|
2024-11-26 01:58:09 +01:00
|
|
|
{}
|
2024-11-24 03:08:06 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-11-26 01:58:09 +01:00
|
|
|
|
2024-11-25 00:53:10 +01:00
|
|
|
event_queue.pop_front();
|
2024-11-24 03:08:06 +01:00
|
|
|
}
|
2024-11-25 00:53:10 +01:00
|
|
|
event_queue.clear();
|
2024-11-24 03:08:06 +01:00
|
|
|
}
|
|
|
|
|
|
2024-11-17 02:40:31 +01:00
|
|
|
void update(){
|
2024-11-26 01:58:09 +01:00
|
|
|
state->update(canvas->camera);
|
|
|
|
|
canvas->update();
|
|
|
|
|
|
2024-11-03 03:48:49 +01:00
|
|
|
mutex.lock();
|
|
|
|
|
updateMidiPorts();
|
|
|
|
|
mutex.unlock();
|
2024-11-26 01:58:09 +01:00
|
|
|
|
2024-11-24 03:08:06 +01:00
|
|
|
doInteraction();
|
2024-11-22 00:24:31 +01:00
|
|
|
mouse.update();
|
2024-11-21 02:03:32 +01:00
|
|
|
|
|
|
|
|
if(KEYS_DOWN.ctrl && IsKeyPressed(KEY_C)){
|
|
|
|
|
VEC_SPTR_Node selected_nodes = gm.getSelection();
|
|
|
|
|
if(!selected_nodes.empty()) cb.pushNodes(selected_nodes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(KEYS_DOWN.ctrl && IsKeyPressed(KEY_V)){
|
|
|
|
|
VEC_SPTR_Node paste_nodes = cb.pullNodes();
|
|
|
|
|
if(!paste_nodes.empty()) gm.pasteNodesAndEdges(paste_nodes, MOD_MOUSE);
|
|
|
|
|
}
|
2024-11-17 02:40:31 +01:00
|
|
|
}
|
|
|
|
|
|
2024-11-26 01:58:09 +01:00
|
|
|
void draw(){
|
|
|
|
|
BeginDrawing();
|
|
|
|
|
ClearBackground((Color){46,46,46});
|
|
|
|
|
canvas->drawStart();
|
|
|
|
|
canvas->draw();
|
|
|
|
|
|
|
|
|
|
gm.drawNodes(SCREEN_MOUSE, canvas->camera);
|
|
|
|
|
gm.drawEdges(SCREEN_MOUSE, canvas->camera);
|
|
|
|
|
sm.drawSignals(SCREEN_MOUSE, canvas->camera);
|
|
|
|
|
|
|
|
|
|
canvas->drawEnd();
|
|
|
|
|
drawUI();
|
|
|
|
|
EndDrawing();
|
2024-10-26 02:14:41 +02:00
|
|
|
}
|
|
|
|
|
|
2024-11-26 01:58:09 +01:00
|
|
|
void drawUI(){
|
2024-11-03 03:48:49 +01:00
|
|
|
mutex.lock();
|
2024-11-26 01:58:09 +01:00
|
|
|
ui.draw(SCREEN_MOUSE, canvas->camera);
|
2024-11-03 03:48:49 +01:00
|
|
|
mutex.unlock();
|
2024-10-26 02:14:41 +02:00
|
|
|
}
|
2024-11-03 03:48:49 +01:00
|
|
|
};
|
|
|
|
|
|
2024-11-14 00:55:09 +01:00
|
|
|
inline void to_json(json& j, const AppManager& A){
|
|
|
|
|
j = json{{"graphmanager", A.gm}, {"signalmanager", A.sm}};
|
2024-11-03 03:48:49 +01:00
|
|
|
}
|
|
|
|
|
|
2024-11-14 00:55:09 +01:00
|
|
|
inline void from_json(const json& j, AppManager& A){
|
2024-11-03 03:48:49 +01:00
|
|
|
j.at("graphmanager").get_to(A.gm); // = json{{"graphmanager", A.gm}, {"signalmanager", A.sm}};
|
|
|
|
|
}
|
|
|
|
|
}
|