| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232 | 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 DeveloperError from "../Core/DeveloperError.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/CesiumGS/3d-tiles/tree/main/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 * const expression = new Cesium.Expression('(regExp("^Chest").test(${County})) && (${YearBuilt} >= 1970)'); * expression.evaluate(feature); // returns true or false depending on the feature's properties * * @example * const 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);  let ast;  try {    ast = jsep(expression);  } catch (e) {    throw new RuntimeError(e);  }  this._runtimeAst = createRuntimeAst(this, ast);}Object.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'sconst 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([]);    }    const 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/CesiumGS/3d-tiles/tree/main/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();  const 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();  const 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} functionSignature Signature of the generated function. * @param {Object} variableSubstitutionMap Maps variable names to shader variable names. * @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 (  functionSignature,  variableSubstitutionMap,  shaderState,  returnType) {  let shaderExpression = this.getShaderExpression(    variableSubstitutionMap,    shaderState  );  shaderExpression =    `${returnType} ${functionSignature}\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 {Object} variableSubstitutionMap Maps variable names to shader variable names. * @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 (  variableSubstitutionMap,  shaderState) {  return this._runtimeAst.getShaderExpression(    variableSubstitutionMap,    shaderState  );};/** * Gets the variables used by the expression. * * @returns {String[]} The variables used by the expression. * * @private */Expression.prototype.getVariables = function () {  let variables = [];  this._runtimeAst.getVariables(variables);  // Remove duplicates  variables = variables.filter(function (variable, index, variables) {    return variables.indexOf(variable) === index;  });  return variables;};const unaryOperators = ["!", "-", "+"];const binaryOperators = [  "+",  "-",  "*",  "/",  "%",  "===",  "!==",  ">",  ">=",  "<",  "<=",  "&&",  "||",  "!~",  "=~",];const variableRegex = /\${(.*?)}/g; // Matches ${variable_name}const backslashRegex = /\\/g;const backslashReplacement = "@#%";const replacementRegex = /@#%/g;const scratchColor = new Color();const 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,};const 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,};const 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 (const key in defines) {    if (defines.hasOwnProperty(key)) {      const definePlaceholder = new RegExp(`\\$\\{${key}\\}`, "g");      const 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) {  let exp = expression;  let result = "";  let i = exp.indexOf("${");  while (i >= 0) {    // Check if string is inside quotes    const openSingleQuote = exp.indexOf("'");    const openDoubleQuote = exp.indexOf('"');    let 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);      const 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) {  const 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) {  const args = ast.arguments;  const argsLength = args.length;  let call;  let val, left, right;  // Member function calls  if (ast.callee.type === "MemberExpression") {    call = ast.callee.property.name;    const 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])) {      const 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 (let 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]);    const 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) {  const args = ast.arguments;  // no arguments, return default regex  if (args.length === 0) {    return new Node(ExpressionNodeType.LITERAL_REGEX, new RegExp());  }  const pattern = createRuntimeAst(expression, args[0]);  let exp;  // optional flag argument supplied  if (args.length > 1) {    const 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)) {    const 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) {  const 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) {  const 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);  }  let val;  const 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) {  let node;  let op;  let left;  let 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;    const 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") {    const 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") {    const val = [];    for (let 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) {  const evaluate = unaryFunctions[call];  return function (feature) {    const left = this._left.evaluate(feature);    return evaluate(call, left);  };}function getEvaluateBinaryFunction(call) {  const evaluate = binaryFunctions[call];  return function (feature) {    const left = this._left.evaluate(feature);    const right = this._right.evaluate(feature);    return evaluate(call, left, right);  };}function getEvaluateTernaryFunction(call) {  const evaluate = ternaryFunctions[call];  return function (feature) {    const left = this._left.evaluate(feature);    const right = this._right.evaluate(feature);    const 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.getPropertyInherited(name);  }}Node.prototype._evaluateLiteral = function () {  return this._value;};Node.prototype._evaluateLiteralColor = function (feature) {  const color = scratchColor;  const 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)    const 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  const components = scratchStorage.getArray();  const call = this._value;  const args = this._left;  const argsLength = args.length;  for (let i = 0; i < argsLength; ++i) {    const 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}.`      );    }  }  const componentsLength = components.length;  const 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    const 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) {  let result = this._value;  let match = variableRegex.exec(result);  while (match !== null) {    const placeholder = match[0];    const variableName = match[1];    let 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 runtimeNode.prototype._evaluateMemberDot = function (feature) {  if (checkFeature(this._left)) {    return getFeatureProperty(feature, this._right.evaluate(feature));  }  const property = this._left.evaluate(feature);  if (!defined(property)) {    return undefined;  }  const 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));  }  const property = this._left.evaluate(feature);  if (!defined(property)) {    return undefined;  }  const 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) {  const array = [];  for (let 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 runtimeNode.prototype._evaluateNot = function (feature) {  const 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) {  const 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) {  const 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) {  const left = this._left.evaluate(feature);  const 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) {  const left = this._left.evaluate(feature);  const 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) {  const left = this._left.evaluate(feature);  const 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) {  const left = this._left.evaluate(feature);  const 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) {  const 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;  }  const 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) {  const 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;  }  const 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) {  const left = this._left.evaluate(feature);  const 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) {  const left = this._left.evaluate(feature);  const 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) {  const left = this._left.evaluate(feature);  const 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) {  const left = this._left.evaluate(feature);  const 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) {  const left = this._left.evaluate(feature);  const 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) {  const left = this._left.evaluate(feature);  const 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) {  const left = this._left.evaluate(feature);  const 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) {  const 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) {  const pattern = this._value.evaluate(feature);  let flags = "";  if (defined(this._left)) {    flags = this._left.evaluate(feature);  }  let exp;  try {    exp = new RegExp(pattern, flags);  } catch (e) {    throw new RuntimeError(e);  }  return exp;};Node.prototype._evaluateRegExpTest = function (feature) {  const left = this._left.evaluate(feature);  const 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) {  const left = this._left.evaluate(feature);  const 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) {  const left = this._left.evaluate(feature);  const 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) {  const left = this._left.evaluate(feature);  const 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}.`    );  }  const exec = left.exec(right);  if (!defined(exec)) {    return null;  }  return exec[1];};Node.prototype._evaluateToString = function (feature) {  const 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.  const channels = ast._left;  const length = channels.length;  for (let i = 0; i < length; ++i) {    if (channels[i]._type !== ExpressionNodeType.LITERAL_NUMBER) {      return undefined;    }  }  const h = channels[0]._value;  const s = channels[1]._value;  const l = channels[2]._value;  const 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.  const channels = ast._left;  const length = channels.length;  for (let i = 0; i < length; ++i) {    if (channels[i]._type !== ExpressionNodeType.LITERAL_NUMBER) {      return undefined;    }  }  const 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) {  const r = numberToString(color.red);  const g = numberToString(color.green);  const b = numberToString(color.blue);  return `vec3(${r}, ${g}, ${b})`;}function colorToVec4(color) {  const r = numberToString(color.red);  const g = numberToString(color.green);  const b = numberToString(color.blue);  const a = numberToString(color.alpha);  return `vec4(${r}, ${g}, ${b}, ${a})`;}function getExpressionArray(  array,  variableSubstitutionMap,  shaderState,  parent) {  const length = array.length;  const expressions = new Array(length);  for (let i = 0; i < length; ++i) {    expressions[i] = array[i].getShaderExpression(      variableSubstitutionMap,      shaderState,      parent    );  }  return expressions;}function getVariableName(variableName, variableSubstitutionMap) {  if (!defined(variableSubstitutionMap[variableName])) {    return Expression.NULL_SENTINEL;  }  return variableSubstitutionMap[variableName];}/** * @private */Expression.NULL_SENTINEL = "czm_infinity"; // null just needs to be some sentinel value that will cause "[expression] === null" to be false in nearly all cases. GLSL doesn't have a NaN constant so use czm_infinity.Node.prototype.getShaderExpression = function (  variableSubstitutionMap,  shaderState,  parent) {  let color;  let left;  let right;  let test;  const type = this._type;  let value = this._value;  if (defined(this._left)) {    if (Array.isArray(this._left)) {      // Left can be an array if the type is LITERAL_COLOR or LITERAL_VECTOR      left = getExpressionArray(        this._left,        variableSubstitutionMap,        shaderState,        this      );    } else {      left = this._left.getShaderExpression(        variableSubstitutionMap,        shaderState,        this      );    }  }  if (defined(this._right)) {    right = this._right.getShaderExpression(      variableSubstitutionMap,      shaderState,      this    );  }  if (defined(this._test)) {    test = this._test.getShaderExpression(      variableSubstitutionMap,      shaderState,      this    );  }  if (Array.isArray(this._value)) {    // For ARRAY type    value = getExpressionArray(      this._value,      variableSubstitutionMap,      shaderState,      this    );  }  let args;  let length;  let vectorExpression;  switch (type) {    case ExpressionNodeType.VARIABLE:      if (checkFeature(this)) {        return undefined;      }      return getVariableName(value, variableSubstitutionMap);    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") {        // In GLSL 2.0 use isnan instead        return `(${left} != ${left})`;      } else if (value === "isFinite") {        // In GLSL 2.0 use isinf instead. GLSL doesn't have an infinity constant so use czm_infinity which is an arbitrarily big enough number.        return `(abs(${left}) < czm_infinity)`;      } else if (        value === "String" ||        value === "isExactClass" ||        value === "isClass" ||        value === "getExactClassName"      ) {        throw new RuntimeError(          `Error generating style shader: "${value}" is not supported.`        );      }      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:      if (checkFeature(this._left)) {        return getVariableName(right, variableSubstitutionMap);      }      // 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:      return Expression.NULL_SENTINEL;    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" ||          checkFeature(parent._left)        ) {          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:      args = left;      if (value === "color") {        if (!defined(args)) {          return "vec4(1.0)";        } else if (args.length > 1) {          const rgb = args[0];          const 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');      length = left.length;      vectorExpression = `${value}(`;      for (let 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:      return Expression.NULL_SENTINEL;    case ExpressionNodeType.BUILTIN_VARIABLE:      if (value === "tiles3d_tileset_time") {        return "u_time";      }  }};Node.prototype.getVariables = function (variables, parent) {  let array;  let length;  let i;  const type = this._type;  const value = this._value;  if (defined(this._left)) {    if (Array.isArray(this._left)) {      // Left can be an array if the type is LITERAL_COLOR or LITERAL_VECTOR      array = this._left;      length = array.length;      for (i = 0; i < length; ++i) {        array[i].getVariables(variables, this);      }    } else {      this._left.getVariables(variables, this);    }  }  if (defined(this._right)) {    this._right.getVariables(variables, this);  }  if (defined(this._test)) {    this._test.getVariables(variables, this);  }  if (Array.isArray(this._value)) {    // For ARRAY type    array = this._value;    length = array.length;    for (i = 0; i < length; ++i) {      array[i].getVariables(variables, this);    }  }  let match;  switch (type) {    case ExpressionNodeType.VARIABLE:      if (!checkFeature(this)) {        variables.push(value);      }      break;    case ExpressionNodeType.VARIABLE_IN_STRING:      match = variableRegex.exec(value);      while (match !== null) {        variables.push(match[1]);        match = variableRegex.exec(value);      }      break;    case ExpressionNodeType.LITERAL_STRING:      if (        defined(parent) &&        parent._type === ExpressionNodeType.MEMBER &&        checkFeature(parent._left)      ) {        variables.push(value);      }      break;  }};export default Expression;
 |