智慧水务管理系统 - 精河县供水工程综合管理平台

Expression.js 70KB

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