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

Add support for rendering CJK glyphs top-to-bottom #3402

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
more work on vertical labels
  • Loading branch information
Nicki Dlugash authored and jakepruitt committed Oct 14, 2016
commit 6c8135cb4f1553832540bb8873c1eac89c127557
3 changes: 2 additions & 1 deletion debug/streets-chinese-test.json
Original file line number Diff line number Diff line change
Expand Up @@ -8282,7 +8282,8 @@
"text-padding": 1,
"text-rotation-alignment": "map",
"text-field": "{name}",
"text-letter-spacing": 0.01
"text-letter-spacing": 0.01,
"text-rotate": 0
},
"paint": {
"text-color": "hsl(0, 0%, 0%)",
Expand Down
40 changes: 31 additions & 9 deletions js/data/bucket/symbol_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,12 @@ SymbolBucket.prototype.populateArrays = function(collisionTile, stacks, icons) {
for (var k = 0; k < features.length; k++) {
if (!geometries[k]) continue;

var verticalOrientation = false;

if (textFeatures[k]) {

shapedText = shapeText(textFeatures[k], stacks[fontstack], maxWidth,
lineHeight, horizontalAlign, verticalAlign, justify, spacing, textOffset);
lineHeight, horizontalAlign, verticalAlign, justify, spacing, textOffset, verticalOrientation);
} else {
shapedText = null;
}
Expand All @@ -252,16 +255,26 @@ SymbolBucket.prototype.populateArrays = function(collisionTile, stacks, icons) {
}

if (shapedText || shapedIcon) {
this.addFeature(geometries[k], shapedText, shapedIcon, features[k]);
this.addFeature(geometries[k], shapedText, shapedIcon, features[k], verticalOrientation);
}

// create another shapedText for vertical orientation if needed
if (shapedText && layout['text-rotation-alignment'] === 'map' && layout['symbol-placement'] === 'line') {
verticalOrientation = true;
//console.log("vertical orientation from populate arrays: " + verticalOrientation);
shapedText = shapeText(textFeatures[k], stacks[fontstack], maxWidth,
lineHeight, horizontalAlign, verticalAlign, justify, spacing, textOffset, verticalOrientation);
this.addFeature(geometries[k], shapedText, shapedIcon, features[k], verticalOrientation);
}

}
this.symbolInstancesEndIndex = this.symbolInstancesArray.length;
this.placeFeatures(collisionTile, this.showCollisionBoxes);

this.trimArrays();
};

SymbolBucket.prototype.addFeature = function(lines, shapedText, shapedIcon, feature) {
SymbolBucket.prototype.addFeature = function(lines, shapedText, shapedIcon, feature, verticalOrientation) {
var layout = this.layer.layout;

var glyphSize = 24;
Expand Down Expand Up @@ -324,12 +337,16 @@ SymbolBucket.prototype.addFeature = function(lines, shapedText, shapedIcon, feat
for (var j = 0, len = anchors.length; j < len; j++) {
var anchor = anchors[j];

if (shapedText && isLine) {
if (shapedText && isLine && verticalOrientation===false) {
// ^ for now don't include vertical labels in this check
if (this.anchorIsTooClose(shapedText.text, textRepeatDistance, anchor)) {
continue;
// ^^^ this check is where all the vertical labels are being skipped
}
}

//console.log("vertical orientation from addFeature: " + verticalOrientation);

var inside = !(anchor.x < 0 || anchor.x > EXTENT || anchor.y < 0 || anchor.y > EXTENT);

if (avoidEdges && !inside) continue;
Expand All @@ -344,10 +361,11 @@ SymbolBucket.prototype.addFeature = function(lines, shapedText, shapedIcon, feat
// be drawn across tile boundaries. Instead they need to be included in
// the buffers for both tiles and clipped to tile boundaries at draw time.
var addToBuffers = inside || mayOverlap;

this.addSymbolInstance(anchor, line, shapedText, shapedIcon, this.layer,
addToBuffers, this.symbolInstancesArray.length, this.collisionBoxArray, feature.index, this.sourceLayerIndex, this.index,
textBoxScale, textPadding, textAlongLine,
iconBoxScale, iconPadding, iconAlongLine, {zoom: this.zoom}, feature.properties);
iconBoxScale, iconPadding, iconAlongLine, {zoom: this.zoom}, feature.properties, verticalOrientation);
}
}
};
Expand Down Expand Up @@ -507,6 +525,9 @@ SymbolBucket.prototype.addSymbols = function(programName, quadsStart, quadsEnd,
var a = (symbol.anchorAngle + placementAngle + Math.PI) % (Math.PI * 2);
if (keepUpright && alongLine && (a <= Math.PI / 2 || a > Math.PI * 3 / 2)) continue;

// drop vertical or horizontal orientation of labels
//if (alongLine && (a <= Math.PI / 2 || a > Math.PI * 3 / 2)) continue;

var tl = symbol.tl,
tr = symbol.tr,
bl = symbol.bl,
Expand Down Expand Up @@ -598,12 +619,13 @@ SymbolBucket.prototype.addToDebugBuffers = function(collisionTile) {

SymbolBucket.prototype.addSymbolInstance = function(anchor, line, shapedText, shapedIcon, layer, addToBuffers, index, collisionBoxArray, featureIndex, sourceLayerIndex, bucketIndex,
textBoxScale, textPadding, textAlongLine,
iconBoxScale, iconPadding, iconAlongLine, globalProperties, featureProperties) {
iconBoxScale, iconPadding, iconAlongLine, globalProperties, featureProperties, verticalOrientation) {

var glyphQuadStartIndex, glyphQuadEndIndex, iconQuadStartIndex, iconQuadEndIndex, textCollisionFeature, iconCollisionFeature, glyphQuads, iconQuads;
if (shapedText) {
glyphQuads = addToBuffers ? getGlyphQuads(anchor, shapedText, textBoxScale, line, layer, textAlongLine) : [];
textCollisionFeature = new CollisionFeature(collisionBoxArray, line, anchor, featureIndex, sourceLayerIndex, bucketIndex, shapedText, textBoxScale, textPadding, textAlongLine, false);
//console.log("vertical orientation from addSymbolInstance: " + verticalOrientation);
glyphQuads = addToBuffers ? getGlyphQuads(anchor, shapedText, textBoxScale, line, layer, textAlongLine, verticalOrientation) : [];
textCollisionFeature = new CollisionFeature(collisionBoxArray, line, anchor, featureIndex, sourceLayerIndex, bucketIndex, shapedText, textBoxScale, textPadding, textAlongLine, false, verticalOrientation);
}

glyphQuadStartIndex = this.symbolQuadsArray.length;
Expand All @@ -619,7 +641,7 @@ SymbolBucket.prototype.addSymbolInstance = function(anchor, line, shapedText, sh

if (shapedIcon) {
iconQuads = addToBuffers ? getIconQuads(anchor, shapedIcon, iconBoxScale, line, layer, iconAlongLine, shapedText, globalProperties, featureProperties) : [];
iconCollisionFeature = new CollisionFeature(collisionBoxArray, line, anchor, featureIndex, sourceLayerIndex, bucketIndex, shapedIcon, iconBoxScale, iconPadding, iconAlongLine, true);
iconCollisionFeature = new CollisionFeature(collisionBoxArray, line, anchor, featureIndex, sourceLayerIndex, bucketIndex, shapedIcon, iconBoxScale, iconPadding, iconAlongLine, true, false);
}

iconQuadStartIndex = this.symbolQuadsArray.length;
Expand Down
10 changes: 7 additions & 3 deletions js/symbol/collision_feature.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = CollisionFeature;
*
* @private
*/
function CollisionFeature(collisionBoxArray, line, anchor, featureIndex, sourceLayerIndex, bucketIndex, shaped, boxScale, padding, alignLine, straight) {
function CollisionFeature(collisionBoxArray, line, anchor, featureIndex, sourceLayerIndex, bucketIndex, shaped, boxScale, padding, alignLine, straight, verticalOrientation) {

var y1 = shaped.top * boxScale - padding;
var y2 = shaped.bottom * boxScale + padding;
Expand All @@ -31,8 +31,10 @@ function CollisionFeature(collisionBoxArray, line, anchor, featureIndex, sourceL

if (alignLine) {

var height = y2 - y1;
var length = x2 - x1;
var height = verticalOrientation ? x2 - x1 : y2 - y1;
var length = verticalOrientation ? y2 - y1 : x2 - x1;

if (verticalOrientation) { console.log("length: " + length + ", boxScale: " + boxScale); }

if (height > 0) {
// set minimum box height to avoid very many small labels
Expand Down Expand Up @@ -70,6 +72,8 @@ function CollisionFeature(collisionBoxArray, line, anchor, featureIndex, sourceL
* @private
*/
CollisionFeature.prototype._addLineCollisionBoxes = function(collisionBoxArray, line, anchor, segment, labelLength, boxSize, featureIndex, sourceLayerIndex, bucketIndex) {
console.log("label length: " + labelLength);

var step = boxSize / 2;
var nBoxes = Math.floor(labelLength / step);

Expand Down
18 changes: 9 additions & 9 deletions js/symbol/get_anchors.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@ function getAnchors(line, spacing, maxAngle, shapedText, shapedIcon, glyphSize,
0;

//commenting out horizontal orientation for now
var labelLength = Math.max(
shapedText ? shapedText.right - shapedText.left : 0,
shapedIcon ? shapedIcon.right - shapedIcon.left : 0);
//var labelLength = Math.max(
//shapedText ? shapedText.right - shapedText.left : 0,
//shapedIcon ? shapedIcon.right - shapedIcon.left : 0);

// This is probably wrong
//var labelLength = Math.max(
//shapedText ? shapedText.bottom - shapedText.top : 0,
//shapedIcon ? shapedIcon.bottom - shapedIcon.top : 0);
var labelLength = Math.max(
shapedText ? shapedText.bottom - shapedText.top : 0,
shapedIcon ? shapedIcon.bottom - shapedIcon.top : 0);

if (shapedText) {
//if (shapedText) {
//console.log("labelLength: " + labelLength + " , bottom: " + shapedText.bottom + " , top: " + shapedText.top);
console.log("labelLength: " + labelLength + " , right: " + shapedText.right + " , left: " + shapedText.left);
}
//console.log("labelLength: " + labelLength + " , right: " + shapedText.right + " , left: " + shapedText.left);
//}

// Is the line continued from outside the tile boundary?
var isLineContinued = line[0].x === 0 || line[0].x === tileExtent || line[0].y === 0 || line[0].y === tileExtent;
Expand Down
15 changes: 14 additions & 1 deletion js/symbol/quads.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ function getIconQuads(anchor, shapedIcon, boxScale, line, layer, alongLine, shap
* @returns {Array<SymbolQuad>}
* @private
*/
function getGlyphQuads(anchor, shaping, boxScale, line, layer, alongLine) {
function getGlyphQuads(anchor, shaping, boxScale, line, layer, alongLine, verticalOrientation) {

var textRotate = layer.layout['text-rotate'] * Math.PI / 180;
var keepUpright = layer.layout['text-keep-upright'];
Expand Down Expand Up @@ -189,6 +189,19 @@ function getGlyphQuads(anchor, shaping, boxScale, line, layer, alongLine) {
bl = obl,
br = obr;

// vertical orientation
console.log("vertical orientation from getGlyphQuads: " + verticalOrientation);
if (verticalOrientation) {
var sin = Math.sin(-Math.PI / 2),
cos = Math.cos(-Math.PI / 2),
matrix = [cos, -sin, sin, cos];

tl = tl.matMult(matrix);
tr = tr.matMult(matrix);
bl = bl.matMult(matrix);
br = br.matMult(matrix);
}

if (textRotate) {
var sin = Math.sin(textRotate),
cos = Math.cos(textRotate),
Expand Down
37 changes: 24 additions & 13 deletions js/symbol/shaping.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function Shaping(positionedGlyphs, text, top, bottom, left, right) {

var newLine = 0x0a;

function shapeText(text, glyphs, maxWidth, lineHeight, horizontalAlign, verticalAlign, justify, spacing, translate) {
function shapeText(text, glyphs, maxWidth, lineHeight, horizontalAlign, verticalAlign, justify, spacing, translate, verticalOrientation) {

var positionedGlyphs = [];
var shaping = new Shaping(positionedGlyphs, text, translate[1], translate[1], translate[0], translate[0]);
Expand All @@ -46,17 +46,22 @@ function shapeText(text, glyphs, maxWidth, lineHeight, horizontalAlign, vertical
if (!glyph && codePoint !== newLine) continue;

positionedGlyphs.push(new PositionedGlyph(codePoint, x, y, glyph));

if (glyph) {
// commenting out horizontal orientation for now
//x += glyph.advance + spacing;
//console.log("vertical orientation from shapeText: " + verticalOrientation);
if (verticalOrientation) {
y += 24 + spacing;
//console.log("vertical orientation from shapeText: " + verticalOrientation);
} else {
x += glyph.advance + spacing;
}
}

if (!positionedGlyphs.length) return false;
//if (positionedGlyphs.length > 13) {
linewrapLong(shaping, glyphs, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify, translate);
linewrapLong(shaping, glyphs, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify, translate, verticalOrientation);
//} else {
// linewrap(shaping, glyphs, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify, translate);
//linewrap(shaping, glyphs, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify, translate);
//}
return shaping;
}
Expand Down Expand Up @@ -101,7 +106,7 @@ var breakableCJK = {

invisible[newLine] = breakable[newLine] = true;

function linewrapLong(shaping, glyphs, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify, translate) {
function linewrapLong(shaping, glyphs, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify, translate, verticalOrientation) {
var lastSafeBreak = null;

var lengthBeforeCurrentLine = 0;
Expand All @@ -111,6 +116,7 @@ function linewrapLong(shaping, glyphs, lineHeight, maxWidth, horizontalAlign, ve
var maxLineLength = 0;

var positionedGlyphs = shaping.positionedGlyphs;
//console.log("vertical orientation from linewraplong: " + verticalOrientation);

if (maxWidth) {

Expand Down Expand Up @@ -153,23 +159,28 @@ function linewrapLong(shaping, glyphs, lineHeight, maxWidth, horizontalAlign, ve
lastSafeBreak = Math.round(wordLength / 2);

}

}

var lastPositionedGlyph = positionedGlyphs[positionedGlyphs.length - 1];
var lastLineLength = lastPositionedGlyph.x + glyphs[lastPositionedGlyph.codePoint].advance;

// For vertical labels, calculate 'length' along the y axis, and 'height' along the x axis
var axisPrimary = verticalOrientation ? 'y' : 'x';
var advance = verticalOrientation ? 24 : glyphs[lastPositionedGlyph.codePoint].advance;
var leading = verticalOrientation ? (lineHeight - 24 + glyphs[lastPositionedGlyph.codePoint].advance) : lineHeight;

var lastLineLength = lastPositionedGlyph[axisPrimary] + advance;
maxLineLength = Math.max(maxLineLength, lastLineLength);

var height = (line + 1) * lineHeight;
var height = (line + 1) * leading;

justifyLine(positionedGlyphs, glyphs, lineStartIndex, positionedGlyphs.length - 1, justify);
align(positionedGlyphs, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line, translate);

// Calculate the bounding box
shaping.top += -verticalAlign * height;
shaping.bottom = shaping.top + height;
shaping.left += -horizontalAlign * maxLineLength;
shaping.right = shaping.left + maxLineLength;
shaping.top += verticalOrientation ? -verticalAlign * maxLineLength : -verticalAlign * height;
shaping.bottom = verticalOrientation ? shaping.top + maxLineLength : shaping.top + height;
shaping.left += verticalOrientation ? -horizontalAlign * height : -horizontalAlign * maxLineLength;
shaping.right = verticalOrientation ? shaping.left + height : shaping.left + maxLineLength;
}

function linewrap(shaping, glyphs, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify, translate) {
Expand Down