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 text-offset with variable label placement #8642

Merged
merged 8 commits into from
Aug 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 19 additions & 15 deletions src/data/array_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -478,11 +478,11 @@ register('StructArrayLayout2i2ui3ul3ui2f3ub1ul44', StructArrayLayout2i2ui3ul3ui2
* [0]: Int16[6]
* [12]: Uint16[11]
* [36]: Uint32[1]
* [40]: Float32[2]
* [40]: Float32[3]
*
* @private
*/
class StructArrayLayout6i11ui1ul2f48 extends StructArray {
class StructArrayLayout6i11ui1ul3f52 extends StructArray {
uint8: Uint8Array;
int16: Int16Array;
uint16: Uint16Array;
Expand All @@ -497,15 +497,15 @@ class StructArrayLayout6i11ui1ul2f48 extends StructArray {
this.float32 = new Float32Array(this.arrayBuffer);
}

emplaceBack(v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, v16: number, v17: number, v18: number, v19: number) {
emplaceBack(v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, v16: number, v17: number, v18: number, v19: number, v20: number) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19);
return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20);
}

emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, v16: number, v17: number, v18: number, v19: number) {
const o2 = i * 24;
const o4 = i * 12;
emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, v16: number, v17: number, v18: number, v19: number, v20: number) {
const o2 = i * 26;
const o4 = i * 13;
this.int16[o2 + 0] = v0;
this.int16[o2 + 1] = v1;
this.int16[o2 + 2] = v2;
Expand All @@ -526,12 +526,13 @@ class StructArrayLayout6i11ui1ul2f48 extends StructArray {
this.uint32[o4 + 9] = v17;
this.float32[o4 + 10] = v18;
this.float32[o4 + 11] = v19;
this.float32[o4 + 12] = v20;
return i;
}
}

StructArrayLayout6i11ui1ul2f48.prototype.bytesPerElement = 48;
register('StructArrayLayout6i11ui1ul2f48', StructArrayLayout6i11ui1ul2f48);
StructArrayLayout6i11ui1ul3f52.prototype.bytesPerElement = 52;
register('StructArrayLayout6i11ui1ul3f52', StructArrayLayout6i11ui1ul3f52);

/**
* Implementation of the StructArray layout:
Expand Down Expand Up @@ -948,7 +949,8 @@ class SymbolInstanceStruct extends Struct {
numIconVertices: number;
crossTileID: number;
textBoxScale: number;
radialTextOffset: number;
textOffset0: number;
textOffset1: number;
get anchorX() { return this._structArray.int16[this._pos2 + 0]; }
set anchorX(x: number) { this._structArray.int16[this._pos2 + 0] = x; }
get anchorY() { return this._structArray.int16[this._pos2 + 1]; }
Expand Down Expand Up @@ -987,18 +989,20 @@ class SymbolInstanceStruct extends Struct {
set crossTileID(x: number) { this._structArray.uint32[this._pos4 + 9] = x; }
get textBoxScale() { return this._structArray.float32[this._pos4 + 10]; }
set textBoxScale(x: number) { this._structArray.float32[this._pos4 + 10] = x; }
get radialTextOffset() { return this._structArray.float32[this._pos4 + 11]; }
set radialTextOffset(x: number) { this._structArray.float32[this._pos4 + 11] = x; }
get textOffset0() { return this._structArray.float32[this._pos4 + 11]; }
set textOffset0(x: number) { this._structArray.float32[this._pos4 + 11] = x; }
get textOffset1() { return this._structArray.float32[this._pos4 + 12]; }
set textOffset1(x: number) { this._structArray.float32[this._pos4 + 12] = x; }
}

SymbolInstanceStruct.prototype.size = 48;
SymbolInstanceStruct.prototype.size = 52;

export type SymbolInstance = SymbolInstanceStruct;

/**
* @private
*/
export class SymbolInstanceArray extends StructArrayLayout6i11ui1ul2f48 {
export class SymbolInstanceArray extends StructArrayLayout6i11ui1ul3f52 {
/**
* Return the SymbolInstanceStruct at the given location in the array.
* @param {number} index The index of the element.
Expand Down Expand Up @@ -1121,7 +1125,7 @@ export {
StructArrayLayout2i2i2i12,
StructArrayLayout2ub2f12,
StructArrayLayout2i2ui3ul3ui2f3ub1ul44,
StructArrayLayout6i11ui1ul2f48,
StructArrayLayout6i11ui1ul3f52,
StructArrayLayout1f4,
StructArrayLayout3i6,
StructArrayLayout1ul2ui8,
Expand Down
2 changes: 1 addition & 1 deletion src/data/bucket/symbol_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export const symbolInstance = createLayout([
{type: 'Uint16', name: 'numIconVertices'},
{type: 'Uint32', name: 'crossTileID'},
{type: 'Float32', name: 'textBoxScale'},
{type: 'Float32', name: 'radialTextOffset'}
{type: 'Float32', components: 2, name: 'textOffset'}
]);

export const glyphOffset = createLayout([
Expand Down
14 changes: 7 additions & 7 deletions src/render/draw_symbol.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {addDynamicAttributes} from '../data/bucket/symbol_bucket';

import {getAnchorAlignment, WritingMode} from '../symbol/shaping';
import ONE_EM from '../symbol/one_em';
import {evaluateRadialOffset} from '../symbol/symbol_layout';
import {evaluateVariableOffset} from '../symbol/symbol_layout';

import {
symbolIconUniformValues,
Expand Down Expand Up @@ -86,14 +86,14 @@ function drawSymbols(painter: Painter, sourceCache: SourceCache, layer: SymbolSt
}
}

function calculateVariableRenderShift(anchor, width, height, radialOffset, textBoxScale, renderTextSize): Point {
function calculateVariableRenderShift(anchor, width, height, textOffset, textBoxScale, renderTextSize): Point {
const {horizontalAlign, verticalAlign} = getAnchorAlignment(anchor);
const shiftX = -(horizontalAlign - 0.5) * width;
const shiftY = -(verticalAlign - 0.5) * height;
const offset = evaluateRadialOffset(anchor, radialOffset);
const variableOffset = evaluateVariableOffset(anchor, textOffset);
return new Point(
(shiftX / textBoxScale + offset[0]) * renderTextSize,
(shiftY / textBoxScale + offset[1]) * renderTextSize
(shiftX / textBoxScale + variableOffset[0]) * renderTextSize,
(shiftY / textBoxScale + variableOffset[1]) * renderTextSize
);
}

Expand All @@ -120,10 +120,10 @@ function updateVariableAnchors(bucket, rotateWithMap, pitchWithMap, variableOffs
renderTextSize *= bucket.tilePixelRatio / tileScale;
}

const {width, height, radialOffset, textBoxScale} = variableOffset;
const {width, height, anchor, textOffset, textBoxScale} = variableOffset;

const shift = calculateVariableRenderShift(
variableOffset.anchor, width, height, radialOffset, textBoxScale, renderTextSize);
anchor, width, height, textOffset, textBoxScale, renderTextSize);

// Usual case is that we take the projected anchor and add the pixel-based shift
// calculated above. In the (somewhat weird) case of pitch-aligned text, we add an equivalent
Expand Down
9 changes: 3 additions & 6 deletions src/style-spec/reference/v8.json
Original file line number Diff line number Diff line change
Expand Up @@ -1855,7 +1855,7 @@
"type": "number",
"units": "ems",
"default": 0,
"doc": "Radial offset of text, in the direction of the symbol's anchor. Useful in combination with `text-variable-anchor`, which doesn't support the two-dimensional `text-offset`.",
"doc": "Radial offset of text, in the direction of the symbol's anchor. Useful in combination with `text-variable-anchor`, which defaults to using the two-dimensional `text-offset` if present.",
"sdk-support": {
"basic functionality": {
"js": "0.54.0",
Expand Down Expand Up @@ -1922,7 +1922,7 @@
]
}
],
"doc": "To increase the chance of placing high-priority labels on the map, you can provide an array of `text-anchor` locations: the render will attempt to place the label at each location, in order, before moving onto the next label. Use `text-justify: auto` to choose justification based on anchor position. To apply an offset, use the `text-radial-offset` instead of the two-dimensional `text-offset`.",
"doc": "To increase the chance of placing high-priority labels on the map, you can provide an array of `text-anchor` locations: the render will attempt to place the label at each location, in order, before moving onto the next label. Use `text-justify: auto` to choose justification based on anchor position. To apply an offset, use the `text-radial-offset` or the two-dimensional `text-offset`.",
"sdk-support": {
"basic functionality": {
"js": "0.54.0",
Expand Down Expand Up @@ -2214,7 +2214,7 @@
},
"text-offset": {
"type": "array",
"doc": "Offset distance of text from its anchor. Positive values indicate right and down, while negative values indicate left and up.",
"doc": "Offset distance of text from its anchor. Positive values indicate right and down, while negative values indicate left and up. If used with text-variable-anchor, input values will be taken as absolute values. Offsets along the x- and y-axis will be applied automatically based on the anchor position.",
"value": "number",
"units": "ems",
"length": 2,
Expand All @@ -2226,9 +2226,6 @@
"text-field",
{
"!": "text-radial-offset"
},
{
"!": "text-variable-anchor"
}
],
"sdk-support": {
Expand Down
21 changes: 11 additions & 10 deletions src/symbol/placement.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import CollisionIndex from './collision_index';
import EXTENT from '../data/extent';
import * as symbolSize from './symbol_size';
import * as projection from './projection';
import {getAnchorJustification, evaluateRadialOffset} from './symbol_layout';
import {getAnchorJustification, evaluateVariableOffset} from './symbol_layout';
import {getAnchorAlignment, WritingMode} from './shaping';
import assert from 'assert';
import pixelsToTileUnits from '../source/pixels_to_tile_units';
Expand Down Expand Up @@ -117,11 +117,11 @@ class CollisionGroups {
}
}

function calculateVariableLayoutOffset(anchor: TextAnchor, width: number, height: number, radialOffset: number, textBoxScale: number): Point {
function calculateVariableLayoutShift(anchor: TextAnchor, width: number, height: number, textOffset: [number, number], textBoxScale: number): Point {
const {horizontalAlign, verticalAlign} = getAnchorAlignment(anchor);
const shiftX = -(horizontalAlign - 0.5) * width;
const shiftY = -(verticalAlign - 0.5) * height;
const offset = evaluateRadialOffset(anchor, radialOffset);
const offset = evaluateVariableOffset(anchor, textOffset);
return new Point(
shiftX + offset[0] * textBoxScale,
shiftY + offset[1] * textBoxScale
Expand Down Expand Up @@ -149,7 +149,7 @@ function shiftVariableCollisionBox(collisionBox: SingleCollisionBox,
}

export type VariableOffset = {
radialOffset: number,
textOffset: [number, number],
width: number,
height: number,
anchor: TextAnchor,
Expand Down Expand Up @@ -235,11 +235,12 @@ export class Placement {
}

attemptAnchorPlacement(anchor: TextAnchor, textBox: SingleCollisionBox, width: number, height: number,
radialTextOffset: number, textBoxScale: number, rotateWithMap: boolean,
textBoxScale: number, rotateWithMap: boolean,
pitchWithMap: boolean, textPixelRatio: number, posMatrix: mat4, collisionGroup: CollisionGroup,
textAllowOverlap: boolean, symbolInstance: SymbolInstance, bucket: SymbolBucket, orientation: number): ?{ box: Array<number>, offscreen: boolean } {

const shift = calculateVariableLayoutOffset(anchor, width, height, radialTextOffset, textBoxScale);
const textOffset = [symbolInstance.textOffset0, symbolInstance.textOffset1];
const shift = calculateVariableLayoutShift(anchor, width, height, textOffset, textBoxScale);

const placedGlyphBoxes = this.collisionIndex.placeCollisionBox(
shiftVariableCollisionBox(
Expand All @@ -259,7 +260,7 @@ export class Placement {
}
assert(symbolInstance.crossTileID !== 0);
this.variableOffsets[symbolInstance.crossTileID] = {
radialOffset: radialTextOffset,
textOffset,
width,
height,
anchor,
Expand Down Expand Up @@ -426,7 +427,7 @@ export class Placement {
const anchor = anchors[i % anchors.length];
const allowOverlap = (i >= anchors.length);
placedBox = this.attemptAnchorPlacement(
anchor, collisionTextBox, width, height, symbolInstance.radialTextOffset,
anchor, collisionTextBox, width, height,
textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, posMatrix,
collisionGroup, allowOverlap, symbolInstance, bucket, orientation);

Expand Down Expand Up @@ -811,10 +812,10 @@ export class Placement {
// successfully placed position (so you can visualize what collision
// just made the symbol disappear, and the most likely place for the
// symbol to come back)
shift = calculateVariableLayoutOffset(variableOffset.anchor,
shift = calculateVariableLayoutShift(variableOffset.anchor,
variableOffset.width,
variableOffset.height,
variableOffset.radialOffset,
variableOffset.textOffset,
variableOffset.textBoxScale);
if (rotateWithMap) {
shift._rotate(pitchWithMap ? this.transform.angle : -this.transform.angle);
Expand Down
Loading