Expressions / introduced the case operator
This operator is used for if/else control flow
This commit is contained in:
@@ -27,6 +27,9 @@ import {asArray, isStringColor} from '../color.js';
|
|||||||
* * `['^', value1, value1]` returns the value of `value1` raised to the `value2` power
|
* * `['^', value1, value1]` returns the value of `value1` raised to the `value2` power
|
||||||
*
|
*
|
||||||
* * Transform operators:
|
* * Transform operators:
|
||||||
|
* * `['case', condition1, output1, ...conditionN, outputN, fallback]` selects the first output whose corresponding
|
||||||
|
* condition evaluates to `true`. If no match is found, returns the `fallback` value.
|
||||||
|
* All conditions should be `boolean`, output and fallback can be any kind.
|
||||||
* * `['match', input, match1, output1, ...matchN, outputN, fallback]` compares the `input` value against all
|
* * `['match', input, match1, output1, ...matchN, outputN, fallback]` compares the `input` value against all
|
||||||
* provided `matchX` values, returning the output associated with the first valid match. If no match is found,
|
* provided `matchX` values, returning the output associated with the first valid match. If no match is found,
|
||||||
* returns the `fallback` value.
|
* returns the `fallback` value.
|
||||||
@@ -289,6 +292,11 @@ function assertArgsEven(args) {
|
|||||||
throw new Error(`An even amount of arguments was expected, got ${args} instead`);
|
throw new Error(`An even amount of arguments was expected, got ${args} instead`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function assertArgsOdd(args) {
|
||||||
|
if (args.length % 2 === 0) {
|
||||||
|
throw new Error(`An even amount of arguments was expected, got ${args} instead`);
|
||||||
|
}
|
||||||
|
}
|
||||||
function assertUniqueInferredType(args, types) {
|
function assertUniqueInferredType(args, types) {
|
||||||
if (!isTypeUnique(types)) {
|
if (!isTypeUnique(types)) {
|
||||||
throw new Error(`Could not infer only one type from the following expression: ${JSON.stringify(args)}`);
|
throw new Error(`Could not infer only one type from the following expression: ${JSON.stringify(args)}`);
|
||||||
@@ -601,7 +609,6 @@ Operators['match'] = {
|
|||||||
assertArgsEven(args);
|
assertArgsEven(args);
|
||||||
assertArgsMinCount(args, 4);
|
assertArgsMinCount(args, 4);
|
||||||
|
|
||||||
// compute input/output types
|
|
||||||
const typeHint = opt_typeHint !== undefined ? opt_typeHint : ValueTypes.ANY;
|
const typeHint = opt_typeHint !== undefined ? opt_typeHint : ValueTypes.ANY;
|
||||||
const outputType = Operators['match'].getReturnType(args) & typeHint;
|
const outputType = Operators['match'].getReturnType(args) & typeHint;
|
||||||
assertUniqueInferredType(args, outputType);
|
assertUniqueInferredType(args, outputType);
|
||||||
@@ -617,3 +624,33 @@ Operators['match'] = {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Operators['case'] = {
|
||||||
|
getReturnType: function(args) {
|
||||||
|
let type = ValueTypes.ANY;
|
||||||
|
for (let i = 1; i < args.length; i += 2) {
|
||||||
|
type = type & getValueType(args[i]);
|
||||||
|
}
|
||||||
|
type = type & getValueType(args[args.length - 1]);
|
||||||
|
return type;
|
||||||
|
},
|
||||||
|
toGlsl: function(context, args, opt_typeHint) {
|
||||||
|
assertArgsOdd(args);
|
||||||
|
assertArgsMinCount(args, 3);
|
||||||
|
|
||||||
|
const typeHint = opt_typeHint !== undefined ? opt_typeHint : ValueTypes.ANY;
|
||||||
|
const outputType = Operators['case'].getReturnType(args) & typeHint;
|
||||||
|
assertUniqueInferredType(args, outputType);
|
||||||
|
for (let i = 0; i < args.length - 1; i += 2) {
|
||||||
|
assertBoolean(args[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fallback = expressionToGlsl(context, args[args.length - 1], outputType);
|
||||||
|
let result = null;
|
||||||
|
for (let i = args.length - 3; i >= 0; i -= 2) {
|
||||||
|
const condition = expressionToGlsl(context, args[i]);
|
||||||
|
const output = expressionToGlsl(context, args[i + 1], outputType);
|
||||||
|
result = `(${condition} ? ${output} : ${result || fallback})`;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -294,6 +294,94 @@ describe('ol.style.expressions', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('case operator', function() {
|
||||||
|
let context;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
context = {
|
||||||
|
variables: [],
|
||||||
|
attributes: [],
|
||||||
|
stringLiteralsMap: {}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly guesses the output type', function() {
|
||||||
|
expect(getValueType(['case', true, 0, false, [3, 4, 5], 'green']))
|
||||||
|
.to.eql(ValueTypes.NONE);
|
||||||
|
expect(getValueType(['case', true, 0, false, 1, 2]))
|
||||||
|
.to.eql(ValueTypes.NUMBER);
|
||||||
|
expect(getValueType(['case', true, [0, 0, 0], true, [1, 2, 3], ['get', 'attr'], [4, 5, 6, 7], [8, 9, 0]]))
|
||||||
|
.to.eql(ValueTypes.COLOR | ValueTypes.NUMBER_ARRAY);
|
||||||
|
expect(getValueType(['case', true, 'red', true, 'yellow', ['get', 'attr'], 'green', 'white']))
|
||||||
|
.to.eql(ValueTypes.COLOR | ValueTypes.STRING);
|
||||||
|
expect(getValueType(['case', true, [0, 0], false, [1, 1], [2, 2]]))
|
||||||
|
.to.eql(ValueTypes.NUMBER_ARRAY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if no single output type could be inferred', function() {
|
||||||
|
let thrown = false;
|
||||||
|
try {
|
||||||
|
expressionToGlsl(context, ['case', false, 'red', true, 'yellow', 'green'], ValueTypes.COLOR);
|
||||||
|
} catch (e) {
|
||||||
|
thrown = true;
|
||||||
|
}
|
||||||
|
expect(thrown).to.be(false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
expressionToGlsl(context, ['case', true, 'red', true, 'yellow', 'green']);
|
||||||
|
} catch (e) {
|
||||||
|
thrown = true;
|
||||||
|
}
|
||||||
|
expect(thrown).to.be(true);
|
||||||
|
|
||||||
|
thrown = false;
|
||||||
|
try {
|
||||||
|
expressionToGlsl(context, ['case', true, 'red', false, 'yellow', 'green'], ValueTypes.NUMBER);
|
||||||
|
} catch (e) {
|
||||||
|
thrown = true;
|
||||||
|
}
|
||||||
|
expect(thrown).to.be(true);
|
||||||
|
|
||||||
|
thrown = false;
|
||||||
|
try {
|
||||||
|
expressionToGlsl(context, ['case', true, 'red', false, 'yellow', 'not_a_color'], ValueTypes.COLOR);
|
||||||
|
} catch (e) {
|
||||||
|
thrown = true;
|
||||||
|
}
|
||||||
|
expect(thrown).to.be(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if invalid argument count', function() {
|
||||||
|
let thrown = false;
|
||||||
|
try {
|
||||||
|
expressionToGlsl(context, ['case', true, 0, false, 1]);
|
||||||
|
} catch (e) {
|
||||||
|
thrown = true;
|
||||||
|
}
|
||||||
|
expect(thrown).to.be(true);
|
||||||
|
|
||||||
|
thrown = false;
|
||||||
|
try {
|
||||||
|
expressionToGlsl(context, ['case', true, 0]);
|
||||||
|
} catch (e) {
|
||||||
|
thrown = true;
|
||||||
|
}
|
||||||
|
expect(thrown).to.be(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
expressionToGlsl(context, ['case', false]);
|
||||||
|
} catch (e) {
|
||||||
|
thrown = true;
|
||||||
|
}
|
||||||
|
expect(thrown).to.be(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly parses the expression (colors)', function() {
|
||||||
|
expect(expressionToGlsl(context, ['case', ['>', ['get', 'attr'], 3], 'red', ['>', ['get', 'attr'], 1], 'yellow', 'white'], ValueTypes.COLOR))
|
||||||
|
.to.eql('((a_attr > 3.0) ? vec4(1.0, 0.0, 0.0, 1.0) : ((a_attr > 1.0) ? vec4(1.0, 1.0, 0.0, 1.0) : vec4(1.0, 1.0, 1.0, 1.0)))');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('match operator', function() {
|
describe('match operator', function() {
|
||||||
let context;
|
let context;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user