From e6af9780de889f44880ca7007c8f9bd5848cffa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A9=BA=E7=99=BD?= <3440771474@qq.com> Date: Thu, 4 Sep 2025 09:56:13 +0800 Subject: [PATCH] Remove deprecated old layout algorithm implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes the legacy layout calculation logic that has been replaced by the new layout system. Signed-off-by: 空白MacNightwalk --- .../ModelVis/rust/layout/Cargo.toml | 10 - .../ModelVis/rust/layout/src/acyclic.rs | 199 -------- .../ModelVis/rust/layout/src/algo/mod.rs | 63 --- .../rust/layout/src/coordinate_system.rs | 75 --- .../ModelVis/rust/layout/src/graph/config.rs | 41 -- .../ModelVis/rust/layout/src/graph/key.rs | 83 ---- .../ModelVis/rust/layout/src/graph/mod.rs | 432 ------------------ .../rust/layout/src/graph/node_edge.rs | 129 ------ .../ModelVis/rust/layout/src/lib.rs | 73 --- .../ModelVis/rust/layout/src/nesting_graph.rs | 231 ---------- .../ModelVis/rust/layout/src/normalize.rs | 96 ---- .../layout/src/order/build_layer_graph.rs | 100 ---- .../rust/layout/src/order/cross_count.rs | 77 ---- .../rust/layout/src/order/init_order.rs | 47 -- .../ModelVis/rust/layout/src/order/mod.rs | 76 --- .../src/order/subgraph/add_constraints.rs | 53 --- .../layout/src/order/subgraph/barycenters.rs | 42 -- .../rust/layout/src/order/subgraph/context.rs | 70 --- .../rust/layout/src/order/subgraph/mod.rs | 64 --- .../src/order/subgraph/resolve_conflict.rs | 161 ------- .../rust/layout/src/order/subgraph/sort.rs | 92 ---- .../rust/layout/src/parent_dummy_chains.rs | 147 ------ .../rust/layout/src/position/align.rs | 107 ----- .../rust/layout/src/position/block_graph.rs | 117 ----- .../rust/layout/src/position/conflict.rs | 173 ------- .../rust/layout/src/position/context.rs | 113 ----- .../ModelVis/rust/layout/src/position/mod.rs | 43 -- .../ModelVis/rust/layout/src/position/xy.rs | 156 ------- .../rust/layout/src/rank/feasible_tree.rs | 70 --- .../rust/layout/src/rank/longest_path.rs | 71 --- .../ModelVis/rust/layout/src/rank/mod.rs | 38 -- .../rust/layout/src/rank/network_simplex.rs | 224 --------- .../ModelVis/rust/layout/src/rank/slack.rs | 27 -- .../ModelVis/rust/layout/src/selfedge.rs | 111 ----- .../ModelVis/rust/layout/src/utils.rs | 280 ------------ 35 files changed, 3891 deletions(-) delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/Cargo.toml delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/acyclic.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/algo/mod.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/coordinate_system.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/config.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/key.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/mod.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/node_edge.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/lib.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/nesting_graph.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/normalize.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/build_layer_graph.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/cross_count.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/init_order.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/mod.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/add_constraints.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/barycenters.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/context.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/mod.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/resolve_conflict.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/sort.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/parent_dummy_chains.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/align.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/block_graph.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/conflict.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/context.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/mod.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/xy.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/feasible_tree.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/longest_path.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/mod.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/network_simplex.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/slack.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/selfedge.rs delete mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/utils.rs diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/Cargo.toml b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/Cargo.toml deleted file mode 100644 index 34459ef3e8..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "layout" -version = "0.0.1" -edition = "2024" - -[dependencies] -mimalloc = { workspace = true } -smallvec = { version = "2.0.0-alpha.11", features = ["serde", "std"] } -ahash = { workspace = true, features = ["serde"] } -anyhow = { workspace = true } diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/acyclic.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/acyclic.rs deleted file mode 100644 index 78469c618d..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/acyclic.rs +++ /dev/null @@ -1,199 +0,0 @@ -//! Graph acyclification utilities for removing and restoring cycles. -//! -//! This module provides algorithms to convert cyclic graphs to DAGs (Directed Acyclic Graphs) -//! by finding feedback arc sets (FAS), with support for cycle restoration. Implemented algorithms: -//! - Depth-First Search (DFS) based FAS detection (`dfs_fas`) -//! - Greedy FAS algorithm (unimplemented placeholder) -//! -//! # Key Concepts -//! - **Feedback Arc Set**: Set of edges whose removal makes the graph acyclic -//! - **Edge Reversal**: Strategy to maintain graph connectivity while breaking cycles - -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::{HashMap, HashMapExt, HashSet, HashSetExt}; -use std::cmp::Reverse; - -use super::{Edge, Graph, Key}; -use crate::Acyclicer::*; - -impl Graph { - /// Converts the graph to a directed acyclic graph (DAG) by reversing edges. - /// - /// The algorithm used depends on the configured `acyclicer`: - /// - Greedy: Uses greedy heuristic (currently unimplemented) - /// - Default: Uses depth-first search (DFS) to find feedback arc set - /// - /// # Behavior - /// 1. Identifies feedback edges using selected algorithm - /// 2. Removes feedback edges from original orientation - /// 3. Reinserts edges in reversed orientation with `reversed` flag set - /// - /// # Notes - /// - Original graph structure can be restored with [`restore_cycles`] - /// - Modified edges maintain their metadata with `reversed` marker - pub(super) fn make_acyclic(&mut self) { - let Some(edges) = (match self.config.acyclicer { - Greedy => Some(self.greedy_fas()), - Dfs | NoAcyclicer => Some(self.dfs_fas()), - }) else { - return; - }; - - for edge in edges { - if let Some(mut graph_edge) = self.edge1(edge).cloned() { - self.remove_edge1(edge); - graph_edge.reversed = true; - self.set_edge(edge.target, edge.source, Some(graph_edge)); - } - } - } - - /// Finds feedback arc set using depth-first search traversal. - /// - /// # Algorithm - /// 1. Maintains two visitation states: global and current traversal - /// 2. Tracks back edges during DFS that connect to already-visited nodes - /// 3. Collects these back edges as the feedback arc set - /// - /// # Complexity - /// - Time: O(V + E) - /// - Space: O(V) - fn dfs_fas(&mut self) -> Vec { - let mut fas: Vec = vec![]; - let mut visited: HashSet = HashSet::new(); - /// (node_id, out_edges, edge_index) - let mut stack: Vec<(Key, Vec, usize)> = vec![]; - - for &key in self.nodes.keys() { - if visited.contains(&key) { - continue; - } - - let mut local_stack: Vec = vec![key]; - let mut local_visited: HashSet = HashSet::new(); - local_visited.insert(key); - - while let Some(current_node) = local_stack.pop() { - if visited.insert(current_node) { - let out_edges = self.out_edges(¤t_node); - stack.push((current_node, out_edges, 0)); - } - - if let Some((_, out_edges, edge_index)) = stack.last_mut() { - if *edge_index < out_edges.len() { - let edge = out_edges[*edge_index]; - *edge_index += 1; - match local_visited.insert(edge.target) { - true => local_stack.push(edge.target), - false => fas.push(edge), - } - } else { - stack.pop(); - } - } - } - } - - fas - } - - /// Restores graph to the original cyclic state - /// by reversing previously modified edges. - /// - /// # Operation - /// 1. Scans all edges in graph - /// 2. Flip any edge with `reversed = true` back to original orientation - /// 3. Maintains all edge attributes during reversal - /// - /// # Invariants - /// - After execution, all edges will have `reversed = false` - /// - Graph topology returns to pre-acyclification state - pub(super) fn restore_cycles(&mut self) -> Option<()> { - for e in self.edges() { - let edge = self.edge_mut1(e)?; - if edge.reversed { - let mut label = edge.clone(); - label.reversed = false; - self.set_edge(e.target, e.source, Some(label)); - } - } - - None - } - - fn greedy_fas(&mut self) -> Vec { - let mut in_deg: HashMap = HashMap::new(); - let mut out_deg: HashMap = HashMap::new(); - let mut src_edges: HashMap> = HashMap::new(); - - for edge in self.edges() { - *out_deg.entry(edge.source).or_insert(0) += 1; - *in_deg.entry(edge.target).or_insert(0) += 1; - src_edges.entry(edge.source).or_default().push(edge); - } - - let mut nodes: Vec = self.nodes.keys().copied().collect(); - nodes.sort_by_cached_key(|&n| { - let out = out_deg.get(&n).unwrap_or(&0); - let ins = in_deg.get(&n).unwrap_or(&0); - ((*out as isize - *ins as isize).abs(), Reverse(n)) - }); - - let mut fas = Vec::new(); - let mut removed = HashSet::new(); - - while let Some(node) = nodes.pop() { - if removed.contains(&node) || !out_deg.contains_key(&node) { - continue; - } - - let out = *out_deg.get(&node).unwrap_or(&0); - let ins = *in_deg.get(&node).unwrap_or(&0); - - if out > ins { - if let Some(edges) = src_edges.get(&node) { - for edge in edges { - if !removed.contains(&edge.target) { - fas.push(*edge); - in_deg.entry(edge.target).and_modify(|x| *x -= 1); - } - } - } - } else { - for edge in self.in_edges(&node) { - if !removed.contains(&edge.source) { - fas.push(edge); - out_deg.entry(edge.source).and_modify(|x| *x -= 1); - } - } - } - - removed.insert(node); - out_deg.remove(&node); - in_deg.remove(&node); - - nodes.sort_by_cached_key(|&n| { - let out = out_deg.get(&n).unwrap_or(&0); - let ins = in_deg.get(&n).unwrap_or(&0); - ((*out as isize - *ins as isize).abs(), Reverse(n)) - }); - } - - fas - } - -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/algo/mod.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/algo/mod.rs deleted file mode 100644 index 619096132e..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/algo/mod.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::{HashSetExt, HashSet}; - -use crate::{Graph, Key}; - -impl Graph { - #[inline] - pub(super) fn preorder(&self, keys: &[Key]) -> Vec { - self.traverse(keys, false) - } - - #[inline] - pub(super) fn postorder(&self, keys: &[Key]) -> Vec { - self.traverse(keys, true) - } - - fn traverse(&self, keys: &[Key], postorder: bool) -> Vec { - let mut acc: Vec = Vec::with_capacity(keys.len() * 2); - let mut visited: HashSet = HashSet::new(); - let mut stack = vec![]; - - for &key in keys { - if visited.contains(&key) { - continue; - } - stack.push(key); - - while let Some(curr) = stack.pop() { - if visited.insert(curr) { - if !postorder { - acc.push(curr); - } - - let mut neighbors = self.navigation(&curr); - if !postorder { - neighbors.reverse(); - } - stack.extend(neighbors); - } - } - } - - if postorder { - acc.reverse(); - } - - acc - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/coordinate_system.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/coordinate_system.rs deleted file mode 100644 index 8630a3a210..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/coordinate_system.rs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::mem; - -use crate::{graph::Graph, RankDir::*}; - -impl Graph { - pub fn coordinate_adjust(&mut self) { - let rank_dir = self.config.rankdir; - - if rank_dir == LR || rank_dir == RL { - self.swap_width_height(); - } - } - - pub fn undo_coordinate_adjust(&mut self) { - let rank_dir = self.config.rankdir; - - if rank_dir == BT || rank_dir == RL { - self.reverse_y(); - } - - if rank_dir == LR || rank_dir == RL { - self.swap_xy(); - self.swap_width_height(); - } - } - - fn swap_width_height(&mut self) { - for node in self.nodes.values_mut() { - mem::swap(&mut node.width, &mut node.height); - } - } - - fn reverse_y(&mut self) { - for node in self.nodes.values_mut() { - node.y = -node.y; - } - - for edge in self.edge_values.values_mut() { - if let Some(points) = &mut edge.points { - for point in points { - point.y = -point.y; - } - } - } - } - - fn swap_xy(&mut self) { - for node in self.nodes.values_mut() { - mem::swap(&mut node.x, &mut node.y); - } - - for edge in self.edge_values.values_mut() { - if let Some(points) = &mut edge.points { - for point in points { - mem::swap(&mut point.x, &mut point.y); - } - } - } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/config.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/config.rs deleted file mode 100644 index b8222df923..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/config.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::{Acyclicer, Acyclicer::NoAcyclicer, RankDir, RankDir::TB, Ranker, Ranker::TightTree}; - -#[derive(Debug, Copy, Clone)] -pub struct GraphConfig { - pub nodesep: f32, - pub edgesep: f32, - pub ranksep: f32, - pub rankdir: RankDir, - pub acyclicer: Acyclicer, - pub ranker: Ranker, - pub node_rank_factor: f32, -} - -impl Default for GraphConfig { - fn default() -> Self { - Self { - nodesep: 20.0, - edgesep: 20.0, - ranksep: 20.0, - rankdir: TB, - acyclicer: NoAcyclicer, - ranker: TightTree, - node_rank_factor: 0.0, - } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/key.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/key.rs deleted file mode 100644 index b8576f7cbc..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/key.rs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/// ## Performance -/// #### String -/// it's challenging to avoid a large number of clone operations, -/// and performance deteriorates sharply. -/// #### SmartString -/// [`SmartString::clone`] usually copies 24 bytes with [`clone`] everywhere. -/// In addition, [`SmartString`] doesn't derive [`Copy`] trait. -/// #### usize -/// only 8 bytes need to be copied. -/// All parameters are passed by value, -/// and the number of clones in the code is greatly reduced. -/// At the same time, most struct can also derive the [`Copy`] trait. -pub type Key = usize; - -#[inline] -pub fn normalize_st(s: Key, t: Key) -> (Key, Key) { - if t < s { (t, s) } else { (s, t) } -} - -pub trait KeyCodecExt { - fn of(source: Key, target: Key) -> Key; - - fn source(self) -> Key; - - fn target(self) -> Key; - - fn decode(self) -> (Key, Key); -} - -/// # Memory Layout -/// -/// The **Key** is represented using 64 bits, with the following layout: -/// -/// | Field | Size (bits) | Description | -/// |-----------------|-------------|--------------------------------------------------| -/// | Reserved | 16 | Reserved and unused.| -/// | Usable | 48 | Used to store keys.| -/// | | | - For `NodeKey`, it occupies the higher 24 bits. | -/// | | | - For `EdgeKey`, source and target each occupy 24 bits.| -/// -/// This allows handling up to 16 million nodes, which is enough for all scenarios. -impl KeyCodecExt for Key { - #[inline] - fn of(source: Key, target: Key) -> Key { - source.wrapping_shl(24) + target - } - - #[inline] - fn source(self) -> Key { - self >> 24 - } - - #[inline] - fn target(self) -> Key { - self & 0xFFFFFF - } - - #[inline] - fn decode(self) -> (Key, Key) { - let s = self >> 24; - let t = self & 0xFFFFFF; - - (s, t) - } -} - -pub const EMPTY_KEY: Key = 1 << 48; -pub const EMPTY_ROOT: Key = EMPTY_KEY + 1; diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/mod.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/mod.rs deleted file mode 100644 index e3a09e01ce..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/mod.rs +++ /dev/null @@ -1,432 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod config; -mod key; -mod node_edge; - -use std::{cmp::PartialEq, fmt::Debug}; - -use ahash::{HashMap, HashMapExt}; -pub use config::*; -pub use key::*; -pub use node_edge::*; - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct Edge { - pub source: Key, - pub target: Key, -} - -impl Edge { - #[inline] - pub fn of(source: Key, target: Key) -> Self { - Self { source, target } - } - - #[inline] - pub fn to_key(self) -> Key { - Key::of(self.source, self.target) - } -} - -/// ### Compiler -/// Currently all hashMaps are stored in this structure. -/// -/// It's challenging to avoid -/// **can't borrow as mutable while borrowed as immutable**. -/// -/// ### Performance -/// Currently, almost all data is stored in [`HashMap`]. -/// There may be a better solution. -/// Node's Key is usize, which is naturally an array index. -/// If store all Nodes in Vec, -/// which can achieve extremely high performance. -/// -/// At the same time, there are some points to note: -/// -/// 1. Vec should store [`Option`] instead of Node, -/// or add an [`is_deleted`] field to Node, -/// so that better cache affinity and performance can be obtained, -/// and the code is more concise. -/// -/// 2. Under the premise of the first point, -/// we can convert all insert operations into push, -/// so that there's no [`Memory-Move`]. -/// -/// 3. Implement [`Indexing`] is needed -/// while the expansion of 10w+ Nodes is a disaster. -/// In our scenario, only one level of indexing is needed. -/// The maximum capacity of each array can be 1024. -/// Use [`LinkedList`] to store these arrays. -/// -/// 4. After implementing [`Indexing`] -/// almost all operations have a [`branch-judgment`]. -/// We should think of some ways to eliminate this performance consumption. -/// Cumbersome and repetitive code is also acceptable. -/// Or use [`likely-unlikely`] to prompt the compiler -#[derive(Debug, Default)] -pub struct Graph { - pub is_directed: bool, - pub is_compound: bool, - pub config: GraphConfig, - pub width: f32, - pub height: f32, - pub nodes: HashMap, - pub in_map: HashMap>, - pub predecessors: HashMap>, - pub out_map: HashMap>, - pub successors: HashMap>, - pub edges: HashMap, - pub edge_values: HashMap, - pub parent_map: HashMap, - pub children_map: HashMap>, - pub selfedge_map: HashMap>, - pub nesting_root: Option, - pub root: Option, - pub dummy_chains: Option>, -} - -impl Graph { - #[inline] - fn of() -> Self { - Self { is_directed: true, ..Self::default() } - } -} - -impl Graph { - #[inline] - pub fn new(directed: bool, compound: bool) -> Self { - let mut graph = Self::of(); - - graph.is_directed = directed; - graph.is_compound = compound; - - if compound { - graph.parent_map = HashMap::new(); - graph.children_map = HashMap::new(); - graph.children_map.insert(EMPTY_ROOT, vec![]); - } - - graph - } - - #[inline] - pub fn nodes(&self) -> Vec { - self.nodes.keys().copied().collect() - } - - #[inline] - pub fn sources(&self) -> Vec { - self.nodes - .keys() - .filter(|&n| self.in_map.get(n).map_or(true, |m| m.is_empty())) - .copied() - .collect() - } - - #[inline] - pub fn sinks(&self) -> Vec { - self.nodes - .keys() - .filter(|&n| self.out_map.get(n).map_or(true, |m| m.is_empty())) - .copied() - .collect() - } - - #[inline] - pub fn set_node(&mut self, key: Key, value: Option) -> &mut Self { - if self.nodes.contains_key(&key) { - value.map(|new_node| self.nodes.insert(key, new_node)); - return self; - } - - self.nodes.insert(key, value.unwrap_or_default()); - - if self.is_compound { - self.parent_map.insert(key, EMPTY_ROOT); - self.children_map.insert(key, vec![]); - self.children_map.entry(EMPTY_ROOT).or_default(); - } - - self.in_map.insert(key, vec![]); - self.predecessors.insert(key, HashMap::new()); - self.out_map.insert(key, vec![]); - self.successors.insert(key, HashMap::new()); - - self - } - - #[inline] - pub fn node(&self, id: &Key) -> Option<&GraphNode> { - self.nodes.get(id) - } - - #[inline] - pub fn node_mut(&mut self, id: &Key) -> Option<&mut GraphNode> { - self.nodes.get_mut(id) - } - - #[inline] - pub fn has_node(&self, id: &Key) -> bool { - self.nodes.contains_key(id) - } - - pub fn remove_node(&mut self, id: &Key) { - if let Some(_) = self.nodes.remove(id) { - if self.is_compound { - self.remove_from_parents_child_list(id); - self.parent_map.remove(id); - for child_id in self.children(id) { - self.set_parent(child_id, None); - } - self.children_map.remove(id); - } - - self.in_map.remove(id).map(|in_edges| { - for edge in in_edges { - self.remove_edge1(edge); - } - }); - - self.predecessors.remove(id); - - self.out_map.remove(id).map(|out_edges| { - for edge in out_edges { - self.remove_edge1(edge); - } - }); - - self.successors.remove(id); - } - } - - pub fn set_parent(&mut self, id: Key, parent: Option) -> &mut Self { - let ancestor = match parent { - Some(p) => { - let mut current = p; - while let Some(new_ancestor) = self.parent(¤t) { - current = new_ancestor; - } - current - } - None => EMPTY_ROOT, - }; - - self.set_node(id, None); - self.remove_from_parents_child_list(&id); - self.parent_map.insert(id, ancestor); - self.children_map.entry(ancestor).or_default().push(id); - - self - } - - #[inline] - fn remove_from_parents_child_list(&mut self, id: &Key) { - self.parent_map - .get(id) - .map(|p| self.children_map.get_mut(p).map(|c| c.retain(|c| c != id))); - } - - #[inline] - pub fn parent(&self, id: &Key) -> Option { - self.is_compound - .then(|| self.parent_map.get(id).filter(|&&p| p != EMPTY_ROOT).copied())? - } - - pub fn children(&self, id: &Key) -> Vec { - match (self.is_compound, id == &EMPTY_ROOT) { - (true, _) => self - .children_map - .get(id) - .map_or(vec![], |children| children.iter().copied().collect()), - (false, true) => self.nodes.keys().copied().collect(), - _ => vec![], - } - } - - #[inline] - pub fn predecessors(&self, id: &Key) -> Vec { - self.predecessors.get(id).unwrap().keys().copied().collect() - } - - #[inline] - pub fn successors(&self, id: &Key) -> Vec { - self.successors.get(id).unwrap().keys().copied().collect() - } - - #[inline] - pub fn neighbors(&self, id: &Key) -> Vec { - let mut ret = self.predecessors(id); - ret.extend(self.successors(id)); - ret - } - - #[inline] - pub fn navigation(&self, id: &Key) -> Vec { - if self.is_directed { self.successors(id) } else { self.neighbors(id) } - } - - #[inline] - pub fn edges(&self) -> Vec { - self.edges.values().copied().collect() - } - - pub fn set_edge(&mut self, source: Key, target: Key, edge: Option) -> &mut Self { - let key = Key::of(source, target); - if self.edge_values.contains_key(&key) { - if let Some(edge) = edge { - self.edge_values.insert(key, edge); - } - return self; - } - - self.set_node(source, None); - self.set_node(target, None); - - if let Some(mut edge) = edge { - edge.source = source; - edge.target = target; - self.edge_values.insert(key, edge); - } else { - self.edge_values.insert(key, GraphEdge::of(source, target)); - } - - let edge = Edge::of(source, target); - - self.edges.insert(key, edge); - if let Some(preds) = self.predecessors.get_mut(&target) { - preds.entry(source).and_modify(|c| *c += 1).or_insert(1); - } - if let Some(succ) = self.successors.get_mut(&source) { - succ.entry(target).and_modify(|c| *c += 1).or_insert(1); - } - - self.in_map.entry(target).or_default().push(edge); - self.out_map.entry(source).or_default().push(edge); - - self - } - - #[inline] - pub fn set_edge_undirected( - &mut self, - source: Key, - target: Key, - edge: Option, - ) -> &mut Self { - let (source, target) = normalize_st(source, target); - - self.set_edge(source, target, edge) - } - - #[inline] - pub fn edge(&self, source: Key, target: Key) -> Option<&GraphEdge> { - let key = Key::of(source, target); - self.edge_values.get(&key) - } - - #[inline] - pub fn edge_mut(&mut self, source: Key, target: Key) -> Option<&mut GraphEdge> { - let key = Key::of(source, target); - self.edge_values.get_mut(&key) - } - - #[inline] - pub fn has_edge(&self, source: Key, target: Key) -> bool { - let key = Key::of(source, target); - self.edge_values.contains_key(&key) - } - - pub fn remove_edge(&mut self, source: Key, target: Key) -> &mut Self { - let key = Key::of(source, target); - - if let Some(edge) = self.edges.get(&key) { - let s = &edge.source; - let t = &edge.target; - - if let Some(in_edges) = self.in_map.get_mut(t) { - in_edges.retain(|e| e != edge) - } - if let Some(out_edges) = self.out_map.get_mut(s) { - out_edges.retain(|e| e != edge) - } - - if let Some(pred) = self.predecessors.get_mut(t) { - decrement_or_remove(pred, &s) - } - - if let Some(suc) = self.successors.get_mut(s) { - decrement_or_remove(suc, &t) - } - - self.edge_values.remove(&key); - self.edges.remove(&key); - } - - self - } - - #[inline] - pub fn edge1(&self, edge: Edge) -> Option<&GraphEdge> { - self.edge(edge.source, edge.target) - } - - #[inline] - pub fn edge_mut1(&mut self, edge: Edge) -> Option<&mut GraphEdge> { - self.edge_mut(edge.source, edge.target) - } - - #[inline] - pub fn set_edge1( - &mut self, - Edge { source, target }: Edge, - edge: Option, - ) -> &mut Self { - self.set_edge(source, target, edge) - } - - #[inline] - pub fn remove_edge1(&mut self, edge: Edge) -> &mut Self { - self.remove_edge(edge.source, edge.target) - } - - #[inline] - pub fn in_edges(&self, key: &Key) -> Vec { - self.in_map[key].clone() - } - - #[inline] - pub fn out_edges(&self, key: &Key) -> Vec { - self.out_map[key].clone() - } - - #[inline] - pub fn node_edges(&self, key: &Key) -> Vec { - let mut ret = self.in_edges(key); - ret.extend(self.out_edges(key)); - ret - } -} - -#[inline] -fn decrement_or_remove(map: &mut HashMap, k: &Key) { - if let Some(value) = map.get_mut(k) { - *value -= 1; - if *value <= 0 { - map.remove(k); - } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/node_edge.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/node_edge.rs deleted file mode 100644 index 1c35530760..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/node_edge.rs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use smallvec::SmallVec; - -use super::{Edge, Key, EMPTY_KEY}; - -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum Dummy { - Root, - Border, - Edge, - EdgeProxy, - SelfEdge, -} - -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum Ranker { - TightTree, - LongestPath, - NetworkSimplex, -} - -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum RankDir { - LR, - RL, - TB, - BT, -} - -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum Acyclicer { - Greedy, - Dfs, - NoAcyclicer, -} - -#[derive(Debug, Copy, Clone, Default)] -pub struct GraphNode { - pub x: f32, - pub y: f32, - pub width: f32, - pub height: f32, - pub dummy: Option, - pub rank: Option, - pub min_rank: Option, - pub max_rank: Option, - pub order: Option, - pub border_top: Option, - pub border_bottom: Option, - pub low: Option, - pub lim: Option, - pub parent: Option, - pub edge: Option, -} - -impl GraphNode { - pub fn of(x: f32, y: f32, width: f32, height: f32) -> Self { - Self { x, y, width, height, ..GraphNode::default() } - } -} - -#[derive(Debug, Copy, Clone, Default)] -pub struct Point { - pub x: f32, - pub y: f32, -} - -impl Point { - #[inline] - pub fn of(x: f32, y: f32) -> Self { - Self { x, y } - } - - #[inline] - pub fn from(node: &GraphNode) -> Self { - Self { x: node.x, y: node.y } - } -} - -#[derive(Debug, Clone)] -pub struct GraphEdge { - pub source: Key, - pub target: Key, - pub reversed: bool, - pub minlen: Option, - pub weight: Option, - pub rank: Option, - pub nesting: bool, - pub cutvalue: Option, - /// Move this field out of the structure and manage it uniformly, - /// so GraphEdge can derive the Copy trait - pub points: Option>, -} - -impl Default for GraphEdge { - fn default() -> Self { - Self { - source: EMPTY_KEY, - target: EMPTY_KEY, - reversed: false, - minlen: Some(1), - weight: Some(1.0), - rank: None, - nesting: false, - cutvalue: None, - points: None, - } - } -} - -impl GraphEdge { - pub fn of(source: Key, target: Key) -> Self { - Self { source, target, ..Self::default() } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/lib.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/lib.rs deleted file mode 100644 index bf91c16a29..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/lib.rs +++ /dev/null @@ -1,73 +0,0 @@ -#![feature(let_chains)] -#![allow(unused_doc_comments)] - -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod acyclic; -mod algo; -mod coordinate_system; -pub mod graph; -mod nesting_graph; -mod normalize; -mod order; -mod parent_dummy_chains; -mod position; -mod rank; -mod selfedge; -mod utils; - -pub use graph::*; -use mimalloc::MiMalloc; -use utils::*; - -/// ### Performance -/// When there are many nodes, most of the performance consumption -/// is Vec, HashMap memory allocation and memory transfer when expanding. -/// -/// And it's often memory allocation of very large objects. -/// -/// [`mimalloc`] is also challenging to achieve large performance improvement, -/// but at the initial stage of expansion, it can Improve performance -#[global_allocator] -static GLOBAL: MiMalloc = MiMalloc; - -pub fn layout(graph: &mut Graph) { - graph.make_space_for_edge_labels(); - graph.remove_self_edges(); - graph.make_acyclic(); - graph.nesting_run(); - let mut ncg: Graph = graph.as_non_compound(); - ncg.rank(); - ncg.transfer_node_edges(graph); - graph.remove_empty_ranks(); - graph.nesting_cleanup(); - graph.normalize_ranks(); - graph.assign_rank_min_max(); - graph.remove_edge_proxies(); - graph.normalize(); - graph.parent_dummy_chains(); - graph.order(); - graph.insert_self_edges(); - graph.coordinate_adjust(); - graph.position(); - graph.position_self_edges(); - graph.denormalize(); - graph.undo_coordinate_adjust(); - graph.translate_graph(); - graph.assign_node_intersects(); - graph.reverse_points_for_reversed_edges(); - graph.restore_cycles(); -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/nesting_graph.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/nesting_graph.rs deleted file mode 100644 index 18d26d256f..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/nesting_graph.rs +++ /dev/null @@ -1,231 +0,0 @@ -//! ### Idea from Sander's "Layout of Compound Directed Graphs." -//! -//! A nesting graph creates dummy nodes for the tops and bottoms of subgraphs, -//! adds appropriate edges to ensure that all cluster nodes are placed between -//! these boundaries, and ensures that the graph is connected. -//! -//! In addition, through the use of the minlen property, that nodes -//! and subgraph border nodes to not end up on the same rank. -//! -//! Pre-Conditions: -//! -//! 1. Input graph is a DAG -//! 2. Nodes in the input graph has a minlen attribute -//! -//! Post-Conditions: -//! -//! 1. The Input graph is connected. -//! 2. Dummy nodes are added for the tops and bottoms of subgraphs. -//! 3. The minlen attribute for nodes is adjusted to ensure nodes do not -//! get placed on the same rank as subgraph border nodes. - -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::{HashMap, HashMapExt}; - -use crate::{ - Dummy::{Border, Root}, - EMPTY_ROOT, Graph, GraphEdge, GraphNode, Key, -}; - -impl Graph { - pub(super) fn nesting_run(&mut self) { - let root = self.init_nesting(); - let node_sep = self.calculate_node_sep(); - self.adjust_edge_minlen(node_sep); - self.process_children(root, node_sep); - self.config.node_rank_factor = node_sep as f32; - } - - pub(super) fn nesting_cleanup(&mut self) { - if let Some(root) = &self.nesting_root.clone() { - self.remove_node(root); - } - self.nesting_root = None; - - for edge in self.edges() { - if let Some(ge) = self.edge1(edge) { - ge.nesting.then(|| self.remove_edge1(edge)); - } - } - } - - fn init_nesting(&mut self) -> Key { - let root = self.add_dummy_node(Root, GraphNode::default()); - self.nesting_root = Some(root); - root - } - - fn calculate_node_sep(&self) -> i32 { - let (_, max_height) = self.tree_depths(); - (2 * max_height + 1) as i32 - } - - fn adjust_edge_minlen(&mut self, node_sep: i32) { - for edge in self.edge_values.values_mut() { - edge.minlen = Some(edge.minlen.unwrap() * node_sep); - } - } - - fn process_children(&mut self, root: Key, node_sep: i32) { - let weight = self.sum_weights() + 1.0; - let mut stack: Vec = self.children(&EMPTY_ROOT).into_iter().collect(); - - let (depths, max_height) = self.tree_depths(); - - while let Some(key) = stack.pop() { - if self.children(&key).is_empty() { - if key != root { - self.set_edge(root, key, Some(GraphEdge::with_minlen(node_sep))); - } - continue; - } - - let (top, bottom) = self.link_border_nodes(key); - self.update_node_borders(key, top, bottom); - - for k in self.children(&key) { - let (child_top, child_bottom, this_weight) = self.nodes[&k].top_bottom(k, weight); - let minlen = - if child_top == child_bottom { max_height - depths[&k] + 1 } else { 1 }; - self.add_border_edge(minlen, this_weight, top, bottom, child_top, child_bottom); - stack.push(k); - } - - if self.parent(&key).is_none() { - self.set_edge( - root, - top, - Some(GraphEdge::with_minlen_nesting(depths[&key] + max_height)), - ); - } - } - } - - fn link_border_nodes(&mut self, key: Key) -> (Key, Key) { - let top = self.add_border_node(); - let bottom = self.add_border_node(); - self.set_parent(top, Some(key)); - self.set_parent(bottom, Some(key)); - (top, bottom) - } - - fn update_node_borders(&mut self, key: Key, top: Key, bottom: Key) { - if let Some(node) = self.node_mut(&key) { - node.border_top = Some(top); - node.border_bottom = Some(bottom); - } - } -} - -impl GraphEdge { - fn with_minlen(minlen: i32) -> Self { - GraphEdge { minlen: Some(minlen), weight: Some(0.0), ..GraphEdge::default() } - } - - fn with_minlen_nesting(minlen: usize) -> Self { - GraphEdge { - minlen: Some(minlen as i32), - weight: Some(0.0), - nesting: true, - ..GraphEdge::default() - } - } -} - -impl Graph { - fn tree_depths(&self) -> (HashMap, usize) { - let mut depths: HashMap = HashMap::new(); - let mut stack: Vec<(Key, usize)> = Vec::new(); - let mut max_depth: usize = 0; - - for node_id in self.children(&EMPTY_ROOT) { - stack.push((node_id, 1)); - } - - while let Some((node_id, depth)) = stack.pop() { - for child_id in self.children(&node_id) { - stack.push((child_id, depth + 1)); - } - depths.insert(node_id, depth); - max_depth = max_depth.max(depth); - } - - if max_depth > 0 { - max_depth -= 1; - } - - (depths, max_depth) - } - - fn sum_weights(&self) -> f32 { - let mut sum_weight: f32 = 0.0; - - for edge in self.edge_values.values() { - if let Some(weight) = edge.weight { - sum_weight += weight; - } - } - - sum_weight - } -} - -impl Graph { - fn add_border_node(&mut self) -> Key { - self.add_dummy_node(Border, GraphNode::default()) - } - - fn add_border_edge( - &mut self, - minlen: usize, - weight: f32, - top: Key, - bottom: Key, - child_top: Key, - child_bottom: Key, - ) { - let top_edge = GraphEdge { - minlen: Some(minlen as i32), - weight: Some(weight), - nesting: true, - ..GraphEdge::default() - }; - self.set_edge(top, child_top, Some(top_edge)); - - let bottom_edge = GraphEdge { - minlen: Some(minlen as i32), - weight: Some(weight), - nesting: true, - ..GraphEdge::default() - }; - self.set_edge(child_bottom, bottom, Some(bottom_edge)); - } -} - -impl GraphNode { - fn top_bottom(&self, key: Key, weight: f32) -> (Key, Key, f32) { - let (top, this_weight) = if let Some(border_top) = self.border_top { - (border_top, 2.0 * weight) - } else { - (key, weight) - }; - - let bottom = if let Some(border_bottom) = self.border_bottom { border_bottom } else { key }; - - (top, bottom, this_weight) - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/normalize.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/normalize.rs deleted file mode 100644 index 36e598960e..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/normalize.rs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use smallvec::smallvec; - -use crate::{Dummy, Dummy::EdgeProxy, Edge, Graph, GraphEdge, GraphNode, Point, EMPTY_KEY}; - -impl Graph { - pub(super) fn normalize(&mut self) { - self.dummy_chains = Some(vec![]); - for edge in self.edges() { - self.normalize_edge(edge); - } - } - - fn normalize_edge(&mut self, e: Edge) -> Option<()> { - let mut s = e.source; - let t = e.target; - let mut s_rank = self.node(&s)?.rank?; - let t_rank = self.node(&t)?.rank?; - - let edge = self.edge_mut1(e)?; - edge.points = Some(smallvec![]); - let weight = edge.weight; - let rank = edge.rank.unwrap_or(0); - - self.remove_edge1(e); - - let mut i = 0; - s_rank += 1; - while s_rank < t_rank { - let mut dummy_node = - GraphNode { edge: Some(e), rank: Some(s_rank), ..GraphNode::default() }; - if s_rank == rank { - dummy_node.dummy = Some(EdgeProxy); - } - let dummy_id = self.add_dummy_node(Dummy::Edge, dummy_node); - let dummy_edge = GraphEdge { weight, ..GraphEdge::default() }; - self.set_edge(s, dummy_id, Some(dummy_edge)); - if i == 0 { - let dummy_chains = &mut self.dummy_chains; - dummy_chains.get_or_insert(vec![]).push(dummy_id); - } - s = dummy_id; - i += 1; - s_rank += 1; - } - - let graph_edge = GraphEdge { weight, ..GraphEdge::default() }; - self.set_edge(s, t, Some(graph_edge)); - - None - } - - pub(super) fn denormalize(&mut self) -> Option<()> { - if let Some(dummy_chains) = self.dummy_chains.clone() { - for &dummy_id in &dummy_chains { - let Some(mut node) = self.node(&dummy_id).copied() else { - continue; - }; - - let edge_obj = node.edge?; - let mut prev_edge = self - .edge1(edge_obj) - .cloned() - .unwrap_or(GraphEdge { points: Some(smallvec![]), ..GraphEdge::default() }); - let mut curr_dummy = dummy_id; - while node.dummy.is_some() { - let new_dummy = - self.successors(&curr_dummy).first().copied().unwrap_or(EMPTY_KEY); - self.remove_node(&curr_dummy); - prev_edge.points.as_mut()?.push(Point::of(node.x, node.y)); - - curr_dummy = new_dummy; - node = self.node(&curr_dummy).cloned()?; - } - - self.set_edge1(edge_obj, Some(prev_edge)); - } - } - - None - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/build_layer_graph.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/build_layer_graph.rs deleted file mode 100644 index a4e84755a1..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/build_layer_graph.rs +++ /dev/null @@ -1,100 +0,0 @@ -//! Constructs a graph that can be used to sort a layer of nodes. -//! The graph contains all base and subgraph nodes from the request -//! layer in their original hierarchy and any edges -//! that are incident on these nodes and is of the type -//! requested by the "relationship" parameter. -//! -//! Nodes from the requested rank that don't have parents are assigned a root -//! node in the output graph, which is set in the root graph attribute. -//! This makes it easy to walk the hierarchy of movable nodes during ordering. -//! -//! Pre-conditions: -//! -//! 1. Input graph is a DAG -//! 2. Base nodes in the input graph have rank attribute -//! 3. Subgraph nodes in the input graph have minRank and maxRank attributes -//! 4. Edges have an assigned weight -//! -//! Post-conditions: -//! -//! 1. The Output graph has all nodes in the movable rank with preserved -//! hierarchy. -//! 2. Root nodes in the movable layer are made children of the node -//! indicated by the root attribute of the graph. -//! 3. Non-movable nodes incident on movable nodes, selected by the -//! relationship parameter, are included in the graph without a hierarchy. -//! 4. Edges incident on movable nodes, selected by the relationship -//! parameter, are added to the output graph. -//! 5. The weights for copied edges are aggregated as needed, since the output -//! graph isn't a multi-graph. - -use crate::{unique_key, Graph, GraphEdge, GraphNode, Key}; - -#[allow(dead_code)] -#[derive(Debug, Copy, Clone, PartialEq)] -pub(super) enum EdgeRelation { - In, - Out, -} - -impl Graph { - pub(super) fn build_layer_graph(&mut self, rank: i32, relation: EdgeRelation) -> Graph { - let root = self.create_root_node(); - let mut lg = Graph::new(true, false); - lg.root = Some(root); - - for (&key, &node) in &self.nodes { - if node.is_in_rank(Some(rank)) { - lg.set_node(key, Some(node)); - lg.set_layer_parent(key, self.parent(&key), root); - - self.process_relations(&mut lg, key, relation); - } - } - - lg - } - - fn set_layer_parent(&mut self, key: Key, parent: Option, root: Key) { - match parent { - Some(p) => self.set_parent(key, Some(p)), - _ => self.set_parent(key, Some(root)), - }; - } - - fn process_relations(&self, lg: &mut Graph, key: Key, relation: EdgeRelation) -> Option<()> { - let edges = match relation { - EdgeRelation::In => &self.in_map[&key], - EdgeRelation::Out => &self.out_map[&key], - }; - for &edge in edges { - let source = if edge.source == key { edge.target } else { edge.source }; - let weight = lg.edge(source, key).and_then(|e| e.weight).unwrap_or(0.0); - let new_weight = self.edge1(edge)?.weight.unwrap_or(0.0) + weight; - lg.set_edge( - source, - key, - Some(GraphEdge { weight: Some(new_weight), ..Default::default() }), - ); - } - - None - } - - fn create_root_node(&self) -> Key { - loop { - let key = unique_key(); - - if !self.has_node(&key) { - return key; - } - } - } -} - -impl GraphNode { - #[inline] - fn is_in_rank(&self, rank: Option) -> bool { - self.rank == rank || self.min_rank <= rank && rank <= self.max_rank - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/cross_count.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/cross_count.rs deleted file mode 100644 index a390e804ad..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/cross_count.rs +++ /dev/null @@ -1,77 +0,0 @@ -//! ### Algorithm derived from Barth et al., "Bilayer Cross Counting" -//! A function that takes a layering (an array of layers, each with an array of -//! ordered nodes) and a graph and returns a weighted crossing count. -//! -//! Pre-Conditions: -//! -//! 1. Input graph must be non-multigraph, directed, and include only simple edges. -//! 2. Edges in the input graph must have assigned weights. -//! -//! Post-Conditions: -//! -//! 1. The graph and layering matrix are left unchanged. - -use ahash::HashMap; - -use crate::{Graph, Key}; - -impl Graph { - pub fn cross_count(&mut self, matrix: &mut [Vec]) -> usize { - let mut count = 0; - - /// Sort all the edges between the north and south layers by their position - /// in the north layer and then the south. - /// Map these edges to the position of their head in the south layer. - for idx in 1..matrix.len() { - let north_idx = idx - 1; - let south_idx = idx; - - let south_layer = &matrix[south_idx]; - let south_pos: HashMap = - south_layer.iter().enumerate().map(|(idx, val)| (*val, idx)).collect(); - - let mut south_entries: Vec<(usize, usize)> = matrix[north_idx] - .iter() - .flat_map(|k| { - self.out_map[k] - .iter() - .map(|&e| { - let pos = south_pos[&e.target]; - let weight = self.edge1(e).unwrap().weight.unwrap(); - (pos, weight as usize) - }) - .collect::>() - }) - .collect(); - - south_entries.sort_by_key(|e| e.0); - - let mut first_index = south_layer.len().next_power_of_two(); - - let tree_size = 2 * first_index - 1; - first_index -= 1; - - let mut tree = vec![0; tree_size]; - let mut c = 0; - - for &(f, s) in &south_entries { - let mut idx = f + first_index; - tree[idx] += s; - - let mut weight_sum = 0; - while idx > 0 { - if idx % 2 != 0 { - weight_sum += tree[idx + 1]; - } - idx = (idx - 1) >> 1; - tree[idx] += s; - } - c += s * weight_sum; - } - - count += c - } - - count - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/init_order.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/init_order.rs deleted file mode 100644 index 6e84116993..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/init_order.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! ### Gansner et al., "A Technique for Drawing Directed Graphs" -//! Assigns an initial order value for each node by performing a DFS search -//! starting from nodes in the first rank. -//! Nodes are assigned an order in their rank as they're first visited. -//! -//! Returns a layering matrix with an array per layer and each layer sorted by -//! the order of its nodes. - -use ahash::{HashSet, HashSetExt}; - -use crate::{Graph, Key}; - -impl Graph { - pub(super) fn init_order(&self) -> Option>> { - let mut visited: HashSet = HashSet::new(); - let mut simple_nodes: Vec = - self.nodes.keys().filter(|k| self.children(k).is_empty()).copied().collect(); - - let mut max_rank = 0; - for id in &simple_nodes { - if let Some(rank) = self.nodes[id].rank { - max_rank = max_rank.max(rank) - } - } - - let mut layers: Vec> = vec![Vec::new(); max_rank as usize + 1]; - - simple_nodes.sort_by_key(|id| Some(self.nodes[id].rank?)); - - for id in simple_nodes { - let mut stack = vec![id]; - - while let Some(id) = stack.pop() { - if !visited.insert(id) { - continue; - } - - let rank = self.nodes[&id].rank? as usize; - layers[rank].push(id); - - stack.extend(self.successors(&id)); - } - } - - Some(layers) - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/mod.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/mod.rs deleted file mode 100644 index bc7ae14358..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/mod.rs +++ /dev/null @@ -1,76 +0,0 @@ -mod build_layer_graph; -mod cross_count; -mod init_order; -mod subgraph; - -use std::ops::Range; - -use build_layer_graph::EdgeRelation; - -use crate::{Graph, Key, EMPTY_KEY}; - -impl Graph { - pub fn order(&mut self) -> Option<()> { - let mut matrix = self.init_order()?; - self.assign_order(&matrix); - - let mut best_cc = f64::INFINITY; - let mut best: Vec> = Vec::new(); - - let mut last_best = 0; - loop { - if last_best >= 4 { - break; - } - - matrix = self.key_matrix(); - let cc = self.cross_count(&mut matrix) as f64; - - if cc < best_cc { - last_best = 0; - best = matrix; - best_cc = cc; - } else { - last_best += 1; - } - } - - self.assign_order(&best) - } - - #[allow(dead_code)] - #[inline] - fn build_layer_graphs(&mut self, ranks: Range, relationship: EdgeRelation) -> Vec { - ranks.map(|rank| self.build_layer_graph(rank, relationship)).collect() - } - - fn assign_order(&mut self, matrix: &[Vec]) -> Option<()> { - for keys in matrix { - for (i, key) in keys.iter().enumerate() { - self.node_mut(key)?.order = Some(i); - } - } - - None - } -} - -#[allow(dead_code)] -fn sweep_graphs(layer_graphs: &mut [Graph], bias_right: bool) -> Option<()> { - let mut graph = Graph::new(true, false); - - for lg in layer_graphs { - let root = lg.root.unwrap_or(EMPTY_KEY); - let sorted = lg.sort_subgraph(&graph, root, bias_right)?; - - for (i, key) in sorted.keys.iter().enumerate() { - if let Some(node) = lg.node_mut(key) { - node.order = Some(i); - } - } - - lg.add_constraints(&mut graph, &sorted.keys); - } - - None -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/add_constraints.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/add_constraints.rs deleted file mode 100644 index 0450567e03..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/add_constraints.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::{HashMap, HashMapExt}; - -use crate::{Graph, Key}; - -impl Graph { - pub(crate) fn add_constraints(&self, cg: &mut Graph, keys: &[Key]) { - let mut prev_map: HashMap = HashMap::new(); - let mut prev_root: Option = None; - - for key in keys { - let mut current = self.parent(key); - - while let Some(child) = current { - match self.parent(&child) { - Some(parent) => { - if let Some(&prev_child) = prev_map.get(&parent) { - if prev_child != child { - cg.set_edge(prev_child, child, None); - return; - } - } - prev_map.insert(parent, child); - } - None => { - if let Some(prev_child) = prev_root { - if prev_child != child { - cg.set_edge(prev_child, child, None); - return; - } - } - prev_root = Some(child); - } - } - current = self.parent(&child); - } - } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/barycenters.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/barycenters.rs deleted file mode 100644 index 60cd153eef..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/barycenters.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::Barycenter; -use crate::{Graph, Key}; - -impl Graph { - pub(super) fn barycenters(&self, movable: &[Key]) -> Vec { - movable - .iter() - .map(|&key| { - if self.edge_values.is_empty() { - return Barycenter { key, barycenter: None, weight: None }; - } - - let (sum, weight) = self - .edge_values - .values() - .try_fold((0.0, 0.0), |(sum, weight), edge| { - let w = edge.weight?; - let order = self.node(&edge.source)?.order? as f32; - Some((sum + w * order, weight + w)) - }) - .unwrap(); - - return Barycenter { key, barycenter: Some(sum / weight), weight: Some(weight) }; - }) - .collect() - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/context.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/context.rs deleted file mode 100644 index 3491ec7a22..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/context.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::HashMap; - -use crate::Key; - -#[derive(Copy, Clone)] -pub(super) struct Barycenter { - pub key: Key, - pub barycenter: Option, - pub weight: Option, -} - -#[derive(Copy, Clone)] -pub struct ResolvedEntry { - /// ## Layout - /// This struct has 3 bytes of padding. If set either [`idx`] - /// or [`indegree`] to u8, the size of the struct be reduced - /// by 4 bytes, but *2^8-1* may not be enough. - /// - /// ## Another Solution - /// Stores these three fields into u32. [`merged`] occupies - /// 1 byte, [`idx`] and [`degree`] each occupies 15 bytes. - pub idx: u16, - pub indegree: u16, - pub merged: bool, - pub barycenter: Option, - pub weight: Option, -} - -impl ResolvedEntry { - #[inline] - pub(super) fn of(idx: u16) -> Self { - Self { idx, indegree: 0, merged: false, barycenter: None, weight: None } - } -} - -/// A context for managing [`ResolvedEntry`] sources, sinks, and keys. -/// -/// Storing these fields directly within `ResolvedEntry` would prevent -/// it from deriving the [`Copy`] trait. Additionally, it'd lead to -/// excessive cloning when accessing these fields. -/// -/// ## Performance -/// There are three more solutions, Raw Pointer preferred. -/// - **Reference** Safe Rust, Manual lifecycle management, High Performance -/// - **RC/RefCell** Safe Rust, with extremely cumbersome boilerplate code. -/// - **NonNull** The best Performance, but Unsafe Rust, -#[derive(Default)] -pub(super) struct Context { - pub entries: HashMap, - pub sources_map: HashMap>, - pub sinks_map: HashMap>, - pub keys_map: HashMap>, - pub keys: Vec, - pub index: usize, -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/mod.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/mod.rs deleted file mode 100644 index 77b545d985..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/mod.rs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -pub mod add_constraints; -mod barycenters; -mod context; -mod resolve_conflict; -mod sort; - -use ahash::{HashMap, HashMapExt}; -pub use context::*; - -use crate::{Graph, Key}; - -#[derive(Debug, Clone, Default)] -pub struct Subgraph { - pub keys: Vec, - pub barycenter: f32, - pub weight: f32, -} - -impl Graph { - pub fn sort_subgraph(&self, cg: &Graph, start_key: Key, bias_right: bool) -> Option { - let mut stack: Vec<(Key, Subgraph)> = vec![(start_key, Subgraph::default())]; - let mut subgraphs: HashMap = HashMap::new(); - - while let Some((curr_key, mut subgraph)) = stack.pop() { - let movable = self.children(&curr_key); - - let mut barycenters = self.barycenters(&movable); - for entry in &mut barycenters { - if self.children(&entry.key).len() > 0 { - stack.push((entry.key, Subgraph::default())); - } - } - - let mut ctx = Context::default(); - ctx.resolve(cg, &barycenters); - ctx.expand_subgraph(&subgraphs); - - subgraph = ctx.next_subgraph(bias_right)?; - - if stack.is_empty() { - return Some(subgraph); - } - - subgraphs.insert(curr_key, subgraph); - } - - None - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/resolve_conflict.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/resolve_conflict.rs deleted file mode 100644 index dc7cb91511..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/resolve_conflict.rs +++ /dev/null @@ -1,161 +0,0 @@ -//! ### Implementation Based on the description in Forster. -//! ### "A Fast and Simple Heuristic for Constrained Two-Level Crossing Reduction" -//! Thought differs in some specific details. -//! -//! Given a list of entries with the form {key, barycenter, weight} and a -//! constraint graph, this function resolves any conflicts between the -//! constraint graph and the barycenters for the entries. -//! If the barycenters for an entry violate a constraint in the constraint graph, -//! then coalesce the nodes in the conflict into a new node that respects the -//! constraint and aggregates barycenter and weight information. -//! -//! Pre-Conditions: -//! -//! 1. Each entry has the form {key, barycenter, weight}, or if the node has -//! no barycenter, then {key}. -//! -//! Returns: -//! -//! A new list of entries with the form {keys, idx, barycenter, weight}. -//! The list `keys` may either be a singleton or it may be aggregation of nodes -//! ordered such that they don't violate constraints from the constraint graph. -//! The property `idx` is the lowest original index of the elements in `keys`. - -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::HashMap; - -use super::{Barycenter, Context, ResolvedEntry, Subgraph}; -use crate::{Edge, Graph, Key}; - -impl Context { - pub(super) fn resolve(&mut self, cg: &Graph, entries: &[Barycenter]) -> Option<()> { - for (idx, entry) in entries.iter().enumerate() { - let mut tmp = ResolvedEntry::of(idx as u16); - if entry.barycenter.is_some() { - tmp.barycenter = entry.barycenter; - tmp.weight = entry.weight; - } - self.entries.insert(entry.key, tmp); - self.keys_map.insert(entry.key, vec![entry.key]); - } - - for &Edge { source, target } in cg.edges.values() { - let flag = self.entries.get(&source).is_some(); - let te = self.entries.get_mut(&target); - if flag && let Some(te) = te { - te.indegree += 1; - self.sinks_map.entry(source).or_default().push(target); - } - } - - let mut source_array: Vec = self - .entries - .iter() - .filter(|(k, _)| self.sources_map.get(k).is_none()) - .map(|(&k, _)| k) - .collect(); - - self.do_resolve(&mut source_array) - } - - fn handle_in(&mut self, source: Key, target: Key) -> Option<()> { - let source_ent = self.entries[&target]; - if source_ent.merged { - return None; - } - - let target_ent = self.entries[&source]; - - if source_ent.barycenter.is_none() - || target_ent.barycenter.is_none() - || source_ent.barycenter >= target_ent.barycenter - { - self.merge_entries(target, source); - } - - None - } - - fn handle_out(&mut self, source: Key, target: Key) -> Option<()> { - self.sources_map.get_mut(&target)?.push(source); - - let target_ent = self.entries.get_mut(&target)?; - target_ent.indegree -= 1; - - if target_ent.indegree == 0 { - self.sinks_map.get_mut(&source)?.push(target); - } - - None - } - - fn do_resolve(&mut self, keys: &mut Vec) -> Option<()> { - while let Some(key) = keys.pop() { - for &source_key in self.sources_map[&key].clone().iter().rev() { - self.handle_in(source_key, key); - } - - for sink_key in self.sinks_map.get(&key).cloned()? { - self.handle_out(key, sink_key); - } - } - - None - } - - fn merge_entries(&mut self, source_key: Key, target_key: Key) -> Option<()> { - let mut sum = 0.0; - let mut weight = 0.0; - - let source = &self.entries[&source_key]; - if let Some(w) = source.weight { - let source_barycenter = source.barycenter?; - sum += source_barycenter * w; - weight += w; - } - let source_idx = source.idx; - - let target = &self.entries[&target_key]; - if let Some(w) = target.weight { - let target_barycenter = target.barycenter?; - sum += target_barycenter * w; - weight += w; - } - - let source_keys = self.keys_map[&target_key].clone(); - let target_keys = self.keys_map.get_mut(&source_key)?; - target_keys.extend(source_keys); - - let target = self.entries.get_mut(&target_key)?; - target.barycenter = Some(sum / weight); - target.weight = Some(weight); - target.idx = source_idx.min(target.idx); - - self.entries.get_mut(&source_key)?.merged = true; - - None - } - - #[inline] - pub(super) fn expand_subgraph(&mut self, subgraphs: &HashMap) { - for (key, keys) in &mut self.keys_map { - if let Some(subgraph) = subgraphs.get(key) { - keys.extend(&subgraph.keys); - } - } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/sort.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/sort.rs deleted file mode 100644 index 6ccdbf796f..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/sort.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::{cmp, cmp::Ordering}; - -use super::{Context, ResolvedEntry, Subgraph}; -use crate::Key; - -impl Context { - pub fn next_subgraph(&mut self, bias_right: bool) -> Option { - let (mut sortable, mut unsortable) = self - .entries - .iter() - .map(|(&k, &e)| (k, e)) - .partition::, _>(|(_, ent)| ent.barycenter.is_some()); - - sortable.sort_by(|(_, lhs), (_, rhs)| cmp_with_bias(lhs, rhs, bias_right)); - unsortable.sort_by_key(|(_, entry)| cmp::Reverse(entry.idx)); - - let mut sum = 0.0; - let mut weight = 0.0; - - self.consume(&mut sortable); - - for (k, entry) in sortable { - let keys = &self.keys_map[&k]; - self.index += keys.len(); - self.keys.extend(keys); - let entry_weight = entry.weight?; - sum += entry.barycenter? * entry_weight; - weight += entry_weight; - self.consume(&mut unsortable); - } - - let mut subgraph = Subgraph::default(); - let mut keys = vec![]; - keys.extend(&self.keys); - subgraph.keys = keys; - if weight > 0.0 { - subgraph.barycenter = sum / weight; - subgraph.weight = weight; - } - - Some(subgraph) - } - - fn consume(&mut self, consumable: &mut Vec<(Key, ResolvedEntry)>) -> Option<()> { - if consumable.is_empty() { - return None; - } - - while let Some((last, _)) = consumable.last() { - let idx = self.entries[last].idx; - if idx > self.index as u16 { - break; - } - - let keys = &self.keys_map[last]; - - self.keys.extend(keys); - - consumable.pop(); - self.index += 1; - } - - None - } -} - -fn cmp_with_bias(lhs: &ResolvedEntry, rhs: &ResolvedEntry, bias: bool) -> Ordering { - let lb = lhs.barycenter; - let rb = rhs.barycenter; - if lb < rb { - return Ordering::Greater; - } else if lb > rb { - return Ordering::Less; - } - - if !bias { lhs.idx.cmp(&rhs.idx) } else { rhs.idx.cmp(&lhs.idx) } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/parent_dummy_chains.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/parent_dummy_chains.rs deleted file mode 100644 index 9d11943941..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/parent_dummy_chains.rs +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -use ahash::{HashMap, HashMapExt}; - -use crate::{Edge, Graph, Key, EMPTY_KEY, EMPTY_ROOT}; - -impl Graph { - /// Processes dummy chains in the graph - /// to establish proper parent-child relationships. - /// - /// This method performs a post-order traversal to determine node limits, - /// then processes each - /// dummy chain to find the lowest common ancestor - /// (LCA) and adjust parent relationships along - /// the path between edge endpoints. - /// - /// # Returns - /// - `Option<()>`: always returns None, - pub(super) fn parent_dummy_chains(&mut self) -> Option<()> { - let lims = postorder(self); - let dummy_chains = self.dummy_chains.clone().unwrap_or(vec![]); - - for mut dummy_id in dummy_chains { - let edge = self.node(&dummy_id)?.edge?; - let (path, ref lca) = self.find_lca(&lims, edge.source, edge.target); - - if !path.is_empty() { - self.traverse_path(&mut dummy_id, &path, lca, edge)?; - } - } - - None - } - - /// Traverses a path between nodes - /// while setting parent relationships for dummy nodes. - /// - /// # Arguments - /// * `dummy_id` - Mutable reference to the current dummy node key - /// * `path` - Path through which to establish relationships - /// * `lca` - Lowest common ancestor of the edge endpoints - /// * `edge` - Original edge being processed - fn traverse_path( - &mut self, - dummy_id: &mut Key, - path: &[Key], - lca: &Key, - edge: Edge, - ) -> Option<()> { - let mut path_iter = path.iter().peekable(); - let mut ascending = true; - - while *dummy_id != edge.target { - let node = self.node(dummy_id)?; - let mut current = (if ascending { - path_iter.find(|k| self.node(k).unwrap().max_rank > node.rank) - } else { - path_iter.rfind(|k| self.node(k).unwrap().min_rank < node.rank) - }) - .unwrap_or(lca); - - if ascending && current == lca { - ascending = false; - path_iter = path.iter().peekable(); - current = path_iter.next_back()?; - } - - self.set_parent(*dummy_id, Some(*current)); - *dummy_id = self.successors(dummy_id).first().copied().unwrap_or(EMPTY_KEY) - } - - None - } - - - /// Finds the lowest common ancestor (LCA) and constructs a path between two nodes. - /// - /// # Arguments - /// * `lims` - Post-order traversal limits map - /// * `source` - Source node key - /// * `target` - Target node key - /// - /// # Returns - /// - `(Vec, Key)`: tuple containing: - /// - Full path from source to target through LCA - /// - Lowest common ancestor key - fn find_lca(&self, lims: &HashMap, source: Key, target: Key) -> (Vec, Key) { - let mut s_path: Vec = vec![]; - let mut t_path: Vec = vec![]; - - let lim = lims[&source].min(lims[&target]); - - let mut lca = source; - while let Some(parent) = self.parent(&lca) { - lca = parent; - s_path.push(parent); - if lims.get(&parent).map_or(false, |&l| lim == l) { - break; - } - } - - let mut parent = self.parent(&target).unwrap_or(lca); - while parent != lca { - t_path.push(parent); - parent = self.parent(&parent).unwrap_or(lca); - } - - t_path.reverse(); - s_path.extend(t_path); - - (s_path, lca) - } -} - -/// Generates post-order traversal limits for graph nodes. -fn postorder(g: &Graph) -> HashMap { - let mut ret: HashMap = HashMap::new(); - let mut lim = 0; - - for child in g.children(&EMPTY_ROOT) { - let mut stack = vec![child]; - - while let Some(node) = stack.pop() { - if ret.contains_key(&node) { - continue; - } - - ret.insert(node, lim); - lim += 1; - stack.extend(g.children(&node)) - } - } - - ret -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/align.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/align.rs deleted file mode 100644 index f45dc79abf..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/align.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::{HashMap, HashMapExt, HashSet, HashSetExt}; - -use super::{BlockGraph, Context, Vertical, Vertical::*}; -use crate::{Graph, Key, KeyCodecExt}; - -impl Graph { - pub(super) fn vertical_alignment( - &self, - ctx: &mut Context, - matrix: &[Vec], - vertical: Vertical, - ) { - let mut pos: HashMap = HashMap::new(); - - for keys in matrix { - for (order, &key) in keys.iter().enumerate() { - ctx.root.insert(key, key); - ctx.align.insert(key, key); - pos.insert(key, order); - } - } - - for keys in matrix { - let mut prev_idx: i32 = -1; - for &key in keys { - let mut neighbors: Vec = match vertical { - Top => self.predecessors(&key), - Bottom => self.successors(&key), - }; - - if neighbors.is_empty() { - continue; - } - - /// Here we can improve performance a little by **unwrap** - neighbors.sort_by_key(|id| pos.get(id)); - let mid = (neighbors.len() as f32 - 1.0) / 2.0000001; - let start = mid.floor() as usize; - let end = mid.ceil() as usize; - for idx in start..=end { - let neighbor = neighbors[idx]; - if ctx.align[&key] == key - && prev_idx < (pos[&neighbor] as i32) - && !ctx.has_conflict(key, neighbor) - { - let x = ctx.root[&neighbor]; - ctx.align.insert(neighbor, key); - ctx.align.insert(key, x); - ctx.root.insert(key, x); - - prev_idx = pos[&neighbor] as i32; - } - } - } - } - } - - pub(super) fn horizontal_compaction( - &self, - matrix: &[Vec], - ctx: &Context, - ) -> HashMap { - let mut compact: HashMap = HashMap::new(); - let block: BlockGraph = self.build_block_graph(matrix, &ctx.root); - - let mut stack = block.nodes(); - let mut visited: HashSet = HashSet::new(); - while let Some(k) = stack.pop() { - if visited.contains(&k) { - let in_edges = &block.in_edges[&k]; - let mut val: f32 = 0.0; - for key in in_edges { - let source = key.source(); - let ev: f32 = compact[&source] + block.edges[key]; - val = val.max(ev) - } - - compact.insert(k, val); - } else { - visited.insert(k); - stack.push(k); - stack.extend(block.predecessors(&k)); - } - } - - for &k in ctx.align.values() { - compact.insert(k, compact[&ctx.root[&k]]); - } - - compact - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/block_graph.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/block_graph.rs deleted file mode 100644 index 61c1c79860..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/block_graph.rs +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::{HashMap, HashMapExt}; - -use crate::{Graph, Key, KeyCodecExt, EMPTY_KEY}; - -#[derive(Default)] -pub struct BlockGraph { - nodes: HashMap, - pub(super) in_edges: HashMap>, - predecessors: HashMap>, - pub(super) edges: HashMap, -} - -impl BlockGraph { - pub fn set_node(&mut self, key: Key) -> &mut Self { - if self.nodes.contains_key(&key) { - return self; - } - - self.nodes.insert(key, EMPTY_KEY); - self.in_edges.insert(key, vec![]); - self.predecessors.insert(key, HashMap::new()); - - self - } - - #[inline] - pub fn nodes(&self) -> Vec { - self.nodes.keys().copied().collect() - } - - #[inline] - pub fn predecessors(&self, k: &Key) -> Vec { - self.predecessors.get(k).map_or(vec![], |p| p.keys().copied().collect()) - } - - pub fn set_edge(&mut self, source: Key, target: Key, val: f32) -> &mut Self { - let key = Key::of(source, target); - if self.edges.contains_key(&key) { - self.edges.insert(key, val); - return self; - } - - self.set_node(source); - self.set_node(target); - - self.edges.insert(key, val); - - self.predecessors - .get_mut(&target) - .map(|preds| preds.entry(source).and_modify(|c| *c += 1).or_insert(1)); - - self.in_edges.entry(target).or_default().push(key); - - self - } -} - -impl Graph { - pub(super) fn build_block_graph( - &self, - matrix: &[Vec], - root: &HashMap, - ) -> BlockGraph { - let mut block_graph: BlockGraph = BlockGraph::default(); - - for keys in matrix { - let mut target: Option = None; - for &key in keys { - let source = root[&key]; - block_graph.set_node(source); - if let Some(t) = target { - let target = root[&t]; - let prev_max = match block_graph.edges.get(&Key::of(target, source)) { - Some(&x) => x, - None => 0.0, - }; - - let max = self.sep(key, t).max(prev_max); - block_graph.set_edge(target, source, max); - } - target = Some(key); - } - } - - block_graph - } - - fn sep(&self, source: Key, target: Key) -> f32 { - let nodesep = self.config.nodesep; - let edgesep = self.config.edgesep; - - let source_node = &self.nodes[&source]; - let target_node = &self.nodes[&target]; - - let mut sum = source_node.width / 2.0; - sum += if source_node.dummy.is_some() { edgesep } else { nodesep } / 2.0; - sum += if target_node.dummy.is_some() { edgesep } else { nodesep } / 2.0; - sum += target_node.width / 2.0; - - sum - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/conflict.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/conflict.rs deleted file mode 100644 index 93823fa1f4..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/conflict.rs +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::Context; -use crate::{normalize_st, Dummy::Border, Graph, Key}; - -#[derive(Copy, Clone)] -struct ScanCtx { - south_start: usize, - south_end: usize, - prev_north: i32, - next_north: i32, -} - -impl ScanCtx { - fn of(south_start: usize, south_end: usize, prev_north: i32, next_north: i32) -> Self { - Self { south_start, south_end, prev_north, next_north } - } -} - -impl Graph { - pub(super) fn find_conflict(&mut self, ctx: &mut Context, matrix: &[Vec]) { - if !matrix.is_empty() { - self.find_type1_conflict(ctx, matrix); - self.find_type2_conflict(ctx, matrix); - } - } - - fn find_type1_conflict(&mut self, ctx: &mut Context, matrix: &[Vec]) -> Option<()> { - let mut prev_len = matrix.first()?.len(); - - for layer in matrix.iter().skip(1) { - let mut k0 = 0; - let mut scan_pos = 0; - let last_key = layer.last()?; - - for (i, key) in layer.iter().enumerate() { - let w = self.other_inner_segment(key); - - let k1 = match (w, key == last_key) { - (None, false) => continue, - (Some(w), _) => self.node(&w)?.order?, - (None, true) => prev_len, - }; - - for scan_key in &layer[scan_pos..i + 1] { - let scan_node = self.node(scan_key)?; - for pre_key in &self.predecessors(scan_key) { - let pre_node = self.node(pre_key)?; - let pos = pre_node.order?; - let both_dummy = pre_node.dummy.is_some() && scan_node.dummy.is_some(); - if (pos < k0 || k1 < pos) && !both_dummy { - ctx.add_conflict(*pre_key,*scan_key) - } - } - } - - scan_pos = i + 1; - k0 = k1; - prev_len = layer.len(); - } - } - - None - } - - fn find_type2_conflict(&mut self, ctx: &mut Context, matrix: &[Vec]) -> Option<()> { - let mut north_len = matrix.first()?.len(); - - for south in matrix.iter().skip(1) { - let mut prev_north_pos = -1; - let mut next_north_pos = 0; - let mut south_pos = 0; - let south_len = south.len(); - - for (south_ahead, key) in south.iter().enumerate() { - if self.node(key)?.dummy == Some(Border) { - let preds = self.predecessors(key); - if let [pk] = preds[..] { - next_north_pos = self.node(&pk)?.order? as i32; - let scan = - ScanCtx::of(south_pos, south_ahead, prev_north_pos, next_north_pos); - self.scan(ctx, south, scan); - south_pos = south_ahead; - prev_north_pos = next_north_pos; - } - } - - let scan = ScanCtx::of(south_pos, south_len, next_north_pos, north_len as i32); - self.scan(ctx, south, scan); - } - - north_len = south_len; - } - - None - } - - fn scan(&mut self, ctx: &mut Context, south: &[Key], scan_ctx: ScanCtx) -> Option<()> { - let ScanCtx { - south_start, - south_end, - prev_north: prev_north_border, - next_north: next_north_border, - } = scan_ctx; - for sid in &south[south_start..south_end] { - if self.node(sid)?.dummy.is_none() { - continue; - } - - for id in self.predecessors(sid) { - let Some(node) = self.node(&id) else { continue }; - - let order = node.order.unwrap_or(0) as i32; - let has_conflict = order < prev_north_border || order > next_north_border; - if node.dummy.is_some() && has_conflict { - ctx.add_conflict(id,*sid); - } - } - } - - None - } - - fn other_inner_segment(&mut self, key: &Key) -> Option { - match self.node(key)?.dummy { - Some(_) => { - /// ### Functional style - /// ```asm - /// .iter().find(|u| g.node(u).unwrap().dummy.is_some()).copied() - /// ``` - /// However, there's [`unwrap`] and [`copied`] - for k in self.predecessors(key) { - if self.node(&k)?.dummy.is_some() { - return Some(k); - } - } - - None - } - None => None, - } - } -} - -impl Context { - fn add_conflict(&mut self, source: Key, target: Key) { - let (s, t) = normalize_st(source, target); - - self.conflicts.entry(s).or_default().push(t); - } - - pub(super) fn has_conflict(&self, source: Key, target: Key) -> bool { - let (s, t) = normalize_st(source, target); - - match self.conflicts.get(&s) { - Some(set) => set.contains(&t), - _ => false, - } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/context.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/context.rs deleted file mode 100644 index 1e01243b01..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/context.rs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::{ - ops, - ops::{Index, IndexMut}, -}; - -use ahash::HashMap; - -use self::{Direction::*, Horizontal::*, Vertical::*}; -use crate::Key; - -#[derive(Copy, Clone, PartialEq)] -pub(super) enum Horizontal { - Left, - Right, -} - -#[derive(Copy, Clone, PartialEq)] -pub(super) enum Vertical { - Top, - Bottom, -} - -#[derive(Copy, Clone, Hash, Eq, PartialEq)] -pub(super) enum Direction { - TopLeft, - TopRight, - BottomLeft, - BottomRight, -} - -impl From for Direction { - fn from(value: usize) -> Self { - match value { - 0 => TopLeft, - 1 => TopRight, - 2 => BottomLeft, - 3 => BottomRight, - _ => unreachable!(), - } - } -} - -impl ops::Add for Vertical { - type Output = Direction; - - fn add(self, rhs: Horizontal) -> Self::Output { - match (self, rhs) { - (Top, Left) => TopLeft, - (Top, Right) => TopRight, - (Bottom, Left) => BottomLeft, - (Bottom, Right) => BottomRight, - } - } -} - -impl Direction { - pub(super) fn horizon(self) -> Horizontal { - match self { - TopLeft | TopRight => Left, - BottomLeft | BottomRight => Right, - } - } -} - -type DirectionMap = [HashMap; 4]; - -impl Index for DirectionMap { - type Output = HashMap; - - fn index(&self, index: Direction) -> &Self::Output { - match index { - TopLeft => &self[0], - TopRight => &self[1], - BottomLeft => &self[2], - BottomRight => &self[3], - } - } -} - -impl IndexMut for DirectionMap { - fn index_mut(&mut self, index: Direction) -> &mut Self::Output { - match index { - TopLeft => &mut self[0], - TopRight => &mut self[1], - BottomLeft => &mut self[2], - BottomRight => &mut self[3], - } - } -} - -#[derive(Default)] -pub(super) struct Context { - pub conflicts: HashMap>, - pub direction_map: DirectionMap, - pub root: HashMap, - pub align: HashMap, - pub balanced: HashMap -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/mod.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/mod.rs deleted file mode 100644 index bab053a421..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/mod.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod align; -mod block_graph; -mod conflict; -mod context; -mod xy; - -use block_graph::*; -use context::*; - -use crate::Graph; - -impl Graph { - pub(super) fn position(&mut self) -> Option<()> { - let mut ncg: Graph = self.as_non_compound(); - - let mut ctx = Context::default(); - ncg.position_y(); - ncg.position_x(&mut ctx); - - for (key, &x) in &ctx.balanced { - let node = self.node_mut(key)?; - node.x = x; - node.y = ncg.nodes[key].y; - } - - None - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/xy.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/xy.rs deleted file mode 100644 index edda8fc6dd..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/xy.rs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::HashMap; - -use super::{Context, Direction, Direction::*, Horizontal::*, Vertical::*}; -use crate::{Graph, Key}; - -trait ExtentExt { - fn extent(&self) -> (f32, f32); -} - -impl ExtentExt for HashMap { - fn extent(&self) -> (f32, f32) { - let iter = self.values().copied(); - - let (mut min, mut max) = (f32::INFINITY, f32::NEG_INFINITY); - - for value in iter { - min = min.min(value); - max = max.max(value); - } - - (min, max) - } -} - -impl Context { - fn balance(&mut self) -> Option<()> { - self.balanced = self.direction_map[TopLeft].clone(); - let keys: Vec = self.balanced.keys().copied().collect(); - - for key in &keys { - let mut vals: Vec = - self.direction_map.iter().map(|dirs| dirs.get(key).copied().unwrap()).collect(); - vals.sort_by_key(|f| f.to_bits()); - let x1 = vals[1]; - let x2 = vals[2]; - let mid = self.balanced.get_mut(key)?; - *mid = (x1 + x2) / 2.0; - } - - None - } - - fn min_alignment(&self, graph: &Graph) -> Option<(Direction, f32, f32)> { - let (idx, align) = self.direction_map.iter().enumerate().min_by_key(|(_, keys)| { - let mut max = f32::NEG_INFINITY; - let mut min = f32::INFINITY; - - for (key, x) in keys.iter() { - let half_width = graph.nodes[key].width / 2.0; - max = max.max(x + half_width); - min = min.min(x - half_width); - } - - (max - min).to_bits() - })?; - - let (min, max) = align.extent(); - - Some((Direction::from(idx), min, max)) - } - - fn align_coordinates(&mut self, graph: &Graph) -> Option<()> { - let (min_direction, min, max) = self.min_alignment(graph)?; - - for direction in [TopLeft, TopRight, BottomLeft, BottomRight] { - if direction != min_direction { - let vals = &mut self.direction_map[direction]; - let (vals_min, vals_max) = vals.extent(); - - let delta = match direction.horizon() { - Left => min - vals_min, - Right => max - vals_max, - }; - - if delta != 0.0 { - for x in vals.values_mut() { - *x += delta; - } - } - } - } - - None - } -} - -impl Graph { - pub(super) fn position_x(&mut self, ctx: &mut Context) -> Option<()> { - let matrix = self.key_matrix(); - - self.find_conflict(ctx, &matrix); - let mut matrix = matrix; - - for vertical in [Top, Bottom] { - if vertical == Bottom { - matrix = self.key_matrix(); - matrix.reverse() - } - - for horizontal in [Left, Right] { - if horizontal == Right { - matrix.iter_mut().for_each(|inner| inner.reverse()); - } - - self.vertical_alignment(ctx, &matrix, vertical); - let mut compact = self.horizontal_compaction(&matrix, ctx); - - if horizontal == Right { - compact.values_mut().for_each(|x| *x = -*x); - } - - ctx.direction_map[vertical + horizontal] = compact; - } - } - - ctx.align_coordinates(self); - ctx.balance() - } - - pub(super) fn position_y(&mut self) -> Option<()> { - let matrix = self.key_matrix(); - let rank_sep = self.config.ranksep; - let mut y = 0.0; - for keys in matrix { - let mut max_height = f32::NEG_INFINITY; - - for key in &keys { - max_height = max_height.max(self.node(key)?.height) - } - - for key in &keys { - let node = self.node_mut(key)?; - node.y = y + max_height / 2.0; - } - - y += max_height + rank_sep; - } - - None - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/feasible_tree.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/feasible_tree.rs deleted file mode 100644 index 78921409fc..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/feasible_tree.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::{Edge, Graph, GraphEdge, GraphNode, Key, EMPTY_KEY}; - -impl Graph { - pub(super) fn feasible_tree(&mut self) -> Graph { - let mut t: Graph = Graph::new(false, false); - - let start = self.nodes().first().copied().unwrap_or(EMPTY_KEY); - let size = self.nodes.len(); - t.set_node(start, Some(GraphNode::default())); - - while tight_tree(&mut t, self) < size { - if let Some(edge) = find_min_stack_edge(&t, self) { - let delta = - if t.has_node(&edge.source) { self.slack(edge) } else { -self.slack(edge) }; - shift_ranks(&t, self, delta); - } - } - - t - } -} - -fn tight_tree(t: &mut Graph, g: &Graph) -> usize { - let mut stack: Vec = t.nodes(); - - while let Some(curr) = stack.pop() { - for edge in g.node_edges(&curr) { - let source = edge.source; - let key = if curr == source { edge.target } else { edge.source }; - if !t.has_node(&key) && g.slack(edge) == 0 { - t.set_node(key, Some(GraphNode::default())); - t.set_edge_undirected(curr, key, Some(GraphEdge::default())); - stack.push(key); - } - } - } - - t.nodes.len() -} - -fn find_min_stack_edge(t: &Graph, g: &Graph) -> Option { - g.edges - .values() - .filter_map(|&e| (t.has_node(&e.source) != t.has_node(&e.target)).then(|| (e, g.slack(e)))) - .min_by_key(|(_, slack)| *slack) - .map(|(e, _)| e) -} - -fn shift_ranks(t: &Graph, g: &mut Graph, delta: i32) { - for node_id in t.nodes.keys() { - if let Some(node) = g.node_mut(node_id) { - node.rank = Some(node.rank.unwrap_or(0).wrapping_add(delta)); - } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/longest_path.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/longest_path.rs deleted file mode 100644 index a5a13334e8..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/longest_path.rs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::{HashSet, HashSetExt}; - -use self::Variant::*; -use crate::{Graph, Key}; - -enum Variant { - Array(Vec), - Single(Key), -} - -impl Graph { - pub(super) fn longest_path(&mut self) { - let mut visited = HashSet::new(); - let init = self.in_map.iter().filter(|(_, vec)| vec.is_empty()).map(|(&k, _)| k).collect(); - let mut stack = vec![Array(init)]; - - while stack.len() > 0 { - let curr = stack.last_mut().unwrap(); - - match curr { - Array(arr) => { - let k = arr.pop().unwrap(); - if arr.is_empty() { - stack.pop(); - } - - if !visited.contains(&k) { - visited.insert(k); - let children: Vec = - self.out_edges(&k).iter().map(|e| e.target).rev().collect(); - if children.len() > 0 { - stack.push(Single(k)); - stack.push(Array(children)) - } else { - self.node_mut(&k).unwrap().rank = Some(0); - } - } - } - Single(k) => { - let k = k.clone(); - stack.pop(); - let mut rank = i32::MAX; - - for &edge in &self.out_map[&k] { - let minlen = self.edge1(edge).unwrap().minlen.unwrap(); - let target_rank = self.nodes[&edge.target].rank.unwrap(); - - rank = rank.min(target_rank - minlen); - } - - self.node_mut(&k).unwrap().rank = Some(rank); - } - } - } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/mod.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/mod.rs deleted file mode 100644 index f91afa087e..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/mod.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod feasible_tree; -mod longest_path; -mod network_simplex; -mod slack; - -use crate::{Graph, Ranker::*}; - -impl Graph { - #[inline] - pub(super) fn rank(&mut self) { - match self.config.ranker { - NetworkSimplex => self.network_simplex(), - TightTree => self.tight_tree(), - LongestPath => self.longest_path(), - } - } - - #[inline] - fn tight_tree(&mut self) { - self.longest_path(); - self.feasible_tree(); - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/network_simplex.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/network_simplex.rs deleted file mode 100644 index 3845282835..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/network_simplex.rs +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::mem; - -use ahash::{HashSet, HashSetExt}; - -use crate::{Edge, Graph, GraphEdge, GraphNode, Key, EMPTY_KEY}; - -impl Graph { - pub(super) fn network_simplex(&mut self) { - self.simplify_ref(); - self.longest_path(); - - let mut t: Graph = self.feasible_tree(); - init_low_lim_values(&mut t); - init_cut_values(&mut t, self); - - while let Some(e) = leave_edge(&t) { - if let Some(f) = enter_edge(&t, &self, e) { - exchange_edges(&mut t, self, e, f); - } - } - } - - fn simplify_ref(&mut self) { - for edge in self.edge_values.values_mut() { - edge.weight.get_or_insert(0.0); - edge.minlen.get_or_insert(1); - } - } -} - -fn init_cut_values(t: &mut Graph, g: &mut Graph) { - let keys = g.postorder(&g.nodes()); - for &key in keys.iter().skip(1) { - assign_cut_value(t, g, key); - } -} - -fn assign_cut_value(t: &mut Graph, g: &mut Graph, child: Key) { - let cutvalue = calc_cut_value(t, g, child); - if let Some(node) = t.node_mut(&child) { - let parent = node.parent.unwrap_or(EMPTY_KEY); - if let Some(edge) = t.edge_mut(child, parent) { - edge.cutvalue = Some(cutvalue); - } - } -} - -fn calc_cut_value(t: &mut Graph, g: &mut Graph, child: Key) -> f32 { - let Some(node) = t.node_mut(&child) else { return 0.0 }; - - let parent = node.parent.unwrap_or(EMPTY_KEY); - let mut child_is_tail = true; - let mut graph_edge = g.edge_mut(child, parent); - - if graph_edge.is_none() { - child_is_tail = false; - graph_edge = g.edge_mut(parent, child); - } - - let mut cut_value = graph_edge.and_then(|e| e.weight).unwrap_or(0.0); - - for edge in g.node_edges(&child) { - let is_out_edge = edge.source == child; - let other = if is_out_edge { edge.target } else { edge.source }; - - if other == parent { - continue; - } - - let points_to_head = is_out_edge == child_is_tail; - let other_weight = g.edge1(edge).and_then(|e| e.weight).unwrap_or(0.0); - - cut_value += if points_to_head { other_weight } else { -other_weight }; - - if is_tree_edge(t, child, other) { - let out_cut_value = t.edge(child, other).and_then(|e| e.cutvalue).unwrap_or(0.0); - cut_value += if points_to_head { -out_cut_value } else { out_cut_value } - } - } - - cut_value -} - -fn init_low_lim_values(tree: &mut Graph) { - let root = tree.nodes().first().copied().unwrap_or(EMPTY_KEY); - let mut visited: HashSet = HashSet::new(); - assign_low_lim(tree, &mut visited, 1, root); -} - -fn assign_low_lim( - tree: &mut Graph, - visited: &mut HashSet, - mut next_lim: usize, - start_key: Key, -) -> usize { - let mut stack: Vec<(Key, usize, Option)> = vec![(start_key, next_lim, None)]; - - while let Some((k, low, parent)) = stack.pop() { - if !visited.insert(k) { - continue; - } - - let neighbors = tree.neighbors(&k); - let unvisited_neighbors = - neighbors.into_iter().filter(|w| !visited.contains(w)).collect::>(); - - if !unvisited_neighbors.is_empty() { - stack.push((k, low, parent)); - - for t in unvisited_neighbors { - stack.push((t, next_lim, Some(k))); - } - } else { - if let Some(node) = tree.node_mut(&k) { - node.low = Some(low); - node.lim = Some(next_lim); - next_lim += 1; - - node.parent = parent; - } - } - } - - next_lim -} - -fn leave_edge(tree: &Graph) -> Option { - tree.edges - .values() - .find(|&&edge_obj| tree.edge1(edge_obj).map(|e| e.cutvalue) < Some(Some(0.0))) - .copied() -} - -fn enter_edge(t: &Graph, g: &Graph, edge: Edge) -> Option { - let mut source = edge.source; - let mut target = edge.target; - - if !g.has_edge(source, target) { - mem::swap(&mut source, &mut target); - } - - let source_node = t.node(&source); - let target_node = t.node(&target); - let mut tail_node = source_node; - let mut flip = false; - - if source_node?.lim > target_node?.lim { - tail_node = target_node; - flip = true; - } - - g.edges - .values() - .filter(|edge_obj| { - let v_node = t.node(&edge_obj.source); - let w_node = t.node(&edge_obj.target); - flip == is_descendant(v_node, tail_node) && flip != is_descendant(w_node, tail_node) - }) - .min_by_key(|&&e| g.slack(e)) - .copied() -} - -fn exchange_edges(t: &mut Graph, g: &mut Graph, e: Edge, f: Edge) { - t.remove_edge(e.source, e.target); - t.set_edge(f.source, f.target, Some(GraphEdge::default())); - init_low_lim_values(t); - init_cut_values(t, g); - update_ranks(t, g); -} - -fn update_ranks(t: &mut Graph, g: &mut Graph) { - let root = t - .nodes - .keys() - .find(|k| !g.node(k).map_or(true, |n| n.parent.is_none())) - .copied() - .unwrap_or(EMPTY_KEY); - let keys = t.preorder(&vec![root]); - for &k in keys.iter().skip(1) { - let parent = t.node(&k).and_then(|n| n.parent).unwrap_or(EMPTY_KEY); - let mut edge = g.edge(k, parent); - let mut flipped = false; - if edge.is_none() { - edge = g.edge(parent, k); - flipped = true; - } - - let mut minlen = edge.and_then(|e| e.minlen).unwrap_or(0); - if !flipped { - minlen = -minlen - } - - let parent_rank = g.node(&parent).and_then(|n| n.rank).unwrap_or(0); - if let Some(node) = g.node_mut(&k) { - node.rank = Some(parent_rank + (minlen)); - } - } -} - -#[inline] -fn is_tree_edge(tree: &Graph, source: Key, target: Key) -> bool { - tree.has_edge(source, target) -} - -fn is_descendant(node: Option<&GraphNode>, root_node: Option<&GraphNode>) -> bool { - let root_node = root_node.unwrap(); - let lim = node.unwrap().lim; - root_node.low <= lim && lim <= root_node.lim -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/slack.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/slack.rs deleted file mode 100644 index c897902558..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/slack.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::{Edge, Graph}; - -/// The slack is defined as the -/// difference between the length of the edge and its minlen. -impl Graph { - pub(super) fn slack(&self, edge: Edge) -> i32 { - let source_rank = self.nodes[&edge.source].rank.unwrap(); - let target_rank = self.nodes[&edge.target].rank.unwrap(); - let minlen = self.edge_values[&edge.to_key()].minlen.unwrap(); - target_rank - source_rank - minlen - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/selfedge.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/selfedge.rs deleted file mode 100644 index 280b5e1a05..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/selfedge.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use smallvec::smallvec; - -use crate::{Dummy::SelfEdge, Graph, GraphNode, Point}; - -/// Self-edge Processing Module -/// -/// ## Overview -/// This module implements a three-phase process to layout -/// self-edges properly in hierarchical graph diagrams. -/// Self-edge is edge where source == target -/// -/// The implementation handles: -/// - Temporary removal of original edges -/// - Insertion of layout markers -/// - Final path calculation and cleanup -/// -/// ## Operation Phases -/// 1. **Edge Removal** (`remove_self_edges`): -/// - Detects and removes self-edges from the main graph structure -/// - Stores original edges in a temporary map for later processing -/// -/// 2. **Marker Insertion** (`insert_self_edges`): -/// - Creates dummy nodes in the layout matrix to reserve space -/// - Maintains proper node ordering through order shifting -/// - Preserves edge metadata for final rendering -/// -/// 3. **Path Positioning** (`position_self_edges`): -/// - Calculates smooth Bézier curve control points -/// - Creates an elliptical path around source node -/// - Removes temporary dummy nodes after path generation -/// -/// ## Key Characteristics -/// - Maintains layout integrity through temporary dummy nodes -/// - Generates consistent elliptical paths for visual clarity -/// - Preserves original-edge data while modifying visual representation -impl Graph { - pub(super) fn remove_self_edges(&mut self) { - for edge in self.edges() { - if edge.source == edge.target { - self.selfedge_map.entry(edge.source).or_default().push(edge); - self.remove_edge1(edge); - } - } - } - - pub(super) fn insert_self_edges(&mut self) -> Option<()> { - let matrix = self.key_matrix(); - for layer in matrix { - let mut order_shift = 0; - for (i, id) in layer.iter().enumerate() { - let node = self.node_mut(id)?; - node.order = Some(i + order_shift); - let rank = node.rank; - - let self_edges = self.selfedge_map.get(id)?.clone(); - for edge in self_edges { - order_shift += 1; - let graph_node = GraphNode { - rank, - order: Some(i + order_shift), - edge: Some(edge), - ..GraphNode::default() - }; - self.add_dummy_node(SelfEdge, graph_node); - } - } - } - - None - } - - pub(super) fn position_self_edges(&mut self) -> Option<()> { - for key in &mut self.nodes() { - let node = &mut self.node(&key)?; - if node.dummy == Some(SelfEdge) { - let self_node = &mut self.node(&node.edge?.source)?; - let x = self_node.x + self_node.width / 2.0; - let y = self_node.y; - let dx = node.x - x; - let dy = self_node.height / 2.0; - let graph_edge = &mut self.edge_mut1(node.edge?)?; - graph_edge.points = Some(smallvec![ - Point::of(x + 2.0 * dx / 3.0, y - dy), - Point::of(x + 2.0 * dx / 3.0, y - dy), - Point::of(x + 5.0 * dx / 6.0, y - dy), - Point::of(x + dx, y), - Point::of(x + 5.0 * dx / 6.0, y + dy), - Point::of(x + 2.0 * dx / 3.0, y + dy), - ]); - self.remove_node(&key); - } - } - - None - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/utils.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/utils.rs deleted file mode 100644 index 465fd7e958..0000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/utils.rs +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::{HashMap, HashMapExt}; -use smallvec::smallvec; - -use crate::{Dummy, Dummy::EdgeProxy, Graph, GraphNode, Key, Point}; - -static mut KEY_COUNTER: Key = 2; - -#[inline] -pub(crate) fn unique_key() -> Key { - unsafe { - KEY_COUNTER += 1; - KEY_COUNTER - } -} - -impl Graph { - fn max_rank(&self) -> usize { - self.nodes.values().map(|n| n.rank.unwrap()).max().unwrap_or(0) as usize - } - - pub(super) fn add_dummy_node(&mut self, dummy: Dummy, mut node: GraphNode) -> Key { - let mut node_id = unique_key(); - while self.has_node(&node_id) { - node_id = unique_key(); - } - - node.dummy = Some(dummy); - self.set_node(node_id, Some(node)); - node_id - } - - pub(super) fn as_non_compound(&mut self) -> Graph { - let mut simplified: Graph = Graph::new(true, false); - simplified.config = self.config; - - self.transfer_node_edges(&mut simplified); - - simplified - } - - pub(super) fn transfer_node_edges(&mut self, dst: &mut Graph) { - for (&node_id, &node) in &self.nodes { - if self.children(&node_id).is_empty() { - dst.set_node(node_id, Some(node)); - } - } - - for &edge in self.edges.values() { - dst.set_edge1(edge, self.edge1(edge).cloned()); - } - } - - /// Adjusts rank for all nodes, then all node sources have - /// **rank(source) >= 0** - /// and at least one node target has **rank(target) = 0**. - pub(super) fn normalize_ranks(&mut self) { - let min = self.nodes.values().map(|n| n.rank.unwrap_or(0)).min().unwrap_or(0); - - for node in &mut self.nodes.values_mut() { - if let Some(rank) = node.rank { - node.rank = Some(rank - min) - } - } - } - - pub(super) fn remove_empty_ranks(&mut self) { - /// Ranks may not start at 0, so we need to offset them - let offset = self.nodes.values().map(|n| n.rank.unwrap_or(0)).min().unwrap_or(0); - - let mut layers: HashMap> = HashMap::new(); - for (&node_id, node) in &self.nodes { - let rank = node.rank.unwrap_or(0).wrapping_sub(offset); - layers.entry(rank).or_default().push(node_id) - } - - let mut delta = 0; - let node_rank_factor = self.config.node_rank_factor as i32; - for (rank, keys) in &layers { - if keys.is_empty() && rank % node_rank_factor != 0 { - delta -= 1; - } else if delta != 0 { - for k in keys { - self.node_mut(k).map(|n| n.rank = Some(n.rank.unwrap_or(0) + delta)); - } - } - } - } - - /// Given a DAG with each node assigned "rank" and "order" properties, this - /// function produces a matrix with the keys of each node. - pub(super) fn key_matrix(&self) -> Vec> { - let mut matrix: Vec> = vec![Vec::new(); self.max_rank() + 1]; - - for (&key, node) in &self.nodes { - if let Some(rank) = node.rank { - matrix[rank as usize].push((node.order.unwrap(), key)); - } - } - - matrix - .iter_mut() - .map(|layer| { - layer.sort_by_key(|&(order, _)| order); - layer.iter().map(|&(_, key)| key).collect() - }) - .collect() - } - - pub(super) fn make_space_for_edge_labels(&mut self) { - let config = &mut self.config; - config.ranksep /= 2.0; - - for edge in self.edge_values.values_mut() { - // In fact, minlen is usually Some(x)! - if let Some(minlen) = &mut edge.minlen { - *minlen *= 2; - } - } - } - - pub(super) fn assign_rank_min_max(&mut self) -> Option<()> { - for key in self.nodes().iter() { - let node = self.node(key)?; - if let (Some(border_top), Some(border_bottom)) = (&node.border_top, &node.border_bottom) - { - let min_rank = self.node(border_top).and_then(|n| n.rank).unwrap_or(0); - let max_rank = self.node(border_bottom).and_then(|n| n.rank).unwrap_or(0); - - let node = self.node_mut(key)?; - node.min_rank = Some(min_rank); - node.max_rank = Some(max_rank); - } - } - - None - } - - pub(super) fn translate_graph(&mut self) { - let mut min_x = f64::INFINITY as f32; - let mut max_x: f32 = 0.0; - let mut min_y = f64::INFINITY as f32; - let mut max_y: f32 = 0.0; - - for GraphNode { x, y, width, height, .. } in self.nodes.values() { - min_x = min_x.min(x - width / 2.0); - max_x = max_x.max(x + width / 2.0); - min_y = min_y.min(y - height / 2.0); - max_y = max_y.max(y + height / 2.0); - } - - for node in self.nodes.values_mut() { - node.x -= min_x; - node.y -= min_y; - } - - for edge in self.edge_values.values_mut() { - if let Some(points) = &mut edge.points { - for point in points { - point.x -= min_x; - point.y -= min_y; - } - } - } - - self.width = max_x - min_x; - self.height = max_y - min_y; - } - - pub(super) fn assign_node_intersects(&mut self) -> Option<()> { - for e in self.edges() { - let ((source_p, source_bbox), (target_p, target_bbox)) = { - let source_node = self.node(&e.source)?; - let target_node = self.node(&e.target)?; - (source_node.coord_bbox(), target_node.coord_bbox()) - }; - - let edge = self.edge_mut1(e)?; - - if let Some(points) = &mut edge.points { - let p1 = source_bbox.intersect_point(Point::of(points[0].x, points[0].y)); - points.insert(0, p1); - let p2 = target_bbox.intersect_point(Point::of( - points[points.len() - 1].x, - points[points.len() - 1].y, - )); - points.push(p2); - } else { - let p1 = source_bbox.intersect_point(target_p); - let p2 = target_bbox.intersect_point(source_p); - edge.points = Some(smallvec![p1, p2]); - }; - } - - None - } - - pub(super) fn remove_edge_proxies(&mut self) -> Option<()> { - for key in &self.nodes() { - let node = self.node(key)?; - if node.dummy == Some(EdgeProxy) { - let rank = node.rank.unwrap_or(0); - if let Some(graph_edge) = self.edge_mut1(node.edge?) { - graph_edge.rank = Some(rank); - } - self.remove_node(key); - } - } - - None - } - - pub(super) fn reverse_points_for_reversed_edges(&mut self) { - for edge in self.edge_values.values_mut() { - if edge.reversed - && let Some(points) = &mut edge.points - { - points.reverse(); - } - } - } -} - -#[derive(Copy, Clone)] -pub(crate) struct Rect { - pub x: f32, - pub y: f32, - pub width: f32, - pub height: f32, -} - -impl GraphNode { - #[inline] - fn bbox(&self) -> Rect { - Rect { x: self.x, y: self.y, width: self.width, height: self.height } - } - - #[inline] - fn coord_bbox(&self) -> (Point, Rect) { - (Point::of(self.x, self.y), self.bbox()) - } -} - -impl Rect { - /// Finds where a line starting at point {x, y} would intersect a rectangle - /// {x, y, width, height} if it were pointing at the rectangle's center. - pub(crate) fn intersect_point(self, point: Point) -> Point { - let x = self.x; - let y = self.y; - - /// Rectangle intersection algorithm - /// [math.stackoverflow](http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes): - let dx = point.x - x; - let dy = point.y - y; - let w = self.width / 2.0; - let h = self.height / 2.0; - - let (sx, sy) = if (dy.abs() * w) > (dx.abs() * h) { - if dy < 0.0 { (-h * dx / dy, -h) } else { (h * dx / dy, h) } - } else { - if dx < 0.0 { (-w, -w * dy / dx) } else { (w, w * dy / dx) } - }; - - Point::of(x + sx, y + sy) - } -} -- Gitee