lib.rs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. use std::fmt::{self, Display, Formatter};
  2. use std::fs::OpenOptions;
  3. use std::io::{self, Write};
  4. const MAX_OUTPUT_FILES: u32 = 500;
  5. /// An SVG document
  6. pub struct SVG {
  7. stuff: Vec<Box<AsSVG>>,
  8. size: (f64, f64),
  9. }
  10. #[derive(Copy, Clone)]
  11. pub struct Inches { amt: f64 }
  12. impl Display for Inches {
  13. fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
  14. self.amt.fmt(f)?;
  15. write!(f, "in")
  16. }
  17. }
  18. fn inches(amt: f64) -> Inches { Inches { amt } }
  19. fn xml_tag(w: &mut Write, name: &str, attrs: &[(&str, &Display)]) -> io::Result<()> {
  20. write!(w, "<{}", name)?;
  21. for &(k, v) in attrs {
  22. write!(w, " {}=\"{}\"", k, v)?;
  23. }
  24. writeln!(w, "/>")?;
  25. Ok(())
  26. }
  27. fn xml_open(w: &mut Write, name: &str, attrs: &[(&str, &Display)]) -> io::Result<()> {
  28. write!(w, "<{}", name)?;
  29. for &(k, v) in attrs {
  30. write!(w, " {}=\"{}\"", k, v)?;
  31. }
  32. writeln!(w, ">")?;
  33. Ok(())
  34. }
  35. fn xml_close(w: &mut Write, name: &str) -> io::Result<()> {
  36. write!(w, "</{}>", name)?;
  37. Ok(())
  38. }
  39. /// Create a new empty SVG document of the specified width and height
  40. pub fn svg(w: f64, h: f64) -> SVG {
  41. SVG {
  42. stuff: Vec::new(),
  43. size: (w, h),
  44. }
  45. }
  46. impl SVG {
  47. /// Print this SVG document to stdout
  48. pub fn to_stdout(self) -> io::Result<()> {
  49. self.write_svg(&mut std::io::stdout())
  50. }
  51. pub fn to_bytes(self) -> io::Result<Vec<u8>> {
  52. let mut buf = Vec::new();
  53. self.write_svg(&mut buf)?;
  54. Ok(buf)
  55. }
  56. /// Print this SVG document to stdout
  57. pub fn output(self, p: &str) -> io::Result<()> {
  58. let mut file = {
  59. let mut n = 0;
  60. let mut path = format!("output/{}{:05}.svg", p, n);
  61. let mut f = OpenOptions::new().write(true).create_new(true).open(&path);
  62. loop {
  63. match f {
  64. Ok(_) => break,
  65. Err(ref e) if e.kind() != io::ErrorKind::AlreadyExists =>
  66. return Err(io::Error::new(e.kind(), "failed to create file")),
  67. _ if n > MAX_OUTPUT_FILES =>
  68. return Err(io::Error::new(io::ErrorKind::Other, "Too many output files already")),
  69. _ => (),
  70. }
  71. n += 1;
  72. path = format!("output/{}{:05}.svg", p, n);
  73. f = OpenOptions::new().write(true).create_new(true).open(&path);
  74. }
  75. f.unwrap()
  76. };
  77. self.write_svg(&mut file)
  78. }
  79. pub fn write_svg<W: Write>(self, buf: &mut W) -> io::Result<()> {
  80. let (w, h) = self.size;
  81. writeln!(buf, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>")?;
  82. xml_open(
  83. buf, "svg",
  84. &[("xmlns", &"http://www.w3.org/2000/svg"),
  85. ("version", &"1.1"),
  86. ("width", &inches(w)),
  87. ("height", &inches(h)),
  88. ("viewBox", &format!("0 0 {} {}", w, h)),
  89. ("stroke-width", &"0.0001in"),
  90. ],
  91. )?;
  92. for elem in self.stuff {
  93. elem.draw_svg(buf)?;
  94. }
  95. xml_close(buf, "svg")
  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 Write) -> io::Result<()>;
  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 Write) -> io::Result<()> {
  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.01"),
  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),
  148. ("stroke", &"black"),
  149. ("fill", &"none"),
  150. ],
  151. )
  152. }
  153. }
  154. fn consume(self) -> Box<AsSVG> {
  155. Box::new(self)
  156. }
  157. }
  158. /// Create a new line at the given starting point
  159. pub fn line(x: f64, y: f64) -> Line {
  160. Line::new(x, y)
  161. }
  162. impl Line {
  163. /// Create a new line at the given starting point
  164. pub fn new(x: f64, y: f64) -> Line {
  165. Line {
  166. start: (x, y),
  167. points: Vec::new(),
  168. }
  169. }
  170. /// Draw a line segment from this point to another point
  171. pub fn to(mut self, x: f64, y: f64) -> Line {
  172. self.points.push((x, y));
  173. self
  174. }
  175. }
  176. pub struct Rect {
  177. position: (f64, f64),
  178. size: (f64, f64),
  179. }
  180. pub fn rect(position: (f64, f64), size: (f64, f64)) -> Rect {
  181. Rect { position, size }
  182. }
  183. impl AsSVG for Rect {
  184. fn draw_svg(&self, buf: &mut Write) -> io::Result<()> {
  185. let (x, y) = self.position;
  186. let (w, h) = self.size;
  187. xml_tag(
  188. buf, "rect",
  189. &[("x", &x),
  190. ("y", &y),
  191. ("width", &w),
  192. ("height", &h),
  193. ("stroke", &"black"),
  194. ("fill", &"white"),
  195. ]
  196. )
  197. }
  198. fn consume(self) -> Box<AsSVG> {
  199. Box::new(self)
  200. }
  201. }
  202. pub struct Circle {
  203. position: (f64, f64),
  204. radius: f64,
  205. }
  206. pub fn circle(position: (f64, f64), radius: f64) -> Circle {
  207. Circle { position, radius }
  208. }
  209. impl AsSVG for Circle {
  210. fn draw_svg(&self, buf: &mut Write) -> io::Result<()> {
  211. let (x, y) = self.position;
  212. let r = self.radius;
  213. xml_tag(
  214. buf, "circle",
  215. &[("cx", &x),
  216. ("cy", &y),
  217. ("r", &r),
  218. ("stroke", &"black"),
  219. ("fill", &"white"),
  220. ]
  221. )
  222. }
  223. fn consume(self) -> Box<AsSVG> {
  224. Box::new(self)
  225. }
  226. }