Ubicación automática de nodos usando niveles

Figura
Figura. Grafo árbol con 4 niveles

Inicié la aplicación Grafos SVG para construir grafos con la publicación del tema estructura de partición que se usa para resolver planificaciones, o bien en el algoritmo de Kruskal para resolver el problema del árbol de recubrimiento mínimo de un grafo. Para la estructura de partición necesitaba representar gráficamente un árbol, que es un grafo conexo no dirigido sin ciclos o, alternativamente, un grafo no dirigido donde cada par de nodos están conectados por un único camino. Si el grafo es dirigido y se anulan las direcciones en la aristas convirtiéndose en un árbol según las definiciones anteriores, entonces se le denomina árbol dirigido.

En un árbol podemos representar los nodos visualmente en niveles, como el de la Figura que tiene 4 niveles, numerados desde el 0 hasta el 3. Cada nodo tiene una arista apuntando a un único padre y una o más aristas a los hijos. El nivel 0 lo ocupa el nodo raíz y los nodos en los sucesivos niveles apuntan a nodos del nivel inmediato superior. Esta forma de ubicar automáticamente los nodos en niveles la usamos para presentar visualmente cualquier grafo, aunque no sea un árbol. El JSON de ese grafo es el siguiente:

{"nodes":[
    {"index":0,"value":"A","parent":[{"index":-1}]},
    {"index":1,"value":"B","parent":[{"index":0}]},
    {"index":2,"value":"C","parent":[{"index":0}]},
    {"index":3,"value":"D","parent":[{"index":0}]},
    {"index":4,"value":"E","parent":[{"index":1}]},
    {"index":5,"value":"F","parent":[{"index":1}]},
    {"index":6,"value":"G","parent":[{"index":3}]},
    {"index":7,"value":"H","parent":[{"index":3}]},
    {"index":8,"value":"I","parent":[{"index":5}]},
    {"index":9,"value":"J","parent":[{"index":5}]},
    {"index":10,"value":"K","parent":[{"index":6}]}
],
"config":{}}

Los array parent sólo tienen un elemento, el padre al que apuntan. Esa disposición es óptima para que la aplicación ubique automáticamente los nodos en niveles. Cada nivel ocupa una altura de línea, de la que ya hemos hablado anteriormente. Con un SVG de 250 píxeles de alto inicial (height), la altura de línea es 1/10 siendo de 25 unidades de ViewBox, tal como explicamos en el tema anterior, en el apartado cambiar tamaño del SVG. Si exportamos el código SVG vemos que las líneas están ubicadas en el eje Y en 12.5, 37.5, 62.5, 87.5, separadas 25 unidades de ViewBox, como se observa en el atributo cy de los elementos CIRCLE de los nodos:

<svg viewBox="0 0 100 100" width="250" height="250" ...>
    ...
    <circle cx="50" cy="12.5" r="6.25" ...>
        <circle cx="25" cy="37.5" r="6.25" ...>
        <circle cx="50" cy="37.5" r="6.25" ...>
        <circle cx="75" cy="37.5" r="6.25" ...>
            <circle cx="20" cy="62.5" r="6.25" ...>
            <circle cx="40" cy="62.5" r="6.25" ...>
            <circle cx="60" cy="62.5" r="6.25" ...>
            <circle cx="80" cy="62.5" r="6.25" ...>
                <circle cx="25" cy="87.5" r="6.25" ...>
                <circle cx="50" cy="87.5" r="6.25" ...>
                <circle cx="75" cy="87.5" r="6.25" ...>
    ...
</svg>
Figura
Figura. Niveles para la posición inicial de nodos en un grafo no árbol

Esta disposición en niveles automática es óptima para árboles, aunque para el resto de grafos quizás no lo sea tanto y será necesario reubicar los nodos. Para ello disponemos de varios recursos que veremos en siguientes apartados y que modifican la ubicación automática inicial por niveles.

Veamos ahora como se gestionan los niveles. Supongamos el grafo de la Figura que generamos en la aplicación con la siguiente matriz de adyacencia en formato SSV:

0 0 0 0 0 0
1 0 0 0 0 0
0 1 0 0 0 0
0 0 1 0 0 0
1 1 0 1 0 0
0 0 0 1 0 0

La ubicación automática los ubica en 5 niveles, con este JSON:

{"nodes":[
    {"index":0,"parent":[{"index":-1}]},
    {"index":1,"parent":[{"index":0}]},
    {"index":2,"parent":[{"index":1}]},
    {"index":3,"parent":[{"index":2}]},
    {"index":4,"parent":[{"index":0},{"index":1},{"index":3}]},
    {"index":5,"parent":[{"index":3}]}
],
"config":{
    "markerEnd":"arrow"
}}

En un momento dado y de forma temporal la aplicación asigna la propiedad level a cada nodo, para poder crear el SVG a partir de esos datos, aunque esa propiedad luego se elimina pues no forma parte del JSON a devolver. Vea los 5 niveles adjudicados desde el 0 al 4:

[{"index":0,"parent":[{"index":-1}],"level":0},
{"index":1,"parent":[{"index":0}],"level":1},
{"index":2,"parent":[{"index":1}],"level":2},
{"index":3,"parent":[{"index":2}],"level":3},
{"index":4,"parent":[{"index":0},{"index":1},{"index":3}],"level":1},
{"index":5,"parent":[{"index":3}],"level":4}]
Figura
Figura. Cambiar nivel nodo 4

Los niveles se descubren a partir del primer padre en el array parent. Así para el nodo 4 con parent":[{"index":0},{"index":1},{"index":3}] que apunta a 3 padres y a ningún hijo (es decir, ningún nodo lo apunta), es el primer padre {"index":0} el que determina que se posicione en el nivel 1 ("level":1).

Por ejemplo, si cambiamos el orden con parent":[{"index":1)},{"index":0},{"index":3}] vemos en la Figura (mismo grafo pero reducido en tamaño) que el nodo 4 se ubica en el tercer nivel, pues su primer padre es {"index":1} que está en el segundo nivel.

Podemos reubicar los nodos modificado la ubicación inicial por niveles usando el panel de ajustes visuales que vimos en el tema anterior, ajustes que afectan a la visualización del grafo pero no a su estructura, como por ejemplo mover elementos o posicionar nodos que ya explicamos en ese tema anterior. En los siguientes apartados veremos otras formas para reubicar los nodos en el grafo, usando el mismo grafo de muestra, ajustes que solo afectan a la visualización, no afectando a la matriz de adyacencia:

Posición nodos en el JSON y obtener padres de un nodo

Figura
Figura. Posición nodos 0,1,2,3,4 en el JSON

Vamos a ver en este apartado como se estructuran los nodos en el JSON. El grafo de la Figura tiene el siguiente JSON:

{"nodes":[
    {"index":0,"parent":[{"index":-1},{"index":4}]},
    {"index":1,"parent":[{"index":0}]},
    {"index":2,"parent":[{"index":0},{"index":1}]},
    {"index":3,"parent":[{"index":1},{"index":4}]},
    {"index":4,"parent":[{"index":1},{"index":2}]}
],
"config":{
    "markerEnd":"arrow"
}}
Figura
Figura. Posición nodos 0,1,2,4,3 en el JSON

La disposición de los nodos en el array nodes no tiene porque coincidir con "index", solo se exige que empiecen en 0 y sean correlativos. Por ejemplo, cambiamos la posición entre los nodos "3" y "4" en el JSON, generando el grafo de la Figura, que es igual que el anterior, solo que se intercambia la posición gráfica de los nodos "3" y "4", pues visualmente se van insertando en el SVG en el orden del array nodes. En el JSON siguiente observamos que el "4" aparece antes del "3" en el array nodes:

{"nodes":[
    {"index":0,"parent":[{"index":-1},{"index":4}]},
    {"index":1,"parent":[{"index":0}]},
    {"index":2,"parent":[{"index":0},{"index":1}]},
    {"index":4,"parent":[{"index":1},{"index":2}]},
    {"index":3,"parent":[{"index":1},{"index":4}]}
],
"config":{
    "markerEnd":"arrow"
}}

Si queremos obtener los padres de un nodo "n" tenemos que buscar una posición en el array nodos con "index":n y recuperar su propiedad parent que nos devuelve un array con los padres del nodo "n". Por ejemplo, obtener los padres del nodo "4" a partir del JSON necesita las funciones getNode() y getParentsNode():

function getNode(nodes=[], index=-1){
    let node = null;
    for (let i=0; i<nodes.length; i++){
        if (nodes[i].index === index){
            node = nodes[i];
            break;
        }
    }
    return node;
}

function getParentsNode(nodes=[], index=-1) {
    let parents = null;
    let node = getNode(nodes, index);
    if (node!==null) {
        parents = node.parent;
    }
    return parents;
}

La función getParentsNode() no es ni siquiera necesaria, pues una vez obtenido el nodo con, por ejemplo, let node = getNode(nodes, 4) devolvemos los padres con let parents = node.parent, devolviendo el array [{"index":1},{"index":2}], aunque por seguridad previamente hemos de verificar que el nodo no es nulo.

Mientras que con la matriz de adyacencia de ese grafo:

[[0, 0, 0, 0, 1],
[1, 0, 0, 0, 0],
[1, 1, 0, 0, 0],
[0, 1, 0, 0, 1],
[0, 1, 1, 0, 0]]

Obtener padres de un nodo con getParents()

Figura
Figura. Obtener padres de un nodo

Accediendo a la sección de algoritmos en el panel Acciones de la izquierda, disponemos de un algoritmo getParents() para obtener los padres de un nodo desde la matriz de adyacencia, como se observa en la Figura, donde recuperamos los padres del nodo con índice 4.

Usando el ejemplo que mostramos en el primer apartado y ejecutando getParents({matrix, index:4}) nos devuelve un array [1, 2] con los índices de los nodos en la matriz de adyacencia que son padres del nodo 4. Vea que los padres son aquellos adonde apuntan las flechas que salen del nodo 4.

La aplicación complementa el resultado con comentarios, incluyendo el tiempo de ejecución que en este caso resulta insignificante:

Time: 0 ms

Obtener padres: [1,2]

Valores del array son índices de nodos

Valores de nodos padres del nodo "4" (index 4) ⇒ 
1, 2

Se devuelve también el código del algoritmo, que en todos los casos recibe como argumento una matriz de adyacencia.

function getParents({matrix=[], index=0, forceUndirected=false}){
    let result = [];
    try {
        let n = matrix.length;
        for (let j=0; j<n; j++) {
            if (matrix[index][j]!==0 || forceUndirected && 
                matrix[j][index]!==0) {
                result.push(j);
            }
        }
    } catch(e) {result = e.message}
    return result;
}

Observe que las funciones que se ejecutan con el JSON tiene un primer argumento nodes que es el array en ese formato. Mientras que las que se ejecutan con la matriz de adyacencia tienen el argumento matrix.

Obtener hijos de un nodo con getChildren()

Accediendo a algoritmos en la sección Acciones podemos encontrar getChildren() para obtener los hijos de un nodo.

Usando el ejemplo que mostramos en el primer apartado y ejecutando getChildren({matrix, index:4}) nos devuelve el array [0, 3] con los índices de los nodos en la matriz de adyacencia que son hijos del nodo 4. Los hijos son aquellos nodos con flechas que apuntan al nodo padre.

Time: 0 ms

Obtener hijos: [0,3]

Valores del array son índices de nodos

Valores de nodos hijos del nodo "4" (index 4) ⇒ 
0, 3

En el código del algoritmo vemos el uso de getParents() y getChildren() que vimos en los apartados anteriores:ç

function getChildren({matrix=[], index=0, forceUndirected=false}){
    let result = [];
    try {
        let n = matrix.length;
        for (let i=0; i<n; i++) {
            if (matrix[i][index]!==0 || forceUndirected && matrix[index][i]!==0) {
                result.push(i);
            }
        }
    } catch(e){result = e.message}
    return result;
}

Al igual que para getParents(), el argumento forzar no dirigido (forceUndirected) trata el grafo como no dirigido, donde los padres o los hijos de un nodo son los vecinos, como veremos en el siguiente apartado. Realmente en un dirigido solo cabría hablar de vecinos, no de padres o hijos.

Obtener vecinos de un nodo con getNeighbors()

Accediendo a algoritmos en la sección Acciones podemos encontrar getChildren() para obtener los vecinos de un nodo, considerándose en este caso tanto los padres como los hijos, es decir, todas las relaciones de un nodo con sus vecinos.

Usando el ejemplo que mostramos en el primer apartado y ejecutando getNeighbors({matrix, index:4}) nos devuelve el array [1, 2, 0, 3] con los índices de los nodos en la matriz de adyacencia que son padres o hijos del nodo 4.

Time: 0 ms

Obtener vecinos: [1,2,0,3]

Valores del array son índices de nodos

Valores de nodos vecinos del nodo "4" (index 4) ⇒ 
1, 2, 0, 3
function getNeighbors({matrix=[], index=0}){
    let result = [];
    try {
        let parents = getParents({matrix, index});
        if (typeof parents==="string") throw new Error(parents);
        result.push(...parents);
        let children = getChildren({matrix, index});
        if (typeof children==="string") throw new Error(children);
        for (let i of children) {
            if (!result.includes(i)) {
                result.push(i);
            }
        }
    } catch(e) {result = e.message}
    return result;
}

Orden de los nodos (nodeOrder)

Figura
Figura. Grafo inicial

Para este apartado vamos a usar como muestra el grafo de la Figura que vimos en el apartado anterior, que puede importar desde la siguiente matriz de adyacencia en formato SSV:

0 0 0 0 0 0
1 0 0 0 0 0
0 1 0 0 0 0
0 0 1 0 0 0
1 1 0 1 0 0
0 0 0 1 0 0

Con esta nueva versión de la aplicación he agregado la propiedad nodeOrder para configurar en que orden del JSON de nodos aparecen bien los nodos o las aristas, que son los enlaces parent. Por lo tanto el orden de los nodos los reubica tras la disposición inicial por niveles.

Figura
Figura. Orden nodos descendente

En la Figura ponemos un orden descendente (desc) y obtenemos una disposición distinta de los nodos, con una inversión horizontal de los nodos "1" y "4" con respecto a lo que vemos en la Figura.

Los órdenes incluyen ninguno, que no tiene efecto, Asc/Desc para ordenar nodos por su índice, Par-impar/Impar-par para ordenar nodos por paridad de sus índices, Padre 0 asc/desc para ordenar nodos por el índice del primer padre, Padres asc/des para ordenar el array de padres por sus índices, Padres inverso para invertir el array de padres usando el método de JavaScript reverse(), Padre fin para quitar el primer índice del array de padres y ubicarlo al final.

Muchas de esas posibilidades no tienen efecto aparente en la visualización. Y además las he puesto en modo de período de pruebas, por lo que podría eliminar algunas o incorporar otras nuevas.

Aplicar un orden puede ser interesante para ver si conseguimos una mejor ubicación de los nodos sin necesidad de aplicar lo que veremos a continuación, pues no será necesaria incorporar más propiedades como presetLevel o nodeOffset al JSON. Pero no tiene sentido aplicar un orden a un grafo que ya hemos posicionado con esas propiedades, pues su disposición podría ser más desordenada.

Figura
Figura. Orden nodos padre al final

Usando repetidas veces la última opción Padre fin se obtienen varias visualizaciones, como las dos mostradas en la Figura y vuelta a la inicial de la Figura. En todas las visualización la matriz de adyacencia se mantiene igual:

0 0 0 0 0 0
1 0 0 0 0 0
0 1 0 0 0 0
0 0 1 0 0 0
1 1 0 1 0 0
0 0 0 1 0 0

Esta opción de padres al final ejecuta nodes.forEach(v => v.parent.push(v.parent.shift())) de tal forma que extrae el primer padre del array con shift() y lo ubica al final con push(). Por eso al repetir la acción se suceden las distintas disposiciones de los padres hasta que volvemos a la situación inicial.

Veamos otro ejemplo. En el apartado Matriz de adyacencia para definir el grafo que publiqué en 2020 incorporaba la matriz de adyacencia a la aplicación, poniendo allí un ejemplo de una matriz ponderada con el modo null/value:

[[null,2,1,null,14],
[null,null,3,20,13],
[null,null,null,null,8],
[null,null,null,null,null],
[null,null,null,2,null]]

Importando esta matriz obtendremos la primera visualización entre las siguientes, que también exponíamos en aquel tema. Aplicando de forma sucesiva padres fin veremos otras visualizaciones del mismo grafo, exponiéndose también en aquel tema la cuarta, pues suponía una mejor disposición de los nodos para conseguir mejorar la visualización.

Figura
Figura. Orden nodos padre al final

Propiedad nivel preestablecido (presetLevel)

Figura
Figura. Uso de nivel preestablecido (presetLevel)

Otra forma de posicionar los nodos modificando la ubicación automática por niveles es usando la nueva propiedad nivel preestablecido mediante la propiedad presetLevel. Como se observa en la Figura, donde vemos que adjudicamos nivel preestablecido igual a 2 (presetLevel = 2) para el nodo "3", evitando que se ubique automáticamente en el nivel 3 y forzándolo a ubicarse en el nivel 2 en la misma línea que el nodo anterior "2".

Este es el JSON que se devuelve:

{"nodes":[
    {"index":0,"parent":[{"index":-1}]},
    {"index":1,"parent":[{"index":0}]},
    {"index":2,"parent":[{"index":1}]},
    {"index":3,"parent":[{"index":2}],"presetLevel":2},
    {"index":4,"parent":[{"index":0},{"index":1},{"index":3}]},
    {"index":5,"parent":[{"index":3}]}
],
"config":{
    "markerEnd":"arrow"
}}

Observamos que el nivel 3 ahora está vacío pues el nodo "5" está en el nivel 4, que podíamos haberlo reposicionado dándole la propiedad "presetLevel":3.

Propiedad separación de los nodos (nodeOffset)

Figura
Figura. Grafo reposicionando el nodo "2"

Veamos otra forma de reubicar los nodos tras la posición automática por niveles. Como vemos en la Figura, podemos reubicar un nodo seleccionándolo y moviéndolo con el ratón, o bien en el panel lateral izquierdo modificando la propiedad separación nodo, o bien en el área de texto de la parte superior modificando esa propiedad nodeOffset.

El valor "-8 -2" es un tipo String con dos números separados por espacio, con el primer valor para la separación horizontal y el segundo para la vertical. Si sólo aparece un tipo número (number) se entiende que aplica a X mientras que para Y será 0. Vea que el nodo se desplaza -8 unidades ViewBox a la izquierda y -2 unidades ViewBox arriba, recordando que el eje vertical está invertido. Así quedará el JSON:

{"nodes":[
    {"index":0,"parent":[{"index":-1}]},
    {"index":1,"parent":[{"index":0}]},
    {"index":2,"parent":[{"index":1}],"nodeOffset":"-8 -2"},
    {"index":3,"parent":[{"index":2}]},
    {"index":4,"parent":[{"index":0},{"index":1},{"index":3}]},
    {"index":5,"parent":[{"index":3}]}
],
"config":{
    "markerEnd":"arrow"
}}
Figura
Figura. Borrando separaciones (offsets)

Podemos borrar todas las separaciones de nodos y aristas (nodeOffset y lineOffset) mediante el botón que abre los paneles de ajustes visuales, usando el subpanel Separaciones o bien encontrando el botón para borrar separaciones en el subpanel para mover elementos.

En la Figura borramos el "nodeOffset":"-8 -2" que habíamos insertado antes y volvemos a la posición inicial automática por niveles.

Reducir configuración

Figura
Figura. Grafo no dirigido creado desde el array nodos

Para este apartado tenemos el grafo no dirigido de la Figura, creado con el array nodos en JSON que se expone a continuación.

[{"index":0,"parent":[{"index":-1},{"index":4}]},
{"index":1,"parent":[{"index":0}]},
{"index":2,"parent":[{"index":0},{"index":1}]},
{"index":3,"parent":[{"index":1},{"index":4}]},
{"index":4,"parent":[{"index":1},{"index":2}]}]

Vamos a convertirlo en un grafo dirigido agregando "markerEnd":"arrow" a cada padre en el array nodos, pudiendo obviar el raíz "-1", pues aunque se agregue no tendrá ningún efecto:

[{"index":0,"parent":[{"index":-1},{"index":4,"markerEnd":"arrow"}]},
{"index":1,"parent":[{"index":0,"markerEnd":"arrow"}]},
{"index":2,"parent":[{"index":0,"markerEnd":"arrow"},{"index":1,"markerEnd":"arrow"}]},
{"index":3,"parent":[{"index":1,"markerEnd":"arrow"},{"index":4,"markerEnd":"arrow"}]},
{"index":4,"parent":[{"index":1,"markerEnd":"arrow"},{"index":2,"markerEnd":"arrow"}]}]
Figura
Figura. Agregando flechas a un grafo no dirigido

Vemos en la Figura que ahora aparecen las flechas. Hemos desactivado la opción reducir configuración, pues en caso de estar activada revisará todas aquellas propiedades que son iguales para todos los nodos o aristas (parent) y las quitará del array nodes, traspasándolos al objeto config que contiene la configuración.

Por lo tanto, si todos los parent tienen la misma propiedad "markerEnd" con el mismo valor "arrow", esa propiedad es eliminada de los parent y se traspasaría a la configuración si la opcion para reducir la configuración estuviese activada, quedando el array nodos como estaba al principio y la configuración como se explica a continuación.

Figura
Figura. Flechas

En la Figura vemos como utilizando la opción para reducir la configuración, se establece en la configuración marcador final al valor flecha, que es la traducción de "markerEnd":"arrow". Para pasar de no dirigido a dirigido un grafo, en lugar de poner "markerEnd":"arrow" en cada parent como hicimos, simplemente iríamos a la configuración para establecerlo.

Así quedaría finalmente el JSON con el array nodos sin propiedades markerEnd, pues al usarlas todos los nodos se traspasa al objeto config:

{"nodes":[
    {"index":0,"parent":[{"index":-1},{"index":4}]},
    {"index":1,"parent":[{"index":0}]},
    {"index":2,"parent":[{"index":0},{"index":1}]},
    {"index":3,"parent":[{"index":1},{"index":4}]},
    {"index":4,"parent":[{"index":1},{"index":2}]}
],
"config":{
    "markerEnd":"arrow"
}}

Preservar configuración

Figura
Figura. Grafo de muestra

En la versión anterior de la aplicación tenía un control tipo check para preservar la configuración. Lo he eliminado pues entendiendo el comportamiento de la aplicación veremos que no es necesario. Veamos el siguiente grafo de muestra de la Figura con este JSON:

{"nodes":[
    {"index":0,"value":1,"parent":[{"index":-1}]},
    {"index":1,"value":2,"parent":[{"index":0}]},
    {"index":2,"value":3,"parent":[{"index":0}]}
],
"config":{
    "wv":75,
    "hv":75,
    "adjust":false,
    "wvIni":75,
    "nodeColor":"cyan",
    "lineColor":"red",
    "markerEnd":"arrow"
}}
Figura
Figura. Preservar configuración

En la Figura damos entrada a un grafo desde el panel de muestras con el botón con el resultado de la Figura. Al importar se actualiza el grafo directamente.

Vemos que en la barra de botones de la parte superior tenemos el botón marcado con 1 y otro en la barra de botones más abajo marcado con 2. Este segundo se aplica sobre la configuración que encontramos en el panel izquierdo que contiene las propiedades del SVG, nodos y aristas. Como la propiedad nodeColor para establecer el color del nodo, observando que para todos los nodos tenemos "nodeColor": "cyan", valor que vimos en la configuración importada. Actuar con el botón 2 significa que preserva la configuración.

Figura
Figura. Color nodo

Por ejemplo, si en el área de texto de la parte superior agregamos color amarillo al primer nodo escribiendo {"index":0,"nodeColor":"yellow","parent":[{"index":-1},... y a continuación pulsamos el botón marcado con 2 vemos el resultado de la Figura, observando que se han preservado el resto de configuraciones.

Figura
Figura. Color nodo

En cambio si actualizamos el grafo con el botón marcado con 1 obtendremos el grafo de la Figura, donde hemos perdido la configuración anterior. Con ese botón se restablece la configuración a los valores iniciales, que también puede llevarse a cabo con el botón

Valores automáticos de los nodos (nodeValueAuto)

Figura
Figura. Grafo con valor "end" en último nodo

Para exponer este apartado tenemos el grafo de la Figura, donde los nodos tienen valores por defecto que son los índices de los mismos, que empiezan en cero, a excepción del último nodo que le hemos dado valor "end", como se observa en el JSON siguiente:

{"nodes":[
    {"index":0,"parent":[{"index":-1}]},
    {"index":1,"parent":[{"index":-1}]},
    {"index":2,"parent":[{"index":-1}]},
    {"index":3,"parent":[{"index":0},{"index":1},{"index":2}]},
    {"index":4,"parent":[{"index":0},{"index":1},{"index":2}]},
    {"index":5,"value":"end","parent":[{"index":0},{"index":1},{"index":2}]}
],
"config":{
}}
Figura
Figura. Valores nodos

Antes tenía un control en la parte superior de la aplicación y lo he modificado por una nueva propiedad valor nodo automático (nodeValueAuto), como se observa en la Figura. Damos la opción alfabético-minúsculas y vemos el resultado, asignando a cada nodo las letras "a, b, c, d, e":

Figura
Figura. Valores nodos alfabético minúsculas

Los nodos que tengan un valor, como "end" para el último nodo, no se modifica. Este es el nuevo JSON obtenido, observándose la propiedad "nodeValueAuto":"alphabetic-lower" en la configuración:

{"nodes":[
    {"index":0,"parent":[{"index":-1}]},
    {"index":1,"parent":[{"index":-1}]},
    {"index":2,"parent":[{"index":-1}]},
    {"index":3,"parent":[{"index":0},{"index":1},{"index":2}]},
    {"index":4,"parent":[{"index":0},{"index":1},{"index":2}]},
    {"index":5,"value":"end","parent":[{"index":0},{"index":1},{"index":2}]}
],
"config":{
    "nodeValueAuto":"alphabetic-lower"
}}
Figura
Figura. Reetiquetado automático de nodos

Estableciendo un valor automático distinto de index significa que si agregamos o eliminamos nodos, todos los valores de nodos serán reetiquetados según el valor nodeValueAuto. Por ejemplo, en el grafo de la Figura eliminamos el primer nodo etiquetado con "a" que estaba en la esquina superior izquierda y agregamos un nuevo nodo solitario a la derecha, observando en la Figura que se reetiquetan todos los nodos a excepción del que tiene la etiqueta "end". Si no queremos que este reetiquetado automático siga actuando podemos poner el valor por defecto index.

Si queremos borrar los valores de los nodos podemos usar el botón "Borrar" que se observa en la Figura.

Los valores automáticos incluidos en la aplicación son los siguientes:

  • index aplica el índice del nodo que recordemos que se inician en 0 y son correlativos. Este es el valor por defecto.
  • index+1 sumando uno con lo que se inician en 1.
  • alphabetic-lower para alfabético minúsculas.
  • alphabetic-upper para alfabético mayúsculas.
  • roman-lower para números romanos minúsculas.
  • roman-upper para números romanos mayúsculas.
  • greek-lower para griego minúsculas.
  • greek-upper para griego mayúsculas.
  • none para ningún valor.