123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755 |
- import Cartesian2 from '../Core/Cartesian2.js';
- import Cartesian3 from '../Core/Cartesian3.js';
- import Cartesian4 from '../Core/Cartesian4.js';
- import Check from '../Core/Check.js';
- import Color from '../Core/Color.js';
- import defined from '../Core/defined.js';
- import defineProperties from '../Core/defineProperties.js';
- import DeveloperError from '../Core/DeveloperError.js';
- import isArray from '../Core/isArray.js';
- import CesiumMath from '../Core/Math.js';
- import RuntimeError from '../Core/RuntimeError.js';
- import jsep from '../ThirdParty/jsep.js';
- import ExpressionNodeType from './ExpressionNodeType.js';
- /**
- * An expression for a style applied to a {@link Cesium3DTileset}.
- * <p>
- * Evaluates an expression defined using the
- * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification/Styling|3D Tiles Styling language}.
- * </p>
- * <p>
- * Implements the {@link StyleExpression} interface.
- * </p>
- *
- * @alias Expression
- * @constructor
- *
- * @param {String} [expression] The expression defined using the 3D Tiles Styling language.
- * @param {Object} [defines] Defines in the style.
- *
- * @example
- * var expression = new Cesium.Expression('(regExp("^Chest").test(${County})) && (${YearBuilt} >= 1970)');
- * expression.evaluate(feature); // returns true or false depending on the feature's properties
- *
- * @example
- * var expression = new Cesium.Expression('(${Temperature} > 90) ? color("red") : color("white")');
- * expression.evaluateColor(feature, result); // returns a Cesium.Color object
- */
- function Expression(expression, defines) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.string('expression', expression);
- //>>includeEnd('debug');
- this._expression = expression;
- expression = replaceDefines(expression, defines);
- expression = replaceVariables(removeBackslashes(expression));
- // customize jsep operators
- jsep.addBinaryOp('=~', 0);
- jsep.addBinaryOp('!~', 0);
- var ast;
- try {
- ast = jsep(expression);
- } catch (e) {
- throw new RuntimeError(e);
- }
- this._runtimeAst = createRuntimeAst(this, ast);
- }
- defineProperties(Expression.prototype, {
- /**
- * Gets the expression defined in the 3D Tiles Styling language.
- *
- * @memberof Expression.prototype
- *
- * @type {String}
- * @readonly
- *
- * @default undefined
- */
- expression : {
- get : function() {
- return this._expression;
- }
- }
- });
- // Scratch storage manager while evaluating deep expressions.
- // For example, an expression like dot(vec4(${red}), vec4(${green}) * vec4(${blue}) requires 3 scratch Cartesian4's
- var scratchStorage = {
- arrayIndex : 0,
- arrayArray : [[]],
- cartesian2Index : 0,
- cartesian3Index : 0,
- cartesian4Index : 0,
- cartesian2Array : [new Cartesian2()],
- cartesian3Array : [new Cartesian3()],
- cartesian4Array : [new Cartesian4()],
- reset : function() {
- this.arrayIndex = 0;
- this.cartesian2Index = 0;
- this.cartesian3Index = 0;
- this.cartesian4Index = 0;
- },
- getArray : function() {
- if (this.arrayIndex >= this.arrayArray.length) {
- this.arrayArray.push([]);
- }
- var array = this.arrayArray[this.arrayIndex++];
- array.length = 0;
- return array;
- },
- getCartesian2 : function() {
- if (this.cartesian2Index >= this.cartesian2Array.length) {
- this.cartesian2Array.push(new Cartesian2());
- }
- return this.cartesian2Array[this.cartesian2Index++];
- },
- getCartesian3 : function() {
- if (this.cartesian3Index >= this.cartesian3Array.length) {
- this.cartesian3Array.push(new Cartesian3());
- }
- return this.cartesian3Array[this.cartesian3Index++];
- },
- getCartesian4 : function() {
- if (this.cartesian4Index >= this.cartesian4Array.length) {
- this.cartesian4Array.push(new Cartesian4());
- }
- return this.cartesian4Array[this.cartesian4Index++];
- }
- };
- /**
- * Evaluates the result of an expression, optionally using the provided feature's properties. If the result of
- * the expression in the
- * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification/Styling|3D Tiles Styling language}
- * is of type <code>Boolean</code>, <code>Number</code>, or <code>String</code>, the corresponding JavaScript
- * primitive type will be returned. If the result is a <code>RegExp</code>, a Javascript <code>RegExp</code>
- * object will be returned. If the result is a <code>Cartesian2</code>, <code>Cartesian3</code>, or <code>Cartesian4</code>,
- * a {@link Cartesian2}, {@link Cartesian3}, or {@link Cartesian4} object will be returned. If the <code>result</code> argument is
- * a {@link Color}, the {@link Cartesian4} value is converted to a {@link Color} and then returned.
- *
- * @param {Cesium3DTileFeature} feature The feature whose properties may be used as variables in the expression.
- * @param {Object} [result] The object onto which to store the result.
- * @returns {Boolean|Number|String|RegExp|Cartesian2|Cartesian3|Cartesian4|Color} The result of evaluating the expression.
- */
- Expression.prototype.evaluate = function(feature, result) {
- scratchStorage.reset();
- var value = this._runtimeAst.evaluate(feature);
- if ((result instanceof Color) && (value instanceof Cartesian4)) {
- return Color.fromCartesian4(value, result);
- }
- if ((value instanceof Cartesian2) || (value instanceof Cartesian3) || (value instanceof Cartesian4)) {
- return value.clone(result);
- }
- return value;
- };
- /**
- * Evaluates the result of a Color expression, optionally using the provided feature's properties.
- * <p>
- * This is equivalent to {@link Expression#evaluate} but always returns a {@link Color} object.
- * </p>
- *
- * @param {Cesium3DTileFeature} feature The feature whose properties may be used as variables in the expression.
- * @param {Color} [result] The object in which to store the result
- * @returns {Color} The modified result parameter or a new Color instance if one was not provided.
- */
- Expression.prototype.evaluateColor = function(feature, result) {
- scratchStorage.reset();
- var color = this._runtimeAst.evaluate(feature);
- return Color.fromCartesian4(color, result);
- };
- /**
- * Gets the shader function for this expression.
- * Returns undefined if the shader function can't be generated from this expression.
- *
- * @param {String} functionName Name to give to the generated function.
- * @param {String} attributePrefix Prefix that is added to any variable names to access vertex attributes.
- * @param {Object} shaderState Stores information about the generated shader function, including whether it is translucent.
- * @param {String} returnType The return type of the generated function.
- *
- * @returns {String} The shader function.
- *
- * @private
- */
- Expression.prototype.getShaderFunction = function(functionName, attributePrefix, shaderState, returnType) {
- var shaderExpression = this.getShaderExpression(attributePrefix, shaderState);
- shaderExpression = returnType + ' ' + functionName + '() \n' +
- '{ \n' +
- ' return ' + shaderExpression + '; \n' +
- '} \n';
- return shaderExpression;
- };
- /**
- * Gets the shader expression for this expression.
- * Returns undefined if the shader expression can't be generated from this expression.
- *
- * @param {String} attributePrefix Prefix that is added to any variable names to access vertex attributes.
- * @param {Object} shaderState Stores information about the generated shader function, including whether it is translucent.
- *
- * @returns {String} The shader expression.
- *
- * @private
- */
- Expression.prototype.getShaderExpression = function(attributePrefix, shaderState) {
- return this._runtimeAst.getShaderExpression(attributePrefix, shaderState);
- };
- var unaryOperators = ['!', '-', '+'];
- var binaryOperators = ['+', '-', '*', '/', '%', '===', '!==', '>', '>=', '<', '<=', '&&', '||', '!~', '=~'];
- var variableRegex = /\${(.*?)}/g; // Matches ${variable_name}
- var backslashRegex = /\\/g;
- var backslashReplacement = '@#%';
- var replacementRegex = /@#%/g;
- var scratchColor = new Color();
- var unaryFunctions = {
- abs : getEvaluateUnaryComponentwise(Math.abs),
- sqrt : getEvaluateUnaryComponentwise(Math.sqrt),
- cos : getEvaluateUnaryComponentwise(Math.cos),
- sin : getEvaluateUnaryComponentwise(Math.sin),
- tan : getEvaluateUnaryComponentwise(Math.tan),
- acos : getEvaluateUnaryComponentwise(Math.acos),
- asin : getEvaluateUnaryComponentwise(Math.asin),
- atan : getEvaluateUnaryComponentwise(Math.atan),
- radians : getEvaluateUnaryComponentwise(CesiumMath.toRadians),
- degrees : getEvaluateUnaryComponentwise(CesiumMath.toDegrees),
- sign : getEvaluateUnaryComponentwise(CesiumMath.sign),
- floor : getEvaluateUnaryComponentwise(Math.floor),
- ceil : getEvaluateUnaryComponentwise(Math.ceil),
- round : getEvaluateUnaryComponentwise(Math.round),
- exp : getEvaluateUnaryComponentwise(Math.exp),
- exp2 : getEvaluateUnaryComponentwise(exp2),
- log : getEvaluateUnaryComponentwise(Math.log),
- log2 : getEvaluateUnaryComponentwise(log2),
- fract : getEvaluateUnaryComponentwise(fract),
- length : length,
- normalize: normalize
- };
- var binaryFunctions = {
- atan2 : getEvaluateBinaryComponentwise(Math.atan2, false),
- pow : getEvaluateBinaryComponentwise(Math.pow, false),
- min : getEvaluateBinaryComponentwise(Math.min, true),
- max : getEvaluateBinaryComponentwise(Math.max, true),
- distance : distance,
- dot : dot,
- cross : cross
- };
- var ternaryFunctions = {
- clamp : getEvaluateTernaryComponentwise(CesiumMath.clamp, true),
- mix : getEvaluateTernaryComponentwise(CesiumMath.lerp, true)
- };
- function fract(number) {
- return number - Math.floor(number);
- }
- function exp2(exponent) {
- return Math.pow(2.0,exponent);
- }
- function log2(number) {
- return CesiumMath.log2(number);
- }
- function getEvaluateUnaryComponentwise(operation) {
- return function(call, left) {
- if (typeof left === 'number') {
- return operation(left);
- } else if (left instanceof Cartesian2) {
- return Cartesian2.fromElements(operation(left.x), operation(left.y), scratchStorage.getCartesian2());
- } else if (left instanceof Cartesian3) {
- return Cartesian3.fromElements(operation(left.x), operation(left.y), operation(left.z), scratchStorage.getCartesian3());
- } else if (left instanceof Cartesian4) {
- return Cartesian4.fromElements(operation(left.x), operation(left.y), operation(left.z), operation(left.w), scratchStorage.getCartesian4());
- }
- throw new RuntimeError('Function "' + call + '" requires a vector or number argument. Argument is ' + left + '.');
- };
- }
- function getEvaluateBinaryComponentwise(operation, allowScalar) {
- return function(call, left, right) {
- if (allowScalar && typeof right === 'number') {
- if (typeof left === 'number') {
- return operation(left, right);
- } else if (left instanceof Cartesian2) {
- return Cartesian2.fromElements(operation(left.x, right), operation(left.y, right), scratchStorage.getCartesian2());
- } else if (left instanceof Cartesian3) {
- return Cartesian3.fromElements(operation(left.x, right), operation(left.y, right), operation(left.z, right), scratchStorage.getCartesian3());
- } else if (left instanceof Cartesian4) {
- return Cartesian4.fromElements(operation(left.x, right), operation(left.y, right), operation(left.z, right), operation(left.w, right), scratchStorage.getCartesian4());
- }
- }
- if (typeof left === 'number' && typeof right === 'number') {
- return operation(left, right);
- } else if (left instanceof Cartesian2 && right instanceof Cartesian2) {
- return Cartesian2.fromElements(operation(left.x, right.x), operation(left.y, right.y), scratchStorage.getCartesian2());
- } else if (left instanceof Cartesian3 && right instanceof Cartesian3) {
- return Cartesian3.fromElements(operation(left.x, right.x), operation(left.y, right.y), operation(left.z, right.z), scratchStorage.getCartesian3());
- } else if (left instanceof Cartesian4 && right instanceof Cartesian4) {
- return Cartesian4.fromElements(operation(left.x, right.x), operation(left.y, right.y), operation(left.z, right.z), operation(left.w, right.w), scratchStorage.getCartesian4());
- }
- throw new RuntimeError('Function "' + call + '" requires vector or number arguments of matching types. Arguments are ' + left + ' and ' + right + '.');
- };
- }
- function getEvaluateTernaryComponentwise(operation, allowScalar) {
- return function(call, left, right, test) {
- if (allowScalar && typeof test === 'number') {
- if (typeof left === 'number' && typeof right === 'number') {
- return operation(left, right, test);
- } else if (left instanceof Cartesian2 && right instanceof Cartesian2) {
- return Cartesian2.fromElements(operation(left.x, right.x, test), operation(left.y, right.y, test), scratchStorage.getCartesian2());
- } else if (left instanceof Cartesian3 && right instanceof Cartesian3) {
- return Cartesian3.fromElements(operation(left.x, right.x, test), operation(left.y, right.y, test), operation(left.z, right.z, test), scratchStorage.getCartesian3());
- } else if (left instanceof Cartesian4 && right instanceof Cartesian4) {
- return Cartesian4.fromElements(operation(left.x, right.x, test), operation(left.y, right.y, test), operation(left.z, right.z, test), operation(left.w, right.w, test), scratchStorage.getCartesian4());
- }
- }
- if (typeof left === 'number' && typeof right === 'number' && typeof test === 'number') {
- return operation(left, right, test);
- } else if (left instanceof Cartesian2 && right instanceof Cartesian2 && test instanceof Cartesian2) {
- return Cartesian2.fromElements(operation(left.x, right.x, test.x), operation(left.y, right.y, test.y), scratchStorage.getCartesian2());
- } else if (left instanceof Cartesian3 && right instanceof Cartesian3 && test instanceof Cartesian3) {
- return Cartesian3.fromElements(operation(left.x, right.x, test.x), operation(left.y, right.y, test.y), operation(left.z, right.z, test.z), scratchStorage.getCartesian3());
- } else if (left instanceof Cartesian4 && right instanceof Cartesian4 && test instanceof Cartesian4) {
- return Cartesian4.fromElements(operation(left.x, right.x, test.x), operation(left.y, right.y, test.y), operation(left.z, right.z, test.z), operation(left.w, right.w, test.w), scratchStorage.getCartesian4());
- }
- throw new RuntimeError('Function "' + call + '" requires vector or number arguments of matching types. Arguments are ' + left + ', ' + right + ', and ' + test + '.');
- };
- }
- function length(call, left) {
- if (typeof left === 'number') {
- return Math.abs(left);
- } else if (left instanceof Cartesian2) {
- return Cartesian2.magnitude(left);
- } else if (left instanceof Cartesian3) {
- return Cartesian3.magnitude(left);
- } else if (left instanceof Cartesian4) {
- return Cartesian4.magnitude(left);
- }
- throw new RuntimeError('Function "' + call + '" requires a vector or number argument. Argument is ' + left + '.');
- }
- function normalize(call, left) {
- if (typeof left === 'number') {
- return 1.0;
- } else if (left instanceof Cartesian2) {
- return Cartesian2.normalize(left, scratchStorage.getCartesian2());
- } else if (left instanceof Cartesian3) {
- return Cartesian3.normalize(left, scratchStorage.getCartesian3());
- } else if (left instanceof Cartesian4) {
- return Cartesian4.normalize(left, scratchStorage.getCartesian4());
- }
- throw new RuntimeError('Function "' + call + '" requires a vector or number argument. Argument is ' + left + '.');
- }
- function distance(call, left, right) {
- if (typeof left === 'number' && typeof right === 'number') {
- return Math.abs(left - right);
- } else if (left instanceof Cartesian2 && right instanceof Cartesian2) {
- return Cartesian2.distance(left, right);
- } else if (left instanceof Cartesian3 && right instanceof Cartesian3) {
- return Cartesian3.distance(left, right);
- } else if (left instanceof Cartesian4 && right instanceof Cartesian4) {
- return Cartesian4.distance(left, right);
- }
- throw new RuntimeError('Function "' + call + '" requires vector or number arguments of matching types. Arguments are ' + left + ' and ' + right + '.');
- }
- function dot(call, left, right) {
- if (typeof left === 'number' && typeof right === 'number') {
- return left * right;
- } else if (left instanceof Cartesian2 && right instanceof Cartesian2) {
- return Cartesian2.dot(left, right);
- } else if (left instanceof Cartesian3 && right instanceof Cartesian3) {
- return Cartesian3.dot(left, right);
- } else if (left instanceof Cartesian4 && right instanceof Cartesian4) {
- return Cartesian4.dot(left, right);
- }
- throw new RuntimeError('Function "' + call + '" requires vector or number arguments of matching types. Arguments are ' + left + ' and ' + right + '.');
- }
- function cross(call, left, right) {
- if (left instanceof Cartesian3 && right instanceof Cartesian3) {
- return Cartesian3.cross(left, right, scratchStorage.getCartesian3());
- }
- throw new RuntimeError('Function "' + call + '" requires vec3 arguments. Arguments are ' + left + ' and ' + right + '.');
- }
- function Node(type, value, left, right, test) {
- this._type = type;
- this._value = value;
- this._left = left;
- this._right = right;
- this._test = test;
- this.evaluate = undefined;
- setEvaluateFunction(this);
- }
- function replaceDefines(expression, defines) {
- if (!defined(defines)) {
- return expression;
- }
- for (var key in defines) {
- if (defines.hasOwnProperty(key)) {
- var definePlaceholder = new RegExp('\\$\\{' + key + '\\}', 'g');
- var defineReplace = '(' + defines[key] + ')';
- if (defined(defineReplace)) {
- expression = expression.replace(definePlaceholder, defineReplace);
- }
- }
- }
- return expression;
- }
- function removeBackslashes(expression) {
- return expression.replace(backslashRegex, backslashReplacement);
- }
- function replaceBackslashes(expression) {
- return expression.replace(replacementRegex, '\\');
- }
- function replaceVariables(expression) {
- var exp = expression;
- var result = '';
- var i = exp.indexOf('${');
- while (i >= 0) {
- // Check if string is inside quotes
- var openSingleQuote = exp.indexOf('\'');
- var openDoubleQuote = exp.indexOf('"');
- var closeQuote;
- if (openSingleQuote >= 0 && openSingleQuote < i) {
- closeQuote = exp.indexOf('\'', openSingleQuote + 1);
- result += exp.substr(0, closeQuote + 1);
- exp = exp.substr(closeQuote + 1);
- i = exp.indexOf('${');
- } else if (openDoubleQuote >= 0 && openDoubleQuote < i) {
- closeQuote = exp.indexOf('"', openDoubleQuote + 1);
- result += exp.substr(0, closeQuote + 1);
- exp = exp.substr(closeQuote + 1);
- i = exp.indexOf('${');
- } else {
- result += exp.substr(0, i);
- var j = exp.indexOf('}');
- if (j < 0) {
- throw new RuntimeError('Unmatched {.');
- }
- result += 'czm_' + exp.substr(i + 2, j - (i + 2));
- exp = exp.substr(j + 1);
- i = exp.indexOf('${');
- }
- }
- result += exp;
- return result;
- }
- function parseLiteral(ast) {
- var type = typeof ast.value;
- if (ast.value === null) {
- return new Node(ExpressionNodeType.LITERAL_NULL, null);
- } else if (type === 'boolean') {
- return new Node(ExpressionNodeType.LITERAL_BOOLEAN, ast.value);
- } else if (type === 'number') {
- return new Node(ExpressionNodeType.LITERAL_NUMBER, ast.value);
- } else if (type === 'string') {
- if (ast.value.indexOf('${') >= 0) {
- return new Node(ExpressionNodeType.VARIABLE_IN_STRING, ast.value);
- }
- return new Node(ExpressionNodeType.LITERAL_STRING, replaceBackslashes(ast.value));
- }
- }
- function parseCall(expression, ast) {
- var args = ast.arguments;
- var argsLength = args.length;
- var call;
- var val, left, right;
- // Member function calls
- if (ast.callee.type === 'MemberExpression') {
- call = ast.callee.property.name;
- var object = ast.callee.object;
- if (call === 'test' || call === 'exec') {
- // Make sure this is called on a valid type
- if (object.callee.name !== 'regExp') {
- throw new RuntimeError(call + ' is not a function.');
- }
- if (argsLength === 0) {
- if (call === 'test') {
- return new Node(ExpressionNodeType.LITERAL_BOOLEAN, false);
- }
- return new Node(ExpressionNodeType.LITERAL_NULL, null);
- }
- left = createRuntimeAst(expression, object);
- right = createRuntimeAst(expression, args[0]);
- return new Node(ExpressionNodeType.FUNCTION_CALL, call, left, right);
- } else if (call === 'toString') {
- val = createRuntimeAst(expression, object);
- return new Node(ExpressionNodeType.FUNCTION_CALL, call, val);
- }
- throw new RuntimeError('Unexpected function call "' + call + '".');
- }
- // Non-member function calls
- call = ast.callee.name;
- if (call === 'color') {
- if (argsLength === 0) {
- return new Node(ExpressionNodeType.LITERAL_COLOR, call);
- }
- val = createRuntimeAst(expression, args[0]);
- if (defined(args[1])) {
- var alpha = createRuntimeAst(expression, args[1]);
- return new Node(ExpressionNodeType.LITERAL_COLOR, call, [val, alpha]);
- }
- return new Node(ExpressionNodeType.LITERAL_COLOR, call, [val]);
- } else if (call === 'rgb' || call === 'hsl') {
- if (argsLength < 3) {
- throw new RuntimeError(call + ' requires three arguments.');
- }
- val = [
- createRuntimeAst(expression, args[0]),
- createRuntimeAst(expression, args[1]),
- createRuntimeAst(expression, args[2])
- ];
- return new Node(ExpressionNodeType.LITERAL_COLOR, call, val);
- } else if (call === 'rgba' || call === 'hsla') {
- if (argsLength < 4) {
- throw new RuntimeError(call + ' requires four arguments.');
- }
- val = [
- createRuntimeAst(expression, args[0]),
- createRuntimeAst(expression, args[1]),
- createRuntimeAst(expression, args[2]),
- 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 (argsLength === 0) {
- if (call === 'isNaN') {
- return new Node(ExpressionNodeType.LITERAL_BOOLEAN, true);
- }
- return new Node(ExpressionNodeType.LITERAL_BOOLEAN, false);
- }
- val = createRuntimeAst(expression, args[0]);
- return new Node(ExpressionNodeType.UNARY, call, val);
- } else if (call === 'isExactClass' || call === 'isClass') {
- if (argsLength < 1 || argsLength > 1) {
- throw new RuntimeError(call + ' requires exactly one argument.');
- }
- val = createRuntimeAst(expression, args[0]);
- return new Node(ExpressionNodeType.UNARY, call, val);
- } else if (call === 'getExactClassName') {
- if (argsLength > 0) {
- throw new RuntimeError(call + ' does not take any argument.');
- }
- return new Node(ExpressionNodeType.UNARY, call);
- } else if (defined(unaryFunctions[call])) {
- if (argsLength !== 1) {
- throw new RuntimeError(call + ' requires exactly one argument.');
- }
- val = createRuntimeAst(expression, args[0]);
- return new Node(ExpressionNodeType.UNARY, call, val);
- } else if (defined(binaryFunctions[call])) {
- if (argsLength !== 2) {
- throw new RuntimeError(call + ' requires exactly two arguments.');
- }
- left = createRuntimeAst(expression, args[0]);
- right = createRuntimeAst(expression, args[1]);
- return new Node(ExpressionNodeType.BINARY, call, left, right);
- } else if (defined(ternaryFunctions[call])) {
- if (argsLength !== 3) {
- throw new RuntimeError(call + ' requires exactly three arguments.');
- }
- left = createRuntimeAst(expression, args[0]);
- right = createRuntimeAst(expression, args[1]);
- var test = createRuntimeAst(expression, args[2]);
- return new Node(ExpressionNodeType.TERNARY, call, left, right, test);
- } else if (call === 'Boolean') {
- if (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 (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 (argsLength === 0) {
- return new Node(ExpressionNodeType.LITERAL_STRING, '');
- }
- val = createRuntimeAst(expression, args[0]);
- return new Node(ExpressionNodeType.UNARY, call, val);
- } else if (call === 'regExp') {
- return parseRegex(expression, ast);
- }
- throw new RuntimeError('Unexpected function call "' + call + '".');
- }
- function parseRegex(expression, ast) {
- var args = ast.arguments;
- // no arguments, return default regex
- if (args.length === 0) {
- return new Node(ExpressionNodeType.LITERAL_REGEX, new RegExp());
- }
- var pattern = createRuntimeAst(expression, args[0]);
- var exp;
- // optional flag argument supplied
- if (args.length > 1) {
- var flags = createRuntimeAst(expression, args[1]);
- if (isLiteralType(pattern) && isLiteralType(flags)) {
- try {
- exp = new RegExp(replaceBackslashes(String(pattern._value)), flags._value);
- } catch (e) {
- throw new RuntimeError(e);
- }
- return new Node(ExpressionNodeType.LITERAL_REGEX, exp);
- }
- return new Node(ExpressionNodeType.REGEX, pattern, flags);
- }
- // only pattern argument supplied
- if (isLiteralType(pattern)) {
- try {
- exp = new RegExp(replaceBackslashes(String(pattern._value)));
- } catch (e) {
- throw new RuntimeError(e);
- }
- return new Node(ExpressionNodeType.LITERAL_REGEX, exp);
- }
- return new Node(ExpressionNodeType.REGEX, pattern);
- }
- function parseKeywordsAndVariables(ast) {
- if (isVariable(ast.name)) {
- var name = getPropertyName(ast.name);
- if (name.substr(0, 8) === 'tiles3d_') {
- return new Node(ExpressionNodeType.BUILTIN_VARIABLE, name);
- }
- return new Node(ExpressionNodeType.VARIABLE, name);
- } else if (ast.name === 'NaN') {
- return new Node(ExpressionNodeType.LITERAL_NUMBER, NaN);
- } else if (ast.name === 'Infinity') {
- return new Node(ExpressionNodeType.LITERAL_NUMBER, Infinity);
- } else if (ast.name === 'undefined') {
- return new Node(ExpressionNodeType.LITERAL_UNDEFINED, undefined);
- }
- throw new RuntimeError(ast.name + ' is not defined.');
- }
- function parseMathConstant(ast) {
- var name = ast.property.name;
- if (name === 'PI') {
- return new Node(ExpressionNodeType.LITERAL_NUMBER, Math.PI);
- } else if (name === 'E') {
- return new Node(ExpressionNodeType.LITERAL_NUMBER, Math.E);
- }
- }
- function parseNumberConstant(ast) {
- var name = ast.property.name;
- if (name === 'POSITIVE_INFINITY') {
- return new Node(ExpressionNodeType.LITERAL_NUMBER, Number.POSITIVE_INFINITY);
- }
- }
- function parseMemberExpression(expression, ast) {
- if (ast.object.name === 'Math') {
- return parseMathConstant(ast);
- } else if (ast.object.name === 'Number') {
- return parseNumberConstant(ast);
- }
- var val;
- var obj = createRuntimeAst(expression, ast.object);
- if (ast.computed) {
- val = createRuntimeAst(expression, ast.property);
- return new Node(ExpressionNodeType.MEMBER, 'brackets', obj, val);
- }
- val = new Node(ExpressionNodeType.LITERAL_STRING, ast.property.name);
- return new Node(ExpressionNodeType.MEMBER, 'dot', obj, val);
- }
- function isLiteralType(node) {
- return (node._type >= ExpressionNodeType.LITERAL_NULL);
- }
- function isVariable(name) {
- return (name.substr(0, 4) === 'czm_');
- }
- function getPropertyName(variable) {
- return variable.substr(4);
- }
- function createRuntimeAst(expression, ast) {
- var node;
- var op;
- var left;
- var right;
- if (ast.type === 'Literal') {
- node = parseLiteral(ast);
- } else if (ast.type === 'CallExpression') {
- node = parseCall(expression, ast);
- } else if (ast.type === 'Identifier') {
- node = parseKeywordsAndVariables(ast);
- } else if (ast.type === 'UnaryExpression') {
- op = ast.operator;
- var child = createRuntimeAst(expression, ast.argument);
- if (unaryOperators.indexOf(op) > -1) {
- node = new Node(ExpressionNodeType.UNARY, op, child);
- } else {
- throw new RuntimeError('Unexpected operator "' + op + '".');
- }
- } else if (ast.type === 'BinaryExpression') {
- op = ast.operator;
- left = createRuntimeAst(expression, ast.left);
- right = createRuntimeAst(expression, ast.right);
- if (binaryOperators.indexOf(op) > -1) {
- node = new Node(ExpressionNodeType.BINARY, op, left, right);
- } else {
- throw new RuntimeError('Unexpected operator "' + op + '".');
- }
- } else if (ast.type === 'LogicalExpression') {
- op = ast.operator;
- left = createRuntimeAst(expression, ast.left);
- right = createRuntimeAst(expression, ast.right);
- if (binaryOperators.indexOf(op) > -1) {
- node = new Node(ExpressionNodeType.BINARY, op, left, right);
- }
- } else if (ast.type === 'ConditionalExpression') {
- var test = createRuntimeAst(expression, ast.test);
- left = createRuntimeAst(expression, ast.consequent);
- right = createRuntimeAst(expression, ast.alternate);
- node = new Node(ExpressionNodeType.CONDITIONAL, '?', left, right, test);
- } else if (ast.type === 'MemberExpression') {
- node = parseMemberExpression(expression, ast);
- } else if (ast.type === 'ArrayExpression') {
- var val = [];
- for (var i = 0; i < ast.elements.length; i++) {
- val[i] = createRuntimeAst(expression, ast.elements[i]);
- }
- node = new Node(ExpressionNodeType.ARRAY, val);
- } else if (ast.type === 'Compound') {
- // empty expression or multiple expressions
- throw new RuntimeError('Provide exactly one expression.');
- } else {
- throw new RuntimeError('Cannot parse expression.');
- }
- return node;
- }
- function setEvaluateFunction(node) {
- if (node._type === ExpressionNodeType.CONDITIONAL) {
- node.evaluate = node._evaluateConditional;
- } else if (node._type === ExpressionNodeType.FUNCTION_CALL) {
- if (node._value === 'test') {
- node.evaluate = node._evaluateRegExpTest;
- } else if (node._value === 'exec') {
- node.evaluate = node._evaluateRegExpExec;
- } else if (node._value === 'toString') {
- node.evaluate = node._evaluateToString;
- }
- } else if (node._type === ExpressionNodeType.UNARY) {
- if (node._value === '!') {
- node.evaluate = node._evaluateNot;
- } else if (node._value === '-') {
- node.evaluate = node._evaluateNegative;
- } else if (node._value === '+') {
- node.evaluate = node._evaluatePositive;
- } else if (node._value === 'isNaN') {
- node.evaluate = node._evaluateNaN;
- } else if (node._value === 'isFinite') {
- node.evaluate = node._evaluateIsFinite;
- } else if (node._value === 'isExactClass') {
- node.evaluate = node._evaluateIsExactClass;
- } else if (node._value === 'isClass') {
- node.evaluate = node._evaluateIsClass;
- } else if (node._value === 'getExactClassName') {
- node.evaluate = node._evaluateGetExactClassName;
- } else if (node._value === 'Boolean') {
- node.evaluate = node._evaluateBooleanConversion;
- } else if (node._value === 'Number') {
- node.evaluate = node._evaluateNumberConversion;
- } else if (node._value === 'String') {
- node.evaluate = node._evaluateStringConversion;
- } else if (defined(unaryFunctions[node._value])) {
- node.evaluate = getEvaluateUnaryFunction(node._value);
- }
- } else if (node._type === ExpressionNodeType.BINARY) {
- if (node._value === '+') {
- node.evaluate = node._evaluatePlus;
- } else if (node._value === '-') {
- node.evaluate = node._evaluateMinus;
- } else if (node._value === '*') {
- node.evaluate = node._evaluateTimes;
- } else if (node._value === '/') {
- node.evaluate = node._evaluateDivide;
- } else if (node._value === '%') {
- node.evaluate = node._evaluateMod;
- } else if (node._value === '===') {
- node.evaluate = node._evaluateEqualsStrict;
- } else if (node._value === '!==') {
- node.evaluate = node._evaluateNotEqualsStrict;
- } else if (node._value === '<') {
- node.evaluate = node._evaluateLessThan;
- } else if (node._value === '<=') {
- node.evaluate = node._evaluateLessThanOrEquals;
- } else if (node._value === '>') {
- node.evaluate = node._evaluateGreaterThan;
- } else if (node._value === '>=') {
- node.evaluate = node._evaluateGreaterThanOrEquals;
- } else if (node._value === '&&') {
- node.evaluate = node._evaluateAnd;
- } else if (node._value === '||') {
- node.evaluate = node._evaluateOr;
- } else if (node._value === '=~') {
- node.evaluate = node._evaluateRegExpMatch;
- } else if (node._value === '!~') {
- node.evaluate = node._evaluateRegExpNotMatch;
- } else if (defined(binaryFunctions[node._value])) {
- node.evaluate = getEvaluateBinaryFunction(node._value);
- }
- } else if (node._type === ExpressionNodeType.TERNARY) {
- node.evaluate = getEvaluateTernaryFunction(node._value);
- } else if (node._type === ExpressionNodeType.MEMBER) {
- if (node._value === 'brackets') {
- node.evaluate = node._evaluateMemberBrackets;
- } else {
- node.evaluate = node._evaluateMemberDot;
- }
- } else if (node._type === ExpressionNodeType.ARRAY) {
- node.evaluate = node._evaluateArray;
- } else if (node._type === ExpressionNodeType.VARIABLE) {
- node.evaluate = node._evaluateVariable;
- } else if (node._type === ExpressionNodeType.VARIABLE_IN_STRING) {
- 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) {
- node.evaluate = node._evaluateRegExp;
- } else if (node._type === ExpressionNodeType.BUILTIN_VARIABLE) {
- if (node._value === 'tiles3d_tileset_time') {
- node.evaluate = evaluateTilesetTime;
- }
- } else {
- node.evaluate = node._evaluateLiteral;
- }
- }
- function evaluateTilesetTime(feature) {
- if (!defined(feature)) {
- return 0.0;
- }
- return feature.content.tileset.timeSinceLoad;
- }
- function getEvaluateUnaryFunction(call) {
- var evaluate = unaryFunctions[call];
- return function(feature) {
- var left = this._left.evaluate(feature);
- return evaluate(call, left);
- };
- }
- function getEvaluateBinaryFunction(call) {
- var evaluate = binaryFunctions[call];
- return function(feature) {
- var left = this._left.evaluate(feature);
- var right = this._right.evaluate(feature);
- return evaluate(call, left, right);
- };
- }
- function getEvaluateTernaryFunction(call) {
- var evaluate = ternaryFunctions[call];
- return function(feature) {
- var left = this._left.evaluate(feature);
- var right = this._right.evaluate(feature);
- var test = this._test.evaluate(feature);
- return evaluate(call, left, right, test);
- };
- }
- function getFeatureProperty(feature, name) {
- // Returns undefined if the feature is not defined or the property name is not defined for that feature
- if (defined(feature)) {
- return feature.getProperty(name);
- }
- }
- Node.prototype._evaluateLiteral = function() {
- return this._value;
- };
- Node.prototype._evaluateLiteralColor = function(feature) {
- var color = scratchColor;
- var args = this._left;
- if (this._value === 'color') {
- if (!defined(args)) {
- Color.fromBytes(255, 255, 255, 255, color);
- } else if (args.length > 1) {
- Color.fromCssColorString(args[0].evaluate(feature), color);
- color.alpha = args[1].evaluate(feature);
- } else {
- Color.fromCssColorString(args[0].evaluate(feature), color);
- }
- } else if (this._value === 'rgb') {
- Color.fromBytes(
- args[0].evaluate(feature),
- args[1].evaluate(feature),
- args[2].evaluate(feature),
- 255, color);
- } else if (this._value === 'rgba') {
- // convert between css alpha (0 to 1) and cesium alpha (0 to 255)
- var a = args[3].evaluate(feature) * 255;
- Color.fromBytes(
- args[0].evaluate(feature),
- args[1].evaluate(feature),
- args[2].evaluate(feature),
- a, color);
- } else if (this._value === 'hsl') {
- Color.fromHsl(
- args[0].evaluate(feature),
- args[1].evaluate(feature),
- args[2].evaluate(feature),
- 1.0, color);
- } else if (this._value === 'hsla') {
- Color.fromHsl(
- args[0].evaluate(feature),
- args[1].evaluate(feature),
- args[2].evaluate(feature),
- args[3].evaluate(feature),
- color);
- }
- return Cartesian4.fromColor(color, scratchStorage.getCartesian4());
- };
- Node.prototype._evaluateLiteralVector = function(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 RuntimeError 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 call = this._value;
- var args = this._left;
- var argsLength = args.length;
- for (var i = 0; i < argsLength; ++i) {
- var value = args[i].evaluate(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);
- } else {
- throw new RuntimeError(call + ' argument must be a vector or number. Argument is ' + value + '.');
- }
- }
- var componentsLength = components.length;
- var vectorLength = parseInt(call.charAt(3));
- if (componentsLength === 0) {
- throw new RuntimeError('Invalid ' + call + ' constructor. No valid arguments.');
- } else if ((componentsLength < vectorLength) && (componentsLength > 1)) {
- throw new RuntimeError('Invalid ' + call + ' constructor. Not enough arguments.');
- } else if ((componentsLength > vectorLength) && (argsLength > 1)) {
- throw new RuntimeError('Invalid ' + call + ' constructor. Too many arguments.');
- }
- 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() {
- return this._value;
- };
- Node.prototype._evaluateVariableString = function(feature) {
- var result = this._value;
- var match = variableRegex.exec(result);
- while (match !== null) {
- var placeholder = match[0];
- var variableName = match[1];
- var property = getFeatureProperty(feature, variableName);
- if (!defined(property)) {
- property = '';
- }
- result = result.replace(placeholder, property);
- match = variableRegex.exec(result);
- }
- return result;
- };
- Node.prototype._evaluateVariable = function(feature) {
- // evaluates to undefined if the property name is not defined for that feature
- return getFeatureProperty(feature, this._value);
- };
- function checkFeature (ast) {
- return (ast._value === 'feature');
- }
- // PERFORMANCE_IDEA: Determine if parent property needs to be computed before runtime
- Node.prototype._evaluateMemberDot = function(feature) {
- if (checkFeature(this._left)) {
- return getFeatureProperty(feature, this._right.evaluate(feature));
- }
- var property = this._left.evaluate(feature);
- if (!defined(property)) {
- return undefined;
- }
- var member = this._right.evaluate(feature);
- if ((property instanceof Cartesian2) || (property instanceof Cartesian3) || (property instanceof Cartesian4)) {
- // Vector components may be accessed with .r, .g, .b, .a and implicitly with .x, .y, .z, .w
- if (member === 'r') {
- return property.x;
- } else if (member === 'g') {
- return property.y;
- } else if (member === 'b') {
- return property.z;
- } else if (member === 'a') {
- return property.w;
- }
- }
- return property[member];
- };
- Node.prototype._evaluateMemberBrackets = function(feature) {
- if (checkFeature(this._left)) {
- return getFeatureProperty(feature, this._right.evaluate(feature));
- }
- var property = this._left.evaluate(feature);
- if (!defined(property)) {
- return undefined;
- }
- var member = this._right.evaluate(feature);
- if ((property instanceof Cartesian2) || (property instanceof Cartesian3) || (property instanceof Cartesian4)) {
- // Vector components may be accessed with [0][1][2][3], ['r']['g']['b']['a'] and implicitly with ['x']['y']['z']['w']
- // For Cartesian2 and Cartesian3 out-of-range components will just return undefined
- if (member === 0 || member === 'r') {
- return property.x;
- } else if (member === 1 || member === 'g') {
- return property.y;
- } else if (member === 2 || member === 'b') {
- return property.z;
- } else if (member === 3 || member === 'a') {
- return property.w;
- }
- }
- return property[member];
- };
- Node.prototype._evaluateArray = function(feature) {
- var array = [];
- for (var i = 0; i < this._value.length; i++) {
- array[i] = this._value[i].evaluate(feature);
- }
- return array;
- };
- // PERFORMANCE_IDEA: Have "fast path" functions that deal only with specific types
- // that we can assign if we know the types before runtime
- Node.prototype._evaluateNot = function(feature) {
- var left = this._left.evaluate(feature);
- if (typeof left !== 'boolean') {
- throw new RuntimeError('Operator "!" requires a boolean argument. Argument is ' + left + '.');
- }
- return !left;
- };
- Node.prototype._evaluateNegative = function(feature) {
- var left = this._left.evaluate(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());
- } else if (typeof left === 'number') {
- return -left;
- }
- throw new RuntimeError('Operator "-" requires a vector or number argument. Argument is ' + left + '.');
- };
- Node.prototype._evaluatePositive = function(feature) {
- var left = this._left.evaluate(feature);
- if (!((left instanceof Cartesian2) || (left instanceof Cartesian3) || (left instanceof Cartesian4) || (typeof left === 'number'))) {
- throw new RuntimeError('Operator "+" requires a vector or number argument. Argument is ' + left + '.');
- }
- return left;
- };
- Node.prototype._evaluateLessThan = function(feature) {
- var left = this._left.evaluate(feature);
- var right = this._right.evaluate(feature);
- if ((typeof left !== 'number') || (typeof right !== 'number')) {
- throw new RuntimeError('Operator "<" requires number arguments. Arguments are ' + left + ' and ' + right + '.');
- }
- return left < right;
- };
- Node.prototype._evaluateLessThanOrEquals = function(feature) {
- var left = this._left.evaluate(feature);
- var right = this._right.evaluate(feature);
- if ((typeof left !== 'number') || (typeof right !== 'number')) {
- throw new RuntimeError('Operator "<=" requires number arguments. Arguments are ' + left + ' and ' + right + '.');
- }
- return left <= right;
- };
- Node.prototype._evaluateGreaterThan = function(feature) {
- var left = this._left.evaluate(feature);
- var right = this._right.evaluate(feature);
- if ((typeof left !== 'number') || (typeof right !== 'number')) {
- throw new RuntimeError('Operator ">" requires number arguments. Arguments are ' + left + ' and ' + right + '.');
- }
- return left > right;
- };
- Node.prototype._evaluateGreaterThanOrEquals = function(feature) {
- var left = this._left.evaluate(feature);
- var right = this._right.evaluate(feature);
- if ((typeof left !== 'number') || (typeof right !== 'number')) {
- throw new RuntimeError('Operator ">=" requires number arguments. Arguments are ' + left + ' and ' + right + '.');
- }
- return left >= right;
- };
- Node.prototype._evaluateOr = function(feature) {
- var left = this._left.evaluate(feature);
- if (typeof left !== 'boolean') {
- throw new RuntimeError('Operator "||" requires boolean arguments. First argument is ' + left + '.');
- }
- // short circuit the expression
- if (left) {
- return true;
- }
- var right = this._right.evaluate(feature);
- if (typeof right !== 'boolean') {
- throw new RuntimeError('Operator "||" requires boolean arguments. Second argument is ' + right + '.');
- }
- return left || right;
- };
- Node.prototype._evaluateAnd = function(feature) {
- var left = this._left.evaluate(feature);
- if (typeof left !== 'boolean') {
- throw new RuntimeError('Operator "&&" requires boolean arguments. First argument is ' + left + '.');
- }
- // short circuit the expression
- if (!left) {
- return false;
- }
- var right = this._right.evaluate(feature);
- if (typeof right !== 'boolean') {
- throw new RuntimeError('Operator "&&" requires boolean arguments. Second argument is ' + right + '.');
- }
- return left && right;
- };
- Node.prototype._evaluatePlus = function(feature) {
- var left = this._left.evaluate(feature);
- var right = this._right.evaluate(feature);
- 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());
- } else if ((typeof left === 'string') || (typeof right === 'string')) {
- // If only one argument is a string the other argument calls its toString function.
- return left + right;
- } else if ((typeof left === 'number') && (typeof right === 'number')) {
- return left + right;
- }
- throw new RuntimeError('Operator "+" requires vector or number arguments of matching types, or at least one string argument. Arguments are ' + left + ' and ' + right + '.');
- };
- Node.prototype._evaluateMinus = function(feature) {
- var left = this._left.evaluate(feature);
- var right = this._right.evaluate(feature);
- 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());
- } else if ((typeof left === 'number') && (typeof right === 'number')) {
- return left - right;
- }
- throw new RuntimeError('Operator "-" requires vector or number arguments of matching types. Arguments are ' + left + ' and ' + right + '.');
- };
- Node.prototype._evaluateTimes = function(feature) {
- var left = this._left.evaluate(feature);
- var right = this._right.evaluate(feature);
- 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());
- } else if ((typeof left === 'number') && (typeof right === 'number')) {
- return left * right;
- }
- throw new RuntimeError('Operator "*" requires vector or number arguments. If both arguments are vectors they must be matching types. Arguments are ' + left + ' and ' + right + '.');
- };
- Node.prototype._evaluateDivide = function(feature) {
- var left = this._left.evaluate(feature);
- var right = this._right.evaluate(feature);
- 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());
- } else if ((typeof left === 'number') && (typeof right === 'number')) {
- return left / right;
- }
- throw new RuntimeError('Operator "/" requires vector or number arguments of matching types, or a number as the second argument. Arguments are ' + left + ' and ' + right + '.');
- };
- Node.prototype._evaluateMod = function(feature) {
- var left = this._left.evaluate(feature);
- var right = this._right.evaluate(feature);
- 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());
- } else if ((typeof left === 'number') && (typeof right === 'number')) {
- return left % right;
- }
- throw new RuntimeError('Operator "%" requires vector or number arguments of matching types. Arguments are ' + left + ' and ' + right + '.');
- };
- Node.prototype._evaluateEqualsStrict = function(feature) {
- var left = this._left.evaluate(feature);
- var right = this._right.evaluate(feature);
- if ((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;
- };
- Node.prototype._evaluateNotEqualsStrict = function(feature) {
- var left = this._left.evaluate(feature);
- var right = this._right.evaluate(feature);
- if ((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;
- };
- Node.prototype._evaluateConditional = function(feature) {
- var test = this._test.evaluate(feature);
- if (typeof test !== 'boolean') {
- throw new RuntimeError('Conditional argument of conditional expression must be a boolean. Argument is ' + test + '.');
- }
- if (test) {
- return this._left.evaluate(feature);
- }
- return this._right.evaluate(feature);
- };
- Node.prototype._evaluateNaN = function(feature) {
- return isNaN(this._left.evaluate(feature));
- };
- Node.prototype._evaluateIsFinite = function(feature) {
- return isFinite(this._left.evaluate(feature));
- };
- Node.prototype._evaluateIsExactClass = function(feature) {
- if (defined(feature)) {
- return feature.isExactClass(this._left.evaluate(feature));
- }
- return false;
- };
- Node.prototype._evaluateIsClass = function(feature) {
- if (defined(feature)) {
- return feature.isClass(this._left.evaluate(feature));
- }
- return false;
- };
- Node.prototype._evaluateGetExactClassName = function(feature) {
- if (defined(feature)) {
- return feature.getExactClassName();
- }
- };
- Node.prototype._evaluateBooleanConversion = function(feature) {
- return Boolean(this._left.evaluate(feature));
- };
- Node.prototype._evaluateNumberConversion = function(feature) {
- return Number(this._left.evaluate(feature));
- };
- Node.prototype._evaluateStringConversion = function(feature) {
- return String(this._left.evaluate(feature));
- };
- Node.prototype._evaluateRegExp = function(feature) {
- var pattern = this._value.evaluate(feature);
- var flags = '';
- if (defined(this._left)) {
- flags = this._left.evaluate(feature);
- }
- var exp;
- try {
- exp = new RegExp(pattern, flags);
- } catch (e) {
- throw new RuntimeError(e);
- }
- return exp;
- };
- Node.prototype._evaluateRegExpTest = function(feature) {
- var left = this._left.evaluate(feature);
- var right = this._right.evaluate(feature);
- if (!((left instanceof RegExp) && (typeof right === 'string'))) {
- throw new RuntimeError('RegExp.test requires the first argument to be a RegExp and the second argument to be a string. Arguments are ' + left + ' and ' + right + '.');
- }
- return left.test(right);
- };
- Node.prototype._evaluateRegExpMatch = function(feature) {
- var left = this._left.evaluate(feature);
- var right = this._right.evaluate(feature);
- if ((left instanceof RegExp) && (typeof right === 'string')) {
- return left.test(right);
- } else if ((right instanceof RegExp) && (typeof left === 'string')) {
- return right.test(left);
- }
- throw new RuntimeError('Operator "=~" requires one RegExp argument and one string argument. Arguments are ' + left + ' and ' + right + '.');
- };
- Node.prototype._evaluateRegExpNotMatch = function(feature) {
- var left = this._left.evaluate(feature);
- var right = this._right.evaluate(feature);
- if ((left instanceof RegExp) && (typeof right === 'string')) {
- return !(left.test(right));
- } else if ((right instanceof RegExp) && (typeof left === 'string')) {
- return !(right.test(left));
- }
- throw new RuntimeError('Operator "!~" requires one RegExp argument and one string argument. Arguments are ' + left + ' and ' + right + '.');
- };
- Node.prototype._evaluateRegExpExec = function(feature) {
- var left = this._left.evaluate(feature);
- var right = this._right.evaluate(feature);
- if (!((left instanceof RegExp) && (typeof right === 'string'))) {
- throw new RuntimeError('RegExp.exec requires the first argument to be a RegExp and the second argument to be a string. Arguments are ' + left + ' and ' + right + '.');
- }
- var exec = left.exec(right);
- if (!defined(exec)) {
- return null;
- }
- return exec[1];
- };
- Node.prototype._evaluateToString = function(feature) {
- var left = this._left.evaluate(feature);
- if ((left instanceof RegExp) || (left instanceof Cartesian2) || (left instanceof Cartesian3) || (left instanceof Cartesian4)) {
- return String(left);
- }
- throw new RuntimeError('Unexpected function call "' + this._value + '".');
- };
- function convertHSLToRGB(ast) {
- // Check if the color contains any nested expressions to see if the color can be converted here.
- // E.g. "hsl(0.9, 0.6, 0.7)" is able to convert directly to rgb, "hsl(0.9, 0.6, ${Height})" is not.
- var channels = ast._left;
- var length = channels.length;
- for (var i = 0; i < length; ++i) {
- if (channels[i]._type !== ExpressionNodeType.LITERAL_NUMBER) {
- return undefined;
- }
- }
- var h = channels[0]._value;
- var s = channels[1]._value;
- var l = channels[2]._value;
- var a = (length === 4) ? channels[3]._value : 1.0;
- return Color.fromHsl(h, s, l, a, scratchColor);
- }
- function convertRGBToColor(ast) {
- // Check if the color contains any nested expressions to see if the color can be converted here.
- // E.g. "rgb(255, 255, 255)" is able to convert directly to Color, "rgb(255, 255, ${Height})" is not.
- var channels = ast._left;
- var length = channels.length;
- for (var i = 0; i < length; ++i) {
- if (channels[i]._type !== ExpressionNodeType.LITERAL_NUMBER) {
- return undefined;
- }
- }
- var color = scratchColor;
- color.red = channels[0]._value / 255.0;
- color.green = channels[1]._value / 255.0;
- color.blue = channels[2]._value / 255.0;
- color.alpha = (length === 4) ? channels[3]._value : 1.0;
- return color;
- }
- function numberToString(number) {
- if (number % 1 === 0) {
- // Add a .0 to whole numbers
- return number.toFixed(1);
- }
- return number.toString();
- }
- function colorToVec3(color) {
- var r = numberToString(color.red);
- var g = numberToString(color.green);
- var b = numberToString(color.blue);
- return 'vec3(' + r + ', ' + g + ', ' + b + ')';
- }
- function colorToVec4(color) {
- var r = numberToString(color.red);
- var g = numberToString(color.green);
- var b = numberToString(color.blue);
- var a = numberToString(color.alpha);
- return 'vec4(' + r + ', ' + g + ', ' + b + ', ' + a + ')';
- }
- function getExpressionArray(array, attributePrefix, shaderState, parent) {
- var length = array.length;
- var expressions = new Array(length);
- for (var i = 0; i < length; ++i) {
- expressions[i] = array[i].getShaderExpression(attributePrefix, shaderState, parent);
- }
- return expressions;
- }
- Node.prototype.getShaderExpression = function(attributePrefix, shaderState, parent) {
- var color;
- var left;
- var right;
- var test;
- var type = this._type;
- var value = this._value;
- 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, this);
- } else {
- left = this._left.getShaderExpression(attributePrefix, shaderState, this);
- }
- }
- if (defined(this._right)) {
- right = this._right.getShaderExpression(attributePrefix, shaderState, this);
- }
- if (defined(this._test)) {
- test = this._test.getShaderExpression(attributePrefix, shaderState, this);
- }
- if (isArray(this._value)) {
- // For ARRAY type
- value = getExpressionArray(this._value, attributePrefix, shaderState, this);
- }
- switch (type) {
- case ExpressionNodeType.VARIABLE:
- return attributePrefix + value;
- case ExpressionNodeType.UNARY:
- // Supported types: +, -, !, Boolean, Number
- if (value === 'Boolean') {
- return 'bool(' + left + ')';
- } else if (value === 'Number') {
- return 'float(' + left + ')';
- } else if (value === 'round') {
- return 'floor(' + left + ' + 0.5)';
- } else if (defined(unaryFunctions[value])) {
- return value + '(' + left + ')';
- } else if ((value === 'isNaN') || (value === 'isFinite') || (value === 'String') || (value === 'isExactClass') || (value === 'isClass') || (value === 'getExactClassName')) {
- throw new RuntimeError('Error generating style shader: "' + value + '" is not supported.');
- } else if (defined(unaryFunctions[value])) {
- return value + '(' + left + ')';
- }
- return value + left;
- case ExpressionNodeType.BINARY:
- // Supported types: ||, &&, ===, !==, <, >, <=, >=, +, -, *, /, %
- if (value === '%') {
- return 'mod(' + left + ', ' + right + ')';
- } else if (value === '===') {
- return '(' + left + ' == ' + right + ')';
- } else if (value === '!==') {
- return '(' + left + ' != ' + right + ')';
- } else if (value === 'atan2') {
- return 'atan(' + left + ', ' + right + ')';
- } else if (defined(binaryFunctions[value])) {
- return value + '(' + left + ', ' + right + ')';
- }
- return '(' + left + ' ' + value + ' ' + right + ')';
- case ExpressionNodeType.TERNARY:
- if (defined(ternaryFunctions[value])) {
- return value + '(' + left + ', ' + right + ', ' + test + ')';
- }
- break;
- case ExpressionNodeType.CONDITIONAL:
- return '(' + test + ' ? ' + left + ' : ' + right + ')';
- case ExpressionNodeType.MEMBER:
- // 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.
- if (right === 'r' || right === 'x' || right === '0.0') {
- return left + '[0]';
- } else if (right === 'g' || right === 'y' || right === '1.0') {
- return left + '[1]';
- } else if (right === 'b' || right === 'z' || right === '2.0') {
- return left + '[2]';
- } else if (right === 'a' || right === 'w' || right === '3.0') {
- return left + '[3]';
- }
- return left + '[int(' + right + ')]';
- case ExpressionNodeType.FUNCTION_CALL:
- throw new RuntimeError('Error generating style shader: "' + value + '" is not supported.');
- case ExpressionNodeType.ARRAY:
- if (value.length === 4) {
- return 'vec4(' + value[0] + ', ' + value[1] + ', ' + value[2] + ', ' + value[3] + ')';
- } else if (value.length === 3) {
- return 'vec3(' + value[0] + ', ' + value[1] + ', ' + value[2] + ')';
- } else if (value.length === 2) {
- return 'vec2(' + value[0] + ', ' + value[1] + ')';
- }
- throw new RuntimeError('Error generating style shader: Invalid array length. Array length should be 2, 3, or 4.');
- case ExpressionNodeType.REGEX:
- throw new RuntimeError('Error generating style shader: Regular expressions are not supported.');
- case ExpressionNodeType.VARIABLE_IN_STRING:
- throw new RuntimeError('Error generating style shader: Converting a variable to a string is not supported.');
- case ExpressionNodeType.LITERAL_NULL:
- throw new RuntimeError('Error generating style shader: null is not supported.');
- case ExpressionNodeType.LITERAL_BOOLEAN:
- return value ? 'true' : 'false';
- case ExpressionNodeType.LITERAL_NUMBER:
- return numberToString(value);
- case ExpressionNodeType.LITERAL_STRING:
- if (defined(parent) && (parent._type === ExpressionNodeType.MEMBER)) {
- if (value === 'r' || value === 'g' || value === 'b' || value === 'a' ||
- 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);
- }
- throw new RuntimeError('Error generating style shader: String literals are not supported.');
- case ExpressionNodeType.LITERAL_COLOR:
- var args = left;
- if (value === 'color') {
- if (!defined(args)) {
- return 'vec4(1.0)';
- } else if (args.length > 1) {
- var rgb = args[0];
- var alpha = args[1];
- if (alpha !== '1.0') {
- shaderState.translucent = true;
- }
- return 'vec4(' + rgb + ', ' + alpha + ')';
- }
- return 'vec4(' + args[0] + ', 1.0)';
- } else if (value === 'rgb') {
- color = convertRGBToColor(this);
- if (defined(color)) {
- return colorToVec4(color);
- }
- return 'vec4(' + args[0] + ' / 255.0, ' + args[1] + ' / 255.0, ' + args[2] + ' / 255.0, 1.0)';
- } else if (value === 'rgba') {
- if (args[3] !== '1.0') {
- shaderState.translucent = true;
- }
- color = convertRGBToColor(this);
- if (defined(color)) {
- return colorToVec4(color);
- }
- return 'vec4(' + args[0] + ' / 255.0, ' + args[1] + ' / 255.0, ' + args[2] + ' / 255.0, ' + args[3] + ')';
- } else if (value === 'hsl') {
- color = convertHSLToRGB(this);
- if (defined(color)) {
- return colorToVec4(color);
- }
- return 'vec4(czm_HSLToRGB(vec3(' + args[0] + ', ' + args[1] + ', ' + args[2] + ')), 1.0)';
- } else if (value === 'hsla') {
- color = convertHSLToRGB(this);
- if (defined(color)) {
- if (color.alpha !== 1.0) {
- shaderState.translucent = true;
- }
- return colorToVec4(color);
- }
- if (args[3] !== '1.0') {
- shaderState.translucent = true;
- }
- return 'vec4(czm_HSLToRGB(vec3(' + args[0] + ', ' + args[1] + ', ' + args[2] + ')), ' + args[3] + ')';
- }
- break;
- case ExpressionNodeType.LITERAL_VECTOR:
- //>>includeStart('debug', pragmas.debug);
- if (!defined(left)) {
- throw new DeveloperError('left should always be defined for type ExpressionNodeType.LITERAL_VECTOR');
- }
- //>>includeEnd('debug');
- 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:
- throw new RuntimeError('Error generating style shader: Regular expressions are not supported.');
- case ExpressionNodeType.LITERAL_UNDEFINED:
- throw new RuntimeError('Error generating style shader: undefined is not supported.');
- case ExpressionNodeType.BUILTIN_VARIABLE:
- if (value === 'tiles3d_tileset_time') {
- return 'u_time';
- }
- }
- };
- export default Expression;
|