Edge cases, more comments and formatting
This commit is contained in:
parent
56a613fb88
commit
573788fbfc
@ -82,7 +82,13 @@ pub(crate) fn dump_route(opt: Opt, house: Point, polys: &[Polygon], route: &[Run
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn save_svg(opt: Opt, filename: &str, house: Point, polys: &[Polygon], route: &[RunState]) {
|
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);
|
let (points, lines, route1, route2_start) = gen_params(opt, house, route);
|
||||||
fs::write(
|
fs::write(
|
||||||
filename,
|
filename,
|
||||||
@ -103,9 +109,7 @@ fn gen_params(
|
|||||||
Point,
|
Point,
|
||||||
) {
|
) {
|
||||||
let last = route.last().unwrap();
|
let last = route.last().unwrap();
|
||||||
let points = vec![
|
let points = vec![(house, "red")];
|
||||||
(house, "red"),
|
|
||||||
];
|
|
||||||
let lines = route
|
let lines = route
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| x.pos)
|
.map(|x| x.pos)
|
||||||
@ -115,7 +119,9 @@ fn gen_params(
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let route1 = route.iter().map(|x| x.pos).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));
|
let route2_start = last
|
||||||
|
.pos
|
||||||
|
.translate(0.0, -last.total_distance * (opt.bus / opt.lisa));
|
||||||
(points, lines, route1, route2_start)
|
(points, lines, route1, route2_start)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,8 +133,25 @@ pub(crate) fn generate_svg(
|
|||||||
route2_start: Point,
|
route2_start: Point,
|
||||||
) -> String {
|
) -> String {
|
||||||
let dot_radius = 25.2;
|
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 width = route1
|
||||||
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();
|
.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()
|
let mut document = Document::new()
|
||||||
// view box at (x,y), (w,h)
|
// view box at (x,y), (w,h)
|
||||||
.set(
|
.set(
|
||||||
@ -137,8 +160,8 @@ pub(crate) fn generate_svg(
|
|||||||
-dot_radius,
|
-dot_radius,
|
||||||
-upper_y - dot_radius,
|
-upper_y - dot_radius,
|
||||||
width,
|
width,
|
||||||
-route2_start.y() + upper_y + dot_radius * 2.0
|
-route2_start.y() + upper_y + dot_radius * 2.0,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.set("xmlns:xlink", "http://www.w3.org/1999/xlink");
|
.set("xmlns:xlink", "http://www.w3.org/1999/xlink");
|
||||||
|
|
||||||
|
32
src/input.rs
32
src/input.rs
@ -60,43 +60,19 @@ fn read_stdin() -> InputData {
|
|||||||
fn _custom_input() -> InputData {
|
fn _custom_input() -> InputData {
|
||||||
let mut polys = vec![];
|
let mut polys = vec![];
|
||||||
polys.push(Polygon::new(
|
polys.push(Polygon::new(
|
||||||
vec![
|
vec![[2.0, 0.0], [6.0, 0.0], [6.0, 6.0], [2.0, 5.0]].into(),
|
||||||
[2.0, 0.0],
|
|
||||||
[6.0, 0.0],
|
|
||||||
[6.0, 6.0],
|
|
||||||
[2.0, 5.0],
|
|
||||||
]
|
|
||||||
.into(),
|
|
||||||
vec![],
|
vec![],
|
||||||
));
|
));
|
||||||
polys.push(Polygon::new(
|
polys.push(Polygon::new(
|
||||||
vec![
|
vec![[1.0, 20.0], [2.0, 20.0], [2.0, 23.0], [1.0, 23.0]].into(),
|
||||||
[1.0, 20.0],
|
|
||||||
[2.0, 20.0],
|
|
||||||
[2.0, 23.0],
|
|
||||||
[1.0, 23.0],
|
|
||||||
]
|
|
||||||
.into(),
|
|
||||||
vec![],
|
vec![],
|
||||||
));
|
));
|
||||||
polys.push(Polygon::new(
|
polys.push(Polygon::new(
|
||||||
vec![
|
vec![[1.0, 25.0], [2.0, 25.0], [2.0, 28.0], [1.0, 28.0]].into(),
|
||||||
[1.0, 25.0],
|
|
||||||
[2.0, 25.0],
|
|
||||||
[2.0, 28.0],
|
|
||||||
[1.0, 28.0],
|
|
||||||
]
|
|
||||||
.into(),
|
|
||||||
vec![],
|
vec![],
|
||||||
));
|
));
|
||||||
let mut stick = Polygon::new(
|
let mut stick = Polygon::new(
|
||||||
vec![
|
vec![[1.0, 115.0], [2.0, 115.0], [2.0, 130.0], [1.0, 130.0]].into(),
|
||||||
[1.0, 115.0],
|
|
||||||
[2.0, 115.0],
|
|
||||||
[2.0, 130.0],
|
|
||||||
[1.0, 130.0],
|
|
||||||
]
|
|
||||||
.into(),
|
|
||||||
vec![],
|
vec![],
|
||||||
);
|
);
|
||||||
polys.push(stick.clone());
|
polys.push(stick.clone());
|
||||||
|
112
src/main.rs
112
src/main.rs
@ -7,6 +7,7 @@ use structopt::StructOpt;
|
|||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::collections::BinaryHeap;
|
use std::collections::BinaryHeap;
|
||||||
use std::f64;
|
use std::f64;
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
mod display;
|
mod display;
|
||||||
mod input;
|
mod input;
|
||||||
@ -16,39 +17,42 @@ type Line = geo::Line<f64>;
|
|||||||
type LineString = geo::LineString<f64>;
|
type LineString = geo::LineString<f64>;
|
||||||
type Polygon = geo::Polygon<f64>;
|
type Polygon = geo::Polygon<f64>;
|
||||||
|
|
||||||
|
/// Datenstruktur für Kommandozeilen-Argumente
|
||||||
#[derive(StructOpt, Clone, Copy)]
|
#[derive(StructOpt, Clone, Copy)]
|
||||||
#[structopt(name = "aufgabe1", about = "Implementierung für Aufgabe 1")]
|
#[structopt(name = "aufgabe1", about = "Implementierung für Aufgabe 1")]
|
||||||
struct Opt {
|
struct Opt {
|
||||||
/// Debug-Modus aktivieren
|
/// Debug-Modus aktivieren
|
||||||
#[structopt(short = "d", long = "debug")]
|
#[structopt(short = "d", long = "debug")]
|
||||||
debug: bool,
|
debug: bool,
|
||||||
/// Zwischenlösungen speichern
|
/// Zwischenlösungen speichern
|
||||||
#[structopt(short = "s", long = "save")]
|
#[structopt(short = "s", long = "save")]
|
||||||
save_intermediates: bool,
|
save_intermediates: bool,
|
||||||
/// Beste Lösung als tkz-Code ausgeben
|
/// Beste Lösung als tkz-Code ausgeben
|
||||||
#[structopt(short = "t", long = "tkz")]
|
#[structopt(short = "t", long = "tkz")]
|
||||||
tkz: bool,
|
tkz: bool,
|
||||||
/// Geschwindigkeit von Lisa (in km/h)
|
/// Geschwindigkeit von Lisa (in km/h)
|
||||||
#[structopt(short = "l", long = "lisa", default_value = "15")]
|
#[structopt(short = "l", long = "lisa", default_value = "15")]
|
||||||
lisa: f64,
|
lisa: f64,
|
||||||
/// Geschwindigkeit vom Bus (in km/h)
|
/// Geschwindigkeit vom Bus (in km/h)
|
||||||
#[structopt(short = "b", long = "bus", default_value = "30")]
|
#[structopt(short = "b", long = "bus", default_value = "30")]
|
||||||
bus: f64,
|
bus: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Möglicher Zustand von Lisas Rennen
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
struct RunState {
|
struct RunState {
|
||||||
/// Theoretisch mögliche Wartezeit
|
/// Theoretisch mögliche Startposition des Busses
|
||||||
delay: f64,
|
bus: f64,
|
||||||
/// Lisas Position
|
/// Lisas Position
|
||||||
pos: Point,
|
pos: Point,
|
||||||
// Bisher zurückgelegte Strecke
|
// Bisher zurückgelegte Strecke
|
||||||
total_distance: f64,
|
total_distance: f64,
|
||||||
}
|
}
|
||||||
|
/// Trait-Implementierung, damit die Struktur in BinaryHeaps passt
|
||||||
impl cmp::Eq for RunState {}
|
impl cmp::Eq for RunState {}
|
||||||
impl cmp::Ord for RunState {
|
impl cmp::Ord for RunState {
|
||||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||||
self.delay.partial_cmp(&other.delay).unwrap()
|
self.bus.partial_cmp(&other.bus).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl cmp::PartialOrd for RunState {
|
impl cmp::PartialOrd for RunState {
|
||||||
@ -60,13 +64,30 @@ impl cmp::PartialOrd for RunState {
|
|||||||
fn main() {
|
fn main() {
|
||||||
// Kommandozeilen-Argumente einlesen
|
// Kommandozeilen-Argumente einlesen
|
||||||
let opt = Opt::from_args();
|
let opt = Opt::from_args();
|
||||||
|
// (unwahrscheinlich)
|
||||||
|
if opt.lisa >= opt.bus {
|
||||||
|
eprintln!("Lisa sollte lieber zur Schule rennen, als den Bus zu nehmen.");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
// (noch unwahrscheinlicher)
|
||||||
|
if opt.bus <= 0.0 {
|
||||||
|
if opt.lisa > 0.0 {
|
||||||
|
eprintln!("Lisa sollte lieber zur Schule rennen, da der Bus vermutlich eine Panne hat oder im Rückwärtsgang ist.");
|
||||||
|
} else {
|
||||||
|
eprintln!("Lisa kann nicht laufen.");
|
||||||
|
}
|
||||||
|
exit(1);
|
||||||
|
} else if opt.lisa <= 0.0 {
|
||||||
|
eprintln!("Lisa muss den Busfahrer bitten, zu ihrem Haus zu fahren.");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
// Geschwindigkeiten in m/s umrechnen
|
// Geschwindigkeiten in m/s umrechnen
|
||||||
let bus_speed = opt.bus / 3.6;
|
let bus_speed = opt.bus / 3.6;
|
||||||
let lisa_speed = opt.lisa / 3.6;
|
let lisa_speed = opt.lisa / 3.6;
|
||||||
// Startzeitpunkt berechnen
|
// Startzeitpunkt berechnen
|
||||||
let calc_time = |total_distance, y| {
|
let calc_time = |total_distance, y| {
|
||||||
NaiveTime::from_hms(7, 30, 0)
|
NaiveTime::from_hms(7, 30, 0)
|
||||||
- Duration::seconds(((total_distance * (opt.bus/opt.lisa) - y) / bus_speed) as i64)
|
- Duration::seconds(((total_distance * (opt.bus / opt.lisa) - y) / bus_speed) as i64)
|
||||||
};
|
};
|
||||||
// Ankuftszeit berechnen
|
// Ankuftszeit berechnen
|
||||||
let calc_end_time = |total_distance, y| {
|
let calc_end_time = |total_distance, y| {
|
||||||
@ -88,12 +109,15 @@ fn main() {
|
|||||||
// Startzustand: Lisa ist an ihrem Haus, zurückgelegte Distanz ist Null
|
// Startzustand: Lisa ist an ihrem Haus, zurückgelegte Distanz ist Null
|
||||||
let start = RunState {
|
let start = RunState {
|
||||||
pos: house,
|
pos: house,
|
||||||
delay: max_possible_delay(&opt, 0.0, house),
|
bus: best_bus_pos(&opt, 0.0, house),
|
||||||
total_distance: 0.0,
|
total_distance: 0.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
// bestmöglicher Fall: Lisa geht direkt zum Bus
|
// bestmöglicher Fall: Lisa geht direkt zum Bus
|
||||||
let meeting_point = Point::new(0.0, house.y() + (opt.lisa/opt.bus).asin().tan() * house.x());
|
let meeting_point = Point::new(
|
||||||
|
0.0,
|
||||||
|
house.y() + (opt.lisa / opt.bus).asin().tan() * house.x(),
|
||||||
|
);
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"Theoretisches Maximum: {:?}",
|
"Theoretisches Maximum: {:?}",
|
||||||
calc_time(distance(meeting_point, house), meeting_point.y())
|
calc_time(distance(meeting_point, house), meeting_point.y())
|
||||||
@ -104,14 +128,14 @@ fn main() {
|
|||||||
states.push(vec![start]);
|
states.push(vec![start]);
|
||||||
|
|
||||||
// beste Lösung speichern
|
// beste Lösung speichern
|
||||||
let mut best_delay = f64::NEG_INFINITY;
|
let mut best_bus = f64::NEG_INFINITY;
|
||||||
let mut best = vec![];
|
let mut best = vec![];
|
||||||
// Zwischenlösungen speichern
|
// Zwischenlösungen speichern
|
||||||
let save_prefix = "tmp_";
|
let save_prefix = "route_";
|
||||||
let mut save_counter = 0;
|
let mut save_counter = 0;
|
||||||
|
|
||||||
// weitersuchen, bis kein besserer Zustand mehr vorhanden ist
|
// weitersuchen, bis kein besserer Zustand mehr vorhanden ist
|
||||||
while states.peek().map(|x| x[0].delay > best_delay) == Some(true) {
|
while states.peek().map(|x| x[0].bus > best_bus) == Some(true) {
|
||||||
// besten Zustand einlesen
|
// besten Zustand einlesen
|
||||||
let state = states.pop().unwrap();
|
let state = states.pop().unwrap();
|
||||||
let last = &state[0];
|
let last = &state[0];
|
||||||
@ -132,14 +156,15 @@ fn main() {
|
|||||||
{
|
{
|
||||||
// Lisa könnte zu dieser Ecke rennen
|
// Lisa könnte zu dieser Ecke rennen
|
||||||
let total_distance = last.total_distance + distance(last.pos, next);
|
let total_distance = last.total_distance + distance(last.pos, next);
|
||||||
let delay = max_possible_delay(&opt, total_distance, next);
|
let bus = best_bus_pos(&opt, total_distance, next);
|
||||||
if delay > best_delay {
|
// theoretisch möglicher Startzeitpunkt ist nach bestem gefundenem Startzeitpunkt
|
||||||
|
if bus > best_bus {
|
||||||
let mut route = state.clone();
|
let mut route = state.clone();
|
||||||
route.insert(
|
route.insert(
|
||||||
0,
|
0,
|
||||||
RunState {
|
RunState {
|
||||||
pos: next,
|
pos: next,
|
||||||
delay,
|
bus,
|
||||||
total_distance,
|
total_distance,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -147,29 +172,29 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// versuche, direkt zum Bus zu gehen
|
// versuche, direkt zum Bus zu gehen
|
||||||
// Lisa trifft Bus mit 60°-Winkel
|
// Lisa trifft Bus in einem bestimmten Winkel (meist 60°)
|
||||||
let next = Point::new(
|
let next = Point::new(
|
||||||
0.0,
|
0.0,
|
||||||
last.pos.y() + (opt.lisa/opt.bus).asin().tan() * last.pos.x(),
|
last.pos.y() + (opt.lisa / opt.bus).asin().tan() * last.pos.x(),
|
||||||
);
|
);
|
||||||
let line = Line::new(last.pos, next);
|
let line = Line::new(last.pos, next);
|
||||||
// freier Weg?
|
// freier Weg?
|
||||||
if none_intersect(&polys, &line) {
|
if none_intersect(&polys, &line) {
|
||||||
let line_length = line.end_point().euclidean_distance(&line.start_point());
|
let line_length = line.end_point().euclidean_distance(&line.start_point());
|
||||||
let total_distance = last.total_distance + line_length;
|
let total_distance = last.total_distance + line_length;
|
||||||
let delay = max_possible_delay(&opt, total_distance, line.end_point());
|
let bus = best_bus_pos(&opt, total_distance, line.end_point());
|
||||||
if delay > best_delay {
|
if bus > best_bus {
|
||||||
// neue beste Wartezeit
|
// neue beste Wartezeit
|
||||||
let mut route = state.clone();
|
let mut route = state.clone();
|
||||||
route.insert(
|
route.insert(
|
||||||
0,
|
0,
|
||||||
RunState {
|
RunState {
|
||||||
pos: next,
|
pos: next,
|
||||||
delay,
|
bus,
|
||||||
total_distance,
|
total_distance,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// Verbesserung anzeigen und speichern
|
// Verbesserung anzeigen und evtl. speichern
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"Verbesserung: {:?} ({:?}+ Möglichkeiten verbleiben)",
|
"Verbesserung: {:?} ({:?}+ Möglichkeiten verbleiben)",
|
||||||
calc_time(total_distance, line.end.y),
|
calc_time(total_distance, line.end.y),
|
||||||
@ -177,7 +202,7 @@ fn main() {
|
|||||||
);
|
);
|
||||||
best = route;
|
best = route;
|
||||||
best.reverse();
|
best.reverse();
|
||||||
best_delay = delay;
|
best_bus = bus;
|
||||||
if opt.save_intermediates {
|
if opt.save_intermediates {
|
||||||
display::save_svg(
|
display::save_svg(
|
||||||
opt,
|
opt,
|
||||||
@ -233,12 +258,15 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Schneidet die Linie keines der Polygone?
|
||||||
|
/// (Berührungen erlaubt)
|
||||||
fn none_intersect(polys: &[Polygon], line: &Line) -> bool {
|
fn none_intersect(polys: &[Polygon], line: &Line) -> bool {
|
||||||
let mut middle = line.start;
|
let mut middle = line.start;
|
||||||
middle.x += line.dx() / 2.0;
|
middle.x += line.dx() / 2.0;
|
||||||
middle.y += line.dy() / 2.0;
|
middle.y += line.dy() / 2.0;
|
||||||
let middle: Point = middle.into();
|
let middle: Point = middle.into();
|
||||||
'poly: for p in polys {
|
'poly: for p in polys {
|
||||||
|
// Alle Seitenlinien des Polygons
|
||||||
for l in p.exterior().lines().chain(vec![Line::new(
|
for l in p.exterior().lines().chain(vec![Line::new(
|
||||||
p.exterior().0[0],
|
p.exterior().0[0],
|
||||||
*p.exterior().0.last().unwrap(),
|
*p.exterior().0.last().unwrap(),
|
||||||
@ -246,7 +274,7 @@ fn none_intersect(polys: &[Polygon], line: &Line) -> bool {
|
|||||||
if (l.start == line.start && l.end == line.end)
|
if (l.start == line.start && l.end == line.end)
|
||||||
|| (l.start == line.end && l.end == line.start)
|
|| (l.start == line.end && l.end == line.start)
|
||||||
{
|
{
|
||||||
// point is on polygon border
|
// Linie liegt auf Kante des Polygons
|
||||||
continue 'poly;
|
continue 'poly;
|
||||||
}
|
}
|
||||||
if l.start == line.start
|
if l.start == line.start
|
||||||
@ -254,25 +282,43 @@ fn none_intersect(polys: &[Polygon], line: &Line) -> bool {
|
|||||||
|| l.start == line.end
|
|| l.start == line.end
|
||||||
|| l.end == line.start
|
|| l.end == line.start
|
||||||
{
|
{
|
||||||
continue; // would always intersect with itself
|
continue; // Linie schneidet sich immer selbst
|
||||||
}
|
}
|
||||||
if l.intersects(line) {
|
if l.intersects(line) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// falls Mittelpunkt in Polygon:
|
||||||
if p.euclidean_distance(&middle) == 0.0 {
|
if p.euclidean_distance(&middle) == 0.0 {
|
||||||
|
// schneidet Polygon
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// (dieser Fall tritt auf, wenn die Linie von einer Polygon-Ecke zu einer anderen Ecke verläuft)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Euklidische Distanz zweier Punkte
|
||||||
fn distance(a: Point, b: Point) -> f64 {
|
fn distance(a: Point, b: Point) -> f64 {
|
||||||
((a.x() - b.x()).powi(2) + (a.y() - b.y()).powi(2)).sqrt()
|
((a.x() - b.x()).powi(2) + (a.y() - b.y()).powi(2)).sqrt()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn max_possible_delay(opt: &Opt, total_distance: f64, lisa: Point) -> f64 {
|
/// Mit dieser Funktion kann man bestimmen, wo der Bus zu Lisas Startzeit war.
|
||||||
let x_l = lisa.x();
|
/// Die Funktion nimmt an, dass Lisa, von ihrer aktuellen Position aus, den Bus ohne Umwege erreichen kann.
|
||||||
let y_l = lisa.y();
|
/// Sehr hilfreich als Heuristik, da sie nie schlechter als das eigentliche Ergebnis sein kann.
|
||||||
y_l - (((opt.bus/opt.lisa).powi(2) - 1.0) * x_l.powi(2)).sqrt() - total_distance * (opt.bus/opt.lisa)
|
fn best_bus_pos(opt: &Opt, total_distance: f64, lisa: Point) -> f64 {
|
||||||
|
let x_l = lisa.x(); // a
|
||||||
|
let y_l = lisa.y(); // b
|
||||||
|
// y-Koordinate des Busses: d
|
||||||
|
// Länge, die Lisa noch laufen muss: l
|
||||||
|
// Verhältnis beider Geschwindigkeiten: x = bus / lisa
|
||||||
|
// l^2 = sqrt(a^2 + (b - xl - d)^2)
|
||||||
|
// die quadratische Gleichung hat zwei Lösungen (siehe Dok.)
|
||||||
|
// nach der ABC-Formel muss b^2 - 4ac = 0 sein, damit die Gleichung nur eine Lösung hat:
|
||||||
|
// (2xb-2xd)^2 - 4*((1-x^2)*(-a^2-b^2-d^2+2bd)) = 0
|
||||||
|
// (eine WolframAlpha-Eingabe später..)
|
||||||
|
// d = sqrt(a^2 * (x^2 - 1)) + b
|
||||||
|
// in dieser Berechnung wird auch die bereits zurückgelegte Strecke berücksichtigt:
|
||||||
|
y_l - (((opt.bus / opt.lisa).powi(2) - 1.0) * x_l.powi(2)).sqrt()
|
||||||
|
- total_distance * (opt.bus / opt.lisa)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user