From 96c2bde0e13b3e08ef1abb368210eb7b2b3b69f0 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 15 Dec 2024 01:47:53 +0100 Subject: [PATCH] Vector UI on its way! --- src/datatypes.h | 8 +-- src/interaction-manager.h | 5 +- src/midi-node.h | 112 +++++++++++++++++++++++---------- src/node-handle.h | 128 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 213 insertions(+), 40 deletions(-) create mode 100644 src/node-handle.h diff --git a/src/datatypes.h b/src/datatypes.h index addce75..455a373 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -94,10 +94,10 @@ namespace VISEQ::MIDI{ inline MidiNote operator+(const MidiNote left, const MidiNote right){ MidiNote added; - added.note = left.note + right.note; - added.channel = left.channel + right.channel; - added.gate = left.gate + right.gate; - added.velocity = left.velocity + right.velocity; + added.note = std::clamp(left.note + right.note, 0, 127); + added.channel = std::clamp(left.channel + right.channel, 0, 15); + added.gate = std::max(left.gate + right.gate, 0); + added.velocity = std::clamp(left.velocity + right.velocity, 0, 127); return added; } diff --git a/src/interaction-manager.h b/src/interaction-manager.h index cd05add..3838993 100644 --- a/src/interaction-manager.h +++ b/src/interaction-manager.h @@ -13,7 +13,6 @@ #include "state.h" #include "canvas.h" #include -#include #include namespace VISEQ::INTERACTION{ @@ -82,11 +81,11 @@ namespace VISEQ::INTERACTION{ } if(!KEYS_DOWN.shift && IsKeyPressed(KEY_TAB)) { - state->node_mode = (state->node_mode + 1) % 6; + state->node_mode = (state->node_mode + 1) % 7; } if(KEYS_DOWN.shift && IsKeyPressed(KEY_TAB)) { - state->node_mode -= state->node_mode > 0 ? 1 : -5; + state->node_mode -= state->node_mode > 0 ? 1 : -6; } } diff --git a/src/midi-node.h b/src/midi-node.h index a5fd1f5..8e13359 100644 --- a/src/midi-node.h +++ b/src/midi-node.h @@ -1,17 +1,15 @@ #pragma once -#include "button.h" #include "config-node.h" -#include "control-node.h" #include "datatypes.h" #include "edge.h" +#include "node-handle.h" #include "node.h" #include "raylib.h" #include "raymath.h" #include "state.h" #include #include -#include namespace VISEQ::APP::UI{ class AppUI; @@ -29,8 +27,13 @@ namespace VISEQ::GRAPH::NODES { MIDI::MidiPitchBend midiPitchBend; MIDI::MidiAftertouch midiAftertouch; MIDI::MidiPolyPressure midiPolyPressure; - - std::vector channelButtons; + + std::vector>> midiNoteHandles; + std::vector>> midiCCHandles; + std::vector>> midiPCHandles; + std::vector>> midiPitchBendHandles; + std::vector>> midiAftertouchHandles; + std::vector>> midiPolyPressureHandles; public: @@ -67,11 +70,29 @@ namespace VISEQ::GRAPH::NODES { float last = 0; void init(){ - channelButtons.clear(); - for(int i = 0; i < 16; ++i){ - channelButtons.emplace_back(position, 4.0f, VS_COLOR_YELLOW); - channelButtons.back().hover_color = VS_COLOR_PINK; - } + midiNoteHandles.emplace_back(std::make_shared>(midiNote.note, position, VS_COLOR_GREEN, "NOTE")); + midiNoteHandles.emplace_back(std::make_shared>(midiNote.channel, position, VS_COLOR_PINK, "CHANNEL", 0, 15, 40, 150)); + midiNoteHandles.emplace_back(std::make_shared>(midiNote.gate, position, VS_COLOR_RED, "GATE", 0, 30000, 40, 300)); + midiNoteHandles.emplace_back(std::make_shared>(midiNote.velocity, position, VS_COLOR_PURPLE, "VELOCITY")); + + + midiCCHandles.emplace_back(std::make_shared>(midiCC.control, position, VS_COLOR_SUN, "CONTROL")); + midiCCHandles.emplace_back(std::make_shared>(midiCC.channel, position, VS_COLOR_PINK, "CHANNEL", 0, 15, 40, 150)); + midiCCHandles.emplace_back(std::make_shared>(midiCC.value, position, VS_COLOR_GREEN, "VALUE")); + + midiPCHandles.emplace_back(std::make_shared>(midiPC.program, position, VS_COLOR_SUN, "PROGRAM")); + midiPCHandles.emplace_back(std::make_shared>(midiPC.channel, position, VS_COLOR_PINK, "CHANNEL", 0, 15, 40, 150)); + midiPCHandles.emplace_back(std::make_shared>(midiPC.value, position, VS_COLOR_GREEN, "VALUE")); + + midiPitchBendHandles.emplace_back(std::make_shared>(midiPitchBend.channel, position, VS_COLOR_PINK, "CHANNEL", 0, 15, 40, 150)); + midiPitchBendHandles.emplace_back(std::make_shared>(midiPitchBend.value, position, VS_COLOR_GREEN, "VALUE", -8192, 8192, 40, 300)); + + midiAftertouchHandles.emplace_back(std::make_shared>(midiAftertouch.channel, position, VS_COLOR_PINK, "CHANNEL", 0, 15, 40, 150)); + midiAftertouchHandles.emplace_back(std::make_shared>(midiAftertouch.value, position, VS_COLOR_GREEN, "VALUE")); + + midiPolyPressureHandles.emplace_back(std::make_shared>(midiPolyPressure.channel, position, VS_COLOR_PINK, "CHANNEL", 0, 15, 40, 150)); + midiPolyPressureHandles.emplace_back(std::make_shared>(midiPolyPressure.value, position, VS_COLOR_GREEN, "VALUE")); + midiPolyPressureHandles.emplace_back(std::make_shared>(midiPolyPressure.note, position, VS_COLOR_SUN, "NOTE")); } std::shared_ptr getPtr(){ @@ -156,6 +177,24 @@ namespace VISEQ::GRAPH::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){ @@ -263,11 +302,36 @@ namespace VISEQ::GRAPH::NODES { void draw(Vec2Df mouse, Camera2D cam) override { Node::draw(); - /*if(hovering()) {*/ - /* radius += radius < hover_radius ? 2 : 0;*/ - /*} else {*/ - /* radius -= radius > node_radius ? 2 : 0;*/ - /*}*/ + 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(); @@ -308,24 +372,6 @@ namespace VISEQ::GRAPH::NODES { if(selected) DrawCircleLinesV(position, radius+8, (Color){158, 255, 210, 255}); - /*if(hovering()){*/ - /* for(int i = 0; i < 16; ++i){*/ - /* channelButtons.at(i).position = position.orbitAtAngle(radius - 6.0f, DEG2RAD * (360.0f/16.0f)*i);*/ - /* Color col = VS_COLOR_YELLOW;*/ - /* if(mode == 0 && midiNote.channel == i) col = VS_COLOR_PINK;*/ - /* if(mode == 1 && midiCC.channel == i) col = VS_COLOR_PINK;*/ - /* if(mode == 2 && midiPitchBend.channel == i) col = VS_COLOR_PINK;*/ - /* channelButtons.at(i).color = col;*/ - /* channelButtons.at(i).draw();*/ - /* if(channelButtons.at(i).hovering()) DrawCenteredText(std::format("{}", i+1), position + (Vec2Df){0,10.0f}, 8, VS_COLOR_WHITE);*/ - /* if(channelButtons.at(i).clicked()) {*/ - /* if(mode == 0) midiNote.channel = i;*/ - /* if(mode == 1) midiCC.channel = i;*/ - /* if(mode == 2) midiPitchBend.channel = i;*/ - /* }*/ - /* }*/ - /*}*/ - if(KEYS_DOWN.alt){ float value = 0; int offset_gate = 0; diff --git a/src/node-handle.h b/src/node-handle.h new file mode 100644 index 0000000..775a572 --- /dev/null +++ b/src/node-handle.h @@ -0,0 +1,128 @@ +#pragma once + +#include "datatypes.h" +#include "draggable.h" +#include "graph-item.h" +#include "raylib.h" +#include "raymath.h" +#include "state.h" +#include + +namespace VISEQ::GRAPH::NODES{ + + template + class NodeHandle : public BASE::Draggable, public GraphItem, public std::enable_shared_from_this>{ + public: + NodeHandle() {} + NodeHandle(T &_assignedValue, Vec2Df parentPos, Color _col, std::string name) : 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();} + + virtual GRAPH::GraphItemType getType() override { + return GraphItemType::HANDLE; + } + + std::shared_ptr> getPtr(){ + return this->shared_from_this(); + } + + T &assignedValueRef; + Vec2Df parentPosition = {0,0}; + Color col; + std::string name = ""; + + float minValue = 0; + float maxValue = 127; + float minDistance = 40; + float maxDistance = 40 + 2*127; + bool stopAtMax = true; + + float radius = 10.0f; + float orbit = 0.0f; + + Vec2Df minP; + Vec2Df maxP; + + bool locked = true; + bool visible = false; + + void init(){ + position = parentPosition.orbitAtAngle(minDistance, DEG2RAD * GetRandomValue(0, 360)) - parentPosition; + } + + void assignValue(T &value){ + assignedValueRef = value; + } + + float distance(){ + return (parentPosition + position).dist(parentPosition); + } + + virtual void drag(Vec2Df mouse_pos) override { + if(KEYS_DOWN.shift) { + orbit = (parentPosition + position).dist(parentPosition); + position = parentPosition.orbitAlongVector(SCREEN_MOUSE, orbit) - parentPosition; + } else { + orbit = parentPosition.dist(SCREEN_MOUSE); + position = parentPosition.orbitAlongVector(maxP, orbit) - parentPosition; + } + if(distance() >= maxDistance) position = parentPosition.orbitAlongVector(maxP, maxDistance) - parentPosition; + if(distance() <= minDistance) position = parentPosition.orbitAlongVector(maxP, minDistance) - parentPosition; + } + + void lock(){ + visible = false; + locked = true; + } + + void unlock(){ + visible = true; + locked = false; + } + + void assignValue(T &value, float min, float max, float minDistance, float maxDistance){ + assignedValueRef = value; + } + + void updateAssignedValue(){ + assignedValueRef = (T)Remap((float)(parentPosition + position).dist(parentPosition), minDistance, maxDistance, minValue, maxValue); + } + + virtual bool hovering() override { + return CheckCollisionPointCircle(SCREEN_MOUSE, position + parentPosition, radius); + } + + virtual void update(Vec2Df pos) override { + if(!locked && visible){ + 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}); + + parentPosition = pos; + if(!locked && visible) updateAssignedValue(); + + minP = parentPosition.orbitAlongVector((parentPosition + position), minDistance); + maxP = parentPosition.orbitAlongVector((parentPosition + position), maxDistance); + } + + virtual void draw() override { + if(!locked && visible){ + Color dcol = ColorBrightness(col, -0.5f); + + DrawLineV(minP, maxP, dcol); + DrawLineV(parentPosition, minP, VS_COLOR_LIGHTGREY); + DrawCircleV(minP, 3.0f, dcol); + DrawCircleV(maxP, 3.0f, dcol); + DrawCircleV(position + parentPosition, radius, hovering() ? col : dcol); + Vec2Df text_position = maxP.center(minP) + (minP - maxP).norm().orth() * 6.0f; + float rotation = Vector2Angle((Vector2){1,0}, (minP - maxP)) * RAD2DEG; + DrawTextPro(GetFontDefault(), name.c_str(), text_position, (Vector2){0,0}, rotation, 10.0f, 2.0f, hovering() ? col : dcol); + } + } + + virtual bool select() override { + return false; + } + }; +}