From 4ace2fedfecc40828c96f2aa6431b08e18b1cb43 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Fri, 6 Jan 2017 10:43:08 -0500 Subject: [PATCH] Support vec2, vec3, and vec4 as distinct types --- .../gallery/3D Tiles Point Cloud Styling.html | 9 +- Source/Scene/Expression.js | 290 +++++++++++++----- Source/Scene/PointCloud3DTileContent.js | 29 +- Specs/Scene/ExpressionSpec.js | 219 +++++++++---- 4 files changed, 383 insertions(+), 164 deletions(-) diff --git a/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html b/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html index 777721bc269f..733b9d6edc37 100644 --- a/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html +++ b/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html @@ -134,7 +134,6 @@ pointSize : "5" }); -// secondaryColor is originally a vec3 - so add vec4(0.0, 0.0, 0.0, 1.0) to make the point cloud opaque addStyle('Clamp and Mix', { color : "color() * clamp(${temperature}, 0.1, 0.2)", pointSize : "mix(${temperature}, 2.0, 0.5) * 0.2" @@ -143,9 +142,9 @@ addStyle('Secondary Color', { color : { conditions : [ - ["${id} < 250", "${secondaryColor} + vec4(0.0, 0.0, 0.0, 1.0)"], - ["${id} < 500", "${secondaryColor} * ${secondaryColor} + vec4(0.0, 0.0, 0.0, 1.0)"], - ["${id} < 750", "${secondaryColor} / 5.0 + vec4(0.0, 0.0, 0.0, 1.0)"], + ["${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)"] ] } @@ -161,7 +160,7 @@ // POSITION contains 0 as its last component, so add 1.0 to make the point cloud opaque addStyle('Color based on position', { - color : "${POSITION} + vec4(0.0, 0.0, 0.0, 1.0)" + color : "vec4(${POSITION}, 1.0)" }); addStyle('Style point size', { diff --git a/Source/Scene/Expression.js b/Source/Scene/Expression.js index 47b024b292f0..780e3f4068f7 100644 --- a/Source/Scene/Expression.js +++ b/Source/Scene/Expression.js @@ -1,5 +1,7 @@ /*global define*/ define([ + '../Core/Cartesian2', + '../Core/Cartesian3', '../Core/Cartesian4', '../Core/Color', '../Core/defined', @@ -10,6 +12,8 @@ define([ '../ThirdParty/jsep', './ExpressionNodeType' ], function( + Cartesian2, + Cartesian3, Cartesian4, Color, defined, @@ -33,28 +37,53 @@ define([ var ScratchStorage = { scratchColorIndex : 0, - scratchColors : [new Color()], - scratchCartesianIndex : 0, - scratchCartesians : [new Cartesian4()], + 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.scratchCartesianIndex = 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++]; }, - getCartesian : function() { - if (this.scratchCartesianIndex >= this.scratchCartesians.length) { - this.scratchCartesians.push(new Cartesian4()); + getArray : function() { + if (this.scratchArrayIndex >= this.scratchArrayArray.length) { + this.scratchArrayArray.push([]); } - var scratchCartesian = this.scratchCartesians[this.scratchCartesianIndex]; - ++this.scratchCartesianIndex; - return scratchCartesian; + 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++]; } }; @@ -156,19 +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 Cartesian4, a {@link Cartesian4} 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|Cartesian4|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); - } else if (result instanceof Cartesian4) { - return Cartesian4.clone(result); + if ((result instanceof Color) || (result instanceof Cartesian2) || (result instanceof Cartesian3) || (result instanceof Cartesian4)) { + return result.clone(); } return result; }; @@ -302,6 +330,7 @@ define([ function parseCall(expression, ast) { var args = ast.arguments; + var argsLength = args.length; var call; var val, left, right; @@ -316,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 { @@ -339,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]); @@ -350,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'); @@ -362,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'); @@ -374,15 +403,14 @@ define([ ]; return new Node(ExpressionNodeType.LITERAL_COLOR, call, val); } else if (call === 'vec2' || call === 'vec3' || call === 'vec4') { - val = [ - defined(args[0]) ? createRuntimeAst(expression, args[0]) : new Node(ExpressionNodeType.LITERAL_NUMBER, 0), - defined(args[1]) ? createRuntimeAst(expression, args[1]) : new Node(ExpressionNodeType.LITERAL_NUMBER, 0), - defined(args[2]) ? createRuntimeAst(expression, args[2]) : new Node(ExpressionNodeType.LITERAL_NUMBER, 0), - defined(args[3]) ? createRuntimeAst(expression, args[3]) : new Node(ExpressionNodeType.LITERAL_NUMBER, 0) - ]; + // Check for invalid constructors at evaluation time - evaluateLiteralVector + 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 { @@ -393,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'); @@ -401,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'); @@ -416,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'); @@ -425,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'); @@ -434,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]); @@ -791,14 +819,62 @@ define([ }; Node.prototype._evaluateLiteralVector = function(frameState, feature) { - var result = ScratchStorage.getCartesian(); + // 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; - return Cartesian4.fromElements( - args[0].evaluate(frameState, feature), - args[1].evaluate(frameState, feature), - args[2].evaluate(frameState, feature), - args[3].evaluate(frameState, feature), - result); + var argsLength = args.length; + for (var i = 0; i < argsLength; ++i) { + var value = args[i].evaluate(frameState, feature); + 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); + } else if (typeof(value) === 'number') { + components.push(value); + } + } + + 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) { @@ -878,8 +954,9 @@ define([ } else if (member === 3 || member === 'w') { return property.alpha; } - } else if (property instanceof Cartesian4) { + } 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) { @@ -910,15 +987,19 @@ define([ Node.prototype._evaluateNegative = function(frameState, feature) { var left = this._left.evaluate(frameState, feature); - if (left instanceof Cartesian4) { - return Cartesian4.negate(left, ScratchStorage.getCartesian()); + 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) { var left = this._left.evaluate(frameState, feature); - if ((left instanceof Color) || (left instanceof Cartesian4)) { + if ((left instanceof Color) || (left instanceof Cartesian2) || (left instanceof Cartesian3) || (left instanceof Cartesian4)) { return left; } return +left; @@ -997,8 +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.getCartesian()); + return Cartesian4.add(left, right, ScratchStorage.getCartesian4()); } return left + right; }; @@ -1008,8 +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.getCartesian()); + return Cartesian4.subtract(left, right, ScratchStorage.getCartesian4()); } return left - right; }; @@ -1023,12 +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.getCartesian()); + return Cartesian4.multiplyComponents(left, right, ScratchStorage.getCartesian4()); } else if ((right instanceof Cartesian4) && (typeof(left) === 'number')) { - return Cartesian4.multiplyByScalar(right, left, ScratchStorage.getCartesian()); + return Cartesian4.multiplyByScalar(right, left, ScratchStorage.getCartesian4()); } else if ((left instanceof Cartesian4) && (typeof(right) === 'number')) { - return Cartesian4.multiplyByScalar(left, right, ScratchStorage.getCartesian()); + return Cartesian4.multiplyByScalar(left, right, ScratchStorage.getCartesian4()); } return left * right; }; @@ -1040,10 +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.getCartesian()); + return Cartesian4.divideComponents(left, right, ScratchStorage.getCartesian4()); } else if ((left instanceof Cartesian4) && (typeof(right) === 'number')) { - return Cartesian4.divideByScalar(left, right, ScratchStorage.getCartesian()); + return Cartesian4.divideByScalar(left, right, ScratchStorage.getCartesian4()); } return left / right; }; @@ -1053,10 +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)) { - // TODO : make mod a built-in Cartesian function? - // TODO : modByScalar? - return Cartesian4.fromElements(left.x % right.x, left.y % right.y, left.z % right.z, left.w % right.w, ScratchStorage.getCartesian()); + return Cartesian4.fromElements(left.x % right.x, left.y % right.y, left.z % right.z, left.w % right.w, ScratchStorage.getCartesian4()); } return left % right; }; @@ -1064,10 +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); - } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { - return Cartesian4.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; }; @@ -1075,10 +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); - } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { - return Cartesian4.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 (===) @@ -1089,10 +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); - } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { - return !Cartesian4.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; }; @@ -1100,10 +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); - } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { - return !Cartesian4.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. @@ -1206,7 +1321,7 @@ define([ Node.prototype._evaluateToString = function(frameState, feature) { var left = this._left.evaluate(frameState, feature); - if ((left instanceof RegExp) || (left instanceof Color) || (left instanceof Cartesian4)) { + if ((left instanceof RegExp) || (left instanceof Color) || (left instanceof Cartesian2) || (left instanceof Cartesian3) || (left instanceof Cartesian4)) { return String(left); } //>>includeStart('debug', pragmas.debug); @@ -1275,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; @@ -1301,7 +1416,7 @@ define([ if (defined(this._left)) { if (isArray(this._left)) { // Left can be an array if the type is LITERAL_COLOR or LITERAL_VECTOR - left = getExpressionArray(this._left, attributePrefix, shaderState); + left = getExpressionArray(this._left, attributePrefix, shaderState, this); } else { left = this._left.getShaderExpression(attributePrefix, shaderState, this); } @@ -1329,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; @@ -1502,7 +1617,16 @@ define([ } break; case ExpressionNodeType.LITERAL_VECTOR: - return 'vec4(' + left[0] + ', ' + left[1] + ', ' + left[2] + ', ' + left[3] + ')'; + 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/PointCloud3DTileContent.js b/Source/Scene/PointCloud3DTileContent.js index bfb377eeb02f..de7d99842219 100644 --- a/Source/Scene/PointCloud3DTileContent.js +++ b/Source/Scene/PointCloud3DTileContent.js @@ -800,8 +800,7 @@ define([ } // Edit the function header to accept the point position, color, and normal - // The styling language expects all vectors to be vec4 - return source.replace('()', '(vec4 position, vec4 color, vec4 normal)'); + return source.replace('()', '(vec3 position, vec4 color, vec3 normal)'); } function createShaders(content, frameState, style) { @@ -910,8 +909,6 @@ define([ } var attributeDeclarations = ''; - var attributeGlobals = ''; - var attributeDefaults = ''; var length = styleableProperties.length; for (i = 0; i < length; ++i) { @@ -928,19 +925,8 @@ define([ var attributeType; if (componentCount === 1) { attributeType = 'float'; - } else if (componentCount === 4) { - attributeType = 'vec4'; } else { - // The styling language expects all vectors to be vec4. GLSL can cast vertex attributes to vec4 but sets - // the w component to 1.0, while the styling language expects 0.0. Since vertex attributes are read-only, - // create global variables and set their w component to 0.0 in main(). - attributeType = 'vec4'; - if (componentCount < 4) { - var globalName = 'czm_tiles3d_style_' + name; - attributeName = 'czm_tiles3d_attribute_' + name; - attributeGlobals = 'vec4 ' + globalName + '; \n'; - attributeDefaults += ' ' + globalName + ' = vec4(' + attributeName + '.xyz, 0.0); \n'; - } + attributeType = 'vec' + componentCount; } attributeDeclarations += 'attribute ' + attributeType + ' ' + attributeName + '; \n'; @@ -955,7 +941,6 @@ define([ 'uniform float u_tilesetTime; \n'; vs += attributeDeclarations; - vs += attributeGlobals; if (usesColors) { if (isTranslucent) { @@ -1003,8 +988,6 @@ define([ vs += 'void main() \n' + '{ \n'; - vs += attributeDefaults; - if (usesColors) { if (isTranslucent) { vs += ' vec4 color = a_color; \n'; @@ -1041,15 +1024,15 @@ define([ } if (hasColorStyle) { - vs += ' color = getColorFromStyle(vec4(position, 0.0), color, vec4(normal, 0.0)); \n'; + vs += ' color = getColorFromStyle(position, color, normal); \n'; } if (hasShowStyle) { - vs += ' float show = float(getShowFromStyle(vec4(position, 0.0), color, vec4(normal, 0.0))); \n'; + vs += ' float show = float(getShowFromStyle(position, color, normal)); \n'; } if (hasPointSizeStyle) { - vs += ' gl_PointSize = getPointSizeFromStyle(vec4(position, 0.0), color, vec4(normal, 0.0)); \n'; + vs += ' gl_PointSize = getPointSizeFromStyle(position, color, normal); \n'; } else { vs += ' gl_PointSize = u_pointSize; \n'; } @@ -1219,4 +1202,4 @@ define([ }; return PointCloud3DTileContent; -}); +}); \ No newline at end of file diff --git a/Specs/Scene/ExpressionSpec.js b/Specs/Scene/ExpressionSpec.js index a39a449f1341..96c3bb595954 100644 --- a/Specs/Scene/ExpressionSpec.js +++ b/Specs/Scene/ExpressionSpec.js @@ -1,12 +1,16 @@ /*global defineSuite*/ defineSuite([ 'Scene/Expression', + 'Core/Cartesian2', + 'Core/Cartesian3', 'Core/Cartesian4', 'Core/Color', 'Core/Math', 'Scene/ExpressionNodeType' ], function( Expression, + Cartesian2, + Cartesian3, Cartesian4, Color, CesiumMath, @@ -557,24 +561,138 @@ defineSuite([ expect(expression.evaluate(frameState, undefined)).toEqual(0.5); }); - it('evaluates vector', function() { + 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(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.0, 0.0, 0.0, 0.0)); + 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('vec3()'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.0, 0.0, 0.0, 0.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()'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.0, 0.0, 0.0, 0.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)); + }); - expression = new Expression('vec2(1.0, 2.0)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1.0, 2.0, 0.0, 0.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('vec3(1.0, 2.0, 3.0)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1.0, 2.0, 3.0, 0.0)); + 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(1.0, 2.0, 3.0, 4.0)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1.0, 2.0, 3.0, 4.0)); + 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() { @@ -589,8 +707,8 @@ defineSuite([ }); it('evaluates expression with multiple nested vectors', function() { - var expression = new Expression('vec4(vec2(1, 2)[vec3(6, 1, 5).y], 2, vec4().w, 5)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(2.0, 2.0, 0.0, 5.0)); + 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() { @@ -607,7 +725,7 @@ defineSuite([ expect(expression.evaluate(frameState, undefined)).toEqual(4.0); }); - it('evaluates vector properties ([0], [1], [2]. [3])', function() { + 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); @@ -929,42 +1047,42 @@ defineSuite([ it('evaluates vector operations', function() { var expression = new Expression('+vec3(1, 2, 3)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1, 2, 3, 0)); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(1, 2, 3)); expression = new Expression('-vec3(1, 2, 3)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(-1, -2, -3, 0)); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(-1, -2, -3)); - expression = new Expression('vec2(1, 2) + vec4(3, 4, 5, 6)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(4, 6, 5, 6)); + expression = new Expression('vec2(1, 2) + vec2(3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(4, 6)); - expression = new Expression('vec2(1, 2) - vec4(3, 4, 5, 6)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(-2, -2, -5, -6)); + expression = new Expression('vec2(1, 2) - vec2(3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(-2, -2)); 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('vec3(1, 2, 3) * 3.0'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3, 6, 9, 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 Cartesian4(3, 6, 9, 0)); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3, 6, 9)); - expression = new Expression('vec3(1, 2, 3) / vec4(2, 5, 3, 1)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.5, 0.4, 1.0, 0.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 Cartesian4(0.5, 1.0, 1.5, 0.0)); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(0.5, 1.0, 1.5)); - expression = new Expression('vec4(2, 3, 4, 5) % vec3(3, 3, 3, 2)'); + 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) == vec4(1, 3, 0, 0)'); + expression = new Expression('vec2(1, 3) == vec2(1, 3)'); expect(expression.evaluate(frameState, undefined)).toEqual(true); - expression = new Expression('vec2(1, 3) === vec4(1, 3, 0, 0)'); + expression = new Expression('vec4(1, 2, 3, 4) === vec4(1, 2, 3, 4)'); expect(expression.evaluate(frameState, undefined)).toEqual(true); - expression = new Expression('!!vec4() == true'); + expression = new Expression('!!vec4(1.0) == true'); expect(expression.evaluate(frameState, undefined)).toEqual(true); expression = new Expression('vec3(1, 2, 3) != vec3(1, 2, 3)'); @@ -992,7 +1110,13 @@ defineSuite([ var feature = new MockFeature(); feature.addProperty('property', new Cartesian4(1, 2, 3, 4)); - var expression = new Expression('vec4(1, 2, 3, 4).toString()'); + 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()'); @@ -2160,16 +2284,6 @@ defineSuite([ expression = new Expression('color()[0] + color()[1] + color()[2] + color()[3]'); shaderExpression = expression.getShaderExpression('', {}); expect(shaderExpression).toEqual(expected); - - // ['red'], ['green'], ['blue'], ['alpha'] - expression = new Expression('color()["red"] + color()["green"] + color()["blue"] + color()["alpha"]'); - shaderExpression = expression.getShaderExpression('', {}); - 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); }); it('gets shader expression for vector', function() { @@ -2177,29 +2291,28 @@ defineSuite([ var shaderExpression = expression.getShaderExpression('', {}); expect(shaderExpression).toEqual('vec4(1.0, 2.0, 3.0, 4.0)'); - expression = new Expression('vec2() + vec3() + vec4()'); + 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(0.0, 0.0, 0.0, 0.0) + vec4(0.0, 0.0, 0.0, 0.0)) + vec4(0.0, 0.0, 0.0, 0.0))'); + expect(shaderExpression).toEqual('vec4(1.0, property, vec2(1.0, 2.0)[0], 0.0)'); - expression = new Expression('vec4(1, ${property}, vec2(1, 2).x)'); + expression = new Expression('vec4(vec3(2), 1.0)'); shaderExpression = expression.getShaderExpression('', {}); - expect(shaderExpression).toEqual('vec4(1.0, property, vec4(1.0, 2.0, 0.0, 0.0)[0], 0.0)'); + 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().x + vec4().y + vec4().z + vec4().w'); + var expression = new Expression('vec4(1).x + vec4(1).y + vec4(1).z + vec4(1).w'); var shaderExpression = expression.getShaderExpression('', {}); - var expected = '(((vec4(0.0, 0.0, 0.0, 0.0)[0] + vec4(0.0, 0.0, 0.0, 0.0)[1]) + vec4(0.0, 0.0, 0.0, 0.0)[2]) + vec4(0.0, 0.0, 0.0, 0.0)[3])'; + 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()[0] + vec4()[1] + vec4()[2] + vec4()[3]'); - shaderExpression = expression.getShaderExpression('', {}); - expect(shaderExpression).toEqual(expected); - - // ['x'], ['y'], ['z'], ['w'] - expression = new Expression('vec4()["x"] + vec4()["y"] + vec4()["z"] + vec4()["w"]'); + expression = new Expression('vec4(1)[0] + vec4(1)[1] + vec4(1)[2] + vec4(1)[3]'); shaderExpression = expression.getShaderExpression('', {}); expect(shaderExpression).toEqual(expected); });