#![allow(dead_code)]
use std::fmt::Write as _;
use std::io;
use std::path::Path;
use std::{fs::File, io::Write};
use robust::{Coord, orient2d};
use crate::prelude::*;
pub struct SVG {
writer: Box<dyn Write>,
s: String,
pub xsize: f64,
pub ysize: f64,
}
impl SVG {
#[inline]
pub fn new(xsize: f64, ysize: f64, out: Option<&str>) -> Self {
let out_writer = match out {
Some(x) => {
let path = Path::new(x);
Box::new(File::create(&path).unwrap()) as Box<dyn Write>
}
None => Box::new(io::stdout()) as Box<dyn Write>,
};
let s = String::new();
SVG { writer: out_writer, s, xsize, ysize }
}
}
#[inline]
#[must_use]
pub fn svg(xsize: f64, ysize: f64) -> SVG {
SVG::new(xsize, ysize, None)
}
impl SVG {
pub fn write(&mut self) {
self.write_stroke_width(0.2);
}
pub fn write_stroke_width(&mut self, stroke_width: f64) {
let mut header = String::new();
write!(
&mut header,
r#"<svg viewBox="0 0 {} {}" xmlns="http://www.w3.org/2000/svg" fill="none" stroke-width="{}" stroke-linecap="round">"#,
self.xsize, self.ysize, stroke_width
).unwrap();
write!(
&mut header,
"\n<rect width=\"100%\" height=\"100%\" fill=\"#ffffffff\" />\n"
)
.unwrap();
header.push('\n');
header.push_str(self.s.as_str());
header.push_str("</svg>\n");
self.writer.write_all(header.as_bytes()).expect("write failed");
}
pub fn circle(&mut self, circle: &Circle, color: &str) {
let mut s = String::new();
write!(
&mut s,
r#"<circle cx="{}" cy="{}" r="{}" stroke="{}" />"#,
circle.c.x,
self.ysize - circle.c.y,
circle.r,
color
)
.unwrap();
self.s.push_str(&s);
self.s.push('\n');
}
pub fn rect(&mut self, rect: &Rect, color: &str) {
let mut s = String::new();
let width = rect.p2.x - rect.p1.x;
let height = rect.p2.y - rect.p1.y;
write!(
&mut s,
r#"<rect x="{}" y="{}" width="{}" height="{}" stroke="{}" />"#,
rect.p1.x,
self.ysize - rect.p2.y,
width,
height,
color
)
.unwrap();
self.s.push_str(&s);
self.s.push('\n');
}
pub fn text(&mut self, p: Point, text: &str, color: &str) {
let mut s = String::new();
write!(
&mut s,
r#"<text x="{}" y="{}" fill="{}" font-size="2.0">{}</text>"#,
p.x + 0.0,
self.ysize - p.y + 0.0,
color,
text
)
.unwrap();
self.s.push_str(&s);
self.s.push('\n');
}
pub fn segment(&mut self, segment: &Segment, color: &str) {
let mut s = String::new();
write!(
&mut s,
r#"<line x1="{}" y1="{}" x2="{}" y2="{}" stroke="{}" id="{}"/>"#,
segment.a.x,
self.ysize - segment.a.y,
segment.b.x,
self.ysize - segment.b.y,
color,
segment.id
)
.unwrap();
self.s.push_str(&s);
self.s.push('\n');
}
pub fn arc(&mut self, arc: &Arc, color: &str) {
let mut s = String::new();
let pa = Coord {
x: arc.a.x,
y: arc.a.y,
};
let pb = Coord {
x: arc.b.x,
y: arc.b.y,
};
let pc = Coord {
x: arc.c.x,
y: arc.c.y,
};
let large_arc_flag: i32 = (orient2d(pa, pb, pc) < 0.0).into();
write!(
&mut s,
r#"<path d="M {} {} A {} {} {} {} {} {} {}" stroke="{}" id="{}"/>"#,
arc.a.x,
self.ysize - arc.a.y,
arc.r,
arc.r,
0,
large_arc_flag,
0, arc.b.x,
self.ysize - arc.b.y,
color,
arc.id
)
.unwrap();
self.s.push_str(&s);
self.s.push('\n');
}
pub fn pvertex(&mut self, p0: Point, p1: Point, g: f64, color: &str) {
if g == 0f64 {
let seg = segment(p0, p1);
self.segment(&seg, color);
} else {
let arc = arc_from_bulge(p0, p1, g);
self.arc(&arc, color);
}
}
pub fn polyline(&mut self, pline: &Polyline, color: &str) {
if pline.len() < 2 {
return; }
let last = pline.len() - 2;
for i in 0..=last {
let p0 = pline[i];
let p1 = pline[i + 1];
self.pvertex(p0.p, p1.p, p0.b, color);
}
let p0 = pline.last().unwrap();
self.pvertex(p0.p, pline[0].p, p0.b, color);
}
pub fn polylines(&mut self, plines: &Vec<Polyline>, color: &str) {
for p in plines {
self.polyline(p, color);
}
}
pub fn arcsegment(&mut self, off: &Arc, color: &str) {
if off.is_seg() {
let seg = segment(off.a, off.b);
self.segment(&seg, color);
} else {
self.arc(off, color);
}
}
pub fn arcline_segment_points(&mut self, off: &Arc, _color: &str) {
self.circle(&circle(off.a, 0.1), "green");
self.circle(&circle(off.b, 0.1), "green");
}
pub fn arcline(&mut self, offs: &Arcline, color: &str) {
for s in offs {
self.arcsegment(s, color);
}
}
pub fn arcline_single_points(&mut self, offs: &Arcline, color: &str) {
for s in offs {
self.arcline_segment_points(s, color);
}
}
pub fn arclines(&mut self, offs: &Vec<Arcline>, color: &str) {
for s in offs {
self.arcline(s, color);
}
}
}
#[cfg(test)]
mod test_svg {
use super::*;
#[test]
fn test_circle_svg_std_out() {
let mut svg = svg(400.0, 300.0);
let c = circle(point(200.0, 150.0), 50.0);
svg.circle(&c, "red");
svg.write(); }
#[test]
#[ignore = "writes to file, not stdout"]
fn test_circle_svg_to_file() {
let mut svg = SVG::new(400.0, 300.0, Some("/tmp/test.svg"));
let c = circle(point(200.0, 150.0), 50.0);
svg.circle(&c, "red");
svg.write(); }
}