@genome-spy/app 0.88.0 → 0.88.1

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.
@@ -3100,11 +3100,15 @@ var pc = class {
3100
3100
  all: 0,
3101
3101
  matching: 1,
3102
3102
  nonmatching: 2
3103
- }, kc = class {
3103
+ };
3104
+ function kc(e, t) {
3105
+ return e.replace(/#pragma orderGuard(?:[ \t]*\r?\n){0,2}/, t);
3106
+ }
3107
+ var Ac = class {
3104
3108
  #e = [];
3105
3109
  #t = !1;
3106
3110
  constructor(e, t, n) {
3107
- this.mark = e, this._glHelper = t, this._rendererResources = n, this.bufferInfo = void 0, this.bytesPerElement = /* @__PURE__ */ new Map(), this.programInfo = void 0, this.vertexArrayInfo = void 0, this.viewUniformInfo = void 0, this.markUniformInfo = void 0, this.markUniformsAltered = !0, this.selectionTextureOps = [], this.scaleResolutionDisposers = [], this.rangeMap = new Ac();
3111
+ this.mark = e, this._glHelper = t, this._rendererResources = n, this.bufferInfo = void 0, this.bytesPerElement = /* @__PURE__ */ new Map(), this.programInfo = void 0, this.vertexArrayInfo = void 0, this.viewUniformInfo = void 0, this.markUniformInfo = void 0, this.markUniformsAltered = !0, this.selectionTextureOps = [], this.scaleResolutionDisposers = [], this.rangeMap = new jc();
3108
3112
  }
3109
3113
  get unitView() {
3110
3114
  return this.mark.unitView;
@@ -3250,7 +3254,7 @@ var pc = class {
3250
3254
  return ${e.join(" || ")};\n}`);
3251
3255
  }
3252
3256
  let oe = "precision highp float;\nprecision highp int;", ce = (e) => e.replace("#pragma markUniforms", S.join("\n"));
3253
- d = d.map(ce), n = ce(n), s = ce(s), n = ((e) => e.replace("#pragma orderGuard\n\n", h ? " if (uOrderMode != 0 &&\n (((uOrderMode & 3) == 1) != isOrderMatch())) {\n gl_Position = vec4(100.0, 0.0, 0.0, 0.0);\n return;\n }" : ""))(n);
3257
+ d = d.map(ce), n = ce(n), s = ce(s), n = ((e) => kc(e, h ? " if (uOrderMode != 0 &&\n (((uOrderMode & 3) == 1) != isOrderMatch())) {\n gl_Position = vec4(100.0, 0.0, 0.0, 0.0);\n return;\n }" : ""))(n);
3254
3258
  let ue = [
3255
3259
  oe,
3256
3260
  v,
@@ -3410,7 +3414,7 @@ var pc = class {
3410
3414
  }
3411
3415
  setViewport(e, t, n, r, i, a = wc) {
3412
3416
  n = n.flatten();
3413
- let o = this.gl, s = this.properties, c = a, l = a, u, d = jc(e, n, s.clip === "never" ? void 0 : r, !1), f = d.coords;
3417
+ let o = this.gl, s = this.properties, c = a, l = a, u, d = Mc(e, n, s.clip === "never" ? void 0 : r, !1), f = d.coords;
3414
3418
  if (d.requiresScissor) {
3415
3419
  if (!f.isDefined()) return !1;
3416
3420
  let r = [
@@ -3433,12 +3437,12 @@ var pc = class {
3433
3437
  return $a(this.viewUniformInfo, {
3434
3438
  ...u,
3435
3439
  uViewportSize: [n.width, n.height],
3436
- uLogicalVisibleRect: Mc(n, i),
3440
+ uLogicalVisibleRect: Nc(n, i),
3437
3441
  uCullByVisibleRange: [+(s.cullByVisibleRange === !0 || s.cullByVisibleRange === "x"), +(s.cullByVisibleRange === !0 || s.cullByVisibleRange === "y")],
3438
3442
  uDevicePixelRatio: t
3439
3443
  }), Qa(this.gl, this.programInfo, this.viewUniformInfo), !0;
3440
3444
  }
3441
- }, Ac = class extends fe {
3445
+ }, jc = class extends fe {
3442
3446
  constructor() {
3443
3447
  super([], JSON.stringify);
3444
3448
  }
@@ -3455,7 +3459,7 @@ var pc = class {
3455
3459
  for (let [t, n] of e.entries()) Object.assign(this.get(t), n);
3456
3460
  }
3457
3461
  };
3458
- function jc(e, t, n, r = !0) {
3462
+ function Mc(e, t, n, r = !0) {
3459
3463
  if (!n || !n.clipX && !n.clipY) return {
3460
3464
  requiresScissor: !1,
3461
3465
  coords: t
@@ -3472,7 +3476,7 @@ function jc(e, t, n, r = !0) {
3472
3476
  coords: i.flatten()
3473
3477
  };
3474
3478
  }
3475
- function Mc(e, t) {
3479
+ function Nc(e, t) {
3476
3480
  let n = t?.clipX ? (t.rect.x - e.x) / e.width : 0, r = t?.clipX ? (t.rect.x2 - e.x) / e.width : 1;
3477
3481
  return [
3478
3482
  n,
@@ -3483,7 +3487,7 @@ function Mc(e, t) {
3483
3487
  }
3484
3488
  //#endregion
3485
3489
  //#region ../core/src/rendering/webgl/marks/arrow.js
3486
- var Nc = Math.PI / 180, Pc = 1e-6, Fc = 1, Ic = 90, Lc = ["triangle", "open"], Rc = ["inside", "outside"], zc = class extends kc {
3490
+ var Pc = Math.PI / 180, Fc = 1e-6, Ic = 1, Lc = 90, Rc = ["triangle", "open"], zc = ["inside", "outside"], Bc = class extends Ac {
3487
3491
  getAttributes() {
3488
3492
  return [
3489
3493
  "uniqueId",
@@ -3511,7 +3515,7 @@ var Nc = Math.PI / 180, Pc = 1e-6, Fc = 1, Ic = 90, Lc = ["triangle", "open"], R
3511
3515
  finalizeGraphicsInitialization() {
3512
3516
  super.finalizeGraphicsInitialization(), this.gl.useProgram(this.programInfo.program);
3513
3517
  let e = this.properties;
3514
- this.registerMarkUniformValue("uHeadSlope", e.headAngle, Vc), this.registerMarkUniformValue("uHeadNotchSlope", e.headNotchAngle, Vc), this.registerMarkUniformValue("uHeadShape", e.headShape, (e) => Bc(Lc, e)), this.registerMarkUniformValue("uMinSize", e.minSize), this.registerMarkUniformValue("uHeadWidth", e.headWidth), this.registerMarkUniformValue("uStartNotch", e.startNotch), this.registerMarkUniformValue("uMinStemLength", e.minStemLength), this.registerMarkUniformValue("uHeadSpacing", e.headSpacing ?? -1, Hc), this.registerMarkUniformValue("uStem", e.stem), this.registerMarkUniformValue("uHeadPlacement", e.headPlacement, (e) => Bc(Rc, e));
3518
+ this.registerMarkUniformValue("uHeadSlope", e.headAngle, Hc), this.registerMarkUniformValue("uHeadNotchSlope", e.headNotchAngle, Hc), this.registerMarkUniformValue("uHeadShape", e.headShape, (e) => Vc(Rc, e)), this.registerMarkUniformValue("uMinSize", e.minSize), this.registerMarkUniformValue("uHeadWidth", e.headWidth), this.registerMarkUniformValue("uStartNotch", e.startNotch), this.registerMarkUniformValue("uMinStemLength", e.minStemLength), this.registerMarkUniformValue("uHeadSpacing", e.headSpacing ?? -1, Uc), this.registerMarkUniformValue("uStem", e.stem), this.registerMarkUniformValue("uHeadPlacement", e.headPlacement, (e) => Vc(zc, e));
3515
3519
  }
3516
3520
  updateGraphicsData(e) {
3517
3521
  let t = e.getItemCount(), n = new hc({
@@ -3534,25 +3538,25 @@ var Nc = Math.PI / 180, Pc = 1e-6, Fc = 1, Ic = 90, Lc = ["triangle", "open"], R
3534
3538
  }, e);
3535
3539
  }
3536
3540
  };
3537
- function Bc(e, t) {
3541
+ function Vc(e, t) {
3538
3542
  let n = e.indexOf(t);
3539
3543
  if (n < 0) throw Error(`Unsupported arrow mark value: ${t}`);
3540
3544
  return n;
3541
3545
  }
3542
- function Vc(e) {
3543
- return Math.max(Math.tan(Math.min(Math.max(e, Fc), Ic) * Nc), Pc);
3544
- }
3545
3546
  function Hc(e) {
3547
+ return Math.max(Math.tan(Math.min(Math.max(e, Ic), Lc) * Pc), Fc);
3548
+ }
3549
+ function Uc(e) {
3546
3550
  return e ?? -1;
3547
3551
  }
3548
3552
  //#endregion
3549
3553
  //#region ../core/src/rendering/webgl/marks/link.vertex.glsl
3550
- var Uc = "flat out vec4 vColor;\n\n/** Stroke width */\nflat out float vSize;\n\n/** The distance from the line center to the direction of normal in pixels */\nout float vNormalLengthInPixels;\n\nflat out float vGamma;\n\n// Centerline distance from the rendered endpoint baseline; negative disables fading.\nout float vFadeDistance;\n\nconst int SHAPE_ARC = 0;\nconst int SHAPE_DOME = 1;\nconst int SHAPE_DIAGONAL = 2;\nconst int SHAPE_LINE = 3;\nconst int ORIENT_VERTICAL = 0;\nconst int ORIENT_HORIZONTAL = 1;\n\nfloat distanceFromLine(vec2 pointOnLine1, vec2 pointOnLine2, vec2 point) {\n vec2 a = point - pointOnLine1;\n vec2 b = pointOnLine2 - pointOnLine1;\n vec2 proj = dot(a, b) / dot(b, b) * b;\n return length(a - proj);\n}\n\nbool isInsideViewport(vec2 point, float marginFactor) {\n vec2 margin = uViewportSize * vec2(marginFactor);\n return point.x >= -margin.x\n && point.x <= uViewportSize.x + margin.x\n && point.y >= -margin.y\n && point.y <= uViewportSize.y + margin.y;\n}\n\nfloat inverseSmoothstep(float t) {\n t = clamp(t, 0.0, 1.0);\n // The chord-axis coordinate of ARC/DOME follows the smoothstep curve.\n return 0.5 - sin(asin(1.0 - 2.0 * t) / 3.0);\n}\n\n/**\n * Remaps the parameter t to concentrate vertices to the part that is visible in the viewport.\n * This keeps the tightly bent endpoints smooth even when zooming in very close.\n */\nfloat remapVisibleChordParameter(\n float stripT,\n float chordStart,\n float chordEnd,\n float viewportLength\n) {\n // Concentrate samples in the viewport-visible chord interval without dropping the rest.\n float chordMin = min(chordStart, chordEnd);\n float chordMax = max(chordStart, chordEnd);\n float chordSpan = chordMax - chordMin;\n\n if (chordSpan <= 0.0) {\n return 0.0;\n }\n\n float visibleChordMin = max(chordMin, 0.0);\n float visibleChordMax = min(chordMax, viewportLength);\n\n if (visibleChordMax <= visibleChordMin) {\n return stripT;\n }\n\n float visibleTMin = inverseSmoothstep((visibleChordMin - chordMin) / chordSpan);\n float visibleTMax = inverseSmoothstep((visibleChordMax - chordMin) / chordSpan);\n float visibleTSpan = visibleTMax - visibleTMin;\n float offscreenTSpan = visibleTMin + (1.0 - visibleTMax);\n\n if (offscreenTSpan <= 0.0) {\n return stripT;\n }\n\n float visibleShare = clamp(0.75 + (1.0 - visibleTSpan) * 0.2, 0.75, 0.95);\n float offscreenShare = 1.0 - visibleShare;\n float leftShare = offscreenShare * visibleTMin / offscreenTSpan;\n float rightShare = offscreenShare * (1.0 - visibleTMax) / offscreenTSpan;\n\n if (stripT <= leftShare) {\n return leftShare > 0.0 ? mix(0.0, visibleTMin, stripT / leftShare) : visibleTMin;\n }\n\n float visibleStart = leftShare;\n float visibleEnd = visibleStart + visibleShare;\n\n if (stripT <= visibleEnd) {\n return visibleShare > 0.0\n ? mix(visibleTMin, visibleTMax, (stripT - visibleStart) / visibleShare)\n : visibleTMin;\n }\n\n return rightShare > 0.0\n ? mix(visibleTMax, 1.0, (stripT - visibleEnd) / rightShare)\n : visibleTMax;\n}\n\nvoid clampChordToViewport(inout vec2 p1, inout vec2 p4, inout float chordLength) {\n if (chordLength > uMaxChordLength) {\n vec2 chordVector = p4 - p1;\n vec2 unitChordVector = normalize(chordVector);\n\n if (isInsideViewport(p1, 2.0)) {\n chordLength = uMaxChordLength;\n p4 = p1 + unitChordVector * uMaxChordLength;\n } else if (isInsideViewport(p4, 2.0)) {\n chordLength = uMaxChordLength;\n p1 = p4 - unitChordVector * uMaxChordLength;\n }\n }\n}\n\nvoid clampDomeApex(inout vec2 p1, inout vec2 p4, int orient) {\n if (orient == ORIENT_VERTICAL) {\n if (p4.x > 0.0) {\n p1.x = max(p1.x, -p4.x);\n }\n if (p1.x < uViewportSize.x) {\n p4.x = min(p4.x, 2.0 * uViewportSize.x - p1.x);\n }\n } else {\n if (p4.y > 0.0) {\n p1.y = max(p1.y, -p4.y);\n }\n if (p1.y < uViewportSize.y) {\n p4.y = min(p4.y, 2.0 * uViewportSize.y - p1.y);\n }\n }\n}\n\nvoid main(void) {\n#pragma orderGuard\n\n float pixelSize = 1.0 / uDevicePixelRatio;\n float opacity = getScaled_opacity() * uViewOpacity;\n\n // The bezier's control points\n vec2 p1, p2, p3, p4;\n\n vec2 a = applyOffset(\n applySampleFacet(vec2(getScaled_x(), getScaled_y())),\n vec2(getScaled_xOffset(), getScaled_yOffset())\n ) * uViewportSize;\n vec2 b = applyOffset(\n applySampleFacet(vec2(getScaled_x2(), getScaled_y2())),\n vec2(getScaled_x2Offset(), getScaled_y2Offset())\n ) * uViewportSize;\n\n if (uShape <= SHAPE_DOME) {\n if (uShape == SHAPE_DOME) {\n vec2 height = vec2(0.0);\n if (uOrient == ORIENT_VERTICAL) {\n p1 = vec2(min(a.x, b.x), b.y);\n p4 = vec2(max(a.x, b.x), b.y);\n height = vec2(0.0, a.y - b.y);\n\n float chordLength = length(p4 - p1);\n clampChordToViewport(p1, p4, chordLength);\n if (uClampApex) {\n clampDomeApex(p1, p4, ORIENT_VERTICAL);\n }\n\n } else {\n p1 = vec2(b.x, min(a.y, b.y));\n p4 = vec2(b.x, max(a.y, b.y));\n height = vec2(a.x - b.x, 0.0);\n\n float chordLength = length(p4 - p1);\n clampChordToViewport(p1, p4, chordLength);\n if (uClampApex) {\n clampDomeApex(p1, p4, ORIENT_HORIZONTAL);\n }\n }\n\n vec2 controlOffset = height / 0.75;\n p2 = p1 + controlOffset;\n p3 = p4 + controlOffset;\n\n } if (uShape == SHAPE_ARC) {\n p1 = a;\n p4 = b;\n\n vec2 chordVector = p4 - p1;\n vec2 unitChordVector = normalize(chordVector);\n vec2 chordNormal = vec2(-unitChordVector.y, unitChordVector.x);\n float chordLength = length(chordVector);\n clampChordToViewport(p1, p4, chordLength);\n\n float height = max(\n chordLength / 2.0 * uArcHeightFactor,\n uMinArcHeight\n );\n\n // This is a bit poor approximation of a circular arc, but it's probably enough for most purposes.\n // TODO: Consider a more sophisticated approach: https://stackoverflow.com/a/44829356/1547896\n vec2 controlOffset = chordNormal * height / 0.75;\n p2 = p1 + controlOffset;\n p3 = p4 + controlOffset;\n }\n\n } else if (uShape == SHAPE_DIAGONAL) {\n if (uOrient == ORIENT_VERTICAL) {\n p1 = a;\n p2 = vec2(a.x, (a.y + b.y) / 2.0);\n p3 = vec2(b.x, (a.y + b.y) / 2.0);\n p4 = b;\n } else {\n p1 = a;\n p2 = vec2((a.x + b.x) / 2.0, a.y);\n p3 = vec2((a.x + b.x) / 2.0, b.y);\n p4 = b;\n }\n } else if (uShape == SHAPE_LINE) {\n p1 = a;\n p2 = (a + b) / 2.0;\n p3 = p2;\n p4 = b;\n }\n\n vec2 strip = vec2(\n float(gl_VertexID / 2) / float(uSegmentBreaks),\n float(gl_VertexID % 2) - 0.5\n );\n\n float t = strip.x;\n\n if (uShape == SHAPE_DOME) {\n if (uOrient == ORIENT_VERTICAL) {\n t = remapVisibleChordParameter(strip.x, p1.x, p4.x, uViewportSize.x);\n } else {\n t = remapVisibleChordParameter(strip.x, p1.y, p4.y, uViewportSize.y);\n }\n } else if (uShape == SHAPE_ARC) {\n if (a.y == b.y) {\n t = remapVisibleChordParameter(strip.x, p1.x, p4.x, uViewportSize.x);\n } else if (a.x == b.x) {\n t = remapVisibleChordParameter(strip.x, p1.y, p4.y, uViewportSize.y);\n }\n }\n\n vec2 p;\n vec2 tangent;\n\n // de Casteljau evaluation keeps the cubic stable for long chords.\n vec2 q1 = mix(p1, p2, t);\n vec2 q2 = mix(p2, p3, t);\n vec2 q3 = mix(p3, p4, t);\n\n vec2 r1 = mix(q1, q2, t);\n vec2 r2 = mix(q2, q3, t);\n\n p = mix(r1, r2, t);\n tangent = 3.0 * (r2 - r1);\n\n tangent = normalize(tangent);\n vec2 normal = vec2(-tangent.y, tangent.x);\n\n float size = getScaled_size();\n\n // Avoid artifacts in very thin lines by clamping the size and adjusting opacity respectively\n if (size < pixelSize) {\n opacity *= size / pixelSize;\n size = pixelSize;\n }\n\n // Handle minimum picking size or add an extra pixel to the stroke width to accommodate edge antialiasing\n float paddedSize = uPickingEnabled\n ? max(size, uMinPickingSize)\n : size + pixelSize;\n\n vNormalLengthInPixels = strip.y * paddedSize;\n \n vFadeDistance = -1.0;\n if ((uShape == SHAPE_ARC || uShape == SHAPE_DOME) &&\n uArcFadingDistance[0] > 0.0 &&\n uArcFadingDistance[1] > 0.0\n#ifdef CONDITIONAL_ORDER\n && (!uNoFadingOnSecondPass || (uOrderMode & 4) == 0)\n#endif\n )\n {\n vFadeDistance = distanceFromLine(p1, p4, p);\n // Keep fully faded triangles collapsed to avoid fragment processing.\n if (1.0 - smoothstep(uArcFadingDistance[0], uArcFadingDistance[1], vFadeDistance) <= 0.0) {\n vNormalLengthInPixels = 0.0;\n }\n }\n\n // Extrude\n p += normal * vNormalLengthInPixels;\n\n gl_Position = pixelsToNdc(p);\n\n vec3 color = getScaled_color();\n\n vColor = vec4(color * opacity, opacity);\n\n vGamma = getGammaForColor(color);\n\n vSize = paddedSize;\n\n setupPicking();\n}\n", Wc = "flat in vec4 vColor;\nflat in float vSize;\nin float vNormalLengthInPixels;\nflat in float vGamma;\nin float vFadeDistance;\n\nout lowp vec4 fragColor;\n\nvoid main(void) {\n float dpr = uDevicePixelRatio;\n\n float distance = abs(vNormalLengthInPixels);\n float opacity = clamp(((vSize / 2.0 - distance) * dpr), 0.0, 1.0);\n\n opacity = pow(opacity, vGamma);\n\n if (vFadeDistance >= 0.0) {\n opacity *= 1.0 - smoothstep(uArcFadingDistance[0], uArcFadingDistance[1], vFadeDistance);\n }\n if (opacity <= 0.0) {\n discard;\n }\n\n fragColor = vColor * opacity;\n\n if (uPickingEnabled) {\n fragColor = vPickingColor;\n }\n}\n", Gc = "layout(std140) uniform Mark {\n uniform float uArcHeightFactor;\n\n /** Make very small arcs visible */\n uniform float uMinArcHeight;\n\n /** The minimum stroke width in pixels when rendering into the picking buffer */\n uniform float uMinPickingSize;\n\n uniform int uShape;\n uniform int uOrient;\n uniform bool uClampApex;\n\n // In pixels\n uniform float uMaxChordLength;\n // In pixels\n uniform vec2 uArcFadingDistance;\n uniform bool uNoFadingOnSecondPass;\n\n uniform int uSegmentBreaks;\n\n#pragma markUniforms\n};\n", Kc = [
3554
+ var Wc = "flat out vec4 vColor;\n\n/** Stroke width */\nflat out float vSize;\n\n/** The distance from the line center to the direction of normal in pixels */\nout float vNormalLengthInPixels;\n\nflat out float vGamma;\n\n// Centerline distance from the rendered endpoint baseline; negative disables fading.\nout float vFadeDistance;\n\nconst int SHAPE_ARC = 0;\nconst int SHAPE_DOME = 1;\nconst int SHAPE_DIAGONAL = 2;\nconst int SHAPE_LINE = 3;\nconst int ORIENT_VERTICAL = 0;\nconst int ORIENT_HORIZONTAL = 1;\n\nfloat distanceFromLine(vec2 pointOnLine1, vec2 pointOnLine2, vec2 point) {\n vec2 a = point - pointOnLine1;\n vec2 b = pointOnLine2 - pointOnLine1;\n vec2 proj = dot(a, b) / dot(b, b) * b;\n return length(a - proj);\n}\n\nbool isInsideViewport(vec2 point, float marginFactor) {\n vec2 margin = uViewportSize * vec2(marginFactor);\n return point.x >= -margin.x\n && point.x <= uViewportSize.x + margin.x\n && point.y >= -margin.y\n && point.y <= uViewportSize.y + margin.y;\n}\n\nfloat inverseSmoothstep(float t) {\n t = clamp(t, 0.0, 1.0);\n // The chord-axis coordinate of ARC/DOME follows the smoothstep curve.\n return 0.5 - sin(asin(1.0 - 2.0 * t) / 3.0);\n}\n\n/**\n * Remaps the parameter t to concentrate vertices to the part that is visible in the viewport.\n * This keeps the tightly bent endpoints smooth even when zooming in very close.\n */\nfloat remapVisibleChordParameter(\n float stripT,\n float chordStart,\n float chordEnd,\n float viewportLength\n) {\n // Concentrate samples in the viewport-visible chord interval without dropping the rest.\n float chordMin = min(chordStart, chordEnd);\n float chordMax = max(chordStart, chordEnd);\n float chordSpan = chordMax - chordMin;\n\n if (chordSpan <= 0.0) {\n return 0.0;\n }\n\n float visibleChordMin = max(chordMin, 0.0);\n float visibleChordMax = min(chordMax, viewportLength);\n\n if (visibleChordMax <= visibleChordMin) {\n return stripT;\n }\n\n float visibleTMin = inverseSmoothstep((visibleChordMin - chordMin) / chordSpan);\n float visibleTMax = inverseSmoothstep((visibleChordMax - chordMin) / chordSpan);\n float visibleTSpan = visibleTMax - visibleTMin;\n float offscreenTSpan = visibleTMin + (1.0 - visibleTMax);\n\n if (offscreenTSpan <= 0.0) {\n return stripT;\n }\n\n float visibleShare = clamp(0.75 + (1.0 - visibleTSpan) * 0.2, 0.75, 0.95);\n float offscreenShare = 1.0 - visibleShare;\n float leftShare = offscreenShare * visibleTMin / offscreenTSpan;\n float rightShare = offscreenShare * (1.0 - visibleTMax) / offscreenTSpan;\n\n if (stripT <= leftShare) {\n return leftShare > 0.0 ? mix(0.0, visibleTMin, stripT / leftShare) : visibleTMin;\n }\n\n float visibleStart = leftShare;\n float visibleEnd = visibleStart + visibleShare;\n\n if (stripT <= visibleEnd) {\n return visibleShare > 0.0\n ? mix(visibleTMin, visibleTMax, (stripT - visibleStart) / visibleShare)\n : visibleTMin;\n }\n\n return rightShare > 0.0\n ? mix(visibleTMax, 1.0, (stripT - visibleEnd) / rightShare)\n : visibleTMax;\n}\n\nvoid clampChordToViewport(inout vec2 p1, inout vec2 p4, inout float chordLength) {\n if (chordLength > uMaxChordLength) {\n vec2 chordVector = p4 - p1;\n vec2 unitChordVector = normalize(chordVector);\n\n if (isInsideViewport(p1, 2.0)) {\n chordLength = uMaxChordLength;\n p4 = p1 + unitChordVector * uMaxChordLength;\n } else if (isInsideViewport(p4, 2.0)) {\n chordLength = uMaxChordLength;\n p1 = p4 - unitChordVector * uMaxChordLength;\n }\n }\n}\n\nvoid clampDomeApex(inout vec2 p1, inout vec2 p4, int orient) {\n if (orient == ORIENT_VERTICAL) {\n if (p4.x > 0.0) {\n p1.x = max(p1.x, -p4.x);\n }\n if (p1.x < uViewportSize.x) {\n p4.x = min(p4.x, 2.0 * uViewportSize.x - p1.x);\n }\n } else {\n if (p4.y > 0.0) {\n p1.y = max(p1.y, -p4.y);\n }\n if (p1.y < uViewportSize.y) {\n p4.y = min(p4.y, 2.0 * uViewportSize.y - p1.y);\n }\n }\n}\n\nvoid main(void) {\n#pragma orderGuard\n\n float pixelSize = 1.0 / uDevicePixelRatio;\n float opacity = getScaled_opacity() * uViewOpacity;\n\n // The bezier's control points\n vec2 p1, p2, p3, p4;\n\n vec2 a = applyOffset(\n applySampleFacet(vec2(getScaled_x(), getScaled_y())),\n vec2(getScaled_xOffset(), getScaled_yOffset())\n ) * uViewportSize;\n vec2 b = applyOffset(\n applySampleFacet(vec2(getScaled_x2(), getScaled_y2())),\n vec2(getScaled_x2Offset(), getScaled_y2Offset())\n ) * uViewportSize;\n\n if (uShape <= SHAPE_DOME) {\n if (uShape == SHAPE_DOME) {\n vec2 height = vec2(0.0);\n if (uOrient == ORIENT_VERTICAL) {\n p1 = vec2(min(a.x, b.x), b.y);\n p4 = vec2(max(a.x, b.x), b.y);\n height = vec2(0.0, a.y - b.y);\n\n float chordLength = length(p4 - p1);\n clampChordToViewport(p1, p4, chordLength);\n if (uClampApex) {\n clampDomeApex(p1, p4, ORIENT_VERTICAL);\n }\n\n } else {\n p1 = vec2(b.x, min(a.y, b.y));\n p4 = vec2(b.x, max(a.y, b.y));\n height = vec2(a.x - b.x, 0.0);\n\n float chordLength = length(p4 - p1);\n clampChordToViewport(p1, p4, chordLength);\n if (uClampApex) {\n clampDomeApex(p1, p4, ORIENT_HORIZONTAL);\n }\n }\n\n vec2 controlOffset = height / 0.75;\n p2 = p1 + controlOffset;\n p3 = p4 + controlOffset;\n\n } if (uShape == SHAPE_ARC) {\n p1 = a;\n p4 = b;\n\n vec2 chordVector = p4 - p1;\n vec2 unitChordVector = normalize(chordVector);\n vec2 chordNormal = vec2(-unitChordVector.y, unitChordVector.x);\n float chordLength = length(chordVector);\n clampChordToViewport(p1, p4, chordLength);\n\n float height = max(\n chordLength / 2.0 * uArcHeightFactor,\n uMinArcHeight\n );\n\n // This is a bit poor approximation of a circular arc, but it's probably enough for most purposes.\n // TODO: Consider a more sophisticated approach: https://stackoverflow.com/a/44829356/1547896\n vec2 controlOffset = chordNormal * height / 0.75;\n p2 = p1 + controlOffset;\n p3 = p4 + controlOffset;\n }\n\n } else if (uShape == SHAPE_DIAGONAL) {\n if (uOrient == ORIENT_VERTICAL) {\n p1 = a;\n p2 = vec2(a.x, (a.y + b.y) / 2.0);\n p3 = vec2(b.x, (a.y + b.y) / 2.0);\n p4 = b;\n } else {\n p1 = a;\n p2 = vec2((a.x + b.x) / 2.0, a.y);\n p3 = vec2((a.x + b.x) / 2.0, b.y);\n p4 = b;\n }\n } else if (uShape == SHAPE_LINE) {\n p1 = a;\n p2 = (a + b) / 2.0;\n p3 = p2;\n p4 = b;\n }\n\n vec2 strip = vec2(\n float(gl_VertexID / 2) / float(uSegmentBreaks),\n float(gl_VertexID % 2) - 0.5\n );\n\n float t = strip.x;\n\n if (uShape == SHAPE_DOME) {\n if (uOrient == ORIENT_VERTICAL) {\n t = remapVisibleChordParameter(strip.x, p1.x, p4.x, uViewportSize.x);\n } else {\n t = remapVisibleChordParameter(strip.x, p1.y, p4.y, uViewportSize.y);\n }\n } else if (uShape == SHAPE_ARC) {\n if (a.y == b.y) {\n t = remapVisibleChordParameter(strip.x, p1.x, p4.x, uViewportSize.x);\n } else if (a.x == b.x) {\n t = remapVisibleChordParameter(strip.x, p1.y, p4.y, uViewportSize.y);\n }\n }\n\n vec2 p;\n vec2 tangent;\n\n // de Casteljau evaluation keeps the cubic stable for long chords.\n vec2 q1 = mix(p1, p2, t);\n vec2 q2 = mix(p2, p3, t);\n vec2 q3 = mix(p3, p4, t);\n\n vec2 r1 = mix(q1, q2, t);\n vec2 r2 = mix(q2, q3, t);\n\n p = mix(r1, r2, t);\n tangent = 3.0 * (r2 - r1);\n\n tangent = normalize(tangent);\n vec2 normal = vec2(-tangent.y, tangent.x);\n\n float size = getScaled_size();\n\n // Avoid artifacts in very thin lines by clamping the size and adjusting opacity respectively\n if (size < pixelSize) {\n opacity *= size / pixelSize;\n size = pixelSize;\n }\n\n // Handle minimum picking size or add an extra pixel to the stroke width to accommodate edge antialiasing\n float paddedSize = uPickingEnabled\n ? max(size, uMinPickingSize)\n : size + pixelSize;\n\n vNormalLengthInPixels = strip.y * paddedSize;\n \n vFadeDistance = -1.0;\n if ((uShape == SHAPE_ARC || uShape == SHAPE_DOME) &&\n uArcFadingDistance[0] > 0.0 &&\n uArcFadingDistance[1] > 0.0\n#ifdef CONDITIONAL_ORDER\n && (!uNoFadingOnSecondPass || (uOrderMode & 4) == 0)\n#endif\n )\n {\n vFadeDistance = distanceFromLine(p1, p4, p);\n // Keep fully faded triangles collapsed to avoid fragment processing.\n if (1.0 - smoothstep(uArcFadingDistance[0], uArcFadingDistance[1], vFadeDistance) <= 0.0) {\n vNormalLengthInPixels = 0.0;\n }\n }\n\n // Extrude\n p += normal * vNormalLengthInPixels;\n\n gl_Position = pixelsToNdc(p);\n\n vec3 color = getScaled_color();\n\n vColor = vec4(color * opacity, opacity);\n\n vGamma = getGammaForColor(color);\n\n vSize = paddedSize;\n\n setupPicking();\n}\n", Gc = "flat in vec4 vColor;\nflat in float vSize;\nin float vNormalLengthInPixels;\nflat in float vGamma;\nin float vFadeDistance;\n\nout lowp vec4 fragColor;\n\nvoid main(void) {\n float dpr = uDevicePixelRatio;\n\n float distance = abs(vNormalLengthInPixels);\n float opacity = clamp(((vSize / 2.0 - distance) * dpr), 0.0, 1.0);\n\n opacity = pow(opacity, vGamma);\n\n if (vFadeDistance >= 0.0) {\n opacity *= 1.0 - smoothstep(uArcFadingDistance[0], uArcFadingDistance[1], vFadeDistance);\n }\n if (opacity <= 0.0) {\n discard;\n }\n\n fragColor = vColor * opacity;\n\n if (uPickingEnabled) {\n fragColor = vPickingColor;\n }\n}\n", Kc = "layout(std140) uniform Mark {\n uniform float uArcHeightFactor;\n\n /** Make very small arcs visible */\n uniform float uMinArcHeight;\n\n /** The minimum stroke width in pixels when rendering into the picking buffer */\n uniform float uMinPickingSize;\n\n uniform int uShape;\n uniform int uOrient;\n uniform bool uClampApex;\n\n // In pixels\n uniform float uMaxChordLength;\n // In pixels\n uniform vec2 uArcFadingDistance;\n uniform bool uNoFadingOnSecondPass;\n\n uniform int uSegmentBreaks;\n\n#pragma markUniforms\n};\n", qc = [
3551
3555
  "arc",
3552
3556
  "dome",
3553
3557
  "diagonal",
3554
3558
  "line"
3555
- ], qc = ["vertical", "horizontal"], Jc = class extends kc {
3559
+ ], Jc = ["vertical", "horizontal"], Yc = class extends Ac {
3556
3560
  constructor(e, t, n) {
3557
3561
  super(e, t, n), this._baseInstanceExt = void 0;
3558
3562
  }
@@ -3574,12 +3578,12 @@ var Uc = "flat out vec4 vColor;\n\n/** Stroke width */\nflat out float vSize;\n\
3574
3578
  ];
3575
3579
  }
3576
3580
  initializeGraphics() {
3577
- super.initializeGraphics(), this._baseInstanceExt = this.gl.getExtension("WEBGL_draw_instanced_base_vertex_base_instance"), this.createAndLinkShaders(Uc, Wc, [Gc]);
3581
+ super.initializeGraphics(), this._baseInstanceExt = this.gl.getExtension("WEBGL_draw_instanced_base_vertex_base_instance"), this.createAndLinkShaders(Wc, Gc, [Kc]);
3578
3582
  }
3579
3583
  finalizeGraphicsInitialization() {
3580
3584
  super.finalizeGraphicsInitialization(), this.gl.useProgram(this.programInfo.program);
3581
3585
  let e = this.properties;
3582
- this.registerMarkUniformValue("uArcFadingDistance", e.arcFadingDistance, (e) => e || [0, 0]), this.registerMarkUniformValue("uArcHeightFactor", e.arcHeightFactor), this.registerMarkUniformValue("uMinArcHeight", e.minArcHeight), this.registerMarkUniformValue("uMinPickingSize", e.minPickingSize), this.registerMarkUniformValue("uShape", e.linkShape, (e) => Kc.indexOf(e)), this.registerMarkUniformValue("uOrient", e.orient, (e) => qc.indexOf(e)), this.registerMarkUniformValue("uClampApex", e.clampApex, (e) => !!e), this.registerMarkUniformValue("uMaxChordLength", e.maxChordLength), this.registerMarkUniformValue("uSegmentBreaks", e.segments, (e) => e), this.registerMarkUniformValue("uNoFadingOnSecondPass", e.noFadingOnSecondPass, (e) => !!e);
3586
+ this.registerMarkUniformValue("uArcFadingDistance", e.arcFadingDistance, (e) => e || [0, 0]), this.registerMarkUniformValue("uArcHeightFactor", e.arcHeightFactor), this.registerMarkUniformValue("uMinArcHeight", e.minArcHeight), this.registerMarkUniformValue("uMinPickingSize", e.minPickingSize), this.registerMarkUniformValue("uShape", e.linkShape, (e) => qc.indexOf(e)), this.registerMarkUniformValue("uOrient", e.orient, (e) => Jc.indexOf(e)), this.registerMarkUniformValue("uClampApex", e.clampApex, (e) => !!e), this.registerMarkUniformValue("uMaxChordLength", e.maxChordLength), this.registerMarkUniformValue("uSegmentBreaks", e.segments, (e) => e), this.registerMarkUniformValue("uNoFadingOnSecondPass", e.noFadingOnSecondPass, (e) => !!e);
3583
3587
  }
3584
3588
  updateGraphicsData(e) {
3585
3589
  let t = e.getItemCount(), n = new _c({
@@ -3610,7 +3614,7 @@ var Uc = "flat out vec4 vColor;\n\n/** Stroke width */\nflat out float vSize;\n\
3610
3614
  Y(t, this.programInfo, this.bufferInfo), t.drawArraysInstanced(t.TRIANGLE_STRIP, 0, n(), r);
3611
3615
  }, e);
3612
3616
  }
3613
- }, Yc = "flat out float vRadius;\nflat out float vRadiusWithPadding;\nflat out lowp vec4 vFillColor;\nflat out lowp vec4 vStrokeColor;\nflat out lowp float vShape;\nflat out lowp float vHalfStrokeWidth;\nflat out mat2 vRotationMatrix;\n\n// Copypaste from fragment shader\nconst float CIRCLE = 0.0;\nconst float SQUARE = 1.0;\nconst float CROSS = 2.0;\nconst float DIAMOND = 3.0;\nconst float TRIANGLE_UP = 4.0;\nconst float TRIANGLE_RIGHT = 5.0;\nconst float TRIANGLE_DOWN = 6.0;\nconst float TRIANGLE_LEFT = 7.0;\nconst float TICK_UP = 8.0;\nconst float TICK_RIGHT = 9.0;\nconst float TICK_DOWN = 10.0;\nconst float TICK_LEFT = 11.0;\nconst float X = 12.0;\nconst float PLUS = 13.0;\n\nfloat computeSemanticThresholdFactor() {\n // TODO: add smooth transition\n return getScaled_semanticScore() >= uSemanticThreshold ? 1.0 : 0.0;\n}\n\n// TODO: Move this into common.glsl or something\nvec2 getDxDy() {\n#if defined(dx_DEFINED) || defined(dy_DEFINED)\n return vec2(getScaled_dx(), getScaled_dy()) / uViewportSize;\n#else\n return vec2(0.0, 0.0);\n#endif\n}\n\nvoid main(void) {\n#pragma orderGuard\n\n float shapeAngle = 0.0;\n\n // Selected points should always be visible\n float semanticThresholdFactor = isDatumSelected()\n ? 1.0\n : computeSemanticThresholdFactor();\n\n if (semanticThresholdFactor <= 0.0) {\n gl_PointSize = 0.0;\n // Place the vertex outside the viewport. The default (0, 0) makes this super-slow\n // on Apple Silicon. Probably related to the tile-based GPU architecture.\n gl_Position = vec4(100.0, 0.0, 0.0, 0.0);\n // Exit early. MAY prevent some unnecessary calculations.\n return;\n }\n\n float size = getScaled_size();\n vec2 pos = vec2(getScaled_x(), getScaled_y()) + getDxDy();\n vec2 facetedPos = applyOffset(\n applySampleFacet(pos),\n vec2(getScaled_xOffset(), getScaled_yOffset())\n );\n\n#ifdef VISIBLE_RANGE_CULLING\n if (isOutsideVisibleRange(facetedPos)) {\n gl_PointSize = 0.0;\n gl_Position = vec4(100.0, 0.0, 0.0, 0.0);\n return;\n }\n#endif\n\n gl_Position = unitToNdc(facetedPos);\n\n float strokeWidth = getScaled_strokeWidth();\n float strokeOpacity = getScaled_strokeOpacity();\n vShape = getScaled_shape();\n\n // Line-only shapes use their width even when the stroke falls back to the\n // fill color. An invisible stroke must not inset other filled shapes.\n if (strokeOpacity <= 0.0 && vShape != X && vShape != PLUS) {\n strokeWidth = 0.0;\n }\n\n float diameter = sqrt(size) *\n uScaleFactor *\n semanticThresholdFactor;\n\n // Clamp minimum size and adjust opacity instead. Yields more pleasing result,\n // no flickering etc.\n float opacity = uViewOpacity;\n if (strokeWidth <= 0.0 || uInwardStroke) {\n float minDiameter = 1.0 / uDevicePixelRatio;\n if (diameter < minDiameter) {\n // We do some \"cheap\" gamma correction here. It breaks on dark background, though.\n // First we take a square of the size and then apply \"gamma\" of 1.5.\n opacity *= pow(diameter / minDiameter, 2.5);\n diameter = minDiameter;\n }\n }\n\n float fillOpa = getScaled_fillOpacity() * opacity;\n float strokeOpa = strokeOpacity * opacity;\n\n // Circle doesn't have sharp corners. Do some special optimizations to minimize the point size.\n bool circle = vShape == 0.0;\n\n if (vShape > TICK_UP && vShape <= TICK_LEFT) {\n shapeAngle = (vShape - TICK_UP) * 90.0;\n vShape = TICK_UP;\n } else if (vShape > TRIANGLE_UP && vShape <= TRIANGLE_LEFT) {\n shapeAngle = (vShape - TRIANGLE_UP) * 90.0;\n vShape = TRIANGLE_UP;\n } else if (vShape == X) {\n // The x shape reuses the perpendicular stroke geometry of plus.\n shapeAngle = -45.0;\n }\n\n float angleInDegrees = getScaled_angle();\n float angle = -(shapeAngle + angleInDegrees) * PI / 180.0;\n float sinTheta = sin(angle);\n float cosTheta = cos(angle);\n vRotationMatrix = mat2(cosTheta, sinTheta, -sinTheta, cosTheta);\n\n // Not needed if we would draw rotated quads instead of gl.POINTS\n float roomForRotation = circle ? 1.0 : sin(mod(angle, PI / 2.0) + PI / 4.0) / sin(PI / 4.0);\n\n float aaPadding = 1.0 / uDevicePixelRatio;\n float rotationPadding = (diameter * roomForRotation) - diameter;\n // sqrt(3.0) ensures that the angles of equilateral triangles have enough room\n float strokePadding = uInwardStroke ? 0.0 : strokeWidth * (circle ? 1.0 : sqrt(3.0));\n float padding = rotationPadding + strokePadding + aaPadding;\n\n gl_PointSize = max(\n (diameter + padding),\n uPickingEnabled ? uMinPickingSize : 0.0\n ) * uDevicePixelRatio;\n\n vRadius = diameter / 2.0;\n vRadiusWithPadding = vRadius + padding / 2.0;\n\n vHalfStrokeWidth = strokeWidth / 2.0;\n\n vFillColor = vec4(getScaled_fill() * fillOpa, fillOpa);\n vStrokeColor = vec4(getScaled_stroke() * strokeOpa, strokeOpa);\n\n setupPicking();\n}\n", Xc = "const lowp vec4 white = vec4(1.0);\nconst lowp vec4 black = vec4(0.0, 0.0, 0.0, 1.0);\n\nflat in float vRadius;\nflat in float vRadiusWithPadding;\n\nflat in lowp vec4 vFillColor;\nflat in lowp vec4 vStrokeColor;\nflat in lowp float vShape;\nflat in lowp float vHalfStrokeWidth;\n\nflat in mat2 vRotationMatrix;\n\nout lowp vec4 fragColor;\n\n// Copypaste from vertex shader\nconst float CIRCLE = 0.0;\nconst float SQUARE = 1.0;\nconst float CROSS = 2.0;\nconst float DIAMOND = 3.0;\nconst float TRIANGLE_UP = 4.0;\nconst float TICK_UP = 8.0;\nconst float X = 12.0;\nconst float PLUS = 13.0;\n\n// The distance functions are inspired by:\n// http://www.iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm\n// However, these are not true distance functions, because the corners need to be sharp.\n\nfloat circle(vec2 p, float r) {\n return length(p) - r;\n}\n\nfloat square(vec2 p, float r) {\n p = abs(p);\n return max(p.x, p.y) - r;\n}\n\nfloat tickUp(vec2 p, float r) {\n float halfR = r * 0.5;\n p.y += halfR;\n p = abs(p);\n return max(p.x - r * 0.15, p.y - halfR);\n}\n\nfloat equilateralTriangle(vec2 p, float r) {\n p.y = -p.y;\n float k = sqrt(3.0);\n float kr = k * r;\n p.y -= kr / 2.0;\n return max((abs(p.x) * k + p.y) / 2.0, -p.y - kr);\n}\n\nfloat crossShape(vec2 p, float r, float armHalfWidth) {\n p = abs(p);\n\n vec2 b = vec2(armHalfWidth, r);\n vec2 v = abs(p) - b.xy;\n vec2 h = abs(p) - b.yx;\n return min(max(v.x, v.y), max(h.x, h.y));\n}\n\nfloat diamond(vec2 p, float r) {\n p = abs(p);\n return (max(abs(p.x - p.y), abs(p.x + p.y)) - r) / sqrt(2.0);\n}\n\nvoid main() {\n float d;\n bool lineShape = false;\n\n /** Normalized point coord */\n vec2 p = vRotationMatrix * (2.0 * gl_PointCoord - 1.0) * vRadiusWithPadding;\n float r = vRadius;\n\n // We could also use textures here. Could even be faster, because we have plenty of branching here.\n if (vShape == CIRCLE) {\n d = circle(p, r);\n\n } else if (vShape == SQUARE) {\n d = square(p, r);\n\n } else if (vShape == CROSS) {\n d = crossShape(p, r, r * 0.4);\n\n } else if (vShape == DIAMOND) {\n d = diamond(p, r);\n\n } else if (vShape == TRIANGLE_UP) {\n d = equilateralTriangle(p, r);\n\n } else if (vShape == TICK_UP) {\n d = tickUp(p, r);\n\n } else if (vShape == X || vShape == PLUS) {\n float lineLength = vShape == X ? r * sqrt(2.0) : r;\n d = crossShape(p, lineLength, vHalfStrokeWidth);\n lineShape = true;\n\n } else {\n d = 0.0;\n }\n\n if (!uPickingEnabled) {\n lowp vec4 fillColor = lineShape\n ? (vStrokeColor.a > 0.0 ? vStrokeColor : vFillColor)\n : mix(vFillColor, white, -d * uGradientStrength / vRadius);\n\n fragColor = distanceToColor(\n d + (!lineShape && uInwardStroke ? vHalfStrokeWidth : 0.0),\n fillColor,\n vStrokeColor,\n vec4(0.0),\n lineShape ? 0.0 : vHalfStrokeWidth);\n\n } else if (d - (lineShape ? 0.0 : vHalfStrokeWidth) <= 0.0) {\n fragColor = vPickingColor;\n\n } else {\n discard;\n }\n}\n", Zc = "layout(std140) uniform Mark {\n /**\n * The stroke should only grow inwards, e.g, the diameter/outline is not affected by the stroke width.\n * Thus, a point that has a zero size has no visible stroke. This allows strokes to be used with\n * geometric zoom, etc.\n */\n uniform bool uInwardStroke;\n\n /** The minimum point size in pixels when rendering into the picking buffer */\n uniform float uMinPickingSize;\n\n /** Scale factor for geometric zoom */\n uniform mediump float uScaleFactor;\n\n uniform mediump float uZoomLevel;\n uniform highp float uSemanticThreshold;\n\n uniform mediump float uGradientStrength;\n\n#pragma markUniforms\n};\n", Qc = class extends kc {
3617
+ }, Xc = "flat out float vRadius;\nflat out float vRadiusWithPadding;\nflat out lowp vec4 vFillColor;\nflat out lowp vec4 vStrokeColor;\nflat out lowp float vShape;\nflat out lowp float vHalfStrokeWidth;\nflat out mat2 vRotationMatrix;\n\n// Copypaste from fragment shader\nconst float CIRCLE = 0.0;\nconst float SQUARE = 1.0;\nconst float CROSS = 2.0;\nconst float DIAMOND = 3.0;\nconst float TRIANGLE_UP = 4.0;\nconst float TRIANGLE_RIGHT = 5.0;\nconst float TRIANGLE_DOWN = 6.0;\nconst float TRIANGLE_LEFT = 7.0;\nconst float TICK_UP = 8.0;\nconst float TICK_RIGHT = 9.0;\nconst float TICK_DOWN = 10.0;\nconst float TICK_LEFT = 11.0;\nconst float X = 12.0;\nconst float PLUS = 13.0;\n\nfloat computeSemanticThresholdFactor() {\n // TODO: add smooth transition\n return getScaled_semanticScore() >= uSemanticThreshold ? 1.0 : 0.0;\n}\n\n// TODO: Move this into common.glsl or something\nvec2 getDxDy() {\n#if defined(dx_DEFINED) || defined(dy_DEFINED)\n return vec2(getScaled_dx(), getScaled_dy()) / uViewportSize;\n#else\n return vec2(0.0, 0.0);\n#endif\n}\n\nvoid main(void) {\n#pragma orderGuard\n\n float shapeAngle = 0.0;\n\n // Selected points should always be visible\n float semanticThresholdFactor = isDatumSelected()\n ? 1.0\n : computeSemanticThresholdFactor();\n\n if (semanticThresholdFactor <= 0.0) {\n gl_PointSize = 0.0;\n // Place the vertex outside the viewport. The default (0, 0) makes this super-slow\n // on Apple Silicon. Probably related to the tile-based GPU architecture.\n gl_Position = vec4(100.0, 0.0, 0.0, 0.0);\n // Exit early. MAY prevent some unnecessary calculations.\n return;\n }\n\n float size = getScaled_size();\n vec2 pos = vec2(getScaled_x(), getScaled_y()) + getDxDy();\n vec2 facetedPos = applyOffset(\n applySampleFacet(pos),\n vec2(getScaled_xOffset(), getScaled_yOffset())\n );\n\n#ifdef VISIBLE_RANGE_CULLING\n if (isOutsideVisibleRange(facetedPos)) {\n gl_PointSize = 0.0;\n gl_Position = vec4(100.0, 0.0, 0.0, 0.0);\n return;\n }\n#endif\n\n gl_Position = unitToNdc(facetedPos);\n\n float strokeWidth = getScaled_strokeWidth();\n float strokeOpacity = getScaled_strokeOpacity();\n vShape = getScaled_shape();\n\n // Line-only shapes use their width even when the stroke falls back to the\n // fill color. An invisible stroke must not inset other filled shapes.\n if (strokeOpacity <= 0.0 && vShape != X && vShape != PLUS) {\n strokeWidth = 0.0;\n }\n\n float diameter = sqrt(size) *\n uScaleFactor *\n semanticThresholdFactor;\n\n // Clamp minimum size and adjust opacity instead. Yields more pleasing result,\n // no flickering etc.\n float opacity = uViewOpacity;\n if (strokeWidth <= 0.0 || uInwardStroke) {\n float minDiameter = 1.0 / uDevicePixelRatio;\n if (diameter < minDiameter) {\n // We do some \"cheap\" gamma correction here. It breaks on dark background, though.\n // First we take a square of the size and then apply \"gamma\" of 1.5.\n opacity *= pow(diameter / minDiameter, 2.5);\n diameter = minDiameter;\n }\n }\n\n float fillOpa = getScaled_fillOpacity() * opacity;\n float strokeOpa = strokeOpacity * opacity;\n\n // Circle doesn't have sharp corners. Do some special optimizations to minimize the point size.\n bool circle = vShape == 0.0;\n\n if (vShape > TICK_UP && vShape <= TICK_LEFT) {\n shapeAngle = (vShape - TICK_UP) * 90.0;\n vShape = TICK_UP;\n } else if (vShape > TRIANGLE_UP && vShape <= TRIANGLE_LEFT) {\n shapeAngle = (vShape - TRIANGLE_UP) * 90.0;\n vShape = TRIANGLE_UP;\n } else if (vShape == X) {\n // The x shape reuses the perpendicular stroke geometry of plus.\n shapeAngle = -45.0;\n }\n\n float angleInDegrees = getScaled_angle();\n float angle = -(shapeAngle + angleInDegrees) * PI / 180.0;\n float sinTheta = sin(angle);\n float cosTheta = cos(angle);\n vRotationMatrix = mat2(cosTheta, sinTheta, -sinTheta, cosTheta);\n\n // Not needed if we would draw rotated quads instead of gl.POINTS\n float roomForRotation = circle ? 1.0 : sin(mod(angle, PI / 2.0) + PI / 4.0) / sin(PI / 4.0);\n\n float aaPadding = 1.0 / uDevicePixelRatio;\n float rotationPadding = (diameter * roomForRotation) - diameter;\n // sqrt(3.0) ensures that the angles of equilateral triangles have enough room\n float strokePadding = uInwardStroke ? 0.0 : strokeWidth * (circle ? 1.0 : sqrt(3.0));\n float padding = rotationPadding + strokePadding + aaPadding;\n\n gl_PointSize = max(\n (diameter + padding),\n uPickingEnabled ? uMinPickingSize : 0.0\n ) * uDevicePixelRatio;\n\n vRadius = diameter / 2.0;\n vRadiusWithPadding = vRadius + padding / 2.0;\n\n vHalfStrokeWidth = strokeWidth / 2.0;\n\n vFillColor = vec4(getScaled_fill() * fillOpa, fillOpa);\n vStrokeColor = vec4(getScaled_stroke() * strokeOpa, strokeOpa);\n\n setupPicking();\n}\n", Zc = "const lowp vec4 white = vec4(1.0);\nconst lowp vec4 black = vec4(0.0, 0.0, 0.0, 1.0);\n\nflat in float vRadius;\nflat in float vRadiusWithPadding;\n\nflat in lowp vec4 vFillColor;\nflat in lowp vec4 vStrokeColor;\nflat in lowp float vShape;\nflat in lowp float vHalfStrokeWidth;\n\nflat in mat2 vRotationMatrix;\n\nout lowp vec4 fragColor;\n\n// Copypaste from vertex shader\nconst float CIRCLE = 0.0;\nconst float SQUARE = 1.0;\nconst float CROSS = 2.0;\nconst float DIAMOND = 3.0;\nconst float TRIANGLE_UP = 4.0;\nconst float TICK_UP = 8.0;\nconst float X = 12.0;\nconst float PLUS = 13.0;\n\n// The distance functions are inspired by:\n// http://www.iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm\n// However, these are not true distance functions, because the corners need to be sharp.\n\nfloat circle(vec2 p, float r) {\n return length(p) - r;\n}\n\nfloat square(vec2 p, float r) {\n p = abs(p);\n return max(p.x, p.y) - r;\n}\n\nfloat tickUp(vec2 p, float r) {\n float halfR = r * 0.5;\n p.y += halfR;\n p = abs(p);\n return max(p.x - r * 0.15, p.y - halfR);\n}\n\nfloat equilateralTriangle(vec2 p, float r) {\n p.y = -p.y;\n float k = sqrt(3.0);\n float kr = k * r;\n p.y -= kr / 2.0;\n return max((abs(p.x) * k + p.y) / 2.0, -p.y - kr);\n}\n\nfloat crossShape(vec2 p, float r, float armHalfWidth) {\n p = abs(p);\n\n vec2 b = vec2(armHalfWidth, r);\n vec2 v = abs(p) - b.xy;\n vec2 h = abs(p) - b.yx;\n return min(max(v.x, v.y), max(h.x, h.y));\n}\n\nfloat diamond(vec2 p, float r) {\n p = abs(p);\n return (max(abs(p.x - p.y), abs(p.x + p.y)) - r) / sqrt(2.0);\n}\n\nvoid main() {\n float d;\n bool lineShape = false;\n\n /** Normalized point coord */\n vec2 p = vRotationMatrix * (2.0 * gl_PointCoord - 1.0) * vRadiusWithPadding;\n float r = vRadius;\n\n // We could also use textures here. Could even be faster, because we have plenty of branching here.\n if (vShape == CIRCLE) {\n d = circle(p, r);\n\n } else if (vShape == SQUARE) {\n d = square(p, r);\n\n } else if (vShape == CROSS) {\n d = crossShape(p, r, r * 0.4);\n\n } else if (vShape == DIAMOND) {\n d = diamond(p, r);\n\n } else if (vShape == TRIANGLE_UP) {\n d = equilateralTriangle(p, r);\n\n } else if (vShape == TICK_UP) {\n d = tickUp(p, r);\n\n } else if (vShape == X || vShape == PLUS) {\n float lineLength = vShape == X ? r * sqrt(2.0) : r;\n d = crossShape(p, lineLength, vHalfStrokeWidth);\n lineShape = true;\n\n } else {\n d = 0.0;\n }\n\n if (!uPickingEnabled) {\n lowp vec4 fillColor = lineShape\n ? (vStrokeColor.a > 0.0 ? vStrokeColor : vFillColor)\n : mix(vFillColor, white, -d * uGradientStrength / vRadius);\n\n fragColor = distanceToColor(\n d + (!lineShape && uInwardStroke ? vHalfStrokeWidth : 0.0),\n fillColor,\n vStrokeColor,\n vec4(0.0),\n lineShape ? 0.0 : vHalfStrokeWidth);\n\n } else if (d - (lineShape ? 0.0 : vHalfStrokeWidth) <= 0.0) {\n fragColor = vPickingColor;\n\n } else {\n discard;\n }\n}\n", Qc = "layout(std140) uniform Mark {\n /**\n * The stroke should only grow inwards, e.g, the diameter/outline is not affected by the stroke width.\n * Thus, a point that has a zero size has no visible stroke. This allows strokes to be used with\n * geometric zoom, etc.\n */\n uniform bool uInwardStroke;\n\n /** The minimum point size in pixels when rendering into the picking buffer */\n uniform float uMinPickingSize;\n\n /** Scale factor for geometric zoom */\n uniform mediump float uScaleFactor;\n\n uniform mediump float uZoomLevel;\n uniform highp float uSemanticThreshold;\n\n uniform mediump float uGradientStrength;\n\n#pragma markUniforms\n};\n", $c = class extends Ac {
3614
3618
  getAttributes() {
3615
3619
  return [
3616
3620
  "uniqueId",
@@ -3633,7 +3637,7 @@ var Uc = "flat out vec4 vColor;\n\n/** Stroke width */\nflat out float vSize;\n\
3633
3637
  ];
3634
3638
  }
3635
3639
  initializeGraphics() {
3636
- super.initializeGraphics(), this.createAndLinkShaders(Yc, Xc, [Zc]);
3640
+ super.initializeGraphics(), this.createAndLinkShaders(Xc, Zc, [Qc]);
3637
3641
  }
3638
3642
  finalizeGraphicsInitialization() {
3639
3643
  super.finalizeGraphicsInitialization(), this.gl.useProgram(this.programInfo.program);
@@ -3669,7 +3673,7 @@ var Uc = "flat out vec4 vColor;\n\n/** Stroke width */\nflat out float vSize;\n\
3669
3673
  n && oo(t, this.vertexArrayInfo, t.POINTS, n, e);
3670
3674
  }, e);
3671
3675
  }
3672
- }, $c = "flat out lowp vec4 vFillColor;\nflat out lowp vec4 vStrokeColor;\nflat out float vHalfStrokeWidth;\nflat out vec4 vCornerRadii;\n\n#if defined(ROUNDED_CORNERS) || defined(STROKED) || defined(SHADOW)\n/** Position for SDF-strokes */\nout vec2 vPosInPixels;\n#endif\n\n/** Size of the rect in pixels */\nflat out vec2 vHalfSizeInPixels;\n\n/**\n * Adjusts the vertex position to ensure that the rectangle is at least `minSpan`\n * wide or high. Returns a value that reflects the amount of clamping and can be\n * used to adjust the opacity of the rectangle.\n *\n * pos: vertex position\n * frac: vertex position within the rectangle, [0, 1]\n * size: width or height of the rectangle\n * minSize: minimum width or height of the rectangle\n */\nfloat clampMinSize(inout float pos, float frac, float size, float minSize) {\n if (minSize > 0.0 && size < minSize) {\n pos += (frac - 0.5) * (minSize - size);\n return size / minSize;\n }\n\n return 1.0;\n}\n\nvoid sort(inout float a, inout float b) {\n if (a > b) {\n float tmp = b;\n b = a;\n a = tmp;\n }\n}\n\n/**\n * The vertex position wrt the rectangle specified by (x, x2, y, y2).\n * [0, 0] = [x, y], [1, 1] = [x2, y2]. \n * The x or y component may contain fractional values if the rectangle \n * have been tessellated.\n */\nvec2 getVertexPos() {\n int index = gl_VertexID % 6;\n return vec2(\n index == 0 || index == 1 || index == 3 ? 0.0 : 1.0,\n index == 0 || index == 1 || index == 2 ? 0.0 : 1.0\n );\n \n}\n\nvoid main(void) {\n#pragma orderGuard\n\n vec2 frac = getVertexPos();\n\n vec2 normalizedMinSize = vec2(uMinWidth, uMinHeight) / uViewportSize;\n vec4 cornerRadii = vec4(\n uCornerRadiusTopRight,\n uCornerRadiusBottomRight,\n uCornerRadiusTopLeft,\n uCornerRadiusBottomLeft\n );\n\n SampleFacetPosition sampleFacetPos = getSampleFacetPos();\n if (sampleFacetPos.height == 0.0) {\n // Filtered samples have no facet height. Cull them before minimum-size\n // clamping can expand their rectangles at the viewport boundary.\n gl_Position = vec4(100.0, 0.0, 0.0, 0.0);\n return;\n }\n\n vec2 pos1 = applyOffset(\n applySampleFacet(vec2(getScaled_x(), getScaled_y())),\n vec2(getScaled_xOffset(), getScaled_yOffset())\n );\n vec2 pos2 = applyOffset(\n applySampleFacet(vec2(getScaled_x2(), getScaled_y2())),\n vec2(getScaled_x2Offset(), getScaled_y2Offset())\n );\n\n sort(pos1.x, pos2.x);\n sort(pos1.y, pos2.y);\n\n // Clamp x to prevent precision artifacts when the scale is zoomed very close.\n // TODO: clamp y as well\n float clampMargin = 1.0;\n pos1.x = clamp(pos1.x, 0.0 - clampMargin, 1.0 + clampMargin);\n pos2.x = clamp(pos2.x, 0.0 - clampMargin, 1.0 + clampMargin);\n\n vec2 size = pos2 - pos1;\n\n if (size.x < 0.0 || size.y < 0.0) {\n // Keep zero-sized rects alive so min-size clamping can stabilize them.\n gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n return;\n }\n\n vec2 pos = pos1 + frac * size;\n\n // Clamp to minimum size, optionally compensate with opacity\n float opaFactor = uViewOpacity * max(uMinOpacity,\n clampMinSize(pos.x, frac.x, size.x, normalizedMinSize.x) *\n clampMinSize(pos.y, frac.y, size.y, normalizedMinSize.y));\n\n size = max(size, normalizedMinSize);\n\n#if defined(ROUNDED_CORNERS) || defined(STROKED) || defined(SHADOW)\n // Add an extra pixel to the stroke width to accommodate edge antialiasing\n float aaPadding = 1.0 / uDevicePixelRatio;\n\n // TODO: Only expand to the offset direction. Now high offsets result in\n // a large expansion in all directions.\n float shadowPadding = uShadowBlur + max(abs(uShadowOffsetX), abs(uShadowOffsetY));\n\n float strokeWidth = getScaled_strokeWidth();\n float strokeOpacity = getScaled_strokeOpacity() * opaFactor;\n\n vec2 centeredFrac = frac - 0.5;\n vec2 expand = centeredFrac * (strokeWidth + aaPadding + shadowPadding * 2.0) / uViewportSize;\n pos += expand;\n\n vec2 sizeInPixels = size * uViewportSize;\n vPosInPixels = (centeredFrac + expand / size) * sizeInPixels;\n\n vHalfSizeInPixels = sizeInPixels / 2.0;\n\n vCornerRadii = min(cornerRadii, min(vHalfSizeInPixels.x, vHalfSizeInPixels.y));\n vHalfStrokeWidth = strokeWidth / 2.0;\n vStrokeColor = vec4(getScaled_stroke() * strokeOpacity, strokeOpacity);\n#endif\n\n gl_Position = unitToNdc(pos);\n\n float fillOpacity = getScaled_fillOpacity() * opaFactor;\n vFillColor = vec4(getScaled_fill() * fillOpacity, fillOpacity);\n\n setupPicking();\n}\n", el = "#if defined(ROUNDED_CORNERS) || defined(STROKED) || defined(SHADOW)\nin vec2 vPosInPixels;\n#endif\n\nflat in vec2 vHalfSizeInPixels;\n\nflat in lowp vec4 vFillColor;\nflat in lowp vec4 vStrokeColor;\nflat in float vHalfStrokeWidth;\nflat in vec4 vCornerRadii;\n\nout lowp vec4 fragColor;\n\n// ----------------------------------------------------------------------------\n// Shadow source: https://madebyevan.com/shaders/fast-rounded-rectangle-shadows/\n// License: CC0 (http://creativecommons.org/publicdomain/zero/1.0/)\n\n#ifdef SHADOW\n\n// A standard gaussian function, used for weighting samples\nfloat gaussian(float x, float sigma) {\n const float pi = 3.141592653589793;\n return exp(-(x * x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * pi) * sigma);\n}\n\n// This approximates the error function, needed for the gaussian integral\nvec2 erf(vec2 x) {\n vec2 s = sign(x), a = abs(x);\n x = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;\n x *= x;\n return s - s / (x * x);\n}\n\n// Return the blurred mask along the x dimension\nfloat roundedBoxShadowX(float x, float y, float sigma, float corner, vec2 halfSize) {\n float delta = min(halfSize.y - corner - abs(y), 0.0);\n float curved = halfSize.x - corner + sqrt(max(0.0, corner * corner - delta * delta));\n vec2 integral = 0.5 + 0.5 * erf((x + vec2(-curved, curved)) * (sqrt(0.5) / sigma));\n return integral.y - integral.x;\n}\n\n// Return the mask for the shadow of a box from lower to upper\nfloat roundedBoxShadow(vec2 lower, vec2 upper, vec2 point, float sigma, float corner) {\n // Center everything to make the math easier\n vec2 center = (lower + upper) * 0.5;\n vec2 halfSize = (upper - lower) * 0.5;\n point -= center;\n\n // The signal is only non-zero in a limited range, so don't waste samples\n float low = point.y - halfSize.y;\n float high = point.y + halfSize.y;\n float start = clamp(-3.0 * sigma, low, high);\n float end = clamp(3.0 * sigma, low, high);\n\n // Accumulate samples (we can get away with surprisingly few samples)\n float step = (end - start) / 4.0;\n float y = start + step * 0.5;\n float value = 0.0;\n for (int i = 0; i < 4; i++) {\n value += roundedBoxShadowX(point.x, point.y - y, sigma, corner, halfSize) * gaussian(y, sigma) * step;\n y += step;\n }\n\n return value;\n}\n\n// ----------------------------------------------------------------------------\n\n#endif\n\n// Source: https://www.iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm\nfloat sdRoundedBox(vec2 p, vec2 b, vec4 r) {\n r.xy = p.x > 0.0 ? r.xy : r.zw;\n r.x = p.y > 0.0 ? r.x : r.y;\n vec2 q = abs(p) - b + r.x;\n return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r.x;\n}\n\n// Not a true SDF. Makes the corners of strokes sharp and is faster.\nfloat sdSharpBox(vec2 p, vec2 b) {\n vec2 q = abs(p) - b;\n return max(q.x, q.y);\n}\n\nfloat diagonalPattern(vec2 uv, float spacing) {\n // Using 1.5 to approximate sqrt(2.0) to reduce aliasing artifacts.\n float divisor = spacing * vHalfStrokeWidth * 2.0 * 1.5;\n return abs(mod(uv.x - uv.y, divisor) - 0.5 * divisor) / 1.5;\n}\n\nfloat verticalPattern(float x, float spacing) {\n float divisor = spacing * vHalfStrokeWidth * 2.0;\n return abs(mod(x, divisor)) / 2.0;\n}\n\nfloat circle(vec2 p, float r) {\n return length(p) - r;\n}\n\nfloat masonryCirclePattern(vec2 uv, float spacing, float radius) {\n float halfSpacing = 0.5 * spacing;\n\n float row = floor(uv.y / spacing);\n float shift = mod(row, 2.0) * halfSpacing;\n\n vec2 shifted = vec2(uv.x + shift, uv.y + halfSpacing);\n vec2 cell = mod(shifted + 0.5 * spacing, spacing) - halfSpacing;\n\n return abs(circle(cell, radius));\n}\n\n/**\n * Patterns:\n * 0 none\n * 1 diagonal (/)\n * 2 antiDiagonal (\\)\n * 3 cross (X)\n * 4 vertical (|)\n * 5 horizontal (-)\n * 6 grid (+)\n * 7 dots (.)\n * 8 rings (o)\n * 9 ringsLarge (O)\n */\nfloat pattern() {\n#ifdef STROKED\n int patternType = uHatchPattern;\n vec2 uv = vPosInPixels;\n float spacing = 4.0;\n\n switch (patternType) {\n case 1:\n return diagonalPattern(vec2(uv.x, -uv.y), spacing);\n case 2:\n return diagonalPattern(uv, spacing);\n case 3:\n return min(\n diagonalPattern(uv, spacing),\n diagonalPattern(vec2(uv.x, -uv.y), spacing)\n );\n case 4:\n return verticalPattern(uv.x, spacing);\n case 5:\n return verticalPattern(uv.y, spacing);\n case 6:\n return min(\n verticalPattern(uv.x, spacing),\n verticalPattern(uv.y, spacing)\n );\n case 7:\n case 8:\n case 9: {\n float spacing = vHalfStrokeWidth * 14.0;\n float radius = spacing * (\n patternType == 8 ? 0.2 :\n patternType == 9 ? 0.35 :\n 0.07\n );\n return masonryCirclePattern(uv, spacing, radius);\n }\n default:\n break;\n }\n#endif\n return 1.0 / 0.0; // Infinity\n}\n\nvoid main(void) {\n\n#if defined(ROUNDED_CORNERS) || defined(STROKED) || defined(SHADOW)\n#ifdef ROUNDED_CORNERS\n // Distance from rectangle's edge in pixels. Negative inside the rectangle.\n float d = sdRoundedBox(vPosInPixels, vHalfSizeInPixels, vCornerRadii);\n#else\n float d = sdSharpBox(vPosInPixels, vHalfSizeInPixels);\n#endif\n\n vec4 backgroundColor = vec4(0.0, 0.0, 0.0, 0.0);\n\n#ifdef SHADOW\n float maxCornerRadius = max(vCornerRadii.x, max(vCornerRadii.y, max(vCornerRadii.z, vCornerRadii.w)));\n\n float shadow = 0.0;\n // Only calculate shadow for the region outside the stroke.\n if (d >= vHalfStrokeWidth - 1.0 && uShadowOpacity > 0.0) {\n shadow = roundedBoxShadow(\n -vHalfSizeInPixels - vHalfStrokeWidth,\n vHalfSizeInPixels + vHalfStrokeWidth,\n vPosInPixels - vec2(uShadowOffsetX, -uShadowOffsetY),\n max(uShadowBlur / 2.5, 0.25),\n maxCornerRadius + vHalfStrokeWidth\n ) * uShadowOpacity * uViewOpacity;\n }\n backgroundColor = vec4(uShadowColor * shadow, shadow);\n#endif\n\n if (vHalfStrokeWidth > 0.0 && uHatchPattern > 0) {\n d = max(d, -pattern());\n }\n\n fragColor = distanceToColor(\n d,\n vFillColor,\n vStrokeColor,\n backgroundColor,\n vHalfStrokeWidth\n );\n\n if (uPickingEnabled) {\n if (d < vHalfStrokeWidth) {\n fragColor = vPickingColor;\n }\n } else if (fragColor.a == 0.0) {\n discard;\n }\n#else\n // The trivial, non-decorated case\n fragColor = vFillColor;\n if (uPickingEnabled) {\n fragColor = vPickingColor;\n }\n#endif\n}\n", tl = "layout(std140) uniform Mark {\n /** Minimum size (width, height) of the displayed rectangle in pixels */\n uniform float uMinWidth;\n uniform float uMinHeight;\n\n /** Minimum opacity for the size clamping */\n uniform float uMinOpacity;\n\n uniform float uCornerRadiusTopRight;\n uniform float uCornerRadiusBottomRight;\n uniform float uCornerRadiusTopLeft;\n uniform float uCornerRadiusBottomLeft;\n\n uniform int uHatchPattern;\n\n uniform vec3 uShadowColor;\n uniform float uShadowOpacity;\n uniform float uShadowBlur;\n uniform float uShadowOffsetX;\n uniform float uShadowOffsetY;\n\n#pragma markUniforms\n};\n", nl = [
3676
+ }, el = "flat out lowp vec4 vFillColor;\nflat out lowp vec4 vStrokeColor;\nflat out float vHalfStrokeWidth;\nflat out vec4 vCornerRadii;\n\n#if defined(ROUNDED_CORNERS) || defined(STROKED) || defined(SHADOW)\n/** Position for SDF-strokes */\nout vec2 vPosInPixels;\n#endif\n\n/** Size of the rect in pixels */\nflat out vec2 vHalfSizeInPixels;\n\n/**\n * Adjusts the vertex position to ensure that the rectangle is at least `minSpan`\n * wide or high. Returns a value that reflects the amount of clamping and can be\n * used to adjust the opacity of the rectangle.\n *\n * pos: vertex position\n * frac: vertex position within the rectangle, [0, 1]\n * size: width or height of the rectangle\n * minSize: minimum width or height of the rectangle\n */\nfloat clampMinSize(inout float pos, float frac, float size, float minSize) {\n if (minSize > 0.0 && size < minSize) {\n pos += (frac - 0.5) * (minSize - size);\n return size / minSize;\n }\n\n return 1.0;\n}\n\nvoid sort(inout float a, inout float b) {\n if (a > b) {\n float tmp = b;\n b = a;\n a = tmp;\n }\n}\n\n/**\n * The vertex position wrt the rectangle specified by (x, x2, y, y2).\n * [0, 0] = [x, y], [1, 1] = [x2, y2]. \n * The x or y component may contain fractional values if the rectangle \n * have been tessellated.\n */\nvec2 getVertexPos() {\n int index = gl_VertexID % 6;\n return vec2(\n index == 0 || index == 1 || index == 3 ? 0.0 : 1.0,\n index == 0 || index == 1 || index == 2 ? 0.0 : 1.0\n );\n \n}\n\nvoid main(void) {\n#pragma orderGuard\n\n vec2 frac = getVertexPos();\n\n vec2 normalizedMinSize = vec2(uMinWidth, uMinHeight) / uViewportSize;\n vec4 cornerRadii = vec4(\n uCornerRadiusTopRight,\n uCornerRadiusBottomRight,\n uCornerRadiusTopLeft,\n uCornerRadiusBottomLeft\n );\n\n SampleFacetPosition sampleFacetPos = getSampleFacetPos();\n if (sampleFacetPos.height == 0.0) {\n // Filtered samples have no facet height. Cull them before minimum-size\n // clamping can expand their rectangles at the viewport boundary.\n gl_Position = vec4(100.0, 0.0, 0.0, 0.0);\n return;\n }\n\n vec2 pos1 = applyOffset(\n applySampleFacet(vec2(getScaled_x(), getScaled_y())),\n vec2(getScaled_xOffset(), getScaled_yOffset())\n );\n vec2 pos2 = applyOffset(\n applySampleFacet(vec2(getScaled_x2(), getScaled_y2())),\n vec2(getScaled_x2Offset(), getScaled_y2Offset())\n );\n\n sort(pos1.x, pos2.x);\n sort(pos1.y, pos2.y);\n\n // Clamp x to prevent precision artifacts when the scale is zoomed very close.\n // TODO: clamp y as well\n float clampMargin = 1.0;\n pos1.x = clamp(pos1.x, 0.0 - clampMargin, 1.0 + clampMargin);\n pos2.x = clamp(pos2.x, 0.0 - clampMargin, 1.0 + clampMargin);\n\n vec2 size = pos2 - pos1;\n\n if (size.x < 0.0 || size.y < 0.0) {\n // Keep zero-sized rects alive so min-size clamping can stabilize them.\n gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n return;\n }\n\n vec2 pos = pos1 + frac * size;\n\n // Clamp to minimum size, optionally compensate with opacity\n float opaFactor = uViewOpacity * max(uMinOpacity,\n clampMinSize(pos.x, frac.x, size.x, normalizedMinSize.x) *\n clampMinSize(pos.y, frac.y, size.y, normalizedMinSize.y));\n\n size = max(size, normalizedMinSize);\n\n#if defined(ROUNDED_CORNERS) || defined(STROKED) || defined(SHADOW)\n // Add an extra pixel to the stroke width to accommodate edge antialiasing\n float aaPadding = 1.0 / uDevicePixelRatio;\n\n // TODO: Only expand to the offset direction. Now high offsets result in\n // a large expansion in all directions.\n float shadowPadding = uShadowBlur + max(abs(uShadowOffsetX), abs(uShadowOffsetY));\n\n float strokeWidth = getScaled_strokeWidth();\n float strokeOpacity = getScaled_strokeOpacity() * opaFactor;\n\n vec2 centeredFrac = frac - 0.5;\n vec2 expand = centeredFrac * (strokeWidth + aaPadding + shadowPadding * 2.0) / uViewportSize;\n pos += expand;\n\n vec2 sizeInPixels = size * uViewportSize;\n vPosInPixels = (centeredFrac + expand / size) * sizeInPixels;\n\n vHalfSizeInPixels = sizeInPixels / 2.0;\n\n vCornerRadii = min(cornerRadii, min(vHalfSizeInPixels.x, vHalfSizeInPixels.y));\n vHalfStrokeWidth = strokeWidth / 2.0;\n vStrokeColor = vec4(getScaled_stroke() * strokeOpacity, strokeOpacity);\n#endif\n\n gl_Position = unitToNdc(pos);\n\n float fillOpacity = getScaled_fillOpacity() * opaFactor;\n vFillColor = vec4(getScaled_fill() * fillOpacity, fillOpacity);\n\n setupPicking();\n}\n", tl = "#if defined(ROUNDED_CORNERS) || defined(STROKED) || defined(SHADOW)\nin vec2 vPosInPixels;\n#endif\n\nflat in vec2 vHalfSizeInPixels;\n\nflat in lowp vec4 vFillColor;\nflat in lowp vec4 vStrokeColor;\nflat in float vHalfStrokeWidth;\nflat in vec4 vCornerRadii;\n\nout lowp vec4 fragColor;\n\n// ----------------------------------------------------------------------------\n// Shadow source: https://madebyevan.com/shaders/fast-rounded-rectangle-shadows/\n// License: CC0 (http://creativecommons.org/publicdomain/zero/1.0/)\n\n#ifdef SHADOW\n\n// A standard gaussian function, used for weighting samples\nfloat gaussian(float x, float sigma) {\n const float pi = 3.141592653589793;\n return exp(-(x * x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * pi) * sigma);\n}\n\n// This approximates the error function, needed for the gaussian integral\nvec2 erf(vec2 x) {\n vec2 s = sign(x), a = abs(x);\n x = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;\n x *= x;\n return s - s / (x * x);\n}\n\n// Return the blurred mask along the x dimension\nfloat roundedBoxShadowX(float x, float y, float sigma, float corner, vec2 halfSize) {\n float delta = min(halfSize.y - corner - abs(y), 0.0);\n float curved = halfSize.x - corner + sqrt(max(0.0, corner * corner - delta * delta));\n vec2 integral = 0.5 + 0.5 * erf((x + vec2(-curved, curved)) * (sqrt(0.5) / sigma));\n return integral.y - integral.x;\n}\n\n// Return the mask for the shadow of a box from lower to upper\nfloat roundedBoxShadow(vec2 lower, vec2 upper, vec2 point, float sigma, float corner) {\n // Center everything to make the math easier\n vec2 center = (lower + upper) * 0.5;\n vec2 halfSize = (upper - lower) * 0.5;\n point -= center;\n\n // The signal is only non-zero in a limited range, so don't waste samples\n float low = point.y - halfSize.y;\n float high = point.y + halfSize.y;\n float start = clamp(-3.0 * sigma, low, high);\n float end = clamp(3.0 * sigma, low, high);\n\n // Accumulate samples (we can get away with surprisingly few samples)\n float step = (end - start) / 4.0;\n float y = start + step * 0.5;\n float value = 0.0;\n for (int i = 0; i < 4; i++) {\n value += roundedBoxShadowX(point.x, point.y - y, sigma, corner, halfSize) * gaussian(y, sigma) * step;\n y += step;\n }\n\n return value;\n}\n\n// ----------------------------------------------------------------------------\n\n#endif\n\n// Source: https://www.iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm\nfloat sdRoundedBox(vec2 p, vec2 b, vec4 r) {\n r.xy = p.x > 0.0 ? r.xy : r.zw;\n r.x = p.y > 0.0 ? r.x : r.y;\n vec2 q = abs(p) - b + r.x;\n return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r.x;\n}\n\n// Not a true SDF. Makes the corners of strokes sharp and is faster.\nfloat sdSharpBox(vec2 p, vec2 b) {\n vec2 q = abs(p) - b;\n return max(q.x, q.y);\n}\n\nfloat diagonalPattern(vec2 uv, float spacing) {\n // Using 1.5 to approximate sqrt(2.0) to reduce aliasing artifacts.\n float divisor = spacing * vHalfStrokeWidth * 2.0 * 1.5;\n return abs(mod(uv.x - uv.y, divisor) - 0.5 * divisor) / 1.5;\n}\n\nfloat verticalPattern(float x, float spacing) {\n float divisor = spacing * vHalfStrokeWidth * 2.0;\n return abs(mod(x, divisor)) / 2.0;\n}\n\nfloat circle(vec2 p, float r) {\n return length(p) - r;\n}\n\nfloat masonryCirclePattern(vec2 uv, float spacing, float radius) {\n float halfSpacing = 0.5 * spacing;\n\n float row = floor(uv.y / spacing);\n float shift = mod(row, 2.0) * halfSpacing;\n\n vec2 shifted = vec2(uv.x + shift, uv.y + halfSpacing);\n vec2 cell = mod(shifted + 0.5 * spacing, spacing) - halfSpacing;\n\n return abs(circle(cell, radius));\n}\n\n/**\n * Patterns:\n * 0 none\n * 1 diagonal (/)\n * 2 antiDiagonal (\\)\n * 3 cross (X)\n * 4 vertical (|)\n * 5 horizontal (-)\n * 6 grid (+)\n * 7 dots (.)\n * 8 rings (o)\n * 9 ringsLarge (O)\n */\nfloat pattern() {\n#ifdef STROKED\n int patternType = uHatchPattern;\n vec2 uv = vPosInPixels;\n float spacing = 4.0;\n\n switch (patternType) {\n case 1:\n return diagonalPattern(vec2(uv.x, -uv.y), spacing);\n case 2:\n return diagonalPattern(uv, spacing);\n case 3:\n return min(\n diagonalPattern(uv, spacing),\n diagonalPattern(vec2(uv.x, -uv.y), spacing)\n );\n case 4:\n return verticalPattern(uv.x, spacing);\n case 5:\n return verticalPattern(uv.y, spacing);\n case 6:\n return min(\n verticalPattern(uv.x, spacing),\n verticalPattern(uv.y, spacing)\n );\n case 7:\n case 8:\n case 9: {\n float spacing = vHalfStrokeWidth * 14.0;\n float radius = spacing * (\n patternType == 8 ? 0.2 :\n patternType == 9 ? 0.35 :\n 0.07\n );\n return masonryCirclePattern(uv, spacing, radius);\n }\n default:\n break;\n }\n#endif\n return 1.0 / 0.0; // Infinity\n}\n\nvoid main(void) {\n\n#if defined(ROUNDED_CORNERS) || defined(STROKED) || defined(SHADOW)\n#ifdef ROUNDED_CORNERS\n // Distance from rectangle's edge in pixels. Negative inside the rectangle.\n float d = sdRoundedBox(vPosInPixels, vHalfSizeInPixels, vCornerRadii);\n#else\n float d = sdSharpBox(vPosInPixels, vHalfSizeInPixels);\n#endif\n\n vec4 backgroundColor = vec4(0.0, 0.0, 0.0, 0.0);\n\n#ifdef SHADOW\n float maxCornerRadius = max(vCornerRadii.x, max(vCornerRadii.y, max(vCornerRadii.z, vCornerRadii.w)));\n\n float shadow = 0.0;\n // Only calculate shadow for the region outside the stroke.\n if (d >= vHalfStrokeWidth - 1.0 && uShadowOpacity > 0.0) {\n shadow = roundedBoxShadow(\n -vHalfSizeInPixels - vHalfStrokeWidth,\n vHalfSizeInPixels + vHalfStrokeWidth,\n vPosInPixels - vec2(uShadowOffsetX, -uShadowOffsetY),\n max(uShadowBlur / 2.5, 0.25),\n maxCornerRadius + vHalfStrokeWidth\n ) * uShadowOpacity * uViewOpacity;\n }\n backgroundColor = vec4(uShadowColor * shadow, shadow);\n#endif\n\n if (vHalfStrokeWidth > 0.0 && uHatchPattern > 0) {\n d = max(d, -pattern());\n }\n\n fragColor = distanceToColor(\n d,\n vFillColor,\n vStrokeColor,\n backgroundColor,\n vHalfStrokeWidth\n );\n\n if (uPickingEnabled) {\n if (d < vHalfStrokeWidth) {\n fragColor = vPickingColor;\n }\n } else if (fragColor.a == 0.0) {\n discard;\n }\n#else\n // The trivial, non-decorated case\n fragColor = vFillColor;\n if (uPickingEnabled) {\n fragColor = vPickingColor;\n }\n#endif\n}\n", nl = "layout(std140) uniform Mark {\n /** Minimum size (width, height) of the displayed rectangle in pixels */\n uniform float uMinWidth;\n uniform float uMinHeight;\n\n /** Minimum opacity for the size clamping */\n uniform float uMinOpacity;\n\n uniform float uCornerRadiusTopRight;\n uniform float uCornerRadiusBottomRight;\n uniform float uCornerRadiusTopLeft;\n uniform float uCornerRadiusBottomLeft;\n\n uniform int uHatchPattern;\n\n uniform vec3 uShadowColor;\n uniform float uShadowOpacity;\n uniform float uShadowBlur;\n uniform float uShadowOffsetX;\n uniform float uShadowOffsetY;\n\n#pragma markUniforms\n};\n", rl = [
3673
3677
  "none",
3674
3678
  "diagonal",
3675
3679
  "antiDiagonal",
@@ -3680,7 +3684,7 @@ var Uc = "flat out vec4 vColor;\n\n/** Stroke width */\nflat out float vSize;\n\
3680
3684
  "dots",
3681
3685
  "rings",
3682
3686
  "ringsLarge"
3683
- ], rl = class extends kc {
3687
+ ], il = class extends Ac {
3684
3688
  get opaque() {
3685
3689
  return ce(this, "opaque", () => !this.#e() && !this.#t() && !this.properties.shadowOpacity && i(this.encoding.fillOpacity) && this.encoding.fillOpacity.value == 1 && this.properties.minOpacity == 1) && this.unitView.getEffectiveOpacity() == 1;
3686
3690
  }
@@ -3714,12 +3718,12 @@ var Uc = "flat out vec4 vColor;\n\n/** Stroke width */\nflat out float vSize;\n\
3714
3718
  initializeGraphics() {
3715
3719
  super.initializeGraphics();
3716
3720
  let e = [];
3717
- this.#e() && e.push("ROUNDED_CORNERS"), this.#t() && e.push("STROKED"), this.properties.shadowOpacity && e.push("SHADOW"), this.createAndLinkShaders($c, el, [tl, ...e.map((e) => "#define " + e)]);
3721
+ this.#e() && e.push("ROUNDED_CORNERS"), this.#t() && e.push("STROKED"), this.properties.shadowOpacity && e.push("SHADOW"), this.createAndLinkShaders(el, tl, [nl, ...e.map((e) => "#define " + e)]);
3718
3722
  }
3719
3723
  finalizeGraphicsInitialization() {
3720
3724
  super.finalizeGraphicsInitialization(), this.gl.useProgram(this.programInfo.program);
3721
3725
  let e = this.properties;
3722
- this.registerMarkUniformValue("uMinWidth", e.minWidth), this.registerMarkUniformValue("uMinHeight", e.minHeight), this.registerMarkUniformValue("uMinOpacity", e.minOpacity), this.registerMarkUniformValue("uCornerRadiusTopRight", e.cornerRadiusTopRight ?? e.cornerRadius ?? 0), this.registerMarkUniformValue("uCornerRadiusBottomRight", e.cornerRadiusBottomRight ?? e.cornerRadius ?? 0), this.registerMarkUniformValue("uCornerRadiusTopLeft", e.cornerRadiusTopLeft ?? e.cornerRadius ?? 0), this.registerMarkUniformValue("uCornerRadiusBottomLeft", e.cornerRadiusBottomLeft ?? e.cornerRadius ?? 0), this.registerMarkUniformValue("uHatchPattern", e.hatch, (e) => Math.max(0, nl.indexOf(e ?? "none"))), this.registerMarkUniformValue("uShadowBlur", e.shadowBlur ?? 0), this.registerMarkUniformValue("uShadowOpacity", e.shadowOpacity ?? 0), this.registerMarkUniformValue("uShadowOffsetX", e.shadowOffsetX ?? 0), this.registerMarkUniformValue("uShadowOffsetY", e.shadowOffsetY ?? 0), this.registerMarkUniformValue("uShadowColor", e.shadowColor ?? "black", Xo);
3726
+ this.registerMarkUniformValue("uMinWidth", e.minWidth), this.registerMarkUniformValue("uMinHeight", e.minHeight), this.registerMarkUniformValue("uMinOpacity", e.minOpacity), this.registerMarkUniformValue("uCornerRadiusTopRight", e.cornerRadiusTopRight ?? e.cornerRadius ?? 0), this.registerMarkUniformValue("uCornerRadiusBottomRight", e.cornerRadiusBottomRight ?? e.cornerRadius ?? 0), this.registerMarkUniformValue("uCornerRadiusTopLeft", e.cornerRadiusTopLeft ?? e.cornerRadius ?? 0), this.registerMarkUniformValue("uCornerRadiusBottomLeft", e.cornerRadiusBottomLeft ?? e.cornerRadius ?? 0), this.registerMarkUniformValue("uHatchPattern", e.hatch, (e) => Math.max(0, rl.indexOf(e ?? "none"))), this.registerMarkUniformValue("uShadowBlur", e.shadowBlur ?? 0), this.registerMarkUniformValue("uShadowOpacity", e.shadowOpacity ?? 0), this.registerMarkUniformValue("uShadowOffsetX", e.shadowOffsetX ?? 0), this.registerMarkUniformValue("uShadowOffsetY", e.shadowOffsetY ?? 0), this.registerMarkUniformValue("uShadowColor", e.shadowColor ?? "black", Xo);
3723
3727
  }
3724
3728
  updateGraphicsData(e) {
3725
3729
  let t = e.getItemCount(), n = new mc({
@@ -3741,7 +3745,7 @@ var Uc = "flat out vec4 vColor;\n\n/** Stroke width */\nflat out float vSize;\n\
3741
3745
  oo(t, this.vertexArrayInfo, t.TRIANGLE_STRIP, n, e);
3742
3746
  }, e);
3743
3747
  }
3744
- }, il = "// Line caps\nconst int BUTT = 0;\nconst int SQUARE = 1;\nconst int ROUND = 2;\n\n/** Position along the rule */\nin float pos;\n\n/** Which side of the stroke: -0.5 or 0.5 */\nin float side;\n\nflat out vec4 vColor;\n\n/** Stroke width */\nflat out float vSize;\n\n/** The distance from the line center to the direction of normal in pixels */\nout float vNormalLengthInPixels;\n\n/** Distances from the line endings. Used for rendering the round caps and dashes */\nout highp vec2 vPosInPixels;\n\n\nvoid main(void) {\n#pragma orderGuard\n\n float pixelSize = 1.0 / uDevicePixelRatio;\n\n // Stroke width in pixels\n float size = getScaled_size();\n float opacity = getScaled_opacity() * uViewOpacity;\n\n // Avoid artifacts in very thin lines by clamping the size and adjusting opacity respectively\n if (size < pixelSize) {\n opacity *= size / pixelSize;\n size = pixelSize;\n }\n\n vec2 a = applyOffset(\n applySampleFacet(vec2(getScaled_x(), getScaled_y())),\n vec2(getScaled_xOffset(), getScaled_yOffset())\n );\n vec2 b = applyOffset(\n applySampleFacet(vec2(getScaled_x2(), getScaled_y2())),\n vec2(getScaled_x2Offset(), getScaled_y2Offset())\n );\n\n vec2 tangent = b - a;\n\n float offset = 0.0;\n float relativeDiff = 0.0;\n if (uMinLength > 0.0 || uStrokeCap != BUTT) {\n float len = length(tangent * uViewportSize);\n\n // Elongate to reach the minimum length.\n // The length difference in pixels\n float diff = max(0.0, uMinLength - len);\n\n // Add line caps\n if (uStrokeCap != BUTT) {\n diff += size;\n }\n\n relativeDiff = diff / len;\n offset = relativeDiff * (pos - 0.5);\n }\n\n // Apply caps and minimum length by spreading the vertices along the tangent\n vec2 p = pos < 1.0\n ? a + tangent * (pos + offset)\n : b + tangent * offset;\n\n // Add an extra pixel to stroke width to accommodate edge antialiasing\n float aaPadding = pixelSize;\n\n // Extrude\n vec2 normal = normalize(vec2(-tangent.y, tangent.x) / uViewportSize);\n p += normal * side * (size + aaPadding) / uViewportSize;\n\n gl_Position = unitToNdc(p);\n\n vColor = vec4(getScaled_color() * opacity, opacity);\n vSize = size;\n vNormalLengthInPixels = side * (size + aaPadding);\n\n // TODO: Here's a precision problem that breaks round caps when zoomed in enough\n vPosInPixels = vec2(pos, (1.0 - pos)) * (1.0 + relativeDiff) * length(tangent * uViewportSize) -\n vec2(uStrokeCap != BUTT ? size / 2.0 : 0.0);\n \n setupPicking();\n}\n", al = "// Line ending\nconst int BUTT = 0;\nconst int SQUARE = 1;\nconst int ROUND = 2;\n\nuniform sampler2D uDashTexture;\n\nflat in vec4 vColor;\nflat in float vSize;\n\n/** Position on the rule along its length in pixels */\nin vec2 vPosInPixels;\nin float vNormalLengthInPixels;\n\nout lowp vec4 fragColor;\n\nvoid main(void) {\n float dpr = uDevicePixelRatio;\n\n float distanceFromEnd = -min(vPosInPixels[0], vPosInPixels[1]);\n float distance; // from the rule centerline or end\n if (distanceFromEnd > 0.0 && uStrokeCap == ROUND) {\n // round cap\n distance = length(vec2(distanceFromEnd, vNormalLengthInPixels));\n } else {\n distance = abs(vNormalLengthInPixels);\n }\n\n // Use a signed distance field to implement edge antialiasing\n float opacity = clamp(((vSize / 2.0 - distance) * dpr), -0.5, 0.5) + 0.5;\n\n if (uDashTextureSize > 0.0) {\n float pos = (vPosInPixels[0] + uStrokeDashOffset) * dpr;\n float floored = floor(pos);\n vec2 texelPositions = (floored + vec2(0.5, 1.5)) / dpr / uDashTextureSize;\n \n // Do antialiasing\n opacity *= mix(\n texture(uDashTexture, vec2(texelPositions[0], 0)).r,\n texture(uDashTexture, vec2(texelPositions[1], 0)).r,\n clamp((pos - floored), 0.0, 1.0));\n }\n\n fragColor = vColor * opacity;\n\n if (uPickingEnabled) {\n fragColor = vPickingColor;\n }\n}\n", ol = "layout(std140) uniform Mark {\n /** Minimum rule length in pixels */\n uniform mediump float uMinLength;\n\n uniform mediump float uDashTextureSize;\n uniform lowp int uStrokeCap;\n uniform mediump float uStrokeDashOffset;\n\n#pragma markUniforms\n};\n", sl = class extends kc {
3748
+ }, al = "// Line caps\nconst int BUTT = 0;\nconst int SQUARE = 1;\nconst int ROUND = 2;\n\n/** Position along the rule */\nin float pos;\n\n/** Which side of the stroke: -0.5 or 0.5 */\nin float side;\n\nflat out vec4 vColor;\n\n/** Stroke width */\nflat out float vSize;\n\n/** The distance from the line center to the direction of normal in pixels */\nout float vNormalLengthInPixels;\n\n/** Distances from the line endings. Used for rendering the round caps and dashes */\nout highp vec2 vPosInPixels;\n\n\nvoid main(void) {\n#pragma orderGuard\n\n float pixelSize = 1.0 / uDevicePixelRatio;\n\n // Stroke width in pixels\n float size = getScaled_size();\n float opacity = getScaled_opacity() * uViewOpacity;\n\n // Avoid artifacts in very thin lines by clamping the size and adjusting opacity respectively\n if (size < pixelSize) {\n opacity *= size / pixelSize;\n size = pixelSize;\n }\n\n vec2 a = applyOffset(\n applySampleFacet(vec2(getScaled_x(), getScaled_y())),\n vec2(getScaled_xOffset(), getScaled_yOffset())\n );\n vec2 b = applyOffset(\n applySampleFacet(vec2(getScaled_x2(), getScaled_y2())),\n vec2(getScaled_x2Offset(), getScaled_y2Offset())\n );\n\n vec2 tangent = b - a;\n\n float offset = 0.0;\n float relativeDiff = 0.0;\n if (uMinLength > 0.0 || uStrokeCap != BUTT) {\n float len = length(tangent * uViewportSize);\n\n // Elongate to reach the minimum length.\n // The length difference in pixels\n float diff = max(0.0, uMinLength - len);\n\n // Add line caps\n if (uStrokeCap != BUTT) {\n diff += size;\n }\n\n relativeDiff = diff / len;\n offset = relativeDiff * (pos - 0.5);\n }\n\n // Apply caps and minimum length by spreading the vertices along the tangent\n vec2 p = pos < 1.0\n ? a + tangent * (pos + offset)\n : b + tangent * offset;\n\n // Add an extra pixel to stroke width to accommodate edge antialiasing\n float aaPadding = pixelSize;\n\n // Extrude\n vec2 normal = normalize(vec2(-tangent.y, tangent.x) / uViewportSize);\n p += normal * side * (size + aaPadding) / uViewportSize;\n\n gl_Position = unitToNdc(p);\n\n vColor = vec4(getScaled_color() * opacity, opacity);\n vSize = size;\n vNormalLengthInPixels = side * (size + aaPadding);\n\n // TODO: Here's a precision problem that breaks round caps when zoomed in enough\n vPosInPixels = vec2(pos, (1.0 - pos)) * (1.0 + relativeDiff) * length(tangent * uViewportSize) -\n vec2(uStrokeCap != BUTT ? size / 2.0 : 0.0);\n \n setupPicking();\n}\n", ol = "// Line ending\nconst int BUTT = 0;\nconst int SQUARE = 1;\nconst int ROUND = 2;\n\nuniform sampler2D uDashTexture;\n\nflat in vec4 vColor;\nflat in float vSize;\n\n/** Position on the rule along its length in pixels */\nin vec2 vPosInPixels;\nin float vNormalLengthInPixels;\n\nout lowp vec4 fragColor;\n\nvoid main(void) {\n float dpr = uDevicePixelRatio;\n\n float distanceFromEnd = -min(vPosInPixels[0], vPosInPixels[1]);\n float distance; // from the rule centerline or end\n if (distanceFromEnd > 0.0 && uStrokeCap == ROUND) {\n // round cap\n distance = length(vec2(distanceFromEnd, vNormalLengthInPixels));\n } else {\n distance = abs(vNormalLengthInPixels);\n }\n\n // Use a signed distance field to implement edge antialiasing\n float opacity = clamp(((vSize / 2.0 - distance) * dpr), -0.5, 0.5) + 0.5;\n\n if (uDashTextureSize > 0.0) {\n float pos = (vPosInPixels[0] + uStrokeDashOffset) * dpr;\n float floored = floor(pos);\n vec2 texelPositions = (floored + vec2(0.5, 1.5)) / dpr / uDashTextureSize;\n \n // Do antialiasing\n opacity *= mix(\n texture(uDashTexture, vec2(texelPositions[0], 0)).r,\n texture(uDashTexture, vec2(texelPositions[1], 0)).r,\n clamp((pos - floored), 0.0, 1.0));\n }\n\n fragColor = vColor * opacity;\n\n if (uPickingEnabled) {\n fragColor = vPickingColor;\n }\n}\n", sl = "layout(std140) uniform Mark {\n /** Minimum rule length in pixels */\n uniform mediump float uMinLength;\n\n uniform mediump float uDashTextureSize;\n uniform lowp int uStrokeCap;\n uniform mediump float uStrokeDashOffset;\n\n#pragma markUniforms\n};\n", cl = class extends Ac {
3745
3749
  constructor(e, t, n) {
3746
3750
  super(e, t, n), this.dashTexture = void 0, this.dashTextureSize = 0;
3747
3751
  }
@@ -3764,7 +3768,7 @@ var Uc = "flat out vec4 vColor;\n\n/** Stroke width */\nflat out float vSize;\n\
3764
3768
  }
3765
3769
  initializeGraphics() {
3766
3770
  super.initializeGraphics();
3767
- let e = this.gl, t = cl(this.properties.strokeDash);
3771
+ let e = this.gl, t = ll(this.properties.strokeDash);
3768
3772
  this.dashTexture = ai(e, {
3769
3773
  level: 0,
3770
3774
  mag: e.NEAREST,
@@ -3773,7 +3777,7 @@ var Uc = "flat out vec4 vColor;\n\n/** Stroke width */\nflat out float vSize;\n\
3773
3777
  format: e.RED,
3774
3778
  src: t,
3775
3779
  height: 1
3776
- }), this.dashTextureSize = t.length, this.createAndLinkShaders(il, al, [ol]);
3780
+ }), this.dashTextureSize = t.length, this.createAndLinkShaders(al, ol, [sl]);
3777
3781
  }
3778
3782
  finalizeGraphicsInitialization() {
3779
3783
  super.finalizeGraphicsInitialization(), this.gl.useProgram(this.programInfo.program);
@@ -3806,7 +3810,7 @@ var Uc = "flat out vec4 vColor;\n\n/** Stroke width */\nflat out float vSize;\n\
3806
3810
  this.dashTexture &&= (this.gl.deleteTexture(this.dashTexture), void 0), super.dispose();
3807
3811
  }
3808
3812
  };
3809
- function cl(e) {
3813
+ function ll(e) {
3810
3814
  if (!e) return /* @__PURE__ */ new Uint8Array();
3811
3815
  if (e.length == 0 || e.length % 2 || e.findIndex((e) => Math.round(e) != e || e < 1 || e > 1e3) >= 0) throw Error("Invalid stroke dash pattern: " + JSON.stringify(e));
3812
3816
  let t = e.reduce((e, t) => e + t), n = new Uint8Array(t), r = !0, i = 0;
@@ -3818,24 +3822,24 @@ function cl(e) {
3818
3822
  }
3819
3823
  //#endregion
3820
3824
  //#region ../core/src/rendering/webgl/marks/text.vertex.glsl
3821
- var ll = "in mediump vec2 vertexCoord;\nin lowp vec2 textureCoord;\n\n// Width of the text (all letters)\nin float width;\n\nout vec2 vTexCoord;\nout float vEdgeFadeOpacity;\nflat out vec4 vColor;\nflat out float vSlope;\nflat out float vGamma;\n\nstruct RangeResult {\n float pos;\n float scale;\n};\n\nfloat minValue(vec4 v) {\n return min(min(v.x, v.y), min(v.z, v.w));\n}\n\nfloat maxValue(vec4 v) {\n return max(max(v.x, v.y), max(v.z, v.w));\n}\n\n/**\n * All measures are in [0, 1]\n */\nRangeResult positionInsideRange(float a, float b, float width, float padding,\n int align, bool flush) {\n float span = b - a;\n float paddedWidth = width + 2.0 * padding;\n\n // Is the text clearly outside the viewport\n if (a > 1.0 || b < 0.0) {\n return RangeResult(0.0, 0.0);\n }\n\n // How much extra space we have for adjusting the position so that the\n // text stays inside the range.\n float extra = max(0.0, span - paddedWidth);\n\n float pos;\n\n // Align the text and try to keep it inside the range and the viewport\n if (align == 0) {\n float centre = a + b;\n\n if (flush) {\n float leftOver = max(0.0, paddedWidth - centre);\n centre += min(leftOver, extra);\n\n float rightOver = max(0.0, paddedWidth + centre - 2.0);\n centre -= min(rightOver, extra);\n }\n\n pos = centre / 2.0;\n\n } else if (align < 0) {\n float edge = a;\n\n if (flush) {\n float over = max(0.0, -edge);\n edge += min(over, extra);\n }\n\n pos = edge + padding;\n\n } else {\n float edge = b;\n\n if (flush) {\n float over = max(0.0, edge - 1.0);\n edge -= min(over, extra);\n }\n\n // TODO: If the text spans the whole viewport, try to keep it centered if possible.\n\n pos = edge - padding;\n }\n\n // How the text should be scaled to make it fit inside the range (if it didn't fit).\n float scale = clamp((span - padding) / paddedWidth, 0.0, 1.0);\n\n // TODO: Fix padding in scale factor. Padding should stay constant\n return RangeResult(pos, scale);\n}\n\nvec2 calculateRotatedDimensions(float width, mat2 rotationMatrix) {\n vec2 a = abs(rotationMatrix * vec2(width / 2.0, 0.5));\n vec2 b = abs(rotationMatrix * vec2(width / 2.0, -0.5));\n return vec2(max(a.x, b.x), max(a.y, b.y)) * 2.0;\n}\n\n/** Needed when using ranged text */\nivec2 fixAlignForAngle(ivec2 align, float angleInDegrees) {\n float a = mod(angleInDegrees + 45.0, 360.0);\n int x = align.x;\n int y = -align.y;\n\n // TODO: Optimize by avoiding branching\n if (a < 90.0) {\n return ivec2(x, y);\n } else if (a < 180.0) {\n return ivec2(y, -x);\n } else if (a < 270.0) {\n return ivec2(-x, y);\n } else {\n return ivec2(-y, x);\n }\n}\n\nvoid main(void) {\n#pragma orderGuard\n\n float opacity = getScaled_opacity() * uViewOpacity;\n vec2 size = vec2(getScaled_size());\n float x = getScaled_x() + getScaled_xOffset() / uViewportSize.x;\n float y = getScaled_y();\n\n float scale = 1.0;\n\n float angleInDegrees = getScaled_angle();\n float angle = -angleInDegrees * PI / 180.0;\n \n float sinTheta = sin(angle);\n float cosTheta = cos(angle);\n mat2 rotationMatrix = mat2(cosTheta, sinTheta, -sinTheta, cosTheta);\n\n vec2 flushSize = calculateRotatedDimensions(width, rotationMatrix);\n\n#if defined(x2_DEFINED) || defined(y2_DEFINED)\n ivec2 align = fixAlignForAngle(uAlign, angleInDegrees);\n#else\n ivec2 align = uAlign;\n#endif\n\n#ifdef x2_DEFINED\n float x2 = getScaled_x2() + getScaled_x2Offset() / uViewportSize.x;\n\n if (uLogoLetter) {\n size.x = (x2 - x) * uViewportSize.x;\n x += (x2 - x) / 2.0;\n\n } else {\n RangeResult result = positionInsideRange(\n min(x, x2), max(x, x2),\n size.x * scale * flushSize.x / uViewportSize.x, uPaddingX / uViewportSize.x,\n align.x, uFlushX);\n \n x = result.pos;\n scale *= result.scale;\n }\n#endif\n\n // Position of the text origo \n vec2 pos = applyOffset(\n applySampleFacet(vec2(x, y)),\n vec2(0.0, getScaled_yOffset())\n );\n\n#ifdef y2_DEFINED\n float y2 = getScaled_y2();\n vec2 pos2 = applyOffset(\n applySampleFacet(vec2(x, y2)),\n vec2(0.0, getScaled_y2Offset())\n );\n\n if (uLogoLetter) {\n size.y = (pos2.y - pos.y) * uViewportSize.y;\n pos.y += (pos2.y - pos.y) / 2.0;\n\n } else {\n RangeResult result = positionInsideRange(\n min(pos.y, pos2.y), max(pos.y, pos2.y),\n size.y * scale * flushSize.y / uViewportSize.y, uPaddingY / uViewportSize.y,\n align.y, uFlushY);\n \n pos.y = result.pos;\n scale *= result.scale;\n }\n#endif\n\n#ifdef VISIBLE_RANGE_CULLING\n if (isOutsideVisibleRange(pos)) {\n gl_Position = vec4(0.0);\n return;\n }\n#endif\n\n if (scale < 1.0) {\n if (uSqueeze) {\n vec2 scaleFadeExtent = vec2(3.0, 6.0) / size;\n\n if (scale < scaleFadeExtent[0]) {\n gl_Position = vec4(0.0);\n return;\n }\n\n size *= scale;\n opacity *= linearstep(scaleFadeExtent[0], scaleFadeExtent[1], scale);\n\n } else if (scale < 1.0) {\n // Eliminate the text\n gl_Position = vec4(0.0);\n return;\n }\n }\n\n // Position of the character vertex in relation to the text origo\n vec2 charPos = rotationMatrix * (vertexCoord * size + uD);\n\n // Position of the character vertex inside the unit viewport\n vec2 unitPos = pos + charPos / uViewportSize;\n\n gl_Position = unitToNdc(unitPos);\n\n // Controls antialiasing of the SDF\n vSlope = max(1.0, min(size.x, size.y) / uSdfNumerator * uDevicePixelRatio);\n\n vec3 color = getScaled_color();\n\n // Premultiply alpha\n vColor = vec4(color * opacity, opacity);\n\n // Do something like gamma correction for text when the color is close to black or white.\n vGamma = getGammaForColor(color);\n\n vTexCoord = textureCoord;\n\n // Edge fading. The implementation is simplistic and fails with primitives that\n // span the whole viewport. However, it works just fine with reasonable font sizes.\n // x: top, y: right, z: bottom, w: left\n if (maxValue(uViewportEdgeFadeDistance) > -pow(10.0, 10.0)) { // -Infinity would be nice\n vEdgeFadeOpacity = minValue(\n ((vec4(1.0, 1.0, 0.0, 0.0) + vec4(-1.0, -1.0, 1.0, 1.0) * unitPos.yxyx) *\n uViewportSize.yxyx - uViewportEdgeFadeDistance) / uViewportEdgeFadeWidth);\n } else {\n vEdgeFadeOpacity = 1.0;\n }\n\n setupPicking();\n}\n", ul = "uniform sampler2D uTexture;\n\nin vec2 vTexCoord;\nin float vEdgeFadeOpacity;\nflat in vec4 vColor;\nflat in float vSlope;\nflat in float vGamma;\n\nout lowp vec4 fragColor;\n\nfloat median(float r, float g, float b) {\n return max(min(r, g), min(max(r, g), b));\n}\n\nfloat getDist(vec2 uv) {\n vec3 c = texture(uTexture, uv).rgb;\n return 1.0 - median(c.r, c.g, c.b);\n}\n\n/**\n * Calculates the super-sampled distance to the edge.\n * This is used to avoid aliasing when rendering small text,\n * as mip-mapping cannot be used here.\n * The distance is averaged over a grid of n x n samples.\n */\nfloat getSuperDist(vec2 uv) {\n vec2 dx = dFdx(uv);\n vec2 dy = dFdy(uv);\n\n float n = 2.0;\n\n float sum = 0.0;\n for (float x = 0.5; x < n; x++){\n for (float y = 0.5; y < n; y++) {\n sum += getDist(\n uv +\n x / n * dx +\n y / n * dy\n ); \n }\n }\n\n return sum / (n * n);\n}\n\nvoid main() {\n float sigDist = getSuperDist(vTexCoord);\n\n float slope = vSlope;\n if (uLogoLetter) {\n // Using screen-space derivatives for logo letters because skewed aspect ratios\n // result in blurry edges otherwise. However, use of screen-space derivatives\n // results in crappy looking text with regular letters text.\n slope = 0.7 / length(vec2(dFdy(sigDist), dFdx(sigDist)));\n }\n\n float opa = clamp((sigDist - 0.5) * slope + 0.5, 0.0, 1.0);\n opa *= clamp(vEdgeFadeOpacity, 0.0, 1.0);\n\n opa = pow(opa, vGamma);\n\n fragColor = vColor * opa;\n\n if (uPickingEnabled) {\n fragColor = vPickingColor;\n }\n}\n", dl = "layout(std140) uniform Mark {\n uniform mediump float uSdfNumerator;\n\n uniform mediump vec2 uD; // dx & dy\n\n uniform mediump vec4 uViewportEdgeFadeWidth;\n uniform mediump vec4 uViewportEdgeFadeDistance;\n \n uniform bool uSqueeze;\n uniform bool uLogoLetter;\n\n // x: -1, 0, 1 = left, center, right\n // y: -1, 0, 1 = top, middle, bottom \n uniform lowp ivec2 uAlign;\n\n uniform mediump float uPaddingX;\n uniform bool uFlushX;\n uniform mediump float uPaddingY;\n uniform bool uFlushY;\n\n#pragma markUniforms\n};\n", fl = 1024, pl = {
3825
+ var ul = "in mediump vec2 vertexCoord;\nin lowp vec2 textureCoord;\n\n// Width of the text (all letters)\nin float width;\n\nout vec2 vTexCoord;\nout float vEdgeFadeOpacity;\nflat out vec4 vColor;\nflat out float vSlope;\nflat out float vGamma;\n\nstruct RangeResult {\n float pos;\n float scale;\n};\n\nfloat minValue(vec4 v) {\n return min(min(v.x, v.y), min(v.z, v.w));\n}\n\nfloat maxValue(vec4 v) {\n return max(max(v.x, v.y), max(v.z, v.w));\n}\n\n/**\n * All measures are in [0, 1]\n */\nRangeResult positionInsideRange(float a, float b, float width, float padding,\n int align, bool flush) {\n float span = b - a;\n float paddedWidth = width + 2.0 * padding;\n\n // Is the text clearly outside the viewport\n if (a > 1.0 || b < 0.0) {\n return RangeResult(0.0, 0.0);\n }\n\n // How much extra space we have for adjusting the position so that the\n // text stays inside the range.\n float extra = max(0.0, span - paddedWidth);\n\n float pos;\n\n // Align the text and try to keep it inside the range and the viewport\n if (align == 0) {\n float centre = a + b;\n\n if (flush) {\n float leftOver = max(0.0, paddedWidth - centre);\n centre += min(leftOver, extra);\n\n float rightOver = max(0.0, paddedWidth + centre - 2.0);\n centre -= min(rightOver, extra);\n }\n\n pos = centre / 2.0;\n\n } else if (align < 0) {\n float edge = a;\n\n if (flush) {\n float over = max(0.0, -edge);\n edge += min(over, extra);\n }\n\n pos = edge + padding;\n\n } else {\n float edge = b;\n\n if (flush) {\n float over = max(0.0, edge - 1.0);\n edge -= min(over, extra);\n }\n\n // TODO: If the text spans the whole viewport, try to keep it centered if possible.\n\n pos = edge - padding;\n }\n\n // How the text should be scaled to make it fit inside the range (if it didn't fit).\n float scale = clamp((span - padding) / paddedWidth, 0.0, 1.0);\n\n // TODO: Fix padding in scale factor. Padding should stay constant\n return RangeResult(pos, scale);\n}\n\nvec2 calculateRotatedDimensions(float width, mat2 rotationMatrix) {\n vec2 a = abs(rotationMatrix * vec2(width / 2.0, 0.5));\n vec2 b = abs(rotationMatrix * vec2(width / 2.0, -0.5));\n return vec2(max(a.x, b.x), max(a.y, b.y)) * 2.0;\n}\n\n/** Needed when using ranged text */\nivec2 fixAlignForAngle(ivec2 align, float angleInDegrees) {\n float a = mod(angleInDegrees + 45.0, 360.0);\n int x = align.x;\n int y = -align.y;\n\n // TODO: Optimize by avoiding branching\n if (a < 90.0) {\n return ivec2(x, y);\n } else if (a < 180.0) {\n return ivec2(y, -x);\n } else if (a < 270.0) {\n return ivec2(-x, y);\n } else {\n return ivec2(-y, x);\n }\n}\n\nvoid main(void) {\n#pragma orderGuard\n\n float opacity = getScaled_opacity() * uViewOpacity;\n vec2 size = vec2(getScaled_size());\n float x = getScaled_x() + getScaled_xOffset() / uViewportSize.x;\n float y = getScaled_y();\n\n float scale = 1.0;\n\n float angleInDegrees = getScaled_angle();\n float angle = -angleInDegrees * PI / 180.0;\n \n float sinTheta = sin(angle);\n float cosTheta = cos(angle);\n mat2 rotationMatrix = mat2(cosTheta, sinTheta, -sinTheta, cosTheta);\n\n vec2 flushSize = calculateRotatedDimensions(width, rotationMatrix);\n\n#if defined(x2_DEFINED) || defined(y2_DEFINED)\n ivec2 align = fixAlignForAngle(uAlign, angleInDegrees);\n#else\n ivec2 align = uAlign;\n#endif\n\n#ifdef x2_DEFINED\n float x2 = getScaled_x2() + getScaled_x2Offset() / uViewportSize.x;\n\n if (uLogoLetter) {\n size.x = (x2 - x) * uViewportSize.x;\n x += (x2 - x) / 2.0;\n\n } else {\n RangeResult result = positionInsideRange(\n min(x, x2), max(x, x2),\n size.x * scale * flushSize.x / uViewportSize.x, uPaddingX / uViewportSize.x,\n align.x, uFlushX);\n \n x = result.pos;\n scale *= result.scale;\n }\n#endif\n\n // Position of the text origo \n vec2 pos = applyOffset(\n applySampleFacet(vec2(x, y)),\n vec2(0.0, getScaled_yOffset())\n );\n\n#ifdef y2_DEFINED\n float y2 = getScaled_y2();\n vec2 pos2 = applyOffset(\n applySampleFacet(vec2(x, y2)),\n vec2(0.0, getScaled_y2Offset())\n );\n\n if (uLogoLetter) {\n size.y = (pos2.y - pos.y) * uViewportSize.y;\n pos.y += (pos2.y - pos.y) / 2.0;\n\n } else {\n RangeResult result = positionInsideRange(\n min(pos.y, pos2.y), max(pos.y, pos2.y),\n size.y * scale * flushSize.y / uViewportSize.y, uPaddingY / uViewportSize.y,\n align.y, uFlushY);\n \n pos.y = result.pos;\n scale *= result.scale;\n }\n#endif\n\n#ifdef VISIBLE_RANGE_CULLING\n if (isOutsideVisibleRange(pos)) {\n gl_Position = vec4(0.0);\n return;\n }\n#endif\n\n if (scale < 1.0) {\n if (uSqueeze) {\n vec2 scaleFadeExtent = vec2(3.0, 6.0) / size;\n\n if (scale < scaleFadeExtent[0]) {\n gl_Position = vec4(0.0);\n return;\n }\n\n size *= scale;\n opacity *= linearstep(scaleFadeExtent[0], scaleFadeExtent[1], scale);\n\n } else if (scale < 1.0) {\n // Eliminate the text\n gl_Position = vec4(0.0);\n return;\n }\n }\n\n // Position of the character vertex in relation to the text origo\n vec2 charPos = rotationMatrix * (vertexCoord * size + uD);\n\n // Position of the character vertex inside the unit viewport\n vec2 unitPos = pos + charPos / uViewportSize;\n\n gl_Position = unitToNdc(unitPos);\n\n // Controls antialiasing of the SDF\n vSlope = max(1.0, min(size.x, size.y) / uSdfNumerator * uDevicePixelRatio);\n\n vec3 color = getScaled_color();\n\n // Premultiply alpha\n vColor = vec4(color * opacity, opacity);\n\n // Do something like gamma correction for text when the color is close to black or white.\n vGamma = getGammaForColor(color);\n\n vTexCoord = textureCoord;\n\n // Edge fading. The implementation is simplistic and fails with primitives that\n // span the whole viewport. However, it works just fine with reasonable font sizes.\n // x: top, y: right, z: bottom, w: left\n if (maxValue(uViewportEdgeFadeDistance) > -pow(10.0, 10.0)) { // -Infinity would be nice\n vEdgeFadeOpacity = minValue(\n ((vec4(1.0, 1.0, 0.0, 0.0) + vec4(-1.0, -1.0, 1.0, 1.0) * unitPos.yxyx) *\n uViewportSize.yxyx - uViewportEdgeFadeDistance) / uViewportEdgeFadeWidth);\n } else {\n vEdgeFadeOpacity = 1.0;\n }\n\n setupPicking();\n}\n", dl = "uniform sampler2D uTexture;\n\nin vec2 vTexCoord;\nin float vEdgeFadeOpacity;\nflat in vec4 vColor;\nflat in float vSlope;\nflat in float vGamma;\n\nout lowp vec4 fragColor;\n\nfloat median(float r, float g, float b) {\n return max(min(r, g), min(max(r, g), b));\n}\n\nfloat getDist(vec2 uv) {\n vec3 c = texture(uTexture, uv).rgb;\n return 1.0 - median(c.r, c.g, c.b);\n}\n\n/**\n * Calculates the super-sampled distance to the edge.\n * This is used to avoid aliasing when rendering small text,\n * as mip-mapping cannot be used here.\n * The distance is averaged over a grid of n x n samples.\n */\nfloat getSuperDist(vec2 uv) {\n vec2 dx = dFdx(uv);\n vec2 dy = dFdy(uv);\n\n float n = 2.0;\n\n float sum = 0.0;\n for (float x = 0.5; x < n; x++){\n for (float y = 0.5; y < n; y++) {\n sum += getDist(\n uv +\n x / n * dx +\n y / n * dy\n ); \n }\n }\n\n return sum / (n * n);\n}\n\nvoid main() {\n float sigDist = getSuperDist(vTexCoord);\n\n float slope = vSlope;\n if (uLogoLetter) {\n // Using screen-space derivatives for logo letters because skewed aspect ratios\n // result in blurry edges otherwise. However, use of screen-space derivatives\n // results in crappy looking text with regular letters text.\n slope = 0.7 / length(vec2(dFdy(sigDist), dFdx(sigDist)));\n }\n\n float opa = clamp((sigDist - 0.5) * slope + 0.5, 0.0, 1.0);\n opa *= clamp(vEdgeFadeOpacity, 0.0, 1.0);\n\n opa = pow(opa, vGamma);\n\n fragColor = vColor * opa;\n\n if (uPickingEnabled) {\n fragColor = vPickingColor;\n }\n}\n", fl = "layout(std140) uniform Mark {\n uniform mediump float uSdfNumerator;\n\n uniform mediump vec2 uD; // dx & dy\n\n uniform mediump vec4 uViewportEdgeFadeWidth;\n uniform mediump vec4 uViewportEdgeFadeDistance;\n \n uniform bool uSqueeze;\n uniform bool uLogoLetter;\n\n // x: -1, 0, 1 = left, center, right\n // y: -1, 0, 1 = top, middle, bottom \n uniform lowp ivec2 uAlign;\n\n uniform mediump float uPaddingX;\n uniform bool uFlushX;\n uniform mediump float uPaddingY;\n uniform bool uFlushY;\n\n#pragma markUniforms\n};\n", pl = 1024, ml = {
3822
3826
  left: -1,
3823
3827
  center: 0,
3824
3828
  right: 1
3825
- }, ml = {
3829
+ }, hl = {
3826
3830
  top: -1,
3827
3831
  middle: 0,
3828
3832
  bottom: 1,
3829
3833
  alphabetic: 1,
3830
3834
  baseline: 1
3831
- }, hl = {
3832
- point: Qc,
3833
- rect: rl,
3834
- arrow: zc,
3835
- rule: sl,
3836
- tick: sl,
3837
- link: Jc,
3838
- text: class extends kc {
3835
+ }, gl = {
3836
+ point: $c,
3837
+ rect: il,
3838
+ arrow: Bc,
3839
+ rule: cl,
3840
+ tick: cl,
3841
+ link: Yc,
3842
+ text: class extends Ac {
3839
3843
  getAttributes() {
3840
3844
  return [
3841
3845
  "uniqueId",
@@ -3855,7 +3859,7 @@ var ll = "in mediump vec2 vertexCoord;\nin lowp vec2 textureCoord;\n\n// Width o
3855
3859
  ];
3856
3860
  }
3857
3861
  initializeGraphics() {
3858
- super.initializeGraphics(), this.createAndLinkShaders(ll, ul, [dl]);
3862
+ super.initializeGraphics(), this.createAndLinkShaders(ul, dl, [fl]);
3859
3863
  }
3860
3864
  finalizeGraphicsInitialization() {
3861
3865
  super.finalizeGraphicsInitialization(), this.gl.useProgram(this.programInfo.program);
@@ -3871,7 +3875,7 @@ var ll = "in mediump vec2 vertexCoord;\nin lowp vec2 textureCoord;\n\n// Width o
3871
3875
  e.viewportEdgeFadeDistanceBottom,
3872
3876
  e.viewportEdgeFadeDistanceLeft
3873
3877
  ]), $a(this.markUniformInfo, {
3874
- uAlign: [pl[e.align], ml[e.baseline]],
3878
+ uAlign: [ml[e.align], hl[e.baseline]],
3875
3879
  uD: [e.dx, -e.dy]
3876
3880
  });
3877
3881
  }
@@ -3895,7 +3899,7 @@ var ll = "in mediump vec2 vertexCoord;\nin lowp vec2 textureCoord;\n\n// Width o
3895
3899
  logoLetters: i
3896
3900
  },
3897
3901
  fontMetrics: this.font.metrics,
3898
- numCharacters: Math.max(o, fl)
3902
+ numCharacters: Math.max(o, pl)
3899
3903
  });
3900
3904
  c.addBatches(e.facetBatches);
3901
3905
  let u = c.toArrays();
@@ -3913,12 +3917,12 @@ var ll = "in mediump vec2 vertexCoord;\nin lowp vec2 textureCoord;\n\n// Width o
3913
3917
  }
3914
3918
  }
3915
3919
  };
3916
- function gl(e, t, n) {
3917
- let r = hl[e.getType()];
3920
+ function _l(e, t, n) {
3921
+ let r = gl[e.getType()];
3918
3922
  if (!r) throw Error("Unsupported WebGL mark type: " + e.getType());
3919
3923
  return new r(e, t, n);
3920
3924
  }
3921
- var _l = class {
3925
+ var vl = class {
3922
3926
  #e = !1;
3923
3927
  #t = /* @__PURE__ */ new Map();
3924
3928
  #n = /* @__PURE__ */ new Map();
@@ -3928,7 +3932,7 @@ var _l = class {
3928
3932
  }
3929
3933
  #i(e) {
3930
3934
  this.#s();
3931
- let t = gl(e, this.glHelper, this), n = {
3935
+ let t = _l(e, this.glHelper, this), n = {
3932
3936
  mark: e,
3933
3937
  graphics: t,
3934
3938
  state: "compiling",
@@ -4079,9 +4083,9 @@ var _l = class {
4079
4083
  };
4080
4084
  //#endregion
4081
4085
  //#region ../core/src/rendering/webgl/svgRasterizer.js
4082
- function vl({ runs: e, viewRoot: t, layoutResult: n, webGLHelper: r, markAdapter: i, logicalWidth: a, logicalHeight: o, pixelRatio: s }) {
4086
+ function yl({ runs: e, viewRoot: t, layoutResult: n, webGLHelper: r, markAdapter: i, logicalWidth: a, logicalHeight: o, pixelRatio: s }) {
4083
4087
  let c = r.gl, l = Math.ceil(a * s), u = Math.ceil(o * s);
4084
- yl(c, l, u);
4088
+ bl(c, l, u);
4085
4089
  let d = s == 1 && n ? n : Se(t, be.create(0, 0, a, o), {
4086
4090
  devicePixelRatio: s,
4087
4091
  renderingOptions: { firstFacet: !0 }
@@ -4116,14 +4120,14 @@ function vl({ runs: e, viewRoot: t, layoutResult: n, webGLHelper: r, markAdapter
4116
4120
  c.bindFramebuffer(c.FRAMEBUFFER, null), c.deleteTexture(f.attachments[0]), c.deleteFramebuffer(f.framebuffer);
4117
4121
  }
4118
4122
  }
4119
- function yl(e, t, n) {
4123
+ function bl(e, t, n) {
4120
4124
  let r = e.getParameter(e.MAX_RENDERBUFFER_SIZE), i = e.getParameter(e.MAX_TEXTURE_SIZE), a = Math.min(r, i);
4121
4125
  if (t <= 0 || n <= 0 || t > a || n > a) throw RangeError(`SVG raster dimensions ${t} x ${n} exceed the WebGL limit ${a}.`);
4122
4126
  }
4123
4127
  //#endregion
4124
4128
  //#region ../core/src/rendering/webgl/index.js
4125
- function bl(e) {
4126
- let t = new Vs(e.container, e.sizeSource, { powerPreference: e.powerPreference }, e.onCanvasResize), n = new _l(t);
4129
+ function xl(e) {
4130
+ let t = new Vs(e.container, e.sizeSource, { powerPreference: e.powerPreference }, e.onCanvasResize), n = new vl(t);
4127
4131
  return {
4128
4132
  surface: t,
4129
4133
  defaultFontBitmapUrl: Oe,
@@ -4144,7 +4148,7 @@ function bl(e) {
4144
4148
  glHelper: t,
4145
4149
  markAdapter: n
4146
4150
  }),
4147
- rasterizeSvgRuns: (e) => vl({
4151
+ rasterizeSvgRuns: (e) => yl({
4148
4152
  ...e,
4149
4153
  webGLHelper: t,
4150
4154
  markAdapter: n
@@ -4156,4 +4160,4 @@ function bl(e) {
4156
4160
  };
4157
4161
  }
4158
4162
  //#endregion
4159
- export { bl as createWebGLRenderingBackend };
4163
+ export { xl as createWebGLRenderingBackend };
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "contributors": [],
9
9
  "license": "MIT",
10
10
  "homepage": "https://genomespy.app/",
11
- "version": "0.88.0",
11
+ "version": "0.88.1",
12
12
  "main": "dist/index.js",
13
13
  "module": "dist/index.es.js",
14
14
  "type": "module",
@@ -71,7 +71,7 @@
71
71
  "@fortawesome/fontawesome-free": "^6.4.2",
72
72
  "@fortawesome/fontawesome-svg-core": "^6.4.2",
73
73
  "@fortawesome/free-solid-svg-icons": "^6.4.2",
74
- "@genome-spy/core": "^0.88.0",
74
+ "@genome-spy/core": "^0.88.1",
75
75
  "@reduxjs/toolkit": "^2.11.0",
76
76
  "d3-color": "^3.1.0",
77
77
  "idb": "^7.1.1",
@@ -84,8 +84,8 @@
84
84
  "zarrita": "^0.6.1"
85
85
  },
86
86
  "devDependencies": {
87
- "@genome-spy/app-agent": "^0.88.0",
88
- "@genome-spy/inspector": "^0.88.0"
87
+ "@genome-spy/app-agent": "^0.88.1",
88
+ "@genome-spy/inspector": "^0.88.1"
89
89
  },
90
- "gitHead": "9d88f5bcdd3d46507acf5bd8cd4212f6b14dd0ed"
90
+ "gitHead": "e0e01085f8f92e5da31120268f4b157be1666523"
91
91
  }