From 2c0d20eae4e9272734b2031ba55fb8f612a94f83 Mon Sep 17 00:00:00 2001 From: Arne Keller Date: Mon, 1 Apr 2019 19:05:18 +0200 Subject: [PATCH] Allow reading svg files --- Cargo.lock | 99 +++++++++++++- Cargo.toml | 1 + DokumentationAufgabe1.tex | 5 +- src/input.rs | 62 ++++++++- welt1.svg | 278 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 439 insertions(+), 6 deletions(-) create mode 100644 welt1.svg diff --git a/Cargo.lock b/Cargo.lock index a9d81bc..4c3f8a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,7 @@ dependencies = [ "geo 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "svg 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)", + "svgdom 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -92,6 +93,14 @@ dependencies = [ "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "float-cmp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "geo" version = "0.12.0" @@ -125,6 +134,14 @@ name = "libc" version = "0.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "log" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-integer" version = "0.1.39" @@ -151,6 +168,22 @@ name = "pdqselect" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "phf" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_shared" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "proc-macro2" version = "0.4.27" @@ -169,9 +202,17 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.51" +version = "0.1.52" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "roxmltree" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "xmlparser 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rstar" version = "0.2.0" @@ -187,6 +228,21 @@ name = "rustc-demangle" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "simplecss" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "siphasher" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "structopt" version = "0.2.15" @@ -212,6 +268,27 @@ name = "svg" version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "svgdom" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "roxmltree 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "simplecss 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "svgtypes 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "svgtypes" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "0.15.29" @@ -255,7 +332,7 @@ version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -293,6 +370,11 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "xmlparser" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [metadata] "checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" "checksum backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5a90e2b463010cd0e0ce9a11d4a9d5d58d9f41d4a6ba3dcaf9e68b466e88b4" @@ -304,22 +386,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" +"checksum float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "134a8fa843d80a51a5b77d36d42bc2def9edcb0262c914861d08129fd1926600" "checksum geo 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8cfb3011a87e562d59a3e3e2e2595a867780ff7ad618fbb2579f03ea038961a2" "checksum geo-types 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "909178c1a808c8d77571b92dc13cf485b827412e2078ded759942bae3563a4d4" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917" +"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" "checksum pdqselect 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27" +"checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" +"checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" "checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" "checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" -"checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85" +"checksum redox_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)" = "d32b3053e5ced86e4bc0411fec997389532bf56b000e66cb4884eeeb41413d69" +"checksum roxmltree 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "53b0200cbfa8b3f6cfd6076592717d697a1ddc57cb2a8fbfd3d133c06011b579" "checksum rstar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "120bfe4837befb82c5a637a5a8c490a27d25524ac19fffec5b4e555ca6e36ee8" "checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" +"checksum simplecss 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "135685097a85a64067df36e28a243e94a94f76d829087ce0be34eeb014260c0e" +"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" +"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "3d0760c312538987d363c36c42339b55f5ee176ea8808bbe4543d484a291c8d1" "checksum structopt-derive 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "528aeb7351d042e6ffbc2a6fb76a86f9b622fdf7c25932798e7a82cb03bc94c6" "checksum svg 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)" = "a863ec1f8e7cfd4ea449f77445cca06aac240b9a677ccf12b0f65ef020db52c7" +"checksum svgdom 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ffe3c59d84b307fc361bbf0baff2aa6f6d7c946fdd4f1fbbcbb7efcd04b0334" +"checksum svgtypes 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "06280cc80ac6d6be32d497f068c40a25552cc837519265edab70d9040e64c52b" "checksum syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1825685f977249735d510a242a6727b46efe914bb67e38d30c071b1b72b1d5c2" "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" @@ -331,3 +423,4 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum xmlparser 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ecec95f00fb0ff019153e64ea520f87d1409769db3e8f4db3ea588638a3e1cee" diff --git a/Cargo.toml b/Cargo.toml index 03b5e2a..d6afb64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,4 @@ geo = "0.12" svg = "0.5.11" chrono = "0.4.6" structopt = { version = "0.2", default-features = false } +svgdom = "0.16.1" diff --git a/DokumentationAufgabe1.tex b/DokumentationAufgabe1.tex index 0249547..ce84744 100644 --- a/DokumentationAufgabe1.tex +++ b/DokumentationAufgabe1.tex @@ -145,7 +145,7 @@ Zunächst kann man feststellen, dass der letzte Abschnitt jeder Route zum Bus im Um den letzten Zeitpunkt, bei dem sie den Bus gerade noch erwischt, zu bestimmen, kann man die Wurzel in der letzen Gleichung gleich Null setzen. Wenn man nach $y_{B}$ auflöst, erhält man die Position des Busses, bei dessen Durchquerung Lisa anfangen sollte zu laufen. So kann man für jeden Startpunkt berechnen, wann Lisa spätestens von dort loslaufen müsste. -Außerdem wird Lisa auf einer optimalen Route vor dem letzten Abschnitt immer von einer Polygonecke zur nächsten gehen. Andernfalls würde sie in Kurven um jene Ecken wertvolle Zeit verschwenden. Der letzte Abschnitt ihrer Route trifft die y-Achse immer in einem 60\degree-Winkel (siehe Abbildungen). Dieser Winkel ist vom Verhältnis der Geschwindigkeiten von Lisa und dem Bus abhängig: $cos(60\degree) = \frac{15}{30}$. +Außerdem wird Lisa auf einer optimalen Route vor dem letzten Abschnitt immer von einer Polygonecke zur nächsten gehen. Andernfalls würde sie in Kurven um jene Ecken wertvolle Zeit verschwenden. Der letzte Abschnitt ihrer Route trifft die y-Achse immer in einem 60\degree-Winkel (siehe Abb. \ref{fig:winkel}). Dieser Winkel ist vom Verhältnis der Geschwindigkeiten von Lisa und dem Bus abhängig: $cos(60\degree) = \frac{15}{30}$. \begin{figure}[H] \centering @@ -228,6 +228,9 @@ Wenn in jedem Schritt jede andere Polygonecke berücksichtigt wird, hat der Algo Das Programm liest die Problemstellung von der Standardeingabe ein. Die grafische Ausgabe wird in die Standardausgabe geschrieben. Wird $-t$ übergeben, kann die Ausgabe in einem \LaTeX-Dokument verwendet werden. Standardmäßig wird ein SVG-Dokument erzeugt. Mit $-s$ werden auch Zwischenlösungen im aktuellen Ordner als SVG-Dateien gespeichert. Falls man die Geschwindigkeit von Lisa oder dem Bus verändern will, kann man dies mit $-l$ bzw. $-b$ tun. Für zusätzliche Debug-Ausgaben kann man $-d$ verwenden. Die Standardoptionen $-h$ und $-V$ zeigen die Hilfe und die Version des Programms an. \lstinputlisting[caption=Hilfetext des Programmes,frame=single,breaklines=true]{help.txt} +\subsection{Eigene Beispiele} +Das vorgegebene Eingabeformat ist mühsam zu schreiben. Daher kann das Programm auch SVG-Dateien einlesen. Punkte werden als Startpunkte interpretiert, sonstige Pfade sind Hindernisse. + \section{Umsetzung} Das Programm durchsucht in einer optimierten Breitensuche alle Wege, die über Polygonecken führen. Priorisiert (mit einem binären Max-Heap) werden jene Wege, bei denen Lisa theoretisch am längsten warten könnte. Um die Route abzuschließen, probiert das Programm danach immer, einen optimalen Weg zur y-Achse zu finden (dieser trifft sie im 60\degree-Winkel, wie oben beschrieben). Die Route, bei dem Lisa sich am meisten Zeit lassen kann, wird gespeichert, falls nicht schon eine bessere Lösung gefunden wurde. Die Suche ist beendet, sobald alle unvollständigen Routen die beste Wartezeit nicht mehr verbessern können. diff --git a/src/input.rs b/src/input.rs index 287b48a..503b575 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,5 +1,8 @@ use super::*; +use svgdom::{AttributeId, AttributeValue, Document, ElementId, FilterSvg, PathSegment, QName}; + +use std::error::Error; use std::io; use std::io::prelude::*; @@ -15,9 +18,15 @@ pub(crate) fn read_input() -> InputData { fn read_stdin() -> InputData { let stdin = io::stdin(); - let stdin = stdin.lock(); + let mut stdin = stdin.lock(); + let mut input = String::new(); + stdin.read_to_string(&mut input).unwrap(); - let mut lines = stdin.lines().map(Result::unwrap); + // SVG? + if let Ok(data) = read_svg(&input) { + return data; + } + let mut lines = input.split('\n'); let polygon_count = lines.next().unwrap().parse().unwrap(); let mut polygons = Vec::with_capacity(polygon_count); @@ -57,6 +66,55 @@ fn read_stdin() -> InputData { } } +fn read_svg(input: &str) -> Result> { + let svg = Document::from_str(input)?; + let svg = svg.svg_element().unwrap(); + let mut house = None; + let mut polys = Vec::new(); + for (id, node) in svg.descendants().svg() { + //eprintln!("<{:?} /> {:?}", id, node); + if id == ElementId::Path && *node.parent().unwrap().tag_name() != QName::Id(ElementId::Marker) { + let attrs = node.attributes(); + if let Some(&AttributeValue::Path(ref path)) = attrs.get_value(AttributeId::D) { + let mut path = path.clone(); + path.conv_to_absolute(); + + let mut points = Vec::with_capacity(path.0.len() - 1); + for p in &path.0 { + match p { + PathSegment::MoveTo { x, y, .. } => { + points.push(Point::new(*x, -*y)); + }, + PathSegment::LineTo { x, y, .. } => { + points.push(Point::new(*x, -*y)); + }, + PathSegment::VerticalLineTo { y, .. } => { + points.push(Point::new(points.last().unwrap().x(), -*y)); + }, + PathSegment::HorizontalLineTo { x, .. } => { + points.push(Point::new(*x, points.last().unwrap().y())); + }, + PathSegment::ClosePath { .. } => break, + _ => unimplemented!("nicht unterstütztes SVG-Element: {:?}", p) + } + } + if points.len() == 1 { + // Startpunkt + house = Some(points[0]); + } else { + // Hindernis + polys.push(Polygon::new(points.into(), vec![])); + } + } + } + } + + Ok(InputData { + start: house.expect("kein Startpunkt definiert!"), + polys + }) +} + fn _custom_input() -> InputData { let mut polys = vec![]; polys.push(Polygon::new( diff --git a/welt1.svg b/welt1.svg new file mode 100644 index 0000000..df88e06 --- /dev/null +++ b/welt1.svg @@ -0,0 +1,278 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +