-
-
Notifications
You must be signed in to change notification settings - Fork 352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exploring designs: thicker lines unzoomed #782
base: main
Are you sure you want to change the base?
Conversation
OK. Ad-hoc code and no good caching yet.
…changes. Again, looks decent... (except for drawing intersection polygons for some extra detail layers)
https://deck.gl/docs/api-reference/layers/line-layer is exactly the effect I'm trying to achieve -- the line is always the same thickness in screen-space. https://github.com/visgl/deck.gl/blob/8.6-release/modules/layers/src/line-layer/line-layer-vertex.glsl.js looks simple enough... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with the possible approaches. I don't really have an opinion on which one makes the most sense right now.
I think the shader approach is the one that will ultimately look best in motion and at rest while having good performance. But it also sounds complex - especially around mouse/hitbox interactions.
The currently proposed changes seem to be only about solving the line-thickness problem, and not other things, like text or texture scaling, right?
Just in case you missed it, Brandon Liu recently posted a nice article about some similar stuff: https://bdon.org/blog/web-map-performance/
@@ -4,12 +4,16 @@ use widgetry::{Color, Drawable, EventCtx, GeomBatch, GfxCtx, Line, TextExt, Widg | |||
|
|||
use crate::app::App; | |||
|
|||
const RADIUS: Distance = Distance::const_meters(10.0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this a radius of? The waypoint markers?
Just recording https://github.com/bbecquet/Leaflet.PolylineOffset. I think the base leaflet line drawing behavior (maybe with this library, maybe without) is good reference. The bus demo is quite nice at all zoom levels. |
Problem: in larger maps, scaling routes in map-space stops looking reasonable when you zoom far out:
Possible solution: thicken polylines and scale up circles as we zoom out:
The first question: does this look better? Is it more usable?
How to implement: ad-hoc + caching
This PR does an ad-hoc approach, discretizing zoom like we do for labels and the bike network layer, then recalculating everything when that zoom changes. The outer-most / application level has to be aware of zoom and trigger recalculating everything. If we want to actually cache drawables and the
World
per discretized zoom instead of just storing one at a time, the code probably gets even grosser.Alt: abstract with something like
ToggledZoomed
ToggleZoomed
hides the fact that one of two things get drawn, based on zoom. App-level code just treats it as something drawable; it's simpler from that perspective.Could we generalize? The performance-intensive but simple answer is to precalculate things for all of the discretized zoom levels upfront, and just switch between them. This might be quite slow; every single time we want to redraw a route as we drag a waypoint around, we would thicken a polyline not just once, but 5 or 10 times! Even though it's unlikely that the zoom level is changing as we drag.
The less simple approach would have some kind of app-level callback to regenerate a
GeomBatch
given a zoom or thickness parameter as input. The lifetimes / borrowing rules might make this tricky. For example, if aState
struct contains thisCacheZoomed
helper, and to calculate for a new zoom level, we need to borrow all of the fields in theState
struct, we might have a double borrow problem, like we have withCached
today sometimes.Another problem with this approach
The second complication is that sometimes we don't want to just recalculate something to draw based on zoom; we also need its hitbox to click on it. The polygon editor recently changed to use the new
World
API, but we scale everything there based on zoom, so we recalculate the entireWorld
for different zooms:abstreet/game/src/devtools/polygon.rs
Line 16 in 7d57deb
If we wanted to stop recalculating everything for different zooms (the ad-hoc + caching approach described earlier, aka, this PR), we might need to teach
World
how to do this natively. Maybe it can handle adjusting the quadtree and drawables as needed.Builder API
The common case is thickening polylines different amounts. Maybe we can make an object that holds onto the polyline, base width, and color, then generates the scaled stuff as needed.
Except it's rarely that simple. In this PR, we also see examples of needing to scale circles and text for the waypoints. And for drawing alternate routes, we need to turn a thickened polyline into an outline/border too. It seems more general-purpose and simple to write a function to draw stuff, given a zoom/thickness.
Alt: Shader?
We just have one shader today that can translate and scale stuff. For drawing in "screen-space", we just temporarily override the parameters for those. Should we start a second shader designed to draw things that have a certain map-space position, but can scale up? Can this work for anything in general (circles, text, thick polylines), or would it just be for polylines? How does it work -- always draw things with a certain size on the screen -- like a polyline with 10 pixels thickness -- but adjusting the position to match the current camera?
I need to find examples of how most maps handle this today. https://mattdesl.svbtle.com/drawing-lines-is-hard "expanding in a vertex shader" looks like a good lead.
Pushing this into the shader doesn't solve everything -- what about hitboxes for clicking the unzoomed route? Maybe we have a new structure, that plays with
World
, that remembers the original polyline and base thickness, projects the cursor onto the nearest line segment, and checks if that distance is within the current threshold.