273 lines
6.2 KiB
Rust
273 lines
6.2 KiB
Rust
use svg::node::element::path::Data;
|
|
use svg::node::element::Path;
|
|
use svg::node::element::{AnimateMotion, Circle, MotionPath};
|
|
use svg::Document;
|
|
use svg::Node;
|
|
|
|
use std::fs;
|
|
|
|
use super::*;
|
|
|
|
pub(crate) fn dump_latex_code(route: &[RunState], polys: &[Polygon]) {
|
|
const SCALE: f64 = 70.0;
|
|
let mut xmax = 0.0;
|
|
let mut ymax = 0.0;
|
|
for poly in polys {
|
|
for point in poly.exterior().points_iter() {
|
|
let x = point.x() / SCALE;
|
|
let y = point.y() / SCALE;
|
|
if x > xmax {
|
|
xmax = x;
|
|
}
|
|
if y > ymax {
|
|
ymax = y;
|
|
}
|
|
}
|
|
}
|
|
if route[route.len() - 1].pos.y() / SCALE > ymax {
|
|
ymax = route[route.len() - 1].pos.y() / SCALE;
|
|
}
|
|
println!("\\begin{{tikzpicture}}");
|
|
println!("\\tkzInit[xmax={},ymax={}]", xmax + 0.3, ymax + 0.3);
|
|
println!("\\tkzAxeXY");
|
|
println!("\\tkzGrid");
|
|
for (idx, poly) in polys.iter().enumerate() {
|
|
print!("\\tkzDefPoints{{");
|
|
for (poly_idx, point) in poly.exterior().points_iter().enumerate() {
|
|
let x = point.x() / SCALE;
|
|
let y = point.y() / SCALE;
|
|
print!("{}/{}/P{}_{}", x, y, idx, poly_idx);
|
|
if poly_idx != poly.exterior().0.len() - 1 {
|
|
print!(",");
|
|
}
|
|
if x > xmax {
|
|
xmax = x;
|
|
}
|
|
if y > ymax {
|
|
ymax = y;
|
|
}
|
|
}
|
|
println!("}}");
|
|
print!("\\tkzDrawPolygon[fill=black,line width=1pt,opacity=0.5](");
|
|
for (poly_idx, _) in poly.exterior().points_iter().enumerate() {
|
|
print!("P{}_{}", idx, poly_idx);
|
|
if poly_idx != poly.exterior().0.len() - 1 {
|
|
print!(",");
|
|
}
|
|
}
|
|
println!(")");
|
|
}
|
|
for (idx, s) in route.iter().enumerate() {
|
|
println!(
|
|
"\\tkzDefPoint({},{}){{R{}}}",
|
|
s.pos.x() / SCALE,
|
|
s.pos.y() / SCALE,
|
|
idx
|
|
);
|
|
}
|
|
for idx in 1..route.len() {
|
|
println!("\\tkzDrawSegment(R{},R{})", idx - 1, idx);
|
|
}
|
|
for idx in 0..route.len() {
|
|
let color = if idx == 0 || idx == route.len() - 1 { "green" } else { "red" };
|
|
println!("\\tkzDrawPoint[fill={},color=black,size=13](R{})", color, idx);
|
|
}
|
|
println!("\\end{{tikzpicture}}");
|
|
}
|
|
|
|
pub(crate) fn dump_route(opt: Opt, house: Point, polys: &[Polygon], route: &[RunState]) {
|
|
let (points, lines, route1, route2_start) = gen_params(opt, house, route);
|
|
print!(
|
|
"{}",
|
|
generate_svg(&points, &lines, polys, &route1, route2_start)
|
|
);
|
|
}
|
|
|
|
pub(crate) fn save_svg(
|
|
opt: Opt,
|
|
filename: &str,
|
|
house: Point,
|
|
polys: &[Polygon],
|
|
route: &[RunState],
|
|
) {
|
|
let (points, lines, route1, route2_start) = gen_params(opt, house, route);
|
|
fs::write(
|
|
filename,
|
|
generate_svg(&points, &lines, polys, &route1, route2_start),
|
|
)
|
|
.unwrap();
|
|
}
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
fn gen_params(
|
|
opt: Opt,
|
|
house: Point,
|
|
route: &[RunState],
|
|
) -> (
|
|
Vec<(Point, &'static str)>,
|
|
Vec<(Line, &'static str)>,
|
|
Vec<Point>,
|
|
Point,
|
|
) {
|
|
let last = route.last().unwrap();
|
|
let points = vec![(house, "red")];
|
|
let lines = route
|
|
.iter()
|
|
.map(|x| x.pos)
|
|
.collect::<LineString>()
|
|
.lines()
|
|
.map(|x| (x, "gray"))
|
|
.collect::<Vec<_>>();
|
|
|
|
let route1 = route.iter().map(|x| x.pos).collect::<Vec<_>>();
|
|
let route2_start = last
|
|
.pos
|
|
.translate(0.0, -last.total_distance * (opt.bus / opt.lisa));
|
|
(points, lines, route1, route2_start)
|
|
}
|
|
|
|
pub(crate) fn generate_svg(
|
|
points: &[(Point, &'static str)],
|
|
lines: &[(Line, &'static str)],
|
|
polys: &[Polygon],
|
|
route1: &[Point],
|
|
route2_start: Point,
|
|
) -> String {
|
|
let dot_radius = 25.2;
|
|
let width = route1
|
|
.iter()
|
|
.map(|p| p.x())
|
|
.chain(
|
|
polys
|
|
.iter()
|
|
.map(|x| x.exterior().0.iter().map(|p| p.x))
|
|
.flatten(),
|
|
)
|
|
.max_by(|a, b| a.partial_cmp(b).unwrap())
|
|
.unwrap()
|
|
+ dot_radius * 2.0;
|
|
let upper_y = polys
|
|
.iter()
|
|
.map(|x| x.exterior().0.iter().map(|p| p.y))
|
|
.flatten()
|
|
.chain(Some(route1.last().unwrap().y()))
|
|
.max_by(|a, b| a.partial_cmp(b).unwrap())
|
|
.unwrap();
|
|
let mut document = Document::new()
|
|
// view box at (x,y), (w,h)
|
|
.set(
|
|
"viewBox",
|
|
(
|
|
-dot_radius,
|
|
-upper_y - dot_radius,
|
|
width,
|
|
-route2_start.y() + upper_y + dot_radius * 2.0,
|
|
),
|
|
)
|
|
.set("xmlns:xlink", "http://www.w3.org/1999/xlink");
|
|
|
|
for circle in points.iter().map(|(p, color)| {
|
|
Circle::new()
|
|
.set("cx", p.x())
|
|
.set("cy", -p.y())
|
|
.set("r", dot_radius)
|
|
.set("opacity", 0.5)
|
|
.set("fill", *color)
|
|
}) {
|
|
document.append(circle);
|
|
}
|
|
for path in lines.iter().map(|(l, color)| {
|
|
let data = Data::new()
|
|
.move_to((l.start.x, -l.start.y))
|
|
.line_to((l.end.x, -l.end.y))
|
|
.close();
|
|
|
|
Path::new()
|
|
.set("fill", "none")
|
|
.set("stroke", *color)
|
|
.set("stroke-width", 2.0)
|
|
.set("opacity", 0.5)
|
|
.set("d", data)
|
|
}) {
|
|
document.append(path);
|
|
}
|
|
|
|
for path in polys.iter().map(|poly| {
|
|
let coords = &poly.exterior().0;
|
|
let mut first = coords[0].x_y();
|
|
first.1 *= -1.0;
|
|
let mut data = Data::new().move_to(first);
|
|
for c in &coords[1..] {
|
|
let mut c = c.x_y();
|
|
c.1 *= -1.0;
|
|
data = data.line_to(c);
|
|
}
|
|
data = data.close();
|
|
|
|
Path::new()
|
|
.set("fill", "black")
|
|
.set("stroke", "black")
|
|
.set("stroke-width", 0)
|
|
.set("d", data)
|
|
}) {
|
|
document.append(path);
|
|
}
|
|
|
|
let mut first: (f64, f64) = (route1[0].x(), route1[0].y());
|
|
first.1 *= -1.0;
|
|
let mut data = Data::new().move_to(first);
|
|
for point in &route1[1..] {
|
|
let mut c = point.x_y();
|
|
c.1 *= -1.0;
|
|
data = data.line_to(c);
|
|
}
|
|
let path = Path::new()
|
|
.set("id", "route1")
|
|
.set("fill", "none")
|
|
.set("stroke", "none")
|
|
.set("d", data);
|
|
document.append(path);
|
|
|
|
let mut dot1 = Circle::new()
|
|
.set("cx", 0.0)
|
|
.set("cy", 0.0)
|
|
.set("r", dot_radius)
|
|
.set("opacity", 0.75)
|
|
.set("fill", "gray");
|
|
let mut motion1 = AnimateMotion::new()
|
|
.set("dur", "2s")
|
|
.set("repeatCount", "indefinite");
|
|
let path = MotionPath::new().set("xlink:href", "#route1");
|
|
motion1.append(path);
|
|
dot1.append(motion1);
|
|
document.append(dot1);
|
|
|
|
let mut first = (route2_start.x(), route2_start.y());
|
|
first.1 *= -1.0;
|
|
let mut data = Data::new().move_to(first);
|
|
let last = route1.last().unwrap();
|
|
data = data.line_to((last.x(), -last.y()));
|
|
let path = Path::new()
|
|
.set("id", "route2")
|
|
.set("fill", "none")
|
|
.set("stroke", "none")
|
|
.set("d", data);
|
|
document.append(path);
|
|
|
|
let mut dot1 = Circle::new()
|
|
.set("cx", 0.0)
|
|
.set("cy", 0.0)
|
|
.set("r", dot_radius)
|
|
.set("opacity", 0.75)
|
|
.set("fill", "gray");
|
|
let mut motion1 = AnimateMotion::new()
|
|
.set("dur", "2s")
|
|
.set("repeatCount", "indefinite");
|
|
let path = MotionPath::new().set("xlink:href", "#route2");
|
|
motion1.append(path);
|
|
dot1.append(motion1);
|
|
document.append(dot1);
|
|
|
|
document.to_string()
|
|
}
|