lib.rs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. use std::fmt::Display;
  2. use std::io::Write;
  3. /// An SVG document
  4. pub struct SVG {
  5. stuff: Vec<Box<dyn AsSVG>>,
  6. size: (f64, f64),
  7. }
  8. fn xml_tag(w: &mut Vec<u8>, name: &str, attrs: &[(&str, &dyn Display)]) {
  9. write!(w, "<{}", name);
  10. for (k, v) in attrs {
  11. write!(w, " {}=\"{}\"", k, v);
  12. }
  13. writeln!(w, "/>");
  14. }
  15. fn xml_open(w: &mut Vec<u8>, name: &str, attrs: &[(&str, &dyn Display)]) {
  16. write!(w, "<{}", name);
  17. for (k, v) in attrs {
  18. write!(w, " {}=\"{}\"", k, v);
  19. }
  20. writeln!(w, ">");
  21. }
  22. fn xml_close(w: &mut Vec<u8>, name: &str) {
  23. write!(w, "<{}/>", name);
  24. }
  25. /// Create a new empty SVG document of the specified width and height
  26. pub fn svg(w: f64, h: f64) -> SVG {
  27. SVG {
  28. stuff: Vec::new(),
  29. size: (w, h),
  30. }
  31. }
  32. impl SVG {
  33. /// Print this SVG document to stdout
  34. pub fn to_stdout(self) {
  35. let buf = self.to_bytes();
  36. println!("{}", String::from_utf8(buf).unwrap());
  37. }
  38. /// Write this SVG document to a file
  39. pub fn to_file<W: Write>(self, w: &mut W) -> Result<(), std::io::Error> {
  40. let buf = self.to_bytes();
  41. w.write(&buf)?;
  42. Ok(())
  43. }
  44. pub fn to_bytes(self) -> Vec<u8> {
  45. let mut buf = Vec::new();
  46. let (w, h) = self.size;
  47. writeln!(buf, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
  48. xml_open(
  49. &mut buf, "svg",
  50. &[("xmlns", &"http://www.w3.org/2000/svg"),
  51. ("version", &"1.1"),
  52. ("width", &w),
  53. ("height", &h),
  54. ],
  55. );
  56. for elem in self.stuff {
  57. elem.draw_svg(&mut buf);
  58. }
  59. xml_close(&mut buf, "svg");
  60. buf
  61. }
  62. /// Add a new drawable thing to this SVG document
  63. pub fn add<T: AsSVG>(&mut self, t: T) {
  64. self.stuff.push(t.consume())
  65. }
  66. }
  67. /// The AsSVG trait represents things which can be rendered to an SVG
  68. /// canvas.
  69. pub trait AsSVG {
  70. fn draw_svg(&self, buf: &mut Vec<u8>);
  71. // This is a bit of a hack to make sure that all of our types live
  72. // long enough: it's now on the implementer of AsSVG to box it up
  73. // and make sure that we can keep a trait object around.
  74. fn consume(self) -> Box<AsSVG>;
  75. }
  76. /// A Line is a sequence of points. It'll be represented as a single
  77. /// dot if it has only one point, and otherwise will be drawn as a
  78. /// line between them.
  79. pub struct Line {
  80. start: (f64, f64),
  81. points: Vec<(f64, f64)>,
  82. }
  83. impl AsSVG for Line {
  84. fn draw_svg(&self, buf: &mut Vec<u8>) {
  85. if self.points.len() == 0 {
  86. // if there are no later points, then we just draw a
  87. // 'point' at the starting position, which is just a tiny
  88. // circle
  89. let (x, y) = self.start;
  90. xml_tag(
  91. buf, "circle",
  92. &[("cx", &x),
  93. ("cy", &y),
  94. ("r", &"0.5"),
  95. ("fill", &"black"),
  96. ],
  97. );
  98. } else {
  99. // Otherwise, we draw a path, which mean assembling this
  100. // somewhat wonky path field
  101. let path = {
  102. let mut path = Vec::new();
  103. let (x, y) = self.start;
  104. write!(&mut path, "M{} {}", x, y);
  105. for (x, y) in self.points.iter() {
  106. write!(&mut path, " L{} {}", x, y);
  107. }
  108. String::from_utf8(path).unwrap()
  109. };
  110. xml_tag(
  111. buf, "path",
  112. &[("d", &path), ("stroke", &"black")],
  113. );
  114. }
  115. }
  116. fn consume(self) -> Box<AsSVG> {
  117. Box::new(self)
  118. }
  119. }
  120. /// Create a new line at the given starting point
  121. pub fn line(x: f64, y: f64) -> Line {
  122. Line::new(x, y)
  123. }
  124. impl Line {
  125. /// Create a new line at the given starting point
  126. pub fn new(x: f64, y: f64) -> Line {
  127. Line {
  128. start: (x, y),
  129. points: Vec::new(),
  130. }
  131. }
  132. /// Draw a line segment from this point to another point
  133. pub fn draw_to(mut self, x: f64, y: f64) -> Line {
  134. self.points.push((x, y));
  135. self
  136. }
  137. }
  138. pub struct Rect {
  139. position: (f64, f64),
  140. size: (f64, f64),
  141. }
  142. pub fn rect(position: (f64, f64), size: (f64, f64)) -> Rect {
  143. Rect { position, size }
  144. }
  145. impl AsSVG for Rect {
  146. fn draw_svg(&self, buf: &mut Vec<u8>) {
  147. let (x, y) = self.position;
  148. let (w, h) = self.size;
  149. xml_tag(
  150. buf, "rect",
  151. &[("x", &x),
  152. ("y", &y),
  153. ("width", &w),
  154. ("height", &h),
  155. ("stroke", &"black"),
  156. ("fill", &"white"),
  157. ]
  158. );
  159. }
  160. fn consume(self) -> Box<AsSVG> {
  161. Box::new(self)
  162. }
  163. }
  164. pub struct Circle {
  165. position: (f64, f64),
  166. radius: f64,
  167. }
  168. pub fn circle(position: (f64, f64), radius: f64) -> Circle {
  169. Circle { position, radius }
  170. }
  171. impl AsSVG for Circle {
  172. fn draw_svg(&self, buf: &mut Vec<u8>) {
  173. let (x, y) = self.position;
  174. let r = self.radius;
  175. xml_tag(
  176. buf, "circle",
  177. &[("cx", &x),
  178. ("cy", &y),
  179. ("r", &r),
  180. ("stroke", &"black"),
  181. ("fill", &"white"),
  182. ]
  183. );
  184. }
  185. fn consume(self) -> Box<AsSVG> {
  186. Box::new(self)
  187. }
  188. }