mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 17:37:22 +03:00
removing geom crate, using just map model
This commit is contained in:
parent
4489abd301
commit
ba310cbd45
@ -1,3 +1,3 @@
|
||||
[workspace]
|
||||
|
||||
members = ["abstutil", "control", "convert_osm", "editor", "ezgui", "geom", "headless", "kml", "map_model", "sim"]
|
||||
members = ["abstutil", "control", "convert_osm", "editor", "ezgui", "headless", "kml", "map_model", "sim"]
|
||||
|
@ -5,7 +5,6 @@ authors = ["Dustin Carlino <dabreegster@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
dimensioned = "0.6.0"
|
||||
geom = { path = "../geom" }
|
||||
map_model = { path = "../map_model" }
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
|
@ -1,12 +1,10 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
extern crate dimensioned;
|
||||
extern crate geom;
|
||||
extern crate map_model;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use geom::GeomMap;
|
||||
use map_model::{IntersectionID, Map, TurnID};
|
||||
use std::collections::HashMap;
|
||||
use stop_signs::{ControlStopSign, TurnPriority};
|
||||
@ -22,7 +20,7 @@ pub struct ControlMap {
|
||||
}
|
||||
|
||||
impl ControlMap {
|
||||
pub fn new(map: &Map, geom_map: &GeomMap) -> ControlMap {
|
||||
pub fn new(map: &Map) -> ControlMap {
|
||||
let mut ctrl = ControlMap {
|
||||
traffic_signals: HashMap::new(),
|
||||
stop_signs: HashMap::new(),
|
||||
@ -31,7 +29,7 @@ impl ControlMap {
|
||||
for i in map.all_intersections() {
|
||||
if i.has_traffic_signal {
|
||||
ctrl.traffic_signals
|
||||
.insert(i.id, ControlTrafficSignal::new(map, i.id, &geom_map));
|
||||
.insert(i.id, ControlTrafficSignal::new(map, i.id));
|
||||
} else {
|
||||
ctrl.stop_signs
|
||||
.insert(i.id, ControlStopSign::new(map, i.id));
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
use ModifiedStopSign;
|
||||
|
||||
use geom::GeomMap;
|
||||
use map_model::{IntersectionID, Map, TurnID};
|
||||
use std::collections::HashMap;
|
||||
|
||||
@ -50,19 +49,17 @@ impl ControlStopSign {
|
||||
self.turns[&turn]
|
||||
}
|
||||
|
||||
pub fn set_priority(&mut self, turn: TurnID, priority: TurnPriority, geom_map: &GeomMap) {
|
||||
pub fn set_priority(&mut self, turn: TurnID, priority: TurnPriority, map: &Map) {
|
||||
if priority == TurnPriority::Priority {
|
||||
assert!(self.could_be_priority_turn(turn, geom_map));
|
||||
assert!(self.could_be_priority_turn(turn, map));
|
||||
}
|
||||
self.turns.insert(turn, priority);
|
||||
self.changed = true;
|
||||
}
|
||||
|
||||
pub fn could_be_priority_turn(&self, id: TurnID, geom_map: &GeomMap) -> bool {
|
||||
pub fn could_be_priority_turn(&self, id: TurnID, map: &Map) -> bool {
|
||||
for (t, pri) in &self.turns {
|
||||
if *pri == TurnPriority::Priority
|
||||
&& geom_map.get_t(id).conflicts_with(geom_map.get_t(*t))
|
||||
{
|
||||
if *pri == TurnPriority::Priority && map.get_t(id).conflicts_with(map.get_t(*t)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ use CycleState;
|
||||
use ModifiedTrafficSignal;
|
||||
|
||||
use dimensioned::si;
|
||||
use geom::GeomMap;
|
||||
use map_model::{IntersectionID, Map, TurnID};
|
||||
|
||||
use std;
|
||||
@ -20,15 +19,11 @@ pub struct ControlTrafficSignal {
|
||||
}
|
||||
|
||||
impl ControlTrafficSignal {
|
||||
pub fn new(
|
||||
map: &Map,
|
||||
intersection: IntersectionID,
|
||||
geom_map: &GeomMap,
|
||||
) -> ControlTrafficSignal {
|
||||
pub fn new(map: &Map, intersection: IntersectionID) -> ControlTrafficSignal {
|
||||
assert!(map.get_i(intersection).has_traffic_signal);
|
||||
ControlTrafficSignal {
|
||||
intersection,
|
||||
cycles: ControlTrafficSignal::greedy_assignment(map, intersection, geom_map),
|
||||
cycles: ControlTrafficSignal::greedy_assignment(map, intersection),
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,11 +68,7 @@ impl ControlTrafficSignal {
|
||||
(cycle, remaining_cycle_time)
|
||||
}
|
||||
|
||||
fn greedy_assignment(
|
||||
map: &Map,
|
||||
intersection: IntersectionID,
|
||||
geom_map: &GeomMap,
|
||||
) -> Vec<Cycle> {
|
||||
fn greedy_assignment(map: &Map, intersection: IntersectionID) -> Vec<Cycle> {
|
||||
let mut cycles = Vec::new();
|
||||
|
||||
// Greedily partition turns into cycles. More clever things later.
|
||||
@ -93,7 +84,7 @@ impl ControlTrafficSignal {
|
||||
while !remaining_turns.is_empty() {
|
||||
let add_turn = remaining_turns
|
||||
.iter()
|
||||
.position(|&t| !current_cycle.conflicts_with(t, geom_map));
|
||||
.position(|&t| !current_cycle.conflicts_with(t, map));
|
||||
match add_turn {
|
||||
Some(idx) => {
|
||||
current_cycle.turns.push(remaining_turns[idx]);
|
||||
@ -125,9 +116,9 @@ pub struct Cycle {
|
||||
}
|
||||
|
||||
impl Cycle {
|
||||
pub fn conflicts_with(&self, t1: TurnID, geom_map: &GeomMap) -> bool {
|
||||
pub fn conflicts_with(&self, t1: TurnID, map: &Map) -> bool {
|
||||
for t2 in &self.turns {
|
||||
if geom_map.get_t(t1).conflicts_with(geom_map.get_t(*t2)) {
|
||||
if map.get_t(t1).conflicts_with(map.get_t(*t2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -94,6 +94,10 @@ wait slow down even more -- before any of this change, lanes on adjacent roads s
|
||||
- FIRST: move geom things into the Map structs directly. get rid of that crate.
|
||||
---> option 1: module per object type, geometry and graph squished together
|
||||
- option 2: try to separate the graph/geom stuff within map model.
|
||||
- CLEANUP: no more 'extern crate' in non lib
|
||||
- CLEANUP: bldg front path should happen upfront, not in render
|
||||
- CLEANUP: gps to screen in map upfront, dont plumb along gps pts for bldg/parcel/etc, so bounds should become private.
|
||||
|
||||
- THEN: express the proto -> runtime map loading as a sequence of phases
|
||||
- keep doing the current road trimming for the moment
|
||||
- later, this could be the same as the OSM conversion. just
|
||||
|
@ -8,7 +8,6 @@ aabb-quadtree = "0.1.0"
|
||||
abstutil = { path = "../abstutil" }
|
||||
control = { path = "../control" }
|
||||
geo = "0.9.1"
|
||||
geom = { path = "../geom" }
|
||||
ezgui = { path = "../ezgui" }
|
||||
map_model = { path = "../map_model" }
|
||||
multimap = "0.4.0"
|
||||
|
@ -5,7 +5,6 @@ extern crate abstutil;
|
||||
extern crate control;
|
||||
extern crate ezgui;
|
||||
extern crate geo;
|
||||
extern crate geom;
|
||||
extern crate glutin_window;
|
||||
extern crate graphics;
|
||||
extern crate map_model;
|
||||
|
@ -6,7 +6,6 @@ use control::ControlMap;
|
||||
use ezgui::GfxCtx;
|
||||
use ezgui::canvas::Canvas;
|
||||
use ezgui::input::UserInput;
|
||||
use geom;
|
||||
use graphics::types::Color;
|
||||
use map_model;
|
||||
use map_model::{BuildingID, IntersectionID, Map, RoadID, TurnID};
|
||||
@ -137,7 +136,6 @@ impl SelectionState {
|
||||
&self,
|
||||
map: &Map,
|
||||
canvas: &Canvas,
|
||||
geom_map: &geom::GeomMap,
|
||||
draw_map: &render::DrawMap,
|
||||
control_map: &ControlMap,
|
||||
sim: &Sim,
|
||||
@ -160,14 +158,14 @@ impl SelectionState {
|
||||
let relevant_turns = map.get_turns_from_road(id);
|
||||
match current_turn_index {
|
||||
Some(idx) => {
|
||||
let turn = draw_map.get_t(relevant_turns[idx % relevant_turns.len()].id);
|
||||
let geom_turn =
|
||||
geom_map.get_t(relevant_turns[idx % relevant_turns.len()].id);
|
||||
turn.draw_full(g, cs.get(Colors::Turn));
|
||||
let turn = map.get_t(relevant_turns[idx % relevant_turns.len()].id);
|
||||
let draw_turn =
|
||||
draw_map.get_t(relevant_turns[idx % relevant_turns.len()].id);
|
||||
draw_turn.draw_full(g, cs.get(Colors::Turn));
|
||||
for map_t in all_turns {
|
||||
let t = map.get_t(map_t.id);
|
||||
let draw_t = draw_map.get_t(map_t.id);
|
||||
let geom_t = geom_map.get_t(map_t.id);
|
||||
if geom_t.conflicts_with(geom_turn) {
|
||||
if t.conflicts_with(turn) {
|
||||
// TODO should we instead change color_t?
|
||||
draw_t.draw_icon(g, cs.get(Colors::ConflictingTurn), cs);
|
||||
}
|
||||
@ -178,10 +176,10 @@ impl SelectionState {
|
||||
},
|
||||
}
|
||||
// TODO tmp
|
||||
draw_map.get_r(id).draw_debug(g, cs, geom_map.get_r(id));
|
||||
draw_map.get_r(id).draw_debug(g, cs, map.get_r(id));
|
||||
}
|
||||
SelectionState::TooltipRoad(id) => {
|
||||
canvas.draw_mouse_tooltip(g, &draw_map.get_r(id).tooltip_lines(map, geom_map));
|
||||
canvas.draw_mouse_tooltip(g, &draw_map.get_r(id).tooltip_lines(map));
|
||||
}
|
||||
SelectionState::SelectedBuilding(id) => {
|
||||
canvas.draw_mouse_tooltip(g, &draw_map.get_b(id).tooltip_lines(map));
|
||||
|
@ -3,7 +3,6 @@
|
||||
use abstutil;
|
||||
use control::ControlMap;
|
||||
use ezgui::input::UserInput;
|
||||
use geom::GeomMap;
|
||||
use map_model::Map;
|
||||
use piston::input::{Key, UpdateEvent};
|
||||
use sim::common;
|
||||
@ -22,9 +21,9 @@ pub struct SimController {
|
||||
}
|
||||
|
||||
impl SimController {
|
||||
pub fn new(map: &Map, geom_map: &GeomMap, rng_seed: Option<u8>) -> SimController {
|
||||
pub fn new(map: &Map, rng_seed: Option<u8>) -> SimController {
|
||||
SimController {
|
||||
sim: straw_model::Sim::new(map, geom_map, rng_seed),
|
||||
sim: straw_model::Sim::new(map, rng_seed),
|
||||
desired_speed: 1.0,
|
||||
last_step: None,
|
||||
benchmark: None,
|
||||
@ -33,13 +32,7 @@ impl SimController {
|
||||
}
|
||||
|
||||
// true if the sim is running
|
||||
pub fn event(
|
||||
&mut self,
|
||||
input: &mut UserInput,
|
||||
geom_map: &GeomMap,
|
||||
map: &Map,
|
||||
control_map: &ControlMap,
|
||||
) -> bool {
|
||||
pub fn event(&mut self, input: &mut UserInput, map: &Map, control_map: &ControlMap) -> bool {
|
||||
if input.unimportant_key_pressed(Key::LeftBracket, "Press [ to slow down sim") {
|
||||
self.desired_speed -= ADJUST_SPEED;
|
||||
self.desired_speed = self.desired_speed.max(0.0);
|
||||
@ -65,7 +58,7 @@ impl SimController {
|
||||
self.last_step = Some(Instant::now());
|
||||
self.benchmark = Some(self.sim.start_benchmark());
|
||||
} else if input.unimportant_key_pressed(Key::M, "press M to run one step") {
|
||||
self.sim.step(geom_map, map, control_map);
|
||||
self.sim.step(map, control_map);
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +68,7 @@ impl SimController {
|
||||
let dt = tick.elapsed();
|
||||
let dt_s = dt.as_secs() as f64 + f64::from(dt.subsec_nanos()) * 1e-9;
|
||||
if dt_s >= common::TIMESTEP.value_unsafe / self.desired_speed {
|
||||
self.sim.step(geom_map, map, control_map);
|
||||
self.sim.step(map, control_map);
|
||||
self.last_step = Some(Instant::now());
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@ use colors::{ColorScheme, Colors};
|
||||
use control::ControlMap;
|
||||
use control::stop_signs::TurnPriority;
|
||||
use ezgui::input::UserInput;
|
||||
use geom::GeomMap;
|
||||
use graphics::types::Color;
|
||||
use map_model::IntersectionID;
|
||||
use map_model::{Map, Turn};
|
||||
@ -26,7 +25,6 @@ impl StopSignEditor {
|
||||
&mut self,
|
||||
input: &mut UserInput,
|
||||
map: &Map,
|
||||
geom_map: &GeomMap,
|
||||
control_map: &mut ControlMap,
|
||||
current_selection: &SelectionState,
|
||||
) -> bool {
|
||||
@ -40,30 +38,30 @@ impl StopSignEditor {
|
||||
match sign.get_priority(id) {
|
||||
TurnPriority::Priority => {
|
||||
if input.key_pressed(Key::D2, "Press 2 to make this turn yield") {
|
||||
sign.set_priority(id, TurnPriority::Yield, geom_map);
|
||||
sign.set_priority(id, TurnPriority::Yield, map);
|
||||
}
|
||||
if input.key_pressed(Key::D3, "Press 3 to make this turn always stop") {
|
||||
sign.set_priority(id, TurnPriority::Stop, geom_map);
|
||||
sign.set_priority(id, TurnPriority::Stop, map);
|
||||
}
|
||||
}
|
||||
TurnPriority::Yield => {
|
||||
if sign.could_be_priority_turn(id, geom_map)
|
||||
if sign.could_be_priority_turn(id, map)
|
||||
&& input.key_pressed(Key::D1, "Press 1 to let this turn go immediately")
|
||||
{
|
||||
sign.set_priority(id, TurnPriority::Priority, geom_map);
|
||||
sign.set_priority(id, TurnPriority::Priority, map);
|
||||
}
|
||||
if input.key_pressed(Key::D3, "Press 3 to make this turn always stop") {
|
||||
sign.set_priority(id, TurnPriority::Stop, geom_map);
|
||||
sign.set_priority(id, TurnPriority::Stop, map);
|
||||
}
|
||||
}
|
||||
TurnPriority::Stop => {
|
||||
if sign.could_be_priority_turn(id, geom_map)
|
||||
if sign.could_be_priority_turn(id, map)
|
||||
&& input.key_pressed(Key::D1, "Press 1 to let this turn go immediately")
|
||||
{
|
||||
sign.set_priority(id, TurnPriority::Priority, geom_map);
|
||||
sign.set_priority(id, TurnPriority::Priority, map);
|
||||
}
|
||||
if input.key_pressed(Key::D2, "Press 2 to make this turn yield") {
|
||||
sign.set_priority(id, TurnPriority::Yield, geom_map);
|
||||
sign.set_priority(id, TurnPriority::Yield, map);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -7,7 +7,6 @@ extern crate map_model;
|
||||
use colors::{ColorScheme, Colors};
|
||||
use control::ControlMap;
|
||||
use ezgui::input::UserInput;
|
||||
use geom::GeomMap;
|
||||
use graphics::types::Color;
|
||||
use map_model::Map;
|
||||
use map_model::{IntersectionID, Turn};
|
||||
@ -31,7 +30,6 @@ impl TrafficSignalEditor {
|
||||
&mut self,
|
||||
input: &mut UserInput,
|
||||
map: &Map,
|
||||
geom_map: &GeomMap,
|
||||
control_map: &mut ControlMap,
|
||||
current_selection: &SelectionState,
|
||||
) -> bool {
|
||||
@ -67,7 +65,7 @@ impl TrafficSignalEditor {
|
||||
) {
|
||||
cycle.remove(id);
|
||||
}
|
||||
} else if !cycle.conflicts_with(id, geom_map) {
|
||||
} else if !cycle.conflicts_with(id, map) {
|
||||
if input.key_pressed(Key::Space, "Press Space to add this turn to this cycle") {
|
||||
cycle.add(id);
|
||||
}
|
||||
@ -81,7 +79,7 @@ impl TrafficSignalEditor {
|
||||
pub fn color_t(
|
||||
&self,
|
||||
t: &Turn,
|
||||
geom_map: &GeomMap,
|
||||
map: &Map,
|
||||
control_map: &ControlMap,
|
||||
cs: &ColorScheme,
|
||||
) -> Option<Color> {
|
||||
@ -93,7 +91,7 @@ impl TrafficSignalEditor {
|
||||
|
||||
if cycle.contains(t.id) {
|
||||
Some(cs.get(Colors::SignalEditorTurnInCurrentCycle))
|
||||
} else if !cycle.conflicts_with(t.id, geom_map) {
|
||||
} else if !cycle.conflicts_with(t.id, map) {
|
||||
Some(cs.get(Colors::SignalEditorTurnCompatibleWithCurrentCycle))
|
||||
} else {
|
||||
Some(cs.get(Colors::SignalEditorTurnConflictsWithCurrentCycle))
|
||||
|
@ -6,12 +6,11 @@ extern crate map_model;
|
||||
|
||||
use aabb_quadtree::geom::Rect;
|
||||
use ezgui::GfxCtx;
|
||||
use geom::GeomMap;
|
||||
use geom::geometry;
|
||||
use graphics;
|
||||
use graphics::math::Vec2d;
|
||||
use graphics::types::Color;
|
||||
use map_model::{Bounds, BuildingID};
|
||||
use map_model::geometry;
|
||||
use map_model::{Bounds, BuildingID, Map};
|
||||
use ordered_float::NotNaN;
|
||||
use std::f64;
|
||||
|
||||
@ -25,12 +24,7 @@ pub struct DrawBuilding {
|
||||
}
|
||||
|
||||
impl DrawBuilding {
|
||||
pub fn new(
|
||||
bldg: &map_model::Building,
|
||||
bounds: &Bounds,
|
||||
map: &map_model::Map,
|
||||
geom_map: &GeomMap,
|
||||
) -> DrawBuilding {
|
||||
pub fn new(bldg: &map_model::Building, bounds: &Bounds, map: &Map) -> DrawBuilding {
|
||||
let pts: Vec<Vec2d> = bldg.points
|
||||
.iter()
|
||||
.map(|pt| {
|
||||
@ -41,7 +35,7 @@ impl DrawBuilding {
|
||||
DrawBuilding {
|
||||
id: bldg.id,
|
||||
// TODO ideally start the path on a side of the building
|
||||
front_path: find_front_path(bldg.id, center(&pts), map, geom_map),
|
||||
front_path: find_front_path(bldg.id, center(&pts), map),
|
||||
polygon: pts,
|
||||
}
|
||||
}
|
||||
@ -61,7 +55,7 @@ impl DrawBuilding {
|
||||
geometry::point_in_polygon(x, y, &self.polygon)
|
||||
}
|
||||
|
||||
pub fn tooltip_lines(&self, map: &map_model::Map) -> Vec<String> {
|
||||
pub fn tooltip_lines(&self, map: &Map) -> Vec<String> {
|
||||
let b = map.get_b(self.id);
|
||||
let mut lines = vec![
|
||||
format!("Building #{:?} (from OSM way {})", self.id, b.osm_way_id),
|
||||
@ -86,9 +80,8 @@ fn center(pts: &Vec<Vec2d>) -> Vec2d {
|
||||
[x / len, y / len]
|
||||
}
|
||||
|
||||
fn road_to_line_string(r: map_model::RoadID, geom_map: &GeomMap) -> geo::LineString<f64> {
|
||||
let pts: Vec<geo::Point<f64>> = geom_map
|
||||
.get_r(r)
|
||||
fn road_to_line_string(r: map_model::RoadID, map: &Map) -> geo::LineString<f64> {
|
||||
let pts: Vec<geo::Point<f64>> = map.get_r(r)
|
||||
.lane_center_lines
|
||||
.iter()
|
||||
.flat_map(|pair| {
|
||||
@ -101,12 +94,7 @@ fn road_to_line_string(r: map_model::RoadID, geom_map: &GeomMap) -> geo::LineStr
|
||||
pts.into()
|
||||
}
|
||||
|
||||
fn find_front_path(
|
||||
id: BuildingID,
|
||||
bldg_center: Vec2d,
|
||||
map: &map_model::Map,
|
||||
geom_map: &GeomMap,
|
||||
) -> Option<[f64; 4]> {
|
||||
fn find_front_path(id: BuildingID, bldg_center: Vec2d, map: &Map) -> Option<[f64; 4]> {
|
||||
use geo::prelude::{ClosestPoint, EuclideanDistance};
|
||||
|
||||
if let Some(tag) = map.get_b(id)
|
||||
@ -127,7 +115,7 @@ fn find_front_path(
|
||||
&& map_model::has_osm_tag(&r.osm_tags, "name", street_name)
|
||||
{
|
||||
if let geo::Closest::SinglePoint(pt) =
|
||||
road_to_line_string(r.id, geom_map).closest_point(¢er_pt)
|
||||
road_to_line_string(r.id, map).closest_point(¢er_pt)
|
||||
{
|
||||
return Some((r.id, pt));
|
||||
}
|
||||
|
@ -5,10 +5,10 @@ extern crate map_model;
|
||||
|
||||
use aabb_quadtree::geom::Rect;
|
||||
use ezgui::GfxCtx;
|
||||
use geom::geometry;
|
||||
use graphics;
|
||||
use graphics::math::Vec2d;
|
||||
use graphics::types::Color;
|
||||
use map_model::geometry;
|
||||
use map_model::{Bounds, IntersectionID, Map};
|
||||
use render::DrawRoad;
|
||||
use std::f64;
|
||||
|
@ -5,8 +5,7 @@ extern crate map_model;
|
||||
|
||||
use aabb_quadtree::QuadTree;
|
||||
use aabb_quadtree::geom::{Point, Rect};
|
||||
use geom::GeomMap;
|
||||
use geom::geometry;
|
||||
use map_model::geometry;
|
||||
use map_model::{Bounds, BuildingID, IntersectionID, Map, ParcelID, Pt2D, RoadID, TurnID};
|
||||
use render::building::DrawBuilding;
|
||||
use render::intersection::DrawIntersection;
|
||||
@ -31,12 +30,12 @@ pub struct DrawMap {
|
||||
|
||||
impl DrawMap {
|
||||
// Also returns the center of the map in map-space
|
||||
pub fn new(map: &Map, geom_map: &GeomMap) -> (DrawMap, Bounds, Pt2D) {
|
||||
pub fn new(map: &Map) -> (DrawMap, Bounds, Pt2D) {
|
||||
let bounds = map.get_gps_bounds();
|
||||
|
||||
let mut roads: Vec<DrawRoad> = Vec::new();
|
||||
for r in map.all_roads() {
|
||||
roads.push(DrawRoad::new(r, geom_map));
|
||||
roads.push(DrawRoad::new(r));
|
||||
}
|
||||
|
||||
let mut turn_to_road_offset: HashMap<TurnID, usize> = HashMap::new();
|
||||
@ -44,8 +43,8 @@ impl DrawMap {
|
||||
let mut turns = map.get_turns_from_road(r.id);
|
||||
// Sort the turn icons by angle.
|
||||
turns.sort_by_key(|t| {
|
||||
let src_pt = geom_map.get_r(t.src).last_pt();
|
||||
let dst_pt = geom_map.get_r(t.dst).first_pt();
|
||||
let src_pt = map.get_r(t.src).last_pt();
|
||||
let dst_pt = map.get_r(t.dst).first_pt();
|
||||
let mut angle = (dst_pt[1] - src_pt[1])
|
||||
.atan2(dst_pt[0] - src_pt[0])
|
||||
.to_degrees();
|
||||
@ -62,7 +61,7 @@ impl DrawMap {
|
||||
|
||||
let turns: Vec<DrawTurn> = map.all_turns()
|
||||
.iter()
|
||||
.map(|t| DrawTurn::new(geom_map, t, turn_to_road_offset[&t.id]))
|
||||
.map(|t| DrawTurn::new(map, t, turn_to_road_offset[&t.id]))
|
||||
.collect();
|
||||
let intersections: Vec<DrawIntersection> = map.all_intersections()
|
||||
.iter()
|
||||
@ -70,7 +69,7 @@ impl DrawMap {
|
||||
.collect();
|
||||
let buildings: Vec<DrawBuilding> = map.all_buildings()
|
||||
.iter()
|
||||
.map(|b| DrawBuilding::new(b, &bounds, map, geom_map))
|
||||
.map(|b| DrawBuilding::new(b, &bounds, map))
|
||||
.collect();
|
||||
let parcels: Vec<DrawParcel> = map.all_parcels()
|
||||
.iter()
|
||||
|
@ -9,7 +9,7 @@ mod parcel;
|
||||
mod road;
|
||||
mod turn;
|
||||
|
||||
use geom;
|
||||
use map_model::geometry;
|
||||
pub use render::map::DrawMap;
|
||||
pub use render::road::DrawRoad;
|
||||
pub use render::turn::DrawTurn;
|
||||
@ -18,7 +18,7 @@ use std::f64;
|
||||
// These are all in meters
|
||||
const PARCEL_BOUNDARY_THICKNESS: f64 = 0.5;
|
||||
|
||||
const TURN_ICON_ARROW_THICKNESS: f64 = geom::BIG_ARROW_THICKNESS / 3.0;
|
||||
const TURN_ICON_ARROW_THICKNESS: f64 = geometry::BIG_ARROW_THICKNESS / 3.0;
|
||||
const BIG_ARROW_TIP_LENGTH: f64 = 1.0;
|
||||
const TURN_ICON_ARROW_TIP_LENGTH: f64 = BIG_ARROW_TIP_LENGTH * 0.8;
|
||||
const TURN_ICON_ARROW_LENGTH: f64 = 2.0;
|
||||
|
@ -5,10 +5,10 @@ extern crate map_model;
|
||||
|
||||
use aabb_quadtree::geom::Rect;
|
||||
use ezgui::GfxCtx;
|
||||
use geom::geometry;
|
||||
use graphics;
|
||||
use graphics::math::Vec2d;
|
||||
use graphics::types::Color;
|
||||
use map_model::geometry;
|
||||
use map_model::{Bounds, ParcelID, Pt2D};
|
||||
use render::PARCEL_BOUNDARY_THICKNESS;
|
||||
|
||||
|
@ -6,11 +6,10 @@ extern crate map_model;
|
||||
use aabb_quadtree::geom::Rect;
|
||||
use colors::{ColorScheme, Colors};
|
||||
use ezgui::GfxCtx;
|
||||
use geom;
|
||||
use geom::geometry;
|
||||
use graphics;
|
||||
use graphics::math::Vec2d;
|
||||
use graphics::types::Color;
|
||||
use map_model::geometry;
|
||||
use map_model::{Pt2D, RoadID};
|
||||
use render::PARCEL_BOUNDARY_THICKNESS;
|
||||
use std::f64;
|
||||
@ -25,16 +24,14 @@ pub struct DrawRoad {
|
||||
}
|
||||
|
||||
impl DrawRoad {
|
||||
pub fn new(road: &map_model::Road, geom_map: &geom::GeomMap) -> DrawRoad {
|
||||
let geom_r = geom_map.get_r(road.id);
|
||||
|
||||
pub fn new(road: &map_model::Road) -> DrawRoad {
|
||||
let thick_line =
|
||||
geometry::ThickLine::DrivingDirectionOnly(geom::LANE_THICKNESS, road.offset);
|
||||
geometry::ThickLine::DrivingDirectionOnly(geometry::LANE_THICKNESS, road.offset);
|
||||
DrawRoad {
|
||||
id: road.id,
|
||||
polygons: geometry::thick_multiline(&thick_line, &geom_r.pts),
|
||||
polygons: geometry::thick_multiline(&thick_line, &road.pts),
|
||||
yellow_center_lines: if road.use_yellow_center_lines {
|
||||
geom_r.pts.clone()
|
||||
road.pts.clone()
|
||||
} else {
|
||||
Vec::new()
|
||||
},
|
||||
@ -49,8 +46,10 @@ impl DrawRoad {
|
||||
}
|
||||
|
||||
pub fn draw_detail(&self, g: &mut GfxCtx, cs: &ColorScheme) {
|
||||
let road_marking =
|
||||
graphics::Line::new_round(cs.get(Colors::RoadOrientation), geom::BIG_ARROW_THICKNESS);
|
||||
let road_marking = graphics::Line::new_round(
|
||||
cs.get(Colors::RoadOrientation),
|
||||
geometry::BIG_ARROW_THICKNESS,
|
||||
);
|
||||
|
||||
for pair in self.yellow_center_lines.windows(2) {
|
||||
road_marking.draw(
|
||||
@ -62,12 +61,12 @@ impl DrawRoad {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_debug(&self, g: &mut GfxCtx, cs: &ColorScheme, geom_r: &geom::GeomRoad) {
|
||||
pub fn draw_debug(&self, g: &mut GfxCtx, cs: &ColorScheme, r: &map_model::Road) {
|
||||
let line =
|
||||
graphics::Line::new_round(cs.get(Colors::Debug), PARCEL_BOUNDARY_THICKNESS / 2.0);
|
||||
let circle = graphics::Ellipse::new(cs.get(Colors::BrightDebug));
|
||||
|
||||
for &(pt1, pt2) in &geom_r.lane_center_lines {
|
||||
for &(pt1, pt2) in &r.lane_center_lines {
|
||||
line.draw(
|
||||
[pt1.x(), pt1.y(), pt2.x(), pt2.y()],
|
||||
&g.ctx.draw_state,
|
||||
@ -102,7 +101,7 @@ impl DrawRoad {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn tooltip_lines(&self, map: &map_model::Map, geom_map: &geom::GeomMap) -> Vec<String> {
|
||||
pub fn tooltip_lines(&self, map: &map_model::Map) -> Vec<String> {
|
||||
let r = map.get_r(self.id);
|
||||
let mut lines = vec![
|
||||
format!(
|
||||
@ -116,7 +115,7 @@ impl DrawRoad {
|
||||
map.get_source_intersection(self.id).elevation_meters,
|
||||
map.get_destination_intersection(self.id).elevation_meters,
|
||||
),
|
||||
format!("Road is {}m long", geom_map.get_r(self.id).length()),
|
||||
format!("Road is {}m long", r.length()),
|
||||
];
|
||||
lines.extend(r.osm_tags.iter().cloned());
|
||||
lines
|
||||
|
@ -6,13 +6,11 @@ extern crate map_model;
|
||||
use aabb_quadtree::geom::Rect;
|
||||
use colors::{ColorScheme, Colors};
|
||||
use ezgui::GfxCtx;
|
||||
use geom;
|
||||
use geom::GeomMap;
|
||||
use geom::geometry;
|
||||
use graphics;
|
||||
use graphics::math::Vec2d;
|
||||
use graphics::types::Color;
|
||||
use map_model::TurnID;
|
||||
use map_model::geometry;
|
||||
use render::{BIG_ARROW_TIP_LENGTH, TURN_ICON_ARROW_LENGTH, TURN_ICON_ARROW_THICKNESS,
|
||||
TURN_ICON_ARROW_TIP_LENGTH};
|
||||
use std::f64;
|
||||
@ -28,12 +26,12 @@ pub struct DrawTurn {
|
||||
}
|
||||
|
||||
impl DrawTurn {
|
||||
pub fn new(geom_map: &GeomMap, turn: &map_model::Turn, offset_along_road: usize) -> DrawTurn {
|
||||
pub fn new(map: &map_model::Map, turn: &map_model::Turn, offset_along_road: usize) -> DrawTurn {
|
||||
let offset_along_road = offset_along_road as f64;
|
||||
let src_pt = geom_map.get_r(turn.src).last_pt();
|
||||
let dst_pt = geom_map.get_r(turn.dst).first_pt();
|
||||
let src_pt = map.get_r(turn.src).last_pt();
|
||||
let dst_pt = map.get_r(turn.dst).first_pt();
|
||||
let slope = vecmath::vec2_normalized([dst_pt[0] - src_pt[0], dst_pt[1] - src_pt[1]]);
|
||||
let last_line = geom_map.get_r(turn.src).last_line();
|
||||
let last_line = map.get_r(turn.src).last_line();
|
||||
|
||||
let icon_center = geometry::dist_along_line(
|
||||
// Start the distance from the intersection
|
||||
@ -64,7 +62,7 @@ impl DrawTurn {
|
||||
}
|
||||
|
||||
pub fn draw_full(&self, g: &mut GfxCtx, color: Color) {
|
||||
let turn_line = graphics::Line::new_round(color, geom::BIG_ARROW_THICKNESS);
|
||||
let turn_line = graphics::Line::new_round(color, geometry::BIG_ARROW_THICKNESS);
|
||||
turn_line.draw_arrow(
|
||||
[
|
||||
self.src_pt[0],
|
||||
|
@ -14,7 +14,6 @@ use ezgui::GfxCtx;
|
||||
use ezgui::ToggleableLayer;
|
||||
use ezgui::canvas::Canvas;
|
||||
use ezgui::input::UserInput;
|
||||
use geom;
|
||||
use graphics;
|
||||
use graphics::types::Color;
|
||||
use gui;
|
||||
@ -44,7 +43,6 @@ const MIN_ZOOM_FOR_ROAD_MARKERS: f64 = 5.0;
|
||||
|
||||
pub struct UI {
|
||||
map: map_model::Map,
|
||||
geom_map: geom::GeomMap,
|
||||
draw_map: render::DrawMap,
|
||||
control_map: ControlMap,
|
||||
|
||||
@ -76,17 +74,15 @@ impl UI {
|
||||
println!("Opening {}", abst_path);
|
||||
let data = map_model::load_pb(abst_path).expect("Couldn't load pb");
|
||||
let map = map_model::Map::new(&data);
|
||||
let geom_map = geom::GeomMap::new(&map);
|
||||
let (draw_map, _, center_pt) = render::DrawMap::new(&map, &geom_map);
|
||||
let control_map = ControlMap::new(&map, &geom_map);
|
||||
let (draw_map, _, center_pt) = render::DrawMap::new(&map);
|
||||
let control_map = ControlMap::new(&map);
|
||||
|
||||
let steepness_viz = SteepnessVisualizer::new(&map);
|
||||
let turn_colors = TurnColors::new(&control_map);
|
||||
let sim_ctrl = SimController::new(&map, &geom_map, rng_seed);
|
||||
let sim_ctrl = SimController::new(&map, rng_seed);
|
||||
|
||||
let mut ui = UI {
|
||||
map,
|
||||
geom_map,
|
||||
draw_map,
|
||||
control_map,
|
||||
steepness_viz,
|
||||
@ -165,10 +161,7 @@ impl UI {
|
||||
Vec::new()
|
||||
};
|
||||
for r in &roads_onscreen {
|
||||
for c in &self.sim_ctrl
|
||||
.sim
|
||||
.get_draw_cars_on_road(r.id, &self.geom_map)
|
||||
{
|
||||
for c in &self.sim_ctrl.sim.get_draw_cars_on_road(r.id, &self.map) {
|
||||
if c.contains_pt(x, y) {
|
||||
return Some(ID::Car(c.id));
|
||||
}
|
||||
@ -186,7 +179,7 @@ impl UI {
|
||||
if self.show_intersections.is_enabled() {
|
||||
for i in &self.draw_map.get_intersections_onscreen(screen_bbox) {
|
||||
for t in &self.map.get_i(i.id).turns {
|
||||
for c in &self.sim_ctrl.sim.get_draw_cars_on_turn(*t, &self.geom_map) {
|
||||
for c in &self.sim_ctrl.sim.get_draw_cars_on_turn(*t, &self.map) {
|
||||
if c.contains_pt(x, y) {
|
||||
return Some(ID::Car(c.id));
|
||||
}
|
||||
@ -201,10 +194,7 @@ impl UI {
|
||||
|
||||
if self.show_roads.is_enabled() {
|
||||
for r in &roads_onscreen {
|
||||
for c in &self.sim_ctrl
|
||||
.sim
|
||||
.get_draw_cars_on_road(r.id, &self.geom_map)
|
||||
{
|
||||
for c in &self.sim_ctrl.sim.get_draw_cars_on_road(r.id, &self.map) {
|
||||
if c.contains_pt(x, y) {
|
||||
return Some(ID::Car(c.id));
|
||||
}
|
||||
@ -287,7 +277,7 @@ impl UI {
|
||||
.unwrap_or_else(|| {
|
||||
self.traffic_signal_editor
|
||||
.as_ref()
|
||||
.and_then(|e| e.color_t(t, &self.geom_map, &self.control_map, &self.cs))
|
||||
.and_then(|e| e.color_t(t, &self.map, &self.control_map, &self.cs))
|
||||
.unwrap_or_else(|| {
|
||||
self.turn_colors
|
||||
.color_t(t)
|
||||
@ -344,7 +334,6 @@ impl gui::GUI for UI {
|
||||
if e.event(
|
||||
input,
|
||||
&self.map,
|
||||
&self.geom_map,
|
||||
&mut self.control_map,
|
||||
&self.current_selection_state,
|
||||
) {
|
||||
@ -359,7 +348,6 @@ impl gui::GUI for UI {
|
||||
if e.event(
|
||||
input,
|
||||
&self.map,
|
||||
&self.geom_map,
|
||||
&mut self.control_map,
|
||||
&self.current_selection_state,
|
||||
) {
|
||||
@ -376,10 +364,7 @@ impl gui::GUI for UI {
|
||||
|
||||
self.current_search_state = self.current_search_state.event(input);
|
||||
|
||||
if !edit_mode
|
||||
&& self.sim_ctrl
|
||||
.event(input, &self.geom_map, &self.map, &self.control_map)
|
||||
{
|
||||
if !edit_mode && self.sim_ctrl.event(input, &self.map, &self.control_map) {
|
||||
event_loop_mode = event_loop_mode.merge(animation::EventLoopMode::Animation);
|
||||
}
|
||||
|
||||
@ -510,7 +495,7 @@ impl gui::GUI for UI {
|
||||
r.draw_detail(g, &self.cs);
|
||||
}
|
||||
if self.debug_mode.is_enabled() {
|
||||
r.draw_debug(g, &self.cs, self.geom_map.get_r(r.id));
|
||||
r.draw_debug(g, &self.cs, self.map.get_r(r.id));
|
||||
}
|
||||
}
|
||||
|
||||
@ -523,20 +508,14 @@ impl gui::GUI for UI {
|
||||
if self.show_icons.is_enabled() {
|
||||
for t in &self.draw_map.get_turn_icons_onscreen(screen_bbox) {
|
||||
t.draw_icon(g, self.color_turn_icon(t.id), &self.cs);
|
||||
for c in &self.sim_ctrl
|
||||
.sim
|
||||
.get_draw_cars_on_turn(t.id, &self.geom_map)
|
||||
{
|
||||
for c in &self.sim_ctrl.sim.get_draw_cars_on_turn(t.id, &self.map) {
|
||||
c.draw(g, self.color_car(c.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for r in &roads_onscreen {
|
||||
for c in &self.sim_ctrl
|
||||
.sim
|
||||
.get_draw_cars_on_road(r.id, &self.geom_map)
|
||||
{
|
||||
for c in &self.sim_ctrl.sim.get_draw_cars_on_road(r.id, &self.map) {
|
||||
c.draw(g, self.color_car(c.id));
|
||||
}
|
||||
}
|
||||
@ -556,7 +535,6 @@ impl gui::GUI for UI {
|
||||
self.current_selection_state.draw(
|
||||
&self.map,
|
||||
&self.canvas,
|
||||
&self.geom_map,
|
||||
&self.draw_map,
|
||||
&self.control_map,
|
||||
&self.sim_ctrl.sim,
|
||||
|
@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "geom"
|
||||
version = "0.1.0"
|
||||
authors = ["Dustin Carlino <dabreegster@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
aabb-quadtree = "0.1.0"
|
||||
dimensioned = "0.6.0"
|
||||
map_model = { path = "../map_model" }
|
||||
piston2d-graphics = "*"
|
||||
vecmath = "0.3.1"
|
@ -1,278 +0,0 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
extern crate aabb_quadtree;
|
||||
|
||||
use aabb_quadtree::geom::{Point, Rect};
|
||||
use dimensioned::si;
|
||||
use graphics::math::Vec2d;
|
||||
use map_model::{Bounds, Pt2D};
|
||||
use std::f64;
|
||||
use vecmath;
|
||||
|
||||
pub mod angles {
|
||||
make_units! {
|
||||
ANGLES;
|
||||
ONE: Unitless;
|
||||
|
||||
base {
|
||||
RAD: Radian, "rad";
|
||||
}
|
||||
derived {}
|
||||
constants {}
|
||||
|
||||
fmt = true;
|
||||
}
|
||||
pub use self::f64consts::*;
|
||||
}
|
||||
|
||||
// TODO there may be existing crates that help with this stuff
|
||||
|
||||
// -1 for driving on the left
|
||||
const DRIVING_DIRECTION: f64 = 1.0;
|
||||
|
||||
use std;
|
||||
pub const EPSILON_METERS: si::Meter<f64> = si::Meter {
|
||||
value_unsafe: 0.00001,
|
||||
_marker: std::marker::PhantomData,
|
||||
};
|
||||
|
||||
pub enum ThickLine {
|
||||
// The f64's represent the full width of the thick line
|
||||
// The u8 is the offset from center
|
||||
DrivingDirectionOnly(f64, u8),
|
||||
Centered(f64),
|
||||
}
|
||||
|
||||
impl ThickLine {
|
||||
// Returns a scaling factor to project away from a center line, left and right side.
|
||||
fn project_away_lengths(&self) -> (f64, f64) {
|
||||
match *self {
|
||||
ThickLine::DrivingDirectionOnly(w, raw_offset) => {
|
||||
let offset = raw_offset as f64;
|
||||
if DRIVING_DIRECTION == 1.0 {
|
||||
(w * (offset + 1.0), w * offset)
|
||||
} else {
|
||||
(-1.0 * w * offset, -1.0 * w * (offset + 1.0))
|
||||
}
|
||||
}
|
||||
ThickLine::Centered(w) => (w / -2.0, w / 2.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn thick_multiline(style: &ThickLine, pts: &[Pt2D]) -> Vec<Vec<Vec2d>> {
|
||||
let mut polygons: Vec<Vec<Vec2d>> = Vec::new();
|
||||
|
||||
for slice in pts.windows(3) {
|
||||
let (pt1, pt2, pt3) = (&slice[0], &slice[1], &slice[2]);
|
||||
let quad1 = thick_line(style, pt1, pt2);
|
||||
let quad2 = thick_line(style, pt2, pt3);
|
||||
// The original quad. This over-extends in some cases, especially with multiple lanes.
|
||||
polygons.push(quad1.clone());
|
||||
// Add a quad to fill in the gap. Comment out to see part of the polyline problem very
|
||||
// clearly.
|
||||
polygons.push(vec![quad1[2], quad1[3], quad2[0], quad2[1], quad1[2]]);
|
||||
}
|
||||
|
||||
// We always need a quad for the last group, since there won't be a window of 3.
|
||||
polygons.push(thick_line(style, &pts[pts.len() - 2], &pts[pts.len() - 1]));
|
||||
|
||||
polygons
|
||||
}
|
||||
|
||||
pub fn thick_line(style: &ThickLine, pt1: &Pt2D, pt2: &Pt2D) -> Vec<Vec2d> {
|
||||
let x1 = pt1.x();
|
||||
let y1 = pt1.y();
|
||||
let x2 = pt2.x();
|
||||
let y2 = pt2.y();
|
||||
let angle = (y2 - y1).atan2(x2 - x1) + (f64::consts::PI / 2.0);
|
||||
// Project away from (x1, y1) in both directions by some amount
|
||||
let (side1_width, side2_width) = style.project_away_lengths();
|
||||
let c1 = [
|
||||
x1 + side1_width * angle.cos(),
|
||||
y1 + side1_width * angle.sin(),
|
||||
];
|
||||
let c2 = [
|
||||
x1 + side2_width * angle.cos(),
|
||||
y1 + side2_width * angle.sin(),
|
||||
];
|
||||
let c3 = [
|
||||
x2 + side1_width * angle.cos(),
|
||||
y2 + side1_width * angle.sin(),
|
||||
];
|
||||
let c4 = [
|
||||
x2 + side2_width * angle.cos(),
|
||||
y2 + side2_width * angle.sin(),
|
||||
];
|
||||
vec![c1, c2, c4, c3, c1]
|
||||
}
|
||||
|
||||
pub fn thick_line_from_angle(
|
||||
thickness: f64,
|
||||
line_length: f64,
|
||||
pt: &Pt2D,
|
||||
angle: angles::Radian<f64>,
|
||||
) -> Vec<Vec2d> {
|
||||
let pt2 = Pt2D::new(
|
||||
pt.x() + line_length * angle.value_unsafe.cos(),
|
||||
pt.y() + line_length * angle.value_unsafe.sin(),
|
||||
);
|
||||
thick_line(&ThickLine::Centered(thickness), &pt, &pt2)
|
||||
}
|
||||
|
||||
pub fn shift_line_perpendicularly_in_driving_direction(
|
||||
width: f64,
|
||||
pt1: &Pt2D,
|
||||
pt2: &Pt2D,
|
||||
) -> (Pt2D, Pt2D) {
|
||||
let x1 = pt1.x();
|
||||
let y1 = pt1.y();
|
||||
let x2 = pt2.x();
|
||||
let y2 = pt2.y();
|
||||
let half_pi = f64::consts::PI / 2.0;
|
||||
let angle = (y2 - y1).atan2(x2 - x1) + DRIVING_DIRECTION * half_pi;
|
||||
let shifted1 = Pt2D::new(x1 + width * angle.cos(), y1 + width * angle.sin());
|
||||
let shifted2 = Pt2D::new(x2 + width * angle.cos(), y2 + width * angle.sin());
|
||||
(shifted1, shifted2)
|
||||
}
|
||||
|
||||
// Algorithm from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html
|
||||
pub fn point_in_polygon(x: f64, y: f64, polygon: &[Vec2d]) -> bool {
|
||||
// TODO fix map conversion
|
||||
//assert_eq!(polygon[0], polygon[polygon.len() - 1]);
|
||||
if polygon[0] != polygon[polygon.len() - 1] {
|
||||
println!("WARNING: polygon {:?} isn't closed", polygon);
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut inside = false;
|
||||
for (pt1, pt2) in polygon.iter().zip(polygon.iter().skip(1)) {
|
||||
let x1 = pt1[0];
|
||||
let y1 = pt1[1];
|
||||
let x2 = pt2[0];
|
||||
let y2 = pt2[1];
|
||||
let intersect = ((y1 > y) != (y2 > y)) && (x < (x2 - x1) * (y - y1) / (y2 - y1) + x1);
|
||||
if intersect {
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
inside
|
||||
}
|
||||
|
||||
pub fn point_in_circle(x: f64, y: f64, center: Vec2d, radius: f64) -> bool {
|
||||
// avoid sqrt by squaring radius instead
|
||||
(x - center[0]).powi(2) + (y - center[1]).powi(2) < radius.powi(2)
|
||||
}
|
||||
|
||||
/*pub fn interpolate_along_line((pt1, pt2): (&Pt2D, &Pt2D), factor_along: f64) -> Vec2d {
|
||||
assert!(factor_along >= 0.0 && factor_along <= 1.0);
|
||||
let x = pt1.x + factor_along * (pt2.x - pt1.x);
|
||||
let y = pt1.y + factor_along * (pt2.y - pt1.y);
|
||||
return [x, y];
|
||||
}*/
|
||||
|
||||
pub fn euclid_dist((pt1, pt2): (&Pt2D, &Pt2D)) -> si::Meter<f64> {
|
||||
return ((pt1.x() - pt2.x()).powi(2) + (pt1.y() - pt2.y()).powi(2)).sqrt() * si::M;
|
||||
}
|
||||
|
||||
pub fn line_segments_intersect((pt1, pt2): (&Vec2d, &Vec2d), (pt3, pt4): (&Vec2d, &Vec2d)) -> bool {
|
||||
// From http://bryceboe.com/2006/10/23/line-segment-intersection-algorithm/
|
||||
is_counter_clockwise(pt1, pt3, pt4) != is_counter_clockwise(pt2, pt3, pt4)
|
||||
&& is_counter_clockwise(pt1, pt2, pt3) != is_counter_clockwise(pt1, pt2, pt4)
|
||||
}
|
||||
|
||||
fn is_counter_clockwise(pt1: &Vec2d, pt2: &Vec2d, pt3: &Vec2d) -> bool {
|
||||
(pt3[1] - pt1[1]) * (pt2[0] - pt1[0]) > (pt2[1] - pt1[1]) * (pt3[0] - pt1[0])
|
||||
}
|
||||
|
||||
pub fn dist_along_line((pt1, pt2): (&Pt2D, &Pt2D), dist_along: f64) -> Vec2d {
|
||||
//assert!(euclid_dist(&pt1, &pt2) <= dist_along);
|
||||
let vec = vecmath::vec2_normalized([pt2.x() - pt1.x(), pt2.y() - pt1.y()]);
|
||||
[pt1.x() + dist_along * vec[0], pt1.y() + dist_along * vec[1]]
|
||||
}
|
||||
|
||||
// TODO rm the other one
|
||||
pub fn safe_dist_along_line((pt1, pt2): (&Pt2D, &Pt2D), dist_along: si::Meter<f64>) -> Vec2d {
|
||||
let len = euclid_dist((pt1, pt2));
|
||||
if dist_along > len + EPSILON_METERS {
|
||||
panic!("cant do {} along a line of length {}", dist_along, len);
|
||||
}
|
||||
|
||||
let percent = (dist_along / len).value_unsafe;
|
||||
[
|
||||
pt1.x() + percent * (pt2.x() - pt1.x()),
|
||||
pt1.y() + percent * (pt2.y() - pt1.y()),
|
||||
]
|
||||
// TODO unit test
|
||||
/*
|
||||
let res_len = euclid_dist((pt1, &Pt2D::new(res[0], res[1])));
|
||||
if res_len != dist_along {
|
||||
println!("whats the delta btwn {} and {}?", res_len, dist_along);
|
||||
}
|
||||
*/}
|
||||
|
||||
pub fn get_bbox_for_polygons(polygons: &[Vec<Vec2d>]) -> Rect {
|
||||
let mut b = Bounds::new();
|
||||
for poly in polygons {
|
||||
for pt in poly {
|
||||
b.update(pt[0], pt[1]);
|
||||
}
|
||||
}
|
||||
Rect {
|
||||
top_left: Point {
|
||||
x: b.min_x as f32,
|
||||
y: b.min_y as f32,
|
||||
},
|
||||
bottom_right: Point {
|
||||
x: b.max_x as f32,
|
||||
y: b.max_y as f32,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gps_to_screen_space(gps: &Pt2D, b: &Bounds) -> Pt2D {
|
||||
// If not, havoc ensues
|
||||
assert!(b.contains(gps.x(), gps.y()));
|
||||
|
||||
// Invert y, so that the northernmost latitude is 0. Screen drawing order, not Cartesian grid.
|
||||
let base = Pt2D::new(b.min_x, b.max_y);
|
||||
// Apparently the aabb_quadtree can't handle 0, so add a bit.
|
||||
// TODO epsilon or epsilon - 1.0?
|
||||
let dx = base.gps_dist_meters(&Pt2D::new(gps.x(), base.y())) + f64::EPSILON;
|
||||
let dy = base.gps_dist_meters(&Pt2D::new(base.x(), gps.y())) + f64::EPSILON;
|
||||
// By default, 1 meter is one pixel. Normal zooming can change that. If we did scaling here,
|
||||
// then we'd have to update all of the other constants too.
|
||||
Pt2D::new(dx, dy)
|
||||
}
|
||||
|
||||
pub fn circle(center_x: f64, center_y: f64, radius: f64) -> [f64; 4] {
|
||||
[
|
||||
center_x - radius,
|
||||
center_y - radius,
|
||||
2.0 * radius,
|
||||
2.0 * radius,
|
||||
]
|
||||
}
|
||||
|
||||
pub fn circle_to_bbox(c: &[f64; 4]) -> Rect {
|
||||
Rect {
|
||||
top_left: Point {
|
||||
x: c[0] as f32,
|
||||
y: c[1] as f32,
|
||||
},
|
||||
bottom_right: Point {
|
||||
x: (c[0] + c[2]) as f32,
|
||||
y: (c[1] + c[3]) as f32,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn angle(from: &Pt2D, to: &Pt2D) -> angles::Radian<f64> {
|
||||
// DON'T invert y here
|
||||
let mut theta = (to.y() - from.y()).atan2(to.x() - from.x());
|
||||
// Normalize for easy output
|
||||
if theta < 0.0 {
|
||||
theta += 2.0 * f64::consts::PI;
|
||||
}
|
||||
theta * angles::RAD
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
// TODO maybe use dimensioned way more thoroughly inside this crate
|
||||
|
||||
extern crate aabb_quadtree;
|
||||
#[macro_use]
|
||||
extern crate dimensioned;
|
||||
extern crate graphics;
|
||||
extern crate map_model;
|
||||
extern crate vecmath;
|
||||
|
||||
pub mod geometry;
|
||||
mod map;
|
||||
mod road;
|
||||
mod turn;
|
||||
|
||||
pub use geometry::angles::{Radian, RAD};
|
||||
pub use map::GeomMap;
|
||||
pub use road::GeomRoad;
|
||||
pub use turn::GeomTurn;
|
||||
|
||||
pub const LANE_THICKNESS: f64 = 2.5;
|
||||
pub const BIG_ARROW_THICKNESS: f64 = 0.5;
|
||||
pub const TURN_DIST_FROM_INTERSECTION: f64 = 7.5;
|
@ -1,39 +0,0 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
extern crate map_model;
|
||||
|
||||
use map_model::{Map, RoadID, TurnID};
|
||||
use road::GeomRoad;
|
||||
use turn::GeomTurn;
|
||||
|
||||
pub struct GeomMap {
|
||||
pub roads: Vec<GeomRoad>,
|
||||
pub turns: Vec<GeomTurn>,
|
||||
}
|
||||
|
||||
impl GeomMap {
|
||||
pub fn new(map: &Map) -> GeomMap {
|
||||
let bounds = map.get_gps_bounds();
|
||||
|
||||
let mut roads: Vec<GeomRoad> = Vec::new();
|
||||
for r in map.all_roads() {
|
||||
roads.push(GeomRoad::new(r, &bounds));
|
||||
}
|
||||
|
||||
let turns: Vec<GeomTurn> = map.all_turns()
|
||||
.iter()
|
||||
.map(|t| GeomTurn::new(&roads, t))
|
||||
.collect();
|
||||
|
||||
GeomMap { roads, turns }
|
||||
}
|
||||
|
||||
// The alt to these is implementing std::ops::Index, but that's way more verbose!
|
||||
pub fn get_r(&self, id: RoadID) -> &GeomRoad {
|
||||
&self.roads[id.0]
|
||||
}
|
||||
|
||||
pub fn get_t(&self, id: TurnID) -> &GeomTurn {
|
||||
&self.turns[id.0]
|
||||
}
|
||||
}
|
116
geom/src/road.rs
116
geom/src/road.rs
@ -1,116 +0,0 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
extern crate dimensioned;
|
||||
extern crate map_model;
|
||||
|
||||
use BIG_ARROW_THICKNESS;
|
||||
use LANE_THICKNESS;
|
||||
use TURN_DIST_FROM_INTERSECTION;
|
||||
|
||||
use dimensioned::si;
|
||||
use geometry;
|
||||
use graphics::math::Vec2d;
|
||||
use map_model::{Bounds, Pt2D, RoadID};
|
||||
use std::f64;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GeomRoad {
|
||||
pub id: RoadID,
|
||||
// TODO need to settle on a proper Line type
|
||||
pub lane_center_lines: Vec<(Pt2D, Pt2D)>,
|
||||
// unshifted center points. consider computing these twice or otherwise not storing them
|
||||
pub pts: Vec<Pt2D>,
|
||||
}
|
||||
|
||||
impl GeomRoad {
|
||||
pub fn new(road: &map_model::Road, bounds: &Bounds) -> GeomRoad {
|
||||
let mut pts: Vec<Pt2D> = road.points
|
||||
.iter()
|
||||
.map(|pt| geometry::gps_to_screen_space(pt, bounds))
|
||||
.collect();
|
||||
// Shove the lines away from the intersection so they don't overlap.
|
||||
// TODO deal with tiny roads
|
||||
let num_pts = pts.len();
|
||||
let new_first_pt =
|
||||
geometry::dist_along_line((&pts[0], &pts[1]), TURN_DIST_FROM_INTERSECTION);
|
||||
let new_last_pt = geometry::dist_along_line(
|
||||
(&pts[num_pts - 1], &pts[num_pts - 2]),
|
||||
TURN_DIST_FROM_INTERSECTION,
|
||||
);
|
||||
pts[0] = Pt2D::from(new_first_pt);
|
||||
pts[num_pts - 1] = Pt2D::from(new_last_pt);
|
||||
|
||||
let offset = road.offset as f64;
|
||||
let lane_center_shift = if road.use_yellow_center_lines {
|
||||
// TODO I think this is unfair to one side, right? If we hover over the yellow line, it
|
||||
// shouldn't match either lane. Needs to be its own thing, or adjust the bbox.
|
||||
(LANE_THICKNESS / 2.0) + (BIG_ARROW_THICKNESS / 2.0)
|
||||
} else {
|
||||
LANE_THICKNESS * (offset + 0.5)
|
||||
};
|
||||
// TODO when drawing cars along these lines, do we have the line overlap problem? yes.
|
||||
let lane_center_lines: Vec<(Pt2D, Pt2D)> = pts.windows(2)
|
||||
.map(|pair| {
|
||||
geometry::shift_line_perpendicularly_in_driving_direction(
|
||||
lane_center_shift,
|
||||
&pair[0],
|
||||
&pair[1],
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
GeomRoad {
|
||||
lane_center_lines,
|
||||
pts,
|
||||
id: road.id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn first_pt(&self) -> Vec2d {
|
||||
let pt = &self.lane_center_lines[0].0;
|
||||
[pt.x(), pt.y()]
|
||||
}
|
||||
|
||||
pub fn last_pt(&self) -> Vec2d {
|
||||
let pt = &self.lane_center_lines.last().unwrap().1;
|
||||
[pt.x(), pt.y()]
|
||||
}
|
||||
|
||||
pub fn last_line(&self) -> (Pt2D, Pt2D) {
|
||||
*self.lane_center_lines.last().unwrap()
|
||||
}
|
||||
|
||||
pub fn dist_along(&self, dist_along: si::Meter<f64>) -> (Pt2D, geometry::angles::Radian<f64>) {
|
||||
// TODO valid to do euclidean distance on screen-space points that're formed from
|
||||
// Haversine?
|
||||
let mut dist_left = dist_along;
|
||||
for (idx, l) in self.lane_center_lines.iter().enumerate() {
|
||||
let length = geometry::euclid_dist((&l.0, &l.1));
|
||||
let epsilon = if idx == self.lane_center_lines.len() - 1 {
|
||||
geometry::EPSILON_METERS
|
||||
} else {
|
||||
0.0 * si::M
|
||||
};
|
||||
if dist_left <= length + epsilon {
|
||||
let vec = geometry::safe_dist_along_line((&l.0, &l.1), dist_left);
|
||||
return (Pt2D::new(vec[0], vec[1]), geometry::angle(&l.0, &l.1));
|
||||
}
|
||||
dist_left -= length;
|
||||
}
|
||||
panic!(
|
||||
"{} is longer than road {:?}'s {}. dist_left is {}",
|
||||
dist_along,
|
||||
self.id,
|
||||
self.length(),
|
||||
dist_left
|
||||
);
|
||||
}
|
||||
|
||||
pub fn length(&self) -> si::Meter<f64> {
|
||||
self.lane_center_lines
|
||||
.iter()
|
||||
.fold(0.0 * si::M, |so_far, l| {
|
||||
so_far + geometry::euclid_dist((&l.0, &l.1))
|
||||
})
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
extern crate dimensioned;
|
||||
extern crate map_model;
|
||||
|
||||
use dimensioned::si;
|
||||
use geometry;
|
||||
use graphics::math::Vec2d;
|
||||
use map_model::{Pt2D, TurnID};
|
||||
use road::GeomRoad;
|
||||
use std::f64;
|
||||
use vecmath;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GeomTurn {
|
||||
pub id: TurnID,
|
||||
src_pt: Vec2d,
|
||||
pub dst_pt: Vec2d,
|
||||
}
|
||||
|
||||
impl GeomTurn {
|
||||
pub fn new(roads: &[GeomRoad], turn: &map_model::Turn) -> GeomTurn {
|
||||
let src_pt = roads[turn.src.0].last_pt();
|
||||
let dst_pt = roads[turn.dst.0].first_pt();
|
||||
|
||||
GeomTurn {
|
||||
id: turn.id,
|
||||
src_pt,
|
||||
dst_pt,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn conflicts_with(&self, other: &GeomTurn) -> bool {
|
||||
if self.src_pt == other.src_pt {
|
||||
return false;
|
||||
}
|
||||
if self.dst_pt == other.dst_pt {
|
||||
return true;
|
||||
}
|
||||
geometry::line_segments_intersect(
|
||||
(&self.src_pt, &self.dst_pt),
|
||||
(&other.src_pt, &other.dst_pt),
|
||||
)
|
||||
}
|
||||
|
||||
// TODO share impl with GeomRoad
|
||||
pub fn dist_along(&self, dist_along: si::Meter<f64>) -> (Pt2D, geometry::angles::Radian<f64>) {
|
||||
let src = Pt2D::new(self.src_pt[0], self.src_pt[1]);
|
||||
let dst = Pt2D::new(self.dst_pt[0], self.dst_pt[1]);
|
||||
let vec = geometry::safe_dist_along_line((&src, &dst), dist_along);
|
||||
(Pt2D::new(vec[0], vec[1]), geometry::angle(&src, &dst))
|
||||
}
|
||||
|
||||
pub fn length(&self) -> si::Meter<f64> {
|
||||
let src = Pt2D::new(self.src_pt[0], self.src_pt[1]);
|
||||
let dst = Pt2D::new(self.dst_pt[0], self.dst_pt[1]);
|
||||
geometry::euclid_dist((&src, &dst))
|
||||
}
|
||||
|
||||
pub fn slope(&self) -> [f64; 2] {
|
||||
vecmath::vec2_normalized([
|
||||
self.dst_pt[0] - self.src_pt[0],
|
||||
self.dst_pt[1] - self.src_pt[1],
|
||||
])
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@ authors = ["Dustin Carlino <dabreegster@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
control = { path = "../control" }
|
||||
geom = { path = "../geom" }
|
||||
map_model = { path = "../map_model" }
|
||||
sim = { path = "../sim" }
|
||||
structopt = "0.2"
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
extern crate control;
|
||||
extern crate geom;
|
||||
extern crate map_model;
|
||||
extern crate sim;
|
||||
#[macro_use]
|
||||
@ -27,10 +26,9 @@ fn main() {
|
||||
println!("Opening {}", flags.abst_input);
|
||||
let data = map_model::load_pb(&flags.abst_input).expect("Couldn't load pb");
|
||||
let map = map_model::Map::new(&data);
|
||||
let geom_map = geom::GeomMap::new(&map);
|
||||
// TODO could load savestate
|
||||
let control_map = control::ControlMap::new(&map, &geom_map);
|
||||
let mut sim = sim::straw_model::Sim::new(&map, &geom_map, flags.rng_seed);
|
||||
let control_map = control::ControlMap::new(&map);
|
||||
let mut sim = sim::straw_model::Sim::new(&map, flags.rng_seed);
|
||||
// TODO need a notion of scenarios
|
||||
sim.spawn_many_on_empty_roads(&map, 100000);
|
||||
|
||||
@ -38,7 +36,7 @@ fn main() {
|
||||
let mut benchmark = sim.start_benchmark();
|
||||
loop {
|
||||
counter += 1;
|
||||
sim.step(&geom_map, &map, &control_map);
|
||||
sim.step(&map, &control_map);
|
||||
if counter % 1000 == 0 {
|
||||
let speed = sim.measure_speed(&mut benchmark);
|
||||
println!("{0}, speed = {1:.2}x", sim.summary(), speed);
|
||||
|
@ -13,7 +13,7 @@ extern crate serde_derive;
|
||||
extern crate vecmath;
|
||||
|
||||
mod building;
|
||||
mod geometry;
|
||||
pub mod geometry;
|
||||
mod intersection;
|
||||
mod map;
|
||||
mod parcel;
|
||||
@ -23,6 +23,7 @@ mod road;
|
||||
mod turn;
|
||||
|
||||
pub use building::{Building, BuildingID};
|
||||
pub use geometry::angles::{Radian, RAD};
|
||||
pub use intersection::{Intersection, IntersectionID};
|
||||
pub use map::Map;
|
||||
use ordered_float::NotNaN;
|
||||
|
@ -7,7 +7,6 @@ authors = ["Dustin Carlino <dabreegster@gmail.com>"]
|
||||
control = { path = "../control" }
|
||||
derivative = "1.0.0"
|
||||
dimensioned = "0.6.0"
|
||||
geom = { path = "../geom" }
|
||||
ezgui = { path = "../ezgui" }
|
||||
map_model = { path = "../map_model" }
|
||||
multimap = "0.4.0"
|
||||
|
@ -5,7 +5,6 @@ extern crate control;
|
||||
extern crate derivative;
|
||||
extern crate dimensioned;
|
||||
extern crate ezgui;
|
||||
extern crate geom;
|
||||
extern crate graphics;
|
||||
extern crate map_model;
|
||||
extern crate multimap;
|
||||
|
@ -4,8 +4,7 @@ use common::{CarID, Tick, SPEED_LIMIT};
|
||||
use control::ControlMap;
|
||||
use control::stop_signs::{ControlStopSign, TurnPriority};
|
||||
use dimensioned::si;
|
||||
use geom::GeomMap;
|
||||
use map_model::{IntersectionID, TurnID};
|
||||
use map_model::{IntersectionID, Map, TurnID};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use std;
|
||||
@ -28,15 +27,15 @@ impl IntersectionPolicy {
|
||||
car: CarID,
|
||||
turn: TurnID,
|
||||
time: Tick,
|
||||
geom_map: &GeomMap,
|
||||
map: &Map,
|
||||
control_map: &ControlMap,
|
||||
) -> bool {
|
||||
match *self {
|
||||
IntersectionPolicy::StopSignPolicy(ref mut p) => {
|
||||
p.can_do_turn(car, turn, time, geom_map, control_map)
|
||||
p.can_do_turn(car, turn, time, map, control_map)
|
||||
}
|
||||
IntersectionPolicy::TrafficSignalPolicy(ref mut p) => {
|
||||
p.can_do_turn(car, turn, time, geom_map, control_map)
|
||||
p.can_do_turn(car, turn, time, map, control_map)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -73,27 +72,25 @@ impl StopSign {
|
||||
}
|
||||
}
|
||||
|
||||
fn conflicts_with_accepted(&self, turn: TurnID, geom_map: &GeomMap) -> bool {
|
||||
let base_t = geom_map.get_t(turn);
|
||||
fn conflicts_with_accepted(&self, turn: TurnID, map: &Map) -> bool {
|
||||
let base_t = map.get_t(turn);
|
||||
self.accepted
|
||||
.values()
|
||||
.find(|t| base_t.conflicts_with(geom_map.get_t(**t)))
|
||||
.find(|t| base_t.conflicts_with(map.get_t(**t)))
|
||||
.is_some()
|
||||
}
|
||||
|
||||
fn conflicts_with_waiting_with_higher_priority(
|
||||
&self,
|
||||
turn: TurnID,
|
||||
geom_map: &GeomMap,
|
||||
map: &Map,
|
||||
ss: &ControlStopSign,
|
||||
) -> bool {
|
||||
let base_t = geom_map.get_t(turn);
|
||||
let base_t = map.get_t(turn);
|
||||
let base_priority = ss.get_priority(turn);
|
||||
self.waiting
|
||||
.values()
|
||||
.find(|t| {
|
||||
base_t.conflicts_with(geom_map.get_t(**t)) && ss.get_priority(**t) > base_priority
|
||||
})
|
||||
.find(|t| base_t.conflicts_with(map.get_t(**t)) && ss.get_priority(**t) > base_priority)
|
||||
.is_some()
|
||||
}
|
||||
|
||||
@ -102,7 +99,7 @@ impl StopSign {
|
||||
car: CarID,
|
||||
turn: TurnID,
|
||||
time: Tick,
|
||||
geom_map: &GeomMap,
|
||||
map: &Map,
|
||||
control_map: &ControlMap,
|
||||
) -> bool {
|
||||
// TODO assert turn is in this intersection
|
||||
@ -115,13 +112,13 @@ impl StopSign {
|
||||
self.started_waiting_at.insert(car, time);
|
||||
}
|
||||
|
||||
if self.conflicts_with_accepted(turn, geom_map) {
|
||||
if self.conflicts_with_accepted(turn, map) {
|
||||
self.waiting.insert(car, turn);
|
||||
return false;
|
||||
}
|
||||
|
||||
let ss = &control_map.stop_signs[&self.id];
|
||||
if self.conflicts_with_waiting_with_higher_priority(turn, geom_map, ss) {
|
||||
if self.conflicts_with_waiting_with_higher_priority(turn, map, ss) {
|
||||
self.waiting.insert(car, turn);
|
||||
return false;
|
||||
}
|
||||
@ -169,7 +166,7 @@ impl TrafficSignal {
|
||||
car: CarID,
|
||||
turn: TurnID,
|
||||
time: Tick,
|
||||
geom_map: &GeomMap,
|
||||
map: &Map,
|
||||
control_map: &ControlMap,
|
||||
) -> bool {
|
||||
// TODO assert turn is in this intersection
|
||||
@ -185,7 +182,7 @@ impl TrafficSignal {
|
||||
return false;
|
||||
}
|
||||
// How long will it take the car to cross the turn?
|
||||
let crossing_time = geom_map.get_t(turn).length() / SPEED_LIMIT;
|
||||
let crossing_time = map.get_t(turn).length() / SPEED_LIMIT;
|
||||
// TODO account for TIMESTEP
|
||||
|
||||
if crossing_time < remaining_cycle_time {
|
||||
|
@ -4,10 +4,10 @@ use common::{CarID, Tick, SPEED_LIMIT};
|
||||
use control::ControlMap;
|
||||
use dimensioned::si;
|
||||
use ezgui::GfxCtx;
|
||||
use geom::{geometry, GeomMap, Radian};
|
||||
use graphics;
|
||||
use graphics::math::Vec2d;
|
||||
use map_model::{LaneType, Map, Pt2D, RoadID, TurnID};
|
||||
use map_model::geometry;
|
||||
use map_model::{LaneType, Map, Pt2D, Radian, RoadID, TurnID};
|
||||
use multimap::MultiMap;
|
||||
use rand::{FromEntropy, Rng, SeedableRng, XorShiftRng};
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
@ -88,17 +88,17 @@ impl On {
|
||||
}
|
||||
}
|
||||
|
||||
fn length(&self, geom_map: &GeomMap) -> si::Meter<f64> {
|
||||
fn length(&self, map: &Map) -> si::Meter<f64> {
|
||||
match self {
|
||||
&On::Road(id) => geom_map.get_r(id).length(),
|
||||
&On::Turn(id) => geom_map.get_t(id).length(),
|
||||
&On::Road(id) => map.get_r(id).length(),
|
||||
&On::Turn(id) => map.get_t(id).length(),
|
||||
}
|
||||
}
|
||||
|
||||
fn dist_along(&self, dist: si::Meter<f64>, geom_map: &GeomMap) -> (Pt2D, Radian<f64>) {
|
||||
fn dist_along(&self, dist: si::Meter<f64>, map: &Map) -> (Pt2D, Radian<f64>) {
|
||||
match self {
|
||||
&On::Road(id) => geom_map.get_r(id).dist_along(dist),
|
||||
&On::Turn(id) => geom_map.get_t(id).dist_along(dist),
|
||||
&On::Road(id) => map.get_r(id).dist_along(dist),
|
||||
&On::Turn(id) => map.get_t(id).dist_along(dist),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -130,13 +130,13 @@ impl Car {
|
||||
]
|
||||
}
|
||||
|
||||
fn step(&self, geom_map: &GeomMap, map: &Map, time: Tick, rng: &mut XorShiftRng) -> Action {
|
||||
fn step(&self, map: &Map, time: Tick, rng: &mut XorShiftRng) -> Action {
|
||||
if let Some(on) = self.waiting_for {
|
||||
return Action::Goto(on);
|
||||
}
|
||||
|
||||
let dist = SPEED_LIMIT * (time - self.started_at).as_time();
|
||||
if dist < self.on.length(geom_map) {
|
||||
if dist < self.on.length(map) {
|
||||
return Action::Continue;
|
||||
}
|
||||
|
||||
@ -158,16 +158,12 @@ impl Car {
|
||||
}
|
||||
|
||||
// Returns the angle and the dist along the road/turn too
|
||||
fn get_best_case_pos(
|
||||
&self,
|
||||
time: Tick,
|
||||
geom_map: &GeomMap,
|
||||
) -> (Pt2D, Radian<f64>, si::Meter<f64>) {
|
||||
fn get_best_case_pos(&self, time: Tick, map: &Map) -> (Pt2D, Radian<f64>, si::Meter<f64>) {
|
||||
let mut dist = SPEED_LIMIT * (time - self.started_at).as_time();
|
||||
if self.waiting_for.is_some() {
|
||||
dist = self.on.length(geom_map);
|
||||
dist = self.on.length(map);
|
||||
}
|
||||
let (pt, angle) = self.on.dist_along(dist, geom_map);
|
||||
let (pt, angle) = self.on.dist_along(dist, map);
|
||||
(pt, angle, dist)
|
||||
}
|
||||
}
|
||||
@ -180,11 +176,11 @@ struct SimQueue {
|
||||
}
|
||||
|
||||
impl SimQueue {
|
||||
fn new(id: On, geom_map: &GeomMap) -> SimQueue {
|
||||
fn new(id: On, map: &Map) -> SimQueue {
|
||||
SimQueue {
|
||||
id,
|
||||
cars_queue: Vec::new(),
|
||||
capacity: ((id.length(geom_map) / FOLLOWING_DISTANCE).floor() as usize).max(1),
|
||||
capacity: ((id.length(map) / FOLLOWING_DISTANCE).floor() as usize).max(1),
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,27 +231,27 @@ impl SimQueue {
|
||||
|
||||
// TODO this starts cars with their front aligned with the end of the road, sticking their back
|
||||
// into the intersection. :(
|
||||
fn get_draw_cars(&self, sim: &Sim, geom_map: &GeomMap) -> Vec<DrawCar> {
|
||||
fn get_draw_cars(&self, sim: &Sim, map: &Map) -> Vec<DrawCar> {
|
||||
if self.cars_queue.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut results = Vec::new();
|
||||
let (pos1, angle1, dist_along1) =
|
||||
sim.cars[&self.cars_queue[0]].get_best_case_pos(sim.time, geom_map);
|
||||
sim.cars[&self.cars_queue[0]].get_best_case_pos(sim.time, map);
|
||||
results.push(DrawCar::new(self.cars_queue[0], &pos1, angle1));
|
||||
let mut dist_along_bound = dist_along1;
|
||||
|
||||
for id in self.cars_queue.iter().skip(1) {
|
||||
let (pos, angle, dist_along) = sim.cars[id].get_best_case_pos(sim.time, geom_map);
|
||||
let (pos, angle, dist_along) = sim.cars[id].get_best_case_pos(sim.time, map);
|
||||
if dist_along_bound - FOLLOWING_DISTANCE > dist_along {
|
||||
results.push(DrawCar::new(*id, &pos, angle));
|
||||
dist_along_bound = dist_along;
|
||||
} else {
|
||||
dist_along_bound -= FOLLOWING_DISTANCE;
|
||||
// If not, we violated room_at_end() and reset() didn't catch it
|
||||
assert!(dist_along_bound >= 0.0 * si::M, "dist_along_bound went negative ({}) for {:?} (length {}) with queue {:?}. first car at {}", dist_along_bound, self.id, self.id.length(geom_map), self.cars_queue, dist_along1);
|
||||
let (pt, angle) = self.id.dist_along(dist_along_bound, geom_map);
|
||||
assert!(dist_along_bound >= 0.0 * si::M, "dist_along_bound went negative ({}) for {:?} (length {}) with queue {:?}. first car at {}", dist_along_bound, self.id, self.id.length(map), self.cars_queue, dist_along1);
|
||||
let (pt, angle) = self.id.dist_along(dist_along_bound, map);
|
||||
results.push(DrawCar::new(*id, &pt, angle));
|
||||
}
|
||||
}
|
||||
@ -284,7 +280,7 @@ pub struct Sim {
|
||||
}
|
||||
|
||||
impl Sim {
|
||||
pub fn new(map: &Map, geom_map: &GeomMap, rng_seed: Option<u8>) -> Sim {
|
||||
pub fn new(map: &Map, rng_seed: Option<u8>) -> Sim {
|
||||
let mut rng = XorShiftRng::from_entropy();
|
||||
if let Some(seed) = rng_seed {
|
||||
rng = XorShiftRng::from_seed([seed; 16]);
|
||||
@ -308,11 +304,11 @@ impl Sim {
|
||||
cars: BTreeMap::new(),
|
||||
roads: map.all_roads()
|
||||
.iter()
|
||||
.map(|r| SimQueue::new(On::Road(r.id), geom_map))
|
||||
.map(|r| SimQueue::new(On::Road(r.id), map))
|
||||
.collect(),
|
||||
turns: map.all_turns()
|
||||
.iter()
|
||||
.map(|t| SimQueue::new(On::Turn(t.id), geom_map))
|
||||
.map(|t| SimQueue::new(On::Turn(t.id), map))
|
||||
.collect(),
|
||||
time: Tick::zero(),
|
||||
id_counter: 0,
|
||||
@ -366,7 +362,7 @@ impl Sim {
|
||||
println!("Spawned {}", n);
|
||||
}
|
||||
|
||||
pub fn step(&mut self, geom_map: &GeomMap, map: &Map, control_map: &ControlMap) {
|
||||
pub fn step(&mut self, map: &Map, control_map: &ControlMap) {
|
||||
self.time.increment();
|
||||
|
||||
// Could be concurrent. Ask all cars for their move, reinterpreting Goto to see if there's
|
||||
@ -381,7 +377,7 @@ impl Sim {
|
||||
for c in self.cars.values() {
|
||||
requested_moves.push((
|
||||
c.id,
|
||||
match c.step(geom_map, map, self.time, &mut self.rng) {
|
||||
match c.step(map, self.time, &mut self.rng) {
|
||||
Action::Goto(on) => {
|
||||
// This is a monotonic property in conjunction with
|
||||
// new_car_entered_this_step. The last car won't go backwards.
|
||||
@ -426,7 +422,7 @@ impl Sim {
|
||||
*id,
|
||||
t,
|
||||
self.time,
|
||||
geom_map,
|
||||
map,
|
||||
control_map,
|
||||
);
|
||||
}
|
||||
@ -488,11 +484,11 @@ impl Sim {
|
||||
self.cars[&c].waiting_for.is_none()
|
||||
}
|
||||
|
||||
pub fn get_draw_cars_on_road(&self, r: RoadID, geom_map: &GeomMap) -> Vec<DrawCar> {
|
||||
let mut cars = self.roads[r.0].get_draw_cars(&self, geom_map);
|
||||
pub fn get_draw_cars_on_road(&self, r: RoadID, map: &Map) -> Vec<DrawCar> {
|
||||
let mut cars = self.roads[r.0].get_draw_cars(&self, map);
|
||||
for c in &mut cars {
|
||||
if let Some(on) = self.cars[&c.id].waiting_for {
|
||||
let slope = geom_map.get_t(on.as_turn()).slope();
|
||||
let slope = map.get_t(on.as_turn()).slope();
|
||||
c.turn_arrow = Some([
|
||||
c.front.x() - (CAR_LENGTH / 2.0) * slope[0],
|
||||
c.front.y() - (CAR_LENGTH / 2.0) * slope[1],
|
||||
@ -504,8 +500,8 @@ impl Sim {
|
||||
cars
|
||||
}
|
||||
|
||||
pub fn get_draw_cars_on_turn(&self, t: TurnID, geom_map: &GeomMap) -> Vec<DrawCar> {
|
||||
self.turns[t.0].get_draw_cars(&self, geom_map)
|
||||
pub fn get_draw_cars_on_turn(&self, t: TurnID, map: &Map) -> Vec<DrawCar> {
|
||||
self.turns[t.0].get_draw_cars(&self, map)
|
||||
}
|
||||
|
||||
pub fn summary(&self) -> String {
|
||||
|
@ -1,5 +1,4 @@
|
||||
extern crate control;
|
||||
extern crate geom;
|
||||
extern crate map_model;
|
||||
extern crate sim;
|
||||
|
||||
@ -15,11 +14,10 @@ fn from_scratch() {
|
||||
// initialization and plumbing is easier
|
||||
let data = map_model::load_pb(input).expect("Couldn't load input");
|
||||
let map = map_model::Map::new(&data);
|
||||
let geom_map = geom::GeomMap::new(&map);
|
||||
let control_map = control::ControlMap::new(&map, &geom_map);
|
||||
let control_map = control::ControlMap::new(&map);
|
||||
|
||||
let mut sim1 = sim::straw_model::Sim::new(&map, &geom_map, Some(rng_seed));
|
||||
let mut sim2 = sim::straw_model::Sim::new(&map, &geom_map, Some(rng_seed));
|
||||
let mut sim1 = sim::straw_model::Sim::new(&map, Some(rng_seed));
|
||||
let mut sim2 = sim::straw_model::Sim::new(&map, Some(rng_seed));
|
||||
sim1.spawn_many_on_empty_roads(&map, spawn_count);
|
||||
sim2.spawn_many_on_empty_roads(&map, spawn_count);
|
||||
|
||||
@ -31,8 +29,8 @@ fn from_scratch() {
|
||||
sim2.write_savestate("sim2_state.json").unwrap();
|
||||
panic!("sim state differs at {}. compare sim1_state.json and sim2_state.json", sim1.time);
|
||||
}
|
||||
sim1.step(&geom_map, &map, &control_map);
|
||||
sim2.step(&geom_map, &map, &control_map);
|
||||
sim1.step(&map, &control_map);
|
||||
sim2.step(&map, &control_map);
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,17 +44,16 @@ fn with_savestating() {
|
||||
println!("Creating two simulations");
|
||||
let data = map_model::load_pb(input).expect("Couldn't load input");
|
||||
let map = map_model::Map::new(&data);
|
||||
let geom_map = geom::GeomMap::new(&map);
|
||||
let control_map = control::ControlMap::new(&map, &geom_map);
|
||||
let control_map = control::ControlMap::new(&map);
|
||||
|
||||
let mut sim1 = sim::straw_model::Sim::new(&map, &geom_map, Some(rng_seed));
|
||||
let mut sim2 = sim::straw_model::Sim::new(&map, &geom_map, Some(rng_seed));
|
||||
let mut sim1 = sim::straw_model::Sim::new(&map, Some(rng_seed));
|
||||
let mut sim2 = sim::straw_model::Sim::new(&map, Some(rng_seed));
|
||||
sim1.spawn_many_on_empty_roads(&map, spawn_count);
|
||||
sim2.spawn_many_on_empty_roads(&map, spawn_count);
|
||||
|
||||
for _ in 1..600 {
|
||||
sim1.step(&geom_map, &map, &control_map);
|
||||
sim2.step(&geom_map, &map, &control_map);
|
||||
sim1.step(&map, &control_map);
|
||||
sim2.step(&map, &control_map);
|
||||
}
|
||||
|
||||
if sim1 != sim2 {
|
||||
@ -68,7 +65,7 @@ fn with_savestating() {
|
||||
sim1.write_savestate("sim1_savestate.json").unwrap();
|
||||
|
||||
for _ in 1..60 {
|
||||
sim1.step(&geom_map, &map, &control_map);
|
||||
sim1.step(&map, &control_map);
|
||||
}
|
||||
|
||||
if sim1 == sim2 {
|
||||
|
Loading…
Reference in New Issue
Block a user