From d08b4a21baf030ce5923d6b172968406222a651b Mon Sep 17 00:00:00 2001 From: Anand Thakker Date: Wed, 7 Jun 2017 17:38:17 -0400 Subject: [PATCH 1/3] Add regression test for cooperation between text-keep-upright & text-anchor --- .../expected.png | Bin 0 -> 1704 bytes .../style.json | 128 ++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 test/integration/render-tests/text-keep-upright/line-placement-true-text-anchor/expected.png create mode 100644 test/integration/render-tests/text-keep-upright/line-placement-true-text-anchor/style.json diff --git a/test/integration/render-tests/text-keep-upright/line-placement-true-text-anchor/expected.png b/test/integration/render-tests/text-keep-upright/line-placement-true-text-anchor/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..11358a8fb5cafcab387d07261a8d1c2783e8307d GIT binary patch literal 1704 zcmb7F`Bzg{5RTXqi$-{8FpUJ{SpgA(7(hUlmOj9^00s~Om=Y)uK~Rw$mkMoo0ZBaU zphii`c4QGk(IQJF5?V1pSRx2mNJI!rgmM&>D1GrSXy=D}XU_TNyEAj<&ak{aJuq5$ zEfflcAs>b)D3k`GqQ2dviJWIT`Z|zHgA9G=o4jEGe|Z3f(xj`c#Ybs16B!%OOMUNe z4Wa{i1Bs+skJI6?;;=l%3C`@CUs&OVb*ib=6GL0kK*P>&f^5%(9WG{EKOG!qF0S$S z=!&lzjmW=K5HAXs|*YRvu}t7S<+uh>%+g5OuI zJ(av3cN1RL*-6`A*59dPTbg8vfW-F+$ouPBNoUK)>PzT#s1K5gL+(4%ZasaOrl>w| z5Gx9J+I7$h-S>RfejJ`}I0;$jASr-yktC7S(_Egkb^c*1-Ep_)WEIu%iUY|#Zrt#9 z!x`BT)t7C~Roe0fCp{KiaRP>oDccu13I(s_-2$AoZscTSLLiegXQXs4`mS>6K;b*a zA_I#At^*wZ)i-ogtm=@3$$@!4{lHgBR?RxVKU%}!*XA_y?Vo$B5fVi-kayY_f&|6K z6vw1&{(N%8-$HEn%7ChI)Ur0Di+pp4TzT-?k1w}ZJPo7el!~K0urVNSFXFO$Y@?rV zTY0<--#_k>VLfN^pPnrrrK?(2zjRK59S_R$BwdQNl?vV$bXV5zGcWL>)G z?S{2rbe9AiF8}3)+-^pndolOwhOzjZ1(l3BXlao(d?`9Aua(h$aHx9ioq;e)pZO)o zoAQGoehMK|jvt?PnoD3}_=3Dom(QBj-I#Fkc3}*Mz1eweWm^)pY%nyaj^Jmm17PZP zfSf99()*uyA-(!pomYlF#GEpuvr=eFXrGTmw(g^GpDR^{G{WIXla*JJiN#Z6IK@%V zW@)s@;ZOEkQm3+T)_ZV9o<{KL=X8O*GAzg&#hNj3VYw#bGI#Xoqc1XB0?729CcYVn zry<$h&&@TcSDfS8(3UIS6Ytep-ihsLk~>^;t%Ml?SU@ta6}~4wzcso9tvV|$Blrbd zfcTSC$ogS!xWT;vN`lRL2d0=c3WA`F6mA~~K|-@^cqHynsFymUY8{|q`uPbgfVqok zDVbCF1v=k_zcVu%X8OYLNGl7MxvwE3=QBhNP6;>nFd+(0zL2ubjvz(c4ua0lcQw(? z+c_FN%O<{VkZ>=;-%1|&YTG>%wyA`~F1Q{6LMz12i*He13bnHmcFvmbm)HMn?v zO1PMq+U?Y>cc??vI`h=Vx8u!=dq3|1dGvg@$C1NIyUou+9rUJxRr(x$QQK;SDw)`N z-6lHVwnAC=h^7K=5}aMr z+(}aE0vJ9r!-lUS(f8dUkR;P#K@X*CShGZ{F7GjFRJ*@QZ0)aDE}b?&pk->P&`vjV z(*AaSZs)#FQEY{Do Date: Fri, 2 Jun 2017 17:36:52 -0400 Subject: [PATCH 2/3] Quick/dirty fix: apply rotation after text-offset --- src/symbol/quads.js | 57 ++++++++++------- src/symbol/shaping.js | 8 +-- .../line-placement-true-offset/expected.png | Bin 0 -> 3050 bytes .../line-placement-true-offset/style.json | 58 ++++++++++++++++++ 4 files changed, 97 insertions(+), 26 deletions(-) create mode 100644 test/integration/render-tests/text-keep-upright/line-placement-true-offset/expected.png create mode 100644 test/integration/render-tests/text-keep-upright/line-placement-true-offset/style.json diff --git a/src/symbol/quads.js b/src/symbol/quads.js index 62e244bd56c..de61e8df504 100644 --- a/src/symbol/quads.js +++ b/src/symbol/quads.js @@ -153,8 +153,10 @@ function getIconQuads(anchor, shapedIcon, boxScale, line, layer, alongLine, shap */ function getGlyphQuads(anchor, shaping, boxScale, line, layer, alongLine, globalProperties, featureProperties) { + const oneEm = 24; const textRotate = layer.getLayoutValue('text-rotate', globalProperties, featureProperties) * Math.PI / 180; const keepUpright = layer.layout['text-keep-upright']; + const textOffset = layer.getLayoutValue('text-offset', globalProperties, featureProperties).map((t)=> t * oneEm); const positionedGlyphs = shaping.positionedGlyphs; const quads = []; @@ -189,32 +191,19 @@ function getGlyphQuads(anchor, shaping, boxScale, line, layer, alongLine, global }]; } - const x1 = positionedGlyph.x + glyph.left; - const y1 = positionedGlyph.y - glyph.top; - const x2 = x1 + rect.w; - const y2 = y1 + rect.h; - - const center = new Point(positionedGlyph.x, glyph.advance / 2); - - const otl = new Point(x1, y1); - const otr = new Point(x2, y1); - const obl = new Point(x1, y2); - const obr = new Point(x2, y2); - - if (positionedGlyph.angle !== 0) { - otl._sub(center)._rotate(positionedGlyph.angle)._add(center); - otr._sub(center)._rotate(positionedGlyph.angle)._add(center); - obl._sub(center)._rotate(positionedGlyph.angle)._add(center); - obr._sub(center)._rotate(positionedGlyph.angle)._add(center); - } + const baseQuad = { + upright: calculateBaseQuad(positionedGlyph, glyph, rect, textOffset), + upsideDown: calculateBaseQuad(positionedGlyph, glyph, rect, [textOffset[0], -textOffset[1]]) + }; for (let i = 0; i < glyphInstances.length; i++) { const instance = glyphInstances[i]; - let tl = otl, - tr = otr, - bl = obl, - br = obr; + const base = baseQuad[instance.upsideDown ? 'upsideDown' : 'upright']; + let tl = base.tl, + tr = base.tr, + bl = base.bl, + br = base.br; if (textRotate) { const sin = Math.sin(textRotate), @@ -240,6 +229,30 @@ function getGlyphQuads(anchor, shaping, boxScale, line, layer, alongLine, global return quads; } +function calculateBaseQuad(positionedGlyph, glyph, rect, offset) { + const x1 = positionedGlyph.x + glyph.left + offset[0]; + const y1 = positionedGlyph.y - glyph.top + offset[1]; + const x2 = x1 + rect.w; + const y2 = y1 + rect.h; + + const center = new Point(positionedGlyph.x, glyph.advance / 2); + + const tl = new Point(x1, y1); + const tr = new Point(x2, y1); + const bl = new Point(x1, y2); + const br = new Point(x2, y2); + + if (positionedGlyph.angle !== 0) { + tl._sub(center)._rotate(positionedGlyph.angle)._add(center); + tr._sub(center)._rotate(positionedGlyph.angle)._add(center); + bl._sub(center)._rotate(positionedGlyph.angle)._add(center); + br._sub(center)._rotate(positionedGlyph.angle)._add(center); + } + + return { tl, tr, bl, br }; +} + + /** * We can only render glyph quads that slide along a straight line. To draw * curved lines we need an instance of a glyph for each segment it appears on. diff --git a/src/symbol/shaping.js b/src/symbol/shaping.js index 20076ea44bc..74410422ff5 100644 --- a/src/symbol/shaping.js +++ b/src/symbol/shaping.js @@ -275,7 +275,7 @@ function shapeLines(shaping, glyphs, lines, lineHeight, horizontalAlign, vertica y += lineHeight; } - align(positionedGlyphs, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, lines.length, translate); + align(positionedGlyphs, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, lines.length); // Calculate the bounding box const height = lines.length * lineHeight; @@ -299,9 +299,9 @@ function justifyLine(positionedGlyphs, glyphs, start, end, justify) { } } -function align(positionedGlyphs, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, lineCount, translate) { - const shiftX = (justify - horizontalAlign) * maxLineLength + translate[0]; - const shiftY = (-verticalAlign * lineCount + 0.5) * lineHeight + translate[1]; +function align(positionedGlyphs, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, lineCount) { + const shiftX = (justify - horizontalAlign) * maxLineLength; + const shiftY = (-verticalAlign * lineCount + 0.5) * lineHeight; for (let j = 0; j < positionedGlyphs.length; j++) { positionedGlyphs[j].x += shiftX; diff --git a/test/integration/render-tests/text-keep-upright/line-placement-true-offset/expected.png b/test/integration/render-tests/text-keep-upright/line-placement-true-offset/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..3c6dfef6d7d611ea1f152740203fefa4924118a9 GIT binary patch literal 3050 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 literal 0 HcmV?d00001 diff --git a/test/integration/render-tests/text-keep-upright/line-placement-true-offset/style.json b/test/integration/render-tests/text-keep-upright/line-placement-true-offset/style.json new file mode 100644 index 00000000000..b587263ccf1 --- /dev/null +++ b/test/integration/render-tests/text-keep-upright/line-placement-true-offset/style.json @@ -0,0 +1,58 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 64, + "operations": [ + [ "setBearing", 20 ], + [ "wait" ] + ] + } + }, + "center": [ 0, 0 ], + "zoom": 0, + "sources": { + "geojson": { + "type": "geojson", + "data": { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [-20, -20], + [20, -20], + [20, 20], + [-20, 20] + ] + } + } + } + }, + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "layers": [ + { + "id": "guid", + "type": "line", + "source": "geojson" + }, + { + "id": "text", + "type": "symbol", + "source": "geojson", + "layout": { + "symbol-placement": "line", + "symbol-spacing": 3, + "text-allow-overlap": true, + "text-ignore-placement": true, + "text-field": "A", + "text-font": [ + "Open Sans Semibold", + "Arial Unicode MS Bold" + ], + "text-keep-upright": true, + "text-offset": [0, -1] + } + } + ] +} From 5561213656143d01c43fa3b160ab2060883019d6 Mon Sep 17 00:00:00 2001 From: Anand Thakker Date: Fri, 2 Jun 2017 17:49:52 -0400 Subject: [PATCH 3/3] Add explanatory comment --- src/symbol/quads.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/symbol/quads.js b/src/symbol/quads.js index de61e8df504..c1120354d9c 100644 --- a/src/symbol/quads.js +++ b/src/symbol/quads.js @@ -193,6 +193,10 @@ function getGlyphQuads(anchor, shaping, boxScale, line, layer, alongLine, global const baseQuad = { upright: calculateBaseQuad(positionedGlyph, glyph, rect, textOffset), + // The quad coordinates represent an offset from the anchor. Since + // we use the same anchor for both the 'upright' and 'upside-down' + // copies of each glyph, invert the y dimension of text-offset for the + // upside-down case. upsideDown: calculateBaseQuad(positionedGlyph, glyph, rect, [textOffset[0], -textOffset[1]]) };