Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6aef199984 | |||
| c3543cffce | |||
| 9b75b36c41 | |||
| 996ed16c01 | |||
| 96c2bde0e1 | |||
| b2ed35a704 |
@@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "node.h"
|
||||||
|
|
||||||
|
namespace VISEQ::GRAPH::NODES{
|
||||||
|
|
||||||
|
class ControlNode: public Node{
|
||||||
|
public:
|
||||||
|
ControlNode(){}
|
||||||
|
|
||||||
|
float radius = 6;
|
||||||
|
|
||||||
|
NodeType nodeType = NodeType::CONTROLNODE;
|
||||||
|
|
||||||
|
Vec2Df center;
|
||||||
|
|
||||||
|
std::shared_ptr<ControlNode> getPtr(){
|
||||||
|
return std::static_pointer_cast<ControlNode>(shared_from_this());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual GRAPH::GraphItemType getType() override {
|
||||||
|
return GraphItemType::NODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual NodeType getNodeType() const override {return nodeType;}
|
||||||
|
|
||||||
|
virtual bool hovering() override {
|
||||||
|
return CheckCollisionPointCircle(SCREEN_MOUSE, position, radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void update() override {
|
||||||
|
Node::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void draw() override {
|
||||||
|
DrawCircleV(position, radius, VS_COLOR_SUN);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool select() override {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
+11
-5
@@ -35,7 +35,7 @@ namespace VISEQ::GRAPH::NODES {
|
|||||||
|
|
||||||
namespace VISEQ::GRAPH{
|
namespace VISEQ::GRAPH{
|
||||||
enum GraphItemType {NODE, EDGE, CONNECTOR, CANVAS, HANDLE, VS_BUTTON};
|
enum GraphItemType {NODE, EDGE, CONNECTOR, CANVAS, HANDLE, VS_BUTTON};
|
||||||
enum NodeType {OSCNODE, MIDINODE, CONFIGNODE};
|
enum NodeType {OSCNODE, MIDINODE, CONFIGNODE, CONTROLNODE};
|
||||||
enum EdgeType {MIDIEDGE, CONFIGEDGE};
|
enum EdgeType {MIDIEDGE, CONFIGEDGE};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,34 +46,40 @@ namespace VISEQ::MIDI{
|
|||||||
int note = 0;
|
int note = 0;
|
||||||
int velocity = 0;
|
int velocity = 0;
|
||||||
int gate = 0; // gate time in milliseconds
|
int gate = 0; // gate time in milliseconds
|
||||||
|
NLOHMANN_DEFINE_TYPE_INTRUSIVE(MidiNote, channel, note, velocity, gate);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MidiCC{
|
struct MidiCC{
|
||||||
int channel = 0;
|
int channel = 0;
|
||||||
int control = 0;
|
int control = 0;
|
||||||
int value = 0;
|
int value = 0;
|
||||||
|
NLOHMANN_DEFINE_TYPE_INTRUSIVE(MidiCC, channel, control, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MidiPC{
|
struct MidiPC{
|
||||||
int channel = 0;
|
int channel = 0;
|
||||||
int program = 0;
|
int program = 0;
|
||||||
int value = 0;
|
int value = 0;
|
||||||
|
NLOHMANN_DEFINE_TYPE_INTRUSIVE(MidiPC, channel, program, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MidiPitchBend{
|
struct MidiPitchBend{
|
||||||
int channel = 0;
|
int channel = 0;
|
||||||
int value = 0;
|
int value = 0;
|
||||||
|
NLOHMANN_DEFINE_TYPE_INTRUSIVE(MidiPitchBend, channel, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MidiAftertouch{
|
struct MidiAftertouch{
|
||||||
int channel = 0;
|
int channel = 0;
|
||||||
int value = 0;
|
int value = 0;
|
||||||
|
NLOHMANN_DEFINE_TYPE_INTRUSIVE(MidiAftertouch, channel, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MidiPolyPressure{
|
struct MidiPolyPressure{
|
||||||
int channel = 0;
|
int channel = 0;
|
||||||
int note = 0;
|
int note = 0;
|
||||||
int value = 0;
|
int value = 0;
|
||||||
|
NLOHMANN_DEFINE_TYPE_INTRUSIVE(MidiPolyPressure, channel, note, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MidiOutPort{
|
struct MidiOutPort{
|
||||||
@@ -94,10 +100,10 @@ namespace VISEQ::MIDI{
|
|||||||
|
|
||||||
inline MidiNote operator+(const MidiNote left, const MidiNote right){
|
inline MidiNote operator+(const MidiNote left, const MidiNote right){
|
||||||
MidiNote added;
|
MidiNote added;
|
||||||
added.note = left.note + right.note;
|
added.note = std::clamp(left.note + right.note, 0, 127);
|
||||||
added.channel = left.channel + right.channel;
|
added.channel = std::clamp(left.channel + right.channel, 0, 15);
|
||||||
added.gate = left.gate + right.gate;
|
added.gate = std::max(left.gate + right.gate, 0);
|
||||||
added.velocity = left.velocity + right.velocity;
|
added.velocity = std::clamp(left.velocity + right.velocity, 0, 127);
|
||||||
return added;
|
return added;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -489,19 +489,7 @@ namespace VISEQ::GRAPH {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawNodeUI(){
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(GraphManager, nodes, edges, selected_nodes)
|
NLOHMANN_DEFINE_TYPE_INTRUSIVE(GraphManager, nodes, edges, selected_nodes)
|
||||||
|
|
||||||
/*inline void to_json(json& j, const GraphManager& G){*/
|
|
||||||
/* j = json{{"nodes", G.nodes}, {"edges", G.edges}};*/
|
|
||||||
/*}*/
|
|
||||||
/**/
|
|
||||||
/*inline void from_json(const json& j, GraphManager& G){*/
|
|
||||||
/* j.at("nodes").template get_to<VEC_SPTR_Node>(G.nodes);*/
|
|
||||||
/*}*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "vmath.h"
|
||||||
|
#include <raymath.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace VISEQ::HELPER{
|
||||||
|
inline void DrawSuperText(std::string text, Vec2Df start, Vec2Df end, float pos, float dist, float size, Color col){
|
||||||
|
float d = start.dist(end);
|
||||||
|
Vector2 text_length = MeasureTextEx(GetFontDefault(), text.c_str(), size, 2.0f);
|
||||||
|
|
||||||
|
Vec2Df middle_handle = start + (end - start) * ((d - text_length.x)*pos)/d;
|
||||||
|
Vec2Df text_position = middle_handle + (start - end).norm().orth() * dist;
|
||||||
|
|
||||||
|
float rotation = Vector2Angle((Vector2){1,0}, (end - start)) * RAD2DEG;
|
||||||
|
DrawTextPro(GetFontDefault(), text.c_str(), text_position, (Vector2){0,0}, rotation, size, 2.0f, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,6 @@
|
|||||||
#include "state.h"
|
#include "state.h"
|
||||||
#include "canvas.h"
|
#include "canvas.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <execution>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace VISEQ::INTERACTION{
|
namespace VISEQ::INTERACTION{
|
||||||
@@ -82,11 +81,11 @@ namespace VISEQ::INTERACTION{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!KEYS_DOWN.shift && IsKeyPressed(KEY_TAB)) {
|
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)) {
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+164
-78
@@ -1,15 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "button.h"
|
|
||||||
#include "config-node.h"
|
#include "config-node.h"
|
||||||
#include "datatypes.h"
|
#include "datatypes.h"
|
||||||
#include "edge.h"
|
#include "edge.h"
|
||||||
|
#include "node-handle.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
#include "raymath.h"
|
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <random>
|
#include <memory>
|
||||||
|
|
||||||
namespace VISEQ::APP::UI{
|
namespace VISEQ::APP::UI{
|
||||||
class AppUI;
|
class AppUI;
|
||||||
@@ -28,7 +27,12 @@ namespace VISEQ::GRAPH::NODES {
|
|||||||
MIDI::MidiAftertouch midiAftertouch;
|
MIDI::MidiAftertouch midiAftertouch;
|
||||||
MIDI::MidiPolyPressure midiPolyPressure;
|
MIDI::MidiPolyPressure midiPolyPressure;
|
||||||
|
|
||||||
std::vector<APP::UI::ButtonRound> channelButtons;
|
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;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -43,6 +47,8 @@ namespace VISEQ::GRAPH::NODES {
|
|||||||
|
|
||||||
NodeType nodeType = NodeType::MIDINODE;
|
NodeType nodeType = NodeType::MIDINODE;
|
||||||
|
|
||||||
|
//std::vector<std::shared_ptr<ControlNode>> controlNodes;
|
||||||
|
|
||||||
bool send_note = true;
|
bool send_note = true;
|
||||||
bool send_cc = false;
|
bool send_cc = false;
|
||||||
bool send_pc = false;
|
bool send_pc = false;
|
||||||
@@ -63,11 +69,28 @@ namespace VISEQ::GRAPH::NODES {
|
|||||||
float last = 0;
|
float last = 0;
|
||||||
|
|
||||||
void init(){
|
void init(){
|
||||||
channelButtons.clear();
|
midiNoteHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiNote.note, position, VS_COLOR_GREEN, "NOTE"));
|
||||||
for(int i = 0; i < 16; ++i){
|
midiNoteHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiNote.channel, position, VS_COLOR_PINK, "CHANNEL", 1, 16, 40, 150));
|
||||||
channelButtons.emplace_back(position, 4.0f, VS_COLOR_YELLOW);
|
midiNoteHandles.emplace_back(std::make_shared<NodeHandle<int>>(midiNote.gate, position, VS_COLOR_RED, "GATE", 10, 30000, 40, 300));
|
||||||
channelButtons.back().hover_color = VS_COLOR_PINK;
|
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"));
|
||||||
|
|
||||||
|
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(){
|
std::shared_ptr<MidiNode> getPtr(){
|
||||||
@@ -152,6 +175,24 @@ namespace VISEQ::GRAPH::NODES {
|
|||||||
void update() override {
|
void update() override {
|
||||||
Node::update();
|
Node::update();
|
||||||
mode = state->node_mode;
|
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){
|
void modMidiNote(int incr){
|
||||||
@@ -259,11 +300,36 @@ namespace VISEQ::GRAPH::NODES {
|
|||||||
|
|
||||||
void draw(Vec2Df mouse, Camera2D cam) override {
|
void draw(Vec2Df mouse, Camera2D cam) override {
|
||||||
Node::draw();
|
Node::draw();
|
||||||
/*if(hovering()) {*/
|
for(auto mh : midiNoteHandles){
|
||||||
/* radius += radius < hover_radius ? 2 : 0;*/
|
if(mode == 0) mh->unlock();
|
||||||
/*} else {*/
|
else mh->lock();
|
||||||
/* radius -= radius > node_radius ? 2 : 0;*/
|
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();
|
if(hovering() || selected) interact();
|
||||||
|
|
||||||
@@ -280,22 +346,9 @@ namespace VISEQ::GRAPH::NODES {
|
|||||||
DrawRing(position, radius, radius + 4, 0.0f - 90.0f, (360.0f/127.0f) * getNotes().velocity - 90.0f, 24, (Color){158, 134, 200, 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});
|
DrawCircleLinesV(position, radius + 4, (Color){100, 100, 100, 255});
|
||||||
|
|
||||||
float midi_gate_map = round((255.f / getNotes().gate) * fade);
|
float trigger_alpha = (1.f / getNotes().gate) * fade;
|
||||||
|
|
||||||
Color gate_color = VS_COLOR_GREEN;
|
if(!picked_up && !selected && !in_selection_rect && !hovering()) DrawCircle(position.x, position.y, radius, ColorLerp(state->mode_color, VS_COLOR_RED, trigger_alpha));
|
||||||
|
|
||||||
switch(mode){
|
|
||||||
case 0:{
|
|
||||||
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){180, 210, 115, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, WHITE);
|
|
||||||
break;}
|
|
||||||
case 1:{
|
|
||||||
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){229, 181, 103, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, WHITE);
|
|
||||||
break;}
|
|
||||||
case 2:{
|
|
||||||
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){232, 125, 62, (unsigned char)Clamp(255 - midi_gate_map, 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(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) DrawCircle(position.x, position.y, radius, (Color){255, 153, 187, 255});
|
||||||
|
|
||||||
@@ -304,23 +357,7 @@ namespace VISEQ::GRAPH::NODES {
|
|||||||
|
|
||||||
if(selected) DrawCircleLinesV(position, radius+8, (Color){158, 255, 210, 255});
|
if(selected) DrawCircleLinesV(position, radius+8, (Color){158, 255, 210, 255});
|
||||||
|
|
||||||
/*if(hovering()){*/
|
/*if(fade > 0) DrawCircle(position.x, position.y, radius, ColorAlpha(VS_COLOR_RED, trigger_alpha));*/
|
||||||
/* 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){
|
if(KEYS_DOWN.alt){
|
||||||
float value = 0;
|
float value = 0;
|
||||||
@@ -366,28 +403,51 @@ namespace VISEQ::GRAPH::NODES {
|
|||||||
friend void to_json(json& j, const MidiNode& N){
|
friend void to_json(json& j, const MidiNode& N){
|
||||||
std::vector<int> conns;
|
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::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{
|
j = json{
|
||||||
{"id", N.id},
|
{"id", N.id},
|
||||||
{"position", N.position},
|
{"position", N.position},
|
||||||
{"distributionMode", N.distributionMode},
|
{"distributionMode", N.distributionMode},
|
||||||
{"nodeType", N.nodeType},
|
{"nodeType", N.nodeType},
|
||||||
{"midi_note_channel",N.midiNote.channel},
|
{"midiNote", N.midiNote},
|
||||||
{"midi_note_note",N.midiNote.note},
|
{"midiCC", N.midiCC},
|
||||||
{"midi_note_velocity",N.midiNote.velocity},
|
{"midiPC", N.midiPC},
|
||||||
{"midi_note_gate",N.midiNote.gate},
|
{"midiPitchBend", N.midiPitchBend},
|
||||||
{"midi_cc_channel",N.midiCC.channel},
|
{"midiAftertouch", N.midiAftertouch},
|
||||||
{"midi_cc_control",N.midiCC.control},
|
{"midiPolyPressure", N.midiPolyPressure},
|
||||||
{"midi_cc_value",N.midiCC.value},
|
{"midiNoteHandle", noteHandles},
|
||||||
{"midi_pc_channel",N.midiPC.channel},
|
{"midiCCHandle", CCHandles},
|
||||||
{"midi_pc_program",N.midiPC.program},
|
{"midiPCHandle", PCHandles},
|
||||||
{"midi_pc_value",N.midiPC.value},
|
{"midiPitchBendHandle", pitchBendHandles},
|
||||||
{"midi_pitch_bend_channel",N.midiPitchBend.channel},
|
{"midiAftertouchHandle", aftertouchHandles},
|
||||||
{"midi_pitch_bend_value",N.midiPitchBend.value},
|
{"midiPolyPressureHandle", polyPressureHandles},
|
||||||
{"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}};
|
{"connections",conns}};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
friend void from_json(const json& j, MidiNode& N){
|
friend void from_json(const json& j, MidiNode& N){
|
||||||
@@ -397,23 +457,49 @@ namespace VISEQ::GRAPH::NODES {
|
|||||||
j.at("position").get_to<Vec2Df>(N.position);
|
j.at("position").get_to<Vec2Df>(N.position);
|
||||||
j.at("distributionMode").get_to(N.distributionMode);
|
j.at("distributionMode").get_to(N.distributionMode);
|
||||||
j.at("nodeType").get_to(N.nodeType);
|
j.at("nodeType").get_to(N.nodeType);
|
||||||
j.at("midi_note_channel").get_to(N.midiNote.channel);
|
j.at("midiNote").get_to(N.midiNote);
|
||||||
j.at("midi_note_note").get_to(N.midiNote.note);
|
j.at("midiCC").get_to(N.midiCC);
|
||||||
j.at("midi_note_velocity").get_to(N.midiNote.velocity);
|
j.at("midiPC").get_to(N.midiPC);
|
||||||
j.at("midi_note_gate").get_to(N.midiNote.gate);
|
j.at("midiPitchBend").get_to(N.midiPitchBend);
|
||||||
j.at("midi_cc_channel").get_to(N.midiCC.channel);
|
j.at("midiAftertouch").get_to(N.midiAftertouch);
|
||||||
j.at("midi_cc_control").get_to(N.midiCC.control);
|
j.at("midiPolyPressure").get_to(N.midiPolyPressure);
|
||||||
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);
|
|
||||||
|
|
||||||
|
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);
|
//j.at("connections").get_to(N.connections);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "datatypes.h"
|
||||||
|
#include "draggable.h"
|
||||||
|
#include "graph-item.h"
|
||||||
|
#include "helper.h"
|
||||||
|
#include "raylib.h"
|
||||||
|
#include "raymath.h"
|
||||||
|
#include "state.h"
|
||||||
|
#include <memory>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
namespace VISEQ::GRAPH::NODES{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
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();}
|
||||||
|
|
||||||
|
virtual GRAPH::GraphItemType getType() override {
|
||||||
|
return GraphItemType::HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<NodeHandle<T>> 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 {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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){
|
||||||
|
bool show = hovering() || picked_up;
|
||||||
|
Color dcol = ColorBrightness(col, -0.5f);
|
||||||
|
Color dyncol = show ? col : dcol;
|
||||||
|
|
||||||
|
DrawLineV(minP, maxP, dcol);
|
||||||
|
//DrawLineV(parentPosition, minP, VS_COLOR_LIGHTGREY);
|
||||||
|
DrawCircleV(minP, 3.0f, dyncol);
|
||||||
|
DrawCircleV(maxP, 3.0f, dyncol);
|
||||||
|
DrawCircleV(position + parentPosition, radius, dyncol);
|
||||||
|
if(show) VISEQ::HELPER::DrawSuperText(name, minP, maxP, 0.5f, 8.0f, 10.0f, dyncol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool select() override {
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
}
|
||||||
+40
-14
@@ -3,6 +3,7 @@
|
|||||||
#include "config-node.h"
|
#include "config-node.h"
|
||||||
#include "midi-node.h"
|
#include "midi-node.h"
|
||||||
#include "graph-item.h"
|
#include "graph-item.h"
|
||||||
|
#include "node-handle.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
#include "edge.h"
|
#include "edge.h"
|
||||||
|
|
||||||
@@ -34,6 +35,27 @@ namespace nlohmann {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*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 {
|
namespace VISEQ::GRAPH::EDGES {
|
||||||
@@ -62,28 +84,32 @@ namespace VISEQ::GRAPH::NODES {
|
|||||||
|
|
||||||
/* ------------------- MIDINODE ---------------------- */
|
/* ------------------- MIDINODE ---------------------- */
|
||||||
|
|
||||||
inline void to_json(json& j, const std::shared_ptr<MidiNode>& N){
|
/*inline void to_json(json& j, const std::shared_ptr<MidiNode>& N){*/
|
||||||
j = *N;
|
/* j = *N;*/
|
||||||
}
|
/*}*/
|
||||||
|
/**/
|
||||||
inline void from_json(const json& j, std::shared_ptr<MidiNode>& N){
|
/*inline void from_json(const json& j, std::shared_ptr<MidiNode>& N){*/
|
||||||
N.reset(new MidiNode(j.get<MidiNode>()));
|
/* N.reset(new MidiNode(j.get<MidiNode>()));*/
|
||||||
}
|
/*}*/
|
||||||
|
|
||||||
inline void to_json(json& j, const MidiNode::NodeMidiOut& MO){
|
inline void to_json(json& j, const MidiNode::NodeMidiOut& MO){
|
||||||
j = json{{"port_id", MO.i}, {"enabled", MO.enabled}, {"name", MO.name}};
|
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 -----------------*/
|
/* ----------------- CONFIG NODE -----------------*/
|
||||||
|
|
||||||
|
|
||||||
inline void to_json(json& j, const std::shared_ptr<ConfigNode>& N){
|
/*inline void to_json(json& j, const std::shared_ptr<ConfigNode>& N){*/
|
||||||
j = *N;
|
/* j = *N;*/
|
||||||
}
|
/*}*/
|
||||||
|
/**/
|
||||||
inline void from_json(const json& j, std::shared_ptr<ConfigNode>& N){
|
/*inline void from_json(const json& j, std::shared_ptr<ConfigNode>& N){*/
|
||||||
N.reset(new ConfigNode(j.get<ConfigNode>()));
|
/* N.reset(new ConfigNode(j.get<ConfigNode>()));*/
|
||||||
}
|
/*}*/
|
||||||
|
|
||||||
/* ------------------- NODE ---------------------- */
|
/* ------------------- NODE ---------------------- */
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ namespace VISEQ::APP{
|
|||||||
float fps;
|
float fps;
|
||||||
float speed = 16.0f;
|
float speed = 16.0f;
|
||||||
int gui_selected_midi_out;
|
int gui_selected_midi_out;
|
||||||
|
Color mode_colors[7] = {VS_COLOR_GREEN, VS_COLOR_SUN, VS_COLOR_PINK, VS_COLOR_ORANGE, VS_COLOR_PURPLE, VS_COLOR_LIGHTGREY, (Color){0,0,0,0}};
|
||||||
};
|
};
|
||||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(AppSettings, width, height, grid, bpm, fps, speed, gui_selected_midi_out)
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(AppSettings, width, height, grid, bpm, fps, speed, gui_selected_midi_out)
|
||||||
|
|
||||||
@@ -85,6 +86,8 @@ namespace VISEQ::APP{
|
|||||||
|
|
||||||
Vec2Df selection_origin;
|
Vec2Df selection_origin;
|
||||||
|
|
||||||
|
Color mode_color = settings.mode_colors[0];
|
||||||
|
|
||||||
void pushEvent(InteractionEvent e){
|
void pushEvent(InteractionEvent e){
|
||||||
event_queue.push_back(e);
|
event_queue.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -133,6 +136,8 @@ namespace VISEQ::APP{
|
|||||||
raw_mouse = {GetMousePosition().x, GetMousePosition().y};
|
raw_mouse = {GetMousePosition().x, GetMousePosition().y};
|
||||||
mod_mouse = {round(mouse_pos.x), round(mouse_pos.y)};
|
mod_mouse = {round(mouse_pos.x), round(mouse_pos.y)};
|
||||||
|
|
||||||
|
mode_color = settings.mode_colors[node_mode];
|
||||||
|
|
||||||
if(keys_down.ctrl) {
|
if(keys_down.ctrl) {
|
||||||
mod_mouse.x = round(mouse_pos.x / settings.grid) * settings.grid;
|
mod_mouse.x = round(mouse_pos.x / settings.grid) * settings.grid;
|
||||||
mod_mouse.y = round(mouse_pos.y / settings.grid) * settings.grid;
|
mod_mouse.y = round(mouse_pos.y / settings.grid) * settings.grid;
|
||||||
|
|||||||
@@ -99,9 +99,9 @@ namespace VISEQ::APP::UI{
|
|||||||
|
|
||||||
std::string shorts[6] = {"NT","CC","PC","PB","AT","PP"};
|
std::string shorts[6] = {"NT","CC","PC","PB","AT","PP"};
|
||||||
for(int i = 0; i < 6; ++i){
|
for(int i = 0; i < 6; ++i){
|
||||||
Color col = VS_COLOR_GREEN;
|
Color col = state->settings.mode_colors[i];
|
||||||
if(state->node_mode == i) col = VS_COLOR_SUN;
|
|
||||||
Vec2Df pos = {state->settings.width - (6.0f*75.0f) + i*75.0f, state->settings.height - 75.0f};
|
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);
|
DrawCircleV(pos, 30.0f, col);
|
||||||
DrawCenteredText(shorts[i], pos, 20, WHITE);
|
DrawCenteredText(shorts[i], pos, 20, WHITE);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user