From f086834da614c8fb851d440ddd5891d467c0c9f2 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 5 Jan 2026 02:56:30 +0100 Subject: [PATCH] refctoring WIP --- src/app-manager.cpp | 146 +++++++++++++ src/app-manager.h | 172 ++------------- src/graph-manager.cpp | 439 ++++++++++++++++++++++++++++++++++++++ src/graph-manager.h | 474 ++++------------------------------------- src/midi-node.h | 1 + src/node-handle.h | 21 +- src/signal-manager.cpp | 131 ++++++++++++ src/signal-manager.h | 134 ++---------- 8 files changed, 804 insertions(+), 714 deletions(-) create mode 100644 src/app-manager.cpp create mode 100644 src/graph-manager.cpp create mode 100644 src/signal-manager.cpp diff --git a/src/app-manager.cpp b/src/app-manager.cpp new file mode 100644 index 0000000..a2b0f9b --- /dev/null +++ b/src/app-manager.cpp @@ -0,0 +1,146 @@ +#include "app-manager.h" + +void VISEQ::APP::AppManager::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 VISEQ::APP::AppManager::close(){ + std::ofstream file("state.json"); + file << json(*this); + std::cout << std::setw(4) << json(*this) << std::endl; +} + +void VISEQ::APP::AppManager::startSignals(){ + sm->startSignalThread(); +} + +void VISEQ::APP::AppManager::loadState(){ + if(std::filesystem::exists("state.json")){ + std::ifstream f("state.json"); + json graph = json::parse(f); + + for(auto n : graph.at("gm").at("nodes")){ + std::cout << "object? " << n << std::endl; + if(n.is_object()){ + std::cout << "nodeType = " << n.at("nodeType").get() << std::endl; + + if(n.at("nodeType").get() == NodeType::MIDINODE) gm->addNode(n.template get>()); + if(n.at("nodeType").get() == NodeType::CONFIGNODE) gm->addNode(n.template get>()); + } + } + + int highest_id = 0; + for(auto n : graph.at("gm").at("nodes")){ + std::cout << n << std::endl; + if(n.is_object()){ + if(n.at("id").get() > highest_id) { + highest_id = n.at("id").get(); + } + } + } + + for(auto e : graph.at("gm").at("edges")){ + if(e.at("id") > highest_id){ + highest_id = e.at("id"); + } + } + + for(auto e : graph.at("gm").at("edges")){ + int start_id = e.at("start").get(); + int end_id = e.at("end").get(); + + VEC_WPTR_Node start = gm->getNodesByID(start_id); + VEC_WPTR_Node end = gm->getNodesByID(end_id); + + if(!start.empty() && !end.empty()) { + std::cout << std::format("connection {} to {}", start_id, end_id) << std::endl; + gm->addConnection(start.front(), end.front()); + } + } + gm->lastEdge().lock()->newId(highest_id); + + for(auto s : graph.at("sm").at("signals")){ + int start_id = s.at("start").get(); + int next_id = s.at("next").get(); + int last_id = s.at("last").get(); + std::shared_ptr ms = s; + VEC_WPTR_Node start = gm->getNodesByID(start_id); + VEC_WPTR_Node last = gm->getNodesByID(last_id); + VEC_WPTR_Node next = gm->getNodesByID(next_id); + + 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->createMidiNode(Vec2Df{(float)i * state->settings.grid*4 + 7 * state->settings.grid, 2 * state->settings.grid*4}); + } + for(int i = 3; i >= 0; --i){ + gm->createMidiNode(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->getNode(i), gm->getNode((i+1) % 8)); + } + sm->addSignalAtNode(gm->getFirstNode()); + } + +} + +void VISEQ::APP::AppManager::updateMidiPorts(){ + for(auto& n : gm->getMidiNodes()){ + auto nc = std::dynamic_pointer_cast(n.lock()); + 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); + } + } + } +} + +void VISEQ::APP::AppManager::update(){ + state->update(canvas->camera); + canvas->update(); + gm->update(); + + mutex.lock(); + updateMidiPorts(); + mutex.unlock(); + + im.update(); + +} + +void VISEQ::APP::AppManager::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); + + im.draw(); + + canvas->drawEnd(); + drawUI(); +EndDrawing(); +} + +void VISEQ::APP::AppManager::drawUI(){ + mutex.lock(); + ui.draw(SCREEN_MOUSE, canvas->camera); + mutex.unlock(); +} diff --git a/src/app-manager.h b/src/app-manager.h index 61b9d8c..6ae03fc 100644 --- a/src/app-manager.h +++ b/src/app-manager.h @@ -21,167 +21,29 @@ using json = nlohmann::json; namespace VISEQ::APP { class AppManager{ public: - AppManager() : im(gm->getPtr(), sm->getPtr(), canvas), ui(sm->getPtr(), gm->getPtr()) {} + AppManager() : im(gm->getPtr(), sm->getPtr(), canvas), ui(sm->getPtr(), gm->getPtr()) {} + std::mutex mutex; - std::mutex mutex; + std::shared_ptr state = APP::State::get(); - std::shared_ptr state = APP::State::get(); + std::shared_ptr canvas = std::make_shared(); + std::shared_ptr gm = std::make_shared(); + std::shared_ptr sm = std::make_shared(gm->getPtr(), &mutex); + VISEQ::INTERACTION::InteractionManager im; - std::shared_ptr canvas = std::make_shared(); - std::shared_ptr gm = std::make_shared(); - std::shared_ptr sm = std::make_shared(gm->getPtr(), &mutex); - VISEQ::INTERACTION::InteractionManager im; + UI::AppUI ui; - UI::AppUI ui; + NLOHMANN_DEFINE_TYPE_INTRUSIVE(AppManager, gm, sm); - NLOHMANN_DEFINE_TYPE_INTRUSIVE(AppManager, gm, sm); - - 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); - - for(auto n : graph.at("gm").at("nodes")){ - std::cout << "object? " << n << std::endl; - if(n.is_object()){ - std::cout << "nodeType = " << n.at("nodeType").get() << std::endl; - - if(n.at("nodeType").get() == NodeType::MIDINODE) gm->addNode(n.template get>()); - if(n.at("nodeType").get() == NodeType::CONFIGNODE) gm->addNode(n.template get>()); - } - } - - int highest_id = 0; - for(auto n : graph.at("gm").at("nodes")){ - std::cout << n << std::endl; - if(n.is_object()){ - if(n.at("id").get() > highest_id) { - highest_id = n.at("id").get(); - } - } - } - - for(auto e : graph.at("gm").at("edges")){ - if(e.at("id") > highest_id){ - highest_id = e.at("id"); - } - } - - for(auto e : graph.at("gm").at("edges")){ - int start_id = e.at("start").get(); - int end_id = e.at("end").get(); - - VEC_WPTR_Node start = gm->getNodesByID(start_id); - VEC_WPTR_Node end = gm->getNodesByID(end_id); - - if(!start.empty() && !end.empty()) { - std::cout << std::format("connection {} to {}", start_id, end_id) << std::endl; - gm->addConnection(start.front(), end.front()); - } - } - gm->lastEdge().lock()->newId(highest_id); - - for(auto s : graph.at("sm").at("signals")){ - int start_id = s.at("start").get(); - int next_id = s.at("next").get(); - int last_id = s.at("last").get(); - std::shared_ptr ms = s; - VEC_WPTR_Node start = gm->getNodesByID(start_id); - VEC_WPTR_Node last = gm->getNodesByID(last_id); - VEC_WPTR_Node next = gm->getNodesByID(next_id); - - 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->createMidiNode(Vec2Df{(float)i * state->settings.grid*4 + 7 * state->settings.grid, 2 * state->settings.grid*4}); - } - for(int i = 3; i >= 0; --i){ - gm->createMidiNode(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->getNode(i), gm->getNode((i+1) % 8)); - } - sm->addSignalAtNode(gm->getFirstNode()); - } - - } - - void updateMidiPorts(){ - for(auto& n : gm->getMidiNodes()){ - auto nc = std::dynamic_pointer_cast(n.lock()); - 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); - } - } - } - } - - void update(){ - state->update(canvas->camera); - canvas->update(); - gm->update(); - - mutex.lock(); - updateMidiPorts(); - mutex.unlock(); - - im.update(); - - } - - 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); - - im.draw(); - - canvas->drawEnd(); - drawUI(); - EndDrawing(); - } - - void drawUI(){ - mutex.lock(); - ui.draw(SCREEN_MOUSE, canvas->camera); - mutex.unlock(); - } - + void init(); + void close(); + void startSignals(); + void loadState(); + void updateMidiPorts(); + void update(); + void draw(); + void drawUI(); }; } diff --git a/src/graph-manager.cpp b/src/graph-manager.cpp new file mode 100644 index 0000000..99b3970 --- /dev/null +++ b/src/graph-manager.cpp @@ -0,0 +1,439 @@ +#include "graph-manager.h" + + +std::shared_ptr VISEQ::GRAPH::GraphManager::getPtr(){ + return shared_from_this(); +} + +int VISEQ::GRAPH::GraphManager::getNodesSize(){ + return nodes.size(); +} + +void VISEQ::GRAPH::GraphManager::createMidiNode(Vec2Df position){ + nodes.emplace_back(std::make_shared(position)); + nodes.back()->update(); +} + +void VISEQ::GRAPH::GraphManager::createConfigNode(Vec2Df position){ + nodes.emplace_back(std::make_shared(position)); + nodes.back()->update(); +} + +void VISEQ::GRAPH::GraphManager::setMidiPortForAll(int number, bool _state){ + for(auto n : nodes){ + if(n->getNodeType() == NodeType::MIDINODE) std::dynamic_pointer_cast(n)->setMidiPort(number, _state); + } +} + +void VISEQ::GRAPH::GraphManager::addNode(SPTR_Node node){ + std::cout << "added node " << node->id << std::endl; + nodes.insert(nodes.end(), node); + node->update(); +} + +void VISEQ::GRAPH::GraphManager::addConnection(WPTR_Node from, WPTR_Node to){ + EdgeType et = EdgeType::MIDIEDGE; + if(from.lock()->getNodeType() == NodeType::CONFIGNODE || to.lock()->getNodeType() == NodeType::CONFIGNODE) et = EdgeType::CONFIGEDGE; + if(from.lock() != to.lock()) { + edges.emplace_back(std::make_shared()); + edges.back()->edgeType = et; + edges.back()->start = from; + edges.back()->end = to; + edges.back()->start_conn = from.lock()->connectors.back(); + edges.back()->end_conn = to.lock()->connectors.back(); + from.lock()->connections.push_back(edges.back()); + to.lock()->connections.push_back(edges.back()); + } +} + +void VISEQ::GRAPH::GraphManager::addToSelection(){ + auto temp_nodes = nodes | RANGE_FILTER([](SPTR_Node n){return n->selected;}) | std::ranges::to(); + selected_nodes.insert(selected_nodes.end(), temp_nodes.begin(), temp_nodes.end()); +} + +void VISEQ::GRAPH::GraphManager::select(){ + auto temp_nodes = nodes | RANGE_FILTER([](SPTR_Node n){return n->select();}) | std::ranges::to(); + if(!KEYS_DOWN.shift) selected_nodes.clear(); + selected_nodes.insert(selected_nodes.end(), temp_nodes.begin(), temp_nodes.end()); + std::for_each(selected_nodes.begin(), selected_nodes.end(), [](WPTR_Node n){n.lock()->selected = true;}); +} + +void VISEQ::GRAPH::GraphManager::deselect(){ + std::cout << "deselect" << std::endl; + for(auto s : selected_nodes){ + s->selected = false; + } + selected_nodes.clear(); +} + +VEC_SPTR_Node VISEQ::GRAPH::GraphManager::getSelection(){ + return selected_nodes; +} + +void VISEQ::GRAPH::GraphManager::copyNodes(VEC_SPTR_Node _nodes, Vec2Df position){ + for(auto n: _nodes){ + nodes.insert(nodes.end(), std::make_shared(*n)); + /*if(n->getNodeType() == NodeType::MIDINODE) nodes.insert(nodes.end(), std::make_shared(*n));*/ + /*if(n->getNodeType() == NodeType::CONFIGNODE) nodes.insert(nodes.end(), std::make_shared(*n));*/ + nodes.back()->setPosition(position + nodes.back()->drag_offset); + nodes.back()->selected = false; + } +} + +void VISEQ::GRAPH::GraphManager::copyNodes(std::vector> _nodes, Vec2Df position){ + for(auto n: _nodes){ + nodes.insert(nodes.end(), std::make_shared(*n)); + nodes.back()->setPosition(position + nodes.back()->drag_offset); + nodes.back()->selected = false; + } +} + +VEC_SPTR_Node VISEQ::GRAPH::GraphManager::getCopiesOfSelectedNodes(){ + VEC_SPTR_Node copied_nodes; + for(auto n : selected_nodes){ + copied_nodes.insert(copied_nodes.end(), std::make_shared(*n)); + /*if(n->getNodeType() == NodeType::MIDINODE) copied_nodes.insert(copied_nodes.end(), std::make_shared(*n));*/ + /*if(n->getNodeType() == NodeType::CONFIGNODE) copied_nodes.insert(copied_nodes.end(), std::make_shared(*n));*/ + copied_nodes.back()->deselect(); + } + return copied_nodes; +} + +SPTR_Node VISEQ::GRAPH::GraphManager::copyNode(SPTR_Node n, Vec2Df position){ + std::cout << "copy basenode" << std::endl; + nodes.insert(nodes.end(), std::make_shared(*n)); + /*if(n->getNodeType() == NodeType::MIDINODE) nodes.insert(nodes.end(), std::make_shared(*n));*/ + /*if(n->getNodeType() == NodeType::CONFIGNODE) nodes.insert(nodes.end(), std::make_shared(*n));*/ + nodes.back()->setPosition(position); + return nodes.back(); +} + +SPTR_Node VISEQ::GRAPH::GraphManager::copyNode(std::shared_ptr n, Vec2Df position){ + std::cout << "copy midinode" << std::endl; + nodes.insert(nodes.end(), std::make_shared(*n)); + nodes.back()->clearConnectors(); + nodes.back()->setPosition(position); + return nodes.back(); +} + +void VISEQ::GRAPH::GraphManager::setNodeMode(int mode){ + for(auto n: nodes){ + n->setMode(mode); + } +} + +void VISEQ::GRAPH::GraphManager::reset(){ + counter.clear(); +} +WPTR_Node VISEQ::GRAPH::GraphManager::getNode(int i){ + return nodes.at(i); +} + +WPTR_Node VISEQ::GRAPH::GraphManager::getFirstNode(){ + return nodes.front(); +} + +WPTR_Node VISEQ::GRAPH::GraphManager::getLastNode(){ + return nodes.back(); +} + +VEC_WPTR_Node VISEQ::GRAPH::GraphManager::getMidiNodes(){ + return nodes | RANGE_FILTER([](VISEQ::GRAPH::NODES::SPTR_Node n){return n->getNodeType() == NodeType::MIDINODE;}) | std::ranges::to(); +} + +NODES::SPTR_Node VISEQ::GRAPH::GraphManager::getRandomNode(){ + VEC_SPTR_GraphItem o; + std::ranges::sample(nodes, std::back_inserter(o), 1, std::mt19937{std::random_device{}()}); + return std::dynamic_pointer_cast(o.front()); +} + +EDGES::SPTR_Edge VISEQ::GRAPH::GraphManager::getRandomEdge(){ + VEC_SPTR_GraphItem o; + std::ranges::sample(edges, std::back_inserter(o), 1, std::mt19937{std::random_device{}()}); + return std::dynamic_pointer_cast(o.front()); +} + +SPTR_Node VISEQ::GRAPH::GraphManager::getClosestNode(Vec2Df position){ + auto neighbors = nodes | RANGE_FILTER([](SPTR_Node n){ return !n->picked_up;}) | std::ranges::to(); + std::sort(neighbors.begin(), neighbors.end(), [position](SPTR_Node na, SPTR_Node nb){ return na->position.dist(position) < nb->position.dist(position);}); + return neighbors.front(); +} +VEC_WPTR_Node VISEQ::GRAPH::GraphManager::getNodesByID(int _id){ + return nodes | RANGE_FILTER([_id](SPTR_Node n){return n->id == _id;}) | std::ranges::to(); // TODO: Multiple nodes with the same ID? Why not return only one node? +} + +WPTR_Edge VISEQ::GRAPH::GraphManager::lastEdge(){ + return edges.back(); +} + +WPTR_Node VISEQ::GRAPH::GraphManager::next(WPTR_Node current, WPTR_Node last){ + VEC_WPTR_Edge possible_connections = current.lock()->connections | RANGE_FILTER([current](WPTR_Edge c){ + return (c.lock()->getEdgeType() == EdgeType::MIDIEDGE) && ((c.lock()->start.lock() == current.lock() && c.lock()->pass_forward) || (c.lock()->end.lock() == current.lock() && c.lock()->pass_backward)); + }) | std::ranges::to(); + + if(possible_connections.empty()) return SPTR_Node(nullptr); + + int return_path = 0; + float angle = -1; + if(current.lock()->distributionMode == Distribution::GEOMETRIC){ + Vec2Df coming_from = (current.lock()->position - last.lock()->position).norm(); + int i = 0; + for(auto n : possible_connections){ + WPTR_Node next_node = n.lock()->getNextNode(current); + float angle_v1_v2 = coming_from.norm().dot((next_node.lock()->position - current.lock()->position).norm()); + if(angle_v1_v2 > angle) { + return_path = i; + angle = angle_v1_v2; + } + ++i; + } + } + + if(current.lock()->distributionMode == Distribution::ROUNDROBIN){ + if(counter.find(current.lock()) == counter.end()) { + counter[current.lock()] = 0; + } else { + counter[current.lock()] = (counter[current.lock()] + 1) % possible_connections.size(); + return_path = counter[current.lock()]; + } + } + + if(current.lock()->distributionMode == Distribution::RANDOM){ + return_path = rand() % possible_connections.size(); + } + + return possible_connections.at(return_path).lock()->getNextNode(current); +} + +void VISEQ::GRAPH::GraphManager::splitEdge(SPTR_Edge edge){ + split_edges.push_back(edge); +} + +void VISEQ::GRAPH::GraphManager::splitEdges(){ + for(auto s : split_edges){ + Vec2Df center = s.lock()->end.lock()->position.center(s.lock()->start.lock()->position); + SPTR_Node cn = copyNode(std::dynamic_pointer_cast(s.lock()->start.lock()), center); // TODO: copy connectors + addConnection(s.lock()->start.lock(), cn); + addConnection(cn, s.lock()->end.lock()); + remove_edges.push_back(s); + } + split_edges.clear(); +} + +void VISEQ::GRAPH::GraphManager::removeEdge(SPTR_Edge edge){ + remove_edges.push_back(edge); +} + +void VISEQ::GRAPH::GraphManager::removeEdges(){ + for(auto edge : remove_edges){ + edge.lock()->start.lock()->removeConnector(edge.lock()->start_conn); + edge.lock()->end.lock()->removeConnector(edge.lock()->end_conn); + std::erase(edge.lock()->start.lock()->connections, edge.lock()); + std::erase(edge.lock()->end.lock()->connections, edge.lock()); + std::erase(edges, edge.lock()); + } + remove_edges.clear(); +} + +VEC_WPTR_Node VISEQ::GRAPH::GraphManager::getAdjacentNodes(VEC_SPTR_Node selected_nodes){ + for(auto n : nodes){ + n->highlighted = false; + } + + auto erase_connections = edges | RANGE_FILTER([selected_nodes](SPTR_Edge c){ return std::ranges::contains(selected_nodes, c->start.lock()) || std::ranges::contains(selected_nodes, c->end.lock());}); + + VEC_WPTR_Node adjacent_nodes; + for(auto c : erase_connections){ + if(!c->start.lock()->selected) adjacent_nodes.push_back(c->start); + if(!c->end.lock()->selected) adjacent_nodes.push_back(c->end); + } + + return adjacent_nodes; +} + +void VISEQ::GRAPH::GraphManager::removeNodesAndEdges(VEC_SPTR_Node selected_nodes){ + VEC_WPTR_Node adjacent_nodes = getAdjacentNodes(selected_nodes); + + for(auto n : adjacent_nodes){ + std::erase_if(n.lock()->connections, [n, selected_nodes](VISEQ::GRAPH::EDGES::WPTR_Edge c){ return std::ranges::contains(selected_nodes, c.lock()->start.lock()) || std::ranges::contains(selected_nodes, c.lock()->end.lock());}); + } + + for(auto n : selected_nodes){ + std::erase_if(this->edges, [n](WPTR_Edge c){ return c.lock()->start.lock() == n || c.lock()->end.lock() == n;}); + } + + std::erase_if(this->nodes, [selected_nodes](SPTR_Node n){ return std::ranges::contains(selected_nodes, n);}); +} + +void VISEQ::GRAPH::GraphManager::pasteNodesAndEdges(VEC_SPTR_Node nodes, Vec2Df position){ + + VEC_SPTR_Edge copy_connections; + for(auto c : edges){ + if(std::ranges::contains(nodes, c->start.lock()) && std::ranges::contains(nodes, c->end.lock())) { + copy_connections.push_back(std::make_shared(*c)); + } + } + std::cout << copy_connections.size() << std::endl; + + for(auto old_node : nodes){ + SPTR_Node new_node; + + if(old_node->getNodeType() == NodeType::MIDINODE) { + new_node = copyNode(std::dynamic_pointer_cast(old_node), position + old_node->drag_offset); + } else { + new_node = copyNode(old_node, position + old_node->drag_offset); + } + + new_node->copied = false; + new_node->selected = true; + + //TODO copy connectors + + for(auto c : copy_connections){ + if(c->start.lock() == old_node) { + c->start = new_node; + c->start.lock()->connections.push_back(c); + } + if(c->end.lock() == old_node) { + c->end = new_node; + c->end.lock()->connections.push_back(c); + } + } + } + std::ranges::copy(copy_connections.begin(), copy_connections.end(), std::back_inserter(edges)); +} + +void VISEQ::GRAPH::GraphManager::update(){ + if(!remove_edges.empty()) removeEdges(); + if(!split_edges.empty()) splitEdges(); +} + +void VISEQ::GRAPH::GraphManager::drawEdges(Vec2Df mouse_pos, Camera2D cam){ + for(auto c : edges){ + Vec2Df v1_position = c->start.lock()->position - ((c->start.lock()->position - c->end.lock()->position)).norm() * (c->start.lock()->radius + c->connector_offset); + Vec2Df v2_position = c->end.lock()->position - ((c->end.lock()->position - c->start.lock()->position)).norm() * (c->end.lock()->radius + c->connector_offset); + float v1_v2_distance = v1_position.dist(&v2_position); + + if(v1_v2_distance < 100) c->switches_offset = v1_v2_distance/2.3f; + Vec2Df from_to_handle = c->start.lock()->position - ((c->start.lock()->position - c->end.lock()->position)).norm() * (c->start.lock()->radius + c->switches_offset); + Vec2Df to_from_handle = c->end.lock()->position - ((c->end.lock()->position - c->start.lock()->position)).norm() * (c->end.lock()->radius + c->switches_offset); + Vec2Df middle_handle = c->end.lock()->position - ((c->end.lock()->position - c->start.lock()->position))*0.4f; + Vec2Df middle_handle_2 = c->end.lock()->position - ((c->end.lock()->position - c->start.lock()->position))*0.6f; + Vec2Df flip_handle = middle_handle + (v1_position - v2_position).norm().orth() * (v1_v2_distance < 120.0f ? Clamp((120.0f - v1_v2_distance)*0.2, 0.f, 20.f) : 1.0f); + Vec2Df flip_handle_2 = middle_handle_2 + (v1_position - v2_position).norm().orth() * (v1_v2_distance < 120.0f ? Clamp((120.0f - v1_v2_distance)*-0.2, -20.f, 0.f) : 1.0f); + + // Dynamic opacity by distance to mouse + unsigned char opacity = 150.0f - Clamp(middle_handle.dist(mouse_pos), 0.0f, 150.0f); + unsigned char opacity_from = 250.0f - Clamp(from_to_handle.dist(mouse_pos), 0.0f, 220.0f); + unsigned char opacity_to = 250.0f - Clamp(to_from_handle.dist(mouse_pos), 0.0f, 220.0f); + + float midi_gate_v1 = 0.0f; + float midi_gate_v2 = 0.0f; + + if(c->start.lock()->getNodeType() == NodeType::MIDINODE){ + midi_gate_v1 = Clamp((4.f / 1000.f) * std::dynamic_pointer_cast(c->start.lock())->getNotes().gate, 1.f, 4.f); + } + if(c->end.lock()->getNodeType() == NodeType::MIDINODE){ + midi_gate_v2 = Clamp((4.f / 1000.f) * std::dynamic_pointer_cast(c->end.lock())->getNotes().gate, 1.f, 4.f); + } + // Line with small connector circles + if(c->end.lock()->getNodeType() == NodeType::MIDINODE && c->start.lock()->getNodeType() == NodeType::MIDINODE){ + + Color col = {229, 181, 103, 255}; + if(c->GActive()) col = {108, 153, 187, 255}; + DrawLine(v1_position.x, v1_position.y, v2_position.x, v2_position.y, col); + { + float d = c->start.lock()->position.dist(c->end.lock()->position); + std::string frac = MIDI::Fractions.at(std::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); + + Vec2Df middle_handle = c->start.lock()->position + (c->end.lock()->position - c->start.lock()->position) * ((d - text_length.x)*0.5f)/d; + Vec2Df middle_handle_frac = c->start.lock()->position + (c->end.lock()->position - c->start.lock()->position) * ((d - text_length_frac.x)*0.5f)/d; + Vec2Df text_position = middle_handle + (c->start.lock()->position - c->end.lock()->position).norm().orth() * 12.0f; + Vec2Df text_position_frac = middle_handle_frac - (c->start.lock()->position - c->end.lock()->position).norm().orth() * 4.0f; + + float rotation = Vector2Angle((Vector2){1,0}, (c->end.lock()->position - c->start.lock()->position)) * RAD2DEG; + DrawTextPro(GetFontDefault(), std::format("{:.1f}",d).c_str(), text_position, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK); + if( (int)d % 60 == 0) DrawTextPro(GetFontDefault(), frac.c_str(), text_position_frac, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK); + } + if(midi_gate_v1 == 4.0f) col = VS_COLOR_RED; + DrawCircle(v1_position.x, v1_position.y, midi_gate_v1, col); + + col = {229, 181, 103, 255}; + if(midi_gate_v2 == 4.0f) col = VS_COLOR_RED; + DrawCircle(v2_position.x, v2_position.y, midi_gate_v2, col); + + if(!c->start.lock()->picked_up && !c->end.lock()->picked_up){ + // On/Off switches on both sides + if(ViButton(mouse_pos, from_to_handle.x, from_to_handle.y, 8.0f, c->pass_forward ? (Color){120, 210, 115, opacity_from} : (Color){232, 50, 32, opacity_from} )) c->pass_forward = !c->pass_forward; + if(ViButton(mouse_pos, to_from_handle.x, to_from_handle.y, 8.0f, c->pass_backward ? (Color){120, 210, 115, opacity_to} : (Color){232, 50, 32, opacity_to} )) c->pass_backward = !c->pass_backward; + + if(!c->pass_forward){ + Vec2Df block_point1 = (v1_position - v2_position).norm().orth()*4.0f + from_to_handle; + Vec2Df block_point2 = (v1_position - v2_position).norm().orth()*-4.0f + from_to_handle; + DrawLineEx(block_point1, block_point2, 2.0f, (Color){232,232,232,opacity_from}); + } + + if(!c->pass_backward){ + Vec2Df block_point1 = (v1_position - v2_position).norm().orth()*4.0f + to_from_handle; + Vec2Df block_point2 = (v1_position - v2_position).norm().orth()*-4.0f + to_from_handle; + DrawLineEx(block_point1, block_point2, 2.0f, (Color){232,232,232,opacity_to}); + } + + if(ViButton(mouse_pos, flip_handle_2.x, flip_handle_2.y, 8.0f, (Color){232, 125, 62, opacity} )) splitEdge(c); + } + } + if(c->start.lock()->getNodeType() == NodeType::CONFIGNODE || c->end.lock()->getNodeType() == NodeType::CONFIGNODE){ + DrawLine(v1_position.x, v1_position.y, v2_position.x, v2_position.y, VS_COLOR_BLUE); + } + + if(ViButton(mouse_pos, flip_handle.x, flip_handle.y, 8.0f, (Color){232, 50, 32, opacity} )) removeEdge(c); + } +} + +void VISEQ::GRAPH::GraphManager::drawNeighborLines(SPTR_Node node, Vec2Df mouse_pos, Camera2D camera){ + float min_d = 480; + auto neighbors_local = nodes | RANGE_FILTER([mouse_pos](SPTR_Node n){ return !n->picked_up && !n->selected && n->position.dist(mouse_pos) < 960;}) | std::ranges::to(); + std::sort(neighbors_local.begin(), neighbors_local.end(), [node, mouse_pos](SPTR_Node na, SPTR_Node nb){ return na->position.dist(mouse_pos) < nb->position.dist(mouse_pos);}); + if(!neighbors_local.empty()){ + min_d = neighbors_local.front()->position.dist(mouse_pos); + DrawCircleLinesV(neighbors_local.front()->position, 22.0f, VS_COLOR_PINK); + node->neighbors = neighbors_local | std::ranges::views::filter([mouse_pos, min_d](SPTR_Node n){return abs(n->position.dist(mouse_pos) - min_d) < 5;}) | std::ranges::to(); + for(auto nb : node->neighbors){ + DrawCircleLinesV(nb->position, 20.0f, VS_COLOR_RED); + float d = nb->position.dist(mouse_pos); + float dn = nb->position.dist(node->position); + if(d > 0 && d < min_d*1.5 && d < 960) { + std::string frac = MIDI::Fractions.at(std::clamp(round(dn/60) - 1, 0, 31)); + Vector2 text_length = MeasureTextEx(GetFontDefault(), std::format("{:.1f}",dn).c_str(), 10.0f, 2.0f); + Vector2 text_length_frac = MeasureTextEx(GetFontDefault(), frac.c_str(), 10.0f, 2.0f); + + Vec2Df middle_handle = nb->position + (node->position - nb->position) * ((dn - text_length.x)*0.5f)/dn; + Vec2Df middle_handle_frac = nb->position + (node->position - nb->position) * ((dn - text_length_frac.x)*0.5f)/dn; + Vec2Df text_position = middle_handle + (nb->position - node->position).norm().orth() * 12.0f; + Vec2Df text_position_frac = middle_handle_frac - (nb->position - node->position).norm().orth() * 4.0f; + + float rotation = Vector2Angle((Vector2){1,0}, (node->position - nb->position)) * RAD2DEG; + DrawTextPro(GetFontDefault(), std::format("{:.1f}",dn).c_str(), text_position, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK); + if(IsKeyDown(KEY_LEFT_SHIFT)) DrawTextPro(GetFontDefault(), frac.c_str(), text_position_frac, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK); + Vec2Df n_position = node->position - ((node->position - nb->position)).norm() * 20.0f; + Vec2Df nb_position = nb->position - ((nb->position - node->position)).norm() * 20.0f; + DrawLineV(n_position, nb_position, VS_COLOR_PINK); + } + } + } +} + +void VISEQ::GRAPH::GraphManager::drawNodes(Vec2Df mouse_pos, Camera2D camera){ + for(auto n : nodes){ + if(KEYS_PRESSED.ctrl && n->hovering()) state->selection_origin = n->position; + } + BASE::Interactable::z_hovering = -1; + for(auto n : nodes){ + n->update(); + if(n->picked_up) drawNeighborLines(n, mouse_pos, camera); + n->draw(mouse_pos, camera); + } +} + diff --git a/src/graph-manager.h b/src/graph-manager.h index fc64799..ddfa50b 100644 --- a/src/graph-manager.h +++ b/src/graph-manager.h @@ -53,441 +53,45 @@ namespace VISEQ::GRAPH { GraphManager() {} - std::shared_ptr getPtr(){ - return shared_from_this(); - } - - int getNodesSize(){ - return nodes.size(); - } - - void createMidiNode(Vec2Df position){ - nodes.emplace_back(std::make_shared(position)); - nodes.back()->update(); - } - - void createConfigNode(Vec2Df position){ - nodes.emplace_back(std::make_shared(position)); - nodes.back()->update(); - } - - void setMidiPortForAll(int number, bool _state){ - for(auto n : nodes){ - if(n->getNodeType() == NodeType::MIDINODE) std::dynamic_pointer_cast(n)->setMidiPort(number, _state); - } - } - - void addNode(SPTR_Node node){ - std::cout << "added node " << node->id << std::endl; - nodes.insert(nodes.end(), node); - node->update(); - } - - void addConnection(WPTR_Node from, WPTR_Node to){ - EdgeType et = EdgeType::MIDIEDGE; - if(from.lock()->getNodeType() == NodeType::CONFIGNODE || to.lock()->getNodeType() == NodeType::CONFIGNODE) et = EdgeType::CONFIGEDGE; - if(from.lock() != to.lock()) { - edges.emplace_back(std::make_shared()); - edges.back()->edgeType = et; - edges.back()->start = from; - edges.back()->end = to; - edges.back()->start_conn = from.lock()->connectors.back(); - edges.back()->end_conn = to.lock()->connectors.back(); - from.lock()->connections.push_back(edges.back()); - to.lock()->connections.push_back(edges.back()); - } - } - - void addToSelection(){ - auto temp_nodes = nodes | RANGE_FILTER([](SPTR_Node n){return n->selected;}) | std::ranges::to(); - selected_nodes.insert(selected_nodes.end(), temp_nodes.begin(), temp_nodes.end()); - } - - void select(){ - auto temp_nodes = nodes | RANGE_FILTER([](SPTR_Node n){return n->select();}) | std::ranges::to(); - if(!KEYS_DOWN.shift) selected_nodes.clear(); - selected_nodes.insert(selected_nodes.end(), temp_nodes.begin(), temp_nodes.end()); - std::for_each(selected_nodes.begin(), selected_nodes.end(), [](WPTR_Node n){n.lock()->selected = true;}); - } - - void deselect(){ - std::cout << "deselect" << std::endl; - for(auto s : selected_nodes){ - s->selected = false; - } - selected_nodes.clear(); - } - - VEC_SPTR_Node getSelection(){ - return selected_nodes; - } - - void copyNodes(VEC_SPTR_Node _nodes, Vec2Df position){ - for(auto n: _nodes){ - nodes.insert(nodes.end(), std::make_shared(*n)); - /*if(n->getNodeType() == NodeType::MIDINODE) nodes.insert(nodes.end(), std::make_shared(*n));*/ - /*if(n->getNodeType() == NodeType::CONFIGNODE) nodes.insert(nodes.end(), std::make_shared(*n));*/ - nodes.back()->setPosition(position + nodes.back()->drag_offset); - nodes.back()->selected = false; - } - } - - void copyNodes(std::vector> _nodes, Vec2Df position){ - for(auto n: _nodes){ - nodes.insert(nodes.end(), std::make_shared(*n)); - nodes.back()->setPosition(position + nodes.back()->drag_offset); - nodes.back()->selected = false; - } - } - - VEC_SPTR_Node getCopiesOfSelectedNodes(){ - VEC_SPTR_Node copied_nodes; - for(auto n : selected_nodes){ - copied_nodes.insert(copied_nodes.end(), std::make_shared(*n)); - /*if(n->getNodeType() == NodeType::MIDINODE) copied_nodes.insert(copied_nodes.end(), std::make_shared(*n));*/ - /*if(n->getNodeType() == NodeType::CONFIGNODE) copied_nodes.insert(copied_nodes.end(), std::make_shared(*n));*/ - copied_nodes.back()->deselect(); - } - return copied_nodes; - } - - SPTR_Node copyNode(SPTR_Node n, Vec2Df position){ - std::cout << "copy basenode" << std::endl; - nodes.insert(nodes.end(), std::make_shared(*n)); - /*if(n->getNodeType() == NodeType::MIDINODE) nodes.insert(nodes.end(), std::make_shared(*n));*/ - /*if(n->getNodeType() == NodeType::CONFIGNODE) nodes.insert(nodes.end(), std::make_shared(*n));*/ - nodes.back()->setPosition(position); - return nodes.back(); - } - - SPTR_Node copyNode(std::shared_ptr n, Vec2Df position){ - std::cout << "copy midinode" << std::endl; - nodes.insert(nodes.end(), std::make_shared(*n)); - nodes.back()->clearConnectors(); - nodes.back()->setPosition(position); - return nodes.back(); - } - - void setNodeMode(int mode){ - for(auto n: nodes){ - n->setMode(mode); - } - } - - void reset(){ - counter.clear(); - } - WPTR_Node getNode(int i){ - return nodes.at(i); - } - - WPTR_Node getFirstNode(){ - return nodes.front(); - } - - WPTR_Node getLastNode(){ - return nodes.back(); - } - - VEC_WPTR_Node getMidiNodes(){ - return nodes | RANGE_FILTER([](VISEQ::GRAPH::NODES::SPTR_Node n){return n->getNodeType() == NodeType::MIDINODE;}) | std::ranges::to(); - } - - NODES::SPTR_Node getRandomNode(){ - VEC_SPTR_GraphItem o; - std::ranges::sample(nodes, std::back_inserter(o), 1, std::mt19937{std::random_device{}()}); - return std::dynamic_pointer_cast(o.front()); - } - - EDGES::SPTR_Edge getRandomEdge(){ - VEC_SPTR_GraphItem o; - std::ranges::sample(edges, std::back_inserter(o), 1, std::mt19937{std::random_device{}()}); - return std::dynamic_pointer_cast(o.front()); - } - - SPTR_Node getClosestNode(Vec2Df position){ - auto neighbors = nodes | RANGE_FILTER([](SPTR_Node n){ return !n->picked_up;}) | std::ranges::to(); - std::sort(neighbors.begin(), neighbors.end(), [position](SPTR_Node na, SPTR_Node nb){ return na->position.dist(position) < nb->position.dist(position);}); - return neighbors.front(); - } - VEC_WPTR_Node getNodesByID(int _id){ - return nodes | RANGE_FILTER([_id](SPTR_Node n){return n->id == _id;}) | std::ranges::to(); // TODO: Multiple nodes with the same ID? Why not return only one node? - } - - WPTR_Edge lastEdge(){ - return edges.back(); - } - - WPTR_Node next(WPTR_Node current, WPTR_Node last){ - VEC_WPTR_Edge possible_connections = current.lock()->connections | RANGE_FILTER([current](WPTR_Edge c){ - return (c.lock()->getEdgeType() == EdgeType::MIDIEDGE) && ((c.lock()->start.lock() == current.lock() && c.lock()->pass_forward) || (c.lock()->end.lock() == current.lock() && c.lock()->pass_backward)); - }) | std::ranges::to(); - - if(possible_connections.empty()) return SPTR_Node(nullptr); - - int return_path = 0; - float angle = -1; - if(current.lock()->distributionMode == Distribution::GEOMETRIC){ - Vec2Df coming_from = (current.lock()->position - last.lock()->position).norm(); - int i = 0; - for(auto n : possible_connections){ - WPTR_Node next_node = n.lock()->getNextNode(current); - float angle_v1_v2 = coming_from.norm().dot((next_node.lock()->position - current.lock()->position).norm()); - if(angle_v1_v2 > angle) { - return_path = i; - angle = angle_v1_v2; - } - ++i; - } - } - - if(current.lock()->distributionMode == Distribution::ROUNDROBIN){ - if(counter.find(current.lock()) == counter.end()) { - counter[current.lock()] = 0; - } else { - counter[current.lock()] = (counter[current.lock()] + 1) % possible_connections.size(); - return_path = counter[current.lock()]; - } - } - - if(current.lock()->distributionMode == Distribution::RANDOM){ - return_path = rand() % possible_connections.size(); - } - - return possible_connections.at(return_path).lock()->getNextNode(current); - } - - void splitEdge(SPTR_Edge edge){ - split_edges.push_back(edge); - } - - void splitEdges(){ - for(auto s : split_edges){ - Vec2Df center = s.lock()->end.lock()->position.center(s.lock()->start.lock()->position); - SPTR_Node cn = copyNode(std::dynamic_pointer_cast(s.lock()->start.lock()), center); // TODO: copy connectors - addConnection(s.lock()->start.lock(), cn); - addConnection(cn, s.lock()->end.lock()); - remove_edges.push_back(s); - } - split_edges.clear(); - } - - void removeEdge(SPTR_Edge edge){ - remove_edges.push_back(edge); - } - - void removeEdges(){ - for(auto edge : remove_edges){ - edge.lock()->start.lock()->removeConnector(edge.lock()->start_conn); - edge.lock()->end.lock()->removeConnector(edge.lock()->end_conn); - std::erase(edge.lock()->start.lock()->connections, edge.lock()); - std::erase(edge.lock()->end.lock()->connections, edge.lock()); - std::erase(edges, edge.lock()); - } - remove_edges.clear(); - } - - VEC_WPTR_Node getAdjacentNodes(VEC_SPTR_Node selected_nodes){ - for(auto n : nodes){ - n->highlighted = false; - } - - auto erase_connections = edges | RANGE_FILTER([selected_nodes](SPTR_Edge c){ return std::ranges::contains(selected_nodes, c->start.lock()) || std::ranges::contains(selected_nodes, c->end.lock());}); - - VEC_WPTR_Node adjacent_nodes; - for(auto c : erase_connections){ - if(!c->start.lock()->selected) adjacent_nodes.push_back(c->start); - if(!c->end.lock()->selected) adjacent_nodes.push_back(c->end); - } - - return adjacent_nodes; - } - - void removeNodesAndEdges(VEC_SPTR_Node selected_nodes){ - VEC_WPTR_Node adjacent_nodes = getAdjacentNodes(selected_nodes); - - for(auto n : adjacent_nodes){ - std::erase_if(n.lock()->connections, [n, selected_nodes](VISEQ::GRAPH::EDGES::WPTR_Edge c){ return std::ranges::contains(selected_nodes, c.lock()->start.lock()) || std::ranges::contains(selected_nodes, c.lock()->end.lock());}); - } - - for(auto n : selected_nodes){ - std::erase_if(this->edges, [n](WPTR_Edge c){ return c.lock()->start.lock() == n || c.lock()->end.lock() == n;}); - } - - std::erase_if(this->nodes, [selected_nodes](SPTR_Node n){ return std::ranges::contains(selected_nodes, n);}); - } - - void pasteNodesAndEdges(VEC_SPTR_Node nodes, Vec2Df position){ - - VEC_SPTR_Edge copy_connections; - for(auto c : edges){ - if(std::ranges::contains(nodes, c->start.lock()) && std::ranges::contains(nodes, c->end.lock())) { - copy_connections.push_back(std::make_shared(*c)); - } - } - std::cout << copy_connections.size() << std::endl; - - for(auto old_node : nodes){ - SPTR_Node new_node; - - if(old_node->getNodeType() == NodeType::MIDINODE) { - new_node = copyNode(std::dynamic_pointer_cast(old_node), position + old_node->drag_offset); - } else { - new_node = copyNode(old_node, position + old_node->drag_offset); - } - - new_node->copied = false; - new_node->selected = true; - - //TODO copy connectors - - for(auto c : copy_connections){ - if(c->start.lock() == old_node) { - c->start = new_node; - c->start.lock()->connections.push_back(c); - } - if(c->end.lock() == old_node) { - c->end = new_node; - c->end.lock()->connections.push_back(c); - } - } - } - std::ranges::copy(copy_connections.begin(), copy_connections.end(), std::back_inserter(edges)); - } - - void update(){ - if(!remove_edges.empty()) removeEdges(); - if(!split_edges.empty()) splitEdges(); - } - - void drawEdges(Vec2Df mouse_pos, Camera2D cam){ - for(auto c : edges){ - Vec2Df v1_position = c->start.lock()->position - ((c->start.lock()->position - c->end.lock()->position)).norm() * (c->start.lock()->radius + c->connector_offset); - Vec2Df v2_position = c->end.lock()->position - ((c->end.lock()->position - c->start.lock()->position)).norm() * (c->end.lock()->radius + c->connector_offset); - float v1_v2_distance = v1_position.dist(&v2_position); - - if(v1_v2_distance < 100) c->switches_offset = v1_v2_distance/2.3f; - Vec2Df from_to_handle = c->start.lock()->position - ((c->start.lock()->position - c->end.lock()->position)).norm() * (c->start.lock()->radius + c->switches_offset); - Vec2Df to_from_handle = c->end.lock()->position - ((c->end.lock()->position - c->start.lock()->position)).norm() * (c->end.lock()->radius + c->switches_offset); - Vec2Df middle_handle = c->end.lock()->position - ((c->end.lock()->position - c->start.lock()->position))*0.4f; - Vec2Df middle_handle_2 = c->end.lock()->position - ((c->end.lock()->position - c->start.lock()->position))*0.6f; - Vec2Df flip_handle = middle_handle + (v1_position - v2_position).norm().orth() * (v1_v2_distance < 120.0f ? Clamp((120.0f - v1_v2_distance)*0.2, 0.f, 20.f) : 1.0f); - Vec2Df flip_handle_2 = middle_handle_2 + (v1_position - v2_position).norm().orth() * (v1_v2_distance < 120.0f ? Clamp((120.0f - v1_v2_distance)*-0.2, -20.f, 0.f) : 1.0f); - - // Dynamic opacity by distance to mouse - unsigned char opacity = 150.0f - Clamp(middle_handle.dist(mouse_pos), 0.0f, 150.0f); - unsigned char opacity_from = 250.0f - Clamp(from_to_handle.dist(mouse_pos), 0.0f, 220.0f); - unsigned char opacity_to = 250.0f - Clamp(to_from_handle.dist(mouse_pos), 0.0f, 220.0f); - - float midi_gate_v1 = 0.0f; - float midi_gate_v2 = 0.0f; - - if(c->start.lock()->getNodeType() == NodeType::MIDINODE){ - midi_gate_v1 = Clamp((4.f / 1000.f) * std::dynamic_pointer_cast(c->start.lock())->getNotes().gate, 1.f, 4.f); - } - if(c->end.lock()->getNodeType() == NodeType::MIDINODE){ - midi_gate_v2 = Clamp((4.f / 1000.f) * std::dynamic_pointer_cast(c->end.lock())->getNotes().gate, 1.f, 4.f); - } - // Line with small connector circles - if(c->end.lock()->getNodeType() == NodeType::MIDINODE && c->start.lock()->getNodeType() == NodeType::MIDINODE){ - - Color col = {229, 181, 103, 255}; - if(c->GActive()) col = {108, 153, 187, 255}; - DrawLine(v1_position.x, v1_position.y, v2_position.x, v2_position.y, col); - { - float d = c->start.lock()->position.dist(c->end.lock()->position); - std::string frac = MIDI::Fractions.at(std::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); - - Vec2Df middle_handle = c->start.lock()->position + (c->end.lock()->position - c->start.lock()->position) * ((d - text_length.x)*0.5f)/d; - Vec2Df middle_handle_frac = c->start.lock()->position + (c->end.lock()->position - c->start.lock()->position) * ((d - text_length_frac.x)*0.5f)/d; - Vec2Df text_position = middle_handle + (c->start.lock()->position - c->end.lock()->position).norm().orth() * 12.0f; - Vec2Df text_position_frac = middle_handle_frac - (c->start.lock()->position - c->end.lock()->position).norm().orth() * 4.0f; - - float rotation = Vector2Angle((Vector2){1,0}, (c->end.lock()->position - c->start.lock()->position)) * RAD2DEG; - DrawTextPro(GetFontDefault(), std::format("{:.1f}",d).c_str(), text_position, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK); - if( (int)d % 60 == 0) DrawTextPro(GetFontDefault(), frac.c_str(), text_position_frac, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK); - } - if(midi_gate_v1 == 4.0f) col = VS_COLOR_RED; - DrawCircle(v1_position.x, v1_position.y, midi_gate_v1, col); - - col = {229, 181, 103, 255}; - if(midi_gate_v2 == 4.0f) col = VS_COLOR_RED; - DrawCircle(v2_position.x, v2_position.y, midi_gate_v2, col); - - if(!c->start.lock()->picked_up && !c->end.lock()->picked_up){ - // On/Off switches on both sides - if(ViButton(mouse_pos, from_to_handle.x, from_to_handle.y, 8.0f, c->pass_forward ? (Color){120, 210, 115, opacity_from} : (Color){232, 50, 32, opacity_from} )) c->pass_forward = !c->pass_forward; - if(ViButton(mouse_pos, to_from_handle.x, to_from_handle.y, 8.0f, c->pass_backward ? (Color){120, 210, 115, opacity_to} : (Color){232, 50, 32, opacity_to} )) c->pass_backward = !c->pass_backward; - - if(!c->pass_forward){ - Vec2Df block_point1 = (v1_position - v2_position).norm().orth()*4.0f + from_to_handle; - Vec2Df block_point2 = (v1_position - v2_position).norm().orth()*-4.0f + from_to_handle; - DrawLineEx(block_point1, block_point2, 2.0f, (Color){232,232,232,opacity_from}); - } - - if(!c->pass_backward){ - Vec2Df block_point1 = (v1_position - v2_position).norm().orth()*4.0f + to_from_handle; - Vec2Df block_point2 = (v1_position - v2_position).norm().orth()*-4.0f + to_from_handle; - DrawLineEx(block_point1, block_point2, 2.0f, (Color){232,232,232,opacity_to}); - } - - if(ViButton(mouse_pos, flip_handle_2.x, flip_handle_2.y, 8.0f, (Color){232, 125, 62, opacity} )) splitEdge(c); - } - } - if(c->start.lock()->getNodeType() == NodeType::CONFIGNODE || c->end.lock()->getNodeType() == NodeType::CONFIGNODE){ - DrawLine(v1_position.x, v1_position.y, v2_position.x, v2_position.y, VS_COLOR_BLUE); - } - - if(ViButton(mouse_pos, flip_handle.x, flip_handle.y, 8.0f, (Color){232, 50, 32, opacity} )) removeEdge(c); - } - } - - void drawNeighborLines(SPTR_Node node, Vec2Df mouse_pos, Camera2D camera){ - float min_d = 480; - auto neighbors_local = nodes | RANGE_FILTER([mouse_pos](SPTR_Node n){ return !n->picked_up && !n->selected && n->position.dist(mouse_pos) < 960;}) | std::ranges::to(); - std::sort(neighbors_local.begin(), neighbors_local.end(), [node, mouse_pos](SPTR_Node na, SPTR_Node nb){ return na->position.dist(mouse_pos) < nb->position.dist(mouse_pos);}); - if(!neighbors_local.empty()){ - min_d = neighbors_local.front()->position.dist(mouse_pos); - DrawCircleLinesV(neighbors_local.front()->position, 22.0f, VS_COLOR_PINK); - node->neighbors = neighbors_local | std::ranges::views::filter([mouse_pos, min_d](SPTR_Node n){return abs(n->position.dist(mouse_pos) - min_d) < 5;}) | std::ranges::to(); - for(auto nb : node->neighbors){ - DrawCircleLinesV(nb->position, 20.0f, VS_COLOR_RED); - float d = nb->position.dist(mouse_pos); - float dn = nb->position.dist(node->position); - if(d > 0 && d < min_d*1.5 && d < 960) { - std::string frac = MIDI::Fractions.at(std::clamp(round(dn/60) - 1, 0, 31)); - Vector2 text_length = MeasureTextEx(GetFontDefault(), std::format("{:.1f}",dn).c_str(), 10.0f, 2.0f); - Vector2 text_length_frac = MeasureTextEx(GetFontDefault(), frac.c_str(), 10.0f, 2.0f); - - Vec2Df middle_handle = nb->position + (node->position - nb->position) * ((dn - text_length.x)*0.5f)/dn; - Vec2Df middle_handle_frac = nb->position + (node->position - nb->position) * ((dn - text_length_frac.x)*0.5f)/dn; - Vec2Df text_position = middle_handle + (nb->position - node->position).norm().orth() * 12.0f; - Vec2Df text_position_frac = middle_handle_frac - (nb->position - node->position).norm().orth() * 4.0f; - - float rotation = Vector2Angle((Vector2){1,0}, (node->position - nb->position)) * RAD2DEG; - DrawTextPro(GetFontDefault(), std::format("{:.1f}",dn).c_str(), text_position, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK); - if(IsKeyDown(KEY_LEFT_SHIFT)) DrawTextPro(GetFontDefault(), frac.c_str(), text_position_frac, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK); - Vec2Df n_position = node->position - ((node->position - nb->position)).norm() * 20.0f; - Vec2Df nb_position = nb->position - ((nb->position - node->position)).norm() * 20.0f; - DrawLineV(n_position, nb_position, VS_COLOR_PINK); - } - } - } - } - - void drawNodes(Vec2Df mouse_pos, Camera2D camera){ - for(auto n : nodes){ - if(KEYS_PRESSED.ctrl && n->hovering()) state->selection_origin = n->position; - } - BASE::Interactable::z_hovering = -1; - for(auto n : nodes){ - n->update(); - if(n->picked_up) drawNeighborLines(n, mouse_pos, camera); - n->draw(mouse_pos, camera); - } - } + std::shared_ptr getPtr(); + int getNodesSize(); + void createMidiNode(Vec2Df position); + void createConfigNode(Vec2Df position); + void setMidiPortForAll(int number, bool _state); + void addNode(SPTR_Node node); + void addConnection(WPTR_Node from, WPTR_Node to); + void addToSelection(); + void select(); + void deselect(); + VEC_SPTR_Node getSelection(); + void copyNodes(VEC_SPTR_Node _nodes, Vec2Df position); + void copyNodes(std::vector> _nodes, Vec2Df position); + VEC_SPTR_Node getCopiesOfSelectedNodes(); + SPTR_Node copyNode(SPTR_Node n, Vec2Df position); + SPTR_Node copyNode(std::shared_ptr n, Vec2Df position); + void setNodeMode(int mode); + void reset(); + WPTR_Node getNode(int i); + WPTR_Node getFirstNode(); + WPTR_Node getLastNode(); + VEC_WPTR_Node getMidiNodes(); + NODES::SPTR_Node getRandomNode(); + EDGES::SPTR_Edge getRandomEdge(); + SPTR_Node getClosestNode(Vec2Df position); + VEC_WPTR_Node getNodesByID(int _id); + WPTR_Edge lastEdge(); + WPTR_Node next(WPTR_Node current, WPTR_Node last); + void splitEdge(SPTR_Edge edge); + void splitEdges(); + void removeEdge(SPTR_Edge edge); + void removeEdges(); + VEC_WPTR_Node getAdjacentNodes(VEC_SPTR_Node selected_nodes); + void removeNodesAndEdges(VEC_SPTR_Node selected_nodes); + void pasteNodesAndEdges(VEC_SPTR_Node nodes, Vec2Df position); + void update(); + void drawEdges(Vec2Df mouse_pos, Camera2D cam); + void drawNeighborLines(SPTR_Node node, Vec2Df mouse_pos, Camera2D camera); + void drawNodes(Vec2Df mouse_pos, Camera2D camera); NLOHMANN_DEFINE_TYPE_INTRUSIVE(GraphManager, nodes, edges, selected_nodes) }; diff --git a/src/midi-node.h b/src/midi-node.h index c700772..d97eccb 100644 --- a/src/midi-node.h +++ b/src/midi-node.h @@ -8,6 +8,7 @@ #include "node.h" #include "raylib.h" #include "state.h" +#include #include #include diff --git a/src/node-handle.h b/src/node-handle.h index 69489cd..21b1d7f 100644 --- a/src/node-handle.h +++ b/src/node-handle.h @@ -17,8 +17,25 @@ namespace VISEQ::GRAPH::NODES{ class NodeHandle : public BASE::Draggable, public GraphItem, public std::enable_shared_from_this>{ public: NodeHandle() {} - NodeHandle(T &_assignedValue, Vec2Df parentPos, Color _col, std::string name, float orbit, int steps) : assignedValueRef(_assignedValue), parentPosition(parentPos), col(_col), name(name), orbit(orbit), steps(steps) {init();} - NodeHandle(T &_assignedValue, Vec2Df parentPos, Color _col, std::string name, float orbit, int steps, float min, float max, float minDist, float maxDist) : assignedValueRef(_assignedValue), parentPosition(parentPos), col(_col), name(name), minValue(min), maxValue(max), minDistance(minDist), maxDistance(maxDist), orbit(orbit), steps(steps) {init();} + NodeHandle(T &_assignedValue, Vec2Df parentPos, Color _col, std::string name, float orbit, int steps) : + assignedValueRef(_assignedValue), + parentPosition(parentPos), + col(_col), + name(name), + orbit(orbit), + steps(steps) {init();} + + NodeHandle(T &_assignedValue, Vec2Df parentPos, Color _col, std::string name, float orbit, int steps, float min, float max, float minDist, float maxDist) : + assignedValueRef(_assignedValue), + parentPosition(parentPos), + col(_col), + name(name), + minValue(min), + maxValue(max), + minDistance(minDist), + maxDistance(maxDist), + orbit(orbit), + steps(steps) {init();} virtual GRAPH::GraphItemType getType() override { return GraphItemType::HANDLE; diff --git a/src/signal-manager.cpp b/src/signal-manager.cpp new file mode 100644 index 0000000..487fda2 --- /dev/null +++ b/src/signal-manager.cpp @@ -0,0 +1,131 @@ +#include "signal-manager.h" + +#include "datatypes.h" +#include "midi-node.h" +#include "node.h" +#include "signal.h" +#include "midi.h" + +#include +#include +#include + + +void VISEQ::SIGNAL::SignalManager::pause(){ + play = false; +} + +void VISEQ::SIGNAL::SignalManager::stop(){ + play = false; + reset(); +} + +void VISEQ::SIGNAL::SignalManager::start(){ + play = true; +} + +void VISEQ::SIGNAL::SignalManager::reset(){ + for(auto &s : signals){ + s->position = s->start.lock()->position + Vec2Df{0.1, 0.1}; + s->next = s->start; + s->last = s->start; + } + gm->reset(); +} + +void VISEQ::SIGNAL::SignalManager::startSignalThread(){ + std::thread t1(&SignalManager::updateSignals, this); + t1.detach(); +} + +void VISEQ::SIGNAL::SignalManager::stopSignalThread(){ + loop = false; +} + +void VISEQ::SIGNAL::SignalManager::addSignalAtNode(WPTR_Node node){ + Color c = {(unsigned char)(20 + (rand() % 200)), 100, (unsigned char)(20 + (int)(rand() % 200)), 200}; + signals.emplace_back(std::make_shared(node.lock()->position + Vec2Df{0.5f, 0.5f}, 8.0f, 480, c)); // position + 1 or else it does not render. why? no idea! + signals.back()->next = node; + signals.back()->last = node; + signals.back()->start = node; +} + +void VISEQ::SIGNAL::SignalManager::rerouteSignal(VEC_SPTR_Node nodes, VEC_WPTR_Node adjacent_nodes){ + auto reroute_signals = signals | RANGE_FILTER([nodes](SIGNAL::SPTR_Signal s){return std::ranges::contains(nodes, s->next.lock());}); + + for(auto s : reroute_signals){ + float dist = 10000000; + SPTR_Node next_node; + for(auto n : adjacent_nodes){ + if(n.lock()->position.dist(s->position) < dist) { + dist = n.lock()->position.dist(s->position); + next_node = n.lock(); + } + } + s->next = next_node; + s->last = next_node; + } + +} + +void VISEQ::SIGNAL::SignalManager::rebaseSignal(VEC_SPTR_Node nodes){ + auto rebase_signals = signals | RANGE_FILTER([nodes](SIGNAL::SPTR_Signal s){return std::ranges::contains(nodes, s->start.lock());}); + + for(auto s : rebase_signals){ + s->start = s->next; + } +} + +void VISEQ::SIGNAL::SignalManager::removeSignal(std::shared_ptr s){ + erase_signals.push_back(s); +} + +void VISEQ::SIGNAL::SignalManager::updateSignals(){ + auto last = std::chrono::high_resolution_clock::now(); + while(loop){ + std::chrono::duration diff = std::chrono::high_resolution_clock::now() - last; + if((int)diff.count() >= 250){ + mutex->lock(); + if(play){ + for(auto& s : signals){ + s->direction = (s->next.lock()->position - s->position).norm(); + s->update(diff.count()); + if(s->position.dist(s->next.lock()->position) < 0.6f) { + WPTR_Node next_node = gm->next(s->next, s->last); + s->next.lock()->trigger(); + + if(next_node.lock() != s->next.lock() && !next_node.expired() && s->next.lock()->getNodeType() == NodeType::MIDINODE) { + auto midinode = std::dynamic_pointer_cast(s->next.lock()); + for(auto &m : midinode->midi_outs){ + if(m.second.enabled) { + if(midinode->send_note) midi.note(m.second.i, midinode->getNotes().channel, (int)Clamp(midinode->getNotes().note + s->midi_note_offset, 0, 127), midinode->getNotes().velocity, midinode->getNotes().gate); + if(midinode->send_cc) midi.cc(m.second.i, midinode->getCC().channel, midinode->getCC().control, (int)Clamp(midinode->getCC().value + s->midi_cc_offset, 0, 127)); + if(midinode->send_pitch_bend) midi.pitch_bend(m.second.i, midinode->getPitchBend().channel, midinode->getPitchBend().value); + } + } + s->last = s->next; + s->next = next_node; + } + } + } + } + midi.update(); + mutex->unlock(); + last = std::chrono::high_resolution_clock::now(); + } + } +} + +void VISEQ::SIGNAL::SignalManager::drawSignals(Vec2Df mouse_pos, Camera2D camera){ + mutex->lock(); + if(!erase_signals.empty()){ + for(auto s: erase_signals){ + std::erase(signals, s); + } + erase_signals.clear(); + } + for(auto s : signals){ + s->draw(mouse_pos, camera); + } + mutex->unlock(); +} diff --git a/src/signal-manager.h b/src/signal-manager.h index 92aa129..f29fa2a 100644 --- a/src/signal-manager.h +++ b/src/signal-manager.h @@ -1,15 +1,11 @@ #pragma once -#include "datatypes.h" #include "graph-manager.h" -#include "midi-node.h" #include "node.h" #include "signal.h" #include "midi.h" -#include #include -#include using namespace VISEQ::GRAPH; @@ -40,124 +36,18 @@ namespace VISEQ::SIGNAL { bool loop = true; bool play = false; - void pause(){ - play = false; - } - - void stop(){ - play = false; - reset(); - } - - void start(){ - play = true; - } - - void reset(){ - for(auto &s : signals){ - s->position = s->start.lock()->position + Vec2Df{0.1, 0.1}; - s->next = s->start; - s->last = s->start; - } - gm->reset(); - } - - void startSignalThread(){ - std::thread t1(&SignalManager::updateSignals, this); - t1.detach(); - } - - void stopSignalThread(){ - loop = false; - } - - void addSignalAtNode(WPTR_Node node){ - Color c = {(unsigned char)(20 + (rand() % 200)), 100, (unsigned char)(20 + (int)(rand() % 200)), 200}; - signals.emplace_back(std::make_shared(node.lock()->position + Vec2Df{0.5f, 0.5f}, 8.0f, 480, c)); // position + 1 or else it does not render. why? no idea! - signals.back()->next = node; - signals.back()->last = node; - signals.back()->start = node; - } - - void rerouteSignal(VEC_SPTR_Node nodes, VEC_WPTR_Node adjacent_nodes){ - auto reroute_signals = signals | RANGE_FILTER([nodes](SIGNAL::SPTR_Signal s){return std::ranges::contains(nodes, s->next.lock());}); - - for(auto s : reroute_signals){ - float dist = 10000000; - SPTR_Node next_node; - for(auto n : adjacent_nodes){ - if(n.lock()->position.dist(s->position) < dist) { - dist = n.lock()->position.dist(s->position); - next_node = n.lock(); - } - } - s->next = next_node; - s->last = next_node; - } - - } - - void rebaseSignal(VEC_SPTR_Node nodes){ - auto rebase_signals = signals | RANGE_FILTER([nodes](SIGNAL::SPTR_Signal s){return std::ranges::contains(nodes, s->start.lock());}); - - for(auto s : rebase_signals){ - s->start = s->next; - } - } - - void removeSignal(std::shared_ptr s){ - erase_signals.push_back(s); - } - - void updateSignals(){ - auto last = std::chrono::high_resolution_clock::now(); - while(loop){ - std::chrono::duration diff = std::chrono::high_resolution_clock::now() - last; - if((int)diff.count() >= 250){ - mutex->lock(); - if(play){ - for(auto& s : signals){ - s->direction = (s->next.lock()->position - s->position).norm(); - s->update(diff.count()); - if(s->position.dist(s->next.lock()->position) < 0.6f) { - WPTR_Node next_node = gm->next(s->next, s->last); - s->next.lock()->trigger(); - - if(next_node.lock() != s->next.lock() && !next_node.expired() && s->next.lock()->getNodeType() == NodeType::MIDINODE) { - auto midinode = std::dynamic_pointer_cast(s->next.lock()); - for(auto &m : midinode->midi_outs){ - if(m.second.enabled) { - if(midinode->send_note) midi.note(m.second.i, midinode->getNotes().channel, (int)Clamp(midinode->getNotes().note + s->midi_note_offset, 0, 127), midinode->getNotes().velocity, midinode->getNotes().gate); - if(midinode->send_cc) midi.cc(m.second.i, midinode->getCC().channel, midinode->getCC().control, (int)Clamp(midinode->getCC().value + s->midi_cc_offset, 0, 127)); - if(midinode->send_pitch_bend) midi.pitch_bend(m.second.i, midinode->getPitchBend().channel, midinode->getPitchBend().value); - } - } - s->last = s->next; - s->next = next_node; - } - } - } - } - midi.update(); - mutex->unlock(); - last = std::chrono::high_resolution_clock::now(); - } - } - } - - void drawSignals(Vec2Df mouse_pos, Camera2D camera){ - mutex->lock(); - if(!erase_signals.empty()){ - for(auto s: erase_signals){ - std::erase(signals, s); - } - erase_signals.clear(); - } - for(auto s : signals){ - s->draw(mouse_pos, camera); - } - mutex->unlock(); - } + void pause(); + void stop(); + void start(); + void reset(); + void startSignalThread(); + void stopSignalThread(); + void addSignalAtNode(WPTR_Node node); + void rerouteSignal(VEC_SPTR_Node nodes, VEC_WPTR_Node adjacent_nodes); + void rebaseSignal(VEC_SPTR_Node nodes); + void removeSignal(std::shared_ptr s); + void updateSignals(); + void drawSignals(Vec2Df mouse_pos, Camera2D camera); NLOHMANN_DEFINE_TYPE_INTRUSIVE(SignalManager, signals)