Ir al contenido

Diseño gráfico ​

Pivotick admite múltiples estrategias de diseño para posicionar nodos. Puedes configurar el diseño a través de la opción layout, o mediante simulation.layout; cuando ambos están establecidos, layout gana.

OpciónTipoPredeterminadoDescripción
type'force' | 'tree' | 'structured''force'El algoritmo de diseño a utilizar. 'force' aplica un diseño de fuerza basado en la física. 'tree' organiza los nodos en una estructura de árbol. 'structured' dibuja un pequeño gráfico como lo haría una persona, consulte Diseño estructurado.

Opciones de diseño de árbol ​

Cuando se selecciona type: 'tree', están disponibles las siguientes opciones adicionales:

OpciónTipoPredeterminadoDescripción
rootIdstringundefinedEspecifique el ID del nodo que se utilizará como raíz. Si no se proporciona, Pivotick seleccionará automáticamente una raíz basada en rootIdAlgorithmFinder.
rootIdAlgorithmFinder-MaxReachabilityAlgoritmo utilizado para encontrar automáticamente el nodo raíz.
parentKeystringundefinedNombre de la clave node.data que contiene la identificación del padre del nodo. Sin estar establecida, la paternidad viene de los límites. Consulte Declaración de la jerarquía.
depthKeystringundefinedNombre de la tecla node.data que contiene la fila en la que se encuentra el nodo, contando desde 0. Sin configurar, un nodo se encuentra una fila debajo de su padre.
strengthnumber0.1La fuerza de fuerza para mantener la estructura del árbol.
horizontalbooleanfalseOrganice los nodos horizontalmente en lugar de verticalmente.
radialbooleanfalseColoque los nodos en un diseño radial en lugar de vertical.
radialGapnumber750Espacio entre capas en diseño radial.
spacing'auto' | 'manual''auto'Si los dos multiplicadores siguientes se ajustan según el tamaño de los nodos y la forma del árbol. Establecer cualquiera de ellos implica explícitamente 'manual'.
levelSpacingnumber1Multiplica la distancia entre niveles consecutivos (entre anillos, cuando radial). 1 ajusta el árbol al lienzo, como antes.
siblingSpacingnumber1Multiplica la distancia entre nodos dentro de un nivel. Se ignora cuando radial, donde un nivel siempre abarca el círculo completo.

Ciclos y gráficos desconectados. ​

La jerarquía se construye a partir de un árbol de expansión con raíz en rootId, no desde el formato original. bordes, por lo que ninguno de estos detiene el diseño de un árbol:

  • Ciclos. El primer borde que llega a un nodo lo convierte en el padre de ese nodo; un borde que llega a un El nodo ya colocado simplemente no es parte de la jerarquía y se dibuja cruzando niveles como cualquier otro. otro borde. Un nodo con dos padres también es reclamado por uno de ellos.
  • Varios componentes. Cada componente al que la raíz no puede llegar obtiene su propia raíz, prefiriendo un nodo sin bordes entrantes, por lo que un componente que es una jerarquía se dibuja como uno solo, y los componentes están dispuestos uno al lado del otro.
  • Nodos sin ningún borde. Estos no tienen lugar en una jerarquía, por lo que están estacionados en lugar que el dado: llenan el espacio muerto al lado de los niveles poco profundos, en el borde posterior del trazado, que un árbol deja vacío porque se ensancha a medida que desciende. En el trazado radial toman un anillo propio fuera del último. Solo los nodos a los que nada apunta y que apuntan a nada son estacionado, de modo que nunca quede ningún borde que se extienda desde el árbol hasta el área de estacionamiento.

Tenga en cuenta que rootIdAlgorithmFinder: 'MinMaxDistance' y 'MinHeight' son la misma búsqueda: el nodo cuyo camino más largo hacia abajo es el más corto, y ambos se basan en una clasificación topológica, por lo que en un gráfico con un ciclo advierten y vuelven al primer nodo. El valor predeterminado 'MaxReachability' no tiene tal limitación.

Buscador de raíces de árboles ​

En un diseño basado en árbol, se debe designar un nodo como raíz. Puede especificar el nodo raíz usando la opción rootId.

Si no está seguro de qué nodo debería ser la raíz, ¡no se preocupe! Pivotick seleccionará uno automáticamente usando el algoritmo definido en rootIdAlgorithmFinder.

Se recorre un rootId con nombre ignorando la dirección del borde, por lo que cualquier nodo puede enraizar un árbol completo; elija un hoja y el gráfico vuelve a colgar debajo de ella, con su padre anterior un nivel más abajo. (Siguiendo las flechas de una hoja no llegaría a nada, y el resto del gráfico se colocaría a su lado como un segundo componente.) Los bordes en sí todavía se dibujan como están, por lo que un enlace se usa al revés se representa como una flecha que apunta hacia un nivel. Una raíz que el rootIdAlgorithmFinder eligió sigue caminando las flechas: fue leído en ellas en primer lugar.

A menos que las flechas no sean una jerarquía. En datos que convergen: cada hoja es una fuente, todas ellas apuntando a algunos centros, que es la forma de la mayoría de los datos de procedencia y "vistos con": ningún nodo llega el gráfico a lo largo de las flechas, cualquiera que sea el buscador al que se le pregunte. Luego, una caminata dirigida sale de casi todos los nodos. como una raíz propia, y el árbol surge como un peine de tocones con la mayoría de los bordes volando a través del diseño. Entonces, cuando ningún nodo puede cubrir la mitad de su propio componente siguiendo las flechas, el El diseño deja de leer la dirección: recorre cada borde en ambos sentidos y vuelve a enraizarse en el medio del gráfico. Los bordes todavía están dibujados exactamente como están, lo que significa que sus flechas apuntan hacia el árbol hacia el raíz: la representación honesta de los datos que apuntan en esa dirección.

Esta es una alternativa, no la regla, y la prueba se trata de la gráfica y no de la raíz que se utilizó. elegido:

  • Cuando las flechas forman una jerarquía, son lo mejor para diseñar, y cada buscador mantiene su propia respuesta, incluida 'MinHeight', cuya respuesta en un árbol es siempre una hoja, alcanzando nada a propósito. Una raíz que cubre poco del gráfico sólo se anula cuando no hay nada más podría haber cubierto más.
  • Un gráfico de varias jerarquías separadas todavía aparece como un bosque: la cobertura se mide en función de propio componente de la raíz, por lo que una jerarquía de múltiples componentes perfectamente buena no se confunde con una fracaso.
  • Un rootId fijado se salta la prueba por completo: ya ha caminado ignorando la dirección.

Un rootId que nombra un nodo que no está en el gráfico: filtrado, eliminado, dentro de un clúster colapsado - se ignora mientras sea cierto y el buscador elige la raíz. La opción se mantiene, entonces el nodo que regresa vuelve a enraizar el árbol.

La raíz también se puede cambiar en tiempo de ejecución: el menú lateral Física la ofrece como un menú en la fila Root siempre que un diseño de árbol esté activo. El menú nombra cada buscador y dice lo que hace; su primera entrada es "el nodo que he seleccionado", que aparece atenuado hasta que se selecciona exactamente un nodo. Una vez que un nodo es Fijada, la fila muestra la etiqueta de ese nodo en lugar del nombre del buscador:

ts
graph.simulation.setTreeRoot({ rootId: 'node-42' })          // pin the tree to a node
graph.simulation.setTreeRoot({ algorithm: 'MinHeight' })     // drop the pin, let the finder choose
graph.simulation.getTreeRoot() // { rootId: undefined, algorithm: 'MinHeight' }

Declarando la jerarquía ​

Normalmente, Pivotick resuelve la jerarquía por sí solo: la paternidad a partir de un árbol de expansión sobre el bordes, y cada nodo una fila debajo de su padre. Dos opciones permiten que los datos lo digan.

  • parentKey nombra la clave node.data que contiene la identificación principal de un nodo.
  • depthKey nombra la tecla node.data que contiene la fila en la que se encuentra, contando desde 0 en el raíz más superficial.

Ambos no están configurados de forma predeterminada y no se lee nada hasta que nombras una clave, por lo que un gráfico cuyos datos que lleva un campo depth o parent no se ve afectado hasta que usted lo solicite.

Cualquiera de las claves puede faltar en algún nodo individual. Un nodo sin padre declarado obtiene uno del bordes como de costumbre; un nodo sin fila declarada se encuentra uno debajo de su padre. Un gráfico que no declara ni está presentado exactamente como siempre.

Comenzando un árbol más abajo ​

Para esto sirve principalmente depthKey. Una gráfica de varios árboles separados es un bosque, y sus raíces todos comparten la fila menos profunda; anteriormente no había forma de decir que uno de ellos pertenece más lejos abajo. Dale una fila a esa raíz:

ts
const data = {
    nodes: [
        { id: 'a' }, { id: 'a1' }, { id: 'a2' },
        { id: 'b', data: { level: 2 } }, { id: 'b1' }, { id: 'b2' },
    ],
    edges: [
        { from: 'a', to: 'a1' }, { from: 'a', to: 'a2' },
        { from: 'b', to: 'b1' }, { from: 'b', to: 'b2' },
    ],
}

const graph = new Pivotick(container, data, {
    layout: { type: 'tree', depthKey: 'level' },
})
row 0     a
row 1   a1  a2
row 2                b          <- b asked for row 2
row 3              b1  b2

Todo el árbol se mueve con su raíz: los hijos de b lo siguen hacia abajo. En el diseño radial, una fila es una ring, por lo que la misma declaración inicia ese árbol con dos anillos.

¿Qué significan las filas? ​

Una fila solo puede empujar un nodo más hacia abajo. Un árbol ordenado coloca a un niño exactamente una fila debajo su padre, por lo que no se puede dibujar una fila que no esté debajo de la del padre; está sujeta a parent + 1 y una advertencia nombra cuántos eran. La alternativa, honrar la fila separando el nodo de su padre, permitiría que un número equivocado rompiera un árbol en pedazos adicionales.

Las filas que los datos omiten se dejan vacías y ocupan espacio real; eso es lo que hace que una declaración fila significa cualquier cosa. Un árbol se escala para que quepa en el lienzo, por lo que declarar la fila 50 en un lugar que de otro modo sería poco profundo El gráfico tiene cincuenta y una filas de alto y reduce cada fila real a nada. levelSpacing y espaciado automático son el camino de regreso.

Lo que hace un padre declarado ​

Un padre declarado se respeta independientemente de que una ventaja se una o no al par. Eso le permite afirmar una jerarquía que los bordes no contienen, pero también significa que dicha rama se dibuja sin línea a lo largo porque allí no hay ningún borde que sacar. De la declaración es de donde proviene la jerarquía; el Los bordes sólo se dibujan tal como están.

Dos consecuencias útiles: un nodo con varias aristas entrantes puede decir cuál es su padre real (de lo contrario, el árbol de expansión tomaría lo que llegara primero) y un nodo sin ningún borde que declara que un padre obtiene un lugar adecuado en el árbol en lugar del área estacionada.

Las declaraciones que no se pueden utilizar se descartan para ese nodo, que luego vuelve a caer en los bordes y una advertencia los cuenta a todos:

  • un padre que nombra un nodo que no se está diseñando: filtrado, eliminado o dentro de un sistema colapsado cluster: de la misma manera rootId retrocede cuando falta el nodo al que nombra;
  • un padre que cerraría un ciclo (el vínculo que lo cierra es el que se cae);
  • una fila que no es un número 0 o mayor.
Prioridad ​

rootId supera a parentKey. Fijar una raíz: en las opciones o eligiendo una en Física carril: es una solicitud para volver a colgar el árbol de ese nodo, de modo que la paternidad se vuelva a derivar de los bordes y los padres declarados quedan apartados para ese pase. Las filas declaradas no se ven afectadas: dicen qué tan profundas se asienta un nodo, no de dónde cuelga.

egoTree ignora ambas opciones; construye una estrella a partir de los propios vecinos de la raíz.

Espaciado ​

Se diseña un árbol para ajustar al lienzo, por lo que su espaciado natural ya sigue el tamaño del lienzo y qué tan profundo y ancho es el árbol. levelSpacing y siblingSpacing escala que se ajusta a la geometría: 2 significa "dos veces más separados que el diseño ajustado" en los ejes de profundidad y anchura respectivamente. ambos El valor predeterminado es 1, que es el diseño ajustado en sí, y ambos aceptan 0.5 a 10.

También se pueden cambiar en tiempo de ejecución: el menú lateral Física los ofrece como controles deslizantes cada vez que un árbol El diseño está activo (en lugar de las perillas de simulación, que un diseño de árbol ignora):

ts
graph.simulation.setTreeSpacing({ levelSpacing: 2 })
graph.simulation.getTreeSpacing() // { levelSpacing: 2, siblingSpacing: 1 }

Espaciado automático ​

Por defecto, el árbol crea su propio espaciado. Un diseño de árbol se dimensiona desde el lienzo y nunca observa qué tan grandes son los nodos, por lo que se colocan un árbol de puntos pequeños y un árbol de nodos grandes etiquetados sale de manera idéntica, y el segundo se superpone. spacing: 'auto' mide el par de pantalones más ajustados vecinos en cada eje, calcula lo que necesitan para sus radios (más espacio para una punta de flecha entre niveles) y escala los multiplicadores para adaptarlos. Los vuelve a derivar cada vez que cambia el gráfico.

Dos cosas que deliberadamente no hará:

  • Nunca empaqueta un árbol más apretado que el diseño ajustado. Auto solo eleva un multiplicador por encima 1, por lo que un gráfico que nunca estuvo lleno de gente se presenta exactamente como siempre.
  • Nunca anula una elección que hayas hecho. Un árbol que establece levelSpacing o siblingSpacing es Se considera que ha tomado una decisión y, al mover cualquiera de los controles deslizantes con la mano, se deja el modo automático para siempre. Entrega el multiplicadores de vuelta con simulation.enableAutoTreeSpacing().

En el diseño radial, donde un nivel siempre abarca el círculo completo, el hacinamiento dentro de un anillo sólo puede se aliviará empujando los anillos más hacia afuera, de modo que ambas medidas lleven levelSpacing allí.

Diseño estructurado ​

type: 'structured' presenta un pequeño gráfico de la forma en que lo dibujaría una persona, para un incrustado visor que tiene que ser legible de un vistazo. Se calcula en lugar de simularse: cada nodo es colocado y fijado, por lo que el mismo gráfico siempre muestra la misma imagen, y al arrastrar un nodo se mueve solo ese nodo.

  • Componentes uno al lado del otro. Cada componente conectado se presenta por sí solo y el Los componentes se empaquetan en filas. El ancho de fila es el que ajusta todo el diseño al lienzo con el zoom más grande.
  • Los ventiladores se alinean. Tres o más hojas del mismo tipo que cuelgan de la misma o dos Los nodos (una hoja tiene como máximo dos vecinos) se encuentran en una fila o columna espaciada uniformemente, con el nodos de los que cuelgan en un lado del mismo. La agrupación es la de Simplify. mismos vecinos usos de reglas, escritos por UI.simplify.typeOf, de lo contrario render.nodeTypeAccessor. No hay nada doblado.
  • Las distancias siguen los bordes. Todo lo demás se coloca mediante mayorización de tensiones: nodos uno borde separado siéntese a un borde de longitud, dos bordes separados el doble, y así sucesivamente. un centro salen unos vecinos y sale una cadena recta, sin caso especial para ninguno de los dos.
  • Una bandeja para nodos sueltos. Los nodos sin ningún borde se cuadriculan debajo del resto, o hasta su correcto.
  • Etiquetas en el lado libre. Una etiqueta flotante (textVerticalShift o textHorizontalShift de 1 o más) se dibuja en el lado de su nodo que sus bordes dejan libres, hasta el estilo lo puso. Todas las etiquetas de un abanico miran en dirección opuesta a los nodos de los que cuelga.

El diseño se rehace cada vez que cambia el gráfico visible, y una vez más después del primer sorteo. cuando se conocen los tamaños reales de los nodos. Los nodos están espaciados por getLayoutRadius(), por lo que un estilo de nodo que declara layoutSize se presenta en ese tamaño, sea lo que sea lo que dibuje. Está destinado a gráficos de hasta unos cientos de nodos.

OpciónTipoPredeterminadoDescripción
groupMinnumber3El menor número sale compartiendo un tipo y sus vecinos que se alinean como abanico. Se colocan menos uno por uno.
direction'auto' | 'row' | 'column''auto'Cómo se alinea un aficionado. 'auto' lo coloca a lo largo de la línea hasta los nodos de los que cuelga.
gapnumber30Despeja el espacio entre dos nodos. Se dibuja un borde dos veces más largo; Los componentes y la bandeja están separados tres veces más.
trayPosition'bottom' | 'right''bottom'Donde van los nodos sin arista.
labelSidesbooleantrueMueve cada etiqueta flotante hacia el lado que sus bordes dejan libres. false deja etiquetas donde las pone el estilo.
ts
new Pivotick(container, data, {
    layout: { type: 'structured', trayPosition: 'right' },
    render: {
        nodeTypeAccessor: (node) => node.getData().type,
        defaultNodeStyle: { text: (node) => node.getData().label, textVerticalShift: -1.4 },
    },
})

graph.simulation.getLabelSide(id) informa el lado elegido para un nodo: 'top', 'bottom', 'left' o 'right', o undefined bajo cualquier otro diseño.

La vista se ajusta a los nodos, no a sus etiquetas, por lo que una etiqueta larga al lado de un nodo en el borde del diseño puede pasar más allá del lienzo.

peligro

¡Agregue un ejemplo con los dos diseños!

árbol vertical ​

js
const options = {
    layout: {
        type: 'tree'
    },
    UI: {
        mode: 'static'
    }
}

árbol vertical ​

js
const options = {
    layout: {
        type: 'tree',
        radial: true,
        radialGap: 300,
    },
    UI: {
        mode: 'static'
    }
}