11use disposition_ir_model:: {
2- edge:: EdgeGroups ,
2+ edge:: { Edge , EdgeGroups } ,
33 entity:: { EntityType , EntityTypes } ,
44 node:: { NodeId , NodeNestingInfos , NodeRank , NodeRanks , NodeRanksNested } ,
55} ;
@@ -22,7 +22,10 @@ use disposition_model_common::{Id, Map};
2222/// contracted into a single logical node for ranking purposes.
2323///
2424/// Only **dependency** edges (not interaction edges) are considered for rank
25- /// computation.
25+ /// computation, plus any **layout edges** passed in separately -- these are
26+ /// invisible edges (from `thing_layout_edges`) that contribute to rank
27+ /// exactly like dependency edges, without ever appearing in `edge_groups` or
28+ /// being rendered.
2629///
2730/// [`IrDiagram`]: disposition_ir_model::IrDiagram
2831///
@@ -80,10 +83,14 @@ impl NodeRanksCalculator {
8083 /// interaction edges.
8184 /// * `node_nesting_infos`: Nesting information for each node, used to build
8285 /// the container-to-children map and compute LCA-level edge attribution.
86+ /// * `layout_edges`: Invisible layout-only edges (from
87+ /// `thing_layout_edges`) that contribute to rank alongside dependency
88+ /// edges, without being backed by an edge group.
8389 pub fn calculate < ' id > (
8490 edge_groups : & EdgeGroups < ' id > ,
8591 entity_types : & EntityTypes < ' id > ,
8692 node_nesting_infos : & NodeNestingInfos < ' id > ,
93+ layout_edges : & [ Edge < ' id > ] ,
8794 ) -> NodeRanksNested < ' id > {
8895 if node_nesting_infos. is_empty ( ) {
8996 return NodeRanksNested :: new ( ) ;
@@ -93,7 +100,8 @@ impl NodeRanksCalculator {
93100 let container_to_children = Self :: container_to_children_build ( node_nesting_infos) ;
94101
95102 // === Collect Dependency Edges === //
96- let dependency_edges = Self :: dependency_edges_collect ( edge_groups, entity_types) ;
103+ let dependency_edges =
104+ Self :: dependency_edges_collect ( edge_groups, entity_types, layout_edges) ;
97105
98106 // === Lift Edges to LCA Level === //
99107 let lca_level_edges = Self :: lca_level_edges_build ( & dependency_edges, node_nesting_infos) ;
@@ -225,23 +233,33 @@ impl NodeRanksCalculator {
225233 Some ( ( lca_container, divergent_from, divergent_to) )
226234 }
227235
228- /// Extracts dependency edges from edge groups, filtering out interaction
229- /// edges.
236+ /// Extracts dependency and layout edges that contribute to rank,
237+ /// filtering out interaction edges.
230238 ///
231- /// Returns a list of `(from_id, to_id)` pairs for dependency edges only.
239+ /// Returns a list of `(from_id, to_id)` pairs for dependency edges (from
240+ /// `edge_groups`, filtered by `entity_types`) and layout edges (passed in
241+ /// directly -- they have no backing edge group).
232242 fn dependency_edges_collect < ' id > (
233243 edge_groups : & EdgeGroups < ' id > ,
234244 entity_types : & EntityTypes < ' id > ,
245+ layout_edges : & [ Edge < ' id > ] ,
235246 ) -> Vec < ( NodeId < ' id > , NodeId < ' id > ) > {
236- edge_groups
247+ let dependency_group_edges = edge_groups
237248 . iter ( )
238249 . filter ( |( edge_group_id, _edge_group) | {
239250 Self :: edge_group_is_dependency ( edge_group_id. as_ref ( ) , entity_types)
240251 } )
241252 . flat_map ( |( _edge_group_id, edge_group) | edge_group. iter ( ) )
253+ . map ( |edge| ( edge. from . clone ( ) , edge. to . clone ( ) ) ) ;
254+
255+ let layout_edge_pairs = layout_edges
256+ . iter ( )
257+ . map ( |edge| ( edge. from . clone ( ) , edge. to . clone ( ) ) ) ;
258+
259+ dependency_group_edges
260+ . chain ( layout_edge_pairs)
242261 // Skip self-loops -- they don't affect rank.
243- . filter ( |edge| edge. from != edge. to )
244- . map ( |edge| ( edge. from . clone ( ) , edge. to . clone ( ) ) )
262+ . filter ( |( from, to) | from != to)
245263 . collect ( )
246264 }
247265
0 commit comments