From 0c283d0da7c10afa211f8a5448b5e5b690c0c0dc Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Sat, 11 Jul 2020 16:05:26 -0700 Subject: [PATCH] finish up the grand geom stabilization with ring and some docs. regenerated everything from scratch. one effect is more parking blockface gets matched onto border roads, but that's fine. --- convert_osm/src/clip.rs | 2 +- convert_osm/src/osm_reader.rs | 2 +- data/MANIFEST.txt | 62 ++++++++++++------------ docs/dev.md | 24 +++++++-- ezgui/src/managed.rs | 3 +- ezgui/src/widgets/dropdown.rs | 5 +- game/src/common/city_picker.rs | 2 +- game/src/common/minimap.rs | 2 +- game/src/devtools/kml.rs | 2 +- game/src/devtools/story.rs | 6 +-- game/src/layer/traffic.rs | 7 ++- game/src/render/building.rs | 4 +- game/src/render/intersection.rs | 4 +- game/src/render/parking_lot.rs | 2 +- game/src/render/traffic_signal.rs | 2 +- game/src/sandbox/dashboards/summaries.rs | 4 +- geom/src/circle.rs | 2 +- geom/src/polygon.rs | 11 ++--- geom/src/polyline.rs | 4 +- geom/src/ring.rs | 42 +++++----------- map_model/src/make/turns.rs | 2 +- 21 files changed, 99 insertions(+), 95 deletions(-) diff --git a/convert_osm/src/clip.rs b/convert_osm/src/clip.rs index 21d1af938b..603762a7d1 100644 --- a/convert_osm/src/clip.rs +++ b/convert_osm/src/clip.rs @@ -9,7 +9,7 @@ pub fn clip_map(map: &mut RawMap, timer: &mut Timer) { // So we can use retain_btreemap without borrowing issues let boundary_polygon = map.boundary_polygon.clone(); - let boundary_ring = Ring::new(boundary_polygon.points().clone()); + let boundary_ring = Ring::must_new(boundary_polygon.points().clone()); // This is kind of indirect and slow, but first pass -- just remove roads that start or end // outside the boundary polygon. diff --git a/convert_osm/src/osm_reader.rs b/convert_osm/src/osm_reader.rs index 5c9545fd21..e829a4bf4c 100644 --- a/convert_osm/src/osm_reader.rs +++ b/convert_osm/src/osm_reader.rs @@ -180,7 +180,7 @@ pub fn extract_osm( } } - let boundary = Ring::new(map.boundary_polygon.points().clone()); + let boundary = Ring::must_new(map.boundary_polygon.points().clone()); let mut simple_turn_restrictions = Vec::new(); let mut complicated_turn_restrictions = Vec::new(); diff --git a/data/MANIFEST.txt b/data/MANIFEST.txt index 2f622ea6de..7c353a46f7 100644 --- a/data/MANIFEST.txt +++ b/data/MANIFEST.txt @@ -3,22 +3,22 @@ data/input/austin/osm/downtown_atx.osm,a30b0f460a481598e494f16a9d07a822,https:// data/input/austin/osm/huge_austin.osm,fb166029fc8006bd20dc959fbbbde3b6,https://www.dropbox.com/s/4x421o9o8px0m6o/huge_austin.osm.zip?dl=0 data/input/krakow/osm/huge_krakow.osm,0b098456c8ae844ff240ca9fe3b617d1,https://www.dropbox.com/s/fe2e8qzd4g5tkoy/huge_krakow.osm.zip?dl=0 data/input/krakow/osm/malopolskie-latest.osm.pbf,d848a278092001aaab3ced09de4d98ff,https://www.dropbox.com/s/ia2uwy3z52r6hpe/malopolskie-latest.osm.pbf.zip?dl=0 -data/input/raw_maps/ballard.bin,fea0486d3925c7ffce54d0e21139ab75,https://www.dropbox.com/s/dfbtfu6qq7shj9t/ballard.bin.zip?dl=0 -data/input/raw_maps/downtown.bin,5da76c12b035bcde09d0735c6673d4ae,https://www.dropbox.com/s/f3jz7qk6xiij5po/downtown.bin.zip?dl=0 +data/input/raw_maps/ballard.bin,4d72687ab860144d19ae3f1328f2b61d,https://www.dropbox.com/s/dkxhkir85rtm906/ballard.bin.zip?dl=0 +data/input/raw_maps/downtown.bin,48886494c43fd7b9db12a53f990dcb4f,https://www.dropbox.com/s/87rpndh1yudrlxb/downtown.bin.zip?dl=0 data/input/raw_maps/downtown_atx.bin,2995df6f32feb8135fbb1126b52402ba,https://www.dropbox.com/s/csuzjqhhbtw657x/downtown_atx.bin.zip?dl=0 data/input/raw_maps/huge_austin.bin,fb671ea39ea254207c334b5662763433,https://www.dropbox.com/s/91i763larrbmx7z/huge_austin.bin.zip?dl=0 data/input/raw_maps/huge_krakow.bin,978426f967f122675ba530edc94b43e3,https://www.dropbox.com/s/5l9ojm1lfq32otz/huge_krakow.bin.zip?dl=0 -data/input/raw_maps/huge_seattle.bin,e7e49c7421e1343371ab06dc9e3dd64b,https://www.dropbox.com/s/dlajjc94bmxf07p/huge_seattle.bin.zip?dl=0 -data/input/raw_maps/lakeslice.bin,95ed0a848ed098c068ada54db04c9495,https://www.dropbox.com/s/g1y26xudv7rkmko/lakeslice.bin.zip?dl=0 -data/input/raw_maps/montlake.bin,1840e685f91589cf267f146f43878c47,https://www.dropbox.com/s/p5itfq7zioz2b3r/montlake.bin.zip?dl=0 -data/input/raw_maps/south_seattle.bin,bb870ffa8bd159a7ce9dca83216ac0b0,https://www.dropbox.com/s/v405rk7wm7luc0o/south_seattle.bin.zip?dl=0 -data/input/raw_maps/udistrict.bin,ae242a89a2b83b2b9fc115dea0c40256,https://www.dropbox.com/s/vefz3ozb7od9xdf/udistrict.bin.zip?dl=0 +data/input/raw_maps/huge_seattle.bin,95798750903b3a7c35f165aaf3e39c91,https://www.dropbox.com/s/5pumdup46zmhb34/huge_seattle.bin.zip?dl=0 +data/input/raw_maps/lakeslice.bin,28ede305a1e45a4298b8d10503324905,https://www.dropbox.com/s/j707o0ka1k4ixgk/lakeslice.bin.zip?dl=0 +data/input/raw_maps/montlake.bin,712532a30baa5225aca621fd1f1f7f0a,https://www.dropbox.com/s/evwmldb5872nf0u/montlake.bin.zip?dl=0 +data/input/raw_maps/south_seattle.bin,5eb52175e7fd06da3fc3e93fc2ca88fc,https://www.dropbox.com/s/jfc4ht5ydm05j8y/south_seattle.bin.zip?dl=0 +data/input/raw_maps/udistrict.bin,a45f45140c03da4abf8d698cda580133,https://www.dropbox.com/s/plspsqay9c77fwz/udistrict.bin.zip?dl=0 data/input/raw_maps/west_seattle.bin,4784c2eeebd11c6c180d11eda73cf1e1,https://www.dropbox.com/s/6lp4foo4l7g487n/west_seattle.bin.zip?dl=0 -data/input/screenshots/downtown.zip,f1cb474323244ef7a31971e3d5b25a12,https://www.dropbox.com/s/qawd35wz62m2acl/downtown.zip.zip?dl=0 -data/input/screenshots/huge_krakow.zip,cfc8fe595097fed224d369f2c647443c,https://www.dropbox.com/s/dbzon7k5ukndtza/huge_krakow.zip.zip?dl=0 -data/input/screenshots/lakeslice.zip,37ad352dbe42ce4aea0ba0a67067d451,https://www.dropbox.com/s/z0z96lsn7bunqfy/lakeslice.zip.zip?dl=0 -data/input/screenshots/montlake.zip,c8b016f00792ef23b9fae44956d2d86c,https://www.dropbox.com/s/r9vkgwesqklfuq2/montlake.zip.zip?dl=0 -data/input/screenshots/udistrict.zip,584036034b44708da6ae006b2f40fa3b,https://www.dropbox.com/s/ecnt1tyza48y9o2/udistrict.zip.zip?dl=0 +data/input/screenshots/downtown.zip,bdad536ddb7f0e5aab912433b3c28e69,https://www.dropbox.com/s/qawd35wz62m2acl/downtown.zip.zip?dl=0 +data/input/screenshots/huge_krakow.zip,8edcb39fedadb7991ab2959064c98629,https://www.dropbox.com/s/dbzon7k5ukndtza/huge_krakow.zip.zip?dl=0 +data/input/screenshots/lakeslice.zip,73c0ed5add17f8c49218b1c22ee464cc,https://www.dropbox.com/s/z0z96lsn7bunqfy/lakeslice.zip.zip?dl=0 +data/input/screenshots/montlake.zip,fbc945c0869c2b85ccfb1fef4340ba2e,https://www.dropbox.com/s/r9vkgwesqklfuq2/montlake.zip.zip?dl=0 +data/input/screenshots/udistrict.zip,c7a7a258e3d0a417ada55f70c897d4d3,https://www.dropbox.com/s/ecnt1tyza48y9o2/udistrict.zip.zip?dl=0 data/input/seattle/N47W122.hgt,0db4e23e51f7680538b0bbbc72208e07,https://www.dropbox.com/s/mmb4mgutwotijdw/N47W122.hgt.zip?dl=0 data/input/seattle/blockface.bin,add872bab9040ae911366328a230f8b5,https://www.dropbox.com/s/rxd2care60tbe75/blockface.bin.zip?dl=0 data/input/seattle/blockface.kml,350bd9e59bf2af4e885a7c0741e6ee6b,https://www.dropbox.com/s/ukknmpjdvilncq9/blockface.kml.zip?dl=0 @@ -33,30 +33,30 @@ data/input/seattle/osm/south_seattle.osm,2a20b3549004a3d37ef962baf07fbf93,https: data/input/seattle/osm/udistrict.osm,769388ffc0d73c6a149ed79698e2b8f3,https://www.dropbox.com/s/3v6axtyuofqatit/udistrict.osm.zip?dl=0 data/input/seattle/osm/washington-latest.osm.pbf,ac31d2a9d3a109f4467e5f64b5922509,https://www.dropbox.com/s/eilk9soyod3war6/washington-latest.osm.pbf.zip?dl=0 data/input/seattle/osm/west_seattle.osm,b87c760bd512c4302846519ed052ad5a,https://www.dropbox.com/s/qhtkotrtp7ilmy1/west_seattle.osm.zip?dl=0 -data/input/seattle/parcels.bin,1cfdc485a35df9edce7b24e5c17b2977,https://www.dropbox.com/s/s977yzbcds355nv/parcels.bin.zip?dl=0 +data/input/seattle/parcels.bin,fbaa5480e614e7f59a9e958f92a12526,https://www.dropbox.com/s/cy88qmctgya2g76/parcels.bin.zip?dl=0 data/input/seattle/parcels_urbansim.txt,db63d7d606e8702d12f9399e87e6a00f,https://www.dropbox.com/s/6g8rbsf200dssj3/parcels_urbansim.txt.zip?dl=0 data/input/seattle/popdat.bin,ad38e1f20ce71f488112fe81e32798d2,https://www.dropbox.com/s/ajqii1qpfke8063/popdat.bin.zip?dl=0 data/input/seattle/trips_2014.csv,d4a8e733045b28c0385fb81359d6df03,https://www.dropbox.com/s/5ppravwmk6bf20d/trips_2014.csv.zip?dl=0 data/system/cities/seattle.bin,9ed9ff25b3f3941fecf530b3815a1b05,https://www.dropbox.com/s/nwb29tt4koqk6um/seattle.bin.zip?dl=0 -data/system/maps/ballard.bin,b98c9b698d876ebadd34bbcb541630d3,https://www.dropbox.com/s/ocv4p7dzrxcogyq/ballard.bin.zip?dl=0 -data/system/maps/downtown.bin,f951b7ca011b91f062334a94585b7083,https://www.dropbox.com/s/ww7jzqlbpxur8l6/downtown.bin.zip?dl=0 +data/system/maps/ballard.bin,d4ab6ceb6905dfa14b2aaac99c495eff,https://www.dropbox.com/s/too29h0nr6s64wm/ballard.bin.zip?dl=0 +data/system/maps/downtown.bin,6b7735033169acdb4c1429b084632de5,https://www.dropbox.com/s/8s6pjo91spchkfx/downtown.bin.zip?dl=0 data/system/maps/downtown_atx.bin,700a4a78af42e37ef1245e4fc63ce0c4,https://www.dropbox.com/s/ixijn46f07zizzi/downtown_atx.bin.zip?dl=0 data/system/maps/huge_austin.bin,f7ea14d6b616a6ca9a63ac4460b0a2a0,https://www.dropbox.com/s/5jlbbinjp4132rp/huge_austin.bin.zip?dl=0 data/system/maps/huge_krakow.bin,f87b77708170bafa72331cccc6df57b6,https://www.dropbox.com/s/friayproln1gtig/huge_krakow.bin.zip?dl=0 -data/system/maps/huge_seattle.bin,4086044e523651a631f0745e73238bb8,https://www.dropbox.com/s/aj0h8igtskfths3/huge_seattle.bin.zip?dl=0 -data/system/maps/lakeslice.bin,ea5e918b7b70db2c8e484f8666c4737b,https://www.dropbox.com/s/p6r64ujz81vx7vd/lakeslice.bin.zip?dl=0 -data/system/maps/montlake.bin,032f96ae28a2db2d6b4aa848469e9aea,https://www.dropbox.com/s/rjei8n91qil1bhw/montlake.bin.zip?dl=0 -data/system/maps/south_seattle.bin,c967a5d372ef7298a711fc213244ccd8,https://www.dropbox.com/s/nynh73cxqggeuj3/south_seattle.bin.zip?dl=0 -data/system/maps/udistrict.bin,a9a320f182af78d235af98a78aa0646c,https://www.dropbox.com/s/92joyhk29cqoipk/udistrict.bin.zip?dl=0 +data/system/maps/huge_seattle.bin,05214611240c878306f351b76d07db3b,https://www.dropbox.com/s/xakfwfqbcyeznuy/huge_seattle.bin.zip?dl=0 +data/system/maps/lakeslice.bin,ecd6b3b127982eb9b42530da2bb0887b,https://www.dropbox.com/s/dkmf2qaa991uxym/lakeslice.bin.zip?dl=0 +data/system/maps/montlake.bin,cb2246cb15edc0553f5b03af4886471c,https://www.dropbox.com/s/0dnysbp2h7wdaf4/montlake.bin.zip?dl=0 +data/system/maps/south_seattle.bin,174a44c25848f36d82a59644ef56582e,https://www.dropbox.com/s/cu8exdobcdaj6sm/south_seattle.bin.zip?dl=0 +data/system/maps/udistrict.bin,c0cf3f4758ebd9faef843cf587b32a99,https://www.dropbox.com/s/z38hxojx3x8qpvl/udistrict.bin.zip?dl=0 data/system/maps/west_seattle.bin,ccfb1547ffceaa8a635b12252951ce8f,https://www.dropbox.com/s/x9o4lg7fgvy5kr2/west_seattle.bin.zip?dl=0 -data/system/prebaked_results/lakeslice/weekday.bin,3bcb09e1d6ddeed75e3baf3b6b476e5c,https://www.dropbox.com/s/5odd5kdxn0caodk/weekday.bin.zip?dl=0 -data/system/prebaked_results/montlake/car vs bike contention.bin,d3e42a758ba4e10e6143c8f8370a638f,https://www.dropbox.com/s/jefg0ikjy9dsrdd/car%20vs%20bike%20contention.bin.zip?dl=0 -data/system/prebaked_results/montlake/weekday.bin,979af6c1db9f5bf31a3cf12301c4c6f9,https://www.dropbox.com/s/0duohjtnbj5y7gc/weekday.bin.zip?dl=0 -data/system/scenarios/ballard/weekday.bin,f6d7dac5af0f35a6ae5589d3943be789,https://www.dropbox.com/s/klvs4wkauh00xud/weekday.bin.zip?dl=0 -data/system/scenarios/downtown/weekday.bin,2b4506c9aab41a0a12f8d0ef769db492,https://www.dropbox.com/s/swmss3vyrrjnv6n/weekday.bin.zip?dl=0 -data/system/scenarios/huge_seattle/weekday.bin,30cf8491099a33c758aa76c31267bd13,https://www.dropbox.com/s/a20dujaxnwhe2aj/weekday.bin.zip?dl=0 -data/system/scenarios/lakeslice/weekday.bin,19812c16dc90945f5c3659b47c1c55e8,https://www.dropbox.com/s/00cjax1g3bcmwvf/weekday.bin.zip?dl=0 -data/system/scenarios/montlake/weekday.bin,ee5359d37d1d9446578d34c3b97b9266,https://www.dropbox.com/s/bx6sv6rogt2alyq/weekday.bin.zip?dl=0 -data/system/scenarios/south_seattle/weekday.bin,9b84537fb47831d6b65f81dcb947600d,https://www.dropbox.com/s/i36o01uv6ud8rj3/weekday.bin.zip?dl=0 -data/system/scenarios/udistrict/weekday.bin,6c08853e72f8fc65b25b69639cb59464,https://www.dropbox.com/s/3c0tlq9k8niz0pr/weekday.bin.zip?dl=0 -data/system/scenarios/west_seattle/weekday.bin,48a9b04d69948436c8eb89f943e36b5d,https://www.dropbox.com/s/8jq9rs6dom8t0y9/weekday.bin.zip?dl=0 +data/system/prebaked_results/lakeslice/weekday.bin,6cb89d0581ad0d78cd7644a882ab0528,https://www.dropbox.com/s/frg14z9f90qrk8v/weekday.bin.zip?dl=0 +data/system/prebaked_results/montlake/car vs bike contention.bin,bd7bbdee559245ed5dd85ddd67165cf9,https://www.dropbox.com/s/jefg0ikjy9dsrdd/car%20vs%20bike%20contention.bin.zip?dl=0 +data/system/prebaked_results/montlake/weekday.bin,be76cdebe55527b2e19e1c09f9540426,https://www.dropbox.com/s/gzxg2orfu8z9s6x/weekday.bin.zip?dl=0 +data/system/scenarios/ballard/weekday.bin,b1993a9f154cf88c78884eb96ee28a08,https://www.dropbox.com/s/2sf37gu7nur9o37/weekday.bin.zip?dl=0 +data/system/scenarios/downtown/weekday.bin,67c8345c4340cbd0a69d3c079c1c6b6c,https://www.dropbox.com/s/kasxsyett83oo03/weekday.bin.zip?dl=0 +data/system/scenarios/huge_seattle/weekday.bin,102b72c09c63042dc7ca3c143bf7508a,https://www.dropbox.com/s/ebvg1zgv2ywnndf/weekday.bin.zip?dl=0 +data/system/scenarios/lakeslice/weekday.bin,0d86fa1518f5e06942c0b2b8869b9000,https://www.dropbox.com/s/0cuc7urc0fsgqi5/weekday.bin.zip?dl=0 +data/system/scenarios/montlake/weekday.bin,3c19364da831e3f81fed81b6edc1a844,https://www.dropbox.com/s/8ypid1mus95ivni/weekday.bin.zip?dl=0 +data/system/scenarios/south_seattle/weekday.bin,16f5512c24bc140b6d333a44819936d2,https://www.dropbox.com/s/rvyypxoc0awubn1/weekday.bin.zip?dl=0 +data/system/scenarios/udistrict/weekday.bin,25bf5370f905ce57c017028a33612c4e,https://www.dropbox.com/s/msb70poj5dl29q2/weekday.bin.zip?dl=0 +data/system/scenarios/west_seattle/weekday.bin,089ee9b267bafc860f8c6952a69f3efe,https://www.dropbox.com/s/l3lfj8gk232cyrl/weekday.bin.zip?dl=0 diff --git a/docs/dev.md b/docs/dev.md index 9ffac9909b..8133dab64b 100644 --- a/docs/dev.md +++ b/docs/dev.md @@ -46,9 +46,6 @@ One-time setup: experience, so they're hidden for now. - `cargo run --bin game -- --tutorial=12` starts somewhere in the tutorial - Adding `--edits='name of edits'` starts with edits applied to the map. -- All code is automatically formatted using - https://github.com/rust-lang/rustfmt; please run `cargo +nightly fmt` before - sending a PR. (You have to install the nightly toolchain just for fmt) - More random notes [here](/docs/misc_dev_tricks.md) ## Downloading more cities @@ -139,3 +136,24 @@ Common utilities: - `abstutil`: a grab-bag of IO helpers, timing and logging utilities, etc - `geom`: types for GPS and map-space points, lines, angles, polylines, polygons, circles, durations, speeds + +## Code conventions + +All code is automatically formatted using https://github.com/rust-lang/rustfmt; +please run `cargo +nightly fmt` before sending a PR. (You have to install the +nightly toolchain just for fmt) + +The error handling is unfortunately inconsistent. The goal is to gracefully +degrade instead of crashing the game. If a crash does happen, make sure the logs +will have enough context to reproduce and debug. For example, giving up when +some geometry problem happens isn't ideal, but at least make sure to print the +road / agent IDs or whatever will help find the problem. It's fine to crash +during map importing, since the player won't deal with this, and loudly stopping +problems is useful. It's also fine to crash when initially constructing all of +the renderable map objects, because this crash will consistently happen at +startup-time and be noticed by somebody developing before a player gets to it. + +I have lots of thoughts about testing that I haven't written anywhere yet. +You'll surely note the lack of unit tests. If it bothers you, let's talk about +what tests should exist. In the meantime, note lots of validation does happen +via importing maps, running the prebaked scenarios, and screenshot diffing. diff --git a/ezgui/src/managed.rs b/ezgui/src/managed.rs index 1abfb8e2a5..649e215224 100644 --- a/ezgui/src/managed.rs +++ b/ezgui/src/managed.rs @@ -421,7 +421,8 @@ impl Widget { batch.push( color, Polygon::rounded_rectangle(width, height, self.layout.rounded_radius) - .to_outline(Distance::meters(thickness)), + .to_outline(Distance::meters(thickness)) + .unwrap(), ); } self.bg = Some(ctx.upload(batch)); diff --git a/ezgui/src/widgets/dropdown.rs b/ezgui/src/widgets/dropdown.rs index 7649df085d..9c100fc82f 100644 --- a/ezgui/src/widgets/dropdown.rs +++ b/ezgui/src/widgets/dropdown.rs @@ -130,7 +130,10 @@ impl WidgetImpl for Dropdown { let rect = Polygon::rounded_rectangle(width, height, Some(5.0)); let draw_bg = g.upload(GeomBatch::from(vec![ (Color::grey(0.3), rect.clone()), - (Color::WHITE, rect.to_outline(Distance::meters(3.0))), + ( + Color::WHITE, + rect.to_outline(Distance::meters(3.0)).unwrap(), + ), ])); g.fork( Pt2D::new(0.0, 0.0), diff --git a/game/src/common/city_picker.rs b/game/src/common/city_picker.rs index 4d577772c9..4876118788 100644 --- a/game/src/common/city_picker.rs +++ b/game/src/common/city_picker.rs @@ -54,7 +54,7 @@ impl CityPicker { if &name == app.primary.map.get_name() { batch.push(color.alpha(0.5), polygon.clone()); } else { - batch.push(color, polygon.to_outline(Distance::meters(200.0))); + batch.push(color, polygon.to_outline(Distance::meters(200.0)).unwrap()); } regions.push((name, color, polygon.scale(zoom_no_scale_factor))); } diff --git a/game/src/common/minimap.rs b/game/src/common/minimap.rs index 4473027b84..8922a669a1 100644 --- a/game/src/common/minimap.rs +++ b/game/src/common/minimap.rs @@ -300,7 +300,7 @@ impl Minimap { }; g.draw_polygon( Color::BLACK, - &Ring::new(vec![ + &Ring::must_new(vec![ Pt2D::new(x1, y1), Pt2D::new(x2, y1), Pt2D::new(x2, y2), diff --git a/game/src/devtools/kml.rs b/game/src/devtools/kml.rs index 020e172c36..9c7ad84fc9 100644 --- a/game/src/devtools/kml.rs +++ b/game/src/devtools/kml.rs @@ -204,7 +204,7 @@ fn make_object( } else if pts[0] == *pts.last().unwrap() { // TODO Toggle between these better //Polygon::new(&pts) - Ring::new(pts).make_polygons(THICKNESS) + Ring::must_new(pts).make_polygons(THICKNESS) } else { let backup = pts[0]; match PolyLine::new(pts) { diff --git a/game/src/devtools/story.rs b/game/src/devtools/story.rs index 8bdc666c99..622ff52f1c 100644 --- a/game/src/devtools/story.rs +++ b/game/src/devtools/story.rs @@ -464,7 +464,7 @@ impl Marker { } else { let poly = Polygon::new(&pts); batch.push(Color::RED.alpha(0.8), poly.clone()); - if let Some(o) = poly.maybe_to_outline(Distance::meters(1.0)) { + if let Ok(o) = poly.to_outline(Distance::meters(1.0)) { batch.push(Color::RED, o); } // TODO Refactor @@ -591,9 +591,9 @@ fn simplify(mut raw: Vec) -> Ring { downsampled.push(Pt2D::new(pt.x, pt.y)); } downsampled.push(downsampled[0]); - Ring::new(downsampled) + Ring::must_new(downsampled) } else { raw.push(raw[0]); - Ring::new(raw) + Ring::must_new(raw) } } diff --git a/game/src/layer/traffic.rs b/game/src/layer/traffic.rs index 1a6da5389a..ae291ea493 100644 --- a/game/src/layer/traffic.rs +++ b/game/src/layer/traffic.rs @@ -498,13 +498,16 @@ impl TrafficJams { app.primary.sim.delayed_intersections(Duration::minutes(5)), ) { cnt += 1; - unzoomed.push(Color::RED, boundary.to_outline(Distance::meters(5.0))); + unzoomed.push( + Color::RED, + boundary.to_outline(Distance::meters(5.0)).unwrap(), + ); unzoomed.push(Color::RED.alpha(0.5), boundary.clone()); unzoomed.push(Color::WHITE, epicenter.clone()); zoomed.push( Color::RED.alpha(0.4), - boundary.to_outline(Distance::meters(5.0)), + boundary.to_outline(Distance::meters(5.0)).unwrap(), ); zoomed.push(Color::RED.alpha(0.3), boundary); zoomed.push(Color::WHITE.alpha(0.4), epicenter); diff --git a/game/src/render/building.rs b/game/src/render/building.rs index 11b59b4f31..ccb8391c3a 100644 --- a/game/src/render/building.rs +++ b/game/src/render/building.rs @@ -40,7 +40,7 @@ impl DrawBuilding { cs.sidewalk, front_path_line.make_polygons(NORMAL_LANE_THICKNESS), ); - if let Some(p) = bldg.polygon.maybe_to_outline(Distance::meters(0.1)) { + if let Ok(p) = bldg.polygon.to_outline(Distance::meters(0.1)) { outlines_batch.push(cs.building_outline, p); } @@ -102,7 +102,7 @@ impl Renderable for DrawBuilding { fn get_outline(&self, map: &Map) -> Polygon { let b = map.get_b(self.id); - if let Some(p) = b.polygon.maybe_to_outline(OUTLINE_THICKNESS) { + if let Ok(p) = b.polygon.to_outline(OUTLINE_THICKNESS) { p } else { b.polygon.clone() diff --git a/game/src/render/intersection.rs b/game/src/render/intersection.rs index f9542acc4d..e4e9af0a2b 100644 --- a/game/src/render/intersection.rs +++ b/game/src/render/intersection.rs @@ -163,7 +163,9 @@ impl Renderable for DrawIntersection { } fn get_outline(&self, map: &Map) -> Polygon { - map.get_i(self.id).polygon.to_outline(OUTLINE_THICKNESS) + let poly = &map.get_i(self.id).polygon; + poly.to_outline(OUTLINE_THICKNESS) + .unwrap_or_else(|_| poly.clone()) } fn contains_pt(&self, pt: Pt2D, map: &Map) -> bool { diff --git a/game/src/render/parking_lot.rs b/game/src/render/parking_lot.rs index 0aff3f8010..c1fd5db798 100644 --- a/game/src/render/parking_lot.rs +++ b/game/src/render/parking_lot.rs @@ -100,7 +100,7 @@ impl Renderable for DrawParkingLot { fn get_outline(&self, map: &Map) -> Polygon { let pl = map.get_pl(self.id); - if let Some(p) = pl.polygon.maybe_to_outline(OUTLINE_THICKNESS) { + if let Ok(p) = pl.polygon.to_outline(OUTLINE_THICKNESS) { p } else { pl.polygon.clone() diff --git a/game/src/render/traffic_signal.rs b/game/src/render/traffic_signal.rs index 2e91b06378..ab56fa1f82 100644 --- a/game/src/render/traffic_signal.rs +++ b/game/src/render/traffic_signal.rs @@ -368,7 +368,7 @@ pub fn make_signal_diagram( let mut hovered = GeomBatch::new(); hovered.append(normal.clone()); - hovered.push(Color::RED, bbox.to_outline(Distance::meters(5.0))); + hovered.push(Color::RED, bbox.to_outline(Distance::meters(5.0)).unwrap()); Btn::custom(normal, hovered, bbox.clone()).build( ctx, diff --git a/game/src/sandbox/dashboards/summaries.rs b/game/src/sandbox/dashboards/summaries.rs index 87dd42fa12..e40f553a47 100644 --- a/game/src/sandbox/dashboards/summaries.rs +++ b/game/src/sandbox/dashboards/summaries.rs @@ -266,7 +266,7 @@ fn contingency_table(ctx: &mut EventCtx, app: &App, filter: &Filter) -> Widget { if num_savings > 0 { let height = (total_savings / max_y) * max_bar_height; let rect = Polygon::rectangle(bar_width, height).translate(x1, max_bar_height - height); - if let Some(o) = rect.maybe_to_outline(Distance::meters(1.5)) { + if let Ok(o) = rect.to_outline(Distance::meters(1.5)) { outlines.push(o); } batch.push(Color::GREEN, rect.clone()); @@ -287,7 +287,7 @@ fn contingency_table(ctx: &mut EventCtx, app: &App, filter: &Filter) -> Widget { let height = (total_loss / max_y) * max_bar_height; let rect = Polygon::rectangle(bar_width, height).translate(x1, total_height - max_bar_height); - if let Some(o) = rect.maybe_to_outline(Distance::meters(1.5)) { + if let Ok(o) = rect.to_outline(Distance::meters(1.5)) { outlines.push(o); } batch.push(Color::RED, rect.clone()); diff --git a/geom/src/circle.rs b/geom/src/circle.rs index 700439506c..6b4dae5e99 100644 --- a/geom/src/circle.rs +++ b/geom/src/circle.rs @@ -74,7 +74,7 @@ impl Circle { if false { let mut pts = Circle::new(center, radius).to_polygon().points().clone(); pts.push(pts[0]); - return Ok(Ring::new(pts).make_polygons(thickness)); + return Ok(Ring::must_new(pts).make_polygons(thickness)); } // TODO Argh this one also leaves a little piece missing, but it looks less bad. Fine. diff --git a/geom/src/polygon.rs b/geom/src/polygon.rs index dc0a224d8d..7cec516c58 100644 --- a/geom/src/polygon.rs +++ b/geom/src/polygon.rs @@ -3,6 +3,7 @@ use geo::algorithm::area::Area; use geo::algorithm::convexhull::ConvexHull; use geo_booleanop::boolean::BooleanOp; use serde::{Deserialize, Serialize}; +use std::error::Error; use std::fmt; #[derive(Serialize, Deserialize, Clone, Debug)] @@ -286,12 +287,8 @@ impl Polygon { // Only works for polygons that're formed from rings. Those made from PolyLines won't work, for // example. - pub fn to_outline(&self, thickness: Distance) -> Polygon { - Ring::new(self.points.clone()).make_polygons(thickness) - } - - pub fn maybe_to_outline(&self, thickness: Distance) -> Option { - Ring::maybe_new(self.points.clone()).map(|r| r.make_polygons(thickness)) + pub fn to_outline(&self, thickness: Distance) -> Result> { + Ring::new(self.points.clone()).map(|r| r.make_polygons(thickness)) } // Usually m^2, unless the polygon is in screen-space @@ -301,7 +298,7 @@ impl Polygon { // Doesn't handle multiple crossings in and out. pub fn clip_polyline(&self, input: &PolyLine) -> Option> { - let ring = Ring::new(self.points.clone()); + let ring = Ring::must_new(self.points.clone()); let hits = ring.all_intersections(input); if hits.len() == 0 { diff --git a/geom/src/polyline.rs b/geom/src/polyline.rs index d580acb6be..eeb3cb6519 100644 --- a/geom/src/polyline.rs +++ b/geom/src/polyline.rs @@ -93,7 +93,7 @@ impl PolyLine { side1.extend(side2); side1.push(side1[0]); side1.dedup(); - Some(Ring::new(side1).make_polygons(boundary_width)) + Some(Ring::must_new(side1).make_polygons(boundary_width)) } pub fn reversed(&self) -> PolyLine { @@ -556,7 +556,7 @@ impl PolyLine { let angle = slice.last_pt().angle_to(self.last_pt()); vec![ p, - Ring::new(vec![ + Ring::must_new(vec![ self.last_pt(), self.last_pt() .project_away(head_size, angle.rotate_degs(-135.0)), diff --git a/geom/src/ring.rs b/geom/src/ring.rs index 0e4c042010..7dc7530d0e 100644 --- a/geom/src/ring.rs +++ b/geom/src/ring.rs @@ -12,39 +12,16 @@ pub struct Ring { } impl Ring { - pub fn new(pts: Vec) -> Ring { - assert!(pts.len() >= 3); - assert_eq!(pts[0], *pts.last().unwrap()); - - // This checks no lines are too small. Could take the other approach and automatically - // squish down points here and make sure the final result is at least EPSILON_DIST. - // But probably better for the callers to do this -- they have better understanding of what - // needs to be squished down, why, and how. - if pts.windows(2).any(|pair| pair[0] == pair[1]) { - panic!("Ring has ~dupe adjacent pts: {:?}", pts); + pub fn new(pts: Vec) -> Result> { + if pts.len() < 3 { + return Err(format!("Can't make a ring with < 3 points").into()); } - - let result = Ring { pts }; - - let mut seen_pts = HashSet::new(); - for pt in result.pts.iter().skip(1) { - seen_pts.insert(pt.to_hashable()); - } - if seen_pts.len() != result.pts.len() - 1 { - panic!("Ring has repeat points: {}", result); - } - - result - } - - pub fn maybe_new(pts: Vec) -> Option { - assert!(pts.len() >= 3); if pts[0] != *pts.last().unwrap() { - return None; + return Err(format!("Can't make a ring with mismatching first/last points").into()); } if pts.windows(2).any(|pair| pair[0] == pair[1]) { - return None; + return Err(format!("Ring has ~dupe adjacent pts").into()); } let result = Ring { pts }; @@ -54,10 +31,13 @@ impl Ring { seen_pts.insert(pt.to_hashable()); } if seen_pts.len() != result.pts.len() - 1 { - return None; + return Err(format!("Ring has repeat non-adjacent points").into()); } - Some(result) + Ok(result) + } + pub fn must_new(pts: Vec) -> Ring { + Ring::new(pts).unwrap() } pub fn make_polygons(&self, thickness: Distance) -> Polygon { @@ -137,7 +117,7 @@ impl Ring { current.push(pt); if intersections.contains(&pt.to_hashable()) && current.len() > 1 { if current[0] == pt { - rings.push(Ring::new(current.drain(..).collect())); + rings.push(Ring::must_new(current.drain(..).collect())); } else { polylines.push(PolyLine::new(current.drain(..).collect())?); } diff --git a/map_model/src/make/turns.rs b/map_model/src/make/turns.rs index 6e8c95483c..548ffb09e3 100644 --- a/map_model/src/make/turns.rs +++ b/map_model/src/make/turns.rs @@ -549,7 +549,7 @@ fn make_shared_sidewalk_corner( // TODO Something like this will be MUCH simpler and avoid going around the long way sometimes. if false { - return Ring::new(i.polygon.points().clone()).get_shorter_slice_btwn(corner1, corner2); + return Ring::must_new(i.polygon.points().clone()).get_shorter_slice_btwn(corner1, corner2); } // The order of the points here seems backwards, but it's because we scan from corner2