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
6pub 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
22impl<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 self.content
47 .draw_with_parameters(|v| self.color[v] as usize, type_size)
48 }
49}
50
51fn 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}