Skip to content
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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft

Conversation

dabreegster
Copy link
Collaborator

Problem: in larger maps, scaling routes in map-space stops looking reasonable when you zoom far out:
before

Possible solution: thicken polylines and scale up circles as we zoom out:
after

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 a State struct contains this CacheZoomed helper, and to calculate for a new zoom level, we need to borrow all of the fields in the State struct, we might have a double borrow problem, like we have with Cached 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 entire World for different zooms:

world: Cached<f64, World<Obj>>,

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.

…changes.

Again, looks decent... (except for drawing intersection polygons for
some extra detail layers)
@dabreegster
Copy link
Collaborator Author

dabreegster commented Oct 15, 2021

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...

Copy link
Collaborator

@michaelkirk michaelkirk left a 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);
Copy link
Collaborator

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?

@dabreegster
Copy link
Collaborator Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants