#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); } }