binary_search_tree_visualizer/visualizer/
svg.rs

1use crate::tree::{BinarySearchTree, Node};
2use crate::Result;
3use crate::visualizer::TreeVisualizer;
4use svg::node::element::{Circle, Line, Text};
5use svg::Document;
6
7/// SVG visualizer for binary search trees
8pub struct SvgVisualizer {
9    pub node_radius: f64,
10    pub level_height: f64,
11    pub horizontal_spacing: f64,
12}
13
14impl Default for SvgVisualizer {
15    fn default() -> Self {
16        Self {
17            node_radius: 20.0,
18            level_height: 60.0,
19            horizontal_spacing: 40.0,
20        }
21    }
22}
23
24impl<T: std::fmt::Display> TreeVisualizer<T> for SvgVisualizer {
25    fn visualize(&self, tree: &BinarySearchTree<T>) -> Result<String> {
26        let mut document = Document::new()
27            .set("width", "800")
28            .set("height", "600")
29            .set("viewBox", "0 0 800 600");
30
31        if let Some(root) = &tree.root {
32            let width = self.calculate_width(root);
33            let start_x = 400.0 - (width * self.horizontal_spacing) / 2.0;
34            self.draw_node(root, start_x, 50.0, &mut document);
35        }
36
37        Ok(document.to_string())
38    }
39}
40
41impl SvgVisualizer {
42    fn calculate_width<T>(&self, node: &Node<T>) -> f64 {
43        let left_width = if let Some(left) = &node.left {
44            self.calculate_width(left)
45        } else {
46            0.0
47        };
48        let right_width = if let Some(right) = &node.right {
49            self.calculate_width(right)
50        } else {
51            0.0
52        };
53        1.0 + left_width + right_width
54    }
55
56    fn draw_node<T: std::fmt::Display>(
57        &self,
58        node: &Node<T>,
59        x: f64,
60        y: f64,
61        document: &mut Document,
62    ) {
63        // Draw node circle
64        let circle = Circle::new()
65            .set("cx", x)
66            .set("cy", y)
67            .set("r", self.node_radius)
68            .set("fill", "white")
69            .set("stroke", "black")
70            .set("stroke-width", 2);
71
72        // Draw node value
73        let text = Text::new()
74            .set("x", x)
75            .set("y", y + 5.0)
76            .set("text-anchor", "middle")
77            .set("font-size", "14")
78            .add(svg::node::Text::new(format!("{}", node.value)));
79
80        *document = document.clone().add(circle).add(text);
81
82        // Draw connections to children
83        if let Some(left) = &node.left {
84            let left_x = x - self.horizontal_spacing;
85            let left_y = y + self.level_height;
86
87            let line = Line::new()
88                .set("x1", x)
89                .set("y1", y + self.node_radius)
90                .set("x2", left_x)
91                .set("y2", left_y - self.node_radius)
92                .set("stroke", "black")
93                .set("stroke-width", 2);
94
95            *document = document.clone().add(line);
96            self.draw_node(left, left_x, left_y, document);
97        }
98
99        if let Some(right) = &node.right {
100            let right_x = x + self.horizontal_spacing;
101            let right_y = y + self.level_height;
102
103            let line = Line::new()
104                .set("x1", x)
105                .set("y1", y + self.node_radius)
106                .set("x2", right_x)
107                .set("y2", right_y - self.node_radius)
108                .set("stroke", "black")
109                .set("stroke-width", 2);
110
111            *document = document.clone().add(line);
112            self.draw_node(right, right_x, right_y, document);
113        }
114    }
115}