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

348 lines
12 KiB
C++
Executable File

#pragma once
#include "ImNodeFlow.h"
namespace ImFlow
{
inline void smart_bezier(const ImVec2& p1, const ImVec2& p2, ImU32 color, float thickness)
{
ImDrawList* dl = ImGui::GetWindowDrawList();
float distance = sqrt(pow((p2.x - p1.x), 2.f) + pow((p2.y - p1.y), 2.f));
float delta = distance * 0.45f;
if (p2.x < p1.x) delta += 0.2f * (p1.x - p2.x);
// float vert = (p2.x < p1.x - 20.f) ? 0.062f * distance * (p2.y - p1.y) * 0.005f : 0.f;
float vert = 0.f;
ImVec2 p22 = p2 - ImVec2(delta, vert);
if (p2.x < p1.x - 50.f) delta *= -1.f;
ImVec2 p11 = p1 + ImVec2(delta, vert);
dl->AddBezierCubic(p1, p11, p22, p2, color, thickness);
}
inline bool smart_bezier_collider(const ImVec2& p, const ImVec2& p1, const ImVec2& p2, float radius)
{
float distance = sqrt(pow((p2.x - p1.x), 2.f) + pow((p2.y - p1.y), 2.f));
float delta = distance * 0.45f;
if (p2.x < p1.x) delta += 0.2f * (p1.x - p2.x);
// float vert = (p2.x < p1.x - 20.f) ? 0.062f * distance * (p2.y - p1.y) * 0.005f : 0.f;
float vert = 0.f;
ImVec2 p22 = p2 - ImVec2(delta, vert);
if (p2.x < p1.x - 50.f) delta *= -1.f;
ImVec2 p11 = p1 + ImVec2(delta, vert);
return ImProjectOnCubicBezier(p, p1, p11, p22, p2).Distance < radius;
}
// -----------------------------------------------------------------------------------------------------------------
// HANDLER
template<typename T, typename... Params>
std::shared_ptr<T> ImNodeFlow::addNode(const ImVec2& pos, Params&&... args)
{
static_assert(std::is_base_of<BaseNode, T>::value, "Pushed type is not a subclass of BaseNode!");
std::shared_ptr<T> n = std::make_shared<T>(std::forward<Params>(args)...);
n->setPos(pos);
n->setHandler(this);
if (!n->getStyle())
n->setStyle(NodeStyle::cyan());
auto uid = reinterpret_cast<uintptr_t>(n.get());
n->setUID(uid);
m_nodes[uid] = n;
return n;
}
template<typename T, typename... Params>
std::shared_ptr<T> ImNodeFlow::placeNodeAt(const ImVec2& pos, Params&&... args)
{
return addNode<T>(screen2grid(pos), std::forward<Params>(args)...);
}
template<typename T, typename... Params>
std::shared_ptr<T> ImNodeFlow::placeNode(Params&&... args)
{
return placeNodeAt<T>(ImGui::GetMousePos(), std::forward<Params>(args)...);
}
// -----------------------------------------------------------------------------------------------------------------
// BASE NODE
template<typename T>
std::shared_ptr<InPin<T>> BaseNode::addIN(const std::string& name, T defReturn, ConnectionFilter filter, std::shared_ptr<PinStyle> style)
{
return addIN_uid(name, name, defReturn, filter, std::move(style));
}
template<typename T, typename U>
std::shared_ptr<InPin<T>> BaseNode::addIN_uid(const U& uid, const std::string& name, T defReturn, ConnectionFilter filter, std::shared_ptr<PinStyle> style)
{
PinUID h = std::hash<U>{}(uid);
auto p = std::make_shared<InPin<T>>(h, name, filter, this, defReturn, &m_inf, std::move(style));
m_ins.emplace_back(p);
return p;
}
template<typename U>
void BaseNode::dropIN(const U& uid)
{
PinUID h = std::hash<U>{}(uid);
for (auto it = m_ins.begin(); it != m_ins.end(); it++)
{
if (it->get()->getUid() == h)
{
m_ins.erase(it);
return;
}
}
}
inline void BaseNode::dropIN(const char* uid)
{
dropIN<std::string>(uid);
}
template<typename T>
const T& BaseNode::showIN(const std::string& name, T defReturn, ConnectionFilter filter, std::shared_ptr<PinStyle> style)
{
return showIN_uid(name, name, defReturn, filter, std::move(style));
}
template<typename T, typename U>
const T& BaseNode::showIN_uid(const U& uid, const std::string& name, T defReturn, ConnectionFilter filter, std::shared_ptr<PinStyle> style)
{
PinUID h = std::hash<U>{}(uid);
for (std::pair<int, std::shared_ptr<Pin>>& p : m_dynamicIns)
{
if (p.second->getUid() == h)
{
p.first = 1;
return static_cast<InPin<T>*>(p.second.get())->val();
}
}
m_dynamicIns.emplace_back(std::make_pair(1, std::make_shared<InPin<T>>(h, name, filter, this, defReturn, &m_inf, std::move(style))));
return static_cast<InPin<T>*>(m_dynamicIns.back().second.get())->val();
}
template<typename T>
std::shared_ptr<OutPin<T>> BaseNode::addOUT(const std::string& name, ConnectionFilter filter, std::shared_ptr<PinStyle> style)
{
return addOUT_uid<T>(name, name, filter, std::move(style));
}
template<typename T, typename U>
std::shared_ptr<OutPin<T>> BaseNode::addOUT_uid(const U& uid, const std::string& name, ConnectionFilter filter, std::shared_ptr<PinStyle> style)
{
PinUID h = std::hash<U>{}(uid);
auto p = std::make_shared<OutPin<T>>(h, name, filter, this, &m_inf, std::move(style));
m_outs.emplace_back(p);
return p;
}
template<typename U>
void BaseNode::dropOUT(const U& uid)
{
PinUID h = std::hash<U>{}(uid);
for (auto it = m_outs.begin(); it != m_outs.end(); it++)
{
if (it->get()->getUid() == h)
{
m_outs.erase(it);
return;
}
}
}
inline void BaseNode::dropOUT(const char* uid)
{
dropOUT<std::string>(uid);
}
template<typename T>
void BaseNode::showOUT(const std::string& name, std::function<T()> behaviour, ConnectionFilter filter, std::shared_ptr<PinStyle> style)
{
showOUT_uid<T>(name, name, std::move(behaviour), filter, std::move(style));
}
template<typename T, typename U>
void BaseNode::showOUT_uid(const U& uid, const std::string& name, std::function<T()> behaviour, ConnectionFilter filter, std::shared_ptr<PinStyle> style)
{
PinUID h = std::hash<U>{}(uid);
for (std::pair<int, std::shared_ptr<Pin>>& p : m_dynamicOuts)
{
if (p.second->getUid() == h)
{
p.first = 2;
return;
}
}
m_dynamicOuts.emplace_back(std::make_pair(2, std::make_shared<OutPin<T>>(h, name, filter, this, &m_inf, std::move(style))));
static_cast<OutPin<T>*>(m_dynamicOuts.back().second.get())->behaviour(std::move(behaviour));
}
template<typename T, typename U>
const T& BaseNode::getInVal(const U& uid)
{
PinUID h = std::hash<U>{}(uid);
auto it = std::find_if(m_ins.begin(), m_ins.end(), [&h](std::shared_ptr<Pin>& p)
{ return p->getUid() == h; });
assert(it != m_ins.end() && "Pin UID not found!");
return static_cast<InPin<T>*>(it->get())->val();
}
template<typename T>
const T& BaseNode::getInVal(const char* uid)
{
return getInVal<T, std::string>(uid);
}
template<typename U>
Pin* BaseNode::inPin(const U& uid)
{
PinUID h = std::hash<U>{}(uid);
auto it = std::find_if(m_ins.begin(), m_ins.end(), [&h](std::shared_ptr<Pin>& p)
{ return p->getUid() == h; });
assert(it != m_ins.end() && "Pin UID not found!");
return it->get();
}
inline Pin* BaseNode::inPin(const char* uid)
{
return inPin<std::string>(uid);
}
template<typename U>
Pin* BaseNode::outPin(const U& uid)
{
PinUID h = std::hash<U>{}(uid);
auto it = std::find_if(m_outs.begin(), m_outs.end(), [&h](std::shared_ptr<Pin>& p)
{ return p->getUid() == h; });
assert(it != m_outs.end() && "Pin UID not found!");
return it->get();
}
inline Pin* BaseNode::outPin(const char* uid)
{
return outPin<std::string>(uid);
}
// -----------------------------------------------------------------------------------------------------------------
// PIN
inline void Pin::drawSocket()
{
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 tl = pinPoint() - ImVec2(m_style->socket_radius, m_style->socket_radius);
ImVec2 br = pinPoint() + ImVec2(m_style->socket_radius, m_style->socket_radius);
if (isConnected())
draw_list->AddCircleFilled(pinPoint(), m_style->socket_connected_radius, m_style->color, m_style->socket_shape);
else
{
if (ImGui::IsItemHovered() || ImGui::IsMouseHoveringRect(tl, br))
draw_list->AddCircle(pinPoint(), m_style->socket_hovered_radius, m_style->color, m_style->socket_shape, m_style->socket_thickness);
else
draw_list->AddCircle(pinPoint(), m_style->socket_radius, m_style->color, m_style->socket_shape, m_style->socket_thickness);
}
if (ImGui::IsMouseHoveringRect(tl, br))
(*m_inf)->hovering(this);
}
inline void Pin::drawDecoration()
{
ImDrawList* draw_list = ImGui::GetWindowDrawList();
if (ImGui::IsItemHovered())
draw_list->AddRectFilled(m_pos - m_style->extra.padding, m_pos + m_size + m_style->extra.padding, m_style->extra.bg_hover_color, m_style->extra.bg_radius);
else
draw_list->AddRectFilled(m_pos - m_style->extra.padding, m_pos + m_size + m_style->extra.padding, m_style->extra.bg_color, m_style->extra.bg_radius);
draw_list->AddRect(m_pos - m_style->extra.padding, m_pos + m_size + m_style->extra.padding, m_style->extra.border_color, m_style->extra.bg_radius, 0, m_style->extra.border_thickness);
}
inline void Pin::update()
{
// Custom rendering
if (m_renderer)
{
ImGui::BeginGroup();
m_renderer(this);
ImGui::EndGroup();
m_size = ImGui::GetItemRectSize();
if (ImGui::IsItemHovered())
(*m_inf)->hovering(this);
return;
}
ImGui::SetCursorPos(m_pos);
ImGui::Text(m_name.c_str());
m_size = ImGui::GetItemRectSize();
drawDecoration();
drawSocket();
if (ImGui::IsItemHovered())
(*m_inf)->hovering(this);
}
// -----------------------------------------------------------------------------------------------------------------
// IN PIN
template<class T>
const T& InPin<T>::val()
{
if(!m_link)
return m_emptyVal;
return reinterpret_cast<OutPin<T>*>(m_link->left())->val();
}
template<class T>
void InPin<T>::createLink(Pin *other)
{
if (other == this || other->getType() == PinType_Input || (m_parent == other->getParent() && (m_filter & ConnectionFilter_SameNode) == 0))
return;
if (!((m_filter & other->getFilter()) != 0 || m_filter == ConnectionFilter_None || other->getFilter() == ConnectionFilter_None)) // Check Filter
return;
if (m_link && m_link->left() == other)
{
m_link.reset();
return;
}
m_link = std::make_shared<Link>(other, this, (*m_inf));
other->setLink(m_link);
(*m_inf)->addLink(m_link);
}
// -----------------------------------------------------------------------------------------------------------------
// OUT PIN
template<class T>
const T &OutPin<T>::val() { return m_val; }
template<class T>
void OutPin<T>::createLink(ImFlow::Pin *other)
{
if (other == this || other->getType() == PinType_Output)
return;
other->createLink(this);
}
template<class T>
void OutPin<T>::setLink(std::shared_ptr<Link>& link)
{
m_links.emplace_back(link);
}
template<class T>
void OutPin<T>::deleteLink()
{
m_links.erase(std::remove_if(m_links.begin(), m_links.end(),
[](const std::weak_ptr<Link>& l) { return l.expired(); }), m_links.end());
}
}