From ddc3ad723443e3282cb7e1f4f44db80554392734 Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Thu, 15 Aug 2019 19:05:43 +0300 Subject: [PATCH 1/8] Enable `text-offset` with variable label placement This PR allows using of `text-offset` together with `text-variable-anchor`, if `text-radial-offset` is not provided. Assuming we got `text-offset` [x, y] the offset is calculated based on the `text-variable-anchor` values as following: "left": [-x, 0] "right": [x, 0] "top": [0, -y] "bottom": [0, y] "top-left": [-x, -y] "top-right": [x, -y] "bottom-left": [-x, y] "bottom-right": [x, y] We allow only positive `text-offset` values for simplicity. --- src/data/array_types.js | 34 ++--- src/data/bucket/symbol_attributes.js | 2 +- src/symbol/placement.js | 13 +- src/symbol/symbol_layout.js | 123 ++++++++++++------ .../expected.png | Bin 0 -> 608 bytes .../style.json | 121 +++++++++++++++++ .../expected.png | Bin 0 -> 1107 bytes .../style.json | 121 +++++++++++++++++ .../expected.png | Bin 0 -> 2339 bytes .../style.json | 121 +++++++++++++++++ 10 files changed, 474 insertions(+), 61 deletions(-) create mode 100644 test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset-negative/expected.png create mode 100644 test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset-negative/style.json create mode 100644 test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset-zero/expected.png create mode 100644 test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset-zero/style.json create mode 100644 test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset/expected.png create mode 100644 test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset/style.json diff --git a/src/data/array_types.js b/src/data/array_types.js index a63a0bd5575..0c93b343c23 100644 --- a/src/data/array_types.js +++ b/src/data/array_types.js @@ -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; @@ -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; @@ -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: @@ -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]; } @@ -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. @@ -1121,7 +1125,7 @@ export { StructArrayLayout2i2i2i12, StructArrayLayout2ub2f12, StructArrayLayout2i2ui3ul3ui2f3ub1ul44, - StructArrayLayout6i11ui1ul2f48, + StructArrayLayout6i11ui1ul3f52, StructArrayLayout1f4, StructArrayLayout3i6, StructArrayLayout1ul2ui8, diff --git a/src/data/bucket/symbol_attributes.js b/src/data/bucket/symbol_attributes.js index d93a011e0a0..420a7c60bc6 100644 --- a/src/data/bucket/symbol_attributes.js +++ b/src/data/bucket/symbol_attributes.js @@ -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([ diff --git a/src/symbol/placement.js b/src/symbol/placement.js index cf302e4eb94..3dded19cb5e 100644 --- a/src/symbol/placement.js +++ b/src/symbol/placement.js @@ -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; @@ -149,7 +149,7 @@ function shiftVariableCollisionBox(collisionBox: SingleCollisionBox, } export type VariableOffset = { - radialOffset: number, + radialOffset: [number, number], width: number, height: number, anchor: TextAnchor, @@ -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, offscreen: boolean } { - const shift = calculateVariableLayoutOffset(anchor, width, height, radialTextOffset, textBoxScale); + const radialOffset = [symbolInstance.radialTextOffset0, symbolInstance.radialTextOffset1]; + const shift = calculateVariableLayoutOffset(anchor, width, height, radialOffset, textBoxScale); const placedGlyphBoxes = this.collisionIndex.placeCollisionBox( shiftVariableCollisionBox( @@ -259,7 +260,7 @@ export class Placement { } assert(symbolInstance.crossTileID !== 0); this.variableOffsets[symbolInstance.crossTileID] = { - radialOffset: radialTextOffset, + radialOffset, width, height, anchor, @@ -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); diff --git a/src/symbol/symbol_layout.js b/src/symbol/symbol_layout.js index 7be720bc932..9a257a5a7f3 100644 --- a/src/symbol/symbol_layout.js +++ b/src/symbol/symbol_layout.js @@ -64,46 +64,86 @@ 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) { - let x = 0, y = 0; - // solve for r where r^2 + r^2 = radialOffset^2 - const hypotenuse = radialOffset / Math.sqrt(2); +export function evaluateRadialOffset(anchor: TextAnchor, offset: [number, number]) { + + function fromRadialOffset(anchor: TextAnchor, radialOffset: number) { + let x = 0, y = 0; + if (radialOffset < 0) radialOffset = 0; // Ignore negative offset. + // solve for r where r^2 + r^2 = radialOffset^2 + const hypotenuse = radialOffset / Math.sqrt(2); + switch (anchor) { + case 'top-right': + case 'top-left': + y = hypotenuse - baselineOffset; + break; + case 'bottom-right': + case 'bottom-left': + y = -hypotenuse + baselineOffset; + break; + case 'bottom': + y = -radialOffset + baselineOffset; + break; + case 'top': + y = radialOffset - baselineOffset; + break; + } - switch (anchor) { - case 'top-right': - case 'top-left': - y = hypotenuse - baselineOffset; - break; - case 'bottom-right': - case 'bottom-left': - y = -hypotenuse + baselineOffset; - break; - case 'bottom': - y = -radialOffset + baselineOffset; - break; - case 'top': - y = radialOffset - baselineOffset; - break; + switch (anchor) { + case 'top-right': + case 'bottom-right': + x = -hypotenuse; + break; + case 'top-left': + case 'bottom-left': + x = hypotenuse; + break; + case 'left': + x = radialOffset; + break; + case 'right': + x = -radialOffset; + break; + } + + return [x, y]; } - switch (anchor) { - case 'top-right': - case 'bottom-right': - x = -hypotenuse; - break; - case 'top-left': - case 'bottom-left': - x = hypotenuse; - break; - case 'left': - x = radialOffset; - break; - case 'right': - x = -radialOffset; - break; + function fromTextOffset(anchor: TextAnchor, offsetX: number, offsetY: number) { + let x = 0, y = 0; + if (offsetX < 0 || offsetY < 0) { // Ignore negative offset. + offsetX = offsetY = 0; + } + + switch (anchor) { + case 'top-right': + case 'top-left': + case 'top': + y = offsetY - baselineOffset; + break; + case 'bottom-right': + case 'bottom-left': + case 'bottom': + y = -offsetY + baselineOffset; + break; + } + + switch (anchor) { + case 'top-right': + case 'bottom-right': + case 'right': + x = -offsetX; + break; + case 'top-left': + case 'bottom-left': + case 'left': + x = offsetX; + break; + } + + return [x, y]; } - return [x, y]; + return (offset[1] !== Number.POSITIVE_INFINITY) ? fromTextOffset(anchor, offset[0], offset[1]) : fromRadialOffset(anchor, offset[0]); } export function performSymbolLayout(bucket: SymbolBucket, @@ -165,15 +205,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 = 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) { // 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 * ONE_EM, Number.POSITIVE_INFINITY]); } else { textOffset = (layout.get('text-offset').evaluate(feature, {}).map(t => t * ONE_EM): any); } @@ -510,7 +550,11 @@ 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; + let radialTextOffset0 = (layer.layout.get('text-radial-offset').evaluate(feature, {}) || 0) * ONE_EM; + let radialTextOffset1 = Number.POSITIVE_INFINITY; + if (!radialTextOffset0) { + [radialTextOffset0, radialTextOffset1] = (layer.layout.get('text-offset').evaluate(feature, {}).map(t => t * ONE_EM): any); + } if (bucket.allowVerticalPlacement && shapedTextOrientations.vertical) { const textRotation = layer.layout.get('text-rotate').evaluate(feature, {}); @@ -623,7 +667,8 @@ function addSymbol(bucket: SymbolBucket, numIconVertices, 0, textBoxScale, - radialTextOffset); + radialTextOffset0, + radialTextOffset1); } function anchorIsTooClose(bucket: any, text: string, repeatDistance: number, anchor: Point) { diff --git a/test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset-negative/expected.png b/test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset-negative/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..78fdbf390c30c0c539a9c549f10090b466924876 GIT binary patch literal 608 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|GzJDHbx#+^kP61P2MxQ=7cv}p z*#G~0TEfJui&R{vIG$`UkXSn9;*&RCpQHr1H{IY_bNs-vDNkQ?2`$W<aG z??df8b)azr1RClOh$!ETmR++gBtJU4x^&5{h}sTCAHzwLuReWM@^wqqefj9s)mK-( z3jV6nw!zM=Xt!0&+!cCZ?Y|y}ZQWfJcDE-Hs965$>sJ+9m(L2_oR$A|uT{KlKTvem z(pTxFYoe~NT79)w;*^eC$?{qGSG!+DZ548!d9-8N>iD%=We(3waw)!g^Og74wYQ?q zu5!QX#gp81@y+^m;nJ(PuQtER*t)(dRQH&{GKrJRUU_X@y(>O;mDsAYR~xh3P2+3( zKn~sc%4ciH+|_nr+E-7%a@e|g)~fcaUVpv=b!^MpxGUN=@9OnezF*f>h3kf#21VZh cfCKUG%wt>3uU-taNd;N&>FVdQ&MBb@07oORS^xk5 literal 0 HcmV?d00001 diff --git a/test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset-negative/style.json b/test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset-negative/style.json new file mode 100644 index 00000000000..c9601c05e0e --- /dev/null +++ b/test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset-negative/style.json @@ -0,0 +1,121 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 256, + "width": 256 + } + }, + "center": [ 0, 0 ], + "zoom": 0, + "sources": { + "point": { + "type": "geojson", + "data": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + } + ] + } + } + }, + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "layers": [ + { + "id": "text", + "type": "symbol", + "source": "point", + "layout": { + "text-field": "x", + "text-size": 20, + "text-justify": "auto", + "text-variable-anchor": [ + "center", + "top", + "bottom", + "left", + "right", + "top-left", + "top-right", + "bottom-left", + "bottom-right" + ], + "text-offset": [2, -1], + "text-font": [ + "Open Sans Semibold", + "Arial Unicode MS Bold" + ] + } + }, + { + "id": "anchor", + "type": "circle", + "source": "point", + "paint" :{ + "circle-radius": 2 + } + } + ] +} diff --git a/test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset-zero/expected.png b/test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset-zero/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..97fd20e897fff9ca705f568bc11f9f20bdc2b53f GIT binary patch literal 1107 zcmc&z?N3`}94$IPXFwC?h>%HQ%$&&(7wxvNb;+a!35c}4FSRdNs4}^wZne8GE>;YS zETX||L`1NRzzWTC+sj7xbW&(hn@y3S0(+t>vay(yGVNs{;pP~qI zp{V!?IvaD&a$}g27^yDe&`N%>9a&Lw;m|ZE1gG$q*b#BmNN=$QIKoY}3p;ET9Lf}&aC*g6<0oyIn4u&aq?RZuAPa{=50C!?A;*zn~ZIGhb0M}1-^ za|}-4bfjFZ(1dn=w7=UPqFuEnlZgn`{p*PuLwXy3#M<9Or98sKbHZ+ZjwzGcAdKET z@d~+47V>`33ZCH+@*^!W?v3v0YXCuy{&f>&GVJmONNvu_mZ*~!n32p`?@-e(jX;57 z;yK0w1+patO!`0!v;(@K zx%U#`N0<7>KW75JEnI&0Iy-YycWzGCy$rHJFL0qnF(KlqK!7?(nv2hCQ&SzIq2mD6#s zpUkI5MK>eVPhI9N>eY(Z6hF^&P~!wm@cb;1>QOy$^&mx#(r+$L9)<&Q3wMcJLN-tZ!XObss7Vk6ZAFSAUPq!tAUBb#JD|9$u?R>a0*Mtw z7$CH@BXvYfM#QZ|NZ7du-fB5wG|0*2DANPjozhAyiYK1G;G9pLSu<;Wc|WYRp68uk z{f?}3FZW<~3tAc=zwu^v2V=spAKR$ZvW2Mv~dsQFsIrln{s9;pENJB z7%>wrj?OQfmjCeC%2OLQ9yu+er}2ue3?_sa_CI0#`~!K*jPOG1o$t;hT>B+0scU~u zh}yw_VbuWN!pVa| zsq*CIIpH(&EB)z3&&SW{t_>(BY2C8x8V7h31f_wtZi?%4F-=$kyscXs7rSv^F(mK zz1~JHHNaT@K=)Mdqhqh#8#Isz(H^N*Pl^!qh`_&DSQ@<)T!Hd{MvX0WVvMnw(uWmb z;xz6GZ-dnls6K3QK#m41O|SZ?KK>I*U7INJ* z4Bq-55zo8*Z6ha8;rLj^f&S5-NXJK+b|$kSv8sU_c6IL1@+@Qu)}gbtH)OuWvZwv) z`7UBrQON~7ew6-Jxd_GvSc*#GUmdlrkV**PMN^_{RsA!gD{N?SwmIh3yIM<;3UaK9 zASVp*#sGoaZ<=b`YU)^HBNxa4!*a#btYu|t7VPMO`q+9|ckpq_owjYvx?yD~-GgR1 zpg#mnaA=)%h&6n&{stfW+W5g%{XqYf_GnI&VDW_~PNK{F%{f+64ZB}t4ccP|InH9WA0Qc*J500cSUGmIh_Bl!6H|D4B0r|p z%;-FI3EkZ(5ph2;yoOPmZQ1ZZrnbs}O&fm^F6KC=SL^<+bz`5$}Mu=IPs__HrdMx`( znmW;eTDXU*WvKPAb!m(jCJEUIwrGd^l*(v`e6B zNkWe(Fs^11oOh5LVQG_m4SQ$j*A}Mgf_t^WlSPoU>4FUy;=S11JJJQ?V#tLIZ`DBo zwp+FP@&~6GkhYg~?f+nL=FqH2C)0=nj5|uy&BAF6X(UvipFS)Oj}P=ANMFi=hO0kn zD#wW^lcZi(a`C?)>i%hB0>Wb1!XJOWy)oga2!Q^69!J00mWF$2QWUsB(nJ+e`NF`D%iIo-gVlffgH2h z9j15QLp2|)RDd@&O7~eb=79~LF#OYFwxL<(_RO-zc5+f!Ay<)0NA5z=V%1B=DIa$1 zRQ+{AU$==4G!z(nY-Fh@&caTPwD-Yt7#qco_tq54oCD2C!czaYBlYxvg8Y;e(N=OH zEL9$$<8*Ahd~y7gH{Z(b{T7NGSNMror>}z11B9UtK1HmYV$|JYcbNt~D){T9O;~2* zRbB~>l5-XPu5eiWV;54(D1EVivUr;BI?(ZZ#G7;b+Rr`L`v-ZLV;Q zz6J;Obs>(-(}@hxZP;y|TVh9_#=^hR&Bz1Ac6VI12lq`I$Mu01%`s5CSW`Y(2ycd0 z6qsEUSY3YnYvXbJeY=pKrwC7i_$B-?V0Vi^<`W;4(3Mh|pK2z<9J Date: Tue, 20 Aug 2019 11:44:08 +0300 Subject: [PATCH 2/8] Update specification --- src/style-spec/reference/v8.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index bce16fc9948..0a9c58db8de 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -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 otherwise uses the two-dimensional `text-offset`.", "sdk-support": { "basic functionality": { "js": "0.54.0", @@ -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. Note that in combination with `text-variable-anchor` only positive values are allowed and they indicate radial offsets along x- and y-axis accordingly.", "value": "number", "units": "ems", "length": 2, @@ -2226,9 +2226,6 @@ "text-field", { "!": "text-radial-offset" - }, - { - "!": "text-variable-anchor" } ], "sdk-support": { From d803520cf2282bc5dcc3b518e50ad9bf40e94a0a Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Thu, 22 Aug 2019 16:35:46 +0300 Subject: [PATCH 3/8] Enable zero text-radial-offset --- src/symbol/symbol_layout.js | 10 +- .../all-anchors-offset-zero/expected.png | Bin 0 -> 608 bytes .../all-anchors-offset-zero/style.json | 122 ++++++++++++++++++ .../expected.png | Bin 0 -> 2339 bytes .../all-anchors-radial-offset-zero/style.json | 121 +++++++++++++++++ 5 files changed, 250 insertions(+), 3 deletions(-) create mode 100644 test/integration/render-tests/text-variable-anchor/all-anchors-offset-zero/expected.png create mode 100644 test/integration/render-tests/text-variable-anchor/all-anchors-offset-zero/style.json create mode 100644 test/integration/render-tests/text-variable-anchor/all-anchors-radial-offset-zero/expected.png create mode 100644 test/integration/render-tests/text-variable-anchor/all-anchors-radial-offset-zero/style.json diff --git a/src/symbol/symbol_layout.js b/src/symbol/symbol_layout.js index 9a257a5a7f3..912d423874e 100644 --- a/src/symbol/symbol_layout.js +++ b/src/symbol/symbol_layout.js @@ -550,10 +550,14 @@ function addSymbol(bucket: SymbolBucket, let numVerticalGlyphVertices = 0; const placedTextSymbolIndices = {}; let key = murmur3(''); - let radialTextOffset0 = (layer.layout.get('text-radial-offset').evaluate(feature, {}) || 0) * ONE_EM; - let radialTextOffset1 = Number.POSITIVE_INFINITY; - if (!radialTextOffset0) { + + let radialTextOffset0 = 0; + let radialTextOffset1 = 0; + if (layer._unevaluatedLayout.getValue('text-radial-offset') === undefined) { [radialTextOffset0, radialTextOffset1] = (layer.layout.get('text-offset').evaluate(feature, {}).map(t => t * ONE_EM): any); + } else { + radialTextOffset0 = layer.layout.get('text-radial-offset').evaluate(feature, {}) * ONE_EM; + radialTextOffset1 = Number.POSITIVE_INFINITY; } if (bucket.allowVerticalPlacement && shapedTextOrientations.vertical) { diff --git a/test/integration/render-tests/text-variable-anchor/all-anchors-offset-zero/expected.png b/test/integration/render-tests/text-variable-anchor/all-anchors-offset-zero/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..78fdbf390c30c0c539a9c549f10090b466924876 GIT binary patch literal 608 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|GzJDHbx#+^kP61P2MxQ=7cv}p z*#G~0TEfJui&R{vIG$`UkXSn9;*&RCpQHr1H{IY_bNs-vDNkQ?2`$W<aG z??df8b)azr1RClOh$!ETmR++gBtJU4x^&5{h}sTCAHzwLuReWM@^wqqefj9s)mK-( z3jV6nw!zM=Xt!0&+!cCZ?Y|y}ZQWfJcDE-Hs965$>sJ+9m(L2_oR$A|uT{KlKTvem z(pTxFYoe~NT79)w;*^eC$?{qGSG!+DZ548!d9-8N>iD%=We(3waw)!g^Og74wYQ?q zu5!QX#gp81@y+^m;nJ(PuQtER*t)(dRQH&{GKrJRUU_X@y(>O;mDsAYR~xh3P2+3( zKn~sc%4ciH+|_nr+E-7%a@e|g)~fcaUVpv=b!^MpxGUN=@9OnezF*f>h3kf#21VZh cfCKUG%wt>3uU-taNd;N&>FVdQ&MBb@07oORS^xk5 literal 0 HcmV?d00001 diff --git a/test/integration/render-tests/text-variable-anchor/all-anchors-offset-zero/style.json b/test/integration/render-tests/text-variable-anchor/all-anchors-offset-zero/style.json new file mode 100644 index 00000000000..9fa5eb1c0b8 --- /dev/null +++ b/test/integration/render-tests/text-variable-anchor/all-anchors-offset-zero/style.json @@ -0,0 +1,122 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 256, + "width": 256 + } + }, + "center": [ 0, 0 ], + "zoom": 0, + "sources": { + "point": { + "type": "geojson", + "data": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 0, 0 ] + } + } + ] + } + } + }, + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "layers": [ + { + "id": "text", + "type": "symbol", + "source": "point", + "layout": { + "text-field": "x", + "text-size": 20, + "text-justify": "auto", + "text-variable-anchor": [ + "center", + "top", + "bottom", + "left", + "right", + "top-left", + "top-right", + "bottom-left", + "bottom-right" + ], + "text-radial-offset": 0, + "text-offset": [2, 2], + "text-font": [ + "Open Sans Semibold", + "Arial Unicode MS Bold" + ] + } + }, + { + "id": "anchor", + "type": "circle", + "source": "point", + "paint" :{ + "circle-radius": 2 + } + } + ] +} diff --git a/test/integration/render-tests/text-variable-anchor/all-anchors-radial-offset-zero/expected.png b/test/integration/render-tests/text-variable-anchor/all-anchors-radial-offset-zero/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..d3edada11e089349926906bc5700c56d53a1c481 GIT binary patch literal 2339 zcmc(h>r+$L9)<&Q3wMcJLN-tZ!XObss7Vk6ZAFSAUPq!tAUBb#JD|9$u?R>a0*Mtw z7$CH@BXvYfM#QZ|NZ7du-fB5wG|0*2DANPjozhAyiYK1G;G9pLSu<;Wc|WYRp68uk z{f?}3FZW<~3tAc=zwu^v2V=spAKR$ZvW2Mv~dsQFsIrln{s9;pENJB z7%>wrj?OQfmjCeC%2OLQ9yu+er}2ue3?_sa_CI0#`~!K*jPOG1o$t;hT>B+0scU~u zh}yw_VbuWN!pVa| zsq*CIIpH(&EB)z3&&SW{t_>(BY2C8x8V7h31f_wtZi?%4F-=$kyscXs7rSv^F(mK zz1~JHHNaT@K=)Mdqhqh#8#Isz(H^N*Pl^!qh`_&DSQ@<)T!Hd{MvX0WVvMnw(uWmb z;xz6GZ-dnls6K3QK#m41O|SZ?KK>I*U7INJ* z4Bq-55zo8*Z6ha8;rLj^f&S5-NXJK+b|$kSv8sU_c6IL1@+@Qu)}gbtH)OuWvZwv) z`7UBrQON~7ew6-Jxd_GvSc*#GUmdlrkV**PMN^_{RsA!gD{N?SwmIh3yIM<;3UaK9 zASVp*#sGoaZ<=b`YU)^HBNxa4!*a#btYu|t7VPMO`q+9|ckpq_owjYvx?yD~-GgR1 zpg#mnaA=)%h&6n&{stfW+W5g%{XqYf_GnI&VDW_~PNK{F%{f+64ZB}t4ccP|InH9WA0Qc*J500cSUGmIh_Bl!6H|D4B0r|p z%;-FI3EkZ(5ph2;yoOPmZQ1ZZrnbs}O&fm^F6KC=SL^<+bz`5$}Mu=IPs__HrdMx`( znmW;eTDXU*WvKPAb!m(jCJEUIwrGd^l*(v`e6B zNkWe(Fs^11oOh5LVQG_m4SQ$j*A}Mgf_t^WlSPoU>4FUy;=S11JJJQ?V#tLIZ`DBo zwp+FP@&~6GkhYg~?f+nL=FqH2C)0=nj5|uy&BAF6X(UvipFS)Oj}P=ANMFi=hO0kn zD#wW^lcZi(a`C?)>i%hB0>Wb1!XJOWy)oga2!Q^69!J00mWF$2QWUsB(nJ+e`NF`D%iIo-gVlffgH2h z9j15QLp2|)RDd@&O7~eb=79~LF#OYFwxL<(_RO-zc5+f!Ay<)0NA5z=V%1B=DIa$1 zRQ+{AU$==4G!z(nY-Fh@&caTPwD-Yt7#qco_tq54oCD2C!czaYBlYxvg8Y;e(N=OH zEL9$$<8*Ahd~y7gH{Z(b{T7NGSNMror>}z11B9UtK1HmYV$|JYcbNt~D){T9O;~2* zRbB~>l5-XPu5eiWV;54(D1EVivUr;BI?(ZZ#G7;b+Rr`L`v-ZLV;Q zz6J;Obs>(-(}@hxZP;y|TVh9_#=^hR&Bz1Ac6VI12lq`I$Mu01%`s5CSW`Y(2ycd0 z6qsEUSY3YnYvXbJeY=pKrwC7i_$B-?V0Vi^<`W;4(3Mh|pK2z<9J Date: Wed, 28 Aug 2019 10:46:17 +0300 Subject: [PATCH 4/8] Update text-radial-offset description Co-Authored-By: Asheem Mamoowala --- src/style-spec/reference/v8.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index 0a9c58db8de..bc24f38ce0d 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -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 otherwise uses 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", From 3aa5a223ccc11ace204784d59d9aa84286336cd7 Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Wed, 28 Aug 2019 11:14:45 +0300 Subject: [PATCH 5/8] `text-offset` absolute values are used with variable label placement Also, render tests image size was decreased. --- src/symbol/symbol_layout.js | 6 +++--- .../all-anchors-offset-zero/expected.png | Bin 608 -> 411 bytes .../all-anchors-offset-zero/style.json | 4 ++-- .../all-anchors-offset/expected.png | Bin 2457 -> 2064 bytes .../all-anchors-offset/style.json | 4 ++-- .../expected.png | Bin 2339 -> 1972 bytes .../all-anchors-radial-offset-zero/style.json | 4 ++-- .../expected.png | Bin 608 -> 1972 bytes .../style.json | 4 ++-- .../expected.png | Bin 1107 -> 830 bytes .../style.json | 4 ++-- .../expected.png | Bin 2339 -> 1972 bytes .../style.json | 4 ++-- .../text-allow-overlap/expected.png | Bin 1396 -> 1094 bytes .../text-allow-overlap/style.json | 4 ++-- 15 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/symbol/symbol_layout.js b/src/symbol/symbol_layout.js index 912d423874e..635e286599f 100644 --- a/src/symbol/symbol_layout.js +++ b/src/symbol/symbol_layout.js @@ -110,9 +110,9 @@ export function evaluateRadialOffset(anchor: TextAnchor, offset: [number, number function fromTextOffset(anchor: TextAnchor, offsetX: number, offsetY: number) { let x = 0, y = 0; - if (offsetX < 0 || offsetY < 0) { // Ignore negative offset. - offsetX = offsetY = 0; - } + // Use absolute offset values. + offsetX = Math.abs(offsetX); + offsetY = Math.abs(offsetY); switch (anchor) { case 'top-right': diff --git a/test/integration/render-tests/text-variable-anchor/all-anchors-offset-zero/expected.png b/test/integration/render-tests/text-variable-anchor/all-anchors-offset-zero/expected.png index 78fdbf390c30c0c539a9c549f10090b466924876..bed8c64a7aae1c03b839b89d80d94b2ea39496e1 100644 GIT binary patch literal 411 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrU`+CKaSW+oe0y+X_xVJI0}qeC z&ocHbZ=&y!>{ZSszu%N1SU|J~@TwrKUQND`3leQL({ zm__f=1pB;1{do_UHToR;#GGFs^V{GsPw|hw&lPzJlRvI`px&3*TpC_@I3ev=348ao zy$4L!NS-+}Bkbep`(eBpM$1^|92KuQTz2LIM>d<0aG z??df8b)azr1RClOh$!ETmR++gBtJU4x^&5{h}sTCAHzwLuReWM@^wqqefj9s)mK-( z3jV6nw!zM=Xt!0&+!cCZ?Y|y}ZQWfJcDE-Hs965$>sJ+9m(L2_oR$A|uT{KlKTvem z(pTxFYoe~NT79)w;*^eC$?{qGSG!+DZ548!d9-8N>iD%=We(3waw)!g^Og74wYQ?q zu5!QX#gp81@y+^m;nJ(PuQtER*t)(dRQH&{GKrJRUU_X@y(>O;mDsAYR~xh3P2+3( zKn~sc%4ciH+|_nr+E-7%a@e|g)~fcaUVpv=b!^MpxGUN=@9OnezF*f>h3kf#21VZh cfCKUG%wt>3uU-taNd;N&>FVdQ&MBb@07oORS^xk5 diff --git a/test/integration/render-tests/text-variable-anchor/all-anchors-offset-zero/style.json b/test/integration/render-tests/text-variable-anchor/all-anchors-offset-zero/style.json index 9fa5eb1c0b8..73fd84ade25 100644 --- a/test/integration/render-tests/text-variable-anchor/all-anchors-offset-zero/style.json +++ b/test/integration/render-tests/text-variable-anchor/all-anchors-offset-zero/style.json @@ -2,8 +2,8 @@ "version": 8, "metadata": { "test": { - "height": 256, - "width": 256 + "height": 128, + "width": 128 } }, "center": [ 0, 0 ], diff --git a/test/integration/render-tests/text-variable-anchor/all-anchors-offset/expected.png b/test/integration/render-tests/text-variable-anchor/all-anchors-offset/expected.png index a820ca32692e9ff0011dbde8266d61441d1a2a66..784d063b996e0cb6b3edd9a50e550e575c2ad9d7 100644 GIT binary patch literal 2064 zcmZuydpr}09^Y)kW_B9G%qy=fRF+4JEzjn0oLDGtGkLV4N!J~bN9HjzXIXK&$Tl5r zQJV}!GGlY&_VKK!*sMC-oX(u%lpgBp|Ni)WfB$}ekMH+e6@Da8ABKSe008~qpn#*> zz4%+{YHy?LQ1Uncpz|a+z&DZs{9StT=f@BWfHM94gZP`G;$?vkjMic!Dt=fQndL}3 z<>~m#)hVV1v&Sl8P9`J?L8=#kJek_HkEig;;sUut(^ETRK zvV$9H#dxThD8MqfH63m_bTGP>)>)q7DHp=#x`bA*mAVAhCo#q%8&kgQ-cfG>(qJD2 zx*ns=>CE7O`q8@~%;`H|=w7)SZwxDSV+Xn}ULhW*0Fu=5rwm-k9ctUsf zR3B!`BHdD8i*=U*?%kNG6yp(IbdW))tYAKh8Pd_)Thl&5n~$o%G}cJlWq)HwWsGo^ z!D<|*1aF!y0Eej&ZVTQ8mCbmg6f*08i|OA-Be8{EnmD%>-F}U3?tD?Vk~}L2lE=>m z#qc+bl=0_&o-#GUii!@J-Iv_(5%cV)-)BBA&z~4sM{|A#q`e%a3 zHVJ{t)*JFObQ6R$13ohTR8aV)JO(S%YhY_PvXY}&VXb!DW}AtCgI6f99#tVv2v1no zx++O}l0vwFgT4`&`{N7(DZtziUmK!{+UE3djm_Gg8VF1h%8w!_C|$n1DmK-QW( zzp+{^Hm%0($f1KTR462!A&sTUcz8~>7mHjH^1LiRv(31PT%y)^JXR7y1A%YlW@mSD zy{v?~Ns1lL@-R9kQE_5zvInC9J{qQ@1Uqa+hIN{ro?bWz2P@Q^nea)P7~RU?f~ua! zmRn4@sI0?_pgX$>O^Vf65Rn%hU}NxwTp_u!;zqWiem@9vyoFJEtw1lp^J znf?dlp%^Mbv)_bciK%_SpQ2gD&)LAUSZ>?_Re$^N;gw?Y=6++rk!N0}IdnjlJO^EE zD26$8-=}_tIjHjDtM$+hF%NqAXm!){p7Pj0nGs%JHi^`ebiQq1@)^bvV)%U* zZ3PwClMz?vg1kmG_`<|A*7Dos{o5-t(@`IS4@WeV?>ar2##3WEWSH zj(6Ws&^n|TXaBmMY*eoDAfcSpxj5Rk3BF0T{jQ7c>#|&-Eg?pH7amxyWG#8BcA~zC z+GH8Z4zzHTU@8J#fTz|=-kk)$R_m-(9hZ0l!;~~TMg+`YHKt06*9%nJpFUhPA>Tn@ z#w^^%pwxcmwY?hXJ*x4;b>pmnI+WeC#QYVy6tU^>b|i+O^Q0y5QxDvqTrLvm zPi#M2voz8zgv{TNP%LxD+7&bJy#ErDNP5lEBfFh=gCM#dYpU{AA2MOh8%DSIC`3Z~ zEDKwmxr3+HZ6j3}UNZWBqNiU}aea_yIAldK*fSX!yRGtD5gLG}%96kVx25PV4#&>V z=v)kE|M%T2$bS+Umm0P`YrO`4f%BE>T%MxuxJ|y!)- z>ka%fG9eq%qa>e`fhR5}=(Irl*E&I-REMkAcXFGJ-CE`c-37kyI_2ALYKt?z&M=sn zvg^l8W-MKuDmLC7{IV^CEv4`%?ziM^-=e1*8WlT7X(I5(!s?Z=s21p!$l~^YT&q6k zz%Aw04J8n~k*yeY>LoYe!fKf*R^jaesm?}`_}-fj+Ch`E1*h}%gI2tIry;Fcey^#9(n BjdTD2 literal 2457 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 diff --git a/test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset-negative/style.json b/test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset-negative/style.json index c9601c05e0e..5d8de72a017 100644 --- a/test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset-negative/style.json +++ b/test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset-negative/style.json @@ -2,8 +2,8 @@ "version": 8, "metadata": { "test": { - "height": 256, - "width": 256 + "height": 128, + "width": 128 } }, "center": [ 0, 0 ], diff --git a/test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset-zero/expected.png b/test/integration/render-tests/text-variable-anchor/all-anchors-two-dimentional-offset-zero/expected.png index 97fd20e897fff9ca705f568bc11f9f20bdc2b53f..9e71ac16b38fcb2a0b94e1a6f240e3d339088bc8 100644 GIT binary patch literal 830 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrU}p7naSW+oe0$J8<0>ly`-k)2 zbe^$v2e55z?p!+c<&_{!RfDrj90OZKuBxusu*7wW)6MubEqbDp^-|mYDm^-qsth@< zEJ)OimOQy^XIhi7DUaFxxOQ2&dw*=3>YCp_S5_h){0kO6>T{5J2V?!?HM`W-9C>Te zZG0-7^PPh`ij@cTK(h1;r7xXQ5O_Xbhl3g+LU0fBb{ zeGblzXke}oTfLD#)?)pfV;{7yB^u7U*rWP`UHancJ--aZPZ#pm9W>jL%zn?+mr=C$ zO61xptC@8o^V^v-SauJ0RTcZL>9tS;o9wwU?;W6L>V0j|kzhAr(E`qWAu zJ5P4D7fbHrv|q%XKIOh=&tthQ3){p$_Oh8|-?7k}F=v%T`NmZz7e7mSEOlj#Vfe#T zA6NZjA6Tx~<6Z#NXxPvrl#QA&1)jP0qH zUVGYGf5=2%XrCF;DZNp8`P9p9IR`j%j_^wy`y0`rC==q~YH0j{@7Y7+J)K(r4snDf z^7TDdfBKn6?x?}MV}(}=IJ6DrMc+93?$kLdwk1KjY}ND21VJk0aP z&D`zE-yTJt1djHp=SyE4Uw&itJDI)&f#w&^p*znUtbB9GeaC`#3VshH`Vt~s$OC`g b|6|?7x1!bP0l+XkKtdmts literal 1107 zcmc&z?N3`}94$IPXFwC?h>%HQ%$&&(7wxvNb;+a!35c}4FSRdNs4}^wZne8GE>;YS zETX||L`1NRzzWTC+sj7xbW&(hn@y3S0(+t>vay(yGVNs{;pP~qI zp{V!?IvaD&a$}g27^yDe&`N%>9a&Lw;m|ZE1gG$q*b#BmNN=$QIKoY}3p;ET9Lf}&aC*g6<0oyIn4u&aq?RZuAPa{=50C!?A;*zn~ZIGhb0M}1-^ za|}-4bfjFZ(1dn=w7=UPqFuEnlZgn`{p*PuLwXy3#M<9Or98sKbHZ+ZjwzGcAdKET z@d~+47V>`33ZCH+@*^!W?v3v0YXCuy{&f>&GVJmONNvu_mZ*~!n32p`?@-e(jX;57 z;yK0w1+patO!`0!v;(@K zx%U#`N0<7>KW75JEnI&0Iy-YycWzGCy$rHJFL0qnF(KlqK!7?(nv2hCQ&SzIq2mD6#s zpUkI5MK>eVPhI9N>eY(Z6hF^&P~!wm@cb;1>QOy$^&mx#(%)q0@I-2H>pXCN5sy+v=))XaXWYG_)lqcr zO`3OAh1L%|=7(yVcZ*~MXX?6J+ghemyXr0(q8gukr5cr?>ZR$)_iGbysk@F$Pux;d z2p&qz#_B`O-_4>Na$P{WW()lBXi@ZnXqCEQFQz0R`<>(V+xmmS z`P7V=t2QM=M;spa;gC0H)n(O{AOxwHQNOmlR`xyaWkiEU27q+bT}`^W9l=}0BEL5J z^=gjQ;%&>pH-vWSe`c_7aGx>HUigdNKsZ4Zwc+lxKfO5>1xCUNC=PcQ$x2Ad^FGIg4p+TIj=8QYQWZ zE0n-6dpa2{2tm}GjuP1F%l*lD9wiFT=V^L`p5K=1{roYV?_cWK&TL&fQJbSJk~iHbOEPRceXT_9TQ!pBQbMwm2lQDx zXg%4Aps2;a@Agfv|A!3xOzbkHwlmG?%SAW+u+uZimsc7r*DZH4ftgwgs+kz020G7J zdFA@}<+?PoydHi}$}Ia~1}vFzm@o;3EXor((u$M3Ba z{ke41vV%*u$%pqky{GcL5&WGgu~POukq*)Hw}a)zpVK9^!P-gz(6E7r-KAX`5@;^1t4kx5?fxBT( z!S{vj>(B{;N$at5(|0NPx9%NTmt69pHk>e5E-zKfvL#Fy-q&E z?5MxfA#%mpbBB=IZx6sdC!oQ~ZK=EuQ4qcwT{HMtwYa08;gTWn6c+hjjp9pZ^&GHt z-G7hRX$1#-#;|$4wXZCN44E_r{H{BMiT{GwZUVJpfL7@C%e-DK@OKL09E!&S_2qV_ zM-SW={8})UBp~JF8v~KjDaLf1-Lz`8?2ab9t7*eJAg1f*!Td(GeKOYJ{7d4NFCw~; z1JYYr{mOHk^oM4H6XfDJ-{;a~2b7A>K~6lW5+I9J6~(UqC!hYSYTb4!sRTgKqMvv5 zTWl%?1|pyu-j3NoT!_$9)mUZeSGV1)>;3=h_<>7iZn4bKY26ZgkqCm0A2|dk#2CZTwM& z{H8;6L3*!ZSb+sAqhPwM>CKnd2#sGMnqrrPxOHIqP`oMKqK}=F0WUX?1Eud){OIZIJ%6{nDKOerV0v)_Jw0(BSURZWzj9@v5nf|4t z=5>Edx=7K<8(;-Ir`Gp$3>IP(U|0|62F`p1h91C1icv8vkGl-$2*dG8+pfL30QR+F zxqy$ghA&_)|JETi(s=GTs1&12v)Sz%31|{;db)UvR?o*l6AU6Y0e%kW^nwxCF0tZ? z-CZr+$L9)<&Q3wMcJLN-tZ!XObss7Vk6ZAFSAUPq!tAUBb#JD|9$u?R>a0*Mtw z7$CH@BXvYfM#QZ|NZ7du-fB5wG|0*2DANPjozhAyiYK1G;G9pLSu<;Wc|WYRp68uk z{f?}3FZW<~3tAc=zwu^v2V=spAKR$ZvW2Mv~dsQFsIrln{s9;pENJB z7%>wrj?OQfmjCeC%2OLQ9yu+er}2ue3?_sa_CI0#`~!K*jPOG1o$t;hT>B+0scU~u zh}yw_VbuWN!pVa| zsq*CIIpH(&EB)z3&&SW{t_>(BY2C8x8V7h31f_wtZi?%4F-=$kyscXs7rSv^F(mK zz1~JHHNaT@K=)Mdqhqh#8#Isz(H^N*Pl^!qh`_&DSQ@<)T!Hd{MvX0WVvMnw(uWmb z;xz6GZ-dnls6K3QK#m41O|SZ?KK>I*U7INJ* z4Bq-55zo8*Z6ha8;rLj^f&S5-NXJK+b|$kSv8sU_c6IL1@+@Qu)}gbtH)OuWvZwv) z`7UBrQON~7ew6-Jxd_GvSc*#GUmdlrkV**PMN^_{RsA!gD{N?SwmIh3yIM<;3UaK9 zASVp*#sGoaZ<=b`YU)^HBNxa4!*a#btYu|t7VPMO`q+9|ckpq_owjYvx?yD~-GgR1 zpg#mnaA=)%h&6n&{stfW+W5g%{XqYf_GnI&VDW_~PNK{F%{f+64ZB}t4ccP|InH9WA0Qc*J500cSUGmIh_Bl!6H|D4B0r|p z%;-FI3EkZ(5ph2;yoOPmZQ1ZZrnbs}O&fm^F6KC=SL^<+bz`5$}Mu=IPs__HrdMx`( znmW;eTDXU*WvKPAb!m(jCJEUIwrGd^l*(v`e6B zNkWe(Fs^11oOh5LVQG_m4SQ$j*A}Mgf_t^WlSPoU>4FUy;=S11JJJQ?V#tLIZ`DBo zwp+FP@&~6GkhYg~?f+nL=FqH2C)0=nj5|uy&BAF6X(UvipFS)Oj}P=ANMFi=hO0kn zD#wW^lcZi(a`C?)>i%hB0>Wb1!XJOWy)oga2!Q^69!J00mWF$2QWUsB(nJ+e`NF`D%iIo-gVlffgH2h z9j15QLp2|)RDd@&O7~eb=79~LF#OYFwxL<(_RO-zc5+f!Ay<)0NA5z=V%1B=DIa$1 zRQ+{AU$==4G!z(nY-Fh@&caTPwD-Yt7#qco_tq54oCD2C!czaYBlYxvg8Y;e(N=OH zEL9$$<8*Ahd~y7gH{Z(b{T7NGSNMror>}z11B9UtK1HmYV$|JYcbNt~D){T9O;~2* zRbB~>l5-XPu5eiWV;54(D1EVivUr;BI?(ZZ#G7;b+Rr`L`v-ZLV;Q zz6J;Obs>(-(}@hxZP;y|TVh9_#=^hR&Bz1Ac6VI12lq`I$Mu01%`s5CSW`Y(2ycd0 z6qsEUSY3YnYvXbJeY=pKrwC7i_$B-?V0Vi^<`W;4(3Mh|pK2z<9JlaSyQ5QN-ZxBEIp|L6C)MozQ7h8=#7V{*S{##~n_S6@ZxC0Vf9^~3AaX5R*gfW=c zQgE`!ilBLFcREyWbj{5;USZQ*$GUP#y5YNP8)W_;P&#v1ZqHJMshfO`?w!%L|B+sf ztM)EK_SqYP6}3`#irxJo5S}o(zton%Ds{YM#l)-2@Q{#ecDpz)9Svogc{*$d729_IYud!S`id4znFFZft)>kt1t9k-@p`aRb)G(^YKQJ zY}uW<8OLjUj@55u3;tm6?$83MRab6{opDotmZ(^@ktw?8Op(m`MEz@4MSov7nmY#4Xsr9{C3}Ui z{$o!WvlQ8532gh!e%;z2xBKIYhwqN)`5ciDgoDM4f~K9E(Yo=4>79);dc)p?cX&mH zJrav)n*L#B+v5##)036v9bLd=qJ28*%`u*s_Arw~RH?~XS(g`E1ty6thyoHl<0 z-t7xTuRo{+B`WQ{gZ_r%-3FZfiKf3qmjo>$9Ht{kC~C)g}i+So*GGAHd4EuEc@)?=L%cCgHIzmtUowQ z_ICE1^t;n68qp@HbJ+4m)8~TyJ;z**J=4rN%r&QJd$Rhv1O6!?%Tjyt4y-q1H{KzA zfBMCl(+sUx+;6@uouR+jc;d#Hhf*!LXMfNTdz^5mN7hJ6SaP;;;nc|0AB)!YuDb$S-eq5{Q;ki)?$N+hC5lbKQf#<9-NkYz)0tq;||f=PgC=~ zj-{|ZdtA3>`*QOn&TWsx-k=9Mi z`)$XFR^g8d+fFa=N$KD_GQCjF?|}Y?rbzi?timM9U1(?YF`WM z4$1%19A{2btYQm)SUIQ9f5ujc_yf`f!g<^C&5{<^7VED&>}bKheCAiV8ZP4>dU8__ z&O8xd+akG#*}Fj8u6ybZVeto+)%nVOM;}^9XTROc8`1OBLa6?@l+FSE0@bpU4}4Ni zm?dyddzdn(aqSKU={K{lTHINq8Mye)R`}e2jFZ=)OcIl0h_M4XHKQxr-vP6s0PeQYRNu={}fx@<1msf9ODc!Ii?iTwR6THRtw(qkEPyB2?QoFo9_0H zd2>4IA7#|=78e@kO$DaA8##xxKZea|37yd||CsB|1fY<_-R|IzYBEi}7V@)GK`DFN z1IOLAZSAQgYDRA9kBes<;*)7jFKF*M1vs$lfpZvz+e)&8|tg4J=I=JYD@<);T3K0RY`~VXOcE diff --git a/test/integration/render-tests/text-variable-anchor/text-allow-overlap/style.json b/test/integration/render-tests/text-variable-anchor/text-allow-overlap/style.json index 3fe9e73524a..ac5402a1d56 100644 --- a/test/integration/render-tests/text-variable-anchor/text-allow-overlap/style.json +++ b/test/integration/render-tests/text-variable-anchor/text-allow-overlap/style.json @@ -2,8 +2,8 @@ "version": 8, "metadata": { "test": { - "height": 256, - "width": 256 + "height": 128, + "width": 128 } }, "center": [ 0, 0 ], From 36f7a13a0e4791e382b763aefce49014eac9880a Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Wed, 28 Aug 2019 11:27:30 +0300 Subject: [PATCH 6/8] Update text-offset description --- src/style-spec/reference/v8.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index bc24f38ce0d..2853b23c70d 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -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", @@ -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. Note that in combination with `text-variable-anchor` only positive values are allowed and they indicate radial offsets along x- and y-axis accordingly.", + "doc": "Offset distance of text from its anchor. Positive values indicate right and down, while negative values indicate left and up. Note that in combination with `text-variable-anchor` absolute values are used and they indicate radial offsets along x- and y-axis accordingly.", "value": "number", "units": "ems", "length": 2, From 833ccda3b4adaecda5e55fc58d1837962e39bd75 Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Thu, 29 Aug 2019 18:20:53 +0300 Subject: [PATCH 7/8] radialText... rename --- src/data/array_types.js | 12 ++++++------ src/data/bucket/symbol_attributes.js | 2 +- src/render/draw_symbol.js | 14 +++++++------- src/style-spec/reference/v8.json | 2 +- src/symbol/placement.js | 18 +++++++++--------- src/symbol/symbol_layout.js | 18 +++++++++--------- 6 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/data/array_types.js b/src/data/array_types.js index 0c93b343c23..31138acba54 100644 --- a/src/data/array_types.js +++ b/src/data/array_types.js @@ -949,8 +949,8 @@ class SymbolInstanceStruct extends Struct { numIconVertices: number; crossTileID: number; textBoxScale: number; - radialTextOffset0: number; - radialTextOffset1: 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]; } @@ -989,10 +989,10 @@ 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 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; } + 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 = 52; diff --git a/src/data/bucket/symbol_attributes.js b/src/data/bucket/symbol_attributes.js index 420a7c60bc6..bf78d74a098 100644 --- a/src/data/bucket/symbol_attributes.js +++ b/src/data/bucket/symbol_attributes.js @@ -96,7 +96,7 @@ export const symbolInstance = createLayout([ {type: 'Uint16', name: 'numIconVertices'}, {type: 'Uint32', name: 'crossTileID'}, {type: 'Float32', name: 'textBoxScale'}, - {type: 'Float32', components: 2, name: 'radialTextOffset'} + {type: 'Float32', components: 2, name: 'textOffset'} ]); export const glyphOffset = createLayout([ diff --git a/src/render/draw_symbol.js b/src/render/draw_symbol.js index 96618fd5863..e27937449a7 100644 --- a/src/render/draw_symbol.js +++ b/src/render/draw_symbol.js @@ -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, @@ -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 ); } @@ -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 diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index 2853b23c70d..6e998fd095a 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -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. Note that in combination with `text-variable-anchor` absolute values are used and they indicate radial offsets along x- and y-axis accordingly.", + "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, diff --git a/src/symbol/placement.js b/src/symbol/placement.js index 3dded19cb5e..8b9a65216a7 100644 --- a/src/symbol/placement.js +++ b/src/symbol/placement.js @@ -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'; @@ -117,11 +117,11 @@ class CollisionGroups { } } -function calculateVariableLayoutOffset(anchor: TextAnchor, width: number, height: number, radialOffset: [number, 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 @@ -149,7 +149,7 @@ function shiftVariableCollisionBox(collisionBox: SingleCollisionBox, } export type VariableOffset = { - radialOffset: [number, number], + textOffset: [number, number], width: number, height: number, anchor: TextAnchor, @@ -239,8 +239,8 @@ export class Placement { pitchWithMap: boolean, textPixelRatio: number, posMatrix: mat4, collisionGroup: CollisionGroup, textAllowOverlap: boolean, symbolInstance: SymbolInstance, bucket: SymbolBucket, orientation: number): ?{ box: Array, offscreen: boolean } { - const radialOffset = [symbolInstance.radialTextOffset0, symbolInstance.radialTextOffset1]; - const shift = calculateVariableLayoutOffset(anchor, width, height, radialOffset, textBoxScale); + const textOffset = [symbolInstance.textOffset0, symbolInstance.textOffset1]; + const shift = calculateVariableLayoutShift(anchor, width, height, textOffset, textBoxScale); const placedGlyphBoxes = this.collisionIndex.placeCollisionBox( shiftVariableCollisionBox( @@ -260,7 +260,7 @@ export class Placement { } assert(symbolInstance.crossTileID !== 0); this.variableOffsets[symbolInstance.crossTileID] = { - radialOffset, + textOffset, width, height, anchor, @@ -812,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); diff --git a/src/symbol/symbol_layout.js b/src/symbol/symbol_layout.js index 635e286599f..3891454af04 100644 --- a/src/symbol/symbol_layout.js +++ b/src/symbol/symbol_layout.js @@ -64,7 +64,7 @@ export type TextAnchor = 'center' | 'left' | 'right' | 'top' | 'bottom' | 'top-l // (see "yOffset" in shaping.js) const baselineOffset = 7; -export function evaluateRadialOffset(anchor: TextAnchor, offset: [number, number]) { +export function evaluateVariableOffset(anchor: TextAnchor, offset: [number, number]) { function fromRadialOffset(anchor: TextAnchor, radialOffset: number) { let x = 0, y = 0; @@ -213,7 +213,7 @@ export function performSymbolLayout(bucket: SymbolBucket, if (radialOffset) { // 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, Number.POSITIVE_INFINITY]); + textOffset = evaluateVariableOffset(textAnchor, [radialOffset * ONE_EM, Number.POSITIVE_INFINITY]); } else { textOffset = (layout.get('text-offset').evaluate(feature, {}).map(t => t * ONE_EM): any); } @@ -551,13 +551,13 @@ function addSymbol(bucket: SymbolBucket, const placedTextSymbolIndices = {}; let key = murmur3(''); - let radialTextOffset0 = 0; - let radialTextOffset1 = 0; + let textOffset0 = 0; + let textOffset1 = 0; if (layer._unevaluatedLayout.getValue('text-radial-offset') === undefined) { - [radialTextOffset0, radialTextOffset1] = (layer.layout.get('text-offset').evaluate(feature, {}).map(t => t * ONE_EM): any); + [textOffset0, textOffset1] = (layer.layout.get('text-offset').evaluate(feature, {}).map(t => t * ONE_EM): any); } else { - radialTextOffset0 = layer.layout.get('text-radial-offset').evaluate(feature, {}) * ONE_EM; - radialTextOffset1 = Number.POSITIVE_INFINITY; + textOffset0 = layer.layout.get('text-radial-offset').evaluate(feature, {}) * ONE_EM; + textOffset1 = Number.POSITIVE_INFINITY; } if (bucket.allowVerticalPlacement && shapedTextOrientations.vertical) { @@ -671,8 +671,8 @@ function addSymbol(bucket: SymbolBucket, numIconVertices, 0, textBoxScale, - radialTextOffset0, - radialTextOffset1); + textOffset0, + textOffset1); } function anchorIsTooClose(bucket: any, text: string, repeatDistance: number, anchor: Point) { From 36dbda2259af48173276b4f2bdde02c899d0084d Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Thu, 29 Aug 2019 18:57:34 +0300 Subject: [PATCH 8/8] Introduce INVALID_TEXT_OFFSET --- src/symbol/symbol_layout.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/symbol/symbol_layout.js b/src/symbol/symbol_layout.js index 3891454af04..cc1b44bdd99 100644 --- a/src/symbol/symbol_layout.js +++ b/src/symbol/symbol_layout.js @@ -63,6 +63,7 @@ export type TextAnchor = 'center' | 'left' | 'right' | 'top' | 'bottom' | 'top-l // We don't actually load baseline data, but we assume an offset of ONE_EM - 17 // (see "yOffset" in shaping.js) const baselineOffset = 7; +const INVALID_TEXT_OFFSET = Number.POSITIVE_INFINITY; export function evaluateVariableOffset(anchor: TextAnchor, offset: [number, number]) { @@ -143,7 +144,7 @@ export function evaluateVariableOffset(anchor: TextAnchor, offset: [number, numb return [x, y]; } - return (offset[1] !== Number.POSITIVE_INFINITY) ? fromTextOffset(anchor, offset[0], offset[1]) : fromRadialOffset(anchor, offset[0]); + return (offset[1] !== INVALID_TEXT_OFFSET) ? fromTextOffset(anchor, offset[0], offset[1]) : fromRadialOffset(anchor, offset[0]); } export function performSymbolLayout(bucket: SymbolBucket, @@ -213,7 +214,7 @@ export function performSymbolLayout(bucket: SymbolBucket, if (radialOffset) { // 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 = evaluateVariableOffset(textAnchor, [radialOffset * ONE_EM, Number.POSITIVE_INFINITY]); + textOffset = evaluateVariableOffset(textAnchor, [radialOffset * ONE_EM, INVALID_TEXT_OFFSET]); } else { textOffset = (layout.get('text-offset').evaluate(feature, {}).map(t => t * ONE_EM): any); } @@ -557,7 +558,7 @@ function addSymbol(bucket: SymbolBucket, [textOffset0, textOffset1] = (layer.layout.get('text-offset').evaluate(feature, {}).map(t => t * ONE_EM): any); } else { textOffset0 = layer.layout.get('text-radial-offset').evaluate(feature, {}) * ONE_EM; - textOffset1 = Number.POSITIVE_INFINITY; + textOffset1 = INVALID_TEXT_OFFSET; } if (bucket.allowVerticalPlacement && shapedTextOrientations.vertical) {