Skip to content

Commit

Permalink
Merge pull request #4761 from Dylan-Brown/ternary-fxns
Browse files Browse the repository at this point in the history
3D Tiles - Ternary Functions (fixed)
  • Loading branch information
lilleyse authored Dec 19, 2016
2 parents fdf4239 + 9ee6145 commit 31cf664
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 48 deletions.
5 changes: 5 additions & 0 deletions Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@
pointSize : "5"
});

addStyle('Clamp and Mix', {
color : "color() * clamp(${temperature}, 0.1, 0.2)",
pointSize : "mix(${temperature}, 2.0, 0.5) * 0.2"
});

addStyle('Secondary Color', {
color : {
expression : "[${secondaryColor}[0], ${secondaryColor}[1], ${secondaryColor}[2], 1.0]",
Expand Down
95 changes: 62 additions & 33 deletions Source/Scene/Expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ define([
degrees : CesiumMath.toDegrees
};

var ternaryFunctions = {
clamp : CesiumMath.clamp,
mix : CesiumMath.lerp
};

/**
* Evaluates an expression defined using the
* {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/Styling|3D Tiles Styling language}.
Expand Down Expand Up @@ -379,21 +384,31 @@ define([
return new Node(ExpressionNodeType.UNARY, call);
} else if (defined(unaryFunctions[call])) {
//>>includeStart('debug', pragmas.debug);
if (args.length < 1 || args.length > 1) {
if (args.length !== 1) {
throw new DeveloperError('Error: ' + call + ' requires exactly one argument.');
}
//>>includeEnd('debug');
val = createRuntimeAst(expression, args[0]);
return new Node(ExpressionNodeType.UNARY, call, val);
} else if (defined(binaryFunctions[call])) {
//>>includeStart('debug', pragmas.debug);
if (args.length < 2 || args.length > 2) {
if (args.length !== 2) {
throw new DeveloperError('Error: ' + call + ' requires exactly two arguments.');
}
//>>includeEnd('debug');
left = createRuntimeAst(expression, args[0]);
right = createRuntimeAst(expression, args[1]);
return new Node(ExpressionNodeType.BINARY, call, left, right);
} else if (defined(ternaryFunctions[call])) {
//>>includeStart('debug', pragmas.debug);
if (args.length !== 3) {
throw new DeveloperError('Error: ' + call + ' requires exactly three arguments.');
}
//>>includeEnd('debug');
left = createRuntimeAst(expression, args[0]);
right = createRuntimeAst(expression, args[1]);
var test = createRuntimeAst(expression, args[2]);
return new Node(ExpressionNodeType.TERNARY, call, left, right, test);
} else if (call === 'Boolean') {
if (args.length === 0) {
return new Node(ExpressionNodeType.LITERAL_BOOLEAN, false);
Expand Down Expand Up @@ -582,6 +597,32 @@ define([
} else if (node._value === 'toString') {
node.evaluate = node._evaluateToString;
}
} else if (node._type === ExpressionNodeType.UNARY) {
if (node._value === '!') {
node.evaluate = node._evaluateNot;
} else if (node._value === '-') {
node.evaluate = node._evaluateNegative;
} else if (node._value === '+') {
node.evaluate = node._evaluatePositive;
} else if (node._value === 'isNaN') {
node.evaluate = node._evaluateNaN;
} else if (node._value === 'isFinite') {
node.evaluate = node._evaluateIsFinite;
} else if (node._value === 'isExactClass') {
node.evaluate = node._evaluateIsExactClass;
} else if (node._value === 'isClass') {
node.evaluate = node._evaluateIsClass;
} else if (node._value === 'getExactClassName') {
node.evaluate = node._evaluategetExactClassName;
} else if (node._value === 'Boolean') {
node.evaluate = node._evaluateBooleanConversion;
} else if (node._value === 'Number') {
node.evaluate = node._evaluateNumberConversion;
} else if (node._value === 'String') {
node.evaluate = node._evaluateStringConversion;
} else if (defined(unaryFunctions[node._value])) {
node.evaluate = getEvaluateUnaryFunction(node._value);
}
} else if (node._type === ExpressionNodeType.BINARY) {
if (node._value === '+') {
node.evaluate = node._evaluatePlus;
Expand Down Expand Up @@ -620,32 +661,8 @@ define([
} else if (defined(binaryFunctions[node._value])) {
node.evaluate = getEvaluateBinaryFunction(node._value);
}
} else if (node._type === ExpressionNodeType.UNARY) {
if (node._value === '!') {
node.evaluate = node._evaluateNot;
} else if (node._value === '-') {
node.evaluate = node._evaluateNegative;
} else if (node._value === '+') {
node.evaluate = node._evaluatePositive;
} else if (node._value === 'isNaN') {
node.evaluate = node._evaluateNaN;
} else if (node._value === 'isFinite') {
node.evaluate = node._evaluateIsFinite;
} else if (node._value === 'isExactClass') {
node.evaluate = node._evaluateIsExactClass;
} else if (node._value === 'isClass') {
node.evaluate = node._evaluateIsClass;
} else if (node._value === 'getExactClassName') {
node.evaluate = node._evaluategetExactClassName;
} else if (defined(unaryFunctions[node._value])) {
node.evaluate = getEvaluateUnaryFunction(node._value);
} else if (node._value === 'Boolean') {
node.evaluate = node._evaluateBooleanConversion;
} else if (node._value === 'Number') {
node.evaluate = node._evaluateNumberConversion;
} else if (node._value === 'String') {
node.evaluate = node._evaluateStringConversion;
}
} else if (node._type === ExpressionNodeType.TERNARY) {
node.evaluate = getEvaluateTernaryFunction(node._value);
} else if (node._type === ExpressionNodeType.MEMBER) {
if (node._value === 'brackets') {
node.evaluate = node._evaluateMemberBrackets;
Expand Down Expand Up @@ -677,17 +694,24 @@ define([
return feature._content._tileset.timeSinceLoad;
}

function getEvaluateUnaryFunction(call) {
var evaluate = unaryFunctions[call];
return function(feature) {
return evaluate(this._left.evaluate(feature));
};
}

function getEvaluateBinaryFunction(call) {
var evaluate = binaryFunctions[call];
return function(feature) {
return evaluate(this._left.evaluate(feature), this._right.evaluate(feature));
};
}

function getEvaluateUnaryFunction(call) {
var evaluate = unaryFunctions[call];
function getEvaluateTernaryFunction(call) {
var evaluate = ternaryFunctions[call];
return function(feature) {
return evaluate(this._left.evaluate(feature));
return evaluate(this._left.evaluate(feature), this._right.evaluate(feature), this._test.evaluate(feature));
};
}

Expand Down Expand Up @@ -1217,14 +1241,14 @@ define([
return 'bool(' + left + ')';
} else if (value === 'Number') {
return 'float(' + left + ')';
} else if (defined(unaryFunctions[value])) {
return value + '(' + left + ')';
} else if (value === 'abs') {
return 'abs(' + left + ')';
} else if (value === 'cos') {
return 'cos(' + left + ')';
} else if (value === 'sqrt') {
return 'sqrt(' + left + ')';
} else if (defined(unaryFunctions[value])) {
return value + '(' + left + ')';
}
//>>includeStart('debug', pragmas.debug);
else if ((value === 'isNaN') || (value === 'isFinite') || (value === 'String') || (value === 'isExactClass') || (value === 'isClass') || (value === 'getExactClassName')) {
Expand All @@ -1246,6 +1270,11 @@ define([
return value + '(' + left + ', ' + right + ')';
}
return '(' + left + ' ' + value + ' ' + right + ')';
case ExpressionNodeType.TERNARY:
if (defined(ternaryFunctions[value])) {
return value + '(' + left + ', ' + right + ', ' + test + ')';
}
break;
case ExpressionNodeType.CONDITIONAL:
return '(' + test + ' ? ' + left + ' : ' + right + ')';
case ExpressionNodeType.MEMBER:
Expand Down
31 changes: 16 additions & 15 deletions Source/Scene/ExpressionNodeType.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*global define*/
define([
'../Core/freezeObject'
], function(
], function(
freezeObject) {
'use strict';

Expand All @@ -12,20 +12,21 @@ define([
VARIABLE : 0,
UNARY : 1,
BINARY : 2,
CONDITIONAL : 3,
MEMBER : 4,
FUNCTION_CALL : 5,
ARRAY : 6,
REGEX: 7,
VARIABLE_IN_STRING : 8,
LITERAL_NULL : 9,
LITERAL_BOOLEAN : 10,
LITERAL_NUMBER : 11,
LITERAL_STRING : 12,
LITERAL_COLOR : 13,
LITERAL_REGEX : 14,
LITERAL_UNDEFINED : 15,
LITERAL_GLOBAL : 16
TERNARY : 3,
CONDITIONAL : 4,
MEMBER : 5,
FUNCTION_CALL : 6,
ARRAY : 7,
REGEX: 8,
VARIABLE_IN_STRING : 9,
LITERAL_NULL : 10,
LITERAL_BOOLEAN : 11,
LITERAL_NUMBER : 12,
LITERAL_STRING : 13,
LITERAL_COLOR : 14,
LITERAL_REGEX : 15,
LITERAL_UNDEFINED : 16,
LITERAL_GLOBAL : 17
};

return freezeObject(ExpressionNodeType);
Expand Down
66 changes: 66 additions & 0 deletions Specs/Scene/ExpressionSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,58 @@ defineSuite([
}).toThrowDeveloperError();
});

it('evaluates clamp function', function() {
var expression = new Expression('clamp(50.0, 0.0, 100.0)');
expect(expression.evaluate(frameState, undefined)).toEqual(50.0);

expression = new Expression('clamp(50.0, 0.0, 25.0)');
expect(expression.evaluate(frameState, undefined)).toEqual(25.0);

expression = new Expression('clamp(50.0, 75.0, 100.0)');
expect(expression.evaluate(frameState, undefined)).toEqual(75.0);
});

it('throws if clamp function takes an invalid number of arguments', function() {
expect(function() {
return new Expression('clamp()');
}).toThrowDeveloperError();

expect(function() {
return new Expression('clamp(1)');
}).toThrowDeveloperError();

expect(function() {
return new Expression('clamp(1, 2)');
}).toThrowDeveloperError();

expect(function() {
return new Expression('clamp(1, 2, 3, 4)');
}).toThrowDeveloperError();
});

it('evaluates mix function', function() {
var expression = new Expression('mix(0.0, 2.0, 0.5)');
expect(expression.evaluate(frameState, undefined)).toEqual(1.0);
});

it('throws if mix function takes an invalid number of arguments', function() {
expect(function() {
return new Expression('mix()');
}).toThrowDeveloperError();

expect(function() {
return new Expression('mix(1)');
}).toThrowDeveloperError();

expect(function() {
return new Expression('mix(1, 2)');
}).toThrowDeveloperError();

expect(function() {
return new Expression('mix(1, 2, 3, 4)');
}).toThrowDeveloperError();
});

it('evaluates atan2 function', function() {
var expression = new Expression('atan2(0,1)');
expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(0.0, CesiumMath.EPSILON10);
Expand Down Expand Up @@ -1955,6 +2007,20 @@ defineSuite([
expect(shaderExpression).toEqual(expected);
});

it('gets shader expression for clamp', function() {
var expression = new Expression('clamp(50.0, 0.0, 100.0)');
var shaderExpression = expression.getShaderExpression('', {});
var expected = 'clamp(50.0, 0.0, 100.0)';
expect(shaderExpression).toEqual(expected);
});

it('gets shader expression for mix', function() {
var expression = new Expression('mix(0.0, 2.0, 0.5)');
var shaderExpression = expression.getShaderExpression('', {});
var expected = 'mix(0.0, 2.0, 0.5)';
expect(shaderExpression).toEqual(expected);
});

it('gets shader expression for atan2', function() {
var expression = new Expression('atan2(0.0,1.0)');
var shaderExpression = expression.getShaderExpression('', {});
Expand Down

0 comments on commit 31cf664

Please sign in to comment.