clean up old design notes

This commit is contained in:
Dustin Carlino 2020-05-24 11:07:29 -07:00
parent ba2f8f8208
commit 47e6a6b76a
26 changed files with 24 additions and 3125 deletions

View File

@ -0,0 +1,9 @@
slu
1
-122.34328944028704 47.63052620643473
-122.34331318531133 47.61799922756238
-122.32871912098699 47.61805896949652
-122.32842183536339 47.63032559206659
-122.34328944028704 47.63052620643473
END
END

View File

@ -1,69 +0,0 @@
# Biking-related design notes
## Bike lanes
How do we model bikes merging to a driving lane to make a left?
## General modeling
Bikes are just like cars and can use all the same code, right? Except...
- different speeds
- can use bike or driving lanes
- they dont park or unpark
- actually, how does a ped start/stop using a bike?
- modeling bike lockup locations is overkill. in practice not a
problem. fixed 60s penalty to start/stop biking. penalty happens on
the sidewalk, then they spawn onto the bike/driving lane at the very end
- they can _maybe_ overtake on a bike lane?
- nah, that should be modeled as two bike lanes (or a bike and driving lane) and normal lanechanging behavior
- different rendering
- dont want to use CarID; could maybe rename it VehicleID, but then bike-specific code (like rendering) gets weird
Because of the orchestration with walking/biking models, I'm tempted to try to share some common code, but keep things a bit separate. However, if they're separate...
- driving lookahead needs to see bikes and vice versa
- do the SimQueues need to be state that both models can access?
Do this in a branch for sure. Roughly:
- introduce BikeID, the rendering, stubs for spawning
- lift SimQueues into Sim
- refactor lookahead
- add biking model
After starting this, I'm not sure now. The driving model as-is can handle bikes
fine. The interaction with the walking sim to appear/disappear is pretty
minimal. Alternate idea for another branch:
- keep existing driving code almost entirely as is.
= Vehicle bike type with super slow speed, modify driving lookahead to use the cap speed.
= no BikeID, just a bit in Car for is_bike.
= spawn param to decide if a trip without an owned car will instead bike
= walking state can own the 'parking/unparking' state.
= need a new DrivingGoal, simpler than ParkNear.
= render peds doing bike prep differently
= entirely new render code, but the same DrawCarInput (plus is_bike
bit). that part shouldn't matter, right?
= make sure biking from border works, needs an extra bit i think
- lastly: rename. Car -> Vehicle? Vehicle -> VehicleParams? DrivingSim -> QueuedSim?
- etc
= calculate_paths in spawn needs plumbing. introduce a PathfindingRequest struct, avoid those bools.
- stats; driving.count and trip score
- vehicle enum instead of is_bus, is_bike
- put this in vehicle properties, not on the main
car... then dont need it in Command::DriveFromBorder.
- spawn commands getting to have lots of similarish cases
- remove the sim helpers that do specific stuff... think of
another way to set up tests, similar to tutorial mode?
- verify abtest consistency
- all of the get_blah_from_blah queries in map are a mess
- Position(lane, dist) type would help, yeah?
- a big help: get rid of dimensioned. make Eq work by wrapping
NotNaN or something else, maybe even requiring explicit
tolerance thing? get rid of all the terrible PartialEq hacks.
- animate the bike preparation thing better... visually show time left somehow

View File

@ -1,145 +0,0 @@
# Code organization-related design notes
## Associated data / ECS
So far, the different structures for representing everything associated with a
road/intersection/etc strongly resembles ECS. Would explicitly using an ECS
library help?
http://www.gameprogrammingpatterns.com/component.html
Road has different representations:
- protobuf
- runtime map_model (mostly focusing on the graph)
- UI wrapper + geometry for simulation (should probably tease this apart)
- "control" layer for editable policies
- Queue of cars on the road
It could be useful to bundle together a context-like object of Map, GeomMap,
ControlMap, DrawMap, etc.
Need to figure out how to handle reaping old IDs for transient objects like
cars, but also things like modified roads. Slot maps?
Sort of related -- http://smallcultfollowing.com/babysteps/blog/2018/11/01/after-nll-interprocedural-conflicts/
## Everything as FSMs
Driving and walking layer are both kind of broken, since they know about
parking spots and bus stops. Feels like they need to be dumb, mechanical layers
that're guided by higher-level behaviors, which understand trips and such.
Could be simpler to flatten. Call each sim and dont affect other sims, then
process all events last to do transitions. except maybe one sim expects the
transition to happen immediately, so have to process events between each one?
Just to kind of document the dependency/call-chain right now...
- sim step
- TODO spawner step
- driving step
- router
- transit.get_action_when_stopped_at_end
- this changes bus state (wouldnt it be nicer to
- foreach parked car as a result, add to parking sim and tell spawner that car reached spot
ask: a way to statically or at runtime print the call-chain, stopping at std libs?
- whenever we push onto events, dump stack?
for each api method in each sim, manually look at its call chain
- transit
- create_empty_route, get_route_starts, bus_created
- spawn.seed_bus_route
- sim.seed_bus_route
- get_action_when_stopped_at_end (changes bus state, returns new path), get_dist_to_stop_at
- router.react_before_lookahead
- driving per car react
- driving step
- step (lots of state transitions happen here, but call stack is simple!)
- walking
- ped_joined_bus
- transit.step
A good measue of how well-structured the code is: how hard would it be to add a
few more delays / states, like time after parking the car (out of the way of
the driving sim) but before getting out of the car?
### Notes on capturing this
https://www.reddit.com/r/rust/comments/9d8gse/higher_level_api_than_syn_for_static_analysis/
- figure out how to capture stacktraces kinda optionally
- manual call at a fxn to dump its stacktrace somewhere (to a file? ideally shared global state to dedupe stuff)
- macro to insert a call at the beginning of a fxn
- macro to apply a macro to all fxns in an impl
- then i can manually edit a few places when I want to gather data
https://crates.io/crates/aspect has some of the pieces
## Per-car properties
Need to associate car length between driving and parking sims.
---> could store this in master Sim; after all, there will be some more permanentish stuff like agent/building/trip/owned car soon
- but soon need to bundle things together and pass less params everywhere
- or stash in parking sim, transfer to driving, and back later
Wait, length of car affects parking pretty critically. A bunch of things plumb
around the precomputed front of the spot, used for drawing and for cars to line
up their front in the sim. I think we need to plumb the true start of the spot
and have a method to interpolate and pick the true front.
Now trying the other way -- plumbing back and forth. How do we represent this for parked cars?
- store Vehicle in ParkedCar
- I remember there was some reason this refactor broke last time, but let's try it again
- and store ParkedCar's directly in ParkingLane
## Logging
Libraries will do it too -- that's fine
nice UI features:
- highlight what general area publishes a message
- filter by area
- remember where the log scroller is, even when hidden
- jump to end or beginning quickly
- start at the end
- show new messages in OSD briefly, then vanish
- wrap long lines
log crate is annoying -- cant initialize it, but also have something else hold
onto it. probably have to use lazy static. not even sure I'll use this implicit
style long-term -- when two sims are running side-by-side, might be very
necessary to plumb more log context anyway.
Wound up using Warn<T> and Timer. Discuss how annoying it is to monadically
plumb warnings and context.
## Code layers
At some point, geometry was a separate layer from the graph base-layer of
map_model. That doesn't work -- we can't even reason about what turns logically
exist without operating on cleaned-up geometry.
Control used to be separate from map_model for similar "purity" reasons.
map_model was supposed to just be unbiased representation of the world, no
semantics on top. Except bus stops and routes crept it, and map edits lived
there. Separate control layer is just awkward.
Sim layering is bit tricky...
- mechanics/ layer handles implementing individual trip legs
- trips should be invoked by mechanics/ and spawning to handle transitions and cause effects in mechanics layer
- scheduler will ultimately become the generic DES driver probably. right now, it spawns peds and cars and handles retrying.
- spawn layer will feed commands to scheduler at the right time. only purpose is to avoid doing all the pathfinding upfront -- maybe we have things spawning from borders over a very long period of time.
- something above this layer takes some constraints to spawn things, uses RNG to populate unfilled things, and then calls spawn layer.
- this is called by interactive UI and tests and scenario (which is a more succinct description of lots of these commands)
Actually, get rid of the deferred nature of the spawn layer for now. pass it (almost) fully-specified stuff to do and let it parallelize. Trip layer can't precompute paths because start is unknown. Could be hard to batch there.
## IDs
Should LaneID have LaneType bundled in for convenience? CarID and VehicleType?

View File

@ -1,62 +0,0 @@
# Demand data
## Depicting
Axes:
- time
- source/destination neighborhood or border
- mode choice
- number of trips
Fix a time, then show a bunch of directed arrows between neighborhood polygons.
Line thickness relative to number of trips. Color coding... if you're
interested in a particular neighborhood, vary all the line colors based on
src/dst.
## Sources
- https://en.wikipedia.org/wiki/Transportation_forecasting
- https://data.seattle.gov/Transportation/2016-Traffic-Flow-Counts/f22b-ywut
- counts per aterial
- high priority
https://gis-kingcounty.opendata.arcgis.com/datasets/acs-means-of-transportation-to-work-acs-b08301-transportation
https://gis-kingcounty.opendata.arcgis.com/datasets/acs-travel-time-to-work-acs-b08303-traveltime
https://gis-kingcounty.opendata.arcgis.com/datasets/consolidated-demographics-index-for-king-county-census-tracts-demographic-index-area
https://gis-kingcounty.opendata.arcgis.com/datasets/acs-household-size-by-vehicles-available-acs-b08201-householdvehicles
https://lehd.ces.census.gov/
https://movement.uber.com/explore/seattle/
- indications of population
https://gis-kingcounty.opendata.arcgis.com/datasets/acs-total-population-acs-b01003-totalpop
https://gis-kingcounty.opendata.arcgis.com/datasets/acs-units-in-structure-acs-b25024-unitsinstructure
https://gis-kingcounty.opendata.arcgis.com/datasets/acs-household-type-including-living-alone-acs-b11001-householdtype
https://gis-kingcounty.opendata.arcgis.com/datasets/acs-housing-units-acs-b25001-housingunits
https://gis-kingcounty.opendata.arcgis.com/datasets/acs-occupancy-status-acs-b25002-occupancystatus
https://gis-kingcounty.opendata.arcgis.com/datasets/acs-household-size-by-number-of-workers-in-household-acs-b08202-householdworkers
- goldmine, but seemingly very hard to interpret
https://www.psrc.org/trip-based-travel-model-4k
https://www.psrc.org/activity-based-travel-model-soundcast
- population
- http://seattlecitygis.maps.arcgis.com/apps/MapSeries/index.html?appid=3eb44a4fdf9a4fff9e1c105cd5e7fe27
- https://data.seattle.gov/Permitting/Rental-Property-Registration-Map/5a7u-vxx7
- https://www.seattle.gov/transportation/document-library/reports-and-studies
- https://commuteseattle.com/wp-content/uploads/2017/02/2016-Mode-Split-Report-FINAL.pdf
- https://www.soundtransit.org/get-to-know-us/documents-reports/service-planning-ridership
- https://gis-kingcounty.opendata.arcgis.com/datasets/parcels-for-king-county-with-address-with-property-information--parcel-address-area
- PREUSE_DESC reveals landuse
## Great info
- https://www.psrc.org/sites/default/files/shrp2_c46.pdf
## PSRC info
- trips: https://github.com/psrc/soundcast/wiki/Outputs#trip-file-_triptsv
- parcels: https://github.com/psrc/soundcast/wiki/Inputs#primary-parcel-file-parcels_urbansimtxt
- input is at https://github.com/psrc/soundcast/releases
- https://www.kingcounty.gov/services/gis/GISData/DataStandards.aspx

View File

@ -1,199 +0,0 @@
# Discrete simulations
## Why are timesteps bad?
Discrete-time sim has two main problems:
Discrete-event sim is the real dream. I know when a ped will reach the end of a
sidewalk and can cheaply interpolate between for drawing. If I could do the
same for all agents and states/actions, I could switch to discrete-event sim
and SUPER speed up all of the everything.
This possibly ties into the FSM revamp idea perfectly. Every state/action has
two durations associated with it:
- the best case time, calculated while pathfinding, which assumes no
conflicts/delays due to scarcity
- the actual time something will take, based on state not known till execution
time
## Intent-based kinematics
Instead of picking acceleration in order to achieve some goal, just state the
goals and magically set the distance/speed based on that. As long as it's
physically possible, why go through extra work?
Instead of returning accel, what if we return (dist to travel this tick, new speed).
- How can we sanity check that those choices are reasonable? Seems like we
still have to sorta reason about acceleration properties of vehicles.
- But maybe not. each constraint...
- dont hit lead... if we're ALREADY close to them (current speed * timestep puts us within follow_dist of them right now), just warp follow_dist away from where they are now.
- if we need to stop for a point that's close, then we ideally want to do the kinda (do this distance, over this duration) thing and somehow not reason about what to do the next few ticks. event based approach for one thing. :\
Or different primitives, much closer to event sim...
- Often return (dist to wind up at, duration needed to get there)
- linear interpolate position for rendering in between there
- use for freeflow travel across most of a lane
- to slow down and stop for a fixed point, do a new interval
(STOPPING_DISTANCE_CONSTANT, few seconds to stop). the vehicle will
kinda linearly slow down into the stop. as long as the stopping
distance and time is reasonable based on the vehicle and the speed
limit (freeflow vs shuffling forwards case?), then this'll look fine.
- What about following vehicles?
- it'd be cool to have some freeflow movement to catch up to a lead vehicle, but if we look at the lead's position when we first analyze, we'll cover some ground, then replan again and do zenos paradox, planning freeflow for less new dist...
- parked cars departing will have to interrupt existing plans!
- rendering becomes expensive to do piecemeal; need to process cars in order on a queue to figure out positions. if we need to lookup a single one, might need to do a bunch of hops to figure out the front.
- not really easy to percolate down a single time to pass the intersection from the lead vehicle... intersections need to stay pretty detailedish.
Lookahead constraints:
- go the speed limit
- dont hit lead vehicle (dist is limited by their dist + length + following dist)
- stop by a certain point
- future: do lane-changing stuff
Blah. Initial simplicity of this idea lost. Maybe try a single, simple hack
out: replace accel_to_follow with something to warp to the right spot behind an
agent and set the speed equal to min(follower's max speed, lead vehicle's
current speed).
- do we have a bug where vehicles can be too close when they're on adjacent but different traversables?
- if we just warp to follow_dist away from a vehicle when we get too close, then we instantly decelerate.
## The software engineering question
Is there a reasonable way to maintain both models and use one headless/editor
for them? Seemingly, the entire Sim API needs to just be abstracted. Like the
DrawAgents trait, but a bit more expanded. Except stuff like time travel
applied to discrete-time sim can't support debugging agents or showing their
path. I think time travel with discrete-event would work fine -- just storing
all the previous states with the exact times.
## What broke about the old simplified driving model?
- straw model has some quirks with queueing
- after the lead vehicle starts the turn, the queue behind it magically warps to the front of the road
- following distance needs to apply across lanes/turns -- lookahead has to still happen
- the first vehicle in the turn jumps to a strange position based on the front/back rendering
- dont remember what this was
- at signals, cars doing the same turn wont start it until the last car finishes it
- dont remember what this was
## New thoughts on this
- picture each lane as the end of the slow queue and the end of the moving
queue. spillback can happen. forget individual agents, just intervals
containing cars that expand/contract.
- dont need to calculate time to change speeds for arbitrary things. just time
to go from 0->speed, speed->0. not anything else. in stop-and-start mode, do
something else -- the bound is on the intersection, not on the car to
accel/deaccel.
- when we apply the accel, check the intent -- if we overshoot, detect it
early, maybe force slight correction. meaure how much correction we need.
- interesting... the most constraining intent flips between FollowCar and StopAt.
- the car winds up at the right dist, but with tiny speed! just pretend they managed zero
## Time-space intervals
An interval is a car traveling along a lane from dist1 to dist2, time1 to
time2, speed1 to speed2. Within these intervals, those values (distance and
speed) are just linearly interpolated. Or it could be more detailed, vf = v0 +
at, and xf = x0 + v0t + 0.5at^2.
These intervals get created a few different ways...
- accelerate from rest to min(speed limit, vehicle's max speed)... need to
calculate distance traveled during that time and time it takes
- opposite thing, deaccelerate
- freeflow travel... speed stays the same. can construct given desired time or desired distance.
Say a single car wants to just cover a lane and stop at the end. First accel as
much as possible. If distance is too much, then... there are actually a whole
space of options. Optimization problem. Whatever, pick some bad solution,
improve it later.
### Following
Do two cars conflict on a lane? If they have timespace intervals that overlap
(distance and time), then maybe. But they might not -- maybe a slow car barely
catches up to a faster car. We have two x(t) parametric equations -- set them
equal, find the time at which the collision occurs, see if that time is
actually in both time ranges. We actually want to check if x1(t) + length +
follow dist = x2(t), of course.
If there is a collision, make a new time-space interval for the follower. It
shouldn't need to reference the leader at all after making the interval!
Specifically...
leader: (x1, t1) (x2, t2)
follower: (x3, t3, x4, t4)
"collision" (with following distance added) at t5 and x5, which are inside the
appropriate intervals. the follower's intervals should now be...
(x3, t3 x5, t5) and (x5, t5, ????)
Position should be the same as leader, but + the length and follow dist.
Correspondingly, the time to reach the same endpoint as the leader should be
offset by some amount...
- We sort of hand-wave assume that the follower can decelerate to match the
speed of the leader.
### Stop-and-go conditions
What if we measured distance over time of this using the current sim? And then
found a low speed that produced the same distance in the same time?
### Gluing together traversables
When we "lookahead", now we just have to call maybe_follow against a single
vehicle. Need an easy way to indicate relative distance behind them.
## Event-based model, redux
Key idea to this one: stop insisting on knowing where individual cars are at all times.
A car's state machine:
- they enter a lane
- they cross it during some time... length / min(speed limit, their max speed)
- now they're in the queue!
- if they're not first, then WAIT.
- now they're at the front of the queue
- if the target lane is at capacity (queue + freeflowers == rest capacity), then WAIT.
- could detect gridlock by looking for dependency cycles here
- ask the intersection when they can go. tell it how long crossing will take (based on freeflow times again)
- intersections know when conflicting turns will finish
- stop sign will just give a little delay to some directions
- traffic signal bit more complex (ohhh but no more overtime!!!)
- probably want to add slight delay to avoid a packet of cars surging forward on a green light change
- cross the turn
- repeat!
How could lane-changing work?
- equivalent to existing: queue per lane, but no lanechanging
- simple: capacity based on all lanes, but one queue for a road. any turn.
- bike/bus lanes don't help anything
- one (usually turn) lane backfilling affects all
- later: queue per lane. turn becomes lane->road
- the LCing itself doesnt really happen, merging has to happen upstream
- the only place this is really weird is when there are multiple turn lanes from src to dst
- or when two incoming roads could turn into different lanes of a road
- actual LCing during freeflow/queueing time could make sense
Now the interesting part... drawing!
- unzoomed
- draw red queue slice at the end of a road, and a blue freeflow slice at the start
- show some kind of movement in the blue?
- while turns are being executed, just draw blue slice of the entire turn?
- since they're individual, could animate by interpolating or something
- zoomed
- hmm. can probably figure out exact car positions somehow, but not important.
This model neglects:
- speed, acceleration at some particular time
- but delays to doing turns after queueing could include time to accelerate

View File

@ -1,86 +0,0 @@
# Driving-related design notes
## Strawman driving model
- Show the FSM
- Explain how the model is based on best-case bounds
- Position is derived lazily from time
- How accurate could it be? Based on inner-city speeds and timesteps
- problems
- straw model has some quirks with queueing
- after the lead vehicle starts the turn, the queue behind it magically warps to the front of the road
- the first vehicle in the turn jumps to a strange position based on the front/back rendering
- at signals, cars doing the same turn wont start it until the last car finishes it
## AORTA driving model ##
- figure out how to dynamically sub out two different driving models. traits or generics on Sim.
- repeat determinism tests for both!
- start sharing code between the two models (and the ped model)
the polymorphism mess:
- a sim can contain one of two different driving models
- driving models both implement the same interface (a trait)
- need to serialize/deserialize them, but cant get deserialization with erased_serde working
- so use enums and just delegate everything. macro?
- tried deref on an enum, returning a trait
- delegate crate can't handle match expressions
## The lookahead exceeding-speed-limit bug
It happens because a car was previously throttling itself due to somebody in
the way, but as soon as they start a turn, the car eagerly jumps ahead.
ah no, it's because we use max_lookahead_dist in accel_to_follow, and the speed limit we assert is the old road's speed.
## Routers, lookahead, cars as FSMs
Hmm, hard to figure out the interactions for the router. when should it get a
chance to roam around for parking? should lookahead make copies of it (harder
to then roam...) or should it index into one of them (preferred...)
rethink cars as a much more careful FSM. on a lane, on a turn, on the LAST lane
lining up for something, parking, unparking, idling at a bus stop. these states
imply things like position (which queue to occupy space in). both lookahead and
physically stepping forwards ought to be able to use the same code -- the
routing, turn<->lane transitions, leftover dist calculation, etc shouldnt care
if its real or projected
Try this again, but with a much more careful interface for the router. But when do we get to mutate?
- during react() when we up-front see the path is empty and theres no parking
- problem: what if lookahead earlier spots this case and doesnt know where to go?
- soln: who cares. when that happens, just recommend stopping at the
end of the lane. when we physically get there, this case will trigger
and we can mutate.
- during react() lookahead when we first try to go beyond the end
- problem: but then it's hard to actually do the lookahead; we cant
clone the router and transition it along during lookahead. we would
have to go update the original one somehow.
- in react() lookahead, return an indicator and then have step() call another mutable method later.
- this is the confusing kind of split I'm trying to avoid.
So I think the first solution works best.
Urgh, we have a mutability mess again -- react() needs a mutable router, but
immutable view into world state to query sim queues and find next car in front
of. Similar to needing to know speed and leader vehicles for intersection sim.
For now, should we do the same hack of copying things around?
- kinda awkward that routers and cars are separate; a router belongs to
a car. maintaining the mappings in the same spots is gross. need to
express in react() that only one part of a car -- the router/plan -- is
mutable.
- it's also horrible that DrivingSimState is passed around to SimQueue to get car positions!
- an alt is to duplicate state into the simqueue and store the dist along. this would be pretty easy actually...
- and passing around properties / all the vehicles is awkward
- maybe it's time to cave and try an ECS?
- just start by listing out what needs to happen.
- car react needs mutable router, immutable queues of lanes and turns, immutable dist/speed of other cars
- intersections need to know car speed, whether cars are leaders (so simqueue positions)
Short term solution: copy a huge nasty thing and pass it into react(). :\
- but I'm still not convinced any mutabulity in react() is good

View File

@ -1,124 +0,0 @@
# Geometry-related design notes
## Intersection geometry brainstorm
- can we merge adjacent polylines at intersections based on closest angle, and then use the existing stuff to get nice geometry?
- i think we still have to trim back correctly
- first figure out all the trimming cases for the T, outside and inside lanes, etc
- 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
- https://www.politesi.polimi.it/bitstream/10589/112826/4/2015_10_TOPTAS.pdf pg38
- just make polygons around center lines, then intersect?
morning thoughts!
- trim lines based on outermost POLYGON border line, not lane center lines or anything
- the ascending angle and skipping existing lines in the thesis seems to make sense
- find where infinite line intersects line segment for some cases?
### Fresh look
Problems right now:
- i28: on a simple 4-way, sidewalk polygons both overlap and leave a little bit of gap space
- fig1: could trim back, but would want to later add back the corners for sidewalks.
- fig2: embrace overlap of sidewalks at corners
- dont worry about agents on sidewalks overlapping
- just draw entire block's curb, just for rendering, not logical center sidewalk lines?
- TODO check sources to see if any have this
- i52: on a 4-way at a slant, road endings are very jagged
- thoughts on trim lines
- need to run it in playground step-by-step
- fig 3: almost want to think about matching up equivalent lanes on adjacent half-roads
- trim adjacent sidewalks, then adjacent parking lanes, ...
- but maybe this looks very weird, and sidewalks are the exception
afternoon idea:
- start with entire roads and the giant thick polygons
- just find the intersection of all those big polygons
- almost like a loop:
- trim back yellow center lines, compute giant road band, see if there's intersection
- then figure out the lane geometry WITHIN the constraint of the big road band
- probably doesnt have to be a fixed point loop. what if we just trim lines based on the outermost lines of all the roads? and then based on how much length we removed there, reduce the original yellow center lines. at some angles this is exactly the same. at others, it's probably different -- is there a specific relation we can figure out?
## Basic geometric types
Not aiming to get it right forever, just improving the mess now.
- Pt2D
- just a pair of f64's, representing world space (non-negative)
- no more ordered_float; have a variant only when needed
- Angle
- normalized, with easy reversing/perpendicularing
- Line
- pair of points
- Polyline
- Polygon
conversions to Vec2d ONLY for graphics; maybe even scope those conversions to render/
Eventually, may want https://www.redblobgames.com/articles/curved-paths/
## Polylines
The polyline problem:
- https://www.codeproject.com/Articles/226569/Drawing-polylines-by-tessellation
- https://stackoverflow.com/questions/36475254/polylines-outline-construction-drawing-thick-polylines
- Will lengths change? Is this a problem?
- Drawing cars as rectangles is funky, because if their front is aligned to a new line segment, their back juts into the center of the road
- https://hal.inria.fr/hal-00907326/document
- https://www.researchgate.net/publication/220200701_High-Quality_Cartographic_Roads_on_High-Resolution_DEMs
https://wiki.openstreetmap.org/wiki/Proposed_features/Street_area
https://www.codeproject.com/Articles/226569/Drawing-polylines-by-tessellation
https://github.com/Toblerity/Shapely looks amazing! parallel_offset method calls single sided buffer in geos
https://github.com/georust/geos bindings
https://gis.stackexchange.com/questions/1197/creating-one-sided-buffers-or-parallel-lines-in-postgis
https://trac.osgeo.org/geos/ticket/215#no1
https://geos.osgeo.org/doxygen/classgeos_1_1operation_1_1buffer_1_1BufferOp.html
https://github.com/DougLau/footile
https://www.reddit.com/r/gamedev/comments/2yfyi5/drawing_lines_is_hard/
https://blog.mapbox.com/drawing-antialiased-lines-with-opengl-8766f34192dc
http://artgrammer.blogspot.com/2011/07/drawing-polylines-by-tessellation.html
https://www.reddit.com/r/GraphicsProgramming/
- are all the sources of doubling back strangely just directly fixable in the geom?
- keep miter joins, but limit how far out we can explode?
- bevel joins
- outlines miss bevels on one edge
- now there are little gaps between adjacent lanes
- slow!
- some bugs that cover a thick triangular area
- refactor all the methods... confusing.
- ideas
- https://stackoverflow.com/questions/5641769/how-to-draw-an-outline-around-any-line
- http://old.cescg.org/CESCG99/SKrivograd/
- https://www.codeproject.com/Articles/226569/Drawing-polylines-by-tessellation
- https://stackoverflow.com/questions/36475254/polylines-outline-construction-drawing-thick-polylines
- https://forum.libcinder.org/topic/smooth-thick-lines-using-geometry-shader
- https://stackoverflow.com/questions/687173/how-do-i-render-thick-2d-lines-as-polygons

View File

@ -1,729 +0,0 @@
# GUI-related design notes
## Moving away from piston
- options
- gfx (pre-II or not?) + winit
- https://suhr.github.io/gsgt/
- glium (unmaintained)
- ggez: https://github.com/nical/lyon/blob/master/examples/gfx_basic/src/main.rs
- too much other stuff
https://www.reddit.com/r/rust_gamedev/comments/7f7w60/auditioning_a_replacement_for_bearlibterminal/
https://github.com/ggez/ggez/blob/master/examples/drawing.rs
things to follow:
- https://suhr.github.io/gsgt/
- https://wiki.alopex.li/LearningGfx
- https://github.com/nical/lyon/blob/master/examples/gfx_basic/src/main.rs
- porting to wasm: https://aimlesslygoingforward.com/blog/2017/12/25/dose-response-ported-to-webassembly/
## Refactoring UI
I want to make bus stops be a selectable layer, and I want to add lake/park
areas soon. What did
https://github.com/dabreegster/abstreet/commit/b55e0ae263fcfe4621765a7d4a8d208ab5b89e76#diff-8257d0ba4a304de185c0125ff99e353b
have to add?
- color scheme entry
- an ID (displayable)
= boilerplate in selection plugin
= a way to ask for tooltip lines
- boilerplate in the hider plugin
- DrawExtraShape
- draw, contains_pt, get_bbox, tooltip_lines
- DrawMap
- list of stuff, with ID lookup
- a quadtree and way to get onscreen stuff
- UI
- a toggleablelayer for it
= and clearing selection state maybe
- are we mouseover it? (right order)
- draw it (right order)
- pick the color for it
try a quadtree with any type of object.
alright, time to move color logic. let's see what it takes for each Renderable to decide its own color -- what state do they need?
- dont forget: search active and lane not a match does yield a color.
- woops, sim Draw{Car,Ped} doesnt implement Renderable. but it'll be nested anyway... we dont want to move stuff in the quadtree constantly. the 'get everything onscreen' routine should do that, interpreting Lanes and Intersections in initial pass of results correctly by querying for more stuff.
- actually, still undecided about color and RenderOptions...
- the whole motivation -- one draw() interface can only return a single color. and dont want to add code to UI for every single object type.
- maybe send in Option<Box<Plugin>> of the current thing, and then it can have a more specific method for each object type? or is that too far the other direction again?
- send in Option<Color>, letting each plugin vote?
- maybe if we only have one or two active plugins, it'll be easier to understand?
- would it be weird to invert and send in all the plugins? the point is
kinda for there to be one place to handle interactions between
plugins -- UI. having a strong concept of one active at a time would probably
_really_ help.
- maybe plugins do have a single color_obj(enum of IDs) -> Option<Color>?
- make it easier to fill out RenderOptions for things generically
- next step: extra things like draw_front_path also take cs, not a specific color -- if it makes sense.
- refactor selection plugin's color_something
- selection plugin currently has this weird case where it can cycle through turns at an intersection. MOVE THAT out.
- more generally, move out all custom logic. make other things know if something is selected and do special stuff if so.
- and make it act generic at last! \o/
OK, so where was I?
- colors are still currently missing for things that need two of them.
** having one active plugin at a time simplifies the color problem and solves a few others, so try that now.
- another refactor to do -- initiate plugins based on current_selection_state in UI or the plugin, but stop mixing so much
- make car and ped also Renderable, for great consistency!
- work on generic quadtree idea
### One active plugin at a time
I wonder if the UI will have a need to reach into plugins beyond event(). Let's find out!
- exceptions
- hider needs to given to finding onscreen stuff
- search can specify colors and OSD lines
- warp can add OSD lines
- show_route can color
- floodfiller can color
- steepness can color
- osm can color
- signal and stop sign editor can color and indicate what icons are onscreen
- road editor can be asked for state to serialize
- sim ctrl can contribute OSD lines (and everything grabs sim from it directly too -- maybe sim should live in UI directly)
- color picker can draw
- turn cycler can draw
- the stuff they take in event() is different. hmm.
- box lil closures
so it feels like we implicitly have a big enum of active plugin, with each of their states kinda hidden inside.
- the simple idea
- UI keeps having a bunch of separate plugins with their real type
- have a list of closures that take UI and do event(). return true if that plugin is active
NOT if something was done with the input
- in event(), go through the list and stop when something becomes
active. remember it's active and just call it directly next time in
event(), until it says its no longer active.
- then figure out the implications for color
tomorrow:
= implement exclusive active plugin thing
= rethink if existing plugins are active or not. maybe dont make event() return if active or not, maybe have a separate method? or just in event, do stuff first, and then have this query at the end.
= tooltips shouldnt be shown while a random plugin is active; move that out to its own plugin! same for debugging. selection plugin should have NO logic.
= refactor the toggleablelayer stuff, then move them to list of plugins too
= clean up selection state... should warp and hider be able to modify it, or just rerun mouseover_something?
= initiate plugins in the plugin's event; stop doing stuff directly in UI
= basically, make UI.event() just the active plugin list thing as much as possible.
- deal with overlapping keys that still kinda happen (sim ctrl, escape game)
- bug: do need to recalculate current_selection whenever anything potentially changes camera, like follow
= make draw car/ped implement renderable.
= move Draw* to editor crate, and have an intermediate thing exposed from sim and do the translation in get_blah_onscreen.
- then rethink colors, with simplified single plugin
= then finally try out a unified quadtree!
= make parcels selectable as well?
- and see how much boilerplate a new type would need, by adding bus stops and water/parks
Alright, replan yet again.
= then rethink colors, with simplified single plugin
= plugin trait, color(id) -> Option<Color>. parallel list of box plugins (or, a fxn that takes the idx)
= refactor to one color_blah method
= handle the two color things... just buildings?
= and see how much boilerplate a new type would need, by adding bus stops and water/parks
- load water/parks and stuff
- deal with overlapping keys that still kinda happen (sim ctrl, escape game)
- and missing keys, like no tooltip for turns, since they're only shown in editors now
- bug: do need to recalculate current_selection whenever anything potentially changes camera, like follow
- consider merging control map into map
- see how hard it is to render textures onto cars or something
= refactor debug and tooltip lines for objects
## Immediate mode GUI
Things organically wound up implementing this pattern. ui.rs is meant to just
be the glue between all the plugins and things, but color logic particularly is
leaking badly into there right now.
## UI plugins
- Things like steepness visualizer used to just be UI-less logic, making it
easy to understand and test. Maybe the sim_ctrl pattern is nicer? A light
adapter to control the thing from the UI? ezgui's textbox and menu are similar
-- no rendering, some input handling.
## GUI refactoring thoughts
- GfxCtx members should be private. make methods for drawing rectangles and such
- should be useful short term. dunno how this will look later with gfx-rs, but dedupes code in the meantime.
- should GfxCtx own Canvas or vice versa?
- Canvas has persistent state, GfxCtx is ephemeral every draw cycle
- dont want to draw outside of render, but may want to readjust camera
- compromise is maybe storing the last known window size in canvas, so we dont have to keep plumbing it between frames anyway.
One UI plugin at a time:
- What can plugins do?
- (rarely) contribute OSD lines (in some order)
- (rarely) do custom drawing (in some order)
- event handling
- mutate themselves or consume+return?
- indicate if the plugin was active and did stuff?
- just quit after handling each plugin? and do panning / some selection stuff earlier
- alright, atfer the current cleanup with short-circuiting... express as a more abstract monadish thing? or since there are side effects sometimes and inconsistent arguments and such, maybe not?
- consistently mutate a plugin or return a copy
- the Optionals might be annoying.
## OpenGL camera transforms
- https://crates.io/crates/cgmath
- https://www.nalgebra.org/cg_recipes/
- https://docs.rs/euclid/0.19.0/euclid/struct.TypedTransform2D.html
- https://www.reddit.com/r/rust/comments/6sukcw/is_it_possible_to_to_create_an_ortho_view_in_glium/ has a direct example of affine transformation
- https://www.reddit.com/r/gamedev/comments/4mn9ly/3d_matrix_transformation_question_rotating/
- https://docs.rs/cgmath/0.14.1/cgmath/trait.Transform.html#tymethod.look_at is ready-made
## Wizard
API looks like coroutines/continuations, but it just works by replaying
previous answers. The caller could even build a branching workflow -- as long
as the calls to the wizard are deterministic.
Menus are super awkward -- drawing extra effects, mainly.
cursive crate is good inspiration for the API
## Menus
Dynamically populating the TreeMenu every frame while possible input keys are collected has problems.
- How do we remember the permanent state between frames?
- What if the possible actions change between frames, screwing up that state anyway?
- stop handing events to the game entirely?
Rethink assumptions. Is it nice to dynamically populate the menu in a bunch of different places?
- Won't have any control whatsoever for order of entries, and I'll definitely want that.
- Hard to understand all the things that could happen; it'd be nice to see them in one place
- Lots of plugins have boilerplate code for state management. Even if they keep
it, might be cool to at least peel out the code to activate the plugin.
- all plugins become Optional; dont have to represent the nil state
- or more extreme: enum of active plugin
- Similarly, might be nice to have kind of a static list of context-sensitive, right click menu actions for each type of object?
current TreeMenu:
- Debug ()
- Show extra ()
- hide Intersection(IntersectionID(59)) (H)
- start searching (/)
- to show OSM classifications (6)
- unhide everything (K)
- visualize steepness (5)
- Show layers ()
- toggle buildings (1)
- toggle debug mode (G)
- toggle extra KML shapes (7)
- toggle intersections (2)
- toggle lanes (3)
- toggle turn icons (9)
- Validate map geometry (I)
- start searching for something to warp to (J)
- Edit map ()
- start drawing a polygon (N)
- Settings ()
- configure colors (8)
- Sim ()
- Seed the map with agents (S)
- Setup ()
- spawn some agents for a scenario (W)
- load sim state (P)
- run one step (M)
- run sim (Space)
- save sim state (O)
- slow down sim ([)
- speed up sim (])
- quit (Escape)
Back up and think about ideal for these background controls...
## Managing different map edits
Should be simple -- I want to bundle together map edits as named things, to
prep for A/B tests. But loading a different set of edits could be kind of
tough...
- new control map state has to propagate to intersection editors.
- easy fix: pass them mut ref from control map every tick. then just have to reload control map.
- road edits have to propogate
- theres a way to do that live right now, but it's kind of brittle and funky. probably safer to load from scratch.
- but then have to reload things like steepness visualizer plugin... actually, just that, seemingly.
- er, but also the hider plugin -- it holds onto laneIDs, which may change!
Alright, I think this is the sequence of things to do:
1) make a few plugins less stateful anyway, by taking refs to map/control map stuff instead of caching stuff. thats useful regardless.
- but wait, then road editor kind of cant work, because mut borrow edits from map while holding immutable lane/road refs. theyre really indep things, so cant store together.
2) make it possible to completely reload UI and everything from scratch, from a plugin. rationale: it'd be nice to switch maps from inside the editor anyway. not necessary, but useful.
3) make road edits propogate correctly, and somehow have a strategy for ensuring nothing is forgotten. impl today is VERY incomplete.
Thinking about this again now that we need two copies of everything to be alive at a time and switch between them...
- very tied together: map, control map, draw map, sim
- current selection is UI state that has to refresh when changing maps
- which plugins have state tied to the map?
- have a method on UI to switch map+edits? no, dont want to reload all this stuff every time...
- bundle that state together, including the plugins!
- make the existing 'load new edits' thing use this new mechanism
- then go back to managing a second sim...
## Rendering a map differently
For "Project Halloween", I want to draw the map model in a very different
visual style. Stuff like intersections are ignored, rendering roads instead of
lanes, and making animated buildings. Let's just start, and see what kind of
common code makes sense to keep.
OK, so far nothing extra to share -- the existing abstractions work well. But
now I think we need a quadtree just to avoid rendering too much stuff. The
Renderable trait from editor is a bit too much -- selection and tooltips, nah.
(Or not yet?) And then since the buildings move, what do we do about the
quadtree? Constantly updating it is silly -- it'd be best to capture the full
extent of the bounding box, the worst case. Actually wait, it's easy -- the
original bbox is fine! The bldg only shrinks closer to the sidewalk when
animating.
So, try adding the quadtree for roads and buildings (diff quadtrees or one
unified? hmm) and see what looks common. Remember we could use quadtrees in map
model construction for building/sidewalk pruning, but there's the awkwardness
of quadtrees _kind of_ being a UI concept.
## Side-by-side
What should this feature do? Is it easier to watch two maps side-by-side moving
in lockstep, with the same camera? Or would a ghostly trace overlay on one map
be easier to understand? The use cases:
- Glancing at a vague overview of how the two runs are doing. Watching graphs
side-by-side might even be more useful here. But for a zoomed out view,
side-by-side with reasonably clear pockets of color (weather model style,
almost) seems nice.
- Detailed inspection of a fixed area. Side-by-side view with full detail seems
nice.
- Detailed inspection of a specific agent, following it. Side-by-side would
have to trace it in both canvases.
- Looking for differences... what are these? For a single agent, wanting to
know are they farther along their journey at some point in time? That could
be visualized nicely with a red or green thick route in front or behind them.
Maybe they're ahead of the baseline by some amount, or behind it. This could
use relative score or relative distance to goal or something. Would it trace
ahead by pure distance or by anticipated distance in a given time?
The side-by-side canvas seems incredibly difficult -- have to revamp everything
to dispatch mouse events, maybe synchronize cameras, other plugins
arbitrarily... No way.
Let's start with two concrete things:
- Start running an A/B test sets up a second optional simulation in the UI.
Some keys can toggle between showing one of the two, for now. Stepping will
step BOTH of them. Have to adjust the OSD and other sim control things.
Possibly even sim control should own the second sim?
- Q: how to prevent scenario instatiation or adding new agents while an
A/B test is in progress? need plugin groups / modes!
- road editing during an A/B test is _definitely_ not k
- argh!! wait, we need a different map, since we need different edits!
- that means also a different control map
- should the Sim own map and control_map to make it clear they're tied? I think so!
- practically all of the UI touches the map...
- wait wait draw map also needs to be duplicated then.
- we're back to the problem of loading map edits
- Make a visual trace abstraction to show something in front of or behind an
agent. It follows bends in the road, crosses intersections, etc. Could be
used for lookahead debugging right now, and this relative ghost comparison
thing next.
## Tedious to register a new plugin
- make the plugin (unavoidable, this is fine)
- plugins/mod
- import in ui
- declare it in ui's UI or PerMapUI
- initialize it (can we use default() or something?)
- event handler thing (unavoidable)
- but maybe Colorizer -> Plugin and make a generic event interface
- could also put draw there!
- number in the plugin list
- sometimes call draw()
alright, the boilerplate is all gone! \o/ I'm happy now.
## Colors
It's too tedious to make new colors, so I find myself hacking in temporary
hardcoded values. Declaring something far from its use sucks, because there's
no context. So how about this for an alternative:
- allow either [0.0, 1.0] or [0, 255] formats. helper rgb(r, g, b) implicit 1.0 alpha and rgba(r, g, b, a)
- cs.get("name", default)
- the colorscheme object lazily accumulates the list of colors, so the color picker plugin still works
- but some are in plugins that dont get called often
- what gets serialized? just marked changes from the color picker? yeah, and
those overrides are loaded in at first. colorscheme obj has a set() fxn that
remembers it's changed from default in code.
- can still later support different colorschemes with different json files.
- shared colors?
- should those be defined in one of the places arbitrarily, that we know will be invoked early?
- could a macro help with registration?
- get() being mutable means we have to use RefCell or propogate mutability in lots of sad places
### Round 2
Alright, the mutability is making some plugin refactoring hard, and I'm
repeating colors, because it's never safe to refer to some other string that
might or might not have been populated yet.
Macros don't look like they'll help here.
So time for hacktastic, old-school codegen. Grep. Sadly, I think python is the easiest route. :\
## Mutex plugins
The state of the world today:
- always let turn_cycler draw, even when it's not "active"
- bug today: edit a traffic signal, the "current phase" stays highlighted while editing
- show_owner can always have an opinion on color, even when "inactive"
- want some plugins to be able to coexist... show route, follow, sim ctrl
- other plugins should not stack... while traffic_signal_editor, tooltip is OK, but not much else
I don't want to think through the O(n^2) conflict matrix for plugins, but maybe just a quick list of conflicts...
- things that truly need to be the only active plugin, usually due to a wizard
- ab test manager
- color picker
- draw neighborhood
- floodfiller (debug fine)
- geom validator (debug fine)
- log display (maybe want to let sim run while this is active, but not allow keys?)
- edits manager
- road editor (debug fine)
- scenario manager
- stop sign editor (debug fine)
- time travel (debug is NOT fine and most other stuff also breaks)
- traffic signal editor (debug fine)
- warping (really should pause the sim while animating)
- things that display stuff and can be deactivated (mutex or not?)
- chokepoint finder
- osm classifier
- diff all | diff worlds
- toggleable layers
- neighborhood summary
- search (in one state, needs to eat the whole keyboard)
- activity heatmap
- show owner
- show route
- steepness viz
- turn cycler (blocks input sometimes)
- things that can kinda be active almost anytime (when the real sim is available)
- debug objects
- hider
- follower
- sim controller (maybe need to split this up into different pieces)
Another way of looking at it: what consumes the entire keyboard?
- anything with a wizard
- search (in only one state)
Or maybe another:
- does a plugin block the sim from running?
Or another:
- is a plugin just a toggleable effect? can it compose with others?
## Hiding crosswalks and signal boxes
Need to hint to DrawIntersection that
1) crosswalks should or should not be drawn
2) traffic signal box icon should or should not be drawn
from turn cycler and traffic signal editor
Ideas:
- overdraw in the background color
- super hacky, high effort, have to match background, doesnt work for the box icon...
- new RenderingHints in PluginCtx
- set stuff in event(), pass it in RenderOptions
- it's just like OSD!
- right now ezgui runner plumbs OSD between event and draw; but why is that special?
- and animation mode is squeezed into OSD, ew!
- event() needs to return (AnimationMode, T)
- draw takes T
- GUI trait becomes generic.
## Finishing the plugin refactor
- time travel needs to be per map
- make sure SimMode can handle loading entirely new map or starting an a/btest
- diff needs to be per UI
- are modes exclusive or not? how will tutorial mode work?
- UI takes a single Mode or State or something. not multiple. the blocking vs not behavior happens elsewhere. tutorial mode will use this, mixing in View/Debug/Edit/Sim as relevant. plugins per UI vs map is confusing, but that might actually be lifted into this more abstract ui state thing?
- could SimMode own primary and secondary? UI shouldn't.
- do make one plugin for UI that toggles between the others... at least edit and sim exclusion.
- can that one thing have some state per map and some per UI? sure. what're the only places that touch that? a/b test editor, map editor, sim controls
- just move PluginsPerUI and PluginsPerMap into this one place
- PluginCtx stays the same... (but moves to plugins/mod)
- and the single plugin that UI talks to is a totally new trait with different context. ;)
- UI will need to reach into this single plugin for different stuff.
First simplify UI in smaller ways.
- get_objects_onscreen should return one list of things
- and between drawing and mousing over, dont recalculate all of the DrawCar/Ped stuff!
- stop doing the generic plugin stuff in UI. be direct, since we have very few "plugins".
Tutorial mode needs to wrap the other stuff (and omit DebugMode!) to reach into SimMode and install a callback. Forces us back to this problem again. Radical idea: What state and code in UI is even important to preserve? Should TutorialMode literally be a new UI impl?
## Right-click context menus
First change the call-sites to do something new, then figure out how to draw it.
- For now, seemingly we don't need to indicate the thing that the menu is
logically attached to, since we can just record the current cursor
position...
- What if the thing we select is moving? Do we always pause when we launch a
menu? (and stop doing everything else except drawing!)
- once we right click something, we keep the same current_selection until the menu is canceled.
- The current impl of context menus is basically forcing lots of behavior in
ezgui. Seems desirable, since the opinionated UserInput is already there.
Probably input populating the OSD and drawing the OSD should also move to
ezgui and always happen.
- for now, repopulate the context menu every time.
- but arguably, the huge monolithic event()s in a bunch of plugins
leads naturally to weird problems like conflicts and overlapping keys
- the alternate to distributed, "decoupled" plugins is the opposite:
complete centralizedness
- one place builds context menus for Lanes. queries the state of
different plugins to add an entry or not.
- once we have a menu built and in place, we know we pause the sim.
dont have to rebuild it.
## Persistent menus
Having options appear and disappear here based on context is NOT good -- too
modal a UI, placement of things jumps around. What we want instead:
- Statically specify menus
- Much easier to organize now that we have modes, at least
- Menu items are stuff that today just takes a key to start (without selecting something first)
- Being forced to statically specify stuff will help organize the code
and my mental model of possible controls too; it's a very simple
visual depiction of exclusiveness
- Grey out things when they don't make sense
- Could know that each tick by seeing if we called input.menu_selected(foo) or not
I think we should wind up with 3 types of controls:
- top menus (can be greyed out by context)
- right-click context menus for specific objects
- Extra controls for special modes (floodfiller, editors) that pops up as a sidebar
- Things in the OSD now should also be written there (current sim time, what intersection am I editing, etc)
And all of these displayed menus can display a hotkey to the side.
## Organizing plugins -- yet again
This'll never be done till it is, y'know? The grouping and layers of
indirection are not so nice, and now there are menus, context-sensitive
actions, (soon to be) stackable modal plugins... So I just want one big flat
struct (State to start with) and to be able to reference plugins directly
without all this downcasting. The per-map vs not is still annoying, but... not
so bad. So, flatten the modes one-by-one into state.
Plugin styles are blocking or ambient. And some can conflict...
- first collapse edit mode; these're simple and all mutex and if present,
immediately dominate all the everything
- sim mode next!
- right now diff plugin blocks everything and its mutex, so just make it part of the active_edit_plugin world.
- show score plugin is modal and nonblocking; lets not instantiate it till we need it. list of stackable modal things, just make sure not to create one again.
- sim controls is a little weird; for now, it's always nonblocking and ambient and ever-present.
- display logs is easy, just an exclusive blocking plugin.
- debug mode stuff...
- a bunch of exclusive blocking stuff... meaning it doesn't actually need to be per map!
- hider... per-map state, shouldnt exist till it does, stackable modal, should modify ctx itself.
- layers... shouldnt be per-map state. should modify stuff itself.
- view mode... whew, one thing at a time.
- warp... exclusive blocking. apparently we used to still let ambient plugins draw and color stuff while it's active, but meh, doesnt seem important.
- search... argh, this is the one that's SOMETIMES exclusive blocking and sometimes stackable modal.
- finally, make time travel exclusive blocking, since lots of other stuff doesnt actually work with it.
## Quick viewers
I need a way to render and debug InitialMaps. Don't want to squeeze it into
editor, but also don't want to go overboard making a new crate with lots of
copy-pasta code. It's like the synthetic editor. Can we extract out a pattern
for this kind of thing?
- cleanup
- make synthetic use stuff
- organize context menu things based on the ID
- not updating road geometry when an intersection changes is nice... hmm
## Too spread out
I want to hover over an agent and preview its route. Where's this functionality belong?
- could put it in Draw{Bike,Car,Ped}, but then it's duplicated and doesnt cache (only recalculate trace when tick changes or mouseover changes)
- could keep it in the ambient ShowRoute plugin, but then it's almost hard to understand that mousing over a car also reveals associated buildings...
- i pretty much want to try attaching some mouseover logic to the car/bike/ped Renderables
Look at show_route plugin. Expressing state changes is awkward. Want to make the 'r' key available in Hovering state, but only if we survive the Hovering state.
## Modal menus
About to let game own this directly, moving it out of ezgui.
- at first, just do this and keep current impl
- do the same for top menu probably, and context menus?
- but what if we dont constantly reset the active options? right after we do one action, the menu is displayed wrong, because no event causes us to build up possible actions again.
- hack: do a no-op event after just to rebuild the menu. very gross.
- one pass that doesnt DO anything, just builds up all the possible events that do something interesting
- then irrelevant events are immediately dropped
- this is kind of a generalization of the 'unused key_release' thing happening now
- two different styles...
- register callbacks (ew? except it maybe avoids weird problems with accidentally using an event twice)
- or have a no-op phase and an execution phase
The more radical design would get rid of event() entirely, invert the control. The game itself would say "poll for next event".
- When do we draw? When we block for events? Is drawing inlined into this one combined method, or is there still a separate function to redo it from scratch?
- screensaver would immediately hand over control to the menu. no more StillActive case!
## Stackable states
What've we wound up with so far?
- sandbox
- playing
- agent spawner
- time travel
- route explorer
- jumping to time
- scoreboard
- browsing trips
- load another map
- edit map
- main diff mode
- saving
- loading
- stop sign editor
- traffic signal editor
- change cycle duration
- choose preset
- tutorial
- various ones...
- debug mode
- main
- polygon debugger
- searching osm
- color picker
- bus route explorer
- mission edit mode
- main
- neighborhood
- load/create
- edit
- load scenario
- create scenario
- scenario editor
- add stuff
- popdat data viz
- individ trip viz
- all trip viz
- trips to scenario wizard
- a/b test mode
- setup
- make new test
- load test
- load savestate for a test
- playing
- scoreboard
- about
thoughts on this structure right now:
- most of these states also quietly have an extra state for warp / navigate, from common
- dont like explicitly having enums with all the states
- sharing code between states happens rarely
- way too much nesting in the code to manage this
- there are a few cases where if we escape from some menu, we pop back too far
- this is jarring; player's mental model is probably a stack
dont other game engines explicitly have states?
- https://book.amethyst.rs/stable/concepts/state.html
- push and replace
roughly what I want:
trait State {
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> StateResult;
fn draw(&self, g: &mut GfxCtx, ui: &UI);
}
StateResult indicates EventLoopMode and indicates if we should do any
transitions... pop current state, push some new state, replace current state
- why not just do this manipulation directly ourselves? because of borrowing.
- shared state in UI
- ... which means secondary has to go in there
- if we tried to nicely pass things between states... how would that actually
work when pushing? only replacing/popping
I don't want to wind up with the mess of plugins again. Why am I sure it's not that?
- these are complete, mutex states
## Text
What's slow right now?
- drawing the rectangles?
- forming the Text?
- doing the draw_queued flush?
Simpler ideas for speedup:
= only one draw_queued flush, to preserve caching behavior!
- batch fork/unfork and all the rectangles? maybe not needed for modal menu
- detect state changes in ModalMenu, recreate Drawable thing only when needed
Current stack:
- https://docs.rs/glium-glyph/0.3.0/glium_glyph/
- https://docs.rs/glyph_brush/0.4.0/glyph_brush/
- https://docs.rs/rusttype/0.7.7/rusttype/
hidpi mess:
- events_loop.get_primary_monitor().get_hidpi_factor() yields 1.25. env var seems ignored after upgrading.

View File

@ -1,200 +0,0 @@
# Intersection-related design notes
## Nice diagrams
http://streetsillustrated.seattle.gov/design-standards/intersections/pedcrossing/
## Stop sign editor
Stop signs are FIFO, except that many intersections only have a stop sign for
some sides. Going straight on the priority roads is immedite, and left turns
from those priority roads also take precedence over the low-priority roads. So
should the stop sign controller mark individual turns as priority/not, or
individual roads, with implied semantics for left turns? There are really 3
priorities if turns are considered...
Figuring out nonconflicting roads seems tricky. For now, going to have a
complicated UI and let individual turns be classified into 3 priority classes.
First group can't conflict, second and third groups can conflict and are FIFO.
Will probably have to revisit this later.
## Stop signs
How to depict stop signs? Each driving lane has a priority... asap go or full
stop. Turns from go lanes might be yields, but shouldn't need to represent that
visually.
- Easy representation: draw red line / stop sign in some driving lanes. Leave the priority lanes alone.
- Harder: draw a stop sign on the side of the road by some lanes. Won't this look weird top-down and at certain angles?
## Traffic signals
- per lane would be weird.
- drawing turn icons as red/yellow/green is pretty clear...
- could draw an unaligned signal box with 3 circles in the middle of the intersection, but what does it represent? maybe just an initial indicator of what's going on; not full detail.
- similarly, draw a single stop sign in the middle of other intersections? :P
- http://streetsillustrated.seattle.gov/design-standards/intersections/its/ probably has hints on cycle duration
- https://ops.fhwa.dot.gov/publications/fhwahop08024/chapter4.htm
- ring and barrier diagrams
- https://www.webpages.uidaho.edu/TrafficSignalSystems/traffic/instructor/ch3a.pdf
- http://iamtraffic.org/evaluation/the-six-way/
ah, think of the real world for the UI. Light per incoming lane. Sometimes need
to show a green/yellow/red arrow. Could similarly show the ped stop hand or the
walking figure, probably at both tips of a sidewalk.
- https://www.slideshare.net/hronaldo10/lecture-06-signalized-intersections-traffic-engineering-profusama-shahdah
- especially slide 11 has the best notation for protected and permited turns!
- slide 12 shows a nice table
### Yielding
Similar to a stop sign, there are turn priorities for cycles. Some turns have
right-of-way, others need to yield, but can go if there's room
## Intersection policies for pedestrians ##
Before figuring out how pedestrians will deterministically use intersections alongside cars, recall how cars currently work...
- ask all cars for next move (continue on same thing, or move to a turn/lane)
- using fixed state, adjust some of the moves that dont have room to move to a new spot to wait instead
- serially ask intersections if a car can start a turn
- serially make sure only one new car enters a lane in the tick
- shouldnt the intersection policy guarantee this by itself?
- very awkwardly reset all queues from scratch
How did AORTA do it?
- agent.step for all of em (mutate stuff)
- enter intersections, telling them. must've previously gotten a ticket
- let all the agents react to the new world (immutable, except for IDEMPOTENTLY asking for turn)
- here we ask for tickets, unless we've already got one
- same for intersections
- grant them here
aka basically yeah, the simple:
- agents send a ticket during the planning phase?
- intersections get a chance to react every tick, granting tickets
- during the next action phase, an agent can act on the approved ticket?
good pattern in intersections:
- a sim state that the rest of the code interacts with for ALL intersections. rest of code doesnt see individual objects.
- that manager object delegates out most of the logic to SPECIALIZED versions of individual objects and does the matching
- no need for this to exist on the individual IntersectionPolicy object
How to share common state in intersections?
- if it's just the accepted set, have a parallel array and pass it into step()
- data locality gets ruined, this is ECS style, bleh
- have a common struct that both enum variants contain
- still have to match on enum type to operate on it commonly!
- have one struct that then contains an enum
- when delegating to specialized thing, can pass this unpacked thing down, right?
Seeing lots of deadlock bugs from accepting non-leader vehicles. For now,
switch to only considering leader vehicles, and later maybe relax to anybody
following only accepted vehicles.
Leader vehicle is a bit vague; could be leader on current queue, which is still a bit far away.
## Stop sign priority
Use OSM highway tags to rank. For all the turns on the higher priority road, detect priority/yield based on turn angle, I guess.
## Handling complicated intersections with tiny roads
AORTA's super-roads
https://www.mapbox.com/mapping/mapping-for-navigation/modeling-intersections-for-map-navigation/
https://wazeopedia.waze.com/wiki/Global/Junction_Style_Guide/Intersections
https://wiki.openstreetmap.org/wiki/Junctions
http://www.sumo.dlr.de/userdoc/Networks/Building_Networks_from_own_XML-descriptions.html#Joining_Nodes
- instead of looking for short roads, clip road lines to intersection geometry and then see if they're short (10m)
Maybe start by hardcoding the things to merge. Don't worry about detection yet. r257
- When to do the operation?
- do it before making lanes ideally...
- first, make lanes separately from making roads, so intersection geometry can move up.
- could then even merge the trim lines step? hmm.
- or even do it to raw data? is that possible?
- r257 example
- extend two roads with points from the tiny road. delete i283
- make sure the road attributes match up
- do this first; preprocess raw_data.
Ah, the problem with just extending geometry... I think turns need to be
polylines sometimes. Need to do the merging all the way at the end,
unfortunately...
### Attempt v2
Could use https://data.seattle.gov/Transportation/Intersections/e7db-mhd7 as a
source of truth? Match all OSM intersections to one of these.
Shelby and 23rd...
- want a big intersection to cover the two tiny horizontal roads
- want to glue turns together from the original things (turn+lane+turn)
- input is still a tiny road to collapse.
- the two intersections it's connected to will logically become one
- redo the intersection and lane geometry
- use the original turns to create composites and just use those; dont recalculate turns
- probably need a HalfMap (in between raw and Map) or something. the order...
- raw intersections -> real intersections
- raw roads -> real roads and lanes
- assign border intersections
- make the intersection polygons
- trim lanes
- populate turns
- then do the merging magic
- need to fix up IDs everywhere
- ... rest of map construction proceeds
## Live editing
Traffic signal changes can happen live, because weird changes will just look
like brief blips of overtime. Same for stop signs, I think...
## Roundabouts
Make em look nice.
- 26th and Boyer (i333)
- there's a single highway=residential, junction=roundabout way
- find all the other ways incident to it
- the road splitting we do in convert_osm isn't appropriate at all
- geometrically, this could just be a single intersection
- could hint that it's a roundabout and make new turns later, or cheat and treat it as an all-way stop sign for now
- Lynn and Boyer (i135)
- way more complicated. there's a junction=roundabout way, but also a bunch of... other stuff
Lynn and Boyer needs more work, but now it seems like it's just merging short
roads. If we just cherry-pick some roads to destroy and make enveloped by the
intersection, will it work? Will reasonable turns result? Or do we have to
retain hints in the raw Intersection about things that should be connected and
the original path inside the intersection that should do it?
## v3 of short road handling
- soln1: lets try MANUALLY merging intersections
- how can we specify in a map-agnostic way? OSM ids? intersection name?
*** none of this is looking good. manually draw what i want.
- soln2: make that road longer by not requiring perpendicular endings?
- soln3: disallow that weird sequence of turns. c615. north on 23rd, left on madison
- does OSM have turn restrictions here? nope.
- what's reality? its a ped island.
- this feels brittle regardless
- right from madison to 23rd southbound MUST happen from oneway, not at the light
- soln4: more complicated sim to lock a sequence of turns
- pushes the problem downstream...
- soln5: loosen the sim rules about proceeding through "blocked" intersection
- soln6: manual map editor, damnit. blow up roads. define new ones by dragging points. copy over md.
- try operating on InitialMaps

View File

@ -1,14 +0,0 @@
# Lane-changing
Ah, this bundle of joy.
Steps:
- decide if LCing can ever fail (or if cars will just stop and wait to merge)
- query to know when is it safe (similar to spawn checking)
- the state to execute it (and assert it completes in time)
- the modifications to lookahead
- change pathfinding to understand new movements
- how many LCs can be done on one road?
- how to render it?
= change what turns are initially created

View File

@ -1,483 +0,0 @@
# Map-related design notes
## Lanes
It's time to model more things:
- multiple driving lanes, with possibly individual turn restrictions
- dedicated bus lanes
- lanes with parked cars
- bike lanes
- sidewalks
Each lane needs some geometry:
- center lines to draw agents on
- for sidewalks, use center line to to draw agents on the left and right sides?
- polygons to draw the lane and mouseover
Open questions:
- Can we assume all lanes are the same width?
- Seems wrong for many sidewalks especially
- Could be wrong for bike lanes, but could just assume it's a bike lane with a buffer
- Some lanes are immutable
- Sidewalks can't be changed to other types; they're raised with a curb
Some modeling questions:
- Where should expansion of roads into lanes happen?
- initial OSM conversion, adding more stuff to the proto?
- initial map_model::new loading, at least for development convenience
- same reason that turns aren't (yet) serialized
- Is it useful to model the entire road?
- the parent/child relation may be hard to maintain
- but lanes need to know their siblings
- maintaining directional sanity could be useful
- what's the UI for changing lane types?
- it's a bit arbitrary which lane should draw the yellow center lines
Initial design:
- "Road" becomes "Lane" with a type
- don't need to know sibling lanes yet
- arbitrarily, one lane might have extra bits/geometry for yellow center line markings
- ideally, get rid of one-wayness and original center points, and plumb along pre-shifted lines
- but due to the polyline problem (affecting both geom center line layer that agents follow, and polygons for drawing), can't do this. encapsulate the messiness at least.
- so, store one way and orig points and index, but have an accessor
- as a compromise, dont interpet OSM points on a one-way road as the center, but as the edge? this is proving hard to do.
Thinking about a new design:
- Much more general "Land" primitive that's just a nice polygon boundary for drawing/selection and one (or more, for sidewalks?) center lines for how to cross the space, with a notion of turns. It's what road is now, but way simpler data.
- Maybe the GeomRoad / DrawRoad split is a little confusing after all, since the layering just isn't perfect. figure out the polygon and centerline up-front, then ditch the other intermediate gunk.
- also ideally make one polygon for the road, not a bunch of individual pieces? but then we'd have to go triangulate later for opengl anyway
- enforce that all the polygons are nonoverlapping
## Representing map edits
Two reasons for edits:
- the basemap is wrong because of bad OSM data or heuristics
- here's a possible edit to A/B test
Types of edits:
- change lane type between driving, parking, biking
- sidewalks are fixed!
- some edits are illegal... parking lane has to be in a certain side... right? well, actually, dont do that yet.
- delete a lane (because the basemap is wrong)
- modify stop sign priorities
- modify traffic signal timings
How to visually diff edits?
- highlight them
- UI to quickly jump and see them
How to encode the edits?
- "Remove lane" is weird; how about per road, list the lane types? Then it's
almost kinda obvious how to plug into part of the current map making
pipeline.
- alright, let's really first think about road vs lane
Need to work through some edits to see how they affect downstream things. What
needs to be recomputed? How do we long-term serialize things like edits? How
can they even refer to things by ID if the IDs could change? What IDs might
change?
Alright, now we can be concrete -- when we have a road edit, what can be affected?
MAP LAYER:
- the road struct state (just list of children, really)
- dont want to blindly run all the road making code, since it'd double-add stuff to intersection
- delete old lanes, make new lanes
- how would IDs work? if we try to reuse the old ones, we might wind up
with gaps, or overflowing available space.
- trim lanes
- need to recalculate original lane_center_pts for all affected lanes
in a certain direction. tricky since they're two-sided; have to
restore just the original direction on it.
- recalculate turns, for the two intersections
- same ID problem
- recalculate some building front paths, maybe
CONTROL LAYER:
- recalculate two intersections
SIM LAYER:
- creating/deleting sidewalks is pretty easy
- SimQueues are associated with turns and lanes, but easyish to create/delete later
- should probably have a way to prevent mutations; maybe need to drain a lane of agents before changing it
UI:
- make a new DrawLane, DrawIntersection, etc
- update quadtrees
- would have to maybe update a bunch of plugin state (highlighting or
floodfilling or something), but since we know road editor is active, is easy!
Strategies:
- testing via equivalence -- reload from scratch should be equal to live edits
- will IDs make this very tricky?
- for things like sim and UI that hook on and have derived state, should we
always kinda lazily grab DrawRoads, SimQueues, etc? or immediately plumb
through deletes and inserts?
- is there a way to programatically record data dependencies or kinda do FRPish stuff from the start?
- could always blindly recalculate everything live, but man, that's gotta be slow
- maybe change constructors that take full map into incremental "hey, this road exists!" mutations. then just need to introduce deletions. in other words, embrace incremental mutability.
- assume the bbox doesn't change as a result of any edit
the ID problem:
- need determinism and deep equality checks for things. if we load a map from
scratch with edits, vs do a live shuffle, the IDs wont match up if they use a
slotmap.
- can we refer to things in more stable ways; no LaneID, but
RoadID+direction+offset. no Turn, but two... effectively lane IDs?
- maybe we can combine these ideas; use nondet slotmaps, but when doing
equality checks, dont use these IDs -- treat these IDs as memory addresses.
IDs for lookup and IDs for equality.
- what're the different things that need this?
- stable objects: building, intersection, road
- malleable
- lane (road, direction, offset, lane type)
- turn (src lane, dst lane)
- recurse and refer to full lane descriptions, or their temporary ID?
- ideally want to store things contiguously in memory
- ideally want a compact, easy thing to type quickly to debug.
- aka, ideally want a nice bijection from the persistent thing to numbers?
- actually, if we "leave room for" enough lanes per road and turns per intersection to begin with...
- can just replace existing IDs when we change something
- still have to mark things dead
- still have to watch out for dangling references
The changes needed:
- figure out the ID problem
- change existing code from big constructors to incremental adds
- exactly what layers and objects?
- implement incremental deletes
- try doing a live edit and comparing with from scratch
Going to start implementing part of this in a branch, just to get more detail.
- when there's a road edit, calculate the affected objects (road and all children, two intersections)
- implement a sanity check to make sure no dangling ref to old IDs
I think this is working so far. The vital question: is it too complicated? Is there a simpler way?
- simpler idea: retain more raw data, violently destroy road and intersection and make from scratch
- problem: it'd percolate, we need to keep old connected roads the same
- EVEN SIMPLER IDEA: stop trying to solve hard problems
- lane deletion is rare and a basemap-only edit; can mark it in the UI temporarily and omit in the next full load
- changing lane types is the main intended edit. what actual consequences does this have? filtering through the list earlier...
- change lane type
- recalculate all turns for two intersections
- the number of turns might go up or down
- control layer intersection policies then need updating
- sim needs to know about changed lanes and turns
- and a few easy edits in the UI layer too
- changing lane direction might be a little more complicated, but NOT BY MUCH
so, I think the steps:
= see what's useful from this branch, bring it to master (encapsulating the driving state stuff)
= ditch TurnIDs; have a BTreeMap of src/dst (LaneID, LaneID)
= add a mutate_lanes() and replace_turns() to all the appropriate layers
Cool, good enough to start. whew.
### Looking at this problem again
raw_map to map is expensive -- so much that we have to precompute it. For the simple edits (change lane type and modify intersection policy), can we do a cheap live update?
MAP LAYER:
- initial Road stores lane_specs, but doesnt use the lanetypes yet
- halfmap uses lane types: is_border, make_all_turns, turn lookup idx
- stop signs and traffic signals assigned last-minute, very easy to override in map layer
- pathfinder graph
SIM LAYER (ignore for now, just ban most live edits):
- parking sim needs to know about lane types
- block some edits
- cant modify turns while intersection has any requests or accepted
- cant change lane type while anybody is on it
- or if parking spot is reserved
- paths are also affected
EDITOR LAYER:
- ooh, luckily DrawLanes aren't batched! should be relatively easy.
equivalence test...
- option 1: load a map from scratch with edits, compare to making live edits, do PartialEq and meld
- option 2: always apply edits as last step of initial map loading. always reset map to canonical form before applying edits.
So how to structure things?
- dont ask for individual changes; mutate a MapEdits object and ask map, sim, editor layer to recalculate everything needed.
- lane IDs will never change; just store LaneID -> LaneType overrides.
- might have to figure out original OSM-based lanetype by recalculating
- applying map edits always takes a BEFORE (possibly empty) and AFTER set
## Notes on King County GIS datasets
- TODO: https://data-seattlecitygis.opendata.arcgis.com/datasets/channelization
- https://data-seattlecitygis.opendata.arcgis.com/datasets/street-signs
- could figure out stop sign orientation from this
- https://data.seattle.gov/dataset/Street-Pavement-Edge-1999/rm29-895w
- good base geometry
- https://data.seattle.gov/Transportation/Traffic-Lanes/v5x7-g7qz
- some streets missing
- info on turn lanes
- https://data.seattle.gov/Transportation/Intersections/e7db-mhd7
- https://gis-kingcounty.opendata.arcgis.com/datasets/transit-signals-for-king-county-metro--tsp-signals-point?geometry=-122.315%2C47.641%2C-122.291%2C47.646
- description says number of phases
- https://www.wsdot.wa.gov/mapsdata/tools/trafficplanningtrends.htm
- small start to demand data
## Speeding up map loading
- can we use quadtrees for the expensive building/sidewalk matching?
- awkward to bring a rendering-layer concept in; dont otherwise care about lane polygons
## Neighborhoods
It's hard to zoom out and quickly pinpoint where interesting things (A/B diffs,
traffic jams, suspicious silence) are happening. What if we could optionally
collapse a region into a big colored polygon and just display quick stats on
it?
- defining them
- what do they include?
- a polygon capturing buildings, lanes, etc
- what happens when the polygon only partially contains an object?
- is it a border thing? makes sense for lanes, not for buildings
- border lanes could be used for some kind of in/out flow
- do they have to fully partition the map?
- they should at least be disjoint
- how to define them?
- the seattle neighborhood definitions are seemingly way too large
- could manually draw them, but including buildings carefully is sort of hard
- we have the automatic parcel grouping stuff for coloring... could it help?
- could find max-cuts to spot important border roads and the neighborhoods they connect
- summary stats
- cars parked, open parking spots, moving peds, moving cars,
stuck cars, busses present, number of agents with A/B test
divergence...
- this can start to force me to be mildly performant.
precompute what objects are in each polygon, then have a
summary thing collect stats every few seconds when shown?
- do these need to be objects in the GUI? at first no, just make a
plugin draw them, but eventually, yes. they should probably be a
map_model concept.
## Border nodes
No matter how things are sliced, we always have to cut off roads somewhere,
unless we simulate a continent. :) So the idea of border nodes is to
start/terminate these cut-off roads with a special intersection where traffic
can begin or end.
- rendering?
- ideally the nodes would actually be at the boundary of the map
- https://wiki.openstreetmap.org/wiki/Osmosis/Detailed_Usage_0.47#Area_Filtering_Tasks
has some flags to explore
- some special color or symbol?
- detection
- for oneways this is easy, but two-ways look like dead-ends
- how to distinguish actual dead-ends?
- get osmosis to also output which OSM ways were clipped?
- don't see how to do this
- manually marking them?
- draw the FSM for cars/peds
- trips starting/ending at border nodes short-circuit some steps
What about border nodes where the sidewalk can cross over, but the driving lane
leads nowhere? Maybe instead of having a special intersection policy, we have
turns to nowhere? Sort of depends how the border node is defined -- do we have
long skinny things jutting out, or not?
- don't add any turns, even if we could
OK, now adapt the control and sim layers to handle border nodes...
Adapt trips / pathfinding. It has to be an explicit origin/goal.
start/end for trips right now is buildings. Needs to be building or a border
node. And driving also gets more complicated -- can start from a parked car or
a border node, and end by parking near a building or going to a border node.
### The master FSM
This doesn't belong in maps.md, but related to border nodes, so for now...
There's ultimately a big state machine for trips that's awkwardly hiding in the
code and slowly being exposed by stuff like the higher-detail pathfinding.
- possible starts:
- ped exits building
- ped appears at border node
- car appears at border node
- possible ends:
- ped enters building
- ped vanishes at border node
- car vanishes at border node
- the stuff in the middle
- ped crosses front path from building to sidewalk spot
- ped crosses front path from sidewalk spot to building
- agent crosses a lane normally
- agent crosses a lane contraflow
- agent makes a turn
- unparking a car from a spot
- parking a car at a spot
- (soon) lanechanging
- wait for a bus
- enter a bus
- ride a bus
- exit a bus
- Roaming around looking for parking dynamically and lazily updates the front of this plan
- Buses are weird
- it's annoying they don't have a trip (and don't work with the route plugin today)
- they share some states (the mechanical driving ones)
- and have a few of their own (deboard people, board people)
- dynamic plan expansion also happens when departing from a stop; since
storing the entire cycle would be weird
- Parked cars aren't really part of this giant state machine
Then the code for these little transitional states can be less weirdly special cased, maybe.
How does initial plan formation and mode choice work? Could have a round of
pathfinding for each possible mode, or could search a single more abstract
action graph. moves from walking on this sidewalk? oh we own this car, could
choose to unpark it, then drive somewhere. Everything has time cost.
This also supercedes the Event thing in sim and makes testing potentially WAY cooler.
## Pathfinding
How do we natively wind up with a list of PathSteps (normal lane, contraflow
lane, turn) without doing the terrible stitch-together later thing?
- easy option: the nodes in the graph we search become PathSteps. expansion is easy.
- is it slightly inefficient that we could loop back and forth on
sidewalks? Should be able to prevent it by not making that a way to
expand.
- maybe less weird option: nodes are (Lane, Intersection). or even more
accuratelyish, (Lane, dist_along), so we can start and end anywhere.
- When stitching together the path, a pair of lanes is a turn.
Otherwise, cross forwards or backwards.
## Road query refactor
What we have right now...
Road
pub fn find_sidewalk(&self, parking_or_driving: LaneID) -> Result<LaneID, Error>
pub fn find_driving_lane(&self, parking: LaneID) -> Result<LaneID, Error>
pub fn find_driving_lane_from_sidewalk(&self, sidewalk: LaneID) -> Result<LaneID, Error>
pub fn find_parking_lane(&self, driving: LaneID) -> Result<LaneID, Error>
pub fn get_opposite_lane(&self, lane: LaneID, lane_type: LaneType) -> Result<LaneID, Error>
pub fn get_siblings(&self, lane: LaneID) -> Vec<(LaneID, LaneType)>
Map
pub fn get_driving_lane_from_bldg(&self, bldg: BuildingID) -> Result<LaneID, Error>
pub fn get_driving_lane_from_sidewalk(&self, sidewalk: LaneID) -> Result<LaneID, Error>
pub fn get_sidewalk_from_driving_lane(&self, driving: LaneID) -> Result<LaneID, Error>
pub fn get_driving_lane_from_parking(&self, parking: LaneID) -> Result<LaneID, Error>
pub fn building_to_road(&self, id: BuildingID) -> &Road
Some themes...
- have some lane, want the nearest lane of some type(s).
- The source doesn't really matter -- we want as close as possible, but there's no requirement of adjacency at all.
- If the source/dest is a sidewalk, some weird handling for one-ways...
## Dist along mismatches
Concrete examples:
- House path on a sidewalk that's slightly longer than the driving lane, so can't spawn a bike
- Parking lane longer than driving lane, so can't make a car start
Solutions
- Don't blindly copy dist_along between lanes. Do perpendicular line segment check to find the proper value.
- But is this allowed to fail? Or just have weird edge cases and round
up/down to the beginning/end of the other lane?
- Embrace failure
- Silently erase agents/trips when they fail
- With better up-front planning, can maybe filter out the trip from the beginning
- But this is hacky and doesn't capture user intention
- Erase problems
- Filter out houses, parking spots, etc that don't match other lanes
- This seems too aggressive...
Start by detecting all the possible problems in the geom validator plugin. Then
probably go with the first solution, after understanding what the most
egregious edge cases look like.
## Debugging turns
- How can we make a TurnID into an easily-typable thing to warp to it?
- hash?
- not actually unique, but good enough probably? could be confusing one day ;)
- make a helper that's exhaustive
- concat?
- ideally it'd be easily bijective, but since this is just for debug,
it's not so terrible to brute-force to lookup
- hash + base 36 is still too much... 1dj33llg4ei37
- lets just separately index them. shrug. ideally part of the TurnID, but oh well.
- How can we display the turn itself in response to warp?
- need to poke turn cycler from warp?
## Faster pathfinding
https://pdfs.semanticscholar.org/36d1/b4ec6a4a2823e9c875318f1952df4abf4876.pdf
- do a bunch of pathfinding queries normally
- then look for common shared sequences
- cache those paths, then anytime normal pathfinding later hits a node, be able to jump to any destination with known cost
- but will the better heuristic to the goal make us actually search those?
- or just use intersections between big roads as these landmarks, precompute paths between them
contraction hierarchies: https://github.com/cc-routing/routing/wiki/Contraction-Hierarchies
https://github.com/aquavit/Project-OSRM/wiki/OSRM-normalized-file-format
https://github.com/deliveroo/osrm-rs
- for transit routing: https://www.microsoft.com/en-us/research/publication/round-based-public-transit-routing/
## Traffic signal data
https://wiki.openstreetmap.org/wiki/Proposed_features/Traffic_Signals
https://wiki.openstreetmap.org/wiki/Proposed_features/Set_of_Traffic_Signals
https://wiki.openstreetmap.org/wiki/Tag:highway%3Dtraffic_signals#Complex_intersections
https://sumo.dlr.de/wiki/Simulation/Traffic_Lights
https://wiki.openstreetmap.org/wiki/Proposed_features/Traffic_Signal_Timings
https://connectedsignals.com/
## CARLA project
http://carla.org/
https://www.vectorzero.io/
http://www.opendrive.org/download.html spec
https://github.com/carla-simulator/carla-map-editor

View File

@ -1,42 +0,0 @@
# Parking-related design notes
## Parking
- already drawing parking spots of some length
- car has to drive on adjacent driving lane past that distance, then spend X seconds parking or unparking
- draw different color while doing this
- this will probably mess up the clunky minimal driving model that infers distance based on time
- Need to mark occupancy of all the parking spots. should be there for parking lanes instead of SimQueue.
- Start the sim with a bunch of parked cars
- how to model those cars? not as active agents.
- no more spawning a car! select a car to wake up. :D
The car's FSM:
```dot
parked -> departing;
departing -> traveling_along_road;
traveling_along_road -> waiting_for_turn;
waiting_for_turn -> executing_turn;
executing_turn -> traveling_along_road;
traveling_along_road -> parking;
parking -> parkd;
```
- I guess CarIDs are going to be handled a little differently now; all the cars will be created once up-front in a parking state
- Don't really want active Car structs for all the parked cars. Or, don't want to ask them to do stuff every tick.
- As we add more agent types, it'll become more clear how to arrange things...
- But for now, make something to manage both active and parked cars.
- Kind of seeing two designs
- master sim owns driving and parking state. a CarID is managed by exactly one. master sim has to enforce that.
- master sim owns car state as an enum, calls high-level step-forward functions for driving and parking
- perf: cant iterate just the active cars?
How to represent departing/parking states?
- could have state in both driving and parking sims. hacks to make driving not advance the car state.
- could represent it in the master sim state, but that's also a slight hack
---> or, own it in the driving state, since thats the major place where we need to block other cars and make sure we dont hit things.
- should we tell parking state about the transitional cars or not? driving should render them. might make statistics and looking for free spots weird, but let's not tell parking about them yet!
Time to implement roaming if there are no spots free!

View File

@ -1,87 +0,0 @@
# Physics-related design notes
## Floating point and units
Currently using si::Second<f64> for time, which means comparing sim state by
deriving Eq is a headache. Since the timestep size is fixed anyway, this should
just become ticks. Was tempted to use usize, but arch dependence is weird, and
with a 0.1s timestep, 2^32 - 1 ticks is about 13.5 years, which is quite a long
timescale for a traffic simulation. :) So, let's switch to u32.
Now I'm hitting all the fun FP imprecision issues. Could probably hack in
epsilon and negative checks everywhere in kinematics, but why? Should research
it more, but I think the better approach is to use fixed-point arithmetic for
everything (aka u8 or whatever).
Problems with floats:
- they dont natively order
- they arent PartialEq
- serialization sometimes breaks
- epsilon comparison issues
Options:
- just to solve epsilon comparison issues
- https://crates.io/crates/float-cmp
- https://crates.io/crates/approx
- just to get Ord and Eq
- https://crates.io/crates/decorum
- https://crates.io/crates/ordered-float
- use rational / fraction types
- https://crates.io/crates/fraction
- https://crates.io/crates/rug
- this seems a bit overpowered
- use my own custom wrapper type around u8 or whatever size makes sense for each thing
- representing 0.1s or 0.1m or whatever
- use a fixed point arithmetic crate
- https://crates.io/crates/fpa
- https://crates.io/crates/fixed
- https://crates.io/crates/fix
- would want to wrap the types anyway; only some operations make sense
- can we compare results with the prev float approach? make them store the
other thing, compare results? or compare the results of a sim?
- is it possible to do this change gradually? unlikely...
- stick all the types in geom, for now
- moment in time (tick)
- resolution: 0.1s with u32, so about 13.5 years
- duration
- resolution: 0.1s with u32, so about 13.5 years
- time - time = duration
- distance (always an interval -- dist_along is relative to the start)
- non-negative by construction!
- say resolution is 0.3m (about 1ft), use u32, huge huge distances
- geometry is polylines, sequences of (f64, f64) representing meters
from some origin. we could keep drawing the same, but do stuff like
dist_along as this new type? or change Pt2D to have more reasonable resolution?
- still represent angles as f64 radians? for drawing, turn calculation, etc
- speed
- meters per second, constructed from distance / duration
- should resolution be 0.3m / 0.1s? 1 unit of distance per one timestep? or, maybe less than that?
- can be negative
- acceleration
- can be negative
- what should the resolution be?
## Types again
Alright, very annoyed about hacking around PartialEq support for dimensioned
stuff everywhere. And dimensioned is providing no benefit -- I'd prefer my own
types. Can also add in validation that there are no negative distances. Also
the constants aren't particularly nice.
One thing -- f64 is PartialEq, not Eq. Why do I need Eq? sim determinism works
fine with with PartialEq. Undid tons of stuff, woot!
Ah, that was easy. Still reasons to make my own native types:
- less dependencies
- explicit operations supported, makes code more clear
- can verify distance is non-negative
- can maybe make the epsilon tolerance consistent
## Coordinate system
Switching to something that's more easily bijective, but
https://crates.io/crates/flat_projection could be a good candidate too.

View File

@ -1,4 +0,0 @@
How to get https://www.arcgis.com/home/item.html?id=777604c09cd24b4eb72952229d6d4caf into qgis?
- map viewer -> content on sidebar -> description -> look for WMTSCapabilities.xml in source
- qgis, add layer from wmts

View File

@ -1,242 +0,0 @@
# General simulation-related design notes
## Spawning agents ##
Ideally, each method would return a future that would do stuff, right? But the
mutations need to be done serially. So I think each sim's method should take
the path outright, not even start/end. Stick the rng work in sim for the
moment. This should let the start/goal selection and the parallelization of
paths happen at a more outer layer, in the sim aggregator.
... and now for scenarios / spawners. these get to run every step, trying to
introduce new things in the different simulations. if a parked car can't
currently begin departing, it'll keep trying every tick.
... and now we've hit the point where Command enum has tons of boilerplate and
also doing the pathfinding every re-attempt is very slow, especially when
spawning agents from a border. I would just go do the work immediately, store a
CreateCar or equivalent walking sim spec, and execute those instead... except
that then we won't parallelize pathfinding!
It'd actually be possibly desirable to do all the expensive pathfinding
up-front at the beginning of the sim (during scenario instantiation) to have
more consistent performance during the running of the sim. (Though I can also
imagine wanting to naturally spread it out too...)
Ahh wait, definitely _cannot_ do all of the pathfinding work up-front. As trip
legs are finished, we still need some of those commands available to start new
trip legs.
Ideas:
- make Commands have a cached path. first time hitting a particular command,
batch all the requests, calculate paths, strip out commands with impossible
paths, and populate the rest.
- probably the easiest change to make right now
- keeps boilerplatey and complicated code around
- flexible... could precompute as many paths at a time as we want
- shift responsibilities...
- car/ped ID counter moves to scenario instantiation (possibly a new thing associated with it)
- the spawn methods that exist today to start new trips actually move
to trips, partly? the calculation of legs given a high-level spec
from scenario.
Argh, spawn is too convoluted; it does too much stuff right now. Split it out more slowly.
- first, just a scheduler that does CreateCar or the pedestrian thing at some
time, and can retry. it's handed ready-to-go commands.
## Notes on determinism ##
- serde tricks
- unit tests
- https://gafferongames.com/post/deterministic_lockstep/
- https://gafferongames.com/post/floating_point_determinism/
## Modeling choices
Don't model sidewalk crowdedness or bike rack availability, because in
practice, these things are never scarce resources or problematic. Finding a
parking spot is difficult and impacts the quality of one trip and causes
externality, so we should model that.
## Trips
Time to get even more multi-modal / multi-phase!
- all trips begin and end at a building
- spawn peds at a building, make them first traverse the front path.
- could model another type of On
- or, just have a special state in the walking sim, just like the driving sim has a temporary parking/unparking state
- the walking layer shouldnt care about the next layer of the trip. just tell
master sim when a ped has reached a bldg or a parking spot, as desired.
- need to draw the FSM for all of this!
maybe need to organize structs/enums a little...
ParkingSpot
- change this to just lane and spot idx, move other stuff to queries for ParkingSim, make it copyable
CarParking
- rename to ParkedCar
SidewalkSpot
- this should cache lane and distance. :)
## Scenarios
Awkward to turn neighborhoods into buildings/streets; we kind of need the
quadtree and stuff for that, which is the Renderable layer right now.
Originally there was a separate geometry layer, probably for stuff like this.
## Scores
Alright, getting much closer to this being a game! Let's return to the idea of utility functions for agents.
- everyone cares about total trip time
- everyone kind of cares about time spent waiting at intersections
- drivers (anybody using a car for part of their trip)
- easiness of parking... partly this is time spent walking (from start bldg or to goal bldg), and partly time spent driving after initially reaching destination lane
- bikes (woops, not implemented yet :P)
- climbing up hills
- amount of time on busy roads
- dedicated lanes are fine
- even dedicated lanes too close to parking are bad -- stress from possibiliy of being doored
- driving lanes with few actual cars passing are bad
- peds
- hills up OR down
- amount of greenery along route
- amount of amenities like cafes along route
- Seattle greenways had more factors that make a road pleasant or not
Per agent, this score is some kind of a linear combination of factors. Coefficients vary per agent -- some people like hills, don't care about busy roads, etc.
But let's start super simple: just track total trip time for all agents. What's the live UI view we want?
- per population type (peds, drivers), number of pending and completed trips. sum score so far (can use time so far for pending trips)
- note that sum score alone is a bit meaningless, even between population types. need to A/B test to meaningfully compare.
- In headless mode, print scores at the end
- in UI, have an optional OSD to pop up on the right with scores so far
## Edit-Invariant
Back in the day, TurnID went from a number to (Lane, Lane, Intersection), so
that a single edit wouldn't totally throw off all the turn IDs. Now seeing a
similar problem when removing a parking lane -- the same 'seed parked cars'
command has vastly different effects between the same sim, and this tiny change
throws off the RNG and percolates everywhere. Let's think about how to make
more things invariant.
- should cars be the same?
- maybe peds have to travel farther from home to start driving.
- does the car on the far road belong to anyone in the other sim?
- forget debugability for a moment, what do we actually need to compare?
- entire trips! maybe some use a bus or not btwn two worlds
- warp to trip (using the active mode), compare trips
- problem: ped IDs right now differ wildly because the rng gets offset. maybe those should always be chosen first? maybe they should be calculated independently and stuck in the Scenario? that part of a trip is a sim-invariant ID, a spawn time, and a start/goal building. the details (legs of trip) are calculated per sim.
- the RNG is also used after spawning to later roam around for parking
- small road edits shouldnt affect this. per-car rng in an extreme, or maybe just an RNG for sim and for spawning?
- but as soon as two cars have to wander for parking instead of one, everything gets offset completely.
- this will be harder later with spawners on map edges...
Alright, the deviations are still starting too early!
- Swapping shouldn't show different parked cars when the parking lane is present in both sims... or should it?
- If 50% of spots in a neighborhood need to be initially populated, then more or less options for those DOES affect it.
- the problem seems like "seed 50% of parked car spots in this area" is too vague. but theres also a desire that scenarios remain human-readable, high-level.
- Peds are picking very different parked cars in the neighborhood to go drive, causing big deviations early on.
Can we avoid storing trip/parked car mappings in the scenario?
- if peds pick the parked car closest to their start building... maybe
### Idea: forking RNGs
50% of spots filled isn't really accurate -- we're saying for every spot, flip
a coin with some weight. The flips are independent. We can regain an amount of
determinism by forking RNGs -- for every single lane in the map, use the main
RNG to generate a new RNG. Use that new RNG only if it really is a parking
lane.
Cool, much closer to working! Spots are consistently filled out or not. Car
colors are different, because car IDs are different. Making CarIDs consistent
would require changing them to have a stable form -- namely, their original
parking spot (lane ID and an offset). We _could_ do that...
But also wait, ped IDs are a bit different in some cases, and a trip is missing
entirely... huh?
Ah, we call gen_range on different inputs. Not sure why that throws off IDs
though... Can we fork RNG for that too?
Problems:
- CarIDs are different, could make them be original parking spot
= gen_range on different inputs
## Parked cars and ownership
Most cars are associated with a household. They usually park close to that
building (during overnight times). Some cars have no building -- buses, or cars
that might be visiting from outside the simulated area. A building might have
several cars.
Given:
- number of cars that should be around each house
- number of visiting cars
Can then seed parked cars kinda greedily.
But back up, other section.
Alright, now how do peds picking a car work?
- easy case: if the same car is present in both worlds, should use the same
- if cars are associated with buildings in a stable way, this should probably
help or at least push the problem to a slightly more explicit place
- if a parking lane is gone and an agent wouldve used something there, of
course they have to walk farther to get to their car. but we want to somehow
localize these effects. same number of parked cars in the neighborhood and same
assignment to buildings, but maybe some other lane gets more crowded?
Next thoughts:
- scrap the concept of SeedParkedCars. instead say "x% of buildings in this neighborhood have 1 or 2 cars." peds from these buildings will use those specific cars, others wont.
- probability of 0 cars = 40, 1 car = 40, 2 cars = 20 <--- thats much nicer
- go through each building, find a spot to seed that parked car.
- try to be close to building, random jitter to be a little far away
- if this process is somewhat stable, then should be fine. doesnt need to be perfectly stable -- removing lanes somewhere might put more pressure on another lane.
steps for this change:
= give cars an optional building as an owner, make a query for parking sim to find available cars, hook up UI debug
= initially randomly assign a building in the neighborhood
= start seeding parked cars per building instead of per spot
= make sure stability is vaguely preserved
= make peds that'll use a car pick from a house with an available car
= should we even specify this, or should they pick mode at the
beginning based on availability? leaning towards this option since
it's easier to implement right now and seems more natural
## Traces between worlds
Alright, now how do we even compare trip progress to show it visually? This is
kind of a UI-only problem; total score at the end can be compared way more
easily.
- easy case: if both worlds are using the same mode AND route, trace_route the
slower agent with the dist_ahead of the faster agent (difference between the
two)
- alright, have to track current distance traveled to do this. can keep
state per trip leg or something.
- if it's unclear who's closer to the goal, could just pathfind to exactly
where the other agent is, display in a neutral way
- mode changes are potentially weird... but just slide over between sidewalks and driving lanes? mmm...
- strawman: dont use the trace route thing, just have a straight arrow to the
other agent and green/red based on straight-line distance to goal bldg
- progress is non-monotonic -- might walk away from goal to get to car, then get there faster. or maybe get stuck in traffic. so straightline distance to goal is EXPECTED to fluctuate. thats kind of exciting to watch anyway.
Ah, upon seeing just the line between, I know what would make more sense --
show the divergence. The point in the route where one version goes one way, and
the second goes another. Two routes shown, symmetric.
Alright, let's see how performant it is to compare all agents!
## Interactive spawning commands
I no longer imagine a use case for these controls other than debugging /
playing around. It's very unclear how it interacts with the new scenario stuff.
Going to remove these for now.

View File

@ -1,80 +0,0 @@
# Testing-related design notes
## Simulation unit tests
To encourage testing, it should be easy to:
- describe a setup
- assert what the outcome should be
- sometimes just that no runtime invariants are broken
- pop up a UI to interactively step through the test
Some first tests to write:
= car starting with no path on road with no parking spots, ensure they wind up parking at the first spot on some
side street
- car stops for departing car (winds up following it)
- departing car waits for other car (winds up following it)
- a line of cars moving through a stop sign looks jittery right now. correct or not?
- following distances for cars of different lengths
Unclear how to nicely let the test inspect stuff every tick.
Rejected ideas:
- make every pub(crate) so a unit test can reach into state anywhere. It ruins viz for testing.
- JSONify stuff and look at that. too slow, and walking the JSON structure is annoying and not type-safe.
- make one-off accessors for interesting stuff. pollutes code and is tedious.
The idea that's sticking:
- every tick, accumulate a list of events that occurred. publish these from various places.
- most of the events are state transitions -- car leaves lane, intersection accepts ticket, car parks, bus departs
- beyond unit testing, this will be useful for building up a compressed schedule for the time traveler
- and already am kind of using this pattern to communicate between sim managers, spawners, etc
- will help compute trip statistics later
- would also be nice to log some of these
## Watch tests easily
- need to organize savestate captures
- dedicated place: data/savestates/MAP/scenario/time
- plumb map name, scenario name
- should be able to just point to one of these saves, not refer to the map or RNG seed again
- also kinda needed for time traveling later
- when a problem happens, we want to back up a little bit
- probably just need automatic occasional savestating, and to print a nice command to rerun from it
## Diffing for A/B tests
Basic problem: how do we show map edits/diffs?
- could be useful for debugging as new data sources come in
- and is vital part of the game
- UI
- highlight edited things
- hold a button to show the original versions of things in a transparentish overlay
How to show diffs for agents?
## Test setup
Tried having some helper methods, but they're not aging well as the new trip
leg stuff continues. Removing for now, will re-evaluate later.
Synthetic editor is awesome. How could it help test setup?
- could specify things like parking spots directly
- but that may be lots of UI work
- and then a synthetic map is only useful for one test, maybe
- could just label things in the synthetic editor
- and easily look em up in the test setup code
## Test runner
- want to tag tests and run groups of them easily
- have to use prefixes to test names for now
- want nicer runner output
- capture output for failed tests in files
- print summary of each test at the end
- for sim tests, print a command to run it in the UI
Let's experiment and make a new binary crate for all tests!
- it'd be neat to later integrate with travis.

View File

@ -1,29 +0,0 @@
# Time travel
aka reversible simulation. There are two ways to think about implementing this:
- the full thing -- make the entire Sim rewind
- just a UI trick
- conceptually, be able to reproduce the Draw{Car,Ped}Inputs parametrically
- baseline: literally record those inputs every tick and just lookup. huge memory hog.
- the cooler thing: record timelines of agents as the real sim plays and interpolate.
Going to just focus on the UI trick approach for now. To be useful, what things in the UI need to work?
- rendering of course -- show things where they were
- warping to an agent? easy
- showing route of an agent? hard
So we need to disable some plugins while we're in this special mode. I wonder
if each plugin should sort of declare a dependency on a live sim or if at a
higher level, the list of plugins should change?
Some initial steps:
= make a plugin that asks for all Draw stuff every tick and just saves it
= activate the time travel plugin and have keys to go back/forward
= supply the Draw{Car,Ped} stuff from the time travel plugin, not the sim
- objects_onscreen is the main place!
- need to get draw car/ped on lane/turn, so index them that way, maybe implement a trait?
- deactivate lots of other plugins while in this mode
= make sim ctrl a proper plugin
- anything that accesses sim and looks up agents becomes weird!
- interpolate instead of storing everything

View File

@ -1,9 +0,0 @@
# Tradeoffs
## Lane-changing
But now...
- stop sign intersections have a red line whenever there are multiple lanes, because the lanes can conflict with each other
- maybe OK since there's no concept of speed
## Sidewalks: contraflow or not?

View File

@ -1,99 +0,0 @@
# Transit-related design notes
## Bus
Before even looking at the GTFS schema, how should we model buses? They're
basically a special car that just goes from bus stop to bus stop (a
SidewalkSpot) and then just stops for a little while. The route is fixed, but
for now, since pathfinding ignores live traffic, probably fine to ignore this.
- step 1: hardcode two BusStops and hardcode spawn a car that cycles between the two
- render a bus stop on the sidewalk
- this actually belongs to the map layer! associated with a sidewalk I guess.
- render the bus in a special color, and also, make it really long (adjust following dist, but not parking spot len)
- how to unit test that a bus has reached a stop and is waiting? how do we even know that a bus is at a stop for peds to soon board it? I think a transit state will happen soon...
- step 2: make some peds pick a SINGLE bus to use for their route, if it helps
- step 3: make peds load on the bus and get off at the correct stop. make buses usually wait a fixed time at each stop, but wait a littl extra if loading passengers takes a while.
- should walking state own peds waiting for a bus?
- yes: easier drawing, later need to know how crowded a sidewalk is, it's just weird to keep indicating we're at a place. router for cars does this, and the transit sim holds the higher-level state. do the same for now.
- kind of want walking sim to have a list of peds idling at bus stops. transit sim can let all of them know when a bus arrives!
- no: transit sim can also contribute DrawPeds. the walking layer has nothing left to do with them... right?
so:
1) ped reaches bus stop, writes event. walking sim flips a bit so they stop trying to step(). also has a multimap of bus stop -> waiting peds. they continue to exist on their sidewalk for rendering / crowding purposes.
2) transit sim gets a step(). for every bus that's waiting, it queries walking sim to see what peds are there. ??? trip thingy will decide if the ped takes the bus or not, but the ownership transfer of ped from walking to transit happens then.
3) when a bus initially arrives at a stop, it queries all passengers to see who wants to get off and join the walking sim again. the trip thingy decides that.
- step N: load in GTFS for seattle to get real routes and stops
later: multiple transfers, dedicated bus lanes, light rail...
Actually, jump to step 3 and just hardcode a ped to use a route, for now. what should the setup be? hardcode what stop to go to, what route to use, what stop to get off at? trip plan is a sequence...
- walk to a sidewalk POI (bldg, parking spot, bus stop)
- drive somewhere and park
- ride bus route until some stop
for now, these trip sequences can be hardcoded, and planned later.
What's the point of the spawner? It does a few things, and that feels messy:
- vaguely specify a scenario later, with things happening over time.
- except this is unimplemented, and is probably easier to understand as a list of trips with start times
- a way to retry parked->driving car, since it might not have room
- a way to parallelize pathfinding for the ticks that happen to have lots of things spawning
- a way to initially introduce stuff
- asap things like a bus route and parked cars
- indirect orders, like make some parked car start driving creates a trip to satisfy that
- handling transitions to start the next leg of a trip
- this is the part I want to split out! it's very separate from the rest.
step 1: move existing trip stuff to its own spot, but owned by spawner still
step 2: move interactive and testish spawning stuff to init() or similar, leaving spawner as just mechanics and transitions
- spawner shouldnt have rng, right?
- sim needs to hand out its internals (spawner, each model) for the spawning
- separate methods that take sim and call a special method to get direct access to things?
- i just physically want the code in a separate file. can we implement a trait in a second file?
step 3: enhance the trip stuff to have a concept of hardcoded legs, and make it choose how to use a bus
- seed a trip using a bus
- test a basic bus scenario
- make BusStop a lightweight, copyable address
loading GTFS is actually a bit unclear -- both the map layer and the sim layer
have to parse the same GTFS? well, it's like RoadEdits -- we can pass it into
the map constructor and physically just load it once.
routing: https://stackoverflow.com/questions/483488/strategy-to-find-your-best-route-via-public-transportation-only
- how do we indicate that the trip uses a bus stop? how will this actually get used?
- in helpers, start trip from/to bldg maybe using transit. pathfind first using transit, then figure out the sequence of bus stops from the route, and turn that into the trip.
- so ideally it's easy to know (stop1, route, stop2) almost as a step of the path. can translate that into trip legs pretty easily.
- feels like a different interface, especially because the point is to just generate the stuff for the trip manager. throwing away the rest of the pathfinding stuff! hmm. same algorithm doesn't fit well at all.
## Trips
Buses have no trip, and that breaks some UI stuff. What if we assign some dummy trip for transit that never ends?
- Trips currently have a ped associated; would have to change that.
- Would need to invent a TripLeg called ServeRoute that can be the only one.
- Have to filter out buses from score summary. Can detect easily by lack of ped.
This seems actually not bad. Let's do that.
## Peds using transit
- Similar to use_bike, have a param to decide if the ped will consider transit or not.
- For now, cost transit rides as ZERO cost. Need to switch to time estimates otherwise.
The complicated part: should RideBus be a PathStep or not?
- eventually yes, along with many more FSM actions
- but right now, no -- riding the bus needs to be a separate trip leg.
- We just need the first walking part of the trip, and to know what two bus stops to use, and the route.
- other problem: dont know trip legs...
- make/scenario calls spawner to make trips using various modes.
- the trip legs are set up-front there.
- queued command to Walk then pathfinds and would discover we should use transit! can we amend the trip easily at that point?
- if we did this lazily in Command::Walk, then bus transfers / park + take a bus could emerge.
- or do we pathfind in spawn and throw away most of the result (or plumb it along as an optimization)?

View File

@ -1,263 +0,0 @@
# Tutorial mode
## Synthetic maps
For tests and tutorial mode, I totally need the ability to create little
synthetic maps in a UI. Should be different than the main UI.
What are the 'abstract' objects to manipulate?
- Intersections... just points
- Move these, have the roads move too
- Ability to connect two intersections with a straight line road
- Edit lane type list in each direction
- This lets border nodes be created
- Place rectangular buildings
This should basically use raw_data as primitives... or actually, no. GPS would
be weird to work with, and roads should be expressed as the two intersections,
so we don't have to update coordinates when we move intersections.
How to map lanes to stuff that make/lanes.rs will like? Might actually be easy,
actually.
Ideally, would render the abstract thing in one pane, and live-convert to the
full map and display it with the editor code in the other pane. But as the
halloween experiment shows -- that'd require a fair bit more refactoring first.
## Playing the levels
I would say individual tutorial levels could just be running a scenario, except...
- I don't want to hand-write scenario JSON. Code greatly preferred.
- I might want to pop up some dialogs at the beginning / middle / end to explain stuff
- and these dialogs don't belong in the main editor crate
- Need to have listeners inspect sim state to detect when the player is correct/wrong
- All of the editor's controls don't make sense in tutorial mode (defining a new scenario)
I almost want to extract most stuff from the editor crate into a reusable
library, then use it from a new tutorial mode crate and the existing editor. But...
- the editor's current plugin architecture doesnt allow for specifying plugin
interactions, which feels very relevant if these become generic
- some plugins know about primary/secondary state and plugins. do the concepts
of per UI and per map have to be generic too, or is that just for editor?
- maybe 'modes' are collections of mutually compatible plugins?
- it's weird that plugins always exist as singletons, with explicit inactive states. maybe need to have new(input) -> option<plugin> and make event() indicate when the plugin should be destroyed. then a mode is an invocation of these, with its tiny little list of active plugins (boxed or maybe not)
- and a tutorial level is just one of these modes, maybe with some kind of setup() that clobbers the map and sim.
- think of modes as just event handlers! straight line code. the plugin trait is maybe not so useful. can share stuff like toggleable layerlike things by a SUBROUTINE
### Simple workflows
Think about the tutorial mode as a FSM. For now, no live editing while a sim is running.
- first make the user go through some dialogs
- still displaying the map in the background, but no interaction yet
- then be in static explore mode
- can do: canvas movement, debug objects, hide objects, toggle layers,
- can do things that are 'active plugin' and eat keyboard, but should pop to previous state
- display log console, save map edits
a_b_tests.rs diff_worlds.rs layers.rs scenarios.rs steep.rs
chokepoints.rs draw_neighborhoods.rs logs.rs search.rs stop_sign_editor.rs
classification.rs floodfill.rs map_edits.rs show_activity.rs time_travel.rs
color_picker.rs follow.rs mod.rs show_owner.rs traffic_signal_editor.rs
debug_objects.rs geom_validation.rs neighborhood_summary.rs show_route.rs turn_cycler.rs
diff_all.rs hider.rs road_editor.rs sim_controls.rs warp.rs
Gah, bite this off in slow pieces. First find layer-like things... things that
draw/hide stuff. Tend to have very simple activate/deactive controls. No sim interaction.
Or maybe with the exclusive editors...
- in the short term, could make a plugin that just delegates to a smaller list
- except some are per-map and some are not
- wouldnt need that concept if we dont store plugins when
theyre inactive and have a notion of what can simultaneously
be active...
- figure out what other plugins are valid alongside the exclusive editors...
- should activating an editor reset toggleable layers and hidden stuff? that's debug state...
- when is moving via mouse still valid? color picker (right?), neighborhood, road/intersection editors
- intersection and road editors... just debug. and actually, is that even true?
- running a sim / doing time travel shouldnt be valid
debug stuff: toggleable layers, hider, geom validation, floodfill
alright maybe we actually do have lots of exclusive states...
- the exclusive editors
- note that if we're running an A/B test, none of these are valid! cant edit stuff during a/b test... just run.
- explore
- sim controls | time travel
- bunch of nonblocking stuff... chokepoints, classification, debug, diff all, diff trip, floodfill, follow...
- different keys to deactivate them? :P
- some blocking stuff... search, warp. when these're around, run them first
- dir structure... all the exclusive stuff side-by-side, and then a shared/ for stuff that might apply during different states
- the exclusive editors: a_b_tests.rs draw_neighborhoods.rs map_edits.rs scenarios.rs traffic_signal_editor.rs
color_picker.rs road_editor.rs stop_sign_editor.rs
maybe as an initial step, can we get rid of plugins per map vs per UI and just have one or the other?
- every plugin per map?
- toggleable layers then arent shared... fine
- logs is per UI? whoa, that's actually maybe messy!
- sim ctrl, diff world/trip does need to be independent though.
- every plugin per UI?
- when we load a new map from edits, still have to replace the world.
- would have to argue that no plugin that keeps per-map state can run during A/B test mode!
- or rather, that we cant swap while any of those plugins hold state!
- show owner, turn cycler, debug, follow (need to recalculate)
- time travel (needs a/b support generally)
- show route (totally valid to swap while this is going... grrr.)
maybe step 1...
- make a single 'Mode' for exclusive editors
- skip it if a secondary sim is present (aka in A/B mode)
- it lives per UI, because of above condition
- for now, impl it as a hierarchial plugin itself that just delegates
- keep plugin trait for each of em for convenience in edit mode, though.
- each of the editors can stop having inactive state. have new() that returns option
and probably step 2...
- start smaller, a Debug mode... stuff that shouldnt really be relevant in tutorial mode, for example
- chokepoints, classification, floodfill, geom validation, hider, toggleable layers, steep
- arguably some of these could stack, but I don't care much yet... dont worry about ambient plugins yet
- each of the editors can stop having inactive state. have new() that returns option
- the permanent ones (hider and toggleable layers) shouldnt even implement Plugin; theyre custom weirdness
- make a single 'Mode' for normal exploration
- the blocking ones: warp
- the ambient ones: debug objects, follow, neighborhood summary, show activity, show owner, show route, turn cycler
- still represent the inactive state? for now, sure
- have to solve the problem of overlapping keys to quit
- what is search? should it be ambient or not?
- dont forget neighborhood summary
- this has to be completely per UI or completely per map
- let a bunch of plugins run non-exclusively there, as relevant
- AmbientPlugin trait, maybe? or maybe just explicitly call on each field in order
- and still have a single blocking plugin possible, like warp
thursday pick-up:
- overlapping keys to quit stuff...
- cant edit mode when sim is actively running
- where does sim ctrler belong?
and step 3...
- dismantle the plugin abstraction in UI and probably also the trait. do something different for modes.
- clean up event vs new_event
- use Escape to quit most plugins, since it'll only be callable normally from some modes
- make it more clear that keys cant overlap... in each mode, specify the trigger key it uses?
- except some of them are more conditional and that makes overlap fine
- can we get rid of PluginsPerUI almost? since we'll likely get rid of plugins entirely... yeah?
- view and debug mode can coexist!
### Overlapping keys
Is there a way to know that ambient plugins in ViewMode don't use the same keys?
- when we construct them in ViewMode, could pass in a hardcoded key and visually see they're not used too much.
- could get fancier and put all of the keys in a struct, move them out as we create plugins, thereby using the compiler to check. :D
- what if some keys are only usable in some contexts and that's OK?
- what if the plugin uses multiple keys? pass in both, at the expense of losing some readability at the creation site...
### One UI logic to rule em all
Almost done organizing plugins. For the last stretch, I think I need to solve a few related problems...
- Some modes can coexist or not. I want to write a single simple Plugin-like thing to do the delegation, outside of UI.
- this single thing will be different for tutorial mode, but easily pull in common collections of stuff like SimMode and ViewMode.
- this single thing will understand primary/secondary, the state that's per map, and the state that's independent
- what's left in UI? there's so much there right now...
## Listening to sim
Callbacks get so confusing. How about SimMode just exposes the most recent
events, and other things can reach in and query. They're responsible for not
double-counting.
## Overall loop / splash screen
I don't really want the top menu active at all during the splash screen.
Probably have to make each application own this state instead, which I
suspected from early on. :) But UserInput is very entangled with stuff,
probably hard to do right now.
splash screen: logo and author
(in the bg, a map in screensaver mode, just zoomed in some amount bouncing around randomlyish)
- sandbox
- choose map
- mission
- list of curated problems, with description/maybe a picture
- tutorial
- about
- quit
maybe even some more things to guide flow:
- a/b test some edits
- edit mode
as a pause screen, add a - resume option too
and rethinking game modes...
- explore
- simulate
- edit
common functionality:
- search
- warp
- legend
other modes...
- scratchpad: map editing and ad-hoc simulation
- saving map edits
- define/edit neighborhoods, scenarios, missions
- very different controls should be available!
- plugins: manage neighborhoods, manage scenarios
- run an a/b test
- setting up a/b test spec, checking results
- special osd or bar for time, current sim
- work on trip diffing
maybe top menu changes by the mode! some stuff could be common (debug plugins?)
Forget top menu, modal menu, OSD, right-click menus, all the current GUI things. Start over from each mode -- how should it work ideally?
- how do we indicate what major mode we're in and explain how to get out? top menu?
- tutorial mode
- challenge creation mode
- manage neighborhoods and scenarios
- how should the main functions be chosen? load/save neighborhood/scenario
- interactive sandbox mode
- spawn traffic
- maybe choose the spawn tool, then can select a building or intersection or road?
- dont even allow selection of things that dont make sense
- persistent (but maybe hideable?) controls for sim, OSD showing time and agent count and stuff
- map edit mode
- this makes sense as a separate thing, to visualize the edits and make sure to save them
- and to make it clear that there's no mixing with a running sim
- but how fluidly should this be enterable from the sandbox mode?
- replace with OSD with a little summary thing.. "sidewalk of 5th Ave"
- debug mode
- stuff like tooltips, warp, search only belong here... until i make more generally usable navigation tools
persisting anything as modes change is hard to do with the borrow checker. ex: modal menus within the edit mode, soon the core components like map and drawmap. when we're processing the current state, we &mut, but then we want to take ownership of the pieces, which should sorta be safe because we're replacing the overall state. solved this for screensaver because it's an Option, and we can take() it -- replace with None.
- can we just take ownership and return back at the end?

View File

@ -1,117 +0,0 @@
# Walking-related design notes
## Crosswalks
- Turns go from a src to a dst, so we'd need to double them for crosswalks, since they're always bidirectional
- Turn icons might not make sense as a UI?
- Many sidewalks directly connect at corners and shouldn't have anything drawn for them
- We don't want to draw diagonals... just from one side of the road to the other
- We want crosswalks at the beginning AND end of the sidewalk!
- v1: remember other_side for sidewalks too. draw crosswalks at the beginning AND end of every sidewalk lane.
- do extra drawing in DrawIntersection for now, figure out modeling later.
- alright, directional lanes and turns dont fit sidewalks at all. turn icons
are drawn at one end. the turns-in-an-intersection invariant is broken, since
src and dst dont match up for one side.
- could kind of cheat by doubling lanes for sidewalks and making the geometry
overlap, but this feels like a worse hack. it's very tempting to ditch lanes
and turns for a different way to model sidewalks and crosswalks.
- for now, let's make a distinct road and lane abstraction and plumb that all the way through. see what it'd be like to have some more primitives:
- Intersection
- Building
- Road (undirected bundle)
- Driving/biking Lane (directed)
- Sidewalk (undirected)
- Parking lane (no orientation... or, kind of like a driving lane)
- Turn (directed and not)
but the fact that sidewalks are oriented is actually convenient, it makes it clear that incoming's last pt should be glued to outgoing's first pt.
what if we just add a bit and make turns bidirectional? still express them in the directional way?
if we're looking at turns from a road that's a sidewalk, bake in some extra logic?
## Pedestrian modeling
- Is it useful to distinguish CarID and PedestrianID? What about when an agent has a multi-modal trip? Probably become AgentID later.
- Worth mentioning that I'm assuming pedestrians don't queue or collide. In
most reasonable sidewalk cases, this is true. Don't need to model more
detailed movement. As a consequence of this, crosswalk turns never conflict.
Assume people can weave.
## Cost of contraflow
Either duplicate sidewalks in both directions (extra rendering and memory, etc)
or have complicated turns and contraflow logic. Making trace_route is example
of time wasted on contraflow mess. Maybe having two directional sides of a
sidewalk is nice anyway? What about the funky turns causing a ped to not even
cross a sidewalk at all and immediately chain together two turns?
Small complication with two directional sidewalks
- SidewalkSpots get more complicated. are they associated with the
original direction always? How to start/end walking?
- would need to be able to enter/exit a sidewalkspot from
either directional lane. modeling 'left turns' into/out of
sidewalk spots is way overkill.
- do they belong to children {forwards, backwards}? They'd no longer be
in order.
- overlapping geometry is wasteful and makes debugging confusing
- could have two distinct sides of the sidewalk
And what about modeling shared left-turn lanes?
- Are these even that important to model? Usually used for turning into
parking lots or driveways, which we're not modeling at all.
One-way sidewalk lanes would NOT solve the turn-chains:
- think about crossing N, then W at a 4-way. legitimately doing two turns in sequence. and this is fine!
- executing two turns in sequence might be tricky
An alternative:
- in sim, pathfinding, map model trace, etc layers only, using some new
abstraction instead of raw lanes and implied turns
- big point here: why dont pathfinding routes explicitly list turns?
then it's clear when a ped doesn't cross a lane and just does two
turns in sequence
- the code to choose turns is kind of annoyingly repeated in some
places anyway
- this probably makes lookahead-type behavior simpler
- this abstraction can just say whether to go backwards on a sidewalk or not
- whether or not sidewalks later get split into 2 lanes, I think this
would be helpful.
- places to change...
- map model pathfinding.
- proper type, backed by VecDeque
- backrefs can store the intermediate piece often
- complication with not crossing a sidewalk? maybe that can be
deduped there, in one spot
- trace_route should move to become part of this Path type
- no more partly duped code btwn walking/driving
- Traversable::slice can probably also go away, or only get
called by this one place?
- sim layer no longer needs to pick turns
- walking code no longer needs to calculate contraflow itself!
- maybe should plumb start/end dist_along into pathfinding too?
## Crosswalks again, after making peace with contraflow
Two types of things to render
- actual crosswalks
- shared corners
What're the desired turns?
- the crosswalks shouldn't go to the other_side; they should be N<->S
- 3 ways are different
- the shared corners are... obvious
- both of these should be bidirectional
Don't duplicate work with DrawIntersection... let's annotate turn types.
- Crosswalk
- SharedSidewalkCorner
- these 2 exclusively mean sidewalks
- can classify straight/right/left turns too, once. so control layer gets a break.
actually, keep the turn angle stuff independent of sidewalk stuff.

View File

@ -1,9 +0,0 @@
- instant
- filesystem stuff => include_dir
- logging? panics? stacktraces?
- glow / winit differences (window size)
- random
- std::process::exit
- cargo magic
- conditional compilation backend pattern, not traits with one impl at a time
- scoped_threadpool

View File

@ -133,3 +133,7 @@ rust-gdb --args ../target/release/game --dev
## Drawing diagrams
draw.io
## Mapping
xodo on Android for annotating maps in the field

View File

@ -1,26 +0,0 @@
# Desired team
## UX
- How should compatible functionality (like following an agent and showing its
route) work simultaneously? What functions should remain mutually exclusive
(showing chokepoints and editing lanes)?
- How should the modal side menus, bottom status bar, right click menus, top
menu, etc be designed?
- Is it confusing to let people edit a map while a simulation is running?
- How should edits to a map be visualized?
- How should traffic be depicted when unzoomed?
- Please sketch how a traffic signal's current state be displayed, including
time until the next cycle. What about editing a traffic signal?
- How to show A/B test results live as a simulation runs?
- Multiple overlapping pedestrians?
- Redesign how cars look, including turn signals
- How to depict demand data?
- What would you change about the way the map is currently displayed? (Building
types, front paths, intersection colors, stop signs, colors)
If they also code:
- How would you prevent 780b86cb9bea79c4b6a89a286e0bf2171453010d from being
necessary? Is there a way to easily express "hover over a bus, press r, ensure
the plugin is activated"?

View File

@ -43,12 +43,14 @@ impl DebugMode {
.align_right(),
]),
Text::new().draw(ctx).named("current info"),
Checkbox::text(ctx, "show buildings", hotkey(Key::Num1), true),
Checkbox::text(ctx, "show intersections", hotkey(Key::Num2), true),
Checkbox::text(ctx, "show lanes", hotkey(Key::Num3), true),
Checkbox::text(ctx, "show areas", hotkey(Key::Num4), true),
Checkbox::text(ctx, "show labels", hotkey(Key::Num5), false),
Checkbox::text(ctx, "show route for all agents", hotkey(Key::R), false),
Checkbox::text(ctx, "show buildings", hotkey(Key::Num1), true).margin_below(5),
Checkbox::text(ctx, "show intersections", hotkey(Key::Num2), true)
.margin_below(5),
Checkbox::text(ctx, "show lanes", hotkey(Key::Num3), true).margin_below(5),
Checkbox::text(ctx, "show areas", hotkey(Key::Num4), true).margin_below(5),
Checkbox::text(ctx, "show labels", hotkey(Key::Num5), false).margin_below(5),
Checkbox::text(ctx, "show route for all agents", hotkey(Key::R), false)
.margin_below(5),
Widget::col(
vec![
(lctrl(Key::H), "unhide everything"),
@ -61,7 +63,9 @@ impl DebugMode {
(None, "pick a savestate to load"),
]
.into_iter()
.map(|(key, action)| Btn::text_fg(action).build_def(ctx, key))
.map(|(key, action)| {
Btn::text_fg(action).build_def(ctx, key).margin_below(5)
})
.collect(),
),
])