1use alloc::string::String;
2use alloc::vec::Vec;
3use core::fmt;
4
5#[derive(Copy, Clone, PartialEq)]
7pub struct Color {
8 pub r: u8,
9 pub g: u8,
10 pub b: u8,
11}
12
13impl Default for Color {
14 fn default() -> Self {
15 black()
16 }
17}
18
19impl fmt::Display for Color {
20 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
21 write!(f, "rgb({},{},{})", self.r, self.g, self.b)
22 }
23}
24
25pub fn rgb(r: u8, g: u8, b: u8) -> Color {
26 Color { r, g, b }
27}
28pub fn black() -> Color {
29 rgb(0, 0, 0)
30}
31pub fn white() -> Color {
32 rgb(255, 255, 255)
33}
34pub fn red() -> Color {
35 rgb(255, 0, 0)
36}
37pub fn green() -> Color {
38 rgb(0, 255, 0)
39}
40pub fn blue() -> Color {
41 rgb(0, 0, 255)
42}
43
44#[derive(Copy, Clone, PartialEq)]
46pub enum Fill {
47 Color(Color),
48 None,
49}
50
51impl Default for Fill {
52 fn default() -> Self {
53 Fill::None
54 }
55}
56
57#[derive(Copy, Clone, PartialEq)]
59pub enum Stroke {
60 Color(Color, f32),
61 None,
62}
63
64impl Default for Stroke {
65 fn default() -> Self {
66 Stroke::Color(black(), 1.0)
67 }
68}
69
70#[derive(Copy, Clone, PartialEq)]
72pub struct Style {
73 pub fill: Fill,
74 pub stroke: Stroke,
75 pub opacity: f32,
76 pub stroke_opacity: f32,
77}
78
79impl fmt::Display for Style {
80 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81 write!(
82 f,
83 "{};{};fill-opacity:{};stroke-opacity:{};",
84 self.fill, self.stroke, self.opacity, self.stroke_opacity,
85 )
86 }
87}
88
89impl Default for Style {
90 fn default() -> Self {
91 Style {
92 fill: Fill::Color(black()),
93 stroke: Stroke::None,
94 opacity: 1.0,
95 stroke_opacity: 1.0,
96 }
97 }
98}
99
100impl From<Fill> for Style {
101 fn from(fill: Fill) -> Style {
102 Style {
103 fill,
104 stroke: Stroke::None,
105 .. Default::default()
106 }
107 }
108}
109
110impl From<Stroke> for Style {
111 fn from(stroke: Stroke) -> Style {
112 Style {
113 fill: Fill::None,
114 stroke,
115 .. Default::default()
116 }
117 }
118}
119
120impl fmt::Display for Fill {
121 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122 match self {
123 Fill::Color(color) => write!(f, "fill:{}", color),
124 Fill::None => write!(f, "fill:none"),
125 }
126 }
127}
128
129impl fmt::Display for Stroke {
130 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131 match self {
132 Stroke::Color(color, radius) => write!(f, "stroke:{};stroke-width:{}", color, radius),
133 Stroke::None => write!(f, "stroke:none"),
134 }
135 }
136}
137
138impl Into<Fill> for Color {
139 fn into(self) -> Fill {
140 Fill::Color(self)
141 }
142}
143
144impl Into<Stroke> for Color {
145 fn into(self) -> Stroke {
146 Stroke::Color(self, 1.0)
147 }
148}
149
150#[derive(Clone, PartialEq)]
152pub struct Rectangle {
153 pub x: f32,
154 pub y: f32,
155 pub w: f32,
156 pub h: f32,
157 pub style: Style,
158 pub border_radius: f32,
159 pub comment: Option<Comment>,
160}
161
162pub fn rectangle(x: f32, y: f32, w: f32, h: f32) -> Rectangle {
163 Rectangle {
164 x,
165 y,
166 w,
167 h,
168 style: Style::default(),
169 border_radius: 0.0,
170 comment: None,
171 }
172}
173
174impl Rectangle {
175 pub fn fill<F>(mut self, fill: F) -> Self
176 where
177 F: Into<Fill>,
178 {
179 self.style.fill = fill.into();
180 self
181 }
182
183 pub fn stroke<S>(mut self, stroke: S) -> Self
184 where
185 S: Into<Stroke>,
186 {
187 self.style.stroke = stroke.into();
188 self
189 }
190
191 pub fn opacity(mut self, opacity: f32) -> Self {
192 self.style.opacity = opacity;
193 self
194 }
195
196 pub fn stroke_opacity(mut self, opacity: f32) -> Self {
197 self.style.stroke_opacity = opacity;
198 self
199 }
200
201 pub fn style(mut self, style: Style) -> Self {
202 self.style = style;
203 self
204 }
205
206 pub fn border_radius(mut self, r: f32) -> Self {
207 self.border_radius = r;
208 self
209 }
210
211 pub fn offset(mut self, dx: f32, dy: f32) -> Self {
212 self.x += dx;
213 self.y += dy;
214 self
215 }
216
217 pub fn inflate(mut self, dx: f32, dy: f32) -> Self {
218 self.x -= dx;
219 self.y -= dy;
220 self.w += 2.0 * dx;
221 self.h += 2.0 * dy;
222 self
223 }
224
225 pub fn comment(mut self, text: &str) -> Self {
226 self.comment = Some(comment(text));
227 self
228 }
229}
230
231impl fmt::Display for Rectangle {
232 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
233 write!(
234 f,
235 r#"<rect x="{}" y="{}" width="{}" height="{}" ry="{}" style="{}""#,
236 self.x, self.y, self.w, self.h, self.border_radius, self.style,
237 )?;
238 if let Some(comment) = &self.comment {
239 write!(f, r#">{}</rect>"#, comment)?;
240 } else {
241 write!(f, r#" />"#)?;
242 }
243 Ok(())
244 }
245}
246
247#[derive(Clone, PartialEq)]
249pub struct Circle {
250 pub x: f32,
251 pub y: f32,
252 pub radius: f32,
253 pub style: Style,
254 pub comment: Option<Comment>,
255}
256
257impl Circle {
258 pub fn fill<F>(mut self, fill: F) -> Self
259 where
260 F: Into<Fill>,
261 {
262 self.style.fill = fill.into();
263 self
264 }
265
266 pub fn stroke<S>(mut self, stroke: S) -> Self
267 where
268 S: Into<Stroke>,
269 {
270 self.style.stroke = stroke.into();
271 self
272 }
273
274 pub fn style(mut self, style: Style) -> Self {
275 self.style = style;
276 self
277 }
278
279 pub fn opacity(mut self, opacity: f32) -> Self {
280 self.style.opacity = opacity;
281 self
282 }
283
284 pub fn stroke_opacity(mut self, opacity: f32) -> Self {
285 self.style.stroke_opacity = opacity;
286 self
287 }
288
289 pub fn offset(mut self, dx: f32, dy: f32) -> Self {
290 self.x += dx;
291 self.y += dy;
292 self
293 }
294
295 pub fn inflate(mut self, by: f32) -> Self {
296 self.radius += by;
297 self
298 }
299
300 pub fn comment(mut self, text: &str) -> Self {
301 self.comment = Some(comment(text));
302 self
303 }
304}
305
306impl fmt::Display for Circle {
307 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
308 write!(
309 f,
310 r#"<circle cx="{}" cy="{}" r="{}" style="{}""#,
311 self.x, self.y, self.radius, self.style,
312 )?;
313 if let Some(comment) = &self.comment {
314 write!(f, r#">{}</circle>"#, comment)?;
315 } else {
316 write!(f, r#" />"#)?;
317 }
318 Ok(())
319 }
320}
321
322#[derive(Clone, PartialEq)]
324pub struct Polygon {
325 pub points: Vec<[f32; 2]>,
326 pub closed: bool,
327 pub style: Style,
328 pub comment: Option<Comment>,
329}
330
331impl fmt::Display for Polygon {
332 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
333 write!(f, r#"<path d="#)?;
334 if self.points.len() > 0 {
335 write!(f, "M {} {} ", self.points[0][0], self.points[0][1])?;
336 for &p in &self.points[1..] {
337 write!(f, "L {} {} ", p[0], p[1])?;
338 }
339 if self.closed {
340 write!(f, "Z")?;
341 }
342 }
343 write!(f, r#"" style="{}"#, self.style)?;
344 if let Some(comment) = &self.comment {
345 write!(f, r#">{}</path>"#, comment)?;
346 } else {
347 write!(f, r#" />"#)?;
348 }
349 Ok(())
350 }
351}
352
353pub fn polygon<T: Copy + Into<[f32; 2]>>(pts: &[T]) -> Polygon {
354 let mut points = Vec::with_capacity(pts.len());
355 for p in pts {
356 points.push((*p).into());
357 }
358 Polygon {
359 points,
360 closed: true,
361 style: Style::default(),
362 comment: None,
363 }
364}
365
366pub fn triangle(x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) -> Polygon {
367 polygon(&[[x1, y1], [x2, y2], [x3, y3]])
368}
369
370impl Polygon {
371 pub fn open(mut self) -> Self {
372 self.closed = false;
373 self
374 }
375
376 pub fn fill<F>(mut self, fill: F) -> Self
377 where
378 F: Into<Fill>,
379 {
380 self.style.fill = fill.into();
381 self
382 }
383
384 pub fn stroke<S>(mut self, stroke: S) -> Self
385 where
386 S: Into<Stroke>,
387 {
388 self.style.stroke = stroke.into();
389 self
390 }
391
392 pub fn opacity(mut self, opacity: f32) -> Self {
393 self.style.opacity = opacity;
394 self
395 }
396
397 pub fn stroke_opacity(mut self, opacity: f32) -> Self {
398 self.style.stroke_opacity = opacity;
399 self
400 }
401
402 pub fn style(mut self, style: Style) -> Self {
403 self.style = style;
404 self
405 }
406
407 pub fn comment(mut self, text: &str) -> Self {
408 self.comment = Some(comment(text));
409 self
410 }
411}
412
413#[derive(Clone, PartialEq)]
415pub struct LineSegment {
416 pub x1: f32,
417 pub x2: f32,
418 pub y1: f32,
419 pub y2: f32,
420 pub color: Color,
421 pub width: f32,
422 pub comment: Option<Comment>,
423}
424
425impl fmt::Display for LineSegment {
426 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
427 write!(
428 f,
429 r#"<path d="M {} {} L {} {}" style="stroke:{};stroke-width:{}""#,
430 self.x1, self.y1, self.x2, self.y2, self.color, self.width
431 )?;
432 if let Some(comment) = &self.comment {
433 write!(f, r#">{}</path>"#, comment)?;
434 } else {
435 write!(f, r#" />"#)?;
436 }
437 Ok(())
438 }
439}
440
441pub fn line_segment(x1: f32, y1: f32, x2: f32, y2: f32) -> LineSegment {
442 LineSegment {
443 x1,
444 y1,
445 x2,
446 y2,
447 color: black(),
448 width: 1.0,
449 comment: None,
450 }
451}
452
453impl LineSegment {
454 pub fn color(mut self, color: Color) -> Self {
455 self.color = color;
456 self
457 }
458
459 pub fn width(mut self, width: f32) -> Self {
460 self.width = width;
461 self
462 }
463
464 pub fn offset(mut self, dx: f32, dy: f32) -> Self {
465 self.x1 += dx;
466 self.y1 += dy;
467 self.x2 += dx;
468 self.y2 += dy;
469 self
470 }
471
472 pub fn comment(mut self, text: &str) -> Self {
473 self.comment = Some(comment(text));
474 self
475 }
476}
477
478#[derive(Clone, PartialEq)]
480pub struct Path {
481 pub ops: Vec<PathOp>,
482 pub style: Style,
483 pub comment: Option<Comment>,
484}
485
486#[derive(Copy, Clone, PartialEq)]
488pub enum PathOp {
489 MoveTo {
490 x: f32,
491 y: f32,
492 },
493 LineTo {
494 x: f32,
495 y: f32,
496 },
497 QuadraticTo {
498 ctrl_x: f32,
499 ctrl_y: f32,
500 x: f32,
501 y: f32,
502 },
503 CubicTo {
504 ctrl1_x: f32,
505 ctrl1_y: f32,
506 ctrl2_x: f32,
507 ctrl2_y: f32,
508 x: f32,
509 y: f32,
510 },
511 Close,
512}
513impl fmt::Display for PathOp {
514 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
515 match *self {
516 PathOp::MoveTo { x, y } => write!(f, "M {} {} ", x, y),
517 PathOp::LineTo { x, y } => write!(f, "L {} {} ", x, y),
518 PathOp::QuadraticTo {
519 ctrl_x,
520 ctrl_y,
521 x,
522 y,
523 } => write!(f, "Q {} {} {} {} ", ctrl_x, ctrl_y, x, y),
524 PathOp::CubicTo {
525 ctrl1_x,
526 ctrl1_y,
527 ctrl2_x,
528 ctrl2_y,
529 x,
530 y,
531 } => write!(
532 f,
533 "C {} {} {} {} {} {} ",
534 ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, x, y
535 ),
536 PathOp::Close => write!(f, "Z "),
537 }
538 }
539}
540
541impl fmt::Display for Path {
542 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
543 write!(f, r#"<path d=""#)?;
544 for op in &self.ops {
545 op.fmt(f)?;
546 }
547 write!(f, r#"" style="{}""#, self.style)?;
548 if let Some(comment) = &self.comment {
549 write!(f, r#">{}</path>"#, comment)?;
550 } else {
551 write!(f, r#"/>"#)?;
552 }
553 Ok(())
554 }
555}
556
557impl Path {
558 pub fn move_to(mut self, x: f32, y: f32) -> Self {
559 self.ops.push(PathOp::MoveTo { x, y });
560 self
561 }
562
563 pub fn line_to(mut self, x: f32, y: f32) -> Self {
564 self.ops.push(PathOp::LineTo { x, y });
565 self
566 }
567
568 pub fn quadratic_bezier_to(mut self, ctrl_x: f32, ctrl_y: f32, x: f32, y: f32) -> Self {
569 self.ops.push(PathOp::QuadraticTo {
570 ctrl_x,
571 ctrl_y,
572 x,
573 y,
574 });
575 self
576 }
577
578 pub fn cubic_bezier_to(
579 mut self,
580 ctrl1_x: f32,
581 ctrl1_y: f32,
582 ctrl2_x: f32,
583 ctrl2_y: f32,
584 x: f32,
585 y: f32,
586 ) -> Self {
587 self.ops.push(PathOp::CubicTo {
588 ctrl1_x,
589 ctrl1_y,
590 ctrl2_x,
591 ctrl2_y,
592 x,
593 y,
594 });
595 self
596 }
597
598 pub fn close(mut self) -> Self {
599 self.ops.push(PathOp::Close);
600 self
601 }
602
603 pub fn fill<F>(mut self, fill: F) -> Self
604 where
605 F: Into<Fill>,
606 {
607 self.style.fill = fill.into();
608 self
609 }
610
611 pub fn stroke<S>(mut self, stroke: S) -> Self
612 where
613 S: Into<Stroke>,
614 {
615 self.style.stroke = stroke.into();
616 self
617 }
618
619 pub fn opacity(mut self, opacity: f32) -> Self {
620 self.style.opacity = opacity;
621 self
622 }
623
624 pub fn stroke_opacity(mut self, opacity: f32) -> Self {
625 self.style.stroke_opacity = opacity;
626 self
627 }
628
629 pub fn style(mut self, style: Style) -> Self {
630 self.style = style;
631 self
632 }
633}
634
635pub fn path() -> Path {
636 Path {
637 ops: Vec::new(),
638 style: Style::default(),
639 comment: None,
640 }
641}
642
643#[derive(Clone, PartialEq)]
645pub struct Text {
646 pub x: f32,
647 pub y: f32,
648 pub text: String,
649 pub color: Color,
650 pub align: Align,
651 pub size: f32,
652 pub comment: Option<Comment>,
653}
654
655impl fmt::Display for Text {
656 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
657 write!(
658 f,
659 r#"<text x="{}" y="{}" style="font-size:{}px;fill:{};{}">"#,
660 self.x, self.y, self.size, self.color, self.align,
661 )?;
662 if let Some(comment) = &self.comment {
663 write!(f, r#" {}"#, comment)?;
664 }
665 write!(f, r#" {} </text>"#, self.text)
666 }
667}
668
669pub fn text<T: Into<String>>(x: f32, y: f32, txt: T) -> Text {
670 Text {
671 x,
672 y,
673 text: txt.into(),
674 color: black(),
675 align: Align::Left,
676 size: 10.0,
677 comment: None,
678 }
679}
680
681impl Text {
682 pub fn color(mut self, color: Color) -> Self {
683 self.color = color;
684 self
685 }
686
687 pub fn size(mut self, size: f32) -> Self {
688 self.size = size;
689 self
690 }
691
692 pub fn align(mut self, align: Align) -> Self {
693 self.align = align;
694 self
695 }
696
697 pub fn offset(mut self, dx: f32, dy: f32) -> Self {
698 self.x += dx;
699 self.y += dy;
700 self
701 }
702
703 pub fn comment(mut self, text: &str) -> Self {
704 self.comment = Some(comment(text));
705 self
706 }
707}
708
709#[derive(Clone, PartialEq)]
710pub struct Comment {
711 pub text: String,
712}
713
714pub fn comment<T: Into<String>>(text: T) -> Comment {
715 Comment { text: text.into() }
716}
717
718impl fmt::Display for Comment {
719 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
720 write!(f, "<!-- {} -->", self.text)
721 }
722}
723
724#[derive(Copy, Clone, PartialEq)]
726pub enum Align {
727 Left,
728 Right,
729 Center,
730}
731
732impl fmt::Display for Align {
733 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
734 match *self {
735 Align::Left => write!(f, "text-anchor:start;text-align:left;"),
736 Align::Right => write!(f, "text-anchor:end;text-align:right;"),
737 Align::Center => write!(f, "text-anchor:middle;text-align:center;"),
738 }
739 }
740}
741
742#[derive(Copy, Clone, PartialEq)]
744pub struct BeginSvg {
745 pub w: f32,
746 pub h: f32,
747}
748
749impl fmt::Display for BeginSvg {
750 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
751 write!(
752 f,
753 r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {} {}">"#,
754 self.w, self.h,
755 )
756 }
757}
758
759#[derive(Copy, Clone, PartialEq)]
761pub struct EndSvg;
762
763impl fmt::Display for EndSvg {
764 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
765 write!(f, "</svg>")
766 }
767}
768
769pub struct Indentation {
771 pub n: u32,
772}
773
774pub fn indent(n: u32) -> Indentation {
775 Indentation { n }
776}
777
778impl Indentation {
779 pub fn push(&mut self) {
780 self.n += 1;
781 }
782
783 pub fn pop(&mut self) {
784 self.n -= 1;
785 }
786}
787
788impl fmt::Display for Indentation {
789 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
790 for _ in 0..self.n {
791 write!(f, " ")?;
792 }
793 Ok(())
794 }
795}