#![allow(dead_code)]
#[allow(dead_code)]
pub struct SvgPolygon {
pub points: Vec<[f32; 2]>,
pub stroke: String,
pub stroke_width: f32,
pub fill: String,
pub opacity: f32,
}
#[allow(dead_code)]
pub struct SvgPolyline {
pub points: Vec<[f32; 2]>,
pub stroke: String,
pub stroke_width: f32,
}
#[allow(dead_code)]
pub fn new_polygon(
points: Vec<[f32; 2]>,
stroke: &str,
stroke_width: f32,
fill: &str,
) -> SvgPolygon {
SvgPolygon {
points,
stroke: stroke.to_string(),
stroke_width,
fill: fill.to_string(),
opacity: 1.0,
}
}
#[allow(dead_code)]
pub fn new_polyline(points: Vec<[f32; 2]>, stroke: &str, stroke_width: f32) -> SvgPolyline {
SvgPolyline {
points,
stroke: stroke.to_string(),
stroke_width,
}
}
#[allow(dead_code)]
pub fn points_to_attr(points: &[[f32; 2]]) -> String {
points
.iter()
.map(|&[x, y]| format!("{:.4},{:.4}", x, y))
.collect::<Vec<_>>()
.join(" ")
}
#[allow(dead_code)]
pub fn polygon_to_tag(poly: &SvgPolygon) -> String {
format!(
"<polygon points=\"{}\" stroke=\"{}\" stroke-width=\"{}\" fill=\"{}\" opacity=\"{}\"/>",
points_to_attr(&poly.points),
poly.stroke,
poly.stroke_width,
poly.fill,
poly.opacity
)
}
#[allow(dead_code)]
pub fn polyline_to_tag(pl: &SvgPolyline) -> String {
format!(
"<polyline points=\"{}\" stroke=\"{}\" stroke-width=\"{}\" fill=\"none\"/>",
points_to_attr(&pl.points),
pl.stroke,
pl.stroke_width
)
}
#[allow(dead_code)]
pub struct SvgPolygonDoc {
pub width: f32,
pub height: f32,
pub polygons: Vec<SvgPolygon>,
pub polylines: Vec<SvgPolyline>,
}
#[allow(dead_code)]
pub fn new_polygon_doc(width: f32, height: f32) -> SvgPolygonDoc {
SvgPolygonDoc {
width,
height,
polygons: Vec::new(),
polylines: Vec::new(),
}
}
#[allow(dead_code)]
pub fn add_polygon(doc: &mut SvgPolygonDoc, poly: SvgPolygon) {
doc.polygons.push(poly);
}
#[allow(dead_code)]
pub fn add_polyline(doc: &mut SvgPolygonDoc, pl: SvgPolyline) {
doc.polylines.push(pl);
}
#[allow(dead_code)]
pub fn export_polygon_svg(doc: &SvgPolygonDoc) -> String {
let mut out = format!(
"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{}\" height=\"{}\">",
doc.width, doc.height
);
for poly in &doc.polygons {
out.push_str(&polygon_to_tag(poly));
}
for pl in &doc.polylines {
out.push_str(&polyline_to_tag(pl));
}
out.push_str("</svg>");
out
}
#[allow(dead_code)]
pub fn polygon_aabb(poly: &SvgPolygon) -> ([f32; 2], [f32; 2]) {
if poly.points.is_empty() {
return ([0.0; 2], [0.0; 2]);
}
let mut mn = poly.points[0];
let mut mx = poly.points[0];
for &p in &poly.points {
mn[0] = mn[0].min(p[0]);
mn[1] = mn[1].min(p[1]);
mx[0] = mx[0].max(p[0]);
mx[1] = mx[1].max(p[1]);
}
(mn, mx)
}
#[allow(dead_code)]
pub fn polygon_vertex_count(poly: &SvgPolygon) -> usize {
poly.points.len()
}
#[cfg(test)]
mod tests {
use super::*;
fn tri_poly() -> SvgPolygon {
new_polygon(
vec![[0.0, 0.0], [100.0, 0.0], [50.0, 100.0]],
"black",
1.0,
"blue",
)
}
#[test]
fn polygon_tag_has_points() {
let poly = tri_poly();
let tag = polygon_to_tag(&poly);
assert!(tag.contains("polygon"));
assert!(tag.contains("points"));
}
#[test]
fn polygon_vertex_count_correct() {
let poly = tri_poly();
assert_eq!(polygon_vertex_count(&poly), 3);
}
#[test]
fn polyline_tag_has_points() {
let pl = new_polyline(vec![[0.0, 0.0], [50.0, 50.0]], "red", 2.0);
let tag = polyline_to_tag(&pl);
assert!(tag.contains("polyline"));
}
#[test]
fn polygon_aabb_correct() {
let poly = tri_poly();
let (mn, mx) = polygon_aabb(&poly);
assert!((mn[0] - 0.0).abs() < 1e-5);
assert!((mx[0] - 100.0).abs() < 1e-5);
}
#[test]
fn doc_export_has_svg_tag() {
let doc = new_polygon_doc(800.0, 600.0);
let svg = export_polygon_svg(&doc);
assert!(svg.contains("<svg"));
assert!(svg.contains("</svg>"));
}
#[test]
fn add_polygon_to_doc() {
let mut doc = new_polygon_doc(800.0, 600.0);
add_polygon(&mut doc, tri_poly());
assert_eq!(doc.polygons.len(), 1);
}
#[test]
fn add_polyline_to_doc() {
let mut doc = new_polygon_doc(800.0, 600.0);
let pl = new_polyline(vec![[0.0, 0.0], [100.0, 100.0]], "green", 1.0);
add_polyline(&mut doc, pl);
assert_eq!(doc.polylines.len(), 1);
}
#[test]
fn points_to_attr_format() {
let attr = points_to_attr(&[[1.0, 2.0], [3.0, 4.0]]);
assert!(attr.contains(','));
assert!(attr.contains(' '));
}
#[test]
fn polygon_fill_in_tag() {
let poly = tri_poly();
let tag = polygon_to_tag(&poly);
assert!(tag.contains("blue"));
}
#[test]
fn empty_polygon_aabb() {
let empty = new_polygon(vec![], "black", 1.0, "none");
let (mn, mx) = polygon_aabb(&empty);
assert_eq!(mn, [0.0, 0.0]);
assert_eq!(mx, [0.0, 0.0]);
}
}