1use std::io::{self, Seek, Write};
5
6use geo_aid_figure::{
7 CircleItem, Figure, Item, LineItem, PointItem, Position, Style, TwoPointItem,
8};
9
10#[derive(Debug)]
12pub struct Svg<W: Write + Seek> {
13 writer: W,
15}
16
17impl<W: Write + Seek> Svg<W> {
18 pub fn draw(figure: &Figure, writer: W) -> io::Result<()> {
20 let mut svg = Self { writer };
21
22 svg.begin(figure)?;
23
24 for item in &figure.items {
25 match item {
26 Item::Point(point) => svg.draw_point(point)?,
27 Item::Line(line) => svg.draw_line(line)?,
28 Item::Ray(ray) => svg.draw_ray(ray)?,
29 Item::Segment(segment) => svg.draw_segment(segment)?,
30 Item::Circle(circle) => svg.draw_circle(circle)?,
31 }
32 }
33
34 svg.end()
35 }
36
37 fn get_style_width(style: Style) -> &'static str {
39 match style {
40 Style::Dashed | Style::Dotted => "0.5",
41 Style::Bold => "2",
42 Style::Solid => "1",
43 }
44 }
45
46 fn get_style_dashing(style: Style) -> &'static str {
48 match style {
49 Style::Dotted => "0.8,1",
50 Style::Dashed => "2,2",
51 Style::Bold | Style::Solid => "1,0",
52 }
53 }
54
55 fn draw_simple_segment(
57 &mut self,
58 (p1, p2): (Position, Position),
59 style: Style,
60 ) -> io::Result<()> {
61 write!(
62 &mut self.writer,
63 r#"
64 <line stroke-width="{}" stroke-dasharray="{}" stroke="black" x1="{}" x2="{}" y1="{}" y2="{}"/>
65 "#,
66 Self::get_style_width(style),
67 Self::get_style_dashing(style),
68 p1.x,
69 p2.x,
70 p1.y,
71 p2.y
72 )
73 }
74
75 fn begin(&mut self, figure: &Figure) -> io::Result<()> {
76 write!(
77 &mut self.writer,
78 r#"
79 <svg height="{}" width="{}" xmlns="http://www.w3.org/2000/svg">
80 <font>
81 <font-face font-family="New Computer Modern">
82 </font-face>
83 </font>
84 <g transform="translate(0,{})">
85 <g transform="scale(1,-1)">
86 "#,
87 figure.width, figure.height, figure.width,
88 )
89 }
90
91 fn draw_point(&mut self, point: &PointItem) -> io::Result<()> {
92 let pos = point.position;
93 write!(
94 &mut self.writer,
95 r#"<circle cx="{}" cy="{}" fill="black" r="1"/>"#,
96 pos.x, pos.y
97 )?;
98
99 if let Some(label) = &point.label {
100 write!(
101 &mut self.writer,
102 r#"
103 <text transform="scale(1,-1)"
104 text-anchor="middle" dominant-baseline="middle"
105 style="font-family: 'Computer Modern'" font-size="10px"
106 stroke="black" stroke-width="0" x="{}" y="-{}">{}
107 </text>
108 "#,
109 label.position.x, label.position.y, label.content
110 )?;
111 }
112
113 Ok(())
114 }
115
116 fn draw_line(&mut self, line: &LineItem) -> io::Result<()> {
117 self.draw_simple_segment(line.points, line.style)
118 }
119
120 fn draw_ray(&mut self, ray: &TwoPointItem) -> io::Result<()> {
121 self.draw_simple_segment(ray.points, ray.style)
122 }
123
124 fn draw_segment(&mut self, segment: &TwoPointItem) -> io::Result<()> {
125 self.draw_simple_segment(segment.points, segment.style)
126 }
127
128 fn draw_circle(&mut self, circle: &CircleItem) -> io::Result<()> {
151 write!(
152 &mut self.writer,
153 r#"
154 <circle cx="{}" cy="{}" r="{}" stroke="black" stroke-width="{}" stroke-dasharray="{}" fill="transparent"/>
155 "#,
156 circle.center.x,
157 circle.center.y,
158 circle.radius,
159 Self::get_style_width(circle.style),
160 Self::get_style_dashing(circle.style),
161 )
162 }
163
164 fn end(&mut self) -> io::Result<()> {
165 write!(&mut self.writer, "</g> </g> </svg>")
166 }
167}