Skip to content

Force line label to keep unflipped in defined rotating angle range to suppress flickering #10622

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

Merged
merged 17 commits into from
May 10, 2021

Conversation

zmiao
Copy link
Contributor

@zmiao zmiao commented Apr 27, 2021

Launch Checklist

Fix: #10423

co-worked with @rreusser.

This pr introduced a field flipState for PlacedSymbolStruct, so that the placed line symbol's flipping state could be recorded every time after reprojection. This flip state could be taken as a reference for making further flipping decisions.
Besides, the flip state retains range (+/- 5 degrees around the vertical direction) is introduced, within which the flip decision is taken directly from the symbol's flipState, so that symbol's flipping state could be kept as the same when rotating back and forth around the vertical direction.

Visual effects provided by @rreusser:
label-flipping

  • briefly describe the changes in this PR
  • include before/after visuals or gifs if this PR includes visual changes
  • write tests for all new functionality
  • document any changes to public APIs
  • post benchmark scores
  • manually test the debug page
  • tagged @mapbox/map-design-team @mapbox/static-apis if this PR includes style spec API or visual changes
  • tagged @mapbox/gl-native if this PR includes shader changes or needs a native port
  • apply changelog label ('bug', 'feature', 'docs', etc) or use the label 'skip changelog'
  • add an entry inside this element for inclusion in the mapbox-gl-js changelog: <changelog>When line lablels are inside the flip state retaining range (+/- 5 degrees around the vertical direction), the lables' flip state will be kept the same.</changelog>

Sorry, something went wrong.

@zmiao zmiao self-assigned this Apr 27, 2021
@zmiao zmiao requested a review from astojilj April 28, 2021 07:43
@rreusser
Copy link
Contributor

Going to give this a careful review in just a few minutes, but excited to see a PR for it! 🎉

Regarding whether it needs to be a property or not, my vote would be to hard-code a constant in the vicinity of 3-5 degrees and apply it to all labels. My sense would be that there's probably a number that's small enough that slightly inverted text won't be unpleasant or noticeable, but large enough to dramatically reduce the flipping.

Based on testing while driving, I mostly noticed this happening when I was driving down a long straight road and the angle was probably only ~1-2 degrees past vertical.

@zmiao
Copy link
Contributor Author

zmiao commented Apr 28, 2021

@rreusser thank you for the comments, I fully agree with you that a small angle range would be enough to mitigate the flickering issue for navigation use cases since the zoom level is relatively high.
My biggest concern for the default configuration is would it lead to unexpected effects for the Static API? The original flipped labels may stay unflipped, just like the pictures shown in the description box. cc: @mapbox/static-apis

@rreusser
Copy link
Contributor

rreusser commented Apr 28, 2021

After playing with this a bit, I think we might want a slightly different approach. The benefit is that it solves text flipping when text is near vertical, but the downsides are that 1) static maps would show slightly inverted text, and 2) the flipping threshold is shifted to e.g. +5 degrees and is less noticeable since it doesn't coincide with vertical roads, but is not actually removed.

flipping-text

Based on slack discussion, maybe an alternate approach is a Schmitt trigger. (Think of a thermostat that has a dead zone near the set point and doesn't quickly flicker on/off when the temperature is near the set point.) This would require storing the upright/flipped state on the symbol, and not update it when it's inside a dead zone (vertical +/- a couple degrees). The downside is needing to store that state somewhere, but the upside is that new maps render correctly so that the static api would be unaffected.

@ansis suggested maybe storing this state on OpacityState (or a similar parallel struct for FlipState). @arindam1993 has suggested maybe PlacedSymbolStruct is the place to store this information.

@zmiao
Copy link
Contributor Author

zmiao commented Apr 29, 2021

@rreusser thanks for the suggestion! I will dig deeper and try with your proposal.

@zmiao
Copy link
Contributor Author

zmiao commented May 3, 2021

@rreusser I updated the pr by following the idea of caching the flip stage, but there are 3 failing render tests, I will have to check further for the reasons.

@rreusser
Copy link
Contributor

rreusser commented May 5, 2021

Pardon the delay, @zmiao! I'll give this another look later this morning.

Copy link
Contributor

@rreusser rreusser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great! 😄 Just a couple nits, but nothing significant.

The only extra thing that would be nice is a render test, if possible. The text-max-angle/line-center test was the closest test I could find. Maybe we could use this test as a template and:

  1. render the map with one of the labels inside the retain range
  2. wait (or idle?)
  3. change the bearing until the text is slightly past vertical
  4. wait
  5. if this fix works, the text should be slightly upside down

(Oh, I also have never successfully modified the symbol code before, so I don't have 100% confidence that I'm an appropriate final reviewer, just in case this touches some subtleties like single glyphs or CJK or other things symbols handle.)

@@ -911,6 +914,8 @@ class PlacedSymbolStruct extends Struct {
get crossTileID() { return this._structArray.uint32[this._pos4 + 10]; }
set crossTileID(x: number) { this._structArray.uint32[this._pos4 + 10] = x; }
get associatedIconIndex() { return this._structArray.int16[this._pos2 + 22]; }
get needsFlipping() { return this._structArray.uint8[this._pos1 + 46]; }
set needsFlipping(x: number) { this._structArray.uint8[this._pos1 + 46] = x; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe rename needsFlipping to flipState: undefined | flipRequired | flipNotRequired? I think either is fine, but on my first time through the symbol code, I got a bit confused whether needsFlip indicated a change or whether it indicated a state.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will have to use the uint8 type as {type: 'Uint8', name: 'flipState'} in symbol_attributes.js, so that PlacedSymbolStruct could be regenerated based on this new descriptor.

@zmiao zmiao marked this pull request as ready for review May 6, 2021 18:11
@zmiao zmiao force-pushed the zmiao-line-label-flip branch from 7172b7f to f3f926c Compare May 7, 2021 13:19
Copy link
Contributor

@rreusser rreusser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything looks good to me. Nice tests as well! 👏 👏

Copy link
Contributor

@rreusser rreusser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zmiao I think this is good to go, but I'll mark this "Request changes" due to release schedule.

If it's alright, let's hold off on merging this until after the beta release we're starting today/Monday. I'm really happy to get this fix in place, but I think it's low-priority for web maps compared to native/navigation, and fog is already plenty of a feature to make sure we don't introduce regressions.

@zmiao
Copy link
Contributor Author

zmiao commented May 7, 2021

@rreusser understood, please feel free to merge it later.

@rreusser rreusser merged commit 470b821 into main May 10, 2021
@rreusser rreusser deleted the zmiao-line-label-flip branch May 10, 2021 21:33
@rreusser
Copy link
Contributor

rreusser commented May 10, 2021

Beta is out the door and looks like this is good to go. Merging. Thanks, @zmiao ! 👏 👏

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

Successfully merging this pull request may close these issues.

Labels flip over vertical repeatedly when text-keep-upright is true
2 participants