#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" #include "node.h" #include "raylib.h" #include "signal-manager.h" #include "state.h" #include "canvas.h" #include #include namespace VISEQ::INTERACTION{ class ScaleHandle : public BASE::Draggable, public GraphItem{ public: ScaleHandle(){} float radius = 6; std::shared_ptr getPtr(){ return std::static_pointer_cast(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(GRAPH::GraphManager *gm, SIGNAL::SignalManager *sm, std::shared_ptr canvas) : canvas(canvas), gm(gm), sm(sm) {} std::shared_ptr canvas; GraphManager *gm; SIGNAL::SignalManager *sm; VISEQ::APP::Clipboard cb; std::shared_ptr state = APP::State::get(); Vec2Df scale_start; std::shared_ptr scale_handle = std::make_shared(); void update(){ if(KEYS_DOWN.ctrl && IsKeyPressed(KEY_C)){ VEC_WPTR_Node selected_nodes = gm->getSelection(); if(!selected_nodes.empty()) cb.pushNodes(selected_nodes); } if(KEYS_DOWN.ctrl && IsKeyPressed(KEY_V)){ VEC_SPTR_Node paste_nodes = cb.pullNodes(); if(!paste_nodes.empty()) gm->pasteNodesAndEdges(paste_nodes, MOD_MOUSE); } if(IsKeyPressed(KEY_DELETE)){ removeNodes(gm->selected_nodes | std::ranges::to()); } if(IsKeyPressed(KEY_TAB)) { state->node_mode = (state->node_mode + 1) % 3; } } void draw(){ doInteraction(); scaleSelection(); if(!gm->selected_nodes.empty()){ DrawRectangleRoundedLines(state->bounding_box, 0.1f, 3, VS_COLOR_LIGHTGREY); scale_handle->draw(); } } void removeNodes(VEC_SPTR_Node nodes){ VEC_SPTR_Node adjacent_nodes = gm->getAdjacentNodes(nodes); sm->rerouteSignal(nodes, adjacent_nodes); sm->rebaseSignal(nodes); gm->removeNodesAndEdges(nodes); } void scaleSelection(){ if(!gm->selected_nodes.empty()){ float offset = 30; Vec2Df corner_tl = {50000, 50000}; Vec2Df corner_br = {-50000, -50000}; std::shared_ptr node_tl; std::shared_ptr node_br; for(auto n : gm->selected_nodes){ 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->selected_nodes){ 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->selected_nodes){ n->position = node_tl->position + (n->drag_offset * scale); } } else { scale_handle->position = corner_br; } } } void snapPolar(std::shared_ptr 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; bool connect_once = false; state->mouse_z = canvas->z_index; while(APP::event_queue.size() > 0 && !exit){ auto e = APP::event_queue.front(); //std::cout << e.item->getType() << " " << e.action << std::endl; if(!connect_once && e.action == APP::Interaction::CONNECT){ auto connector = std::dynamic_pointer_cast(e.item); auto connector_to = std::dynamic_pointer_cast(connector->partner); gm->addConnection(std::dynamic_pointer_cast(connector->parent), std::dynamic_pointer_cast(connector_to->parent)); std::cout << "connector use count: " << connector->getPtr().use_count() << std::endl; 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(e.item); if(e.action == APP::Interaction::PICKUP) { node->pickUp(); if(node->selected){ for(auto s : gm->selected_nodes){ 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->selected_nodes){ s->drag(MOD_MOUSE + s->drag_offset); } } once = true; } if(e.action == APP::Interaction::PUTDOWN) { node->putDown(); if(node->selected){ for(auto s : gm->selected_nodes){ 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(); } }; }