diff --git a/debug/style.json b/debug/style.json index a16766a89a6..f466bbc4e95 100644 --- a/debug/style.json +++ b/debug/style.json @@ -90,8 +90,7 @@ "type": "fill", "style": { "fill-color": { - "fn": "stops", - "stops": [[2, "@red"], [6, "@water"]] + "stops": [[1, "@red"], [5, "@water"]] } }, "style.satellite": { @@ -105,11 +104,8 @@ "style": { "line-color": "@water", "line-width": { - "fn": "linear", - "z": 9, - "val": 1, - "slope": 0.5, - "min": 0.5 + "base": 1.01, + "stops": [[7, 0.5], [19, 6.5]] } } }, { @@ -124,11 +120,7 @@ "line-color": "rgba(0,0,0,0.5)", "line-width": 1, "line-offset": { - "fn": "exponential", - "z": 9, - "val": -1, - "slope": 0.2, - "min": 1 + "stops": [[12.11458208048225, 1], [19, 93.2862864971161]] } } }, { @@ -141,11 +133,7 @@ "line-color": "rgba(0,0,0,0.5)", "line-width": 1, "line-offset": { - "fn": "exponential", - "z": 11, - "val": 0.5, - "slope": 0.2, - "min": 1 + "stops": [[11.637356828788917, 1], [19, 31.287358856201173]] } } }, { @@ -154,11 +142,7 @@ "style": { "line-color": "rgba(255,255,255,0.5)", "line-width": { - "fn": "exponential", - "z": 9, - "val": -1, - "slope": 0.2, - "min": 1 + "stops": [[12.11458208048225, 1], [19, 93.2862864971161]] } } }, { @@ -167,11 +151,7 @@ "style": { "line-color": "rgba(255,255,255,0.5)", "line-width": { - "fn": "exponential", - "z": 11, - "val": -1, - "slope": 0.2, - "min": 1 + "stops": [[14.11458208048225, 1], [19, 29.787358856201173]] } } }, { @@ -187,19 +167,11 @@ "style": { "line-color": "rgba(154,154,154,0.5)", "line-width": { - "fn": "exponential", - "z": 9, - "val": 1, - "slope": 0.21, - "min": 4 + "stops": [[12.751938909271168, 4], [19, 100.00060082197189]] }, "line-opacity": { - "fn": "linear", - "z": 14, - "val": 0, - "slope": 1, - "min": 0, - "max": 1 + "base": 1.01, + "stops": [[13, 0], [14, 1]] }, "transition-line-width": { "duration": 500 @@ -208,11 +180,7 @@ }, "style.satellite": { "line-width": { - "fn": "exponential", - "z": 10, - "val": 1, - "slope": 0.21, - "min": 4 + "stops": [[13.751938909271168, 4], [19, 57.57177189826965]] }, "transition-line-width": { "duration": 500, @@ -221,11 +189,7 @@ }, "style.test": { "line-width": { - "fn": "exponential", - "z": 8, - "val": 1, - "slope": 0.21, - "min": 4 + "stops": [[11.751938909271168, 4], [19, 174.25105143845082]] }, "line-color": "rgba(255,0,0,1)", "transition-line-width": { @@ -249,19 +213,11 @@ "style": { "line-color": "rgba(154,154,154,0.5)", "line-width": { - "fn": "exponential", - "z": 10, - "val": 0.5, - "slope": 0.2, - "min": 1 + "stops": [[10.637356828788917, 1], [19, 54.377877998352055]] }, "line-opacity": { - "fn": "linear", - "z": 15.5, - "val": 0, - "slope": 1, - "min": 0, - "max": 1 + "base": 1.01, + "stops": [[14.5, 0], [15.5, 1]] }, "line-blur": "@road_blur" }, @@ -271,11 +227,7 @@ "delay": 1000 }, "line-width": { - "fn": "exponential", - "z": 11, - "val": 0.5, - "slope": 0.2, - "min": 1 + "stops": [[11.637356828788917, 1], [19, 31.287358856201173]] } } }, { @@ -294,11 +246,7 @@ "line-color": "@road", "line-blur": "@road_blur", "line-width": { - "fn": "exponential", - "z": 11, - "val": -1, - "slope": 0.2, - "min": 1 + "stops": [[13.11458208048225, 1], [19, 52.877877998352055]] } } }, { @@ -308,11 +256,7 @@ "line-color": "@road", "line-blur": "@road_blur", "line-width": { - "fn": "exponential", - "z": 9, - "val": -1, - "slope": 0.2, - "min": 1 + "stops": [[12.11458208048225, 1], [19, 93.2862864971161]] } }, "style.satellite": { @@ -321,11 +265,7 @@ "delay": 1000 }, "line-width": { - "fn": "exponential", - "z": 10, - "val": -1, - "slope": 0.2, - "min": 1 + "stops": [[13.11458208048225, 1], [19, 52.877877998352055]] } } }, { @@ -335,11 +275,7 @@ "line-color": "@road", "line-blur": "@road_blur", "line-width": { - "fn": "exponential", - "z": 10, - "val": -1, - "slope": 0.2, - "min": 1 + "stops": [[13.11458208048225, 1], [19, 52.877877998352055]] } }, "style.satellite": { @@ -348,11 +284,7 @@ "delay": 1000 }, "line-width": { - "fn": "exponential", - "z": 11, - "val": -1, - "slope": 0.2, - "min": 1 + "stops": [[14.11458208048225, 1], [19, 29.787358856201173]] } } }, { @@ -418,11 +350,7 @@ "style": { "line-color": "#EC8D8D", "line-width": { - "fn": "exponential", - "z": 9, - "val": 1, - "slope": 0.21, - "min": 4 + "stops": [[12.751938909271168, 4], [19, 100.00060082197189]] } } }, { @@ -437,12 +365,8 @@ "delay": 500 }, "fill-opacity": { - "fn": "linear", - "z": 14, - "val": 0, - "slope": 1, - "min": 0, - "max": 1 + "base": 1.01, + "stops": [[13, 0], [14, 1]] }, "fill-outline-color": "@building_outline" }, @@ -470,11 +394,7 @@ "style": { "line-color": "rgba(0,0,0,0.4)", "line-width": { - "fn": "exponential", - "z": 9, - "val": 1.5, - "slope": 0.2, - "min": 1 + "stops": [[2.6468052936710817, 1.51], [19, 95.7862864971161]] } } }, { @@ -483,11 +403,7 @@ "style": { "line-color": "@road", "line-width": { - "fn": "exponential", - "z": 9, - "val": -1, - "slope": 0.2, - "min": 1 + "stops": [[12.11458208048225, 1], [19, 93.2862864971161]] } } }, { @@ -497,19 +413,19 @@ "filter": { "maki": "park" }, "type": "symbol", "render": { - "icon-image": "dot.sdf", - "icon-allow-overlap": true, - "icon-ignore-placement": true + "icon-image": "dot.sdf", + "icon-allow-overlap": true, + "icon-ignore-placement": true }, "style": { - "icon-color": "#8f8", - "icon-halo-color": "#393", - "icon-halo-width": 0, - "icon-halo-blur": 5, - "icon-opacity": 0, - "icon-halo-opacity": 0.9, - "icon-translate": [2, -1], - "icon-size": 15 + "icon-color": "#8f8", + "icon-halo-color": "#393", + "icon-halo-width": 0, + "icon-halo-blur": 5, + "icon-opacity": 0, + "icon-halo-opacity": 0.9, + "icon-translate": [2, -1], + "icon-size": 15 } }, { "id": "country_label", @@ -568,12 +484,7 @@ "text-halo-color": "rgba(255,255,255,0.7)", "text-halo-width": "@stroke_width", "text-size": { - "fn": "exponential", - "z": 14, - "val": 8, - "slope": 1, - "min": 8, - "max": 12 + "stops": [[4.770835839035499, 8.01], [15.477225251693334, 12]] } } }, { diff --git a/js/style/styledeclaration.js b/js/style/styledeclaration.js index f4f2640fcb0..cdaeafce2b9 100644 --- a/js/style/styledeclaration.js +++ b/js/style/styledeclaration.js @@ -56,7 +56,7 @@ StyleDeclaration.prototype.parseValue = function(value, type, values, constants) }; function parseNumber(num) { - num = parseFunction(num); + if (num.stops) num = stopsFn(num); var value = +num; return !isNaN(value) ? value : num; } @@ -76,12 +76,12 @@ function parseNumberArray(array) { var colorCache = {}; function parseColor(value) { - if (value.fn === 'stops') { + if (value.stops) { for (var i = 0; i < value.stops.length; i++) { // store the parsed color as the 3rd element in the array value.stops[i][2] = parseCSSColor(value.stops[i][1]); } - return parseFunction(value, true); + return stopsFn(value, true); } if (colorCache[value]) { @@ -92,97 +92,41 @@ function parseColor(value) { return color; } - -var functionParsers = StyleDeclaration.functionParsers = { - linear: linear, - exponential: exponential, - min: min, - stops: stopsFn -}; - -function parseFunction(fn, color) { - if (fn.fn) { - if (!functionParsers[fn.fn]) { - throw new Error('The function "' + fn.fn + '" does not exist'); - } - return functionParsers[fn.fn](fn, color); - } else { - return fn; - } -} - -/* - * Function parsers - */ - -function linear(params) { - var z_base = +params.z || 0, - val = +params.val || 0, - slope = +params.slope || 0, - min = +params.min || 0, - max = +params.max || Infinity; - return function(z) { - return Math.min(Math.max(min, val + (z - z_base) * slope), max); - }; -} - -function exponential(params) { - var z_base = +params.z || 0, - val = +params.val || 0, - slope = +params.slope || 0, - min = +params.min || 0, - max = +params.max || Infinity, - base = +params.base || 1.75; - return function(z) { - return Math.min(Math.max(min, val + Math.pow(base, (z - z_base)) * slope), max); - }; -} - -function min(params) { - var min_z = +params.min || 0; - return function(z) { - return z >= min_z; - }; -} - function stopsFn(params, color) { var stops = params.stops; + var base = params.base || (color ? 1.05 : 1.75); + return function(z) { - z += 1; + // find the two stops which the current z is between var low = null; var high = null; - for (var i = 0; i < stops.length; i++) { var stop = stops[i]; - if (stop[0] <= z && (!low || low[0] < stop[0])) low = stop; - if (stop[0] >= z && (!high || high[0] > stop[0])) high = stop; + if (stop[0] <= z) low = stop; + if (stop[0] > z) { + high = stop; + break; + } } if (low && high) { - if (high[0] == low[0] || high[1] == low[1]) { - if (color) return prepareColor(low[2]); - return low[1]; - } - var factor = (z - low[0]) / (high[0] - low[0]); - - // If color, interpolate between values - if (color) return prepareColor(interpColor(low[2], high[2], factor)); - // Linear interpolation if base is 0 - if (low[1] === 0) return factor * high[1]; - // Exponential interpolation between the values - return low[1] * Math.pow(high[1] / low[1], factor); - } else if (high || low) { - // use the closest stop for z beyond the stops range - if (color) return low ? prepareColor(low[2]) : prepareColor(high[2]); - return low ? low[1] : high[1]; - - // Exponential extrapolation of the low or high value - //var edge = high || low; - //return Math.pow(2, z) * (edge.val / Math.pow(2, edge.z)); + var zoomDiff = high[0] - low[0]; + var t = (Math.pow(base, z - low[0]) - 1) / (Math.pow(base, zoomDiff) - 1); + if (color) return prepareColor(interpColor(low[2], high[2], t)); + else return util.interp(low[1], high[1], t); + + } else if (low) { + if (color) return prepareColor(low[2]); + else return low[1]; + + } else if (high) { + if (color) return prepareColor(high[2]); + else return high[1]; + } else { - // No stop defined. - return 1; + if (color) return [0, 0, 0, 1]; + else return 1; } }; } diff --git a/test/expected/style-basic-computed.json b/test/expected/style-basic-computed.json index c91477fb62e..09b9e7be26a 100644 --- a/test/expected/style-basic-computed.json +++ b/test/expected/style-basic-computed.json @@ -62,14 +62,32 @@ "line-width": 1 }, "poi": { - "icon-color": [ 0, 0, 0, 1 ], - "icon-halo-color": [ 0, 0, 0, 0 ], - "text-color": [ 0, 0, 0, 1 ], - "text-halo-color": [ 0, 0, 0, 0 ] + "text-color": [ + 0, + 0, + 0, + 1 + ], + "text-halo-color": [ + 0, + 0, + 0, + 0 + ], + "icon-color": [ + 0, + 0, + 0, + 1 + ], + "icon-halo-color": [ + 0, + 0, + 0, + 0 + ] }, "country_label": { - "icon-color": [ 0, 0, 0, 1 ], - "icon-halo-color": [ 0, 0, 0, 0 ], "text-halo-color": [ 0.7, 0.7, @@ -82,6 +100,18 @@ 0, 0, 1 + ], + "icon-color": [ + 0, + 0, + 0, + 1 + ], + "icon-halo-color": [ + 0, + 0, + 0, + 0 ] }, "road_label": { @@ -91,8 +121,6 @@ 0, 1 ], - "icon-color": [ 0, 0, 0, 1 ], - "icon-halo-color": [ 0, 0, 0, 0 ], "text-halo-color": [ 0.7, 0.7, @@ -100,6 +128,18 @@ 0.7 ], "text-halo-width": 0.25, - "text-size": 8.106622240733028 + "text-size": 8.186588921282798, + "icon-color": [ + 0, + 0, + 0, + 1 + ], + "icon-halo-color": [ + 0, + 0, + 0, + 0 + ] } -} +} \ No newline at end of file diff --git a/test/expected/style-basic-layergroups.json b/test/expected/style-basic-layergroups.json index 3d1942f52e2..aa74df0697c 100644 --- a/test/expected/style-basic-layergroups.json +++ b/test/expected/style-basic-layergroups.json @@ -37,9 +37,10 @@ "id": "park", "bucket": "park" } - ],[ + ], + [ { "id": "background" } ] -] +] \ No newline at end of file diff --git a/test/expected/style-basic-layers.json b/test/expected/style-basic-layers.json index 66fcd7d7bf7..98bd571157b 100644 --- a/test/expected/style-basic-layers.json +++ b/test/expected/style-basic-layers.json @@ -98,7 +98,7 @@ "declaration": { "prop": "line-width", "type": "number", - "json": "{\"fn\":\"exponential\",\"z\":10,\"val\":-1,\"slope\":0.2,\"min\":1}" + "json": "{\"stops\":[[13.11458208048225,1],[19,52.877877998352055]]}" }, "endTime": 1402963200000, "startTime": 1402963200000, @@ -126,7 +126,7 @@ "declaration": { "prop": "fill-opacity", "type": "number", - "json": "{\"fn\":\"linear\",\"z\":14,\"val\":0,\"slope\":1,\"min\":0,\"max\":1}" + "json": "{\"base\":1.01,\"stops\":[[13,0],[14,1]]}" }, "endTime": 1402963200000, "startTime": 1402963200000, @@ -152,7 +152,12 @@ "borders": { "line-color": { "declaration": { - "value": [0, 0, 0, 0.3], + "value": [ + 0, + 0, + 0, + 0.3 + ], "prop": "line-color", "type": "color", "json": "\"rgba(0,0,0,0.3)\"" @@ -267,11 +272,11 @@ "declaration": { "prop": "text-size", "type": "number", - "json": "{\"fn\":\"exponential\",\"z\":14,\"val\":8,\"slope\":1,\"min\":8,\"max\":12}" + "json": "{\"stops\":[[4.770835839035499,8.01],[15.477225251693334,12]]}" }, "endTime": 1402963200000, "startTime": 1402963200000, "loopID": 18 } } -} +} \ No newline at end of file diff --git a/test/fixtures/style-basic.json b/test/fixtures/style-basic.json index 5689701fe1d..98a12af45e4 100644 --- a/test/fixtures/style-basic.json +++ b/test/fixtures/style-basic.json @@ -1,5 +1,5 @@ { - "version": 3, + "version": 4, "constants": { "@land": "#eee", "@water": "#999", @@ -62,11 +62,7 @@ "line-color": "@road", "line-blur": "@road_blur", "line-width": { - "fn": "exponential", - "z": 10, - "val": -1, - "slope": 0.2, - "min": 1 + "stops": [[13.11458208048225, 1], [19, 52.877877998352055]] } } }], @@ -90,12 +86,8 @@ "delay": 500 }, "fill-opacity": { - "fn": "linear", - "z": 14, - "val": 0, - "slope": 1, - "min": 0, - "max": 1 + "base": 1.01, + "stops": [[13, 0], [14, 1]] }, "fill-outline-color": "@building_outline" } @@ -144,7 +136,7 @@ "text-field": "{name}", "text-font": "Open Sans Regular, Arial Unicode MS Regular", "text-max-size": 12, - "text-max-angle": 1.04, + "text-max-angle": 59.59, "symbol-min-distance": 250 }, "style": { @@ -152,12 +144,7 @@ "text-halo-color": "rgba(255,255,255,0.7)", "text-halo-width": "@stroke_width", "text-size": { - "fn": "exponential", - "z": 14, - "val": 8, - "slope": 1, - "min": 8, - "max": 12 + "stops": [[4.770835839035499, 8.01], [15.477225251693334, 12]] } } }] diff --git a/test/js/style/styledeclaration.test.js b/test/js/style/styledeclaration.test.js index deab7d69fd4..08aff4f3f62 100644 --- a/test/js/style/styledeclaration.test.js +++ b/test/js/style/styledeclaration.test.js @@ -37,24 +37,14 @@ test('styledeclaration', function(t) { }); t.test('functions', function(t) { - t.equal((new StyleDeclaration('fill', 'fill-opacity', { fn: 'linear' })).calculate(0), 0); - t.equal((new StyleDeclaration('fill', 'fill-opacity', { fn: 'linear', max: 10, slope: 0.5 })).calculate(10), 5); - t.equal((new StyleDeclaration('fill', 'fill-opacity', { fn: 'exponential' })).calculate(0), 0); - t.equal((new StyleDeclaration('fill', 'fill-opacity', { fn: 'min' })).calculate(0), true); - t.equal((new StyleDeclaration('fill', 'fill-opacity', { fn: 'stops', stops: [] })).calculate(0), 1); - t.equal((new StyleDeclaration('fill', 'fill-opacity', { fn: 'stops', stops: [[0, 0], [5, 10]] })).calculate(0), 2); - t.equal((new StyleDeclaration('fill', 'fill-opacity', { fn: 'stops', stops: [[0, 0], [5, 10]] })).calculate(10), 10); - t.equal((new StyleDeclaration('fill', 'fill-opacity', { fn: 'stops', stops: [[0, 0], [5, 10]] })).calculate(6), 10); - t.equal((new StyleDeclaration('fill', 'fill-opacity', { fn: 'stops', stops: [[0, 0], [0, 10]] })).calculate(6), 0); - t.equal((new StyleDeclaration('fill', 'fill-opacity', { fn: 'stops', stops: [[0, 0], [0, 10]] })).calculate(6), 0); - t.equal((new StyleDeclaration('fill', 'fill-opacity', { fn: 'stops', stops: [[0, 0], [1, 10], [2, 20]] })).calculate(2), 20); - t.equal((new StyleDeclaration('fill', 'fill-opacity', { fn: 'stops', stops: [[0, 0], [1, 10], [2, 20]] })).calculate(1), 20); - t.equal((new StyleDeclaration('fill', 'fill-opacity', { fn: 'stops', stops: [[0, 10], [2, 20]] })).calculate(1), 20); - t.equal((new StyleDeclaration('fill', 'fill-opacity', { fn: 'stops', stops: [[0, 0]] })).calculate(6), 0); - - t.throws(function() { - new StyleDeclaration('fill', 'fill-opacity', { fn: 'blah' }); - }, 'rejects unknown fns'); + 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.end(); }); @@ -62,7 +52,7 @@ test('styledeclaration', function(t) { 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', { fn: 'stops', stops: [[0, '#f00'], [1, '#0f0']] }).calculate(0), [0, 1, 0, 1]); + t.deepEqual(new StyleDeclaration('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 ]);