making geom validation interactive

This commit is contained in:
Dustin Carlino 2018-07-05 15:37:35 -07:00
parent dee6c97f4c
commit 445a1925eb
6 changed files with 110 additions and 42 deletions

View File

@ -97,8 +97,21 @@ wait slow down even more -- before any of this change, lanes on adjacent roads s
- follow aorta's multi phase map construction better.
- polish intersection geometry
- figure out how to iterate quickly in experimental.
- aha, big bug! we only try to trim first/last lines. do the whole polyline.
- can think of an easy fixpoint approach to try first, even though it's inefficient.
- wait, the fixpoint is also incorrect. :(
- before trimming back lines, project out the correct width. sort all those points by angle from the center. thats the intersection polygon? then somehow trim back lines to hit that nicely.
- do the current trim_lines thing, but with lines, not segments? no, there'd be many almost-parallel lines.
- at a T intersection, some lines aren't trimmed back at all
- the lane polygons overlap, even though the lines dont
- https://www.politesi.polimi.it/bitstream/10589/112826/4/2015_10_TOPTAS.pdf pg38
- just make polygons around center lines, then intersect?
- shift turn icons and stop markings and such away from crosswalk
- rename Road to Lane
@ -120,10 +133,6 @@ wait slow down even more -- before any of this change, lanes on adjacent roads s
- also a polygon struct? for parcels and buildings. maybe have a form that's pre-triangulated?
- isolate vec2d
- improve intersection geom?
- https://www.politesi.polimi.it/bitstream/10589/112826/4/2015_10_TOPTAS.pdf
- just make polygons around center lines, then intersect?
- shift turn icons and stop markings and such away from crosswalk
- figure out what to do about yellow center lines
- yellow and white lines intersect cars and turn icons and such
- who should own drawing them?

View File

@ -7,7 +7,7 @@ OSM, elevation data, King Country GIS data, etc, and also manual edits.
- Polygons shouldn't overlap
- Can export to various formats and announce for anyone's use (though waiting for the editor might be wiser)
- Have fun with themed rendering: halloween, winter, jungle, 8bit, organic (deform buildings)
- Have fun with themed rendering: halloween, winter, jungle, 8bit, organic (deform buildings), floorplan
## Phase 2: Editor

View File

@ -9,6 +9,7 @@ abstutil = { path = "../abstutil" }
control = { path = "../control" }
dimensioned = "0.6.0"
ezgui = { path = "../ezgui" }
generator = "0.6"
geo = "0.9.1"
geom = { path = "../geom" }
map_model = { path = "../map_model" }

View File

@ -5,6 +5,8 @@ extern crate abstutil;
extern crate control;
extern crate dimensioned;
extern crate ezgui;
#[macro_use]
extern crate generator;
extern crate geo;
extern crate geom;
extern crate glutin_window;

View File

@ -1,4 +1,7 @@
use generator;
use geo;
use ezgui::input::UserInput;
use piston::input::Key;
use geo::prelude::Intersects;
use graphics::math::Vec2d;
use map_model::{BuildingID, IntersectionID, ParcelID, RoadID};
@ -14,47 +17,90 @@ enum ID {
// Eventually this should be part of an interactive map fixing pipeline. Find problems, jump to
// them, ask for the resolution, record it.
pub fn validate_geometry(draw_map: &render::DrawMap) {
let mut objects: Vec<(ID, geo::Polygon<f64>)> = Vec::new();
for r in &draw_map.roads {
for poly in &r.polygons {
objects.push((ID::Road(r.id), make_poly(poly)));
pub struct Validator {
gen: generator::Generator<'static, (), (ID, ID)>,
current_problem: Option<(ID, ID)>,
}
impl Validator {
pub fn new(draw_map: &render::DrawMap) -> Validator {
let mut objects: Vec<(ID, Vec<geo::Polygon<f64>>)> = Vec::new();
for r in &draw_map.roads {
objects.push((ID::Road(r.id), r.polygons.iter().map(|poly| make_poly(poly)).collect()));
}
for i in &draw_map.intersections {
objects.push((ID::Intersection(i.id), vec![make_poly(&i.polygon)]));
}
for b in &draw_map.buildings {
objects.push((ID::Building(b.id), vec![make_poly(&b.polygon)]));
}
for p in &draw_map.parcels {
objects.push((ID::Parcel(p.id), vec![make_poly(&p.fill_polygon)]));
}
println!(
"{} objects total. About {} possible overlaps",
objects.len(),
objects.len().pow(2)
);
// TODO scoped vs unscoped?
let gen = generator::Gn::<()>::new_scoped(move |mut s| {
// TODO use a quadtree to prune
for (id1, ls1) in &objects {
for (id2, ls2) in &objects {
// Overlaps are symmetric and we're not worried about self-intersection, so only
// check when id1 < id2.
if id1 >= id2 {
continue;
}
// Buildings and parcels are expected to overlap.
match (id1, id2) {
(ID::Building(_), ID::Parcel(_)) => continue,
(ID::Parcel(_), ID::Building(_)) => continue,
_ => {}
};
'outer: for poly1 in ls1 {
for poly2 in ls2 {
if poly1.intersects(poly2) {
s.yield_((*id1, *id2));
break 'outer;
}
}
}
}
}
done!();
});
Validator {
gen,
current_problem: None,
}
}
for i in &draw_map.intersections {
objects.push((ID::Intersection(i.id), make_poly(&i.polygon)));
}
for b in &draw_map.buildings {
objects.push((ID::Building(b.id), make_poly(&b.polygon)));
}
for p in &draw_map.parcels {
objects.push((ID::Parcel(p.id), make_poly(&p.fill_polygon)));
}
println!(
"{} objects total. About {} possible overlaps",
objects.len(),
objects.len().pow(2)
);
pub fn event(&mut self, input: &mut UserInput) -> bool {
// Initialize or advance?
if !self.current_problem.is_some() || input.key_pressed(Key::N, "Press N to see the next problem") {
self.current_problem = self.gen.next();
// TODO use a quadtree to prune
for (id1, poly1) in &objects {
for (id2, poly2) in &objects {
// Overlaps are symmetric and we're not worried about self-intersection, so only
// check when id1 < id2.
if id1 >= id2 {
continue;
}
// Buildings and parcels are expected to overlap.
match (id1, id2) {
(ID::Building(_), ID::Parcel(_)) => continue,
(ID::Parcel(_), ID::Building(_)) => continue,
_ => {}
};
if poly1.intersects(poly2) {
println!("{:?} and {:?} overlap", id1, id2);
if let Some((id1, id2)) = self.current_problem {
println!("{:?} and {:?} intersect", id1, id2);
return false;
} else {
println!("No more problems!");
return true;
}
}
if input.key_pressed(Key::Escape, "Press Escape to stop looking at problems") {
println!("Quit geometry validator");
return true;
}
// Later, keys for resolving problems
false
}
}

View File

@ -21,7 +21,7 @@ use piston::window::Size;
use plugins::classification::OsmClassifier;
use plugins::color_picker::ColorPicker;
use plugins::floodfill::Floodfiller;
use plugins::geom_validation;
use plugins::geom_validation::Validator;
use plugins::search::SearchState;
use plugins::selection::{Hider, SelectionState, ID};
use plugins::sim_controls::SimController;
@ -65,6 +65,7 @@ pub struct UI {
stop_sign_editor: Option<StopSignEditor>,
sim_ctrl: SimController,
color_picker: ColorPicker,
geom_validator: Option<Validator>,
canvas: Canvas,
// TODO maybe never pass this to other places? Always resolve colors here?
@ -116,6 +117,7 @@ impl UI {
traffic_signal_editor: None,
stop_sign_editor: None,
color_picker: ColorPicker::new(),
geom_validator: None,
canvas: Canvas::new(),
cs: ColorScheme::load("color_scheme").unwrap(),
@ -476,12 +478,20 @@ impl gui::GUI for UI {
}
}
if let Some(mut v) = self.geom_validator {
if v.event(input) {
self.geom_validator = None;
} else {
self.geom_validator = Some(v);
}
}
if input.unimportant_key_pressed(Key::S, "Spawn 1000 cars in random places") {
self.sim_ctrl.sim.spawn_many_on_empty_roads(&self.map, 1000);
}
if input.unimportant_key_pressed(Key::I, "Validate map geometry") {
geom_validation::validate_geometry(&self.draw_map);
self.geom_validator = Some(Validator::new(&self.draw_map));
}
if input.unimportant_key_pressed(Key::Escape, "Press escape to quit") {