一个小型、无依赖的节点编辑器扩展,适用于亲爱的 imgui。
Imnodes 旨在提供一个简单的即时模式界面,用于在 ImGui 窗口中创建节点编辑器。 Imnodes 提供了用户构建节点编辑器所需的简单、可定制的构建块。
特征:
imnodes.h
、 imnodes_internal.h
和imnodes.cpp
与 ImGui 一起复制粘贴到您的项目中。 该存储库包含一些示例文件,位于example/
下。它们旨在作为简单的示例,让您了解可以使用 imnodes 构建什么。
如果您需要构建示例,可以使用提供的 CMake 脚本来执行此操作。
# Initialize the vcpkg submodule
$ git submodule update --init
# Run the generation step and build
$ cmake -B build-release/ -S . -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake
$ cmake --build build-release -- -j
请注意,这尚未在 Linux 上进行测试,并且在该平台上可能会失败。
以下是如何使用该扩展的简要概述。有关示例用法的更多信息,请滚动到自述文件的底部。
在进行任何操作之前,必须对库进行初始化。这可以与dear imgui
初始化同时完成。
ImGui::CreateContext ();
ImNodes::CreateContext ();
// elsewhere in the code...
ImNodes::DestroyContext ();
ImGui::DestroyContext ();
节点编辑器是包含节点的工作区。节点编辑器必须在窗口中实例化,就像任何其他 UI 元素一样。
ImGui::Begin ( " node editor " );
ImNodes::BeginNodeEditor ();
ImNodes::EndNodeEditor ();
ImGui::End ();
现在您应该有一个工作区,并且在窗口中可以看到网格。现在可以实例化一个空节点:
const int hardcoded_node_id = 1 ;
ImNodes::BeginNodeEditor ();
ImNodes::BeginNode (hardcoded_node_id);
ImGui::Dummy (ImVec2( 80 . 0f , 45 . 0f ));
ImNodes::EndNode ();
ImNodes::EndNodeEditor ();
节点,就像dear imgui
中的窗口一样,必须被唯一标识。但是我们不能使用节点标题来进行标识,因为工作区中应该可能存在许多同名的节点。相反,您只需使用整数进行标识。
属性是节点的 UI 内容。属性在节点的两侧都有一个引脚(小圆圈)。属性有两种类型:输入属性和输出属性。输入属性引脚位于节点的左侧,输出属性引脚位于节点的右侧。与节点一样,引脚必须被唯一标识。
ImNodes::BeginNode (hardcoded_node_id);
const int output_attr_id = 2 ;
ImNodes::BeginOutputAttribute (output_attr_id);
// in between Begin|EndAttribute calls, you can call ImGui
// UI functions
ImGui::Text ( " output pin " );
ImNodes::EndOutputAttribute ();
ImNodes::EndNode ();
扩展并不真正关心属性中的内容。它只是呈现属性的引脚,并允许用户在引脚之间创建链接。
可以使用BeginNodeTitleBar
和EndNodeTitleBar
将标题栏添加到节点。与属性一样,您可以将标题栏的内容放置在函数调用之间。请注意,必须在向节点添加属性或其他dear imgui
UI 元素之前调用这些函数,因为节点的布局是按顺序从上到下构建的。
ImNodes::BeginNode (hardcoded_node_id);
ImNodes::BeginNodeTitleBar ();
ImGui::TextUnformatted ( " output node " );
ImNodes::EndNodeTitleBar ();
// pins and other node UI content omitted...
ImNodes::EndNode ();
用户还必须呈现自己的节点之间的链接。链接是连接两个属性的曲线。链接只是一对属性 ID。与节点和属性一样,链接也必须由唯一的整数值来标识:
std::vector<std::pair< int , int >> links;
// elsewhere in the code...
for ( int i = 0 ; i < links.size(); ++i)
{
const std::pair< int , int > p = links[i];
// in this case, we just use the array index of the link
// as the unique identifier
ImNodes::Link (i, p. first , p. second );
}
调用EndNodeEditor
后,您可以使用函数调用IsLinkCreated
检查帧期间是否创建了链接:
int start_attr, end_attr;
if (ImNodes::IsLinkCreated(&start_attr, &end_attr))
{
links. push_back ( std::make_pair (start_attr, end_attr));
}
除了检查新链接之外,您还可以检查鼠标光标是否悬停在 UI 元素上:
int node_id;
if (ImNodes::IsNodeHovered(&node_id))
{
node_hovered = node_id;
}
您还可以检查是否已选择任何节点。可以单击节点,也可以通过单击节点并将框选择器拖动到节点上来选择节点。
// Note that since many nodes can be selected at once, we first need to query the number of
// selected nodes before getting them.
const int num_selected_nodes = ImNodes::NumSelectedNodes();
if (num_selected_nodes > 0 )
{
std::vector< int > selected_nodes;
selected_nodes. resize (num_selected_nodes);
ImNodes::GetSelectedNodes (selected_nodes. data ());
}
有关更多 UI 事件相关函数,请参阅imnodes.h
。
就像dear imgui
一样,UI 的风格是可以改变的。您可以通过调用ImNodes::PushColorStyle
和ImNodes::PopColorStyle
设置单个节点、引脚和链接中帧的颜色样式。
// set the titlebar color of an individual node
ImNodes::PushColorStyle (
ImNodesCol_TitleBar, IM_COL32( 11 , 109 , 191 , 255 ));
ImNodes::PushColorStyle (
ImNodesCol_TitleBarSelected, IM_COL32( 81 , 148 , 204 , 255 ));
ImNodes::BeginNode (hardcoded_node_id);
// node internals here...
ImNodes::EndNode ();
ImNodes::PopColorStyle ();
ImNodes::PopColorStyle ();
如果未在帧中设置样式,则可以调用ImNodes::GetStyle
,并将值直接设置到样式数组中。
// set the titlebar color for all nodes
ImNodesStyle& style = ImNodes::GetStyle();
style.colors[ImNodesCol_TitleBar] = IM_COL32( 232 , 27 , 86 , 255 );
style.colors[ImNodesCol_TitleBarSelected] = IM_COL32( 241 , 108 , 146 , 255 );
为了更快地导航大型图形,您可以使用交互式迷你地图叠加层。小地图可以缩放和滚动。编辑器节点将相应地跟踪小地图的平移。
ImGui::Begin ( " node editor " );
ImNodes::BeginNodeEditor ();
// add nodes...
// must be called right before EndNodeEditor
ImNodes::MiniMap ();
ImNodes::EndNodeEditor ();
ImGui::End ();
编辑器空间中小地图的相对大小和角位置可以这样指定:
// MiniMap is a square region with a side length that is 20% the largest editor canvas dimension
// See ImNodesMiniMapLocation_ for other corner locations
ImNodes::MiniMap ( 0 . 2f , ImNodesMiniMapLocation_TopRight);
小地图还支持通过用户定义的回调进行有限的节点悬停定制。
// User callback
void mini_map_node_hovering_callback ( int node_id, void * user_data)
{
ImGui::SetTooltip ( " This is node %d " , node_id);
}
// Later on...
ImNodes::MiniMap ( 0 . 2f , ImNodesMiniMapLocation_TopRight, mini_map_node_hovering_callback, custom_user_data);
// 'custom_user_data' can be used to supply extra information needed for drawing within the callback
可以通过提供imnodes_config.h
标头并在编译时指定定义IMNODES_USER_CONFIG=imnodes_config.h
来自定义 ImNodes。
目前可以覆盖小地图悬停回调函数的类型。这在为另一种语言生成绑定时很有用。
下面是一个示例 imnodes_config.h,它为回调生成 pybind 包装器。
# pragma once
# include < pybind11/functional.h >
namespace pybind11 {
inline bool PyWrapper_Check (PyObject *o) { return true ; }
class wrapper : public object {
public:
PYBIND11_OBJECT_DEFAULT (wrapper, object, PyWrapper_Check)
wrapper ( void * x) { m_ptr = (PyObject*)x; }
explicit operator bool () const { return m_ptr != nullptr && m_ptr != Py_None; }
};
} // namespace pybind11
namespace py = pybind11;
# define ImNodesMiniMapNodeHoveringCallback py::wrapper
# define ImNodesMiniMapNodeHoveringCallbackUserData py::wrapper
ImGui::Separator()
跨越当前窗口范围。因此,在节点内使用分隔符将导致分隔符从节点溢出到节点编辑器网格中。 请参阅examples/
目录以更详细地了解库的使用情况。