21 Commits

Author SHA1 Message Date
Sebastian 159255adda midi node design changes 2026-07-19 02:38:33 +02:00
Sebastian c7c489fde5 signal timing needs work 2026-07-18 02:07:50 +02:00
Sebastian 1f7f5314a3 midi modes added, context menu adapted 2026-07-17 02:03:27 +02:00
Sebastian 1f2cbc4c31 modifier chains implemented for notes 2026-07-17 01:07:09 +02:00
Sebastian 183d89ceaf modifier chains added 2026-07-17 00:13:01 +02:00
Sebastian 0be3d6cd77 changed node font to Inter 2026-07-16 00:23:32 +02:00
Sebastian 4613bfd072 fixed connector mismatch after breaking it after fixing it last time 2026-07-14 02:19:29 +02:00
Sebastian 585e5e1167 connectors fixed 2026-07-02 08:28:21 +02:00
Sebastian 30408393e7 debugging connector problem 2026-06-30 10:54:45 +02:00
Sebastian e9c8cc9aea connectors need fixing 2026-06-28 23:45:54 +02:00
Sebastian 03b2989130 penpot file added 2026-06-28 00:46:18 +02:00
Sebastian e9c2872187 more textures and ui refinements 2026-06-28 00:34:57 +02:00
Sebastian b9ad5293bf blocker not showing 2026-06-27 03:13:40 +02:00
Sebastian e4b2df21bc small steps forward - placement of edge buttons changed and one more texture added 2026-06-27 01:35:20 +02:00
Sebastian 970faad32d wip 2026-06-26 19:32:00 +02:00
Sebastian 0fb517f282 position fixed finally 2026-06-26 17:43:45 +02:00
Sebastian 9fefa903bd still wrong position 2026-06-26 10:26:41 +02:00
Sebastian 3f8984b69f delete button texturized but still at incorrect position 2026-06-26 02:23:08 +02:00
Sebastian 9ea1305c27 ui improvements and texture tests 2026-06-26 01:50:32 +02:00
Sebastian 7c7c3b37ff ui improvements for modifier value changes 2026-06-25 19:34:23 +02:00
Sebastian 792e537f2b removed orbits unified ctrl/alt/shift modifiers for alle node types 2026-06-25 11:41:55 +02:00
52 changed files with 1040 additions and 340 deletions
+6 -2
View File
@@ -9,13 +9,17 @@ LDFLAGS := -Llib -L/usr/lib -L/usr/local/lib
SRCS := $(wildcard src/*.cpp)
OBJECTS := $(patsubst src/%.cpp,build/%.o, $(SRCS))
DEPENDS := $(patsubst src/%.cpp,build/%.d, $(SRCS))
viseq: $(OBJECTS)
$(CXX) $(OBJECTS) -o $@ $(LDFLAGS) $(LDLIBS)
$(OBJECTS): build/%.o : src/%.cpp
-include $(DEPENDS)
#$(OBJECTS): build/%.o : src/%.cpp
build/%.o: src/%.cpp Makefile
@mkdir -p build
$(CXX) -c $< -o $@ $(CXXFLAGS) $(CXXINC)
$(CXX) -c $< -o $@ $(CXXFLAGS) $(CXXINC) -MMD -MP
clean:
rm -f build/*
Binary file not shown.
+122
View File
@@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg1"
inkscape:export-filename="blocker_closed.png"
inkscape:export-xdpi="24"
inkscape:export-ydpi="24"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="block.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showguides="true"
inkscape:zoom="1.1398148"
inkscape:cx="87.294883"
inkscape:cy="268.026"
inkscape:window-width="2560"
inkscape:window-height="1414"
inkscape:window-x="2560"
inkscape:window-y="1106"
inkscape:window-maximized="1"
inkscape:current-layer="g5">
<sodipodi:guide
position="0,256"
orientation="0,1"
id="guide1"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="256,256"
orientation="1,0"
id="guide2"
inkscape:locked="false" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Blocker Open"
inkscape:groupmode="layer"
id="layer1"
style="display:none">
<rect
style="fill:#b4d273;fill-opacity:1;fill-rule:evenodd;stroke-width:0.742756"
id="rect1"
width="282.46313"
height="512"
x="114.76843"
y="0"
ry="141.23157" />
<rect
style="fill:#d6d6d6;fill-opacity:1;fill-rule:evenodd;stroke-width:0.950718"
id="rect2"
width="379.43814"
height="45.092152"
x="-445.71906"
y="233.45392"
transform="rotate(-90)" />
<circle
style="fill:#d6d6d6;fill-opacity:1"
id="path1"
cx="255.99994"
cy="106.15759"
r="56.149471" />
<circle
style="fill:#d6d6d6;fill-opacity:1"
id="path1-0"
cx="256"
cy="407.41348"
r="56.149471" />
</g>
<g
inkscape:label="Blocker Closed"
inkscape:groupmode="layer"
id="g5"
transform="rotate(-90,256,256)">
<rect
style="fill:#c35355;fill-opacity:1;fill-rule:evenodd;stroke-width:0.742756"
id="rect3"
width="282.46313"
height="512"
x="114.76843"
y="0"
ry="141.23157" />
<rect
style="fill:#d6d6d6;fill-opacity:1;fill-rule:evenodd;stroke-width:0.950718"
id="rect4"
width="379.43814"
height="45.092152"
x="-445.71906"
y="233.45392"
transform="rotate(-90)" />
<circle
style="fill:#d6d6d6;fill-opacity:1"
id="circle4"
cx="255.99994"
cy="106.15759"
r="56.149471" />
<circle
style="fill:#d6d6d6;fill-opacity:1"
id="circle5"
cx="256"
cy="407.41348"
r="56.149471" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

+81
View File
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg1"
inkscape:export-filename="round-delete.png"
inkscape:export-xdpi="24"
inkscape:export-ydpi="24"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="textures.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showguides="true"
inkscape:zoom="1.1398148"
inkscape:cx="87.73355"
inkscape:cy="268.026"
inkscape:window-width="2560"
inkscape:window-height="1414"
inkscape:window-x="2560"
inkscape:window-y="1106"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<sodipodi:guide
position="0,256"
orientation="0,1"
id="guide1"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="256,256"
orientation="1,0"
id="guide2"
inkscape:locked="false" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<circle
style="fill:#c35355;fill-opacity:1"
id="path2"
cx="256"
cy="256"
r="256" />
<rect
style="fill:#d6d6d6;fill-opacity:1;fill-rule:evenodd"
id="rect2"
width="419.79556"
height="45.092152"
x="-571.93646"
y="-22.546076"
transform="rotate(-135)" />
<rect
style="fill:#d6d6d6;fill-opacity:1;fill-rule:evenodd"
id="rect2-0"
width="419.79556"
height="45.092152"
x="-209.89778"
y="339.49261"
transform="rotate(-45)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.
Binary file not shown.
+96
View File
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg1"
inkscape:export-filename="textures/split.png"
inkscape:export-xdpi="24"
inkscape:export-ydpi="24"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="split.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showguides="true"
inkscape:zoom="0.80597077"
inkscape:cx="-42.805522"
inkscape:cy="129.65731"
inkscape:window-width="2560"
inkscape:window-height="1414"
inkscape:window-x="2560"
inkscape:window-y="1106"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<sodipodi:guide
position="0,256"
orientation="0,1"
id="guide1"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="256,256"
orientation="1,0"
id="guide2"
inkscape:locked="false" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#b4d273;fill-opacity:1;fill-rule:evenodd;stroke-width:0.742756"
id="rect1"
width="282.46313"
height="512"
x="-1027.9529"
y="0"
ry="141.23157" />
<circle
style="fill:#b4d273;fill-opacity:1"
id="path2"
cx="256"
cy="256"
r="256" />
<circle
style="fill:#d6d6d6;fill-opacity:1;stroke-width:0.832073"
id="path2-7"
cx="256"
cy="256"
r="213.01056" />
<rect
style="fill:#b4d273;fill-opacity:1;fill-rule:evenodd"
id="rect2"
width="45.621445"
height="300.92609"
x="233.18927"
y="105.53696"
ry="22.810722" />
<rect
style="fill:#b4d273;fill-opacity:1;fill-rule:evenodd"
id="rect2-6"
width="45.621445"
height="300.92609"
x="233.18927"
y="-406.46304"
ry="22.810722"
transform="rotate(90)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

+81
View File
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg1"
inkscape:export-filename="round-delete.png"
inkscape:export-xdpi="24"
inkscape:export-ydpi="24"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="textures.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showguides="true"
inkscape:zoom="1.1398148"
inkscape:cx="87.73355"
inkscape:cy="268.026"
inkscape:window-width="2560"
inkscape:window-height="1414"
inkscape:window-x="2560"
inkscape:window-y="1106"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<sodipodi:guide
position="0,256"
orientation="0,1"
id="guide1"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="256,256"
orientation="1,0"
id="guide2"
inkscape:locked="false" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<circle
style="fill:#c35355;fill-opacity:1"
id="path2"
cx="256"
cy="256"
r="256" />
<rect
style="fill:#d6d6d6;fill-opacity:1;fill-rule:evenodd"
id="rect2"
width="419.79556"
height="45.092152"
x="-571.93646"
y="-22.546076"
transform="rotate(-135)" />
<rect
style="fill:#d6d6d6;fill-opacity:1;fill-rule:evenodd"
id="rect2-0"
width="419.79556"
height="45.092152"
x="-209.89778"
y="339.49261"
transform="rotate(-45)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

+22 -1
View File
@@ -1,5 +1,6 @@
#include "app-manager.h"
#include "datatypes.h"
#include "interaction-manager.h"
#include "nlohmann/json.hpp"
#include "graph-manager.h"
@@ -10,6 +11,7 @@
#include <memory>
#include <fstream>
#include <raylib.h>
namespace VISEQ::APP{
void AppManager::init(){
@@ -19,6 +21,25 @@ namespace VISEQ::APP{
canvas->camera.zoom = 1.0f;
}
void AppManager::loadTextures(){
TEXTURES::tex_connector = LoadTexture("resources/textures/connector.png");
TEXTURES::tex_connector_hover = LoadTexture("resources/textures/connector_hover.png");
TEXTURES::tex_connector_pressed = LoadTexture("resources/textures/connector_pressed.png");
TEXTURES::tex_connector_connected = LoadTexture("resources/textures/connector_connected.png");
TEXTURES::tex_delete = LoadTexture("resources/textures/delete.png");
TEXTURES::tex_split = LoadTexture("resources/textures/split.png");
TEXTURES::tex_blocker_open = LoadTexture("resources/textures/blocker_open.png");
TEXTURES::tex_blocker_closed= LoadTexture("resources/textures/blocker_closed.png");
TEXTURES::tex_delete_hover = LoadTexture("resources/textures/delete_hover.png");
TEXTURES::tex_split_hover = LoadTexture("resources/textures/split_hover.png");
TEXTURES::tex_blocker_open_hover = LoadTexture("resources/textures/blocker_open_hover.png");
TEXTURES::tex_blocker_closed_hover = LoadTexture("resources/textures/blocker_closed_hover.png");
TEXTURES::tex_delete_pressed = LoadTexture("resources/textures/delete_pressed.png");
TEXTURES::tex_split_pressed = LoadTexture("resources/textures/split_pressed.png");
TEXTURES::tex_blocker_open_pressed = LoadTexture("resources/textures/blocker_open_pressed.png");
TEXTURES::tex_blocker_closed_pressed = LoadTexture("resources/textures/blocker_closed_pressed.png");
}
void AppManager::close(){
std::ofstream file("state.json");
file << json(*this);
@@ -154,8 +175,8 @@ namespace VISEQ::APP{
canvas->drawStart();
canvas->draw();
gm->drawNodes(SCREEN_MOUSE, canvas->camera);
gm->drawEdges(SCREEN_MOUSE, canvas->camera);
gm->drawNodes(SCREEN_MOUSE, canvas->camera);
sm->drawSignals(SCREEN_MOUSE, canvas->camera);
im.draw();
+1
View File
@@ -30,6 +30,7 @@ namespace VISEQ::APP {
UI::AppUI ui;
void init();
void loadTextures();
void close();
void startSignals();
void loadState();
+45 -1
View File
@@ -12,6 +12,45 @@ inline bool ViButton(Vec2Df mp, float x, float y, float radius, Color color){
return (Vector2Distance(mp, (Vector2){x,y}) < radius && IsMouseButtonPressed(MOUSE_BUTTON_LEFT));
}
inline bool ViTexButton(Vec2Df mp, Vec2Df position, float rotation, float radius, Texture2D tex, Texture2D tex_hover, Texture2D tex_pressed, Color color){
float scale = (radius*2.0f) / (float)tex.width;
Vec2Df start_position = position - Vec2Df((tex.width*scale) / 2.0f, (tex.height*scale) / 2.0f);
Vec2Df center_pos = start_position.rotateAround(position, rotation);
bool hover = Vector2Distance(mp, position) < radius;
bool pressed = hover && IsMouseButtonPressed(MOUSE_BUTTON_LEFT);
if(!hover && !pressed) DrawTexturePro(tex, Rectangle(0.0, 0.0, tex.width, tex.height), Rectangle(center_pos.x, center_pos.y, tex.width*scale, tex.height*scale), Vec2Df(0.0f,tex.height*scale/2.0f), rotation, color);
if(hover) DrawTexturePro(tex_hover, Rectangle(0.0, 0.0, tex.width, tex.height), Rectangle(center_pos.x, center_pos.y, tex.width*scale, tex.height*scale), Vec2Df(0.0f,tex.height*scale/2.0f), rotation, color);
if(pressed) DrawTexturePro(tex_pressed, Rectangle(0.0, 0.0, tex.width, tex.height), Rectangle(center_pos.x, center_pos.y, tex.width*scale, tex.height*scale), Vec2Df(0.0f,tex.height*scale/2.0f), rotation, color);
return pressed;
}
inline bool ViTexButton(Vec2Df mp, Vec2Df position, float rotation, float radius, Texture2D tex, Texture2D tex_hover, Color color){
float scale = (radius*2.0f) / (float)tex.width;
Vec2Df start_position = position - Vec2Df((tex.width*scale) / 2.0f, (tex.height*scale) / 2.0f);
Vec2Df center_pos = start_position.rotateAround(position, rotation);
bool hover = Vector2Distance(mp, position) < radius;
bool pressed = hover && IsMouseButtonPressed(MOUSE_BUTTON_LEFT);
if(!hover) DrawTexturePro(tex, Rectangle(0.0, 0.0, tex.width, tex.height), Rectangle(center_pos.x, center_pos.y, tex.width*scale, tex.height*scale), Vec2Df(0.0f,tex.height*scale/2.0f), rotation, color);
if(hover) DrawTexturePro(tex_hover, Rectangle(0.0, 0.0, tex.width, tex.height), Rectangle(center_pos.x, center_pos.y, tex.width*scale, tex.height*scale), Vec2Df(0.0f,tex.height*scale/2.0f), rotation, color);
return pressed;
}
inline bool ViTexButton(Vec2Df mp, Vec2Df position, float rotation, float radius, Texture2D tex, Color color){
float scale = (radius*2.0f) / (float)tex.width;
Vec2Df start_position = position - Vec2Df((tex.width*scale) / 2.0f, (tex.height*scale) / 2.0f);
Vec2Df center_pos = start_position.rotateAround(position, rotation);
bool hover = Vector2Distance(mp, position) < radius;
bool pressed = hover && IsMouseButtonPressed(MOUSE_BUTTON_LEFT);
DrawTexturePro(tex, Rectangle(0.0, 0.0, tex.width, tex.height), Rectangle(center_pos.x, center_pos.y, tex.width*scale, tex.height*scale), Vec2Df(0.0f,tex.height*scale/2.0f), rotation, color);
return pressed;
}
inline bool ViButton(Vec2Df mp, Vec2Df position, float radius, Color color){
return ViButton(mp, position.x, position.y, radius, color);
}
@@ -30,7 +69,12 @@ inline bool ViSegButton(Vec2Df mp, float x, float y, float start_angle, float en
inline void DrawCenteredText(std::string text, Vec2Df position, float text_size, Color color){
float offset = MeasureText(text.c_str(), text_size);
DrawText(text.c_str(), position.x-offset/2.0f, position.y-text_size/2.0f, text_size, color);
//DrawText(text.c_str(), position.x-offset/2.0f, position.y-text_size/2.0f, text_size, color);
DrawTextEx(VISEQ::APP::inter, text.c_str(), Vec2Df(position.x-offset/2.0f, position.y-(float)text_size/2.0f+1), text_size, 2.0f, color);
}
inline void DrawText(std::string text, Vec2Df position, float text_size, Color color){
DrawText(text.c_str(), position.x, position.y-text_size/2.0f, text_size, color);
}
namespace VISEQ::APP::UI{
+2 -2
View File
@@ -21,14 +21,14 @@ namespace VISEQ::APP::CANVAS {
}
void Canvas::pickUp() {
std::cout << "canvas pickUp" << std::endl;
//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;
//std::cout << "canvas putDown" << std::endl;
BASE::Draggable::putDown();
state->pushEvent({getPtr(), Interaction::SELECT});
}
+2 -2
View File
@@ -82,14 +82,14 @@ namespace VISEQ::GRAPH::NODES {
void ConfigNode::update() {
Node::update();
mode = state->node_mode;
//mode = state->node_mode;
}
void ConfigNode::interact(){
float wheel = GetMouseWheelMove();
if(KEYS_DOWN.ctrl && !KEYS_DOWN.alt){
switch(state->node_mode){
switch(mode){
case 0:{
midiNote.note += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 12 : wheel;
break;}
+90
View File
@@ -0,0 +1,90 @@
#include "connector.h"
#include "helper.h"
#include "node.h"
#include "raymath.h"
#include "state.h"
#include "button.h"
#include <execution>
#include <memory>
namespace VISEQ::GRAPH {
GraphItemType Connector::getType() {
return GraphItemType::CONNECTOR;
}
std::shared_ptr<Connector> Connector::getPtr(){
return shared_from_this();
}
bool Connector::hovering() {
return partner.expired() && CheckCollisionPointCircle(SCREEN_MOUSE, position, radius);
}
void Connector::setParent(std::shared_ptr<GraphItem> _parent){
parent = _parent;
}
void Connector::setPosition(Vec2Df pos) {
center_position = pos;
parent_position = pos;
}
void Connector::setOrbit(float r) {
orbit = r;
}
void Connector::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(connected) {
state->pushEvent({getPtr(), APP::Interaction::CONNECT});
connected = false;
}
if(partner.expired()) {
position = center_position.orbitAlongVector(SCREEN_MOUSE, orbit);
position_text = center_position.orbitAlongVector(SCREEN_MOUSE, orbit+10);
}
if(!partner.expired()) {
position = center_position.orbitAlongVector(partner.lock()->position, orbit);
position_text = center_position.orbitAlongVector(partner.lock()->position, orbit+10);
}
if(new_partner) {
new_partner = false;
state->pushEvent({getPtr(), APP::Interaction::CONNECT});
}
//parent_position = std::dynamic_pointer_cast<NODES::Node>(parent.lock())->position;
}
void Connector::draw() {
BASE::Connectable::draw();
float opacity = Clamp((400.0f - Clamp(position.dist(SCREEN_MOUSE), 0.0f, 400.0f)) / 400.0f, 0.0f, 1.0f);
float rotation = 0.0f;
if(partner.expired()) rotation = HELPER::GetRotation(parent_position, SCREEN_MOUSE) + 90.0f;
if(!partner.expired()) {
rotation = HELPER::GetRotation(parent_position, partner.lock()->position) + 90.0f;
opacity = 1.0f;
}
if(connecting) opacity = 1.0f;
if(partner.expired() && !connecting) ViTexButton(SCREEN_MOUSE, position, rotation, 8.0f,
TEXTURES::tex_connector, TEXTURES::tex_connector_hover, TEXTURES::tex_connector_pressed,
ColorLerp(Fade(VS_COLOR_WHITE, .0f), Fade(VS_COLOR_WHITE, 1.0f), opacity));
if(!partner.expired() || connecting) ViTexButton(SCREEN_MOUSE, position, rotation, 8.0f,
TEXTURES::tex_connector_connected, TEXTURES::tex_connector_hover,
ColorLerp(Fade(VS_COLOR_WHITE, .0f), Fade(VS_COLOR_WHITE, 1.0f), opacity));
//int partner_id = -1;
//if(!partner.expired()) partner_id = std::dynamic_pointer_cast<Connector>(partner.lock())->id;
//DrawTextPro(GetFontDefault(), std::format("{} {}", id, partner_id).c_str(), position_text, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK);
//DrawText(std::format("{}", id), position + Vec2Df(100.0f,100.0f), 8.0f, Color(255, 0, 0));
}
}
+15 -53
View File
@@ -3,12 +3,10 @@
#include "connectable.h"
#include "datatypes.h"
#include "graph-item.h"
//#include "interaction-event.h"
#include "raymath.h"
#include "state.h"
#include "vmath.h"
#include <fcntl.h>
#include <memory>
#include <raylib.h>
namespace VISEQ::GRAPH {
class Connector : public BASE::Connectable, public GraphItem, public std::enable_shared_from_this<Connector> {
@@ -18,6 +16,8 @@ namespace VISEQ::GRAPH {
Connector(std::shared_ptr<GraphItem> parent, Vec2Df center_position, float orbit) : orbit(orbit) {
setPosition(center_position);
setParent(parent);
_id++;
id = _id;
}
std::weak_ptr<GraphItem> parent;
@@ -27,58 +27,20 @@ namespace VISEQ::GRAPH {
float radius = 8.0f;
float orbit = 20.0f;
Vec2Df center_position;
Vec2Df parent_position;
Vec2Df position_text;
inline static int _id = 0;
int id = 0;
GraphItemType getType() override{
return GraphItemType::CONNECTOR;
}
GraphItemType getType() override;
std::shared_ptr<Connector> getPtr(){
return shared_from_this();
}
std::shared_ptr<Connector> getPtr();
virtual bool hovering() override {
return partner.expired() && CheckCollisionPointCircle(SCREEN_MOUSE, position, radius);
}
void setParent(std::shared_ptr<GraphItem> _parent){
parent = _parent;
}
void setPosition(Vec2Df pos) override {
center_position = pos;
}
void setOrbit(float r) {
orbit = r;
}
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(connected) {
state->pushEvent({getPtr(), APP::Interaction::CONNECT});
connected = false;
}
if(partner.expired()) position = center_position.orbitAlongVector(SCREEN_MOUSE, orbit);
if(!partner.expired()) position = center_position .orbitAlongVector(partner.lock()->position, orbit);
if(new_partner) {
new_partner = false;
state->pushEvent({getPtr(), APP::Interaction::CONNECT});
}
}
void draw() override {
BASE::Connectable::draw();
float opacity = 150.0f - Clamp(position.dist(SCREEN_MOUSE), 0.0f, 150.0f);
if(hovering()){
DrawCircle(position.x, position.y, 8.0f, (Color){108, 203, 237, 255});
} else {
DrawCircle(position.x, position.y, 8.0f, (Color){108, 153, 187, (unsigned char)opacity});
}
}
virtual bool hovering() override;
void setParent(std::shared_ptr<GraphItem> _parent);
void setPosition(Vec2Df pos) override;
void setOrbit(float r);
void update() override;
void draw() override;
};
}
+25 -1
View File
@@ -15,6 +15,7 @@
#define VS_COLOR_PURPLE (Color){158, 134, 200, 255}
#define VS_COLOR_PINK (Color){176, 82, 121, 255}
#define VS_COLOR_BLUE (Color){108, 153, 187, 255}
#define VS_COLOR_LIGHTBLUE (Color){143, 206, 255, 255}
#define VS_COLOR_RED (Color){195, 83, 85, 255}
#define VS_COLOR_BROWN (Color){84, 75, 61, 255}
@@ -22,13 +23,36 @@
#include "nlohmann/json.hpp"
using json = nlohmann::json;
#include <raylib.h>
#include <cmath>
#include <math.h>
#include "libremidi/libremidi.hpp"
#define RANGE_FILTER(L) std::ranges::views::filter(L)
namespace VISEQ::APP{
inline Font inter;
}
namespace VISEQ::TEXTURES {
inline Texture2D tex_connector;
inline Texture2D tex_connector_hover;
inline Texture2D tex_connector_pressed;
inline Texture2D tex_connector_connected;
inline Texture2D tex_delete;
inline Texture2D tex_split;
inline Texture2D tex_blocker_open;
inline Texture2D tex_blocker_closed;
inline Texture2D tex_delete_hover;
inline Texture2D tex_split_hover;
inline Texture2D tex_blocker_open_hover;
inline Texture2D tex_blocker_closed_hover;
inline Texture2D tex_delete_pressed;
inline Texture2D tex_split_pressed;
inline Texture2D tex_blocker_open_pressed;
inline Texture2D tex_blocker_closed_pressed;
}
namespace VISEQ::GRAPH::NODES {
enum Distribution {GEOMETRIC, ROUNDROBIN, RANDOM};
}
+1 -1
View File
@@ -32,7 +32,7 @@ namespace VISEQ::GRAPH::EDGES {
float speed = 0;
float bpm = 120.0f;
float connector_offset = 8.0f;
float connector_offset = 2.0f;
float switches_offset = 25.0f;
virtual bool GActive();
+60 -43
View File
@@ -2,6 +2,7 @@
#include "config-node.h"
#include "datatypes.h"
#include "helper.h"
#include "state.h"
#include "vmath.h"
#include "raylib.h"
@@ -52,7 +53,7 @@ namespace VISEQ::GRAPH{
void GraphManager::addNode(SPTR_Node node){
std::cout << "added node " << node->id << std::endl;
nodes.insert(nodes.end(), node);
node->update();
nodes.back()->update();
}
void GraphManager::addConnection(WPTR_Node from, WPTR_Node to){
@@ -63,10 +64,19 @@ namespace VISEQ::GRAPH{
edges.back()->edgeType = et;
edges.back()->start = from;
edges.back()->end = to;
from.lock()->update();
to.lock()->update();
from.lock()->connectors.back()->partner = to.lock()->getConnector();
to.lock()->connectors.back()->partner = from.lock()->getConnector();
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());
}
}
@@ -147,6 +157,12 @@ namespace VISEQ::GRAPH{
}
}
void GraphManager::cycleModeOfSelectedNodes(bool direction){
for(auto n: selected_nodes){
n->cycleMode(direction);
}
}
void GraphManager::reset(){
counter.clear();
}
@@ -191,10 +207,15 @@ namespace VISEQ::GRAPH{
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));
VEC_WPTR_Edge GraphManager::getPassableConnections(WPTR_Node node){
VEC_WPTR_Edge passable_connections = node.lock()->connections | RANGE_FILTER([node](WPTR_Edge c){
return (c.lock()->getEdgeType() == EdgeType::MIDIEDGE) && ((c.lock()->start.lock() == node.lock() && c.lock()->pass_forward) || (c.lock()->end.lock() == node.lock() && c.lock()->pass_backward));
}) | std::ranges::to<VEC_WPTR_Edge>();
return passable_connections;
}
WPTR_Node GraphManager::next(WPTR_Node current, WPTR_Node last){
VEC_WPTR_Edge possible_connections = getPassableConnections(current);
if(possible_connections.empty()) return SPTR_Node(nullptr);
@@ -347,41 +368,41 @@ namespace VISEQ::GRAPH{
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);
float from_radius = c->start.lock()->radius;
float to_radius = c->end.lock()->radius;
Vec2Df from_to_handle = c->start.lock()->position - ((c->start.lock()->position - c->end.lock()->position)).norm() * (from_radius + 35.0f);
Vec2Df to_from_handle = c->end.lock()->position - ((c->end.lock()->position - c->start.lock()->position)).norm() * (to_radius + 35.0f);
//Vec2Df middle_handle_2 = c->end.lock()->position - ((c->end.lock()->position - c->start.lock()->position)).norm()*65.0f;
//Vec2Df middle_handle = c->start.lock()->position - ((c->start.lock()->position - c->end.lock()->position)).norm()*65.0f;
Vec2Df center_handle = c->end.lock()->position - ((c->end.lock()->position - c->start.lock()->position))*0.5f;
Vec2Df delete_position = center_handle + (c->start.lock()->position - c->end.lock()->position).norm().orth() * 12.0f;
Vec2Df split_position = center_handle - (c->start.lock()->position - c->end.lock()->position).norm().orth() * 12.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 = 150.0f - Clamp(center_handle.dist(mouse_pos), 0.0f, 130.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;
float opacity_normalized = Clamp((float)opacity / 255.0f, .0f, 1.0f);
//float opacity_from_normalized = Clamp((float)opacity_from / 255.0f, .0f, 1.0f);
//float opacity_to_normalized = Clamp((float)opacity_to / 255.0f, .0f, 1.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);
{
if(state->draw_edge_lengths){
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 middle_handle = c->start.lock()->position + (c->end.lock()->position - c->start.lock()->position) * ((d - text_length.x)*0.35f)/d;
Vec2Df middle_handle_frac = c->start.lock()->position + (c->end.lock()->position - c->start.lock()->position) * ((d - text_length_frac.x)*0.35f)/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;
@@ -389,38 +410,34 @@ namespace VISEQ::GRAPH{
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(ViTexButton(mouse_pos, from_to_handle, HELPER::GetRotation(from_to_handle, to_from_handle) + 90.0f, 8.0f,
c->pass_forward ? TEXTURES::tex_blocker_open : TEXTURES::tex_blocker_closed,
c->pass_forward ? TEXTURES::tex_blocker_open_hover : TEXTURES::tex_blocker_closed_hover,
c->pass_forward ? TEXTURES::tex_blocker_open_pressed : TEXTURES::tex_blocker_closed_pressed,
ColorLerp(Fade(VS_COLOR_WHITE, .0f), Fade(VS_COLOR_WHITE, 1.0f), 1.0))) c->pass_forward = !c->pass_forward;
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(ViTexButton(mouse_pos, to_from_handle, HELPER::GetRotation(to_from_handle, from_to_handle) + 90.0f, 8.0f,
c->pass_backward ? TEXTURES::tex_blocker_open : TEXTURES::tex_blocker_closed,
c->pass_backward ? TEXTURES::tex_blocker_open_hover : TEXTURES::tex_blocker_closed_hover,
c->pass_backward ? TEXTURES::tex_blocker_open_pressed : TEXTURES::tex_blocker_closed_pressed,
ColorLerp(Fade(VS_COLOR_WHITE, .0f), Fade(VS_COLOR_WHITE, 1.0f), 1.0))) c->pass_backward = !c->pass_backward;
if(ViButton(mouse_pos, flip_handle_2.x, flip_handle_2.y, 8.0f, (Color){232, 125, 62, opacity} )) splitEdge(c);
if(ViTexButton(mouse_pos, split_position, HELPER::GetRotation(from_to_handle, to_from_handle) + 90.0f, 8.0f,
TEXTURES::tex_split, TEXTURES::tex_split_hover, TEXTURES::tex_split_pressed, ColorLerp(Fade(VS_COLOR_WHITE, .0f), Fade(VS_COLOR_WHITE, 1.0f), opacity_normalized) )) 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);
if(IsWindowReady()){
if(ViTexButton(mouse_pos, delete_position, HELPER::GetRotation(from_to_handle, to_from_handle), 8.0f,
TEXTURES::tex_delete, TEXTURES::tex_delete_hover, TEXTURES::tex_delete_pressed,
ColorLerp(Fade(VS_COLOR_WHITE, .0f), Fade(VS_COLOR_WHITE, 1.0f), opacity_normalized) )) removeEdge(c);
}
}
}
+2
View File
@@ -56,6 +56,7 @@ namespace VISEQ::GRAPH {
SPTR_Node copyNode(SPTR_Node n, Vec2Df position);
SPTR_Node copyNode(std::shared_ptr<MidiNode> n, Vec2Df position);
void setNodeMode(int mode);
void cycleModeOfSelectedNodes(bool direction);
void reset();
WPTR_Node getNode(int i);
WPTR_Node getFirstNode();
@@ -66,6 +67,7 @@ namespace VISEQ::GRAPH {
SPTR_Node getClosestNode(Vec2Df position);
VEC_WPTR_Node getNodesByID(int _id);
WPTR_Edge lastEdge();
VEC_WPTR_Edge getPassableConnections(WPTR_Node node);
WPTR_Node next(WPTR_Node current, WPTR_Node last);
void splitEdge(SPTR_Edge edge);
void splitEdges();
+4
View File
@@ -15,4 +15,8 @@ namespace VISEQ::HELPER{
float rotation = Vector2Angle((Vector2){1,0}, (end - start)) * RAD2DEG;
DrawTextPro(GetFontDefault(), text.c_str(), text_position, (Vector2){0,0}, rotation, size, 2.0f, col);
}
inline float GetRotation(Vec2Df start, Vec2Df end){
return Vector2Angle((Vector2){1,0}, (end - start)) * RAD2DEG;
}
}
+7 -4
View File
@@ -26,13 +26,16 @@ namespace VISEQ::INTERACTION{
}
if(!KEYS_DOWN.shift && IsKeyPressed(KEY_TAB)) {
state->node_mode = (state->node_mode + 1) % 7;
gm->cycleModeOfSelectedNodes(true);
}
if(KEYS_DOWN.shift && IsKeyPressed(KEY_TAB)) {
state->node_mode -= state->node_mode > 0 ? 1 : -6;
gm->cycleModeOfSelectedNodes(false);
}
if(IsKeyPressed(KEY_V)){
state->draw_edge_lengths = !state->draw_edge_lengths;
}
}
void InteractionManager::draw(){
@@ -149,8 +152,8 @@ namespace VISEQ::INTERACTION{
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;
//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){
+3 -1
View File
@@ -52,13 +52,15 @@ int main(){
app.init();
app.ui.init();
app.loadTextures();
app.loadState();
app.startSignals();
while(!WindowShouldClose()){
app.update();
app.draw();
if(IsWindowReady()) app.draw();
}
app.close();
+181 -173
View File
@@ -3,7 +3,8 @@
#include "config-node.h"
#include "datatypes.h"
#include "edge.h"
#include "node-handle.h"
#include "interaction-event.h"
#include "midi.h"
#include "node.h"
#include "raylib.h"
#include "state.h"
@@ -14,40 +15,10 @@
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;
midiNote.note = 64;
midiNote.channel = 1;
midiNote.gate = 250;
midiNote.velocity = 64;
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(){
@@ -134,40 +105,49 @@ namespace VISEQ::GRAPH::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){
void MidiNode::modMidiChannel(int incr){
}
void MidiNode::modMidiNoteValue(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){
void MidiNode::modMidiNoteVelocity(int incr){
midiNote.velocity += incr;
if(midiNote.velocity < 0 ) midiNote.velocity = 127;
if(midiNote.velocity > 127 ) midiNote.velocity = 0;
}
void MidiNode::modMidiNoteGate(int incr){
midiNote.gate += incr;
if(midiNote.gate < 0 ) midiNote.gate = 0;
if(midiNote.gate > 20000) midiNote.gate = 20000;
}
void MidiNode::modMidiCCValue(int incr){
midiCC.value += incr;
if(midiCC.value < 0 ) midiCC.value = 127;
if(midiCC.value > 127 ) midiCC.value = 0;
}
void MidiNode::modMidiCCControl(int incr){
midiCC.control += incr;
if(midiCC.control < 0 ) midiCC.control = 127;
if(midiCC.control > 127 ) midiCC.control = 0;
}
void MidiNode::modMidiPC(int incr){
void MidiNode::modMidiPCValue(int incr){
midiPC.value += incr;
if(midiPC.value < 0 ) midiPC.value = 127;
if(midiPC.value > 127 ) midiPC.value = 0;
}
void MidiNode::modMidiPCProgram(int incr){
midiPC.program += incr;
if(midiPC.program < 0 ) midiPC.program = 127;
if(midiPC.program > 127 ) midiPC.program = 0;
@@ -185,88 +165,73 @@ namespace VISEQ::GRAPH::NODES {
if(midiAftertouch.value > 127 ) midiAftertouch.value = 127;
}
void MidiNode::modMidiPolyPressure(int incr){
void MidiNode::modMidiPolyPressureValue(int incr){
midiPolyPressure.value += incr;
if(midiPolyPressure.value < 0 ) midiPolyPressure.value = 0;
if(midiPolyPressure.value > 127 ) midiPolyPressure.value = 127;
if(midiPolyPressure.value > 16383 ) midiPolyPressure.value = 16383;
}
void MidiNode::modMidiPolyPressureNote(int incr){
midiPolyPressure.note += incr;
if(midiPolyPressure.note < 0 ) midiPolyPressure.note = VISEQ::MIDI::Notes.size()-1;
if(midiPolyPressure.note >= (int)VISEQ::MIDI::Notes.size()) midiPolyPressure.note = 0;
}
void MidiNode::interact(){
float wheel = GetMouseWheelMove();
if(KEYS_DOWN.ctrl && !KEYS_DOWN.alt){
switch(state->node_mode){
case 0:{
modMidiNote(wheel);
switch(mode){
case NOTE:{
if(state->isModChain(APP::CTRL)) modMidiNoteValue(wheel);
if(state->isModChain(APP::CTRL, APP::SHIFT)) modMidiNoteValue(wheel*12);
if(state->isModChain(APP::CTRL, APP::ALT)) modMidiNoteValue(wheel*12);
if(state->isModChain(APP::CTRL, APP::SHIFT, APP::ALT)) modMidiNoteValue(wheel*24);
if(state->isModChain(APP::CTRL, APP::ALT, APP::SHIFT)) modMidiNoteValue(wheel*24);
if(state->isModChain(APP::SHIFT)) modMidiNoteVelocity(wheel);
if(state->isModChain(APP::SHIFT, APP::CTRL)) modMidiNoteVelocity(wheel*16);
if(state->isModChain(APP::SHIFT, APP::ALT)) modMidiNoteVelocity(wheel*16);
if(state->isModChain(APP::SHIFT, APP::CTRL, APP::ALT)) modMidiNoteVelocity(wheel*32);
if(state->isModChain(APP::SHIFT, APP::ALT, APP::CTRL)) modMidiNoteVelocity(wheel*32);
if(state->isModChain(APP::ALT)) modMidiNoteGate(wheel*100);
if(state->isModChain(APP::ALT, APP::CTRL)) modMidiNoteGate(wheel*1000);
if(state->isModChain(APP::ALT, APP::SHIFT)) modMidiNoteGate(wheel*10);
if(state->isModChain(APP::ALT, APP::SHIFT, APP::CTRL)) modMidiNoteGate(wheel);
break;}
case 1:{
modMidiCC(wheel);
case CC:{
if(KEYS_DOWN.ctrl && !KEYS_DOWN.shift && !KEYS_DOWN.alt) modMidiCCValue(wheel);
if(!KEYS_DOWN.ctrl && KEYS_DOWN.shift && !KEYS_DOWN.alt) modMidiCCControl(wheel);
break;}
case 2:{
modMidiPC(wheel);
case PC:{
if(KEYS_DOWN.ctrl && !KEYS_DOWN.shift && !KEYS_DOWN.alt) modMidiPCValue(wheel);
break;}
case 3:{
modMidiPitchBend(wheel);
case PB:{
if(!KEYS_DOWN.ctrl && !KEYS_DOWN.shift && KEYS_DOWN.alt) modMidiPitchBend(wheel*64);
if(KEYS_DOWN.ctrl && !KEYS_DOWN.shift && KEYS_DOWN.alt) modMidiPitchBend(wheel*2048);
if(!KEYS_DOWN.ctrl && KEYS_DOWN.shift && KEYS_DOWN.alt) modMidiPitchBend(wheel*256);
if(KEYS_DOWN.ctrl && KEYS_DOWN.shift && KEYS_DOWN.alt) modMidiPitchBend(wheel);
break;}
case 4:{
modMidiAftertouch(wheel);
case AT:{
if(!KEYS_DOWN.ctrl && !KEYS_DOWN.shift && KEYS_DOWN.alt) modMidiAftertouch(wheel*10);
if(KEYS_DOWN.ctrl && !KEYS_DOWN.shift && KEYS_DOWN.alt) modMidiAftertouch(wheel*100);
if(KEYS_DOWN.ctrl && KEYS_DOWN.shift && KEYS_DOWN.alt) modMidiAftertouch(wheel);
break;}
case 5:{
modMidiPolyPressure(wheel);
case PP:{
if(!KEYS_DOWN.ctrl && KEYS_DOWN.shift && !KEYS_DOWN.alt) modMidiPolyPressureNote(wheel);
if(!KEYS_DOWN.ctrl && !KEYS_DOWN.shift && !KEYS_DOWN.alt) modMidiPolyPressureNote(wheel*10);
if(!KEYS_DOWN.ctrl && !KEYS_DOWN.shift && KEYS_DOWN.alt) modMidiPolyPressureValue(wheel*64);
if(KEYS_DOWN.ctrl && !KEYS_DOWN.shift && KEYS_DOWN.alt) modMidiPolyPressureValue(wheel*2048);
if(!KEYS_DOWN.ctrl && KEYS_DOWN.shift && KEYS_DOWN.alt) modMidiPolyPressureValue(wheel*256);
if(KEYS_DOWN.ctrl && KEYS_DOWN.shift && KEYS_DOWN.alt) modMidiPolyPressureValue(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;
@@ -278,60 +243,127 @@ namespace VISEQ::GRAPH::NODES {
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});
switch((Handles)mode){
case NOTE:{
radius = (float)midiNote.velocity * 0.2f + 15.0f;
break;}
case CC:{
radius = (float)midiCC.value * 0.2f + 15.0f;
break;}
case PC:{
radius = (float)midiPC.value * 0.2f + 15.0f;
break;}
case PB:{
radius = (float)midiPitchBend.value * 0.002f + 15.0f;
break;}
case AT:{
radius = (float)midiAftertouch.value * 0.002f + 15.0f;
break;}
case PP:{
radius = (float)midiPolyPressure.value * 0.002f + 15.0f;
break;}
}
if(!selected) DrawCircle(position.x, position.y, radius, state->settings.mode_colors[mode]);
if(selected) DrawCircle(position.x, position.y, radius, VS_COLOR_LIGHTBLUE);
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(!selected && !pickedUp() && !hovering()) DrawRing(position, radius, radius + 3, 0, 360, 24, VS_COLOR_GREY);
DrawRing(position, radius, radius + 3, 0, 360, 24, ColorLerp(VS_COLOR_GREY, VS_COLOR_RED, trigger_alpha));
if(selected) DrawRing(position, radius, radius + 3, 0, 360, 24, VS_COLOR_RED);
if(picked_up || (in_selection_rect && MOUSE_LEFT.down) || selected) DrawRing(position, radius, radius + 3, 0, 360, 24, VS_COLOR_LIGHTBLUE);
if(hovering()) DrawRing(position, radius, radius + 3, 0, 360, 24, VS_COLOR_PINK);
DrawCircleLinesV(position, radius + 3, VS_COLOR_LIGHTGREY);
//if(hovering()) DrawCircleLinesV(position, radius + 4, VS_COLOR_PINK);
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(selected && 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){
VMATH::Vec2D<float> desc_position = position + VMATH::Vec2D<float>(radius + 7.0f, radius + 7.0f);
switch(mode){
case NOTE:{
DrawCenteredText(MIDI::Notes[getNotes().note], position, 12, WHITE);
if(state->isModChainStart(APP::CTRL) || state->modifier_chain.size() == 0) DrawCenteredText(MIDI::Notes[getNotes().note], position, 14, VS_COLOR_DARKGREY);
if(state->isModChainStart(APP::SHIFT)) DrawCenteredText(std::format("{}", getNotes().velocity), position, 12, VS_COLOR_DARKGREY);
if(state->isModChain(APP::ALT)) DrawCenteredText(std::format("{:.1f}s", (float)getNotes().gate / 1000.f), position, 12, VS_COLOR_DARKGREY);
if(state->isModChainStart(APP::ALT, APP::CTRL)) DrawCenteredText(std::format("{:.2f}s", (float)getNotes().gate / 1000.f), position, 12, VS_COLOR_DARKGREY);
if(state->isModChain(APP::ALT, APP::SHIFT)) DrawCenteredText(std::format("{:.2f}s", (float)getNotes().gate / 1000.f), position, 12, VS_COLOR_DARKGREY);
if(state->isModChain(APP::ALT, APP::SHIFT, APP::CTRL)) DrawCenteredText(std::format("{:.3f}s", (float)getNotes().gate / 1000.f), position, 12, VS_COLOR_DARKGREY);
if(selected || hovering()){
if(state->isModChain(APP::CTRL)) DrawText("NOTE +1", desc_position, 8, VS_COLOR_WHITE);
if(state->isModChain(APP::CTRL, APP::SHIFT)) DrawText("NOTE +12", desc_position, 8, VS_COLOR_WHITE);
if(state->isModChain(APP::CTRL, APP::ALT)) DrawText("NOTE +12", desc_position, 8, VS_COLOR_WHITE);
if(state->isModChain(APP::CTRL, APP::SHIFT, APP::ALT)) DrawText("NOTE +24", desc_position, 8, VS_COLOR_WHITE);
if(state->isModChain(APP::CTRL, APP::ALT, APP::SHIFT)) DrawText("NOTE +24", desc_position, 8, VS_COLOR_WHITE);
if(state->isModChain(APP::SHIFT)) DrawText("VELOCITY +1", desc_position, 8, VS_COLOR_WHITE);
if(state->isModChain(APP::SHIFT, APP::CTRL)) DrawText("VELOCITY +16", desc_position, 8, VS_COLOR_WHITE);
if(state->isModChain(APP::SHIFT, APP::ALT)) DrawText("VELOCITY +16", desc_position, 8, VS_COLOR_WHITE);
if(state->isModChain(APP::SHIFT, APP::CTRL, APP::ALT)) DrawText("VELOCITY +32", desc_position, 8, VS_COLOR_WHITE);
if(state->isModChain(APP::SHIFT, APP::ALT, APP::CTRL)) DrawText("VELOCITY +32", desc_position, 8, VS_COLOR_WHITE);
if(state->isModChain(APP::ALT)) DrawText("GATE 0.1s", desc_position, 8, VS_COLOR_WHITE);
if(state->isModChainStart(APP::ALT, APP::CTRL)) DrawText("GATE 1.0s", desc_position, 8, VS_COLOR_WHITE);
if(state->isModChain(APP::ALT, APP::SHIFT)) DrawText("GATE 0.01s", desc_position, 8, VS_COLOR_WHITE);
if(state->isModChain(APP::ALT, APP::SHIFT, APP::CTRL)) DrawText("GATE 0.001s", desc_position, 8, VS_COLOR_WHITE);
}
break;}
case CC:{
DrawCenteredText(std::format("{}", getCC().value), position, 12, WHITE);
if(!KEYS_DOWN.shift) DrawCenteredText(std::format("{}", getCC().value), position, 12, VS_COLOR_DARKGREY);
if(!KEYS_DOWN.ctrl && KEYS_DOWN.shift && !KEYS_DOWN.alt) DrawCenteredText(std::format("{}", getCC().control), position, 12, VS_COLOR_WHITE);
if(selected){
if(!KEYS_DOWN.ctrl && !KEYS_DOWN.shift && !KEYS_DOWN.alt) DrawText("CC", desc_position, 8, VS_COLOR_WHITE);
if(KEYS_DOWN.ctrl && !KEYS_DOWN.shift && !KEYS_DOWN.alt) DrawText("VALUE", desc_position, 8, VS_COLOR_WHITE);
if(!KEYS_DOWN.ctrl && KEYS_DOWN.shift && !KEYS_DOWN.alt) DrawText("CONTROL", desc_position, 8, VS_COLOR_WHITE);
}
break;}
case PC:{
DrawCenteredText(std::format("{}", getPC().value), position, 12, WHITE);
if(!KEYS_DOWN.shift) DrawCenteredText(std::format("{}", getPC().value), position, 12, VS_COLOR_DARKGREY);
if(selected){
if(!KEYS_DOWN.ctrl && !KEYS_DOWN.shift && !KEYS_DOWN.alt) DrawText("PROGRAM CHANGE", desc_position, 8, VS_COLOR_WHITE);
if(KEYS_DOWN.ctrl && !KEYS_DOWN.shift && !KEYS_DOWN.alt) DrawText("PROGRAM", desc_position, 8, VS_COLOR_WHITE);
}
break;}
case PB:{
DrawCenteredText(std::format("{}", round(((float)getPitchBend().value / 8192.0f) * 100)), position, 12, WHITE);
DrawCenteredText(std::format("{}", getPitchBend().value), position, 10, VS_COLOR_DARKGREY);
if(selected){
if(!KEYS_DOWN.ctrl && !KEYS_DOWN.shift && !KEYS_DOWN.alt) DrawText("PITCHBEND", desc_position, 8, VS_COLOR_WHITE);
if(!KEYS_DOWN.ctrl && !KEYS_DOWN.shift && KEYS_DOWN.alt) DrawText("64", desc_position, 8, VS_COLOR_WHITE);
if(KEYS_DOWN.ctrl && !KEYS_DOWN.shift && KEYS_DOWN.alt) DrawText("2048", desc_position, 8, VS_COLOR_WHITE);
if(!KEYS_DOWN.ctrl && KEYS_DOWN.shift && KEYS_DOWN.alt) DrawText("256", desc_position, 8, VS_COLOR_WHITE);
if(KEYS_DOWN.ctrl && KEYS_DOWN.shift && KEYS_DOWN.alt) DrawText("1", desc_position, 8, VS_COLOR_WHITE);
}
break;}
case AT:{
DrawCenteredText(std::format("{}", getAftertouch().value), position, 12, WHITE);
DrawCenteredText(std::format("{}", getAftertouch().value), position, 12, VS_COLOR_DARKGREY);
if(selected){
if(!KEYS_DOWN.ctrl && !KEYS_DOWN.shift && !KEYS_DOWN.alt) DrawText("AFTERTOUCH", desc_position, 8, VS_COLOR_WHITE);
if(!KEYS_DOWN.ctrl && !KEYS_DOWN.shift && KEYS_DOWN.alt) DrawText("10", desc_position, 8, VS_COLOR_WHITE);
if(KEYS_DOWN.ctrl && !KEYS_DOWN.shift && KEYS_DOWN.alt) DrawText("100", desc_position, 8, VS_COLOR_WHITE);
if(KEYS_DOWN.ctrl && KEYS_DOWN.shift && KEYS_DOWN.alt) DrawText("1", desc_position, 8, VS_COLOR_WHITE);
}
break;}
case PP:{
DrawCenteredText(std::format("{}", getPolyPressure().value), position, 12, WHITE);
break;}
}
if(!KEYS_DOWN.ctrl && KEYS_DOWN.shift && !KEYS_DOWN.alt) DrawCenteredText(MIDI::Notes[getPolyPressure().note], position, 12, VS_COLOR_DARKGREY);
if(!KEYS_DOWN.ctrl && !KEYS_DOWN.shift && KEYS_DOWN.alt) DrawCenteredText(std::format("{}", getPolyPressure().value), position, 12, VS_COLOR_DARKGREY);
if(KEYS_DOWN.ctrl && !KEYS_DOWN.shift && KEYS_DOWN.alt) DrawCenteredText(std::format("{}", getPolyPressure().value), position, 12, VS_COLOR_DARKGREY);
if(KEYS_DOWN.ctrl && KEYS_DOWN.shift && KEYS_DOWN.alt) DrawCenteredText(std::format("{}", getPolyPressure().value), position, 12, VS_COLOR_DARKGREY);
if(selected){
if(!KEYS_DOWN.ctrl && !KEYS_DOWN.shift && !KEYS_DOWN.alt) DrawText("POLYPRESSURE", desc_position, 8, VS_COLOR_WHITE);
if(!KEYS_DOWN.ctrl && !KEYS_DOWN.shift && KEYS_DOWN.alt) DrawText("64", desc_position, 8, VS_COLOR_WHITE);
if(KEYS_DOWN.ctrl && !KEYS_DOWN.shift && KEYS_DOWN.alt) DrawText("2048", desc_position, 8, VS_COLOR_WHITE);
if(!KEYS_DOWN.ctrl && KEYS_DOWN.shift && KEYS_DOWN.alt) DrawText("256", desc_position, 8, VS_COLOR_WHITE);
if(KEYS_DOWN.ctrl && KEYS_DOWN.shift && KEYS_DOWN.alt) DrawText("1", desc_position, 8, VS_COLOR_WHITE);
}
break;}
};
if(highlighted) DrawCircleLinesV(position, radius+2, (Color){255, 0, 0, 200});
}
@@ -340,15 +372,6 @@ namespace VISEQ::GRAPH::NODES {
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},
@@ -360,7 +383,6 @@ namespace VISEQ::GRAPH::NODES {
{"midiPitchBend", N.midiPitchBend},
{"midiAftertouch", N.midiAftertouch},
{"midiPolyPressure", N.midiPolyPressure},
{"handles", handles},
{"connections",conns}};
}
@@ -378,20 +400,6 @@ namespace VISEQ::GRAPH::NODES {
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){
+12 -6
View File
@@ -27,8 +27,6 @@ namespace VISEQ::GRAPH::NODES {
MIDI::MidiAftertouch midiAftertouch;
MIDI::MidiPolyPressure midiPolyPressure;
std::map<Handles, std::vector<std::shared_ptr<NodeHandle<int>>>> midiHandles;
public:
struct NodeMidiOut{
@@ -48,6 +46,8 @@ namespace VISEQ::GRAPH::NODES {
bool send_cc = false;
bool send_pc = false;
bool send_pitch_bend = false;
bool send_aftertouch = false;
bool send_polypressure = false;
int midi_out = -1;
@@ -77,12 +77,18 @@ namespace VISEQ::GRAPH::NODES {
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 modMidiChannel(int incr);
void modMidiNoteValue(int incr);
void modMidiNoteVelocity(int incr);
void modMidiNoteGate(int incr);
void modMidiCCValue(int incr);
void modMidiCCControl(int incr);
void modMidiPCValue(int incr);
void modMidiPCProgram(int incr);
void modMidiPitchBend(int incr);
void modMidiAftertouch(int incr);
void modMidiPolyPressure(int incr);
void modMidiPolyPressureValue(int incr);
void modMidiPolyPressureNote(int incr);
void interact();
void draw(Vec2Df mouse, Camera2D cam) override ;
+25 -2
View File
@@ -4,6 +4,7 @@
#include "raylib.h"
#include <iostream>
#include <libremidi/message.hpp>
#include <memory>
#include <sstream>
#include "datatypes.h"
@@ -61,12 +62,10 @@ namespace VISEQ::MIDI {
}
void Midi::note(int midi_out, int channel, int note, int velocity, int gate){
std::cout << "note on " << midi_out << " " << midi_outs.contains(midi_out) << std::endl;
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);
std::cout << "note on " << midi_out << std::endl;
}
}
}
@@ -79,6 +78,14 @@ namespace VISEQ::MIDI {
}
}
void Midi::pc(int midi_out, int channel, 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::program_change(channel, 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()){
@@ -87,6 +94,22 @@ namespace VISEQ::MIDI {
}
}
void Midi::aftertouch(int midi_out, int channel, int aftertouch_value){
if(midi_outs.contains(midi_out)){
if(midi_outs[midi_out]->is_port_open()){
midi_outs[midi_out]->send_message(libremidi::channel_events::aftertouch(channel, aftertouch_value));
}
}
}
void Midi::polypressure(int midi_out, int channel, int note, int polypressure_value){
if(midi_outs.contains(midi_out)){
if(midi_outs[midi_out]->is_port_open()){
midi_outs[midi_out]->send_message(libremidi::channel_events::poly_pressure(channel, note, polypressure_value));
}
}
}
void Midi::all_notes_off(){
for(auto &n : notes){
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));
+3
View File
@@ -50,7 +50,10 @@ namespace VISEQ::MIDI {
void close_midi_out(VirtualMidiOutPort *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 pc(int midi_out, int channel, int value);
void pitch_bend(int midi_out, int channel, int pitch_value);
void aftertouch(int midi_out, int channel, int aftertouch_value);
void polypressure(int midi_out, int channel, int note, int polypressure_value);
void all_notes_off();
void update();
};
+2 -2
View File
@@ -85,14 +85,14 @@ namespace VISEQ::GRAPH::NODES {
void ModNode::update() {
Node::update();
mode = state->node_mode;
//mode = state->node_mode;
}
void ModNode::interact(){
float wheel = GetMouseWheelMove();
if(KEYS_DOWN.ctrl && !KEYS_DOWN.alt){
switch(state->node_mode){
switch(mode){
case 0:{
midiNote.note += KEYS_DOWN.shift ? ((wheel > 0) - (wheel < 0)) * 12 : wheel;
break;}
+18
View File
@@ -63,6 +63,16 @@ namespace VISEQ::GRAPH::NODES {
c->update();
}
if(free_connectors == 0) addConnector();
in_selection_rect = CheckCollisionPointRec(position, state->selection);
}
std::weak_ptr<Connector> Node::getConnector(){
for(auto c : connectors){
if(c->partner.expired()) {
return c;
}
}
return connectors.back();
}
void Node::draw(Vec2Df mp, Camera2D cam) {}
@@ -73,6 +83,14 @@ namespace VISEQ::GRAPH::NODES {
}
}
void Node::cycleMode(bool direction){
if(direction){
mode = (mode + 1) % 6;
} else {
mode -= mode > 0 ? 1 : -5;
}
}
void to_json(json& j, const Node& N){
if(N.getNodeType() == NodeType::MIDINODE){
j = dynamic_cast<const MidiNode&>(N);
+2
View File
@@ -44,6 +44,7 @@ namespace VISEQ::GRAPH::NODES {
std::shared_ptr<Node> getPtr();
virtual bool hovering() override;
void addConnector();
std::weak_ptr<Connector> getConnector();
void removeConnector(std::weak_ptr<Connector> connector);
virtual NodeType getNodeType() const;
void deselect() override;
@@ -52,6 +53,7 @@ namespace VISEQ::GRAPH::NODES {
void update() override;
void draw(Vec2Df mp, Camera2D cam) override;
void draw() override;
virtual void cycleMode(bool direction);
friend void to_json(json& j, const Node& N);
friend void from_json(const json& j, Node& N);
+1
View File
@@ -16,6 +16,7 @@ namespace VISEQ::BASE {
bool erase = false;
Vec2Df selection_offset;
void update();
virtual bool clicked() override;
virtual bool select() = 0;
+6 -3
View File
@@ -86,24 +86,27 @@ namespace VISEQ::SIGNAL {
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){
if((int)diff.count() >= 100){
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) {
if(round(s->position.dist(s->next.lock()->position)) == 0) {
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->selected_midi_outs){
std::cout << m.second.name << " " << m.second.i << " " << m.second.enabled << std::endl;
if(m.second.enabled) {
std::cout << std::chrono::high_resolution_clock::now() << " : " << diff.count() << " " << m.second.name << " " << m.second.i << " " << m.second.enabled << std::endl;
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_pc) midi.pc(m.second.i, midinode->getPC().channel, midinode->getPC().value);
if(midinode->send_pitch_bend) midi.pitch_bend(m.second.i, midinode->getPitchBend().channel, midinode->getPitchBend().value);
if(midinode->send_aftertouch) midi.aftertouch(m.second.i, midinode->getAftertouch().channel, midinode->getAftertouch().value);
if(midinode->send_polypressure) midi.polypressure(m.second.i, midinode->getPolyPressure().channel, midinode->getPolyPressure().note, midinode->getPolyPressure().value);
}
}
s->last = s->next;
+50
View File
@@ -4,6 +4,7 @@
#include "raylib.h"
#include "vmath.h"
#include <memory>
#include <ostream>
namespace VISEQ::APP{
@@ -54,6 +55,29 @@ namespace VISEQ::APP{
keys_released.shift = IsKeyReleased(KEY_LEFT_SHIFT) || IsKeyReleased(KEY_RIGHT_SHIFT);
keys_released.alt = IsKeyReleased(KEY_LEFT_ALT) || IsKeyReleased(KEY_RIGHT_ALT);
int n_modifier = keys_down.alt + keys_down.ctrl + keys_down.shift;
if(modifier_chain.size() < n_modifier){
ModifierKeyNames modKey = keys_pressed.ctrl ? CTRL : keys_pressed.shift ? SHIFT : ALT;
modifier_chain.push_back(modKey);
//std::cout << modifier_chain.size() << " " << modKey << std::endl;
for(ModifierKeyNames n : modifier_chain){
std::cout << n << " ";
}
std::cout << std::endl;
}
if(modifier_chain.size() > n_modifier){
ModifierKeyNames modKey = keys_released.ctrl ? CTRL : keys_released.shift ? SHIFT : ALT;
auto it = std::remove(modifier_chain.begin(), modifier_chain.end(), modKey);
modifier_chain.erase(it);
//std::cout << modifier_chain.size() << " " << modKey << std::endl;
for(ModifierKeyNames n : modifier_chain){
std::cout << n << " ";
}
std::cout << std::endl;
}
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)};
@@ -73,4 +97,30 @@ namespace VISEQ::APP{
}
return State::instance;
}
bool State::isModChainEmpty(){
return modifier_chain.size() == 0;
}
bool State::isModChainStart(ModifierKeyNames first){
if(modifier_chain.size() < 1) return false;
return std::equal(modifier_chain.begin(), modifier_chain.begin() + 1, std::vector<ModifierKeyNames>{first}.begin());
}
bool State::isModChainStart(ModifierKeyNames first, ModifierKeyNames second){
if(modifier_chain.size() < 2) return false;
return std::equal(modifier_chain.begin(), modifier_chain.begin() + 2, std::vector<ModifierKeyNames>{first, second}.begin());
}
bool State::isModChain(ModifierKeyNames first){
return modifier_chain == std::vector<ModifierKeyNames>{first};
}
bool State::isModChain(ModifierKeyNames first, ModifierKeyNames second){
return modifier_chain == std::vector<ModifierKeyNames>{first, second};
}
bool State::isModChain(ModifierKeyNames first, ModifierKeyNames second, ModifierKeyNames third){
return modifier_chain == std::vector<ModifierKeyNames>{first, second, third};
}
}
+12
View File
@@ -49,6 +49,8 @@ namespace VISEQ::APP{
bool alt = false;
};
enum ModifierKeyNames {CTRL, SHIFT, ALT};
class State{
public:
State(){}
@@ -66,6 +68,7 @@ namespace VISEQ::APP{
int node_mode = 0;
bool context_menu = false;
bool draw_edge_lengths = false;
bool hovering = false;
@@ -79,6 +82,8 @@ namespace VISEQ::APP{
ModifierKeys keys_pressed;
ModifierKeys keys_released;
std::vector<ModifierKeyNames> modifier_chain;
Rectangle selection;
Rectangle bounding_box;
@@ -96,6 +101,13 @@ namespace VISEQ::APP{
static std::shared_ptr<State> get();
std::deque<InteractionEvent> event_queue;
bool isModChainEmpty();
bool isModChainStart(ModifierKeyNames first);
bool isModChainStart(ModifierKeyNames first, ModifierKeyNames second);
bool isModChain(ModifierKeyNames first);
bool isModChain(ModifierKeyNames first, ModifierKeyNames second);
bool isModChain(ModifierKeyNames first, ModifierKeyNames second, ModifierKeyNames third);
};
}
+27 -15
View File
@@ -6,6 +6,7 @@
#include "raygui/raygui.h"
#include "datatypes.h"
#include "vmath.h"
#include <memory>
#include <ranges>
@@ -14,6 +15,7 @@
namespace VISEQ::APP::UI{
void AppUI::init(){
GuiLoadStyleDark();
inter = LoadFontEx("resources/fonts/Inter_28pt-Bold.ttf", 96, 0, 250);
}
void AppUI::drawSideBar(){
@@ -77,9 +79,11 @@ namespace VISEQ::APP::UI{
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;
GuiLabel((Rectangle){175, (float)s_signal+70 + signal_configs*20, 40, 20}, std::format("{}",signal_bpm).c_str());
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++;
@@ -93,19 +97,20 @@ namespace VISEQ::APP::UI{
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);
}
// 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 );
//DrawText(text.c_str(), position.x-offset/2.0f, position.y-(float)size/2.0f+1, size, color );
DrawTextEx(inter, text.c_str(), Vec2Df(position.x-offset/2.0f, position.y-(float)size/2.0f+1), size, 2.0f, color);
}
void AppUI::drawContextMenu(Camera2D camera){
@@ -138,14 +143,21 @@ namespace VISEQ::APP::UI{
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);
GuiCheckBox((Rectangle){np.x + 160, np.y + y_channel, 20, 20}, "PITCH-B", &mn->send_pitch_bend);
int y_cc_note = y_channel + 120;
switch(state->node_mode){
case 0:{
int y_send_bools = y_channel + 30;
GuiCheckBox((Rectangle){np.x + 20, np.y + y_send_bools, 20, 20}, "PC", &mn->send_pc);
GuiCheckBox((Rectangle){np.x + 90, np.y + y_send_bools, 20, 20}, "AT", &mn->send_aftertouch);
GuiCheckBox((Rectangle){np.x + 160, np.y + y_send_bools, 20, 20}, "POLY-P", &mn->send_polypressure);
int y_cc_note = y_send_bools + 120;
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);
/*
switch(state->node_mode){
case 0:{
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());
@@ -194,7 +206,7 @@ namespace VISEQ::APP::UI{
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 16383);
mn->midiPitchBend.value = round(v);
break;}
}
}*/
}
}
}
+9 -1
View File
@@ -57,6 +57,14 @@ namespace VISEQ::VMATH{
return Vec2D(-y, x);
};
Vec2D rotate(float deg){
return Vec2D(cos(deg*DEG2RAD) * x, sin(deg*DEG2RAD) * y);
};
Vec2D rotateAround(Vec2D<T> v, float deg){
return Vec2D(cos(deg*DEG2RAD) * (x - v.x), sin(deg*DEG2RAD) * (y-v.y)) + v;
};
Vec2D norm() { return *this / length();};
Vec2D center(Vec2D v){
@@ -166,7 +174,7 @@ namespace VISEQ::VMATH{
template<typename T>
Vec2D<T> polarToCartesian(T r, T q) {
return Vec2D<T>{r * cos(q), r * sin(q)};
return Vec2D<T>{r * (T)cos(q), r * (T)sin(q)};
}
}