Skip to content

Commit

Permalink
Support icon-text-fit, icon-text-fit-padding (#2720)
Browse files Browse the repository at this point in the history
* Add support for icon-text-fit, icon-text-fit-padding

* Add unit tests for icon-text-fit support in getIconQuads()

* Add support for padding in all 4 directions.

* Update all hashes post-merge
  • Loading branch information
yhahn authored and lucaswoj committed Jun 15, 2016
1 parent 9a34d7b commit 6b3cc68
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 8 deletions.
2 changes: 1 addition & 1 deletion js/data/bucket/symbol_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ SymbolBucket.prototype.addSymbolInstance = function(anchor, line, shapedText, sh
var textBoxEndIndex = textCollisionFeature ? textCollisionFeature.boxEndIndex : this.collisionBoxArray.length;

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

Expand Down
39 changes: 34 additions & 5 deletions js/symbol/quads.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,11 @@ function SymbolQuad(anchorPoint, tl, tr, bl, br, tex, anchorAngle, glyphAngle, m
* @param {Array<Array<Point>>} line
* @param {LayoutProperties} layout
* @param {boolean} alongLine Whether the icon should be placed along the line.
* @param {Shaping} shapedText Shaping for corresponding text
* @returns {Array<SymbolQuad>}
* @private
*/
function getIconQuads(anchor, shapedIcon, boxScale, line, layout, alongLine) {
function getIconQuads(anchor, shapedIcon, boxScale, line, layout, alongLine, shapedText) {

var rect = shapedIcon.image.rect;

Expand All @@ -63,10 +64,38 @@ function getIconQuads(anchor, shapedIcon, boxScale, line, layout, alongLine) {
var right = left + rect.w / shapedIcon.image.pixelRatio;
var top = shapedIcon.top - border;
var bottom = top + rect.h / shapedIcon.image.pixelRatio;
var tl = new Point(left, top);
var tr = new Point(right, top);
var br = new Point(right, bottom);
var bl = new Point(left, bottom);
var tl, tr, br, bl;

// text-fit mode
if (layout['icon-text-fit'] !== 'none' && shapedText) {
var iconWidth = (right - left),
iconHeight = (bottom - top),
size = layout['text-size'] / 24,
textLeft = shapedText.left * size,
textRight = shapedText.right * size,
textTop = shapedText.top * size,
textBottom = shapedText.bottom * size,
textWidth = textRight - textLeft,
textHeight = textBottom - textTop,
padT = layout['icon-text-fit-padding'][0],
padR = layout['icon-text-fit-padding'][1],
padB = layout['icon-text-fit-padding'][2],
padL = layout['icon-text-fit-padding'][3],
offsetY = layout['icon-text-fit'] === 'width' ? (textHeight - iconHeight) * 0.5 : 0,
offsetX = layout['icon-text-fit'] === 'height' ? (textWidth - iconWidth) * 0.5 : 0,
width = layout['icon-text-fit'] === 'width' || layout['icon-text-fit'] === 'both' ? textWidth : iconWidth,
height = layout['icon-text-fit'] === 'height' || layout['icon-text-fit'] === 'both' ? textHeight : iconHeight;
tl = new Point(textLeft + offsetX - padL, textTop + offsetY - padT);
tr = new Point(textLeft + offsetX + padR + width, textTop + offsetY - padT);
br = new Point(textLeft + offsetX + padR + width, textTop + offsetY + padB + height);
bl = new Point(textLeft + offsetX - padL, textTop + offsetY + padB + height);
// Normal icon size mode
} else {
tl = new Point(left, top);
tr = new Point(right, top);
br = new Point(right, bottom);
bl = new Point(left, bottom);
}

var angle = layout['icon-rotate'] * Math.PI / 180;
if (alongLine) {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"mapbox-gl-function": "^1.2.1",
"mapbox-gl-js-supported": "^1.1.0",
"mapbox-gl-shaders": "mapbox/mapbox-gl-shaders#59e998295d548f208ee3ec10cdd21ff2630e2079",
"mapbox-gl-style-spec": "mapbox/mapbox-gl-style-spec#2461efc3d883f2f2e56a6c6b2bfd7d54bbfe9f86",
"mapbox-gl-style-spec": "mapbox/mapbox-gl-style-spec#194fc55b6a7dd54c1e2cf2dd9048fbb5e836716d",
"minifyify": "^7.0.1",
"pbf": "^1.3.2",
"pngjs": "^2.2.0",
Expand Down Expand Up @@ -57,7 +57,7 @@
"highlight.js": "9.3.0",
"istanbul": "^0.4.2",
"lodash": "^4.13.1",
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#3d06b422b1aa81aec71b4c67128f3d947205d6af",
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#146a348f1768ce13e153fce3b32bbed469aa5fe4",
"nyc": "6.4.0",
"remark": "4.2.2",
"remark-html": "3.0.0",
Expand Down
188 changes: 188 additions & 0 deletions test/js/symbol/quads.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,191 @@ test('getIconQuads', function(t) {
});
t.end();
});

test('getIconQuads text-fit', function(t) {
var anchor = new Anchor(0, 0, 0, undefined);
var fakeShapedIcon = {
top: -10,
bottom: 10,
left: -10,
right: 10,
image: {
pixelRatio: 1,
rect: { w: 20, h: 20 }
}
};
var fakeShapedText = {
top: -10,
bottom: 30,
left: -60,
right: 20
};

t.test('icon-text-fit: none', function(t) {
var quads = getIconQuads(anchor, fakeShapedIcon, 2, [], {
'icon-text-fit': 'none'
}, false, fakeShapedText);
t.deepEqual(quads[0].tl, { x: -11, y: -11 });
t.deepEqual(quads[0].tr, { x: 9, y: -11 });
t.deepEqual(quads[0].bl, { x: -11, y: 9 });
t.deepEqual(quads[0].br, { x: 9, y: 9 });
t.deepEqual(quads, getIconQuads(anchor, fakeShapedIcon, 2, [], {
'icon-text-fit': 'none',
'icon-text-fit-padding': [10, 10]
}, false, fakeShapedText), 'ignores padding');
t.end();
});

t.test('icon-text-fit: width', function(t) {
// - Uses text width
// - Preserves icon height, centers vertically
var quads = getIconQuads(anchor, fakeShapedIcon, 2, [], {
'text-size': 24,
'icon-text-fit': 'width',
'icon-text-fit-padding': [ 0, 0, 0, 0 ]
}, false, fakeShapedText);
t.deepEqual(quads[0].tl, { x: -60, y: 0 });
t.deepEqual(quads[0].tr, { x: 20, y: 0 });
t.deepEqual(quads[0].bl, { x: -60, y: 20 });
t.deepEqual(quads[0].br, { x: 20, y: 20 });
t.end();
});

t.test('icon-text-fit: width, x textSize', function(t) {
// - Uses text width (adjusted for textSize)
// - Preserves icon height, centers vertically
var quads = getIconQuads(anchor, fakeShapedIcon, 2, [], {
'text-size': 12,
'icon-text-fit': 'width',
'icon-text-fit-padding': [ 0, 0, 0, 0 ]
}, false, fakeShapedText);
t.deepEqual(quads[0].tl, { x: -30, y: -5 });
t.deepEqual(quads[0].tr, { x: 10, y: -5 });
t.deepEqual(quads[0].bl, { x: -30, y: 15 });
t.deepEqual(quads[0].br, { x: 10, y: 15 });
t.end();
});

t.test('icon-text-fit: width, x textSize, + padding', function(t) {
// - Uses text width (adjusted for textSize)
// - Preserves icon height, centers vertically
// - Applies padding x, padding y
var quads = getIconQuads(anchor, fakeShapedIcon, 2, [], {
'text-size': 12,
'icon-text-fit': 'width',
'icon-text-fit-padding': [ 5, 10, 5, 10 ]
}, false, fakeShapedText);
t.deepEqual(quads[0].tl, { x: -40, y: -10 });
t.deepEqual(quads[0].tr, { x: 20, y: -10 });
t.deepEqual(quads[0].bl, { x: -40, y: 20 });
t.deepEqual(quads[0].br, { x: 20, y: 20 });
t.end();
});

t.test('icon-text-fit: height', function(t) {
// - Uses text height
// - Preserves icon width, centers horizontally
var quads = getIconQuads(anchor, fakeShapedIcon, 2, [], {
'text-size': 24,
'icon-text-fit': 'height',
'icon-text-fit-padding': [ 0, 0, 0, 0 ]
}, false, fakeShapedText);
t.deepEqual(quads[0].tl, { x: -30, y: -10 });
t.deepEqual(quads[0].tr, { x: -10, y: -10 });
t.deepEqual(quads[0].bl, { x: -30, y: 30 });
t.deepEqual(quads[0].br, { x: -10, y: 30 });
t.end();
});

t.test('icon-text-fit: height, x textSize', function(t) {
// - Uses text height (adjusted for textSize)
// - Preserves icon width, centers horizontally
var quads = getIconQuads(anchor, fakeShapedIcon, 2, [], {
'text-size': 12,
'icon-text-fit': 'height',
'icon-text-fit-padding': [ 0, 0, 0, 0 ]
}, false, fakeShapedText);
t.deepEqual(quads[0].tl, { x: -20, y: -5 });
t.deepEqual(quads[0].tr, { x: 0, y: -5 });
t.deepEqual(quads[0].bl, { x: -20, y: 15 });
t.deepEqual(quads[0].br, { x: 0, y: 15 });
t.end();
});

t.test('icon-text-fit: height, x textSize, + padding', function(t) {
// - Uses text height (adjusted for textSize)
// - Preserves icon width, centers horizontally
// - Applies padding x, padding y
var quads = getIconQuads(anchor, fakeShapedIcon, 2, [], {
'text-size': 12,
'icon-text-fit': 'height',
'icon-text-fit-padding': [ 5, 10, 5, 10 ]
}, false, fakeShapedText);
t.deepEqual(quads[0].tl, { x: -30, y: -10 });
t.deepEqual(quads[0].tr, { x: 10, y: -10 });
t.deepEqual(quads[0].bl, { x: -30, y: 20 });
t.deepEqual(quads[0].br, { x: 10, y: 20 });
t.end();
});

t.test('icon-text-fit: both', function(t) {
// - Uses text width + height
var quads = getIconQuads(anchor, fakeShapedIcon, 2, [], {
'text-size': 24,
'icon-text-fit': 'both',
'icon-text-fit-padding': [ 0, 0, 0, 0 ]
}, false, fakeShapedText);
t.deepEqual(quads[0].tl, { x: -60, y: -10 });
t.deepEqual(quads[0].tr, { x: 20, y: -10 });
t.deepEqual(quads[0].bl, { x: -60, y: 30 });
t.deepEqual(quads[0].br, { x: 20, y: 30 });
t.end();
});

t.test('icon-text-fit: both, x textSize', function(t) {
// - Uses text width + height (adjusted for textSize)
var quads = getIconQuads(anchor, fakeShapedIcon, 2, [], {
'text-size': 12,
'icon-text-fit': 'both',
'icon-text-fit-padding': [ 0, 0, 0, 0 ]
}, false, fakeShapedText);
t.deepEqual(quads[0].tl, { x: -30, y: -5 });
t.deepEqual(quads[0].tr, { x: 10, y: -5 });
t.deepEqual(quads[0].bl, { x: -30, y: 15 });
t.deepEqual(quads[0].br, { x: 10, y: 15 });
t.end();
});

t.test('icon-text-fit: both, x textSize, + padding', function(t) {
// - Uses text width + height (adjusted for textSize)
// - Applies padding x, padding y
var quads = getIconQuads(anchor, fakeShapedIcon, 2, [], {
'text-size': 12,
'icon-text-fit': 'both',
'icon-text-fit-padding': [ 5, 10, 5, 10 ]
}, false, fakeShapedText);
t.deepEqual(quads[0].tl, { x: -40, y: -10 });
t.deepEqual(quads[0].tr, { x: 20, y: -10 });
t.deepEqual(quads[0].bl, { x: -40, y: 20 });
t.deepEqual(quads[0].br, { x: 20, y: 20 });
t.end();
});

t.test('icon-text-fit: both, padding t/r/b/l', function(t) {
// - Uses text width + height (adjusted for textSize)
// - Applies padding t/r/b/l
var quads = getIconQuads(anchor, fakeShapedIcon, 2, [], {
'text-size': 12,
'icon-text-fit': 'both',
'icon-text-fit-padding': [ 0, 5, 10, 15 ]
}, false, fakeShapedText);
t.deepEqual(quads[0].tl, { x: -45, y: -5 });
t.deepEqual(quads[0].tr, { x: 15, y: -5 });
t.deepEqual(quads[0].bl, { x: -45, y: 25 });
t.deepEqual(quads[0].br, { x: 15, y: 25 });
t.end();
});

t.end();
});

0 comments on commit 6b3cc68

Please sign in to comment.