Files
viseq/src/interaction-manager.h
T

278 lines
8.0 KiB
C++
Raw Normal View History

#pragma once
#include "clipboard.h"
#include "connector.h"
#include "datatypes.h"
#include "draggable.h"
#include "graph-item.h"
#include "graph-manager.h"
#include "interaction-event.h"
2024-12-03 01:25:49 +01:00
#include "node.h"
#include "raylib.h"
#include "signal-manager.h"
#include "state.h"
#include "canvas.h"
2024-12-03 01:25:49 +01:00
#include <algorithm>
#include <memory>
namespace VISEQ::INTERACTION{
class ScaleHandle : public BASE::Draggable, public GraphItem{
public:
ScaleHandle(){}
float radius = 6;
std::shared_ptr<ScaleHandle> getPtr(){
return std::static_pointer_cast<ScaleHandle>(shared_from_this());
}
virtual GRAPH::GraphItemType getType() override {
return GraphItemType::HANDLE;
}
virtual bool hovering() override {
return CheckCollisionPointCircle(SCREEN_MOUSE, position, radius);
}
virtual 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});
}
virtual void draw() override {
DrawCircleV(position, radius, VS_COLOR_SUN);
}
virtual bool select() override {
return false;
}
};
class InteractionManager{
public:
InteractionManager() {}
InteractionManager(std::shared_ptr<GRAPH::GraphManager> gm, std::shared_ptr<SIGNAL::SignalManager> sm, std::shared_ptr<APP::CANVAS::Canvas> canvas) : canvas(canvas), gm(gm), sm(sm) {}
std::shared_ptr<APP::CANVAS::Canvas> canvas;
std::shared_ptr<GraphManager> gm;
std::shared_ptr<SIGNAL::SignalManager> sm;
VISEQ::APP::Clipboard cb;
std::shared_ptr<APP::State> state = APP::State::get();
Vec2Df scale_start;
std::shared_ptr<ScaleHandle> scale_handle = std::make_shared<ScaleHandle>();
void update(){
if(KEYS_DOWN.ctrl && IsKeyPressed(KEY_C)){
VEC_SPTR_Node selected_nodes = gm->getCopiesOfSelectedNodes();
2024-12-03 11:08:40 +01:00
if(!selected_nodes.empty()) cb.copyNodes(selected_nodes);
}
if(KEYS_DOWN.ctrl && IsKeyPressed(KEY_V)){
2024-12-03 11:08:40 +01:00
VEC_SPTR_Node paste_nodes = cb.pasteNodes();
if(!paste_nodes.empty()) gm->pasteNodesAndEdges(paste_nodes, MOD_MOUSE);
}
if(IsKeyPressed(KEY_DELETE)){
removeNodes(gm->getSelection());
}
if(IsKeyPressed(KEY_TAB)) {
state->node_mode = (state->node_mode + 1) % 3;
}
2024-11-30 12:47:33 +01:00
}
void draw(){
doInteraction();
scaleSelection();
if(!gm->getSelection().empty()){
2024-11-30 12:47:33 +01:00
DrawRectangleRoundedLines(state->bounding_box, 0.1f, 3, VS_COLOR_LIGHTGREY);
scale_handle->draw();
}
}
void removeNodes(VEC_SPTR_Node nodes){
VEC_WPTR_Node adjacent_nodes = gm->getAdjacentNodes(nodes);
sm->rerouteSignal(nodes, adjacent_nodes);
sm->rebaseSignal(nodes);
gm->removeNodesAndEdges(nodes);
}
void scaleSelection(){
if(!gm->getSelection().empty()){
float offset = 30;
Vec2Df corner_tl = {50000, 50000};
Vec2Df corner_br = {-50000, -50000};
std::shared_ptr<Node> node_tl;
std::shared_ptr<Node> node_br;
for(auto n : gm->getSelection()){
float x = n->position.x;
float y = n->position.y;
if(x < corner_tl.x && y < corner_tl.y) node_tl = n;
if(x > corner_br.x && y > corner_br.y) node_br = n;
if(x <= corner_tl.x) corner_tl.x = x;
if(x >= corner_br.x) corner_br.x = x;
if(y <= corner_tl.y) corner_tl.y = y;
if(y >= corner_br.y) corner_br.y = y;
}
corner_tl -= Vec2Df(offset, offset);
corner_br += Vec2Df(offset, offset);
state->bounding_box = (Rectangle){corner_tl.x, corner_tl.y, corner_br.x - corner_tl.x, corner_br.y - corner_tl.y};
scale_handle->update();
if(scale_handle->clicked()){
scale_start = (corner_br - corner_tl);
for(auto n : gm->getSelection()){
n->drag_offset = n->position - node_tl->position;
}
}
if(scale_handle->picked_up){
Vec2Df scale = (scale_handle->position - corner_tl) / scale_start;
if(IsKeyDown(KEY_LEFT_SHIFT)) scale.y = scale.x;
DrawText(std::format("{:.2f} {:.2f}", scale.x, scale.y).c_str(), corner_br.x+5, corner_br.y+5, 10, RED);
for(auto n : gm->getSelection()){
n->position = node_tl->position + (n->drag_offset * scale);
}
} else {
scale_handle->position = corner_br;
}
}
}
void snapPolar(std::shared_ptr<Node> dragNode){
float steps = round((dragNode->neighbors.front()->position.dist(SCREEN_MOUSE)) / state->settings.grid) * state->settings.grid;
if(dragNode->neighbors.size() > 1){
Vec2Df dn1 = dragNode->neighbors[0]->position;
Vec2Df dn2 = dragNode->neighbors[1]->position;
Vec2Df v1_orth = dn1.center(dn2);
if(dn1.x < dn2.x) std::swap(dn1,dn2);
float dist = dn1.dist(dn2);
float dist_orth = std::sqrt(std::pow(steps,2) - std::pow(dist/2,2));
if(SCREEN_MOUSE.y < v1_orth.y) dist_orth *= -1;
Vec2Df v1_orth_t = v1_orth + (dn1-dn2).norm().orth() * dist_orth;
DrawLineV(dn1, dn2, VS_COLOR_RED);
DrawLineV(v1_orth, v1_orth_t, VS_COLOR_RED);
DrawCircleV(v1_orth, 3.0f, VS_COLOR_RED);
dragNode->position = v1_orth_t;
} else if(!dragNode->neighbors.empty()){
float pol_r = steps;
float pol_theta = Vector2Angle(Vec2Df{1,0},(SCREEN_MOUSE - dragNode->neighbors.front()->position));
Vec2Df polar = VISEQ::VMATH::polarToCartesian(pol_r, pol_theta);
dragNode->position = dragNode->neighbors.front()->position + polar;
}
}
void doInteraction(){
bool exit = false;
bool once = false;
2024-11-30 12:47:33 +01:00
bool connect_once = false;
state->mouse_z = canvas->z_index;
while(APP::event_queue.size() > 0 && !exit){
auto e = APP::event_queue.front();
2024-11-30 12:47:33 +01:00
if(!connect_once && e.action == APP::Interaction::CONNECT){
auto connector = std::dynamic_pointer_cast<Connector>(e.item);
auto connector_to = std::dynamic_pointer_cast<Connector>(connector->partner.lock());
gm->addConnection(std::dynamic_pointer_cast<Node>(connector->parent), std::dynamic_pointer_cast<Node>(connector_to->parent));
2024-11-30 12:47:33 +01:00
connect_once = true;
}
if(e.action == APP::Interaction::HOVER){
state->mouse_z = e.item->z_index;
}
if(e.item->getType() == GraphItemType::CANVAS) {
if(e.action == APP::Interaction::SELECT) {
gm->select();
canvas->clearSelectionRect();
}
if(e.action == APP::Interaction::DESELECT) {
gm->deselect();
}
}
if(!once && e.item->getType() == GraphItemType::NODE){
auto node = std::dynamic_pointer_cast<Node>(e.item);
if(e.action == APP::Interaction::PICKUP) {
node->pickUp();
if(node->selected){
for(auto s : gm->getSelection()){
s->pickUp(node->position);
}
}
once = true;
}
if(e.action == APP::Interaction::DRAG) {
if(KEYS_DOWN.ctrl) node->drag(MOD_MOUSE);
else if(KEYS_DOWN.shift) {
snapPolar(node);
}
else node->drag(MOD_MOUSE + node->drag_offset);
if(node->selected){
for(auto s : gm->getSelection()){
s->drag(MOD_MOUSE + s->drag_offset);
}
}
once = true;
}
if(e.action == APP::Interaction::PUTDOWN) {
node->putDown();
if(node->selected){
for(auto s : gm->getSelection()){
s->putDown();
}
}
}
}
if(exit) break;
if(!once && (e.item->getType() == GraphItemType::CONNECTOR || e.item->getType() == GraphItemType::CANVAS || e.item->getType() == GraphItemType::HANDLE)) {
switch(e.action){
case VISEQ::APP::Interaction::PICKUP:
{
e.item->pickUp();
once = true;
break;
}
case VISEQ::APP::Interaction::DRAG:
{
e.item->drag(MOD_MOUSE);
once = true;
break;
}
case VISEQ::APP::Interaction::PUTDOWN:
{
e.item->putDown();
break;
}
default:
{}
}
}
APP::event_queue.pop_front();
}
APP::event_queue.clear();
}
};
}