lib.rs 6.5 KB

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