Files
viseq/src/graph-manager.h
T

500 lines
20 KiB
C++
Raw Normal View History

2024-10-30 14:55:24 +01:00
#pragma once
2024-11-01 14:08:27 +01:00
#include "config-node.h"
2024-10-30 14:55:24 +01:00
#include "datatypes.h"
#include "state.h"
2024-11-14 00:55:09 +01:00
#include "vmath.h"
2024-11-01 14:08:27 +01:00
#include "raylib.h"
#include "raymath.h"
#include "nlohmann/json.hpp"
2024-11-01 14:08:27 +01:00
#include "button.h"
#include "midi-node.h"
#include "edge.h"
2024-11-03 03:48:49 +01:00
#include "node.h"
2024-11-01 14:08:27 +01:00
#include <algorithm>
2024-11-01 14:08:27 +01:00
#include <cstdio>
#include <iostream>
#include <iterator>
2024-10-30 14:55:24 +01:00
#include <map>
#include <memory>
#include <ostream>
2024-10-30 14:55:24 +01:00
#include <random>
#include <ranges>
#include <vector>
2024-10-30 14:55:24 +01:00
#include "state-io.h"
using json = nlohmann::json;
2024-11-03 03:48:49 +01:00
using namespace VISEQ::GRAPH::NODES;
using namespace VISEQ::GRAPH::EDGES;
2024-11-03 03:48:49 +01:00
namespace VISEQ::GRAPH {
2024-10-30 14:55:24 +01:00
class GraphManager : public std::enable_shared_from_this<GraphManager>{
2024-12-03 01:25:49 +01:00
private:
NODES::VEC_SPTR_Node nodes;
EDGES::VEC_SPTR_Edge edges;
EDGES::VEC_WPTR_Edge remove_edges;
EDGES::VEC_WPTR_Edge split_edges;
VEC_SPTR_Node selected_nodes;
2024-12-03 01:25:49 +01:00
std::map<NODES::SPTR_Node, int> counter;
2024-12-03 01:25:49 +01:00
std::shared_ptr<APP::State> state = APP::State::get();
2024-10-30 14:55:24 +01:00
2024-11-03 03:48:49 +01:00
public:
2024-10-30 14:55:24 +01:00
2024-11-03 03:48:49 +01:00
GraphManager() {}
2024-11-01 14:08:27 +01:00
std::shared_ptr<GraphManager> getPtr(){
return shared_from_this();
}
int getNodesSize(){
return nodes.size();
}
void createMidiNode(Vec2Df position){
nodes.emplace_back(std::make_shared<MidiNode>(position));
nodes.back()->update();
}
void createConfigNode(Vec2Df position){
nodes.emplace_back(std::make_shared<ConfigNode>(position));
nodes.back()->update();
}
void setMidiPortForAll(int number, bool _state){
for(auto n : nodes){
if(n->getNodeType() == NodeType::MIDINODE) std::dynamic_pointer_cast<MidiNode>(n)->setMidiPort(number, _state);
}
}
void addNode(SPTR_Node node){
std::cout << "added node " << node->id << std::endl;
nodes.insert(nodes.end(), node);
node->update();
}
2024-12-03 01:25:49 +01:00
void addConnection(WPTR_Node from, WPTR_Node to){
if(from.lock() != to.lock()) {
edges.emplace_back(std::make_shared<Edge>());
edges.back()->start = from;
edges.back()->end = to;
edges.back()->start_conn = from.lock()->connectors.back(); // TODO: crashes on start
edges.back()->end_conn = to.lock()->connectors.back();
2024-12-03 01:25:49 +01:00
from.lock()->connections.push_back(edges.back());
to.lock()->connections.push_back(edges.back());
}
2024-11-03 03:48:49 +01:00
}
2024-12-05 23:39:01 +01:00
void addToSelection(){
auto temp_nodes = nodes | RANGE_FILTER([](SPTR_Node n){return n->selected;}) | std::ranges::to<VEC_SPTR_Node>();
selected_nodes.insert(selected_nodes.end(), temp_nodes.begin(), temp_nodes.end());
}
void select(){
auto temp_nodes = nodes | RANGE_FILTER([](SPTR_Node n){return n->select();}) | std::ranges::to<VEC_SPTR_Node>();
if(!KEYS_DOWN.shift) selected_nodes.clear();
selected_nodes.insert(selected_nodes.end(), temp_nodes.begin(), temp_nodes.end());
2024-12-03 01:25:49 +01:00
std::for_each(selected_nodes.begin(), selected_nodes.end(), [](WPTR_Node n){n.lock()->selected = true;});
}
void deselect(){
std::cout << "deselect" << std::endl;
for(auto s : selected_nodes){
s->selected = false;
}
selected_nodes.clear();
}
VEC_SPTR_Node getSelection(){
return selected_nodes;
}
2024-11-03 03:48:49 +01:00
void copyNodes(VEC_SPTR_Node _nodes, Vec2Df position){
for(auto n: _nodes){
nodes.insert(nodes.end(), std::make_shared<Node>(*n));
nodes.back()->setPosition(position + nodes.back()->drag_offset);
nodes.back()->selected = false;
}
}
void copyNodes(std::vector<std::shared_ptr<MidiNode>> _nodes, Vec2Df position){
for(auto n: _nodes){
nodes.insert(nodes.end(), std::make_shared<MidiNode>(*n));
nodes.back()->setPosition(position + nodes.back()->drag_offset);
nodes.back()->selected = false;
}
}
VEC_SPTR_Node getCopiesOfSelectedNodes(){
VEC_SPTR_Node copied_nodes;
for(auto n : selected_nodes){
// if(n->getNodeType() == NodeType::MIDINODE) copied_nodes.insert(copied_nodes.end(), std::make_shared<MidiNode>(*n));
copied_nodes.insert(copied_nodes.end(), std::make_shared<Node>(*n));
copied_nodes.back()->deselect();
}
return copied_nodes;
}
2024-11-03 03:48:49 +01:00
SPTR_Node copyNode(SPTR_Node n, Vec2Df position){
std::cout << "copy basenode" << std::endl;
nodes.insert(nodes.end(), std::make_shared<Node>(*n));
2024-11-03 03:48:49 +01:00
nodes.back()->setPosition(position);
return nodes.back();
2024-10-30 14:55:24 +01:00
}
2024-11-03 03:48:49 +01:00
SPTR_Node copyNode(std::shared_ptr<MidiNode> n, Vec2Df position){
std::cout << "copy midinode" << std::endl;
nodes.insert(nodes.end(), std::make_shared<MidiNode>(*n));
2024-12-05 23:39:01 +01:00
nodes.back()->clearConnectors();
2024-11-03 03:48:49 +01:00
nodes.back()->setPosition(position);
return nodes.back();
}
2024-11-03 03:48:49 +01:00
void setNodeMode(int mode){
for(auto n: nodes){
n->SMode(mode);
}
}
2024-10-30 14:55:24 +01:00
2024-11-03 03:48:49 +01:00
void reset(){
counter.clear();
}
2024-12-03 01:25:49 +01:00
WPTR_Node getNode(int i){
return nodes.at(i);
}
WPTR_Node getFirstNode(){
return nodes.front();
}
WPTR_Node getLastNode(){
return nodes.back();
}
2024-12-03 01:25:49 +01:00
VEC_WPTR_Node getMidiNodes(){
return nodes | RANGE_FILTER([](VISEQ::GRAPH::NODES::SPTR_Node n){return n->getNodeType() == NodeType::MIDINODE;}) | std::ranges::to<VEC_WPTR_Node>();
}
2024-10-30 14:55:24 +01:00
NODES::SPTR_Node getRandomNode(){
VEC_SPTR_GraphItem o;
std::ranges::sample(nodes, std::back_inserter(o), 1, std::mt19937{std::random_device{}()});
return std::dynamic_pointer_cast<VISEQ::GRAPH::NODES::Node>(o.front());
}
EDGES::SPTR_Edge getRandomEdge(){
VEC_SPTR_GraphItem o;
std::ranges::sample(edges, std::back_inserter(o), 1, std::mt19937{std::random_device{}()});
return std::dynamic_pointer_cast<VISEQ::GRAPH::EDGES::Edge>(o.front());
}
SPTR_Node getClosestNode(Vec2Df position){
auto neighbors = nodes | RANGE_FILTER([](SPTR_Node n){ return !n->picked_up;}) | std::ranges::to<VEC_SPTR_Node>();
std::sort(neighbors.begin(), neighbors.end(), [position](SPTR_Node na, SPTR_Node nb){ return na->position.dist(position) < nb->position.dist(position);});
return neighbors.front();
}
2024-12-03 01:25:49 +01:00
VEC_WPTR_Node getNodesByID(int _id){
return nodes | RANGE_FILTER([_id](SPTR_Node n){return n->id == _id;}) | std::ranges::to<VEC_WPTR_Node>(); // TODO: Multiple nodes with the same ID? Why not return only one node?
}
WPTR_Edge lastEdge(){
return edges.back();
}
2024-12-03 01:25:49 +01:00
WPTR_Node next(WPTR_Node current, WPTR_Node last){
VEC_WPTR_Edge possible_connections = current.lock()->connections | RANGE_FILTER([current](WPTR_Edge c){
return (c.lock()->start.lock() == current.lock() && c.lock()->pass_forward) || (c.lock()->end.lock() == current.lock() && c.lock()->pass_backward);
}) | std::ranges::to<VEC_WPTR_Edge>();
2024-10-30 14:55:24 +01:00
2024-11-03 03:48:49 +01:00
if(possible_connections.empty()) return SPTR_Node(nullptr);
int return_path = 0;
float angle = -1;
2024-12-03 01:25:49 +01:00
if(current.lock()->distributionMode == Distribution::GEOMETRIC){
Vec2Df coming_from = (current.lock()->position - last.lock()->position).norm();
2024-11-03 03:48:49 +01:00
int i = 0;
for(auto n : possible_connections){
2024-12-03 01:25:49 +01:00
WPTR_Node next_node = n.lock()->getNextNode(current);
float angle_v1_v2 = coming_from.norm().dot((next_node.lock()->position - current.lock()->position).norm());
2024-11-03 03:48:49 +01:00
if(angle_v1_v2 > angle) {
return_path = i;
angle = angle_v1_v2;
}
++i;
}
}
2024-12-03 01:25:49 +01:00
if(current.lock()->distributionMode == Distribution::ROUNDROBIN){
if(counter.find(current.lock()) == counter.end()) {
counter[current.lock()] = 0;
2024-11-03 03:48:49 +01:00
} else {
counter[current.lock()] = (counter[current.lock()] + 1) % possible_connections.size();
return_path = counter[current.lock()];
2024-10-30 14:55:24 +01:00
}
}
2024-11-03 03:48:49 +01:00
2024-12-03 01:25:49 +01:00
if(current.lock()->distributionMode == Distribution::RANDOM){
2024-11-03 03:48:49 +01:00
return_path = rand() % possible_connections.size();
}
2024-12-03 01:25:49 +01:00
return possible_connections.at(return_path).lock()->getNextNode(current);
2024-10-30 14:55:24 +01:00
}
2024-11-03 03:48:49 +01:00
void splitEdge(SPTR_Edge edge){
split_edges.push_back(edge);
2024-10-30 14:55:24 +01:00
}
2024-11-03 03:48:49 +01:00
void splitEdges(){
for(auto s : split_edges){
2024-12-03 01:25:49 +01:00
Vec2Df center = s.lock()->end.lock()->position.center(s.lock()->start.lock()->position);
SPTR_Node cn = copyNode(std::dynamic_pointer_cast<MidiNode>(s.lock()->start.lock()), center); // TODO: copy connectors
2024-12-03 01:25:49 +01:00
addConnection(s.lock()->start.lock(), cn);
addConnection(cn, s.lock()->end.lock());
2024-11-03 03:48:49 +01:00
remove_edges.push_back(s);
}
split_edges.clear();
}
2024-11-03 03:48:49 +01:00
void removeEdge(SPTR_Edge edge){
remove_edges.push_back(edge);
}
2024-11-03 03:48:49 +01:00
void removeEdges(){
for(auto edge : remove_edges){
2024-12-03 01:25:49 +01:00
edge.lock()->start.lock()->removeConnector(edge.lock()->start_conn);
edge.lock()->end.lock()->removeConnector(edge.lock()->end_conn);
std::erase(edge.lock()->start.lock()->connections, edge.lock());
std::erase(edge.lock()->end.lock()->connections, edge.lock());
std::erase(edges, edge.lock());
2024-11-03 03:48:49 +01:00
}
remove_edges.clear();
2024-11-01 14:08:27 +01:00
}
2024-11-03 03:48:49 +01:00
2024-12-03 01:25:49 +01:00
VEC_WPTR_Node getAdjacentNodes(VEC_SPTR_Node selected_nodes){
for(auto n : nodes){
n->highlighted = false;
}
2024-12-03 01:25:49 +01:00
auto erase_connections = edges | RANGE_FILTER([selected_nodes](SPTR_Edge c){ return std::ranges::contains(selected_nodes, c->start.lock()) || std::ranges::contains(selected_nodes, c->end.lock());});
2024-12-03 01:25:49 +01:00
VEC_WPTR_Node adjacent_nodes;
for(auto c : erase_connections){
2024-12-03 01:25:49 +01:00
if(!c->start.lock()->selected) adjacent_nodes.push_back(c->start);
if(!c->end.lock()->selected) adjacent_nodes.push_back(c->end);
}
return adjacent_nodes;
}
void removeNodesAndEdges(VEC_SPTR_Node selected_nodes){
2024-12-03 01:25:49 +01:00
VEC_WPTR_Node adjacent_nodes = getAdjacentNodes(selected_nodes);
for(auto n : adjacent_nodes){
2024-12-03 01:25:49 +01:00
std::erase_if(n.lock()->connections, [n, selected_nodes](VISEQ::GRAPH::EDGES::WPTR_Edge c){ return std::ranges::contains(selected_nodes, c.lock()->start.lock()) || std::ranges::contains(selected_nodes, c.lock()->end.lock());});
}
for(auto n : selected_nodes){
2024-12-03 01:25:49 +01:00
std::erase_if(this->edges, [n](WPTR_Edge c){ return c.lock()->start.lock() == n || c.lock()->end.lock() == n;});
}
std::erase_if(this->nodes, [selected_nodes](SPTR_Node n){ return std::ranges::contains(selected_nodes, n);});
}
void pasteNodesAndEdges(VEC_SPTR_Node nodes, Vec2Df position){
VEC_SPTR_Edge copy_connections;
for(auto c : edges){
2024-12-03 01:25:49 +01:00
if(std::ranges::contains(nodes, c->start.lock()) && std::ranges::contains(nodes, c->end.lock())) {
copy_connections.push_back(std::make_shared<Edge>(*c));
}
}
std::cout << copy_connections.size() << std::endl;
for(auto old_node : nodes){
SPTR_Node new_node;
if(old_node->getNodeType() == NodeType::MIDINODE) {
new_node = copyNode(std::dynamic_pointer_cast<MidiNode>(old_node), position + old_node->drag_offset);
} else {
new_node = copyNode(old_node, position + old_node->drag_offset);
}
new_node->copied = false;
new_node->selected = true;
//TODO copy connectors
for(auto c : copy_connections){
2024-12-03 01:25:49 +01:00
if(c->start.lock() == old_node) {
c->start = new_node;
2024-12-03 01:25:49 +01:00
c->start.lock()->connections.push_back(c);
}
2024-12-03 01:25:49 +01:00
if(c->end.lock() == old_node) {
c->end = new_node;
2024-12-03 01:25:49 +01:00
c->end.lock()->connections.push_back(c);
}
}
}
std::ranges::copy(copy_connections.begin(), copy_connections.end(), std::back_inserter(edges));
}
void update(){
2024-11-03 03:48:49 +01:00
if(!remove_edges.empty()) removeEdges();
if(!split_edges.empty()) splitEdges();
}
void drawEdges(Vec2Df mouse_pos, Camera2D cam){
2024-11-03 03:48:49 +01:00
for(auto c : edges){
2024-12-03 01:25:49 +01:00
Vec2Df v1_position = c->start.lock()->position - ((c->start.lock()->position - c->end.lock()->position)).norm() * (c->start.lock()->radius + c->connector_offset);
Vec2Df v2_position = c->end.lock()->position - ((c->end.lock()->position - c->start.lock()->position)).norm() * (c->end.lock()->radius + c->connector_offset);
2024-11-03 03:48:49 +01:00
float v1_v2_distance = v1_position.dist(&v2_position);
if(v1_v2_distance < 100) c->switches_offset = v1_v2_distance/2.3f;
2024-12-03 01:25:49 +01:00
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;
2024-11-03 03:48:49 +01:00
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);
2024-11-03 03:48:49 +01:00
// Dynamic opacity by distance to mouse
unsigned char opacity = 150.0f - Clamp(middle_handle.dist(mouse_pos), 0.0f, 150.0f);
unsigned char opacity_from = 250.0f - Clamp(from_to_handle.dist(mouse_pos), 0.0f, 220.0f);
unsigned char opacity_to = 250.0f - Clamp(to_from_handle.dist(mouse_pos), 0.0f, 220.0f);
float midi_gate_v1 = 0.0f;
float midi_gate_v2 = 0.0f;
if(c->start.lock()->getNodeType() == NodeType::MIDINODE){
midi_gate_v1 = Clamp((4.f / 1000.f) * std::dynamic_pointer_cast<MidiNode>(c->start.lock())->GGate(), 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())->GGate(), 1.f, 4.f);
}
// Line with small connector circles
if(c->end.lock()->getNodeType() == NodeType::MIDINODE && c->start.lock()->getNodeType() == NodeType::MIDINODE){
Color col = {229, 181, 103, 255};
if(c->GActive()) col = {108, 153, 187, 255};
DrawLine(v1_position.x, v1_position.y, v2_position.x, v2_position.y, col);
{
float d = c->start.lock()->position.dist(c->end.lock()->position);
std::string frac = MIDI::Fractions.at(std::clamp<int>(round(d/60) - 1, 0, 31));
Vector2 text_length = MeasureTextEx(GetFontDefault(), std::format("{:.1f}",d).c_str(), 10.0f, 2.0f);
Vector2 text_length_frac = MeasureTextEx(GetFontDefault(), frac.c_str(), 10.0f, 2.0f);
Vec2Df middle_handle = c->start.lock()->position + (c->end.lock()->position - c->start.lock()->position) * ((d - text_length.x)*0.5f)/d;
Vec2Df middle_handle_frac = c->start.lock()->position + (c->end.lock()->position - c->start.lock()->position) * ((d - text_length_frac.x)*0.5f)/d;
Vec2Df text_position = middle_handle + (c->start.lock()->position - c->end.lock()->position).norm().orth() * 12.0f;
Vec2Df text_position_frac = middle_handle_frac - (c->start.lock()->position - c->end.lock()->position).norm().orth() * 4.0f;
float rotation = Vector2Angle((Vector2){1,0}, (c->end.lock()->position - c->start.lock()->position)) * RAD2DEG;
DrawTextPro(GetFontDefault(), std::format("{:.1f}",d).c_str(), text_position, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK);
if( (int)d % 60 == 0) DrawTextPro(GetFontDefault(), frac.c_str(), text_position_frac, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK);
2024-11-03 03:48:49 +01:00
}
if(midi_gate_v1 == 4.0f) col = VS_COLOR_RED;
DrawCircle(v1_position.x, v1_position.y, midi_gate_v1, col);
2024-11-03 03:48:49 +01:00
col = {229, 181, 103, 255};
if(midi_gate_v2 == 4.0f) col = VS_COLOR_RED;
DrawCircle(v2_position.x, v2_position.y, midi_gate_v2, col);
if(!c->start.lock()->picked_up && !c->end.lock()->picked_up){
// On/Off switches on both sides
if(ViButton(mouse_pos, from_to_handle.x, from_to_handle.y, 8.0f, c->pass_forward ? (Color){120, 210, 115, opacity_from} : (Color){232, 50, 32, opacity_from} )) c->pass_forward = !c->pass_forward;
if(ViButton(mouse_pos, to_from_handle.x, to_from_handle.y, 8.0f, c->pass_backward ? (Color){120, 210, 115, opacity_to} : (Color){232, 50, 32, opacity_to} )) c->pass_backward = !c->pass_backward;
if(!c->pass_forward){
Vec2Df block_point1 = (v1_position - v2_position).norm().orth()*4.0f + from_to_handle;
Vec2Df block_point2 = (v1_position - v2_position).norm().orth()*-4.0f + from_to_handle;
DrawLineEx(block_point1, block_point2, 2.0f, (Color){232,232,232,opacity_from});
}
if(!c->pass_backward){
Vec2Df block_point1 = (v1_position - v2_position).norm().orth()*4.0f + to_from_handle;
Vec2Df block_point2 = (v1_position - v2_position).norm().orth()*-4.0f + to_from_handle;
DrawLineEx(block_point1, block_point2, 2.0f, (Color){232,232,232,opacity_to});
}
if(ViButton(mouse_pos, flip_handle_2.x, flip_handle_2.y, 8.0f, (Color){232, 125, 62, opacity} )) splitEdge(c);
2024-11-03 03:48:49 +01:00
}
2024-11-01 14:08:27 +01:00
}
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);
2024-11-01 14:08:27 +01:00
}
2024-10-30 14:55:24 +01:00
}
2024-11-03 03:48:49 +01:00
2024-11-14 00:55:09 +01:00
void drawNeighborLines(SPTR_Node node, Vec2Df mouse_pos, Camera2D camera){
2024-11-03 03:48:49 +01:00
float min_d = 480;
2024-11-14 00:55:09 +01:00
auto neighbors_local = nodes | RANGE_FILTER([mouse_pos](SPTR_Node n){ return !n->picked_up && !n->selected && n->position.dist(mouse_pos) < 960;}) | std::ranges::to<VEC_SPTR_Node>();
std::sort(neighbors_local.begin(), neighbors_local.end(), [node, mouse_pos](SPTR_Node na, SPTR_Node nb){ return na->position.dist(mouse_pos) < nb->position.dist(mouse_pos);});
if(!neighbors_local.empty()){
min_d = neighbors_local.front()->position.dist(mouse_pos);
DrawCircleLinesV(neighbors_local.front()->position, 22.0f, VS_COLOR_PINK);
node->neighbors = neighbors_local | std::ranges::views::filter([mouse_pos, min_d](SPTR_Node n){return abs(n->position.dist(mouse_pos) - min_d) < 5;}) | std::ranges::to<VEC_SPTR_Node>();
for(auto nb : node->neighbors){
DrawCircleLinesV(nb->position, 20.0f, VS_COLOR_RED);
float d = nb->position.dist(mouse_pos);
float dn = nb->position.dist(node->position);
if(d > 0 && d < min_d*1.5 && d < 960) {
std::string frac = MIDI::Fractions.at(std::clamp<int>(round(dn/60) - 1, 0, 31));
Vector2 text_length = MeasureTextEx(GetFontDefault(), std::format("{:.1f}",dn).c_str(), 10.0f, 2.0f);
Vector2 text_length_frac = MeasureTextEx(GetFontDefault(), frac.c_str(), 10.0f, 2.0f);
Vec2Df middle_handle = nb->position + (node->position - nb->position) * ((dn - text_length.x)*0.5f)/dn;
Vec2Df middle_handle_frac = nb->position + (node->position - nb->position) * ((dn - text_length_frac.x)*0.5f)/dn;
Vec2Df text_position = middle_handle + (nb->position - node->position).norm().orth() * 12.0f;
Vec2Df text_position_frac = middle_handle_frac - (nb->position - node->position).norm().orth() * 4.0f;
float rotation = Vector2Angle((Vector2){1,0}, (node->position - nb->position)) * RAD2DEG;
DrawTextPro(GetFontDefault(), std::format("{:.1f}",dn).c_str(), text_position, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK);
if(IsKeyDown(KEY_LEFT_SHIFT)) DrawTextPro(GetFontDefault(), frac.c_str(), text_position_frac, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK);
Vec2Df n_position = node->position - ((node->position - nb->position)).norm() * 20.0f;
Vec2Df nb_position = nb->position - ((nb->position - node->position)).norm() * 20.0f;
DrawLineV(n_position, nb_position, VS_COLOR_PINK);
2024-10-30 14:55:24 +01:00
}
}
2024-11-14 00:55:09 +01:00
}
}
void drawNodes(Vec2Df mouse_pos, Camera2D camera){
for(auto n : nodes){
if(KEYS_PRESSED.ctrl && n->hovering()) state->selection_origin = n->position;
}
BASE::Interactable::z_hovering = -1;
2024-11-14 00:55:09 +01:00
for(auto n : nodes){
n->update();
if(n->picked_up) drawNeighborLines(n, mouse_pos, camera);
2024-11-03 03:48:49 +01:00
n->draw(mouse_pos, camera);
2024-10-30 14:55:24 +01:00
}
}
void drawNodeUI(){
2024-12-03 01:25:49 +01:00
}
NLOHMANN_DEFINE_TYPE_INTRUSIVE(GraphManager, nodes, edges)
/*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);*/
/*}*/
2024-11-03 03:48:49 +01:00
};
2024-10-30 14:55:24 +01:00
2024-11-03 03:48:49 +01:00
}