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

Enable vertical point label placement via 'text-writing-mode' style property #8399

Merged
merged 24 commits into from
Aug 7, 2019

Conversation

vakila
Copy link

@vakila vakila commented Jun 26, 2019

This PR completes the introduction of the 'text-writing-mode' style property (#8372) and makes use of this property to place point labels vertically if needed.
This is the counterpoint to mapbox/mapbox-gl-native#14932 and is heavily based on those changes.

Notes on the placement algorithm:

  • Vertical labels may only appear if 'text-writing-mode' is set and has 'vertical' as one of its values.
  • Only labels containing at least one character from a range that supports vertical writing (e.g. CJK characters) may be placed vertically.
  • Vertical placement is possible with both fixed- and variable-anchor placement.
  • During placement, we will try all possible orientations (and anchors, if using variable anchors) in the orders of preference described in the style, until we find a usable placement or have exhausted all possibilities without success.

See commit messages for more details about changes.

Launch Checklist

cc @alexshalamov @asheemmamoowala @chloekraw @mapbox/gl-native @mapbox/map-design-team @mapbox/studio

@vakila vakila self-assigned this Jun 26, 2019
@vakila vakila marked this pull request as ready for review June 28, 2019 19:05
@vakila
Copy link
Author

vakila commented Jul 2, 2019

Finally managed to run the benchmarks, screenshot linked in the description above.

Copy link
Contributor

@asheemmamoowala asheemmamoowala left a comment

Choose a reason for hiding this comment

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

I'm not familiar with the symbol placement code, but there's a few nitpicky comments here.

💯 Fo all the render tests!

@@ -319,6 +324,17 @@ class SymbolBucket implements Bucket {
this.sortFeaturesByY = zOrderByViewportY && (layout.get('text-allow-overlap') || layout.get('icon-allow-overlap') ||
layout.get('text-ignore-placement') || layout.get('icon-ignore-placement'));

if (layout.get('symbol-placement') === 'point') {
Copy link
Contributor

Choose a reason for hiding this comment

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

Have you considered doing the duplicate removal during style parsing. That way layer grouping can be done on the de-duped value. As it stands, two layers with all other properties identical, but text-writing-mode set to ['horizontal'] vs ['horizontal', 'horizontal'] will not be grouped.

With that change, the de-duping can also be done for the style, and not for each tile

Copy link
Author

Choose a reason for hiding this comment

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

Like most of this PR I had just copied what @alexshalamov had done in mapbox/mapbox-gl-native#14932, that's why this was done here. I hadn't considered doing it during style parsing (I'm not really familiar with that yet) but what you suggest makes sense to me - I'll look into it.

Copy link
Contributor

Choose a reason for hiding this comment

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

@asheemmamoowala same is applicable for text-variable-anchor, duplicates should be removed before layers are grouped.

Copy link
Contributor

Choose a reason for hiding this comment

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

Captured in #8445

src/style-spec/reference/v8.json Show resolved Hide resolved
src/style-spec/reference/v8.json Outdated Show resolved Hide resolved
src/symbol/quads.js Outdated Show resolved Hide resolved
src/symbol/quads.js Show resolved Hide resolved
src/symbol/symbol_layout.js Outdated Show resolved Hide resolved
yarn.lock Outdated Show resolved Hide resolved
@@ -199,7 +200,8 @@ function shapeText(text: Formatted,
left: translate[0],
right: translate[0],
writingMode,
lineCount: lines.length
lineCount: lines.length,
yOffset: -17 // the y offset *should* be part of the font metadata
Copy link
Contributor

Choose a reason for hiding this comment

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

This is confusing to me. If the comment is suggesting that the yOffset is hard-coded here because it is not currently part of the font metadata, can that be clarified a bit. Is that going to be worked on in a separate ticket?

Copy link
Author

Choose a reason for hiding this comment

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

It's confusing to me too. This comment predates this PR, I just moved it - it looks like it was introduced here, perhaps @ansis can shed some light on the matter?

This comment was marked as resolved.

src/symbol/placement.js Outdated Show resolved Hide resolved
const height = collisionTextBox.y2 - collisionTextBox.y1;
const textBoxScale = symbolInstance.textBoxScale;

let placedBox = { box: null, offscreen: null };
Copy link
Contributor

Choose a reason for hiding this comment

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

The placedBox type is Missing flow type annotation. Probably also good to annotate the return type of attemptAnchorPlacement()

Copy link
Author

Choose a reason for hiding this comment

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

For my own learning as I am still new to Flow:

  • how would this pass Flow tests if some annotation was missing?
  • why would we want to annotate placedBox but not width height etc?
  • If I'm reading correctly, many (most?) of the functions in this module - including attemptAnchorPlacement() - were already missing return value annotations. Should they all be added, and if so does it make sense for that to be part of this PR? Seems like it might make more sense to be addressed separately..

Copy link
Contributor

Choose a reason for hiding this comment

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

how would this pass Flow tests if some annotation was missing?

Flow does not test for missing annotations and cannot test objects/methods that are not annotated. That allows subtle bugs in those areas of code to be introduced without the ability to

why would we want to annotate placedBox but not width height etc?

collisionTextBox is typed as a SingleCollisionBox, implying that width and height will be treated as number types.

If I'm reading correctly, many (most?) of the functions in this module [...] were already missing return value annotations. Should they all be added, and if so does it make sense for that to be part of this PR? Seems like it might make more sense to be addressed separately..

I prefer to add annotations to methods when I make a bunch of changes to them - it allows for learning the contracts of a method, and how it is used. But this can also be tackled in a separate PR - as a larger fix.

Copy link
Contributor

@ansis ansis left a comment

Choose a reason for hiding this comment

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

Overall this looks great to me. Some parts are tricky to understand but I think that's just the area of the code. I don't have any suggestions to make things clearer. I think your decision to stick close to the -native pr is great and will make future adjustments easier to do in both projects.

👍

src/data/bucket/symbol_bucket.js Outdated Show resolved Hide resolved
@@ -199,7 +200,8 @@ function shapeText(text: Formatted,
left: translate[0],
right: translate[0],
writingMode,
lineCount: lines.length
lineCount: lines.length,
yOffset: -17 // the y offset *should* be part of the font metadata

This comment was marked as resolved.

@chloekraw
Copy link
Contributor

@vakila flagging here too that we should wait to merge this until we address the behavior of Latin characters in vertical point labels

cc @asheemmamoowala @ansis

alexshalamov and others added 15 commits July 29, 2019 10:53
…placement

- Add placedOrientation attribute to PlacedSymbol
- Add verticalTextBox attributes to SymbolInstance alongside textBox & iconBox
Port changes from gl-native@alexshalamov_wip_vertical_labels
(shaping.cpp, quads.cpp glyphs.hpp)
- Add writingModes and allowVerticalPlacement properties to SymbolBucket
- Compute writingModes from `text-writing-mode` style property
- Determine glyph dependencies based on whether vertical placement is allowed
- Rotate symbols as needed
- Add updateVerticalLabels to hide glyphs for unused orientations
- Modify updateVariableAnchors to hide unused orientation glyphs
  when using vertical labels and variable anchors
- Attempt to place vertical/horizontal boxes for each symbol
  according to writing mode order of preference
- Integrate with variable-anchor placement to try each
  anchor/orientation combination possible
- Prefer previously placed orientations & anchors
- Hide unplaced orientations
Add yOffset property to shaping objects
@asheemmamoowala
Copy link
Contributor

we should wait to merge this until we address the behavior of Latin characters in vertical point labels

Latin characters will also be verticalized in text-writing-mode:"vertical". That is not implemented in this PR, but can be implemented in a follow up PR.

LGTM!

cc @vakila @chloekraw

This change forces glyphs whose natural orientation in vertical writing
mode is 'sideways' to be rendered in upright orientation (only for non complex
text layouts). This is different compared to W3C / browser behavior that is by
default, renders glyphs in their respective natural orientation.

In the future, there  might need to add a new layout property that would control
glyph orientation separately (e.g., text-glyph-orientation: natural | upright).
@alexshalamov
Copy link
Contributor

Latin characters will also be verticalized in text-writing-mode:"vertical".

@asheemmamoowala could you please take a look at last two commits? Initially I verticalized all characters that do not have upright glyph orientation, but then, shaped Arabic text became unreadable, so I had to exclude it (and whitespace) from verticalization. Not sure if did it correctly and whether I need to exclude other ranges.

@vakila
Copy link
Author

vakila commented Aug 2, 2019

Thanks @alexshalamov for the updates! I think excluding Arabic from verticalization is indeed the right approach 👍 As you mention, we may ultimately want to also exclude other scripts that don't work well vertically, e.g. Hindi? But I will defer to @asheemmamoowala.

Copy link
Contributor

@asheemmamoowala asheemmamoowala left a comment

Choose a reason for hiding this comment

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

@alexshalamov The changes to keep all non-complex shaped characters upright look good to me.

Just one request - could you add a test that verifies that Arabic remains in it's natural orientation?

Copy link
Contributor

@asheemmamoowala asheemmamoowala left a comment

Choose a reason for hiding this comment

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

@alexshalamov Before this is finally committed - it would be good to get another set of benchmark results. The benchmarks work with the existing styles so they won't be testing all the new code paths, but it will at least show the impact on existing code.

@alexshalamov
Copy link
Contributor

alexshalamov commented Aug 4, 2019

@asheemmamoowala

Just one request - could you add a test that verifies that Arabic remains in it's natural orientation?

We have render test for that:

cjk-arabic-vertical-mode

Do you think there is a need to add new test?

Before this is finally committed - it would be good to get another set of benchmark results.

Sure, I will run tests on Monday and post benchmark results.

Benchmarks for vakila-vertical 7d2733f

Copy link
Contributor

@asheemmamoowala asheemmamoowala left a comment

Choose a reason for hiding this comment

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

Do you think there is a need to add new test?

Sorry I missed that test. No additional tests should be needed.

Benchmark results look good. Is the 1+ms change in SymbolLayout expected or an anomaly in this run.

@ansis
Copy link
Contributor

ansis commented Aug 7, 2019

The most recent changes look great.

@ansis ansis merged commit f867842 into master Aug 7, 2019
@ansis ansis deleted the vakila-vertical branch August 7, 2019 17:28
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.

6 participants