import type { Class_DrawingArea } from '../types/DrawingArea'; import type { Class_NodeElement } from '../Elements/Node'; import type { Class_LinkElement } from '../Elements/Link'; export declare class NodePositioningCyclesCore { readonly drawingArea: Class_DrawingArea; constructor(drawingArea: Class_DrawingArea); /** * Liens que l'utilisateur a explicitement verrouilles comme liens de recyclage. * Pre-amorces dans la detection de cycles : le DFS les considere deja coupes et ne descend pas * par eux, donc aucune autre arete du meme cycle n'est flaguee back-edge. */ get user_forced_recycling_link_ids(): Set; /** Liens que l'utilisateur a explicitement verrouilles comme NON recyclage. */ get user_forbidden_recycling_link_ids(): Set; /** * SOCLE : calcule l'index horizontal (colonne, base 0) de chaque noeud et la liste des liens de * recyclage. Unique point d'entree pour `position_x` (computeAutoSankey) comme pour `position_u` * (computeParametrization) — cf. `detectAllCyclesAndOptimize`. * * 1. Amorcage : tous les noeuds a -1, liens de recyclage forces par l'utilisateur deja coupes. * 2. Propagation depuis les sources (aucune entree structurelle), puis les noeuds isoles, puis * les composantes restantes (cycles purs sans source). * 3. Compaction : chaque lien de recyclage tente de decaler sa source vers un trou de colonne. * 4. Verrouillage : les noeuds dont la colonne est epinglee reprennent leur `position_u`. */ computeHorizontalIndexes(nodes_to_process: Class_NodeElement[]): { recycling_links: string[]; horizontal_indexes: { [node_id: string]: number; }; }; /** * Detecte les cycles et calcule les index horizontaux qui pilotent `position_u`. * * Historiquement un algorithme distinct (profondeurs + recyclage par comparaison de profondeurs), * d'ou la divergence x/u ; c'est aujourd'hui le socle commun (opensankey#1253). Signature * conservee : appele par `computeParametrization`. */ detectAllCyclesAndOptimize(nodes_to_process: Class_NodeElement[]): { recycling_links: string[]; horizontal_indexes: { [node_id: string]: number; }; }; /** * Bandes sur lesquelles juger la progression de `link` d'apres son orientation, ou `undefined` * quand la question n'a pas de sens ('hv'/'vh', ou 'vv' sans rangees fournies). Cf. * `markRecyclingLinks` pour la regle. Une orientation absente vaut 'hh' (defaut historique). */ private progressionIndexes; /** * Reflague `shape_is_recycling` d'apres des bandes deja connues : un flux est en recyclage * s'il RECULE, c'est-a-dire si sa cible est STRICTEMENT en amont de sa source SUR SON PROPRE * AXE. Un flux dont les deux extremites partagent une bande n'est PAS du recyclage : le rendu * en boucle n'y apporte rien et le declenchement etait trop sensible — la tolerance de * `clusterNodesByAxis` suffit a faire entrer un noeud deplace dans la bande de sa cible. Le * verrouillage tri-state de l'utilisateur (OpenSankey#711) prime toujours sur la geometrie. * * AXE PAR FLUX (`shape_orientation`) : * - 'hh' (ou orientation absente) → colonnes `horizontal_indexes` : reculer = aller a gauche ; * - 'vv' → rangees `vertical_indexes` : reculer = remonter ; * - 'hv' / 'vh' → RIEN. Un flux mixte part sur un axe et arrive sur l'autre : * aucune des deux comparaisons ne decrit sa progression, et trancher revenait a inventer un * critere. Son statut est laisse tel quel (a l'utilisateur de le poser via le verrou #711). * * Sans cette distinction, un diagramme vertical voyait chacun de ses flux bascule en recyclage * des qu'il descendait en biais vers la gauche, alors qu'il progresse normalement vers le bas. * * Utilise par la branche `skip_horizontal` de computeAutoSankey (colonnes issues de position_u) * et par le recalcul incremental apres un deplacement de noeud (sankeyapplication#153, bandes * deduites des x/y). Les noeuds hors des index (echange) ne contraignent rien. * * @param only_touching_nodes Si fourni, seuls les flux dont la source OU la cible est dans cet * ensemble sont reflagues ; les autres gardent leur statut (un flux arriere voulu loin du drag * ne doit pas basculer parce que les bandes globales ont bouge). * @param vertical_indexes Rangees deduites des y. Absent ⇒ les flux 'vv' sont laisses tels * quels, comme les mixtes (l'appelant ne raisonne alors que sur l'axe horizontal). * @returns pour chaque lien dont le statut a CHANGE, sa valeur precedente (utile a l'undo). */ markRecyclingLinks(nodes_to_process: Class_NodeElement[], horizontal_indexes: { [node_id: string]: number; }, only_touching_nodes?: Set, vertical_indexes?: { [node_id: string]: number; }): { [link_id: string]: boolean; }; /** * Verrouille (tristate OpenSankey#711) les flux dont le statut recyclage CHARGE diverge de ce * que la geometrie calculerait : le fichier fait foi, la detection auto ne doit jamais * rebasculer un flux arriere voulu par l'auteur (sankeyapplication#153). Appele apres le * chargement d'un fichier qui contient une geometrie. Les flux deja verrouilles sont ignores, * ainsi que les flux mixtes 'hv'/'vh', que le recalcul ne touche de toute facon jamais. * * GUERISON (sankeyapplication#153, correctif de l'axe) : « le fichier fait foi » suppose que le * statut enregistre traduit l'INTENTION de l'auteur. Or les fichiers sauves avant le correctif * portent le verdict de l'ANCIENNE regle — colonnes en x pour tous les flux, comparaison large * — qui declarait en recyclage des flux verticaux progressant normalement. Verrouiller ces * valeurs-la graverait le bug dans le fichier. Quand le statut charge correspond EXACTEMENT au * verdict de l'ancienne regle et diverge de la nouvelle, on l'attribue donc au bug : le flux * est recalcule au lieu d'etre verrouille. Une divergence que l'ancienne regle n'explique pas * reste, elle, un choix d'auteur — verrouille comme avant. * * SENS UNIQUE de la guerison : elle ne defait qu'un recyclage enregistre A TORT (true → false), * jamais l'inverse. Le bug d'axe SURDECLARAIT le recyclage (comparaison large sur x) ; un statut * `false` n'en est donc jamais une sequelle. Le prendre pour tel FABRIQUAIT une boucle que * l'auteur n'a jamais vue : dans un diagramme classique a satellites (imports sous l'atelier, * exports au-dessus), tout flux vertical montant est « en arriere » sur les rangees alors qu'il * progresse normalement, et se retrouvait dessine en recyclage au chargement. Ces flux-la sont * desormais verrouilles sur la valeur du fichier, comme n'importe quelle autre divergence. * * @returns les ids des flux verrouilles par la passe (les flux gueris n'y figurent pas). */ lockRecyclingStatusDivergences(nodes_to_process: Class_NodeElement[], horizontal_indexes: { [node_id: string]: number; }, vertical_indexes?: { [node_id: string]: number; }): string[]; /** * Explore les branches issues de `start_node` et affecte leur index horizontal. * DFS iteratif (tri topologique + detection des back-edges) puis relaxation en une passe * (plus long chemin). O(V+E). Les back-edges rencontrees sont ajoutees a `recycling_links_ids`. * * @param {Class_NodeElement} start_node Noeud de depart * @param {Class_NodeElement[]} nodes_to_process Sous-graphe considere * @param {number} starting_index Index affecte a `start_node` s'il n'en a pas de plus grand * @param {string[]} _visited_nodes_ids Inutilise (compatibilite d'API) * @param {string[]} recycling_links_ids Liens deja coupes ; enrichi des back-edges detectees * @param {object} horizontal_indexes_per_nodes_ids Index courant par id de noeud (mute en place) */ computeHorizontalIndex(start_node: Class_NodeElement, nodes_to_process: Class_NodeElement[], starting_index: number, _visited_nodes_ids: string[], recycling_links_ids: string[], horizontal_indexes_per_nodes_ids: { [node_id: string]: number; }): void; /** * Compacte le graphe autour d'un lien de recyclage : si la source du lien peut reculer dans un * « trou » de colonne laisse par ses propres predecesseurs, on l'y decale et on repropage. * * @param {Class_NodeElement[]} nodes_to_process Sous-graphe considere * @param {Class_LinkElement} link Lien identifie comme lien de recyclage * @param {string[]} recycling_links_ids Liens coupes pour la repropagation * @param {object} horizontal_indexes_per_nodes_ids Index courant par id de noeud (mute en place) */ computeRecyclingHorizontalIndex(nodes_to_process: Class_NodeElement[], link: Class_LinkElement, recycling_links_ids: string[], horizontal_indexes_per_nodes_ids: { [node_id: string]: number; }): void; /** * Repositionnement des nœuds sans entrée (logique existante préservée) */ repositionNodesWithoutInputs(nodes_per_horizontal_indexes: { [index: number]: Class_NodeElement[]; }, horizontal_indexes_per_nodes_ids: { [node_id: string]: number; }, max_horizontal_index: number): void; }