Una pequeña extensión de editor de nodos sin dependencias para el querido imgui.
Imnodes tiene como objetivo proporcionar una interfaz sencilla y en modo inmediato para crear un editor de nodos dentro de una ventana ImGui. Imnodes proporciona bloques de construcción simples y personalizables que un usuario necesita para construir su editor de nodos.
Características:
imnodes.h
, imnodes_internal.h
e imnodes.cpp
en su proyecto junto con ImGui. Este repositorio incluye algunos archivos de ejemplo, en example/
. Están pensados como ejemplos simples que le dan una idea de lo que puede construir con imnodes.
Si necesita crear los ejemplos, puede utilizar el script CMake proporcionado para hacerlo.
# 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
Tenga en cuenta que esto no se ha probado en Linux y es probable que falle en la plataforma.
A continuación se ofrece una pequeña descripción general de cómo se utiliza la extensión. Para obtener más información sobre el uso de ejemplo, desplácese hasta la parte inferior del archivo README.
Antes de poder hacer algo, se debe inicializar la biblioteca. Esto se puede hacer al mismo tiempo que la inicialización dear imgui
.
ImGui::CreateContext ();
ImNodes::CreateContext ();
// elsewhere in the code...
ImNodes::DestroyContext ();
ImGui::DestroyContext ();
El editor de nodos es un espacio de trabajo que contiene nodos. Se debe crear una instancia del editor de nodos dentro de una ventana, como cualquier otro elemento de la interfaz de usuario.
ImGui::Begin ( " node editor " );
ImNodes::BeginNodeEditor ();
ImNodes::EndNodeEditor ();
ImGui::End ();
Ahora deberías tener un espacio de trabajo con una cuadrícula visible en la ventana. Ahora se puede crear una instancia de un nodo vacío:
const int hardcoded_node_id = 1 ;
ImNodes::BeginNodeEditor ();
ImNodes::BeginNode (hardcoded_node_id);
ImGui::Dummy (ImVec2( 80 . 0f , 45 . 0f ));
ImNodes::EndNode ();
ImNodes::EndNodeEditor ();
Los nodos, como las ventanas en dear imgui
deben identificarse de forma única. Pero no podemos usar los títulos de los nodos para la identificación, porque debería ser posible tener muchos nodos con el mismo nombre en el espacio de trabajo. En su lugar, simplemente utiliza números enteros para la identificación.
Los atributos son el contenido de la interfaz de usuario del nodo. Un atributo tendrá un alfiler (el pequeño círculo) a cada lado del nodo. Hay dos tipos de atributos: atributos de entrada y de salida. Los pines de atributos de entrada están en el lado izquierdo del nodo y los pines de atributos de salida están en el lado derecho. Al igual que los nodos, los pines deben identificarse de forma única.
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 ();
A la extensión realmente no le importa lo que haya en el atributo. Simplemente representa el pin del atributo y permite al usuario crear enlaces entre pines.
Se puede agregar una barra de título al nodo usando BeginNodeTitleBar
y EndNodeTitleBar
. Al igual que los atributos, colocas el contenido de tu barra de título entre las llamadas a funciones. Tenga en cuenta que estas funciones deben llamarse antes de agregar atributos u otros elementos de la interfaz de usuario dear imgui
al nodo, ya que el diseño del nodo está construido en orden, de arriba a abajo.
ImNodes::BeginNode (hardcoded_node_id);
ImNodes::BeginNodeTitleBar ();
ImGui::TextUnformatted ( " output node " );
ImNodes::EndNodeTitleBar ();
// pins and other node UI content omitted...
ImNodes::EndNode ();
El usuario también debe representar sus propios enlaces entre nodos. Un vínculo es una curva que conecta dos atributos. Un enlace es sólo un par de identificadores de atributos. Y al igual que los nodos y los atributos, los enlaces también deben identificarse mediante valores enteros únicos:
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 );
}
Después de llamar EndNodeEditor
, puede verificar si se creó un enlace durante el marco con la llamada de función IsLinkCreated
:
int start_attr, end_attr;
if (ImNodes::IsLinkCreated(&start_attr, &end_attr))
{
links. push_back ( std::make_pair (start_attr, end_attr));
}
Además de comprobar si hay nuevos enlaces, también puede comprobar si el cursor del ratón pasa sobre los elementos de la interfaz de usuario:
int node_id;
if (ImNodes::IsNodeHovered(&node_id))
{
node_hovered = node_id;
}
También puede verificar si se ha seleccionado algún nodo. Se puede hacer clic en los nodos o se pueden seleccionar haciendo clic y arrastrando el selector de cuadro sobre ellos.
// 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 ());
}
Consulte imnodes.h
para obtener más funciones relacionadas con eventos de la interfaz de usuario.
Como dear imgui
, el estilo de la interfaz de usuario se puede cambiar. Puede establecer el estilo de color de nodos, pines y enlaces individuales en la mitad del fotograma llamando ImNodes::PushColorStyle
e 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 ();
Si el estilo no se establece a mitad del fotograma, se puede llamar ImNodes::GetStyle
y los valores se pueden establecer directamente en la matriz de estilos.
// 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 );
Para manejar una navegación más rápida de gráficos grandes, puede utilizar una superposición de minimapa interactivo. El minimapa se puede ampliar y desplazar. Los nodos del editor seguirán la panorámica del minimapa en consecuencia.
ImGui::Begin ( " node editor " );
ImNodes::BeginNodeEditor ();
// add nodes...
// must be called right before EndNodeEditor
ImNodes::MiniMap ();
ImNodes::EndNodeEditor ();
ImGui::End ();
El tamaño relativo y la ubicación de las esquinas del minimapa en el espacio del editor se pueden especificar así:
// 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);
El minimapa también admite una personalización limitada del desplazamiento del nodo a través de una devolución de llamada definida por el usuario.
// 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 se puede personalizar proporcionando un encabezado imnodes_config.h
y especificando la definición de IMNODES_USER_CONFIG=imnodes_config.h
al compilar.
Actualmente es posible anular el tipo de función de devolución de llamada al pasar el cursor sobre el minimapa. Esto es útil al generar enlaces para otro idioma.
Aquí hay un ejemplo de imnodes_config.h, que genera un contenedor pybind para la devolución de llamada.
# 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()
abarca el intervalo de la ventana actual. Como resultado, el uso de un separador dentro de un nodo hará que el separador se extienda fuera del nodo hacia la cuadrícula del editor de nodos. Consulte el directorio examples/
para ver el uso de la biblioteca con mayor detalle.