tidy-tree 0.1.0

A tree visualization tool
Documentation
#![allow(dead_code, unused_imports, unused_variables)]
pub mod geometry;
mod iter;
mod layout;
mod node;
mod utils;
pub use iter::Iter;
use std::{any::Any, collections::HashMap, ptr::NonNull};

use geometry::Coord;
use layout::BoundingBox;
pub use layout::{BasicLayout, Layout, TidyLayout};
pub use node::Node;

#[derive(PartialEq, Eq)]
pub enum LayoutType {
    Basic,
    Tidy,
    LayeredTidy,
}

pub struct TidyTree {
    root: Node,
    layout_type: LayoutType,
    layout: Box<dyn Layout>,
    map: HashMap<usize, NonNull<Node>>,
}

impl TidyTree {
    pub fn with_basic_layout(parent_child_margin: Coord, peer_margin: Coord) -> Self {
        TidyTree {
            layout_type: LayoutType::Basic,
            root: Default::default(),
            layout: Box::new(BasicLayout {
                parent_child_margin,
                peer_margin,
            }),
            map: HashMap::new(),
        }
    }

    pub fn with_tidy_layout(parent_child_margin: Coord, peer_margin: Coord) -> Self {
        TidyTree {
            layout_type: LayoutType::Tidy,
            root: Default::default(),
            layout: Box::new(TidyLayout::new(parent_child_margin, peer_margin)),
            map: HashMap::new(),
        }
    }

    pub fn with_layered_tidy(parent_child_margin: Coord, peer_margin: Coord) -> Self {
        TidyTree {
            layout_type: LayoutType::Tidy,
            root: Default::default(),
            layout: Box::new(TidyLayout::new_layered(parent_child_margin, peer_margin)),
            map: HashMap::new(),
        }
    }

    pub fn change_layout(&mut self, layout_type: LayoutType) {
        if layout_type == self.layout_type {
            return;
        }

        let parent_child_margin = self.layout.parent_child_margin();
        let peer_margin = self.layout.peer_margin();
        match layout_type {
            LayoutType::Basic => {
                self.layout = Box::new(BasicLayout {
                    parent_child_margin,
                    peer_margin,
                });
            }
            LayoutType::Tidy => {
                self.layout = Box::new(TidyLayout::new(parent_child_margin, peer_margin));
            }
            LayoutType::LayeredTidy => {
                self.layout = Box::new(TidyLayout::new_layered(parent_child_margin, peer_margin))
            }
        }

        self.layout_type = layout_type;
    }

    pub fn is_empty(&self) -> bool {
        self.root.id == usize::MAX
    }

    pub fn add_node(&mut self, id: usize, width: Coord, height: Coord, parent_id: usize) {
        let node = Node::new(id, width, height);
        if self.is_empty() || parent_id == usize::MAX {
            self.root = node;
            self.map.insert(id, (&self.root).into());
            return;
        }

        let mut parent = *self.map.get(&parent_id).unwrap();
        let parent = unsafe { parent.as_mut() };

        let ptr = parent.append_child(node);
        self.map.insert(id, ptr);
    }

    pub fn remove_node(&mut self, id: usize) {
        if self.is_empty() {
            return;
        }

        if let Some(node) = self.map.get(&id) {
            let node = unsafe { &mut *node.as_ptr() };
            node.pre_order_traversal(|node| {
                self.map.remove(&node.id);
            });
            node.parent_mut().unwrap().remove_child(id);
        }
    }

    pub fn data(&mut self, id: &[usize], width: &[Coord], height: &[Coord], parent_id: &[usize]) {
        for (i, &id) in id.iter().enumerate() {
            let width = width[i];
            let height = height[i];
            let parent_id = parent_id[i];
            self.add_node(id, width, height, parent_id);
        }
    }

    pub fn layout(&mut self) {
        if self.is_empty() {
            return;
        }

        self.layout.layout(&mut self.root);
    }

    pub fn get_pos(&self) -> Vec<Coord> {
        let mut ans = vec![];
        for (id, node) in self.map.iter() {
            let node = unsafe { node.as_ref() };
            ans.push((*id) as Coord);
            ans.push(node.x);
            ans.push(node.y);
        }

        ans
    }
}

pub const NULL_ID: usize = usize::MAX;