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

Introduce two component text radial offset #8609

Closed
wants to merge 2 commits into from
Closed
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;
radialTextOffset0: number;
radialTextOffset1: 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 radialTextOffset0() { return this._structArray.float32[this._pos4 + 11]; }
set radialTextOffset0(x: number) { this._structArray.float32[this._pos4 + 11] = x; }
get radialTextOffset1() { return this._structArray.float32[this._pos4 + 12]; }
set radialTextOffset1(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: 'radialTextOffset'}
]);

export const glyphOffset = createLayout([
Expand Down
6 changes: 4 additions & 2 deletions src/style-spec/reference/v8.json
Original file line number Diff line number Diff line change
Expand Up @@ -1852,9 +1852,11 @@
"property-type": "data-driven"
},
"text-radial-offset": {
"type": "number",
"type": "array",
"legacy-type": "number",
"value": "number",
"units": "ems",
"default": 0,
"default": [0, 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`.",
"sdk-support": {
"basic functionality": {
Expand Down
2 changes: 1 addition & 1 deletion src/style-spec/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ export type SymbolLayerSpecification = {|
"text-line-height"?: PropertyValueSpecification<number>,
"text-letter-spacing"?: DataDrivenPropertyValueSpecification<number>,
"text-justify"?: DataDrivenPropertyValueSpecification<"auto" | "left" | "center" | "right">,
"text-radial-offset"?: DataDrivenPropertyValueSpecification<number>,
"text-radial-offset"?: DataDrivenPropertyValueSpecification<Array<number>>,
"text-variable-anchor"?: PropertyValueSpecification<Array<"center" | "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right">>,
"text-anchor"?: DataDrivenPropertyValueSpecification<"center" | "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right">,
"text-max-angle"?: PropertyValueSpecification<number>,
Expand Down
13 changes: 12 additions & 1 deletion src/style-spec/validate/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,18 @@ export default function validate(options) {
return validateExpression(options);

} else if (valueSpec.type && VALIDATORS[valueSpec.type]) {
return VALIDATORS[valueSpec.type](options);
const errors = VALIDATORS[valueSpec.type](options);
alexshalamov marked this conversation as resolved.
Show resolved Hide resolved
if (errors.length) {
const legacyType = valueSpec["legacy-type"];
if (legacyType && VALIDATORS[legacyType]) {
const legacyErrors = VALIDATORS[legacyType](options);
if (legacyErrors.length) {
return errors.concat(legacyErrors);
}
return legacyErrors;
}
}
return errors;

} else {
const valid = validateObject(extend({}, options, {
Expand Down
2 changes: 1 addition & 1 deletion src/style/style_layer/symbol_style_layer_properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export type LayoutProps = {|
"text-line-height": DataConstantProperty<number>,
"text-letter-spacing": DataDrivenProperty<number>,
"text-justify": DataDrivenProperty<"auto" | "left" | "center" | "right">,
"text-radial-offset": DataDrivenProperty<number>,
"text-radial-offset": DataDrivenProperty<Array<number>>,
"text-variable-anchor": DataConstantProperty<Array<"center" | "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right">>,
"text-anchor": DataDrivenProperty<"center" | "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right">,
"text-max-angle": DataConstantProperty<number>,
Expand Down
8 changes: 4 additions & 4 deletions src/symbol/placement.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class CollisionGroups {
}
}

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

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

attemptAnchorPlacement(anchor: TextAnchor, textBox: SingleCollisionBox, width: number, height: number,
radialTextOffset: number, textBoxScale: number, rotateWithMap: boolean,
radialTextOffset: [number, number], 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 } {

Expand Down Expand Up @@ -424,7 +424,7 @@ export class Placement {

for (const anchor of anchors) {
placedBox = this.attemptAnchorPlacement(
anchor, collisionTextBox, width, height, symbolInstance.radialTextOffset,
anchor, collisionTextBox, width, height, [symbolInstance.radialTextOffset0, symbolInstance.radialTextOffset1],
textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, posMatrix,
collisionGroup, textAllowOverlap, symbolInstance, bucket, orientation);

Expand Down
53 changes: 38 additions & 15 deletions src/symbol/symbol_layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,48 +64,69 @@ export type TextAnchor = 'center' | 'left' | 'right' | 'top' | 'bottom' | 'top-l
// (see "yOffset" in shaping.js)
const baselineOffset = 7;

export function evaluateRadialOffset(anchor: TextAnchor, radialOffset: number) {
export function evaluateRadialOffset(anchor: TextAnchor, radialOffset: [number, number]) {
let x = 0, y = 0;
// solve for r where r^2 + r^2 = radialOffset^2
const hypotenuse = radialOffset / Math.sqrt(2);
const sqrt2 = Math.sqrt(2);
const radialOffsetX = radialOffset[0];
const hypotenuseX = radialOffsetX / sqrt2;
const radialOffsetY = radialOffset[1];
const hypotenuseY = radialOffsetY / sqrt2;

switch (anchor) {
case 'top-right':
case 'top-left':
y = hypotenuse - baselineOffset;
y = hypotenuseY - baselineOffset;
break;
case 'bottom-right':
case 'bottom-left':
y = -hypotenuse + baselineOffset;
y = -hypotenuseY + baselineOffset;
break;
case 'bottom':
y = -radialOffset + baselineOffset;
y = -radialOffsetY + baselineOffset;
break;
case 'top':
y = radialOffset - baselineOffset;
y = radialOffsetY - baselineOffset;
break;
}

switch (anchor) {
case 'top-right':
case 'bottom-right':
x = -hypotenuse;
x = -hypotenuseX;
break;
case 'top-left':
case 'bottom-left':
x = hypotenuse;
x = hypotenuseX;
break;
case 'left':
x = radialOffset;
x = radialOffsetX;
break;
case 'right':
x = -radialOffset;
x = -radialOffsetX;
break;
}

return [x, y];
}

function handleRadialTextOffset(value) {
if (!value) return [0, 0];
alexshalamov marked this conversation as resolved.
Show resolved Hide resolved

if (Array.isArray(value)) {
switch (value.length) {
case 0:
return [0, 0];
case 1:
return [value[0] * ONE_EM, value[0] * ONE_EM];
default:
return [value[0] * ONE_EM, value[1] * ONE_EM];
}
}

return [value * ONE_EM, value * ONE_EM];
}

export function performSymbolLayout(bucket: SymbolBucket,
glyphMap: {[string]: {[number]: ?StyleGlyph}},
glyphPositions: {[string]: {[number]: GlyphPosition}},
Expand Down Expand Up @@ -165,15 +186,15 @@ export function performSymbolLayout(bucket: SymbolBucket,

const textAnchor = layout.get('text-anchor').evaluate(feature, {});
const variableTextAnchor = layout.get('text-variable-anchor');
const radialOffset = layout.get('text-radial-offset').evaluate(feature, {});

if (!variableTextAnchor) {
const radialOffset: [number, number] = handleRadialTextOffset(layout.get('text-radial-offset').evaluate(feature, {}));
// Layers with variable anchors use the `text-radial-offset` property and the [x, y] offset vector
// is calculated at placement time instead of layout time
if (radialOffset) {
if (radialOffset[0]) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This will be false when radialOffset[0] == 0, regardless of radialOffset[1]'s value. This check needs to test both x and y.

// The style spec says don't use `text-offset` and `text-radial-offset` together
// but doesn't actually specify what happens if you use both. We go with the radial offset.
textOffset = evaluateRadialOffset(textAnchor, radialOffset * ONE_EM);
textOffset = evaluateRadialOffset(textAnchor, radialOffset);
} else {
textOffset = (layout.get('text-offset').evaluate(feature, {}).map(t => t * ONE_EM): any);
}
Expand Down Expand Up @@ -510,7 +531,8 @@ function addSymbol(bucket: SymbolBucket,
let numVerticalGlyphVertices = 0;
const placedTextSymbolIndices = {};
let key = murmur3('');
const radialTextOffset = (layer.layout.get('text-radial-offset').evaluate(feature, {}) || 0) * ONE_EM;

const radialTextOffset: [number, number] = handleRadialTextOffset(layer.layout.get('text-radial-offset').evaluate(feature, {}));

if (bucket.allowVerticalPlacement && shapedTextOrientations.vertical) {
const textRotation = layer.layout.get('text-rotate').evaluate(feature, {});
Expand Down Expand Up @@ -623,7 +645,8 @@ function addSymbol(bucket: SymbolBucket,
numIconVertices,
0,
textBoxScale,
radialTextOffset);
radialTextOffset[0],
radialTextOffset[1]);
}

function anchorIsTooClose(bucket: any, text: string, repeatDistance: number, anchor: Point) {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading