diff --git a/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html b/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html
index 3fe63914b5f8..733b9d6edc37 100644
--- a/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html
+++ b/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html
@@ -130,7 +130,7 @@
});
addStyle('Min and Max', {
- color : "rgb(min(${POSITION}[0], 0.75) * 255, max(${POSITION}[2], 0.25) * 255, 255)",
+ color : "rgb(min(${POSITION}.x, 0.75) * 255, max(${POSITION}.z, 0.25) * 255, 255)",
pointSize : "5"
});
@@ -141,12 +141,11 @@
addStyle('Secondary Color', {
color : {
- expression : "[${secondaryColor}[0], ${secondaryColor}[1], ${secondaryColor}[2], 1.0]",
conditions : [
- ["${id} < 250", "${expression}"],
- ["${id} < 500", "${expression} * ${expression}"],
- ["${id} < 750", "${expression} / 5.0"],
- ["${id} < 1000", "rgb(0, 0, Number(${expression}[0] < 0.5) * 255)"]
+ ["${id} < 250", "vec4(${secondaryColor}, 1.0)"],
+ ["${id} < 500", "vec4(${secondaryColor} * ${secondaryColor}, 1.0)"],
+ ["${id} < 750", "vec4(${secondaryColor} / 5.0, 1.0)"],
+ ["${id} < 1000", "rgb(0, 0, Number(${secondaryColor}.x < 0.5) * 255)"]
]
}
});
@@ -159,8 +158,9 @@
show : "${POSITION}[0] > 0.5 || ${POSITION}[1] > 0.5 || ${POSITION}[2] > 0.5"
});
+// POSITION contains 0 as its last component, so add 1.0 to make the point cloud opaque
addStyle('Color based on position', {
- color : "rgb(${POSITION}[0] * 255, ${POSITION}[1] * 255, ${POSITION}[2] * 255)"
+ color : "vec4(${POSITION}, 1.0)"
});
addStyle('Style point size', {
diff --git a/Source/Scene/Expression.js b/Source/Scene/Expression.js
index d3eaaa8b4f55..17ae36798635 100644
--- a/Source/Scene/Expression.js
+++ b/Source/Scene/Expression.js
@@ -1,5 +1,8 @@
/*global define*/
define([
+ '../Core/Cartesian2',
+ '../Core/Cartesian3',
+ '../Core/Cartesian4',
'../Core/Color',
'../Core/defined',
'../Core/defineProperties',
@@ -9,6 +12,9 @@ define([
'../ThirdParty/jsep',
'./ExpressionNodeType'
], function(
+ Cartesian2,
+ Cartesian3,
+ Cartesian4,
Color,
defined,
defineProperties,
@@ -31,17 +37,53 @@ define([
var ScratchStorage = {
scratchColorIndex : 0,
- scratchColors : [new Color()],
+ scratchColorArray : [new Color()],
+ scratchArrayIndex : 0,
+ scratchArrayArray : [[]],
+ scratchCartesian2Index : 0,
+ scratchCartesian3Index : 0,
+ scratchCartesian4Index : 0,
+ scratchCartesian2Array : [new Cartesian2()],
+ scratchCartesian3Array : [new Cartesian3()],
+ scratchCartesian4Array : [new Cartesian4()],
reset : function() {
this.scratchColorIndex = 0;
+ this.scratchArrayIndex = 0;
+ this.scratchCartesian2Index = 0;
+ this.scratchCartesian3Index = 0;
+ this.scratchCartesian4Index = 0;
},
getColor : function() {
- if (this.scratchColorIndex >= this.scratchColors.length) {
- this.scratchColors.push(new Color());
+ if (this.scratchColorIndex >= this.scratchColorArray.length) {
+ this.scratchColorArray.push(new Color());
}
- var scratchColor = this.scratchColors[this.scratchColorIndex];
- ++this.scratchColorIndex;
- return scratchColor;
+ return this.scratchColorArray[this.scratchColorIndex++];
+ },
+ getArray : function() {
+ if (this.scratchArrayIndex >= this.scratchArrayArray.length) {
+ this.scratchArrayArray.push([]);
+ }
+ var scratchArray = this.scratchArrayArray[this.scratchArrayIndex++];
+ scratchArray.length = 0;
+ return scratchArray;
+ },
+ getCartesian2 : function() {
+ if (this.scratchCartesian2Index >= this.scratchCartesian2Array.length) {
+ this.scratchCartesian2Array.push(new Cartesian2());
+ }
+ return this.scratchCartesian2Array[this.scratchCartesian2Index++];
+ },
+ getCartesian3 : function() {
+ if (this.scratchCartesian3Index >= this.scratchCartesian3Array.length) {
+ this.scratchCartesian3Array.push(new Cartesian3());
+ }
+ return this.scratchCartesian3Array[this.scratchCartesian3Index++];
+ },
+ getCartesian4 : function() {
+ if (this.scratchCartesian4Index >= this.scratchCartesian4Array.length) {
+ this.scratchCartesian4Array.push(new Cartesian4());
+ }
+ return this.scratchCartesian4Array[this.scratchCartesian4Index++];
}
};
@@ -143,16 +185,18 @@ define([
* is of type Boolean
, Number
, or String
, the corresponding JavaScript
* primitive type will be returned. If the result is a RegExp
, a Javascript RegExp
* object will be returned. If the result is a Color
, a {@link Color} object will be returned.
+ * If the result is a Cartesian2
, Cartesian3
, or Cartesian4
,
+ * a {@link Cartesian2}, {@link Cartesian3}, or {@link Cartesian4} object will be returned.
*
* @param {FrameState} frameState The frame state.
* @param {Cesium3DTileFeature} feature The feature who's properties may be used as variables in the expression.
- * @returns {Boolean|Number|String|Color|RegExp} The result of evaluating the expression.
+ * @returns {Boolean|Number|String|Color|Cartesian2|Cartesian3|Cartesian4|RegExp} The result of evaluating the expression.
*/
Expression.prototype.evaluate = function(frameState, feature) {
ScratchStorage.reset();
var result = this._runtimeAst.evaluate(frameState, feature);
- if (result instanceof Color) {
- return Color.clone(result);
+ if ((result instanceof Color) || (result instanceof Cartesian2) || (result instanceof Cartesian3) || (result instanceof Cartesian4)) {
+ return result.clone();
}
return result;
};
@@ -286,6 +330,7 @@ define([
function parseCall(expression, ast) {
var args = ast.arguments;
+ var argsLength = args.length;
var call;
var val, left, right;
@@ -300,7 +345,7 @@ define([
throw new DeveloperError('Error: ' + call + ' is not a function.');
}
//>>includeEnd('debug');
- if (args.length === 0) {
+ if (argsLength === 0) {
if (call === 'test') {
return new Node(ExpressionNodeType.LITERAL_BOOLEAN, false);
} else {
@@ -323,7 +368,7 @@ define([
// Non-member function calls
call = ast.callee.name;
if (call === 'color') {
- if (args.length === 0) {
+ if (argsLength === 0) {
return new Node(ExpressionNodeType.LITERAL_COLOR, call);
}
val = createRuntimeAst(expression, args[0]);
@@ -334,7 +379,7 @@ define([
return new Node(ExpressionNodeType.LITERAL_COLOR, call, [val]);
} else if (call === 'rgb' || call === 'hsl') {
//>>includeStart('debug', pragmas.debug);
- if (args.length < 3) {
+ if (argsLength < 3) {
throw new DeveloperError('Error: ' + call + ' requires three arguments.');
}
//>>includeEnd('debug');
@@ -346,7 +391,7 @@ define([
return new Node(ExpressionNodeType.LITERAL_COLOR, call, val);
} else if (call === 'rgba' || call === 'hsla') {
//>>includeStart('debug', pragmas.debug);
- if (args.length < 4) {
+ if (argsLength < 4) {
throw new DeveloperError('Error: ' + call + ' requires four arguments.');
}
//>>includeEnd('debug');
@@ -357,8 +402,15 @@ define([
createRuntimeAst(expression, args[3])
];
return new Node(ExpressionNodeType.LITERAL_COLOR, call, val);
+ } else if (call === 'vec2' || call === 'vec3' || call === 'vec4') {
+ // Check for invalid constructors at evaluation time
+ val = new Array(argsLength);
+ for (var i = 0; i < argsLength; ++i) {
+ val[i] = createRuntimeAst(expression, args[i]);
+ }
+ return new Node(ExpressionNodeType.LITERAL_VECTOR, call, val);
} else if (call === 'isNaN' || call === 'isFinite') {
- if (args.length === 0) {
+ if (argsLength === 0) {
if (call === 'isNaN') {
return new Node(ExpressionNodeType.LITERAL_BOOLEAN, true);
} else {
@@ -369,7 +421,7 @@ define([
return new Node(ExpressionNodeType.UNARY, call, val);
} else if (call === 'isExactClass' || call === 'isClass') {
//>>includeStart('debug', pragmas.debug);
- if (args.length < 1 || args.length > 1) {
+ if (argsLength < 1 || argsLength > 1) {
throw new DeveloperError('Error: ' + call + ' requires exactly one argument.');
}
//>>includeEnd('debug');
@@ -377,14 +429,14 @@ define([
return new Node(ExpressionNodeType.UNARY, call, val);
} else if (call === 'getExactClassName') {
//>>includeStart('debug', pragmas.debug);
- if (args.length > 0) {
+ if (argsLength > 0) {
throw new DeveloperError('Error: ' + call + ' does not take any argument.');
}
//>>includeEnd('debug');
return new Node(ExpressionNodeType.UNARY, call);
} else if (defined(unaryFunctions[call])) {
//>>includeStart('debug', pragmas.debug);
- if (args.length !== 1) {
+ if (argsLength !== 1) {
throw new DeveloperError('Error: ' + call + ' requires exactly one argument.');
}
//>>includeEnd('debug');
@@ -392,7 +444,7 @@ define([
return new Node(ExpressionNodeType.UNARY, call, val);
} else if (defined(binaryFunctions[call])) {
//>>includeStart('debug', pragmas.debug);
- if (args.length !== 2) {
+ if (argsLength !== 2) {
throw new DeveloperError('Error: ' + call + ' requires exactly two arguments.');
}
//>>includeEnd('debug');
@@ -401,7 +453,7 @@ define([
return new Node(ExpressionNodeType.BINARY, call, left, right);
} else if (defined(ternaryFunctions[call])) {
//>>includeStart('debug', pragmas.debug);
- if (args.length !== 3) {
+ if (argsLength !== 3) {
throw new DeveloperError('Error: ' + call + ' requires exactly three arguments.');
}
//>>includeEnd('debug');
@@ -410,19 +462,19 @@ define([
var test = createRuntimeAst(expression, args[2]);
return new Node(ExpressionNodeType.TERNARY, call, left, right, test);
} else if (call === 'Boolean') {
- if (args.length === 0) {
+ if (argsLength === 0) {
return new Node(ExpressionNodeType.LITERAL_BOOLEAN, false);
}
val = createRuntimeAst(expression, args[0]);
return new Node(ExpressionNodeType.UNARY, call, val);
} else if (call === 'Number') {
- if (args.length === 0) {
+ if (argsLength === 0) {
return new Node(ExpressionNodeType.LITERAL_NUMBER, 0);
}
val = createRuntimeAst(expression, args[0]);
return new Node(ExpressionNodeType.UNARY, call, val);
} else if (call === 'String') {
- if (args.length === 0) {
+ if (argsLength === 0) {
return new Node(ExpressionNodeType.LITERAL_STRING, '');
}
val = createRuntimeAst(expression, args[0]);
@@ -499,12 +551,14 @@ define([
}
function parseMemberExpression(expression, ast) {
+ var val;
var obj = createRuntimeAst(expression, ast.object);
if (ast.computed) {
- var val = createRuntimeAst(expression, ast.property);
+ val = createRuntimeAst(expression, ast.property);
return new Node(ExpressionNodeType.MEMBER, 'brackets', obj, val);
} else {
- return new Node(ExpressionNodeType.MEMBER, 'dot', obj, ast.property.name);
+ val = new Node(ExpressionNodeType.LITERAL_STRING, ast.property.name);
+ return new Node(ExpressionNodeType.MEMBER, 'dot', obj, val);
}
}
@@ -677,6 +731,8 @@ define([
node.evaluate = node._evaluateVariableString;
} else if (node._type === ExpressionNodeType.LITERAL_COLOR) {
node.evaluate = node._evaluateLiteralColor;
+ } else if (node._type === ExpressionNodeType.LITERAL_VECTOR) {
+ node.evaluate = node._evaluateLiteralVector;
} else if (node._type === ExpressionNodeType.LITERAL_STRING) {
node.evaluate = node._evaluateLiteralString;
} else if (node._type === ExpressionNodeType.REGEX) {
@@ -726,42 +782,101 @@ define([
if (!defined(args)) {
return Color.fromBytes(255, 255, 255, 255, result);
} else if (args.length > 1) {
- Color.fromCssColorString(args[0].evaluate(frameState, feature, result), result);
- result.alpha = args[1].evaluate(frameState, feature, result);
+ Color.fromCssColorString(args[0].evaluate(frameState, feature), result);
+ result.alpha = args[1].evaluate(frameState, feature);
} else {
- Color.fromCssColorString(args[0].evaluate(frameState, feature, result), result);
+ Color.fromCssColorString(args[0].evaluate(frameState, feature), result);
}
} else if (this._value === 'rgb') {
Color.fromBytes(
- args[0].evaluate(frameState, feature, result),
- args[1].evaluate(frameState, feature, result),
- args[2].evaluate(frameState, feature, result),
+ args[0].evaluate(frameState, feature),
+ args[1].evaluate(frameState, feature),
+ args[2].evaluate(frameState, feature),
255, result);
} else if (this._value === 'rgba') {
// convert between css alpha (0 to 1) and cesium alpha (0 to 255)
- var a = args[3].evaluate(frameState, feature, result) * 255;
+ var a = args[3].evaluate(frameState, feature) * 255;
Color.fromBytes(
- args[0].evaluate(frameState, feature, result),
- args[1].evaluate(frameState, feature, result),
- args[2].evaluate(frameState, feature, result),
+ args[0].evaluate(frameState, feature),
+ args[1].evaluate(frameState, feature),
+ args[2].evaluate(frameState, feature),
a, result);
} else if (this._value === 'hsl') {
Color.fromHsl(
- args[0].evaluate(frameState, feature, result),
- args[1].evaluate(frameState, feature, result),
- args[2].evaluate(frameState, feature, result),
+ args[0].evaluate(frameState, feature),
+ args[1].evaluate(frameState, feature),
+ args[2].evaluate(frameState, feature),
1.0, result);
} else if (this._value === 'hsla') {
Color.fromHsl(
- args[0].evaluate(frameState, feature, result),
- args[1].evaluate(frameState, feature, result),
- args[2].evaluate(frameState, feature, result),
- args[3].evaluate(frameState, feature, result),
+ args[0].evaluate(frameState, feature),
+ args[1].evaluate(frameState, feature),
+ args[2].evaluate(frameState, feature),
+ args[3].evaluate(frameState, feature),
result);
}
return result;
};
+ Node.prototype._evaluateLiteralVector = function(frameState, feature) {
+ // Gather the components that make up the vector, which includes components from interior vectors.
+ // For example vec3(1, 2, 3) or vec3(vec2(1, 2), 3) are both valid.
+ //
+ // If the number of components does not equal the vector's size, then a DeveloperError is thrown - with two exceptions:
+ // 1. A vector may be constructed from a larger vector and drop the extra components.
+ // 2. A vector may be constructed from a single component - vec3(1) will become vec3(1, 1, 1).
+ //
+ // Examples of invalid constructors include:
+ // vec4(1, 2) // not enough components
+ // vec3(vec2(1, 2)) // not enough components
+ // vec3(1, 2, 3, 4) // too many components
+ // vec2(vec4(1), 1) // too many components
+
+ var components = ScratchStorage.getArray();
+ var args = this._left;
+ var argsLength = args.length;
+ for (var i = 0; i < argsLength; ++i) {
+ var value = args[i].evaluate(frameState, feature);
+ if (typeof(value) === 'number') {
+ components.push(value);
+ } else if (value instanceof Cartesian2) {
+ components.push(value.x, value.y);
+ } else if (value instanceof Cartesian3) {
+ components.push(value.x, value.y, value.z);
+ } else if (value instanceof Cartesian4) {
+ components.push(value.x, value.y, value.z, value.w);
+ }
+ }
+
+ var componentsLength = components.length;
+ var call = this._value;
+ var vectorLength = parseInt(call.charAt(3));
+
+ //>>includeStart('debug', pragmas.debug);
+ if (componentsLength === 0) {
+ throw new DeveloperError('Error: Invalid ' + call + ' constructor. No valid arguments.');
+ } else if ((componentsLength < vectorLength) && (componentsLength > 1)) {
+ throw new DeveloperError('Error: Invalid ' + call + ' constructor. Not enough arguments.');
+ } else if ((componentsLength > vectorLength) && (argsLength > 1)) {
+ throw new DeveloperError('Error: Invalid ' + call + ' constructor. Too many arguments.');
+ }
+ //>>includeEnd('debug');
+
+ if (componentsLength === 1) {
+ // Add the same component 3 more times
+ var component = components[0];
+ components.push(component, component, component);
+ }
+
+ if (call === 'vec2') {
+ return Cartesian2.fromArray(components, 0, ScratchStorage.getCartesian2());
+ } else if (call === 'vec3') {
+ return Cartesian3.fromArray(components, 0, ScratchStorage.getCartesian3());
+ } else if (call === 'vec4') {
+ return Cartesian4.fromArray(components, 0, ScratchStorage.getCartesian4());
+ }
+ };
+
Node.prototype._evaluateLiteralString = function(frameState, feature) {
return this._value;
};
@@ -793,25 +908,66 @@ define([
// PERFORMANCE_IDEA: Determine if parent property needs to be computed before runtime
Node.prototype._evaluateMemberDot = function(frameState, feature) {
- if(checkFeature(this._left)) {
- return feature.getProperty(this._right);
+ if (checkFeature(this._left)) {
+ return feature.getProperty(this._right.evaluate(frameState, feature));
}
var property = this._left.evaluate(frameState, feature);
if (!defined(property)) {
return undefined;
}
- return property[this._right];
+
+ var member = this._right.evaluate(frameState, feature);
+ if (property instanceof Color) {
+ // Color components may be accessed with .x, .y, .z, .w and implicitly with .red, .green, .blue, .alpha
+ if (member === 'x') {
+ return property.red;
+ } else if (member === 'y') {
+ return property.green;
+ } else if (member === 'z') {
+ return property.blue;
+ } else if (member === 'w') {
+ return property.alpha;
+ }
+ }
+
+ return property[member];
};
Node.prototype._evaluateMemberBrackets = function(frameState, feature) {
- if(checkFeature(this._left)) {
+ if (checkFeature(this._left)) {
return feature.getProperty(this._right.evaluate(frameState, feature));
}
var property = this._left.evaluate(frameState, feature);
if (!defined(property)) {
return undefined;
}
- return property[this._right.evaluate(frameState, feature)];
+
+ var member = this._right.evaluate(frameState, feature);
+ if (property instanceof Color) {
+ // Color components may be accessed with [0][1][2][3], ['x']['y']['z']['w'], and implicitly with ['red']['green']['blue']['alpha']
+ if (member === 0 || member === 'x') {
+ return property.red;
+ } else if (member === 1 || member === 'y') {
+ return property.green;
+ } else if (member === 2 || member === 'z') {
+ return property.blue;
+ } else if (member === 3 || member === 'w') {
+ return property.alpha;
+ }
+ } else if ((property instanceof Cartesian2) || (property instanceof Cartesian3) || (property instanceof Cartesian4)) {
+ // Vector components may be accessed with [0][1][2][3] and implicitly with ['x']['y']['z']['w']
+ // For Cartesian2 and Cartesian3 out-of-range components will just return undefined
+ if (member === 0) {
+ return property.x;
+ } else if (member === 1) {
+ return property.y;
+ } else if (member === 2) {
+ return property.z;
+ } else if (member === 3) {
+ return property.w;
+ }
+ }
+ return property[member];
};
Node.prototype._evaluateArray = function(frameState, feature) {
@@ -830,11 +986,23 @@ define([
};
Node.prototype._evaluateNegative = function(frameState, feature) {
- return -(this._left.evaluate(frameState, feature));
+ var left = this._left.evaluate(frameState, feature);
+ if (left instanceof Cartesian2) {
+ return Cartesian2.negate(left, ScratchStorage.getCartesian2());
+ } else if (left instanceof Cartesian3) {
+ return Cartesian3.negate(left, ScratchStorage.getCartesian3());
+ } else if (left instanceof Cartesian4) {
+ return Cartesian4.negate(left, ScratchStorage.getCartesian4());
+ }
+ return -left;
};
Node.prototype._evaluatePositive = function(frameState, feature) {
- return +(this._left.evaluate(frameState, feature));
+ var left = this._left.evaluate(frameState, feature);
+ if ((left instanceof Color) || (left instanceof Cartesian2) || (left instanceof Cartesian3) || (left instanceof Cartesian4)) {
+ return left;
+ }
+ return +left;
};
Node.prototype._evaluateLessThan = function(frameState, feature) {
@@ -910,6 +1078,12 @@ define([
var right = this._right.evaluate(frameState, feature);
if ((right instanceof Color) && (left instanceof Color)) {
return Color.add(left, right, ScratchStorage.getColor());
+ } else if ((right instanceof Cartesian2) && (left instanceof Cartesian2)) {
+ return Cartesian2.add(left, right, ScratchStorage.getCartesian2());
+ } else if ((right instanceof Cartesian3) && (left instanceof Cartesian3)) {
+ return Cartesian3.add(left, right, ScratchStorage.getCartesian3());
+ } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) {
+ return Cartesian4.add(left, right, ScratchStorage.getCartesian4());
}
return left + right;
};
@@ -919,6 +1093,12 @@ define([
var right = this._right.evaluate(frameState, feature);
if ((right instanceof Color) && (left instanceof Color)) {
return Color.subtract(left, right, ScratchStorage.getColor());
+ } else if ((right instanceof Cartesian2) && (left instanceof Cartesian2)) {
+ return Cartesian2.subtract(left, right, ScratchStorage.getCartesian2());
+ } else if ((right instanceof Cartesian3) && (left instanceof Cartesian3)) {
+ return Cartesian3.subtract(left, right, ScratchStorage.getCartesian3());
+ } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) {
+ return Cartesian4.subtract(left, right, ScratchStorage.getCartesian4());
}
return left - right;
};
@@ -932,6 +1112,24 @@ define([
return Color.multiplyByScalar(right, left, ScratchStorage.getColor());
} else if ((left instanceof Color) && (typeof(right) === 'number')) {
return Color.multiplyByScalar(left, right, ScratchStorage.getColor());
+ } else if ((right instanceof Cartesian2) && (left instanceof Cartesian2)) {
+ return Cartesian2.multiplyComponents(left, right, ScratchStorage.getCartesian2());
+ } else if ((right instanceof Cartesian2) && (typeof(left) === 'number')) {
+ return Cartesian2.multiplyByScalar(right, left, ScratchStorage.getCartesian2());
+ } else if ((left instanceof Cartesian2) && (typeof(right) === 'number')) {
+ return Cartesian2.multiplyByScalar(left, right, ScratchStorage.getCartesian2());
+ } else if ((right instanceof Cartesian3) && (left instanceof Cartesian3)) {
+ return Cartesian3.multiplyComponents(left, right, ScratchStorage.getCartesian3());
+ } else if ((right instanceof Cartesian3) && (typeof(left) === 'number')) {
+ return Cartesian3.multiplyByScalar(right, left, ScratchStorage.getCartesian3());
+ } else if ((left instanceof Cartesian3) && (typeof(right) === 'number')) {
+ return Cartesian3.multiplyByScalar(left, right, ScratchStorage.getCartesian3());
+ } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) {
+ return Cartesian4.multiplyComponents(left, right, ScratchStorage.getCartesian4());
+ } else if ((right instanceof Cartesian4) && (typeof(left) === 'number')) {
+ return Cartesian4.multiplyByScalar(right, left, ScratchStorage.getCartesian4());
+ } else if ((left instanceof Cartesian4) && (typeof(right) === 'number')) {
+ return Cartesian4.multiplyByScalar(left, right, ScratchStorage.getCartesian4());
}
return left * right;
};
@@ -943,6 +1141,18 @@ define([
return Color.divide(left, right, ScratchStorage.getColor());
} else if ((left instanceof Color) && (typeof(right) === 'number')) {
return Color.divideByScalar(left, right, ScratchStorage.getColor());
+ } else if ((right instanceof Cartesian2) && (left instanceof Cartesian2)) {
+ return Cartesian2.divideComponents(left, right, ScratchStorage.getCartesian2());
+ } else if ((left instanceof Cartesian2) && (typeof(right) === 'number')) {
+ return Cartesian2.divideByScalar(left, right, ScratchStorage.getCartesian2());
+ } else if ((right instanceof Cartesian3) && (left instanceof Cartesian3)) {
+ return Cartesian3.divideComponents(left, right, ScratchStorage.getCartesian3());
+ } else if ((left instanceof Cartesian3) && (typeof(right) === 'number')) {
+ return Cartesian3.divideByScalar(left, right, ScratchStorage.getCartesian3());
+ } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) {
+ return Cartesian4.divideComponents(left, right, ScratchStorage.getCartesian4());
+ } else if ((left instanceof Cartesian4) && (typeof(right) === 'number')) {
+ return Cartesian4.divideByScalar(left, right, ScratchStorage.getCartesian4());
}
return left / right;
};
@@ -952,6 +1162,12 @@ define([
var right = this._right.evaluate(frameState, feature);
if ((right instanceof Color) && (left instanceof Color)) {
return Color.mod(left, right, ScratchStorage.getColor());
+ } else if ((right instanceof Cartesian2) && (left instanceof Cartesian2)) {
+ return Cartesian2.fromElements(left.x % right.x, left.y % right.y, ScratchStorage.getCartesian2());
+ } else if ((right instanceof Cartesian3) && (left instanceof Cartesian3)) {
+ return Cartesian3.fromElements(left.x % right.x, left.y % right.y, left.z % right.z, ScratchStorage.getCartesian3());
+ } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) {
+ return Cartesian4.fromElements(left.x % right.x, left.y % right.y, left.z % right.z, left.w % right.w, ScratchStorage.getCartesian4());
}
return left % right;
};
@@ -959,8 +1175,11 @@ define([
Node.prototype._evaluateEqualsStrict = function(frameState, feature) {
var left = this._left.evaluate(frameState, feature);
var right = this._right.evaluate(frameState, feature);
- if ((right instanceof Color) && (left instanceof Color)) {
- return Color.equals(left, right);
+ if ((right instanceof Color) && (left instanceof Color) ||
+ (right instanceof Cartesian2) && (left instanceof Cartesian2) ||
+ (right instanceof Cartesian3) && (left instanceof Cartesian3) ||
+ (right instanceof Cartesian4) && (left instanceof Cartesian4)) {
+ return left.equals(right);
}
return left === right;
};
@@ -968,8 +1187,11 @@ define([
Node.prototype._evaluateEquals = function(frameState, feature) {
var left = this._left.evaluate(frameState, feature);
var right = this._right.evaluate(frameState, feature);
- if ((right instanceof Color) && (left instanceof Color)) {
- return Color.equals(left, right);
+ if ((right instanceof Color) && (left instanceof Color) ||
+ (right instanceof Cartesian2) && (left instanceof Cartesian2) ||
+ (right instanceof Cartesian3) && (left instanceof Cartesian3) ||
+ (right instanceof Cartesian4) && (left instanceof Cartesian4)) {
+ return left.equals(right);
}
// Specifically want to do an abstract equality comparison (==) instead of a strict equality comparison (===)
@@ -980,8 +1202,11 @@ define([
Node.prototype._evaluateNotEqualsStrict = function(frameState, feature) {
var left = this._left.evaluate(frameState, feature);
var right = this._right.evaluate(frameState, feature);
- if ((right instanceof Color) && (left instanceof Color)) {
- return !Color.equals(left, right);
+ if ((right instanceof Color) && (left instanceof Color) ||
+ (right instanceof Cartesian2) && (left instanceof Cartesian2) ||
+ (right instanceof Cartesian3) && (left instanceof Cartesian3) ||
+ (right instanceof Cartesian4) && (left instanceof Cartesian4)) {
+ return !left.equals(right);
}
return left !== right;
};
@@ -989,8 +1214,11 @@ define([
Node.prototype._evaluateNotEquals = function(frameState, feature) {
var left = this._left.evaluate(frameState, feature);
var right = this._right.evaluate(frameState, feature);
- if ((right instanceof Color) && (left instanceof Color)) {
- return !Color.equals(left, right);
+ if ((right instanceof Color) && (left instanceof Color) ||
+ (right instanceof Cartesian2) && (left instanceof Cartesian2) ||
+ (right instanceof Cartesian3) && (left instanceof Cartesian3) ||
+ (right instanceof Cartesian4) && (left instanceof Cartesian4)) {
+ return !left.equals(right);
}
// Specifically want to do an abstract inequality comparison (!=) instead of a strict inequality comparison (!==)
// so that cases like "5 != '5'" return false. Tell jsHint to ignore this line.
@@ -1093,7 +1321,7 @@ define([
Node.prototype._evaluateToString = function(frameState, feature) {
var left = this._left.evaluate(frameState, feature);
- if ((left instanceof RegExp) || (left instanceof Color)) {
+ if ((left instanceof RegExp) || (left instanceof Color) || (left instanceof Cartesian2) || (left instanceof Cartesian3) || (left instanceof Cartesian4)) {
return String(left);
}
//>>includeStart('debug', pragmas.debug);
@@ -1162,11 +1390,11 @@ define([
return 'vec4(' + r + ', ' + g + ', ' + b + ', ' + a + ')';
}
- function getExpressionArray(array, attributePrefix, shaderState) {
+ function getExpressionArray(array, attributePrefix, shaderState, parent) {
var length = array.length;
var expressions = new Array(length);
for (var i = 0; i < length; ++i) {
- var shader = array[i].getShaderExpression(attributePrefix, shaderState);
+ var shader = array[i].getShaderExpression(attributePrefix, shaderState, parent);
if (!defined(shader)) {
// If any of the expressions are not valid, the array is not valid
return undefined;
@@ -1176,7 +1404,7 @@ define([
return expressions;
}
- Node.prototype.getShaderExpression = function(attributePrefix, shaderState) {
+ Node.prototype.getShaderExpression = function(attributePrefix, shaderState, parent) {
var color;
var left;
var right;
@@ -1185,21 +1413,12 @@ define([
var type = this._type;
var value = this._value;
- // Right may be a string if it's a member variable: e.g. "${property.name}"
- if (typeof(this._right) === 'string') {
- //>>includeStart('debug', pragmas.debug);
- throw new DeveloperError('Error generating style shader: string members are not supported.');
- //>>includeEnd('debug');
- // Return undefined when not in debug. Tell jsHint to ignore this line.
- return; // jshint ignore:line
- }
-
if (defined(this._left)) {
if (isArray(this._left)) {
- // Left can be an array if the type is LITERAL_COLOR
- left = getExpressionArray(this._left, attributePrefix, shaderState);
+ // Left can be an array if the type is LITERAL_COLOR or LITERAL_VECTOR
+ left = getExpressionArray(this._left, attributePrefix, shaderState, this);
} else {
- left = this._left.getShaderExpression(attributePrefix, shaderState);
+ left = this._left.getShaderExpression(attributePrefix, shaderState, this);
}
if (!defined(left)) {
// If the left side is not valid shader code, then the expression is not valid
@@ -1208,7 +1427,7 @@ define([
}
if (defined(this._right)) {
- right = this._right.getShaderExpression(attributePrefix, shaderState);
+ right = this._right.getShaderExpression(attributePrefix, shaderState, this);
if (!defined(right)) {
// If the right side is not valid shader code, then the expression is not valid
return undefined;
@@ -1216,7 +1435,7 @@ define([
}
if (defined(this._test)) {
- test = this._test.getShaderExpression(attributePrefix, shaderState);
+ test = this._test.getShaderExpression(attributePrefix, shaderState, this);
if (!defined(test)) {
// If the test is not valid shader code, then the expression is not valid
return undefined;
@@ -1225,7 +1444,7 @@ define([
if (isArray(this._value)) {
// For ARRAY type
- value = getExpressionArray(this._value, attributePrefix, shaderState);
+ value = getExpressionArray(this._value, attributePrefix, shaderState, this);
if (!defined(value)) {
// If the values are not valid shader code, then the expression is not valid
return undefined;
@@ -1247,14 +1466,15 @@ define([
return 'cos(' + left + ')';
} else if (value === 'sqrt') {
return 'sqrt(' + left + ')';
+ } else if ((value === 'isNaN') || (value === 'isFinite') || (value === 'String') || (value === 'isExactClass') || (value === 'isClass') || (value === 'getExactClassName')) {
+ //>>includeStart('debug', pragmas.debug);
+ throw new DeveloperError('Error generating style shader: "' + value + '" is not supported.');
+ //>>includeEnd('debug');
+ // Return undefined when not in debug. Tell jsHint to ignore this line.
+ return undefined; // jshint ignore:line
} 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')) {
- throw new DeveloperError('Error generating style shader: "' + value + '" is not supported.');
- }
- //>>includeEnd('debug');
return value + left;
case ExpressionNodeType.BINARY:
// Supported types: ||, &&, ===, ==, !==, !=, <, >, <=, >=, +, -, *, /, %
@@ -1278,7 +1498,18 @@ define([
case ExpressionNodeType.CONDITIONAL:
return '(' + test + ' ? ' + left + ' : ' + right + ')';
case ExpressionNodeType.MEMBER:
- // This is intended for accessing the components of vec2, vec3, and vec4 properties. String members aren't supported.
+ // This is intended for accessing the components of vector properties. String members aren't supported.
+ // Check for 0.0 rather than 0 because all numbers are previously converted to decimals.
+ // In this shader there is not much distinction between colors and vectors so allow .red to access the 0th component for both.
+ if (right === 'red' || right === 'x' || right === '0.0') {
+ return left + '[0]';
+ } else if (right === 'green' || right === 'y' || right === '1.0') {
+ return left + '[1]';
+ } else if (right === 'blue' || right === 'z' || right === '2.0') {
+ return left + '[2]';
+ } else if (right === 'alpha' || right === 'w' || right === '3.0') {
+ return left + '[3]';
+ }
return left + '[int(' + right + ')]';
case ExpressionNodeType.FUNCTION_CALL:
//>>includeStart('debug', pragmas.debug);
@@ -1315,7 +1546,15 @@ define([
case ExpressionNodeType.LITERAL_NUMBER:
return numberToString(value);
case ExpressionNodeType.LITERAL_STRING:
- // The only supported strings are css color strings
+ // Check if parent is of type MEMBER. Otherwise it is not possible to know whether 'red', 'green', and 'blue'
+ // refer to CSS strings or component accessors.
+ if (defined(parent) && (parent._type === ExpressionNodeType.MEMBER)) {
+ if (value === 'red' || value === 'green' || value === 'blue' || value === 'alpha' ||
+ value === 'x' || value === 'y' || value === 'z' || value === 'w') {
+ return value;
+ }
+ }
+ // Check for css color strings
color = Color.fromCssColorString(value, scratchColor);
if (defined(color)) {
return colorToVec3(color);
@@ -1377,6 +1616,17 @@ define([
}
}
break;
+ case ExpressionNodeType.LITERAL_VECTOR:
+ var length = left.length;
+ var vectorExpression = value + '(';
+ for (var i = 0; i < length; ++i) {
+ vectorExpression += left[i];
+ if (i < (length - 1)) {
+ vectorExpression += ', ';
+ }
+ }
+ vectorExpression += ')';
+ return vectorExpression;
case ExpressionNodeType.LITERAL_REGEX:
//>>includeStart('debug', pragmas.debug);
throw new DeveloperError('Error generating style shader: Regular expressions are not supported.');
diff --git a/Source/Scene/ExpressionNodeType.js b/Source/Scene/ExpressionNodeType.js
index bf8ccd9e9094..f7d9a85fa1ca 100644
--- a/Source/Scene/ExpressionNodeType.js
+++ b/Source/Scene/ExpressionNodeType.js
@@ -24,9 +24,10 @@ define([
LITERAL_NUMBER : 12,
LITERAL_STRING : 13,
LITERAL_COLOR : 14,
- LITERAL_REGEX : 15,
- LITERAL_UNDEFINED : 16,
- LITERAL_GLOBAL : 17
+ LITERAL_VECTOR : 15,
+ LITERAL_REGEX : 16,
+ LITERAL_UNDEFINED : 17,
+ LITERAL_GLOBAL : 18
};
return freezeObject(ExpressionNodeType);
diff --git a/Source/Scene/PointCloud3DTileContent.js b/Source/Scene/PointCloud3DTileContent.js
index ae1972107bae..e00cea225aec 100644
--- a/Source/Scene/PointCloud3DTileContent.js
+++ b/Source/Scene/PointCloud3DTileContent.js
@@ -908,12 +908,7 @@ define([
attributeLocations.a_batchId = batchIdLocation;
}
- var vs = 'attribute vec3 a_position; \n' +
- 'varying vec4 v_color; \n' +
- 'uniform float u_pointSize; \n' +
- 'uniform vec4 u_constantColor; \n' +
- 'uniform vec4 u_highlightColor; \n' +
- 'uniform float u_tilesetTime; \n';
+ var attributeDeclarations = '';
var length = styleableProperties.length;
for (i = 0; i < length; ++i) {
@@ -934,10 +929,19 @@ define([
attributeType = 'vec' + componentCount;
}
- vs += 'attribute ' + attributeType + ' ' + attributeName + '; \n';
+ attributeDeclarations += 'attribute ' + attributeType + ' ' + attributeName + '; \n';
attributeLocations[attributeName] = attribute.location;
}
+ var vs = 'attribute vec3 a_position; \n' +
+ 'varying vec4 v_color; \n' +
+ 'uniform float u_pointSize; \n' +
+ 'uniform vec4 u_constantColor; \n' +
+ 'uniform vec4 u_highlightColor; \n' +
+ 'uniform float u_tilesetTime; \n';
+
+ vs += attributeDeclarations;
+
if (usesColors) {
if (isTranslucent) {
vs += 'attribute vec4 a_color; \n';
diff --git a/Specs/Scene/ExpressionSpec.js b/Specs/Scene/ExpressionSpec.js
index c8b8ced4c52d..078761b40a2e 100644
--- a/Specs/Scene/ExpressionSpec.js
+++ b/Specs/Scene/ExpressionSpec.js
@@ -1,11 +1,17 @@
/*global defineSuite*/
defineSuite([
'Scene/Expression',
+ 'Core/Cartesian2',
+ 'Core/Cartesian3',
+ 'Core/Cartesian4',
'Core/Color',
'Core/Math',
'Scene/ExpressionNodeType'
], function(
Expression,
+ Cartesian2,
+ Cartesian3,
+ Cartesian4,
Color,
CesiumMath,
ExpressionNodeType) {
@@ -485,7 +491,7 @@ defineSuite([
}).toThrowDeveloperError();
});
- it('evaluates color properties', function() {
+ it('evaluates color properties (reg, green, blue, alpha)', function() {
var expression = new Expression('color(\'#ffffff\').red');
expect(expression.evaluate(frameState, undefined)).toEqual(1);
@@ -499,6 +505,254 @@ defineSuite([
expect(expression.evaluate(frameState, undefined)).toEqual(0.5);
});
+ it('evaluates color properties (x, y, z, w)', function() {
+ var expression = new Expression('color(\'#ffffff\').x');
+ expect(expression.evaluate(frameState, undefined)).toEqual(1);
+
+ expression = new Expression('rgb(255, 255, 0).y');
+ expect(expression.evaluate(frameState, undefined)).toEqual(1);
+
+ expression = new Expression('color("cyan").z');
+ expect(expression.evaluate(frameState, undefined)).toEqual(1);
+
+ expression = new Expression('rgba(255, 255, 0, 0.5).w');
+ expect(expression.evaluate(frameState, undefined)).toEqual(0.5);
+ });
+
+ it('evaluates color properties ([0], [1], [2]. [3])', function() {
+ var expression = new Expression('color(\'#ffffff\')[0]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(1);
+
+ expression = new Expression('rgb(255, 255, 0)[1]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(1);
+
+ expression = new Expression('color("cyan")[2]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(1);
+
+ expression = new Expression('rgba(255, 255, 0, 0.5)[3]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(0.5);
+ });
+
+ it('evaluates color properties (["red"], ["green"], ["blue"], ["alpha"])', function() {
+ var expression = new Expression('color(\'#ffffff\')["red"]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(1);
+
+ expression = new Expression('rgb(255, 255, 0)["green"]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(1);
+
+ expression = new Expression('color("cyan")["blue"]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(1);
+
+ expression = new Expression('rgba(255, 255, 0, 0.5)["alpha"]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(0.5);
+ });
+
+ it('evaluates color properties (["x"], ["y"], ["z"], ["w"])', function() {
+ var expression = new Expression('color(\'#ffffff\')["x"]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(1);
+
+ expression = new Expression('rgb(255, 255, 0)["y"]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(1);
+
+ expression = new Expression('color("cyan")["z"]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(1);
+
+ expression = new Expression('rgba(255, 255, 0, 0.5)["w"]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(0.5);
+ });
+
+ it('evaluates vec2', function() {
+ var expression = new Expression('vec2(2.0)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(2.0, 2.0));
+
+ expression = new Expression('vec2(3.0, 4.0)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3.0, 4.0));
+
+ expression = new Expression('vec2(vec2(3.0, 4.0))');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3.0, 4.0));
+
+ expression = new Expression('vec2(vec3(3.0, 4.0, 5.0))');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3.0, 4.0));
+
+ expression = new Expression('vec2(vec4(3.0, 4.0, 5.0, 6.0))');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3.0, 4.0));
+ });
+
+ it('throws if vec2 has invalid number of arguments', function() {
+ var expression = new Expression('vec2()');
+ expect(function() {
+ expression.evaluate(frameState, undefined);
+ }).toThrowDeveloperError();
+
+ expression = new Expression('vec2(3.0, 4.0, 5.0)');
+ expect(function() {
+ expression.evaluate(frameState, undefined);
+ }).toThrowDeveloperError();
+
+ expression = new Expression('vec2(vec2(3.0, 4.0), 5.0)');
+ expect(function() {
+ expression.evaluate(frameState, undefined);
+ }).toThrowDeveloperError();
+ });
+
+ it('evaluates vec3', function() {
+ var expression = new Expression('vec3(2.0)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(2.0, 2.0, 2.0));
+
+ expression = new Expression('vec3(3.0, 4.0, 5.0)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3.0, 4.0, 5.0));
+
+ expression = new Expression('vec3(vec2(3.0, 4.0), 5.0)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3.0, 4.0, 5.0));
+
+ expression = new Expression('vec3(3.0, vec2(4.0, 5.0))');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3.0, 4.0, 5.0));
+
+ expression = new Expression('vec3(vec3(3.0, 4.0, 5.0))');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3.0, 4.0, 5.0));
+
+ expression = new Expression('vec3(vec4(3.0, 4.0, 5.0, 6.0))');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3.0, 4.0, 5.0));
+ });
+
+ it ('throws if vec3 has invalid number of arguments', function() {
+ var expression = new Expression('vec3()');
+ expect(function() {
+ expression.evaluate(frameState, undefined);
+ }).toThrowDeveloperError();
+
+ expression = new Expression('vec3(3.0, 4.0)');
+ expect(function() {
+ expression.evaluate(frameState, undefined);
+ }).toThrowDeveloperError();
+
+ expression = new Expression('vec3(3.0, 4.0, 5.0, 6.0)');
+ expect(function() {
+ expression.evaluate(frameState, undefined);
+ }).toThrowDeveloperError();
+
+ expression = new Expression('vec3(vec2(3.0, 4.0), vec2(5.0, 6.0))');
+ expect(function() {
+ expression.evaluate(frameState, undefined);
+ }).toThrowDeveloperError();
+
+ expression = new Expression('vec3(vec4(3.0, 4.0, 5.0, 6.0), 1.0)');
+ expect(function() {
+ expression.evaluate(frameState, undefined);
+ }).toThrowDeveloperError();
+ });
+
+ it('evaluates vec4', function() {
+ var expression = new Expression('vec4(2.0)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(2.0, 2.0, 2.0, 2.0));
+
+ expression = new Expression('vec4(3.0, 4.0, 5.0, 6.0)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0));
+
+ expression = new Expression('vec4(vec2(3.0, 4.0), 5.0, 6.0)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0));
+
+ expression = new Expression('vec4(3.0, vec2(4.0, 5.0), 6.0)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0));
+
+ expression = new Expression('vec4(3.0, 4.0, vec2(5.0, 6.0))');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0));
+
+ expression = new Expression('vec4(vec3(3.0, 4.0, 5.0), 6.0)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0));
+
+ expression = new Expression('vec4(3.0, vec3(4.0, 5.0, 6.0))');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0));
+
+ expression = new Expression('vec4(vec4(3.0, 4.0, 5.0, 6.0))');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0));
+ });
+
+ it ('throws if vec4 has invalid number of arguments', function() {
+ var expression = new Expression('vec4()');
+ expect(function() {
+ expression.evaluate(frameState, undefined);
+ }).toThrowDeveloperError();
+
+ expression = new Expression('vec4(3.0, 4.0)');
+ expect(function() {
+ expression.evaluate(frameState, undefined);
+ }).toThrowDeveloperError();
+
+ expression = new Expression('vec4(3.0, 4.0, 5.0)');
+ expect(function() {
+ expression.evaluate(frameState, undefined);
+ }).toThrowDeveloperError();
+
+ expression = new Expression('vec4(3.0, 4.0, 5.0, 6.0, 7.0)');
+ expect(function() {
+ expression.evaluate(frameState, undefined);
+ }).toThrowDeveloperError();
+
+ expression = new Expression('vec4(vec3(3.0, 4.0, 5.0))');
+ expect(function() {
+ expression.evaluate(frameState, undefined);
+ }).toThrowDeveloperError();
+ });
+
+ it('evaluates vector with expressions as arguments', function() {
+ var feature = new MockFeature();
+ feature.addProperty('height', 2);
+ feature.addProperty('width', 4);
+ feature.addProperty('depth', 3);
+ feature.addProperty('scale', 1);
+
+ var expression = new Expression('vec4(${height}, ${width}, ${depth}, ${scale})');
+ expect(expression.evaluate(frameState, feature)).toEqual(new Cartesian4(2.0, 4.0, 3.0, 1.0));
+ });
+
+ it('evaluates expression with multiple nested vectors', function() {
+ var expression = new Expression('vec4(vec2(1, 2)[vec3(6, 1, 5).y], 2, vec4(1.0).w, 5)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(2.0, 2.0, 1.0, 5.0));
+ });
+
+ it('evaluates vector properties (x, y, z, w)', function() {
+ var expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0).x');
+ expect(expression.evaluate(frameState, undefined)).toEqual(1.0);
+
+ expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0).y');
+ expect(expression.evaluate(frameState, undefined)).toEqual(2.0);
+
+ expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0).z');
+ expect(expression.evaluate(frameState, undefined)).toEqual(3.0);
+
+ expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0).w');
+ expect(expression.evaluate(frameState, undefined)).toEqual(4.0);
+ });
+
+ it('evaluates vector properties ([0], [1], [2], [3])', function() {
+ var expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)[0]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(1.0);
+
+ expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)[1]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(2.0);
+
+ expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)[2]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(3.0);
+
+ expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)[3]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(4.0);
+ });
+
+ it('evaluates vector properties (["x"], ["y"], ["z"]. ["w"])', function() {
+ var expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)["x"]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(1.0);
+
+ expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)["y"]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(2.0);
+
+ expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)["z"]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(3.0);
+
+ expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)["w"]');
+ expect(expression.evaluate(frameState, undefined)).toEqual(4.0);
+ });
+
it('evaluates unary not', function() {
var expression = new Expression('!true');
expect(expression.evaluate(frameState, undefined)).toEqual(false);
@@ -748,7 +1002,10 @@ defineSuite([
});
it('evaluates color operations', function() {
- var expression = new Expression('rgba(255, 0, 0, 0.5) + rgba(0, 0, 255, 0.5)');
+ var expression = new Expression('+rgba(255, 0, 0, 1.0)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(Color.RED);
+
+ expression = new Expression('rgba(255, 0, 0, 0.5) + rgba(0, 0, 255, 0.5)');
expect(expression.evaluate(frameState, undefined)).toEqual(Color.MAGENTA);
expression = new Expression('rgba(0, 255, 255, 1.0) - rgba(0, 255, 0, 0)');
@@ -783,6 +1040,140 @@ defineSuite([
expression = new Expression('color(\'green\') != color(\'green\')');
expect(expression.evaluate(frameState, undefined)).toEqual(false);
+
+ expression = new Expression('color(\'green\') !== color(\'green\')');
+ expect(expression.evaluate(frameState, undefined)).toEqual(false);
+ });
+
+ it('evaluates vector operations', function() {
+ var expression = new Expression('+vec2(1, 2)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(1, 2));
+
+ expression = new Expression('+vec3(1, 2, 3)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(1, 2, 3));
+
+ expression = new Expression('+vec4(1, 2, 3, 4)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1, 2, 3, 4));
+
+ expression = new Expression('-vec2(1, 2)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(-1, -2));
+
+ expression = new Expression('-vec3(1, 2, 3)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(-1, -2, -3));
+
+ expression = new Expression('-vec4(1, 2, 3, 4)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(-1, -2, -3, -4));
+
+ expression = new Expression('vec2(1, 2) + vec2(3, 4)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(4, 6));
+
+ expression = new Expression('vec3(1, 2, 3) + vec3(3, 4, 5)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(4, 6, 8));
+
+ expression = new Expression('vec4(1, 2, 3, 4) + vec4(3, 4, 5, 6)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(4, 6, 8, 10));
+
+ expression = new Expression('vec2(1, 2) - vec2(3, 4)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(-2, -2));
+
+ expression = new Expression('vec3(1, 2, 3) - vec3(3, 4, 5)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(-2, -2, -2));
+
+ expression = new Expression('vec4(1, 2, 3, 4) - vec4(3, 4, 5, 6)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(-2, -2, -2, -2));
+
+ expression = new Expression('vec2(1, 2) * vec2(3, 4)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3, 8));
+
+ expression = new Expression('vec2(1, 2) * 3.0');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3, 6));
+
+ expression = new Expression('3.0 * vec2(1, 2)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3, 6));
+
+ expression = new Expression('vec3(1, 2, 3) * vec3(3, 4, 5)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3, 8, 15));
+
+ expression = new Expression('vec3(1, 2, 3) * 3.0');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3, 6, 9));
+
+ expression = new Expression('3.0 * vec3(1, 2, 3)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3, 6, 9));
+
+ expression = new Expression('vec4(1, 2, 3, 4) * vec4(3, 4, 5, 6)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3, 8, 15, 24));
+
+ expression = new Expression('vec4(1, 2, 3, 4) * 3.0');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3, 6, 9, 12));
+
+ expression = new Expression('3.0 * vec4(1, 2, 3, 4)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3, 6, 9, 12));
+
+ expression = new Expression('vec2(1, 2) / vec2(2, 5)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(0.5, 0.4));
+
+ expression = new Expression('vec2(1, 2) / 2.0');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(0.5, 1.0));
+
+ expression = new Expression('vec3(1, 2, 3) / vec3(2, 5, 3)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(0.5, 0.4, 1.0));
+
+ expression = new Expression('vec3(1, 2, 3) / 2.0');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(0.5, 1.0, 1.5));
+
+ expression = new Expression('vec4(1, 2, 3, 4) / vec4(2, 5, 3, 2)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.5, 0.4, 1.0, 2.0));
+
+ expression = new Expression('vec4(1, 2, 3, 4) / 2.0');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.5, 1.0, 1.5, 2.0));
+
+ expression = new Expression('vec2(2, 3) % vec2(3, 3)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(2, 0));
+
+ expression = new Expression('vec3(2, 3, 4) % vec3(3, 3, 3)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(2, 0, 1));
+
+ expression = new Expression('vec4(2, 3, 4, 5) % vec4(3, 3, 3, 2)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(2, 0, 1, 1));
+
+ expression = new Expression('vec2(1, 3) == vec2(1, 3)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(true);
+
+ expression = new Expression('vec3(1, 3, 4) == vec3(1, 3, 4)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(true);
+
+ expression = new Expression('vec4(1, 3, 4, 6) == vec4(1, 3, 4, 6)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(true);
+
+ expression = new Expression('vec2(1, 2) === vec2(1, 2)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(true);
+
+ expression = new Expression('vec3(1, 2, 3) === vec3(1, 2, 3)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(true);
+
+ expression = new Expression('vec4(1, 2, 3, 4) === vec4(1, 2, 3, 4)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(true);
+
+ expression = new Expression('!!vec4(1.0) == true');
+ expect(expression.evaluate(frameState, undefined)).toEqual(true);
+
+ expression = new Expression('vec2(1, 2) != vec2(1, 2)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(false);
+
+ expression = new Expression('vec3(1, 2, 3) != vec3(1, 2, 3)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(false);
+
+ expression = new Expression('vec4(1, 2, 3, 4) != vec4(1, 2, 3, 4)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(false);
+
+ expression = new Expression('vec2(1, 2) !== vec2(1, 2)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(false);
+
+ expression = new Expression('vec3(1, 2, 3) !== vec3(1, 2, 3)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(false);
+
+ expression = new Expression('vec4(1, 2, 3, 4) !== vec4(1, 2, 3, 4)');
+ expect(expression.evaluate(frameState, undefined)).toEqual(false);
});
it('evaluates color toString function', function() {
@@ -799,6 +1190,23 @@ defineSuite([
expect(expression.evaluate(frameState, feature)).toEqual('(0, 0, 1, 1)');
});
+ it('evaluates vector toString function', function() {
+ var feature = new MockFeature();
+ feature.addProperty('property', new Cartesian4(1, 2, 3, 4));
+
+ var expression = new Expression('vec2(1, 2).toString()');
+ expect(expression.evaluate(frameState, undefined)).toEqual('(1, 2)');
+
+ expression = new Expression('vec3(1, 2, 3).toString()');
+ expect(expression.evaluate(frameState, undefined)).toEqual('(1, 2, 3)');
+
+ expression = new Expression('vec4(1, 2, 3, 4).toString()');
+ expect(expression.evaluate(frameState, undefined)).toEqual('(1, 2, 3, 4)');
+
+ expression = new Expression('${property}.toString()');
+ expect(expression.evaluate(frameState, feature)).toEqual('(1, 2, 3, 4)');
+ });
+
it('evaluates isNaN function', function() {
var expression = new Expression('isNaN()');
expect(expression.evaluate(frameState, undefined)).toEqual(true);
@@ -1524,7 +1932,7 @@ defineSuite([
expect(expression.evaluate(frameState, feature)).toEqual(false);
});
- it('throws if test is not call with a RegExp', function() {
+ it('throws if test is not called with a RegExp', function() {
expect(function() {
return new Expression('color("blue").test()');
}).toThrowDeveloperError();
@@ -1780,12 +2188,12 @@ defineSuite([
it('gets shader expression for array indexing', function() {
var expression = new Expression('${property[0]}');
var shaderExpression = expression.getShaderExpression('', {});
- var expected = 'property[int(0.0)]';
+ var expected = 'property[0]';
expect(shaderExpression).toEqual(expected);
- expression = new Expression('rgb(0,0,0)[1]');
+ expression = new Expression('${property[4 / 2]}');
shaderExpression = expression.getShaderExpression('', {});
- expected = 'vec4(0.0, 0.0, 0.0, 1.0)[int(1.0)]';
+ expected = 'property[int((4.0 / 2.0))]';
expect(shaderExpression).toEqual(expected);
});
@@ -1944,6 +2352,55 @@ defineSuite([
expect(shaderState.translucent).toBe(true);
});
+ it('gets shader expression for color components', function() {
+ // .red, .green, .blue, .alpha
+ var expression = new Expression('color().red + color().green + color().blue + color().alpha');
+ var shaderExpression = expression.getShaderExpression('', {});
+ var expected = '(((vec4(1.0)[0] + vec4(1.0)[1]) + vec4(1.0)[2]) + vec4(1.0)[3])';
+ expect(shaderExpression).toEqual(expected);
+
+ // .x, .y, .z, .w
+ expression = new Expression('color().x + color().y + color().z + color().w');
+ shaderExpression = expression.getShaderExpression('', {});
+ expect(shaderExpression).toEqual(expected);
+
+ // [0], [1], [2], [3]
+ expression = new Expression('color()[0] + color()[1] + color()[2] + color()[3]');
+ shaderExpression = expression.getShaderExpression('', {});
+ expect(shaderExpression).toEqual(expected);
+ });
+
+ it('gets shader expression for vector', function() {
+ var expression = new Expression('vec4(1, 2, 3, 4)');
+ var shaderExpression = expression.getShaderExpression('', {});
+ expect(shaderExpression).toEqual('vec4(1.0, 2.0, 3.0, 4.0)');
+
+ expression = new Expression('vec4(1) + vec4(2)');
+ shaderExpression = expression.getShaderExpression('', {});
+ expect(shaderExpression).toEqual('(vec4(1.0) + vec4(2.0))');
+
+ expression = new Expression('vec4(1, ${property}, vec2(1, 2).x, 0)');
+ shaderExpression = expression.getShaderExpression('', {});
+ expect(shaderExpression).toEqual('vec4(1.0, property, vec2(1.0, 2.0)[0], 0.0)');
+
+ expression = new Expression('vec4(vec3(2), 1.0)');
+ shaderExpression = expression.getShaderExpression('', {});
+ expect(shaderExpression).toEqual('vec4(vec3(2.0), 1.0)');
+ });
+
+ it('gets shader expression for vector components', function() {
+ // .x, .y, .z, .w
+ var expression = new Expression('vec4(1).x + vec4(1).y + vec4(1).z + vec4(1).w');
+ var shaderExpression = expression.getShaderExpression('', {});
+ var expected = '(((vec4(1.0)[0] + vec4(1.0)[1]) + vec4(1.0)[2]) + vec4(1.0)[3])';
+ expect(shaderExpression).toEqual(expected);
+
+ // [0], [1], [2], [3]
+ expression = new Expression('vec4(1)[0] + vec4(1)[1] + vec4(1)[2] + vec4(1)[3]');
+ shaderExpression = expression.getShaderExpression('', {});
+ expect(shaderExpression).toEqual(expected);
+ });
+
it('gets shader expression for TILES3D_TILESET_TIME', function() {
var expression = new Expression('TILES3D_TILESET_TIME');
var shaderExpression = expression.getShaderExpression('', {});