#![allow(dead_code)]
use std::collections::{HashMap, HashSet};
use super::GridLayoutConfig;
use crate::graph::space::FRect;
use crate::graph::{Direction, Shape};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NodeBounds {
pub x: usize,
pub y: usize,
pub width: usize,
pub height: usize,
pub layout_center_x: Option<usize>,
pub layout_center_y: Option<usize>,
}
impl NodeBounds {
pub fn center_x(&self) -> usize {
self.layout_center_x.unwrap_or(self.x + self.width / 2)
}
pub fn center_y(&self) -> usize {
self.layout_center_y.unwrap_or(self.y + self.height / 2)
}
pub fn contains(&self, x: usize, y: usize) -> bool {
x >= self.x && x < self.x + self.width && y >= self.y && y < self.y + self.height
}
pub fn top(&self) -> (usize, usize) {
(self.center_x(), self.y)
}
pub fn bottom(&self) -> (usize, usize) {
(self.center_x(), self.y + self.height - 1)
}
pub fn left(&self) -> (usize, usize) {
(self.x, self.center_y())
}
pub fn right(&self) -> (usize, usize) {
(self.x + self.width - 1, self.center_y())
}
}
#[derive(Debug, Clone)]
pub struct SubgraphBounds {
pub x: usize,
pub y: usize,
pub width: usize,
pub height: usize,
pub title: String,
pub depth: usize,
pub invisible: bool,
}
#[derive(Debug, Clone)]
pub struct SelfEdgeDrawData {
pub node_id: String,
pub edge_index: usize,
pub points: Vec<(usize, usize)>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GridPos {
pub layer: usize,
pub pos: usize,
}
pub(crate) struct CoordTransform<'a> {
pub(crate) scale_x: f64,
pub(crate) scale_y: f64,
pub(crate) layout_min_x: f64,
pub(crate) layout_min_y: f64,
pub(crate) max_overhang_x: usize,
pub(crate) max_overhang_y: usize,
pub(crate) config: &'a GridLayoutConfig,
}
impl CoordTransform<'_> {
pub(crate) fn to_draw(&self, x: f64, y: f64) -> (usize, usize) {
let dx = ((x - self.layout_min_x) * self.scale_x).round() as isize;
let dy = ((y - self.layout_min_y) * self.scale_y).round() as isize;
let draw_x = dx.max(0) as usize
+ self.max_overhang_x
+ self.config.padding
+ self.config.left_label_margin;
let draw_y = dy.max(0) as usize + self.max_overhang_y + self.config.padding;
(draw_x, draw_y)
}
}
#[derive(Debug, Default)]
pub struct GridLayout {
pub grid_positions: HashMap<String, GridPos>,
pub draw_positions: HashMap<String, (usize, usize)>,
pub node_bounds: HashMap<String, NodeBounds>,
pub width: usize,
pub height: usize,
pub h_spacing: usize,
pub v_spacing: usize,
pub edge_waypoints: HashMap<usize, Vec<(usize, usize)>>,
pub routed_edge_paths: HashMap<usize, Vec<(usize, usize)>>,
pub preserve_routed_path_topology: HashSet<usize>,
pub edge_label_positions: HashMap<usize, (usize, usize)>,
pub node_shapes: HashMap<String, Shape>,
pub subgraph_bounds: HashMap<String, SubgraphBounds>,
pub self_edges: Vec<SelfEdgeDrawData>,
pub node_directions: HashMap<String, Direction>,
}
impl GridLayout {
pub fn get_bounds(&self, node_id: &str) -> Option<&NodeBounds> {
self.node_bounds.get(node_id)
}
pub fn effective_edge_direction(&self, from: &str, to: &str, fallback: Direction) -> Direction {
let src_dir = self.node_directions.get(from).copied().unwrap_or(fallback);
let tgt_dir = self.node_directions.get(to).copied().unwrap_or(fallback);
if src_dir == tgt_dir {
return src_dir;
}
if src_dir == fallback || tgt_dir == fallback {
return fallback;
}
match (self.node_bounds.get(from), self.node_bounds.get(to)) {
(Some(fb), Some(tb)) => {
let dx = (fb.center_x() as isize - tb.center_x() as isize).unsigned_abs();
let dy = (fb.center_y() as isize - tb.center_y() as isize).unsigned_abs();
if dx > dy {
if fb.center_x() <= tb.center_x() {
Direction::LeftRight
} else {
Direction::RightLeft
}
} else if dy > 0 {
if fb.center_y() <= tb.center_y() {
Direction::TopDown
} else {
Direction::BottomTop
}
} else {
fallback
}
}
_ => fallback,
}
}
}
pub(crate) struct RawCenter {
pub(crate) id: String,
pub(crate) cx: usize,
pub(crate) cy: usize,
pub(crate) w: usize,
pub(crate) h: usize,
}
pub(crate) struct TransformContext {
pub(crate) layout_min_x: f64,
pub(crate) layout_min_y: f64,
pub(crate) scale_x: f64,
pub(crate) scale_y: f64,
pub(crate) padding: usize,
pub(crate) left_label_margin: usize,
pub(crate) overhang_x: usize,
pub(crate) overhang_y: usize,
}
impl TransformContext {
#[allow(dead_code)]
pub(crate) fn to_grid_rect(&self, rect: &FRect) -> (usize, usize, usize, usize) {
let (x1, y1) = self.to_grid(rect.x, rect.y);
let (x2, y2) = self.to_grid(rect.x + rect.width, rect.y + rect.height);
let draw_x = x1.min(x2);
let draw_y = y1.min(y2);
let draw_w = x1.max(x2) - draw_x;
let draw_h = y1.max(y2) - draw_y;
(draw_x, draw_y, draw_w.max(1), draw_h.max(1))
}
pub(crate) fn to_grid(&self, layout_x: f64, layout_y: f64) -> (usize, usize) {
let x = ((layout_x - self.layout_min_x) * self.scale_x).round() as usize
+ self.overhang_x
+ self.padding
+ self.left_label_margin;
let y = ((layout_y - self.layout_min_y) * self.scale_y).round() as usize
+ self.overhang_y
+ self.padding;
(x, y)
}
}