Disposition du graphique
Pivotick prend en charge plusieurs stratégies de disposition pour positionner les nœuds. Vous pouvez configurer la mise en page via l'option layout ou via simulation.layout ; lorsque les deux sont définis, layout gagne.
| Options | Type | Valeur par défaut | Description |
|---|---|---|---|
type | 'force' | 'tree' | 'structured' | 'force' | L'algorithme de mise en page à utiliser. 'force' applique une disposition des forces basée sur la physique. 'tree' organise les nœuds dans une structure arborescente. 'structured' dessine un petit graphique comme le ferait une personne, voir Mise en page structurée. |
Options de disposition de l'arborescence
Lorsque type: 'tree' est sélectionné, les options supplémentaires suivantes sont disponibles :
| Options | Type | Valeur par défaut | Description |
|---|---|---|---|
rootId | string | undefined | Spécifiez l'ID du nœud à utiliser comme racine. S'il n'est pas fourni, Pivotick sélectionnera automatiquement une racine basée sur rootIdAlgorithmFinder. |
rootIdAlgorithmFinder | - | MaxReachability | Algorithme utilisé pour trouver automatiquement le nœud racine. |
parentKey | string | undefined | Nom de la clé node.data contenant l'identifiant du parent du nœud. Lorsqu’elle n’est pas définie, la parentalité vient des limites. Voir Déclarer la hiérarchie. |
depthKey | string | undefined | Nom de la clé node.data contenant la ligne sur laquelle se trouve le nœud, en comptant à partir de 0. Non défini, un nœud se trouve une ligne en dessous de son parent. |
strength | number | 0.1 | La force nécessaire pour maintenir la structure arborescente. |
horizontal | boolean | false | Disposez les nœuds horizontalement plutôt que verticalement. |
radial | boolean | false | Placez les nœuds dans une disposition radiale plutôt que verticale. |
radialGap | number | 750 | Espace entre les couches dans une disposition radiale. |
spacing | 'auto' | 'manual' | 'auto' | Si les deux multiplicateurs ci-dessous s'ajustent en fonction de la taille des nœuds et de la forme de l'arbre. Définir l'un ou l'autre implique explicitement 'manual'. |
levelSpacing | number | 1 | Multiplie la distance entre les niveaux consécutifs (entre les anneaux, lorsque radial). 1 ajuste l'arbre à la toile, comme auparavant. |
siblingSpacing | number | 1 | Multiplie la distance entre les nœuds dans un niveau. Ignoré lorsque radial, où un niveau s'étend toujours sur le cercle complet. |
Cycles et graphiques déconnectés
La hiérarchie est construite à partir d'un arbre couvrant en largeur en premier, enraciné à rootId, et non à partir des bords bruts, donc aucun de ces éléments n'arrête une disposition arborescente :
- Cycles. Le premier bord à atteindre un nœud en fait le parent de ce nœud ; une arête qui arrive à un nœud déjà placé ne fait tout simplement pas partie de la hiérarchie et est dessinée en traversant les niveaux comme n'importe quelle autre arête. Un nœud avec deux parents est également revendiqué par l'un d'eux.
- Plusieurs composants. Chaque composant que la racine ne peut pas atteindre obtient sa propre racine — préférant un nœud sans arêtes entrantes, donc un composant qui est une hiérarchie est dessiné comme un seul — et les composants sont disposés côte à côte.
- Nœuds sans arêtes du tout. Ceux-ci n'ont pas leur place dans une hiérarchie, ils sont donc garés plutôt que de recevoir une : ils remplissent l'espace mort à côté des niveaux peu profonds, au bord de fuite du tracé, qu'un arbre laisse vide car il s'élargit en descendant. Dans la disposition radiale, ils prennent leur propre anneau en dehors du dernier. Seuls les nœuds rien ne pointe vers et qui pointent vers rien sont garés, donc aucun bord ne s'étend de l'arbre à l'aire de stationnement.
Notez que rootIdAlgorithmFinder: 'MinMaxDistance' et 'MinHeight' sont la même recherche — le nœud dont le chemin descendant le plus long est le plus court — et les deux sont basés sur un tri topologique, donc sur un graphique avec un cycle, ils avertissent et reviennent au premier nœud. La valeur par défaut 'MaxReachability' n'a pas une telle limitation.
Recherche de racine d'arbre
Dans une présentation arborescente, un nœud doit être désigné comme racine. Vous pouvez spécifier le nœud racine à l'aide de l'option rootId.
Si vous ne savez pas quel nœud doit être la racine, ne vous inquiétez pas ! Pivotick en sélectionnera automatiquement un en utilisant l'algorithme défini en rootIdAlgorithmFinder.
Un nom rootId est parcouru en ignorant la direction du bord, de sorte que n'importe quel nœud peut racine un arbre entier - choisissez une feuille et le graphique se raccroche en dessous, avec son ancien parent un niveau plus bas. (Suivre les flèches d'une feuille n'atteindrait rien, et le reste du graphique serait disposé à côté comme deuxième composant.) Les bords eux-mêmes sont toujours dessinés tels quels, donc un lien utilisé dans l'autre sens s'affiche comme une flèche pointant vers un niveau. Une racine choisie par rootIdAlgorithmFinder continue de marcher le long des flèches : elle a été lue en premier lieu.
À moins que les flèches ne soient pas une hiérarchie. Sur des données qui convergent — chaque feuille est une source, toutes pointant vers quelques centres, qui est la forme de la plupart des données de provenance et « vues avec » — aucun nœud n'atteint le graphique le long des flèches, quel que soit le chercheur demandé. Une marche dirigée laisse alors presque chaque nœud comme racine qui lui est propre, et l'arbre apparaît comme un peigne de talons dont la plupart des bords volent à travers le réseau. Ainsi, lorsque aucun nœud ne peut couvrir la moitié de son propre composant en suivant les flèches, la mise en page arrête de lire la direction : elle parcourt chaque bord dans les deux sens et se réenracine au milieu du graphique. Les bords sont toujours dessinés exactement tels qu'ils sont, ce qui signifie que leurs flèches pointent vers le haut de l'arbre vers la racine - le rendu honnête des données qui pointent dans cette direction.
Il s'agit d'une solution de secours, pas d'une règle, et le test porte sur le graphique plutôt que sur la racine sélectionnée :
- Là où les flèches forment une hiérarchie, elles sont la meilleure chose à utiliser, et chaque chercheur garde sa propre réponse - y compris
'MinHeight', dont la réponse sur un arbre est toujours un feuille, n'atteignant rien exprès. Une racine qui couvre une petite partie du graphique n’est annulée que lorsque rien d’autre n’aurait pu en couvrir davantage. - Un graphique composé de plusieurs hiérarchies distinctes apparaît toujours comme une forêt : la couverture est mesurée par rapport au composant propre de la racine, de sorte qu'une hiérarchie multi-composants parfaitement bonne n'est pas confondue avec un échec.
- Un
rootIdépinglé ignore complètement le test : il est déjà parcouru en ignorant la direction.
Un rootId nommant un nœud qui n'est pas dans le graphique - filtré, supprimé, à l'intérieur d'un cluster réduit - est ignoré tant que cela est vrai, et le chercheur sélectionne la racine à la place. L'option est conservée, donc le nœud qui revient réenracine l'arborescence.
La racine peut également être modifiée au moment de l'exécution — le menu volant Physique la propose sous forme de menu sur la ligne Root chaque fois qu'une arborescence est active. Le menu nomme chaque chercheur et indique ce qu'il fait ; sa première entrée est "le nœud que j'ai sélectionné", qui est grisée jusqu'à ce qu'exactement un nœud soit sélectionné. Une fois qu'un nœud est épinglé, la ligne affiche l'étiquette de ce nœud plutôt que le nom d'un chercheur :
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' }Déclarer la hiérarchie
Normalement, Pivotick établit la hiérarchie pour elle-même : la parentalité à partir d'un arbre couvrant sur les bords, et chaque nœud une ligne en dessous de son parent. Deux options permettent aux données de le dire à la place.
parentKeynomme la clénode.datacontenant l'identifiant parent d'un nœud.depthKeynomme la touchenode.datatenant la ligne sur laquelle elle se trouve, en comptant à partir de0à la racine la moins profonde.
Les deux ne sont pas définis par défaut et rien n'est lu jusqu'à ce que vous nommiez une clé - donc un graphique dont les données contiennent un champ depth ou parent n'est pas affecté jusqu'à ce que vous le demandiez.
L’une ou l’autre clé peut manquer sur n’importe quel nœud individuel. Un nœud sans parent déclaré en obtient un depuis les bords comme d'habitude ; un nœud sans ligne déclarée se trouve en dessous de son parent. Un graphique qui ne déclare ni l’un ni l’autre est présenté exactement comme il l’a toujours été.
Commencer un arbre plus bas
C'est à cela que sert principalement depthKey. Un graphique de plusieurs arbres distincts est une forêt, et ses racines partagent toutes la rangée la moins profonde – il n'y avait auparavant aucun moyen de dire que l'une d'elles appartient plus bas. Donnez une ligne à cette racine :
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 b2L'arbre entier se déplace avec sa racine : les enfants de b le suivent. Dans la disposition radial, une ligne est un ring, donc la même déclaration commence cet arbre à deux anneaux.
Que signifient les lignes
Une ligne ne peut pousser un nœud plus bas. Un arbre bien rangé place un enfant exactement une ligne en dessous de son parent, donc une ligne qui n'est pas en dessous de celle du parent ne peut pas être dessinée - elle est limitée à parent + 1 et un avertissement indique combien il y en avait. L'alternative, honorer la ligne en détachant le nœud de son parent, permettrait à un mauvais numéro de briser un arbre en morceaux supplémentaires.
Les lignes que les données sautent sont laissées vides et occupent de l'espace réel — c'est ce qui donne un sens à une ligne déclarée. Un arbre est mis à l'échelle pour s'adapter au canevas, donc déclarer la ligne 50 sur un graphique par ailleurs peu profond lui donne une hauteur de cinquante et une lignes et réduit chaque ligne réelle à néant. levelSpacing et espacement automatique sont le chemin de retour.
Ce que fait un parent déclaré
Un parent déclaré est honoré qu'un bord rejoigne ou non la paire. Cela lui permet d'énoncer une hiérarchie que les arêtes ne contiennent pas - mais cela signifie également qu'une telle branche est dessinée sans ligne le long d'elle, car il n'y a pas d'arête à dessiner. La déclaration est l'origine de la hiérarchie ; les bords ne sont dessinés que tels quels.
Deux conséquences utiles : un nœud avec plusieurs arêtes entrantes peut dire lequel est son véritable parent (l'arbre couvrant autrement prendrait celui qui l'atteindrait en premier), et un nœud sans aucune arête qui déclare un parent obtient une place appropriée dans l'arbre au lieu de la zone garée.
Les déclarations qui ne peuvent pas être utilisées sont supprimées pour ce nœud, qui retombe ensuite sur les bords, et un avertissement les répertorie toutes :
- un parent nommant un nœud qui n'est pas présenté - filtré, supprimé ou à l'intérieur d'un cluster réduit - de la même manière que
rootIdrevient lorsque le nœud qu'il nomme est manquant ; - un parent qui fermerait un cycle (le lien qui le ferme est celui abandonné) ;
- une ligne qui n'est pas un nombre
0ou supérieur.
Priorité
rootId devance parentKey. Épingler une racine - dans les options, ou en en choisissant une dans le rail Physique - est une demande de raccrocher l'arbre à ce nœud, de sorte que la parentalité est dérivée des bords et que les parents déclarés sont réservés pour cette passe. Les rows déclarées ne sont pas affectées : elles indiquent la profondeur à laquelle se trouve un nœud, pas à quoi il est suspendu.
egoTree ignore les deux options ; il construit une étoile à partir des voisins de la racine.
Espacement
Un arbre est disposé pour adapter à la toile, de sorte que son espacement naturel suit déjà la taille de la toile et la profondeur et la largeur de l'arbre. levelSpacing et siblingSpacing échelles qui correspondent à la géométrie : 2 signifie "deux fois plus éloigné que la disposition ajustée" sur les axes de profondeur et de largeur respectivement. Les deux sont par défaut 1, qui est la mise en page ajustée elle-même, et les deux acceptent 0.5 à 10.
Ils peuvent également être modifiés au moment de l'exécution — le menu volant Physique les propose sous forme de curseurs chaque fois qu'une disposition en arborescence est active (à la place des boutons de simulation, qu'une disposition en arborescence ignore) :
graph.simulation.setTreeSpacing({ levelSpacing: 2 })
graph.simulation.getTreeSpacing() // { levelSpacing: 2, siblingSpacing: 1 }Espacement automatique
Par défaut, l'arbre applique son propre espacement. Une disposition arborescente est dimensionnée à partir du canevas et ne tient jamais compte de la taille des nœuds, donc un arbre de petits points et un arbre de grands nœuds étiquetés sont disposés de la même manière – et le second se chevauche. spacing: 'auto' mesure la paire de voisins la plus serrée sur chaque axe, détermine ce dont ils ont besoin pour leurs rayons (plus de la place pour une pointe de flèche entre les niveaux) et adapte les multiplicateurs en conséquence. Il les redirige chaque fois que le graphique change.
Deux choses qu’il ne fera délibérément pas :
- Il ne regroupe jamais un arbre plus serré que la disposition ajustée. Auto n'augmente qu'un multiplicateur au-dessus de
1, de sorte qu'un graphique qui n'a jamais été encombré est présenté exactement comme il l'a toujours été. - Il n'annule jamais un choix que vous avez fait. Un arbre qui définit
levelSpacingousiblingSpacingest considéré comme ayant pris sa décision, et déplacer l'un ou l'autre curseur à la main quitte automatiquement l'option pour de bon. Rendez les multiplicateurs avecsimulation.enableAutoTreeSpacing().
Dans la disposition radial, où un niveau s'étend toujours sur le cercle complet, l'encombrement dans un anneau ne peut être soulagé qu'en poussant les anneaux plus loin - de sorte que les deux mesures conduisent levelSpacing là-bas.
Disposition structurée
type: 'structured' présente un petit graphique comme une personne le dessinerait, pour une visionneuse intégrée qui doit être lisible d'un seul coup d'œil. Il est calculé plutôt que simulé : chaque nœud est placé et épinglé, donc le même graphique donne toujours la même image, et faire glisser un nœud ne déplace que ce nœud.
- Composants côte à côte. Chaque composant connecté est disposé seul et les composants sont regroupés en rangées. La largeur de ligne est celle qui adapte l'ensemble de la mise en page au canevas avec le zoom le plus grand.
- Les éventails s'alignent. Trois feuilles ou plus du même type qui pendent sur un ou deux nœuds identiques (une feuille a au plus deux voisins) se dressent sur une rangée ou une colonne uniformément espacées, les nœuds auxquels elles pendent d'un côté. Le regroupement est celui utilisé par la règle mêmes voisins de Simplify, tapé par
UI.simplify.typeOf, sinonrender.nodeTypeAccessor. Rien n'est plié. - Les distances suivent les arêtes. Tout le reste est placé par majoration des contraintes : les nœuds espacés d'une arête sont espacés d'une longueur d'arête, deux arêtes espacées deux fois plus, et ainsi de suite. Les voisins d'un hub sortent autour et une chaîne en ressort droite, sans cas particulier ni pour l'un ni pour l'autre.
- Un plateau pour les nœuds lâches. Les nœuds sans bord sont quadrillés en dessous du reste, ou à sa droite.
- Étiquettes du côté libre. Une étiquette flottante (
textVerticalShiftoutextHorizontalShiftde 1 ou plus) est dessinée du côté de son nœud que ses bords laissent libre, aussi loin que le style le dit. Les étiquettes d'un ventilateur sont toutes tournées vers les nœuds auxquels il est suspendu.
Le tracé est refait à chaque fois que le graphe visible change, et à nouveau après le premier tirage, lorsque les tailles réelles des nœuds sont connues. Les nœuds sont espacés de getLayoutRadius(), donc un style de nœud qui déclare layoutSize est disposé à cette taille, quel que soit son dessin. Il est destiné aux graphiques comportant jusqu’à quelques centaines de nœuds.
| Options | Type | Valeur par défaut | Description |
|---|---|---|---|
groupMin | number | 3 | Le moins de feuilles partagent un type et leurs voisins font la queue en éventail. Moins sont placés un par un. |
direction | 'auto' | 'row' | 'column' | 'auto' | Comment un fan fait la queue. 'auto' le place à travers la ligne jusqu'aux nœuds auxquels il est suspendu. |
gap | number | 30 | Libérez l’espace entre deux nœuds. Un bord est dessiné deux fois plus long ; les composants et le plateau sont trois fois plus éloignés. |
trayPosition | 'bottom' | 'right' | 'bottom' | Où vont les nœuds sans bord. |
labelSides | boolean | true | Déplacez chaque étiquette flottante sur le côté que ses bords laissent libres. false laisse les étiquettes là où le style les place. |
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) rapporte le côté choisi pour un nœud : 'top', 'bottom', 'left' ou 'right', ou undefined sous toute autre disposition.
La vue est adaptée aux nœuds, et non à leurs étiquettes, de sorte qu'une longue étiquette à côté d'un nœud au bord de la mise en page peut dépasser le canevas.
danger
Ajoutez un exemple avec les deux mises en page !
Arbre vertical
const options = {
layout: {
type: 'tree'
},
UI: {
mode: 'static'
}
}Arbre vertical
const options = {
layout: {
type: 'tree',
radial: true,
radialGap: 300,
},
UI: {
mode: 'static'
}
}