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