flag_algebra/tools/
draw.rs

1use crate::flag::SubClass;
2use crate::flags::{CGraph, Colored, DirectedGraph, Graph, OrientedGraph};
3use svg::node::element::{Circle, Line, Polygon, SVG};
4use svg::node::Node;
5
6/// Draw flags in svg
7pub trait Draw: Sized {
8    fn draw_with_parameters<C>(&self, color: C, type_size: usize) -> SVG
9    where
10        C: FnMut(usize) -> usize;
11    fn draw(&self) -> SVG {
12        self.draw_with_parameters(|_| 0, 0)
13    }
14    fn draw_typed(&self, type_size: usize) -> SVG {
15        self.draw_with_parameters(|_| 0, type_size)
16    }
17    fn to_svg_file(&self, filename: &str) {
18        svg::save(filename, &self.draw()).unwrap()
19    }
20}
21
22/// Inheritance
23impl<F, A> Draw for SubClass<F, A>
24where
25    F: Draw,
26{
27    fn draw_with_parameters<C>(&self, color: C, type_size: usize) -> SVG
28    where
29        C: FnMut(usize) -> usize,
30    {
31        self.content.draw_with_parameters(color, type_size)
32    }
33}
34
35impl<F, const N: u8> Draw for Colored<F, N>
36where
37    F: Draw,
38{
39    fn draw_with_parameters<C>(&self, mut color: C, type_size: usize) -> SVG
40    where
41        C: FnMut(usize) -> usize,
42    {
43        let n = self.color.len();
44        assert!((0..n).all(|v| { color(v) == 0 }));
45        //
46        self.content
47            .draw_with_parameters(|v| self.color[v] as usize, type_size)
48    }
49}
50
51/// Particular implementations
52
53fn coordinates(i: usize, n: usize) -> (f64, f64) {
54    assert!(i < n);
55    if n == 1 {
56        (50., 50.)
57    } else {
58        let angle = 2. * std::f64::consts::PI * i as f64 / n as f64;
59        (50. + 41. * angle.cos(), 50. + 41. * angle.sin())
60    }
61}
62
63fn vertex(i: usize, n: usize) -> Circle {
64    let (cx, cy) = coordinates(i, n);
65    Circle::new()
66        .set("r", 6.)
67        .set("stroke", "black")
68        .set("cx", cx)
69        .set("cy", cy)
70}
71
72fn type_marker(i: usize, n: usize) -> Circle {
73    let (cx, cy) = coordinates(i, n);
74    Circle::new()
75        .set("r", 8.5)
76        .set("stroke", "black")
77        .set("stroke-width", 1.5)
78        .set("fill", "none")
79        .set("cx", cx)
80        .set("cy", cy)
81}
82
83fn line(i: usize, j: usize, n: usize) -> Line {
84    let (x1, y1) = coordinates(i, n);
85    let (x2, y2) = coordinates(j, n);
86    Line::new()
87        .set("x1", x1)
88        .set("x2", x2)
89        .set("y1", y1)
90        .set("y2", y2)
91        .set("stroke-width", 4)
92}
93
94fn arrow(i: usize, j: usize, n: usize) -> Polygon {
95    let (x1, y1) = coordinates(i, n);
96    let (x2, y2) = coordinates(j, n);
97    let (dx, dy) = (x2 - x1, y2 - y1);
98    let dist = (dx * dx + dy * dy).sqrt();
99    let u = (dx / dist, dy / dist);
100    let pic = (x2 - 6. * u.0, y2 - 6. * u.1);
101    let mid = (x2 - 18. * u.0, y2 - 18. * u.1);
102    let t = (u.1 * 4., -u.0 * 4.);
103    let th = (u.1 * 1.5, -u.0 * 1.5);
104    Polygon::new().set(
105        "points",
106        format!(
107            "{},{} {},{} {},{} {},{} {},{} {},{} {},{}",
108            x1 + th.0,
109            y1 + th.1,
110            mid.0 + th.0,
111            mid.1 + th.1,
112            mid.0 + t.0,
113            mid.1 + t.1,
114            pic.0,
115            pic.1,
116            mid.0 - t.0,
117            mid.1 - t.1,
118            mid.0 - th.0,
119            mid.1 - th.1,
120            x1 - th.0,
121            y1 - th.1,
122        ),
123    )
124}
125
126fn color(c: usize) -> &'static str {
127    ["black", "red", "blue"][c]
128}
129
130fn frame() -> SVG {
131    SVG::new().set("viewBox", "0 0 100 100").set("width", 100)
132}
133
134fn add_vertex<N: Node>(node: &mut N, i: usize, n: usize, col: &'static str, in_type: bool) {
135    node.append(vertex(i, n).set("fill", col));
136    if in_type {
137        node.append(type_marker(i, n));
138    }
139}
140
141impl<const N: u8> Draw for CGraph<N> {
142    fn draw_with_parameters<C>(&self, mut col: C, type_size: usize) -> SVG
143    where
144        C: FnMut(usize) -> usize,
145    {
146        let mut res = frame();
147        let n = self.size;
148        for u in 0..n {
149            for v in 0..u {
150                match self.edge(u, v) {
151                    0 => (),
152                    c => res = res.add(line(u, v, n).set("stroke", color(c as usize - 1))),
153                }
154            }
155        }
156        for v in 0..n {
157            add_vertex(&mut res, v, n, color(col(v)), v < type_size)
158        }
159        res
160    }
161}
162
163impl Draw for Graph {
164    fn draw_with_parameters<C>(&self, mut col: C, type_size: usize) -> SVG
165    where
166        C: FnMut(usize) -> usize,
167    {
168        let mut res = frame();
169        let n = self.size();
170        for u in 0..n {
171            for v in 0..u {
172                if self.edge(u, v) {
173                    res = res.add(line(u, v, n).set("stroke", "black"))
174                }
175            }
176        }
177        for v in 0..n {
178            add_vertex(&mut res, v, n, color(col(v)), v < type_size)
179        }
180        res
181    }
182}
183
184impl Draw for DirectedGraph {
185    fn draw_with_parameters<C>(&self, mut col: C, type_size: usize) -> SVG
186    where
187        C: FnMut(usize) -> usize,
188    {
189        let mut res = frame();
190        let n = self.size();
191        for u in 0..n {
192            for v in self.out_nbrs(u) {
193                res = res.add(arrow(u, v, n).set("stroke", "black"));
194            }
195        }
196        for v in 0..n {
197            add_vertex(&mut res, v, n, color(col(v)), v < type_size)
198        }
199        res
200    }
201}
202
203impl Draw for OrientedGraph {
204    fn draw_with_parameters<C>(&self, col: C, type_size: usize) -> SVG
205    where
206        C: FnMut(usize) -> usize,
207    {
208        self.as_directed().draw_with_parameters(col, type_size)
209    }
210}