Expression.js 77 KB

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