Skip to content

Commit

Permalink
Add functions for layout properties; refs #361
Browse files Browse the repository at this point in the history
  • Loading branch information
Lauren Budorick committed Jan 27, 2015
1 parent 35b4280 commit a91af9b
Show file tree
Hide file tree
Showing 9 changed files with 65 additions and 48 deletions.
5 changes: 5 additions & 0 deletions js/data/create_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ var FillBucket = require('./fill_bucket');
var SymbolBucket = require('./symbol_bucket');
var LayoutProperties = require('../style/layout_properties');
var featureFilter = require('feature-filter');
var StyleDeclaration = require('../style/style_declaration');

function createBucket(layer, buffers, collision, indices) {

Expand All @@ -15,6 +16,10 @@ function createBucket(layer, buffers, collision, indices) {
return;
}

for (var k in layer.layout) {
layer.layout[k] = new StyleDeclaration('layout', layer.type, k, layer.layout[k]).value;
}

var layoutProperties = new LayoutProperties[layer.type](layer.layout);

var BucketClass =
Expand Down
6 changes: 5 additions & 1 deletion js/data/line_bucket.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
'use strict';

var ElementGroups = require('./element_groups');
var util = require('../util/util');

module.exports = LineBucket;

function LineBucket(layoutProperties, buffers, placement, elementGroups) {
this.layoutProperties = layoutProperties;
this.buffers = buffers;
this.placement = placement;
this.elementGroups = elementGroups || new ElementGroups(buffers.lineVertex, buffers.lineElement);
}

Expand All @@ -30,6 +32,8 @@ LineBucket.prototype.addFeature = function(lines) {
};

LineBucket.prototype.addLine = function(vertices, join, cap, miterLimit, roundLimit) {
miterLimit = util.evalFn(miterLimit, this.placement.zoom);
roundLimit = util.evalFn(roundLimit, this.placement.zoom);

var len = vertices.length;
// If the line has duplicate vertices at the end, adjust length to remove them.
Expand All @@ -51,7 +55,7 @@ LineBucket.prototype.addLine = function(vertices, join, cap, miterLimit, roundLi
var lineVertex = this.buffers.lineVertex;
var lineElement = this.buffers.lineElement;

// we could be more precies, but it would only save a negligible amount of space
// we could be more precise, but it would only save a negligible amount of space
this.elementGroups.makeRoomFor(len * 4);
var elementGroup = this.elementGroups.current;
var vertexStartIndex = elementGroup.vertexStartIndex;
Expand Down
25 changes: 14 additions & 11 deletions js/data/symbol_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ var Shaping = require('../symbol/shaping');
var resolveText = require('../symbol/resolve_text');
var resolveIcons = require('../symbol/resolve_icons');
var mergeLines = require('../symbol/mergelines');
var util = require('../util/util');

module.exports = SymbolBucket;

Expand Down Expand Up @@ -68,12 +69,14 @@ SymbolBucket.prototype.addFeatures = function() {
if (layoutProperties['text-justify'] === 'right') justify = 1;
else if (layoutProperties['text-justify'] === 'left') justify = 0;

var oneEm = 24;
var lineHeight = layoutProperties['text-line-height'] * oneEm;
var maxWidth = layoutProperties['symbol-placement'] !== 'line' && layoutProperties['text-max-width'] * oneEm;
var spacing = layoutProperties['text-letter-spacing'] * oneEm;
var oneEm = 24,
zoom = this.collision.zoom;
var lineHeight = util.evalFn(layoutProperties['text-line-height'], zoom) * oneEm;
var maxWidth = layoutProperties['symbol-placement'] !== 'line' && util.evalFn(layoutProperties['text-max-width'], zoom) * oneEm;
var spacing = util.evalFn(layoutProperties['text-letter-spacing'], zoom) * oneEm;
var fontstack = layoutProperties['text-font'];
var textOffset = [layoutProperties['text-offset'][0] * oneEm, layoutProperties['text-offset'][1] * oneEm];
var calculatedOffset = util.evalFn(layoutProperties['text-offset'], zoom);
var textOffset = [calculatedOffset[0] * oneEm, calculatedOffset[1] * oneEm];

var geometries = [],
k;
Expand Down Expand Up @@ -130,9 +133,9 @@ SymbolBucket.prototype.addFeature = function(lines, faces, shaping, image) {

var horizontalText = layoutProperties['text-rotation-alignment'] === 'viewport',
horizontalIcon = layoutProperties['icon-rotation-alignment'] === 'viewport',
fontScale = layoutProperties['text-max-size'] / glyphSize,
fontScale = util.evalFn(layoutProperties['text-max-size'], collision.zoom) / glyphSize,
textBoxScale = collision.tilePixelRatio * fontScale,
iconBoxScale = collision.tilePixelRatio * layoutProperties['icon-max-size'],
iconBoxScale = collision.tilePixelRatio * util.evalFn(layoutProperties['icon-max-size'], collision.zoom),
iconWithoutText = layoutProperties['text-optional'] || !shaping,
textWithoutIcon = layoutProperties['icon-optional'] || !image,
avoidEdges = layoutProperties['symbol-avoid-edges'];
Expand All @@ -144,7 +147,8 @@ SymbolBucket.prototype.addFeature = function(lines, faces, shaping, image) {

if (layoutProperties['symbol-placement'] === 'line') {
// Line labels
anchors = interpolate(line, layoutProperties['symbol-min-distance'], minScale, collision.maxPlacementScale, collision.tilePixelRatio);
var minDistance = util.evalFn(layoutProperties['symbol-min-distance'], collision.zoom);
anchors = interpolate(line, minDistance, minScale, collision.maxPlacementScale, collision.tilePixelRatio);

// Sort anchors by segment so that we can start placement with the
// anchors that can be shown at the lowest zoom levels.
Expand Down Expand Up @@ -172,14 +176,14 @@ SymbolBucket.prototype.addFeature = function(lines, faces, shaping, image) {
var iconScale = null;

if (shaping) {
glyph = Placement.getGlyphs(anchor, origin, shaping, faces, textBoxScale, horizontalText, line, layoutProperties);
glyph = Placement.getGlyphs(anchor, origin, shaping, faces, textBoxScale, horizontalText, line, layoutProperties, collision.zoom);
glyphScale = layoutProperties['text-allow-overlap'] ? glyph.minScale
: collision.getPlacementScale(glyph.boxes, glyph.minScale, avoidEdges);
if (!glyphScale && !iconWithoutText) continue;
}

if (image) {
icon = Placement.getIcon(anchor, image, iconBoxScale, line, layoutProperties);
icon = Placement.getIcon(anchor, image, iconBoxScale, line, layoutProperties, collision.zoom);
iconScale = layoutProperties['icon-allow-overlap'] ? icon.minScale
: collision.getPlacementScale(icon.boxes, icon.minScale, avoidEdges);
if (!iconScale && !textWithoutIcon) continue;
Expand Down Expand Up @@ -231,7 +235,6 @@ SymbolBucket.prototype.addFeature = function(lines, faces, shaping, image) {
};

SymbolBucket.prototype.addSymbols = function(buffer, elementGroups, symbols, scale, placementRange) {

var zoom = this.collision.zoom;

elementGroups.makeRoomFor(0);
Expand Down
4 changes: 2 additions & 2 deletions js/style/style_declaration.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ module.exports = StyleDeclaration;
/*
* A parsed representation of a property:value pair
*/
function StyleDeclaration(renderType, prop, value) {
var className = 'paint_' + renderType;
function StyleDeclaration(propType, renderType, prop, value) {
var className = [propType, '_', renderType].join('');
var propReference = reference[className] && reference[className][prop];
if (!propReference) return;

Expand Down
2 changes: 1 addition & 1 deletion js/style/style_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ StyleLayer.prototype = {
continue;

var declaration = declarations[k] =
new StyleDeclaration(this.type, k, paint[k]);
new StyleDeclaration('paint', this.type, k, paint[k]);

var t = paint[k + '-transition'] || {};
declaration.transition = {
Expand Down
21 changes: 11 additions & 10 deletions js/symbol/placement.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

var Point = require('point-geometry');
var util = require('../util/util');

module.exports = {
getIcon: getIcon,
Expand All @@ -9,13 +10,14 @@ module.exports = {

var minScale = 0.5; // underscale by 1 zoom level

function getIcon(anchor, image, boxScale, line, props) {
function getIcon(anchor, image, boxScale, line, props, zoom) {

var x = image.w / 2;
var y = image.h / 2;

var dx = props['icon-offset'][0];
var dy = props['icon-offset'][1];
var offset = util.evalFn(props['icon-offset'], zoom);
var dx = offset[0];
var dy = offset[1];
var x1 = (dx - x);
var x2 = (dx + x);
var y1 = (dy - y);
Expand All @@ -26,7 +28,7 @@ function getIcon(anchor, image, boxScale, line, props) {
var br = new Point(x2, y2);
var bl = new Point(x1, y2);

var angle = props['icon-rotate'] * Math.PI / 180;
var angle = util.evalFn(props['icon-rotate'], zoom) * Math.PI / 180;
if (anchor.segment !== undefined && props['icon-rotation-alignment'] !== 'viewport') {
var next = line[anchor.segment];
angle += -Math.atan2(next.x - anchor.x, next.y - anchor.y) + Math.PI / 2;
Expand Down Expand Up @@ -59,7 +61,7 @@ function getIcon(anchor, image, boxScale, line, props) {
anchor: anchor,
minScale: minScale,
maxScale: Infinity,
padding: props['icon-padding']
padding: util.evalFn(props['icon-padding'], zoom)
};

var icon = {
Expand All @@ -81,11 +83,10 @@ function getIcon(anchor, image, boxScale, line, props) {
};
}

function getGlyphs(anchor, origin, shaping, faces, boxScale, horizontal, line, props) {

var maxAngleDelta = props['text-max-angle'] * Math.PI / 180;
var rotate = props['text-rotate'] * Math.PI / 180;
var padding = props['text-padding'];
function getGlyphs(anchor, origin, shaping, faces, boxScale, horizontal, line, props, zoom) {
var maxAngleDelta = util.evalFn(props['text-max-angle'], zoom) * Math.PI / 180;
var rotate = util.evalFn(props['text-rotate'], zoom) * Math.PI / 180;
var padding = util.evalFn(props['text-padding'], zoom);
var alongLine = props['text-rotation-alignment'] !== 'viewport';
var keepUpright = props['text-keep-upright'];

Expand Down
4 changes: 4 additions & 0 deletions js/util/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,7 @@ exports.bindAll = function(fns, context) {
context[fn] = context[fn].bind(context);
});
};

exports.evalFn = function(prop, zoom) {
return typeof prop === 'function' ? prop(zoom) : prop;
};
4 changes: 2 additions & 2 deletions test/js/data/line_bucket.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ var feature = vt.layers.road.feature(0);
test('LineBucket', function(t) {
var info = {};
var buffers = new BufferSet();
var bucket = new LineBucket(info, buffers);
var placement = { zoom: 14 };
var bucket = new LineBucket(info, buffers, placement);
t.ok(bucket);

var pointWithScale = new Point(0, 0);
Expand Down Expand Up @@ -50,4 +51,3 @@ test('LineBucket', function(t) {

t.end();
});

42 changes: 21 additions & 21 deletions test/js/style/style_declaration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,61 +6,61 @@ var StyleDeclaration = require('../../../js/style/style_declaration');
test('styledeclaration', function(t) {

t.test('boolean', function(t) {
t.equal((new StyleDeclaration('fill', 'fill-antialias', false)).calculate(0), false);
t.equal((new StyleDeclaration('paint', 'fill', 'fill-antialias', false)).calculate(0), false);
t.end();
});

t.test('image', function(t) {
t.equal((new StyleDeclaration('fill', 'fill-image', 'smilingclownstaringatyou.png')).calculate(0),
t.equal((new StyleDeclaration('paint', 'fill', 'fill-image', 'smilingclownstaringatyou.png')).calculate(0),
'smilingclownstaringatyou.png');
t.end();
});

t.test('keywords', function(t) {
t.equal((new StyleDeclaration('fill', 'fill-translate-anchor', 'viewport')).calculate(0),
t.equal((new StyleDeclaration('paint', 'fill', 'fill-translate-anchor', 'viewport')).calculate(0),
'viewport');
t.end();
});

t.test('parseWidthArray', function(t) {
var dashFn = new StyleDeclaration('line', 'line-dasharray', [0, 10, 5]);
var dashFn = new StyleDeclaration('paint', 'line', 'line-dasharray', [0, 10, 5]);
t.ok(dashFn instanceof StyleDeclaration);
t.deepEqual(dashFn.calculate(0), [0, 10, 5]);
t.end();
});

t.test('constant', function(t) {
t.equal((new StyleDeclaration('line', 'line-width', 5)).calculate(0), 5);
t.equal((new StyleDeclaration('line', 'line-width', 5)).calculate(100), 5);
t.equal((new StyleDeclaration('paint', 'line', 'line-width', 5)).calculate(0), 5);
t.equal((new StyleDeclaration('paint', 'line', 'line-width', 5)).calculate(100), 5);
t.end();
});

t.test('functions', function(t) {
t.equal((new StyleDeclaration('fill', 'fill-opacity', { stops: [] })).calculate(0), 1);
t.equal((new StyleDeclaration('fill', 'fill-opacity', { stops: [[2, 2], [5, 10]] })).calculate(0), 2);
t.equal((new StyleDeclaration('fill', 'fill-opacity', { stops: [[0, 0], [5, 10]] })).calculate(12), 10);
t.equal((new StyleDeclaration('fill', 'fill-opacity', { stops: [[0, 0], [5, 10]] })).calculate(6), 10);
t.equal(Math.round((new StyleDeclaration('fill', 'fill-opacity', { stops: [[0, 0], [5, 10]], base: 1.01 })).calculate(2.5)), 5);
t.equal((new StyleDeclaration('fill', 'fill-opacity', { stops: [[0, 0], [1, 10], [2, 20]] })).calculate(2), 20);
t.equal((new StyleDeclaration('fill', 'fill-opacity', { stops: [[0, 0], [1, 10], [2, 20]] })).calculate(1), 10);
t.equal((new StyleDeclaration('fill', 'fill-opacity', { stops: [[0, 0]] })).calculate(6), 0);
t.equal((new StyleDeclaration('paint', 'fill', 'fill-opacity', { stops: [] })).calculate(0), 1);
t.equal((new StyleDeclaration('paint', 'fill', 'fill-opacity', { stops: [[2, 2], [5, 10]] })).calculate(0), 2);
t.equal((new StyleDeclaration('paint', 'fill', 'fill-opacity', { stops: [[0, 0], [5, 10]] })).calculate(12), 10);
t.equal((new StyleDeclaration('paint', 'fill', 'fill-opacity', { stops: [[0, 0], [5, 10]] })).calculate(6), 10);
t.equal(Math.round((new StyleDeclaration('paint', 'fill', 'fill-opacity', { stops: [[0, 0], [5, 10]], base: 1.01 })).calculate(2.5)), 5);
t.equal((new StyleDeclaration('paint', 'fill', 'fill-opacity', { stops: [[0, 0], [1, 10], [2, 20]] })).calculate(2), 20);
t.equal((new StyleDeclaration('paint', 'fill', 'fill-opacity', { stops: [[0, 0], [1, 10], [2, 20]] })).calculate(1), 10);
t.equal((new StyleDeclaration('paint', 'fill', 'fill-opacity', { stops: [[0, 0]] })).calculate(6), 0);

t.end();
});

t.test('color parsing', function(t) {
t.deepEqual(new StyleDeclaration('line', 'line-color', 'red').calculate(0), [ 1, 0, 0, 1 ]);
t.deepEqual(new StyleDeclaration('line', 'line-color', '#ff00ff').calculate(0), [ 1, 0, 1, 1 ]);
t.deepEqual(new StyleDeclaration('line', 'line-color', { stops: [[0, '#f00'], [1, '#0f0']] }).calculate(0), [1, 0, 0, 1]);
t.deepEqual(new StyleDeclaration('paint', 'line', 'line-color', 'red').calculate(0), [ 1, 0, 0, 1 ]);
t.deepEqual(new StyleDeclaration('paint', 'line', 'line-color', '#ff00ff').calculate(0), [ 1, 0, 1, 1 ]);
t.deepEqual(new StyleDeclaration('paint', 'line', 'line-color', { stops: [[0, '#f00'], [1, '#0f0']] }).calculate(0), [1, 0, 0, 1]);
// cached
t.deepEqual(new StyleDeclaration('line', 'line-color', '#ff00ff').calculate(0), [ 1, 0, 1, 1 ]);
t.deepEqual(new StyleDeclaration('line', 'line-color', 'rgba(255, 51, 0, 1)').calculate(0), [ 1, 0.2, 0, 1 ]);
t.deepEqual(new StyleDeclaration('paint', 'line', 'line-color', '#ff00ff').calculate(0), [ 1, 0, 1, 1 ]);
t.deepEqual(new StyleDeclaration('paint', 'line', 'line-color', 'rgba(255, 51, 0, 1)').calculate(0), [ 1, 0.2, 0, 1 ]);
t.end();
});

t.equal((new StyleDeclaration('', 'unknown-prop')).prop, undefined, 'unknown prop');
t.equal((new StyleDeclaration('paint', '', 'unknown-prop')).prop, undefined, 'unknown prop');

var widthfn = new StyleDeclaration('line', 'line-width', function(z) {
var widthfn = new StyleDeclaration('paint', 'line', 'line-width', function(z) {
return Math.pow(z, 2);
});
t.equal(widthfn.calculate(10), 100);
Expand Down

0 comments on commit a91af9b

Please sign in to comment.