diff --git a/src/ol/style/LiteralStyle.js b/src/ol/style/LiteralStyle.js index 8c38ce92e8..b4cb84e320 100644 --- a/src/ol/style/LiteralStyle.js +++ b/src/ol/style/LiteralStyle.js @@ -17,11 +17,15 @@ * * `['time']` returns the time in seconds since the creation of the layer * * * Math operators: - * * `['*', value1, value1]` multiplies value1 by value2 - * * `['+', value1, value1]` adds value1 and value2 - * * `['clamp', value1, value2, value3]` clamps value1 between values2 and value3 - * * `['stretch', value1, value2, value3, value4, value5]` maps value1 from [value2, value3] range to - * [value4, value5] range, clamping values along the way + * * `['*', value1, value1]` multiplies `value1` by `value2` + * * `['+', value1, value1]` adds `value1` and `value2` + * * `['clamp', value, low, high]` clamps `value` between `low` and `high` + * * `['stretch', value, low1, high1, low2, high2]` maps `value` from [`low1`, `high1`] range to + * [`low2`, `high2`] range, clamping values along the way + * + * * Color operators: + * * `['interpolate', ratio, color1, color2]` returns a color through interpolation between `color1` and + * `color2` with the given `ratio` * * * Logical operators: * * `['<', value1, value2]` returns `1` if `value1` is strictly lower than value 2, or `0` otherwise. @@ -33,9 +37,13 @@ * * `['between', value1, value2, value3]` returns `1` if `value1` is contained between `value2` and `value3` * (inclusively), or `0` otherwise. * - * Values can either be literals (numbers) or another operator, as they will be evaluated recursively. + * Values can either be literals or another operator, as they will be evaluated recursively. + * Literal values can be of the following types: + * * `number` + * * `string` + * * {@link module:ol/color~Color} * - * @typedef {Array<*>|number} ExpressionValue + * @typedef {Array<*>|import("../color.js").Color|string|number} ExpressionValue */ /** diff --git a/src/ol/webgl/ShaderBuilder.js b/src/ol/webgl/ShaderBuilder.js index 15e74d736b..ead1aff6d1 100644 --- a/src/ol/webgl/ShaderBuilder.js +++ b/src/ol/webgl/ShaderBuilder.js @@ -642,11 +642,7 @@ export function parseLiteralStyle(style) { const symbStyle = style.symbol; const size = Array.isArray(symbStyle.size) && typeof symbStyle.size[0] == 'number' ? symbStyle.size : [symbStyle.size, symbStyle.size]; - const color = (typeof symbStyle.color === 'string' ? - asArray(symbStyle.color).map(function(c, i) { - return i < 3 ? c / 255 : c; - }) : - symbStyle.color || [255, 255, 255, 1]); + const color = symbStyle.color || 'white'; const texCoord = symbStyle.textureCoord || [0, 0, 1, 1]; const offset = symbStyle.offset || [0, 0]; const opacity = symbStyle.opacity !== undefined ? symbStyle.opacity : 1; @@ -660,8 +656,8 @@ export function parseLiteralStyle(style) { const fragAttributes = []; // parse function for fragment shader - function pFrag(value) { - return parse(value, fragAttributes, 'v_', variables); + function pFrag(value, type) { + return parse(value, fragAttributes, 'v_', variables, type); } let opacityFilter = '1.0'; @@ -682,6 +678,8 @@ export function parseLiteralStyle(style) { default: throw new Error('Unexpected symbol type: ' + symbStyle.symbolType); } + const parsedColor = pFrag(color, ValueTypes.COLOR); + const builder = new ShaderBuilder() .setSizeExpression(`vec2(${pVert(size[0])}, ${pVert(size[1])})`) .setSymbolOffsetExpression(`vec2(${pVert(offset[0])}, ${pVert(offset[1])})`) @@ -689,7 +687,7 @@ export function parseLiteralStyle(style) { `vec4(${pVert(texCoord[0])}, ${pVert(texCoord[1])}, ${pVert(texCoord[2])}, ${pVert(texCoord[3])})`) .setSymbolRotateWithView(!!symbStyle.rotateWithView) .setColorExpression( - `vec4(${pFrag(color[0])}, ${pFrag(color[1])}, ${pFrag(color[2])}, ${pFrag(color[3])} * ${pFrag(opacity)} * ${opacityFilter})`); + `vec4(${parsedColor}.rgb, ${parsedColor}.a * ${pFrag(opacity)} * ${opacityFilter})`); if (style.filter) { builder.setFragmentDiscardExpression(`${pFrag(style.filter)} <= 0.0`); diff --git a/test/spec/ol/webgl/shaderbuilder.test.js b/test/spec/ol/webgl/shaderbuilder.test.js index a9d339c59b..fc2ecf1593 100644 --- a/test/spec/ol/webgl/shaderbuilder.test.js +++ b/test/spec/ol/webgl/shaderbuilder.test.js @@ -417,7 +417,7 @@ void main(void) { symbol: { symbolType: 'square', size: [4, 8], - color: '#336699', + color: '#ff0000', rotateWithView: true } }); @@ -425,7 +425,8 @@ void main(void) { expect(result.builder.uniforms).to.eql([]); expect(result.builder.attributes).to.eql([]); expect(result.builder.varyings).to.eql([]); - expect(result.builder.colorExpression).to.eql('vec4(0.2, 0.4, 0.6, 1.0 * 1.0 * 1.0)'); + expect(result.builder.colorExpression).to.eql( + 'vec4(vec4(1.0, 0.0, 0.0, 1.0).rgb, vec4(1.0, 0.0, 0.0, 1.0).a * 1.0 * 1.0)'); expect(result.builder.sizeExpression).to.eql('vec2(4.0, 8.0)'); expect(result.builder.offsetExpression).to.eql('vec2(0.0, 0.0)'); expect(result.builder.texCoordExpression).to.eql('vec4(0.0, 0.0, 1.0, 1.0)'); @@ -439,35 +440,29 @@ void main(void) { symbol: { symbolType: 'square', size: ['get', 'attr1'], - color: [ - 1.0, 0.0, 0.5, ['get', 'attr2'] - ], + color: [255, 127.5, 63.75, 0.25], textureCoord: [0.5, 0.5, 0.5, 1], offset: [3, ['get', 'attr3']] } }); expect(result.builder.uniforms).to.eql([]); - expect(result.builder.attributes).to.eql(['float a_attr1', 'float a_attr3', 'float a_attr2']); + expect(result.builder.attributes).to.eql(['float a_attr1', 'float a_attr3']); expect(result.builder.varyings).to.eql([{ name: 'v_attr1', type: 'float', expression: 'a_attr1' - }, { - name: 'v_attr2', - type: 'float', - expression: 'a_attr2' }]); expect(result.builder.colorExpression).to.eql( - 'vec4(1.0, 0.0, 0.5, v_attr2 * 1.0 * 1.0)'); + 'vec4(vec4(1.0, 0.5, 0.25, 0.25).rgb, vec4(1.0, 0.5, 0.25, 0.25).a * 1.0 * 1.0)' + ); expect(result.builder.sizeExpression).to.eql('vec2(a_attr1, a_attr1)'); expect(result.builder.offsetExpression).to.eql('vec2(3.0, a_attr3)'); expect(result.builder.texCoordExpression).to.eql('vec4(0.5, 0.5, 0.5, 1.0)'); expect(result.builder.rotateWithView).to.eql(false); - expect(result.attributes.length).to.eql(3); + expect(result.attributes.length).to.eql(2); expect(result.attributes[0].name).to.eql('attr1'); expect(result.attributes[1].name).to.eql('attr3'); - expect(result.attributes[2].name).to.eql('attr2'); expect(result.uniforms).to.eql({}); }); @@ -486,7 +481,8 @@ void main(void) { expect(result.builder.attributes).to.eql([]); expect(result.builder.varyings).to.eql([]); expect(result.builder.colorExpression).to.eql( - 'vec4(0.2, 0.4, 0.6, 1.0 * 0.5 * 1.0) * texture2D(u_texture, v_texCoord)'); + 'vec4(vec4(0.2, 0.4, 0.6, 1.0).rgb, vec4(0.2, 0.4, 0.6, 1.0).a * 0.5 * 1.0) * texture2D(u_texture, v_texCoord)' + ); expect(result.builder.sizeExpression).to.eql('vec2(6.0, 6.0)'); expect(result.builder.offsetExpression).to.eql('vec2(0.0, 0.0)'); expect(result.builder.texCoordExpression).to.eql('vec4(0.0, 0.0, 1.0, 1.0)'); @@ -516,9 +512,12 @@ void main(void) { type: 'float', expression: 'a_population' }]); - expect(result.builder.colorExpression).to.eql('vec4(0.2, 0.4, 0.6, 1.0 * 0.5 * 1.0)'); + expect(result.builder.colorExpression).to.eql( + 'vec4(vec4(0.2, 0.4, 0.6, 1.0).rgb, vec4(0.2, 0.4, 0.6, 1.0).a * 0.5 * 1.0)' + ); expect(result.builder.sizeExpression).to.eql( - 'vec2((clamp(a_population, u_lower, u_higher) * ((8.0 - 4.0) / (u_higher - u_lower)) + 4.0), (clamp(a_population, u_lower, u_higher) * ((8.0 - 4.0) / (u_higher - u_lower)) + 4.0))'); + 'vec2((clamp(a_population, u_lower, u_higher) * ((8.0 - 4.0) / (u_higher - u_lower)) + 4.0), (clamp(a_population, u_lower, u_higher) * ((8.0 - 4.0) / (u_higher - u_lower)) + 4.0))' + ); expect(result.builder.offsetExpression).to.eql('vec2(0.0, 0.0)'); expect(result.builder.texCoordExpression).to.eql('vec4(0.0, 0.0, 1.0, 1.0)'); expect(result.builder.rotateWithView).to.eql(false); @@ -544,7 +543,9 @@ void main(void) { type: 'float', expression: 'a_attr0' }]); - expect(result.builder.colorExpression).to.eql('vec4(0.2, 0.4, 0.6, 1.0 * 1.0 * 1.0)'); + expect(result.builder.colorExpression).to.eql( + 'vec4(vec4(0.2, 0.4, 0.6, 1.0).rgb, vec4(0.2, 0.4, 0.6, 1.0).a * 1.0 * 1.0)' + ); expect(result.builder.sizeExpression).to.eql('vec2(6.0, 6.0)'); expect(result.builder.offsetExpression).to.eql('vec2(0.0, 0.0)'); expect(result.builder.texCoordExpression).to.eql('vec4(0.0, 0.0, 1.0, 1.0)'); @@ -553,6 +554,28 @@ void main(void) { expect(result.attributes.length).to.eql(1); expect(result.attributes[0].name).to.eql('attr0'); }); + + it('parses a style with a color interpolation', function() { + const result = parseLiteralStyle({ + symbol: { + symbolType: 'square', + size: 6, + color: ['interpolate', ['var', 'ratio'], [255, 255, 0], 'red'] + } + }); + + expect(result.builder.attributes).to.eql([]); + expect(result.builder.varyings).to.eql([]); + expect(result.builder.colorExpression).to.eql( + 'vec4(mix(vec4(1.0, 1.0, 0.0, 1.0), vec4(1.0, 0.0, 0.0, 1.0), u_ratio).rgb, mix(vec4(1.0, 1.0, 0.0, 1.0), vec4(1.0, 0.0, 0.0, 1.0), u_ratio).a * 1.0 * 1.0)' + ); + expect(result.builder.sizeExpression).to.eql('vec2(6.0, 6.0)'); + expect(result.builder.offsetExpression).to.eql('vec2(0.0, 0.0)'); + expect(result.builder.texCoordExpression).to.eql('vec4(0.0, 0.0, 1.0, 1.0)'); + expect(result.builder.rotateWithView).to.eql(false); + expect(result.attributes).to.eql([]); + expect(result.uniforms).to.have.property('u_ratio'); + }); }); });