Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d881ef43a3 | |||
| 77a227f40d | |||
| f086834da6 | |||
| e5f614ce06 | |||
| 011552c020 | |||
| 2725806749 | |||
| bf2f34c902 | |||
| 0e6f2c7256 | |||
| 9e9c32bff4 |
@@ -1,32 +1,16 @@
|
||||
CXX = g++
|
||||
CXXFLAGS = -std=c++23 -Wall -g -fno-omit-frame-pointer -gdwarf-4 -Og -Wnarrowing -DLIBREMIDI_ALSA=1 -DLIBREMIDI_HEADER_ONLY=1
|
||||
CXXINC = -isysteminclude
|
||||
|
||||
LDLIBS = -lraylib -lasound -llibremidi -pthread
|
||||
LDFLAGS = -Llib -L/usr/lib -L/usr/local/lib
|
||||
|
||||
hdr_files = ${wildcard src/*.h}
|
||||
|
||||
src_files = ${wildcard src/*.cpp}
|
||||
#src_files += ${wildcard include/libremidi/*.cpp}
|
||||
hdr_files = $(wildcard src/*.h)
|
||||
|
||||
all: viseq
|
||||
|
||||
viseq: $(src_files) $(hdr_files)
|
||||
viseq:
|
||||
mkdir -p build
|
||||
$(CXX) ${wildcard build/*.o} -o viseq $(LDFLAGS) $(LDLIBS)
|
||||
|
||||
$(src_files): %.cpp : %_src
|
||||
$(CXX) -c $@ -o build/$(*F).o $(CXXFLAGS) $(CXXINC)
|
||||
|
||||
$(hdr_files): %.h : %_hdr
|
||||
$(NOECHO) $(NOOP)
|
||||
|
||||
%_src:
|
||||
$(NOECHO) $(NOOP)
|
||||
|
||||
%_hdr:
|
||||
$(NOECHO) $(NOOP)
|
||||
cd src; $(MAKE)
|
||||
$(CXX) $(wildcard build/*.o) -o viseq $(LDFLAGS) $(LDLIBS)
|
||||
|
||||
clean:
|
||||
rm -f build/*
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
CXX = g++
|
||||
CXXFLAGS = -std=c++23 -Wall -g -fno-omit-frame-pointer -gdwarf-4 -Og -Wnarrowing -DLIBREMIDI_ALSA=1 -DLIBREMIDI_HEADER_ONLY=1
|
||||
CXXINC = -I../include -isysteminclude
|
||||
|
||||
obj_files = main.o app-manager.o graph-manager.o signal-manager.o interaction-manager.o ui.o node.o midi-node.o config-node.o modifier-node.o midi.o signal.o canvas.o state.o edge.o
|
||||
|
||||
|
||||
all: $(obj_files)
|
||||
|
||||
$(obj_files): %.o : %.cpp
|
||||
$(CXX) -c $^ -o ../build/$@ $(CXXFLAGS) $(CXXINC)
|
||||
|
||||
#all: viseq
|
||||
|
||||
#viseq: main.o app-manager.o graph-manager.o signal-manager.o interaction-manager.o ui.o node.o midi-node.o config-node.o modifier-node.o midi.o signal.o canvas.o state.o edge.o
|
||||
# $(CXX) ${wildcard build/*.o} -o viseq $(LDFLAGS) $(LDLIBS)
|
||||
|
||||
#main.o:
|
||||
# $(CXX) -c src/main.cpp -o ../build/main.o $(CXXFLAGS) $(CXXINC)
|
||||
#
|
||||
#app-manager.o:
|
||||
# $(CXX) -c src/app-manager.cpp -o ../build/app-manager.o $(CXXFLAGS) $(CXXINC)
|
||||
|
||||
|
||||
|
||||
#src/canvas.cpp
|
||||
#src/signal.cpp
|
||||
#src/midi.cpp
|
||||
#src/node.cpp
|
||||
#src/graph-manager.cpp
|
||||
#src/modifier-node.cpp
|
||||
#src/config-node.cpp
|
||||
#src/midi-node.cpp
|
||||
#src/ui.cpp
|
||||
#src/state.cpp
|
||||
#src/edge.cpp
|
||||
#src/interaction-manager.cpp
|
||||
#src/signal-manager.cpp
|
||||
#src/app-manager.cpp
|
||||
#src/main.cpp
|
||||
@@ -0,0 +1,158 @@
|
||||
#include "app-manager.h"
|
||||
|
||||
#include "interaction-manager.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "graph-manager.h"
|
||||
#include "midi-node.h"
|
||||
#include "state.h"
|
||||
#include "ui.h"
|
||||
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
|
||||
namespace VISEQ::APP{
|
||||
void 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 AppManager::close(){
|
||||
std::ofstream file("state.json");
|
||||
file << json(*this);
|
||||
std::cout << std::setw(4) << json(*this) << std::endl;
|
||||
}
|
||||
|
||||
void AppManager::startSignals(){
|
||||
sm->startSignalThread();
|
||||
}
|
||||
|
||||
void 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 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 AppManager::update(){
|
||||
state->update(canvas->camera);
|
||||
canvas->update();
|
||||
gm->update();
|
||||
|
||||
mutex.lock();
|
||||
updateMidiPorts();
|
||||
mutex.unlock();
|
||||
|
||||
im.update();
|
||||
|
||||
}
|
||||
|
||||
void 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 AppManager::drawUI(){
|
||||
mutex.lock();
|
||||
ui.draw(SCREEN_MOUSE, canvas->camera);
|
||||
mutex.unlock();
|
||||
}
|
||||
}
|
||||
+18
-161
@@ -1,15 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include "interaction-manager.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "datatypes.h"
|
||||
#include "canvas.h"
|
||||
#include "graph-manager.h"
|
||||
#include "midi-node.h"
|
||||
#include "node.h"
|
||||
#include "raylib.h"
|
||||
#include "signal-manager.h"
|
||||
#include "state.h"
|
||||
#include "ui.h"
|
||||
@@ -21,167 +16,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<APP::State> state = APP::State::get();
|
||||
|
||||
std::shared_ptr<APP::State> state = APP::State::get();
|
||||
std::shared_ptr<CANVAS::Canvas> canvas = std::make_shared<CANVAS::Canvas>();
|
||||
std::shared_ptr<GraphManager> gm = std::make_shared<GraphManager>();
|
||||
std::shared_ptr<SIGNAL::SignalManager> sm = std::make_shared<SIGNAL::SignalManager>(gm->getPtr(), &mutex);
|
||||
VISEQ::INTERACTION::InteractionManager im;
|
||||
|
||||
std::shared_ptr<CANVAS::Canvas> canvas = std::make_shared<CANVAS::Canvas>();
|
||||
std::shared_ptr<GraphManager> gm = std::make_shared<GraphManager>();
|
||||
std::shared_ptr<SIGNAL::SignalManager> sm = std::make_shared<SIGNAL::SignalManager>(gm->getPtr(), &mutex);
|
||||
VISEQ::INTERACTION::InteractionManager im;
|
||||
UI::AppUI ui;
|
||||
|
||||
UI::AppUI ui;
|
||||
|
||||
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<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();
|
||||
}
|
||||
|
||||
void init();
|
||||
void close();
|
||||
void startSignals();
|
||||
void loadState();
|
||||
void updateMidiPorts();
|
||||
void update();
|
||||
void draw();
|
||||
void drawUI();
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(AppManager, gm, sm)
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
#include "canvas.h"
|
||||
|
||||
#include "datatypes.h"
|
||||
#include "interaction-event.h"
|
||||
#include "state.h"
|
||||
#include "draggable.h"
|
||||
#include "raylib.h"
|
||||
#include "raymath.h"
|
||||
#include "vmath.h"
|
||||
#include <memory>
|
||||
|
||||
namespace VISEQ::APP::CANVAS {
|
||||
bool Canvas::hovering() {
|
||||
return state->mouse_z == z_index;
|
||||
}
|
||||
|
||||
void Canvas::drag(Vec2Df position) {
|
||||
Vec2Df select1 = {SCREEN_MOUSE.x > select_start.x ? select_start.x : SCREEN_MOUSE.x, SCREEN_MOUSE.y > select_start.y ? select_start.y : SCREEN_MOUSE.y};
|
||||
Vec2Df select2 = {SCREEN_MOUSE.x > select_start.x ? SCREEN_MOUSE.x - select_start.x : select_start.x - SCREEN_MOUSE.x, SCREEN_MOUSE.y > select_start.y ? SCREEN_MOUSE.y - select_start.y : select_start.y - SCREEN_MOUSE.y};
|
||||
state->selection = (Rectangle){select1.x, select1.y, select2.x, select2.y};
|
||||
}
|
||||
|
||||
void Canvas::pickUp() {
|
||||
std::cout << "canvas pickUp" << std::endl;
|
||||
BASE::Draggable::pickUp();
|
||||
select_start = SCREEN_MOUSE;
|
||||
state->pushEvent({getPtr(), Interaction::DESELECT});
|
||||
}
|
||||
|
||||
void Canvas::putDown() {
|
||||
std::cout << "canvas putDown" << std::endl;
|
||||
BASE::Draggable::putDown();
|
||||
state->pushEvent({getPtr(), Interaction::SELECT});
|
||||
}
|
||||
|
||||
void Canvas::clearSelectionRect(){
|
||||
state->selection = (Rectangle){0,0,0,0};
|
||||
}
|
||||
|
||||
void Canvas::drawStart(){
|
||||
BeginMode2D(camera);
|
||||
}
|
||||
|
||||
void Canvas::drawEnd(){
|
||||
EndMode2D();
|
||||
}
|
||||
|
||||
GRAPH::GraphItemType Canvas::getType() {
|
||||
return GRAPH::GraphItemType::CANVAS;
|
||||
}
|
||||
|
||||
std::shared_ptr<Canvas> Canvas::getPtr(){
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
void Canvas::update() {
|
||||
camera_delta = state->mouse_delta * (-1.0f/camera.zoom);
|
||||
|
||||
if(state->mouse_right.down) {
|
||||
camera.target = Vector2Add(camera.target, camera_delta);
|
||||
}
|
||||
|
||||
if(hovering() && clicked()) state->pushEvent({getPtr(), APP::Interaction::PICKUP});
|
||||
if(pickedUp()) state->pushEvent({getPtr(), APP::Interaction::DRAG});
|
||||
if(picked_up && released()) state->pushEvent({getPtr(), APP::Interaction::PUTDOWN});
|
||||
|
||||
float wheel = GetMouseWheelMove();
|
||||
if(wheel != 0){
|
||||
if(!KEYS_DOWN.shift && !KEYS_DOWN.ctrl && !KEYS_DOWN.alt){
|
||||
camera.offset = state->raw_mouse;
|
||||
camera.target = SCREEN_MOUSE;
|
||||
float scaleFactor = 1.0f + (0.25f*fabsf(wheel));
|
||||
if (wheel < 0) scaleFactor = 1.0f/scaleFactor;
|
||||
camera.zoom = Clamp(camera.zoom*scaleFactor, 0.125f, 64.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::draw() {
|
||||
for(int x = -SETTINGS.grid*100; x < SETTINGS.grid*100; x+=SETTINGS.grid){
|
||||
for(int y = -SETTINGS.grid*100; y < SETTINGS.grid*100; y+=SETTINGS.grid){
|
||||
DrawPixel(x, y, (Color){130,130,130,200});
|
||||
}
|
||||
}
|
||||
if(state->selection.width > 0 || state->selection.height > 0) DrawRectangleLines(state->selection.x, state->selection.y, state->selection.width, state->selection.height, (Color){150,150,150,150});
|
||||
|
||||
}
|
||||
}
|
||||
+15
-83
@@ -1,99 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "datatypes.h"
|
||||
#include "interaction-event.h"
|
||||
#include "state.h"
|
||||
#include "draggable.h"
|
||||
#include "raylib.h"
|
||||
#include "raymath.h"
|
||||
#include "vmath.h"
|
||||
#include <memory>
|
||||
|
||||
namespace VISEQ::APP::CANVAS {
|
||||
class Canvas : public BASE::Draggable, public std::enable_shared_from_this<Canvas> {
|
||||
public:
|
||||
Canvas() {}
|
||||
Canvas() {}
|
||||
|
||||
Camera2D camera;
|
||||
Vec2Df camera_delta;
|
||||
Vec2Df select_start;
|
||||
Camera2D camera;
|
||||
Vec2Df camera_delta;
|
||||
Vec2Df select_start;
|
||||
|
||||
virtual bool hovering() override {
|
||||
return state->mouse_z == z_index;
|
||||
}
|
||||
|
||||
virtual void drag(Vec2Df position) override {
|
||||
Vec2Df select1 = {SCREEN_MOUSE.x > select_start.x ? select_start.x : SCREEN_MOUSE.x, SCREEN_MOUSE.y > select_start.y ? select_start.y : SCREEN_MOUSE.y};
|
||||
Vec2Df select2 = {SCREEN_MOUSE.x > select_start.x ? SCREEN_MOUSE.x - select_start.x : select_start.x - SCREEN_MOUSE.x, SCREEN_MOUSE.y > select_start.y ? SCREEN_MOUSE.y - select_start.y : select_start.y - SCREEN_MOUSE.y};
|
||||
state->selection = (Rectangle){select1.x, select1.y, select2.x, select2.y};
|
||||
}
|
||||
|
||||
virtual void pickUp() override {
|
||||
std::cout << "canvas pickUp" << std::endl;
|
||||
BASE::Draggable::pickUp();
|
||||
select_start = SCREEN_MOUSE;
|
||||
state->pushEvent({getPtr(), Interaction::DESELECT});
|
||||
}
|
||||
|
||||
virtual void putDown() override {
|
||||
std::cout << "canvas putDown" << std::endl;
|
||||
BASE::Draggable::putDown();
|
||||
state->pushEvent({getPtr(), Interaction::SELECT});
|
||||
}
|
||||
|
||||
void clearSelectionRect(){
|
||||
state->selection = (Rectangle){0,0,0,0};
|
||||
}
|
||||
|
||||
virtual void drawStart(){
|
||||
BeginMode2D(camera);
|
||||
}
|
||||
|
||||
virtual void drawEnd(){
|
||||
EndMode2D();
|
||||
}
|
||||
|
||||
GRAPH::GraphItemType getType() override {
|
||||
return GRAPH::GraphItemType::CANVAS;
|
||||
}
|
||||
|
||||
std::shared_ptr<Canvas> getPtr(){
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
virtual void update() override {
|
||||
camera_delta = state->mouse_delta * (-1.0f/camera.zoom);
|
||||
|
||||
if(state->mouse_right.down) {
|
||||
camera.target = Vector2Add(camera.target, camera_delta);
|
||||
}
|
||||
|
||||
if(hovering() && clicked()) state->pushEvent({getPtr(), APP::Interaction::PICKUP});
|
||||
if(pickedUp()) state->pushEvent({getPtr(), APP::Interaction::DRAG});
|
||||
if(picked_up && released()) state->pushEvent({getPtr(), APP::Interaction::PUTDOWN});
|
||||
|
||||
float wheel = GetMouseWheelMove();
|
||||
if(wheel != 0){
|
||||
if(!KEYS_DOWN.shift && !KEYS_DOWN.ctrl && !KEYS_DOWN.alt){
|
||||
camera.offset = state->raw_mouse;
|
||||
camera.target = SCREEN_MOUSE;
|
||||
float scaleFactor = 1.0f + (0.25f*fabsf(wheel));
|
||||
if (wheel < 0) scaleFactor = 1.0f/scaleFactor;
|
||||
camera.zoom = Clamp(camera.zoom*scaleFactor, 0.125f, 64.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void draw() {
|
||||
for(int x = -SETTINGS.grid*100; x < SETTINGS.grid*100; x+=SETTINGS.grid){
|
||||
for(int y = -SETTINGS.grid*100; y < SETTINGS.grid*100; y+=SETTINGS.grid){
|
||||
DrawPixel(x, y, (Color){130,130,130,200});
|
||||
}
|
||||
}
|
||||
if(state->selection.width > 0 || state->selection.height > 0) DrawRectangleLines(state->selection.x, state->selection.y, state->selection.width, state->selection.height, (Color){150,150,150,150});
|
||||
|
||||
}
|
||||
virtual bool hovering() override;
|
||||
virtual void drag(Vec2Df position) override;
|
||||
virtual void pickUp() override;
|
||||
virtual void putDown() override;
|
||||
void clearSelectionRect();
|
||||
virtual void drawStart();
|
||||
virtual void drawEnd();
|
||||
GRAPH::GraphItemType getType() override;
|
||||
std::shared_ptr<Canvas> getPtr();
|
||||
virtual void update() override;
|
||||
virtual void draw();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
#include "config-node.h"
|
||||
|
||||
#include "datatypes.h"
|
||||
#include "edge.h"
|
||||
#include "node.h"
|
||||
#include "raymath.h"
|
||||
#include "state.h"
|
||||
#include <memory>
|
||||
|
||||
namespace VISEQ::GRAPH::NODES {
|
||||
|
||||
std::shared_ptr<ConfigNode> ConfigNode::getPtr() {
|
||||
return std::static_pointer_cast<ConfigNode>(shared_from_this());
|
||||
}
|
||||
|
||||
NodeType ConfigNode::getNodeType() const {return nodeType;}
|
||||
|
||||
void ConfigNode::trigger() {}
|
||||
|
||||
void ConfigNode::setMode(int _mode) {
|
||||
mode = _mode;
|
||||
}
|
||||
|
||||
MIDI::MidiNote ConfigNode::getNotes() const {
|
||||
MIDI::MidiNote cNote;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cNote += cn.lock()->getNotes();
|
||||
}
|
||||
return midiNote + cNote;
|
||||
}
|
||||
|
||||
MIDI::MidiCC ConfigNode::getCC() const {
|
||||
MIDI::MidiCC cCC;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cCC += cn.lock()->getCC();
|
||||
}
|
||||
return midiCC + cCC;
|
||||
}
|
||||
|
||||
MIDI::MidiPC ConfigNode::getPC() const {
|
||||
MIDI::MidiPC cPC;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cPC += cn.lock()->getPC();
|
||||
}
|
||||
return midiPC + cPC;
|
||||
}
|
||||
|
||||
MIDI::MidiPitchBend ConfigNode::getPitchBend() const {
|
||||
MIDI::MidiPitchBend cPitchBend;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cPitchBend += cn.lock()->getPitchBend();
|
||||
}
|
||||
return midiPitchBend + cPitchBend;
|
||||
}
|
||||
|
||||
MIDI::MidiAftertouch ConfigNode::getAftertouch() const {
|
||||
MIDI::MidiAftertouch cAftertouch;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cAftertouch += cn.lock()->getAftertouch();
|
||||
}
|
||||
return midiAftertouch + cAftertouch;
|
||||
}
|
||||
|
||||
MIDI::MidiPolyPressure ConfigNode::getPolyPressure() const {
|
||||
MIDI::MidiPolyPressure cPolyPressure;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cPolyPressure += cn.lock()->getPolyPressure();
|
||||
}
|
||||
return midiPolyPressure + cPolyPressure;
|
||||
}
|
||||
|
||||
std::vector<std::weak_ptr<ConfigNode>> ConfigNode::getConnectedConfigNodes() const { // TODO: check for cyclic connections of config nodes (crashes)
|
||||
auto config_conns = connections | RANGE_FILTER([](EDGES::SPTR_Edge c){ return c->start.lock()->getNodeType() == NodeType::CONFIGNODE || c->end.lock()->getNodeType() == NodeType::CONFIGNODE;}) | std::ranges::to<std::vector<std::shared_ptr<EDGES::Edge>>>();
|
||||
std::vector<std::weak_ptr<ConfigNode>> config_nodes;
|
||||
for(auto e : config_conns){
|
||||
if(e->start.lock().get() != this && e->start.lock()->getNodeType() == NodeType::CONFIGNODE) config_nodes.push_back(std::dynamic_pointer_cast<ConfigNode>(e->start.lock()));
|
||||
//if(e->end.lock().get() != this && e->end.lock()->getNodeType() == NodeType::CONFIGNODE) config_nodes.push_back(std::dynamic_pointer_cast<ConfigNode>(e->end.lock()));
|
||||
}
|
||||
/*std::cout << "id: " << id << " n cn: " << config_nodes.size() << std::endl;*/
|
||||
return config_nodes;
|
||||
}
|
||||
|
||||
void ConfigNode::update() {
|
||||
Node::update();
|
||||
mode = state->node_mode;
|
||||
}
|
||||
|
||||
void ConfigNode::interact(){
|
||||
float wheel = GetMouseWheelMove();
|
||||
|
||||
if(KEYS_DOWN.ctrl && !KEYS_DOWN.alt){
|
||||
switch(state->node_mode){
|
||||
case 0:{
|
||||
midiNote.note += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 12 : wheel;
|
||||
break;}
|
||||
case 1:{
|
||||
midiCC.value += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 12 : wheel;
|
||||
break;}
|
||||
case 2:{
|
||||
midiPitchBend.value += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 12 : wheel;
|
||||
break;}
|
||||
};
|
||||
|
||||
if(midiNote.note < -127 ) midiNote.note = -127;
|
||||
if(midiNote.note > 127 ) midiNote.note = 127;
|
||||
|
||||
if(midiCC.value < -127 ) midiCC.value = -127;
|
||||
if(midiCC.value > 127 ) midiCC.value = 127;
|
||||
|
||||
if(midiPitchBend.value < -16383 ) midiPitchBend.value = -16383;
|
||||
if(midiPitchBend.value > 16383 ) midiPitchBend.value = 16383;
|
||||
|
||||
} else if(KEYS_DOWN.alt) {
|
||||
switch(state->node_mode){
|
||||
case 0:{
|
||||
midiNote.channel += wheel;
|
||||
if(midiNote.channel < 0 ) midiNote.channel = 0;
|
||||
if(midiNote.channel > 15) midiNote.channel = 15;
|
||||
break;}
|
||||
case 1:{
|
||||
midiCC.channel += wheel;
|
||||
if(midiCC.channel < 0 ) midiCC.channel = 0;
|
||||
if(midiCC.channel > 15 ) midiCC.channel = 15;
|
||||
break;}
|
||||
case 2:{
|
||||
midiPitchBend.channel += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 100 : wheel;
|
||||
if(midiPitchBend.channel < 0 ) midiPitchBend.channel = 0;
|
||||
if(midiPitchBend.channel > 15 ) midiPitchBend.channel = 15;
|
||||
break;}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigNode::draw(Vec2Df mouse, Camera2D cam) {
|
||||
Node::draw();
|
||||
if(hovering() || selected) interact();
|
||||
|
||||
DrawRing(position, radius, radius+4, 0, 360, 24, (Color){60, 60, 60, 255});
|
||||
DrawCircleLinesV(position, radius + 4, (Color){100, 100, 100, 255});
|
||||
|
||||
Color gate_color = VS_COLOR_GREEN;
|
||||
|
||||
switch(mode){
|
||||
case 0:{
|
||||
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){180, 210, 115 + 70, (unsigned char)Clamp(255, 0, 255)}, WHITE);
|
||||
break;}
|
||||
case 1:{
|
||||
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){229, 181, 103 + 70, (unsigned char)Clamp(255, 0, 255)}, WHITE);
|
||||
break;}
|
||||
case 2:{
|
||||
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){232, 125, 62 + 70, (unsigned char)Clamp(255, 0, 255)}, WHITE);
|
||||
break;}
|
||||
}
|
||||
if(!picked_up && !selected && !in_selection_rect && !hovering()) DrawCircle(position.x, position.y, radius, gate_color);
|
||||
if(picked_up || in_selection_rect || hovering()) DrawCircle(position.x, position.y, radius, (Color){108, 153, 187, 255});
|
||||
if(selected) DrawCircle(position.x, position.y, radius, (Color){255, 153, 187, 255});
|
||||
|
||||
if(distributionMode == Distribution::RANDOM) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_PINK);
|
||||
if(distributionMode == Distribution::ROUNDROBIN) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_YELLOW);
|
||||
|
||||
if(selected) DrawCircleLinesV(position, radius+8, (Color){158, 255, 210, 255});
|
||||
|
||||
if(KEYS_DOWN.alt){
|
||||
float value = 0;
|
||||
int offset_gate = 0;
|
||||
switch(mode){
|
||||
case 0:
|
||||
value = (float)getNotes().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 1:
|
||||
value = (float)getCC().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 2:
|
||||
value = (float)getPC().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 3:
|
||||
value = (float)getPitchBend().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 4:
|
||||
value = (float)getAftertouch().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 5:
|
||||
value = (float)getPolyPressure().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(mode){
|
||||
case 0:{
|
||||
int offset = MeasureText(std::format("{:+}", getNotes().note).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getNotes().note).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 1:{
|
||||
int offset = MeasureText(std::format("{:+}", getCC().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getCC().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 2:{
|
||||
int offset = MeasureText(std::format("{:+}", getPC().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getPC().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 3:{
|
||||
int offset = MeasureText(std::format("{:+}", getPitchBend().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getPitchBend().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 4:{
|
||||
int offset = MeasureText(std::format("{:+}", getAftertouch().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getAftertouch().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 5:{
|
||||
int offset = MeasureText(std::format("{:+}", getPolyPressure().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getPolyPressure().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
}
|
||||
}
|
||||
|
||||
if(highlighted) DrawCircleLinesV(position, radius+2, (Color){255, 0, 0, 200});
|
||||
//DrawText(std::format("{}", id).c_str(), position.x + 20.0f, position.y + 20.0f, 10, WHITE);
|
||||
}
|
||||
|
||||
void to_json(json& j, const ConfigNode& N){
|
||||
std::vector<int> conns;
|
||||
std::ranges::transform(N.connections.begin(), N.connections.end(), std::back_inserter(conns), [](VISEQ::GRAPH::EDGES::SPTR_Edge e){ return e->id;});
|
||||
|
||||
j = json{
|
||||
{"id", N.id},
|
||||
{"position", N.position},
|
||||
{"distributionMode", N.distributionMode},
|
||||
{"nodeType", N.nodeType},
|
||||
{"midi_note_channel",N.midiNote.channel},
|
||||
{"midi_note_note",N.midiNote.note},
|
||||
{"midi_note_velocity",N.midiNote.velocity},
|
||||
{"midi_note_gate",N.midiNote.gate},
|
||||
{"midi_cc_channel",N.midiCC.channel},
|
||||
{"midi_cc_control",N.midiCC.control},
|
||||
{"midi_cc_value",N.midiCC.value},
|
||||
{"midi_pc_channel",N.midiPC.channel},
|
||||
{"midi_pc_program",N.midiPC.program},
|
||||
{"midi_pc_value",N.midiPC.value},
|
||||
{"midi_pitch_bend_channel",N.midiPitchBend.channel},
|
||||
{"midi_pitch_bend_value",N.midiPitchBend.value},
|
||||
{"midi_aftertouch_channel",N.midiAftertouch.channel},
|
||||
{"midi_aftertouch_value",N.midiAftertouch.value},
|
||||
{"midi_polypressure_channel",N.midiPolyPressure.channel},
|
||||
{"midi_polypressure_value",N.midiPolyPressure.value},
|
||||
{"connections",conns}};
|
||||
}
|
||||
|
||||
void from_json(const json& j, ConfigNode& N){
|
||||
std::cout << "configNode from_json" << std::endl;
|
||||
|
||||
j.at("id").get_to(N.id);
|
||||
j.at("position").get_to<Vec2Df>(N.position);
|
||||
j.at("distributionMode").get_to(N.distributionMode);
|
||||
j.at("nodeType").get_to(N.nodeType);
|
||||
j.at("midi_note_channel").get_to(N.midiNote.channel);
|
||||
j.at("midi_note_note").get_to(N.midiNote.note);
|
||||
j.at("midi_note_velocity").get_to(N.midiNote.velocity);
|
||||
j.at("midi_note_gate").get_to(N.midiNote.gate);
|
||||
j.at("midi_cc_channel").get_to(N.midiCC.channel);
|
||||
j.at("midi_cc_control").get_to(N.midiCC.control);
|
||||
j.at("midi_cc_value").get_to(N.midiCC.value);
|
||||
j.at("midi_pc_channel").get_to(N.midiPC.channel);
|
||||
j.at("midi_pc_program").get_to(N.midiPC.program);
|
||||
j.at("midi_pc_value").get_to(N.midiPC.value);
|
||||
j.at("midi_pitch_bend_channel").get_to(N.midiPitchBend.channel);
|
||||
j.at("midi_pitch_bend_value").get_to(N.midiPitchBend.value);
|
||||
j.at("midi_aftertouch_channel").get_to(N.midiAftertouch.channel);
|
||||
j.at("midi_aftertouch_value").get_to(N.midiAftertouch.value);
|
||||
j.at("midi_polypressure_channel").get_to(N.midiPolyPressure.channel);
|
||||
j.at("midi_polypressure_value").get_to(N.midiPolyPressure.value);
|
||||
|
||||
//j.at("connections").get_to(N.connections);
|
||||
}
|
||||
|
||||
void to_json(json& j, const std::shared_ptr<ConfigNode>& N){
|
||||
j = *N;
|
||||
}
|
||||
|
||||
void from_json(const json& j, std::shared_ptr<ConfigNode>& N){
|
||||
N.reset(new ConfigNode(j.get<ConfigNode>()));
|
||||
}
|
||||
};
|
||||
+18
-271
@@ -1,10 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "datatypes.h"
|
||||
#include "edge.h"
|
||||
#include "node.h"
|
||||
#include "raymath.h"
|
||||
#include "state.h"
|
||||
#include <memory>
|
||||
|
||||
namespace VISEQ::GRAPH::NODES {
|
||||
@@ -15,9 +12,7 @@ namespace VISEQ::GRAPH::NODES {
|
||||
ConfigNode() {std::cout << "ConfigNode Constructor std" << std::endl;}
|
||||
ConfigNode(Vec2Df position) : Node(position) {}
|
||||
|
||||
std::shared_ptr<ConfigNode> getPtr() {
|
||||
return std::static_pointer_cast<ConfigNode>(shared_from_this());
|
||||
}
|
||||
std::shared_ptr<ConfigNode> getPtr();
|
||||
|
||||
NodeType nodeType = NodeType::CONFIGNODE;
|
||||
|
||||
@@ -31,274 +26,26 @@ namespace VISEQ::GRAPH::NODES {
|
||||
int mode = 0;
|
||||
float radius = 20.0f;
|
||||
|
||||
virtual NodeType getNodeType() const override {return nodeType;}
|
||||
virtual NodeType getNodeType() const override;
|
||||
|
||||
virtual void trigger() override {}
|
||||
virtual void trigger() override;
|
||||
|
||||
virtual void setMode(int _mode) override {
|
||||
mode = _mode;
|
||||
}
|
||||
virtual void setMode(int _mode) override;
|
||||
|
||||
MIDI::MidiNote getNotes() const {
|
||||
MIDI::MidiNote cNote;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cNote += cn.lock()->getNotes();
|
||||
}
|
||||
return midiNote + cNote;
|
||||
}
|
||||
MIDI::MidiNote getNotes() const;
|
||||
MIDI::MidiCC getCC() const;
|
||||
MIDI::MidiPC getPC() const;
|
||||
MIDI::MidiPitchBend getPitchBend() const;
|
||||
MIDI::MidiAftertouch getAftertouch() const;
|
||||
MIDI::MidiPolyPressure getPolyPressure() const;
|
||||
std::vector<std::weak_ptr<ConfigNode>> getConnectedConfigNodes() const;
|
||||
void update() override;
|
||||
void interact();
|
||||
void draw(Vec2Df mouse, Camera2D cam) override;
|
||||
|
||||
MIDI::MidiCC getCC() const {
|
||||
MIDI::MidiCC cCC;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cCC += cn.lock()->getCC();
|
||||
}
|
||||
return midiCC + cCC;
|
||||
}
|
||||
|
||||
MIDI::MidiPC getPC() const {
|
||||
MIDI::MidiPC cPC;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cPC += cn.lock()->getPC();
|
||||
}
|
||||
return midiPC + cPC;
|
||||
}
|
||||
|
||||
MIDI::MidiPitchBend getPitchBend() const {
|
||||
MIDI::MidiPitchBend cPitchBend;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cPitchBend += cn.lock()->getPitchBend();
|
||||
}
|
||||
return midiPitchBend + cPitchBend;
|
||||
}
|
||||
|
||||
MIDI::MidiAftertouch getAftertouch() const {
|
||||
MIDI::MidiAftertouch cAftertouch;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cAftertouch += cn.lock()->getAftertouch();
|
||||
}
|
||||
return midiAftertouch + cAftertouch;
|
||||
}
|
||||
|
||||
MIDI::MidiPolyPressure getPolyPressure() const {
|
||||
MIDI::MidiPolyPressure cPolyPressure;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cPolyPressure += cn.lock()->getPolyPressure();
|
||||
}
|
||||
return midiPolyPressure + cPolyPressure;
|
||||
}
|
||||
|
||||
std::vector<std::weak_ptr<ConfigNode>> getConnectedConfigNodes() const { // TODO: check for cyclic connections of config nodes (crashes)
|
||||
auto config_conns = connections | RANGE_FILTER([](EDGES::SPTR_Edge c){ return c->start.lock()->getNodeType() == NodeType::CONFIGNODE || c->end.lock()->getNodeType() == NodeType::CONFIGNODE;}) | std::ranges::to<std::vector<std::shared_ptr<EDGES::Edge>>>();
|
||||
std::vector<std::weak_ptr<ConfigNode>> config_nodes;
|
||||
for(auto e : config_conns){
|
||||
if(e->start.lock().get() != this && e->start.lock()->getNodeType() == NodeType::CONFIGNODE) config_nodes.push_back(std::dynamic_pointer_cast<ConfigNode>(e->start.lock()));
|
||||
//if(e->end.lock().get() != this && e->end.lock()->getNodeType() == NodeType::CONFIGNODE) config_nodes.push_back(std::dynamic_pointer_cast<ConfigNode>(e->end.lock()));
|
||||
}
|
||||
/*std::cout << "id: " << id << " n cn: " << config_nodes.size() << std::endl;*/
|
||||
return config_nodes;
|
||||
}
|
||||
|
||||
void update() override {
|
||||
Node::update();
|
||||
mode = state->node_mode;
|
||||
}
|
||||
|
||||
void interact(){
|
||||
float wheel = GetMouseWheelMove();
|
||||
|
||||
if(KEYS_DOWN.ctrl && !KEYS_DOWN.alt){
|
||||
switch(state->node_mode){
|
||||
case 0:{
|
||||
midiNote.note += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 12 : wheel;
|
||||
break;}
|
||||
case 1:{
|
||||
midiCC.value += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 12 : wheel;
|
||||
break;}
|
||||
case 2:{
|
||||
midiPitchBend.value += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 12 : wheel;
|
||||
break;}
|
||||
};
|
||||
|
||||
if(midiNote.note < -127 ) midiNote.note = -127;
|
||||
if(midiNote.note > 127 ) midiNote.note = 127;
|
||||
|
||||
if(midiCC.value < -127 ) midiCC.value = -127;
|
||||
if(midiCC.value > 127 ) midiCC.value = 127;
|
||||
|
||||
if(midiPitchBend.value < -16383 ) midiPitchBend.value = -16383;
|
||||
if(midiPitchBend.value > 16383 ) midiPitchBend.value = 16383;
|
||||
|
||||
} else if(KEYS_DOWN.alt) {
|
||||
switch(state->node_mode){
|
||||
case 0:{
|
||||
midiNote.channel += wheel;
|
||||
if(midiNote.channel < 0 ) midiNote.channel = 0;
|
||||
if(midiNote.channel > 15) midiNote.channel = 15;
|
||||
break;}
|
||||
case 1:{
|
||||
midiCC.channel += wheel;
|
||||
if(midiCC.channel < 0 ) midiCC.channel = 0;
|
||||
if(midiCC.channel > 15 ) midiCC.channel = 15;
|
||||
break;}
|
||||
case 2:{
|
||||
midiPitchBend.channel += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 100 : wheel;
|
||||
if(midiPitchBend.channel < 0 ) midiPitchBend.channel = 0;
|
||||
if(midiPitchBend.channel > 15 ) midiPitchBend.channel = 15;
|
||||
break;}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void draw(Vec2Df mouse, Camera2D cam) override {
|
||||
Node::draw();
|
||||
if(hovering() || selected) interact();
|
||||
|
||||
DrawRing(position, radius, radius+4, 0, 360, 24, (Color){60, 60, 60, 255});
|
||||
DrawCircleLinesV(position, radius + 4, (Color){100, 100, 100, 255});
|
||||
|
||||
Color gate_color = VS_COLOR_GREEN;
|
||||
|
||||
switch(mode){
|
||||
case 0:{
|
||||
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){180, 210, 115 + 70, (unsigned char)Clamp(255, 0, 255)}, WHITE);
|
||||
break;}
|
||||
case 1:{
|
||||
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){229, 181, 103 + 70, (unsigned char)Clamp(255, 0, 255)}, WHITE);
|
||||
break;}
|
||||
case 2:{
|
||||
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){232, 125, 62 + 70, (unsigned char)Clamp(255, 0, 255)}, WHITE);
|
||||
break;}
|
||||
}
|
||||
if(!picked_up && !selected && !in_selection_rect && !hovering()) DrawCircle(position.x, position.y, radius, gate_color);
|
||||
if(picked_up || in_selection_rect || hovering()) DrawCircle(position.x, position.y, radius, (Color){108, 153, 187, 255});
|
||||
if(selected) DrawCircle(position.x, position.y, radius, (Color){255, 153, 187, 255});
|
||||
|
||||
if(distributionMode == Distribution::RANDOM) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_PINK);
|
||||
if(distributionMode == Distribution::ROUNDROBIN) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_YELLOW);
|
||||
|
||||
if(selected) DrawCircleLinesV(position, radius+8, (Color){158, 255, 210, 255});
|
||||
|
||||
if(KEYS_DOWN.alt){
|
||||
float value = 0;
|
||||
int offset_gate = 0;
|
||||
switch(mode){
|
||||
case 0:
|
||||
value = (float)getNotes().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 1:
|
||||
value = (float)getCC().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 2:
|
||||
value = (float)getPC().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 3:
|
||||
value = (float)getPitchBend().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 4:
|
||||
value = (float)getAftertouch().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 5:
|
||||
value = (float)getPolyPressure().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(mode){
|
||||
case 0:{
|
||||
int offset = MeasureText(std::format("{:+}", getNotes().note).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getNotes().note).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 1:{
|
||||
int offset = MeasureText(std::format("{:+}", getCC().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getCC().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 2:{
|
||||
int offset = MeasureText(std::format("{:+}", getPC().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getPC().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 3:{
|
||||
int offset = MeasureText(std::format("{:+}", getPitchBend().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getPitchBend().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 4:{
|
||||
int offset = MeasureText(std::format("{:+}", getAftertouch().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getAftertouch().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 5:{
|
||||
int offset = MeasureText(std::format("{:+}", getPolyPressure().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getPolyPressure().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
}
|
||||
}
|
||||
|
||||
if(highlighted) DrawCircleLinesV(position, radius+2, (Color){255, 0, 0, 200});
|
||||
//DrawText(std::format("{}", id).c_str(), position.x + 20.0f, position.y + 20.0f, 10, WHITE);
|
||||
}
|
||||
|
||||
friend void to_json(json& j, const ConfigNode& N){
|
||||
std::vector<int> conns;
|
||||
std::ranges::transform(N.connections.begin(), N.connections.end(), std::back_inserter(conns), [](VISEQ::GRAPH::EDGES::SPTR_Edge e){ return e->id;});
|
||||
|
||||
j = json{
|
||||
{"id", N.id},
|
||||
{"position", N.position},
|
||||
{"distributionMode", N.distributionMode},
|
||||
{"nodeType", N.nodeType},
|
||||
{"midi_note_channel",N.midiNote.channel},
|
||||
{"midi_note_note",N.midiNote.note},
|
||||
{"midi_note_velocity",N.midiNote.velocity},
|
||||
{"midi_note_gate",N.midiNote.gate},
|
||||
{"midi_cc_channel",N.midiCC.channel},
|
||||
{"midi_cc_control",N.midiCC.control},
|
||||
{"midi_cc_value",N.midiCC.value},
|
||||
{"midi_pc_channel",N.midiPC.channel},
|
||||
{"midi_pc_program",N.midiPC.program},
|
||||
{"midi_pc_value",N.midiPC.value},
|
||||
{"midi_pitch_bend_channel",N.midiPitchBend.channel},
|
||||
{"midi_pitch_bend_value",N.midiPitchBend.value},
|
||||
{"midi_aftertouch_channel",N.midiAftertouch.channel},
|
||||
{"midi_aftertouch_value",N.midiAftertouch.value},
|
||||
{"midi_polypressure_channel",N.midiPolyPressure.channel},
|
||||
{"midi_polypressure_value",N.midiPolyPressure.value},
|
||||
{"connections",conns}};
|
||||
}
|
||||
|
||||
friend void from_json(const json& j, ConfigNode& N){
|
||||
std::cout << "configNode from_json" << std::endl;
|
||||
|
||||
j.at("id").get_to(N.id);
|
||||
j.at("position").get_to<Vec2Df>(N.position);
|
||||
j.at("distributionMode").get_to(N.distributionMode);
|
||||
j.at("nodeType").get_to(N.nodeType);
|
||||
j.at("midi_note_channel").get_to(N.midiNote.channel);
|
||||
j.at("midi_note_note").get_to(N.midiNote.note);
|
||||
j.at("midi_note_velocity").get_to(N.midiNote.velocity);
|
||||
j.at("midi_note_gate").get_to(N.midiNote.gate);
|
||||
j.at("midi_cc_channel").get_to(N.midiCC.channel);
|
||||
j.at("midi_cc_control").get_to(N.midiCC.control);
|
||||
j.at("midi_cc_value").get_to(N.midiCC.value);
|
||||
j.at("midi_pc_channel").get_to(N.midiPC.channel);
|
||||
j.at("midi_pc_program").get_to(N.midiPC.program);
|
||||
j.at("midi_pc_value").get_to(N.midiPC.value);
|
||||
j.at("midi_pitch_bend_channel").get_to(N.midiPitchBend.channel);
|
||||
j.at("midi_pitch_bend_value").get_to(N.midiPitchBend.value);
|
||||
j.at("midi_aftertouch_channel").get_to(N.midiAftertouch.channel);
|
||||
j.at("midi_aftertouch_value").get_to(N.midiAftertouch.value);
|
||||
j.at("midi_polypressure_channel").get_to(N.midiPolyPressure.channel);
|
||||
j.at("midi_polypressure_value").get_to(N.midiPolyPressure.value);
|
||||
|
||||
//j.at("connections").get_to(N.connections);
|
||||
}
|
||||
friend void to_json(json& j, const ConfigNode& N);
|
||||
friend void from_json(const json& j, ConfigNode& N);
|
||||
friend void to_json(json& j, const std::shared_ptr<ConfigNode>& N);
|
||||
friend void from_json(const json& j, std::shared_ptr<ConfigNode>& N);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#define VS_COLOR_BLACK (Color){20, 20, 20, 255}
|
||||
#define VS_COLOR_DARKGREY (Color){46, 46, 46, 255}
|
||||
#define VS_COLOR_GREY (Color){80, 80, 80, 255}
|
||||
#define VS_COLOR_LIGHTGREY (Color){100, 100, 100, 255}
|
||||
#define VS_COLOR_WHITE (Color){214, 214, 214, 255}
|
||||
#define VS_COLOR_YELLOW (Color){229, 181, 103, 255}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "edge.h"
|
||||
|
||||
#include <memory>
|
||||
#include "node.h"
|
||||
|
||||
namespace VISEQ::GRAPH::EDGES {
|
||||
bool Edge::GActive() {return active;}
|
||||
|
||||
EdgeType Edge::getEdgeType() const {return edgeType;}
|
||||
|
||||
std::weak_ptr<NODES::Node> Edge::getNextNode(std::weak_ptr<NODES::Node> origin){
|
||||
return start.lock() == origin.lock() ? end : start;
|
||||
}
|
||||
|
||||
void Edge::draw(Vec2Df mouse_pos, Camera2D cam) {}
|
||||
|
||||
void to_json(json& j, const VISEQ::GRAPH::EDGES::Edge& E){
|
||||
j = json{{"id",E.id}, {"start", E.start.lock()->id}, {"end",E.end.lock()->id}, {"pass_forward",E.pass_forward}, {"pass_backward", E.pass_backward}};
|
||||
}
|
||||
|
||||
void from_json(const json& j, Edge& E){
|
||||
j.at("id").get_to(E.id);
|
||||
j.at("pass_forward").get_to(E.pass_forward);
|
||||
j.at("pass_backward").get_to(E.pass_backward);
|
||||
}
|
||||
|
||||
void to_json(json& j, const VISEQ::GRAPH::EDGES::SPTR_Edge& E){
|
||||
j = *E;
|
||||
}
|
||||
|
||||
void from_json(const json& j, VISEQ::GRAPH::EDGES::SPTR_Edge& E){
|
||||
E.reset(new Edge(j.get<Edge>()));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+8
-9
@@ -35,15 +35,14 @@ namespace VISEQ::GRAPH::EDGES {
|
||||
float connector_offset = 8.0f;
|
||||
float switches_offset = 25.0f;
|
||||
|
||||
virtual bool GActive() {return active;}
|
||||
|
||||
virtual EdgeType getEdgeType() const {return edgeType;}
|
||||
|
||||
std::weak_ptr<NODES::Node> getNextNode(std::weak_ptr<NODES::Node> origin){
|
||||
return start.lock() == origin.lock() ? end : start;
|
||||
}
|
||||
|
||||
void draw(Vec2Df mouse_pos, Camera2D cam) {}
|
||||
virtual bool GActive();
|
||||
virtual EdgeType getEdgeType() const;
|
||||
std::weak_ptr<NODES::Node> getNextNode(std::weak_ptr<NODES::Node> origin);
|
||||
void draw(Vec2Df mouse_pos, Camera2D cam);
|
||||
friend void to_json(json& j, const Edge& E);
|
||||
friend void from_json(const json& j, Edge& E);
|
||||
friend void to_json(json& j, const std::shared_ptr<Edge>& E);
|
||||
friend void from_json(const json& j, std::shared_ptr<Edge>& E);
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Edge> SPTR_Edge;
|
||||
|
||||
@@ -0,0 +1,471 @@
|
||||
#include "graph-manager.h"
|
||||
|
||||
#include "config-node.h"
|
||||
#include "datatypes.h"
|
||||
#include "state.h"
|
||||
#include "vmath.h"
|
||||
#include "raylib.h"
|
||||
#include "raymath.h"
|
||||
|
||||
#include "button.h"
|
||||
#include "midi-node.h"
|
||||
#include "edge.h"
|
||||
#include "node.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <random>
|
||||
#include <ranges>
|
||||
#include <vector>
|
||||
|
||||
#include "state-io.h"
|
||||
|
||||
namespace VISEQ::GRAPH{
|
||||
std::shared_ptr<GraphManager> GraphManager::getPtr(){
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
int GraphManager::getNodesSize(){
|
||||
return nodes.size();
|
||||
}
|
||||
|
||||
void GraphManager::createMidiNode(Vec2Df position){
|
||||
nodes.emplace_back(std::make_shared<MidiNode>(position));
|
||||
nodes.back()->update();
|
||||
}
|
||||
|
||||
void GraphManager::createConfigNode(Vec2Df position){
|
||||
nodes.emplace_back(std::make_shared<ConfigNode>(position));
|
||||
nodes.back()->update();
|
||||
}
|
||||
|
||||
void 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 GraphManager::addNode(SPTR_Node node){
|
||||
std::cout << "added node " << node->id << std::endl;
|
||||
nodes.insert(nodes.end(), node);
|
||||
node->update();
|
||||
}
|
||||
|
||||
void 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 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 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 GraphManager::deselect(){
|
||||
std::cout << "deselect" << std::endl;
|
||||
for(auto s : selected_nodes){
|
||||
s->selected = false;
|
||||
}
|
||||
selected_nodes.clear();
|
||||
}
|
||||
|
||||
VEC_SPTR_Node GraphManager::getSelection(){
|
||||
return selected_nodes;
|
||||
}
|
||||
|
||||
void 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 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 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 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 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 GraphManager::setNodeMode(int mode){
|
||||
for(auto n: nodes){
|
||||
n->setMode(mode);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphManager::reset(){
|
||||
counter.clear();
|
||||
}
|
||||
WPTR_Node GraphManager::getNode(int i){
|
||||
return nodes.at(i);
|
||||
}
|
||||
|
||||
WPTR_Node GraphManager::getFirstNode(){
|
||||
return nodes.front();
|
||||
}
|
||||
|
||||
WPTR_Node GraphManager::getLastNode(){
|
||||
return nodes.back();
|
||||
}
|
||||
|
||||
VEC_WPTR_Node GraphManager::getMidiNodes(){
|
||||
return nodes | RANGE_FILTER([](NODES::SPTR_Node n){return n->getNodeType() == NodeType::MIDINODE;}) | std::ranges::to<VEC_WPTR_Node>();
|
||||
}
|
||||
|
||||
NODES::SPTR_Node 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<NODES::Node>(o.front());
|
||||
}
|
||||
|
||||
EDGES::SPTR_Edge 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<EDGES::Edge>(o.front());
|
||||
}
|
||||
|
||||
SPTR_Node 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 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 GraphManager::lastEdge(){
|
||||
return edges.back();
|
||||
}
|
||||
|
||||
WPTR_Node 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 GraphManager::splitEdge(SPTR_Edge edge){
|
||||
split_edges.push_back(edge);
|
||||
}
|
||||
|
||||
void 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 GraphManager::removeEdge(SPTR_Edge edge){
|
||||
remove_edges.push_back(edge);
|
||||
}
|
||||
|
||||
void 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 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 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](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 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 GraphManager::update(){
|
||||
if(!remove_edges.empty()) removeEdges();
|
||||
if(!split_edges.empty()) splitEdges();
|
||||
}
|
||||
|
||||
void 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 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 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);
|
||||
}
|
||||
}
|
||||
|
||||
void to_json(json& j, const std::shared_ptr<GraphManager>& G){
|
||||
j = *G;
|
||||
}
|
||||
|
||||
void from_json(const json& j, std::shared_ptr<GraphManager>& G){
|
||||
G.reset(new GraphManager(j.get<GraphManager>()));
|
||||
}
|
||||
}
|
||||
+42
-449
@@ -1,33 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "config-node.h"
|
||||
#include "datatypes.h"
|
||||
#include "state.h"
|
||||
#include "vmath.h"
|
||||
#include "raylib.h"
|
||||
#include "raymath.h"
|
||||
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
#include "button.h"
|
||||
#include "midi-node.h"
|
||||
#include "edge.h"
|
||||
#include "node.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <random>
|
||||
#include <ranges>
|
||||
#include <vector>
|
||||
|
||||
#include "state-io.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
using namespace VISEQ::GRAPH::NODES;
|
||||
@@ -53,443 +39,50 @@ namespace VISEQ::GRAPH {
|
||||
|
||||
GraphManager() {}
|
||||
|
||||
std::shared_ptr<GraphManager> getPtr(){
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
int getNodesSize(){
|
||||
return nodes.size();
|
||||
}
|
||||
|
||||
void createMidiNode(Vec2Df position){
|
||||
nodes.emplace_back(std::make_shared<MidiNode>(position));
|
||||
nodes.back()->update();
|
||||
}
|
||||
|
||||
void createConfigNode(Vec2Df position){
|
||||
nodes.emplace_back(std::make_shared<ConfigNode>(position));
|
||||
nodes.back()->update();
|
||||
}
|
||||
|
||||
void setMidiPortForAll(int number, bool _state){
|
||||
for(auto n : nodes){
|
||||
if(n->getNodeType() == NodeType::MIDINODE) std::dynamic_pointer_cast<MidiNode>(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<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 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);
|
||||
}
|
||||
}
|
||||
std::shared_ptr<GraphManager> 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<std::shared_ptr<MidiNode>> _nodes, Vec2Df position);
|
||||
VEC_SPTR_Node getCopiesOfSelectedNodes();
|
||||
SPTR_Node copyNode(SPTR_Node n, Vec2Df position);
|
||||
SPTR_Node copyNode(std::shared_ptr<MidiNode> 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)
|
||||
|
||||
friend void from_json(const json& j, std::shared_ptr<GraphManager>& G);
|
||||
friend void to_json(json& j, const std::shared_ptr<GraphManager>& G);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
#include "interaction-manager.h"
|
||||
|
||||
#include "clipboard.h"
|
||||
#include "connector.h"
|
||||
#include "datatypes.h"
|
||||
#include "interaction-event.h"
|
||||
#include "node.h"
|
||||
#include "raylib.h"
|
||||
#include "state.h"
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace VISEQ::INTERACTION{
|
||||
void InteractionManager::update(){
|
||||
if(KEYS_DOWN.ctrl && IsKeyPressed(KEY_C)){
|
||||
VEC_SPTR_Node selected_nodes = gm->getCopiesOfSelectedNodes();
|
||||
if(!selected_nodes.empty()) cb.copyNodes(selected_nodes);
|
||||
}
|
||||
|
||||
if(KEYS_DOWN.ctrl && IsKeyPressed(KEY_V)){
|
||||
VEC_SPTR_Node paste_nodes = cb.pasteNodes();
|
||||
if(!paste_nodes.empty()) gm->pasteNodesAndEdges(paste_nodes, MOD_MOUSE);
|
||||
}
|
||||
|
||||
if(IsKeyPressed(KEY_DELETE)){
|
||||
removeNodes(gm->getSelection());
|
||||
}
|
||||
|
||||
if(!KEYS_DOWN.shift && IsKeyPressed(KEY_TAB)) {
|
||||
state->node_mode = (state->node_mode + 1) % 7;
|
||||
}
|
||||
|
||||
if(KEYS_DOWN.shift && IsKeyPressed(KEY_TAB)) {
|
||||
state->node_mode -= state->node_mode > 0 ? 1 : -6;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InteractionManager::draw(){
|
||||
doInteraction();
|
||||
scaleSelection();
|
||||
if(!gm->getSelection().empty()){
|
||||
DrawRectangleRoundedLines(state->bounding_box, 0.1f, 3, VS_COLOR_LIGHTGREY);
|
||||
scale_handle->draw();
|
||||
}
|
||||
}
|
||||
|
||||
void InteractionManager::removeNodes(VEC_SPTR_Node nodes){
|
||||
|
||||
VEC_WPTR_Node adjacent_nodes = gm->getAdjacentNodes(nodes);
|
||||
|
||||
sm->rerouteSignal(nodes, adjacent_nodes);
|
||||
sm->rebaseSignal(nodes);
|
||||
|
||||
gm->removeNodesAndEdges(nodes);
|
||||
}
|
||||
|
||||
void InteractionManager::scaleSelection(){
|
||||
if(!gm->getSelection().empty()){
|
||||
float offset = 30;
|
||||
Vec2Df corner_tl = {50000, 50000};
|
||||
Vec2Df corner_br = {-50000, -50000};
|
||||
std::shared_ptr<Node> node_tl;
|
||||
std::shared_ptr<Node> node_br;
|
||||
|
||||
for(auto n : gm->getSelection()){
|
||||
float x = n->position.x;
|
||||
float y = n->position.y;
|
||||
|
||||
if(x < corner_tl.x && y < corner_tl.y) node_tl = n;
|
||||
if(x > corner_br.x && y > corner_br.y) node_br = n;
|
||||
|
||||
if(x <= corner_tl.x) corner_tl.x = x;
|
||||
if(x >= corner_br.x) corner_br.x = x;
|
||||
if(y <= corner_tl.y) corner_tl.y = y;
|
||||
if(y >= corner_br.y) corner_br.y = y;
|
||||
}
|
||||
corner_tl -= Vec2Df(offset, offset);
|
||||
corner_br += Vec2Df(offset, offset);
|
||||
state->bounding_box = (Rectangle){corner_tl.x, corner_tl.y, corner_br.x - corner_tl.x, corner_br.y - corner_tl.y};
|
||||
|
||||
scale_handle->update();
|
||||
|
||||
if(scale_handle->clicked()){
|
||||
scale_start = (corner_br - corner_tl);
|
||||
for(auto n : gm->getSelection()){
|
||||
n->drag_offset = n->position - node_tl->position;
|
||||
}
|
||||
}
|
||||
if(scale_handle->picked_up){
|
||||
Vec2Df scale = (scale_handle->position - corner_tl) / scale_start;
|
||||
if(IsKeyDown(KEY_LEFT_SHIFT)) scale.y = scale.x;
|
||||
DrawText(std::format("{:.2f} {:.2f}", scale.x, scale.y).c_str(), corner_br.x+5, corner_br.y+5, 10, RED);
|
||||
for(auto n : gm->getSelection()){
|
||||
n->position = node_tl->position + (n->drag_offset * scale);
|
||||
}
|
||||
} else {
|
||||
scale_handle->position = corner_br;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InteractionManager::snapPolar(std::shared_ptr<Node> dragNode){
|
||||
float steps = round((dragNode->neighbors.front()->position.dist(SCREEN_MOUSE)) / state->settings.grid) * state->settings.grid;
|
||||
if(dragNode->neighbors.size() > 1){
|
||||
Vec2Df dn1 = dragNode->neighbors[0]->position;
|
||||
Vec2Df dn2 = dragNode->neighbors[1]->position;
|
||||
Vec2Df v1_orth = dn1.center(dn2);
|
||||
|
||||
if(dn1.x < dn2.x) std::swap(dn1,dn2);
|
||||
|
||||
float dist = dn1.dist(dn2);
|
||||
float dist_orth = std::sqrt(std::pow(steps,2) - std::pow(dist/2,2));
|
||||
|
||||
if(SCREEN_MOUSE.y < v1_orth.y) dist_orth *= -1;
|
||||
|
||||
Vec2Df v1_orth_t = v1_orth + (dn1-dn2).norm().orth() * dist_orth;
|
||||
DrawLineV(dn1, dn2, VS_COLOR_RED);
|
||||
DrawLineV(v1_orth, v1_orth_t, VS_COLOR_RED);
|
||||
DrawCircleV(v1_orth, 3.0f, VS_COLOR_RED);
|
||||
dragNode->position = v1_orth_t;
|
||||
} else if(!dragNode->neighbors.empty()){
|
||||
float pol_r = steps;
|
||||
float pol_theta = Vector2Angle(Vec2Df{1,0},(SCREEN_MOUSE - dragNode->neighbors.front()->position));
|
||||
Vec2Df polar = VISEQ::VMATH::polarToCartesian(pol_r, pol_theta);
|
||||
dragNode->position = dragNode->neighbors.front()->position + polar;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractionManager::doInteraction(){
|
||||
bool exit = false;
|
||||
bool once = false;
|
||||
bool connect_once = false;
|
||||
|
||||
state->mouse_z = canvas->z_index;
|
||||
|
||||
for(auto e : APP::event_queue){
|
||||
if(!connect_once && e.action == APP::Interaction::CONNECT){
|
||||
APP::InteractionEvent partner;
|
||||
bool found = false;
|
||||
for(auto ei : APP::event_queue){
|
||||
if(ei.action == APP::Interaction::HOVER && ei.item->getType() == GraphItemType::CONNECTOR) {
|
||||
std::cout << "YEAH" << std::endl;
|
||||
partner = ei;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto connector = std::dynamic_pointer_cast<Connector>(e.item);
|
||||
if(found && partner != e){
|
||||
auto connector_to = std::dynamic_pointer_cast<Connector>(partner.item);
|
||||
connector->partner = connector_to;
|
||||
connector_to->partner = connector;
|
||||
gm->addConnection(std::dynamic_pointer_cast<Node>(connector->parent.lock()), std::dynamic_pointer_cast<Node>(connector_to->parent.lock()));
|
||||
} else if (partner != e){
|
||||
if(KEYS_DOWN.alt){
|
||||
gm->createConfigNode(SCREEN_MOUSE);
|
||||
} else {
|
||||
gm->createMidiNode(SCREEN_MOUSE);
|
||||
}
|
||||
gm->addConnection(std::dynamic_pointer_cast<Node>(connector->parent.lock()), gm->getLastNode());
|
||||
}
|
||||
connect_once = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while(APP::event_queue.size() > 0 && !exit){
|
||||
auto e = APP::event_queue.front();
|
||||
if(e.action == APP::Interaction::RIGHTCLICK){
|
||||
gm->addToSelection();
|
||||
state->context_menu = !state->context_menu;
|
||||
}
|
||||
if(state->context_menu) break;
|
||||
|
||||
if(e.action == APP::Interaction::HOVER){
|
||||
state->mouse_z = e.item->z_index;
|
||||
}
|
||||
|
||||
if(e.item->getType() == GraphItemType::CANVAS) {
|
||||
if(e.action == APP::Interaction::SELECT) {
|
||||
gm->select();
|
||||
canvas->clearSelectionRect();
|
||||
}
|
||||
if(e.action == APP::Interaction::DESELECT) {
|
||||
gm->deselect();
|
||||
}
|
||||
}
|
||||
|
||||
if(!once && e.item->getType() == GraphItemType::NODE){
|
||||
auto node = std::dynamic_pointer_cast<Node>(e.item);
|
||||
if(e.action == APP::Interaction::PICKUP) {
|
||||
node->pickUp();
|
||||
if(node->selected){
|
||||
for(auto s : gm->getSelection()){
|
||||
s->pickUp(node->position);
|
||||
}
|
||||
}
|
||||
once = true;
|
||||
}
|
||||
if(e.action == APP::Interaction::DRAG) {
|
||||
if(KEYS_DOWN.ctrl) node->drag(MOD_MOUSE);
|
||||
else if(KEYS_DOWN.shift) {
|
||||
snapPolar(node);
|
||||
}
|
||||
else node->drag(MOD_MOUSE + node->drag_offset);
|
||||
|
||||
if(node->selected){
|
||||
for(auto s : gm->getSelection()){
|
||||
s->drag(MOD_MOUSE + s->drag_offset);
|
||||
}
|
||||
}
|
||||
once = true;
|
||||
}
|
||||
if(e.action == APP::Interaction::PUTDOWN) {
|
||||
node->putDown();
|
||||
if(node->selected){
|
||||
for(auto s : gm->getSelection()){
|
||||
s->putDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(exit) break;
|
||||
|
||||
if(!once && (e.item->getType() == GraphItemType::CONNECTOR || e.item->getType() == GraphItemType::CANVAS || e.item->getType() == GraphItemType::HANDLE)) {
|
||||
switch(e.action){
|
||||
case VISEQ::APP::Interaction::PICKUP:
|
||||
{
|
||||
e.item->pickUp();
|
||||
once = true;
|
||||
break;
|
||||
}
|
||||
case VISEQ::APP::Interaction::DRAG:
|
||||
{
|
||||
e.item->drag(MOD_MOUSE);
|
||||
once = true;
|
||||
break;
|
||||
}
|
||||
case VISEQ::APP::Interaction::PUTDOWN:
|
||||
{
|
||||
e.item->putDown();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{}
|
||||
}
|
||||
}
|
||||
|
||||
APP::event_queue.pop_front();
|
||||
}
|
||||
APP::event_queue.clear();
|
||||
}
|
||||
}
|
||||
+6
-244
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "clipboard.h"
|
||||
#include "connector.h"
|
||||
#include "datatypes.h"
|
||||
#include "draggable.h"
|
||||
#include "graph-item.h"
|
||||
@@ -12,7 +11,6 @@
|
||||
#include "signal-manager.h"
|
||||
#include "state.h"
|
||||
#include "canvas.h"
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace VISEQ::INTERACTION{
|
||||
@@ -65,247 +63,11 @@ namespace VISEQ::INTERACTION{
|
||||
Vec2Df scale_start;
|
||||
std::shared_ptr<ScaleHandle> scale_handle = std::make_shared<ScaleHandle>();
|
||||
|
||||
void update(){
|
||||
if(KEYS_DOWN.ctrl && IsKeyPressed(KEY_C)){
|
||||
VEC_SPTR_Node selected_nodes = gm->getCopiesOfSelectedNodes();
|
||||
if(!selected_nodes.empty()) cb.copyNodes(selected_nodes);
|
||||
}
|
||||
|
||||
if(KEYS_DOWN.ctrl && IsKeyPressed(KEY_V)){
|
||||
VEC_SPTR_Node paste_nodes = cb.pasteNodes();
|
||||
if(!paste_nodes.empty()) gm->pasteNodesAndEdges(paste_nodes, MOD_MOUSE);
|
||||
}
|
||||
|
||||
if(IsKeyPressed(KEY_DELETE)){
|
||||
removeNodes(gm->getSelection());
|
||||
}
|
||||
|
||||
if(!KEYS_DOWN.shift && IsKeyPressed(KEY_TAB)) {
|
||||
state->node_mode = (state->node_mode + 1) % 7;
|
||||
}
|
||||
|
||||
if(KEYS_DOWN.shift && IsKeyPressed(KEY_TAB)) {
|
||||
state->node_mode -= state->node_mode > 0 ? 1 : -6;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void draw(){
|
||||
doInteraction();
|
||||
scaleSelection();
|
||||
if(!gm->getSelection().empty()){
|
||||
DrawRectangleRoundedLines(state->bounding_box, 0.1f, 3, VS_COLOR_LIGHTGREY);
|
||||
scale_handle->draw();
|
||||
}
|
||||
}
|
||||
|
||||
void removeNodes(VEC_SPTR_Node nodes){
|
||||
|
||||
VEC_WPTR_Node adjacent_nodes = gm->getAdjacentNodes(nodes);
|
||||
|
||||
sm->rerouteSignal(nodes, adjacent_nodes);
|
||||
sm->rebaseSignal(nodes);
|
||||
|
||||
gm->removeNodesAndEdges(nodes);
|
||||
}
|
||||
|
||||
void scaleSelection(){
|
||||
if(!gm->getSelection().empty()){
|
||||
float offset = 30;
|
||||
Vec2Df corner_tl = {50000, 50000};
|
||||
Vec2Df corner_br = {-50000, -50000};
|
||||
std::shared_ptr<Node> node_tl;
|
||||
std::shared_ptr<Node> node_br;
|
||||
|
||||
for(auto n : gm->getSelection()){
|
||||
float x = n->position.x;
|
||||
float y = n->position.y;
|
||||
|
||||
if(x < corner_tl.x && y < corner_tl.y) node_tl = n;
|
||||
if(x > corner_br.x && y > corner_br.y) node_br = n;
|
||||
|
||||
if(x <= corner_tl.x) corner_tl.x = x;
|
||||
if(x >= corner_br.x) corner_br.x = x;
|
||||
if(y <= corner_tl.y) corner_tl.y = y;
|
||||
if(y >= corner_br.y) corner_br.y = y;
|
||||
}
|
||||
corner_tl -= Vec2Df(offset, offset);
|
||||
corner_br += Vec2Df(offset, offset);
|
||||
state->bounding_box = (Rectangle){corner_tl.x, corner_tl.y, corner_br.x - corner_tl.x, corner_br.y - corner_tl.y};
|
||||
|
||||
scale_handle->update();
|
||||
|
||||
if(scale_handle->clicked()){
|
||||
scale_start = (corner_br - corner_tl);
|
||||
for(auto n : gm->getSelection()){
|
||||
n->drag_offset = n->position - node_tl->position;
|
||||
}
|
||||
}
|
||||
if(scale_handle->picked_up){
|
||||
Vec2Df scale = (scale_handle->position - corner_tl) / scale_start;
|
||||
if(IsKeyDown(KEY_LEFT_SHIFT)) scale.y = scale.x;
|
||||
DrawText(std::format("{:.2f} {:.2f}", scale.x, scale.y).c_str(), corner_br.x+5, corner_br.y+5, 10, RED);
|
||||
for(auto n : gm->getSelection()){
|
||||
n->position = node_tl->position + (n->drag_offset * scale);
|
||||
}
|
||||
} else {
|
||||
scale_handle->position = corner_br;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void snapPolar(std::shared_ptr<Node> dragNode){
|
||||
float steps = round((dragNode->neighbors.front()->position.dist(SCREEN_MOUSE)) / state->settings.grid) * state->settings.grid;
|
||||
if(dragNode->neighbors.size() > 1){
|
||||
Vec2Df dn1 = dragNode->neighbors[0]->position;
|
||||
Vec2Df dn2 = dragNode->neighbors[1]->position;
|
||||
Vec2Df v1_orth = dn1.center(dn2);
|
||||
|
||||
if(dn1.x < dn2.x) std::swap(dn1,dn2);
|
||||
|
||||
float dist = dn1.dist(dn2);
|
||||
float dist_orth = std::sqrt(std::pow(steps,2) - std::pow(dist/2,2));
|
||||
|
||||
if(SCREEN_MOUSE.y < v1_orth.y) dist_orth *= -1;
|
||||
|
||||
Vec2Df v1_orth_t = v1_orth + (dn1-dn2).norm().orth() * dist_orth;
|
||||
DrawLineV(dn1, dn2, VS_COLOR_RED);
|
||||
DrawLineV(v1_orth, v1_orth_t, VS_COLOR_RED);
|
||||
DrawCircleV(v1_orth, 3.0f, VS_COLOR_RED);
|
||||
dragNode->position = v1_orth_t;
|
||||
} else if(!dragNode->neighbors.empty()){
|
||||
float pol_r = steps;
|
||||
float pol_theta = Vector2Angle(Vec2Df{1,0},(SCREEN_MOUSE - dragNode->neighbors.front()->position));
|
||||
Vec2Df polar = VISEQ::VMATH::polarToCartesian(pol_r, pol_theta);
|
||||
dragNode->position = dragNode->neighbors.front()->position + polar;
|
||||
}
|
||||
}
|
||||
|
||||
void doInteraction(){
|
||||
bool exit = false;
|
||||
bool once = false;
|
||||
bool connect_once = false;
|
||||
|
||||
state->mouse_z = canvas->z_index;
|
||||
|
||||
for(auto e : APP::event_queue){
|
||||
if(!connect_once && e.action == APP::Interaction::CONNECT){
|
||||
APP::InteractionEvent partner;
|
||||
bool found = false;
|
||||
for(auto ei : APP::event_queue){
|
||||
if(ei.action == APP::Interaction::HOVER && ei.item->getType() == GraphItemType::CONNECTOR) {
|
||||
std::cout << "YEAH" << std::endl;
|
||||
partner = ei;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto connector = std::dynamic_pointer_cast<Connector>(e.item);
|
||||
if(found && partner != e){
|
||||
auto connector_to = std::dynamic_pointer_cast<Connector>(partner.item);
|
||||
connector->partner = connector_to;
|
||||
connector_to->partner = connector;
|
||||
gm->addConnection(std::dynamic_pointer_cast<Node>(connector->parent.lock()), std::dynamic_pointer_cast<Node>(connector_to->parent.lock()));
|
||||
} else if (partner != e){
|
||||
if(KEYS_DOWN.alt){
|
||||
gm->createConfigNode(SCREEN_MOUSE);
|
||||
} else {
|
||||
gm->createMidiNode(SCREEN_MOUSE);
|
||||
}
|
||||
gm->addConnection(std::dynamic_pointer_cast<Node>(connector->parent.lock()), gm->getLastNode());
|
||||
}
|
||||
connect_once = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while(APP::event_queue.size() > 0 && !exit){
|
||||
auto e = APP::event_queue.front();
|
||||
if(e.action == APP::Interaction::RIGHTCLICK){
|
||||
gm->addToSelection();
|
||||
state->context_menu = !state->context_menu;
|
||||
}
|
||||
if(state->context_menu) break;
|
||||
|
||||
if(e.action == APP::Interaction::HOVER){
|
||||
state->mouse_z = e.item->z_index;
|
||||
}
|
||||
|
||||
if(e.item->getType() == GraphItemType::CANVAS) {
|
||||
if(e.action == APP::Interaction::SELECT) {
|
||||
gm->select();
|
||||
canvas->clearSelectionRect();
|
||||
}
|
||||
if(e.action == APP::Interaction::DESELECT) {
|
||||
gm->deselect();
|
||||
}
|
||||
}
|
||||
|
||||
if(!once && e.item->getType() == GraphItemType::NODE){
|
||||
auto node = std::dynamic_pointer_cast<Node>(e.item);
|
||||
if(e.action == APP::Interaction::PICKUP) {
|
||||
node->pickUp();
|
||||
if(node->selected){
|
||||
for(auto s : gm->getSelection()){
|
||||
s->pickUp(node->position);
|
||||
}
|
||||
}
|
||||
once = true;
|
||||
}
|
||||
if(e.action == APP::Interaction::DRAG) {
|
||||
if(KEYS_DOWN.ctrl) node->drag(MOD_MOUSE);
|
||||
else if(KEYS_DOWN.shift) {
|
||||
snapPolar(node);
|
||||
}
|
||||
else node->drag(MOD_MOUSE + node->drag_offset);
|
||||
|
||||
if(node->selected){
|
||||
for(auto s : gm->getSelection()){
|
||||
s->drag(MOD_MOUSE + s->drag_offset);
|
||||
}
|
||||
}
|
||||
once = true;
|
||||
}
|
||||
if(e.action == APP::Interaction::PUTDOWN) {
|
||||
node->putDown();
|
||||
if(node->selected){
|
||||
for(auto s : gm->getSelection()){
|
||||
s->putDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(exit) break;
|
||||
|
||||
if(!once && (e.item->getType() == GraphItemType::CONNECTOR || e.item->getType() == GraphItemType::CANVAS || e.item->getType() == GraphItemType::HANDLE)) {
|
||||
switch(e.action){
|
||||
case VISEQ::APP::Interaction::PICKUP:
|
||||
{
|
||||
e.item->pickUp();
|
||||
once = true;
|
||||
break;
|
||||
}
|
||||
case VISEQ::APP::Interaction::DRAG:
|
||||
{
|
||||
e.item->drag(MOD_MOUSE);
|
||||
once = true;
|
||||
break;
|
||||
}
|
||||
case VISEQ::APP::Interaction::PUTDOWN:
|
||||
{
|
||||
e.item->putDown();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{}
|
||||
}
|
||||
}
|
||||
|
||||
APP::event_queue.pop_front();
|
||||
}
|
||||
APP::event_queue.clear();
|
||||
}
|
||||
|
||||
void update();
|
||||
void draw();
|
||||
void removeNodes(VEC_SPTR_Node nodes);
|
||||
void scaleSelection();
|
||||
void snapPolar(std::shared_ptr<Node> dragNode);
|
||||
void doInteraction();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
#include <cstdlib>
|
||||
#include <stdio.h>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,399 @@
|
||||
#include "midi-node.h"
|
||||
|
||||
#include "config-node.h"
|
||||
#include "datatypes.h"
|
||||
#include "edge.h"
|
||||
#include "node-handle.h"
|
||||
#include "node.h"
|
||||
#include "raylib.h"
|
||||
#include "state.h"
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
namespace VISEQ::GRAPH::NODES {
|
||||
|
||||
void MidiNode::init(){
|
||||
float orbit_1 = radius + 25.0f;
|
||||
float orbit_2 = orbit_1 + 20.0f;
|
||||
float orbit_3 = orbit_2 + 20.0f;
|
||||
float orbit_4 = orbit_3 + 20.0f;
|
||||
float orbit_5 = orbit_4 + 30.0f;
|
||||
|
||||
midiHandles[NOTE].emplace_back(std::make_shared<NodeHandle<int>>(midiNote.note, position, VS_COLOR_GREEN, "NOTE", orbit_5, 12));
|
||||
midiHandles[NOTE].emplace_back(std::make_shared<NodeHandle<int>>(midiNote.channel, position, VS_COLOR_PINK, "CHANNEL", orbit_1, 16, 1, 16, 40, 150));
|
||||
midiHandles[NOTE].emplace_back(std::make_shared<NodeHandle<int>>(midiNote.gate, position, VS_COLOR_RED, "GATE", orbit_3, 30, 10, 30000, 40, 300));
|
||||
midiHandles[NOTE].back()->logScale = true;
|
||||
midiHandles[NOTE].emplace_back(std::make_shared<NodeHandle<int>>(midiNote.velocity, position, VS_COLOR_PURPLE, "VELOCITY", orbit_4, 32));
|
||||
|
||||
midiHandles[CC].emplace_back(std::make_shared<NodeHandle<int>>(midiCC.control, position, VS_COLOR_SUN, "CONTROL", orbit_3, 32));
|
||||
midiHandles[CC].emplace_back(std::make_shared<NodeHandle<int>>(midiCC.channel, position, VS_COLOR_PINK, "CHANNEL", orbit_1, 1, 1, 16, 40, 150));
|
||||
midiHandles[CC].emplace_back(std::make_shared<NodeHandle<int>>(midiCC.value, position, VS_COLOR_GREEN, "VALUE", orbit_4, 32));
|
||||
|
||||
midiHandles[PC].emplace_back(std::make_shared<NodeHandle<int>>(midiPC.program, position, VS_COLOR_SUN, "PROGRAM", orbit_3, 32));
|
||||
midiHandles[PC].emplace_back(std::make_shared<NodeHandle<int>>(midiPC.channel, position, VS_COLOR_PINK, "CHANNEL", orbit_1, 1, 1, 16, 40, 150));
|
||||
midiHandles[PC].emplace_back(std::make_shared<NodeHandle<int>>(midiPC.value, position, VS_COLOR_GREEN, "VALUE", orbit_4, 32));
|
||||
|
||||
midiHandles[PB].emplace_back(std::make_shared<NodeHandle<int>>(midiPitchBend.channel, position, VS_COLOR_PINK, "CHANNEL", orbit_1, 1, 1, 16, 40, 150));
|
||||
midiHandles[PB].emplace_back(std::make_shared<NodeHandle<int>>(midiPitchBend.value, position, VS_COLOR_GREEN, "VALUE", orbit_3, 256, -8192, 8192, 40, 300));
|
||||
|
||||
midiHandles[AT].emplace_back(std::make_shared<NodeHandle<int>>(midiAftertouch.channel, position, VS_COLOR_PINK, "CHANNEL", orbit_1, 1, 1, 16, 40, 150));
|
||||
midiHandles[AT].emplace_back(std::make_shared<NodeHandle<int>>(midiAftertouch.value, position, VS_COLOR_GREEN, "VALUE", orbit_3, 32));
|
||||
|
||||
midiHandles[PP].emplace_back(std::make_shared<NodeHandle<int>>(midiPolyPressure.channel, position, VS_COLOR_PINK, "CHANNEL", orbit_1, 1, 1, 16, 40, 150));
|
||||
midiHandles[PP].emplace_back(std::make_shared<NodeHandle<int>>(midiPolyPressure.value, position, VS_COLOR_GREEN, "VALUE", orbit_4, 32));
|
||||
midiHandles[PP].emplace_back(std::make_shared<NodeHandle<int>>(midiPolyPressure.note, position, VS_COLOR_SUN, "NOTE", orbit_5, 12));
|
||||
}
|
||||
|
||||
std::shared_ptr<MidiNode> MidiNode::getPtr(){
|
||||
return std::static_pointer_cast<MidiNode>(shared_from_this());
|
||||
}
|
||||
|
||||
NodeType MidiNode::getNodeType() const {
|
||||
return nodeType;
|
||||
}
|
||||
|
||||
void MidiNode::setMidiPort(int midi_port, bool _state){
|
||||
midi_outs[midi_port].enabled = _state;
|
||||
}
|
||||
|
||||
void MidiNode::trigger() {
|
||||
triggered = true;
|
||||
start = GetTime();
|
||||
fade = getNotes().gate;
|
||||
}
|
||||
|
||||
void MidiNode::setMode(int _mode) {
|
||||
mode = _mode;
|
||||
}
|
||||
|
||||
MIDI::MidiNote MidiNode::getNotes() const {
|
||||
MIDI::MidiNote cNote;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cNote += cn.lock()->getNotes();
|
||||
}
|
||||
return midiNote + cNote;
|
||||
}
|
||||
|
||||
MIDI::MidiCC MidiNode::getCC() const {
|
||||
MIDI::MidiCC cCC;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cCC += cn.lock()->getCC();
|
||||
}
|
||||
return midiCC + cCC;
|
||||
}
|
||||
|
||||
MIDI::MidiPC MidiNode::getPC() const {
|
||||
MIDI::MidiPC cPC;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cPC += cn.lock()->getPC();
|
||||
}
|
||||
return midiPC + cPC;
|
||||
}
|
||||
|
||||
MIDI::MidiPitchBend MidiNode::getPitchBend() const {
|
||||
MIDI::MidiPitchBend cPitchBend;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cPitchBend += cn.lock()->getPitchBend();
|
||||
}
|
||||
return midiPitchBend + cPitchBend;
|
||||
}
|
||||
|
||||
MIDI::MidiAftertouch MidiNode::getAftertouch() const {
|
||||
MIDI::MidiAftertouch cAftertouch;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cAftertouch += cn.lock()->getAftertouch();
|
||||
}
|
||||
return midiAftertouch + cAftertouch;
|
||||
}
|
||||
|
||||
MIDI::MidiPolyPressure MidiNode::getPolyPressure() const {
|
||||
MIDI::MidiPolyPressure cPolyPressure;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cPolyPressure += cn.lock()->getPolyPressure();
|
||||
}
|
||||
return midiPolyPressure + cPolyPressure;
|
||||
}
|
||||
|
||||
std::vector<std::weak_ptr<ConfigNode>> MidiNode::getConnectedConfigNodes() const {
|
||||
auto config_conns = connections | RANGE_FILTER([](EDGES::SPTR_Edge c){ return c->start.lock()->getNodeType() == NodeType::CONFIGNODE || c->end.lock()->getNodeType() == NodeType::CONFIGNODE;}) | std::ranges::to<std::vector<std::shared_ptr<EDGES::Edge>>>();
|
||||
std::vector<std::weak_ptr<ConfigNode>> config_nodes;
|
||||
for(auto e : config_conns){
|
||||
if(e->start.lock()->getNodeType() == NodeType::CONFIGNODE) config_nodes.push_back(std::dynamic_pointer_cast<ConfigNode>(e->start.lock()));
|
||||
if(e->end.lock()->getNodeType() == NodeType::CONFIGNODE) config_nodes.push_back(std::dynamic_pointer_cast<ConfigNode>(e->end.lock()));
|
||||
}
|
||||
/*std::cout << "id: " << id << " n cn: " << config_nodes.size() << std::endl;*/
|
||||
return config_nodes;
|
||||
}
|
||||
|
||||
void MidiNode::update() {
|
||||
Node::update();
|
||||
mode = state->node_mode;
|
||||
for(auto mh : midiHandles[NOTE]){
|
||||
mh->update(position);
|
||||
}
|
||||
for(auto mh : midiHandles[CC]){
|
||||
mh->update(position);
|
||||
}
|
||||
for(auto mh : midiHandles[PC]){
|
||||
mh->update(position);
|
||||
}
|
||||
for(auto mh : midiHandles[PB]){
|
||||
mh->update(position);
|
||||
}
|
||||
for(auto mh : midiHandles[AT]){
|
||||
mh->update(position);
|
||||
}
|
||||
for(auto mh : midiHandles[PP]){
|
||||
mh->update(position);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiNode::modMidiNote(int incr){
|
||||
midiNote.note += incr;
|
||||
if(midiNote.note < 0 ) midiNote.note = VISEQ::MIDI::Notes.size()-1;
|
||||
if(midiNote.note >= (int)VISEQ::MIDI::Notes.size()) midiNote.note = 0;
|
||||
}
|
||||
|
||||
void MidiNode::modMidiCC(int incr){
|
||||
midiCC.control += incr;
|
||||
if(midiCC.control < 0 ) midiCC.control = 127;
|
||||
if(midiCC.control > 127 ) midiCC.control = 0;
|
||||
}
|
||||
|
||||
void MidiNode::modMidiPC(int incr){
|
||||
midiPC.program += incr;
|
||||
if(midiPC.program < 0 ) midiPC.program = 127;
|
||||
if(midiPC.program > 127 ) midiPC.program = 0;
|
||||
}
|
||||
|
||||
void MidiNode::modMidiPitchBend(int incr){
|
||||
midiPitchBend.value += incr;
|
||||
if(midiPitchBend.value < 0 ) midiPitchBend.value = 0;
|
||||
if(midiPitchBend.value > 16383 ) midiPitchBend.value = 16383;
|
||||
}
|
||||
|
||||
void MidiNode::modMidiAftertouch(int incr){
|
||||
midiAftertouch.value += incr;
|
||||
if(midiAftertouch.value < 0 ) midiAftertouch.value = 0;
|
||||
if(midiAftertouch.value > 127 ) midiAftertouch.value = 127;
|
||||
}
|
||||
|
||||
void MidiNode::modMidiPolyPressure(int incr){
|
||||
midiPolyPressure.value += incr;
|
||||
if(midiPolyPressure.value < 0 ) midiPolyPressure.value = 0;
|
||||
if(midiPolyPressure.value > 127 ) midiPolyPressure.value = 127;
|
||||
}
|
||||
|
||||
void MidiNode::interact(){
|
||||
float wheel = GetMouseWheelMove();
|
||||
|
||||
if(KEYS_DOWN.ctrl && !KEYS_DOWN.alt){
|
||||
switch(state->node_mode){
|
||||
case 0:{
|
||||
modMidiNote(wheel);
|
||||
break;}
|
||||
case 1:{
|
||||
modMidiCC(wheel);
|
||||
break;}
|
||||
case 2:{
|
||||
modMidiPC(wheel);
|
||||
break;}
|
||||
case 3:{
|
||||
modMidiPitchBend(wheel);
|
||||
break;}
|
||||
case 4:{
|
||||
modMidiAftertouch(wheel);
|
||||
break;}
|
||||
case 5:{
|
||||
modMidiPolyPressure(wheel);
|
||||
break;}
|
||||
};
|
||||
} else if(KEYS_DOWN.shift && !KEYS_DOWN.alt) {
|
||||
midiNote.velocity += wheel;
|
||||
if(midiNote.velocity < 0 ) midiNote.velocity = 127;
|
||||
if(midiNote.velocity > 127 ) midiNote.velocity = 0;
|
||||
} else if(KEYS_DOWN.alt) {
|
||||
switch(state->node_mode){
|
||||
case 0:{
|
||||
float incr = (float)wheel * (KEYS_DOWN.shift ? 100.0f : KEYS_DOWN.ctrl ? 1000.0f : 10.0f);
|
||||
midiNote.gate += incr;
|
||||
if(midiNote.gate < 0 ) midiNote.gate = 0;
|
||||
break;}
|
||||
case 1:{
|
||||
midiCC.value += wheel;
|
||||
if(midiCC.value < 0 ) midiCC.value = 0;
|
||||
if(midiCC.value > 127 ) midiCC.value = 127;
|
||||
break;}
|
||||
case 2:{
|
||||
midiPC.value += wheel;
|
||||
if(midiPC.value < 0 ) midiPC.value = 0;
|
||||
if(midiPC.value > 127 ) midiPC.value = 127;
|
||||
break;}
|
||||
case 3:{
|
||||
float incr = (float)wheel * (KEYS_DOWN.shift ? 100.0f : KEYS_DOWN.ctrl ? 1000.0f : 10.0f);
|
||||
midiPitchBend.value += incr;
|
||||
if(midiPitchBend.value < 0 ) midiPitchBend.value = 0;
|
||||
if(midiPitchBend.value > 16383 ) midiPitchBend.value = 16383;
|
||||
break;}
|
||||
case 4:{
|
||||
float incr = (float)wheel * (KEYS_DOWN.shift ? 100.0f : KEYS_DOWN.ctrl ? 1000.0f : 10.0f);
|
||||
midiAftertouch.value += incr;
|
||||
if(midiAftertouch.value < 0 ) midiAftertouch.value = 0;
|
||||
if(midiAftertouch.value > 127 ) midiAftertouch.value = 127;
|
||||
break;}
|
||||
case 5:{
|
||||
float incr = (float)wheel * (KEYS_DOWN.shift ? 100.0f : KEYS_DOWN.ctrl ? 1000.0f : 10.0f);
|
||||
midiPolyPressure.value += incr;
|
||||
if(midiPolyPressure.value < 0 ) midiPolyPressure.value = 0;
|
||||
if(midiPolyPressure.value > 127 ) midiPolyPressure.value = 127;
|
||||
break;}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void MidiNode::draw(Vec2Df mouse, Camera2D cam) {
|
||||
Node::draw();
|
||||
|
||||
for(int i = 0; i < 6; ++i){
|
||||
for(auto mh : midiHandles[(Handles)i]){
|
||||
mh->lock();
|
||||
}
|
||||
}
|
||||
|
||||
if(hovering() || selected) interact();
|
||||
|
||||
last = time;
|
||||
time = GetTime();
|
||||
if(time - start >= (float)getNotes().gate / 1000.0f){
|
||||
triggered = false;
|
||||
fade = 0;
|
||||
} else {
|
||||
fade -= (time - last)*1000.0f;
|
||||
}
|
||||
|
||||
DrawRing(position, radius, radius + 4, 0, 360, 24, (Color){60, 60, 60, 255});
|
||||
DrawRing(position, radius, radius + 4, 0.0f - 90.0f, (360.0f/127.0f) * getNotes().velocity - 90.0f, 24, (Color){158, 134, 200, 255});
|
||||
DrawCircleLinesV(position, radius + 4, (Color){100, 100, 100, 255});
|
||||
|
||||
float trigger_alpha = (1.f / getNotes().gate) * fade;
|
||||
|
||||
if(!picked_up && !selected && !in_selection_rect && !hovering()) DrawCircle(position.x, position.y, radius, ColorLerp(state->mode_color, VS_COLOR_RED, trigger_alpha));
|
||||
if(picked_up || in_selection_rect || hovering()) DrawCircle(position.x, position.y, radius, (Color){108, 153, 187, 255});
|
||||
if(selected) DrawCircle(position.x, position.y, radius, (Color){255, 153, 187, 255});
|
||||
|
||||
if(distributionMode == Distribution::RANDOM) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_PINK);
|
||||
if(distributionMode == Distribution::ROUNDROBIN) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_YELLOW);
|
||||
|
||||
if(selected) DrawCircleLinesV(position, radius+8, (Color){158, 255, 210, 255});
|
||||
bool modified = false;
|
||||
if(mode >= 0 && mode < 6){
|
||||
for(auto mh : midiHandles[(Handles)mode]){
|
||||
mh->unlock();
|
||||
mh->draw();
|
||||
std::string text = "";
|
||||
if(mh->hovering() || mh->picked_up){
|
||||
text = std::format("{}", mh->assignedValueRef);
|
||||
if(mh->name == "NOTE") text = MIDI::Notes[mh->assignedValueRef];
|
||||
if(mh->name == "GATE") text = std::format("{:.2f}", mh->assignedValueRef / 1000.0f);
|
||||
if(mh->name == "VALUE" && (Handles)mode == PB) text = std::format("{}", round(((float)mh->assignedValueRef / 8192.0f) * 100));
|
||||
DrawCenteredText(text, position, 12, WHITE);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!modified){
|
||||
switch((Handles)mode){
|
||||
case NOTE:{
|
||||
DrawCenteredText(MIDI::Notes[getNotes().note], position, 12, WHITE);
|
||||
break;}
|
||||
case CC:{
|
||||
DrawCenteredText(std::format("{}", getCC().value), position, 12, WHITE);
|
||||
break;}
|
||||
case PC:{
|
||||
DrawCenteredText(std::format("{}", getPC().value), position, 12, WHITE);
|
||||
break;}
|
||||
case PB:{
|
||||
DrawCenteredText(std::format("{}", round(((float)getPitchBend().value / 8192.0f) * 100)), position, 12, WHITE);
|
||||
break;}
|
||||
case AT:{
|
||||
DrawCenteredText(std::format("{}", getAftertouch().value), position, 12, WHITE);
|
||||
break;}
|
||||
case PP:{
|
||||
DrawCenteredText(std::format("{}", getPolyPressure().value), position, 12, WHITE);
|
||||
break;}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(highlighted) DrawCircleLinesV(position, radius+2, (Color){255, 0, 0, 200});
|
||||
}
|
||||
|
||||
void to_json(json& j, const MidiNode& N){
|
||||
std::vector<int> conns;
|
||||
std::ranges::transform(N.connections.begin(), N.connections.end(), std::back_inserter(conns), [](VISEQ::GRAPH::EDGES::SPTR_Edge e){ return e->id;});
|
||||
|
||||
std::vector<std::vector<Vec2Df>> handles;
|
||||
|
||||
for(auto i : N.midiHandles){
|
||||
handles.emplace_back();
|
||||
for(auto n : i.second){
|
||||
handles.at(i.first).push_back(n->position);
|
||||
}
|
||||
}
|
||||
|
||||
j = json{
|
||||
{"id", N.id},
|
||||
{"position", N.position},
|
||||
{"distributionMode", N.distributionMode},
|
||||
{"nodeType", N.nodeType},
|
||||
{"midiNote", N.midiNote},
|
||||
{"midiCC", N.midiCC},
|
||||
{"midiPC", N.midiPC},
|
||||
{"midiPitchBend", N.midiPitchBend},
|
||||
{"midiAftertouch", N.midiAftertouch},
|
||||
{"midiPolyPressure", N.midiPolyPressure},
|
||||
{"handles", handles},
|
||||
{"connections",conns}};
|
||||
|
||||
}
|
||||
|
||||
void from_json(const json& j, MidiNode& N){
|
||||
std::cout << "MidiNode" << std::endl;
|
||||
|
||||
j.at("id").get_to(N.id);
|
||||
j.at("position").get_to<Vec2Df>(N.position);
|
||||
j.at("distributionMode").get_to(N.distributionMode);
|
||||
j.at("nodeType").get_to(N.nodeType);
|
||||
j.at("midiNote").get_to(N.midiNote);
|
||||
j.at("midiCC").get_to(N.midiCC);
|
||||
j.at("midiPC").get_to(N.midiPC);
|
||||
j.at("midiPitchBend").get_to(N.midiPitchBend);
|
||||
j.at("midiAftertouch").get_to(N.midiAftertouch);
|
||||
j.at("midiPolyPressure").get_to(N.midiPolyPressure);
|
||||
|
||||
std::vector<std::vector<Vec2Df>> handles;
|
||||
|
||||
j.at("handles").get_to(handles);
|
||||
|
||||
int i = 0;
|
||||
for(auto n : handles){
|
||||
int j = 0;
|
||||
for(auto nn : n){
|
||||
N.midiHandles[(Handles)i][j]->position = nn;
|
||||
j++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void to_json(json& j, const std::shared_ptr<MidiNode>& N){
|
||||
j = *N;
|
||||
}
|
||||
|
||||
void from_json(const json& j, std::shared_ptr<MidiNode>& N){
|
||||
N.reset(new MidiNode(j.get<MidiNode>()));
|
||||
}
|
||||
|
||||
}
|
||||
+30
-441
@@ -2,11 +2,9 @@
|
||||
|
||||
#include "config-node.h"
|
||||
#include "datatypes.h"
|
||||
#include "edge.h"
|
||||
#include "node-handle.h"
|
||||
#include "node.h"
|
||||
#include "raylib.h"
|
||||
#include "state.h"
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
@@ -15,6 +13,8 @@ namespace VISEQ::APP::UI{
|
||||
}
|
||||
|
||||
namespace VISEQ::GRAPH::NODES {
|
||||
|
||||
enum Handles {NOTE = 0, CC, PC, PB, AT, PP};
|
||||
|
||||
class MidiNode : public Node{
|
||||
friend class APP::UI::AppUI;
|
||||
@@ -27,12 +27,7 @@ namespace VISEQ::GRAPH::NODES {
|
||||
MIDI::MidiAftertouch midiAftertouch;
|
||||
MIDI::MidiPolyPressure midiPolyPressure;
|
||||
|
||||
std::vector<std::shared_ptr<NodeHandle<int>>> midiNoteHandles;
|
||||
std::vector<std::shared_ptr<NodeHandle<int>>> midiCCHandles;
|
||||
std::vector<std::shared_ptr<NodeHandle<int>>> midiPCHandles;
|
||||
std::vector<std::shared_ptr<NodeHandle<int>>> midiPitchBendHandles;
|
||||
std::vector<std::shared_ptr<NodeHandle<int>>> midiAftertouchHandles;
|
||||
std::vector<std::shared_ptr<NodeHandle<int>>> midiPolyPressureHandles;
|
||||
std::map<Handles, std::vector<std::shared_ptr<NodeHandle<int>>>> midiHandles;
|
||||
|
||||
public:
|
||||
|
||||
@@ -68,439 +63,33 @@ namespace VISEQ::GRAPH::NODES {
|
||||
float time = 0;
|
||||
float last = 0;
|
||||
|
||||
void init(){
|
||||
midiNoteHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiNote.note, position, VS_COLOR_GREEN, "NOTE"));
|
||||
midiNoteHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiNote.channel, position, VS_COLOR_PINK, "CHANNEL", 1, 16, 40, 150));
|
||||
midiNoteHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiNote.gate, position, VS_COLOR_RED, "GATE", 10, 30000, 40, 300));
|
||||
midiNoteHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiNote.velocity, position, VS_COLOR_PURPLE, "VELOCITY"));
|
||||
|
||||
midiCCHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiCC.control, position, VS_COLOR_SUN, "CONTROL"));
|
||||
midiCCHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiCC.channel, position, VS_COLOR_PINK, "CHANNEL", 1, 16, 40, 150));
|
||||
midiCCHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiCC.value, position, VS_COLOR_GREEN, "VALUE"));
|
||||
|
||||
midiPCHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiPC.program, position, VS_COLOR_SUN, "PROGRAM"));
|
||||
midiPCHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiPC.channel, position, VS_COLOR_PINK, "CHANNEL", 1, 16, 40, 150));
|
||||
midiPCHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiPC.value, position, VS_COLOR_GREEN, "VALUE"));
|
||||
void init();
|
||||
std::shared_ptr<MidiNode> getPtr();
|
||||
virtual NodeType getNodeType() const override;
|
||||
void setMidiPort(int midi_port, bool _state);
|
||||
void trigger() override ;
|
||||
void setMode(int _mode) override ;
|
||||
MIDI::MidiNote getNotes() const ;
|
||||
MIDI::MidiCC getCC() const ;
|
||||
MIDI::MidiPC getPC() const ;
|
||||
MIDI::MidiPitchBend getPitchBend() const ;
|
||||
MIDI::MidiAftertouch getAftertouch() const ;
|
||||
MIDI::MidiPolyPressure getPolyPressure() const ;
|
||||
std::vector<std::weak_ptr<ConfigNode>> getConnectedConfigNodes() const ;
|
||||
void update() override ;
|
||||
void modMidiNote(int incr);
|
||||
void modMidiCC(int incr);
|
||||
void modMidiPC(int incr);
|
||||
void modMidiPitchBend(int incr);
|
||||
void modMidiAftertouch(int incr);
|
||||
void modMidiPolyPressure(int incr);
|
||||
void interact();
|
||||
void draw(Vec2Df mouse, Camera2D cam) override ;
|
||||
|
||||
midiPitchBendHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiPitchBend.channel, position, VS_COLOR_PINK, "CHANNEL", 1, 16, 40, 150));
|
||||
midiPitchBendHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiPitchBend.value, position, VS_COLOR_GREEN, "VALUE", -8192, 8192, 40, 300));
|
||||
|
||||
midiAftertouchHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiAftertouch.channel, position, VS_COLOR_PINK, "CHANNEL", 1, 16, 40, 150));
|
||||
midiAftertouchHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiAftertouch.value, position, VS_COLOR_GREEN, "VALUE"));
|
||||
|
||||
midiPolyPressureHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiPolyPressure.channel, position, VS_COLOR_PINK, "CHANNEL", 1, 16, 40, 150));
|
||||
midiPolyPressureHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiPolyPressure.value, position, VS_COLOR_GREEN, "VALUE"));
|
||||
midiPolyPressureHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiPolyPressure.note, position, VS_COLOR_SUN, "NOTE"));
|
||||
}
|
||||
|
||||
std::shared_ptr<MidiNode> getPtr(){
|
||||
return std::static_pointer_cast<MidiNode>(shared_from_this());
|
||||
}
|
||||
|
||||
virtual NodeType getNodeType() const override {return nodeType;}
|
||||
|
||||
void setMidiPort(int midi_port, bool _state){
|
||||
midi_outs[midi_port].enabled = _state;
|
||||
}
|
||||
|
||||
void trigger() override {
|
||||
triggered = true;
|
||||
start = GetTime();
|
||||
fade = getNotes().gate;
|
||||
}
|
||||
|
||||
void setMode(int _mode) override {
|
||||
mode = _mode;
|
||||
}
|
||||
|
||||
MIDI::MidiNote getNotes() const {
|
||||
MIDI::MidiNote cNote;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cNote += cn.lock()->getNotes();
|
||||
}
|
||||
return midiNote + cNote;
|
||||
}
|
||||
|
||||
MIDI::MidiCC getCC() const {
|
||||
MIDI::MidiCC cCC;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cCC += cn.lock()->getCC();
|
||||
}
|
||||
return midiCC + cCC;
|
||||
}
|
||||
|
||||
MIDI::MidiPC getPC() const {
|
||||
MIDI::MidiPC cPC;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cPC += cn.lock()->getPC();
|
||||
}
|
||||
return midiPC + cPC;
|
||||
}
|
||||
|
||||
MIDI::MidiPitchBend getPitchBend() const {
|
||||
MIDI::MidiPitchBend cPitchBend;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cPitchBend += cn.lock()->getPitchBend();
|
||||
}
|
||||
return midiPitchBend + cPitchBend;
|
||||
}
|
||||
|
||||
MIDI::MidiAftertouch getAftertouch() const {
|
||||
MIDI::MidiAftertouch cAftertouch;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cAftertouch += cn.lock()->getAftertouch();
|
||||
}
|
||||
return midiAftertouch + cAftertouch;
|
||||
}
|
||||
|
||||
MIDI::MidiPolyPressure getPolyPressure() const {
|
||||
MIDI::MidiPolyPressure cPolyPressure;
|
||||
for(auto cn : getConnectedConfigNodes()){
|
||||
cPolyPressure += cn.lock()->getPolyPressure();
|
||||
}
|
||||
return midiPolyPressure + cPolyPressure;
|
||||
}
|
||||
|
||||
std::vector<std::weak_ptr<ConfigNode>> getConnectedConfigNodes() const {
|
||||
auto config_conns = connections | RANGE_FILTER([](EDGES::SPTR_Edge c){ return c->start.lock()->getNodeType() == NodeType::CONFIGNODE || c->end.lock()->getNodeType() == NodeType::CONFIGNODE;}) | std::ranges::to<std::vector<std::shared_ptr<EDGES::Edge>>>();
|
||||
std::vector<std::weak_ptr<ConfigNode>> config_nodes;
|
||||
for(auto e : config_conns){
|
||||
if(e->start.lock()->getNodeType() == NodeType::CONFIGNODE) config_nodes.push_back(std::dynamic_pointer_cast<ConfigNode>(e->start.lock()));
|
||||
if(e->end.lock()->getNodeType() == NodeType::CONFIGNODE) config_nodes.push_back(std::dynamic_pointer_cast<ConfigNode>(e->end.lock()));
|
||||
}
|
||||
/*std::cout << "id: " << id << " n cn: " << config_nodes.size() << std::endl;*/
|
||||
return config_nodes;
|
||||
}
|
||||
|
||||
void update() override {
|
||||
Node::update();
|
||||
mode = state->node_mode;
|
||||
for(auto mh : midiNoteHandles){
|
||||
mh->update(position);
|
||||
}
|
||||
for(auto mh : midiCCHandles){
|
||||
mh->update(position);
|
||||
}
|
||||
for(auto mh : midiPCHandles){
|
||||
mh->update(position);
|
||||
}
|
||||
for(auto mh : midiPitchBendHandles){
|
||||
mh->update(position);
|
||||
}
|
||||
for(auto mh : midiAftertouchHandles){
|
||||
mh->update(position);
|
||||
}
|
||||
for(auto mh : midiPolyPressureHandles){
|
||||
mh->update(position);
|
||||
}
|
||||
}
|
||||
|
||||
void modMidiNote(int incr){
|
||||
midiNote.note += incr;
|
||||
if(midiNote.note < 0 ) midiNote.note = VISEQ::MIDI::Notes.size()-1;
|
||||
if(midiNote.note >= (int)VISEQ::MIDI::Notes.size()) midiNote.note = 0;
|
||||
}
|
||||
|
||||
void modMidiCC(int incr){
|
||||
midiCC.control += incr;
|
||||
if(midiCC.control < 0 ) midiCC.control = 127;
|
||||
if(midiCC.control > 127 ) midiCC.control = 0;
|
||||
}
|
||||
|
||||
void modMidiPC(int incr){
|
||||
midiPC.program += incr;
|
||||
if(midiPC.program < 0 ) midiPC.program = 127;
|
||||
if(midiPC.program > 127 ) midiPC.program = 0;
|
||||
}
|
||||
|
||||
void modMidiPitchBend(int incr){
|
||||
midiPitchBend.value += incr;
|
||||
if(midiPitchBend.value < 0 ) midiPitchBend.value = 0;
|
||||
if(midiPitchBend.value > 16383 ) midiPitchBend.value = 16383;
|
||||
}
|
||||
|
||||
void modMidiAftertouch(int incr){
|
||||
midiAftertouch.value += incr;
|
||||
if(midiAftertouch.value < 0 ) midiAftertouch.value = 0;
|
||||
if(midiAftertouch.value > 127 ) midiAftertouch.value = 127;
|
||||
}
|
||||
|
||||
void modMidiPolyPressure(int incr){
|
||||
midiPolyPressure.value += incr;
|
||||
if(midiPolyPressure.value < 0 ) midiPolyPressure.value = 0;
|
||||
if(midiPolyPressure.value > 127 ) midiPolyPressure.value = 127;
|
||||
}
|
||||
|
||||
void interact(){
|
||||
float wheel = GetMouseWheelMove();
|
||||
|
||||
if(KEYS_DOWN.ctrl && !KEYS_DOWN.alt){
|
||||
switch(state->node_mode){
|
||||
case 0:{
|
||||
modMidiNote(wheel);
|
||||
break;}
|
||||
case 1:{
|
||||
modMidiCC(wheel);
|
||||
break;}
|
||||
case 2:{
|
||||
modMidiPC(wheel);
|
||||
break;}
|
||||
case 3:{
|
||||
modMidiPitchBend(wheel);
|
||||
break;}
|
||||
case 4:{
|
||||
modMidiAftertouch(wheel);
|
||||
break;}
|
||||
case 5:{
|
||||
modMidiPolyPressure(wheel);
|
||||
break;}
|
||||
};
|
||||
} else if(KEYS_DOWN.shift && !KEYS_DOWN.alt) {
|
||||
midiNote.velocity += wheel;
|
||||
if(midiNote.velocity < 0 ) midiNote.velocity = 127;
|
||||
if(midiNote.velocity > 127 ) midiNote.velocity = 0;
|
||||
} else if(KEYS_DOWN.alt) {
|
||||
switch(state->node_mode){
|
||||
case 0:{
|
||||
float incr = (float)wheel * (KEYS_DOWN.shift ? 100.0f : KEYS_DOWN.ctrl ? 1000.0f : 10.0f);
|
||||
midiNote.gate += incr;
|
||||
if(midiNote.gate < 0 ) midiNote.gate = 0;
|
||||
break;}
|
||||
case 1:{
|
||||
midiCC.value += wheel;
|
||||
if(midiCC.value < 0 ) midiCC.value = 0;
|
||||
if(midiCC.value > 127 ) midiCC.value = 127;
|
||||
break;}
|
||||
case 2:{
|
||||
midiPC.value += wheel;
|
||||
if(midiPC.value < 0 ) midiPC.value = 0;
|
||||
if(midiPC.value > 127 ) midiPC.value = 127;
|
||||
break;}
|
||||
case 3:{
|
||||
float incr = (float)wheel * (KEYS_DOWN.shift ? 100.0f : KEYS_DOWN.ctrl ? 1000.0f : 10.0f);
|
||||
midiPitchBend.value += incr;
|
||||
if(midiPitchBend.value < 0 ) midiPitchBend.value = 0;
|
||||
if(midiPitchBend.value > 16383 ) midiPitchBend.value = 16383;
|
||||
break;}
|
||||
case 4:{
|
||||
float incr = (float)wheel * (KEYS_DOWN.shift ? 100.0f : KEYS_DOWN.ctrl ? 1000.0f : 10.0f);
|
||||
midiAftertouch.value += incr;
|
||||
if(midiAftertouch.value < 0 ) midiAftertouch.value = 0;
|
||||
if(midiAftertouch.value > 127 ) midiAftertouch.value = 127;
|
||||
break;}
|
||||
case 5:{
|
||||
float incr = (float)wheel * (KEYS_DOWN.shift ? 100.0f : KEYS_DOWN.ctrl ? 1000.0f : 10.0f);
|
||||
midiPolyPressure.value += incr;
|
||||
if(midiPolyPressure.value < 0 ) midiPolyPressure.value = 0;
|
||||
if(midiPolyPressure.value > 127 ) midiPolyPressure.value = 127;
|
||||
break;}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void draw(Vec2Df mouse, Camera2D cam) override {
|
||||
Node::draw();
|
||||
for(auto mh : midiNoteHandles){
|
||||
if(mode == 0) mh->unlock();
|
||||
else mh->lock();
|
||||
mh->draw();
|
||||
}
|
||||
for(auto mh : midiCCHandles){
|
||||
if(mode == 1) mh->unlock();
|
||||
else mh->lock();
|
||||
mh->draw();
|
||||
}
|
||||
for(auto mh : midiPCHandles){
|
||||
if(mode == 2) mh->unlock();
|
||||
else mh->lock();
|
||||
mh->draw();
|
||||
}
|
||||
for(auto mh : midiPitchBendHandles){
|
||||
if(mode == 3) mh->unlock();
|
||||
else mh->lock();
|
||||
mh->draw();
|
||||
}
|
||||
for(auto mh : midiAftertouchHandles){
|
||||
if(mode == 4) mh->unlock();
|
||||
else mh->lock();
|
||||
mh->draw();
|
||||
}
|
||||
for(auto mh : midiPolyPressureHandles){
|
||||
if(mode == 5) mh->unlock();
|
||||
else mh->lock();
|
||||
mh->draw();
|
||||
}
|
||||
|
||||
if(hovering() || selected) interact();
|
||||
|
||||
last = time;
|
||||
time = GetTime();
|
||||
if(time - start >= (float)getNotes().gate / 1000.0f){
|
||||
triggered = false;
|
||||
fade = 0;
|
||||
} else {
|
||||
fade -= (time - last)*1000.0f;
|
||||
}
|
||||
|
||||
DrawRing(position, radius, radius + 4, 0, 360, 24, (Color){60, 60, 60, 255});
|
||||
DrawRing(position, radius, radius + 4, 0.0f - 90.0f, (360.0f/127.0f) * getNotes().velocity - 90.0f, 24, (Color){158, 134, 200, 255});
|
||||
DrawCircleLinesV(position, radius + 4, (Color){100, 100, 100, 255});
|
||||
|
||||
float trigger_alpha = (1.f / getNotes().gate) * fade;
|
||||
|
||||
if(!picked_up && !selected && !in_selection_rect && !hovering()) DrawCircle(position.x, position.y, radius, ColorLerp(state->mode_color, VS_COLOR_RED, trigger_alpha));
|
||||
if(picked_up || in_selection_rect || hovering()) DrawCircle(position.x, position.y, radius, (Color){108, 153, 187, 255});
|
||||
if(selected) DrawCircle(position.x, position.y, radius, (Color){255, 153, 187, 255});
|
||||
|
||||
if(distributionMode == Distribution::RANDOM) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_PINK);
|
||||
if(distributionMode == Distribution::ROUNDROBIN) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_YELLOW);
|
||||
|
||||
if(selected) DrawCircleLinesV(position, radius+8, (Color){158, 255, 210, 255});
|
||||
|
||||
/*if(fade > 0) DrawCircle(position.x, position.y, radius, ColorAlpha(VS_COLOR_RED, trigger_alpha));*/
|
||||
|
||||
if(KEYS_DOWN.alt){
|
||||
float value = 0;
|
||||
int offset_gate = 0;
|
||||
switch(mode){
|
||||
case 0:
|
||||
value = (float)getNotes().gate/1000.0f;
|
||||
offset_gate = MeasureText(std::format("{:.2f}", value).c_str(), 12);
|
||||
DrawText(std::format("{:.2f}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, WHITE );
|
||||
break;
|
||||
case 1:
|
||||
value = (float)getCC().value;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, WHITE );
|
||||
break;
|
||||
case 2:
|
||||
value = (float)getPitchBend().value;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, WHITE );
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(mode){
|
||||
case 0:{
|
||||
int offset = MeasureText(MIDI::Notes[(int)getNotes().note].c_str(), 12);
|
||||
DrawText(MIDI::Notes[getNotes().note].c_str(), position.x-offset/2.0f, position.y-5, 12, WHITE );
|
||||
break;}
|
||||
case 1:{
|
||||
int offset = MeasureText(std::format("{}", getCC().control).c_str(), 12);
|
||||
DrawText(std::format("{}", getCC().control).c_str(), position.x-offset/2.0f, position.y-5, 12, WHITE );
|
||||
break;}
|
||||
case 2:{
|
||||
int offset = MeasureText(std::format("{}", getPitchBend().value).c_str(), 12);
|
||||
DrawText(std::format("{}", getPitchBend().value).c_str(), position.x-offset/2.0f, position.y-5, 12, WHITE );
|
||||
break;}
|
||||
}
|
||||
}
|
||||
|
||||
if(highlighted) DrawCircleLinesV(position, radius+2, (Color){255, 0, 0, 200});
|
||||
//DrawText(std::format("{}", id).c_str(), position.x + 20.0f, position.y + 20.0f, 10, WHITE);
|
||||
}
|
||||
|
||||
friend void to_json(json& j, const MidiNode& N){
|
||||
std::vector<int> conns;
|
||||
std::ranges::transform(N.connections.begin(), N.connections.end(), std::back_inserter(conns), [](VISEQ::GRAPH::EDGES::SPTR_Edge e){ return e->id;});
|
||||
std::map<std::string, Vec2Df> noteHandles;
|
||||
std::map<std::string, Vec2Df> CCHandles;
|
||||
std::map<std::string, Vec2Df> PCHandles;
|
||||
std::map<std::string, Vec2Df> pitchBendHandles;
|
||||
std::map<std::string, Vec2Df> aftertouchHandles;
|
||||
std::map<std::string, Vec2Df> polyPressureHandles;
|
||||
|
||||
for(auto i : N.midiNoteHandles){
|
||||
noteHandles.insert(std::make_pair(i->name, i->position));
|
||||
}
|
||||
for(auto i : N.midiCCHandles){
|
||||
CCHandles.insert(std::make_pair(i->name, i->position));
|
||||
}
|
||||
for(auto i : N.midiPCHandles){
|
||||
PCHandles.insert(std::make_pair(i->name, i->position));
|
||||
}
|
||||
for(auto i : N.midiPitchBendHandles){
|
||||
pitchBendHandles.insert(std::make_pair(i->name, i->position));
|
||||
}
|
||||
for(auto i : N.midiAftertouchHandles){
|
||||
pitchBendHandles.insert(std::make_pair(i->name, i->position));
|
||||
}
|
||||
for(auto i : N.midiPolyPressureHandles){
|
||||
polyPressureHandles.insert(std::make_pair(i->name, i->position));
|
||||
}
|
||||
|
||||
j = json{
|
||||
{"id", N.id},
|
||||
{"position", N.position},
|
||||
{"distributionMode", N.distributionMode},
|
||||
{"nodeType", N.nodeType},
|
||||
{"midiNote", N.midiNote},
|
||||
{"midiCC", N.midiCC},
|
||||
{"midiPC", N.midiPC},
|
||||
{"midiPitchBend", N.midiPitchBend},
|
||||
{"midiAftertouch", N.midiAftertouch},
|
||||
{"midiPolyPressure", N.midiPolyPressure},
|
||||
{"midiNoteHandle", noteHandles},
|
||||
{"midiCCHandle", CCHandles},
|
||||
{"midiPCHandle", PCHandles},
|
||||
{"midiPitchBendHandle", pitchBendHandles},
|
||||
{"midiAftertouchHandle", aftertouchHandles},
|
||||
{"midiPolyPressureHandle", polyPressureHandles},
|
||||
{"connections",conns}};
|
||||
|
||||
}
|
||||
|
||||
friend void from_json(const json& j, MidiNode& N){
|
||||
std::cout << "MidiNode" << std::endl;
|
||||
|
||||
j.at("id").get_to(N.id);
|
||||
j.at("position").get_to<Vec2Df>(N.position);
|
||||
j.at("distributionMode").get_to(N.distributionMode);
|
||||
j.at("nodeType").get_to(N.nodeType);
|
||||
j.at("midiNote").get_to(N.midiNote);
|
||||
j.at("midiCC").get_to(N.midiCC);
|
||||
j.at("midiPC").get_to(N.midiPC);
|
||||
j.at("midiPitchBend").get_to(N.midiPitchBend);
|
||||
j.at("midiAftertouch").get_to(N.midiAftertouch);
|
||||
j.at("midiPolyPressure").get_to(N.midiPolyPressure);
|
||||
|
||||
std::map<std::string, Vec2Df> noteHandles;
|
||||
std::map<std::string, Vec2Df> CCHandles;
|
||||
std::map<std::string, Vec2Df> PCHandles;
|
||||
std::map<std::string, Vec2Df> pitchBendHandles;
|
||||
std::map<std::string, Vec2Df> aftertouchHandles;
|
||||
std::map<std::string, Vec2Df> polyPressureHandles;
|
||||
|
||||
j.at("midiNoteHandle").get_to(noteHandles);
|
||||
j.at("midiCCHandle").get_to(CCHandles);
|
||||
j.at("midiPCHandle").get_to(PCHandles);
|
||||
j.at("midiPitchBendHandle").get_to(pitchBendHandles);
|
||||
j.at("midiAftertouchHandle").get_to(aftertouchHandles);
|
||||
j.at("midiPolyPressureHandle").get_to(polyPressureHandles);
|
||||
|
||||
N.midiNoteHandles[0]->position = noteHandles["NOTE"];
|
||||
N.midiNoteHandles[1]->position = noteHandles["CHANNEL"];
|
||||
N.midiNoteHandles[2]->position = noteHandles["GATE"];
|
||||
N.midiNoteHandles[3]->position = noteHandles["VELOCITY"];
|
||||
|
||||
N.midiCCHandles[0]->position = CCHandles["CONTROL"];
|
||||
N.midiCCHandles[1]->position = CCHandles["CHANNEL"];
|
||||
N.midiCCHandles[2]->position = CCHandles["VALUE"];
|
||||
|
||||
N.midiPCHandles[0]->position = PCHandles["PROGRAM"];
|
||||
N.midiPCHandles[1]->position = PCHandles["CHANNEL"];
|
||||
N.midiPCHandles[2]->position = PCHandles["VALUE"];
|
||||
|
||||
N.midiPitchBendHandles[0]->position = pitchBendHandles["CHANNEL"];
|
||||
N.midiPitchBendHandles[1]->position = pitchBendHandles["VALUE"];
|
||||
|
||||
N.midiAftertouchHandles[0]->position = aftertouchHandles["CHANNEL"];
|
||||
N.midiAftertouchHandles[1]->position = aftertouchHandles["VALUE"];
|
||||
|
||||
N.midiPolyPressureHandles[0]->position = polyPressureHandles["CHANNEL"];
|
||||
N.midiPolyPressureHandles[1]->position = polyPressureHandles["VALUE"];
|
||||
N.midiPolyPressureHandles[2]->position = polyPressureHandles["NOTE"];
|
||||
//j.at("connections").get_to(N.connections);
|
||||
}
|
||||
friend void to_json(json& j, const MidiNode& N);
|
||||
friend void from_json(const json& j, MidiNode& N);
|
||||
friend void to_json(json& j, const std::shared_ptr<MidiNode>& N);
|
||||
friend void from_json(const json& j, std::shared_ptr<MidiNode>& N);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "midi.h"
|
||||
|
||||
#include "libremidi/libremidi.hpp"
|
||||
#include "raylib.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include "datatypes.h"
|
||||
|
||||
namespace VISEQ::MIDI {
|
||||
|
||||
void Midi::enumerate(){
|
||||
std::cout << (int)obs.get_current_api() << std::endl;
|
||||
int i = 0;
|
||||
midi_out_ports.clear();
|
||||
for(const libremidi::output_port& port : obs.get_output_ports()){
|
||||
midi_out_ports.push_back({port, i, false});
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void Midi::open_midi_out(MidiOutPort *out_port){
|
||||
if(!midi_outs.contains(out_port->number)){
|
||||
auto out = std::make_shared<libremidi::midi_out>();
|
||||
out->open_port(out_port->port);
|
||||
midi_outs[out_port->number] = out;
|
||||
}
|
||||
}
|
||||
|
||||
void Midi::close_midi_out(MidiOutPort *out_port){
|
||||
if(midi_outs.contains(out_port->number)){
|
||||
midi_outs[out_port->number]->close_port();
|
||||
midi_outs.erase(out_port->number);
|
||||
}
|
||||
}
|
||||
|
||||
void Midi::note(int midi_out, int channel, int note, int velocity, int gate){
|
||||
if(midi_outs.contains(midi_out)){
|
||||
if(midi_outs[midi_out]->is_port_open()){
|
||||
midi_outs[midi_out]->send_message(libremidi::channel_events::note_on(channel, note, velocity));
|
||||
notes.emplace_back(midi_out, channel, note, velocity, GetTime(), gate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Midi::cc(int midi_out, int channel, int cc, int value){
|
||||
if(midi_outs.contains(midi_out)){
|
||||
if(midi_outs[midi_out]->is_port_open()){
|
||||
midi_outs[midi_out]->send_message(libremidi::channel_events::control_change(channel, cc, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Midi::pitch_bend(int midi_out, int channel, int pitch_value){
|
||||
if(midi_outs.contains(midi_out)){
|
||||
if(midi_outs[midi_out]->is_port_open()){
|
||||
midi_outs[midi_out]->send_message(libremidi::channel_events::pitch_bend(channel, pitch_value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Midi::update(){
|
||||
float time = GetTime();
|
||||
for(auto &n : notes){
|
||||
if(time - n.start >= (float)n.gate / 1000.0f){
|
||||
if(midi_outs.contains(n.midi_out) && midi_outs[n.midi_out]->is_port_open()) midi_outs[n.midi_out]->send_message(libremidi::channel_events::note_off(n.channel, n.note, n.velocity));
|
||||
n.erase = true;
|
||||
}
|
||||
}
|
||||
std::erase_if(notes, [](const auto& item){return item.erase;});
|
||||
|
||||
for(auto& p : midi_out_ports){
|
||||
if(p.enabled) {
|
||||
open_midi_out(&p);
|
||||
} else if(!p.enabled) {
|
||||
close_midi_out(&p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-71
@@ -1,11 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "libremidi/libremidi.hpp"
|
||||
#include "libremidi/message.hpp"
|
||||
#include "libremidi/observer_configuration.hpp"
|
||||
#include "raylib.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include "datatypes.h"
|
||||
@@ -39,74 +36,13 @@ namespace VISEQ::MIDI {
|
||||
|
||||
float timer;
|
||||
|
||||
void enumerate(){
|
||||
std::cout << (int)obs.get_current_api() << std::endl;
|
||||
int i = 0;
|
||||
midi_out_ports.clear();
|
||||
for(const libremidi::output_port& port : obs.get_output_ports()){
|
||||
midi_out_ports.push_back({port, i, false});
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void open_midi_out(MidiOutPort *out_port){
|
||||
if(!midi_outs.contains(out_port->number)){
|
||||
auto out = std::make_shared<libremidi::midi_out>();
|
||||
out->open_port(out_port->port);
|
||||
midi_outs[out_port->number] = out;
|
||||
}
|
||||
}
|
||||
|
||||
void close_midi_out(MidiOutPort *out_port){
|
||||
if(midi_outs.contains(out_port->number)){
|
||||
midi_outs[out_port->number]->close_port();
|
||||
midi_outs.erase(out_port->number);
|
||||
}
|
||||
}
|
||||
|
||||
void note(int midi_out, int channel, int note, int velocity, int gate){
|
||||
if(midi_outs.contains(midi_out)){
|
||||
if(midi_outs[midi_out]->is_port_open()){
|
||||
midi_outs[midi_out]->send_message(libremidi::channel_events::note_on(channel, note, velocity));
|
||||
notes.emplace_back(midi_out, channel, note, velocity, GetTime(), gate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cc(int midi_out, int channel, int cc, int value){
|
||||
if(midi_outs.contains(midi_out)){
|
||||
if(midi_outs[midi_out]->is_port_open()){
|
||||
midi_outs[midi_out]->send_message(libremidi::channel_events::control_change(channel, cc, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pitch_bend(int midi_out, int channel, int pitch_value){
|
||||
if(midi_outs.contains(midi_out)){
|
||||
if(midi_outs[midi_out]->is_port_open()){
|
||||
midi_outs[midi_out]->send_message(libremidi::channel_events::pitch_bend(channel, pitch_value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update(){
|
||||
float time = GetTime();
|
||||
for(auto &n : notes){
|
||||
if(time - n.start >= (float)n.gate / 1000.0f){
|
||||
if(midi_outs.contains(n.midi_out) && midi_outs[n.midi_out]->is_port_open()) midi_outs[n.midi_out]->send_message(libremidi::channel_events::note_off(n.channel, n.note, n.velocity));
|
||||
n.erase = true;
|
||||
}
|
||||
}
|
||||
std::erase_if(notes, [](const auto& item){return item.erase;});
|
||||
|
||||
for(auto& p : midi_out_ports){
|
||||
if(p.enabled) {
|
||||
open_midi_out(&p);
|
||||
} else if(!p.enabled) {
|
||||
close_midi_out(&p);
|
||||
}
|
||||
}
|
||||
}
|
||||
void enumerate();
|
||||
void open_midi_out(MidiOutPort *out_port);
|
||||
void close_midi_out(MidiOutPort *out_port);
|
||||
void note(int midi_out, int channel, int note, int velocity, int gate);
|
||||
void cc(int midi_out, int channel, int cc, int value);
|
||||
void pitch_bend(int midi_out, int channel, int pitch_value);
|
||||
void update();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
#include "modifier-node.h"
|
||||
|
||||
namespace VISEQ::GRAPH::NODES {
|
||||
std::shared_ptr<ModNode> ModNode::getPtr() {
|
||||
return std::static_pointer_cast<ModNode>(shared_from_this());
|
||||
}
|
||||
|
||||
NodeType ModNode::getNodeType() const {return nodeType;}
|
||||
|
||||
void ModNode::trigger() {}
|
||||
|
||||
void ModNode::setMode(int _mode) {
|
||||
mode = _mode;
|
||||
}
|
||||
|
||||
MIDI::MidiNote ModNode::getNotes() const {
|
||||
MIDI::MidiNote cNote;
|
||||
for(auto cn : getConnectedModNodes()){
|
||||
cNote += cn.lock()->getNotes();
|
||||
}
|
||||
return midiNote + cNote;
|
||||
}
|
||||
|
||||
MIDI::MidiCC ModNode::getCC() const {
|
||||
MIDI::MidiCC cCC;
|
||||
for(auto cn : getConnectedModNodes()){
|
||||
cCC += cn.lock()->getCC();
|
||||
}
|
||||
return midiCC + cCC;
|
||||
}
|
||||
|
||||
MIDI::MidiPC ModNode::getPC() const {
|
||||
MIDI::MidiPC cPC;
|
||||
for(auto cn : getConnectedModNodes()){
|
||||
cPC += cn.lock()->getPC();
|
||||
}
|
||||
return midiPC + cPC;
|
||||
}
|
||||
|
||||
MIDI::MidiPitchBend ModNode::getPitchBend() const {
|
||||
MIDI::MidiPitchBend cPitchBend;
|
||||
for(auto cn : getConnectedModNodes()){
|
||||
cPitchBend += cn.lock()->getPitchBend();
|
||||
}
|
||||
return midiPitchBend + cPitchBend;
|
||||
}
|
||||
|
||||
MIDI::MidiAftertouch ModNode::getAftertouch() const {
|
||||
MIDI::MidiAftertouch cAftertouch;
|
||||
for(auto cn : getConnectedModNodes()){
|
||||
cAftertouch += cn.lock()->getAftertouch();
|
||||
}
|
||||
return midiAftertouch + cAftertouch;
|
||||
}
|
||||
|
||||
MIDI::MidiPolyPressure ModNode::getPolyPressure() const {
|
||||
MIDI::MidiPolyPressure cPolyPressure;
|
||||
for(auto cn : getConnectedModNodes()){
|
||||
cPolyPressure += cn.lock()->getPolyPressure();
|
||||
}
|
||||
return midiPolyPressure + cPolyPressure;
|
||||
}
|
||||
|
||||
std::vector<std::weak_ptr<ModNode>> ModNode::getConnectedModNodes() const { // TODO: check for cyclic connections of config nodes (crashes)
|
||||
auto config_conns = connections | RANGE_FILTER([](EDGES::SPTR_Edge c){ return c->start.lock()->getNodeType() == NodeType::CONFIGNODE || c->end.lock()->getNodeType() == NodeType::CONFIGNODE;}) | std::ranges::to<std::vector<std::shared_ptr<EDGES::Edge>>>();
|
||||
std::vector<std::weak_ptr<ModNode>> config_nodes;
|
||||
for(auto e : config_conns){
|
||||
if(e->start.lock().get() != this && e->start.lock()->getNodeType() == NodeType::CONFIGNODE) config_nodes.push_back(std::dynamic_pointer_cast<ModNode>(e->start.lock()));
|
||||
//if(e->end.lock().get() != this && e->end.lock()->getNodeType() == NodeType::CONFIGNODE) config_nodes.push_back(std::dynamic_pointer_cast<ModNode>(e->end.lock()));
|
||||
}
|
||||
/*std::cout << "id: " << id << " n cn: " << config_nodes.size() << std::endl;*/
|
||||
return config_nodes;
|
||||
}
|
||||
|
||||
std::vector<std::weak_ptr<ModNode>> ModNode::getConnectedMidiNodes() const {
|
||||
auto config_conns = connections | RANGE_FILTER([](EDGES::SPTR_Edge c){ return c->start.lock()->getNodeType() == NodeType::MIDINODE || c->end.lock()->getNodeType() == NodeType::MIDINODE;}) | std::ranges::to<std::vector<std::shared_ptr<EDGES::Edge>>>();
|
||||
std::vector<std::weak_ptr<ModNode>> config_nodes;
|
||||
for(auto e : config_conns){
|
||||
if(e->start.lock().get() != this && e->start.lock()->getNodeType() == NodeType::MIDINODE) config_nodes.push_back(std::dynamic_pointer_cast<ModNode>(e->start.lock()));
|
||||
if(e->end.lock().get() != this && e->end.lock()->getNodeType() == NodeType::MIDINODE) config_nodes.push_back(std::dynamic_pointer_cast<ModNode>(e->end.lock()));
|
||||
}
|
||||
/*std::cout << "id: " << id << " n cn: " << config_nodes.size() << std::endl;*/
|
||||
return config_nodes;
|
||||
}
|
||||
|
||||
void ModNode::update() {
|
||||
Node::update();
|
||||
mode = state->node_mode;
|
||||
}
|
||||
|
||||
void ModNode::interact(){
|
||||
float wheel = GetMouseWheelMove();
|
||||
|
||||
if(KEYS_DOWN.ctrl && !KEYS_DOWN.alt){
|
||||
switch(state->node_mode){
|
||||
case 0:{
|
||||
midiNote.note += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 12 : wheel;
|
||||
break;}
|
||||
case 1:{
|
||||
midiCC.value += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 12 : wheel;
|
||||
break;}
|
||||
case 2:{
|
||||
midiPitchBend.value += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 12 : wheel;
|
||||
break;}
|
||||
};
|
||||
|
||||
if(midiNote.note < -127 ) midiNote.note = -127;
|
||||
if(midiNote.note > 127 ) midiNote.note = 127;
|
||||
|
||||
if(midiCC.value < -127 ) midiCC.value = -127;
|
||||
if(midiCC.value > 127 ) midiCC.value = 127;
|
||||
|
||||
if(midiPitchBend.value < -16383 ) midiPitchBend.value = -16383;
|
||||
if(midiPitchBend.value > 16383 ) midiPitchBend.value = 16383;
|
||||
|
||||
} else if(KEYS_DOWN.alt) {
|
||||
switch(state->node_mode){
|
||||
case 0:{
|
||||
midiNote.channel += wheel;
|
||||
if(midiNote.channel < 0 ) midiNote.channel = 0;
|
||||
if(midiNote.channel > 15) midiNote.channel = 15;
|
||||
break;}
|
||||
case 1:{
|
||||
midiCC.channel += wheel;
|
||||
if(midiCC.channel < 0 ) midiCC.channel = 0;
|
||||
if(midiCC.channel > 15 ) midiCC.channel = 15;
|
||||
break;}
|
||||
case 2:{
|
||||
midiPitchBend.channel += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 100 : wheel;
|
||||
if(midiPitchBend.channel < 0 ) midiPitchBend.channel = 0;
|
||||
if(midiPitchBend.channel > 15 ) midiPitchBend.channel = 15;
|
||||
break;}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void ModNode::draw(Vec2Df mouse, Camera2D cam) {
|
||||
Node::draw();
|
||||
if(hovering() || selected) interact();
|
||||
|
||||
DrawRing(position, radius, radius+4, 0, 360, 24, (Color){60, 60, 60, 255});
|
||||
DrawCircleLinesV(position, radius + 4, (Color){100, 100, 100, 255});
|
||||
|
||||
Color gate_color = VS_COLOR_GREEN;
|
||||
|
||||
switch(mode){
|
||||
case 0:{
|
||||
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){180, 210, 115 + 70, (unsigned char)Clamp(255, 0, 255)}, WHITE);
|
||||
break;}
|
||||
case 1:{
|
||||
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){229, 181, 103 + 70, (unsigned char)Clamp(255, 0, 255)}, WHITE);
|
||||
break;}
|
||||
case 2:{
|
||||
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){232, 125, 62 + 70, (unsigned char)Clamp(255, 0, 255)}, WHITE);
|
||||
break;}
|
||||
}
|
||||
if(!picked_up && !selected && !in_selection_rect && !hovering()) DrawCircle(position.x, position.y, radius, gate_color);
|
||||
if(picked_up || in_selection_rect || hovering()) DrawCircle(position.x, position.y, radius, (Color){108, 153, 187, 255});
|
||||
if(selected) DrawCircle(position.x, position.y, radius, (Color){255, 153, 187, 255});
|
||||
|
||||
if(distributionMode == Distribution::RANDOM) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_PINK);
|
||||
if(distributionMode == Distribution::ROUNDROBIN) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_YELLOW);
|
||||
|
||||
if(selected) DrawCircleLinesV(position, radius+8, (Color){158, 255, 210, 255});
|
||||
|
||||
if(KEYS_DOWN.alt){
|
||||
float value = 0;
|
||||
int offset_gate = 0;
|
||||
switch(mode){
|
||||
case 0:
|
||||
value = (float)getNotes().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 1:
|
||||
value = (float)getCC().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 2:
|
||||
value = (float)getPC().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 3:
|
||||
value = (float)getPitchBend().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 4:
|
||||
value = (float)getAftertouch().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 5:
|
||||
value = (float)getPolyPressure().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(mode){
|
||||
case 0:{
|
||||
int offset = MeasureText(std::format("{:+}", getNotes().note).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getNotes().note).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 1:{
|
||||
int offset = MeasureText(std::format("{:+}", getCC().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getCC().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 2:{
|
||||
int offset = MeasureText(std::format("{:+}", getPC().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getPC().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 3:{
|
||||
int offset = MeasureText(std::format("{:+}", getPitchBend().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getPitchBend().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 4:{
|
||||
int offset = MeasureText(std::format("{:+}", getAftertouch().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getAftertouch().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 5:{
|
||||
int offset = MeasureText(std::format("{:+}", getPolyPressure().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getPolyPressure().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
}
|
||||
}
|
||||
|
||||
if(highlighted) DrawCircleLinesV(position, radius+2, (Color){255, 0, 0, 200});
|
||||
//DrawText(std::format("{}", id).c_str(), position.x + 20.0f, position.y + 20.0f, 10, WHITE);
|
||||
}
|
||||
|
||||
void to_json(json& j, const ModNode& N){
|
||||
std::vector<int> conns;
|
||||
std::ranges::transform(N.connections.begin(), N.connections.end(), std::back_inserter(conns), [](VISEQ::GRAPH::EDGES::SPTR_Edge e){ return e->id;});
|
||||
|
||||
j = json{
|
||||
{"id", N.id},
|
||||
{"position", N.position},
|
||||
{"distributionMode", N.distributionMode},
|
||||
{"nodeType", N.nodeType},
|
||||
{"midi_note_channel",N.midiNote.channel},
|
||||
{"midi_note_note",N.midiNote.note},
|
||||
{"midi_note_velocity",N.midiNote.velocity},
|
||||
{"midi_note_gate",N.midiNote.gate},
|
||||
{"midi_cc_channel",N.midiCC.channel},
|
||||
{"midi_cc_control",N.midiCC.control},
|
||||
{"midi_cc_value",N.midiCC.value},
|
||||
{"midi_pc_channel",N.midiPC.channel},
|
||||
{"midi_pc_program",N.midiPC.program},
|
||||
{"midi_pc_value",N.midiPC.value},
|
||||
{"midi_pitch_bend_channel",N.midiPitchBend.channel},
|
||||
{"midi_pitch_bend_value",N.midiPitchBend.value},
|
||||
{"midi_aftertouch_channel",N.midiAftertouch.channel},
|
||||
{"midi_aftertouch_value",N.midiAftertouch.value},
|
||||
{"midi_polypressure_channel",N.midiPolyPressure.channel},
|
||||
{"midi_polypressure_value",N.midiPolyPressure.value},
|
||||
{"connections",conns}};
|
||||
}
|
||||
|
||||
void from_json(const json& j, ModNode& N){
|
||||
std::cout << "configNode from_json" << std::endl;
|
||||
|
||||
j.at("id").get_to(N.id);
|
||||
j.at("position").get_to<Vec2Df>(N.position);
|
||||
j.at("distributionMode").get_to(N.distributionMode);
|
||||
j.at("nodeType").get_to(N.nodeType);
|
||||
j.at("midi_note_channel").get_to(N.midiNote.channel);
|
||||
j.at("midi_note_note").get_to(N.midiNote.note);
|
||||
j.at("midi_note_velocity").get_to(N.midiNote.velocity);
|
||||
j.at("midi_note_gate").get_to(N.midiNote.gate);
|
||||
j.at("midi_cc_channel").get_to(N.midiCC.channel);
|
||||
j.at("midi_cc_control").get_to(N.midiCC.control);
|
||||
j.at("midi_cc_value").get_to(N.midiCC.value);
|
||||
j.at("midi_pc_channel").get_to(N.midiPC.channel);
|
||||
j.at("midi_pc_program").get_to(N.midiPC.program);
|
||||
j.at("midi_pc_value").get_to(N.midiPC.value);
|
||||
j.at("midi_pitch_bend_channel").get_to(N.midiPitchBend.channel);
|
||||
j.at("midi_pitch_bend_value").get_to(N.midiPitchBend.value);
|
||||
j.at("midi_aftertouch_channel").get_to(N.midiAftertouch.channel);
|
||||
j.at("midi_aftertouch_value").get_to(N.midiAftertouch.value);
|
||||
j.at("midi_polypressure_channel").get_to(N.midiPolyPressure.channel);
|
||||
j.at("midi_polypressure_value").get_to(N.midiPolyPressure.value);
|
||||
|
||||
//j.at("connections").get_to(N.connections);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-280
@@ -15,9 +15,7 @@ namespace VISEQ::GRAPH::NODES {
|
||||
ModNode() {std::cout << "ModNode Constructor std" << std::endl;}
|
||||
ModNode(Vec2Df position) : Node(position) {}
|
||||
|
||||
std::shared_ptr<ModNode> getPtr() {
|
||||
return std::static_pointer_cast<ModNode>(shared_from_this());
|
||||
}
|
||||
std::shared_ptr<ModNode> getPtr();
|
||||
|
||||
NodeType nodeType = NodeType::CONFIGNODE;
|
||||
|
||||
@@ -31,285 +29,24 @@ namespace VISEQ::GRAPH::NODES {
|
||||
int mode = 0;
|
||||
float radius = 20.0f;
|
||||
|
||||
virtual NodeType getNodeType() const override {return nodeType;}
|
||||
virtual NodeType getNodeType() const override;
|
||||
|
||||
virtual void trigger() override {}
|
||||
virtual void trigger() override;
|
||||
|
||||
virtual void setMode(int _mode) override {
|
||||
mode = _mode;
|
||||
}
|
||||
virtual void setMode(int _mode) override ;
|
||||
|
||||
MIDI::MidiNote getNotes() const {
|
||||
MIDI::MidiNote cNote;
|
||||
for(auto cn : getConnectedModNodes()){
|
||||
cNote += cn.lock()->getNotes();
|
||||
}
|
||||
return midiNote + cNote;
|
||||
}
|
||||
|
||||
MIDI::MidiCC getCC() const {
|
||||
MIDI::MidiCC cCC;
|
||||
for(auto cn : getConnectedModNodes()){
|
||||
cCC += cn.lock()->getCC();
|
||||
}
|
||||
return midiCC + cCC;
|
||||
}
|
||||
|
||||
MIDI::MidiPC getPC() const {
|
||||
MIDI::MidiPC cPC;
|
||||
for(auto cn : getConnectedModNodes()){
|
||||
cPC += cn.lock()->getPC();
|
||||
}
|
||||
return midiPC + cPC;
|
||||
}
|
||||
|
||||
MIDI::MidiPitchBend getPitchBend() const {
|
||||
MIDI::MidiPitchBend cPitchBend;
|
||||
for(auto cn : getConnectedModNodes()){
|
||||
cPitchBend += cn.lock()->getPitchBend();
|
||||
}
|
||||
return midiPitchBend + cPitchBend;
|
||||
}
|
||||
|
||||
MIDI::MidiAftertouch getAftertouch() const {
|
||||
MIDI::MidiAftertouch cAftertouch;
|
||||
for(auto cn : getConnectedModNodes()){
|
||||
cAftertouch += cn.lock()->getAftertouch();
|
||||
}
|
||||
return midiAftertouch + cAftertouch;
|
||||
}
|
||||
|
||||
MIDI::MidiPolyPressure getPolyPressure() const {
|
||||
MIDI::MidiPolyPressure cPolyPressure;
|
||||
for(auto cn : getConnectedModNodes()){
|
||||
cPolyPressure += cn.lock()->getPolyPressure();
|
||||
}
|
||||
return midiPolyPressure + cPolyPressure;
|
||||
}
|
||||
|
||||
std::vector<std::weak_ptr<ModNode>> getConnectedModNodes() const { // TODO: check for cyclic connections of config nodes (crashes)
|
||||
auto config_conns = connections | RANGE_FILTER([](EDGES::SPTR_Edge c){ return c->start.lock()->getNodeType() == NodeType::CONFIGNODE || c->end.lock()->getNodeType() == NodeType::CONFIGNODE;}) | std::ranges::to<std::vector<std::shared_ptr<EDGES::Edge>>>();
|
||||
std::vector<std::weak_ptr<ModNode>> config_nodes;
|
||||
for(auto e : config_conns){
|
||||
if(e->start.lock().get() != this && e->start.lock()->getNodeType() == NodeType::CONFIGNODE) config_nodes.push_back(std::dynamic_pointer_cast<ModNode>(e->start.lock()));
|
||||
//if(e->end.lock().get() != this && e->end.lock()->getNodeType() == NodeType::CONFIGNODE) config_nodes.push_back(std::dynamic_pointer_cast<ModNode>(e->end.lock()));
|
||||
}
|
||||
/*std::cout << "id: " << id << " n cn: " << config_nodes.size() << std::endl;*/
|
||||
return config_nodes;
|
||||
}
|
||||
|
||||
std::vector<std::weak_ptr<ModNode>> getConnectedMidiNodes() const {
|
||||
auto config_conns = connections | RANGE_FILTER([](EDGES::SPTR_Edge c){ return c->start.lock()->getNodeType() == NodeType::MIDINODE || c->end.lock()->getNodeType() == NodeType::MIDINODE;}) | std::ranges::to<std::vector<std::shared_ptr<EDGES::Edge>>>();
|
||||
std::vector<std::weak_ptr<ModNode>> config_nodes;
|
||||
for(auto e : config_conns){
|
||||
if(e->start.lock().get() != this && e->start.lock()->getNodeType() == NodeType::MIDINODE) config_nodes.push_back(std::dynamic_pointer_cast<ModNode>(e->start.lock()));
|
||||
if(e->end.lock().get() != this && e->end.lock()->getNodeType() == NodeType::MIDINODE) config_nodes.push_back(std::dynamic_pointer_cast<ModNode>(e->end.lock()));
|
||||
}
|
||||
/*std::cout << "id: " << id << " n cn: " << config_nodes.size() << std::endl;*/
|
||||
return config_nodes;
|
||||
}
|
||||
|
||||
void update() override {
|
||||
Node::update();
|
||||
mode = state->node_mode;
|
||||
}
|
||||
|
||||
void interact(){
|
||||
float wheel = GetMouseWheelMove();
|
||||
|
||||
if(KEYS_DOWN.ctrl && !KEYS_DOWN.alt){
|
||||
switch(state->node_mode){
|
||||
case 0:{
|
||||
midiNote.note += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 12 : wheel;
|
||||
break;}
|
||||
case 1:{
|
||||
midiCC.value += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 12 : wheel;
|
||||
break;}
|
||||
case 2:{
|
||||
midiPitchBend.value += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 12 : wheel;
|
||||
break;}
|
||||
};
|
||||
|
||||
if(midiNote.note < -127 ) midiNote.note = -127;
|
||||
if(midiNote.note > 127 ) midiNote.note = 127;
|
||||
|
||||
if(midiCC.value < -127 ) midiCC.value = -127;
|
||||
if(midiCC.value > 127 ) midiCC.value = 127;
|
||||
|
||||
if(midiPitchBend.value < -16383 ) midiPitchBend.value = -16383;
|
||||
if(midiPitchBend.value > 16383 ) midiPitchBend.value = 16383;
|
||||
|
||||
} else if(KEYS_DOWN.alt) {
|
||||
switch(state->node_mode){
|
||||
case 0:{
|
||||
midiNote.channel += wheel;
|
||||
if(midiNote.channel < 0 ) midiNote.channel = 0;
|
||||
if(midiNote.channel > 15) midiNote.channel = 15;
|
||||
break;}
|
||||
case 1:{
|
||||
midiCC.channel += wheel;
|
||||
if(midiCC.channel < 0 ) midiCC.channel = 0;
|
||||
if(midiCC.channel > 15 ) midiCC.channel = 15;
|
||||
break;}
|
||||
case 2:{
|
||||
midiPitchBend.channel += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 100 : wheel;
|
||||
if(midiPitchBend.channel < 0 ) midiPitchBend.channel = 0;
|
||||
if(midiPitchBend.channel > 15 ) midiPitchBend.channel = 15;
|
||||
break;}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void draw(Vec2Df mouse, Camera2D cam) override {
|
||||
Node::draw();
|
||||
if(hovering() || selected) interact();
|
||||
|
||||
DrawRing(position, radius, radius+4, 0, 360, 24, (Color){60, 60, 60, 255});
|
||||
DrawCircleLinesV(position, radius + 4, (Color){100, 100, 100, 255});
|
||||
|
||||
Color gate_color = VS_COLOR_GREEN;
|
||||
|
||||
switch(mode){
|
||||
case 0:{
|
||||
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){180, 210, 115 + 70, (unsigned char)Clamp(255, 0, 255)}, WHITE);
|
||||
break;}
|
||||
case 1:{
|
||||
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){229, 181, 103 + 70, (unsigned char)Clamp(255, 0, 255)}, WHITE);
|
||||
break;}
|
||||
case 2:{
|
||||
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){232, 125, 62 + 70, (unsigned char)Clamp(255, 0, 255)}, WHITE);
|
||||
break;}
|
||||
}
|
||||
if(!picked_up && !selected && !in_selection_rect && !hovering()) DrawCircle(position.x, position.y, radius, gate_color);
|
||||
if(picked_up || in_selection_rect || hovering()) DrawCircle(position.x, position.y, radius, (Color){108, 153, 187, 255});
|
||||
if(selected) DrawCircle(position.x, position.y, radius, (Color){255, 153, 187, 255});
|
||||
|
||||
if(distributionMode == Distribution::RANDOM) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_PINK);
|
||||
if(distributionMode == Distribution::ROUNDROBIN) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_YELLOW);
|
||||
|
||||
if(selected) DrawCircleLinesV(position, radius+8, (Color){158, 255, 210, 255});
|
||||
|
||||
if(KEYS_DOWN.alt){
|
||||
float value = 0;
|
||||
int offset_gate = 0;
|
||||
switch(mode){
|
||||
case 0:
|
||||
value = (float)getNotes().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 1:
|
||||
value = (float)getCC().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 2:
|
||||
value = (float)getPC().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 3:
|
||||
value = (float)getPitchBend().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 4:
|
||||
value = (float)getAftertouch().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
case 5:
|
||||
value = (float)getPolyPressure().channel;
|
||||
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
|
||||
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(mode){
|
||||
case 0:{
|
||||
int offset = MeasureText(std::format("{:+}", getNotes().note).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getNotes().note).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 1:{
|
||||
int offset = MeasureText(std::format("{:+}", getCC().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getCC().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 2:{
|
||||
int offset = MeasureText(std::format("{:+}", getPC().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getPC().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 3:{
|
||||
int offset = MeasureText(std::format("{:+}", getPitchBend().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getPitchBend().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 4:{
|
||||
int offset = MeasureText(std::format("{:+}", getAftertouch().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getAftertouch().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
case 5:{
|
||||
int offset = MeasureText(std::format("{:+}", getPolyPressure().value).c_str(), 12);
|
||||
DrawText(std::format("{:+}", getPolyPressure().value).c_str(), position.x-offset/2.0f, position.y-5, 12, VS_COLOR_BLACK );
|
||||
break;}
|
||||
}
|
||||
}
|
||||
|
||||
if(highlighted) DrawCircleLinesV(position, radius+2, (Color){255, 0, 0, 200});
|
||||
//DrawText(std::format("{}", id).c_str(), position.x + 20.0f, position.y + 20.0f, 10, WHITE);
|
||||
}
|
||||
|
||||
friend void to_json(json& j, const ModNode& N){
|
||||
std::vector<int> conns;
|
||||
std::ranges::transform(N.connections.begin(), N.connections.end(), std::back_inserter(conns), [](VISEQ::GRAPH::EDGES::SPTR_Edge e){ return e->id;});
|
||||
|
||||
j = json{
|
||||
{"id", N.id},
|
||||
{"position", N.position},
|
||||
{"distributionMode", N.distributionMode},
|
||||
{"nodeType", N.nodeType},
|
||||
{"midi_note_channel",N.midiNote.channel},
|
||||
{"midi_note_note",N.midiNote.note},
|
||||
{"midi_note_velocity",N.midiNote.velocity},
|
||||
{"midi_note_gate",N.midiNote.gate},
|
||||
{"midi_cc_channel",N.midiCC.channel},
|
||||
{"midi_cc_control",N.midiCC.control},
|
||||
{"midi_cc_value",N.midiCC.value},
|
||||
{"midi_pc_channel",N.midiPC.channel},
|
||||
{"midi_pc_program",N.midiPC.program},
|
||||
{"midi_pc_value",N.midiPC.value},
|
||||
{"midi_pitch_bend_channel",N.midiPitchBend.channel},
|
||||
{"midi_pitch_bend_value",N.midiPitchBend.value},
|
||||
{"midi_aftertouch_channel",N.midiAftertouch.channel},
|
||||
{"midi_aftertouch_value",N.midiAftertouch.value},
|
||||
{"midi_polypressure_channel",N.midiPolyPressure.channel},
|
||||
{"midi_polypressure_value",N.midiPolyPressure.value},
|
||||
{"connections",conns}};
|
||||
}
|
||||
|
||||
friend void from_json(const json& j, ModNode& N){
|
||||
std::cout << "configNode from_json" << std::endl;
|
||||
|
||||
j.at("id").get_to(N.id);
|
||||
j.at("position").get_to<Vec2Df>(N.position);
|
||||
j.at("distributionMode").get_to(N.distributionMode);
|
||||
j.at("nodeType").get_to(N.nodeType);
|
||||
j.at("midi_note_channel").get_to(N.midiNote.channel);
|
||||
j.at("midi_note_note").get_to(N.midiNote.note);
|
||||
j.at("midi_note_velocity").get_to(N.midiNote.velocity);
|
||||
j.at("midi_note_gate").get_to(N.midiNote.gate);
|
||||
j.at("midi_cc_channel").get_to(N.midiCC.channel);
|
||||
j.at("midi_cc_control").get_to(N.midiCC.control);
|
||||
j.at("midi_cc_value").get_to(N.midiCC.value);
|
||||
j.at("midi_pc_channel").get_to(N.midiPC.channel);
|
||||
j.at("midi_pc_program").get_to(N.midiPC.program);
|
||||
j.at("midi_pc_value").get_to(N.midiPC.value);
|
||||
j.at("midi_pitch_bend_channel").get_to(N.midiPitchBend.channel);
|
||||
j.at("midi_pitch_bend_value").get_to(N.midiPitchBend.value);
|
||||
j.at("midi_aftertouch_channel").get_to(N.midiAftertouch.channel);
|
||||
j.at("midi_aftertouch_value").get_to(N.midiAftertouch.value);
|
||||
j.at("midi_polypressure_channel").get_to(N.midiPolyPressure.channel);
|
||||
j.at("midi_polypressure_value").get_to(N.midiPolyPressure.value);
|
||||
|
||||
//j.at("connections").get_to(N.connections);
|
||||
}
|
||||
MIDI::MidiNote getNotes() const;
|
||||
MIDI::MidiCC getCC() const;
|
||||
MIDI::MidiPC getPC() const;
|
||||
MIDI::MidiPitchBend getPitchBend() const;
|
||||
MIDI::MidiAftertouch getAftertouch() const;
|
||||
MIDI::MidiPolyPressure getPolyPressure() const;
|
||||
std::vector<std::weak_ptr<ModNode>> getConnectedModNodes() const;
|
||||
std::vector<std::weak_ptr<ModNode>> getConnectedMidiNodes() const;
|
||||
void update() override;
|
||||
void interact();
|
||||
void draw(Vec2Df mouse, Camera2D cam) override;
|
||||
friend void to_json(json& j, const ModNode& N);
|
||||
friend void from_json(const json& j, ModNode& N);
|
||||
};
|
||||
}
|
||||
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "draggable.h"
|
||||
#include "graph-manager.h"
|
||||
#include "interaction-event.h"
|
||||
#include <memory>
|
||||
namespace VISEQ::APP {
|
||||
class Mouse {
|
||||
public:
|
||||
Mouse() {}
|
||||
Mouse(GraphManager *gm) : gm(gm) {}
|
||||
|
||||
GraphManager *gm;
|
||||
|
||||
void update(){
|
||||
/*auto hovering_nodes = gm->nodes | RANGE_FILTER([](SPTR_Node n) { return n->hovering() || n->picked_up;}) | std::ranges::to<std::vector<std::shared_ptr<BASE::Draggable>>>();*/
|
||||
/**/
|
||||
/*if(!hovering_nodes.empty()){*/
|
||||
/* hovering_nodes.front()->is_hovering = true;*/
|
||||
/**/
|
||||
/* if(hovering_nodes.front()->pickedUp()) hovering_nodes.front()->drag();*/
|
||||
/* if(hovering_nodes.front()->released()) hovering_nodes.front()->putDown();*/
|
||||
/**/
|
||||
/*}*/
|
||||
|
||||
}
|
||||
|
||||
void interact(InteractionEvent e){
|
||||
|
||||
}
|
||||
|
||||
/*virtual void select() override {*/
|
||||
/* if(STATE == IState::DRAW_SELECTION) in_selection_rect = CheckCollisionPointRec(position, state->selection);*/
|
||||
/* if(MOUSE_LEFT.released) selected = (KEYS_DOWN.shift && selected) || in_selection_rect;*/
|
||||
/*}*/
|
||||
/**/
|
||||
/*virtual void update() override {*/
|
||||
/* if(STATE != IState::HOVER_NODE) select();*/
|
||||
/* if((pickedUp() || hovering()) && z_hovering == -1) z_hovering = z_index;*/
|
||||
/**/
|
||||
/* if(z_index == z_hovering || selected){*/
|
||||
/* if(pickedUp()){*/
|
||||
/* drag();*/
|
||||
/* STATE = IState::DRAG_NODE;*/
|
||||
/* }*/
|
||||
/* }*/
|
||||
/* if(STATE != IState::DRAG_NODE){*/
|
||||
/* if(entered()){*/
|
||||
/* STATE = IState::HOVER_NODE;*/
|
||||
/* std::cout << "z_index=" << z_index << " z_hovering=" << z_hovering << std::endl;*/
|
||||
/* is_hovering = true;*/
|
||||
/* }*/
|
||||
/* if(exited()){*/
|
||||
/* STATE = IState::HOVER_CANVAS;*/
|
||||
/* is_hovering = false;*/
|
||||
/* }*/
|
||||
/* }*/
|
||||
/* if(released() && picked_up){*/
|
||||
/* putDown();*/
|
||||
/* STATE = IState::HOVER_CANVAS;*/
|
||||
/* if(hovering()) STATE = IState::HOVER_NODE;*/
|
||||
/* }*/
|
||||
/*}*/
|
||||
};
|
||||
}
|
||||
+68
-22
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "button.h"
|
||||
#include "datatypes.h"
|
||||
#include "draggable.h"
|
||||
#include "graph-item.h"
|
||||
@@ -16,8 +17,25 @@ namespace VISEQ::GRAPH::NODES{
|
||||
class NodeHandle : public BASE::Draggable, public GraphItem, public std::enable_shared_from_this<NodeHandle<T>>{
|
||||
public:
|
||||
NodeHandle() {}
|
||||
NodeHandle(T &_assignedValue, Vec2Df parentPos, Color _col, std::string name) : assignedValueRef(_assignedValue), parentPosition(parentPos), col(_col), name(name) {init();}
|
||||
NodeHandle(T &_assignedValue, Vec2Df parentPos, Color _col, std::string name, float min, float max, float minDist, float maxDist) : assignedValueRef(_assignedValue), parentPosition(parentPos), col(_col), name(name), minValue(min), maxValue(max), minDistance(minDist), maxDistance(maxDist) {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;
|
||||
@@ -37,9 +55,14 @@ namespace VISEQ::GRAPH::NODES{
|
||||
float minDistance = 40;
|
||||
float maxDistance = 40 + 2*127;
|
||||
bool stopAtMax = true;
|
||||
bool logScale = false;
|
||||
|
||||
float radius = 10.0f;
|
||||
float orbit = 0.0f;
|
||||
int steps = 1;
|
||||
float angle_g = 0;
|
||||
|
||||
float angle_start = 0;
|
||||
|
||||
Vec2Df minP;
|
||||
Vec2Df maxP;
|
||||
@@ -48,7 +71,7 @@ namespace VISEQ::GRAPH::NODES{
|
||||
bool visible = false;
|
||||
|
||||
void init(){
|
||||
position = parentPosition.orbitAtAngle(minDistance, DEG2RAD * GetRandomValue(0, 360)) - parentPosition;
|
||||
position = parentPosition.orbitAtAngle(orbit, 0) - parentPosition;
|
||||
}
|
||||
|
||||
void assignValue(T &value){
|
||||
@@ -60,16 +83,18 @@ namespace VISEQ::GRAPH::NODES{
|
||||
}
|
||||
|
||||
virtual void drag(Vec2Df mouse_pos) override {
|
||||
if(KEYS_DOWN.shift) {
|
||||
orbit = (parentPosition + position).dist(parentPosition);
|
||||
position = parentPosition.orbitAlongVector(SCREEN_MOUSE, orbit) - parentPosition;
|
||||
} else {
|
||||
float step = (maxDistance - minDistance) / (maxValue - minValue);
|
||||
orbit = round(parentPosition.dist(SCREEN_MOUSE) / step) * step;
|
||||
position = parentPosition.orbitAlongVector(maxP, orbit) - parentPosition;
|
||||
}
|
||||
if(distance() >= maxDistance) position = parentPosition.orbitAlongVector(maxP, maxDistance) - parentPosition;
|
||||
if(distance() <= minDistance) position = parentPosition.orbitAlongVector(maxP, minDistance) - parentPosition;
|
||||
float range = (maxValue - minValue) + 1;
|
||||
float stepping = 360.0f / range;
|
||||
|
||||
if(KEYS_DOWN.shift) stepping = stepping * steps;
|
||||
|
||||
float angle = Vector2Angle((SCREEN_MOUSE - parentPosition), Vector2(0,1)) * RAD2DEG - 180; // -180 because round(180 / 33.75) * 33.75 is not 0
|
||||
|
||||
if(KEYS_PRESSED.ctrl) angle_start = angle;
|
||||
if(KEYS_DOWN.ctrl) angle = angle_start + (angle - angle_start) / 10;
|
||||
|
||||
float stepped_angle = (round(angle / stepping) * stepping + 180) * DEG2RAD; // see above. -180 + 180 to get 0 right round(0 / 33.75) * 33.75 is still 0
|
||||
position = parentPosition.orbitAtAngle(orbit, stepped_angle) - parentPosition;
|
||||
}
|
||||
|
||||
void lock(){
|
||||
@@ -87,7 +112,17 @@ namespace VISEQ::GRAPH::NODES{
|
||||
}
|
||||
|
||||
void updateAssignedValue(){
|
||||
assignedValueRef = (T)Remap((float)(parentPosition + position).dist(parentPosition), minDistance, maxDistance, minValue, maxValue);
|
||||
float angle = 360 - (Vector2Angle(position, Vector2(0,1)) * RAD2DEG + 180);
|
||||
float range = (maxValue - minValue) + 1;
|
||||
float stepping = 360.0f / range;
|
||||
angle_g = angle;
|
||||
T value = (T)Remap(angle, 0, 359 - stepping, minValue, maxValue);
|
||||
if(logScale) {
|
||||
float exp = value / maxValue;
|
||||
float p = 2.2;
|
||||
value = (pow(exp, p) / pow(1.0f, p)) * maxValue;
|
||||
}
|
||||
assignedValueRef = value;
|
||||
}
|
||||
|
||||
virtual bool hovering() override {
|
||||
@@ -114,13 +149,27 @@ namespace VISEQ::GRAPH::NODES{
|
||||
bool show = hovering() || picked_up;
|
||||
Color dcol = ColorBrightness(col, -0.5f);
|
||||
Color dyncol = show ? col : dcol;
|
||||
Color dGrey = ColorBrightness(VS_COLOR_GREY, -0.2f);
|
||||
Color dynGrey = show ? VS_COLOR_GREY : dGrey;
|
||||
|
||||
DrawLineV(minP, maxP, dcol);
|
||||
//DrawLineV(parentPosition, minP, VS_COLOR_LIGHTGREY);
|
||||
DrawCircleV(minP, 3.0f, dyncol);
|
||||
DrawCircleV(maxP, 3.0f, dyncol);
|
||||
if(steps > 0){
|
||||
for(int i = 0; i < steps; i++){
|
||||
Vec2Df pos = parentPosition.orbitAtAngle(orbit, DEG2RAD * i * (360.0f/steps));
|
||||
Vec2Df pos1 = parentPosition.orbitAtAngle(orbit - 3.0f, DEG2RAD * i * (360.0f/steps));
|
||||
DrawLineV(pos, pos1, dynGrey);
|
||||
}
|
||||
}
|
||||
|
||||
float r = (parentPosition + position).dist(parentPosition);
|
||||
DrawRing(parentPosition, r - 5, r + 5, 0, 360, 90, ColorAlpha(dyncol, 0.1));
|
||||
DrawRing(parentPosition, r - 5, r + 5, -90, angle_g - 90, 90, ColorAlpha(dyncol, 0.3));
|
||||
DrawCircleV(position + parentPosition, radius, dyncol);
|
||||
if(show) VISEQ::HELPER::DrawSuperText(name, minP, maxP, 0.5f, 8.0f, 10.0f, dyncol);
|
||||
//VISEQ::HELPER::DrawSuperText(std::format("{}", assignedValueRef), position + parentPosition, position + parentPosition + Vec2Df(50,0), 1, 0, 8, dyncol);
|
||||
|
||||
/*if(show) {*/
|
||||
/* std::string text = std::format("{}", assignedValueRef);*/
|
||||
/* DrawCenteredText(text, parentPosition, 12, ColorBrightness(dyncol, 0.75f));*/
|
||||
/*}*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,9 +177,6 @@ namespace VISEQ::GRAPH::NODES{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*friend void to_json(json& j, const NodeHandle<T>& N){*/
|
||||
/* j = json{{"position",N.position}};*/
|
||||
/*}*/
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(NodeHandle, position, name);
|
||||
};
|
||||
}
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
#include "node.h"
|
||||
|
||||
#include <memory>
|
||||
#include <raylib.h>
|
||||
|
||||
#include "connector.h"
|
||||
#include "datatypes.h"
|
||||
#include "interaction-event.h"
|
||||
#include "state.h"
|
||||
#include "vmath.h"
|
||||
|
||||
#include "midi-node.h"
|
||||
#include "config-node.h"
|
||||
|
||||
namespace VISEQ::GRAPH::NODES {
|
||||
void Node::setMode(int _mode) {mode = _mode;}
|
||||
|
||||
std::shared_ptr<Node> Node::getPtr(){
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
bool Node::hovering() {
|
||||
return CheckCollisionPointCircle(SCREEN_MOUSE, position, radius);
|
||||
}
|
||||
|
||||
void Node::addConnector() {
|
||||
connectors.emplace_back(std::make_shared<Connector>(getPtr(), position, radius + 5.0f));
|
||||
}
|
||||
|
||||
void Node::removeConnector(std::weak_ptr<Connector> connector){
|
||||
std::erase(connectors, connector.lock());
|
||||
}
|
||||
|
||||
NodeType Node::getNodeType() const {return nodeType;}
|
||||
|
||||
void Node::deselect() {
|
||||
selected = false;
|
||||
}
|
||||
|
||||
GraphItemType Node::getType() {
|
||||
return GraphItemType::NODE;
|
||||
}
|
||||
|
||||
void Node::clearConnectors(){
|
||||
connectors.clear();
|
||||
update();
|
||||
}
|
||||
|
||||
void Node::update() {
|
||||
if(hovering()) state->pushEvent({getPtr(), APP::Interaction::HOVER});
|
||||
if(hovering() && clicked()) state->pushEvent({getPtr(), APP::Interaction::PICKUP});
|
||||
if(pickedUp()) state->pushEvent({getPtr(), APP::Interaction::DRAG});
|
||||
if(picked_up && released()) state->pushEvent({getPtr(), APP::Interaction::PUTDOWN});
|
||||
if(hovering() && MOUSE_RIGHT.clicked) {
|
||||
selected = true;
|
||||
config = true;
|
||||
state->pushEvent({getPtr(), APP::Interaction::RIGHTCLICK});
|
||||
}
|
||||
int free_connectors = 0;
|
||||
for(auto c : connectors){
|
||||
if(c->partner.expired()) free_connectors++;
|
||||
c->setPosition(position);
|
||||
c->setOrbit(radius + 8.0f);
|
||||
c->update();
|
||||
}
|
||||
if(free_connectors == 0) addConnector();
|
||||
}
|
||||
|
||||
void Node::draw(Vec2Df mp, Camera2D cam) {}
|
||||
|
||||
void Node::draw() {
|
||||
for(auto c : connectors){
|
||||
c->draw();
|
||||
}
|
||||
}
|
||||
|
||||
void to_json(json& j, const Node& N){
|
||||
if(N.getNodeType() == NodeType::MIDINODE){
|
||||
j = dynamic_cast<const MidiNode&>(N);
|
||||
}
|
||||
if(N.getNodeType() == NodeType::CONFIGNODE){
|
||||
j = dynamic_cast<const ConfigNode&>(N);
|
||||
}
|
||||
}
|
||||
|
||||
void from_json(const json& j, Node& N){
|
||||
|
||||
if(j.at("nodeType") == NodeType::MIDINODE){
|
||||
N = j.template get<MidiNode>();
|
||||
}
|
||||
if(j.at("nodeType") == NodeType::CONFIGNODE){
|
||||
N = j.template get<ConfigNode>();
|
||||
}
|
||||
}
|
||||
|
||||
void to_json(json& j, const SPTR_Node& N){
|
||||
j = *N;
|
||||
}
|
||||
|
||||
void from_json(const json& j, std::shared_ptr<Node>& N){
|
||||
std::cout << "SPTR_Node" << std::endl;
|
||||
if(N->getNodeType() == NodeType::MIDINODE){
|
||||
N.reset(new MidiNode(j.get<MidiNode>()));
|
||||
}
|
||||
if(N->getNodeType() == NodeType::CONFIGNODE){
|
||||
N.reset(new ConfigNode(j.get<ConfigNode>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
-63
@@ -7,8 +7,6 @@
|
||||
#include "datatypes.h"
|
||||
#include "draggable.h"
|
||||
#include "graph-item.h"
|
||||
#include "interaction-event.h"
|
||||
#include "state.h"
|
||||
#include "vmath.h"
|
||||
|
||||
namespace VISEQ::GRAPH::EDGES{
|
||||
@@ -20,8 +18,7 @@ namespace VISEQ::GRAPH::NODES {
|
||||
class Node : public BASE::Draggable, public GraphItem, public std::enable_shared_from_this<Node> {
|
||||
|
||||
public:
|
||||
|
||||
Node() {std::cout << "Node constructor" << std::endl;}
|
||||
Node() {}
|
||||
Node(float x, float y) : Draggable(Vec2Df(x, y)) {}
|
||||
Node(Vec2Df p) : Draggable(p) {}
|
||||
|
||||
@@ -43,66 +40,23 @@ namespace VISEQ::GRAPH::NODES {
|
||||
int mode = 0;
|
||||
|
||||
virtual void trigger() {}
|
||||
virtual void setMode(int _mode) {mode = _mode;}
|
||||
virtual void setMode(int _mode);
|
||||
std::shared_ptr<Node> getPtr();
|
||||
virtual bool hovering() override;
|
||||
void addConnector();
|
||||
void removeConnector(std::weak_ptr<Connector> connector);
|
||||
virtual NodeType getNodeType() const;
|
||||
void deselect() override;
|
||||
GraphItemType getType() override;
|
||||
void clearConnectors();
|
||||
void update() override;
|
||||
void draw(Vec2Df mp, Camera2D cam) override;
|
||||
void draw() override;
|
||||
|
||||
std::shared_ptr<Node> getPtr(){
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
virtual bool hovering() override {
|
||||
return CheckCollisionPointCircle(SCREEN_MOUSE, position, radius);
|
||||
}
|
||||
|
||||
void addConnector() {
|
||||
connectors.emplace_back(std::make_shared<Connector>(getPtr(), position, radius + 5.0f));
|
||||
}
|
||||
|
||||
void removeConnector(std::weak_ptr<Connector> connector){
|
||||
std::erase(connectors, connector.lock());
|
||||
}
|
||||
|
||||
virtual NodeType getNodeType() const {return nodeType;}
|
||||
|
||||
void deselect() override {
|
||||
selected = false;
|
||||
}
|
||||
|
||||
GraphItemType getType() override{
|
||||
return GraphItemType::NODE;
|
||||
}
|
||||
|
||||
void clearConnectors(){
|
||||
connectors.clear();
|
||||
update();
|
||||
}
|
||||
|
||||
void update() override{
|
||||
if(hovering()) state->pushEvent({getPtr(), APP::Interaction::HOVER});
|
||||
if(hovering() && clicked()) state->pushEvent({getPtr(), APP::Interaction::PICKUP});
|
||||
if(pickedUp()) state->pushEvent({getPtr(), APP::Interaction::DRAG});
|
||||
if(picked_up && released()) state->pushEvent({getPtr(), APP::Interaction::PUTDOWN});
|
||||
if(hovering() && MOUSE_RIGHT.clicked) {
|
||||
selected = true;
|
||||
config = true;
|
||||
state->pushEvent({getPtr(), APP::Interaction::RIGHTCLICK});
|
||||
}
|
||||
int free_connectors = 0;
|
||||
for(auto c : connectors){
|
||||
if(c->partner.expired()) free_connectors++;
|
||||
c->setPosition(position);
|
||||
c->setOrbit(radius + 8.0f);
|
||||
c->update();
|
||||
}
|
||||
if(free_connectors == 0) addConnector();
|
||||
}
|
||||
|
||||
void draw(Vec2Df mp, Camera2D cam) override {}
|
||||
|
||||
void draw() override{
|
||||
for(auto c : connectors){
|
||||
c->draw();
|
||||
}
|
||||
}
|
||||
friend void to_json(json& j, const Node& N);
|
||||
friend void from_json(const json& j, Node& N);
|
||||
friend void to_json(json& j, const std::shared_ptr<Node>& N);
|
||||
friend void from_json(const json& j, std::shared_ptr<Node>& N);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
#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>
|
||||
|
||||
namespace VISEQ::SIGNAL {
|
||||
|
||||
void SignalManager::pause(){
|
||||
play = false;
|
||||
}
|
||||
|
||||
void SignalManager::stop(){
|
||||
play = false;
|
||||
reset();
|
||||
}
|
||||
|
||||
void SignalManager::start(){
|
||||
play = true;
|
||||
}
|
||||
|
||||
void 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 SignalManager::startSignalThread(){
|
||||
std::thread t1(&SignalManager::updateSignals, this);
|
||||
t1.detach();
|
||||
}
|
||||
|
||||
void SignalManager::stopSignalThread(){
|
||||
loop = false;
|
||||
}
|
||||
|
||||
void 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 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 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 SignalManager::removeSignal(std::shared_ptr<Signal> s){
|
||||
erase_signals.push_back(s);
|
||||
}
|
||||
|
||||
void 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 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();
|
||||
}
|
||||
|
||||
void to_json(json& j, const std::shared_ptr<SignalManager>& G){
|
||||
j = *G;
|
||||
}
|
||||
|
||||
void from_json(const json& j, std::shared_ptr<SignalManager>& G){
|
||||
G.reset(new SignalManager(j.get<SignalManager>()));
|
||||
}
|
||||
}
|
||||
+16
-123
@@ -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 <chrono>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
using namespace VISEQ::GRAPH;
|
||||
|
||||
@@ -40,126 +36,23 @@ 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<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();
|
||||
}
|
||||
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<Signal> s);
|
||||
void updateSignals();
|
||||
void drawSignals(Vec2Df mouse_pos, Camera2D camera);
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(SignalManager, signals)
|
||||
|
||||
|
||||
friend void from_json(const json& j, std::shared_ptr<SignalManager>& G);
|
||||
friend void to_json(json& j, const std::shared_ptr<SignalManager>& G);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "signal.h"
|
||||
|
||||
#include <raylib.h>
|
||||
#include "node.h"
|
||||
|
||||
namespace VISEQ::SIGNAL {
|
||||
void Signal::reset(){
|
||||
toStart();
|
||||
}
|
||||
|
||||
void Signal::toStart(){
|
||||
position = start.lock()->position;
|
||||
}
|
||||
|
||||
void Signal::update(double t_delta){
|
||||
// 120px / 16th = 1920px / whole note
|
||||
// whole note = 2s at 120bpm
|
||||
// 960px / s
|
||||
if(run) position += direction.norm() * speed * multiplier * (float)t_delta / 1000000.0f;
|
||||
}
|
||||
|
||||
void Signal::draw(Vec2Df mouse, Camera2D cam) {
|
||||
DrawCircle(position.x, position.y, radius, color);
|
||||
}
|
||||
|
||||
void to_json(json& j, const Signal& S){
|
||||
std::map<std::string, int> fc = {{"r",S.color.r},{"g",S.color.g},{"b",S.color.b},{"a",S.color.a}};
|
||||
j = json{{"color",fc},{"position", S.position},{"radius",S.radius},{"start",S.start.lock()->id},{"next",S.next.lock()->id},{"last",S.last.lock()->id},{"speed",S.speed},{"midi_note_offset",S.midi_note_offset},{"midi_cc_offset",S.midi_cc_offset},{"multiplier",S.multiplier}};
|
||||
}
|
||||
|
||||
void from_json(const json& j, Signal& S){
|
||||
|
||||
Color c;
|
||||
c.r = j.at("color")["r"].get<int>();
|
||||
c.g = j.at("color")["g"].get<int>();
|
||||
c.b = j.at("color")["b"].get<int>();
|
||||
c.a = j.at("color")["a"].get<int>();
|
||||
|
||||
j.at("position").get_to<Vec2Df>(S.position);
|
||||
j.at("speed").get_to<float>(S.speed);
|
||||
j.at("radius").get_to<float>(S.radius);
|
||||
j.at("midi_note_offset").get_to(S.midi_note_offset);
|
||||
j.at("midi_cc_offset").get_to(S.midi_cc_offset);
|
||||
|
||||
S.color = c;
|
||||
}
|
||||
|
||||
void to_json(json& j, const std::shared_ptr<Signal>& S){
|
||||
j = *S;
|
||||
}
|
||||
|
||||
void from_json(const json& j, std::shared_ptr<Signal>& S){
|
||||
S.reset(new Signal(j.get<Signal>()));
|
||||
}
|
||||
}
|
||||
+8
-16
@@ -35,24 +35,16 @@ namespace VISEQ::SIGNAL {
|
||||
int midi_note_offset = 0;
|
||||
int midi_cc_offset = 0;
|
||||
|
||||
void reset(){
|
||||
toStart();
|
||||
}
|
||||
void reset();
|
||||
void toStart();
|
||||
void update(double t_delta);
|
||||
void draw(Vec2Df mouse, Camera2D cam);
|
||||
|
||||
void toStart(){
|
||||
position = start.lock()->position;
|
||||
}
|
||||
friend void to_json(json& j, const Signal& S);
|
||||
friend void from_json(const json& j, Signal& S);
|
||||
friend void to_json(json& j, const std::shared_ptr<Signal>& S);
|
||||
friend void from_json(const json& j, std::shared_ptr<Signal>& S);
|
||||
|
||||
void update(double t_delta){
|
||||
// 120px / 16th = 1920px / whole note
|
||||
// whole note = 2s at 120bpm
|
||||
// 960px / s
|
||||
if(run) position += direction.norm() * speed * multiplier * (float)t_delta / 1000000.0f;
|
||||
}
|
||||
|
||||
void draw(Vec2Df mouse, Camera2D cam) {
|
||||
DrawCircle(position.x, position.y, radius, color);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+49
-163
@@ -3,7 +3,6 @@
|
||||
#include "config-node.h"
|
||||
#include "midi-node.h"
|
||||
#include "graph-item.h"
|
||||
#include "node-handle.h"
|
||||
#include "node.h"
|
||||
#include "edge.h"
|
||||
|
||||
@@ -16,139 +15,26 @@
|
||||
typedef std::shared_ptr<VISEQ::GRAPH::GraphItem> SPTR_GraphItem;
|
||||
typedef std::vector<SPTR_GraphItem> VEC_SPTR_GraphItem;
|
||||
|
||||
namespace nlohmann {
|
||||
template <typename T>
|
||||
struct adl_serializer<std::shared_ptr<T>> {
|
||||
static void to_json(json& j, const std::shared_ptr<T>& opt) {
|
||||
if (opt) {
|
||||
j = *opt;
|
||||
} else {
|
||||
j = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void from_json(const json& j, std::shared_ptr<T>& opt) {
|
||||
if (j.is_null()) {
|
||||
opt = nullptr;
|
||||
} else {
|
||||
opt.reset(new T(j.get<T>()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*template <typename T>*/
|
||||
/*struct adl_serializer<std::shared_ptr<T>> {*/
|
||||
/* template <typename J>*/
|
||||
/* static void to_json(J& j, const std::shared_ptr<T>& opt) {*/
|
||||
/* if (opt) {*/
|
||||
/* j = *opt;*/
|
||||
/* } else {*/
|
||||
/* j = nullptr;*/
|
||||
/* }*/
|
||||
/* }*/
|
||||
/**/
|
||||
/* template <typename J>*/
|
||||
/* static void from_json(const J& j, std::shared_ptr<T>& opt) {*/
|
||||
/* if (j.is_null()) {*/
|
||||
/* opt = nullptr;*/
|
||||
/* } else {*/
|
||||
/* opt.reset(new T(j.template get<T>()));*/
|
||||
/* }*/
|
||||
/* }*/
|
||||
/*};*/
|
||||
}
|
||||
|
||||
namespace VISEQ::GRAPH::EDGES {
|
||||
using namespace VISEQ::GRAPH::NODES;
|
||||
|
||||
inline void to_json(json& j, const VISEQ::GRAPH::EDGES::Edge& E){
|
||||
j = json{{"id",E.id}, {"start", E.start.lock()->id}, {"end",E.end.lock()->id}, {"pass_forward",E.pass_forward}, {"pass_backward", E.pass_backward}};
|
||||
}
|
||||
|
||||
inline void from_json(const json& j, Edge& E){
|
||||
j.at("id").get_to(E.id);
|
||||
j.at("pass_forward").get_to(E.pass_forward);
|
||||
j.at("pass_backward").get_to(E.pass_backward);
|
||||
}
|
||||
|
||||
inline void to_json(json& j, const VISEQ::GRAPH::EDGES::SPTR_Edge& E){
|
||||
j = *E;
|
||||
}
|
||||
|
||||
inline void from_json(const json& j, VISEQ::GRAPH::EDGES::SPTR_Edge& E){
|
||||
E.reset(new Edge(j.get<Edge>()));
|
||||
}
|
||||
}
|
||||
|
||||
namespace VISEQ::GRAPH::NODES {
|
||||
|
||||
/* ------------------- MIDINODE ---------------------- */
|
||||
|
||||
/*inline void to_json(json& j, const std::shared_ptr<MidiNode>& N){*/
|
||||
/* j = *N;*/
|
||||
/*}*/
|
||||
/**/
|
||||
/*inline void from_json(const json& j, std::shared_ptr<MidiNode>& N){*/
|
||||
/* N.reset(new MidiNode(j.get<MidiNode>()));*/
|
||||
/*}*/
|
||||
|
||||
inline void to_json(json& j, const MidiNode::NodeMidiOut& MO){
|
||||
j = json{{"port_id", MO.i}, {"enabled", MO.enabled}, {"name", MO.name}};
|
||||
}
|
||||
|
||||
/*template< typename T>*/
|
||||
/*inline void to_json(json& j, const std::shared_ptr<NodeHandle<T>>& NH){*/
|
||||
/* j = json{{"position", NH->position}};*/
|
||||
/*}*/
|
||||
/* ----------------- CONFIG NODE -----------------*/
|
||||
|
||||
|
||||
/*inline void to_json(json& j, const std::shared_ptr<ConfigNode>& N){*/
|
||||
/* j = *N;*/
|
||||
/*}*/
|
||||
/**/
|
||||
/*inline void from_json(const json& j, std::shared_ptr<ConfigNode>& N){*/
|
||||
/* N.reset(new ConfigNode(j.get<ConfigNode>()));*/
|
||||
/*}*/
|
||||
|
||||
/* ------------------- NODE ---------------------- */
|
||||
|
||||
|
||||
inline void to_json(json& j, const Node& N){
|
||||
if(N.getNodeType() == NodeType::MIDINODE){
|
||||
j = dynamic_cast<const MidiNode&>(N);
|
||||
}
|
||||
if(N.getNodeType() == NodeType::CONFIGNODE){
|
||||
j = dynamic_cast<const ConfigNode&>(N);
|
||||
}
|
||||
}
|
||||
|
||||
inline void from_json(const json& j, Node& N){
|
||||
|
||||
if(j.at("nodeType") == NodeType::MIDINODE){
|
||||
N = j.template get<MidiNode>();
|
||||
}
|
||||
if(j.at("nodeType") == NodeType::CONFIGNODE){
|
||||
N = j.template get<ConfigNode>();
|
||||
}
|
||||
}
|
||||
|
||||
inline void to_json(json& j, const SPTR_Node& N){
|
||||
j = *N;
|
||||
}
|
||||
|
||||
inline void from_json(const json& j, std::shared_ptr<Node>& N){
|
||||
std::cout << "SPTR_Node" << std::endl;
|
||||
if(N->getNodeType() == NodeType::MIDINODE){
|
||||
N.reset(new MidiNode(j.get<MidiNode>()));
|
||||
}
|
||||
if(N->getNodeType() == NodeType::CONFIGNODE){
|
||||
N.reset(new ConfigNode(j.get<ConfigNode>()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//namespace nlohmann {
|
||||
// template <typename T>
|
||||
// struct adl_serializer<std::shared_ptr<T>> {
|
||||
// static void to_json(json& j, const std::shared_ptr<T>& opt) {
|
||||
// if (opt) {
|
||||
// j = *opt;
|
||||
// } else {
|
||||
// j = nullptr;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// static void from_json(const json& j, std::shared_ptr<T>& opt) {
|
||||
// if (j.is_null()) {
|
||||
// opt = nullptr;
|
||||
// } else {
|
||||
// opt.reset(new T(j.get<T>()));
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
//}
|
||||
|
||||
namespace VISEQ::GRAPH {
|
||||
inline void to_json(json& j, const GraphItem& G){
|
||||
@@ -182,35 +68,35 @@ namespace VISEQ::BASE{
|
||||
}
|
||||
|
||||
namespace VISEQ::SIGNAL{
|
||||
inline void to_json(json& j, const Signal& G){
|
||||
std::map<std::string, int> fc = {{"r",G.color.r},{"g",G.color.g},{"b",G.color.b},{"a",G.color.a}};
|
||||
j = json{{"color",fc},{"position", G.position},{"radius",G.radius},{"start",G.start.lock()->id},{"next",G.next.lock()->id},{"last",G.last.lock()->id},{"speed",G.speed},{"midi_note_offset",G.midi_note_offset},{"midi_cc_offset",G.midi_cc_offset},{"multiplier",G.multiplier}};
|
||||
}
|
||||
|
||||
inline void from_json(const json& j, Signal& S){
|
||||
|
||||
Color c;
|
||||
c.r = j.at("color")["r"].get<int>();
|
||||
c.g = j.at("color")["g"].get<int>();
|
||||
c.b = j.at("color")["b"].get<int>();
|
||||
c.a = j.at("color")["a"].get<int>();
|
||||
|
||||
j.at("position").get_to<Vec2Df>(S.position);
|
||||
j.at("speed").get_to<float>(S.speed);
|
||||
j.at("radius").get_to<float>(S.radius);
|
||||
j.at("midi_note_offset").get_to(S.midi_note_offset);
|
||||
j.at("midi_cc_offset").get_to(S.midi_cc_offset);
|
||||
|
||||
S.color = c;
|
||||
}
|
||||
|
||||
inline void to_json(json& j, const std::shared_ptr<Signal>& G){
|
||||
j = *G;
|
||||
}
|
||||
|
||||
inline void from_json(const json& j, std::shared_ptr<Signal>& S){
|
||||
S.reset(new Signal(j.get<Signal>()));
|
||||
}
|
||||
// inline void to_json(json& j, const Signal& G){
|
||||
// std::map<std::string, int> fc = {{"r",G.color.r},{"g",G.color.g},{"b",G.color.b},{"a",G.color.a}};
|
||||
// j = json{{"color",fc},{"position", G.position},{"radius",G.radius},{"start",G.start.lock()->id},{"next",G.next.lock()->id},{"last",G.last.lock()->id},{"speed",G.speed},{"midi_note_offset",G.midi_note_offset},{"midi_cc_offset",G.midi_cc_offset},{"multiplier",G.multiplier}};
|
||||
// }
|
||||
//
|
||||
// inline void from_json(const json& j, Signal& S){
|
||||
//
|
||||
// Color c;
|
||||
// c.r = j.at("color")["r"].get<int>();
|
||||
// c.g = j.at("color")["g"].get<int>();
|
||||
// c.b = j.at("color")["b"].get<int>();
|
||||
// c.a = j.at("color")["a"].get<int>();
|
||||
//
|
||||
// j.at("position").get_to<Vec2Df>(S.position);
|
||||
// j.at("speed").get_to<float>(S.speed);
|
||||
// j.at("radius").get_to<float>(S.radius);
|
||||
// j.at("midi_note_offset").get_to(S.midi_note_offset);
|
||||
// j.at("midi_cc_offset").get_to(S.midi_cc_offset);
|
||||
//
|
||||
// S.color = c;
|
||||
// }
|
||||
//
|
||||
// inline void to_json(json& j, const std::shared_ptr<Signal>& G){
|
||||
// j = *G;
|
||||
// }
|
||||
//
|
||||
// inline void from_json(const json& j, std::shared_ptr<Signal>& S){
|
||||
// S.reset(new Signal(j.get<Signal>()));
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "state.h"
|
||||
|
||||
#include "interaction-event.h"
|
||||
#include "raylib.h"
|
||||
#include "vmath.h"
|
||||
#include <memory>
|
||||
|
||||
namespace VISEQ::APP{
|
||||
|
||||
void State::pushEvent(InteractionEvent e){
|
||||
event_queue.push_back(e);
|
||||
}
|
||||
|
||||
InteractionEvent pullEvent(){
|
||||
if(!event_queue.empty()){
|
||||
InteractionEvent e = event_queue.front();
|
||||
event_queue.pop_front();
|
||||
return e;
|
||||
}
|
||||
return {nullptr, Interaction::NONE};
|
||||
}
|
||||
|
||||
void State::update(Camera2D camera){
|
||||
|
||||
if(istate != old_istate) std::cout << state_string[istate] << std::endl;
|
||||
|
||||
mouse_delta = {GetMouseDelta().x, GetMouseDelta().y};
|
||||
|
||||
settings.speed = (settings.grid * 16.0f) * (settings.bpm / 120.f);
|
||||
|
||||
Vector2 mp = GetScreenToWorld2D(GetMousePosition(), camera);
|
||||
Vec2Df mouse_pos = {(float)mp.x, (float)mp.y};
|
||||
|
||||
mouse_left.down = IsMouseButtonDown(MOUSE_BUTTON_LEFT);
|
||||
mouse_left.clicked = IsMouseButtonPressed(MOUSE_BUTTON_LEFT);
|
||||
mouse_left.released = IsMouseButtonReleased(MOUSE_BUTTON_LEFT);
|
||||
|
||||
mouse_right.down = IsMouseButtonDown(MOUSE_BUTTON_RIGHT);
|
||||
mouse_right.clicked = IsMouseButtonPressed(MOUSE_BUTTON_RIGHT);
|
||||
mouse_right.released = IsMouseButtonReleased(MOUSE_BUTTON_RIGHT);
|
||||
|
||||
keys_down.ctrl = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL);
|
||||
keys_down.shift = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT);
|
||||
keys_down.alt = IsKeyDown(KEY_LEFT_ALT) || IsKeyDown(KEY_RIGHT_ALT);
|
||||
|
||||
keys_pressed.ctrl = IsKeyPressed(KEY_LEFT_CONTROL) || IsKeyPressed(KEY_RIGHT_CONTROL);
|
||||
keys_pressed.shift = IsKeyPressed(KEY_LEFT_SHIFT) || IsKeyPressed(KEY_RIGHT_SHIFT);
|
||||
keys_pressed.alt = IsKeyPressed(KEY_LEFT_ALT) || IsKeyPressed(KEY_RIGHT_ALT);
|
||||
|
||||
keys_released.ctrl = IsKeyReleased(KEY_LEFT_CONTROL) || IsKeyReleased(KEY_RIGHT_CONTROL);
|
||||
keys_released.shift = IsKeyReleased(KEY_LEFT_SHIFT) || IsKeyReleased(KEY_RIGHT_SHIFT);
|
||||
keys_released.alt = IsKeyReleased(KEY_LEFT_ALT) || IsKeyReleased(KEY_RIGHT_ALT);
|
||||
|
||||
screen_mouse = {round(mouse_pos.x), round(mouse_pos.y)};
|
||||
raw_mouse = {GetMousePosition().x, GetMousePosition().y};
|
||||
mod_mouse = {round(mouse_pos.x), round(mouse_pos.y)};
|
||||
|
||||
mode_color = settings.mode_colors[node_mode];
|
||||
|
||||
if(keys_down.ctrl) {
|
||||
mod_mouse.x = round(mouse_pos.x / settings.grid) * settings.grid;
|
||||
mod_mouse.y = round(mouse_pos.y / settings.grid) * settings.grid;
|
||||
}
|
||||
old_istate = istate;
|
||||
}
|
||||
|
||||
std::shared_ptr<State> State::get() {
|
||||
if(!State::instance){
|
||||
State::instance.reset(new State());
|
||||
}
|
||||
return State::instance;
|
||||
}
|
||||
}
|
||||
+4
-63
@@ -5,7 +5,6 @@
|
||||
#include "raylib.h"
|
||||
#include "vmath.h"
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
#define STATE state->istate
|
||||
#define SETTINGS state->settings
|
||||
@@ -88,70 +87,12 @@ namespace VISEQ::APP{
|
||||
|
||||
Color mode_color = settings.mode_colors[0];
|
||||
|
||||
void pushEvent(InteractionEvent e){
|
||||
event_queue.push_back(e);
|
||||
}
|
||||
|
||||
InteractionEvent pullEvent(){
|
||||
if(!event_queue.empty()){
|
||||
InteractionEvent e = event_queue.front();
|
||||
event_queue.pop_front();
|
||||
return e;
|
||||
}
|
||||
return {nullptr, Interaction::NONE};
|
||||
}
|
||||
|
||||
void update(Camera2D camera){
|
||||
|
||||
if(istate != old_istate) std::cout << state_string[istate] << std::endl;
|
||||
|
||||
mouse_delta = {GetMouseDelta().x, GetMouseDelta().y};
|
||||
|
||||
settings.speed = (settings.grid * 16.0f) * (settings.bpm / 120.f);
|
||||
|
||||
Vector2 mp = GetScreenToWorld2D(GetMousePosition(), camera);
|
||||
Vec2Df mouse_pos = {(float)mp.x, (float)mp.y};
|
||||
|
||||
mouse_left.down = IsMouseButtonDown(MOUSE_BUTTON_LEFT);
|
||||
mouse_left.clicked = IsMouseButtonPressed(MOUSE_BUTTON_LEFT);
|
||||
mouse_left.released = IsMouseButtonReleased(MOUSE_BUTTON_LEFT);
|
||||
|
||||
mouse_right.down = IsMouseButtonDown(MOUSE_BUTTON_RIGHT);
|
||||
mouse_right.clicked = IsMouseButtonPressed(MOUSE_BUTTON_RIGHT);
|
||||
mouse_right.released = IsMouseButtonReleased(MOUSE_BUTTON_RIGHT);
|
||||
|
||||
keys_down.ctrl = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL);
|
||||
keys_down.shift = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT);
|
||||
keys_down.alt = IsKeyDown(KEY_LEFT_ALT) || IsKeyDown(KEY_RIGHT_ALT);
|
||||
|
||||
keys_pressed.ctrl = IsKeyPressed(KEY_LEFT_CONTROL) || IsKeyPressed(KEY_RIGHT_CONTROL);
|
||||
keys_pressed.shift = IsKeyPressed(KEY_LEFT_SHIFT) || IsKeyPressed(KEY_RIGHT_SHIFT);
|
||||
keys_pressed.alt = IsKeyPressed(KEY_LEFT_ALT) || IsKeyPressed(KEY_RIGHT_ALT);
|
||||
|
||||
keys_released.ctrl = IsKeyReleased(KEY_LEFT_CONTROL) || IsKeyReleased(KEY_RIGHT_CONTROL);
|
||||
keys_released.shift = IsKeyReleased(KEY_LEFT_SHIFT) || IsKeyReleased(KEY_RIGHT_SHIFT);
|
||||
keys_released.alt = IsKeyReleased(KEY_LEFT_ALT) || IsKeyReleased(KEY_RIGHT_ALT);
|
||||
|
||||
screen_mouse = {round(mouse_pos.x), round(mouse_pos.y)};
|
||||
raw_mouse = {GetMousePosition().x, GetMousePosition().y};
|
||||
mod_mouse = {round(mouse_pos.x), round(mouse_pos.y)};
|
||||
|
||||
mode_color = settings.mode_colors[node_mode];
|
||||
|
||||
if(keys_down.ctrl) {
|
||||
mod_mouse.x = round(mouse_pos.x / settings.grid) * settings.grid;
|
||||
mod_mouse.y = round(mouse_pos.y / settings.grid) * settings.grid;
|
||||
}
|
||||
old_istate = istate;
|
||||
}
|
||||
void pushEvent(InteractionEvent e);
|
||||
InteractionEvent pullEvent();
|
||||
void update(Camera2D camera);
|
||||
|
||||
inline static std::shared_ptr<State> instance;
|
||||
static std::shared_ptr<State> get() {
|
||||
if(!instance){
|
||||
instance.reset(new State());
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
static std::shared_ptr<State> get();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
#include "ui.h"
|
||||
|
||||
#include "state.h"
|
||||
#include "midi-node.h"
|
||||
#include "raylib.h"
|
||||
#include "raygui/raygui.h"
|
||||
|
||||
#include "datatypes.h"
|
||||
|
||||
#include <memory>
|
||||
#include <ranges>
|
||||
|
||||
namespace VISEQ::APP::UI{
|
||||
void AppUI::init(){
|
||||
GuiLoadStyleDark();
|
||||
}
|
||||
|
||||
void AppUI::drawSideBar(){
|
||||
|
||||
GuiPanel((Rectangle){-1,-30,380,(float)state->settings.height + 31}, "");
|
||||
const int s_controls = 25;
|
||||
//GuiSetStyle(BUTTON, STATE_FOCUSED, 0xFF0000FF);
|
||||
if(GuiButton((Rectangle){20, (float)s_controls, 80, 30}, "PLAY")) sm->play ? sm->pause() : sm->start();
|
||||
if(GuiButton((Rectangle){120, (float)s_controls, 60, 30}, "STOP")) sm->stop();
|
||||
if(GuiButton((Rectangle){200, (float)s_controls, 60, 30}, "RESET")) sm->reset();
|
||||
|
||||
const int s_bpm = s_controls + 45;
|
||||
GuiLabel((Rectangle){20, (float)s_bpm, 120, 20}, "TIMING");
|
||||
GuiLabel((Rectangle){200, (float)s_bpm, 40, 20}, std::format("{}",state->settings.bpm).c_str());
|
||||
int _bpm = state->settings.bpm;
|
||||
// GuiSlider((Rectangle){20, (float)s_bpm + 25, 240, 20}, "", "", &_bpm, 1, 300);
|
||||
if(GuiSpinner((Rectangle){20, (float)s_bpm + 25, 240, 20}, "", &_bpm, 1, 300, bpm_spinner_edit_mode)) bpm_spinner_edit_mode = !bpm_spinner_edit_mode;
|
||||
state->settings.bpm = _bpm;
|
||||
|
||||
int s_midi = s_bpm + 75;
|
||||
GuiLabel((Rectangle){20, (float)s_midi, 240, 20}, "MIDI OUT");
|
||||
if(GuiButton((Rectangle){200, (float)s_midi, 60, 20}, "RELOAD")) sm->midi.enumerate();
|
||||
|
||||
int i = 0;
|
||||
for(auto& mo : sm->midi.midi_out_ports){
|
||||
GuiCheckBox((Rectangle){20, (float)s_midi + 25 + i*20.0f, 20, 20}, mo.port.port_name.substr(0,32).c_str(), &mo.enabled);
|
||||
i++;
|
||||
}
|
||||
|
||||
if(GuiButton((Rectangle){20, (float)s_midi + 45 + i*20.0f, 240, 20}, "ENABLE FOR ALL")) {
|
||||
for(auto &midis : sm->midi.midi_out_ports){
|
||||
if(midis.enabled){
|
||||
gm->setMidiPortForAll(midis.number, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int s_signal = s_midi + 100 + i*20;
|
||||
GuiLabel((Rectangle){20, (float)s_signal, 240, 20}, "SIGNALS");
|
||||
if(GuiButton((Rectangle){20, (float)s_signal + 35, 240, 20}, "ADD RANDOM SIGNAL")){
|
||||
std::shared_ptr<Node> ni = gm->getRandomNode();
|
||||
sm->addSignalAtNode(ni);
|
||||
}
|
||||
|
||||
int signal_configs = 0;
|
||||
for(auto &s : sm->signals){
|
||||
if(GuiButton((Rectangle){20,(float)s_signal+70 + signal_configs*20, 20, 20},"X")) sm->removeSignal(s);
|
||||
DrawCircle(55, s_signal+80 + signal_configs*20, 6.0F, s->color);
|
||||
|
||||
int s_speed = round(s->speed);
|
||||
s->multiplier = state->settings.bpm / 120.0f;
|
||||
float signal_bpm = round(state->settings.bpm * s->multiplier * (s->speed / state->settings.speed));
|
||||
if(GuiSpinner((Rectangle){70, (float)s_signal+70 + signal_configs*20, 100, 20}, "", &s_speed, 1, 16 * state->settings.grid * 4, s->ui_edit_speed)) s->ui_edit_speed = !s->ui_edit_speed;
|
||||
if(GuiButton((Rectangle){200,(float)s_signal+70 + signal_configs*20, 30, 20}, "1/2")) s_speed = state->settings.speed / 2;
|
||||
//if(GuiButton((Rectangle){200,(float)s_signal+70 + signal_configs*20, 30, 20},"1/4")) s_speed = state->settings.speed / 4;
|
||||
GuiLabel((Rectangle){195, (float)s_signal+70 + signal_configs*20, 30, 20}, std::format("{}",signal_bpm).c_str());
|
||||
GuiToggle((Rectangle){240, (float)s_signal+70 + signal_configs*20, 20, 20},"P", &s->run);
|
||||
s->speed = s_speed;
|
||||
signal_configs++;
|
||||
}
|
||||
|
||||
int s_nodes = s_signal + 70 + signal_configs*20;
|
||||
GuiLabel((Rectangle){20, (float)s_nodes + 30, 240, 20}, "NODES");
|
||||
if(GuiButton((Rectangle){20, (float)s_nodes + 60, 240, 20}, "ADD RANDOM NODE")){
|
||||
gm->createMidiNode(Vec2Df{200.0f, 200.0f});
|
||||
}
|
||||
DrawText(std::format("{}", gm->getNodesSize()).c_str(), 20, state->settings.height - 70, 15, VS_COLOR_WHITE);
|
||||
DrawFPS(20, state->settings.height - 30);
|
||||
|
||||
std::string shorts[6] = {"NT","CC","PC","PB","AT","PP"};
|
||||
for(int i = 0; i < 6; ++i){
|
||||
Color col = state->settings.mode_colors[i];
|
||||
Vec2Df pos = {state->settings.width - (6.0f*75.0f) + i*75.0f, state->settings.height - 75.0f};
|
||||
DrawRing(pos, 30.0f, 34.0f, 0.0f, 360.0f, 24, state->node_mode == i ? VS_COLOR_WHITE : (Color){0,0,0,0});
|
||||
DrawCircleV(pos, 30.0f, col);
|
||||
DrawCenteredText(shorts[i], pos, 20, WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
void AppUI::DrawCenteredText(std::string text, Vec2Df position, int size, Color color){
|
||||
float offset = MeasureText(text.c_str(), size);
|
||||
DrawText(text.c_str(), position.x-offset/2.0f, position.y-(float)size/2.0f+1, size, color );
|
||||
}
|
||||
|
||||
void AppUI::drawContextMenu(Camera2D camera){
|
||||
for(auto n : gm->getSelection() | std::views::filter([](std::shared_ptr<Node> n){ return n->config; })){
|
||||
Vector2 np = GetWorldToScreen2D((Vector2){n->position.x, n->position.y}, camera);
|
||||
|
||||
state->context_menu = !GuiWindowBox((Rectangle){np.x + 10, np.y + 10, 240, 440}, "config");
|
||||
n->config = state->context_menu;
|
||||
|
||||
int option = n->distributionMode;
|
||||
GuiToggleGroup((Rectangle){ np.x + 20, np.y + 40, 73, 20 }, "GEO;RR;RAND", &option);
|
||||
n->distributionMode = (Distribution)option;
|
||||
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + 65, 220, 20}, "SIGNALS");
|
||||
if(GuiButton((Rectangle){np.x + 20, np.y + 90, 220, 20}, "ADD SIGNAL")){
|
||||
sm->addSignalAtNode(n);
|
||||
}
|
||||
|
||||
int y_midi = 120;
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_midi, 220, 20}, "MIDI OUT");
|
||||
|
||||
int i = 0;
|
||||
if(n->getNodeType() == NodeType::MIDINODE){
|
||||
std::shared_ptr<MidiNode> mn = std::dynamic_pointer_cast<MidiNode>(n);
|
||||
for(auto& mo : mn->midi_outs){
|
||||
GuiCheckBox((Rectangle){np.x + 20, np.y + y_midi + 20 + i*20.0f, 20, 20}, mo.second.name.c_str(), &mo.second.enabled);
|
||||
i++;
|
||||
}
|
||||
|
||||
int y_channel = y_midi + 30 + i*20;
|
||||
GuiCheckBox((Rectangle){np.x + 20, np.y + y_channel, 20, 20}, "NOTE", &mn->send_note);
|
||||
GuiCheckBox((Rectangle){np.x + 90, np.y + y_channel, 20, 20}, "CC", &mn->send_cc);
|
||||
GuiCheckBox((Rectangle){np.x + 160, np.y + y_channel, 20, 20}, "PITCH", &mn->send_pitch_bend);
|
||||
|
||||
int y_cc_note = y_channel + 120;
|
||||
switch(state->node_mode){
|
||||
case 0:{
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note - 75, 220, 20}, "NOTE CHANNEL");
|
||||
GuiToggleGroup((Rectangle){np.x + 20, np.y + y_cc_note - 50, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &mn->midiNote.channel);
|
||||
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "NOTE");
|
||||
float v = mn->midiNote.note;
|
||||
GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note , 20, 20}, MIDI::Notes[(int)round(v)].c_str());
|
||||
GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",mn->midiNote.note).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 127);
|
||||
mn->midiNote.note = round(v);
|
||||
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 60, 100, 20}, "VELOCITY");
|
||||
v = mn->midiNote.velocity;
|
||||
GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 60, 20, 20}, std::format("{}",mn->midiNote.velocity).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 90, 220, 20}, "", "", &v, 0, 127);
|
||||
mn->midiNote.velocity = round(v);
|
||||
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 120, 100, 20}, "GATE");
|
||||
v = mn->midiNote.gate;
|
||||
GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 120, 20, 20}, std::format("{}",(float)mn->midiNote.gate/1000.0f).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 150, 220, 20}, "", "", &v, 0, 30000);
|
||||
mn->midiNote.gate= round(v);
|
||||
break;}
|
||||
case 1:
|
||||
{
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note - 75, 220, 20}, "CC CHANNEL");
|
||||
GuiToggleGroup((Rectangle){np.x + 20, np.y + y_cc_note - 50, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &mn->midiCC.channel);
|
||||
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "CC");
|
||||
float v = mn->midiCC.control;
|
||||
GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",mn->midiCC.control).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 127);
|
||||
mn->midiCC.control = round(v);
|
||||
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 60, 100, 20}, "CC VALUE");
|
||||
v = mn->midiCC.value;
|
||||
GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 60, 20, 20}, std::format("{}",mn->midiCC.value).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 90, 220, 20}, "", "", &v, 0, 127);
|
||||
mn->midiCC.value = round(v);
|
||||
|
||||
break;}
|
||||
case 2:
|
||||
{
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note - 75, 220, 20}, "PITCH BEND CHANNEL");
|
||||
GuiToggleGroup((Rectangle){np.x + 20, np.y + y_cc_note - 50, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &mn->midiPitchBend.channel);
|
||||
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "CC");
|
||||
float v = mn->midiPitchBend.value;
|
||||
GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",mn->midiPitchBend.value).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 16383);
|
||||
mn->midiPitchBend.value = round(v);
|
||||
break;}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AppUI::draw(Vec2Df mouse_pos, Camera2D camera){
|
||||
drawSideBar();
|
||||
if(state->context_menu) drawContextMenu(camera);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "state.h"
|
||||
#include "midi-node.h"
|
||||
#include "raylib.h"
|
||||
#include "raygui/raygui.h"
|
||||
#include "raygui/dark/style_dark.h"
|
||||
|
||||
#include "datatypes.h"
|
||||
#include "graph-manager.h"
|
||||
#include "signal-manager.h"
|
||||
|
||||
#include <memory>
|
||||
#include <ranges>
|
||||
|
||||
namespace VISEQ::APP::UI{
|
||||
class AppUI{
|
||||
@@ -26,187 +23,10 @@ namespace VISEQ::APP::UI{
|
||||
|
||||
bool bpm_spinner_edit_mode = false;
|
||||
|
||||
void init(){
|
||||
GuiLoadStyleDark();
|
||||
}
|
||||
|
||||
void drawSideBar(){
|
||||
|
||||
GuiPanel((Rectangle){-1,-30,380,(float)state->settings.height + 31}, "");
|
||||
const int s_controls = 25;
|
||||
//GuiSetStyle(BUTTON, STATE_FOCUSED, 0xFF0000FF);
|
||||
if(GuiButton((Rectangle){20, (float)s_controls, 80, 30}, "PLAY")) sm->play ? sm->pause() : sm->start();
|
||||
if(GuiButton((Rectangle){120, (float)s_controls, 60, 30}, "STOP")) sm->stop();
|
||||
if(GuiButton((Rectangle){200, (float)s_controls, 60, 30}, "RESET")) sm->reset();
|
||||
|
||||
const int s_bpm = s_controls + 45;
|
||||
GuiLabel((Rectangle){20, (float)s_bpm, 120, 20}, "TIMING");
|
||||
GuiLabel((Rectangle){200, (float)s_bpm, 40, 20}, std::format("{}",state->settings.bpm).c_str());
|
||||
int _bpm = state->settings.bpm;
|
||||
// GuiSlider((Rectangle){20, (float)s_bpm + 25, 240, 20}, "", "", &_bpm, 1, 300);
|
||||
if(GuiSpinner((Rectangle){20, (float)s_bpm + 25, 240, 20}, "", &_bpm, 1, 300, bpm_spinner_edit_mode)) bpm_spinner_edit_mode = !bpm_spinner_edit_mode;
|
||||
state->settings.bpm = _bpm;
|
||||
|
||||
int s_midi = s_bpm + 75;
|
||||
GuiLabel((Rectangle){20, (float)s_midi, 240, 20}, "MIDI OUT");
|
||||
if(GuiButton((Rectangle){200, (float)s_midi, 60, 20}, "RELOAD")) sm->midi.enumerate();
|
||||
|
||||
int i = 0;
|
||||
for(auto& mo : sm->midi.midi_out_ports){
|
||||
GuiCheckBox((Rectangle){20, (float)s_midi + 25 + i*20.0f, 20, 20}, mo.port.port_name.substr(0,32).c_str(), &mo.enabled);
|
||||
i++;
|
||||
}
|
||||
|
||||
if(GuiButton((Rectangle){20, (float)s_midi + 45 + i*20.0f, 240, 20}, "ENABLE FOR ALL")) {
|
||||
for(auto &midis : sm->midi.midi_out_ports){
|
||||
if(midis.enabled){
|
||||
gm->setMidiPortForAll(midis.number, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int s_signal = s_midi + 100 + i*20;
|
||||
GuiLabel((Rectangle){20, (float)s_signal, 240, 20}, "SIGNALS");
|
||||
if(GuiButton((Rectangle){20, (float)s_signal + 35, 240, 20}, "ADD RANDOM SIGNAL")){
|
||||
std::shared_ptr<Node> ni = gm->getRandomNode();
|
||||
sm->addSignalAtNode(ni);
|
||||
}
|
||||
|
||||
int signal_configs = 0;
|
||||
for(auto &s : sm->signals){
|
||||
if(GuiButton((Rectangle){20,(float)s_signal+70 + signal_configs*20, 20, 20},"X")) sm->removeSignal(s);
|
||||
DrawCircle(55, s_signal+80 + signal_configs*20, 6.0F, s->color);
|
||||
|
||||
int s_speed = round(s->speed);
|
||||
s->multiplier = state->settings.bpm / 120.0f;
|
||||
float signal_bpm = round(state->settings.bpm * s->multiplier * (s->speed / state->settings.speed));
|
||||
if(GuiSpinner((Rectangle){70, (float)s_signal+70 + signal_configs*20, 100, 20}, "", &s_speed, 1, 16 * state->settings.grid * 4, s->ui_edit_speed)) s->ui_edit_speed = !s->ui_edit_speed;
|
||||
if(GuiButton((Rectangle){200,(float)s_signal+70 + signal_configs*20, 30, 20}, "1/2")) s_speed = state->settings.speed / 2;
|
||||
//if(GuiButton((Rectangle){200,(float)s_signal+70 + signal_configs*20, 30, 20},"1/4")) s_speed = state->settings.speed / 4;
|
||||
GuiLabel((Rectangle){195, (float)s_signal+70 + signal_configs*20, 30, 20}, std::format("{}",signal_bpm).c_str());
|
||||
GuiToggle((Rectangle){240, (float)s_signal+70 + signal_configs*20, 20, 20},"P", &s->run);
|
||||
s->speed = s_speed;
|
||||
signal_configs++;
|
||||
}
|
||||
|
||||
int s_nodes = s_signal + 70 + signal_configs*20;
|
||||
GuiLabel((Rectangle){20, (float)s_nodes + 30, 240, 20}, "NODES");
|
||||
if(GuiButton((Rectangle){20, (float)s_nodes + 60, 240, 20}, "ADD RANDOM NODE")){
|
||||
gm->createMidiNode(Vec2Df{200.0f, 200.0f});
|
||||
}
|
||||
DrawText(std::format("{}", gm->getNodesSize()).c_str(), 20, state->settings.height - 70, 15, VS_COLOR_WHITE);
|
||||
DrawFPS(20, state->settings.height - 30);
|
||||
|
||||
std::string shorts[6] = {"NT","CC","PC","PB","AT","PP"};
|
||||
for(int i = 0; i < 6; ++i){
|
||||
Color col = state->settings.mode_colors[i];
|
||||
Vec2Df pos = {state->settings.width - (6.0f*75.0f) + i*75.0f, state->settings.height - 75.0f};
|
||||
DrawRing(pos, 30.0f, 34.0f, 0.0f, 360.0f, 24, state->node_mode == i ? VS_COLOR_WHITE : (Color){0,0,0,0});
|
||||
DrawCircleV(pos, 30.0f, col);
|
||||
DrawCenteredText(shorts[i], pos, 20, WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawCenteredText(std::string text, Vec2Df position, int size, Color color){
|
||||
float offset = MeasureText(text.c_str(), size);
|
||||
DrawText(text.c_str(), position.x-offset/2.0f, position.y-(float)size/2.0f+1, size, color );
|
||||
}
|
||||
|
||||
void drawContextMenu(Camera2D camera){
|
||||
for(auto n : gm->getSelection() | std::views::filter([](std::shared_ptr<Node> n){ return n->config; })){
|
||||
Vector2 np = GetWorldToScreen2D((Vector2){n->position.x, n->position.y}, camera);
|
||||
|
||||
state->context_menu = !GuiWindowBox((Rectangle){np.x + 10, np.y + 10, 240, 440}, "config");
|
||||
n->config = state->context_menu;
|
||||
|
||||
int option = n->distributionMode;
|
||||
GuiToggleGroup((Rectangle){ np.x + 20, np.y + 40, 73, 20 }, "GEO;RR;RAND", &option);
|
||||
n->distributionMode = (Distribution)option;
|
||||
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + 65, 220, 20}, "SIGNALS");
|
||||
if(GuiButton((Rectangle){np.x + 20, np.y + 90, 220, 20}, "ADD SIGNAL")){
|
||||
sm->addSignalAtNode(n);
|
||||
}
|
||||
|
||||
int y_midi = 120;
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_midi, 220, 20}, "MIDI OUT");
|
||||
|
||||
int i = 0;
|
||||
if(n->getNodeType() == NodeType::MIDINODE){
|
||||
std::shared_ptr<MidiNode> mn = std::dynamic_pointer_cast<MidiNode>(n);
|
||||
for(auto& mo : mn->midi_outs){
|
||||
GuiCheckBox((Rectangle){np.x + 20, np.y + y_midi + 20 + i*20.0f, 20, 20}, mo.second.name.c_str(), &mo.second.enabled);
|
||||
i++;
|
||||
}
|
||||
|
||||
int y_channel = y_midi + 30 + i*20;
|
||||
GuiCheckBox((Rectangle){np.x + 20, np.y + y_channel, 20, 20}, "NOTE", &mn->send_note);
|
||||
GuiCheckBox((Rectangle){np.x + 90, np.y + y_channel, 20, 20}, "CC", &mn->send_cc);
|
||||
GuiCheckBox((Rectangle){np.x + 160, np.y + y_channel, 20, 20}, "PITCH", &mn->send_pitch_bend);
|
||||
|
||||
int y_cc_note = y_channel + 120;
|
||||
switch(state->node_mode){
|
||||
case 0:{
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note - 75, 220, 20}, "NOTE CHANNEL");
|
||||
GuiToggleGroup((Rectangle){np.x + 20, np.y + y_cc_note - 50, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &mn->midiNote.channel);
|
||||
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "NOTE");
|
||||
float v = mn->midiNote.note;
|
||||
GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note , 20, 20}, MIDI::Notes[(int)round(v)].c_str());
|
||||
GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",mn->midiNote.note).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 127);
|
||||
mn->midiNote.note = round(v);
|
||||
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 60, 100, 20}, "VELOCITY");
|
||||
v = mn->midiNote.velocity;
|
||||
GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 60, 20, 20}, std::format("{}",mn->midiNote.velocity).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 90, 220, 20}, "", "", &v, 0, 127);
|
||||
mn->midiNote.velocity = round(v);
|
||||
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 120, 100, 20}, "GATE");
|
||||
v = mn->midiNote.gate;
|
||||
GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 120, 20, 20}, std::format("{}",(float)mn->midiNote.gate/1000.0f).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 150, 220, 20}, "", "", &v, 0, 30000);
|
||||
mn->midiNote.gate= round(v);
|
||||
break;}
|
||||
case 1:
|
||||
{
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note - 75, 220, 20}, "CC CHANNEL");
|
||||
GuiToggleGroup((Rectangle){np.x + 20, np.y + y_cc_note - 50, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &mn->midiCC.channel);
|
||||
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "CC");
|
||||
float v = mn->midiCC.control;
|
||||
GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",mn->midiCC.control).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 127);
|
||||
mn->midiCC.control = round(v);
|
||||
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 60, 100, 20}, "CC VALUE");
|
||||
v = mn->midiCC.value;
|
||||
GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 60, 20, 20}, std::format("{}",mn->midiCC.value).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 90, 220, 20}, "", "", &v, 0, 127);
|
||||
mn->midiCC.value = round(v);
|
||||
|
||||
break;}
|
||||
case 2:
|
||||
{
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note - 75, 220, 20}, "PITCH BEND CHANNEL");
|
||||
GuiToggleGroup((Rectangle){np.x + 20, np.y + y_cc_note - 50, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &mn->midiPitchBend.channel);
|
||||
|
||||
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "CC");
|
||||
float v = mn->midiPitchBend.value;
|
||||
GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",mn->midiPitchBend.value).c_str());
|
||||
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 16383);
|
||||
mn->midiPitchBend.value = round(v);
|
||||
break;}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw(Vec2Df mouse_pos, Camera2D camera){
|
||||
drawSideBar();
|
||||
if(state->context_menu) drawContextMenu(camera);
|
||||
}
|
||||
|
||||
void init();
|
||||
void drawSideBar();
|
||||
void drawContextMenu(Camera2D camera);
|
||||
void draw(Vec2Df mouse_pos, Camera2D camera);
|
||||
void DrawCenteredText(std::string text, Vec2Df position, int size, Color color);
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user