Expression.js 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280
  1. import Cartesian2 from "../Core/Cartesian2.js";
  2. import Cartesian3 from "../Core/Cartesian3.js";
  3. import Cartesian4 from "../Core/Cartesian4.js";
  4. import Check from "../Core/Check.js";
  5. import Color from "../Core/Color.js";
  6. import defined from "../Core/defined.js";
  7. import DeveloperError from "../Core/DeveloperError.js";
  8. import CesiumMath from "../Core/Math.js";
  9. import RuntimeError from "../Core/RuntimeError.js";
  10. import jsep from "../ThirdParty/jsep.js";
  11. import ExpressionNodeType from "./ExpressionNodeType.js";
  12. /**
  13. * An expression for a style applied to a {@link Cesium3DTileset}.
  14. * <p>
  15. * Evaluates an expression defined using the
  16. * {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification/Styling|3D Tiles Styling language}.
  17. * </p>
  18. * <p>
  19. * Implements the {@link StyleExpression} interface.
  20. * </p>
  21. *
  22. * @alias Expression
  23. * @constructor
  24. *
  25. * @param {String} [expression] The expression defined using the 3D Tiles Styling language.
  26. * @param {Object} [defines] Defines in the style.
  27. *
  28. * @example
  29. * var expression = new Cesium.Expression('(regExp("^Chest").test(${County})) && (${YearBuilt} >= 1970)');
  30. * expression.evaluate(feature); // returns true or false depending on the feature's properties
  31. *
  32. * @example
  33. * var expression = new Cesium.Expression('(${Temperature} > 90) ? color("red") : color("white")');
  34. * expression.evaluateColor(feature, result); // returns a Cesium.Color object
  35. */
  36. function Expression(expression, defines) {
  37. //>>includeStart('debug', pragmas.debug);
  38. Check.typeOf.string("expression", expression);
  39. //>>includeEnd('debug');
  40. this._expression = expression;
  41. expression = replaceDefines(expression, defines);
  42. expression = replaceVariables(removeBackslashes(expression));
  43. // customize jsep operators
  44. jsep.addBinaryOp("=~", 0);
  45. jsep.addBinaryOp("!~", 0);
  46. var ast;
  47. try {
  48. ast = jsep(expression);
  49. } catch (e) {
  50. throw new RuntimeError(e);
  51. }
  52. this._runtimeAst = createRuntimeAst(this, ast);
  53. }
  54. Object.defineProperties(Expression.prototype, {
  55. /**
  56. * Gets the expression defined in the 3D Tiles Styling language.
  57. *
  58. * @memberof Expression.prototype
  59. *
  60. * @type {String}
  61. * @readonly
  62. *
  63. * @default undefined
  64. */
  65. expression: {
  66. get: function () {
  67. return this._expression;
  68. },
  69. },
  70. });
  71. // Scratch storage manager while evaluating deep expressions.
  72. // For example, an expression like dot(vec4(${red}), vec4(${green}) * vec4(${blue}) requires 3 scratch Cartesian4's
  73. var scratchStorage = {
  74. arrayIndex: 0,
  75. arrayArray: [[]],
  76. cartesian2Index: 0,
  77. cartesian3Index: 0,
  78. cartesian4Index: 0,
  79. cartesian2Array: [new Cartesian2()],
  80. cartesian3Array: [new Cartesian3()],
  81. cartesian4Array: [new Cartesian4()],
  82. reset: function () {
  83. this.arrayIndex = 0;
  84. this.cartesian2Index = 0;
  85. this.cartesian3Index = 0;
  86. this.cartesian4Index = 0;
  87. },
  88. getArray: function () {
  89. if (this.arrayIndex >= this.arrayArray.length) {
  90. this.arrayArray.push([]);
  91. }
  92. var array = this.arrayArray[this.arrayIndex++];
  93. array.length = 0;
  94. return array;
  95. },
  96. getCartesian2: function () {
  97. if (this.cartesian2Index >= this.cartesian2Array.length) {
  98. this.cartesian2Array.push(new Cartesian2());
  99. }
  100. return this.cartesian2Array[this.cartesian2Index++];
  101. },
  102. getCartesian3: function () {
  103. if (this.cartesian3Index >= this.cartesian3Array.length) {
  104. this.cartesian3Array.push(new Cartesian3());
  105. }
  106. return this.cartesian3Array[this.cartesian3Index++];
  107. },
  108. getCartesian4: function () {
  109. if (this.cartesian4Index >= this.cartesian4Array.length) {
  110. this.cartesian4Array.push(new Cartesian4());
  111. }
  112. return this.cartesian4Array[this.cartesian4Index++];
  113. },
  114. };
  115. /**
  116. * Evaluates the result of an expression, optionally using the provided feature's properties. If the result of
  117. * the expression in the
  118. * {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification/Styling|3D Tiles Styling language}
  119. * is of type <code>Boolean</code>, <code>Number</code>, or <code>String</code>, the corresponding JavaScript
  120. * primitive type will be returned. If the result is a <code>RegExp</code>, a Javascript <code>RegExp</code>
  121. * object will be returned. If the result is a <code>Cartesian2</code>, <code>Cartesian3</code>, or <code>Cartesian4</code>,
  122. * a {@link Cartesian2}, {@link Cartesian3}, or {@link Cartesian4} object will be returned. If the <code>result</code> argument is
  123. * a {@link Color}, the {@link Cartesian4} value is converted to a {@link Color} and then returned.
  124. *
  125. * @param {Cesium3DTileFeature} feature The feature whose properties may be used as variables in the expression.
  126. * @param {Object} [result] The object onto which to store the result.
  127. * @returns {Boolean|Number|String|RegExp|Cartesian2|Cartesian3|Cartesian4|Color} The result of evaluating the expression.
  128. */
  129. Expression.prototype.evaluate = function (feature, result) {
  130. scratchStorage.reset();
  131. var value = this._runtimeAst.evaluate(feature);
  132. if (result instanceof Color && value instanceof Cartesian4) {
  133. return Color.fromCartesian4(value, result);
  134. }
  135. if (
  136. value instanceof Cartesian2 ||
  137. value instanceof Cartesian3 ||
  138. value instanceof Cartesian4
  139. ) {
  140. return value.clone(result);
  141. }
  142. return value;
  143. };
  144. /**
  145. * Evaluates the result of a Color expression, optionally using the provided feature's properties.
  146. * <p>
  147. * This is equivalent to {@link Expression#evaluate} but always returns a {@link Color} object.
  148. * </p>
  149. *
  150. * @param {Cesium3DTileFeature} feature The feature whose properties may be used as variables in the expression.
  151. * @param {Color} [result] The object in which to store the result
  152. * @returns {Color} The modified result parameter or a new Color instance if one was not provided.
  153. */
  154. Expression.prototype.evaluateColor = function (feature, result) {
  155. scratchStorage.reset();
  156. var color = this._runtimeAst.evaluate(feature);
  157. return Color.fromCartesian4(color, result);
  158. };
  159. /**
  160. * Gets the shader function for this expression.
  161. * Returns undefined if the shader function can't be generated from this expression.
  162. *
  163. * @param {String} functionName Name to give to the generated function.
  164. * @param {String} propertyNameMap Maps property variable names to shader attribute names.
  165. * @param {Object} shaderState Stores information about the generated shader function, including whether it is translucent.
  166. * @param {String} returnType The return type of the generated function.
  167. *
  168. * @returns {String} The shader function.
  169. *
  170. * @private
  171. */
  172. Expression.prototype.getShaderFunction = function (
  173. functionName,
  174. propertyNameMap,
  175. shaderState,
  176. returnType
  177. ) {
  178. var shaderExpression = this.getShaderExpression(propertyNameMap, shaderState);
  179. shaderExpression =
  180. returnType +
  181. " " +
  182. functionName +
  183. "() \n" +
  184. "{ \n" +
  185. " return " +
  186. shaderExpression +
  187. "; \n" +
  188. "} \n";
  189. return shaderExpression;
  190. };
  191. /**
  192. * Gets the shader expression for this expression.
  193. * Returns undefined if the shader expression can't be generated from this expression.
  194. *
  195. * @param {String} propertyNameMap Maps property variable names to shader attribute names.
  196. * @param {Object} shaderState Stores information about the generated shader function, including whether it is translucent.
  197. *
  198. * @returns {String} The shader expression.
  199. *
  200. * @private
  201. */
  202. Expression.prototype.getShaderExpression = function (
  203. propertyNameMap,
  204. shaderState
  205. ) {
  206. return this._runtimeAst.getShaderExpression(propertyNameMap, shaderState);
  207. };
  208. var unaryOperators = ["!", "-", "+"];
  209. var binaryOperators = [
  210. "+",
  211. "-",
  212. "*",
  213. "/",
  214. "%",
  215. "===",
  216. "!==",
  217. ">",
  218. ">=",
  219. "<",
  220. "<=",
  221. "&&",
  222. "||",
  223. "!~",
  224. "=~",
  225. ];
  226. var variableRegex = /\${(.*?)}/g; // Matches ${variable_name}
  227. var backslashRegex = /\\/g;
  228. var backslashReplacement = "@#%";
  229. var replacementRegex = /@#%/g;
  230. var scratchColor = new Color();
  231. var unaryFunctions = {
  232. abs: getEvaluateUnaryComponentwise(Math.abs),
  233. sqrt: getEvaluateUnaryComponentwise(Math.sqrt),
  234. cos: getEvaluateUnaryComponentwise(Math.cos),
  235. sin: getEvaluateUnaryComponentwise(Math.sin),
  236. tan: getEvaluateUnaryComponentwise(Math.tan),
  237. acos: getEvaluateUnaryComponentwise(Math.acos),
  238. asin: getEvaluateUnaryComponentwise(Math.asin),
  239. atan: getEvaluateUnaryComponentwise(Math.atan),
  240. radians: getEvaluateUnaryComponentwise(CesiumMath.toRadians),
  241. degrees: getEvaluateUnaryComponentwise(CesiumMath.toDegrees),
  242. sign: getEvaluateUnaryComponentwise(CesiumMath.sign),
  243. floor: getEvaluateUnaryComponentwise(Math.floor),
  244. ceil: getEvaluateUnaryComponentwise(Math.ceil),
  245. round: getEvaluateUnaryComponentwise(Math.round),
  246. exp: getEvaluateUnaryComponentwise(Math.exp),
  247. exp2: getEvaluateUnaryComponentwise(exp2),
  248. log: getEvaluateUnaryComponentwise(Math.log),
  249. log2: getEvaluateUnaryComponentwise(log2),
  250. fract: getEvaluateUnaryComponentwise(fract),
  251. length: length,
  252. normalize: normalize,
  253. };
  254. var binaryFunctions = {
  255. atan2: getEvaluateBinaryComponentwise(Math.atan2, false),
  256. pow: getEvaluateBinaryComponentwise(Math.pow, false),
  257. min: getEvaluateBinaryComponentwise(Math.min, true),
  258. max: getEvaluateBinaryComponentwise(Math.max, true),
  259. distance: distance,
  260. dot: dot,
  261. cross: cross,
  262. };
  263. var ternaryFunctions = {
  264. clamp: getEvaluateTernaryComponentwise(CesiumMath.clamp, true),
  265. mix: getEvaluateTernaryComponentwise(CesiumMath.lerp, true),
  266. };
  267. function fract(number) {
  268. return number - Math.floor(number);
  269. }
  270. function exp2(exponent) {
  271. return Math.pow(2.0, exponent);
  272. }
  273. function log2(number) {
  274. return CesiumMath.log2(number);
  275. }
  276. function getEvaluateUnaryComponentwise(operation) {
  277. return function (call, left) {
  278. if (typeof left === "number") {
  279. return operation(left);
  280. } else if (left instanceof Cartesian2) {
  281. return Cartesian2.fromElements(
  282. operation(left.x),
  283. operation(left.y),
  284. scratchStorage.getCartesian2()
  285. );
  286. } else if (left instanceof Cartesian3) {
  287. return Cartesian3.fromElements(
  288. operation(left.x),
  289. operation(left.y),
  290. operation(left.z),
  291. scratchStorage.getCartesian3()
  292. );
  293. } else if (left instanceof Cartesian4) {
  294. return Cartesian4.fromElements(
  295. operation(left.x),
  296. operation(left.y),
  297. operation(left.z),
  298. operation(left.w),
  299. scratchStorage.getCartesian4()
  300. );
  301. }
  302. throw new RuntimeError(
  303. 'Function "' +
  304. call +
  305. '" requires a vector or number argument. Argument is ' +
  306. left +
  307. "."
  308. );
  309. };
  310. }
  311. function getEvaluateBinaryComponentwise(operation, allowScalar) {
  312. return function (call, left, right) {
  313. if (allowScalar && typeof right === "number") {
  314. if (typeof left === "number") {
  315. return operation(left, right);
  316. } else if (left instanceof Cartesian2) {
  317. return Cartesian2.fromElements(
  318. operation(left.x, right),
  319. operation(left.y, right),
  320. scratchStorage.getCartesian2()
  321. );
  322. } else if (left instanceof Cartesian3) {
  323. return Cartesian3.fromElements(
  324. operation(left.x, right),
  325. operation(left.y, right),
  326. operation(left.z, right),
  327. scratchStorage.getCartesian3()
  328. );
  329. } else if (left instanceof Cartesian4) {
  330. return Cartesian4.fromElements(
  331. operation(left.x, right),
  332. operation(left.y, right),
  333. operation(left.z, right),
  334. operation(left.w, right),
  335. scratchStorage.getCartesian4()
  336. );
  337. }
  338. }
  339. if (typeof left === "number" && typeof right === "number") {
  340. return operation(left, right);
  341. } else if (left instanceof Cartesian2 && right instanceof Cartesian2) {
  342. return Cartesian2.fromElements(
  343. operation(left.x, right.x),
  344. operation(left.y, right.y),
  345. scratchStorage.getCartesian2()
  346. );
  347. } else if (left instanceof Cartesian3 && right instanceof Cartesian3) {
  348. return Cartesian3.fromElements(
  349. operation(left.x, right.x),
  350. operation(left.y, right.y),
  351. operation(left.z, right.z),
  352. scratchStorage.getCartesian3()
  353. );
  354. } else if (left instanceof Cartesian4 && right instanceof Cartesian4) {
  355. return Cartesian4.fromElements(
  356. operation(left.x, right.x),
  357. operation(left.y, right.y),
  358. operation(left.z, right.z),
  359. operation(left.w, right.w),
  360. scratchStorage.getCartesian4()
  361. );
  362. }
  363. throw new RuntimeError(
  364. 'Function "' +
  365. call +
  366. '" requires vector or number arguments of matching types. Arguments are ' +
  367. left +
  368. " and " +
  369. right +
  370. "."
  371. );
  372. };
  373. }
  374. function getEvaluateTernaryComponentwise(operation, allowScalar) {
  375. return function (call, left, right, test) {
  376. if (allowScalar && typeof test === "number") {
  377. if (typeof left === "number" && typeof right === "number") {
  378. return operation(left, right, test);
  379. } else if (left instanceof Cartesian2 && right instanceof Cartesian2) {
  380. return Cartesian2.fromElements(
  381. operation(left.x, right.x, test),
  382. operation(left.y, right.y, test),
  383. scratchStorage.getCartesian2()
  384. );
  385. } else if (left instanceof Cartesian3 && right instanceof Cartesian3) {
  386. return Cartesian3.fromElements(
  387. operation(left.x, right.x, test),
  388. operation(left.y, right.y, test),
  389. operation(left.z, right.z, test),
  390. scratchStorage.getCartesian3()
  391. );
  392. } else if (left instanceof Cartesian4 && right instanceof Cartesian4) {
  393. return Cartesian4.fromElements(
  394. operation(left.x, right.x, test),
  395. operation(left.y, right.y, test),
  396. operation(left.z, right.z, test),
  397. operation(left.w, right.w, test),
  398. scratchStorage.getCartesian4()
  399. );
  400. }
  401. }
  402. if (
  403. typeof left === "number" &&
  404. typeof right === "number" &&
  405. typeof test === "number"
  406. ) {
  407. return operation(left, right, test);
  408. } else if (
  409. left instanceof Cartesian2 &&
  410. right instanceof Cartesian2 &&
  411. test instanceof Cartesian2
  412. ) {
  413. return Cartesian2.fromElements(
  414. operation(left.x, right.x, test.x),
  415. operation(left.y, right.y, test.y),
  416. scratchStorage.getCartesian2()
  417. );
  418. } else if (
  419. left instanceof Cartesian3 &&
  420. right instanceof Cartesian3 &&
  421. test instanceof Cartesian3
  422. ) {
  423. return Cartesian3.fromElements(
  424. operation(left.x, right.x, test.x),
  425. operation(left.y, right.y, test.y),
  426. operation(left.z, right.z, test.z),
  427. scratchStorage.getCartesian3()
  428. );
  429. } else if (
  430. left instanceof Cartesian4 &&
  431. right instanceof Cartesian4 &&
  432. test instanceof Cartesian4
  433. ) {
  434. return Cartesian4.fromElements(
  435. operation(left.x, right.x, test.x),
  436. operation(left.y, right.y, test.y),
  437. operation(left.z, right.z, test.z),
  438. operation(left.w, right.w, test.w),
  439. scratchStorage.getCartesian4()
  440. );
  441. }
  442. throw new RuntimeError(
  443. 'Function "' +
  444. call +
  445. '" requires vector or number arguments of matching types. Arguments are ' +
  446. left +
  447. ", " +
  448. right +
  449. ", and " +
  450. test +
  451. "."
  452. );
  453. };
  454. }
  455. function length(call, left) {
  456. if (typeof left === "number") {
  457. return Math.abs(left);
  458. } else if (left instanceof Cartesian2) {
  459. return Cartesian2.magnitude(left);
  460. } else if (left instanceof Cartesian3) {
  461. return Cartesian3.magnitude(left);
  462. } else if (left instanceof Cartesian4) {
  463. return Cartesian4.magnitude(left);
  464. }
  465. throw new RuntimeError(
  466. 'Function "' +
  467. call +
  468. '" requires a vector or number argument. Argument is ' +
  469. left +
  470. "."
  471. );
  472. }
  473. function normalize(call, left) {
  474. if (typeof left === "number") {
  475. return 1.0;
  476. } else if (left instanceof Cartesian2) {
  477. return Cartesian2.normalize(left, scratchStorage.getCartesian2());
  478. } else if (left instanceof Cartesian3) {
  479. return Cartesian3.normalize(left, scratchStorage.getCartesian3());
  480. } else if (left instanceof Cartesian4) {
  481. return Cartesian4.normalize(left, scratchStorage.getCartesian4());
  482. }
  483. throw new RuntimeError(
  484. 'Function "' +
  485. call +
  486. '" requires a vector or number argument. Argument is ' +
  487. left +
  488. "."
  489. );
  490. }
  491. function distance(call, left, right) {
  492. if (typeof left === "number" && typeof right === "number") {
  493. return Math.abs(left - right);
  494. } else if (left instanceof Cartesian2 && right instanceof Cartesian2) {
  495. return Cartesian2.distance(left, right);
  496. } else if (left instanceof Cartesian3 && right instanceof Cartesian3) {
  497. return Cartesian3.distance(left, right);
  498. } else if (left instanceof Cartesian4 && right instanceof Cartesian4) {
  499. return Cartesian4.distance(left, right);
  500. }
  501. throw new RuntimeError(
  502. 'Function "' +
  503. call +
  504. '" requires vector or number arguments of matching types. Arguments are ' +
  505. left +
  506. " and " +
  507. right +
  508. "."
  509. );
  510. }
  511. function dot(call, left, right) {
  512. if (typeof left === "number" && typeof right === "number") {
  513. return left * right;
  514. } else if (left instanceof Cartesian2 && right instanceof Cartesian2) {
  515. return Cartesian2.dot(left, right);
  516. } else if (left instanceof Cartesian3 && right instanceof Cartesian3) {
  517. return Cartesian3.dot(left, right);
  518. } else if (left instanceof Cartesian4 && right instanceof Cartesian4) {
  519. return Cartesian4.dot(left, right);
  520. }
  521. throw new RuntimeError(
  522. 'Function "' +
  523. call +
  524. '" requires vector or number arguments of matching types. Arguments are ' +
  525. left +
  526. " and " +
  527. right +
  528. "."
  529. );
  530. }
  531. function cross(call, left, right) {
  532. if (left instanceof Cartesian3 && right instanceof Cartesian3) {
  533. return Cartesian3.cross(left, right, scratchStorage.getCartesian3());
  534. }
  535. throw new RuntimeError(
  536. 'Function "' +
  537. call +
  538. '" requires vec3 arguments. Arguments are ' +
  539. left +
  540. " and " +
  541. right +
  542. "."
  543. );
  544. }
  545. function Node(type, value, left, right, test) {
  546. this._type = type;
  547. this._value = value;
  548. this._left = left;
  549. this._right = right;
  550. this._test = test;
  551. this.evaluate = undefined;
  552. setEvaluateFunction(this);
  553. }
  554. function replaceDefines(expression, defines) {
  555. if (!defined(defines)) {
  556. return expression;
  557. }
  558. for (var key in defines) {
  559. if (defines.hasOwnProperty(key)) {
  560. var definePlaceholder = new RegExp("\\$\\{" + key + "\\}", "g");
  561. var defineReplace = "(" + defines[key] + ")";
  562. if (defined(defineReplace)) {
  563. expression = expression.replace(definePlaceholder, defineReplace);
  564. }
  565. }
  566. }
  567. return expression;
  568. }
  569. function removeBackslashes(expression) {
  570. return expression.replace(backslashRegex, backslashReplacement);
  571. }
  572. function replaceBackslashes(expression) {
  573. return expression.replace(replacementRegex, "\\");
  574. }
  575. function replaceVariables(expression) {
  576. var exp = expression;
  577. var result = "";
  578. var i = exp.indexOf("${");
  579. while (i >= 0) {
  580. // Check if string is inside quotes
  581. var openSingleQuote = exp.indexOf("'");
  582. var openDoubleQuote = exp.indexOf('"');
  583. var closeQuote;
  584. if (openSingleQuote >= 0 && openSingleQuote < i) {
  585. closeQuote = exp.indexOf("'", openSingleQuote + 1);
  586. result += exp.substr(0, closeQuote + 1);
  587. exp = exp.substr(closeQuote + 1);
  588. i = exp.indexOf("${");
  589. } else if (openDoubleQuote >= 0 && openDoubleQuote < i) {
  590. closeQuote = exp.indexOf('"', openDoubleQuote + 1);
  591. result += exp.substr(0, closeQuote + 1);
  592. exp = exp.substr(closeQuote + 1);
  593. i = exp.indexOf("${");
  594. } else {
  595. result += exp.substr(0, i);
  596. var j = exp.indexOf("}");
  597. if (j < 0) {
  598. throw new RuntimeError("Unmatched {.");
  599. }
  600. result += "czm_" + exp.substr(i + 2, j - (i + 2));
  601. exp = exp.substr(j + 1);
  602. i = exp.indexOf("${");
  603. }
  604. }
  605. result += exp;
  606. return result;
  607. }
  608. function parseLiteral(ast) {
  609. var type = typeof ast.value;
  610. if (ast.value === null) {
  611. return new Node(ExpressionNodeType.LITERAL_NULL, null);
  612. } else if (type === "boolean") {
  613. return new Node(ExpressionNodeType.LITERAL_BOOLEAN, ast.value);
  614. } else if (type === "number") {
  615. return new Node(ExpressionNodeType.LITERAL_NUMBER, ast.value);
  616. } else if (type === "string") {
  617. if (ast.value.indexOf("${") >= 0) {
  618. return new Node(ExpressionNodeType.VARIABLE_IN_STRING, ast.value);
  619. }
  620. return new Node(
  621. ExpressionNodeType.LITERAL_STRING,
  622. replaceBackslashes(ast.value)
  623. );
  624. }
  625. }
  626. function parseCall(expression, ast) {
  627. var args = ast.arguments;
  628. var argsLength = args.length;
  629. var call;
  630. var val, left, right;
  631. // Member function calls
  632. if (ast.callee.type === "MemberExpression") {
  633. call = ast.callee.property.name;
  634. var object = ast.callee.object;
  635. if (call === "test" || call === "exec") {
  636. // Make sure this is called on a valid type
  637. if (object.callee.name !== "regExp") {
  638. throw new RuntimeError(call + " is not a function.");
  639. }
  640. if (argsLength === 0) {
  641. if (call === "test") {
  642. return new Node(ExpressionNodeType.LITERAL_BOOLEAN, false);
  643. }
  644. return new Node(ExpressionNodeType.LITERAL_NULL, null);
  645. }
  646. left = createRuntimeAst(expression, object);
  647. right = createRuntimeAst(expression, args[0]);
  648. return new Node(ExpressionNodeType.FUNCTION_CALL, call, left, right);
  649. } else if (call === "toString") {
  650. val = createRuntimeAst(expression, object);
  651. return new Node(ExpressionNodeType.FUNCTION_CALL, call, val);
  652. }
  653. throw new RuntimeError('Unexpected function call "' + call + '".');
  654. }
  655. // Non-member function calls
  656. call = ast.callee.name;
  657. if (call === "color") {
  658. if (argsLength === 0) {
  659. return new Node(ExpressionNodeType.LITERAL_COLOR, call);
  660. }
  661. val = createRuntimeAst(expression, args[0]);
  662. if (defined(args[1])) {
  663. var alpha = createRuntimeAst(expression, args[1]);
  664. return new Node(ExpressionNodeType.LITERAL_COLOR, call, [val, alpha]);
  665. }
  666. return new Node(ExpressionNodeType.LITERAL_COLOR, call, [val]);
  667. } else if (call === "rgb" || call === "hsl") {
  668. if (argsLength < 3) {
  669. throw new RuntimeError(call + " requires three arguments.");
  670. }
  671. val = [
  672. createRuntimeAst(expression, args[0]),
  673. createRuntimeAst(expression, args[1]),
  674. createRuntimeAst(expression, args[2]),
  675. ];
  676. return new Node(ExpressionNodeType.LITERAL_COLOR, call, val);
  677. } else if (call === "rgba" || call === "hsla") {
  678. if (argsLength < 4) {
  679. throw new RuntimeError(call + " requires four arguments.");
  680. }
  681. val = [
  682. createRuntimeAst(expression, args[0]),
  683. createRuntimeAst(expression, args[1]),
  684. createRuntimeAst(expression, args[2]),
  685. createRuntimeAst(expression, args[3]),
  686. ];
  687. return new Node(ExpressionNodeType.LITERAL_COLOR, call, val);
  688. } else if (call === "vec2" || call === "vec3" || call === "vec4") {
  689. // Check for invalid constructors at evaluation time
  690. val = new Array(argsLength);
  691. for (var i = 0; i < argsLength; ++i) {
  692. val[i] = createRuntimeAst(expression, args[i]);
  693. }
  694. return new Node(ExpressionNodeType.LITERAL_VECTOR, call, val);
  695. } else if (call === "isNaN" || call === "isFinite") {
  696. if (argsLength === 0) {
  697. if (call === "isNaN") {
  698. return new Node(ExpressionNodeType.LITERAL_BOOLEAN, true);
  699. }
  700. return new Node(ExpressionNodeType.LITERAL_BOOLEAN, false);
  701. }
  702. val = createRuntimeAst(expression, args[0]);
  703. return new Node(ExpressionNodeType.UNARY, call, val);
  704. } else if (call === "isExactClass" || call === "isClass") {
  705. if (argsLength < 1 || argsLength > 1) {
  706. throw new RuntimeError(call + " requires exactly one argument.");
  707. }
  708. val = createRuntimeAst(expression, args[0]);
  709. return new Node(ExpressionNodeType.UNARY, call, val);
  710. } else if (call === "getExactClassName") {
  711. if (argsLength > 0) {
  712. throw new RuntimeError(call + " does not take any argument.");
  713. }
  714. return new Node(ExpressionNodeType.UNARY, call);
  715. } else if (defined(unaryFunctions[call])) {
  716. if (argsLength !== 1) {
  717. throw new RuntimeError(call + " requires exactly one argument.");
  718. }
  719. val = createRuntimeAst(expression, args[0]);
  720. return new Node(ExpressionNodeType.UNARY, call, val);
  721. } else if (defined(binaryFunctions[call])) {
  722. if (argsLength !== 2) {
  723. throw new RuntimeError(call + " requires exactly two arguments.");
  724. }
  725. left = createRuntimeAst(expression, args[0]);
  726. right = createRuntimeAst(expression, args[1]);
  727. return new Node(ExpressionNodeType.BINARY, call, left, right);
  728. } else if (defined(ternaryFunctions[call])) {
  729. if (argsLength !== 3) {
  730. throw new RuntimeError(call + " requires exactly three arguments.");
  731. }
  732. left = createRuntimeAst(expression, args[0]);
  733. right = createRuntimeAst(expression, args[1]);
  734. var test = createRuntimeAst(expression, args[2]);
  735. return new Node(ExpressionNodeType.TERNARY, call, left, right, test);
  736. } else if (call === "Boolean") {
  737. if (argsLength === 0) {
  738. return new Node(ExpressionNodeType.LITERAL_BOOLEAN, false);
  739. }
  740. val = createRuntimeAst(expression, args[0]);
  741. return new Node(ExpressionNodeType.UNARY, call, val);
  742. } else if (call === "Number") {
  743. if (argsLength === 0) {
  744. return new Node(ExpressionNodeType.LITERAL_NUMBER, 0);
  745. }
  746. val = createRuntimeAst(expression, args[0]);
  747. return new Node(ExpressionNodeType.UNARY, call, val);
  748. } else if (call === "String") {
  749. if (argsLength === 0) {
  750. return new Node(ExpressionNodeType.LITERAL_STRING, "");
  751. }
  752. val = createRuntimeAst(expression, args[0]);
  753. return new Node(ExpressionNodeType.UNARY, call, val);
  754. } else if (call === "regExp") {
  755. return parseRegex(expression, ast);
  756. }
  757. throw new RuntimeError('Unexpected function call "' + call + '".');
  758. }
  759. function parseRegex(expression, ast) {
  760. var args = ast.arguments;
  761. // no arguments, return default regex
  762. if (args.length === 0) {
  763. return new Node(ExpressionNodeType.LITERAL_REGEX, new RegExp());
  764. }
  765. var pattern = createRuntimeAst(expression, args[0]);
  766. var exp;
  767. // optional flag argument supplied
  768. if (args.length > 1) {
  769. var flags = createRuntimeAst(expression, args[1]);
  770. if (isLiteralType(pattern) && isLiteralType(flags)) {
  771. try {
  772. exp = new RegExp(
  773. replaceBackslashes(String(pattern._value)),
  774. flags._value
  775. );
  776. } catch (e) {
  777. throw new RuntimeError(e);
  778. }
  779. return new Node(ExpressionNodeType.LITERAL_REGEX, exp);
  780. }
  781. return new Node(ExpressionNodeType.REGEX, pattern, flags);
  782. }
  783. // only pattern argument supplied
  784. if (isLiteralType(pattern)) {
  785. try {
  786. exp = new RegExp(replaceBackslashes(String(pattern._value)));
  787. } catch (e) {
  788. throw new RuntimeError(e);
  789. }
  790. return new Node(ExpressionNodeType.LITERAL_REGEX, exp);
  791. }
  792. return new Node(ExpressionNodeType.REGEX, pattern);
  793. }
  794. function parseKeywordsAndVariables(ast) {
  795. if (isVariable(ast.name)) {
  796. var name = getPropertyName(ast.name);
  797. if (name.substr(0, 8) === "tiles3d_") {
  798. return new Node(ExpressionNodeType.BUILTIN_VARIABLE, name);
  799. }
  800. return new Node(ExpressionNodeType.VARIABLE, name);
  801. } else if (ast.name === "NaN") {
  802. return new Node(ExpressionNodeType.LITERAL_NUMBER, NaN);
  803. } else if (ast.name === "Infinity") {
  804. return new Node(ExpressionNodeType.LITERAL_NUMBER, Infinity);
  805. } else if (ast.name === "undefined") {
  806. return new Node(ExpressionNodeType.LITERAL_UNDEFINED, undefined);
  807. }
  808. throw new RuntimeError(ast.name + " is not defined.");
  809. }
  810. function parseMathConstant(ast) {
  811. var name = ast.property.name;
  812. if (name === "PI") {
  813. return new Node(ExpressionNodeType.LITERAL_NUMBER, Math.PI);
  814. } else if (name === "E") {
  815. return new Node(ExpressionNodeType.LITERAL_NUMBER, Math.E);
  816. }
  817. }
  818. function parseNumberConstant(ast) {
  819. var name = ast.property.name;
  820. if (name === "POSITIVE_INFINITY") {
  821. return new Node(
  822. ExpressionNodeType.LITERAL_NUMBER,
  823. Number.POSITIVE_INFINITY
  824. );
  825. }
  826. }
  827. function parseMemberExpression(expression, ast) {
  828. if (ast.object.name === "Math") {
  829. return parseMathConstant(ast);
  830. } else if (ast.object.name === "Number") {
  831. return parseNumberConstant(ast);
  832. }
  833. var val;
  834. var obj = createRuntimeAst(expression, ast.object);
  835. if (ast.computed) {
  836. val = createRuntimeAst(expression, ast.property);
  837. return new Node(ExpressionNodeType.MEMBER, "brackets", obj, val);
  838. }
  839. val = new Node(ExpressionNodeType.LITERAL_STRING, ast.property.name);
  840. return new Node(ExpressionNodeType.MEMBER, "dot", obj, val);
  841. }
  842. function isLiteralType(node) {
  843. return node._type >= ExpressionNodeType.LITERAL_NULL;
  844. }
  845. function isVariable(name) {
  846. return name.substr(0, 4) === "czm_";
  847. }
  848. function getPropertyName(variable) {
  849. return variable.substr(4);
  850. }
  851. function createRuntimeAst(expression, ast) {
  852. var node;
  853. var op;
  854. var left;
  855. var right;
  856. if (ast.type === "Literal") {
  857. node = parseLiteral(ast);
  858. } else if (ast.type === "CallExpression") {
  859. node = parseCall(expression, ast);
  860. } else if (ast.type === "Identifier") {
  861. node = parseKeywordsAndVariables(ast);
  862. } else if (ast.type === "UnaryExpression") {
  863. op = ast.operator;
  864. var child = createRuntimeAst(expression, ast.argument);
  865. if (unaryOperators.indexOf(op) > -1) {
  866. node = new Node(ExpressionNodeType.UNARY, op, child);
  867. } else {
  868. throw new RuntimeError('Unexpected operator "' + op + '".');
  869. }
  870. } else if (ast.type === "BinaryExpression") {
  871. op = ast.operator;
  872. left = createRuntimeAst(expression, ast.left);
  873. right = createRuntimeAst(expression, ast.right);
  874. if (binaryOperators.indexOf(op) > -1) {
  875. node = new Node(ExpressionNodeType.BINARY, op, left, right);
  876. } else {
  877. throw new RuntimeError('Unexpected operator "' + op + '".');
  878. }
  879. } else if (ast.type === "LogicalExpression") {
  880. op = ast.operator;
  881. left = createRuntimeAst(expression, ast.left);
  882. right = createRuntimeAst(expression, ast.right);
  883. if (binaryOperators.indexOf(op) > -1) {
  884. node = new Node(ExpressionNodeType.BINARY, op, left, right);
  885. }
  886. } else if (ast.type === "ConditionalExpression") {
  887. var test = createRuntimeAst(expression, ast.test);
  888. left = createRuntimeAst(expression, ast.consequent);
  889. right = createRuntimeAst(expression, ast.alternate);
  890. node = new Node(ExpressionNodeType.CONDITIONAL, "?", left, right, test);
  891. } else if (ast.type === "MemberExpression") {
  892. node = parseMemberExpression(expression, ast);
  893. } else if (ast.type === "ArrayExpression") {
  894. var val = [];
  895. for (var i = 0; i < ast.elements.length; i++) {
  896. val[i] = createRuntimeAst(expression, ast.elements[i]);
  897. }
  898. node = new Node(ExpressionNodeType.ARRAY, val);
  899. } else if (ast.type === "Compound") {
  900. // empty expression or multiple expressions
  901. throw new RuntimeError("Provide exactly one expression.");
  902. } else {
  903. throw new RuntimeError("Cannot parse expression.");
  904. }
  905. return node;
  906. }
  907. function setEvaluateFunction(node) {
  908. if (node._type === ExpressionNodeType.CONDITIONAL) {
  909. node.evaluate = node._evaluateConditional;
  910. } else if (node._type === ExpressionNodeType.FUNCTION_CALL) {
  911. if (node._value === "test") {
  912. node.evaluate = node._evaluateRegExpTest;
  913. } else if (node._value === "exec") {
  914. node.evaluate = node._evaluateRegExpExec;
  915. } else if (node._value === "toString") {
  916. node.evaluate = node._evaluateToString;
  917. }
  918. } else if (node._type === ExpressionNodeType.UNARY) {
  919. if (node._value === "!") {
  920. node.evaluate = node._evaluateNot;
  921. } else if (node._value === "-") {
  922. node.evaluate = node._evaluateNegative;
  923. } else if (node._value === "+") {
  924. node.evaluate = node._evaluatePositive;
  925. } else if (node._value === "isNaN") {
  926. node.evaluate = node._evaluateNaN;
  927. } else if (node._value === "isFinite") {
  928. node.evaluate = node._evaluateIsFinite;
  929. } else if (node._value === "isExactClass") {
  930. node.evaluate = node._evaluateIsExactClass;
  931. } else if (node._value === "isClass") {
  932. node.evaluate = node._evaluateIsClass;
  933. } else if (node._value === "getExactClassName") {
  934. node.evaluate = node._evaluateGetExactClassName;
  935. } else if (node._value === "Boolean") {
  936. node.evaluate = node._evaluateBooleanConversion;
  937. } else if (node._value === "Number") {
  938. node.evaluate = node._evaluateNumberConversion;
  939. } else if (node._value === "String") {
  940. node.evaluate = node._evaluateStringConversion;
  941. } else if (defined(unaryFunctions[node._value])) {
  942. node.evaluate = getEvaluateUnaryFunction(node._value);
  943. }
  944. } else if (node._type === ExpressionNodeType.BINARY) {
  945. if (node._value === "+") {
  946. node.evaluate = node._evaluatePlus;
  947. } else if (node._value === "-") {
  948. node.evaluate = node._evaluateMinus;
  949. } else if (node._value === "*") {
  950. node.evaluate = node._evaluateTimes;
  951. } else if (node._value === "/") {
  952. node.evaluate = node._evaluateDivide;
  953. } else if (node._value === "%") {
  954. node.evaluate = node._evaluateMod;
  955. } else if (node._value === "===") {
  956. node.evaluate = node._evaluateEqualsStrict;
  957. } else if (node._value === "!==") {
  958. node.evaluate = node._evaluateNotEqualsStrict;
  959. } else if (node._value === "<") {
  960. node.evaluate = node._evaluateLessThan;
  961. } else if (node._value === "<=") {
  962. node.evaluate = node._evaluateLessThanOrEquals;
  963. } else if (node._value === ">") {
  964. node.evaluate = node._evaluateGreaterThan;
  965. } else if (node._value === ">=") {
  966. node.evaluate = node._evaluateGreaterThanOrEquals;
  967. } else if (node._value === "&&") {
  968. node.evaluate = node._evaluateAnd;
  969. } else if (node._value === "||") {
  970. node.evaluate = node._evaluateOr;
  971. } else if (node._value === "=~") {
  972. node.evaluate = node._evaluateRegExpMatch;
  973. } else if (node._value === "!~") {
  974. node.evaluate = node._evaluateRegExpNotMatch;
  975. } else if (defined(binaryFunctions[node._value])) {
  976. node.evaluate = getEvaluateBinaryFunction(node._value);
  977. }
  978. } else if (node._type === ExpressionNodeType.TERNARY) {
  979. node.evaluate = getEvaluateTernaryFunction(node._value);
  980. } else if (node._type === ExpressionNodeType.MEMBER) {
  981. if (node._value === "brackets") {
  982. node.evaluate = node._evaluateMemberBrackets;
  983. } else {
  984. node.evaluate = node._evaluateMemberDot;
  985. }
  986. } else if (node._type === ExpressionNodeType.ARRAY) {
  987. node.evaluate = node._evaluateArray;
  988. } else if (node._type === ExpressionNodeType.VARIABLE) {
  989. node.evaluate = node._evaluateVariable;
  990. } else if (node._type === ExpressionNodeType.VARIABLE_IN_STRING) {
  991. node.evaluate = node._evaluateVariableString;
  992. } else if (node._type === ExpressionNodeType.LITERAL_COLOR) {
  993. node.evaluate = node._evaluateLiteralColor;
  994. } else if (node._type === ExpressionNodeType.LITERAL_VECTOR) {
  995. node.evaluate = node._evaluateLiteralVector;
  996. } else if (node._type === ExpressionNodeType.LITERAL_STRING) {
  997. node.evaluate = node._evaluateLiteralString;
  998. } else if (node._type === ExpressionNodeType.REGEX) {
  999. node.evaluate = node._evaluateRegExp;
  1000. } else if (node._type === ExpressionNodeType.BUILTIN_VARIABLE) {
  1001. if (node._value === "tiles3d_tileset_time") {
  1002. node.evaluate = evaluateTilesetTime;
  1003. }
  1004. } else {
  1005. node.evaluate = node._evaluateLiteral;
  1006. }
  1007. }
  1008. function evaluateTilesetTime(feature) {
  1009. if (!defined(feature)) {
  1010. return 0.0;
  1011. }
  1012. return feature.content.tileset.timeSinceLoad;
  1013. }
  1014. function getEvaluateUnaryFunction(call) {
  1015. var evaluate = unaryFunctions[call];
  1016. return function (feature) {
  1017. var left = this._left.evaluate(feature);
  1018. return evaluate(call, left);
  1019. };
  1020. }
  1021. function getEvaluateBinaryFunction(call) {
  1022. var evaluate = binaryFunctions[call];
  1023. return function (feature) {
  1024. var left = this._left.evaluate(feature);
  1025. var right = this._right.evaluate(feature);
  1026. return evaluate(call, left, right);
  1027. };
  1028. }
  1029. function getEvaluateTernaryFunction(call) {
  1030. var evaluate = ternaryFunctions[call];
  1031. return function (feature) {
  1032. var left = this._left.evaluate(feature);
  1033. var right = this._right.evaluate(feature);
  1034. var test = this._test.evaluate(feature);
  1035. return evaluate(call, left, right, test);
  1036. };
  1037. }
  1038. function getFeatureProperty(feature, name) {
  1039. // Returns undefined if the feature is not defined or the property name is not defined for that feature
  1040. if (defined(feature)) {
  1041. return feature.getProperty(name);
  1042. }
  1043. }
  1044. Node.prototype._evaluateLiteral = function () {
  1045. return this._value;
  1046. };
  1047. Node.prototype._evaluateLiteralColor = function (feature) {
  1048. var color = scratchColor;
  1049. var args = this._left;
  1050. if (this._value === "color") {
  1051. if (!defined(args)) {
  1052. Color.fromBytes(255, 255, 255, 255, color);
  1053. } else if (args.length > 1) {
  1054. Color.fromCssColorString(args[0].evaluate(feature), color);
  1055. color.alpha = args[1].evaluate(feature);
  1056. } else {
  1057. Color.fromCssColorString(args[0].evaluate(feature), color);
  1058. }
  1059. } else if (this._value === "rgb") {
  1060. Color.fromBytes(
  1061. args[0].evaluate(feature),
  1062. args[1].evaluate(feature),
  1063. args[2].evaluate(feature),
  1064. 255,
  1065. color
  1066. );
  1067. } else if (this._value === "rgba") {
  1068. // convert between css alpha (0 to 1) and cesium alpha (0 to 255)
  1069. var a = args[3].evaluate(feature) * 255;
  1070. Color.fromBytes(
  1071. args[0].evaluate(feature),
  1072. args[1].evaluate(feature),
  1073. args[2].evaluate(feature),
  1074. a,
  1075. color
  1076. );
  1077. } else if (this._value === "hsl") {
  1078. Color.fromHsl(
  1079. args[0].evaluate(feature),
  1080. args[1].evaluate(feature),
  1081. args[2].evaluate(feature),
  1082. 1.0,
  1083. color
  1084. );
  1085. } else if (this._value === "hsla") {
  1086. Color.fromHsl(
  1087. args[0].evaluate(feature),
  1088. args[1].evaluate(feature),
  1089. args[2].evaluate(feature),
  1090. args[3].evaluate(feature),
  1091. color
  1092. );
  1093. }
  1094. return Cartesian4.fromColor(color, scratchStorage.getCartesian4());
  1095. };
  1096. Node.prototype._evaluateLiteralVector = function (feature) {
  1097. // Gather the components that make up the vector, which includes components from interior vectors.
  1098. // For example vec3(1, 2, 3) or vec3(vec2(1, 2), 3) are both valid.
  1099. //
  1100. // If the number of components does not equal the vector's size, then a RuntimeError is thrown - with two exceptions:
  1101. // 1. A vector may be constructed from a larger vector and drop the extra components.
  1102. // 2. A vector may be constructed from a single component - vec3(1) will become vec3(1, 1, 1).
  1103. //
  1104. // Examples of invalid constructors include:
  1105. // vec4(1, 2) // not enough components
  1106. // vec3(vec2(1, 2)) // not enough components
  1107. // vec3(1, 2, 3, 4) // too many components
  1108. // vec2(vec4(1), 1) // too many components
  1109. var components = scratchStorage.getArray();
  1110. var call = this._value;
  1111. var args = this._left;
  1112. var argsLength = args.length;
  1113. for (var i = 0; i < argsLength; ++i) {
  1114. var value = args[i].evaluate(feature);
  1115. if (typeof value === "number") {
  1116. components.push(value);
  1117. } else if (value instanceof Cartesian2) {
  1118. components.push(value.x, value.y);
  1119. } else if (value instanceof Cartesian3) {
  1120. components.push(value.x, value.y, value.z);
  1121. } else if (value instanceof Cartesian4) {
  1122. components.push(value.x, value.y, value.z, value.w);
  1123. } else {
  1124. throw new RuntimeError(
  1125. call +
  1126. " argument must be a vector or number. Argument is " +
  1127. value +
  1128. "."
  1129. );
  1130. }
  1131. }
  1132. var componentsLength = components.length;
  1133. var vectorLength = parseInt(call.charAt(3));
  1134. if (componentsLength === 0) {
  1135. throw new RuntimeError(
  1136. "Invalid " + call + " constructor. No valid arguments."
  1137. );
  1138. } else if (componentsLength < vectorLength && componentsLength > 1) {
  1139. throw new RuntimeError(
  1140. "Invalid " + call + " constructor. Not enough arguments."
  1141. );
  1142. } else if (componentsLength > vectorLength && argsLength > 1) {
  1143. throw new RuntimeError(
  1144. "Invalid " + call + " constructor. Too many arguments."
  1145. );
  1146. }
  1147. if (componentsLength === 1) {
  1148. // Add the same component 3 more times
  1149. var component = components[0];
  1150. components.push(component, component, component);
  1151. }
  1152. if (call === "vec2") {
  1153. return Cartesian2.fromArray(components, 0, scratchStorage.getCartesian2());
  1154. } else if (call === "vec3") {
  1155. return Cartesian3.fromArray(components, 0, scratchStorage.getCartesian3());
  1156. } else if (call === "vec4") {
  1157. return Cartesian4.fromArray(components, 0, scratchStorage.getCartesian4());
  1158. }
  1159. };
  1160. Node.prototype._evaluateLiteralString = function () {
  1161. return this._value;
  1162. };
  1163. Node.prototype._evaluateVariableString = function (feature) {
  1164. var result = this._value;
  1165. var match = variableRegex.exec(result);
  1166. while (match !== null) {
  1167. var placeholder = match[0];
  1168. var variableName = match[1];
  1169. var property = getFeatureProperty(feature, variableName);
  1170. if (!defined(property)) {
  1171. property = "";
  1172. }
  1173. result = result.replace(placeholder, property);
  1174. match = variableRegex.exec(result);
  1175. }
  1176. return result;
  1177. };
  1178. Node.prototype._evaluateVariable = function (feature) {
  1179. // evaluates to undefined if the property name is not defined for that feature
  1180. return getFeatureProperty(feature, this._value);
  1181. };
  1182. function checkFeature(ast) {
  1183. return ast._value === "feature";
  1184. }
  1185. // PERFORMANCE_IDEA: Determine if parent property needs to be computed before runtime
  1186. Node.prototype._evaluateMemberDot = function (feature) {
  1187. if (checkFeature(this._left)) {
  1188. return getFeatureProperty(feature, this._right.evaluate(feature));
  1189. }
  1190. var property = this._left.evaluate(feature);
  1191. if (!defined(property)) {
  1192. return undefined;
  1193. }
  1194. var member = this._right.evaluate(feature);
  1195. if (
  1196. property instanceof Cartesian2 ||
  1197. property instanceof Cartesian3 ||
  1198. property instanceof Cartesian4
  1199. ) {
  1200. // Vector components may be accessed with .r, .g, .b, .a and implicitly with .x, .y, .z, .w
  1201. if (member === "r") {
  1202. return property.x;
  1203. } else if (member === "g") {
  1204. return property.y;
  1205. } else if (member === "b") {
  1206. return property.z;
  1207. } else if (member === "a") {
  1208. return property.w;
  1209. }
  1210. }
  1211. return property[member];
  1212. };
  1213. Node.prototype._evaluateMemberBrackets = function (feature) {
  1214. if (checkFeature(this._left)) {
  1215. return getFeatureProperty(feature, this._right.evaluate(feature));
  1216. }
  1217. var property = this._left.evaluate(feature);
  1218. if (!defined(property)) {
  1219. return undefined;
  1220. }
  1221. var member = this._right.evaluate(feature);
  1222. if (
  1223. property instanceof Cartesian2 ||
  1224. property instanceof Cartesian3 ||
  1225. property instanceof Cartesian4
  1226. ) {
  1227. // Vector components may be accessed with [0][1][2][3], ['r']['g']['b']['a'] and implicitly with ['x']['y']['z']['w']
  1228. // For Cartesian2 and Cartesian3 out-of-range components will just return undefined
  1229. if (member === 0 || member === "r") {
  1230. return property.x;
  1231. } else if (member === 1 || member === "g") {
  1232. return property.y;
  1233. } else if (member === 2 || member === "b") {
  1234. return property.z;
  1235. } else if (member === 3 || member === "a") {
  1236. return property.w;
  1237. }
  1238. }
  1239. return property[member];
  1240. };
  1241. Node.prototype._evaluateArray = function (feature) {
  1242. var array = [];
  1243. for (var i = 0; i < this._value.length; i++) {
  1244. array[i] = this._value[i].evaluate(feature);
  1245. }
  1246. return array;
  1247. };
  1248. // PERFORMANCE_IDEA: Have "fast path" functions that deal only with specific types
  1249. // that we can assign if we know the types before runtime
  1250. Node.prototype._evaluateNot = function (feature) {
  1251. var left = this._left.evaluate(feature);
  1252. if (typeof left !== "boolean") {
  1253. throw new RuntimeError(
  1254. 'Operator "!" requires a boolean argument. Argument is ' + left + "."
  1255. );
  1256. }
  1257. return !left;
  1258. };
  1259. Node.prototype._evaluateNegative = function (feature) {
  1260. var left = this._left.evaluate(feature);
  1261. if (left instanceof Cartesian2) {
  1262. return Cartesian2.negate(left, scratchStorage.getCartesian2());
  1263. } else if (left instanceof Cartesian3) {
  1264. return Cartesian3.negate(left, scratchStorage.getCartesian3());
  1265. } else if (left instanceof Cartesian4) {
  1266. return Cartesian4.negate(left, scratchStorage.getCartesian4());
  1267. } else if (typeof left === "number") {
  1268. return -left;
  1269. }
  1270. throw new RuntimeError(
  1271. 'Operator "-" requires a vector or number argument. Argument is ' +
  1272. left +
  1273. "."
  1274. );
  1275. };
  1276. Node.prototype._evaluatePositive = function (feature) {
  1277. var left = this._left.evaluate(feature);
  1278. if (
  1279. !(
  1280. left instanceof Cartesian2 ||
  1281. left instanceof Cartesian3 ||
  1282. left instanceof Cartesian4 ||
  1283. typeof left === "number"
  1284. )
  1285. ) {
  1286. throw new RuntimeError(
  1287. 'Operator "+" requires a vector or number argument. Argument is ' +
  1288. left +
  1289. "."
  1290. );
  1291. }
  1292. return left;
  1293. };
  1294. Node.prototype._evaluateLessThan = function (feature) {
  1295. var left = this._left.evaluate(feature);
  1296. var right = this._right.evaluate(feature);
  1297. if (typeof left !== "number" || typeof right !== "number") {
  1298. throw new RuntimeError(
  1299. 'Operator "<" requires number arguments. Arguments are ' +
  1300. left +
  1301. " and " +
  1302. right +
  1303. "."
  1304. );
  1305. }
  1306. return left < right;
  1307. };
  1308. Node.prototype._evaluateLessThanOrEquals = function (feature) {
  1309. var left = this._left.evaluate(feature);
  1310. var right = this._right.evaluate(feature);
  1311. if (typeof left !== "number" || typeof right !== "number") {
  1312. throw new RuntimeError(
  1313. 'Operator "<=" requires number arguments. Arguments are ' +
  1314. left +
  1315. " and " +
  1316. right +
  1317. "."
  1318. );
  1319. }
  1320. return left <= right;
  1321. };
  1322. Node.prototype._evaluateGreaterThan = function (feature) {
  1323. var left = this._left.evaluate(feature);
  1324. var right = this._right.evaluate(feature);
  1325. if (typeof left !== "number" || typeof right !== "number") {
  1326. throw new RuntimeError(
  1327. 'Operator ">" requires number arguments. Arguments are ' +
  1328. left +
  1329. " and " +
  1330. right +
  1331. "."
  1332. );
  1333. }
  1334. return left > right;
  1335. };
  1336. Node.prototype._evaluateGreaterThanOrEquals = function (feature) {
  1337. var left = this._left.evaluate(feature);
  1338. var right = this._right.evaluate(feature);
  1339. if (typeof left !== "number" || typeof right !== "number") {
  1340. throw new RuntimeError(
  1341. 'Operator ">=" requires number arguments. Arguments are ' +
  1342. left +
  1343. " and " +
  1344. right +
  1345. "."
  1346. );
  1347. }
  1348. return left >= right;
  1349. };
  1350. Node.prototype._evaluateOr = function (feature) {
  1351. var left = this._left.evaluate(feature);
  1352. if (typeof left !== "boolean") {
  1353. throw new RuntimeError(
  1354. 'Operator "||" requires boolean arguments. First argument is ' +
  1355. left +
  1356. "."
  1357. );
  1358. }
  1359. // short circuit the expression
  1360. if (left) {
  1361. return true;
  1362. }
  1363. var right = this._right.evaluate(feature);
  1364. if (typeof right !== "boolean") {
  1365. throw new RuntimeError(
  1366. 'Operator "||" requires boolean arguments. Second argument is ' +
  1367. right +
  1368. "."
  1369. );
  1370. }
  1371. return left || right;
  1372. };
  1373. Node.prototype._evaluateAnd = function (feature) {
  1374. var left = this._left.evaluate(feature);
  1375. if (typeof left !== "boolean") {
  1376. throw new RuntimeError(
  1377. 'Operator "&&" requires boolean arguments. First argument is ' +
  1378. left +
  1379. "."
  1380. );
  1381. }
  1382. // short circuit the expression
  1383. if (!left) {
  1384. return false;
  1385. }
  1386. var right = this._right.evaluate(feature);
  1387. if (typeof right !== "boolean") {
  1388. throw new RuntimeError(
  1389. 'Operator "&&" requires boolean arguments. Second argument is ' +
  1390. right +
  1391. "."
  1392. );
  1393. }
  1394. return left && right;
  1395. };
  1396. Node.prototype._evaluatePlus = function (feature) {
  1397. var left = this._left.evaluate(feature);
  1398. var right = this._right.evaluate(feature);
  1399. if (right instanceof Cartesian2 && left instanceof Cartesian2) {
  1400. return Cartesian2.add(left, right, scratchStorage.getCartesian2());
  1401. } else if (right instanceof Cartesian3 && left instanceof Cartesian3) {
  1402. return Cartesian3.add(left, right, scratchStorage.getCartesian3());
  1403. } else if (right instanceof Cartesian4 && left instanceof Cartesian4) {
  1404. return Cartesian4.add(left, right, scratchStorage.getCartesian4());
  1405. } else if (typeof left === "string" || typeof right === "string") {
  1406. // If only one argument is a string the other argument calls its toString function.
  1407. return left + right;
  1408. } else if (typeof left === "number" && typeof right === "number") {
  1409. return left + right;
  1410. }
  1411. throw new RuntimeError(
  1412. 'Operator "+" requires vector or number arguments of matching types, or at least one string argument. Arguments are ' +
  1413. left +
  1414. " and " +
  1415. right +
  1416. "."
  1417. );
  1418. };
  1419. Node.prototype._evaluateMinus = function (feature) {
  1420. var left = this._left.evaluate(feature);
  1421. var right = this._right.evaluate(feature);
  1422. if (right instanceof Cartesian2 && left instanceof Cartesian2) {
  1423. return Cartesian2.subtract(left, right, scratchStorage.getCartesian2());
  1424. } else if (right instanceof Cartesian3 && left instanceof Cartesian3) {
  1425. return Cartesian3.subtract(left, right, scratchStorage.getCartesian3());
  1426. } else if (right instanceof Cartesian4 && left instanceof Cartesian4) {
  1427. return Cartesian4.subtract(left, right, scratchStorage.getCartesian4());
  1428. } else if (typeof left === "number" && typeof right === "number") {
  1429. return left - right;
  1430. }
  1431. throw new RuntimeError(
  1432. 'Operator "-" requires vector or number arguments of matching types. Arguments are ' +
  1433. left +
  1434. " and " +
  1435. right +
  1436. "."
  1437. );
  1438. };
  1439. Node.prototype._evaluateTimes = function (feature) {
  1440. var left = this._left.evaluate(feature);
  1441. var right = this._right.evaluate(feature);
  1442. if (right instanceof Cartesian2 && left instanceof Cartesian2) {
  1443. return Cartesian2.multiplyComponents(
  1444. left,
  1445. right,
  1446. scratchStorage.getCartesian2()
  1447. );
  1448. } else if (right instanceof Cartesian2 && typeof left === "number") {
  1449. return Cartesian2.multiplyByScalar(
  1450. right,
  1451. left,
  1452. scratchStorage.getCartesian2()
  1453. );
  1454. } else if (left instanceof Cartesian2 && typeof right === "number") {
  1455. return Cartesian2.multiplyByScalar(
  1456. left,
  1457. right,
  1458. scratchStorage.getCartesian2()
  1459. );
  1460. } else if (right instanceof Cartesian3 && left instanceof Cartesian3) {
  1461. return Cartesian3.multiplyComponents(
  1462. left,
  1463. right,
  1464. scratchStorage.getCartesian3()
  1465. );
  1466. } else if (right instanceof Cartesian3 && typeof left === "number") {
  1467. return Cartesian3.multiplyByScalar(
  1468. right,
  1469. left,
  1470. scratchStorage.getCartesian3()
  1471. );
  1472. } else if (left instanceof Cartesian3 && typeof right === "number") {
  1473. return Cartesian3.multiplyByScalar(
  1474. left,
  1475. right,
  1476. scratchStorage.getCartesian3()
  1477. );
  1478. } else if (right instanceof Cartesian4 && left instanceof Cartesian4) {
  1479. return Cartesian4.multiplyComponents(
  1480. left,
  1481. right,
  1482. scratchStorage.getCartesian4()
  1483. );
  1484. } else if (right instanceof Cartesian4 && typeof left === "number") {
  1485. return Cartesian4.multiplyByScalar(
  1486. right,
  1487. left,
  1488. scratchStorage.getCartesian4()
  1489. );
  1490. } else if (left instanceof Cartesian4 && typeof right === "number") {
  1491. return Cartesian4.multiplyByScalar(
  1492. left,
  1493. right,
  1494. scratchStorage.getCartesian4()
  1495. );
  1496. } else if (typeof left === "number" && typeof right === "number") {
  1497. return left * right;
  1498. }
  1499. throw new RuntimeError(
  1500. 'Operator "*" requires vector or number arguments. If both arguments are vectors they must be matching types. Arguments are ' +
  1501. left +
  1502. " and " +
  1503. right +
  1504. "."
  1505. );
  1506. };
  1507. Node.prototype._evaluateDivide = function (feature) {
  1508. var left = this._left.evaluate(feature);
  1509. var right = this._right.evaluate(feature);
  1510. if (right instanceof Cartesian2 && left instanceof Cartesian2) {
  1511. return Cartesian2.divideComponents(
  1512. left,
  1513. right,
  1514. scratchStorage.getCartesian2()
  1515. );
  1516. } else if (left instanceof Cartesian2 && typeof right === "number") {
  1517. return Cartesian2.divideByScalar(
  1518. left,
  1519. right,
  1520. scratchStorage.getCartesian2()
  1521. );
  1522. } else if (right instanceof Cartesian3 && left instanceof Cartesian3) {
  1523. return Cartesian3.divideComponents(
  1524. left,
  1525. right,
  1526. scratchStorage.getCartesian3()
  1527. );
  1528. } else if (left instanceof Cartesian3 && typeof right === "number") {
  1529. return Cartesian3.divideByScalar(
  1530. left,
  1531. right,
  1532. scratchStorage.getCartesian3()
  1533. );
  1534. } else if (right instanceof Cartesian4 && left instanceof Cartesian4) {
  1535. return Cartesian4.divideComponents(
  1536. left,
  1537. right,
  1538. scratchStorage.getCartesian4()
  1539. );
  1540. } else if (left instanceof Cartesian4 && typeof right === "number") {
  1541. return Cartesian4.divideByScalar(
  1542. left,
  1543. right,
  1544. scratchStorage.getCartesian4()
  1545. );
  1546. } else if (typeof left === "number" && typeof right === "number") {
  1547. return left / right;
  1548. }
  1549. throw new RuntimeError(
  1550. 'Operator "/" requires vector or number arguments of matching types, or a number as the second argument. Arguments are ' +
  1551. left +
  1552. " and " +
  1553. right +
  1554. "."
  1555. );
  1556. };
  1557. Node.prototype._evaluateMod = function (feature) {
  1558. var left = this._left.evaluate(feature);
  1559. var right = this._right.evaluate(feature);
  1560. if (right instanceof Cartesian2 && left instanceof Cartesian2) {
  1561. return Cartesian2.fromElements(
  1562. left.x % right.x,
  1563. left.y % right.y,
  1564. scratchStorage.getCartesian2()
  1565. );
  1566. } else if (right instanceof Cartesian3 && left instanceof Cartesian3) {
  1567. return Cartesian3.fromElements(
  1568. left.x % right.x,
  1569. left.y % right.y,
  1570. left.z % right.z,
  1571. scratchStorage.getCartesian3()
  1572. );
  1573. } else if (right instanceof Cartesian4 && left instanceof Cartesian4) {
  1574. return Cartesian4.fromElements(
  1575. left.x % right.x,
  1576. left.y % right.y,
  1577. left.z % right.z,
  1578. left.w % right.w,
  1579. scratchStorage.getCartesian4()
  1580. );
  1581. } else if (typeof left === "number" && typeof right === "number") {
  1582. return left % right;
  1583. }
  1584. throw new RuntimeError(
  1585. 'Operator "%" requires vector or number arguments of matching types. Arguments are ' +
  1586. left +
  1587. " and " +
  1588. right +
  1589. "."
  1590. );
  1591. };
  1592. Node.prototype._evaluateEqualsStrict = function (feature) {
  1593. var left = this._left.evaluate(feature);
  1594. var right = this._right.evaluate(feature);
  1595. if (
  1596. (right instanceof Cartesian2 && left instanceof Cartesian2) ||
  1597. (right instanceof Cartesian3 && left instanceof Cartesian3) ||
  1598. (right instanceof Cartesian4 && left instanceof Cartesian4)
  1599. ) {
  1600. return left.equals(right);
  1601. }
  1602. return left === right;
  1603. };
  1604. Node.prototype._evaluateNotEqualsStrict = function (feature) {
  1605. var left = this._left.evaluate(feature);
  1606. var right = this._right.evaluate(feature);
  1607. if (
  1608. (right instanceof Cartesian2 && left instanceof Cartesian2) ||
  1609. (right instanceof Cartesian3 && left instanceof Cartesian3) ||
  1610. (right instanceof Cartesian4 && left instanceof Cartesian4)
  1611. ) {
  1612. return !left.equals(right);
  1613. }
  1614. return left !== right;
  1615. };
  1616. Node.prototype._evaluateConditional = function (feature) {
  1617. var test = this._test.evaluate(feature);
  1618. if (typeof test !== "boolean") {
  1619. throw new RuntimeError(
  1620. "Conditional argument of conditional expression must be a boolean. Argument is " +
  1621. test +
  1622. "."
  1623. );
  1624. }
  1625. if (test) {
  1626. return this._left.evaluate(feature);
  1627. }
  1628. return this._right.evaluate(feature);
  1629. };
  1630. Node.prototype._evaluateNaN = function (feature) {
  1631. return isNaN(this._left.evaluate(feature));
  1632. };
  1633. Node.prototype._evaluateIsFinite = function (feature) {
  1634. return isFinite(this._left.evaluate(feature));
  1635. };
  1636. Node.prototype._evaluateIsExactClass = function (feature) {
  1637. if (defined(feature)) {
  1638. return feature.isExactClass(this._left.evaluate(feature));
  1639. }
  1640. return false;
  1641. };
  1642. Node.prototype._evaluateIsClass = function (feature) {
  1643. if (defined(feature)) {
  1644. return feature.isClass(this._left.evaluate(feature));
  1645. }
  1646. return false;
  1647. };
  1648. Node.prototype._evaluateGetExactClassName = function (feature) {
  1649. if (defined(feature)) {
  1650. return feature.getExactClassName();
  1651. }
  1652. };
  1653. Node.prototype._evaluateBooleanConversion = function (feature) {
  1654. return Boolean(this._left.evaluate(feature));
  1655. };
  1656. Node.prototype._evaluateNumberConversion = function (feature) {
  1657. return Number(this._left.evaluate(feature));
  1658. };
  1659. Node.prototype._evaluateStringConversion = function (feature) {
  1660. return String(this._left.evaluate(feature));
  1661. };
  1662. Node.prototype._evaluateRegExp = function (feature) {
  1663. var pattern = this._value.evaluate(feature);
  1664. var flags = "";
  1665. if (defined(this._left)) {
  1666. flags = this._left.evaluate(feature);
  1667. }
  1668. var exp;
  1669. try {
  1670. exp = new RegExp(pattern, flags);
  1671. } catch (e) {
  1672. throw new RuntimeError(e);
  1673. }
  1674. return exp;
  1675. };
  1676. Node.prototype._evaluateRegExpTest = function (feature) {
  1677. var left = this._left.evaluate(feature);
  1678. var right = this._right.evaluate(feature);
  1679. if (!(left instanceof RegExp && typeof right === "string")) {
  1680. throw new RuntimeError(
  1681. "RegExp.test requires the first argument to be a RegExp and the second argument to be a string. Arguments are " +
  1682. left +
  1683. " and " +
  1684. right +
  1685. "."
  1686. );
  1687. }
  1688. return left.test(right);
  1689. };
  1690. Node.prototype._evaluateRegExpMatch = function (feature) {
  1691. var left = this._left.evaluate(feature);
  1692. var right = this._right.evaluate(feature);
  1693. if (left instanceof RegExp && typeof right === "string") {
  1694. return left.test(right);
  1695. } else if (right instanceof RegExp && typeof left === "string") {
  1696. return right.test(left);
  1697. }
  1698. throw new RuntimeError(
  1699. 'Operator "=~" requires one RegExp argument and one string argument. Arguments are ' +
  1700. left +
  1701. " and " +
  1702. right +
  1703. "."
  1704. );
  1705. };
  1706. Node.prototype._evaluateRegExpNotMatch = function (feature) {
  1707. var left = this._left.evaluate(feature);
  1708. var right = this._right.evaluate(feature);
  1709. if (left instanceof RegExp && typeof right === "string") {
  1710. return !left.test(right);
  1711. } else if (right instanceof RegExp && typeof left === "string") {
  1712. return !right.test(left);
  1713. }
  1714. throw new RuntimeError(
  1715. 'Operator "!~" requires one RegExp argument and one string argument. Arguments are ' +
  1716. left +
  1717. " and " +
  1718. right +
  1719. "."
  1720. );
  1721. };
  1722. Node.prototype._evaluateRegExpExec = function (feature) {
  1723. var left = this._left.evaluate(feature);
  1724. var right = this._right.evaluate(feature);
  1725. if (!(left instanceof RegExp && typeof right === "string")) {
  1726. throw new RuntimeError(
  1727. "RegExp.exec requires the first argument to be a RegExp and the second argument to be a string. Arguments are " +
  1728. left +
  1729. " and " +
  1730. right +
  1731. "."
  1732. );
  1733. }
  1734. var exec = left.exec(right);
  1735. if (!defined(exec)) {
  1736. return null;
  1737. }
  1738. return exec[1];
  1739. };
  1740. Node.prototype._evaluateToString = function (feature) {
  1741. var left = this._left.evaluate(feature);
  1742. if (
  1743. left instanceof RegExp ||
  1744. left instanceof Cartesian2 ||
  1745. left instanceof Cartesian3 ||
  1746. left instanceof Cartesian4
  1747. ) {
  1748. return String(left);
  1749. }
  1750. throw new RuntimeError('Unexpected function call "' + this._value + '".');
  1751. };
  1752. function convertHSLToRGB(ast) {
  1753. // Check if the color contains any nested expressions to see if the color can be converted here.
  1754. // E.g. "hsl(0.9, 0.6, 0.7)" is able to convert directly to rgb, "hsl(0.9, 0.6, ${Height})" is not.
  1755. var channels = ast._left;
  1756. var length = channels.length;
  1757. for (var i = 0; i < length; ++i) {
  1758. if (channels[i]._type !== ExpressionNodeType.LITERAL_NUMBER) {
  1759. return undefined;
  1760. }
  1761. }
  1762. var h = channels[0]._value;
  1763. var s = channels[1]._value;
  1764. var l = channels[2]._value;
  1765. var a = length === 4 ? channels[3]._value : 1.0;
  1766. return Color.fromHsl(h, s, l, a, scratchColor);
  1767. }
  1768. function convertRGBToColor(ast) {
  1769. // Check if the color contains any nested expressions to see if the color can be converted here.
  1770. // E.g. "rgb(255, 255, 255)" is able to convert directly to Color, "rgb(255, 255, ${Height})" is not.
  1771. var channels = ast._left;
  1772. var length = channels.length;
  1773. for (var i = 0; i < length; ++i) {
  1774. if (channels[i]._type !== ExpressionNodeType.LITERAL_NUMBER) {
  1775. return undefined;
  1776. }
  1777. }
  1778. var color = scratchColor;
  1779. color.red = channels[0]._value / 255.0;
  1780. color.green = channels[1]._value / 255.0;
  1781. color.blue = channels[2]._value / 255.0;
  1782. color.alpha = length === 4 ? channels[3]._value : 1.0;
  1783. return color;
  1784. }
  1785. function numberToString(number) {
  1786. if (number % 1 === 0) {
  1787. // Add a .0 to whole numbers
  1788. return number.toFixed(1);
  1789. }
  1790. return number.toString();
  1791. }
  1792. function colorToVec3(color) {
  1793. var r = numberToString(color.red);
  1794. var g = numberToString(color.green);
  1795. var b = numberToString(color.blue);
  1796. return "vec3(" + r + ", " + g + ", " + b + ")";
  1797. }
  1798. function colorToVec4(color) {
  1799. var r = numberToString(color.red);
  1800. var g = numberToString(color.green);
  1801. var b = numberToString(color.blue);
  1802. var a = numberToString(color.alpha);
  1803. return "vec4(" + r + ", " + g + ", " + b + ", " + a + ")";
  1804. }
  1805. function getExpressionArray(array, propertyNameMap, shaderState, parent) {
  1806. var length = array.length;
  1807. var expressions = new Array(length);
  1808. for (var i = 0; i < length; ++i) {
  1809. expressions[i] = array[i].getShaderExpression(
  1810. propertyNameMap,
  1811. shaderState,
  1812. parent
  1813. );
  1814. }
  1815. return expressions;
  1816. }
  1817. function getVariableName(variableName, propertyNameMap) {
  1818. if (!defined(propertyNameMap[variableName])) {
  1819. throw new RuntimeError(
  1820. 'Style references a property "' +
  1821. variableName +
  1822. '" that does not exist or is not styleable.'
  1823. );
  1824. }
  1825. return propertyNameMap[variableName];
  1826. }
  1827. var nullSentinel = "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.
  1828. Node.prototype.getShaderExpression = function (
  1829. propertyNameMap,
  1830. shaderState,
  1831. parent
  1832. ) {
  1833. var color;
  1834. var left;
  1835. var right;
  1836. var test;
  1837. var type = this._type;
  1838. var value = this._value;
  1839. if (defined(this._left)) {
  1840. if (Array.isArray(this._left)) {
  1841. // Left can be an array if the type is LITERAL_COLOR or LITERAL_VECTOR
  1842. left = getExpressionArray(this._left, propertyNameMap, shaderState, this);
  1843. } else {
  1844. left = this._left.getShaderExpression(propertyNameMap, shaderState, this);
  1845. }
  1846. }
  1847. if (defined(this._right)) {
  1848. right = this._right.getShaderExpression(propertyNameMap, shaderState, this);
  1849. }
  1850. if (defined(this._test)) {
  1851. test = this._test.getShaderExpression(propertyNameMap, shaderState, this);
  1852. }
  1853. if (Array.isArray(this._value)) {
  1854. // For ARRAY type
  1855. value = getExpressionArray(this._value, propertyNameMap, shaderState, this);
  1856. }
  1857. switch (type) {
  1858. case ExpressionNodeType.VARIABLE:
  1859. if (checkFeature(this)) {
  1860. return undefined;
  1861. }
  1862. return getVariableName(value, propertyNameMap);
  1863. case ExpressionNodeType.UNARY:
  1864. // Supported types: +, -, !, Boolean, Number
  1865. if (value === "Boolean") {
  1866. return "bool(" + left + ")";
  1867. } else if (value === "Number") {
  1868. return "float(" + left + ")";
  1869. } else if (value === "round") {
  1870. return "floor(" + left + " + 0.5)";
  1871. } else if (defined(unaryFunctions[value])) {
  1872. return value + "(" + left + ")";
  1873. } else if (value === "isNaN") {
  1874. // In GLSL 2.0 use isnan instead
  1875. return "(" + left + " != " + left + ")";
  1876. } else if (value === "isFinite") {
  1877. // 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.
  1878. return "(abs(" + left + ") < czm_infinity)";
  1879. } else if (
  1880. value === "String" ||
  1881. value === "isExactClass" ||
  1882. value === "isClass" ||
  1883. value === "getExactClassName"
  1884. ) {
  1885. throw new RuntimeError(
  1886. 'Error generating style shader: "' + value + '" is not supported.'
  1887. );
  1888. }
  1889. return value + left;
  1890. case ExpressionNodeType.BINARY:
  1891. // Supported types: ||, &&, ===, !==, <, >, <=, >=, +, -, *, /, %
  1892. if (value === "%") {
  1893. return "mod(" + left + ", " + right + ")";
  1894. } else if (value === "===") {
  1895. return "(" + left + " == " + right + ")";
  1896. } else if (value === "!==") {
  1897. return "(" + left + " != " + right + ")";
  1898. } else if (value === "atan2") {
  1899. return "atan(" + left + ", " + right + ")";
  1900. } else if (defined(binaryFunctions[value])) {
  1901. return value + "(" + left + ", " + right + ")";
  1902. }
  1903. return "(" + left + " " + value + " " + right + ")";
  1904. case ExpressionNodeType.TERNARY:
  1905. if (defined(ternaryFunctions[value])) {
  1906. return value + "(" + left + ", " + right + ", " + test + ")";
  1907. }
  1908. break;
  1909. case ExpressionNodeType.CONDITIONAL:
  1910. return "(" + test + " ? " + left + " : " + right + ")";
  1911. case ExpressionNodeType.MEMBER:
  1912. if (checkFeature(this._left)) {
  1913. return getVariableName(right, propertyNameMap);
  1914. }
  1915. // This is intended for accessing the components of vector properties. String members aren't supported.
  1916. // Check for 0.0 rather than 0 because all numbers are previously converted to decimals.
  1917. if (right === "r" || right === "x" || right === "0.0") {
  1918. return left + "[0]";
  1919. } else if (right === "g" || right === "y" || right === "1.0") {
  1920. return left + "[1]";
  1921. } else if (right === "b" || right === "z" || right === "2.0") {
  1922. return left + "[2]";
  1923. } else if (right === "a" || right === "w" || right === "3.0") {
  1924. return left + "[3]";
  1925. }
  1926. return left + "[int(" + right + ")]";
  1927. case ExpressionNodeType.FUNCTION_CALL:
  1928. throw new RuntimeError(
  1929. 'Error generating style shader: "' + value + '" is not supported.'
  1930. );
  1931. case ExpressionNodeType.ARRAY:
  1932. if (value.length === 4) {
  1933. return (
  1934. "vec4(" +
  1935. value[0] +
  1936. ", " +
  1937. value[1] +
  1938. ", " +
  1939. value[2] +
  1940. ", " +
  1941. value[3] +
  1942. ")"
  1943. );
  1944. } else if (value.length === 3) {
  1945. return "vec3(" + value[0] + ", " + value[1] + ", " + value[2] + ")";
  1946. } else if (value.length === 2) {
  1947. return "vec2(" + value[0] + ", " + value[1] + ")";
  1948. }
  1949. throw new RuntimeError(
  1950. "Error generating style shader: Invalid array length. Array length should be 2, 3, or 4."
  1951. );
  1952. case ExpressionNodeType.REGEX:
  1953. throw new RuntimeError(
  1954. "Error generating style shader: Regular expressions are not supported."
  1955. );
  1956. case ExpressionNodeType.VARIABLE_IN_STRING:
  1957. throw new RuntimeError(
  1958. "Error generating style shader: Converting a variable to a string is not supported."
  1959. );
  1960. case ExpressionNodeType.LITERAL_NULL:
  1961. return nullSentinel;
  1962. case ExpressionNodeType.LITERAL_BOOLEAN:
  1963. return value ? "true" : "false";
  1964. case ExpressionNodeType.LITERAL_NUMBER:
  1965. return numberToString(value);
  1966. case ExpressionNodeType.LITERAL_STRING:
  1967. if (defined(parent) && parent._type === ExpressionNodeType.MEMBER) {
  1968. if (
  1969. value === "r" ||
  1970. value === "g" ||
  1971. value === "b" ||
  1972. value === "a" ||
  1973. value === "x" ||
  1974. value === "y" ||
  1975. value === "z" ||
  1976. value === "w" ||
  1977. checkFeature(parent._left)
  1978. ) {
  1979. return value;
  1980. }
  1981. }
  1982. // Check for css color strings
  1983. color = Color.fromCssColorString(value, scratchColor);
  1984. if (defined(color)) {
  1985. return colorToVec3(color);
  1986. }
  1987. throw new RuntimeError(
  1988. "Error generating style shader: String literals are not supported."
  1989. );
  1990. case ExpressionNodeType.LITERAL_COLOR:
  1991. var args = left;
  1992. if (value === "color") {
  1993. if (!defined(args)) {
  1994. return "vec4(1.0)";
  1995. } else if (args.length > 1) {
  1996. var rgb = args[0];
  1997. var alpha = args[1];
  1998. if (alpha !== "1.0") {
  1999. shaderState.translucent = true;
  2000. }
  2001. return "vec4(" + rgb + ", " + alpha + ")";
  2002. }
  2003. return "vec4(" + args[0] + ", 1.0)";
  2004. } else if (value === "rgb") {
  2005. color = convertRGBToColor(this);
  2006. if (defined(color)) {
  2007. return colorToVec4(color);
  2008. }
  2009. return (
  2010. "vec4(" +
  2011. args[0] +
  2012. " / 255.0, " +
  2013. args[1] +
  2014. " / 255.0, " +
  2015. args[2] +
  2016. " / 255.0, 1.0)"
  2017. );
  2018. } else if (value === "rgba") {
  2019. if (args[3] !== "1.0") {
  2020. shaderState.translucent = true;
  2021. }
  2022. color = convertRGBToColor(this);
  2023. if (defined(color)) {
  2024. return colorToVec4(color);
  2025. }
  2026. return (
  2027. "vec4(" +
  2028. args[0] +
  2029. " / 255.0, " +
  2030. args[1] +
  2031. " / 255.0, " +
  2032. args[2] +
  2033. " / 255.0, " +
  2034. args[3] +
  2035. ")"
  2036. );
  2037. } else if (value === "hsl") {
  2038. color = convertHSLToRGB(this);
  2039. if (defined(color)) {
  2040. return colorToVec4(color);
  2041. }
  2042. return (
  2043. "vec4(czm_HSLToRGB(vec3(" +
  2044. args[0] +
  2045. ", " +
  2046. args[1] +
  2047. ", " +
  2048. args[2] +
  2049. ")), 1.0)"
  2050. );
  2051. } else if (value === "hsla") {
  2052. color = convertHSLToRGB(this);
  2053. if (defined(color)) {
  2054. if (color.alpha !== 1.0) {
  2055. shaderState.translucent = true;
  2056. }
  2057. return colorToVec4(color);
  2058. }
  2059. if (args[3] !== "1.0") {
  2060. shaderState.translucent = true;
  2061. }
  2062. return (
  2063. "vec4(czm_HSLToRGB(vec3(" +
  2064. args[0] +
  2065. ", " +
  2066. args[1] +
  2067. ", " +
  2068. args[2] +
  2069. ")), " +
  2070. args[3] +
  2071. ")"
  2072. );
  2073. }
  2074. break;
  2075. case ExpressionNodeType.LITERAL_VECTOR:
  2076. //>>includeStart('debug', pragmas.debug);
  2077. if (!defined(left)) {
  2078. throw new DeveloperError(
  2079. "left should always be defined for type ExpressionNodeType.LITERAL_VECTOR"
  2080. );
  2081. }
  2082. //>>includeEnd('debug');
  2083. var length = left.length;
  2084. var vectorExpression = value + "(";
  2085. for (var i = 0; i < length; ++i) {
  2086. vectorExpression += left[i];
  2087. if (i < length - 1) {
  2088. vectorExpression += ", ";
  2089. }
  2090. }
  2091. vectorExpression += ")";
  2092. return vectorExpression;
  2093. case ExpressionNodeType.LITERAL_REGEX:
  2094. throw new RuntimeError(
  2095. "Error generating style shader: Regular expressions are not supported."
  2096. );
  2097. case ExpressionNodeType.LITERAL_UNDEFINED:
  2098. return nullSentinel;
  2099. case ExpressionNodeType.BUILTIN_VARIABLE:
  2100. if (value === "tiles3d_tileset_time") {
  2101. return "u_time";
  2102. }
  2103. }
  2104. };
  2105. export default Expression;