@versatiles/svg-renderer 2.4.2 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7470,7 +7470,7 @@ function pixelAlignedBox(project) {
7470
7470
  return [minX, minY, maxX, maxY];
7471
7471
  }
7472
7472
 
7473
- //#region src/reference/v8.json
7473
+ // src/reference/v8.json
7474
7474
  var v8_default = {
7475
7475
  $version: 8,
7476
7476
  $root: {
@@ -7565,6 +7565,14 @@ var v8_default = {
7565
7565
  },
7566
7566
  "default": "xyz"
7567
7567
  },
7568
+ "emptyResponse": {
7569
+ "type": "enum",
7570
+ "values": {
7571
+ "blank": {},
7572
+ "missing": {}
7573
+ },
7574
+ "default": "blank"
7575
+ },
7568
7576
  "minzoom": {
7569
7577
  "type": "number",
7570
7578
  "default": 0
@@ -7632,6 +7640,14 @@ var v8_default = {
7632
7640
  },
7633
7641
  "default": "xyz"
7634
7642
  },
7643
+ "emptyResponse": {
7644
+ "type": "enum",
7645
+ "values": {
7646
+ "blank": {},
7647
+ "missing": {}
7648
+ },
7649
+ "default": "blank"
7650
+ },
7635
7651
  "attribution": { "type": "string" },
7636
7652
  "volatile": {
7637
7653
  "type": "boolean",
@@ -7674,6 +7690,14 @@ var v8_default = {
7674
7690
  "default": 512,
7675
7691
  "units": "pixels"
7676
7692
  },
7693
+ "emptyResponse": {
7694
+ "type": "enum",
7695
+ "values": {
7696
+ "blank": {},
7697
+ "missing": {}
7698
+ },
7699
+ "default": "blank"
7700
+ },
7677
7701
  "attribution": { "type": "string" },
7678
7702
  "encoding": {
7679
7703
  "type": "enum",
@@ -8953,6 +8977,21 @@ var v8_default = {
8953
8977
  },
8954
8978
  "property-type": "data-constant"
8955
8979
  },
8980
+ "fill-layer-blend": {
8981
+ "type": "enum",
8982
+ "values": {
8983
+ "normal": {},
8984
+ "multiply": {},
8985
+ "color": {},
8986
+ "luminosity": {},
8987
+ "plus-lighter": {},
8988
+ "screen": {},
8989
+ "overlay": {}
8990
+ },
8991
+ "default": "normal",
8992
+ "property-type": "constant",
8993
+ "transition": false
8994
+ },
8956
8995
  "fill-color": {
8957
8996
  "type": "color",
8958
8997
  "default": "#000000",
@@ -9041,7 +9080,8 @@ var v8_default = {
9041
9080
  "parameters": [
9042
9081
  "zoom",
9043
9082
  "feature",
9044
- "feature-state"
9083
+ "feature-state",
9084
+ "elevation"
9045
9085
  ]
9046
9086
  },
9047
9087
  "property-type": "data-driven"
@@ -9153,6 +9193,21 @@ var v8_default = {
9153
9193
  },
9154
9194
  "property-type": "data-constant"
9155
9195
  },
9196
+ "line-layer-blend": {
9197
+ "type": "enum",
9198
+ "values": {
9199
+ "normal": {},
9200
+ "multiply": {},
9201
+ "color": {},
9202
+ "luminosity": {},
9203
+ "plus-lighter": {},
9204
+ "screen": {},
9205
+ "overlay": {}
9206
+ },
9207
+ "default": "normal",
9208
+ "property-type": "constant",
9209
+ "transition": false
9210
+ },
9156
9211
  "line-color": {
9157
9212
  "type": "color",
9158
9213
  "default": "#000000",
@@ -9775,6 +9830,21 @@ var v8_default = {
9775
9830
  },
9776
9831
  "property-type": "data-constant"
9777
9832
  },
9833
+ "raster-layer-blend": {
9834
+ "type": "enum",
9835
+ "values": {
9836
+ "normal": {},
9837
+ "multiply": {},
9838
+ "color": {},
9839
+ "luminosity": {},
9840
+ "plus-lighter": {},
9841
+ "screen": {},
9842
+ "overlay": {}
9843
+ },
9844
+ "default": "normal",
9845
+ "property-type": "constant",
9846
+ "transition": false
9847
+ },
9778
9848
  "raster-hue-rotate": {
9779
9849
  "type": "number",
9780
9850
  "default": 0,
@@ -10135,10 +10205,44 @@ var v8_default = {
10135
10205
  }
10136
10206
  };
10137
10207
 
10138
- //#region src/reference/latest.ts
10208
+ // src/reference/latest.ts
10139
10209
  const latest = v8_default;
10140
10210
 
10141
- //#region src/expression/types.ts
10211
+ // src/expression/parsing_error.ts
10212
+ var ExpressionParsingError = class extends Error {
10213
+ constructor(key, message) {
10214
+ super(message);
10215
+ this.message = message;
10216
+ this.key = key;
10217
+ }
10218
+ };
10219
+
10220
+ // src/expression/scope.ts
10221
+ /**
10222
+ * Tracks `let` bindings during expression parsing.
10223
+ * @private
10224
+ */
10225
+ var Scope = class Scope {
10226
+ constructor(parent, bindings = []) {
10227
+ this.parent = parent;
10228
+ this.bindings = {};
10229
+ for (const [name, expression] of bindings) this.bindings[name] = expression;
10230
+ }
10231
+ concat(bindings) {
10232
+ return new Scope(this, bindings);
10233
+ }
10234
+ get(name) {
10235
+ if (this.bindings[name]) return this.bindings[name];
10236
+ if (this.parent) return this.parent.get(name);
10237
+ throw new Error(`${name} not found in scope.`);
10238
+ }
10239
+ has(name) {
10240
+ if (this.bindings[name]) return true;
10241
+ return this.parent ? this.parent.has(name) : false;
10242
+ }
10243
+ };
10244
+
10245
+ // src/expression/types.ts
10142
10246
  const NullType = { kind: "null" };
10143
10247
  const NumberType = { kind: "number" };
10144
10248
  const StringType = { kind: "string" };
@@ -10234,7 +10338,7 @@ function verifyType(provided, sample) {
10234
10338
  return provided.kind === sample.kind;
10235
10339
  }
10236
10340
 
10237
- //#region src/expression/types/color_spaces.ts
10341
+ // src/expression/types/color_spaces.ts
10238
10342
  const Xn = .96422;
10239
10343
  const Yn = 1;
10240
10344
  const Zn = .82521;
@@ -10329,7 +10433,7 @@ function hslToRgb([h, s, l, alpha]) {
10329
10433
  ];
10330
10434
  }
10331
10435
 
10332
- //#region src/util/get_own.ts
10436
+ // src/util/get_own.ts
10333
10437
  const hasOwnProperty = Object.hasOwn || function hasOwnProperty(object, key) {
10334
10438
  return Object.prototype.hasOwnProperty.call(object, key);
10335
10439
  };
@@ -10337,7 +10441,7 @@ function getOwn(object, key) {
10337
10441
  return hasOwnProperty(object, key) ? object[key] : void 0;
10338
10442
  }
10339
10443
 
10340
- //#region src/expression/types/parse_css_color.ts
10444
+ // src/expression/types/parse_css_color.ts
10341
10445
  /**
10342
10446
  * CSS color parser compliant with CSS Color 4 Specification.
10343
10447
  * Supports: named colors, `transparent` keyword, all rgb hex notations,
@@ -11215,7 +11319,7 @@ const namedColors = {
11215
11319
  ]
11216
11320
  };
11217
11321
 
11218
- //#region src/util/interpolate-primitives.ts
11322
+ // src/util/interpolate-primitives.ts
11219
11323
  function interpolateNumber(from, to, t) {
11220
11324
  return from + t * (to - from);
11221
11325
  }
@@ -11225,7 +11329,7 @@ function interpolateArray(from, to, t) {
11225
11329
  });
11226
11330
  }
11227
11331
 
11228
- //#region src/expression/types/color.ts
11332
+ // src/expression/types/color.ts
11229
11333
  /**
11230
11334
  * Checks whether the specified color space is one of the supported interpolation color spaces.
11231
11335
  *
@@ -11409,7 +11513,26 @@ var Color$1 = class Color {
11409
11513
  }
11410
11514
  };
11411
11515
 
11412
- //#region src/expression/types/formatted.ts
11516
+ // src/expression/types/collator.ts
11517
+ var Collator = class {
11518
+ constructor(caseSensitive, diacriticSensitive, locale) {
11519
+ if (caseSensitive) this.sensitivity = diacriticSensitive ? "variant" : "case";
11520
+ else this.sensitivity = diacriticSensitive ? "accent" : "base";
11521
+ this.locale = locale;
11522
+ this.collator = new Intl.Collator(this.locale ? this.locale : [], {
11523
+ sensitivity: this.sensitivity,
11524
+ usage: "search"
11525
+ });
11526
+ }
11527
+ compare(lhs, rhs) {
11528
+ return this.collator.compare(lhs, rhs);
11529
+ }
11530
+ resolvedLocale() {
11531
+ return new Intl.Collator(this.locale ? this.locale : []).resolvedOptions().locale;
11532
+ }
11533
+ };
11534
+
11535
+ // src/expression/types/formatted.ts
11413
11536
  const VERTICAL_ALIGN_OPTIONS = [
11414
11537
  "bottom",
11415
11538
  "center",
@@ -11446,7 +11569,7 @@ var Formatted = class Formatted {
11446
11569
  }
11447
11570
  };
11448
11571
 
11449
- //#region src/expression/types/padding.ts
11572
+ // src/expression/types/padding.ts
11450
11573
  /**
11451
11574
  * A set of four numbers representing padding around a box. Create instances from
11452
11575
  * bare arrays or numeric values using the static method `Padding.parse`.
@@ -11506,7 +11629,7 @@ var Padding = class Padding {
11506
11629
  }
11507
11630
  };
11508
11631
 
11509
- //#region src/expression/types/number_array.ts
11632
+ // src/expression/types/number_array.ts
11510
11633
  /**
11511
11634
  * An array of numbers. Create instances from
11512
11635
  * bare arrays or numeric values using the static method `NumberArray.parse`.
@@ -11536,7 +11659,7 @@ var NumberArray = class NumberArray {
11536
11659
  }
11537
11660
  };
11538
11661
 
11539
- //#region src/expression/types/color_array.ts
11662
+ // src/expression/types/color_array.ts
11540
11663
  /**
11541
11664
  * An array of colors. Create instances from
11542
11665
  * bare arrays or strings using the static method `ColorArray.parse`.
@@ -11579,7 +11702,7 @@ var ColorArray = class ColorArray {
11579
11702
  }
11580
11703
  };
11581
11704
 
11582
- //#region src/expression/runtime_error.ts
11705
+ // src/expression/runtime_error.ts
11583
11706
  var RuntimeError = class extends Error {
11584
11707
  constructor(message, path) {
11585
11708
  super(message);
@@ -11591,7 +11714,7 @@ var RuntimeError = class extends Error {
11591
11714
  }
11592
11715
  };
11593
11716
 
11594
- //#region src/expression/types/variable_anchor_offset_collection.ts
11717
+ // src/expression/types/variable_anchor_offset_collection.ts
11595
11718
  /** Set of valid anchor positions, as a set for validation */
11596
11719
  const anchors = /* @__PURE__ */ new Set([
11597
11720
  "center",
@@ -11643,7 +11766,7 @@ var VariableAnchorOffsetCollection = class VariableAnchorOffsetCollection {
11643
11766
  }
11644
11767
  };
11645
11768
 
11646
- //#region src/expression/types/resolved_image.ts
11769
+ // src/expression/types/resolved_image.ts
11647
11770
  var ResolvedImage = class ResolvedImage {
11648
11771
  constructor(options) {
11649
11772
  this.name = options.name;
@@ -11661,7 +11784,7 @@ var ResolvedImage = class ResolvedImage {
11661
11784
  }
11662
11785
  };
11663
11786
 
11664
- //#region src/expression/types/projection_definition.ts
11787
+ // src/expression/types/projection_definition.ts
11665
11788
  var ProjectionDefinition = class ProjectionDefinition {
11666
11789
  constructor(from, to, transition) {
11667
11790
  this.from = from;
@@ -11687,26 +11810,7 @@ var ProjectionDefinition = class ProjectionDefinition {
11687
11810
  }
11688
11811
  };
11689
11812
 
11690
- //#region src/expression/types/collator.ts
11691
- var Collator = class {
11692
- constructor(caseSensitive, diacriticSensitive, locale) {
11693
- if (caseSensitive) this.sensitivity = diacriticSensitive ? "variant" : "case";
11694
- else this.sensitivity = diacriticSensitive ? "accent" : "base";
11695
- this.locale = locale;
11696
- this.collator = new Intl.Collator(this.locale ? this.locale : [], {
11697
- sensitivity: this.sensitivity,
11698
- usage: "search"
11699
- });
11700
- }
11701
- compare(lhs, rhs) {
11702
- return this.collator.compare(lhs, rhs);
11703
- }
11704
- resolvedLocale() {
11705
- return new Intl.Collator(this.locale ? this.locale : []).resolvedOptions().locale;
11706
- }
11707
- };
11708
-
11709
- //#region src/expression/values.ts
11813
+ // src/expression/values.ts
11710
11814
  function validateRGBA(r, g, b, a) {
11711
11815
  if (!(typeof r === "number" && r >= 0 && r <= 255 && typeof g === "number" && g >= 0 && g <= 255 && typeof b === "number" && b >= 0 && b <= 255)) return `Invalid rgba value [${(typeof a === "number" ? [
11712
11816
  r,
@@ -11773,7 +11877,7 @@ function valueToString(value) {
11773
11877
  else return JSON.stringify(value);
11774
11878
  }
11775
11879
 
11776
- //#region src/expression/definitions/literal.ts
11880
+ // src/expression/definitions/literal.ts
11777
11881
  var Literal = class Literal {
11778
11882
  constructor(type, value) {
11779
11883
  this.type = type;
@@ -11797,539 +11901,100 @@ var Literal = class Literal {
11797
11901
  }
11798
11902
  };
11799
11903
 
11800
- //#region src/expression/evaluation_context.ts
11801
- const geometryTypes = [
11802
- "Unknown",
11803
- "Point",
11804
- "LineString",
11805
- "Polygon"
11806
- ];
11807
- var EvaluationContext = class {
11808
- constructor() {
11809
- this.globals = null;
11810
- this.feature = null;
11811
- this.featureState = null;
11812
- this.formattedSection = null;
11813
- this._parseColorCache = /* @__PURE__ */ new Map();
11814
- this.availableImages = null;
11815
- this.canonical = null;
11816
- }
11817
- id() {
11818
- return this.feature && "id" in this.feature ? this.feature.id : null;
11819
- }
11820
- geometryType() {
11821
- return this.feature ? typeof this.feature.type === "number" ? geometryTypes[this.feature.type] : this.feature.type : null;
11904
+ // src/expression/definitions/assertion.ts
11905
+ const types$1 = {
11906
+ string: StringType,
11907
+ number: NumberType,
11908
+ boolean: BooleanType,
11909
+ object: ObjectType
11910
+ };
11911
+ var Assertion = class Assertion {
11912
+ constructor(type, args, key) {
11913
+ this.type = type;
11914
+ this.args = args;
11915
+ this.key = key;
11822
11916
  }
11823
- geometry() {
11824
- return this.feature && "geometry" in this.feature ? this.feature.geometry : null;
11917
+ static parse(args, context) {
11918
+ if (args.length < 2) return context.error("Expected at least one argument.");
11919
+ let i = 1;
11920
+ let type;
11921
+ const name = args[0];
11922
+ if (name === "array") {
11923
+ let itemType;
11924
+ if (args.length > 2) {
11925
+ const type = args[1];
11926
+ if (typeof type !== "string" || !(type in types$1) || type === "object") return context.error("The item type argument of \"array\" must be one of string, number, boolean", 1);
11927
+ itemType = types$1[type];
11928
+ i++;
11929
+ } else itemType = ValueType;
11930
+ let N;
11931
+ if (args.length > 3) {
11932
+ if (args[2] !== null && (typeof args[2] !== "number" || args[2] < 0 || args[2] !== Math.floor(args[2]))) return context.error("The length argument to \"array\" must be a positive integer literal", 2);
11933
+ N = args[2];
11934
+ i++;
11935
+ }
11936
+ type = array(itemType, N);
11937
+ } else {
11938
+ if (!types$1[name]) throw new Error(`Types doesn't contain name = ${name}`);
11939
+ type = types$1[name];
11940
+ }
11941
+ const parsed = [];
11942
+ for (; i < args.length; i++) {
11943
+ const input = context.parse(args[i], i, ValueType);
11944
+ if (!input) return null;
11945
+ parsed.push(input);
11946
+ }
11947
+ return new Assertion(type, parsed, context.key);
11825
11948
  }
11826
- canonicalID() {
11827
- return this.canonical;
11949
+ evaluate(ctx) {
11950
+ for (let i = 0; i < this.args.length; i++) {
11951
+ const value = this.args[i].evaluate(ctx);
11952
+ if (!checkSubtype(this.type, typeOf(value))) return value;
11953
+ else if (i === this.args.length - 1) throw new RuntimeError(`Expected value to be of type ${typeToString(this.type)}, but found ${typeToString(typeOf(value))} instead.`, this.key);
11954
+ }
11955
+ throw new Error();
11828
11956
  }
11829
- properties() {
11830
- return this.feature && this.feature.properties || {};
11957
+ eachChild(fn) {
11958
+ this.args.forEach(fn);
11831
11959
  }
11832
- parseColor(input) {
11833
- let cached = this._parseColorCache.get(input);
11834
- if (!cached) {
11835
- cached = Color$1.parse(input);
11836
- this._parseColorCache.set(input, cached);
11837
- }
11838
- return cached;
11960
+ outputDefined() {
11961
+ return this.args.every((arg) => arg.outputDefined());
11839
11962
  }
11840
11963
  };
11841
11964
 
11842
- //#region src/expression/stops.ts
11965
+ // src/expression/definitions/coercion.ts
11966
+ const types = {
11967
+ "to-boolean": BooleanType,
11968
+ "to-color": ColorType,
11969
+ "to-number": NumberType,
11970
+ "to-string": StringType
11971
+ };
11843
11972
  /**
11844
- * Returns the index of the last stop <= input, or 0 if it doesn't exist.
11973
+ * Special form for error-coalescing coercion expressions "to-number",
11974
+ * "to-color". Since these coercions can fail at runtime, they accept multiple
11975
+ * arguments, only evaluating one at a time until one succeeds.
11976
+ *
11845
11977
  * @private
11846
11978
  */
11847
- function findStopLessThanOrEqualTo(stops, input, key) {
11848
- const lastIndex = stops.length - 1;
11849
- let lowerIndex = 0;
11850
- let upperIndex = lastIndex;
11851
- let currentIndex = 0;
11852
- let currentValue, nextValue;
11853
- while (lowerIndex <= upperIndex) {
11854
- currentIndex = Math.floor((lowerIndex + upperIndex) / 2);
11855
- currentValue = stops[currentIndex];
11856
- nextValue = stops[currentIndex + 1];
11857
- if (currentValue <= input) {
11858
- if (currentIndex === lastIndex || input < nextValue) return currentIndex;
11859
- lowerIndex = currentIndex + 1;
11860
- } else if (currentValue > input) upperIndex = currentIndex - 1;
11861
- else throw new RuntimeError("Input is not a number.", key);
11862
- }
11863
- return 0;
11864
- }
11865
-
11866
- //#region src/expression/definitions/step.ts
11867
- var Step = class Step {
11868
- constructor(type, input, stops, key) {
11979
+ var Coercion = class Coercion {
11980
+ constructor(type, args, key) {
11869
11981
  this.type = type;
11870
- this.input = input;
11982
+ this.args = args;
11871
11983
  this.key = key;
11872
- this.labels = [];
11873
- this.outputs = [];
11874
- for (const [label, expression] of stops) {
11875
- this.labels.push(label);
11876
- this.outputs.push(expression);
11877
- }
11878
11984
  }
11879
11985
  static parse(args, context) {
11880
- if (args.length - 1 < 4) return context.error(`Expected at least 4 arguments, but found only ${args.length - 1}.`);
11881
- if ((args.length - 1) % 2 !== 0) return context.error("Expected an even number of arguments.");
11882
- const input = context.parse(args[1], 1, NumberType);
11883
- if (!input) return null;
11884
- const stops = [];
11885
- let outputType = null;
11886
- if (context.expectedType && context.expectedType.kind !== "value") outputType = context.expectedType;
11887
- for (let i = 1; i < args.length; i += 2) {
11888
- const label = i === 1 ? -Infinity : args[i];
11889
- const value = args[i + 1];
11890
- const labelKey = i;
11891
- const valueKey = i + 1;
11892
- if (typeof label !== "number") return context.error("Input/output pairs for \"step\" expressions must be defined using literal numeric values (not computed expressions) for the input values.", labelKey);
11893
- if (stops.length && stops[stops.length - 1][0] >= label) return context.error("Input/output pairs for \"step\" expressions must be arranged with input values in strictly ascending order.", labelKey);
11894
- const parsed = context.parse(value, valueKey, outputType);
11895
- if (!parsed) return null;
11896
- outputType = outputType || parsed.type;
11897
- stops.push([label, parsed]);
11986
+ if (args.length < 2) return context.error("Expected at least one argument.");
11987
+ const name = args[0];
11988
+ if (!types[name]) throw new Error(`Can't parse ${name} as it is not part of the known types`);
11989
+ if ((name === "to-boolean" || name === "to-string") && args.length !== 2) return context.error("Expected one argument.");
11990
+ const type = types[name];
11991
+ const parsed = [];
11992
+ for (let i = 1; i < args.length; i++) {
11993
+ const input = context.parse(args[i], i, ValueType);
11994
+ if (!input) return null;
11995
+ parsed.push(input);
11898
11996
  }
11899
- return new Step(outputType, input, stops, context.key);
11900
- }
11901
- evaluate(ctx) {
11902
- const labels = this.labels;
11903
- const outputs = this.outputs;
11904
- if (labels.length === 1) return outputs[0].evaluate(ctx);
11905
- const value = this.input.evaluate(ctx);
11906
- if (value <= labels[0]) return outputs[0].evaluate(ctx);
11907
- const stopCount = labels.length;
11908
- if (value >= labels[stopCount - 1]) return outputs[stopCount - 1].evaluate(ctx);
11909
- return outputs[findStopLessThanOrEqualTo(labels, value, this.key)].evaluate(ctx);
11910
- }
11911
- eachChild(fn) {
11912
- fn(this.input);
11913
- for (const expression of this.outputs) fn(expression);
11914
- }
11915
- outputDefined() {
11916
- return this.outputs.every((out) => out.outputDefined());
11917
- }
11918
- };
11919
-
11920
- //#region node_modules/@mapbox/unitbezier/index.js
11921
- function unitBezier(p1x, p1y, p2x, p2y) {
11922
- const cx = 3 * p1x;
11923
- const bx = 3 * (p2x - p1x) - cx;
11924
- const ax = 1 - cx - bx;
11925
- const cy = 3 * p1y;
11926
- const by = 3 * (p2y - p1y) - cy;
11927
- const ay = 1 - cy - by;
11928
- return function solve(x, epsilon = 1e-6) {
11929
- if (x <= 0) return 0;
11930
- if (x >= 1) return 1;
11931
- let t = x;
11932
- for (let i = 0; i < 8; i++) {
11933
- const x2 = ((ax * t + bx) * t + cx) * t - x;
11934
- if (Math.abs(x2) < epsilon) return ((ay * t + by) * t + cy) * t;
11935
- const d2 = (3 * ax * t + 2 * bx) * t + cx;
11936
- if (Math.abs(d2) < 1e-6) break;
11937
- t -= x2 / d2;
11938
- }
11939
- let t0 = 0;
11940
- let t1 = 1;
11941
- t = x;
11942
- for (let i = 0; i < 20; i++) {
11943
- const x2 = ((ax * t + bx) * t + cx) * t;
11944
- if (Math.abs(x2 - x) < epsilon) break;
11945
- if (x > x2) t0 = t;
11946
- else t1 = t;
11947
- t = (t0 + t1) * .5;
11948
- }
11949
- return ((ay * t + by) * t + cy) * t;
11950
- };
11951
- }
11952
-
11953
- //#region src/expression/definitions/interpolate.ts
11954
- var Interpolate = class Interpolate {
11955
- constructor(type, operator, interpolation, input, stops, key) {
11956
- this.type = type;
11957
- this.operator = operator;
11958
- this.interpolation = interpolation;
11959
- this.input = input;
11960
- this.key = key;
11961
- this.labels = [];
11962
- this.outputs = [];
11963
- for (const [label, expression] of stops) {
11964
- this.labels.push(label);
11965
- this.outputs.push(expression);
11966
- }
11967
- }
11968
- static interpolationFactor(interpolation, input, lower, upper) {
11969
- let t = 0;
11970
- if (interpolation.name === "exponential") t = exponentialInterpolation(input, interpolation.base, lower, upper);
11971
- else if (interpolation.name === "linear") t = exponentialInterpolation(input, 1, lower, upper);
11972
- else if (interpolation.name === "cubic-bezier") {
11973
- const c = interpolation.controlPoints;
11974
- t = unitBezier(c[0], c[1], c[2], c[3])(exponentialInterpolation(input, 1, lower, upper));
11975
- }
11976
- return t;
11977
- }
11978
- static parse(args, context) {
11979
- let [operator, interpolation, input, ...rest] = args;
11980
- if (!Array.isArray(interpolation) || interpolation.length === 0) return context.error("Expected an interpolation type expression.", 1);
11981
- if (interpolation[0] === "linear") interpolation = { name: "linear" };
11982
- else if (interpolation[0] === "exponential") {
11983
- const base = interpolation[1];
11984
- if (typeof base !== "number") return context.error("Exponential interpolation requires a numeric base.", 1, 1);
11985
- interpolation = {
11986
- name: "exponential",
11987
- base
11988
- };
11989
- } else if (interpolation[0] === "cubic-bezier") {
11990
- const controlPoints = interpolation.slice(1);
11991
- if (controlPoints.length !== 4 || controlPoints.some((t) => typeof t !== "number" || t < 0 || t > 1)) return context.error("Cubic bezier interpolation requires four numeric arguments with values between 0 and 1.", 1);
11992
- interpolation = {
11993
- name: "cubic-bezier",
11994
- controlPoints
11995
- };
11996
- } else return context.error(`Unknown interpolation type ${String(interpolation[0])}`, 1, 0);
11997
- if (args.length - 1 < 4) return context.error(`Expected at least 4 arguments, but found only ${args.length - 1}.`);
11998
- if ((args.length - 1) % 2 !== 0) return context.error("Expected an even number of arguments.");
11999
- input = context.parse(input, 2, NumberType);
12000
- if (!input) return null;
12001
- const stops = [];
12002
- let outputType = null;
12003
- if ((operator === "interpolate-hcl" || operator === "interpolate-lab") && context.expectedType != ColorArrayType) outputType = ColorType;
12004
- else if (context.expectedType && context.expectedType.kind !== "value") outputType = context.expectedType;
12005
- for (let i = 0; i < rest.length; i += 2) {
12006
- const label = rest[i];
12007
- const value = rest[i + 1];
12008
- const labelKey = i + 3;
12009
- const valueKey = i + 4;
12010
- if (typeof label !== "number") return context.error("Input/output pairs for \"interpolate\" expressions must be defined using literal numeric values (not computed expressions) for the input values.", labelKey);
12011
- if (stops.length && stops[stops.length - 1][0] >= label) return context.error("Input/output pairs for \"interpolate\" expressions must be arranged with input values in strictly ascending order.", labelKey);
12012
- const parsed = context.parse(value, valueKey, outputType);
12013
- if (!parsed) return null;
12014
- outputType = outputType || parsed.type;
12015
- stops.push([label, parsed]);
12016
- }
12017
- if (!verifyType(outputType, NumberType) && !verifyType(outputType, ProjectionDefinitionType) && !verifyType(outputType, ColorType) && !verifyType(outputType, PaddingType) && !verifyType(outputType, NumberArrayType) && !verifyType(outputType, ColorArrayType) && !verifyType(outputType, VariableAnchorOffsetCollectionType) && !verifyType(outputType, array(NumberType))) return context.error(`Type ${typeToString(outputType)} is not interpolatable.`);
12018
- return new Interpolate(outputType, operator, interpolation, input, stops, context.key);
12019
- }
12020
- evaluate(ctx) {
12021
- const labels = this.labels;
12022
- const outputs = this.outputs;
12023
- if (labels.length === 1) return outputs[0].evaluate(ctx);
12024
- const value = this.input.evaluate(ctx);
12025
- if (value <= labels[0]) return outputs[0].evaluate(ctx);
12026
- const stopCount = labels.length;
12027
- if (value >= labels[stopCount - 1]) return outputs[stopCount - 1].evaluate(ctx);
12028
- const index = findStopLessThanOrEqualTo(labels, value, this.key);
12029
- const lower = labels[index];
12030
- const upper = labels[index + 1];
12031
- const t = Interpolate.interpolationFactor(this.interpolation, value, lower, upper);
12032
- const outputLower = outputs[index].evaluate(ctx);
12033
- const outputUpper = outputs[index + 1].evaluate(ctx);
12034
- switch (this.operator) {
12035
- case "interpolate": switch (this.type.kind) {
12036
- case "number": return interpolateNumber(outputLower, outputUpper, t);
12037
- case "color": return Color$1.interpolate(outputLower, outputUpper, t);
12038
- case "padding": return Padding.interpolate(outputLower, outputUpper, t);
12039
- case "colorArray": return ColorArray.interpolate(outputLower, outputUpper, t);
12040
- case "numberArray": return NumberArray.interpolate(outputLower, outputUpper, t);
12041
- case "variableAnchorOffsetCollection": return VariableAnchorOffsetCollection.interpolate(outputLower, outputUpper, t, this.key);
12042
- case "array": return interpolateArray(outputLower, outputUpper, t);
12043
- case "projectionDefinition": return ProjectionDefinition.interpolate(outputLower, outputUpper, t);
12044
- }
12045
- case "interpolate-hcl": switch (this.type.kind) {
12046
- case "color": return Color$1.interpolate(outputLower, outputUpper, t, "hcl");
12047
- case "colorArray": return ColorArray.interpolate(outputLower, outputUpper, t, "hcl");
12048
- }
12049
- case "interpolate-lab": switch (this.type.kind) {
12050
- case "color": return Color$1.interpolate(outputLower, outputUpper, t, "lab");
12051
- case "colorArray": return ColorArray.interpolate(outputLower, outputUpper, t, "lab");
12052
- }
12053
- }
12054
- }
12055
- eachChild(fn) {
12056
- fn(this.input);
12057
- for (const expression of this.outputs) fn(expression);
12058
- }
12059
- outputDefined() {
12060
- return this.outputs.every((out) => out.outputDefined());
12061
- }
12062
- };
12063
- /**
12064
- * Returns a ratio that can be used to interpolate between exponential function
12065
- * stops.
12066
- * How it works: Two consecutive stop values define a (scaled and shifted) exponential function `f(x) = a * base^x + b`, where `base` is the user-specified base,
12067
- * and `a` and `b` are constants affording sufficient degrees of freedom to fit
12068
- * the function to the given stops.
12069
- *
12070
- * Here's a bit of algebra that lets us compute `f(x)` directly from the stop
12071
- * values without explicitly solving for `a` and `b`:
12072
- *
12073
- * First stop value: `f(x0) = y0 = a * base^x0 + b`
12074
- * Second stop value: `f(x1) = y1 = a * base^x1 + b`
12075
- * => `y1 - y0 = a(base^x1 - base^x0)`
12076
- * => `a = (y1 - y0)/(base^x1 - base^x0)`
12077
- *
12078
- * Desired value: `f(x) = y = a * base^x + b`
12079
- * => `f(x) = y0 + a * (base^x - base^x0)`
12080
- *
12081
- * From the above, we can replace the `a` in `a * (base^x - base^x0)` and do a
12082
- * little algebra:
12083
- * ```
12084
- * a * (base^x - base^x0) = (y1 - y0)/(base^x1 - base^x0) * (base^x - base^x0)
12085
- * = (y1 - y0) * (base^x - base^x0) / (base^x1 - base^x0)
12086
- * ```
12087
- *
12088
- * If we let `(base^x - base^x0) / (base^x1 base^x0)`, then we have
12089
- * `f(x) = y0 + (y1 - y0) * ratio`. In other words, `ratio` may be treated as
12090
- * an interpolation factor between the two stops' output values.
12091
- *
12092
- * (Note: a slightly different form for `ratio`,
12093
- * `(base^(x-x0) - 1) / (base^(x1-x0) - 1) `, is equivalent, but requires fewer
12094
- * expensive `Math.pow()` operations.)
12095
- *
12096
- * @private
12097
- */
12098
- function exponentialInterpolation(input, base, lowerValue, upperValue) {
12099
- const difference = upperValue - lowerValue;
12100
- const progress = input - lowerValue;
12101
- if (difference === 0) return 0;
12102
- else if (base === 1) return progress / difference;
12103
- else return (Math.pow(base, progress) - 1) / (Math.pow(base, difference) - 1);
12104
- }
12105
- const interpolateFactory = {
12106
- color: Color$1.interpolate,
12107
- number: interpolateNumber,
12108
- padding: Padding.interpolate,
12109
- numberArray: NumberArray.interpolate,
12110
- colorArray: ColorArray.interpolate,
12111
- variableAnchorOffsetCollection: VariableAnchorOffsetCollection.interpolate,
12112
- array: interpolateArray
12113
- };
12114
-
12115
- //#region src/expression/definitions/format.ts
12116
- var FormatExpression = class FormatExpression {
12117
- constructor(sections) {
12118
- this.type = FormattedType;
12119
- this.sections = sections;
12120
- }
12121
- static parse(args, context) {
12122
- if (args.length < 2) return context.error("Expected at least one argument.");
12123
- const firstArg = args[1];
12124
- if (!Array.isArray(firstArg) && typeof firstArg === "object") return context.error("First argument must be an image or text section.");
12125
- const sections = [];
12126
- let nextTokenMayBeObject = false;
12127
- for (let i = 1; i <= args.length - 1; ++i) {
12128
- const arg = args[i];
12129
- if (nextTokenMayBeObject && typeof arg === "object" && !Array.isArray(arg)) {
12130
- nextTokenMayBeObject = false;
12131
- let scale = null;
12132
- if (arg["font-scale"]) {
12133
- scale = context.parse(arg["font-scale"], 1, NumberType);
12134
- if (!scale) return null;
12135
- }
12136
- let font = null;
12137
- if (arg["text-font"]) {
12138
- font = context.parse(arg["text-font"], 1, array(StringType));
12139
- if (!font) return null;
12140
- }
12141
- let textColor = null;
12142
- if (arg["text-color"]) {
12143
- textColor = context.parse(arg["text-color"], 1, ColorType);
12144
- if (!textColor) return null;
12145
- }
12146
- let verticalAlign = null;
12147
- if (arg["vertical-align"]) {
12148
- if (typeof arg["vertical-align"] === "string" && !VERTICAL_ALIGN_OPTIONS.includes(arg["vertical-align"])) return context.error(`'vertical-align' must be one of: 'bottom', 'center', 'top' but found '${arg["vertical-align"]}' instead.`);
12149
- verticalAlign = context.parse(arg["vertical-align"], 1, StringType);
12150
- if (!verticalAlign) return null;
12151
- }
12152
- const lastExpression = sections[sections.length - 1];
12153
- lastExpression.scale = scale;
12154
- lastExpression.font = font;
12155
- lastExpression.textColor = textColor;
12156
- lastExpression.verticalAlign = verticalAlign;
12157
- } else {
12158
- const content = context.parse(args[i], 1, ValueType);
12159
- if (!content) return null;
12160
- const kind = content.type.kind;
12161
- if (kind !== "string" && kind !== "value" && kind !== "null" && kind !== "resolvedImage") return context.error("Formatted text type must be 'string', 'value', 'image' or 'null'.");
12162
- nextTokenMayBeObject = true;
12163
- sections.push({
12164
- content,
12165
- scale: null,
12166
- font: null,
12167
- textColor: null,
12168
- verticalAlign: null
12169
- });
12170
- }
12171
- }
12172
- return new FormatExpression(sections);
12173
- }
12174
- evaluate(ctx) {
12175
- const evaluateSection = (section) => {
12176
- const evaluatedContent = section.content.evaluate(ctx);
12177
- if (typeOf(evaluatedContent) === ResolvedImageType) return new FormattedSection("", evaluatedContent, null, null, null, section.verticalAlign ? section.verticalAlign.evaluate(ctx) : null);
12178
- return new FormattedSection(valueToString(evaluatedContent), null, section.scale ? section.scale.evaluate(ctx) : null, section.font ? section.font.evaluate(ctx).join(",") : null, section.textColor ? section.textColor.evaluate(ctx) : null, section.verticalAlign ? section.verticalAlign.evaluate(ctx) : null);
12179
- };
12180
- return new Formatted(this.sections.map(evaluateSection));
12181
- }
12182
- eachChild(fn) {
12183
- for (const section of this.sections) {
12184
- fn(section.content);
12185
- if (section.scale) fn(section.scale);
12186
- if (section.font) fn(section.font);
12187
- if (section.textColor) fn(section.textColor);
12188
- if (section.verticalAlign) fn(section.verticalAlign);
12189
- }
12190
- }
12191
- outputDefined() {
12192
- return false;
12193
- }
12194
- };
12195
-
12196
- //#region src/util/classify_rings.ts
12197
- /**
12198
- * Classifies an array of rings into polygons with outer rings and holes
12199
- * @param rings - the rings to classify
12200
- * @param maxRings - the maximum number of rings to include in a polygon, use 0 to include all rings
12201
- * @returns an array of polygons with internal rings as holes
12202
- */
12203
- function classifyRings$1(rings, maxRings) {
12204
- if (rings.length <= 1) return [rings];
12205
- const polygons = [];
12206
- let polygon;
12207
- let ccw;
12208
- for (const ring of rings) {
12209
- const area = calculateSignedArea(ring);
12210
- if (area === 0) continue;
12211
- ring.area = Math.abs(area);
12212
- if (ccw === void 0) ccw = area < 0;
12213
- if (ccw === area < 0) {
12214
- if (polygon) polygons.push(polygon);
12215
- polygon = [ring];
12216
- } else polygon.push(ring);
12217
- }
12218
- if (polygon) polygons.push(polygon);
12219
- return polygons;
12220
- }
12221
- /**
12222
- * Returns the signed area for the polygon ring. Positive areas are exterior rings and
12223
- * have a clockwise winding. Negative areas are interior rings and have a counter clockwise
12224
- * ordering.
12225
- *
12226
- * @param ring - Exterior or interior ring
12227
- * @returns Signed area
12228
- */
12229
- function calculateSignedArea(ring) {
12230
- let sum = 0;
12231
- for (let i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
12232
- p1 = ring[i];
12233
- p2 = ring[j];
12234
- sum += (p2.x - p1.x) * (p1.y + p2.y);
12235
- }
12236
- return sum;
12237
- }
12238
-
12239
- //#region src/expression/definitions/assertion.ts
12240
- const types$1 = {
12241
- string: StringType,
12242
- number: NumberType,
12243
- boolean: BooleanType,
12244
- object: ObjectType
12245
- };
12246
- var Assertion = class Assertion {
12247
- constructor(type, args, key) {
12248
- this.type = type;
12249
- this.args = args;
12250
- this.key = key;
12251
- }
12252
- static parse(args, context) {
12253
- if (args.length < 2) return context.error("Expected at least one argument.");
12254
- let i = 1;
12255
- let type;
12256
- const name = args[0];
12257
- if (name === "array") {
12258
- let itemType;
12259
- if (args.length > 2) {
12260
- const type = args[1];
12261
- if (typeof type !== "string" || !(type in types$1) || type === "object") return context.error("The item type argument of \"array\" must be one of string, number, boolean", 1);
12262
- itemType = types$1[type];
12263
- i++;
12264
- } else itemType = ValueType;
12265
- let N;
12266
- if (args.length > 3) {
12267
- if (args[2] !== null && (typeof args[2] !== "number" || args[2] < 0 || args[2] !== Math.floor(args[2]))) return context.error("The length argument to \"array\" must be a positive integer literal", 2);
12268
- N = args[2];
12269
- i++;
12270
- }
12271
- type = array(itemType, N);
12272
- } else {
12273
- if (!types$1[name]) throw new Error(`Types doesn't contain name = ${name}`);
12274
- type = types$1[name];
12275
- }
12276
- const parsed = [];
12277
- for (; i < args.length; i++) {
12278
- const input = context.parse(args[i], i, ValueType);
12279
- if (!input) return null;
12280
- parsed.push(input);
12281
- }
12282
- return new Assertion(type, parsed, context.key);
12283
- }
12284
- evaluate(ctx) {
12285
- for (let i = 0; i < this.args.length; i++) {
12286
- const value = this.args[i].evaluate(ctx);
12287
- if (!checkSubtype(this.type, typeOf(value))) return value;
12288
- else if (i === this.args.length - 1) throw new RuntimeError(`Expected value to be of type ${typeToString(this.type)}, but found ${typeToString(typeOf(value))} instead.`, this.key);
12289
- }
12290
- throw new Error();
12291
- }
12292
- eachChild(fn) {
12293
- this.args.forEach(fn);
12294
- }
12295
- outputDefined() {
12296
- return this.args.every((arg) => arg.outputDefined());
12297
- }
12298
- };
12299
-
12300
- //#region src/expression/definitions/coercion.ts
12301
- const types = {
12302
- "to-boolean": BooleanType,
12303
- "to-color": ColorType,
12304
- "to-number": NumberType,
12305
- "to-string": StringType
12306
- };
12307
- /**
12308
- * Special form for error-coalescing coercion expressions "to-number",
12309
- * "to-color". Since these coercions can fail at runtime, they accept multiple
12310
- * arguments, only evaluating one at a time until one succeeds.
12311
- *
12312
- * @private
12313
- */
12314
- var Coercion = class Coercion {
12315
- constructor(type, args, key) {
12316
- this.type = type;
12317
- this.args = args;
12318
- this.key = key;
12319
- }
12320
- static parse(args, context) {
12321
- if (args.length < 2) return context.error("Expected at least one argument.");
12322
- const name = args[0];
12323
- if (!types[name]) throw new Error(`Can't parse ${name} as it is not part of the known types`);
12324
- if ((name === "to-boolean" || name === "to-string") && args.length !== 2) return context.error("Expected one argument.");
12325
- const type = types[name];
12326
- const parsed = [];
12327
- for (let i = 1; i < args.length; i++) {
12328
- const input = context.parse(args[i], i, ValueType);
12329
- if (!input) return null;
12330
- parsed.push(input);
12331
- }
12332
- return new Coercion(type, parsed, context.key);
11997
+ return new Coercion(type, parsed, context.key);
12333
11998
  }
12334
11999
  evaluate(ctx) {
12335
12000
  switch (this.type.kind) {
@@ -12379,45 +12044,209 @@ var Coercion = class Coercion {
12379
12044
  }
12380
12045
  throw new RuntimeError(`Could not parse colorArray from value '${typeof input === "string" ? input : JSON.stringify(input)}'`, this.key);
12381
12046
  }
12382
- case "variableAnchorOffsetCollection": {
12383
- let input;
12384
- for (const arg of this.args) {
12385
- input = arg.evaluate(ctx);
12386
- const coll = VariableAnchorOffsetCollection.parse(input);
12387
- if (coll) return coll;
12047
+ case "variableAnchorOffsetCollection": {
12048
+ let input;
12049
+ for (const arg of this.args) {
12050
+ input = arg.evaluate(ctx);
12051
+ const coll = VariableAnchorOffsetCollection.parse(input);
12052
+ if (coll) return coll;
12053
+ }
12054
+ throw new RuntimeError(`Could not parse variableAnchorOffsetCollection from value '${typeof input === "string" ? input : JSON.stringify(input)}'`, this.key);
12055
+ }
12056
+ case "number": {
12057
+ let value = null;
12058
+ for (const arg of this.args) {
12059
+ value = arg.evaluate(ctx);
12060
+ if (value === null) return 0;
12061
+ const num = Number(value);
12062
+ if (isNaN(num)) continue;
12063
+ return num;
12064
+ }
12065
+ throw new RuntimeError(`Could not convert ${JSON.stringify(value)} to number.`, this.key);
12066
+ }
12067
+ case "formatted": return Formatted.fromString(valueToString(this.args[0].evaluate(ctx)));
12068
+ case "resolvedImage": return ResolvedImage.fromString(valueToString(this.args[0].evaluate(ctx)));
12069
+ case "projectionDefinition": {
12070
+ const input = this.args[0].evaluate(ctx);
12071
+ if (ProjectionDefinition.parse(input)) return input;
12072
+ throw new RuntimeError(`Could not parse projectionDefinition from value '${typeof input === "string" ? input : JSON.stringify(input)}'`, this.key);
12073
+ }
12074
+ default: return valueToString(this.args[0].evaluate(ctx));
12075
+ }
12076
+ }
12077
+ eachChild(fn) {
12078
+ this.args.forEach(fn);
12079
+ }
12080
+ outputDefined() {
12081
+ return this.args.every((arg) => arg.outputDefined());
12082
+ }
12083
+ };
12084
+
12085
+ // src/expression/evaluation_context.ts
12086
+ const geometryTypes = [
12087
+ "Unknown",
12088
+ "Point",
12089
+ "LineString",
12090
+ "Polygon"
12091
+ ];
12092
+ var EvaluationContext = class {
12093
+ constructor() {
12094
+ this.globals = null;
12095
+ this.feature = null;
12096
+ this.featureState = null;
12097
+ this.formattedSection = null;
12098
+ this._parseColorCache = /* @__PURE__ */ new Map();
12099
+ this.availableImages = null;
12100
+ this.canonical = null;
12101
+ }
12102
+ id() {
12103
+ return this.feature && "id" in this.feature ? this.feature.id : null;
12104
+ }
12105
+ geometryType() {
12106
+ return this.feature ? typeof this.feature.type === "number" ? geometryTypes[this.feature.type] : this.feature.type : null;
12107
+ }
12108
+ geometry() {
12109
+ return this.feature && "geometry" in this.feature ? this.feature.geometry : null;
12110
+ }
12111
+ canonicalID() {
12112
+ return this.canonical;
12113
+ }
12114
+ properties() {
12115
+ return this.feature && this.feature.properties || {};
12116
+ }
12117
+ parseColor(input) {
12118
+ let cached = this._parseColorCache.get(input);
12119
+ if (!cached) {
12120
+ cached = Color$1.parse(input);
12121
+ this._parseColorCache.set(input, cached);
12122
+ }
12123
+ return cached;
12124
+ }
12125
+ };
12126
+
12127
+ // src/expression/parsing_context.ts
12128
+ /**
12129
+ * State associated parsing at a given point in an expression tree.
12130
+ * @private
12131
+ */
12132
+ var ParsingContext = class ParsingContext {
12133
+ constructor(registry, isConstantFunc, path = [], expectedType, scope = new Scope(), errors = []) {
12134
+ this.registry = registry;
12135
+ this.path = path;
12136
+ this.key = path.map((part) => `[${part}]`).join("");
12137
+ this.scope = scope;
12138
+ this.errors = errors;
12139
+ this.expectedType = expectedType;
12140
+ this._isConstant = isConstantFunc;
12141
+ }
12142
+ /**
12143
+ * @param expr the JSON expression to parse
12144
+ * @param index the optional argument index if this expression is an argument of a parent expression that's being parsed
12145
+ * @param options
12146
+ * @param options.omitTypeAnnotations set true to omit inferred type annotations. Caller beware: with this option set, the parsed expression's type will NOT satisfy `expectedType` if it would normally be wrapped in an inferred annotation.
12147
+ * @private
12148
+ */
12149
+ parse(expr, index, expectedType, bindings, options = {}) {
12150
+ if (index != null) return this.concat(index, expectedType, bindings)._parse(expr, options);
12151
+ return this._parse(expr, options);
12152
+ }
12153
+ _parse(expr, options) {
12154
+ if (expr === null || typeof expr === "string" || typeof expr === "boolean" || typeof expr === "number") expr = ["literal", expr];
12155
+ const key = this.key;
12156
+ function annotate(parsed, type, typeAnnotation) {
12157
+ if (typeAnnotation === "assert") return new Assertion(type, [parsed], key);
12158
+ else if (typeAnnotation === "coerce") return new Coercion(type, [parsed], key);
12159
+ else return parsed;
12160
+ }
12161
+ if (Array.isArray(expr)) {
12162
+ if (expr.length === 0) return this.error("Expected an array with at least one element. If you wanted a literal array, use [\"literal\", []].");
12163
+ const op = expr[0];
12164
+ if (typeof op !== "string") {
12165
+ this.error(`Expression name must be a string, but found ${typeof op} instead. If you wanted a literal array, use ["literal", [...]].`, 0);
12166
+ return null;
12167
+ }
12168
+ const Expr = this.registry[op];
12169
+ if (Expr) {
12170
+ let parsed = Expr.parse(expr, this);
12171
+ if (!parsed) return null;
12172
+ if (this.expectedType) {
12173
+ const expected = this.expectedType;
12174
+ const actual = parsed.type;
12175
+ if ((expected.kind === "string" || expected.kind === "number" || expected.kind === "boolean" || expected.kind === "object" || expected.kind === "array") && actual.kind === "value") parsed = annotate(parsed, expected, options.typeAnnotation || "assert");
12176
+ else if ("projectionDefinition" === expected.kind && [
12177
+ "string",
12178
+ "array",
12179
+ "value"
12180
+ ].includes(actual.kind) || [
12181
+ "color",
12182
+ "formatted",
12183
+ "resolvedImage"
12184
+ ].includes(expected.kind) && ["value", "string"].includes(actual.kind) || ["padding", "numberArray"].includes(expected.kind) && [
12185
+ "value",
12186
+ "number",
12187
+ "array"
12188
+ ].includes(actual.kind) || "colorArray" === expected.kind && [
12189
+ "value",
12190
+ "string",
12191
+ "array"
12192
+ ].includes(actual.kind) || "variableAnchorOffsetCollection" === expected.kind && ["value", "array"].includes(actual.kind)) parsed = annotate(parsed, expected, options.typeAnnotation || "coerce");
12193
+ else if (this.checkSubtype(expected, actual)) return null;
12388
12194
  }
12389
- throw new RuntimeError(`Could not parse variableAnchorOffsetCollection from value '${typeof input === "string" ? input : JSON.stringify(input)}'`, this.key);
12390
- }
12391
- case "number": {
12392
- let value = null;
12393
- for (const arg of this.args) {
12394
- value = arg.evaluate(ctx);
12395
- if (value === null) return 0;
12396
- const num = Number(value);
12397
- if (isNaN(num)) continue;
12398
- return num;
12195
+ if (!(parsed instanceof Literal) && parsed.type.kind !== "resolvedImage" && this._isConstant(parsed)) {
12196
+ const ec = new EvaluationContext();
12197
+ try {
12198
+ parsed = new Literal(parsed.type, parsed.evaluate(ec));
12199
+ } catch (e) {
12200
+ this.error(e.message);
12201
+ return null;
12202
+ }
12399
12203
  }
12400
- throw new RuntimeError(`Could not convert ${JSON.stringify(value)} to number.`, this.key);
12401
- }
12402
- case "formatted": return Formatted.fromString(valueToString(this.args[0].evaluate(ctx)));
12403
- case "resolvedImage": return ResolvedImage.fromString(valueToString(this.args[0].evaluate(ctx)));
12404
- case "projectionDefinition": {
12405
- const input = this.args[0].evaluate(ctx);
12406
- if (ProjectionDefinition.parse(input)) return input;
12407
- throw new RuntimeError(`Could not parse projectionDefinition from value '${typeof input === "string" ? input : JSON.stringify(input)}'`, this.key);
12204
+ return parsed;
12408
12205
  }
12409
- default: return valueToString(this.args[0].evaluate(ctx));
12410
- }
12206
+ return this.error(`Unknown expression "${op}". If you wanted a literal array, use ["literal", [...]].`, 0);
12207
+ } else if (typeof expr === "undefined") return this.error("'undefined' value invalid. Use null instead.");
12208
+ else if (typeof expr === "object") return this.error("Bare objects invalid. Use [\"literal\", {...}] instead.");
12209
+ else return this.error(`Expected an array, but found ${typeof expr} instead.`);
12411
12210
  }
12412
- eachChild(fn) {
12413
- this.args.forEach(fn);
12211
+ /**
12212
+ * Returns a copy of this context suitable for parsing the subexpression at
12213
+ * index `index`, optionally appending to 'let' binding map.
12214
+ *
12215
+ * Note that `errors` property, intended for collecting errors while
12216
+ * parsing, is copied by reference rather than cloned.
12217
+ * @private
12218
+ */
12219
+ concat(index, expectedType, bindings) {
12220
+ const path = typeof index === "number" ? this.path.concat(index) : this.path;
12221
+ const scope = bindings ? this.scope.concat(bindings) : this.scope;
12222
+ return new ParsingContext(this.registry, this._isConstant, path, expectedType || null, scope, this.errors);
12414
12223
  }
12415
- outputDefined() {
12416
- return this.args.every((arg) => arg.outputDefined());
12224
+ /**
12225
+ * Push a parsing (or type checking) error into the `this.errors`
12226
+ * @param error The message
12227
+ * @param keys Optionally specify the source of the error at a child
12228
+ * of the current expression at `this.key`.
12229
+ * @private
12230
+ */
12231
+ error(error, ...keys) {
12232
+ const key = `${this.key}${keys.map((k) => `[${k}]`).join("")}`;
12233
+ this.errors.push(new ExpressionParsingError(key, error));
12234
+ }
12235
+ /**
12236
+ * Returns null if `t` is a subtype of `expected`; otherwise returns an
12237
+ * error message and also pushes it to `this.errors`.
12238
+ * @param expected The expected type
12239
+ * @param t The actual type
12240
+ * @returns null if `t` is a subtype of `expected`; otherwise returns an error message
12241
+ */
12242
+ checkSubtype(expected, t) {
12243
+ const error = checkSubtype(expected, t);
12244
+ if (error) this.error(error);
12245
+ return error;
12417
12246
  }
12418
12247
  };
12419
12248
 
12420
- //#region src/expression/definitions/let.ts
12249
+ // src/expression/definitions/let.ts
12421
12250
  var Let = class Let {
12422
12251
  constructor(bindings, result) {
12423
12252
  this.type = result.type;
@@ -12451,7 +12280,7 @@ var Let = class Let {
12451
12280
  }
12452
12281
  };
12453
12282
 
12454
- //#region src/expression/definitions/var.ts
12283
+ // src/expression/definitions/var.ts
12455
12284
  var Var = class Var {
12456
12285
  constructor(name, boundExpression) {
12457
12286
  this.type = boundExpression.type;
@@ -12473,7 +12302,7 @@ var Var = class Var {
12473
12302
  }
12474
12303
  };
12475
12304
 
12476
- //#region src/expression/definitions/at.ts
12305
+ // src/expression/definitions/at.ts
12477
12306
  var At = class At {
12478
12307
  constructor(type, index, input, key) {
12479
12308
  this.type = type;
@@ -12506,7 +12335,7 @@ var At = class At {
12506
12335
  }
12507
12336
  };
12508
12337
 
12509
- //#region src/expression/definitions/in.ts
12338
+ // src/expression/definitions/in.ts
12510
12339
  var In = class In {
12511
12340
  constructor(needle, haystack, key) {
12512
12341
  this.needle = needle;
@@ -12550,7 +12379,7 @@ var In = class In {
12550
12379
  }
12551
12380
  };
12552
12381
 
12553
- //#region src/expression/definitions/index_of.ts
12382
+ // src/expression/definitions/index_of.ts
12554
12383
  var IndexOf = class IndexOf {
12555
12384
  constructor(needle, haystack, key, fromIndex) {
12556
12385
  this.needle = needle;
@@ -12605,7 +12434,7 @@ var IndexOf = class IndexOf {
12605
12434
  }
12606
12435
  };
12607
12436
 
12608
- //#region src/expression/definitions/match.ts
12437
+ // src/expression/definitions/match.ts
12609
12438
  var Match = class Match {
12610
12439
  constructor(inputType, outputType, input, cases, outputs, otherwise) {
12611
12440
  this.inputType = inputType;
@@ -12664,7 +12493,7 @@ var Match = class Match {
12664
12493
  }
12665
12494
  };
12666
12495
 
12667
- //#region src/expression/definitions/case.ts
12496
+ // src/expression/definitions/case.ts
12668
12497
  var Case = class Case {
12669
12498
  constructor(type, branches, otherwise) {
12670
12499
  this.type = type;
@@ -12706,51 +12535,324 @@ var Case = class Case {
12706
12535
  }
12707
12536
  };
12708
12537
 
12709
- //#region src/expression/definitions/slice.ts
12710
- var Slice = class Slice {
12711
- constructor(type, input, beginIndex, key, endIndex) {
12538
+ // src/expression/definitions/slice.ts
12539
+ var Slice = class Slice {
12540
+ constructor(type, input, beginIndex, key, endIndex) {
12541
+ this.type = type;
12542
+ this.input = input;
12543
+ this.beginIndex = beginIndex;
12544
+ this.key = key;
12545
+ this.endIndex = endIndex;
12546
+ }
12547
+ static parse(args, context) {
12548
+ if (args.length <= 2 || args.length >= 5) return context.error(`Expected 2 or 3 arguments, but found ${args.length - 1} instead.`);
12549
+ const input = context.parse(args[1], 1, ValueType);
12550
+ const beginIndex = context.parse(args[2], 2, NumberType);
12551
+ if (!input || !beginIndex) return null;
12552
+ if (!isValidType(input.type, [
12553
+ array(ValueType),
12554
+ StringType,
12555
+ ValueType
12556
+ ])) return context.error(`Expected first argument to be of type array or string, but found ${typeToString(input.type)} instead`);
12557
+ if (args.length === 4) {
12558
+ const endIndex = context.parse(args[3], 3, NumberType);
12559
+ if (!endIndex) return null;
12560
+ return new Slice(input.type, input, beginIndex, context.key, endIndex);
12561
+ } else return new Slice(input.type, input, beginIndex, context.key);
12562
+ }
12563
+ evaluate(ctx) {
12564
+ const input = this.input.evaluate(ctx);
12565
+ const beginIndex = this.beginIndex.evaluate(ctx);
12566
+ let endIndex;
12567
+ if (this.endIndex) endIndex = this.endIndex.evaluate(ctx);
12568
+ if (isValidNativeType(input, ["string"])) return [...input].slice(beginIndex, endIndex).join("");
12569
+ else if (isValidNativeType(input, ["array"])) return input.slice(beginIndex, endIndex);
12570
+ else throw new RuntimeError(`Expected first argument to be of type array or string, but found ${typeToString(typeOf(input))} instead.`, this.key);
12571
+ }
12572
+ eachChild(fn) {
12573
+ fn(this.input);
12574
+ fn(this.beginIndex);
12575
+ if (this.endIndex) fn(this.endIndex);
12576
+ }
12577
+ outputDefined() {
12578
+ return false;
12579
+ }
12580
+ };
12581
+
12582
+ // src/expression/stops.ts
12583
+ /**
12584
+ * Returns the index of the last stop <= input, or 0 if it doesn't exist.
12585
+ * @private
12586
+ */
12587
+ function findStopLessThanOrEqualTo(stops, input, key) {
12588
+ const lastIndex = stops.length - 1;
12589
+ let lowerIndex = 0;
12590
+ let upperIndex = lastIndex;
12591
+ let currentIndex = 0;
12592
+ let currentValue, nextValue;
12593
+ while (lowerIndex <= upperIndex) {
12594
+ currentIndex = Math.floor((lowerIndex + upperIndex) / 2);
12595
+ currentValue = stops[currentIndex];
12596
+ nextValue = stops[currentIndex + 1];
12597
+ if (currentValue <= input) {
12598
+ if (currentIndex === lastIndex || input < nextValue) return currentIndex;
12599
+ lowerIndex = currentIndex + 1;
12600
+ } else if (currentValue > input) upperIndex = currentIndex - 1;
12601
+ else throw new RuntimeError("Input is not a number.", key);
12602
+ }
12603
+ return 0;
12604
+ }
12605
+
12606
+ // src/expression/definitions/step.ts
12607
+ var Step = class Step {
12608
+ constructor(type, input, stops, key) {
12609
+ this.type = type;
12610
+ this.input = input;
12611
+ this.key = key;
12612
+ this.labels = [];
12613
+ this.outputs = [];
12614
+ for (const [label, expression] of stops) {
12615
+ this.labels.push(label);
12616
+ this.outputs.push(expression);
12617
+ }
12618
+ }
12619
+ static parse(args, context) {
12620
+ if (args.length - 1 < 4) return context.error(`Expected at least 4 arguments, but found only ${args.length - 1}.`);
12621
+ if ((args.length - 1) % 2 !== 0) return context.error("Expected an even number of arguments.");
12622
+ const input = context.parse(args[1], 1, NumberType);
12623
+ if (!input) return null;
12624
+ const stops = [];
12625
+ let outputType = null;
12626
+ if (context.expectedType && context.expectedType.kind !== "value") outputType = context.expectedType;
12627
+ for (let i = 1; i < args.length; i += 2) {
12628
+ const label = i === 1 ? -Infinity : args[i];
12629
+ const value = args[i + 1];
12630
+ const labelKey = i;
12631
+ const valueKey = i + 1;
12632
+ if (typeof label !== "number") return context.error("Input/output pairs for \"step\" expressions must be defined using literal numeric values (not computed expressions) for the input values.", labelKey);
12633
+ if (stops.length && stops[stops.length - 1][0] >= label) return context.error("Input/output pairs for \"step\" expressions must be arranged with input values in strictly ascending order.", labelKey);
12634
+ const parsed = context.parse(value, valueKey, outputType);
12635
+ if (!parsed) return null;
12636
+ outputType = outputType || parsed.type;
12637
+ stops.push([label, parsed]);
12638
+ }
12639
+ return new Step(outputType, input, stops, context.key);
12640
+ }
12641
+ evaluate(ctx) {
12642
+ const labels = this.labels;
12643
+ const outputs = this.outputs;
12644
+ if (labels.length === 1) return outputs[0].evaluate(ctx);
12645
+ const value = this.input.evaluate(ctx);
12646
+ if (value <= labels[0]) return outputs[0].evaluate(ctx);
12647
+ const stopCount = labels.length;
12648
+ if (value >= labels[stopCount - 1]) return outputs[stopCount - 1].evaluate(ctx);
12649
+ return outputs[findStopLessThanOrEqualTo(labels, value, this.key)].evaluate(ctx);
12650
+ }
12651
+ eachChild(fn) {
12652
+ fn(this.input);
12653
+ for (const expression of this.outputs) fn(expression);
12654
+ }
12655
+ outputDefined() {
12656
+ return this.outputs.every((out) => out.outputDefined());
12657
+ }
12658
+ };
12659
+
12660
+ // node_modules/@mapbox/unitbezier/index.js
12661
+ function unitBezier(p1x, p1y, p2x, p2y) {
12662
+ const cx = 3 * p1x;
12663
+ const bx = 3 * (p2x - p1x) - cx;
12664
+ const ax = 1 - cx - bx;
12665
+ const cy = 3 * p1y;
12666
+ const by = 3 * (p2y - p1y) - cy;
12667
+ const ay = 1 - cy - by;
12668
+ return function solve(x, epsilon = 1e-6) {
12669
+ if (x <= 0) return 0;
12670
+ if (x >= 1) return 1;
12671
+ let t = x;
12672
+ for (let i = 0; i < 8; i++) {
12673
+ const x2 = ((ax * t + bx) * t + cx) * t - x;
12674
+ if (Math.abs(x2) < epsilon) return ((ay * t + by) * t + cy) * t;
12675
+ const d2 = (3 * ax * t + 2 * bx) * t + cx;
12676
+ if (Math.abs(d2) < 1e-6) break;
12677
+ t -= x2 / d2;
12678
+ }
12679
+ let t0 = 0;
12680
+ let t1 = 1;
12681
+ t = x;
12682
+ for (let i = 0; i < 20; i++) {
12683
+ const x2 = ((ax * t + bx) * t + cx) * t;
12684
+ if (Math.abs(x2 - x) < epsilon) break;
12685
+ if (x > x2) t0 = t;
12686
+ else t1 = t;
12687
+ t = (t0 + t1) * .5;
12688
+ }
12689
+ return ((ay * t + by) * t + cy) * t;
12690
+ };
12691
+ }
12692
+
12693
+ // src/expression/definitions/interpolate.ts
12694
+ var Interpolate = class Interpolate {
12695
+ constructor(type, operator, interpolation, input, stops, key) {
12712
12696
  this.type = type;
12697
+ this.operator = operator;
12698
+ this.interpolation = interpolation;
12713
12699
  this.input = input;
12714
- this.beginIndex = beginIndex;
12715
12700
  this.key = key;
12716
- this.endIndex = endIndex;
12701
+ this.labels = [];
12702
+ this.outputs = [];
12703
+ for (const [label, expression] of stops) {
12704
+ this.labels.push(label);
12705
+ this.outputs.push(expression);
12706
+ }
12707
+ }
12708
+ static interpolationFactor(interpolation, input, lower, upper) {
12709
+ let t = 0;
12710
+ if (interpolation.name === "exponential") t = exponentialInterpolation(input, interpolation.base, lower, upper);
12711
+ else if (interpolation.name === "linear") t = exponentialInterpolation(input, 1, lower, upper);
12712
+ else if (interpolation.name === "cubic-bezier") {
12713
+ const c = interpolation.controlPoints;
12714
+ t = unitBezier(c[0], c[1], c[2], c[3])(exponentialInterpolation(input, 1, lower, upper));
12715
+ }
12716
+ return t;
12717
12717
  }
12718
12718
  static parse(args, context) {
12719
- if (args.length <= 2 || args.length >= 5) return context.error(`Expected 2 or 3 arguments, but found ${args.length - 1} instead.`);
12720
- const input = context.parse(args[1], 1, ValueType);
12721
- const beginIndex = context.parse(args[2], 2, NumberType);
12722
- if (!input || !beginIndex) return null;
12723
- if (!isValidType(input.type, [
12724
- array(ValueType),
12725
- StringType,
12726
- ValueType
12727
- ])) return context.error(`Expected first argument to be of type array or string, but found ${typeToString(input.type)} instead`);
12728
- if (args.length === 4) {
12729
- const endIndex = context.parse(args[3], 3, NumberType);
12730
- if (!endIndex) return null;
12731
- return new Slice(input.type, input, beginIndex, context.key, endIndex);
12732
- } else return new Slice(input.type, input, beginIndex, context.key);
12719
+ let [operator, interpolation, input, ...rest] = args;
12720
+ if (!Array.isArray(interpolation) || interpolation.length === 0) return context.error("Expected an interpolation type expression.", 1);
12721
+ if (interpolation[0] === "linear") interpolation = { name: "linear" };
12722
+ else if (interpolation[0] === "exponential") {
12723
+ const base = interpolation[1];
12724
+ if (typeof base !== "number") return context.error("Exponential interpolation requires a numeric base.", 1, 1);
12725
+ interpolation = {
12726
+ name: "exponential",
12727
+ base
12728
+ };
12729
+ } else if (interpolation[0] === "cubic-bezier") {
12730
+ const controlPoints = interpolation.slice(1);
12731
+ if (controlPoints.length !== 4 || controlPoints.some((t) => typeof t !== "number" || t < 0 || t > 1)) return context.error("Cubic bezier interpolation requires four numeric arguments with values between 0 and 1.", 1);
12732
+ interpolation = {
12733
+ name: "cubic-bezier",
12734
+ controlPoints
12735
+ };
12736
+ } else return context.error(`Unknown interpolation type ${String(interpolation[0])}`, 1, 0);
12737
+ if (args.length - 1 < 4) return context.error(`Expected at least 4 arguments, but found only ${args.length - 1}.`);
12738
+ if ((args.length - 1) % 2 !== 0) return context.error("Expected an even number of arguments.");
12739
+ input = context.parse(input, 2, NumberType);
12740
+ if (!input) return null;
12741
+ const stops = [];
12742
+ let outputType = null;
12743
+ if ((operator === "interpolate-hcl" || operator === "interpolate-lab") && context.expectedType != ColorArrayType) outputType = ColorType;
12744
+ else if (context.expectedType && context.expectedType.kind !== "value") outputType = context.expectedType;
12745
+ for (let i = 0; i < rest.length; i += 2) {
12746
+ const label = rest[i];
12747
+ const value = rest[i + 1];
12748
+ const labelKey = i + 3;
12749
+ const valueKey = i + 4;
12750
+ if (typeof label !== "number") return context.error("Input/output pairs for \"interpolate\" expressions must be defined using literal numeric values (not computed expressions) for the input values.", labelKey);
12751
+ if (stops.length && stops[stops.length - 1][0] >= label) return context.error("Input/output pairs for \"interpolate\" expressions must be arranged with input values in strictly ascending order.", labelKey);
12752
+ const parsed = context.parse(value, valueKey, outputType);
12753
+ if (!parsed) return null;
12754
+ outputType = outputType || parsed.type;
12755
+ stops.push([label, parsed]);
12756
+ }
12757
+ if (!verifyType(outputType, NumberType) && !verifyType(outputType, ProjectionDefinitionType) && !verifyType(outputType, ColorType) && !verifyType(outputType, PaddingType) && !verifyType(outputType, NumberArrayType) && !verifyType(outputType, ColorArrayType) && !verifyType(outputType, VariableAnchorOffsetCollectionType) && !verifyType(outputType, array(NumberType))) return context.error(`Type ${typeToString(outputType)} is not interpolatable.`);
12758
+ return new Interpolate(outputType, operator, interpolation, input, stops, context.key);
12733
12759
  }
12734
12760
  evaluate(ctx) {
12735
- const input = this.input.evaluate(ctx);
12736
- const beginIndex = this.beginIndex.evaluate(ctx);
12737
- let endIndex;
12738
- if (this.endIndex) endIndex = this.endIndex.evaluate(ctx);
12739
- if (isValidNativeType(input, ["string"])) return [...input].slice(beginIndex, endIndex).join("");
12740
- else if (isValidNativeType(input, ["array"])) return input.slice(beginIndex, endIndex);
12741
- else throw new RuntimeError(`Expected first argument to be of type array or string, but found ${typeToString(typeOf(input))} instead.`, this.key);
12761
+ const labels = this.labels;
12762
+ const outputs = this.outputs;
12763
+ if (labels.length === 1) return outputs[0].evaluate(ctx);
12764
+ const value = this.input.evaluate(ctx);
12765
+ if (value <= labels[0]) return outputs[0].evaluate(ctx);
12766
+ const stopCount = labels.length;
12767
+ if (value >= labels[stopCount - 1]) return outputs[stopCount - 1].evaluate(ctx);
12768
+ const index = findStopLessThanOrEqualTo(labels, value, this.key);
12769
+ const lower = labels[index];
12770
+ const upper = labels[index + 1];
12771
+ const t = Interpolate.interpolationFactor(this.interpolation, value, lower, upper);
12772
+ const outputLower = outputs[index].evaluate(ctx);
12773
+ const outputUpper = outputs[index + 1].evaluate(ctx);
12774
+ switch (this.operator) {
12775
+ case "interpolate": switch (this.type.kind) {
12776
+ case "number": return interpolateNumber(outputLower, outputUpper, t);
12777
+ case "color": return Color$1.interpolate(outputLower, outputUpper, t);
12778
+ case "padding": return Padding.interpolate(outputLower, outputUpper, t);
12779
+ case "colorArray": return ColorArray.interpolate(outputLower, outputUpper, t);
12780
+ case "numberArray": return NumberArray.interpolate(outputLower, outputUpper, t);
12781
+ case "variableAnchorOffsetCollection": return VariableAnchorOffsetCollection.interpolate(outputLower, outputUpper, t, this.key);
12782
+ case "array": return interpolateArray(outputLower, outputUpper, t);
12783
+ case "projectionDefinition": return ProjectionDefinition.interpolate(outputLower, outputUpper, t);
12784
+ }
12785
+ case "interpolate-hcl": switch (this.type.kind) {
12786
+ case "color": return Color$1.interpolate(outputLower, outputUpper, t, "hcl");
12787
+ case "colorArray": return ColorArray.interpolate(outputLower, outputUpper, t, "hcl");
12788
+ }
12789
+ case "interpolate-lab": switch (this.type.kind) {
12790
+ case "color": return Color$1.interpolate(outputLower, outputUpper, t, "lab");
12791
+ case "colorArray": return ColorArray.interpolate(outputLower, outputUpper, t, "lab");
12792
+ }
12793
+ }
12742
12794
  }
12743
12795
  eachChild(fn) {
12744
12796
  fn(this.input);
12745
- fn(this.beginIndex);
12746
- if (this.endIndex) fn(this.endIndex);
12797
+ for (const expression of this.outputs) fn(expression);
12747
12798
  }
12748
12799
  outputDefined() {
12749
- return false;
12800
+ return this.outputs.every((out) => out.outputDefined());
12750
12801
  }
12751
12802
  };
12803
+ /**
12804
+ * Returns a ratio that can be used to interpolate between exponential function
12805
+ * stops.
12806
+ * How it works: Two consecutive stop values define a (scaled and shifted) exponential function `f(x) = a * base^x + b`, where `base` is the user-specified base,
12807
+ * and `a` and `b` are constants affording sufficient degrees of freedom to fit
12808
+ * the function to the given stops.
12809
+ *
12810
+ * Here's a bit of algebra that lets us compute `f(x)` directly from the stop
12811
+ * values without explicitly solving for `a` and `b`:
12812
+ *
12813
+ * First stop value: `f(x0) = y0 = a * base^x0 + b`
12814
+ * Second stop value: `f(x1) = y1 = a * base^x1 + b`
12815
+ * => `y1 - y0 = a(base^x1 - base^x0)`
12816
+ * => `a = (y1 - y0)/(base^x1 - base^x0)`
12817
+ *
12818
+ * Desired value: `f(x) = y = a * base^x + b`
12819
+ * => `f(x) = y0 + a * (base^x - base^x0)`
12820
+ *
12821
+ * From the above, we can replace the `a` in `a * (base^x - base^x0)` and do a
12822
+ * little algebra:
12823
+ * ```
12824
+ * a * (base^x - base^x0) = (y1 - y0)/(base^x1 - base^x0) * (base^x - base^x0)
12825
+ * = (y1 - y0) * (base^x - base^x0) / (base^x1 - base^x0)
12826
+ * ```
12827
+ *
12828
+ * If we let `(base^x - base^x0) / (base^x1 base^x0)`, then we have
12829
+ * `f(x) = y0 + (y1 - y0) * ratio`. In other words, `ratio` may be treated as
12830
+ * an interpolation factor between the two stops' output values.
12831
+ *
12832
+ * (Note: a slightly different form for `ratio`,
12833
+ * `(base^(x-x0) - 1) / (base^(x1-x0) - 1) `, is equivalent, but requires fewer
12834
+ * expensive `Math.pow()` operations.)
12835
+ *
12836
+ * @private
12837
+ */
12838
+ function exponentialInterpolation(input, base, lowerValue, upperValue) {
12839
+ const difference = upperValue - lowerValue;
12840
+ const progress = input - lowerValue;
12841
+ if (difference === 0) return 0;
12842
+ else if (base === 1) return progress / difference;
12843
+ else return (Math.pow(base, progress) - 1) / (Math.pow(base, difference) - 1);
12844
+ }
12845
+ const interpolateFactory = {
12846
+ color: Color$1.interpolate,
12847
+ number: interpolateNumber,
12848
+ padding: Padding.interpolate,
12849
+ numberArray: NumberArray.interpolate,
12850
+ colorArray: ColorArray.interpolate,
12851
+ variableAnchorOffsetCollection: VariableAnchorOffsetCollection.interpolate,
12852
+ array: interpolateArray
12853
+ };
12752
12854
 
12753
- //#region src/expression/definitions/coalesce.ts
12855
+ // src/expression/definitions/coalesce.ts
12754
12856
  var Coalesce = class Coalesce {
12755
12857
  constructor(type, args) {
12756
12858
  this.type = type;
@@ -12795,7 +12897,7 @@ var Coalesce = class Coalesce {
12795
12897
  }
12796
12898
  };
12797
12899
 
12798
- //#region src/expression/definitions/comparison.ts
12900
+ // src/expression/definitions/comparison.ts
12799
12901
  function isComparableType(op, type) {
12800
12902
  if (op === "==" || op === "!=") return type.kind === "boolean" || type.kind === "string" || type.kind === "number" || type.kind === "null" || type.kind === "value";
12801
12903
  else return type.kind === "string" || type.kind === "number" || type.kind === "value";
@@ -12918,7 +13020,7 @@ const GreaterThan = makeComparison(">", gt, gtCollate);
12918
13020
  const LessThanOrEqual = makeComparison("<=", lteq, lteqCollate);
12919
13021
  const GreaterThanOrEqual = makeComparison(">=", gteq, gteqCollate);
12920
13022
 
12921
- //#region src/expression/definitions/collator.ts
13023
+ // src/expression/definitions/collator.ts
12922
13024
  var CollatorExpression = class CollatorExpression {
12923
13025
  constructor(caseSensitive, diacriticSensitive, locale) {
12924
13026
  this.type = CollatorType;
@@ -12954,7 +13056,7 @@ var CollatorExpression = class CollatorExpression {
12954
13056
  }
12955
13057
  };
12956
13058
 
12957
- //#region src/expression/definitions/number_format.ts
13059
+ // src/expression/definitions/number_format.ts
12958
13060
  var NumberFormat = class NumberFormat {
12959
13061
  constructor(number, locale, currency, unit, minFractionDigits, maxFractionDigits) {
12960
13062
  this.type = StringType;
@@ -13021,7 +13123,88 @@ var NumberFormat = class NumberFormat {
13021
13123
  }
13022
13124
  };
13023
13125
 
13024
- //#region src/expression/definitions/image.ts
13126
+ // src/expression/definitions/format.ts
13127
+ var FormatExpression = class FormatExpression {
13128
+ constructor(sections) {
13129
+ this.type = FormattedType;
13130
+ this.sections = sections;
13131
+ }
13132
+ static parse(args, context) {
13133
+ if (args.length < 2) return context.error("Expected at least one argument.");
13134
+ const firstArg = args[1];
13135
+ if (!Array.isArray(firstArg) && typeof firstArg === "object") return context.error("First argument must be an image or text section.");
13136
+ const sections = [];
13137
+ let nextTokenMayBeObject = false;
13138
+ for (let i = 1; i <= args.length - 1; ++i) {
13139
+ const arg = args[i];
13140
+ if (nextTokenMayBeObject && typeof arg === "object" && !Array.isArray(arg)) {
13141
+ nextTokenMayBeObject = false;
13142
+ let scale = null;
13143
+ if (arg["font-scale"]) {
13144
+ scale = context.parse(arg["font-scale"], 1, NumberType);
13145
+ if (!scale) return null;
13146
+ }
13147
+ let font = null;
13148
+ if (arg["text-font"]) {
13149
+ font = context.parse(arg["text-font"], 1, array(StringType));
13150
+ if (!font) return null;
13151
+ }
13152
+ let textColor = null;
13153
+ if (arg["text-color"]) {
13154
+ textColor = context.parse(arg["text-color"], 1, ColorType);
13155
+ if (!textColor) return null;
13156
+ }
13157
+ let verticalAlign = null;
13158
+ if (arg["vertical-align"]) {
13159
+ if (typeof arg["vertical-align"] === "string" && !VERTICAL_ALIGN_OPTIONS.includes(arg["vertical-align"])) return context.error(`'vertical-align' must be one of: 'bottom', 'center', 'top' but found '${arg["vertical-align"]}' instead.`);
13160
+ verticalAlign = context.parse(arg["vertical-align"], 1, StringType);
13161
+ if (!verticalAlign) return null;
13162
+ }
13163
+ const lastExpression = sections[sections.length - 1];
13164
+ lastExpression.scale = scale;
13165
+ lastExpression.font = font;
13166
+ lastExpression.textColor = textColor;
13167
+ lastExpression.verticalAlign = verticalAlign;
13168
+ } else {
13169
+ const content = context.parse(args[i], 1, ValueType);
13170
+ if (!content) return null;
13171
+ const kind = content.type.kind;
13172
+ if (kind !== "string" && kind !== "value" && kind !== "null" && kind !== "resolvedImage") return context.error("Formatted text type must be 'string', 'value', 'image' or 'null'.");
13173
+ nextTokenMayBeObject = true;
13174
+ sections.push({
13175
+ content,
13176
+ scale: null,
13177
+ font: null,
13178
+ textColor: null,
13179
+ verticalAlign: null
13180
+ });
13181
+ }
13182
+ }
13183
+ return new FormatExpression(sections);
13184
+ }
13185
+ evaluate(ctx) {
13186
+ const evaluateSection = (section) => {
13187
+ const evaluatedContent = section.content.evaluate(ctx);
13188
+ if (typeOf(evaluatedContent) === ResolvedImageType) return new FormattedSection("", evaluatedContent, null, null, null, section.verticalAlign ? section.verticalAlign.evaluate(ctx) : null);
13189
+ return new FormattedSection(valueToString(evaluatedContent), null, section.scale ? section.scale.evaluate(ctx) : null, section.font ? section.font.evaluate(ctx).join(",") : null, section.textColor ? section.textColor.evaluate(ctx) : null, section.verticalAlign ? section.verticalAlign.evaluate(ctx) : null);
13190
+ };
13191
+ return new Formatted(this.sections.map(evaluateSection));
13192
+ }
13193
+ eachChild(fn) {
13194
+ for (const section of this.sections) {
13195
+ fn(section.content);
13196
+ if (section.scale) fn(section.scale);
13197
+ if (section.font) fn(section.font);
13198
+ if (section.textColor) fn(section.textColor);
13199
+ if (section.verticalAlign) fn(section.verticalAlign);
13200
+ }
13201
+ }
13202
+ outputDefined() {
13203
+ return false;
13204
+ }
13205
+ };
13206
+
13207
+ // src/expression/definitions/image.ts
13025
13208
  var ImageExpression = class ImageExpression {
13026
13209
  constructor(input) {
13027
13210
  this.type = ResolvedImageType;
@@ -13047,7 +13230,7 @@ var ImageExpression = class ImageExpression {
13047
13230
  }
13048
13231
  };
13049
13232
 
13050
- //#region src/expression/definitions/length.ts
13233
+ // src/expression/definitions/length.ts
13051
13234
  var Length = class Length {
13052
13235
  constructor(input, key) {
13053
13236
  this.input = input;
@@ -13075,7 +13258,7 @@ var Length = class Length {
13075
13258
  }
13076
13259
  };
13077
13260
 
13078
- //#region src/util/geometry_util.ts
13261
+ // src/util/geometry_util.ts
13079
13262
  const EXTENT = 8192;
13080
13263
  function getTileCoordinates(p, canonical) {
13081
13264
  const x = mercatorXfromLng(p[0]);
@@ -13171,7 +13354,7 @@ function twoSided(p1, p2, q1, q2) {
13171
13354
  return false;
13172
13355
  }
13173
13356
 
13174
- //#region src/expression/definitions/within.ts
13357
+ // src/expression/definitions/within.ts
13175
13358
  function getTilePolygon(coordinates, bbox, canonical) {
13176
13359
  const polygon = [];
13177
13360
  for (let i = 0; i < coordinates.length; i++) {
@@ -13333,7 +13516,7 @@ var Within = class Within {
13333
13516
  }
13334
13517
  };
13335
13518
 
13336
- //#region node_modules/tinyqueue/index.js
13519
+ // node_modules/tinyqueue/index.js
13337
13520
  var TinyQueue = class {
13338
13521
  constructor(data = [], compare = (a, b) => a < b ? -1 : a > b ? 1 : 0) {
13339
13522
  this.data = data;
@@ -13386,7 +13569,50 @@ var TinyQueue = class {
13386
13569
  }
13387
13570
  };
13388
13571
 
13389
- //#region src/util/cheap_ruler.ts
13572
+ // src/util/classify_rings.ts
13573
+ /**
13574
+ * Classifies an array of rings into polygons with outer rings and holes
13575
+ * @param rings - the rings to classify
13576
+ * @param maxRings - the maximum number of rings to include in a polygon, use 0 to include all rings
13577
+ * @returns an array of polygons with internal rings as holes
13578
+ */
13579
+ function classifyRings$1(rings, maxRings) {
13580
+ if (rings.length <= 1) return [rings];
13581
+ const polygons = [];
13582
+ let polygon;
13583
+ let ccw;
13584
+ for (const ring of rings) {
13585
+ const area = calculateSignedArea(ring);
13586
+ if (area === 0) continue;
13587
+ ring.area = Math.abs(area);
13588
+ if (ccw === void 0) ccw = area < 0;
13589
+ if (ccw === area < 0) {
13590
+ if (polygon) polygons.push(polygon);
13591
+ polygon = [ring];
13592
+ } else polygon.push(ring);
13593
+ }
13594
+ if (polygon) polygons.push(polygon);
13595
+ return polygons;
13596
+ }
13597
+ /**
13598
+ * Returns the signed area for the polygon ring. Positive areas are exterior rings and
13599
+ * have a clockwise winding. Negative areas are interior rings and have a counter clockwise
13600
+ * ordering.
13601
+ *
13602
+ * @param ring - Exterior or interior ring
13603
+ * @returns Signed area
13604
+ */
13605
+ function calculateSignedArea(ring) {
13606
+ let sum = 0;
13607
+ for (let i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
13608
+ p1 = ring[i];
13609
+ p2 = ring[j];
13610
+ sum += (p2.x - p1.x) * (p1.y + p2.y);
13611
+ }
13612
+ return sum;
13613
+ }
13614
+
13615
+ // src/util/cheap_ruler.ts
13390
13616
  const RE = 6378.137;
13391
13617
  const E2 = .0066943799901413165;
13392
13618
  const RAD = Math.PI / 180;
@@ -13469,7 +13695,7 @@ var CheapRuler = class {
13469
13695
  }
13470
13696
  };
13471
13697
 
13472
- //#region src/expression/definitions/distance.ts
13698
+ // src/expression/definitions/distance.ts
13473
13699
  const MinPointsSize = 100;
13474
13700
  const MinLinePointsSize = 50;
13475
13701
  function compareDistPair(a, b) {
@@ -13837,7 +14063,7 @@ var Distance = class Distance {
13837
14063
  }
13838
14064
  };
13839
14065
 
13840
- //#region src/expression/definitions/semiliteral.ts
14066
+ // src/expression/definitions/semiliteral.ts
13841
14067
  var Semiliteral = class Semiliteral {
13842
14068
  constructor(arr) {
13843
14069
  let elementType = null;
@@ -13878,7 +14104,7 @@ var Semiliteral = class Semiliteral {
13878
14104
  }
13879
14105
  };
13880
14106
 
13881
- //#region src/expression/definitions/global_state.ts
14107
+ // src/expression/definitions/global_state.ts
13882
14108
  var GlobalState = class GlobalState {
13883
14109
  constructor(key) {
13884
14110
  this.key = key;
@@ -13902,7 +14128,7 @@ var GlobalState = class GlobalState {
13902
14128
  }
13903
14129
  };
13904
14130
 
13905
- //#region src/expression/definitions/index.ts
14131
+ // src/expression/definitions/index.ts
13906
14132
  const expressions = {
13907
14133
  "==": Equals,
13908
14134
  "!=": NotEquals,
@@ -13944,163 +14170,7 @@ const expressions = {
13944
14170
  "global-state": GlobalState
13945
14171
  };
13946
14172
 
13947
- //#region src/expression/parsing_error.ts
13948
- var ExpressionParsingError = class extends Error {
13949
- constructor(key, message) {
13950
- super(message);
13951
- this.message = message;
13952
- this.key = key;
13953
- }
13954
- };
13955
-
13956
- //#region src/expression/scope.ts
13957
- /**
13958
- * Tracks `let` bindings during expression parsing.
13959
- * @private
13960
- */
13961
- var Scope = class Scope {
13962
- constructor(parent, bindings = []) {
13963
- this.parent = parent;
13964
- this.bindings = {};
13965
- for (const [name, expression] of bindings) this.bindings[name] = expression;
13966
- }
13967
- concat(bindings) {
13968
- return new Scope(this, bindings);
13969
- }
13970
- get(name) {
13971
- if (this.bindings[name]) return this.bindings[name];
13972
- if (this.parent) return this.parent.get(name);
13973
- throw new Error(`${name} not found in scope.`);
13974
- }
13975
- has(name) {
13976
- if (this.bindings[name]) return true;
13977
- return this.parent ? this.parent.has(name) : false;
13978
- }
13979
- };
13980
-
13981
- //#region src/expression/parsing_context.ts
13982
- /**
13983
- * State associated parsing at a given point in an expression tree.
13984
- * @private
13985
- */
13986
- var ParsingContext = class ParsingContext {
13987
- constructor(registry, isConstantFunc, path = [], expectedType, scope = new Scope(), errors = []) {
13988
- this.registry = registry;
13989
- this.path = path;
13990
- this.key = path.map((part) => `[${part}]`).join("");
13991
- this.scope = scope;
13992
- this.errors = errors;
13993
- this.expectedType = expectedType;
13994
- this._isConstant = isConstantFunc;
13995
- }
13996
- /**
13997
- * @param expr the JSON expression to parse
13998
- * @param index the optional argument index if this expression is an argument of a parent expression that's being parsed
13999
- * @param options
14000
- * @param options.omitTypeAnnotations set true to omit inferred type annotations. Caller beware: with this option set, the parsed expression's type will NOT satisfy `expectedType` if it would normally be wrapped in an inferred annotation.
14001
- * @private
14002
- */
14003
- parse(expr, index, expectedType, bindings, options = {}) {
14004
- if (index != null) return this.concat(index, expectedType, bindings)._parse(expr, options);
14005
- return this._parse(expr, options);
14006
- }
14007
- _parse(expr, options) {
14008
- if (expr === null || typeof expr === "string" || typeof expr === "boolean" || typeof expr === "number") expr = ["literal", expr];
14009
- const key = this.key;
14010
- function annotate(parsed, type, typeAnnotation) {
14011
- if (typeAnnotation === "assert") return new Assertion(type, [parsed], key);
14012
- else if (typeAnnotation === "coerce") return new Coercion(type, [parsed], key);
14013
- else return parsed;
14014
- }
14015
- if (Array.isArray(expr)) {
14016
- if (expr.length === 0) return this.error("Expected an array with at least one element. If you wanted a literal array, use [\"literal\", []].");
14017
- const op = expr[0];
14018
- if (typeof op !== "string") {
14019
- this.error(`Expression name must be a string, but found ${typeof op} instead. If you wanted a literal array, use ["literal", [...]].`, 0);
14020
- return null;
14021
- }
14022
- const Expr = this.registry[op];
14023
- if (Expr) {
14024
- let parsed = Expr.parse(expr, this);
14025
- if (!parsed) return null;
14026
- if (this.expectedType) {
14027
- const expected = this.expectedType;
14028
- const actual = parsed.type;
14029
- if ((expected.kind === "string" || expected.kind === "number" || expected.kind === "boolean" || expected.kind === "object" || expected.kind === "array") && actual.kind === "value") parsed = annotate(parsed, expected, options.typeAnnotation || "assert");
14030
- else if ("projectionDefinition" === expected.kind && [
14031
- "string",
14032
- "array",
14033
- "value"
14034
- ].includes(actual.kind) || [
14035
- "color",
14036
- "formatted",
14037
- "resolvedImage"
14038
- ].includes(expected.kind) && ["value", "string"].includes(actual.kind) || ["padding", "numberArray"].includes(expected.kind) && [
14039
- "value",
14040
- "number",
14041
- "array"
14042
- ].includes(actual.kind) || "colorArray" === expected.kind && [
14043
- "value",
14044
- "string",
14045
- "array"
14046
- ].includes(actual.kind) || "variableAnchorOffsetCollection" === expected.kind && ["value", "array"].includes(actual.kind)) parsed = annotate(parsed, expected, options.typeAnnotation || "coerce");
14047
- else if (this.checkSubtype(expected, actual)) return null;
14048
- }
14049
- if (!(parsed instanceof Literal) && parsed.type.kind !== "resolvedImage" && this._isConstant(parsed)) {
14050
- const ec = new EvaluationContext();
14051
- try {
14052
- parsed = new Literal(parsed.type, parsed.evaluate(ec));
14053
- } catch (e) {
14054
- this.error(e.message);
14055
- return null;
14056
- }
14057
- }
14058
- return parsed;
14059
- }
14060
- return this.error(`Unknown expression "${op}". If you wanted a literal array, use ["literal", [...]].`, 0);
14061
- } else if (typeof expr === "undefined") return this.error("'undefined' value invalid. Use null instead.");
14062
- else if (typeof expr === "object") return this.error("Bare objects invalid. Use [\"literal\", {...}] instead.");
14063
- else return this.error(`Expected an array, but found ${typeof expr} instead.`);
14064
- }
14065
- /**
14066
- * Returns a copy of this context suitable for parsing the subexpression at
14067
- * index `index`, optionally appending to 'let' binding map.
14068
- *
14069
- * Note that `errors` property, intended for collecting errors while
14070
- * parsing, is copied by reference rather than cloned.
14071
- * @private
14072
- */
14073
- concat(index, expectedType, bindings) {
14074
- const path = typeof index === "number" ? this.path.concat(index) : this.path;
14075
- const scope = bindings ? this.scope.concat(bindings) : this.scope;
14076
- return new ParsingContext(this.registry, this._isConstant, path, expectedType || null, scope, this.errors);
14077
- }
14078
- /**
14079
- * Push a parsing (or type checking) error into the `this.errors`
14080
- * @param error The message
14081
- * @param keys Optionally specify the source of the error at a child
14082
- * of the current expression at `this.key`.
14083
- * @private
14084
- */
14085
- error(error, ...keys) {
14086
- const key = `${this.key}${keys.map((k) => `[${k}]`).join("")}`;
14087
- this.errors.push(new ExpressionParsingError(key, error));
14088
- }
14089
- /**
14090
- * Returns null if `t` is a subtype of `expected`; otherwise returns an
14091
- * error message and also pushes it to `this.errors`.
14092
- * @param expected The expected type
14093
- * @param t The actual type
14094
- * @returns null if `t` is a subtype of `expected`; otherwise returns an error message
14095
- */
14096
- checkSubtype(expected, t) {
14097
- const error = checkSubtype(expected, t);
14098
- if (error) this.error(error);
14099
- return error;
14100
- }
14101
- };
14102
-
14103
- //#region src/expression/compound_expression.ts
14173
+ // src/expression/compound_expression.ts
14104
14174
  var CompoundExpression = class CompoundExpression {
14105
14175
  constructor(name, type, evaluate, args, key) {
14106
14176
  this.name = name;
@@ -14675,7 +14745,21 @@ function isGlobalPropertyConstant(e, properties) {
14675
14745
  return result;
14676
14746
  }
14677
14747
 
14678
- //#region src/util/properties.ts
14748
+ // src/util/result.ts
14749
+ function success(value) {
14750
+ return {
14751
+ result: "success",
14752
+ value
14753
+ };
14754
+ }
14755
+ function error(value) {
14756
+ return {
14757
+ result: "error",
14758
+ value
14759
+ };
14760
+ }
14761
+
14762
+ // src/util/properties.ts
14679
14763
  function supportsPropertyExpression(spec) {
14680
14764
  return spec["property-type"] === "data-driven" || spec["property-type"] === "cross-faded-data-driven";
14681
14765
  }
@@ -14686,13 +14770,13 @@ function supportsInterpolation(spec) {
14686
14770
  return !!spec.expression && spec.expression.interpolated;
14687
14771
  }
14688
14772
 
14689
- //#region src/util/extend.ts
14773
+ // src/util/extend.ts
14690
14774
  function extendBy(output, ...inputs) {
14691
14775
  for (const input of inputs) for (const k in input) output[k] = input[k];
14692
14776
  return output;
14693
14777
  }
14694
14778
 
14695
- //#region src/util/get_type.ts
14779
+ // src/util/get_type.ts
14696
14780
  function getType(val) {
14697
14781
  if (val instanceof Number) return "number";
14698
14782
  else if (val instanceof String) return "string";
@@ -14702,7 +14786,7 @@ function getType(val) {
14702
14786
  else return typeof val;
14703
14787
  }
14704
14788
 
14705
- //#region src/function/index.ts
14789
+ // src/function/index.ts
14706
14790
  function isFunction(value) {
14707
14791
  return typeof value === "object" && value !== null && !Array.isArray(value) && typeOf(value) === ObjectType;
14708
14792
  }
@@ -14911,21 +14995,7 @@ function interpolationFactor(input, base, lowerValue, upperValue) {
14911
14995
  else return (Math.pow(base, progress) - 1) / (Math.pow(base, difference) - 1);
14912
14996
  }
14913
14997
 
14914
- //#region src/util/result.ts
14915
- function success(value) {
14916
- return {
14917
- result: "success",
14918
- value
14919
- };
14920
- }
14921
- function error(value) {
14922
- return {
14923
- result: "error",
14924
- value
14925
- };
14926
- }
14927
-
14928
- //#region src/expression/index.ts
14998
+ // src/expression/index.ts
14929
14999
  var StyleExpression = class {
14930
15000
  constructor(expression, rootKey, propertySpec, globalState) {
14931
15001
  this.expression = expression;
@@ -15140,21 +15210,26 @@ function normalizePropertyExpression(value, rootKey, specification, globalState)
15140
15210
  };
15141
15211
  }
15142
15212
  }
15143
- function findZoomCurve(expression) {
15213
+ function findZoomCurve(expression, seen = /* @__PURE__ */ new Map()) {
15214
+ if (seen.has(expression)) return seen.get(expression);
15144
15215
  let result = null;
15145
- if (expression instanceof Let) result = findZoomCurve(expression.result);
15216
+ if (expression instanceof Let) result = findZoomCurve(expression.result, seen);
15146
15217
  else if (expression instanceof Coalesce) for (const arg of expression.args) {
15147
- result = findZoomCurve(arg);
15218
+ result = findZoomCurve(arg, seen);
15148
15219
  if (result) break;
15149
15220
  }
15150
15221
  else if ((expression instanceof Step || expression instanceof Interpolate) && expression.input instanceof CompoundExpression && expression.input.name === "zoom") result = expression;
15151
- if (result instanceof ExpressionParsingError) return result;
15222
+ if (result instanceof ExpressionParsingError) {
15223
+ seen.set(expression, result);
15224
+ return result;
15225
+ }
15152
15226
  expression.eachChild((child) => {
15153
- const childResult = findZoomCurve(child);
15227
+ const childResult = findZoomCurve(child, seen);
15154
15228
  if (childResult instanceof ExpressionParsingError) result = childResult;
15155
15229
  else if (!result && childResult) result = new ExpressionParsingError("", "\"zoom\" expression may only be used as input to a top-level \"step\" or \"interpolate\" expression.");
15156
15230
  else if (result && childResult && result !== childResult) result = new ExpressionParsingError("", "Only one zoom-based \"step\" or \"interpolate\" subexpression may be used in an expression.");
15157
15231
  });
15232
+ seen.set(expression, result);
15158
15233
  return result;
15159
15234
  }
15160
15235
  function findGlobalStateRefs(expression, results = /* @__PURE__ */ new Set()) {
@@ -15207,7 +15282,7 @@ function addGlobalState(globals, globalState) {
15207
15282
  };
15208
15283
  }
15209
15284
 
15210
- //#region src/feature_filter/index.ts
15285
+ // src/feature_filter/index.ts
15211
15286
  function classifyChildren(children) {
15212
15287
  let sawLegacy = false;
15213
15288
  for (const child of children) {
@@ -15420,7 +15495,7 @@ function convertNegation(filter) {
15420
15495
  return ["!", filter];
15421
15496
  }
15422
15497
 
15423
- //#region src/expression/visibility.ts
15498
+ // src/expression/visibility.ts
15424
15499
  const visibilitySpec = {
15425
15500
  type: "enum",
15426
15501
  "property-type": "data-constant",
@@ -15803,6 +15878,174 @@ async function loadGlyphRange(template, fontStack, start, fetchFn) {
15803
15878
  }
15804
15879
  }
15805
15880
 
15881
+ /**
15882
+ * A PNG file of RGBA pixels, for images that are drawn from a data URI, like the sheets of a
15883
+ * sprite. Without compression: its deflate stream holds the pixels in stored blocks, which needs
15884
+ * no dependency and runs in browsers too. The file is a little larger than the pixels.
15885
+ */
15886
+ /** The CRC-32 table of PNG chunks, built on first use. */
15887
+ let crcTable;
15888
+ function crc32(bytes, start, end) {
15889
+ if (!crcTable) {
15890
+ crcTable = new Uint32Array(256);
15891
+ for (let n = 0; n < 256; n++) {
15892
+ let c = n;
15893
+ for (let k = 0; k < 8; k++)
15894
+ c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
15895
+ crcTable[n] = c >>> 0;
15896
+ }
15897
+ }
15898
+ let crc = 0xffffffff;
15899
+ for (let i = start; i < end; i++)
15900
+ crc = crcTable[(crc ^ bytes[i]) & 0xff] ^ (crc >>> 8);
15901
+ return (crc ^ 0xffffffff) >>> 0;
15902
+ }
15903
+ /** The checksum of a zlib stream. */
15904
+ function adler32(bytes) {
15905
+ let a = 1;
15906
+ let b = 0;
15907
+ // sums of up to 5552 bytes cannot overflow before the modulo
15908
+ for (let i = 0; i < bytes.length;) {
15909
+ const end = Math.min(i + 5552, bytes.length);
15910
+ for (; i < end; i++) {
15911
+ a += bytes[i];
15912
+ b += a;
15913
+ }
15914
+ a %= 65521;
15915
+ b %= 65521;
15916
+ }
15917
+ return ((b << 16) | a) >>> 0;
15918
+ }
15919
+ /** The longest stored deflate block. */
15920
+ const MAX_BLOCK = 65535;
15921
+ /** A zlib stream of the bytes in stored deflate blocks. */
15922
+ function zlibStored(raw) {
15923
+ const blocks = Math.max(1, Math.ceil(raw.length / MAX_BLOCK));
15924
+ const out = new Uint8Array(2 + blocks * 5 + raw.length + 4);
15925
+ const view = new DataView(out.buffer);
15926
+ // deflate with a 32 KB window, no dictionary, the fastest level
15927
+ out[0] = 0x78;
15928
+ out[1] = 0x01;
15929
+ let at = 2;
15930
+ for (let block = 0; block < blocks; block++) {
15931
+ const start = block * MAX_BLOCK;
15932
+ const length = Math.min(MAX_BLOCK, raw.length - start);
15933
+ out[at] = block === blocks - 1 ? 1 : 0;
15934
+ view.setUint16(at + 1, length, true);
15935
+ view.setUint16(at + 3, ~length & 0xffff, true);
15936
+ out.set(raw.subarray(start, start + length), at + 5);
15937
+ at += 5 + length;
15938
+ }
15939
+ view.setUint32(at, adler32(raw));
15940
+ return out;
15941
+ }
15942
+ /** A chunk of a PNG file: its length, type, data and CRC. */
15943
+ function chunk(type, data) {
15944
+ const out = new Uint8Array(12 + data.length);
15945
+ const view = new DataView(out.buffer);
15946
+ view.setUint32(0, data.length);
15947
+ for (let i = 0; i < 4; i++)
15948
+ out[4 + i] = type.charCodeAt(i);
15949
+ out.set(data, 8);
15950
+ view.setUint32(8 + data.length, crc32(out, 4, 8 + data.length));
15951
+ return out;
15952
+ }
15953
+ /**
15954
+ * A PNG file of an image of `width` × `height` pixels, given as RGBA, row by row, not
15955
+ * premultiplied: as `ImageData` and MapLibre's `map.addImage` have them.
15956
+ * @throws If `data` does not hold `width * height` pixels.
15957
+ */
15958
+ function encodePNG(width, height, data) {
15959
+ if (!Number.isInteger(width) || !Number.isInteger(height) || width <= 0 || height <= 0) {
15960
+ throw new Error(`An image must have a positive whole width and height, not ${String(width)} × ${String(height)}`);
15961
+ }
15962
+ if (data.length !== width * height * 4) {
15963
+ throw new Error(`An image of ${String(width)} × ${String(height)} pixels needs ${String(width * height * 4)} bytes, not ${String(data.length)}`);
15964
+ }
15965
+ const header = new Uint8Array(13);
15966
+ const view = new DataView(header.buffer);
15967
+ view.setUint32(0, width);
15968
+ view.setUint32(4, height);
15969
+ // 8 bits per channel, RGBA, no interlace
15970
+ header[8] = 8;
15971
+ header[9] = 6;
15972
+ // each row starts with its filter: none
15973
+ const row = width * 4;
15974
+ const raw = new Uint8Array((row + 1) * height);
15975
+ for (let y = 0; y < height; y++)
15976
+ raw.set(data.subarray(y * row, (y + 1) * row), y * (row + 1) + 1);
15977
+ const parts = [
15978
+ new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]),
15979
+ chunk('IHDR', header),
15980
+ chunk('IDAT', zlibStored(raw)),
15981
+ chunk('IEND', new Uint8Array(0)),
15982
+ ];
15983
+ const png = new Uint8Array(parts.reduce((sum, part) => sum + part.length, 0));
15984
+ let at = 0;
15985
+ for (const part of parts) {
15986
+ png.set(part, at);
15987
+ at += part.length;
15988
+ }
15989
+ return png;
15990
+ }
15991
+
15992
+ /**
15993
+ * The images in an atlas like the sprite's, each on a sheet of its own. An image that cannot be
15994
+ * drawn, e.g. with too few pixels, is left out with a warning.
15995
+ */
15996
+ function imageAtlas(images, warn) {
15997
+ const atlas = new Map();
15998
+ for (const image of images) {
15999
+ const { id, width, height, data, pixelRatio = 1, sdf = false } = image;
16000
+ if (atlas.has(id)) {
16001
+ warn(`The image "${id}" is given twice in "images": the first one is drawn.`);
16002
+ continue;
16003
+ }
16004
+ if (!(pixelRatio > 0)) {
16005
+ warn(`The image "${id}" in "images" has no positive pixelRatio: it is left out.`);
16006
+ continue;
16007
+ }
16008
+ let png;
16009
+ try {
16010
+ png = encodePNG(width, height, data);
16011
+ }
16012
+ catch (error) {
16013
+ warn(`The image "${id}" in "images" is left out: ${error.message}.`);
16014
+ continue;
16015
+ }
16016
+ const buffer = png.buffer.slice(png.byteOffset, png.byteOffset + png.byteLength);
16017
+ atlas.set(id, {
16018
+ width,
16019
+ height,
16020
+ x: 0,
16021
+ y: 0,
16022
+ pixelRatio,
16023
+ sdf,
16024
+ sheetDataUri: `data:image/png;base64,${arrayBufferToBase64(buffer)}`,
16025
+ sheetWidth: width,
16026
+ sheetHeight: height,
16027
+ });
16028
+ }
16029
+ return atlas;
16030
+ }
16031
+ /**
16032
+ * The sprite's atlas with the images added, like `map.addImage` in MapLibre GL JS, which fails for
16033
+ * an id that the style has already: an image of the sprite keeps its place, with a warning.
16034
+ */
16035
+ function withImages(sprite, images, warn) {
16036
+ if (images.size === 0)
16037
+ return sprite;
16038
+ const atlas = new Map(sprite);
16039
+ for (const [id, entry] of images) {
16040
+ if (atlas.has(id)) {
16041
+ warn(`The image "${id}" in "images" is in the sprite too: the sprite's is drawn.`);
16042
+ continue;
16043
+ }
16044
+ atlas.set(id, entry);
16045
+ }
16046
+ return atlas;
16047
+ }
16048
+
15806
16049
  // DEFLATE is a complex format; to read this code, you should probably check the RFC first:
15807
16050
  // https://tools.ietf.org/html/rfc1951
15808
16051
  // You may also wish to take a look at the guide I made about this program:
@@ -21196,6 +21439,9 @@ class SVGMapRenderer {
21196
21439
  #pmtiles;
21197
21440
  #fetch;
21198
21441
  #sprite;
21442
+ /** The images of {@link SVGMapRendererOptions.images}, encoded on first use. */
21443
+ #imageList;
21444
+ #images;
21199
21445
  #sources;
21200
21446
  #glyphRanges = new Map();
21201
21447
  #onWarning;
@@ -21208,6 +21454,7 @@ class SVGMapRenderer {
21208
21454
  constructor(options) {
21209
21455
  this.#style = options.style;
21210
21456
  this.#onWarning = options.onWarning ?? ((message) => console.warn(message));
21457
+ this.#imageList = options.images ?? [];
21211
21458
  let labels =
21212
21459
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- still supported, mapped to `labels`
21213
21460
  options.labels ?? (options.renderLabels === true ? 'glyphs-text' : 'none');
@@ -21335,7 +21582,8 @@ class SVGMapRenderer {
21335
21582
  const sprite = loadSprite(this.#style, this.#fetch).then(({ atlas, complete }) => {
21336
21583
  if (!complete && this.#sprite === sprite)
21337
21584
  this.#sprite = undefined;
21338
- return atlas;
21585
+ this.#images ??= imageAtlas(this.#imageList, (message) => this.#warn(message));
21586
+ return withImages(atlas, this.#images, (message) => this.#warn(message));
21339
21587
  });
21340
21588
  this.#sprite = sprite;
21341
21589
  return sprite;