1 use std::fmt;
2
3 /// `rgb({r},{g},{b})`
4 #[derive(Copy, Clone, PartialEq)]
5 pub struct Color {
6 pub r: u8,
7 pub g: u8,
8 pub b: u8,
9 }
10
11 impl fmt::Display for Color {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result12 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
13 write!(f, "rgb({},{},{})", self.r, self.g, self.b)
14 }
15 }
16
rgb(r: u8, g: u8, b: u8) -> Color17 pub fn rgb(r: u8, g: u8, b: u8) -> Color { Color { r, g, b } }
black() -> Color18 pub fn black() -> Color { rgb(0, 0, 0) }
white() -> Color19 pub fn white() -> Color { rgb(255, 255, 255) }
red() -> Color20 pub fn red() -> Color { rgb(255, 0, 0) }
green() -> Color21 pub fn green() -> Color { rgb(0, 255, 0) }
blue() -> Color22 pub fn blue() -> Color { rgb(0, 0, 255) }
23
24 /// `fill:{self}`
25 #[derive(Copy, Clone, PartialEq)]
26 pub enum Fill {
27 Color(Color),
28 None,
29 }
30
31 /// `stroke:{self}`
32 #[derive(Copy, Clone, PartialEq)]
33 pub enum Stroke {
34 Color(Color, f32),
35 None,
36 }
37
38 /// `fill:{fill};stroke:{stroke};fill-opacity:{opacity};`
39 #[derive(Copy, Clone, PartialEq)]
40 pub struct Style {
41 pub fill: Fill,
42 pub stroke: Stroke,
43 pub opacity: f32,
44 pub stroke_opacity: f32,
45 }
46
47 impl fmt::Display for Style {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result48 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49 write!(f, "{};{};fill-opacity:{};stroke-opacity:{};",
50 self.fill,
51 self.stroke,
52 self.opacity,
53 self.stroke_opacity,
54 )
55 }
56 }
57
58 impl Style {
default() -> Self59 pub fn default() -> Self {
60 Style {
61 fill: Fill::Color(black()),
62 stroke: Stroke::None,
63 opacity: 1.0,
64 stroke_opacity: 1.0,
65 }
66 }
67 }
68
69 impl fmt::Display for Fill {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result70 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71 match self {
72 Fill::Color(color) => write!(f, "fill:{}", color),
73 Fill::None => write!(f, "fill:none"),
74 }
75 }
76 }
77
78 impl fmt::Display for Stroke {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result79 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80 match self {
81 Stroke::Color(color, radius) => write!(f, "stroke:{};stroke-width:{}", color, radius),
82 Stroke::None => write!(f, "stroke:none"),
83 }
84 }
85 }
86
87 impl Into<Fill> for Color {
into(self) -> Fill88 fn into(self) -> Fill {
89 Fill::Color(self)
90 }
91 }
92
93 impl Into<Stroke> for Color {
into(self) -> Stroke94 fn into(self) -> Stroke {
95 Stroke::Color(self, 1.0)
96 }
97 }
98
99 /// `<rect x="{x}" y="{y}" width="{w}" height="{h}" ... />`,
100 #[derive(Copy, Clone, PartialEq)]
101 pub struct Rectangle {
102 pub x: f32,
103 pub y: f32,
104 pub w: f32,
105 pub h: f32,
106 pub style: Style,
107 pub border_radius: f32,
108 }
109
rectangle(x: f32, y: f32, w: f32, h: f32) -> Rectangle110 pub fn rectangle(x: f32, y: f32, w: f32, h: f32) -> Rectangle {
111 Rectangle {
112 x, y, w, h,
113 style: Style::default(),
114 border_radius: 0.0,
115 }
116 }
117
118 impl Rectangle {
fill<F>(mut self, fill: F) -> Self where F: Into<Fill>119 pub fn fill<F>(mut self, fill: F) -> Self
120 where F: Into<Fill> {
121 self.style.fill = fill.into();
122 self
123 }
124
stroke<S>(mut self, stroke: S) -> Self where S: Into<Stroke>125 pub fn stroke<S>(mut self, stroke: S) -> Self
126 where S: Into<Stroke> {
127 self.style.stroke = stroke.into();
128 self
129 }
130
opacity(mut self, opacity: f32) -> Self131 pub fn opacity(mut self, opacity: f32) -> Self {
132 self.style.opacity = opacity;
133 self
134 }
135
stroke_opacity(mut self, opacity: f32) -> Self136 pub fn stroke_opacity(mut self, opacity: f32) -> Self {
137 self.style.stroke_opacity = opacity;
138 self
139 }
140
style(mut self, style: Style) -> Self141 pub fn style(mut self, style: Style) -> Self {
142 self.style = style;
143 self
144 }
145
border_radius(mut self, r: f32) -> Self146 pub fn border_radius(mut self, r: f32) -> Self {
147 self.border_radius = r;
148 self
149 }
150
offset(mut self, dx: f32, dy: f32) -> Self151 pub fn offset(mut self, dx: f32, dy: f32) -> Self {
152 self.x += dx;
153 self.y += dy;
154 self
155 }
156
inflate(mut self, dx: f32, dy: f32) -> Self157 pub fn inflate(mut self, dx: f32, dy: f32) -> Self {
158 self.x -= dx;
159 self.y -= dy;
160 self.w += 2.0 * dx;
161 self.h += 2.0 * dy;
162 self
163 }
164 }
165
166 impl fmt::Display for Rectangle {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result167 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
168 write!(f,
169 r#"<rect x="{}" y="{}" width="{}" height="{}" ry="{}" style="{}" />""#,
170 self.x, self.y, self.w, self.h,
171 self.border_radius,
172 self.style,
173 )
174 }
175 }
176
177 /// `<circle cx="{x}" cy="{y}" r="{radius}" .../>`
178 #[derive(Copy, Clone, PartialEq)]
179 pub struct Circle {
180 pub x: f32,
181 pub y: f32,
182 pub radius: f32,
183 pub style: Style,
184 }
185
186 impl Circle {
fill<F>(mut self, fill: F) -> Self where F: Into<Fill>187 pub fn fill<F>(mut self, fill: F) -> Self
188 where F: Into<Fill> {
189 self.style.fill = fill.into();
190 self
191 }
192
stroke<S>(mut self, stroke: S) -> Self where S: Into<Stroke>193 pub fn stroke<S>(mut self, stroke: S) -> Self
194 where S: Into<Stroke> {
195 self.style.stroke = stroke.into();
196 self
197 }
198
style(mut self, style: Style) -> Self199 pub fn style(mut self, style: Style) -> Self {
200 self.style = style;
201 self
202 }
203
opacity(mut self, opacity: f32) -> Self204 pub fn opacity(mut self, opacity: f32) -> Self {
205 self.style.opacity = opacity;
206 self
207 }
208
stroke_opacity(mut self, opacity: f32) -> Self209 pub fn stroke_opacity(mut self, opacity: f32) -> Self {
210 self.style.stroke_opacity = opacity;
211 self
212 }
213
offset(mut self, dx: f32, dy: f32) -> Self214 pub fn offset(mut self, dx: f32, dy: f32) -> Self {
215 self.x += dx;
216 self.y += dy;
217 self
218 }
219
inflate(mut self, by: f32) -> Self220 pub fn inflate(mut self, by: f32) -> Self {
221 self.radius += by;
222 self
223 }
224 }
225
226 impl fmt::Display for Circle {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result227 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
228 write!(f,
229 r#"<circle cx="{}" cy="{}" r="{}" style="{}" />""#,
230 self.x, self.y, self.radius,
231 self.style,
232 )
233 }
234 }
235
236 /// `<path d="..." style="..."/>`
237 #[derive(Clone, PartialEq)]
238 pub struct Polygon {
239 pub points: Vec<[f32; 2]>,
240 pub closed: bool,
241 pub style: Style,
242 }
243
244 impl fmt::Display for Polygon {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result245 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
246 write!(f, r#"<path d="#)?;
247 if self.points.len() > 0 {
248 write!(f, "M {} {} ", self.points[0][0], self.points[0][1])?;
249 for &p in &self.points[1..] {
250 write!(f, "L {} {} ", p[0], p[1])?;
251 }
252 if self.closed {
253 write!(f, "Z")?;
254 }
255 }
256 write!(f, r#"" style="{}"/>"#, self.style)
257 }
258 }
259
polygon<T: Copy + Into<[f32; 2]>>(pts: &[T]) -> Polygon260 pub fn polygon<T: Copy + Into<[f32; 2]>>(pts: &[T]) -> Polygon {
261 let mut points = Vec::with_capacity(pts.len());
262 for p in pts {
263 points.push((*p).into());
264 }
265 Polygon {
266 points,
267 closed: true,
268 style: Style::default(),
269 }
270 }
271
triangle(x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) -> Polygon272 pub fn triangle(x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) -> Polygon {
273 polygon(&[[x1, y1], [x2, y2], [x3, y3]])
274 }
275
276 impl Polygon {
open(mut self) -> Self277 pub fn open(mut self) -> Self {
278 self.closed = false;
279 self
280 }
281
fill<F>(mut self, fill: F) -> Self where F: Into<Fill>282 pub fn fill<F>(mut self, fill: F) -> Self
283 where F: Into<Fill> {
284 self.style.fill = fill.into();
285 self
286 }
287
stroke<S>(mut self, stroke: S) -> Self where S: Into<Stroke>288 pub fn stroke<S>(mut self, stroke: S) -> Self
289 where S: Into<Stroke> {
290 self.style.stroke = stroke.into();
291 self
292 }
293
opacity(mut self, opacity: f32) -> Self294 pub fn opacity(mut self, opacity: f32) -> Self {
295 self.style.opacity = opacity;
296 self
297 }
298
stroke_opacity(mut self, opacity: f32) -> Self299 pub fn stroke_opacity(mut self, opacity: f32) -> Self {
300 self.style.stroke_opacity = opacity;
301 self
302 }
303
style(mut self, style: Style) -> Self304 pub fn style(mut self, style: Style) -> Self {
305 self.style = style;
306 self
307 }
308 }
309
310 /// `<path d="M {x1} {y1} L {x2} {y2}" ... />`
311 #[derive(Copy, Clone, PartialEq)]
312 pub struct LineSegment {
313 pub x1: f32,
314 pub x2: f32,
315 pub y1: f32,
316 pub y2: f32,
317 pub color: Color,
318 pub width: f32,
319 }
320
321 impl fmt::Display for LineSegment {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result322 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
323 write!(f,
324 r#"<path d="M {} {} L {} {}" style="stroke:{};stroke-width:{}"/>"#,
325 self.x1, self.y1,
326 self.x2, self.y2,
327 self.color,
328 self.width,
329 )
330 }
331 }
332
line_segment(x1: f32, y1: f32, x2: f32, y2: f32) -> LineSegment333 pub fn line_segment(x1: f32, y1: f32, x2: f32, y2: f32) -> LineSegment {
334 LineSegment {
335 x1, y1, x2, y2,
336 color: black(),
337 width: 1.0,
338 }
339 }
340
341 impl LineSegment {
color(mut self, color: Color) -> Self342 pub fn color(mut self, color: Color) -> Self {
343 self.color = color;
344 self
345 }
346
width(mut self, width: f32) -> Self347 pub fn width(mut self, width: f32) -> Self {
348 self.width = width;
349 self
350 }
351
offset(mut self, dx: f32, dy: f32) -> Self352 pub fn offset(mut self, dx: f32, dy: f32) -> Self {
353 self.x1 += dx;
354 self.y1 += dy;
355 self.x2 += dx;
356 self.y2 += dy;
357 self
358 }
359 }
360
361 /// `<path d="..." />`
362 #[derive(Clone, PartialEq)]
363 pub struct Path {
364 pub ops: Vec<PathOp>,
365 pub style: Style,
366 }
367
368 /// `M {} {} L {} {} ...`
369 #[derive(Copy, Clone, PartialEq)]
370 pub enum PathOp {
371 MoveTo { x: f32, y: f32 },
372 LineTo { x: f32, y: f32 },
373 QuadraticTo { ctrl_x: f32, ctrl_y: f32, x: f32, y: f32 },
374 CubicTo { ctrl1_x: f32, ctrl1_y: f32, ctrl2_x: f32, ctrl2_y: f32, x: f32, y: f32 },
375 Close,
376 }
377 impl fmt::Display for PathOp {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result378 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
379 match *self {
380 PathOp::MoveTo { x, y } => write!(f, "M {} {} ", x, y),
381 PathOp::LineTo { x, y } => write!(f, "L {} {} ", x, y),
382 PathOp::QuadraticTo { ctrl_x, ctrl_y, x, y } => write!(f, "Q {} {} {} {} ", ctrl_x, ctrl_y, x, y),
383 PathOp::CubicTo { ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, x, y } => write!(f, "C {} {} {} {} {} {} ", ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, x, y),
384 PathOp::Close => write!(f, "Z "),
385 }
386 }
387 }
388
389 impl fmt::Display for Path {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result390 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
391 write!(f, r#"<path d=""#)?;
392 for op in &self.ops {
393 op.fmt(f)?;
394 }
395 write!(f, r#"" style="{}" />"#, self.style)
396 }
397 }
398
399 impl Path {
move_to(mut self, x: f32, y: f32) -> Self400 pub fn move_to(mut self, x: f32, y: f32) -> Self {
401 self.ops.push(PathOp::MoveTo { x, y });
402 self
403 }
404
line_to(mut self, x: f32, y: f32) -> Self405 pub fn line_to(mut self, x: f32, y: f32) -> Self {
406 self.ops.push(PathOp::LineTo { x, y });
407 self
408 }
409
quadratic_bezier_to( mut self, ctrl_x: f32, ctrl_y: f32, x: f32, y: f32, ) -> Self410 pub fn quadratic_bezier_to(
411 mut self,
412 ctrl_x: f32, ctrl_y: f32,
413 x: f32, y: f32,
414 ) -> Self {
415 self.ops.push(PathOp::QuadraticTo { ctrl_x, ctrl_y, x, y });
416 self
417 }
418
cubic_bezier_to( mut self, ctrl1_x: f32, ctrl1_y: f32, ctrl2_x: f32, ctrl2_y: f32, x: f32, y: f32, ) -> Self419 pub fn cubic_bezier_to(
420 mut self,
421 ctrl1_x: f32, ctrl1_y: f32,
422 ctrl2_x: f32, ctrl2_y: f32,
423 x: f32, y: f32,
424 ) -> Self {
425 self.ops.push(PathOp::CubicTo { ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, x, y });
426 self
427 }
428
close(mut self) -> Self429 pub fn close(mut self) -> Self {
430 self.ops.push(PathOp::Close);
431 self
432 }
433
fill<F>(mut self, fill: F) -> Self where F: Into<Fill>434 pub fn fill<F>(mut self, fill: F) -> Self
435 where F: Into<Fill> {
436 self.style.fill = fill.into();
437 self
438 }
439
stroke<S>(mut self, stroke: S) -> Self where S: Into<Stroke>440 pub fn stroke<S>(mut self, stroke: S) -> Self
441 where S: Into<Stroke> {
442 self.style.stroke = stroke.into();
443 self
444 }
445
opacity(mut self, opacity: f32) -> Self446 pub fn opacity(mut self, opacity: f32) -> Self {
447 self.style.opacity = opacity;
448 self
449 }
450
stroke_opacity(mut self, opacity: f32) -> Self451 pub fn stroke_opacity(mut self, opacity: f32) -> Self {
452 self.style.stroke_opacity = opacity;
453 self
454 }
455
style(mut self, style: Style) -> Self456 pub fn style(mut self, style: Style) -> Self {
457 self.style = style;
458 self
459 }
460 }
461
path() -> Path462 pub fn path() -> Path {
463 Path {
464 ops: Vec::new(),
465 style: Style::default(),
466 }
467 }
468
469 /// `<text x="{x}" y="{y}" ... > {text} </text>`
470 #[derive(Clone, PartialEq)]
471 pub struct Text {
472 pub x: f32, pub y: f32,
473 pub text: String,
474 pub color: Color,
475 pub align: Align,
476 pub size: f32,
477 }
478
479 impl fmt::Display for Text {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result480 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
481 write!(f,
482 r#"<text x="{}" y="{}" style="font-size:{}px;fill:{};{}"> {} </text>"#,
483 self.x, self.y,
484 self.size,
485 self.color,
486 self.align,
487 self.text,
488 )
489 }
490 }
491
text<T: Into<String>>(x: f32, y: f32, txt: T) -> Text492 pub fn text<T: Into<String>>(x: f32, y: f32, txt: T) -> Text {
493 Text {
494 x, y,
495 text: txt.into(),
496 color: black(),
497 align: Align::Left,
498 size: 10.0,
499 }
500 }
501
502 impl Text {
color(mut self, color: Color) -> Self503 pub fn color(mut self, color: Color) -> Self {
504 self.color = color;
505 self
506 }
507
size(mut self, size: f32) -> Self508 pub fn size(mut self, size: f32) -> Self {
509 self.size = size;
510 self
511 }
512
align(mut self, align: Align) -> Self513 pub fn align(mut self, align: Align) -> Self {
514 self.align = align;
515 self
516 }
517
offset(mut self, dx: f32, dy: f32) -> Self518 pub fn offset(mut self, dx: f32, dy: f32) -> Self {
519 self.x += dx;
520 self.y += dy;
521 self
522 }
523 }
524
525 pub struct Comment {
526 pub text: String,
527 }
528
comment<T: Into<String>>(text: T) -> Comment529 pub fn comment<T: Into<String>>(text: T) -> Comment {
530 Comment {
531 text: text.into()
532 }
533 }
534
535 impl fmt::Display for Comment {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result536 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
537 write!(f, "<!-- {} -->", self.text)
538 }
539 }
540
541 /// `text-align:{self}`
542 #[derive(Copy, Clone, PartialEq)]
543 pub enum Align {
544 Left, Right, Center
545 }
546
547 impl fmt::Display for Align {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result548 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
549 match *self {
550 Align::Left => write!(f, "text-anchor:start;text-align:left;"),
551 Align::Right => write!(f, "text-anchor:end;text-align:right;"),
552 Align::Center => write!(f, "text-anchor:middle;text-align:center;"),
553 }
554 }
555 }
556
557 /// `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {w} {y}">`
558 #[derive(Copy, Clone, PartialEq)]
559 pub struct BeginSvg {
560 pub w: f32,
561 pub h: f32,
562 }
563
564 impl fmt::Display for BeginSvg {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result565 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
566 write!(f,
567 r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {} {}">"#,
568 self.w,
569 self.h,
570 )
571 }
572 }
573
574
575 /// `</svg>`
576 #[derive(Copy, Clone, PartialEq)]
577 pub struct EndSvg;
578
579 impl fmt::Display for EndSvg {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result580 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
581 write!(f, "</svg>")
582 }
583 }
584
585 /// `" "`
586 pub struct Indentation {
587 pub n: u32,
588 }
589
indent(n: u32) -> Indentation590 pub fn indent(n: u32) -> Indentation {
591 Indentation { n }
592 }
593
594 impl Indentation {
push(&mut self)595 pub fn push(&mut self) {
596 self.n += 1;
597 }
598
pop(&mut self)599 pub fn pop(&mut self) {
600 self.n -= 1;
601 }
602 }
603
604 impl fmt::Display for Indentation {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result605 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
606 for _ in 0..self.n {
607 write!(f, " ")?;
608 }
609 Ok(())
610 }
611 }
612
613 #[test]
foo()614 fn foo() {
615 println!("{}", BeginSvg { w: 800.0, h: 600.0 });
616 println!(" {}",
617 rectangle(20.0, 50.0, 200.0, 100.0)
618 .fill(red())
619 .stroke(Stroke::Color(black(), 3.0))
620 .border_radius(5.0)
621 );
622 println!(" {}", text(25.0, 100.0, "Foo!").size(42.0).color(white()));
623 println!("{}", EndSvg);
624 }
625