Skip to content

Commit 31cf664

Browse files
authored
Merge pull request #4761 from Dylan-Brown/ternary-fxns
3D Tiles - Ternary Functions (fixed)
2 parents fdf4239 + 9ee6145 commit 31cf664

File tree

4 files changed

+149
-48
lines changed

4 files changed

+149
-48
lines changed

Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html

+5
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@
134134
pointSize : "5"
135135
});
136136

137+
addStyle('Clamp and Mix', {
138+
color : "color() * clamp(${temperature}, 0.1, 0.2)",
139+
pointSize : "mix(${temperature}, 2.0, 0.5) * 0.2"
140+
});
141+
137142
addStyle('Secondary Color', {
138143
color : {
139144
expression : "[${secondaryColor}[0], ${secondaryColor}[1], ${secondaryColor}[2], 1.0]",

Source/Scene/Expression.js

+62-33
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ define([
6565
degrees : CesiumMath.toDegrees
6666
};
6767

68+
var ternaryFunctions = {
69+
clamp : CesiumMath.clamp,
70+
mix : CesiumMath.lerp
71+
};
72+
6873
/**
6974
* Evaluates an expression defined using the
7075
* {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/Styling|3D Tiles Styling language}.
@@ -379,21 +384,31 @@ define([
379384
return new Node(ExpressionNodeType.UNARY, call);
380385
} else if (defined(unaryFunctions[call])) {
381386
//>>includeStart('debug', pragmas.debug);
382-
if (args.length < 1 || args.length > 1) {
387+
if (args.length !== 1) {
383388
throw new DeveloperError('Error: ' + call + ' requires exactly one argument.');
384389
}
385390
//>>includeEnd('debug');
386391
val = createRuntimeAst(expression, args[0]);
387392
return new Node(ExpressionNodeType.UNARY, call, val);
388393
} else if (defined(binaryFunctions[call])) {
389394
//>>includeStart('debug', pragmas.debug);
390-
if (args.length < 2 || args.length > 2) {
395+
if (args.length !== 2) {
391396
throw new DeveloperError('Error: ' + call + ' requires exactly two arguments.');
392397
}
393398
//>>includeEnd('debug');
394399
left = createRuntimeAst(expression, args[0]);
395400
right = createRuntimeAst(expression, args[1]);
396401
return new Node(ExpressionNodeType.BINARY, call, left, right);
402+
} else if (defined(ternaryFunctions[call])) {
403+
//>>includeStart('debug', pragmas.debug);
404+
if (args.length !== 3) {
405+
throw new DeveloperError('Error: ' + call + ' requires exactly three arguments.');
406+
}
407+
//>>includeEnd('debug');
408+
left = createRuntimeAst(expression, args[0]);
409+
right = createRuntimeAst(expression, args[1]);
410+
var test = createRuntimeAst(expression, args[2]);
411+
return new Node(ExpressionNodeType.TERNARY, call, left, right, test);
397412
} else if (call === 'Boolean') {
398413
if (args.length === 0) {
399414
return new Node(ExpressionNodeType.LITERAL_BOOLEAN, false);
@@ -582,6 +597,32 @@ define([
582597
} else if (node._value === 'toString') {
583598
node.evaluate = node._evaluateToString;
584599
}
600+
} else if (node._type === ExpressionNodeType.UNARY) {
601+
if (node._value === '!') {
602+
node.evaluate = node._evaluateNot;
603+
} else if (node._value === '-') {
604+
node.evaluate = node._evaluateNegative;
605+
} else if (node._value === '+') {
606+
node.evaluate = node._evaluatePositive;
607+
} else if (node._value === 'isNaN') {
608+
node.evaluate = node._evaluateNaN;
609+
} else if (node._value === 'isFinite') {
610+
node.evaluate = node._evaluateIsFinite;
611+
} else if (node._value === 'isExactClass') {
612+
node.evaluate = node._evaluateIsExactClass;
613+
} else if (node._value === 'isClass') {
614+
node.evaluate = node._evaluateIsClass;
615+
} else if (node._value === 'getExactClassName') {
616+
node.evaluate = node._evaluategetExactClassName;
617+
} else if (node._value === 'Boolean') {
618+
node.evaluate = node._evaluateBooleanConversion;
619+
} else if (node._value === 'Number') {
620+
node.evaluate = node._evaluateNumberConversion;
621+
} else if (node._value === 'String') {
622+
node.evaluate = node._evaluateStringConversion;
623+
} else if (defined(unaryFunctions[node._value])) {
624+
node.evaluate = getEvaluateUnaryFunction(node._value);
625+
}
585626
} else if (node._type === ExpressionNodeType.BINARY) {
586627
if (node._value === '+') {
587628
node.evaluate = node._evaluatePlus;
@@ -620,32 +661,8 @@ define([
620661
} else if (defined(binaryFunctions[node._value])) {
621662
node.evaluate = getEvaluateBinaryFunction(node._value);
622663
}
623-
} else if (node._type === ExpressionNodeType.UNARY) {
624-
if (node._value === '!') {
625-
node.evaluate = node._evaluateNot;
626-
} else if (node._value === '-') {
627-
node.evaluate = node._evaluateNegative;
628-
} else if (node._value === '+') {
629-
node.evaluate = node._evaluatePositive;
630-
} else if (node._value === 'isNaN') {
631-
node.evaluate = node._evaluateNaN;
632-
} else if (node._value === 'isFinite') {
633-
node.evaluate = node._evaluateIsFinite;
634-
} else if (node._value === 'isExactClass') {
635-
node.evaluate = node._evaluateIsExactClass;
636-
} else if (node._value === 'isClass') {
637-
node.evaluate = node._evaluateIsClass;
638-
} else if (node._value === 'getExactClassName') {
639-
node.evaluate = node._evaluategetExactClassName;
640-
} else if (defined(unaryFunctions[node._value])) {
641-
node.evaluate = getEvaluateUnaryFunction(node._value);
642-
} else if (node._value === 'Boolean') {
643-
node.evaluate = node._evaluateBooleanConversion;
644-
} else if (node._value === 'Number') {
645-
node.evaluate = node._evaluateNumberConversion;
646-
} else if (node._value === 'String') {
647-
node.evaluate = node._evaluateStringConversion;
648-
}
664+
} else if (node._type === ExpressionNodeType.TERNARY) {
665+
node.evaluate = getEvaluateTernaryFunction(node._value);
649666
} else if (node._type === ExpressionNodeType.MEMBER) {
650667
if (node._value === 'brackets') {
651668
node.evaluate = node._evaluateMemberBrackets;
@@ -677,17 +694,24 @@ define([
677694
return feature._content._tileset.timeSinceLoad;
678695
}
679696

697+
function getEvaluateUnaryFunction(call) {
698+
var evaluate = unaryFunctions[call];
699+
return function(feature) {
700+
return evaluate(this._left.evaluate(feature));
701+
};
702+
}
703+
680704
function getEvaluateBinaryFunction(call) {
681705
var evaluate = binaryFunctions[call];
682706
return function(feature) {
683707
return evaluate(this._left.evaluate(feature), this._right.evaluate(feature));
684708
};
685709
}
686710

687-
function getEvaluateUnaryFunction(call) {
688-
var evaluate = unaryFunctions[call];
711+
function getEvaluateTernaryFunction(call) {
712+
var evaluate = ternaryFunctions[call];
689713
return function(feature) {
690-
return evaluate(this._left.evaluate(feature));
714+
return evaluate(this._left.evaluate(feature), this._right.evaluate(feature), this._test.evaluate(feature));
691715
};
692716
}
693717

@@ -1217,14 +1241,14 @@ define([
12171241
return 'bool(' + left + ')';
12181242
} else if (value === 'Number') {
12191243
return 'float(' + left + ')';
1220-
} else if (defined(unaryFunctions[value])) {
1221-
return value + '(' + left + ')';
12221244
} else if (value === 'abs') {
12231245
return 'abs(' + left + ')';
12241246
} else if (value === 'cos') {
12251247
return 'cos(' + left + ')';
12261248
} else if (value === 'sqrt') {
12271249
return 'sqrt(' + left + ')';
1250+
} else if (defined(unaryFunctions[value])) {
1251+
return value + '(' + left + ')';
12281252
}
12291253
//>>includeStart('debug', pragmas.debug);
12301254
else if ((value === 'isNaN') || (value === 'isFinite') || (value === 'String') || (value === 'isExactClass') || (value === 'isClass') || (value === 'getExactClassName')) {
@@ -1246,6 +1270,11 @@ define([
12461270
return value + '(' + left + ', ' + right + ')';
12471271
}
12481272
return '(' + left + ' ' + value + ' ' + right + ')';
1273+
case ExpressionNodeType.TERNARY:
1274+
if (defined(ternaryFunctions[value])) {
1275+
return value + '(' + left + ', ' + right + ', ' + test + ')';
1276+
}
1277+
break;
12491278
case ExpressionNodeType.CONDITIONAL:
12501279
return '(' + test + ' ? ' + left + ' : ' + right + ')';
12511280
case ExpressionNodeType.MEMBER:

Source/Scene/ExpressionNodeType.js

+16-15
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*global define*/
22
define([
33
'../Core/freezeObject'
4-
], function(
4+
], function(
55
freezeObject) {
66
'use strict';
77

@@ -12,20 +12,21 @@ define([
1212
VARIABLE : 0,
1313
UNARY : 1,
1414
BINARY : 2,
15-
CONDITIONAL : 3,
16-
MEMBER : 4,
17-
FUNCTION_CALL : 5,
18-
ARRAY : 6,
19-
REGEX: 7,
20-
VARIABLE_IN_STRING : 8,
21-
LITERAL_NULL : 9,
22-
LITERAL_BOOLEAN : 10,
23-
LITERAL_NUMBER : 11,
24-
LITERAL_STRING : 12,
25-
LITERAL_COLOR : 13,
26-
LITERAL_REGEX : 14,
27-
LITERAL_UNDEFINED : 15,
28-
LITERAL_GLOBAL : 16
15+
TERNARY : 3,
16+
CONDITIONAL : 4,
17+
MEMBER : 5,
18+
FUNCTION_CALL : 6,
19+
ARRAY : 7,
20+
REGEX: 8,
21+
VARIABLE_IN_STRING : 9,
22+
LITERAL_NULL : 10,
23+
LITERAL_BOOLEAN : 11,
24+
LITERAL_NUMBER : 12,
25+
LITERAL_STRING : 13,
26+
LITERAL_COLOR : 14,
27+
LITERAL_REGEX : 15,
28+
LITERAL_UNDEFINED : 16,
29+
LITERAL_GLOBAL : 17
2930
};
3031

3132
return freezeObject(ExpressionNodeType);

Specs/Scene/ExpressionSpec.js

+66
Original file line numberDiff line numberDiff line change
@@ -1064,6 +1064,58 @@ defineSuite([
10641064
}).toThrowDeveloperError();
10651065
});
10661066

1067+
it('evaluates clamp function', function() {
1068+
var expression = new Expression('clamp(50.0, 0.0, 100.0)');
1069+
expect(expression.evaluate(frameState, undefined)).toEqual(50.0);
1070+
1071+
expression = new Expression('clamp(50.0, 0.0, 25.0)');
1072+
expect(expression.evaluate(frameState, undefined)).toEqual(25.0);
1073+
1074+
expression = new Expression('clamp(50.0, 75.0, 100.0)');
1075+
expect(expression.evaluate(frameState, undefined)).toEqual(75.0);
1076+
});
1077+
1078+
it('throws if clamp function takes an invalid number of arguments', function() {
1079+
expect(function() {
1080+
return new Expression('clamp()');
1081+
}).toThrowDeveloperError();
1082+
1083+
expect(function() {
1084+
return new Expression('clamp(1)');
1085+
}).toThrowDeveloperError();
1086+
1087+
expect(function() {
1088+
return new Expression('clamp(1, 2)');
1089+
}).toThrowDeveloperError();
1090+
1091+
expect(function() {
1092+
return new Expression('clamp(1, 2, 3, 4)');
1093+
}).toThrowDeveloperError();
1094+
});
1095+
1096+
it('evaluates mix function', function() {
1097+
var expression = new Expression('mix(0.0, 2.0, 0.5)');
1098+
expect(expression.evaluate(frameState, undefined)).toEqual(1.0);
1099+
});
1100+
1101+
it('throws if mix function takes an invalid number of arguments', function() {
1102+
expect(function() {
1103+
return new Expression('mix()');
1104+
}).toThrowDeveloperError();
1105+
1106+
expect(function() {
1107+
return new Expression('mix(1)');
1108+
}).toThrowDeveloperError();
1109+
1110+
expect(function() {
1111+
return new Expression('mix(1, 2)');
1112+
}).toThrowDeveloperError();
1113+
1114+
expect(function() {
1115+
return new Expression('mix(1, 2, 3, 4)');
1116+
}).toThrowDeveloperError();
1117+
});
1118+
10671119
it('evaluates atan2 function', function() {
10681120
var expression = new Expression('atan2(0,1)');
10691121
expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(0.0, CesiumMath.EPSILON10);
@@ -1955,6 +2007,20 @@ defineSuite([
19552007
expect(shaderExpression).toEqual(expected);
19562008
});
19572009

2010+
it('gets shader expression for clamp', function() {
2011+
var expression = new Expression('clamp(50.0, 0.0, 100.0)');
2012+
var shaderExpression = expression.getShaderExpression('', {});
2013+
var expected = 'clamp(50.0, 0.0, 100.0)';
2014+
expect(shaderExpression).toEqual(expected);
2015+
});
2016+
2017+
it('gets shader expression for mix', function() {
2018+
var expression = new Expression('mix(0.0, 2.0, 0.5)');
2019+
var shaderExpression = expression.getShaderExpression('', {});
2020+
var expected = 'mix(0.0, 2.0, 0.5)';
2021+
expect(shaderExpression).toEqual(expected);
2022+
});
2023+
19582024
it('gets shader expression for atan2', function() {
19592025
var expression = new Expression('atan2(0.0,1.0)');
19602026
var shaderExpression = expression.getShaderExpression('', {});

0 commit comments

Comments
 (0)