diff --git a/src/ol/style/expressions.js b/src/ol/style/expressions.js index 8000271db5..f1f51dcb2b 100644 --- a/src/ol/style/expressions.js +++ b/src/ol/style/expressions.js @@ -50,6 +50,13 @@ import {asArray, isStringColor} from '../color.js'; * * `['between', value1, value2, value3]` returns `1` if `value1` is contained between `value2` and `value3` * (inclusively), or `0` otherwise. * + * * Conversion operators: + * * `['array', value1, ...valueN]` creates a numerical array from `number` values; please note that the amount of + * values can currently only be 2, 3 or 4. + * * `['color', red, green, blue, alpha]` creates a `color` value from `number` values; the `alpha` parameter is + * optional; if not specified, it will be set to 1. + * Note: `red`, `green` and `blue` components must be values between 0 and 255; `alpha` between 0 and 1. + * * Values can either be literals or another operator, as they will be evaluated recursively. * Literal values can be of the following types: * * `number` @@ -265,6 +272,11 @@ function assertArgsMinCount(args, count) { throw new Error(`At least ${count} arguments were expected, got ${args.length} instead`); } } +function assertArgsMaxCount(args, count) { + if (args.length > count) { + throw new Error(`At most ${count} arguments were expected, got ${args.length} instead`); + } +} function assertArgsEven(args) { if (args.length % 2 !== 0) { throw new Error(`An even amount of arguments was expected, got ${args} instead`); @@ -492,6 +504,42 @@ Operators['between'] = { return `(${value} >= ${min} && ${value} <= ${max})`; } }; +Operators['array'] = { + getReturnType: function(args) { + return ValueTypes.NUMBER_ARRAY; + }, + toGlsl: function(context, args) { + assertArgsMinCount(args, 2); + assertArgsMaxCount(args, 4); + for (let i = 0; i < args.length; i++) { + assertNumber(args[i]); + } + const parsedArgs = args.map(function(val) { + return expressionToGlsl(context, val, ValueTypes.NUMBER); + }); + return `vec${args.length}(${parsedArgs.join(', ')})`; + } +}; +Operators['color'] = { + getReturnType: function(args) { + return ValueTypes.COLOR; + }, + toGlsl: function(context, args) { + assertArgsMinCount(args, 3); + assertArgsMaxCount(args, 4); + for (let i = 0; i < args.length; i++) { + assertNumber(args[i]); + } + const array = /** @type {number[]} */(args); + if (args.length === 3) { + array.push(1); + } + const parsedArgs = args.map(function(val, i) { + return expressionToGlsl(context, val, ValueTypes.NUMBER) + (i < 3 ? ' / 255.0' : ''); + }); + return `vec${args.length}(${parsedArgs.join(', ')})`; + } +}; Operators['interpolate'] = { getReturnType: function(args) { let type = ValueTypes.COLOR | ValueTypes.NUMBER; diff --git a/test/spec/ol/style/expressions.test.js b/test/spec/ol/style/expressions.test.js index 9a0519d9b8..d2aa7bf273 100644 --- a/test/spec/ol/style/expressions.test.js +++ b/test/spec/ol/style/expressions.test.js @@ -138,7 +138,8 @@ describe('ol.style.expressions', function() { expect(getValueType(['==', 10, ['get', 'attr4']])).to.eql(ValueTypes.BOOLEAN); expect(getValueType(['between', ['get', 'attr4'], -4.0, 5.0])).to.eql(ValueTypes.BOOLEAN); expect(getValueType(['!', ['get', 'attr4']])).to.eql(ValueTypes.BOOLEAN); - expect(getValueType(['interpolate', ['get', 'attr4'], [255, 255, 255, 1], 'transparent'])).to.eql(ValueTypes.COLOR); + expect(getValueType(['array', ['get', 'attr4'], 1, 2, 3])).to.eql(ValueTypes.NUMBER_ARRAY); + expect(getValueType(['color', ['get', 'attr4'], 1, 2])).to.eql(ValueTypes.COLOR); }); }); @@ -171,6 +172,8 @@ describe('ol.style.expressions', function() { expect(expressionToGlsl(context, ['==', 10, ['get', 'attr4']])).to.eql('(10.0 == a_attr4)'); expect(expressionToGlsl(context, ['between', ['get', 'attr4'], -4.0, 5.0])).to.eql('(a_attr4 >= -4.0 && a_attr4 <= 5.0)'); expect(expressionToGlsl(context, ['!', ['get', 'attr4']])).to.eql('(!a_attr4)'); + expect(expressionToGlsl(context, ['array', ['get', 'attr4'], 1, 2, 3])).to.eql('vec4(a_attr4, 1.0, 2.0, 3.0)'); + expect(expressionToGlsl(context, ['color', ['get', 'attr4'], 1, 2, 0.5])).to.eql('vec4(a_attr4 / 255.0, 1.0 / 255.0, 2.0 / 255.0, 0.5)'); }); it('correctly adapts output for fragment shaders', function() { @@ -201,17 +204,39 @@ describe('ol.style.expressions', function() { } catch (e) { thrown = true; } + expect(thrown).to.be(true); + + thrown = false; try { expressionToGlsl(context, ['<', 0, 'aa']); } catch (e) { thrown = true; } + expect(thrown).to.be(true); + + thrown = false; try { expressionToGlsl(context, ['+', true, ['get', 'attr']]); } catch (e) { thrown = true; } expect(thrown).to.be(true); + + thrown = false; + try { + expressionToGlsl(context, ['color', 1, 2, 'red']); + } catch (e) { + thrown = true; + } + expect(thrown).to.be(true); + + thrown = false; + try { + expressionToGlsl(context, ['array', 1, '2', 3]); + } catch (e) { + thrown = true; + } + expect(thrown).to.be(true); }); it('throws with the wrong number of arguments', function() { @@ -221,31 +246,43 @@ describe('ol.style.expressions', function() { } catch (e) { thrown = true; } + expect(thrown).to.be(true); + + thrown = false; try { expressionToGlsl(context, ['<', 4]); } catch (e) { thrown = true; } + expect(thrown).to.be(true); + + thrown = false; try { expressionToGlsl(context, ['+']); } catch (e) { thrown = true; } expect(thrown).to.be(true); + + thrown = false; + try { + expressionToGlsl(context, ['array', 1]); + } catch (e) { + thrown = true; + } + expect(thrown).to.be(true); + + thrown = false; + try { + expressionToGlsl(context, ['color', 1, 2, 3, 4, 5]); + } catch (e) { + thrown = true; + } + expect(thrown).to.be(true); }); it('throws on invalid expressions', function() { let thrown = false; - try { - expressionToGlsl(context, true); - } catch (e) { - thrown = true; - } - try { - expressionToGlsl(context, [123, 456]); - } catch (e) { - thrown = true; - } try { expressionToGlsl(context, null); } catch (e) {