refctoring WIP
This commit is contained in:
@@ -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<int>() << std::endl;
|
||||||
|
|
||||||
|
if(n.at("nodeType").get<int>() == NodeType::MIDINODE) gm->addNode(n.template get<std::shared_ptr<MidiNode>>());
|
||||||
|
if(n.at("nodeType").get<int>() == NodeType::CONFIGNODE) gm->addNode(n.template get<std::shared_ptr<ConfigNode>>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<int>() > highest_id) {
|
||||||
|
highest_id = n.at("id").get<int>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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>();
|
||||||
|
int end_id = e.at("end").get<int>();
|
||||||
|
|
||||||
|
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>();
|
||||||
|
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_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<MidiNode>(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();
|
||||||
|
}
|
||||||
+8
-146
@@ -23,7 +23,6 @@ namespace VISEQ::APP {
|
|||||||
public:
|
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<APP::State> state = APP::State::get();
|
std::shared_ptr<APP::State> state = APP::State::get();
|
||||||
@@ -37,151 +36,14 @@ namespace VISEQ::APP {
|
|||||||
|
|
||||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(AppManager, gm, sm);
|
NLOHMANN_DEFINE_TYPE_INTRUSIVE(AppManager, gm, sm);
|
||||||
|
|
||||||
void init(){
|
void init();
|
||||||
canvas->camera.target = (Vector2){ state->settings.width/2.0f, state->settings.height/2.0f };
|
void close();
|
||||||
canvas->camera.offset = (Vector2){ state->settings.width/2.0f, state->settings.height/2.0f };
|
void startSignals();
|
||||||
canvas->camera.rotation = 0.0f;
|
void loadState();
|
||||||
canvas->camera.zoom = 1.0f;
|
void updateMidiPorts();
|
||||||
}
|
void update();
|
||||||
|
void draw();
|
||||||
void close(){
|
void drawUI();
|
||||||
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<int>() << std::endl;
|
|
||||||
|
|
||||||
if(n.at("nodeType").get<int>() == NodeType::MIDINODE) gm->addNode(n.template get<std::shared_ptr<MidiNode>>());
|
|
||||||
if(n.at("nodeType").get<int>() == NodeType::CONFIGNODE) gm->addNode(n.template get<std::shared_ptr<ConfigNode>>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<int>() > highest_id) {
|
|
||||||
highest_id = n.at("id").get<int>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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>();
|
|
||||||
int end_id = e.at("end").get<int>();
|
|
||||||
|
|
||||||
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>();
|
|
||||||
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_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<MidiNode>(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();
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,439 @@
|
|||||||
|
#include "graph-manager.h"
|
||||||
|
|
||||||
|
|
||||||
|
std::shared_ptr<GraphManager> 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<MidiNode>(position));
|
||||||
|
nodes.back()->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VISEQ::GRAPH::GraphManager::createConfigNode(Vec2Df position){
|
||||||
|
nodes.emplace_back(std::make_shared<ConfigNode>(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<MidiNode>(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<Edge>());
|
||||||
|
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<VEC_SPTR_Node>();
|
||||||
|
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<VEC_SPTR_Node>();
|
||||||
|
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<Node>(*n));
|
||||||
|
/*if(n->getNodeType() == NodeType::MIDINODE) nodes.insert(nodes.end(), std::make_shared<MidiNode>(*n));*/
|
||||||
|
/*if(n->getNodeType() == NodeType::CONFIGNODE) nodes.insert(nodes.end(), std::make_shared<ConfigNode>(*n));*/
|
||||||
|
nodes.back()->setPosition(position + nodes.back()->drag_offset);
|
||||||
|
nodes.back()->selected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VISEQ::GRAPH::GraphManager::copyNodes(std::vector<std::shared_ptr<MidiNode>> _nodes, Vec2Df position){
|
||||||
|
for(auto n: _nodes){
|
||||||
|
nodes.insert(nodes.end(), std::make_shared<MidiNode>(*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<Node>(*n));
|
||||||
|
/*if(n->getNodeType() == NodeType::MIDINODE) copied_nodes.insert(copied_nodes.end(), std::make_shared<MidiNode>(*n));*/
|
||||||
|
/*if(n->getNodeType() == NodeType::CONFIGNODE) copied_nodes.insert(copied_nodes.end(), std::make_shared<ConfigNode>(*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<Node>(*n));
|
||||||
|
/*if(n->getNodeType() == NodeType::MIDINODE) nodes.insert(nodes.end(), std::make_shared<MidiNode>(*n));*/
|
||||||
|
/*if(n->getNodeType() == NodeType::CONFIGNODE) nodes.insert(nodes.end(), std::make_shared<ConfigNode>(*n));*/
|
||||||
|
nodes.back()->setPosition(position);
|
||||||
|
return nodes.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPTR_Node VISEQ::GRAPH::GraphManager::copyNode(std::shared_ptr<MidiNode> n, Vec2Df position){
|
||||||
|
std::cout << "copy midinode" << std::endl;
|
||||||
|
nodes.insert(nodes.end(), std::make_shared<MidiNode>(*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<VEC_WPTR_Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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<VISEQ::GRAPH::NODES::Node>(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<VISEQ::GRAPH::EDGES::Edge>(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<VEC_SPTR_Node>();
|
||||||
|
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<VEC_WPTR_Node>(); // 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<VEC_WPTR_Edge>();
|
||||||
|
|
||||||
|
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<MidiNode>(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<Edge>(*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<MidiNode>(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<MidiNode>(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<MidiNode>(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<int>(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<VEC_SPTR_Node>();
|
||||||
|
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<VEC_SPTR_Node>();
|
||||||
|
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<int>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+39
-435
@@ -53,441 +53,45 @@ namespace VISEQ::GRAPH {
|
|||||||
|
|
||||||
GraphManager() {}
|
GraphManager() {}
|
||||||
|
|
||||||
std::shared_ptr<GraphManager> getPtr(){
|
std::shared_ptr<GraphManager> getPtr();
|
||||||
return shared_from_this();
|
int getNodesSize();
|
||||||
}
|
void createMidiNode(Vec2Df position);
|
||||||
|
void createConfigNode(Vec2Df position);
|
||||||
int getNodesSize(){
|
void setMidiPortForAll(int number, bool _state);
|
||||||
return nodes.size();
|
void addNode(SPTR_Node node);
|
||||||
}
|
void addConnection(WPTR_Node from, WPTR_Node to);
|
||||||
|
void addToSelection();
|
||||||
void createMidiNode(Vec2Df position){
|
void select();
|
||||||
nodes.emplace_back(std::make_shared<MidiNode>(position));
|
void deselect();
|
||||||
nodes.back()->update();
|
VEC_SPTR_Node getSelection();
|
||||||
}
|
void copyNodes(VEC_SPTR_Node _nodes, Vec2Df position);
|
||||||
|
void copyNodes(std::vector<std::shared_ptr<MidiNode>> _nodes, Vec2Df position);
|
||||||
void createConfigNode(Vec2Df position){
|
VEC_SPTR_Node getCopiesOfSelectedNodes();
|
||||||
nodes.emplace_back(std::make_shared<ConfigNode>(position));
|
SPTR_Node copyNode(SPTR_Node n, Vec2Df position);
|
||||||
nodes.back()->update();
|
SPTR_Node copyNode(std::shared_ptr<MidiNode> n, Vec2Df position);
|
||||||
}
|
void setNodeMode(int mode);
|
||||||
|
void reset();
|
||||||
void setMidiPortForAll(int number, bool _state){
|
WPTR_Node getNode(int i);
|
||||||
for(auto n : nodes){
|
WPTR_Node getFirstNode();
|
||||||
if(n->getNodeType() == NodeType::MIDINODE) std::dynamic_pointer_cast<MidiNode>(n)->setMidiPort(number, _state);
|
WPTR_Node getLastNode();
|
||||||
}
|
VEC_WPTR_Node getMidiNodes();
|
||||||
}
|
NODES::SPTR_Node getRandomNode();
|
||||||
|
EDGES::SPTR_Edge getRandomEdge();
|
||||||
void addNode(SPTR_Node node){
|
SPTR_Node getClosestNode(Vec2Df position);
|
||||||
std::cout << "added node " << node->id << std::endl;
|
VEC_WPTR_Node getNodesByID(int _id);
|
||||||
nodes.insert(nodes.end(), node);
|
WPTR_Edge lastEdge();
|
||||||
node->update();
|
WPTR_Node next(WPTR_Node current, WPTR_Node last);
|
||||||
}
|
void splitEdge(SPTR_Edge edge);
|
||||||
|
void splitEdges();
|
||||||
void addConnection(WPTR_Node from, WPTR_Node to){
|
void removeEdge(SPTR_Edge edge);
|
||||||
EdgeType et = EdgeType::MIDIEDGE;
|
void removeEdges();
|
||||||
if(from.lock()->getNodeType() == NodeType::CONFIGNODE || to.lock()->getNodeType() == NodeType::CONFIGNODE) et = EdgeType::CONFIGEDGE;
|
VEC_WPTR_Node getAdjacentNodes(VEC_SPTR_Node selected_nodes);
|
||||||
if(from.lock() != to.lock()) {
|
void removeNodesAndEdges(VEC_SPTR_Node selected_nodes);
|
||||||
edges.emplace_back(std::make_shared<Edge>());
|
void pasteNodesAndEdges(VEC_SPTR_Node nodes, Vec2Df position);
|
||||||
edges.back()->edgeType = et;
|
void update();
|
||||||
edges.back()->start = from;
|
void drawEdges(Vec2Df mouse_pos, Camera2D cam);
|
||||||
edges.back()->end = to;
|
void drawNeighborLines(SPTR_Node node, Vec2Df mouse_pos, Camera2D camera);
|
||||||
edges.back()->start_conn = from.lock()->connectors.back();
|
void drawNodes(Vec2Df mouse_pos, Camera2D camera);
|
||||||
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<VEC_SPTR_Node>();
|
|
||||||
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<VEC_SPTR_Node>();
|
|
||||||
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<Node>(*n));
|
|
||||||
/*if(n->getNodeType() == NodeType::MIDINODE) nodes.insert(nodes.end(), std::make_shared<MidiNode>(*n));*/
|
|
||||||
/*if(n->getNodeType() == NodeType::CONFIGNODE) nodes.insert(nodes.end(), std::make_shared<ConfigNode>(*n));*/
|
|
||||||
nodes.back()->setPosition(position + nodes.back()->drag_offset);
|
|
||||||
nodes.back()->selected = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void copyNodes(std::vector<std::shared_ptr<MidiNode>> _nodes, Vec2Df position){
|
|
||||||
for(auto n: _nodes){
|
|
||||||
nodes.insert(nodes.end(), std::make_shared<MidiNode>(*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<Node>(*n));
|
|
||||||
/*if(n->getNodeType() == NodeType::MIDINODE) copied_nodes.insert(copied_nodes.end(), std::make_shared<MidiNode>(*n));*/
|
|
||||||
/*if(n->getNodeType() == NodeType::CONFIGNODE) copied_nodes.insert(copied_nodes.end(), std::make_shared<ConfigNode>(*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<Node>(*n));
|
|
||||||
/*if(n->getNodeType() == NodeType::MIDINODE) nodes.insert(nodes.end(), std::make_shared<MidiNode>(*n));*/
|
|
||||||
/*if(n->getNodeType() == NodeType::CONFIGNODE) nodes.insert(nodes.end(), std::make_shared<ConfigNode>(*n));*/
|
|
||||||
nodes.back()->setPosition(position);
|
|
||||||
return nodes.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
SPTR_Node copyNode(std::shared_ptr<MidiNode> n, Vec2Df position){
|
|
||||||
std::cout << "copy midinode" << std::endl;
|
|
||||||
nodes.insert(nodes.end(), std::make_shared<MidiNode>(*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<VEC_WPTR_Node>();
|
|
||||||
}
|
|
||||||
|
|
||||||
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<VISEQ::GRAPH::NODES::Node>(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<VISEQ::GRAPH::EDGES::Edge>(o.front());
|
|
||||||
}
|
|
||||||
|
|
||||||
SPTR_Node getClosestNode(Vec2Df position){
|
|
||||||
auto neighbors = nodes | RANGE_FILTER([](SPTR_Node n){ return !n->picked_up;}) | std::ranges::to<VEC_SPTR_Node>();
|
|
||||||
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<VEC_WPTR_Node>(); // 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<VEC_WPTR_Edge>();
|
|
||||||
|
|
||||||
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<MidiNode>(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<Edge>(*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<MidiNode>(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<MidiNode>(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<MidiNode>(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<int>(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<VEC_SPTR_Node>();
|
|
||||||
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<VEC_SPTR_Node>();
|
|
||||||
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<int>(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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(GraphManager, nodes, edges, selected_nodes)
|
NLOHMANN_DEFINE_TYPE_INTRUSIVE(GraphManager, nodes, edges, selected_nodes)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "node.h"
|
#include "node.h"
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
|||||||
+19
-2
@@ -17,8 +17,25 @@ namespace VISEQ::GRAPH::NODES{
|
|||||||
class NodeHandle : public BASE::Draggable, public GraphItem, public std::enable_shared_from_this<NodeHandle<T>>{
|
class NodeHandle : public BASE::Draggable, public GraphItem, public std::enable_shared_from_this<NodeHandle<T>>{
|
||||||
public:
|
public:
|
||||||
NodeHandle() {}
|
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) :
|
||||||
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();}
|
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 {
|
virtual GRAPH::GraphItemType getType() override {
|
||||||
return GraphItemType::HANDLE;
|
return GraphItemType::HANDLE;
|
||||||
|
|||||||
@@ -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 <chrono>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
|
||||||
|
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<Signal>(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<Signal> s){
|
||||||
|
erase_signals.push_back(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VISEQ::SIGNAL::SignalManager::updateSignals(){
|
||||||
|
auto last = std::chrono::high_resolution_clock::now();
|
||||||
|
while(loop){
|
||||||
|
std::chrono::duration<double, std::micro> 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<MidiNode>(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();
|
||||||
|
}
|
||||||
+12
-122
@@ -1,15 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "datatypes.h"
|
|
||||||
#include "graph-manager.h"
|
#include "graph-manager.h"
|
||||||
#include "midi-node.h"
|
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
#include "signal.h"
|
#include "signal.h"
|
||||||
#include "midi.h"
|
#include "midi.h"
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
using namespace VISEQ::GRAPH;
|
using namespace VISEQ::GRAPH;
|
||||||
|
|
||||||
@@ -40,124 +36,18 @@ namespace VISEQ::SIGNAL {
|
|||||||
bool loop = true;
|
bool loop = true;
|
||||||
bool play = false;
|
bool play = false;
|
||||||
|
|
||||||
void pause(){
|
void pause();
|
||||||
play = false;
|
void stop();
|
||||||
}
|
void start();
|
||||||
|
void reset();
|
||||||
void stop(){
|
void startSignalThread();
|
||||||
play = false;
|
void stopSignalThread();
|
||||||
reset();
|
void addSignalAtNode(WPTR_Node node);
|
||||||
}
|
void rerouteSignal(VEC_SPTR_Node nodes, VEC_WPTR_Node adjacent_nodes);
|
||||||
|
void rebaseSignal(VEC_SPTR_Node nodes);
|
||||||
void start(){
|
void removeSignal(std::shared_ptr<Signal> s);
|
||||||
play = true;
|
void updateSignals();
|
||||||
}
|
void drawSignals(Vec2Df mouse_pos, Camera2D camera);
|
||||||
|
|
||||||
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<Signal>(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<Signal> s){
|
|
||||||
erase_signals.push_back(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateSignals(){
|
|
||||||
auto last = std::chrono::high_resolution_clock::now();
|
|
||||||
while(loop){
|
|
||||||
std::chrono::duration<double, std::micro> 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<MidiNode>(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();
|
|
||||||
}
|
|
||||||
|
|
||||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(SignalManager, signals)
|
NLOHMANN_DEFINE_TYPE_INTRUSIVE(SignalManager, signals)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user