#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 std::shared_ptr ImNodeFlow::addNode(const ImVec2& pos, Params&&... args) { static_assert(std::is_base_of::value, "Pushed type is not a subclass of BaseNode!"); std::shared_ptr n = std::make_shared(std::forward(args)...); n->setPos(pos); n->setHandler(this); if (!n->getStyle()) n->setStyle(NodeStyle::cyan()); auto uid = reinterpret_cast(n.get()); n->setUID(uid); m_nodes[uid] = n; return n; } template std::shared_ptr ImNodeFlow::placeNodeAt(const ImVec2& pos, Params&&... args) { return addNode(screen2grid(pos), std::forward(args)...); } template std::shared_ptr ImNodeFlow::placeNode(Params&&... args) { return placeNodeAt(ImGui::GetMousePos(), std::forward(args)...); } // ----------------------------------------------------------------------------------------------------------------- // BASE NODE template std::shared_ptr> BaseNode::addIN(const std::string& name, T defReturn, ConnectionFilter filter, std::shared_ptr style) { return addIN_uid(name, name, defReturn, filter, std::move(style)); } template std::shared_ptr> BaseNode::addIN_uid(const U& uid, const std::string& name, T defReturn, ConnectionFilter filter, std::shared_ptr style) { PinUID h = std::hash{}(uid); auto p = std::make_shared>(h, name, filter, this, defReturn, &m_inf, std::move(style)); m_ins.emplace_back(p); return p; } template void BaseNode::dropIN(const U& uid) { PinUID h = std::hash{}(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(uid); } template const T& BaseNode::showIN(const std::string& name, T defReturn, ConnectionFilter filter, std::shared_ptr style) { return showIN_uid(name, name, defReturn, filter, std::move(style)); } template const T& BaseNode::showIN_uid(const U& uid, const std::string& name, T defReturn, ConnectionFilter filter, std::shared_ptr style) { PinUID h = std::hash{}(uid); for (std::pair>& p : m_dynamicIns) { if (p.second->getUid() == h) { p.first = 1; return static_cast*>(p.second.get())->val(); } } m_dynamicIns.emplace_back(std::make_pair(1, std::make_shared>(h, name, filter, this, defReturn, &m_inf, std::move(style)))); return static_cast*>(m_dynamicIns.back().second.get())->val(); } template std::shared_ptr> BaseNode::addOUT(const std::string& name, ConnectionFilter filter, std::shared_ptr style) { return addOUT_uid(name, name, filter, std::move(style)); } template std::shared_ptr> BaseNode::addOUT_uid(const U& uid, const std::string& name, ConnectionFilter filter, std::shared_ptr style) { PinUID h = std::hash{}(uid); auto p = std::make_shared>(h, name, filter, this, &m_inf, std::move(style)); m_outs.emplace_back(p); return p; } template void BaseNode::dropOUT(const U& uid) { PinUID h = std::hash{}(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(uid); } template void BaseNode::showOUT(const std::string& name, std::function behaviour, ConnectionFilter filter, std::shared_ptr style) { showOUT_uid(name, name, std::move(behaviour), filter, std::move(style)); } template void BaseNode::showOUT_uid(const U& uid, const std::string& name, std::function behaviour, ConnectionFilter filter, std::shared_ptr style) { PinUID h = std::hash{}(uid); for (std::pair>& p : m_dynamicOuts) { if (p.second->getUid() == h) { p.first = 2; return; } } m_dynamicOuts.emplace_back(std::make_pair(2, std::make_shared>(h, name, filter, this, &m_inf, std::move(style)))); static_cast*>(m_dynamicOuts.back().second.get())->behaviour(std::move(behaviour)); } template const T& BaseNode::getInVal(const U& uid) { PinUID h = std::hash{}(uid); auto it = std::find_if(m_ins.begin(), m_ins.end(), [&h](std::shared_ptr& p) { return p->getUid() == h; }); assert(it != m_ins.end() && "Pin UID not found!"); return static_cast*>(it->get())->val(); } template const T& BaseNode::getInVal(const char* uid) { return getInVal(uid); } template Pin* BaseNode::inPin(const U& uid) { PinUID h = std::hash{}(uid); auto it = std::find_if(m_ins.begin(), m_ins.end(), [&h](std::shared_ptr& 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(uid); } template Pin* BaseNode::outPin(const U& uid) { PinUID h = std::hash{}(uid); auto it = std::find_if(m_outs.begin(), m_outs.end(), [&h](std::shared_ptr& 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(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 const T& InPin::val() { if(!m_link) return m_emptyVal; return reinterpret_cast*>(m_link->left())->val(); } template void InPin::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(other, this, (*m_inf)); other->setLink(m_link); (*m_inf)->addLink(m_link); } // ----------------------------------------------------------------------------------------------------------------- // OUT PIN template const T &OutPin::val() { return m_val; } template void OutPin::createLink(ImFlow::Pin *other) { if (other == this || other->getType() == PinType_Output) return; other->createLink(this); } template void OutPin::setLink(std::shared_ptr& link) { m_links.emplace_back(link); } template void OutPin::deleteLink() { m_links.erase(std::remove_if(m_links.begin(), m_links.end(), [](const std::weak_ptr& l) { return l.expired(); }), m_links.end()); } }