@vitessce/spatial-utils 4.0.8 → 4.0.10

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.
@@ -18,6 +18,10 @@ function _mergeNamespaces(n3, m) {
18
18
  }
19
19
  return Object.freeze(Object.defineProperty(n3, Symbol.toStringTag, { value: "Module" }));
20
20
  }
21
+ const ViewType = {
22
+ SCATTERPLOT: "scatterplot",
23
+ SPATIAL: "spatial"
24
+ };
21
25
  const GLOBAL_LABELS = ["z", "t"];
22
26
  const DEFAULT_RASTER_DOMAIN_TYPE = "Min/Max";
23
27
  const DEFAULT_MOLECULES_LAYER = {
@@ -1263,8 +1267,8 @@ class Vector extends MathArray {
1263
1267
  distanceSquared(mathArray) {
1264
1268
  let length2 = 0;
1265
1269
  for (let i2 = 0; i2 < this.ELEMENTS; ++i2) {
1266
- const dist = this[i2] - mathArray[i2];
1267
- length2 += dist * dist;
1270
+ const dist2 = this[i2] - mathArray[i2];
1271
+ length2 += dist2 * dist2;
1268
1272
  }
1269
1273
  return checkNumber(length2);
1270
1274
  }
@@ -1585,6 +1589,12 @@ function multiply$4(out, a2, b) {
1585
1589
  out[2] = a2[2] * b[2];
1586
1590
  return out;
1587
1591
  }
1592
+ function distance$3(a2, b) {
1593
+ var x = b[0] - a2[0];
1594
+ var y = b[1] - a2[1];
1595
+ var z = b[2] - a2[2];
1596
+ return Math.hypot(x, y, z);
1597
+ }
1588
1598
  function squaredLength$2(a2) {
1589
1599
  var x = a2[0];
1590
1600
  var y = a2[1];
@@ -1708,6 +1718,7 @@ function angle$2(a2, b) {
1708
1718
  }
1709
1719
  var sub$1 = subtract$1;
1710
1720
  var mul$1 = multiply$4;
1721
+ var dist = distance$3;
1711
1722
  var len = length$4;
1712
1723
  var sqrLen = squaredLength$2;
1713
1724
  (function() {
@@ -42374,7 +42385,7 @@ function noop$2() {
42374
42385
  const getCursor = ({
42375
42386
  isDragging
42376
42387
  }) => isDragging ? "grabbing" : "grab";
42377
- const defaultProps$O = {
42388
+ const defaultProps$P = {
42378
42389
  id: "",
42379
42390
  width: "100%",
42380
42391
  height: "100%",
@@ -42505,7 +42516,7 @@ class Deck {
42505
42516
  this._lastPointerDownInfo = pickedInfo.result[0] || pickedInfo.emptyInfo;
42506
42517
  });
42507
42518
  this.props = {
42508
- ...defaultProps$O,
42519
+ ...defaultProps$P,
42509
42520
  ...props
42510
42521
  };
42511
42522
  props = this.props;
@@ -43007,7 +43018,7 @@ class Deck {
43007
43018
  metrics.gpuMemory = memoryStats.get("GPU Memory").count;
43008
43019
  }
43009
43020
  }
43010
- _defineProperty(Deck, "defaultProps", defaultProps$O);
43021
+ _defineProperty(Deck, "defaultProps", defaultProps$P);
43011
43022
  _defineProperty(Deck, "VERSION", deckGlobal.VERSION);
43012
43023
  class ShaderAttribute {
43013
43024
  constructor(dataColumn, opts) {
@@ -45762,7 +45773,7 @@ const areViewportsEqual = memoize$1(({
45762
45773
  return oldViewport.equals(viewport);
45763
45774
  });
45764
45775
  let pickingColorCache = new Uint8ClampedArray(0);
45765
- const defaultProps$N = {
45776
+ const defaultProps$O = {
45766
45777
  data: {
45767
45778
  type: "data",
45768
45779
  value: EMPTY_ARRAY$1,
@@ -46637,7 +46648,7 @@ class Layer extends Component {
46637
46648
  this.setNeedsUpdate();
46638
46649
  }
46639
46650
  }
46640
- _defineProperty(Layer, "defaultProps", defaultProps$N);
46651
+ _defineProperty(Layer, "defaultProps", defaultProps$O);
46641
46652
  _defineProperty(Layer, "layerName", "Layer");
46642
46653
  const TRACE_RENDER_LAYERS = "compositeLayer.renderLayers";
46643
46654
  class CompositeLayer extends Layer {
@@ -47673,6 +47684,124 @@ const esm$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
47673
47684
  project32,
47674
47685
  shadow
47675
47686
  }, Symbol.toStringTag, { value: "Module" }));
47687
+ const dashShaders = {
47688
+ inject: {
47689
+ "vs:#decl": "\nattribute vec2 instanceDashArrays;\nattribute float instanceDashOffsets;\nvarying vec2 vDashArray;\nvarying float vDashOffset;\n",
47690
+ "vs:#main-end": "\nvDashArray = instanceDashArrays;\nvDashOffset = instanceDashOffsets / width.x;\n",
47691
+ "fs:#decl": "\nuniform float dashAlignMode;\nuniform float capType;\nuniform bool dashGapPickable;\nvarying vec2 vDashArray;\nvarying float vDashOffset;\n\nfloat round(float x) {\n return floor(x + 0.5);\n}\n",
47692
+ "fs:#main-start": "\n float solidLength = vDashArray.x;\n float gapLength = vDashArray.y;\n float unitLength = solidLength + gapLength;\n\n float offset;\n\n if (unitLength > 0.0) {\n if (dashAlignMode == 0.0) {\n offset = vDashOffset;\n } else {\n unitLength = vPathLength / round(vPathLength / unitLength);\n offset = solidLength / 2.0;\n }\n\n float unitOffset = mod(clamp(vPathPosition.y, 0.0, vPathLength) + offset, unitLength);\n\n if (gapLength > 0.0 && unitOffset > solidLength) {\n if (capType <= 0.5) {\n if (!(dashGapPickable && picking_uActive)) {\n discard;\n }\n } else {\n // caps are rounded, test the distance to solid ends\n float distToEnd = length(vec2(\n min(unitOffset - solidLength, unitLength - unitOffset),\n vPathPosition.x\n ));\n if (distToEnd > 1.0) {\n if (!(dashGapPickable && picking_uActive)) {\n discard;\n }\n }\n }\n }\n }\n"
47693
+ }
47694
+ };
47695
+ const offsetShaders = {
47696
+ inject: {
47697
+ "vs:#decl": "\nattribute float instanceOffsets;\n",
47698
+ "vs:DECKGL_FILTER_SIZE": "\n float offsetWidth = abs(instanceOffsets * 2.0) + 1.0;\n size *= offsetWidth;\n",
47699
+ "vCornerOffset = offsetVec;": "\n float offsetWidth = abs(instanceOffsets * 2.0) + 1.0;\n vec2 offsetCenter = -instanceOffsets * (isCap ? perp : miterVec * miterSize) * 2.0;\n vCornerOffset = vCornerOffset * offsetWidth - offsetCenter;\n",
47700
+ "fs:#main-start": "\n float isInside;\n isInside = step(-1.0, vPathPosition.x) * step(vPathPosition.x, 1.0);\n if (isInside == 0.0) {\n discard;\n }\n"
47701
+ }
47702
+ };
47703
+ const defaultProps$N = {
47704
+ getDashArray: {
47705
+ type: "accessor",
47706
+ value: [0, 0]
47707
+ },
47708
+ getOffset: {
47709
+ type: "accessor",
47710
+ value: 0
47711
+ },
47712
+ dashJustified: false,
47713
+ dashGapPickable: false
47714
+ };
47715
+ class PathStyleExtension extends LayerExtension {
47716
+ constructor({
47717
+ dash = false,
47718
+ offset = false,
47719
+ highPrecisionDash = false
47720
+ } = {}) {
47721
+ super({
47722
+ dash: dash || highPrecisionDash,
47723
+ offset,
47724
+ highPrecisionDash
47725
+ });
47726
+ }
47727
+ isEnabled(layer) {
47728
+ return "pathTesselator" in layer.state;
47729
+ }
47730
+ getShaders(extension) {
47731
+ if (!extension.isEnabled(this)) {
47732
+ return null;
47733
+ }
47734
+ let result = {};
47735
+ if (extension.opts.dash) {
47736
+ result = mergeShaders(result, dashShaders);
47737
+ }
47738
+ if (extension.opts.offset) {
47739
+ result = mergeShaders(result, offsetShaders);
47740
+ }
47741
+ return result;
47742
+ }
47743
+ initializeState(context, extension) {
47744
+ const attributeManager = this.getAttributeManager();
47745
+ if (!attributeManager || !extension.isEnabled(this)) {
47746
+ return;
47747
+ }
47748
+ if (extension.opts.dash) {
47749
+ attributeManager.addInstanced({
47750
+ instanceDashArrays: {
47751
+ size: 2,
47752
+ accessor: "getDashArray"
47753
+ }
47754
+ });
47755
+ }
47756
+ if (extension.opts.highPrecisionDash) {
47757
+ attributeManager.addInstanced({
47758
+ instanceDashOffsets: {
47759
+ size: 1,
47760
+ accessor: "getPath",
47761
+ transform: extension.getDashOffsets.bind(this)
47762
+ }
47763
+ });
47764
+ }
47765
+ if (extension.opts.offset) {
47766
+ attributeManager.addInstanced({
47767
+ instanceOffsets: {
47768
+ size: 1,
47769
+ accessor: "getOffset"
47770
+ }
47771
+ });
47772
+ }
47773
+ }
47774
+ updateState(params, extension) {
47775
+ if (!extension.isEnabled(this)) {
47776
+ return;
47777
+ }
47778
+ const uniforms = {};
47779
+ if (extension.opts.dash) {
47780
+ uniforms.dashAlignMode = this.props.dashJustified ? 1 : 0;
47781
+ uniforms.dashGapPickable = Boolean(this.props.dashGapPickable);
47782
+ }
47783
+ this.state.model.setUniforms(uniforms);
47784
+ }
47785
+ getDashOffsets(path) {
47786
+ const result = [0];
47787
+ const positionSize = this.props.positionFormat === "XY" ? 2 : 3;
47788
+ const isNested2 = Array.isArray(path[0]);
47789
+ const geometrySize = isNested2 ? path.length : path.length / positionSize;
47790
+ let p;
47791
+ let prevP;
47792
+ for (let i2 = 0; i2 < geometrySize - 1; i2++) {
47793
+ p = isNested2 ? path[i2] : path.slice(i2 * positionSize, i2 * positionSize + positionSize);
47794
+ p = this.projectPosition(p);
47795
+ if (i2 > 0) {
47796
+ result[i2] = result[i2 - 1] + dist(prevP, p);
47797
+ }
47798
+ prevP = p;
47799
+ }
47800
+ return result;
47801
+ }
47802
+ }
47803
+ _defineProperty(PathStyleExtension, "defaultProps", defaultProps$N);
47804
+ _defineProperty(PathStyleExtension, "extensionName", "PathStyleExtension");
47676
47805
  const defaultProps$M = {
47677
47806
  clipBounds: [0, 0, 1, 1]
47678
47807
  };
@@ -103784,8 +103913,8 @@ function requireJs$b() {
103784
103913
  var DeltaPsi = Math.log(Math.tan(phi2 / 2 + Math.PI / 4) / Math.tan(phi12 / 2 + Math.PI / 4));
103785
103914
  var q = Math.abs(DeltaPsi) > 1e-11 ? DeltaPhi / DeltaPsi : Math.cos(phi12);
103786
103915
  var delta = Math.sqrt(DeltaPhi * DeltaPhi + q * q * DeltaLambda * DeltaLambda);
103787
- var dist = delta * R;
103788
- return dist;
103916
+ var dist2 = delta * R;
103917
+ return dist2;
103789
103918
  }
103790
103919
  js$a.default = rhumbDistance;
103791
103920
  return js$a;
@@ -104098,7 +104227,7 @@ function requireUtils$2() {
104098
104227
  var x;
104099
104228
  var y;
104100
104229
  var segmentIdx;
104101
- var dist;
104230
+ var dist2;
104102
104231
  if (coords.length > 1 && pointCoords.length) {
104103
104232
  var lineCoordinates;
104104
104233
  var pointCoordinate;
@@ -104115,17 +104244,17 @@ function requireUtils$2() {
104115
104244
  if (lineCoordinates[n3][0] !== lineCoordinates[n3 - 1][0]) {
104116
104245
  var slope = (lineCoordinates[n3][1] - lineCoordinates[n3 - 1][1]) / (lineCoordinates[n3][0] - lineCoordinates[n3 - 1][0]);
104117
104246
  var inverseSlope = lineCoordinates[n3][1] - slope * lineCoordinates[n3][0];
104118
- dist = Math.abs(slope * pointCoordinate[0] + inverseSlope - pointCoordinate[1]) / Math.sqrt(slope * slope + 1);
104119
- } else dist = Math.abs(pointCoordinate[0] - lineCoordinates[n3][0]);
104247
+ dist2 = Math.abs(slope * pointCoordinate[0] + inverseSlope - pointCoordinate[1]) / Math.sqrt(slope * slope + 1);
104248
+ } else dist2 = Math.abs(pointCoordinate[0] - lineCoordinates[n3][0]);
104120
104249
  var rl2 = Math.pow(lineCoordinates[n3][1] - lineCoordinates[n3 - 1][1], 2) + Math.pow(lineCoordinates[n3][0] - lineCoordinates[n3 - 1][0], 2);
104121
104250
  var ln2 = Math.pow(lineCoordinates[n3][1] - pointCoordinate[1], 2) + Math.pow(lineCoordinates[n3][0] - pointCoordinate[0], 2);
104122
104251
  var lnm12 = Math.pow(lineCoordinates[n3 - 1][1] - pointCoordinate[1], 2) + Math.pow(lineCoordinates[n3 - 1][0] - pointCoordinate[0], 2);
104123
- var dist2 = Math.pow(dist, 2);
104124
- var calcrl2 = ln2 - dist2 + lnm12 - dist2;
104252
+ var dist22 = Math.pow(dist2, 2);
104253
+ var calcrl2 = ln2 - dist22 + lnm12 - dist22;
104125
104254
  if (calcrl2 > rl2) {
104126
- dist = Math.sqrt(Math.min(ln2, lnm12));
104255
+ dist2 = Math.sqrt(Math.min(ln2, lnm12));
104127
104256
  }
104128
- if (minDist === null || minDist === void 0 || minDist > dist) {
104257
+ if (minDist === null || minDist === void 0 || minDist > dist2) {
104129
104258
  if (calcrl2 > rl2) {
104130
104259
  if (lnm12 < ln2) {
104131
104260
  to = 0;
@@ -104135,10 +104264,10 @@ function requireUtils$2() {
104135
104264
  to = 1;
104136
104265
  }
104137
104266
  } else {
104138
- to = Math.sqrt(lnm12 - dist2) / Math.sqrt(rl2);
104139
- from = Math.sqrt(ln2 - dist2) / Math.sqrt(rl2);
104267
+ to = Math.sqrt(lnm12 - dist22) / Math.sqrt(rl2);
104268
+ from = Math.sqrt(ln2 - dist22) / Math.sqrt(rl2);
104140
104269
  }
104141
- minDist = dist;
104270
+ minDist = dist2;
104142
104271
  segmentIdx = n3;
104143
104272
  }
104144
104273
  }
@@ -120396,22 +120525,22 @@ function addDecoder(cases, importFn) {
120396
120525
  }
120397
120526
  cases.forEach((c) => registry$2.set(c, importFn));
120398
120527
  }
120399
- addDecoder([void 0, 1], () => import("./raw-BFsjOa9D.js").then((m) => m.default));
120400
- addDecoder(5, () => import("./lzw-C0hm4LU9.js").then((m) => m.default));
120528
+ addDecoder([void 0, 1], () => import("./raw-taMYakY-.js").then((m) => m.default));
120529
+ addDecoder(5, () => import("./lzw-D7u1eQSe.js").then((m) => m.default));
120401
120530
  addDecoder(6, () => {
120402
120531
  throw new Error("old style JPEG compression is not supported.");
120403
120532
  });
120404
- addDecoder(7, () => import("./jpeg-CEsy1aGu.js").then((m) => m.default));
120405
- addDecoder([8, 32946], () => import("./deflate-BpOWypHr.js").then((m) => m.default));
120406
- addDecoder(32773, () => import("./packbits-CkQ8qELh.js").then((m) => m.default));
120533
+ addDecoder(7, () => import("./jpeg-ISmJxKn1.js").then((m) => m.default));
120534
+ addDecoder([8, 32946], () => import("./deflate-CNjgm89Q.js").then((m) => m.default));
120535
+ addDecoder(32773, () => import("./packbits-D4yNoB9e.js").then((m) => m.default));
120407
120536
  addDecoder(
120408
120537
  34887,
120409
- () => import("./lerc-CoY4QzZT.js").then(async (m) => {
120538
+ () => import("./lerc-Bx1iViC_.js").then(async (m) => {
120410
120539
  await m.zstd.init();
120411
120540
  return m;
120412
120541
  }).then((m) => m.default)
120413
120542
  );
120414
- addDecoder(50001, () => import("./webimage-C8ngyR6p.js").then((m) => m.default));
120543
+ addDecoder(50001, () => import("./webimage-DjvdutBx.js").then((m) => m.default));
120415
120544
  function decodeRowAcc(row, stride) {
120416
120545
  let length2 = row.length - stride;
120417
120546
  let offset = 0;
@@ -124412,8 +124541,8 @@ function TreeDesc(dyn_tree, stat_desc) {
124412
124541
  this.max_code = 0;
124413
124542
  this.stat_desc = stat_desc;
124414
124543
  }
124415
- const d_code = (dist) => {
124416
- return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)];
124544
+ const d_code = (dist2) => {
124545
+ return dist2 < 256 ? _dist_code[dist2] : _dist_code[256 + (dist2 >>> 7)];
124417
124546
  };
124418
124547
  const put_short = (s2, w2) => {
124419
124548
  s2.pending_buf[s2.pending++] = w2 & 255;
@@ -124547,7 +124676,7 @@ const tr_static_init = () => {
124547
124676
  let bits;
124548
124677
  let length2;
124549
124678
  let code;
124550
- let dist;
124679
+ let dist2;
124551
124680
  const bl_count = new Array(MAX_BITS + 1);
124552
124681
  length2 = 0;
124553
124682
  for (code = 0; code < LENGTH_CODES - 1; code++) {
@@ -124557,18 +124686,18 @@ const tr_static_init = () => {
124557
124686
  }
124558
124687
  }
124559
124688
  _length_code[length2 - 1] = code;
124560
- dist = 0;
124689
+ dist2 = 0;
124561
124690
  for (code = 0; code < 16; code++) {
124562
- base_dist[code] = dist;
124691
+ base_dist[code] = dist2;
124563
124692
  for (n3 = 0; n3 < 1 << extra_dbits[code]; n3++) {
124564
- _dist_code[dist++] = code;
124693
+ _dist_code[dist2++] = code;
124565
124694
  }
124566
124695
  }
124567
- dist >>= 7;
124696
+ dist2 >>= 7;
124568
124697
  for (; code < D_CODES; code++) {
124569
- base_dist[code] = dist << 7;
124698
+ base_dist[code] = dist2 << 7;
124570
124699
  for (n3 = 0; n3 < 1 << extra_dbits[code] - 7; n3++) {
124571
- _dist_code[256 + dist++] = code;
124700
+ _dist_code[256 + dist2++] = code;
124572
124701
  }
124573
124702
  }
124574
124703
  for (bits = 0; bits <= MAX_BITS; bits++) {
@@ -124659,17 +124788,17 @@ const pqdownheap = (s2, tree, k) => {
124659
124788
  s2.heap[k] = v;
124660
124789
  };
124661
124790
  const compress_block = (s2, ltree, dtree) => {
124662
- let dist;
124791
+ let dist2;
124663
124792
  let lc;
124664
124793
  let lx = 0;
124665
124794
  let code;
124666
124795
  let extra;
124667
124796
  if (s2.last_lit !== 0) {
124668
124797
  do {
124669
- dist = s2.pending_buf[s2.d_buf + lx * 2] << 8 | s2.pending_buf[s2.d_buf + lx * 2 + 1];
124798
+ dist2 = s2.pending_buf[s2.d_buf + lx * 2] << 8 | s2.pending_buf[s2.d_buf + lx * 2 + 1];
124670
124799
  lc = s2.pending_buf[s2.l_buf + lx];
124671
124800
  lx++;
124672
- if (dist === 0) {
124801
+ if (dist2 === 0) {
124673
124802
  send_code(s2, lc, ltree);
124674
124803
  } else {
124675
124804
  code = _length_code[lc];
@@ -124679,13 +124808,13 @@ const compress_block = (s2, ltree, dtree) => {
124679
124808
  lc -= base_length[code];
124680
124809
  send_bits(s2, lc, extra);
124681
124810
  }
124682
- dist--;
124683
- code = d_code(dist);
124811
+ dist2--;
124812
+ code = d_code(dist2);
124684
124813
  send_code(s2, code, dtree);
124685
124814
  extra = extra_dbits[code];
124686
124815
  if (extra !== 0) {
124687
- dist -= base_dist[code];
124688
- send_bits(s2, dist, extra);
124816
+ dist2 -= base_dist[code];
124817
+ send_bits(s2, dist2, extra);
124689
124818
  }
124690
124819
  }
124691
124820
  } while (lx < s2.last_lit);
@@ -124956,18 +125085,18 @@ const _tr_flush_block = (s2, buf, stored_len, last) => {
124956
125085
  bi_windup(s2);
124957
125086
  }
124958
125087
  };
124959
- const _tr_tally = (s2, dist, lc) => {
124960
- s2.pending_buf[s2.d_buf + s2.last_lit * 2] = dist >>> 8 & 255;
124961
- s2.pending_buf[s2.d_buf + s2.last_lit * 2 + 1] = dist & 255;
125088
+ const _tr_tally = (s2, dist2, lc) => {
125089
+ s2.pending_buf[s2.d_buf + s2.last_lit * 2] = dist2 >>> 8 & 255;
125090
+ s2.pending_buf[s2.d_buf + s2.last_lit * 2 + 1] = dist2 & 255;
124962
125091
  s2.pending_buf[s2.l_buf + s2.last_lit] = lc & 255;
124963
125092
  s2.last_lit++;
124964
- if (dist === 0) {
125093
+ if (dist2 === 0) {
124965
125094
  s2.dyn_ltree[lc * 2]++;
124966
125095
  } else {
124967
125096
  s2.matches++;
124968
- dist--;
125097
+ dist2--;
124969
125098
  s2.dyn_ltree[(_length_code[lc] + LITERALS + 1) * 2]++;
124970
- s2.dyn_dtree[d_code(dist) * 2]++;
125099
+ s2.dyn_dtree[d_code(dist2) * 2]++;
124971
125100
  }
124972
125101
  return s2.last_lit === s2.lit_bufsize - 1;
124973
125102
  };
@@ -126472,7 +126601,7 @@ var inffast = function inflate_fast(strm, start) {
126472
126601
  let here;
126473
126602
  let op;
126474
126603
  let len2;
126475
- let dist;
126604
+ let dist2;
126476
126605
  let from;
126477
126606
  let from_source;
126478
126607
  let input, output;
@@ -126538,7 +126667,7 @@ var inffast = function inflate_fast(strm, start) {
126538
126667
  bits -= op;
126539
126668
  op = here >>> 16 & 255;
126540
126669
  if (op & 16) {
126541
- dist = here & 65535;
126670
+ dist2 = here & 65535;
126542
126671
  op &= 15;
126543
126672
  if (bits < op) {
126544
126673
  hold += input[_in++] << bits;
@@ -126548,8 +126677,8 @@ var inffast = function inflate_fast(strm, start) {
126548
126677
  bits += 8;
126549
126678
  }
126550
126679
  }
126551
- dist += hold & (1 << op) - 1;
126552
- if (dist > dmax) {
126680
+ dist2 += hold & (1 << op) - 1;
126681
+ if (dist2 > dmax) {
126553
126682
  strm.msg = "invalid distance too far back";
126554
126683
  state.mode = BAD;
126555
126684
  break top;
@@ -126557,8 +126686,8 @@ var inffast = function inflate_fast(strm, start) {
126557
126686
  hold >>>= op;
126558
126687
  bits -= op;
126559
126688
  op = _out - beg;
126560
- if (dist > op) {
126561
- op = dist - op;
126689
+ if (dist2 > op) {
126690
+ op = dist2 - op;
126562
126691
  if (op > whave) {
126563
126692
  if (state.sane) {
126564
126693
  strm.msg = "invalid distance too far back";
@@ -126575,7 +126704,7 @@ var inffast = function inflate_fast(strm, start) {
126575
126704
  do {
126576
126705
  output[_out++] = s_window[from++];
126577
126706
  } while (--op);
126578
- from = _out - dist;
126707
+ from = _out - dist2;
126579
126708
  from_source = output;
126580
126709
  }
126581
126710
  } else if (wnext < op) {
@@ -126593,7 +126722,7 @@ var inffast = function inflate_fast(strm, start) {
126593
126722
  do {
126594
126723
  output[_out++] = s_window[from++];
126595
126724
  } while (--op);
126596
- from = _out - dist;
126725
+ from = _out - dist2;
126597
126726
  from_source = output;
126598
126727
  }
126599
126728
  }
@@ -126604,7 +126733,7 @@ var inffast = function inflate_fast(strm, start) {
126604
126733
  do {
126605
126734
  output[_out++] = s_window[from++];
126606
126735
  } while (--op);
126607
- from = _out - dist;
126736
+ from = _out - dist2;
126608
126737
  from_source = output;
126609
126738
  }
126610
126739
  }
@@ -126621,7 +126750,7 @@ var inffast = function inflate_fast(strm, start) {
126621
126750
  }
126622
126751
  }
126623
126752
  } else {
126624
- from = _out - dist;
126753
+ from = _out - dist2;
126625
126754
  do {
126626
126755
  output[_out++] = output[from++];
126627
126756
  output[_out++] = output[from++];
@@ -127179,7 +127308,7 @@ const fixedtables = (state) => {
127179
127308
  state.distbits = 5;
127180
127309
  };
127181
127310
  const updatewindow = (strm, src, end, copy2) => {
127182
- let dist;
127311
+ let dist2;
127183
127312
  const state = strm.state;
127184
127313
  if (state.window === null) {
127185
127314
  state.wsize = 1 << state.wbits;
@@ -127192,23 +127321,23 @@ const updatewindow = (strm, src, end, copy2) => {
127192
127321
  state.wnext = 0;
127193
127322
  state.whave = state.wsize;
127194
127323
  } else {
127195
- dist = state.wsize - state.wnext;
127196
- if (dist > copy2) {
127197
- dist = copy2;
127324
+ dist2 = state.wsize - state.wnext;
127325
+ if (dist2 > copy2) {
127326
+ dist2 = copy2;
127198
127327
  }
127199
- state.window.set(src.subarray(end - copy2, end - copy2 + dist), state.wnext);
127200
- copy2 -= dist;
127328
+ state.window.set(src.subarray(end - copy2, end - copy2 + dist2), state.wnext);
127329
+ copy2 -= dist2;
127201
127330
  if (copy2) {
127202
127331
  state.window.set(src.subarray(end - copy2, end), 0);
127203
127332
  state.wnext = copy2;
127204
127333
  state.whave = state.wsize;
127205
127334
  } else {
127206
- state.wnext += dist;
127335
+ state.wnext += dist2;
127207
127336
  if (state.wnext === state.wsize) {
127208
127337
  state.wnext = 0;
127209
127338
  }
127210
127339
  if (state.whave < state.wsize) {
127211
- state.whave += dist;
127340
+ state.whave += dist2;
127212
127341
  }
127213
127342
  }
127214
127343
  }
@@ -131378,7 +131507,7 @@ function padColors({ colors, channelsVisible }) {
131378
131507
  ).reduce((acc, val) => acc.concat(val), []);
131379
131508
  return paddedColors;
131380
131509
  }
131381
- const defaultProps$3$1 = {
131510
+ const defaultProps$3$2 = {
131382
131511
  colors: { type: "array", value: null, compare: true },
131383
131512
  opacity: { type: "number", value: 1, compare: true },
131384
131513
  transparentColor: { type: "array", value: null, compare: true },
@@ -131395,9 +131524,9 @@ const ColorPaletteExtension = class extends LayerExtension {
131395
131524
  const {
131396
131525
  colors,
131397
131526
  channelsVisible,
131398
- opacity = defaultProps$3$1.opacity.value,
131399
- transparentColor = defaultProps$3$1.transparentColor.value,
131400
- useTransparentColor = defaultProps$3$1.useTransparentColor.value
131527
+ opacity = defaultProps$3$2.opacity.value,
131528
+ transparentColor = defaultProps$3$2.transparentColor.value,
131529
+ useTransparentColor = defaultProps$3$2.useTransparentColor.value
131401
131530
  } = this.props;
131402
131531
  const paddedColors = padColors({
131403
131532
  channelsVisible: channelsVisible || this.selections.map(() => true),
@@ -131413,7 +131542,7 @@ const ColorPaletteExtension = class extends LayerExtension {
131413
131542
  }
131414
131543
  };
131415
131544
  ColorPaletteExtension.extensionName = "ColorPaletteExtension";
131416
- ColorPaletteExtension.defaultProps = defaultProps$3$1;
131545
+ ColorPaletteExtension.defaultProps = defaultProps$3$2;
131417
131546
  const fs$4 = `// lens bounds for ellipse
131418
131547
  uniform float majorLensAxis;
131419
131548
  uniform float minorLensAxis;
@@ -132708,7 +132837,7 @@ const MultiscaleImageLayer = class extends CompositeLayer {
132708
132837
  };
132709
132838
  MultiscaleImageLayer.layerName = "MultiscaleImageLayer";
132710
132839
  MultiscaleImageLayer.defaultProps = defaultProps$4;
132711
- const defaultProps$3 = {
132840
+ const defaultProps$3$1 = {
132712
132841
  pickable: { type: "boolean", value: true, compare: true },
132713
132842
  loader: {
132714
132843
  type: "object",
@@ -132795,7 +132924,7 @@ const OverviewLayer = class extends CompositeLayer {
132795
132924
  }
132796
132925
  };
132797
132926
  OverviewLayer.layerName = "OverviewLayer";
132798
- OverviewLayer.defaultProps = defaultProps$3;
132927
+ OverviewLayer.defaultProps = defaultProps$3$1;
132799
132928
  function getPosition(boundingBox, position, length2) {
132800
132929
  const viewWidth = boundingBox[2][0] - boundingBox[0][0];
132801
132930
  const viewHeight = boundingBox[2][1] - boundingBox[0][1];
@@ -133783,7 +133912,7 @@ function padWithDefault$1(arr, defaultValue2, padWidth) {
133783
133912
  }
133784
133913
  return newArr;
133785
133914
  }
133786
- const defaultProps$2 = {
133915
+ const defaultProps$3 = {
133787
133916
  hoveredCell: { type: "number", value: null, compare: true },
133788
133917
  // We do not want to deep-compare cellColorData,
133789
133918
  // as it is potentially a TypedArray with millions of elements.
@@ -133900,7 +134029,7 @@ let BitmaskLayer$1 = class BitmaskLayer extends XRLayer {
133900
134029
  }
133901
134030
  };
133902
134031
  BitmaskLayer$1.layerName = "BitmaskLayer";
133903
- BitmaskLayer$1.defaultProps = defaultProps$2;
134032
+ BitmaskLayer$1.defaultProps = defaultProps$3;
133904
134033
  const vs = `
133905
134034
  #define SHADER_NAME bitmask-layer-vertex-shader
133906
134035
 
@@ -134242,7 +134371,7 @@ function isEqualShallow(prevArr, nextArr) {
134242
134371
  }
134243
134372
  return prevArr === nextArr;
134244
134373
  }
134245
- const defaultProps$1 = {
134374
+ const defaultProps$2 = {
134246
134375
  channelStrokeWidths: { type: "array", value: null, compare: true },
134247
134376
  channelsFilled: { type: "array", value: null, compare: true },
134248
134377
  channelOpacities: { type: "array", value: null, compare: true },
@@ -134506,7 +134635,7 @@ class BitmaskLayer2 extends XRLayer {
134506
134635
  }
134507
134636
  }
134508
134637
  BitmaskLayer2.layerName = "BitmaskLayer";
134509
- BitmaskLayer2.defaultProps = defaultProps$1;
134638
+ BitmaskLayer2.defaultProps = defaultProps$2;
134510
134639
  var isNumeric = /^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i, mathceil = Math.ceil, mathfloor = Math.floor, bignumberError = "[BigNumber Error] ", tooManyDigits = bignumberError + "Number primitive has more than 15 significant digits: ", BASE = 1e14, LOG_BASE = 14, MAX_SAFE_INTEGER = 9007199254740991, POWS_TEN = [1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13], SQRT_BASE = 1e7, MAX = 1e9;
134511
134640
  function clone(configObject) {
134512
134641
  var div2, convertBase2, parseNumeric, P2 = BigNumber2.prototype = { constructor: BigNumber2, toString: null, valueOf: null }, ONE = new BigNumber2(1), DECIMAL_PLACES = 20, ROUNDING_MODE = 4, TO_EXP_NEG = -7, TO_EXP_POS = 21, MIN_EXP = -1e7, MAX_EXP = 1e7, CRYPTO = false, MODULO_MODE = 1, POW_PRECISION = 0, FORMAT = {
@@ -137449,7 +137578,7 @@ function ringArea(coords) {
137449
137578
  return total * FACTOR;
137450
137579
  }
137451
137580
  const DEFAULT_THRESHOLD = 1;
137452
- const defaultProps = {
137581
+ const defaultProps$1 = {
137453
137582
  // for text
137454
137583
  obsSetPath: { type: "object" },
137455
137584
  sampleSetPath: { type: "object" },
@@ -137544,9 +137673,9 @@ class ContourLayerWithText extends ContourLayer {
137544
137673
  const circlePoint = point(circleCoord);
137545
137674
  polygonVertices.forEach((polyCoord) => {
137546
137675
  const polyPoint = point(polyCoord);
137547
- const dist = distance$1(circlePoint, polyPoint);
137548
- if (dist < minDist) {
137549
- minDist = dist;
137676
+ const dist2 = distance$1(circlePoint, polyPoint);
137677
+ if (dist2 < minDist) {
137678
+ minDist = dist2;
137550
137679
  minCirclePoint = circleCoord;
137551
137680
  minCirclePointI = circlePointI;
137552
137681
  minPolygonPoint = polyCoord;
@@ -137623,7 +137752,1077 @@ class ContourLayerWithText extends ContourLayer {
137623
137752
  }
137624
137753
  }
137625
137754
  ContourLayerWithText.layerName = "ContourLayerWithText";
137626
- ContourLayerWithText.defaultProps = defaultProps;
137755
+ ContourLayerWithText.defaultProps = defaultProps$1;
137756
+ const ARROW_SCREEN_PX = 14;
137757
+ const LABEL_FONT_FAMILY = '"Roboto", "Helvetica", "Arial", sans-serif';
137758
+ const LABEL_FONT_WEIGHT = "bold";
137759
+ const LABEL_FONT_SIZE = 14;
137760
+ const RATIO_CLUSTER = 0.1;
137761
+ const RATIO_DOT = 0.3;
137762
+ const RATIO_GEO_FADE = 0.55;
137763
+ const RATIO_TIPS = 0.7;
137764
+ const RATIO_LABEL = 0.55;
137765
+ const RATIO_LABEL_FULL = 0.75;
137766
+ function textBgColor(strokeColor) {
137767
+ const [r2, g2, b] = strokeColor;
137768
+ const lum = (0.299 * r2 + 0.587 * g2 + 0.114 * b) / 255;
137769
+ return lum > 0.45 ? [20, 20, 20, 210] : [245, 245, 245, 210];
137770
+ }
137771
+ function parseDashArray(strokeDashArray) {
137772
+ if (!strokeDashArray || strokeDashArray === "none")
137773
+ return null;
137774
+ const nums = strokeDashArray.trim().split(/\s+/).map(Number).filter((n3) => !Number.isNaN(n3) && n3 >= 0);
137775
+ return nums.length >= 2 ? [nums[0], nums[1]] : null;
137776
+ }
137777
+ function computeEllipsePolygon(cx, cy, rx, ry, numSegments = 64) {
137778
+ const pts = [];
137779
+ for (let i2 = 0; i2 < numSegments; i2++) {
137780
+ const angle2 = 2 * Math.PI * i2 / numSegments;
137781
+ pts.push([cx + rx * Math.cos(angle2), cy + ry * Math.sin(angle2)]);
137782
+ }
137783
+ return pts;
137784
+ }
137785
+ function createPreviewLayer(inProgress, hoverCoord, strokeColor = [255, 255, 0]) {
137786
+ if (!inProgress || inProgress.vertices.length === 0)
137787
+ return null;
137788
+ const { type, vertices } = inProgress;
137789
+ const base = { coordinateSystem: COORDINATE_SYSTEM.CARTESIAN };
137790
+ const previewColor = [...strokeColor, 160];
137791
+ const cursor = hoverCoord ?? vertices[vertices.length - 1];
137792
+ if (type === "rectangle" && vertices.length === 1) {
137793
+ const [ax, ay] = vertices[0];
137794
+ const [bx, by] = cursor;
137795
+ const path = [[ax, ay], [bx, ay], [bx, by], [ax, by], [ax, ay]];
137796
+ return new PathLayer({
137797
+ ...base,
137798
+ id: "annotation-preview-rectangle",
137799
+ data: [{ path }],
137800
+ getPath: (d) => d.path,
137801
+ getColor: previewColor,
137802
+ getWidth: 1,
137803
+ widthUnits: "pixels",
137804
+ extensions: [new PathStyleExtension({ dash: true })],
137805
+ getDashArray: [8, 4]
137806
+ });
137807
+ }
137808
+ if (type === "line" && vertices.length === 1) {
137809
+ const path = [vertices[0], cursor];
137810
+ return new PathLayer({
137811
+ ...base,
137812
+ id: "annotation-preview-line",
137813
+ data: [{ path }],
137814
+ getPath: (d) => d.path,
137815
+ getColor: previewColor,
137816
+ getWidth: 1,
137817
+ widthUnits: "pixels",
137818
+ extensions: [new PathStyleExtension({ dash: true })],
137819
+ getDashArray: [8, 4]
137820
+ });
137821
+ }
137822
+ if (type === "ellipse" && vertices.length === 1) {
137823
+ const [cx, cy] = vertices[0];
137824
+ const [ex, ey] = cursor;
137825
+ const rx = Math.abs(ex - cx);
137826
+ const ry = Math.abs(ey - cy);
137827
+ if (rx < 1 && ry < 1)
137828
+ return null;
137829
+ const pts = computeEllipsePolygon(cx, cy, Math.max(rx, 1), Math.max(ry, 1));
137830
+ const path = [...pts, pts[0]];
137831
+ return new PathLayer({
137832
+ ...base,
137833
+ id: "annotation-preview-ellipse",
137834
+ data: [{ path }],
137835
+ getPath: (d) => d.path,
137836
+ getColor: previewColor,
137837
+ getWidth: 1,
137838
+ widthUnits: "pixels",
137839
+ extensions: [new PathStyleExtension({ dash: true })],
137840
+ getDashArray: [8, 4]
137841
+ });
137842
+ }
137843
+ if ((type === "polygon" || type === "polyline") && vertices.length >= 1) {
137844
+ const path = [...vertices, cursor];
137845
+ if (type === "polygon" && vertices.length >= 2)
137846
+ path.push(vertices[0]);
137847
+ return new PathLayer({
137848
+ ...base,
137849
+ id: `annotation-preview-${type}`,
137850
+ data: [{ path }],
137851
+ getPath: (d) => d.path,
137852
+ getColor: previewColor,
137853
+ getWidth: 1,
137854
+ widthUnits: "pixels",
137855
+ jointRounded: true,
137856
+ capRounded: true,
137857
+ extensions: [new PathStyleExtension({ dash: true })],
137858
+ getDashArray: [8, 4]
137859
+ });
137860
+ }
137861
+ return null;
137862
+ }
137863
+ function computeTickCap(tipX, tipY, dirX, dirY, dataSize) {
137864
+ const len2 = Math.sqrt(dirX * dirX + dirY * dirY);
137865
+ if (len2 === 0)
137866
+ return null;
137867
+ const ux = dirX / len2;
137868
+ const uy = dirY / len2;
137869
+ const px = -uy;
137870
+ const py = ux;
137871
+ const half = dataSize * 0.55;
137872
+ return [
137873
+ [tipX + px * half, tipY + py * half],
137874
+ [tipX - px * half, tipY - py * half]
137875
+ ];
137876
+ }
137877
+ function computeArrowhead(tipX, tipY, dirX, dirY, dataSize) {
137878
+ const len2 = Math.sqrt(dirX * dirX + dirY * dirY);
137879
+ if (len2 === 0)
137880
+ return null;
137881
+ const ux = dirX / len2;
137882
+ const uy = dirY / len2;
137883
+ const px = -uy;
137884
+ const py = ux;
137885
+ const halfBase = dataSize * 0.4;
137886
+ const bx = tipX - ux * dataSize;
137887
+ const by = tipY - uy * dataSize;
137888
+ return [
137889
+ [tipX, tipY],
137890
+ [bx + px * halfBase, by + py * halfBase],
137891
+ [bx - px * halfBase, by - py * halfBase]
137892
+ ];
137893
+ }
137894
+ function fmtCommas(n3) {
137895
+ return Math.round(n3).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
137896
+ }
137897
+ function getMeasurementLabel(shape, physicalPixelSize, viewType = ViewType.SPATIAL) {
137898
+ const pps = physicalPixelSize;
137899
+ const px = pps?.x ?? 1;
137900
+ const py = pps?.y ?? 1;
137901
+ const unit2 = pps ? pps.unit ?? "" : null;
137902
+ const hasPhys = !!pps;
137903
+ const isEmbedding = viewType === ViewType.SCATTERPLOT;
137904
+ const fmtDist = (d) => {
137905
+ if (hasPhys)
137906
+ return `${d.toFixed(2)} ${unit2}`;
137907
+ if (isEmbedding)
137908
+ return d.toFixed(2);
137909
+ return `${fmtCommas(d)} px`;
137910
+ };
137911
+ const fmtArea = (a2) => {
137912
+ if (hasPhys)
137913
+ return `${a2.toFixed(2)} ${unit2}²`;
137914
+ if (isEmbedding)
137915
+ return a2.toFixed(2);
137916
+ return `${fmtCommas(a2)} px²`;
137917
+ };
137918
+ if (shape.type === "line") {
137919
+ const dx = (shape.x2 ?? 0) - (shape.x1 ?? 0);
137920
+ const dy = (shape.y2 ?? 0) - (shape.y1 ?? 0);
137921
+ const dist2 = hasPhys ? Math.sqrt((dx * px) ** 2 + (dy * py) ** 2) : Math.sqrt(dx * dx + dy * dy);
137922
+ return fmtDist(dist2);
137923
+ }
137924
+ if (shape.type === "rectangle") {
137925
+ return fmtArea(hasPhys ? Math.abs(shape.width ?? 0) * px * Math.abs(shape.height ?? 0) * py : Math.abs((shape.width ?? 0) * (shape.height ?? 0)));
137926
+ }
137927
+ if (shape.type === "ellipse") {
137928
+ const rx = Math.abs(shape.radiusX ?? 0);
137929
+ const ry = Math.abs(shape.radiusY ?? 0);
137930
+ return fmtArea(hasPhys ? Math.PI * rx * px * ry * py : Math.PI * rx * ry);
137931
+ }
137932
+ if (shape.type === "polygon" && shape.points?.length >= 3) {
137933
+ const pts = shape.points;
137934
+ let shoelace = 0;
137935
+ for (let i2 = 0; i2 < pts.length; i2++) {
137936
+ const [ax, ay] = pts[i2];
137937
+ const [bx, by] = pts[(i2 + 1) % pts.length];
137938
+ shoelace += (hasPhys ? ax * px : ax) * (hasPhys ? by * py : by) - (hasPhys ? bx * px : bx) * (hasPhys ? ay * py : ay);
137939
+ }
137940
+ return fmtArea(Math.abs(shoelace / 2));
137941
+ }
137942
+ if (shape.type === "polyline" && shape.points?.length >= 2) {
137943
+ let total = 0;
137944
+ for (let i2 = 0; i2 < shape.points.length - 1; i2++) {
137945
+ const [ax, ay] = shape.points[i2];
137946
+ const [bx, by] = shape.points[i2 + 1];
137947
+ total += hasPhys ? Math.sqrt(((bx - ax) * px) ** 2 + ((by - ay) * py) ** 2) : Math.sqrt((bx - ax) ** 2 + (by - ay) ** 2);
137948
+ }
137949
+ return fmtDist(total);
137950
+ }
137951
+ return null;
137952
+ }
137953
+ function pickBelowPerp(ux, uy) {
137954
+ const rp = [uy, -ux];
137955
+ const lp = [-uy, ux];
137956
+ return rp[1] > lp[1] || rp[1] === lp[1] && rp[0] > lp[0] ? rp : lp;
137957
+ }
137958
+ function readableAngle(deg) {
137959
+ let a2 = deg;
137960
+ if (a2 > 90)
137961
+ a2 -= 180;
137962
+ if (a2 < -90)
137963
+ a2 += 180;
137964
+ return a2;
137965
+ }
137966
+ function getShapeCentroid(shape) {
137967
+ if (shape.type === "rectangle") {
137968
+ return [(shape.x ?? 0) + (shape.width ?? 0) / 2, (shape.y ?? 0) + (shape.height ?? 0) / 2];
137969
+ }
137970
+ if (shape.type === "line") {
137971
+ return [((shape.x1 ?? 0) + (shape.x2 ?? 0)) / 2, ((shape.y1 ?? 0) + (shape.y2 ?? 0)) / 2];
137972
+ }
137973
+ if (shape.type === "ellipse") {
137974
+ return [shape.x1 ?? 0, shape.y1 ?? 0];
137975
+ }
137976
+ if ((shape.type === "polygon" || shape.type === "polyline") && shape.points?.length >= 1) {
137977
+ const mx = shape.points.reduce((s2, p) => s2 + p[0], 0) / shape.points.length;
137978
+ const my = shape.points.reduce((s2, p) => s2 + p[1], 0) / shape.points.length;
137979
+ return [mx, my];
137980
+ }
137981
+ return [0, 0];
137982
+ }
137983
+ function computeClusters(shapeList, zoom, cellPx = 50) {
137984
+ const cellSize = cellPx / 2 ** Math.floor(zoom);
137985
+ const cells = /* @__PURE__ */ new Map();
137986
+ shapeList.forEach(({ shape }) => {
137987
+ const [cx, cy] = getShapeCentroid(shape);
137988
+ const key = `${Math.floor(cx / cellSize)},${Math.floor(cy / cellSize)}`;
137989
+ if (!cells.has(key)) {
137990
+ cells.set(key, { sumX: 0, sumY: 0, count: 0, color: shape.strokeColor ?? [255, 255, 255] });
137991
+ }
137992
+ const c = cells.get(key);
137993
+ c.sumX += cx;
137994
+ c.sumY += cy;
137995
+ c.count++;
137996
+ });
137997
+ return Array.from(cells.values()).map((c) => ({
137998
+ position: [c.sumX / c.count, c.sumY / c.count, 0],
137999
+ count: c.count,
138000
+ color: c.color
138001
+ }));
138002
+ }
138003
+ function createAnnotationLayers(shapes = [], zoom = 0, selectedShapeUid = null, physicalPixelSize = null, authoredZoom = null, semanticZoom = true, viewType = ViewType.SPATIAL) {
138004
+ const selectionLayers = [];
138005
+ if (selectedShapeUid) {
138006
+ const sel = shapes.find((s2) => s2.uid === selectedShapeUid);
138007
+ if (sel) {
138008
+ const { uid: sUid, type: sType, strokeWidth: sStrokeWidth = 3 } = sel;
138009
+ const haloColor = [255, 220, 50, 70];
138010
+ const haloWidth = Math.max(sStrokeWidth + 8, 10);
138011
+ const base = { coordinateSystem: COORDINATE_SYSTEM.CARTESIAN };
138012
+ if (sType === "rectangle") {
138013
+ const { x, y, width, height } = sel;
138014
+ selectionLayers.push(new PathLayer({
138015
+ ...base,
138016
+ id: `annotation-select-${sUid}`,
138017
+ data: [{
138018
+ path: [
138019
+ [x, y],
138020
+ [x + width, y],
138021
+ [x + width, y + height],
138022
+ [x, y + height],
138023
+ [x, y]
138024
+ ]
138025
+ }],
138026
+ getPath: (d) => d.path,
138027
+ getColor: haloColor,
138028
+ getWidth: haloWidth,
138029
+ widthUnits: "pixels",
138030
+ jointRounded: true,
138031
+ capRounded: true
138032
+ }));
138033
+ }
138034
+ if (sType === "line") {
138035
+ const { x1: x12, y1: y12, x2, y2 } = sel;
138036
+ selectionLayers.push(new PathLayer({
138037
+ ...base,
138038
+ id: `annotation-select-${sUid}`,
138039
+ data: [{ path: [[x12, y12], [x2, y2]] }],
138040
+ getPath: (d) => d.path,
138041
+ getColor: haloColor,
138042
+ getWidth: haloWidth,
138043
+ widthUnits: "pixels",
138044
+ capRounded: true
138045
+ }));
138046
+ }
138047
+ if (sType === "ellipse") {
138048
+ const polygon2 = computeEllipsePolygon(sel.x1, sel.y1, sel.radiusX, sel.radiusY);
138049
+ selectionLayers.push(new PathLayer({
138050
+ ...base,
138051
+ id: `annotation-select-${sUid}`,
138052
+ data: [{ path: [...polygon2, polygon2[0]] }],
138053
+ getPath: (d) => d.path,
138054
+ getColor: haloColor,
138055
+ getWidth: haloWidth,
138056
+ widthUnits: "pixels",
138057
+ jointRounded: true
138058
+ }));
138059
+ }
138060
+ if (sType === "polygon" && sel.points?.length >= 3) {
138061
+ selectionLayers.push(new PathLayer({
138062
+ ...base,
138063
+ id: `annotation-select-${sUid}`,
138064
+ data: [{ path: [...sel.points, sel.points[0]] }],
138065
+ getPath: (d) => d.path,
138066
+ getColor: haloColor,
138067
+ getWidth: haloWidth,
138068
+ widthUnits: "pixels",
138069
+ jointRounded: true
138070
+ }));
138071
+ }
138072
+ if (sType === "polyline" && sel.points?.length >= 2) {
138073
+ selectionLayers.push(new PathLayer({
138074
+ ...base,
138075
+ id: `annotation-select-${sUid}`,
138076
+ data: [{ path: sel.points }],
138077
+ getPath: (d) => d.path,
138078
+ getColor: haloColor,
138079
+ getWidth: haloWidth,
138080
+ widthUnits: "pixels",
138081
+ jointRounded: true,
138082
+ capRounded: true
138083
+ }));
138084
+ }
138085
+ }
138086
+ }
138087
+ const layers = [];
138088
+ const dotShapes = [];
138089
+ const clusterDotShapes = [];
138090
+ shapes.forEach((shape, i2) => {
138091
+ const { uid: uid2, type, strokeColor = [255, 255, 255], strokeWidth = 3, strokeDashArray } = shape;
138092
+ const isSelected = uid2 === selectedShapeUid;
138093
+ let shapeOpacity = 1;
138094
+ let tipsOpacity = 1;
138095
+ let labelOpacity = 0;
138096
+ if (isSelected || !semanticZoom || authoredZoom === null) {
138097
+ tipsOpacity = 1;
138098
+ labelOpacity = 1;
138099
+ } else {
138100
+ const zoomRatio = 2 ** (zoom - authoredZoom);
138101
+ if (zoomRatio < RATIO_CLUSTER) {
138102
+ clusterDotShapes.push({ shape });
138103
+ return;
138104
+ }
138105
+ if (zoomRatio < RATIO_DOT) {
138106
+ dotShapes.push({ shape, alpha: 200 });
138107
+ return;
138108
+ }
138109
+ if (zoomRatio < RATIO_GEO_FADE) {
138110
+ const t = (zoomRatio - RATIO_DOT) / (RATIO_GEO_FADE - RATIO_DOT);
138111
+ shapeOpacity = t;
138112
+ dotShapes.push({ shape, alpha: Math.round((1 - t) * 200) });
138113
+ }
138114
+ tipsOpacity = zoomRatio >= RATIO_TIPS ? shapeOpacity : Math.max(0, (zoomRatio - RATIO_GEO_FADE) / (RATIO_TIPS - RATIO_GEO_FADE) * shapeOpacity);
138115
+ if (zoomRatio >= RATIO_LABEL_FULL) {
138116
+ labelOpacity = shapeOpacity;
138117
+ } else if (zoomRatio >= RATIO_LABEL) {
138118
+ labelOpacity = (zoomRatio - RATIO_LABEL) / (RATIO_LABEL_FULL - RATIO_LABEL) * shapeOpacity;
138119
+ }
138120
+ }
138121
+ const arrowDataSize = Math.max(ARROW_SCREEN_PX, strokeWidth * 4) / 2 ** zoom;
138122
+ const base = { coordinateSystem: COORDINATE_SYSTEM.CARTESIAN, opacity: shapeOpacity };
138123
+ const tipsBase = { coordinateSystem: COORDINATE_SYSTEM.CARTESIAN, opacity: tipsOpacity };
138124
+ const labelBase = { coordinateSystem: COORDINATE_SYSTEM.CARTESIAN, opacity: labelOpacity };
138125
+ const labelBgProps = shape.labelBackground ? {
138126
+ background: true,
138127
+ getBackgroundColor: textBgColor(strokeColor),
138128
+ backgroundPadding: [3, 1, 3, 2]
138129
+ } : {};
138130
+ const labelProps = { ...labelBgProps };
138131
+ const dashArray = parseDashArray(strokeDashArray);
138132
+ const dashExt = [new PathStyleExtension({ dash: true })];
138133
+ const getDash = dashArray ?? [0, 0];
138134
+ if (type === "rectangle") {
138135
+ const { x, y, width, height, fillColor = strokeColor, fillOpacity = 0 } = shape;
138136
+ const filled = fillOpacity > 0;
138137
+ const rectPolygon = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]];
138138
+ layers.push(new PolygonLayer({
138139
+ ...base,
138140
+ id: `annotation-rect-${uid2}-${i2}`,
138141
+ data: [{ polygon: rectPolygon }],
138142
+ getPolygon: (d) => d.polygon,
138143
+ filled,
138144
+ stroked: true,
138145
+ getFillColor: filled ? [...fillColor, Math.round(fillOpacity * 255)] : [0, 0, 0, 0],
138146
+ getLineColor: strokeColor,
138147
+ getLineWidth: strokeWidth,
138148
+ lineWidthUnits: "pixels",
138149
+ ...dashArray ? {
138150
+ extensions: [new PathStyleExtension({ dash: true })],
138151
+ getDashArray: dashArray
138152
+ } : {}
138153
+ }));
138154
+ if (shape.text && labelOpacity > 0) {
138155
+ layers.push(new TextLayer({
138156
+ ...labelBase,
138157
+ ...labelProps,
138158
+ id: `annotation-rect-text-${uid2}-${i2}`,
138159
+ data: [{ position: [x, y], text: shape.text }],
138160
+ getPosition: (d) => d.position,
138161
+ getText: (d) => d.text,
138162
+ getColor: strokeColor,
138163
+ getSize: LABEL_FONT_SIZE,
138164
+ sizeUnits: "pixels",
138165
+ fontFamily: LABEL_FONT_FAMILY,
138166
+ fontWeight: LABEL_FONT_WEIGHT,
138167
+ getPixelOffset: [3, -7],
138168
+ getTextAnchor: "start",
138169
+ getAlignmentBaseline: "bottom"
138170
+ }));
138171
+ }
138172
+ }
138173
+ if (type === "line") {
138174
+ const { x1: x12, y1: y12, x2, y2, markerStart, markerEnd } = shape;
138175
+ const dx = x2 - x12;
138176
+ const dy = y2 - y12;
138177
+ const len2 = Math.sqrt(dx * dx + dy * dy);
138178
+ const ux = len2 > 0 ? dx / len2 : 0;
138179
+ const uy = len2 > 0 ? dy / len2 : 0;
138180
+ const srcX = markerStart === "Arrow" ? x12 + ux * arrowDataSize : x12;
138181
+ const srcY = markerStart === "Arrow" ? y12 + uy * arrowDataSize : y12;
138182
+ const tgtX = markerEnd === "Arrow" ? x2 - ux * arrowDataSize : x2;
138183
+ const tgtY = markerEnd === "Arrow" ? y2 - uy * arrowDataSize : y2;
138184
+ layers.push(new PathLayer({
138185
+ ...base,
138186
+ id: `annotation-line-${uid2}-${i2}`,
138187
+ extensions: [new PathStyleExtension({ dash: true })],
138188
+ getDashArray: dashArray ?? [0, 0],
138189
+ data: [{ path: [[srcX, srcY], [tgtX, tgtY]] }],
138190
+ getPath: (d) => d.path,
138191
+ getColor: strokeColor,
138192
+ getWidth: strokeWidth,
138193
+ widthUnits: "pixels",
138194
+ capRounded: true
138195
+ }));
138196
+ if (markerEnd === "Arrow" && tipsOpacity > 0) {
138197
+ const head = computeArrowhead(x2, y2, x2 - x12, y2 - y12, arrowDataSize);
138198
+ if (head) {
138199
+ layers.push(new PolygonLayer({
138200
+ ...tipsBase,
138201
+ id: `annotation-end-arrow-${uid2}-${i2}`,
138202
+ data: [{ polygon: head }],
138203
+ getPolygon: (d) => d.polygon,
138204
+ filled: true,
138205
+ stroked: false,
138206
+ getFillColor: strokeColor
138207
+ }));
138208
+ }
138209
+ }
138210
+ if (markerStart === "Arrow" && tipsOpacity > 0) {
138211
+ const head = computeArrowhead(x12, y12, x12 - x2, y12 - y2, arrowDataSize);
138212
+ if (head) {
138213
+ layers.push(new PolygonLayer({
138214
+ ...tipsBase,
138215
+ id: `annotation-start-arrow-${uid2}-${i2}`,
138216
+ data: [{ polygon: head }],
138217
+ getPolygon: (d) => d.polygon,
138218
+ filled: true,
138219
+ stroked: false,
138220
+ getFillColor: strokeColor
138221
+ }));
138222
+ }
138223
+ }
138224
+ if (markerEnd === "Tick" && tipsOpacity > 0) {
138225
+ const tick = computeTickCap(x2, y2, x2 - x12, y2 - y12, arrowDataSize);
138226
+ if (tick) {
138227
+ layers.push(new PathLayer({
138228
+ ...tipsBase,
138229
+ id: `annotation-end-tick-${uid2}-${i2}`,
138230
+ data: [{ path: tick }],
138231
+ getPath: (d) => d.path,
138232
+ getColor: strokeColor,
138233
+ getWidth: strokeWidth,
138234
+ widthUnits: "pixels",
138235
+ capRounded: true
138236
+ }));
138237
+ }
138238
+ }
138239
+ if (markerStart === "Tick" && tipsOpacity > 0) {
138240
+ const tick = computeTickCap(x12, y12, x12 - x2, y12 - y2, arrowDataSize);
138241
+ if (tick) {
138242
+ layers.push(new PathLayer({
138243
+ ...tipsBase,
138244
+ id: `annotation-start-tick-${uid2}-${i2}`,
138245
+ data: [{ path: tick }],
138246
+ getPath: (d) => d.path,
138247
+ getColor: strokeColor,
138248
+ getWidth: strokeWidth,
138249
+ widthUnits: "pixels",
138250
+ capRounded: true
138251
+ }));
138252
+ }
138253
+ }
138254
+ if (shape.text) {
138255
+ const pos = shape.textPosition ?? "start";
138256
+ let textX;
138257
+ let textY;
138258
+ if (pos === "start") {
138259
+ textX = x12;
138260
+ textY = y12;
138261
+ } else if (pos === "end") {
138262
+ textX = x2;
138263
+ textY = y2;
138264
+ } else {
138265
+ textX = (x12 + x2) / 2;
138266
+ textY = (y12 + y2) / 2;
138267
+ }
138268
+ let pixelOffset = [0, 0];
138269
+ let textAnchor = "middle";
138270
+ let alignmentBaseline = "center";
138271
+ if (pos !== "middle") {
138272
+ const dxT = x2 - x12;
138273
+ const dyT = y2 - y12;
138274
+ const lenT = Math.sqrt(dxT * dxT + dyT * dyT);
138275
+ if (lenT > 0) {
138276
+ const uxT = dxT / lenT;
138277
+ const uyT = dyT / lenT;
138278
+ const sign2 = pos === "start" ? -1 : 1;
138279
+ const bufferPx = shape.textBufferPx ?? 8;
138280
+ const offsetX = sign2 * uxT * bufferPx;
138281
+ const offsetY = sign2 * uyT * bufferPx;
138282
+ pixelOffset = [offsetX, offsetY];
138283
+ const absX = Math.abs(offsetX);
138284
+ const absY = Math.abs(offsetY);
138285
+ if (absX >= absY) {
138286
+ textAnchor = offsetX > 0 ? "start" : "end";
138287
+ }
138288
+ if (absY >= absX) {
138289
+ alignmentBaseline = offsetY > 0 ? "top" : "bottom";
138290
+ }
138291
+ }
138292
+ } else if (len2 > 0) {
138293
+ const belowPerp = pickBelowPerp(ux, uy);
138294
+ const bufferPx = shape.textBufferPx ?? 14;
138295
+ pixelOffset = [-belowPerp[0] * bufferPx, -belowPerp[1] * bufferPx];
138296
+ textAnchor = "middle";
138297
+ alignmentBaseline = "center";
138298
+ }
138299
+ const middleAngle = pos === "middle" && len2 > 0 ? -readableAngle(Math.atan2(dy, dx) * 180 / Math.PI) : null;
138300
+ if (labelOpacity <= 0)
138301
+ return;
138302
+ layers.push(new TextLayer({
138303
+ ...labelBase,
138304
+ ...labelProps,
138305
+ id: `annotation-line-text-${uid2}-${i2}`,
138306
+ data: [{ position: [textX, textY], text: shape.text }],
138307
+ getPosition: (d) => d.position,
138308
+ getText: (d) => d.text,
138309
+ getColor: strokeColor,
138310
+ getSize: LABEL_FONT_SIZE,
138311
+ sizeUnits: "pixels",
138312
+ fontFamily: LABEL_FONT_FAMILY,
138313
+ fontWeight: LABEL_FONT_WEIGHT,
138314
+ getPixelOffset: pixelOffset,
138315
+ getTextAnchor: textAnchor,
138316
+ getAlignmentBaseline: alignmentBaseline,
138317
+ ...middleAngle !== null && { getAngle: middleAngle }
138318
+ }));
138319
+ }
138320
+ }
138321
+ if (type === "ellipse") {
138322
+ const { x1: x12, y1: y12, radiusX, radiusY, fillColor = strokeColor, fillOpacity = 0 } = shape;
138323
+ const polygon2 = computeEllipsePolygon(x12, y12, radiusX, radiusY);
138324
+ const filled = fillOpacity > 0;
138325
+ layers.push(new PolygonLayer({
138326
+ ...base,
138327
+ id: `annotation-ellipse-${uid2}-${i2}`,
138328
+ data: [{ polygon: polygon2 }],
138329
+ getPolygon: (d) => d.polygon,
138330
+ filled,
138331
+ stroked: true,
138332
+ getFillColor: filled ? [...fillColor, Math.round(fillOpacity * 255)] : [0, 0, 0, 0],
138333
+ getLineColor: strokeColor,
138334
+ getLineWidth: strokeWidth,
138335
+ lineWidthUnits: "pixels",
138336
+ ...dashArray ? {
138337
+ extensions: [new PathStyleExtension({ dash: true })],
138338
+ getDashArray: dashArray
138339
+ } : {}
138340
+ }));
138341
+ if (shape.text && labelOpacity > 0) {
138342
+ layers.push(new TextLayer({
138343
+ ...labelBase,
138344
+ ...labelProps,
138345
+ id: `annotation-ellipse-text-${uid2}-${i2}`,
138346
+ data: [{ position: [x12, y12 - radiusY], text: shape.text }],
138347
+ getPosition: (d) => d.position,
138348
+ getText: (d) => d.text,
138349
+ getColor: strokeColor,
138350
+ getSize: LABEL_FONT_SIZE,
138351
+ sizeUnits: "pixels",
138352
+ fontFamily: LABEL_FONT_FAMILY,
138353
+ fontWeight: LABEL_FONT_WEIGHT,
138354
+ getPixelOffset: [0, -4],
138355
+ getTextAnchor: "middle",
138356
+ getAlignmentBaseline: "bottom"
138357
+ }));
138358
+ }
138359
+ }
138360
+ if (type === "polygon") {
138361
+ const { points, fillColor = strokeColor, fillOpacity = 0 } = shape;
138362
+ if (!points || points.length < 3)
138363
+ return;
138364
+ const closedPath = [...points, points[0]];
138365
+ if (fillOpacity > 0) {
138366
+ layers.push(new PolygonLayer({
138367
+ ...base,
138368
+ id: `annotation-polygon-fill-${uid2}-${i2}`,
138369
+ data: [{ polygon: points }],
138370
+ getPolygon: (d) => d.polygon,
138371
+ stroked: false,
138372
+ filled: true,
138373
+ getFillColor: [...fillColor, Math.round(fillOpacity * 255)]
138374
+ }));
138375
+ }
138376
+ layers.push(new PathLayer({
138377
+ ...base,
138378
+ id: `annotation-polygon-${uid2}-${i2}`,
138379
+ extensions: dashExt,
138380
+ getDashArray: getDash,
138381
+ data: [{ path: closedPath }],
138382
+ getPath: (d) => d.path,
138383
+ getColor: strokeColor,
138384
+ getWidth: strokeWidth,
138385
+ widthUnits: "pixels",
138386
+ jointRounded: true
138387
+ }));
138388
+ if (shape.text && labelOpacity > 0) {
138389
+ const minX = Math.min(...points.map((p) => p[0]));
138390
+ const maxX = Math.max(...points.map((p) => p[0]));
138391
+ const minY = Math.min(...points.map((p) => p[1]));
138392
+ const topCenterX = (minX + maxX) / 2;
138393
+ layers.push(new TextLayer({
138394
+ ...labelBase,
138395
+ ...labelProps,
138396
+ id: `annotation-polygon-text-${uid2}-${i2}`,
138397
+ data: [{ position: [topCenterX, minY], text: shape.text }],
138398
+ getPosition: (d) => d.position,
138399
+ getText: (d) => d.text,
138400
+ getColor: strokeColor,
138401
+ getSize: LABEL_FONT_SIZE,
138402
+ sizeUnits: "pixels",
138403
+ fontFamily: LABEL_FONT_FAMILY,
138404
+ fontWeight: LABEL_FONT_WEIGHT,
138405
+ getPixelOffset: [0, -4],
138406
+ getTextAnchor: "middle",
138407
+ getAlignmentBaseline: "bottom"
138408
+ }));
138409
+ }
138410
+ }
138411
+ if (type === "polyline") {
138412
+ const { points, markerStart, markerEnd } = shape;
138413
+ if (!points || points.length < 2)
138414
+ return;
138415
+ let pathPoints = points;
138416
+ if (markerStart === "Arrow") {
138417
+ const [x02, y02] = points[0];
138418
+ const [x12, y12] = points[1];
138419
+ const dx = x02 - x12;
138420
+ const dy = y02 - y12;
138421
+ const len2 = Math.sqrt(dx * dx + dy * dy);
138422
+ if (len2 > 0) {
138423
+ const ux = dx / len2;
138424
+ const uy = dy / len2;
138425
+ pathPoints = [[x02 - ux * arrowDataSize, y02 - uy * arrowDataSize], ...pathPoints.slice(1)];
138426
+ }
138427
+ }
138428
+ if (markerEnd === "Arrow") {
138429
+ const last = pathPoints[pathPoints.length - 1];
138430
+ const prev = pathPoints[pathPoints.length - 2];
138431
+ const dx = last[0] - prev[0];
138432
+ const dy = last[1] - prev[1];
138433
+ const len2 = Math.sqrt(dx * dx + dy * dy);
138434
+ if (len2 > 0) {
138435
+ const ux = dx / len2;
138436
+ const uy = dy / len2;
138437
+ const shortened = [last[0] - ux * arrowDataSize, last[1] - uy * arrowDataSize];
138438
+ pathPoints = [...pathPoints.slice(0, -1), shortened];
138439
+ }
138440
+ }
138441
+ layers.push(new PathLayer({
138442
+ ...base,
138443
+ id: `annotation-polyline-${uid2}-${i2}`,
138444
+ extensions: dashExt,
138445
+ getDashArray: getDash,
138446
+ data: [{ path: pathPoints }],
138447
+ getPath: (d) => d.path,
138448
+ getColor: strokeColor,
138449
+ getWidth: strokeWidth,
138450
+ widthUnits: "pixels",
138451
+ jointRounded: true,
138452
+ capRounded: true
138453
+ }));
138454
+ if (markerStart === "Arrow" && tipsOpacity > 0) {
138455
+ const [x02, y02] = points[0];
138456
+ const [x12, y12] = points[1];
138457
+ const head = computeArrowhead(x02, y02, x02 - x12, y02 - y12, arrowDataSize);
138458
+ if (head) {
138459
+ layers.push(new PolygonLayer({
138460
+ ...tipsBase,
138461
+ id: `annotation-polyline-start-arrow-${uid2}-${i2}`,
138462
+ data: [{ polygon: head }],
138463
+ getPolygon: (d) => d.polygon,
138464
+ filled: true,
138465
+ stroked: false,
138466
+ getFillColor: strokeColor
138467
+ }));
138468
+ }
138469
+ }
138470
+ if (markerEnd === "Arrow" && tipsOpacity > 0) {
138471
+ const last = points[points.length - 1];
138472
+ const prev = points[points.length - 2];
138473
+ const dxArrow = last[0] - prev[0];
138474
+ const dyArrow = last[1] - prev[1];
138475
+ const head = computeArrowhead(last[0], last[1], dxArrow, dyArrow, arrowDataSize);
138476
+ if (head) {
138477
+ layers.push(new PolygonLayer({
138478
+ ...tipsBase,
138479
+ id: `annotation-polyline-end-arrow-${uid2}-${i2}`,
138480
+ data: [{ polygon: head }],
138481
+ getPolygon: (d) => d.polygon,
138482
+ filled: true,
138483
+ stroked: false,
138484
+ getFillColor: strokeColor
138485
+ }));
138486
+ }
138487
+ }
138488
+ if (markerStart === "Tick" && tipsOpacity > 0) {
138489
+ const [x02, y02] = points[0];
138490
+ const [x12, y12] = points[1];
138491
+ const tick = computeTickCap(x02, y02, x02 - x12, y02 - y12, arrowDataSize);
138492
+ if (tick) {
138493
+ layers.push(new PathLayer({
138494
+ ...tipsBase,
138495
+ id: `annotation-polyline-start-tick-${uid2}-${i2}`,
138496
+ data: [{ path: tick }],
138497
+ getPath: (d) => d.path,
138498
+ getColor: strokeColor,
138499
+ getWidth: strokeWidth,
138500
+ widthUnits: "pixels",
138501
+ capRounded: true
138502
+ }));
138503
+ }
138504
+ }
138505
+ if (markerEnd === "Tick" && tipsOpacity > 0) {
138506
+ const last = points[points.length - 1];
138507
+ const prev = points[points.length - 2];
138508
+ const dxTick = last[0] - prev[0];
138509
+ const dyTick = last[1] - prev[1];
138510
+ const tick = computeTickCap(last[0], last[1], dxTick, dyTick, arrowDataSize);
138511
+ if (tick) {
138512
+ layers.push(new PathLayer({
138513
+ ...tipsBase,
138514
+ id: `annotation-polyline-end-tick-${uid2}-${i2}`,
138515
+ data: [{ path: tick }],
138516
+ getPath: (d) => d.path,
138517
+ getColor: strokeColor,
138518
+ getWidth: strokeWidth,
138519
+ widthUnits: "pixels",
138520
+ capRounded: true
138521
+ }));
138522
+ }
138523
+ }
138524
+ if (shape.text) {
138525
+ const pos = shape.textPosition ?? "start";
138526
+ const bufferPx = shape.textBufferPx ?? 8;
138527
+ let textPt;
138528
+ let dirX;
138529
+ let dirY;
138530
+ if (pos === "end") {
138531
+ const last = points[points.length - 1];
138532
+ const prev = points[points.length - 2];
138533
+ textPt = last;
138534
+ dirX = last[0] - prev[0];
138535
+ dirY = last[1] - prev[1];
138536
+ } else if (pos === "middle") {
138537
+ const mx = points.reduce((s2, p) => s2 + p[0], 0) / points.length;
138538
+ const my = points.reduce((s2, p) => s2 + p[1], 0) / points.length;
138539
+ textPt = [mx, my];
138540
+ dirX = 0;
138541
+ dirY = 0;
138542
+ } else {
138543
+ [textPt] = points;
138544
+ dirX = points[0][0] - points[1][0];
138545
+ dirY = points[0][1] - points[1][1];
138546
+ }
138547
+ const [textX, textY] = textPt;
138548
+ const lenT = Math.sqrt(dirX * dirX + dirY * dirY);
138549
+ let polylinePixelOffset = [0, 0];
138550
+ let polylineTextAnchor = "middle";
138551
+ let polylineAlignmentBaseline = pos === "middle" ? "center" : "bottom";
138552
+ if (lenT > 0) {
138553
+ const uxT = dirX / lenT;
138554
+ const uyT = dirY / lenT;
138555
+ const offX = uxT * bufferPx;
138556
+ const offY = uyT * bufferPx;
138557
+ polylinePixelOffset = [offX, offY];
138558
+ if (Math.abs(offX) >= Math.abs(offY)) {
138559
+ polylineTextAnchor = offX > 0 ? "start" : "end";
138560
+ }
138561
+ if (Math.abs(offY) >= Math.abs(offX)) {
138562
+ polylineAlignmentBaseline = offY > 0 ? "top" : "bottom";
138563
+ }
138564
+ }
138565
+ if (labelOpacity <= 0)
138566
+ return;
138567
+ layers.push(new TextLayer({
138568
+ ...labelBase,
138569
+ ...labelProps,
138570
+ id: `annotation-polyline-text-${uid2}-${i2}`,
138571
+ data: [{ position: [textX, textY], text: shape.text }],
138572
+ getPosition: (d) => d.position,
138573
+ getText: (d) => d.text,
138574
+ getColor: strokeColor,
138575
+ getSize: LABEL_FONT_SIZE,
138576
+ sizeUnits: "pixels",
138577
+ fontFamily: LABEL_FONT_FAMILY,
138578
+ fontWeight: LABEL_FONT_WEIGHT,
138579
+ getPixelOffset: polylinePixelOffset,
138580
+ getTextAnchor: polylineTextAnchor,
138581
+ getAlignmentBaseline: polylineAlignmentBaseline
138582
+ }));
138583
+ }
138584
+ }
138585
+ });
138586
+ if (dotShapes.length > 0) {
138587
+ layers.push(new ScatterplotLayer({
138588
+ coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
138589
+ id: "annotation-dots",
138590
+ data: dotShapes.map(({ shape: s2, alpha: alpha2 }) => ({
138591
+ position: getShapeCentroid(s2),
138592
+ color: [...s2.strokeColor ?? [255, 255, 255], alpha2]
138593
+ })),
138594
+ getPosition: (d) => d.position,
138595
+ getFillColor: (d) => d.color,
138596
+ getRadius: 4,
138597
+ radiusUnits: "pixels",
138598
+ filled: true,
138599
+ stroked: false
138600
+ }));
138601
+ }
138602
+ if (clusterDotShapes.length > 0) {
138603
+ const clusters = computeClusters(clusterDotShapes, zoom);
138604
+ layers.push(new ScatterplotLayer({
138605
+ coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
138606
+ id: "annotation-clusters",
138607
+ data: clusters,
138608
+ getPosition: (d) => d.position,
138609
+ getFillColor: (d) => [...d.color, 180],
138610
+ getRadius: (d) => Math.max(6, 4 + Math.sqrt(d.count) * 2),
138611
+ radiusUnits: "pixels",
138612
+ filled: true,
138613
+ stroked: true,
138614
+ getLineColor: (d) => [...d.color, 220],
138615
+ lineWidthMinPixels: 1.5
138616
+ }));
138617
+ const multiClusters = clusters.filter((c) => c.count > 1);
138618
+ if (multiClusters.length > 0) {
138619
+ layers.push(new TextLayer({
138620
+ coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
138621
+ id: "annotation-cluster-counts",
138622
+ data: multiClusters,
138623
+ getPosition: (d) => d.position,
138624
+ getText: (d) => String(d.count),
138625
+ getColor: [255, 255, 255],
138626
+ getSize: 10,
138627
+ sizeUnits: "pixels",
138628
+ fontWeight: "bold",
138629
+ getTextAnchor: "middle",
138630
+ getAlignmentBaseline: "center"
138631
+ }));
138632
+ }
138633
+ }
138634
+ const measureLayers = [];
138635
+ shapes.forEach((shape) => {
138636
+ if (!shape.showMeasure)
138637
+ return;
138638
+ const mIsSelected = shape.uid === selectedShapeUid;
138639
+ const label = getMeasurementLabel(shape, physicalPixelSize, viewType);
138640
+ if (!label)
138641
+ return;
138642
+ const color = shape.strokeColor ?? [255, 255, 255];
138643
+ let mShapeOpacity = 1;
138644
+ let mLabelFactor = 0;
138645
+ if (mIsSelected || !semanticZoom || authoredZoom === null) {
138646
+ mLabelFactor = 1;
138647
+ } else {
138648
+ const mZoomRatio = 2 ** (zoom - authoredZoom);
138649
+ if (mZoomRatio < RATIO_DOT)
138650
+ return;
138651
+ if (mZoomRatio < RATIO_GEO_FADE) {
138652
+ mShapeOpacity = (mZoomRatio - RATIO_DOT) / (RATIO_GEO_FADE - RATIO_DOT);
138653
+ }
138654
+ if (mZoomRatio >= RATIO_LABEL_FULL) {
138655
+ mLabelFactor = 1;
138656
+ } else if (mZoomRatio >= RATIO_LABEL) {
138657
+ mLabelFactor = (mZoomRatio - RATIO_LABEL) / (RATIO_LABEL_FULL - RATIO_LABEL);
138658
+ }
138659
+ }
138660
+ const measureOpacity = mShapeOpacity * mLabelFactor;
138661
+ if (measureOpacity <= 0)
138662
+ return;
138663
+ const base = {
138664
+ coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
138665
+ opacity: measureOpacity,
138666
+ getText: (d) => d.text,
138667
+ getPosition: (d) => d.position,
138668
+ getColor: color,
138669
+ getSize: 13,
138670
+ sizeUnits: "pixels",
138671
+ fontFamily: LABEL_FONT_FAMILY,
138672
+ fontWeight: "bold",
138673
+ // 'auto' builds the font atlas from the actual rendered characters (e.g. µ, ²)
138674
+ // instead of deck.gl's default ASCII-only atlas.
138675
+ characterSet: "auto",
138676
+ ...shape.measureBackground ? {
138677
+ background: true,
138678
+ getBackgroundColor: textBgColor(color),
138679
+ backgroundPadding: [3, 1, 3, 2]
138680
+ } : {}
138681
+ };
138682
+ if (shape.type === "line") {
138683
+ const mx = ((shape.x1 ?? 0) + (shape.x2 ?? 0)) / 2;
138684
+ const my = ((shape.y1 ?? 0) + (shape.y2 ?? 0)) / 2;
138685
+ const dx = (shape.x2 ?? 0) - (shape.x1 ?? 0);
138686
+ const dy = (shape.y2 ?? 0) - (shape.y1 ?? 0);
138687
+ const len2 = Math.sqrt(dx * dx + dy * dy);
138688
+ if (len2 === 0)
138689
+ return;
138690
+ const perp = pickBelowPerp(dx / len2, dy / len2);
138691
+ const angle2 = readableAngle(Math.atan2(dy, dx) * 180 / Math.PI);
138692
+ measureLayers.push(new TextLayer({
138693
+ ...base,
138694
+ id: `annotation-measure-${shape.uid}`,
138695
+ data: [{ position: [mx, my], text: label }],
138696
+ getAngle: -angle2,
138697
+ getTextAnchor: "middle",
138698
+ getAlignmentBaseline: "center",
138699
+ getPixelOffset: [perp[0] * 16, perp[1] * 16]
138700
+ }));
138701
+ } else if (shape.type === "polyline" && shape.points?.length >= 2) {
138702
+ const pts = shape.points;
138703
+ const mi = Math.floor((pts.length - 1) / 2);
138704
+ const [ax, ay] = pts[mi];
138705
+ const [bx, by] = pts[mi + 1];
138706
+ const dx = bx - ax;
138707
+ const dy = by - ay;
138708
+ const len2 = Math.sqrt(dx * dx + dy * dy);
138709
+ if (len2 === 0)
138710
+ return;
138711
+ const perp = pickBelowPerp(dx / len2, dy / len2);
138712
+ const angle2 = readableAngle(Math.atan2(dy, dx) * 180 / Math.PI);
138713
+ measureLayers.push(new TextLayer({
138714
+ ...base,
138715
+ id: `annotation-measure-${shape.uid}`,
138716
+ data: [{ position: [(ax + bx) / 2, (ay + by) / 2], text: label }],
138717
+ getAngle: -angle2,
138718
+ getTextAnchor: "middle",
138719
+ getAlignmentBaseline: "center",
138720
+ getPixelOffset: [perp[0] * 16, perp[1] * 16]
138721
+ }));
138722
+ } else if (shape.type === "rectangle") {
138723
+ const x02 = shape.x ?? 0;
138724
+ const y02 = shape.y ?? 0;
138725
+ const w2 = shape.width ?? 0;
138726
+ const h = shape.height ?? 0;
138727
+ const rx = x02 + (w2 >= 0 ? w2 : 0);
138728
+ const ry = y02 + (h >= 0 ? h : 0);
138729
+ measureLayers.push(new TextLayer({
138730
+ ...base,
138731
+ id: `annotation-measure-${shape.uid}`,
138732
+ data: [{ position: [rx, ry], text: label }],
138733
+ getTextAnchor: "end",
138734
+ getAlignmentBaseline: "bottom",
138735
+ getPixelOffset: [-6, -6]
138736
+ }));
138737
+ } else if (shape.type === "ellipse") {
138738
+ const cx = shape.x1 ?? 0;
138739
+ const cy = shape.y1 ?? 0;
138740
+ const ry = Math.abs(shape.radiusY ?? 0);
138741
+ measureLayers.push(new TextLayer({
138742
+ ...base,
138743
+ id: `annotation-measure-${shape.uid}`,
138744
+ data: [{ position: [cx, cy + ry], text: label }],
138745
+ getTextAnchor: "middle",
138746
+ getAlignmentBaseline: "top",
138747
+ getPixelOffset: [0, 8]
138748
+ }));
138749
+ } else if (shape.type === "polygon" && shape.points?.length >= 3) {
138750
+ const xs = shape.points.map((p) => p[0]);
138751
+ const ys = shape.points.map((p) => p[1]);
138752
+ const cx = (Math.min(...xs) + Math.max(...xs)) / 2;
138753
+ const maxY = Math.max(...ys);
138754
+ measureLayers.push(new TextLayer({
138755
+ ...base,
138756
+ id: `annotation-measure-${shape.uid}`,
138757
+ data: [{ position: [cx, maxY], text: label }],
138758
+ getTextAnchor: "middle",
138759
+ getAlignmentBaseline: "top",
138760
+ getPixelOffset: [0, 8]
138761
+ }));
138762
+ }
138763
+ });
138764
+ return [...selectionLayers, ...layers, ...measureLayers];
138765
+ }
138766
+ const defaultProps = {
138767
+ // Array of annotation shape objects (see annotationShapeObj in @vitessce/schemas).
138768
+ data: { type: "object", value: [], compare: true },
138769
+ // UID of the shape to highlight with a selection halo.
138770
+ selectedShapeUid: { type: "string", value: null },
138771
+ // { x, y, unit } used to format measurement labels in physical units.
138772
+ physicalPixelSize: { type: "object", value: null, compare: true },
138773
+ // The zoom level at which the shapes were authored (reference for semantic zoom).
138774
+ authoredZoom: { type: "number", value: null },
138775
+ // Whether to apply semantic zoom (level-of-detail) based on authoredZoom.
138776
+ semanticZoom: { type: "boolean", value: true },
138777
+ // The view type in which the layer is rendered (affects measurement label formatting).
138778
+ viewType: { type: "string", value: ViewType.SPATIAL },
138779
+ // In-progress shape being drawn, as { type, vertices }.
138780
+ inProgressShape: { type: "object", value: null, compare: true },
138781
+ // Current mouse position (in data coordinates) while drawing.
138782
+ hoverCoord: { type: "array", value: null, compare: true },
138783
+ // Stroke color [r, g, b] of the in-progress shape preview.
138784
+ previewStrokeColor: { type: "color", value: [255, 255, 0] }
138785
+ };
138786
+ class AnnotationLayer extends CompositeLayer {
138787
+ // The semantic zoom and arrowhead sizes depend on the current zoom level,
138788
+ // so the sublayers must also be re-rendered when the viewport changes.
138789
+ // eslint-disable-next-line class-methods-use-this
138790
+ shouldUpdateState({ changeFlags }) {
138791
+ return changeFlags.propsOrDataChanged || changeFlags.viewportChanged;
138792
+ }
138793
+ /**
138794
+ * Prefix the sublayer ID with this layer's ID (so that multiple
138795
+ * AnnotationLayers can co-exist), and combine this layer's
138796
+ * opacity with the per-shape opacity of the sublayer.
138797
+ * Note: we avoid getSubLayerProps here, since it would
138798
+ * overwrite the sublayer extensions (e.g., PathStyleExtension)
138799
+ * and opacity values.
138800
+ * @param {object} layer A DeckGL layer instance.
138801
+ * @returns {object} The cloned layer instance.
138802
+ */
138803
+ wrapSubLayer(layer) {
138804
+ const { id, opacity, pickable, visible } = this.props;
138805
+ return layer.clone({
138806
+ id: `${id}-${layer.id}`,
138807
+ opacity: layer.props.opacity * opacity,
138808
+ pickable,
138809
+ visible
138810
+ });
138811
+ }
138812
+ renderLayers() {
138813
+ const { data, selectedShapeUid, physicalPixelSize, authoredZoom, semanticZoom, viewType, inProgressShape, hoverCoord, previewStrokeColor } = this.props;
138814
+ const zoom = this.context.viewport?.zoom ?? 0;
138815
+ const visibleShapes = (data ?? []).filter((shape) => shape.visible !== false);
138816
+ const annotationLayers = createAnnotationLayers(visibleShapes, zoom, selectedShapeUid, physicalPixelSize, authoredZoom, semanticZoom, viewType);
138817
+ const previewLayer = createPreviewLayer(inProgressShape, hoverCoord, previewStrokeColor.slice(0, 3));
138818
+ return [
138819
+ ...annotationLayers,
138820
+ ...previewLayer ? [previewLayer] : []
138821
+ ].map((layer) => this.wrapSubLayer(layer));
138822
+ }
138823
+ }
138824
+ AnnotationLayer.layerName = "AnnotationLayer";
138825
+ AnnotationLayer.defaultProps = defaultProps;
137627
138826
  const toRgbUIString = (on, arr, theme) => {
137628
138827
  const color = on || theme !== "dark" && arr?.every((i2) => i2 === 255) ? [220, 220, 220] : arr;
137629
138828
  return `rgb(${color})`;
@@ -137641,8 +138840,8 @@ async function getSingleSelectionStats2D({ loader, selection }) {
137641
138840
  const filteredSelection = filterSelection(loader, selection);
137642
138841
  const raster = await data.getRaster({ selection: filteredSelection });
137643
138842
  const selectionStats = getChannelStats(raster.data);
137644
- const { domain, contrastLimits: slider } = selectionStats;
137645
- return { domain, slider };
138843
+ const { domain, contrastLimits: slider, q1, q3 } = selectionStats;
138844
+ return { domain, slider, q1, q3 };
137646
138845
  }
137647
138846
  async function getSingleSelectionStats3D({ loader, selection }) {
137648
138847
  const lowResSource = loader[loader.length - 1];
@@ -137677,7 +138876,9 @@ async function getSingleSelectionStats3D({ loader, selection }) {
137677
138876
  statsMid.contrastLimits[1],
137678
138877
  statsTop.contrastLimits[1]
137679
138878
  )
137680
- ]
138879
+ ],
138880
+ q1: Math.min(stats0.q1[0], statsMid.q1[0], statsTop.q1[0]),
138881
+ q3: Math.max(stats0.q3[1], statsMid.q3[1], statsTop.q3[1])
137681
138882
  };
137682
138883
  }
137683
138884
  const getSingleSelectionStats = async ({ loader, selection, use3d }) => {
@@ -137690,7 +138891,9 @@ const getMultiSelectionStats = async ({ loader, selections, use3d }) => {
137690
138891
  );
137691
138892
  const domains = stats.map((stat) => stat.domain);
137692
138893
  const sliders = stats.map((stat) => stat.slider);
137693
- return { domains, sliders };
138894
+ const q1s = stats.map((stat) => stat.q1);
138895
+ const q3s = stats.map((stat) => stat.q3);
138896
+ return { domains, sliders, q1s, q3s };
137694
138897
  };
137695
138898
  function getPhysicalSizeScalingMatrix(loader) {
137696
138899
  const { x, y, z } = loader?.meta?.physicalSizes ?? {};