ppdnode/lib/ImNodeFlow.h
2024-05-07 20:10:53 -04:00

1319 lines
40 KiB
C++
Executable File

#ifndef IM_NODE_FLOW
#define IM_NODE_FLOW
#pragma once
#include "context_wrapper.h"
#include "imgui.h"
#include "imgui_bezier_math.h"
#include "imgui_internal.h"
#include <algorithm>
#include <cmath>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
namespace ImFlow {
// -----------------------------------------------------------------------------------------------------------------
// HELPERS
/**
* @brief <BR>Draw a sensible bezier between two points
* @param p1 Starting point
* @param p2 Ending point
* @param color Color of the curve
* @param thickness Thickness of the curve
*/
inline static void smart_bezier(const ImVec2 &p1, const ImVec2 &p2, ImU32 color,
float thickness);
/**
* @brief <BR>Collider checker for smart_bezier
* @details Projects the point "p" orthogonally onto the bezier curve and
* checks if the distance is less than the given radius.
* @param p Point to be tested
* @param p1 Starting point of smart_bezier
* @param p2 Ending point of smart_bezier
* @param radius Lateral width of the hit box
* @return [TRUE] if "p" is inside the collider
*
* Intended to be used in union with smart_bezier();
*/
inline static bool smart_bezier_collider(const ImVec2 &p, const ImVec2 &p1,
const ImVec2 &p2, float radius);
// -----------------------------------------------------------------------------------------------------------------
// CLASSES PRE-DEFINITIONS
template <typename T> class InPin;
template <typename T> class OutPin;
class Pin;
class BaseNode;
class ImNodeFlow;
// -----------------------------------------------------------------------------------------------------------------
// PIN'S PROPERTIES
/**
* @brief <BR>Basic filters
* @details List of, ready to use, basic filters. It's possible to create more
* filters with the help of "ConnectionFilter_MakeCustom".
*/
enum ConnectionFilter_ {
ConnectionFilter_None = 0,
ConnectionFilter_SameNode = 1 << 1,
ConnectionFilter_Bool = 1 << 2,
ConnectionFilter_Int = 1 << 3,
ConnectionFilter_Float = 1 << 4,
ConnectionFilter_Double = 1 << 5,
ConnectionFilter_String = 1 << 6,
ConnectionFilter_MakeCustom = 1 << 7,
ConnectionFilter_Numbers =
ConnectionFilter_Int | ConnectionFilter_Float | ConnectionFilter_Double
};
typedef long ConnectionFilter;
typedef unsigned long long int PinUID;
/**
* @brief Extra pin's style setting
*/
struct PinStyleExtras {
/// @brief Top and bottom spacing
ImVec2 padding = ImVec2(3.f, 1.f);
/// @brief Border and background corner rounding
float bg_radius = 8.f;
/// @brief Border thickness
float border_thickness = 1.f;
/// @brief Background color
ImU32 bg_color = IM_COL32(23, 16, 16, 0);
/// @brief Background color when hovered
ImU32 bg_hover_color = IM_COL32(100, 100, 255, 70);
/// @brief Border color
ImU32 border_color = IM_COL32(255, 255, 255, 0);
/// @brief Link thickness
float link_thickness = 2.6f;
/// @brief Link thickness when dragged
float link_dragged_thickness = 2.2f;
/// @brief Link thickness when hovered
float link_hovered_thickness = 3.5f;
/// @brief Thickness of the outline of a selected link
float link_selected_outline_thickness = 0.5f;
/// @brief Color of the outline of a selected link
ImU32 outline_color = IM_COL32(80, 20, 255, 200);
/// @brief Spacing between pin content and socket
float socket_padding = 6.6f;
};
/**
* @brief Defines the visual appearance of a pin
*/
class PinStyle {
public:
PinStyle(ImU32 color, int socket_shape, float socket_radius,
float socket_hovered_radius, float socket_connected_radius,
float socket_thickness)
: color(color), socket_shape(socket_shape), socket_radius(socket_radius),
socket_hovered_radius(socket_hovered_radius),
socket_connected_radius(socket_connected_radius),
socket_thickness(socket_thickness) {}
/// @brief Socket and link color
ImU32 color;
/// @brief Socket shape ID
int socket_shape;
/// @brief Socket radius
float socket_radius;
/// @brief Socket radius when hovered
float socket_hovered_radius;
/// @brief Socket radius when connected
float socket_connected_radius;
/// @brief Socket outline thickness when empty
float socket_thickness;
/// @brief List of less common properties
PinStyleExtras extra;
public:
/// @brief <BR>Default cyan style
static std::shared_ptr<PinStyle> cyan() {
return std::make_shared<PinStyle>(
PinStyle(IM_COL32(87, 155, 185, 255), 0, 4.f, 4.67f, 3.7f, 1.f));
}
/// @brief <BR>Default green style
static std::shared_ptr<PinStyle> green() {
return std::make_shared<PinStyle>(
PinStyle(IM_COL32(90, 191, 93, 255), 4, 4.f, 4.67f, 4.2f, 1.3f));
}
/// @brief <BR>Default blue style
static std::shared_ptr<PinStyle> blue() {
return std::make_shared<PinStyle>(
PinStyle(IM_COL32(90, 117, 191, 255), 0, 4.f, 4.67f, 3.7f, 1.f));
}
/// @brief <BR>Default brown style
static std::shared_ptr<PinStyle> brown() {
return std::make_shared<PinStyle>(
PinStyle(IM_COL32(191, 134, 90, 255), 0, 4.f, 4.67f, 3.7f, 1.f));
}
/// @brief <BR>Default red style
static std::shared_ptr<PinStyle> red() {
return std::make_shared<PinStyle>(
PinStyle(IM_COL32(191, 90, 90, 255), 0, 4.f, 4.67f, 3.7f, 1.f));
}
/// @brief <BR>Default white style
static std::shared_ptr<PinStyle> white() {
return std::make_shared<PinStyle>(
PinStyle(IM_COL32(255, 255, 255, 255), 5, 4.f, 4.67f, 4.2f, 1.f));
}
};
// -----------------------------------------------------------------------------------------------------------------
// NODE'S PROPERTIES
typedef uintptr_t NodeUID;
/**
* @brief Defines the visual appearance of a node
*/
class NodeStyle {
public:
NodeStyle(ImU32 header_bg, ImColor header_title_color, float radius)
: header_bg(header_bg), header_title_color(header_title_color),
radius(radius) {}
/// @brief Body's background color
ImU32 bg = IM_COL32(55, 64, 75, 255);
/// @brief Header's background color
ImU32 header_bg;
/// @brief Header title color
ImColor header_title_color;
/// @brief Border color
ImU32 border_color = IM_COL32(30, 38, 41, 128);
/// @brief Border color when selected
ImU32 border_selected_color = IM_COL32(170, 190, 205, 255);
/// @brief Body's content padding (Left Top Right Bottom)
ImVec4 padding = ImVec4(13.7f, 6.f, 13.7f, 2.f);
/// @brief Edges rounding
float radius;
/// @brief Border thickness
float border_thickness = -1.35f;
/// @brief Border thickness when selected
float border_selected_thickness = 2.f;
public:
/// @brief <BR>Default cyan style
static std::shared_ptr<NodeStyle> cyan() {
return std::make_shared<NodeStyle>(IM_COL32(71, 142, 173, 255),
ImColor(233, 241, 244, 255), 6.5f);
}
/// @brief <BR>Default green style
static std::shared_ptr<NodeStyle> green() {
return std::make_shared<NodeStyle>(IM_COL32(90, 191, 93, 255),
ImColor(233, 241, 244, 255), 6.5f);
}
/// @brief <BR>Default red style
static std::shared_ptr<NodeStyle> red() {
return std::make_shared<NodeStyle>(IM_COL32(191, 90, 90, 255),
ImColor(233, 241, 244, 255), 6.f);
}
/// @brief <BR>Default brown style
static std::shared_ptr<NodeStyle> brown() {
return std::make_shared<NodeStyle>(IM_COL32(191, 134, 90, 255),
ImColor(233, 241, 244, 255), 6.5f);
}
};
// -----------------------------------------------------------------------------------------------------------------
// LINK
/**
* @brief Link between two Pins of two different Nodes
*/
class Link {
public:
/**
* @brief <BR>Construct a link
* @param left Pointer to the output Pin of the Link
* @param right Pointer to the input Pin of the Link
* @param inf Pointer to the Handler that contains the Link
*/
explicit Link(Pin *left, Pin *right, ImNodeFlow *inf)
: m_left(left), m_right(right), m_inf(inf) {}
/**
* @brief <BR>Destruction of a link
* @details Deletes references of this links form connected pins
*/
~Link();
/**
* @brief <BR>Looping function to update the Link
* @details Draws the Link and updates Hovering and Selected status.
*/
void update();
/**
* @brief <BR>Get Left pin of the link
* @return Pointer to the Pin
*/
[[nodiscard]] Pin *left() const { return m_left; }
/**
* @brief <BR>Get Right pin of the link
* @return Pointer to the Pin
*/
[[nodiscard]] Pin *right() const { return m_right; }
/**
* @brief <BR>Get hovering status
* @return [TRUE] If the link is hovered in the current frame
*/
[[nodiscard]] bool isHovered() const { return m_hovered; }
/**
* @brief <BR>Get selected status
* @return [TRUE] If the link is selected in the current frame
*/
[[nodiscard]] bool isSelected() const { return m_selected; }
private:
ImNodeFlow *m_inf;
Pin *m_left;
Pin *m_right;
bool m_hovered = false;
bool m_selected = false;
};
// -----------------------------------------------------------------------------------------------------------------
// HANDLER
/**
* @brief Grid's the color parameters
*/
struct InfColors {
/// @brief Background of the grid
ImU32 background = IM_COL32(33, 41, 45, 255);
/// @brief Main lines of the grid
ImU32 grid = IM_COL32(200, 200, 200, 40);
/// @brief Secondary lines
ImU32 subGrid = IM_COL32(200, 200, 200, 10);
};
/**
* @brief ALl the grid's appearance parameters. Sizes + Colors
*/
struct InfStyler {
/// @brief Size of main grid
float grid_size = 50.f;
/// @brief Sub-grid divisions for Node snapping
float grid_subdivisions = 5.f;
/// @brief ImNodeFlow colors
InfColors colors;
};
/**
* @brief Main node editor
* @details Handles the infinite grid, nodes and links. Also handles all the
* logic.
*/
class ImNodeFlow {
private:
static int m_instances;
public:
/**
* @brief <BR>Instantiate a new editor with default name.
* <BR> Editor name will be "FlowGrid + the number of editors"
*/
ImNodeFlow() : ImNodeFlow("FlowGrid" + std::to_string(m_instances)) {}
/**
* @brief <BR>Instantiate a new editor with given name
* @details Creates a new Node Editor with the given name.
* @param name Name of the editor
*/
explicit ImNodeFlow(std::string name) : m_name(std::move(name)) {
m_instances++;
m_context.config().extra_window_wrapper = true;
m_context.config().color = m_style.colors.background;
}
/**
* @brief <BR>Handler loop
* @details Main update function. Refreshes all the logic and draws
* everything. Must be called every frame.
*/
void update();
/**
* @brief <BR>Add a node to the grid
* @tparam T Derived class of <BaseNode> to be added
* @tparam Params types of optional args to forward to derived class ctor
* @param pos Position of the Node in grid coordinates
* @param args Optional arguments to be forwarded to derived class ctor
* @return Shared pointer of the pushed type to the newly added node
*
* Inheritance is checked at compile time, \<T> MUST be derived from BaseNode.
*/
template <typename T, typename... Params>
std::shared_ptr<T> addNode(const ImVec2 &pos, Params &&...args);
/**
* @brief <BR>Add a node to the grid
* @tparam T Derived class of <BaseNode> to be added
* @tparam Params types of optional args to forward to derived class ctor
* @param pos Position of the Node in screen coordinates
* @param args Optional arguments to be forwarded to derived class ctor
* @return Shared pointer of the pushed type to the newly added node
*
* Inheritance is checked at compile time, \<T> MUST be derived from BaseNode.
*/
template <typename T, typename... Params>
std::shared_ptr<T> placeNodeAt(const ImVec2 &pos, Params &&...args);
/**
* @brief <BR>Add a node to the grid using mouse position
* @tparam T Derived class of <BaseNode> to be added
* @tparam Params types of optional args to forward to derived class ctor
* @param args Optional arguments to be forwarded to derived class ctor
* @return Shared pointer of the pushed type to the newly added node
*
* Inheritance is checked at compile time, \<T> MUST be derived from BaseNode.
*/
template <typename T, typename... Params>
std::shared_ptr<T> placeNode(Params &&...args);
/**
* @brief <BR>Add link to the handler internal list
* @param link Reference to the link
*/
void addLink(std::shared_ptr<Link> &link);
/**
* @brief <BR>Pop-up when link is "dropped"
* @details Sets the content of a pop-up that can be displayed when dragging a
* link in the open instead of onto another pin.
* @details If "key = ImGuiKey_None" the pop-up will always open when a link
* is dropped.
* @param content Function or Lambda containing only the contents of the
* pop-up and the subsequent logic
* @param key Optional key required in order to open the pop-up
*/
void droppedLinkPopUpContent(std::function<void(Pin *dragged)> content,
ImGuiKey key = ImGuiKey_None) {
m_droppedLinkPopUp = std::move(content);
m_droppedLinkPupUpComboKey = key;
}
/**
* @brief <BR>Pop-up when right-clicking
* @details Sets the content of a pop-up that can be displayed when
* right-clicking on the grid.
* @param content Function or Lambda containing only the contents of the
* pop-up and the subsequent logic
*/
void rightClickPopUpContent(std::function<void(BaseNode *node)> content) {
m_rightClickPopUp = std::move(content);
}
/**
* @brief <BR>Get mouse clicking status
* @return [TRUE] if mouse is clicked and click hasn't been consumed
*/
[[nodiscard]] bool getSingleUseClick() const { return m_singleUseClick; }
/**
* @brief <BR>Consume the click for the given frame
*/
void consumeSingleUseClick() { m_singleUseClick = false; }
/**
* @brief <BR>Get editor's name
* @return Const reference to editor's name
*/
const std::string &getName() { return m_name; }
/**
* @brief <BR>Get editor's position
* @return Const reference to editor's position in screen coordinates
*/
const ImVec2 &getPos() { return m_context.origin(); }
/**
* @brief <BR>Get editor's grid scroll
* @details Scroll is the offset from the origin of the grid, changes while
* navigating the grid.
* @return Const reference to editor's grid scroll
*/
const ImVec2 &getScroll() { return m_context.scroll(); }
/**
* @brief <BR>Get editor's list of nodes
* @return Const reference to editor's internal nodes list
*/
std::unordered_map<NodeUID, std::shared_ptr<BaseNode>> &getNodes() {
return m_nodes;
}
/**
* @brief <BR>Get nodes count
* @return Number of nodes present in the editor
*/
uint32_t getNodesCount() { return (uint32_t)m_nodes.size(); }
/**
* @brief <BR>Get editor's list of links
* @return Const reference to editor's internal links list
*/
const std::vector<std::weak_ptr<Link>> &getLinks() { return m_links; }
/**
* @brief <BR>Get zooming viewport
* @return Const reference to editor's internal viewport for zoom support
*/
ContainedContext &getGrid() { return m_context; }
/**
* @brief <BR>Get dragging status
* @return [TRUE] if a Node is being dragged around the grid
*/
[[nodiscard]] bool isNodeDragged() const { return m_draggingNode; }
/**
* @brief <BR>Get current style
* @return Reference to style variables
*/
InfStyler &getStyle() { return m_style; }
/**
* @brief <BR>Set editor's size
* @param size Editor's size. Set to (0, 0) to auto-fit.
*/
void setSize(const ImVec2 &size) { m_context.config().size = size; }
/**
* @brief <BR>Set dragging status
* @param state New dragging state
*
* The new state will only be updated one at the start of each frame.
*/
void draggingNode(bool state) { m_draggingNodeNext = state; }
/**
* @brief <BR>Set what pin is being hovered
* @param hovering Pointer to the hovered pin
*/
void hovering(Pin *hovering) { m_hovering = hovering; }
/**
* @brief <BR>Set what node is being hovered
* @param hovering Pointer to the hovered node
*/
void hoveredNode(BaseNode *hovering) { m_hoveredNode = hovering; }
/**
* @brief <BR>Convert coordinates from screen to grid
* @param p Point in screen coordinates to be converted
* @return Point in grid's coordinates
*/
ImVec2 screen2grid(const ImVec2 &p);
/**
* @brief <BR>Convert coordinates from grid to screen
* @param p Point in grid's coordinates to be converted
* @return Point in screen coordinates
*/
ImVec2 grid2screen(const ImVec2 &p);
/**
* @brief <BR>Check if mouse is on selected node
* @return [TRUE] if the mouse is hovering a selected node
*/
bool on_selected_node();
/**
* @brief <BR>Check if mouse is on a free point on the grid
* @return [TRUE] if the mouse is not hovering a node or a link
*/
bool on_free_space();
private:
std::string m_name;
ContainedContext m_context;
bool m_singleUseClick = false;
std::unordered_map<NodeUID, std::shared_ptr<BaseNode>> m_nodes;
std::vector<std::weak_ptr<Link>> m_links;
std::function<void(Pin *dragged)> m_droppedLinkPopUp;
ImGuiKey m_droppedLinkPupUpComboKey = ImGuiKey_None;
Pin *m_droppedLinkLeft = nullptr;
std::function<void(BaseNode *node)> m_rightClickPopUp;
BaseNode *m_hoveredNodeAux = nullptr;
BaseNode *m_hoveredNode = nullptr;
bool m_draggingNode = false, m_draggingNodeNext = false;
Pin *m_hovering = nullptr;
Pin *m_dragOut = nullptr;
InfStyler m_style;
};
// -----------------------------------------------------------------------------------------------------------------
// BASE NODE
/**
* @brief Parent class for custom nodes
* @details Main class from which custom nodes can be created. All interactions
* with the main grid are handled internally.
*/
class BaseNode {
public:
BaseNode() = default;
/**
* @brief <BR>Main loop of the node
* @details Updates position, hovering and selected status, and renders the
* node. Must be called each frame.
*/
void update();
/**
* @brief <BR>Content of the node
* @details Function to be implemented by derived custom nodes.
* Must contain the body of the node. If left empty the node will
* only have input and output pins.
*/
virtual void draw() {}
/**
* @brief <BR>Add an Input to the node
* @details Will add an Input pin to the node with the given name and data
* type. <BR> <BR> In this case the name of the pin will also be its UID. <BR>
* <BR> The UID must be unique only in the context of the current node's
* inputs.
* @tparam T Type of the data the pin will handle
* @param name Name of the pin
* @param defReturn Default return value when the pin is not connected
* @param filter Connection filter
* @param style Style of the pin
* @return Shared pointer to the newly added pin
*/
template <typename T>
std::shared_ptr<InPin<T>>
addIN(const std::string &name, T defReturn,
ConnectionFilter filter = ConnectionFilter_None,
std::shared_ptr<PinStyle> style = nullptr);
/**
* @brief <BR>Add an Input to the node
* @details Will add an Input pin to the node with the given name and data
* type. <BR> <BR> The UID must be unique only in the context of the current
* node's inputs.
* @tparam T Type of the data the pin will handle
* @tparam U Type of the UID
* @param uid Unique identifier of the pin
* @param name Name of the pin
* @param defReturn Default return value when the pin is not connected
* @param filter Connection filter
* @param style Style of the pin
* @return Shared pointer to the newly added pin
*/
template <typename T, typename U>
std::shared_ptr<InPin<T>>
addIN_uid(const U &uid, const std::string &name, T defReturn,
ConnectionFilter filter = ConnectionFilter_None,
std::shared_ptr<PinStyle> style = nullptr);
/**
* @brief <BR>Remove input pin
* @tparam U Type of the UID
* @param uid Unique identifier of the pin
*/
template <typename U> void dropIN(const U &uid);
/**
* @brief <BR>Remove input pin
* @param uid Unique identifier of the pin
*/
void dropIN(const char *uid);
/**
* @brief <BR>Show a temporary input pin
* @details Will show an input pin with the given name.
* The pin is created the first time showIN is called and kept alive
* as long as showIN is called each frame. <BR> <BR> In this case the name of
* the pin will also be its UID. <BR> <BR> The UID must be unique only in the
* context of the current node's inputs.
* @tparam T Type of the data the pin will handle
* @param name Name of the pin
* @param defReturn Default return value when the pin is not connected
* @param filter Connection filter
* @param style Style of the pin
* @return Const reference to the value of the connected link for the current
* frame of defReturn
*/
template <typename T>
const T &showIN(const std::string &name, T defReturn,
ConnectionFilter filter = ConnectionFilter_None,
std::shared_ptr<PinStyle> style = nullptr);
/**
* @brief <BR>Show a temporary input pin
* @details Will show an input pin with the given name and UID.
* The pin is created the first time showIN_uid is called and kept
* alive as long as showIN_uid is called each frame. <BR> <BR> The UID must be
* unique only in the context of the current node's inputs.
* @tparam T Type of the data the pin will handle
* @tparam U Type of the UID
* @param uid Unique identifier of the pin
* @param name Name of the pin
* @param defReturn Default return value when the pin is not connected
* @param filter Connection filter
* @param style Style of the pin
* @return Const reference to the value of the connected link for the current
* frame of defReturn
*/
template <typename T, typename U>
const T &showIN_uid(const U &uid, const std::string &name, T defReturn,
ConnectionFilter filter = ConnectionFilter_None,
std::shared_ptr<PinStyle> style = nullptr);
/**
* @brief <BR>Add an Output to the node
* @details Must be called in the node constructor. WIll add an Output pin to
* the node with the given name and data type. <BR> <BR> In this case the name
* of the pin will also be its UID. <BR> <BR> The UID must be unique only in
* the context of the current node's outputs.
* @tparam T Type of the data the pin will handle
* @param name Name of the pin
* @param filter Connection filter
* @param style Style of the pin
* @return Shared pointer to the newly added pin. Must be used to set the
* behaviour
*/
template <typename T>
[[nodiscard]] std::shared_ptr<OutPin<T>>
addOUT(const std::string &name,
ConnectionFilter filter = ConnectionFilter_None,
std::shared_ptr<PinStyle> style = nullptr);
/**
* @brief <BR>Add an Output to the node
* @details Must be called in the node constructor. WIll add an Output pin to
* the node with the given name and data type. <BR> <BR> The UID must be
* unique only in the context of the current node's outputs.
* @tparam T Type of the data the pin will handle
* @tparam U Type of the UID
* @param uid Unique identifier of the pin
* @param name Name of the pin
* @param filter Connection filter
* @param style Style of the pin
* @return Shared pointer to the newly added pin. Must be used to set the
* behaviour
*/
template <typename T, typename U>
[[nodiscard]] std::shared_ptr<OutPin<T>>
addOUT_uid(const U &uid, const std::string &name,
ConnectionFilter filter = ConnectionFilter_None,
std::shared_ptr<PinStyle> style = nullptr);
/**
* @brief <BR>Remove output pin
* @tparam U Type of the UID
* @param uid Unique identifier of the pin
*/
template <typename U> void dropOUT(const U &uid);
/**
* @brief <BR>Remove output pin
* @param uid Unique identifier of the pin
*/
void dropOUT(const char *uid);
/**
* @brief <BR>Show a temporary output pin
* @details Will show an output pin with the given name.
* The pin is created the first time showOUT is called and kept alive
* as long as showOUT is called each frame. <BR> <BR> In this case the name of
* the pin will also be its UID. <BR> <BR> The UID must be unique only in the
* context of the current node's outputs.
* @tparam T Type of the data the pin will handle
* @param name Name of the pin
* @param behaviour Function or lambda expression used to calculate output
* value
* @param filter Connection filter
* @param style Style of the pin
*/
template <typename T>
void showOUT(const std::string &name, std::function<T()> behaviour,
ConnectionFilter filter = ConnectionFilter_None,
std::shared_ptr<PinStyle> style = nullptr);
/**
* @brief <BR>Show a temporary output pin
* @details Will show an output pin with the given name.
* The pin is created the first time showOUT_uid is called and kept
* alive as long as showOUT_uid is called each frame. <BR> <BR> The UID must
* be unique only in the context of the current node's outputs.
* @tparam T Type of the data the pin will handle
* @tparam U Type of the UID
* @param uid Unique identifier of the pin
* @param name Name of the pin
* @param behaviour Function or lambda expression used to calculate output
* value
* @param filter Connection filter
* @param style Style of the pin
*/
template <typename T, typename U>
void showOUT_uid(const U &uid, const std::string &name,
std::function<T()> behaviour,
ConnectionFilter filter = ConnectionFilter_None,
std::shared_ptr<PinStyle> style = nullptr);
/**
* @brief <BR>Get Input value from an InPin
* @details Get a reference to the value of an input pin, the value is stored
* in the output pin at the other end of the link.
* @tparam T Data type
* @tparam U Type of the UID
* @param uid Unique identifier of the pin
* @return Const reference to the value
*/
template <typename T, typename U> const T &getInVal(const U &uid);
/**
* @brief <BR>Get Input value from an InPin
* @details Get a reference to the value of an input pin, the value is stored
* in the output pin at the other end of the link.
* @tparam T Data type
* @param uid Unique identifier of the pin
* @return Const reference to the value
*/
template <typename T> const T &getInVal(const char *uid);
/**
* @brief <BR>Get generic reference to input pin
* @tparam U Type of the UID
* @param uid Unique identifier of the pin
* @return Generic pointer to the pin
*/
template <typename U> Pin *inPin(const U &uid);
/**
* @brief <BR>Get generic reference to input pin
* @param uid Unique identifier of the pin
* @return Generic pointer to the pin
*/
Pin *inPin(const char *uid);
/**
* @brief <BR>Get generic reference to output pin
* @tparam U Type of the UID
* @param uid Unique identifier of the pin
* @return Generic pointer to the pin
*/
template <typename U> Pin *outPin(const U &uid);
/**
* @brief <BR>Get generic reference to output pin
* @param uid Unique identifier of the pin
* @return Generic pointer to the pin
*/
Pin *outPin(const char *uid);
/**
* @brief <BR>Get internal input pins list
* @return Const reference to node's internal list
*/
const std::vector<std::shared_ptr<Pin>> &getIns() { return m_ins; }
/**
* @brief <BR>Get internal output pins list
* @return Const reference to node's internal list
*/
const std::vector<std::shared_ptr<Pin>> &getOuts() { return m_outs; }
/**
* @brief <BR>Delete itself
*/
void destroy() { m_destroyed = true; }
/*
* @brief <BR>Get if node must be deleted
*/
[[nodiscard]] bool toDestroy() const { return m_destroyed; }
/**
* @brief <BR>Get hovered status
* @return [TRUE] if the mouse is hovering the node
*/
bool isHovered();
/**
* @brief <BR>Get node's UID
* @return Node's unique identifier
*/
[[nodiscard]] NodeUID getUID() const { return m_uid; }
/**
* @brief <BR>Get node name
* @return Const reference to the node's name
*/
const std::string &getName() { return m_title; }
/**
* @brief <BR>Get node size
* @return Const reference to the node's size
*/
const ImVec2 &getSize() { return m_size; }
/**
* @brief <BR>Get node position
* @return Const reference to the node's position
*/
const ImVec2 &getPos() { return m_pos; }
/**
* @brief <BR>Get grid handler bound to node
* @return Pointer to the handler
*/
ImNodeFlow *getHandler() { return m_inf; }
/**
* @brief <BR>Get node's style
* @return Shared pointer to the node's style
*/
const std::shared_ptr<NodeStyle> &getStyle() { return m_style; }
/**
* @brief <BR>Get selected status
* @return [TRUE] if the node is selected
*/
[[nodiscard]] bool isSelected() const { return m_selected; }
/**
* @brief <BR>Get dragged status
* @return [TRUE] if the node is being dragged
*/
[[nodiscard]] bool isDragged() const { return m_dragged; }
/**
* @brief <BR>Set node's uid
* @param uid Node's unique identifier
*/
BaseNode *setUID(NodeUID uid) {
m_uid = uid;
return this;
}
/**
* @brief <BR>Set node's name
* @param name New title
*/
BaseNode *setTitle(const std::string &title) {
m_title = title;
return this;
}
/**
* @brief <BR>Set node's position
* @param pos Position in grid coordinates
*/
BaseNode *setPos(const ImVec2 &pos) {
m_pos = pos;
m_posTarget = pos;
return this;
}
/**
* @brief <BR>Set ImNodeFlow handler
* @param inf Grid handler for the node
*/
BaseNode *setHandler(ImNodeFlow *inf) {
m_inf = inf;
return this;
}
/**
* @brief Set node's style
* @param style New style
*/
BaseNode *setStyle(std::shared_ptr<NodeStyle> style) {
m_style = std::move(style);
return this;
}
/**
* @brief <BR>Set selected status
* @param state New selected state
*
* Status only updates when updatePublicStatus() is called
*/
BaseNode *selected(bool state) {
m_selectedNext = state;
return this;
}
/**
* @brief <BR>Update the isSelected status of the node
*/
void updatePublicStatus() { m_selected = m_selectedNext; }
private:
NodeUID m_uid = 0;
std::string m_title;
ImVec2 m_pos, m_posTarget;
ImVec2 m_size;
ImNodeFlow *m_inf = nullptr;
std::shared_ptr<NodeStyle> m_style;
bool m_selected = false, m_selectedNext = false;
bool m_dragged = false;
bool m_destroyed = false;
std::vector<std::shared_ptr<Pin>> m_ins;
std::vector<std::pair<int, std::shared_ptr<Pin>>> m_dynamicIns;
std::vector<std::shared_ptr<Pin>> m_outs;
std::vector<std::pair<int, std::shared_ptr<Pin>>> m_dynamicOuts;
};
// -----------------------------------------------------------------------------------------------------------------
// PINS
/**
* @brief Pins type identifier
*/
enum PinType { PinType_Input, PinType_Output };
/**
* @brief Generic base class for pins
*/
class Pin {
public:
/**
* @brief <BR>Generic pin constructor
* @param name Name of the pin
* @param filter Connection filter
* @param kind Specifies Input or Output
* @param parent Pointer to the Node containing the pin
* @param inf Pointer to the Grid Handler the pin is in (same as parent)
* @param style Style of the pin
*/
explicit Pin(PinUID uid, std::string name, ConnectionFilter filter,
PinType kind, BaseNode *parent, ImNodeFlow **inf,
std::shared_ptr<PinStyle> style)
: m_uid(uid), m_name(std::move(name)), m_filter(filter), m_type(kind),
m_parent(parent), m_inf(inf), m_style(std::move(style)) {
if (!m_style)
m_style = PinStyle::cyan();
}
/**
* @brief <BR>Main loop of the pin
* @details Updates position, hovering and dragging status, and renders the
* pin. Must be called each frame.
*/
void update();
/**
* @brief <BR>Draw default pin's socket
*/
void drawSocket();
/**
* @brief <BR>Draw default pin's decoration (border, bg, and hover overlay)
*/
void drawDecoration();
/**
* @brief <BR>Used by output pins to calculate their values
*/
virtual void resolve() {}
/**
* @brief <BR>Custom render function to override Pin appearance
* @param r Function or lambda expression with new ImGui rendering
*/
Pin *renderer(std::function<void(Pin *p)> r) {
m_renderer = std::move(r);
return this;
}
/**
* @brief <BR>Create link between pins
* @param other Pointer to the other pin
*/
virtual void createLink(Pin *other) = 0;
/**
* @brief <BR>Set the reference to a link
* @param link Smart pointer to the link
*/
virtual void setLink(std::shared_ptr<Link> &link) {}
/**
* @brief <BR>Delete link reference
*/
virtual void deleteLink() = 0;
/**
* @brief <BR>Get connected status
* @return [TRUE] if the pin is connected
*/
virtual bool isConnected() = 0;
/**
* @brief <BR>Get pin's link
* @return Weak_ptr reference to pin's link
*/
virtual std::weak_ptr<Link> getLink() { return std::weak_ptr<Link>{}; }
/**
* @brief <BR>Get pin's UID
* @return Unique identifier of the pin
*/
[[nodiscard]] PinUID getUid() const { return m_uid; }
/**
* @brief <BR>Get pin's name
* @return Const reference to pin's name
*/
const std::string &getName() { return m_name; }
/**
* @brief <BR>Get pin's position
* @return Const reference to pin's position in grid coordinates
*/
[[nodiscard]] const ImVec2 &getPos() { return m_pos; }
/**
* @brief <BR>Get pin's hit-box size
* @return Const reference to pin's hit-box size
*/
[[nodiscard]] const ImVec2 &getSize() { return m_size; }
/**
* @brief <BR>Get pin's parent node
* @return Generic type pointer to pin's parent node. (Node that contains it)
*/
BaseNode *getParent() { return m_parent; }
/**
* @brief <BR>Get pin's type
* @return The pin type. Either Input or Output
*/
PinType getType() { return m_type; }
/**
* @brief <BR>Get pin's connection filter
* @return Pin's connection filter configuration
*/
[[nodiscard]] ConnectionFilter getFilter() const { return m_filter; }
/**
* @brief <BR>Get pin's style
* @return Smart pointer to pin's style
*/
std::shared_ptr<PinStyle> &getStyle() { return m_style; }
/**
* @brief <BR>Get pin's link attachment point (socket)
* @return Grid coordinates to the attachment point between the link and the
* pin's socket
*/
virtual ImVec2 pinPoint() = 0;
/**
* @brief <BR>Calculate pin's width pre-rendering
* @return The with of the pin once it will be rendered
*/
float calcWidth() { return ImGui::CalcTextSize(m_name.c_str()).x; }
/**
* @brief <BR>Set pin's position
* @param pos Position in screen coordinates
*/
void setPos(ImVec2 pos) { m_pos = pos; }
protected:
PinUID m_uid;
std::string m_name;
ImVec2 m_pos = ImVec2(0.f, 0.f);
ImVec2 m_size = ImVec2(0.f, 0.f);
PinType m_type;
ConnectionFilter m_filter;
std::shared_ptr<PinStyle> m_style;
BaseNode *m_parent = nullptr;
ImNodeFlow **m_inf;
std::function<void(Pin *p)> m_renderer;
};
/**
* @brief Input specific pin
* @details Derived from the generic class Pin. The input pin owns the link
* pointer.
* @tparam T Data type handled by the pin
*/
template <class T> class InPin : public Pin {
public:
/**
* @brief <BR>Input pin constructor
* @param name Name of the pin
* @param filter Connection filter
* @param parent Pointer to the Node containing the pin
* @param defReturn Default return value when the pin is not connected
* @param inf Pointer to the Grid Handler the pin is in (same as parent)
* @param style Style of the pin
*/
explicit InPin(PinUID uid, const std::string &name, ConnectionFilter filter,
BaseNode *parent, T defReturn, ImNodeFlow **inf,
std::shared_ptr<PinStyle> style)
: Pin(uid, name, filter, PinType_Input, parent, inf, style),
m_emptyVal(defReturn) {}
/**
* @brief <BR>Create link between pins
* @param other Pointer to the other pin
*/
void createLink(Pin *other) override;
/**
* @brief <BR>Delete the link connected to the pin
*/
void deleteLink() override { m_link.reset(); }
/**
* @brief <BR>Get connected status
* @return [TRUE] is pin is connected to a link
*/
bool isConnected() override { return m_link != nullptr; }
/**
* @brief <BR>Get pin's link
* @return Weak_ptr reference to the link connected to the pin
*/
std::weak_ptr<Link> getLink() override { return m_link; }
/**
* @brief <BR>Get pin's link attachment point (socket)
* @return Grid coordinates to the attachment point between the link and the
* pin's socket
*/
ImVec2 pinPoint() override {
return m_pos + ImVec2(-m_style->extra.socket_padding, m_size.y / 2);
}
/**
* @brief <BR>Get value carried by the connected link
* @return Reference to the value of the connected OutPin. Or the default
* value if not connected
*/
const T &val();
private:
std::shared_ptr<Link> m_link;
T m_emptyVal;
};
/**
* @brief Output specific pin
* @details Derived from the generic class Pin. The output pin handles the
* logic.
* @tparam T Data type handled by the pin
*/
template <class T> class OutPin : public Pin {
public:
/**
* @brief <BR>Output pin constructor
* @param name Name of the pin
* @param filter Connection filter
* @param parent Pointer to the Node containing the pin
* @param inf Pointer to the Grid Handler the pin is in (same as parent)
* @param style Style of the pin
*/
explicit OutPin(PinUID uid, const std::string &name, ConnectionFilter filter,
BaseNode *parent, ImNodeFlow **inf,
std::shared_ptr<PinStyle> style)
: Pin(uid, name, filter, PinType_Output, parent, inf, style) {}
/**
* @brief <BR>When parent gets deleted, remove the links
*/
~OutPin() {
for (auto &l : m_links)
if (!l.expired())
l.lock()->right()->deleteLink();
}
/**
* @brief <BR>Calculate output value based on set behaviour
*/
void resolve() override { m_val = m_behaviour(); }
/**
* @brief <BR>Create link between pins
* @param other Pointer to the other pin
*/
void createLink(Pin *other) override;
/**
* @brief <BR>Add a connected link to the internal list
* @param link Pointer to the link
*/
void setLink(std::shared_ptr<Link> &link) override;
/**
* @brief <BR>Delete any expired weak pointers to a (now deleted) link
*/
void deleteLink() override;
/**
* @brief <BR>Get connected status
* @return [TRUE] is pin is connected to one or more links
*/
bool isConnected() override { return !m_links.empty(); }
/**
* @brief <BR>Get pin's link attachment point (socket)
* @return Grid coordinates to the attachment point between the link and the
* pin's socket
*/
ImVec2 pinPoint() override {
return m_pos +
ImVec2(m_size.x + m_style->extra.socket_padding, m_size.y / 2);
}
/**
* @brief <BR>Get output value
* @return Const reference to the internal value of the pin
*/
const T &val();
/**
* @brief <BR>Set logic to calculate output value
* @details Used to define the pin behaviour. This is what gets the data from
* the parent's inputs, and applies the needed logic.
* @param func Function or lambda expression used to calculate output value
*/
OutPin<T> *behaviour(std::function<T()> func) {
m_behaviour = std::move(func);
return this;
}
private:
std::vector<std::weak_ptr<Link>> m_links;
std::function<T()> m_behaviour;
T m_val;
};
} // namespace ImFlow
#include "./ImNodeFlow.inl"
#endif