1use crate::graph::GitGraph;
4use crate::settings::Settings;
5use svg::node::element::path::Data;
6use svg::node::element::{Circle, Line, Path};
7use svg::Document;
8
9pub fn print_svg(graph: &GitGraph, settings: &Settings) -> Result<String, String> {
11 let mut document = Document::new();
12
13 let max_idx = graph.commits.len();
14 let mut max_column = 0;
15
16 if settings.debug {
17 for branch in &graph.all_branches {
18 if let (Some(start), Some(end)) = branch.range {
19 document = document.add(bold_line(
20 start,
21 branch.visual.column.unwrap(),
22 end,
23 branch.visual.column.unwrap(),
24 "cyan",
25 ));
26 }
27 }
28 }
29
30 for (idx, info) in graph.commits.iter().enumerate() {
31 if let Some(trace) = info.branch_trace {
32 let branch = &graph.all_branches[trace];
33 let branch_color = &branch.visual.svg_color;
34
35 if branch.visual.column.unwrap() > max_column {
36 max_column = branch.visual.column.unwrap();
37 }
38
39 for p in 0..2 {
40 if let Some(par_oid) = info.parents[p] {
41 if let Some(par_idx) = graph.indices.get(&par_oid) {
42 let par_info = &graph.commits[*par_idx];
43 let par_branch = &graph.all_branches[par_info.branch_trace.unwrap()];
44
45 let color = if info.is_merge {
46 &par_branch.visual.svg_color
47 } else {
48 branch_color
49 };
50
51 if branch.visual.column == par_branch.visual.column {
52 document = document.add(line(
53 idx,
54 branch.visual.column.unwrap(),
55 *par_idx,
56 par_branch.visual.column.unwrap(),
57 color,
58 ));
59 } else {
60 let split_index = super::get_deviate_index(graph, idx, *par_idx);
61 document = document.add(path(
62 idx,
63 branch.visual.column.unwrap(),
64 *par_idx,
65 par_branch.visual.column.unwrap(),
66 split_index,
67 color,
68 ));
69 }
70 }
71 }
72 }
73
74 document = document.add(commit_dot(
75 idx,
76 branch.visual.column.unwrap(),
77 branch_color,
78 !info.is_merge,
79 ));
80 }
81 }
82 let (x_max, y_max) = commit_coord(max_idx + 1, max_column + 1);
83 document = document
84 .set("viewBox", (0, 0, x_max, y_max))
85 .set("width", x_max)
86 .set("height", y_max);
87
88 let mut out: Vec<u8> = vec![];
89 svg::write(&mut out, &document).map_err(|err| err.to_string())?;
90 Ok(String::from_utf8(out).unwrap_or_else(|_| "Invalid UTF8 character.".to_string()))
91}
92
93fn commit_dot(index: usize, column: usize, color: &str, filled: bool) -> Circle {
94 let (x, y) = commit_coord(index, column);
95 Circle::new()
96 .set("cx", x)
97 .set("cy", y)
98 .set("r", 4)
99 .set("fill", if filled { color } else { "white" })
100 .set("stroke", color)
101 .set("stroke-width", 1)
102}
103
104fn line(index1: usize, column1: usize, index2: usize, column2: usize, color: &str) -> Line {
105 let (x1, y1) = commit_coord(index1, column1);
106 let (x2, y2) = commit_coord(index2, column2);
107 Line::new()
108 .set("x1", x1)
109 .set("y1", y1)
110 .set("x2", x2)
111 .set("y2", y2)
112 .set("stroke", color)
113 .set("stroke-width", 1)
114}
115
116fn bold_line(index1: usize, column1: usize, index2: usize, column2: usize, color: &str) -> Line {
117 let (x1, y1) = commit_coord(index1, column1);
118 let (x2, y2) = commit_coord(index2, column2);
119 Line::new()
120 .set("x1", x1)
121 .set("y1", y1)
122 .set("x2", x2)
123 .set("y2", y2)
124 .set("stroke", color)
125 .set("stroke-width", 5)
126}
127
128fn path(
129 index1: usize,
130 column1: usize,
131 index2: usize,
132 column2: usize,
133 split_idx: usize,
134 color: &str,
135) -> Path {
136 let c0 = commit_coord(index1, column1);
137
138 let c1 = commit_coord(split_idx, column1);
139 let c2 = commit_coord(split_idx + 1, column2);
140
141 let c3 = commit_coord(index2, column2);
142
143 let m = (0.5 * (c1.0 + c2.0), 0.5 * (c1.1 + c2.1));
144
145 let data = Data::new()
146 .move_to(c0)
147 .line_to(c1)
148 .quadratic_curve_to((c1.0, m.1, m.0, m.1))
149 .quadratic_curve_to((c2.0, m.1, c2.0, c2.1))
150 .line_to(c3);
151
152 Path::new()
153 .set("d", data)
154 .set("fill", "none")
155 .set("stroke", color)
156 .set("stroke-width", 1)
157}
158
159fn commit_coord(index: usize, column: usize) -> (f32, f32) {
160 (15.0 * (column as f32 + 1.0), 15.0 * (index as f32 + 1.0))
161}