@jbrowse/render-core 5.0.0-beta.13 → 5.0.0-beta.15

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.
Files changed (153) hide show
  1. package/esm/OverlayCanvas.d.ts +3 -2
  2. package/esm/OverlayCanvas.js +4 -2
  3. package/esm/RenderCanvas.d.ts +4 -3
  4. package/esm/RenderCanvas.js +4 -2
  5. package/esm/ScrollLockedOverlay.d.ts +2 -2
  6. package/esm/ScrollLockedOverlay.js +3 -2
  7. package/esm/blockClipUtils.d.ts +5 -5
  8. package/esm/blockClipUtils.js +4 -3
  9. package/esm/canvas2dUtils.d.ts +15 -21
  10. package/esm/canvas2dUtils.js +25 -55
  11. package/esm/colorRampLut.d.ts +2 -2
  12. package/esm/colorRampLut.js +2 -2
  13. package/esm/coverageBand.d.ts +1 -2
  14. package/esm/coverageBand.js +1 -6
  15. package/esm/coverageBandBuffers.d.ts +8 -5
  16. package/esm/createRenderingBackend.d.ts +1 -1
  17. package/esm/displayPhase.d.ts +5 -9
  18. package/esm/displayPhase.js +2 -2
  19. package/esm/hal/mockHal.d.ts +3 -4
  20. package/esm/hal/mockHal.js +5 -4
  21. package/esm/hal/types.d.ts +5 -5
  22. package/esm/hal/webgl2Hal.d.ts +4 -1
  23. package/esm/hal/webgl2Hal.js +15 -6
  24. package/esm/hal/webgpuHal.js +2 -2
  25. package/esm/linkFeet.d.ts +4 -0
  26. package/esm/linkFeet.js +7 -0
  27. package/esm/marks/barMark.d.ts +1 -1
  28. package/esm/marks/colorFill.d.ts +4 -4
  29. package/esm/marks/colorFill.js +4 -4
  30. package/esm/marks/connectorMark.d.ts +48 -0
  31. package/esm/marks/connectorMark.js +285 -0
  32. package/esm/marks/drawAgainstHit.d.ts +1 -1
  33. package/esm/marks/drawAgainstHit.js +4 -4
  34. package/esm/marks/glyphPaint.d.ts +5 -4
  35. package/esm/marks/glyphPaint.js +17 -7
  36. package/esm/marks/index.d.ts +6 -3
  37. package/esm/marks/index.js +5 -3
  38. package/esm/marks/lineMark.d.ts +44 -10
  39. package/esm/marks/lineMark.js +20 -19
  40. package/esm/marks/linkMark.d.ts +22 -5
  41. package/esm/marks/linkMark.js +103 -25
  42. package/esm/marks/markBackend.d.ts +1 -1
  43. package/esm/marks/markBackend.js +1 -1
  44. package/esm/marks/markRamp.d.ts +11 -11
  45. package/esm/marks/markRamp.js +9 -9
  46. package/esm/marks/pointMark.d.ts +11 -5
  47. package/esm/marks/pointMark.js +34 -9
  48. package/esm/marks/rowLane.d.ts +3 -3
  49. package/esm/marks/rowLane.js +2 -2
  50. package/esm/marks/rowTable.d.ts +5 -5
  51. package/esm/marks/rowTable.js +4 -4
  52. package/esm/marks/ruleMark.js +1 -0
  53. package/esm/marks/spanMark.d.ts +4 -4
  54. package/esm/marks/types.d.ts +4 -4
  55. package/esm/marks/types.js +5 -0
  56. package/esm/perRegionRenderingBackend.d.ts +1 -1
  57. package/esm/perRegionRenderingBackend.js +1 -1
  58. package/esm/regionDataMap.d.ts +2 -2
  59. package/esm/regionDataMap.js +2 -2
  60. package/esm/shaders/capsule.js.generated.d.ts +1 -1
  61. package/esm/shaders/capsule.js.generated.js +2 -2
  62. package/esm/shaders/connectorMark.consts.generated.d.ts +11 -0
  63. package/esm/shaders/connectorMark.consts.generated.js +13 -0
  64. package/esm/shaders/{coverageIndicator.generated.d.ts → connectorMark.generated.d.ts} +2 -1
  65. package/esm/shaders/connectorMark.generated.js +8 -0
  66. package/esm/shaders/connectorMark.glsl.generated.d.ts +2 -0
  67. package/esm/shaders/connectorMark.glsl.generated.js +4 -0
  68. package/esm/shaders/connectorMark.iface.generated.d.ts +88 -0
  69. package/esm/shaders/connectorMark.iface.generated.js +1194 -0
  70. package/esm/shaders/connectorMark.js.generated.d.ts +5 -0
  71. package/esm/shaders/connectorMark.js.generated.js +47 -0
  72. package/esm/shaders/connectorMark.wgsl.generated.d.ts +1 -0
  73. package/esm/shaders/connectorMark.wgsl.generated.js +3 -0
  74. package/esm/shaders/coverageInterbase.glsl.generated.d.ts +1 -1
  75. package/esm/shaders/coverageInterbase.glsl.generated.js +1 -1
  76. package/esm/shaders/coverageInterbase.wgsl.generated.d.ts +1 -1
  77. package/esm/shaders/coverageInterbase.wgsl.generated.js +1 -1
  78. package/esm/shaders/curveDistance.js.generated.d.ts +3 -0
  79. package/esm/shaders/curveDistance.js.generated.js +104 -0
  80. package/esm/shaders/lineCenterMark.glsl.generated.d.ts +2 -2
  81. package/esm/shaders/lineCenterMark.glsl.generated.js +2 -2
  82. package/esm/shaders/lineCenterMark.wgsl.generated.d.ts +1 -1
  83. package/esm/shaders/lineCenterMark.wgsl.generated.js +1 -1
  84. package/esm/shaders/linkMark.consts.generated.d.ts +1 -0
  85. package/esm/shaders/linkMark.consts.generated.js +1 -0
  86. package/esm/shaders/linkMark.glsl.generated.d.ts +2 -2
  87. package/esm/shaders/linkMark.glsl.generated.js +2 -2
  88. package/esm/shaders/linkMark.js.generated.d.ts +2 -1
  89. package/esm/shaders/linkMark.js.generated.js +30 -1
  90. package/esm/shaders/linkMark.wgsl.generated.d.ts +1 -1
  91. package/esm/shaders/linkMark.wgsl.generated.js +1 -1
  92. package/esm/shaders/pointMark.glsl.generated.d.ts +1 -1
  93. package/esm/shaders/pointMark.glsl.generated.js +1 -1
  94. package/esm/shaders/pointMark.iface.generated.d.ts +10 -8
  95. package/esm/shaders/pointMark.iface.generated.js +18 -16
  96. package/esm/shaders/pointMark.wgsl.generated.d.ts +1 -1
  97. package/esm/shaders/pointMark.wgsl.generated.js +1 -1
  98. package/package.json +7 -4
  99. package/src/shaders/arcHull.test.ts +87 -0
  100. package/src/shaders/barMark.slang +2 -2
  101. package/src/shaders/capsule.js.generated.ts +2 -2
  102. package/src/shaders/capsule.slang +69 -24
  103. package/src/shaders/clipStrip.slang +1 -1
  104. package/src/shaders/colorPack.slang +2 -2
  105. package/src/shaders/colorRampLut.slang +7 -7
  106. package/src/shaders/connectorMark.consts.generated.ts +24 -0
  107. package/src/shaders/connectorMark.generated.ts +12 -0
  108. package/src/shaders/connectorMark.glsl.generated.ts +6 -0
  109. package/src/shaders/connectorMark.iface.generated.ts +1270 -0
  110. package/src/shaders/connectorMark.js.generated.ts +52 -0
  111. package/src/shaders/connectorMark.slang +287 -0
  112. package/src/shaders/connectorMark.wgsl.generated.ts +4 -0
  113. package/src/shaders/coverageBand.slang +10 -12
  114. package/src/shaders/coverageInterbase.glsl.generated.ts +1 -1
  115. package/src/shaders/coverageInterbase.slang +24 -21
  116. package/src/shaders/coverageInterbase.wgsl.generated.ts +1 -1
  117. package/src/shaders/coverageSnp.slang +1 -1
  118. package/src/shaders/curveDistance.js.generated.ts +100 -0
  119. package/src/shaders/curveDistance.slang +104 -1
  120. package/src/shaders/farDome.test.ts +117 -0
  121. package/src/shaders/hpClampFloor.test.ts +0 -4
  122. package/src/shaders/lineCenterMark.glsl.generated.ts +2 -2
  123. package/src/shaders/lineCenterMark.slang +14 -22
  124. package/src/shaders/lineCenterMark.wgsl.generated.ts +1 -1
  125. package/src/shaders/lineCommon.slang +10 -10
  126. package/src/shaders/lineStepMark.slang +5 -5
  127. package/src/shaders/linkMark.consts.generated.ts +2 -0
  128. package/src/shaders/linkMark.glsl.generated.ts +2 -2
  129. package/src/shaders/linkMark.js.generated.ts +27 -1
  130. package/src/shaders/linkMark.slang +58 -18
  131. package/src/shaders/linkMark.wgsl.generated.ts +1 -1
  132. package/src/shaders/markColor.slang +10 -10
  133. package/src/shaders/pointMark.glsl.generated.ts +1 -1
  134. package/src/shaders/pointMark.iface.generated.ts +19 -16
  135. package/src/shaders/pointMark.slang +12 -8
  136. package/src/shaders/pointMark.wgsl.generated.ts +1 -1
  137. package/src/shaders/rowRect.slang +3 -3
  138. package/src/shaders/rowTable.slang +7 -7
  139. package/src/shaders/scoreScale.slang +1 -1
  140. package/src/shaders/spanMark.slang +5 -5
  141. package/src/shaders/viewRegion.slang +13 -0
  142. package/esm/shaders/coverageIndicator.generated.js +0 -7
  143. package/esm/shaders/coverageIndicator.glsl.generated.d.ts +0 -2
  144. package/esm/shaders/coverageIndicator.glsl.generated.js +0 -4
  145. package/esm/shaders/coverageIndicator.iface.generated.d.ts +0 -85
  146. package/esm/shaders/coverageIndicator.iface.generated.js +0 -114
  147. package/esm/shaders/coverageIndicator.wgsl.generated.d.ts +0 -1
  148. package/esm/shaders/coverageIndicator.wgsl.generated.js +0 -3
  149. package/src/shaders/coverageIndicator.generated.ts +0 -11
  150. package/src/shaders/coverageIndicator.glsl.generated.ts +0 -6
  151. package/src/shaders/coverageIndicator.iface.generated.ts +0 -172
  152. package/src/shaders/coverageIndicator.slang +0 -61
  153. package/src/shaders/coverageIndicator.wgsl.generated.ts +0 -4
@@ -1,29 +1,32 @@
1
1
  import type { ColorChannel } from './markRamp.ts';
2
2
  import type { RowChannel, RowParams } from './rowLane.ts';
3
- import type { MarkColorScale, MarkShape } from './types.ts';
3
+ import type { MarkColorScale, MarkContext2D, MarkShape } from './types.ts';
4
4
  import type { MarkValueScale } from './valueScale.ts';
5
5
  /**
6
6
  * The `line` shape's channels: a stroke through consecutive instances of one
7
- * row, in the order given, each instance a span from `x` to `x2` at `y` on the
8
- * value scale. `gapBp` is the linear variant's break: a midpoint further than
9
- * that from the previous one on its row starts a new run. It rides with the
10
- * data, as the neighbour fields the packer derives from it do, so both
11
- * backends break in the same place; absent, a row is one run.
7
+ * row and one `series`, in the order given, each instance a span from `x` to
8
+ * `x2` at `y` on the value scale. The caller hands each series' instances
9
+ * together; a change of row or series starts a new run, and absent, a row is
10
+ * one series. `gapBp` is the linear variant's break: a midpoint further than
11
+ * that from the previous one starts a new run. Both ride with the data, as
12
+ * the neighbour fields the packer derives from them do, so both backends
13
+ * break in the same place.
12
14
  */
13
15
  export interface LineChannels extends ColorChannel, RowChannel {
14
16
  x: Uint32Array;
15
17
  x2: Uint32Array;
16
18
  y: Float32Array;
19
+ series?: Uint32Array;
17
20
  gapBp?: number;
18
21
  count: number;
19
22
  }
20
23
  export interface LineParams extends RowParams, MarkValueScale {
21
- /** The quantitative colour scale, for a line whose colour is a ramp or a threshold. */
24
+ /** The quantitative color scale, for a line whose color is a ramp or a threshold. */
22
25
  colorScale?: MarkColorScale;
23
26
  /**
24
- * Whether the colour field is the plotted `y`. Only then does a threshold's
25
- * cut sit on the y scale, so the line changes colour where it crosses one;
26
- * otherwise each instance takes its own value's colour.
27
+ * Whether the color field is the plotted `y`. Only then does a threshold's
28
+ * cut sit on the y scale, so the line changes color where it crosses one;
29
+ * otherwise each instance takes its own value's color.
27
30
  */
28
31
  colorFromY?: boolean;
29
32
  /** The value a step drops to across a gap. */
@@ -35,6 +38,37 @@ export interface LineParams extends RowParams, MarkValueScale {
35
38
  }
36
39
  /** `step` holds each value across its span; `linear` joins the spans' midpoints. */
37
40
  export type LineInterpolate = 'step' | 'linear';
41
+ /**
42
+ * What the two tracers hand a pen: the polyline's points, each with the y its
43
+ * color is read at where that differs from the y it is drawn at, and the
44
+ * instance the next points belong to.
45
+ */
46
+ export interface LinePen {
47
+ instance(i: number): void;
48
+ moveTo(x: number, y: number, colorY?: number): void;
49
+ lineTo(x: number, y: number, colorY?: number): void;
50
+ /** Reads the next segment's color from `colorY` without moving the pen. */
51
+ recolor(colorY: number): void;
52
+ endRun(): void;
53
+ end(): void;
54
+ }
55
+ export declare class BandPen implements LinePen {
56
+ private readonly ctx;
57
+ private readonly top;
58
+ private readonly bottom;
59
+ private x;
60
+ private y;
61
+ private colorY;
62
+ private drawing;
63
+ private readonly path;
64
+ constructor(ctx: MarkContext2D, top: number, bottom: number);
65
+ instance(): void;
66
+ moveTo(x: number, y: number, colorY?: number): void;
67
+ recolor(colorY: number): void;
68
+ lineTo(x: number, y: number, colorY?: number): void;
69
+ endRun(): void;
70
+ end(): void;
71
+ }
38
72
  /** The step variant: three square-capped quads an instance, flat filled. */
39
73
  export declare const lineStepMark: MarkShape<LineChannels, LineParams>;
40
74
  /** The linear variant: one round-capped capsule an instance, max blended. */
@@ -13,18 +13,19 @@ import { colorBits, isThreshold, paintColors, rampUniforms, } from './markRamp.j
13
13
  import { bandHeightPx, rowColor, rowColorOverride, rowLane, rowSlot, rowTableKeys, rowTableTextures, } from './rowLane.js';
14
14
  import { valueScaleUniforms } from './valueScale.js';
15
15
  const NO_GAP = Number.POSITIVE_INFINITY;
16
- function sameRow(row, a, b) {
17
- return row[a] === row[b];
16
+ // Whether instances `a` and `b` stand on one row and in one series.
17
+ function sameRun(c, row, a, b) {
18
+ return row[a] === row[b] && (!c.series || c.series[a] === c.series[b]);
18
19
  }
19
- // Whether instance `i` abuts the one before it on its row, so the step joins
20
+ // Whether instance `i` abuts the one before it in its run, so the step joins
20
21
  // them at a shared corner.
21
22
  function abutsBefore(c, row, i) {
22
- return i > 0 && sameRow(row, i - 1, i) && c.x2[i - 1] === c.x[i];
23
+ return i > 0 && sameRun(c, row, i - 1, i) && c.x2[i - 1] === c.x[i];
23
24
  }
24
25
  // Whether the linear variant links instance `i` to the one before it: the same
25
- // row, within `gapBp` midpoint to midpoint.
26
+ // row and series, within `gapBp` midpoint to midpoint.
26
27
  function linksBefore(c, row, i) {
27
- if (i === 0 || !sameRow(row, i - 1, i)) {
28
+ if (i === 0 || !sameRun(c, row, i - 1, i)) {
28
29
  return false;
29
30
  }
30
31
  const gapBp = c.gapBp ?? NO_GAP;
@@ -98,16 +99,16 @@ function valueYPx(g, value, slot) {
98
99
  return (rowBandTopPx(g.rowOffsetPx, g.pitch, slot) +
99
100
  valueToYPxScaled(value, g.domainMin, g.domainMax, g.band, g.scaleType, g.symlogConstant));
100
101
  }
101
- // A value's y in the band for the colour test, placed UNCLAMPED where
102
+ // A value's y in the band for the color test, placed UNCLAMPED where
102
103
  // `valueYPx` clamps it to draw: the cuts go through it, and so does the line a
103
- // threshold colours along, as the shader's `lineColorYPx` does.
104
+ // threshold colors along, as the shader's `lineColorYPx` does.
104
105
  function colorYPx(g, value, slot) {
105
106
  const norm = normalizeScoreUnclamped(value, g.domainMin, g.domainMax, g.scaleType, g.symlogConstant);
106
107
  return rowBandTopPx(g.rowOffsetPx, g.pitch, slot) + (1 - norm) * g.band;
107
108
  }
108
- // One colour per instance: a change ends the stroke batch and reopens it from
109
- // the pen, so a segment carries the colour of the instance it belongs to, as
110
- // the shader's per-instance colour does.
109
+ // One color per instance: a change ends the stroke batch and reopens it from
110
+ // the pen, so a segment carries the color of the instance it belongs to, as
111
+ // the shader's per-instance color does.
111
112
  class InstancePen {
112
113
  ctx;
113
114
  colorOf;
@@ -154,7 +155,7 @@ class InstancePen {
154
155
  }
155
156
  // The part of the segment from y0 to y1, as a fraction of it, inside the band
156
157
  // top < y <= bottom; undefined where it misses. A band owns its lower edge,
157
- // so a line lying on a cut takes the colour above it, as the shader does.
158
+ // so a line lying on a cut takes the color above it, as the shader does.
158
159
  function bandSpan(y0, y1, top, bottom) {
159
160
  if (y0 === y1) {
160
161
  return top < y0 && y0 <= bottom ? [0, 1] : undefined;
@@ -168,10 +169,10 @@ function bandSpan(y0, y1, top, bottom) {
168
169
  : undefined;
169
170
  }
170
171
  // Keeps the part of each segment inside one threshold band, cut where it
171
- // crosses the band's edges, so a line stroked once per band changes colour
172
- // where the shader's fragments do. A point's colour y is where the band test
172
+ // crosses the band's edges, so a line stroked once per band changes color
173
+ // where the shader's fragments do. A point's color y is where the band test
173
174
  // reads it and its y where it draws, which part where the drawn y is clamped.
174
- class BandPen {
175
+ export class BandPen {
175
176
  ctx;
176
177
  top;
177
178
  bottom;
@@ -227,8 +228,8 @@ class BandPen {
227
228
  }
228
229
  // The step polyline over `indices`, in order: a rise from the origin, the top
229
230
  // across the span, the step at each joint and a drop to the origin at a gap.
230
- // A rise is coloured along the y it is drawn at, a level run by its value's
231
- // colour y, as the shader reads them.
231
+ // A rise is colored along the y it is drawn at, a level run by its value's
232
+ // color y, as the shader reads them.
232
233
  function traceStep(pen, c, g, rows, indices, origin) {
233
234
  const { x, x2, y, row, count } = c;
234
235
  let inRun = false;
@@ -285,7 +286,7 @@ function traceCenter(pen, c, g, rows, indices) {
285
286
  }
286
287
  // Along y, the line is stroked once per band per row, each pass keeping the
287
288
  // parts of the polyline inside its band; otherwise once, in each instance's
288
- // own colour. A row the table recolours takes its override either way.
289
+ // own color. A row the table recolors takes its override either way.
289
290
  function paintLine(ctx, c, g, params, trace) {
290
291
  const { row, count } = c;
291
292
  const scale = params.colorScale;
@@ -315,7 +316,7 @@ function paintLine(ctx, c, g, params, trace) {
315
316
  if (scale && isThreshold(scale)) {
316
317
  const { cuts, colors } = scale;
317
318
  for (const [slot, indices] of bySlot) {
318
- const cutYs = cuts.map(cut => colorYPx(g, cut, slot));
319
+ const cutYs = cuts.map(cut => colorYPx(g, Math.fround(cut), slot));
319
320
  for (let k = 0; k <= cuts.length; k++) {
320
321
  ctx.strokeStyle = abgrToCssRgba(colors[k] ?? 0);
321
322
  const pen = new BandPen(ctx, k < cuts.length ? cutYs[k] : Number.NEGATIVE_INFINITY, k > 0 ? cutYs[k - 1] : Number.POSITIVE_INFINITY);
@@ -25,10 +25,9 @@ export interface LinkChannels extends ColorChannel, RowChannel {
25
25
  feet?: Uint8Array;
26
26
  count: number;
27
27
  }
28
- export declare const LINK_FOOT_FORWARD = 1;
29
- export declare const LINK_FOOT_REVERSE = 2;
30
- /** The `feet` lane's value for a tick at each foot, by genomic direction. */
31
- export declare function linkFeet(xDir: number, x2Dir: number): number;
28
+ export { LINK_FOOT_FORWARD, LINK_FOOT_REVERSE, linkFeet } from '../linkFeet.ts';
29
+ /** The channels cut down to instance `i`, for its own painter to trace alone. */
30
+ export declare function linkInstance(c: LinkChannels, i: number): LinkChannels;
32
31
  /**
33
32
  * One displayed region as a foot places through it: the canvas px of an
34
33
  * anchor bp chosen near the view, so a foot's offset from it stays inside
@@ -50,7 +49,7 @@ export interface LinkSizeScale {
50
49
  }
51
50
  export type LinkShape = 'dome' | 'arc' | 'line';
52
51
  export interface LinkParams extends RowParams, MarkValueScale {
53
- /** The quantitative colour scale, for a link whose colour is a ramp or a threshold. */
52
+ /** The quantitative color scale, for a link whose color is a ramp or a threshold. */
54
53
  colorScale?: MarkColorScale;
55
54
  /** The view's displayed regions, indexed as `x2Region` and the block's own index are. */
56
55
  regions: readonly LinkRegion[];
@@ -73,6 +72,7 @@ export interface LinkParams extends RowParams, MarkValueScale {
73
72
  /** How far inside the band the value range ends, the axis's own. */
74
73
  insetPx?: number;
75
74
  }
75
+ export declare function regionPx(regions: readonly LinkRegion[], index: number, bp: number): number;
76
76
  /**
77
77
  * Where an instance's curve peaks as the painter and the shader draw it: the
78
78
  * centreline's apex in canvas px and how far it stands off its baseline.
@@ -92,5 +92,22 @@ export interface LinkApex {
92
92
  * at all, or a far pair's circle, which the band clips to its legs.
93
93
  */
94
94
  export declare function linkApex(c: LinkChannels, block: RenderBlock, frame: MarkFrame, params: LinkParams, i: number): LinkApex | undefined;
95
+ /**
96
+ * Where a label of instance `i` stands: its apex, or for a far dome whose
97
+ * apex is off the canvas, the dome's point over the middle of the stretch the
98
+ * canvas shows, so a view inside the pair still carries the label. `rise` is
99
+ * the curve's height there.
100
+ */
101
+ export declare function linkLabelAnchor(c: LinkChannels, block: RenderBlock, frame: MarkFrame, params: LinkParams, i: number): LinkApex | undefined;
102
+ /**
103
+ * The view's region table as a view-spanning shader's uniforms take it: the
104
+ * entries in use, their count, and the drawing region's own entry. Shared
105
+ * arrays, rewritten per call, so a frame allocates nothing per block.
106
+ */
107
+ export declare function viewRegionUniforms(regions: readonly LinkRegion[], own: number): {
108
+ regionCount: number;
109
+ ownEntry: [number, number, number, number];
110
+ regionTable: [[number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number]];
111
+ };
95
112
  export declare const linkMark: MarkShape<LinkChannels, LinkParams>;
96
113
  export { LINK_ELSEWHERE, LINK_NO_REGION };
@@ -1,22 +1,30 @@
1
1
  import { getDpr } from '../canvas2dUtils.js';
2
2
  import { SCALE_TYPE_LOG } from '../scoreScale.js';
3
- import { distToWideCirclePx, legSweepAngle, } from '../shaders/curveDistance.js.generated.js';
3
+ import { distToWideCirclePx, farDomeParam, legSweepAngle, } from '../shaders/curveDistance.js.generated.js';
4
4
  import { LINK_CURVE_SEGMENTS, LINK_ELSEWHERE, LINK_FOOT_PX, LINK_MAX_REGIONS, LINK_NO_REGION, LINK_NO_SIZE, LINK_LINE_MIN_PX, LINK_SHAPE_ARC, LINK_SHAPE_DOME, LINK_SHAPE_LINE, LINK_SIZE_CONSTANT, LINK_SIZE_SCALED, LINK_STEM_PX, } from '../shaders/linkMark.consts.generated.js';
5
5
  import * as shader from '../shaders/linkMark.generated.js';
6
- import { linkApexPx, linkBaseYPx, linkFootDir, linkFootLenPx, linkIsFar, linkRadiiPx, linkStrokeWidthPx, linkValuePx, } from '../shaders/linkMark.js.generated.js';
6
+ import { linkApexHeld, linkApexPx, linkBaseYPx, linkFootDir, linkFootLenPx, linkIsFar, linkRadiiPx, linkStrokeWidthPx, linkValuePx, } from '../shaders/linkMark.js.generated.js';
7
7
  import { slangPass } from '../slangPass.js';
8
8
  import { abgrToCssRgba } from './colorFill.js';
9
9
  import { ellipseNearest } from './ellipseDistance.js';
10
10
  import { nearestInk } from './markHit.js';
11
- import { colorBits, paintColors, rampUniforms } from './markRamp.js';
11
+ import { colorBits, instanceColor, paintColors, rampUniforms, } from './markRamp.js';
12
12
  import { bandHeightPx, rowColor, rowLane, rowSlot, rowTableKeys, rowTableTextures, } from './rowLane.js';
13
13
  import { valueScaleUniforms } from './valueScale.js';
14
- export const LINK_FOOT_FORWARD = 1;
15
- export const LINK_FOOT_REVERSE = 2;
16
- /** The `feet` lane's value for a tick at each foot, by genomic direction. */
17
- export function linkFeet(xDir, x2Dir) {
18
- const bits = (d) => d > 0 ? LINK_FOOT_FORWARD : d < 0 ? LINK_FOOT_REVERSE : 0;
19
- return bits(xDir) | (bits(x2Dir) << 2);
14
+ export { LINK_FOOT_FORWARD, LINK_FOOT_REVERSE, linkFeet } from '../linkFeet.js';
15
+ /** The channels cut down to instance `i`, for its own painter to trace alone. */
16
+ export function linkInstance(c, i) {
17
+ return {
18
+ x: c.x.subarray(i, i + 1),
19
+ x2: c.x2.subarray(i, i + 1),
20
+ x2Region: c.x2Region.subarray(i, i + 1),
21
+ y: c.y?.subarray(i, i + 1),
22
+ size: c.size?.subarray(i, i + 1),
23
+ feet: c.feet?.subarray(i, i + 1),
24
+ row: c.row?.subarray(i, i + 1),
25
+ color: instanceColor(c.color, i),
26
+ count: 1,
27
+ };
20
28
  }
21
29
  const KIND_ELLIPSE = 0;
22
30
  const KIND_CIRCLE = 1;
@@ -26,6 +34,7 @@ const KIND_LINE = 4;
26
34
  // The painter's leg polyline: enough segments that the chord sagitta on any
27
35
  // leg the band can show is under a pixel.
28
36
  const LEG_SEGMENTS = 32;
37
+ const FAR_DOME_COARSE = 16;
29
38
  function linkFrame(block, frame, params) {
30
39
  const band = bandHeightPx(params, frame.canvasHeight);
31
40
  const { valueScaleType, valueSymlogConstant } = valueScaleUniforms(params);
@@ -88,7 +97,7 @@ function linkShapeCode(shape) {
88
97
  ? LINK_SHAPE_LINE
89
98
  : LINK_SHAPE_DOME;
90
99
  }
91
- function regionPx(regions, index, bp) {
100
+ export function regionPx(regions, index, bp) {
92
101
  const r = regions[index];
93
102
  return r.anchorPx + (bp - r.anchorBp) * r.signedPxPerBp;
94
103
  }
@@ -171,10 +180,12 @@ function placeCurve(c, g, i) {
171
180
  g.ry = apex;
172
181
  return;
173
182
  }
174
- const [rx, ry] = linkRadiiPx(pairHalf, apex, g.screenW);
183
+ const [rx, ry] = linkRadiiPx(pairHalf, apex, g.screenW, linkApexHeld(g.valued, c.y ? c.y[i] : 0, g.domainMax));
175
184
  g.rx = rx;
176
185
  g.ry = ry;
177
- if (linkIsFar(pairHalf, g.screenW)) {
186
+ // A far pair with a low apex keeps its apex (`linkRadiiPx`) and is an
187
+ // ellipse still, traced as chords (`farDomePoints`).
188
+ if (linkIsFar(pairHalf, g.screenW) && ry >= rx) {
178
189
  g.kind = KIND_CIRCLE;
179
190
  g.legSweep = legSweepAngle(rx, g.reach + g.strokePx / 2);
180
191
  g.legHeight = rx * Math.sin(g.legSweep);
@@ -183,13 +194,7 @@ function placeCurve(c, g, i) {
183
194
  g.kind = KIND_ELLIPSE;
184
195
  }
185
196
  }
186
- /**
187
- * Instance `i`'s apex, undefined where it draws none on the band: no curve
188
- * at all, or a far pair's circle, which the band clips to its legs.
189
- */
190
- export function linkApex(c, block, frame, params, i) {
191
- const g = linkFrame(block, frame, params);
192
- placeCurve(c, g, i);
197
+ function apexOf(g) {
193
198
  if (g.kind === KIND_NONE || g.kind === KIND_CIRCLE) {
194
199
  return undefined;
195
200
  }
@@ -202,6 +207,37 @@ export function linkApex(c, block, frame, params, i) {
202
207
  inward: g.reverse ? -1 : 1,
203
208
  };
204
209
  }
210
+ /**
211
+ * Instance `i`'s apex, undefined where it draws none on the band: no curve
212
+ * at all, or a far pair's circle, which the band clips to its legs.
213
+ */
214
+ export function linkApex(c, block, frame, params, i) {
215
+ const g = linkFrame(block, frame, params);
216
+ placeCurve(c, g, i);
217
+ return apexOf(g);
218
+ }
219
+ /**
220
+ * Where a label of instance `i` stands: its apex, or for a far dome whose
221
+ * apex is off the canvas, the dome's point over the middle of the stretch the
222
+ * canvas shows, so a view inside the pair still carries the label. `rise` is
223
+ * the curve's height there.
224
+ */
225
+ export function linkLabelAnchor(c, block, frame, params, i) {
226
+ const g = linkFrame(block, frame, params);
227
+ placeCurve(c, g, i);
228
+ const apex = apexOf(g);
229
+ if (!apex || g.kind !== KIND_ELLIPSE || !linkIsFar(g.rx, g.screenW)) {
230
+ return apex;
231
+ }
232
+ const from = Math.max(apex.x - g.rx, 0);
233
+ const to = Math.min(apex.x + g.rx, g.screenW);
234
+ if (to <= from || (apex.x >= 0 && apex.x <= g.screenW)) {
235
+ return apex;
236
+ }
237
+ const x = (from + to) / 2;
238
+ const rise = g.ry * Math.sqrt(Math.max(1 - ((x - apex.x) / g.rx) ** 2, 0));
239
+ return { ...apex, x, y: yAt(g, rise), rise };
240
+ }
205
241
  function sizeLane(c) {
206
242
  const lane = new Float32Array(c.count);
207
243
  for (let i = 0; i < c.count; i++) {
@@ -243,6 +279,18 @@ function fillTable(regions) {
243
279
  }
244
280
  return n;
245
281
  }
282
+ /**
283
+ * The view's region table as a view-spanning shader's uniforms take it: the
284
+ * entries in use, their count, and the drawing region's own entry. Shared
285
+ * arrays, rewritten per call, so a frame allocates nothing per block.
286
+ */
287
+ export function viewRegionUniforms(regions, own) {
288
+ return {
289
+ regionCount: fillTable(regions),
290
+ ownEntry: tableEntry(regions[own]),
291
+ regionTable: TABLE,
292
+ };
293
+ }
246
294
  // Canvas y of a point `up` px from the baseline on the drawn side.
247
295
  function yAt(g, up) {
248
296
  return g.baseY - g.ySign * up;
@@ -265,6 +313,34 @@ function legPoints(g, footX, legDir) {
265
313
  }
266
314
  return points;
267
315
  }
316
+ // A far dome as chords from the foot nearer the canvas, stepped as the shader
317
+ // steps it: fine over the stretch the canvas shows, coarse over the rest. A
318
+ // canvas flattens an ellipse tens of thousands of px wide into too few curves
319
+ // to hold its feet.
320
+ function farDomePoints(g, left, right) {
321
+ const fromRight = g.screenW / 2 - left > g.rx;
322
+ const footX = fromRight ? right : left;
323
+ const dir = fromRight ? -1 : 1;
324
+ const margin = g.strokePx;
325
+ const u0 = farDomeParam(fromRight ? footX - g.screenW - margin : -margin - footX, g.rx);
326
+ const u1 = Math.max(u0, farDomeParam(fromRight ? footX + margin : g.screenW + margin - footX, g.rx));
327
+ const coarse = Array.from({ length: FAR_DOME_COARSE + 1 }, (_, k) => Math.sin(((k / FAR_DOME_COARSE) * Math.PI) / 2));
328
+ const fine = Array.from({ length: LINK_CURVE_SEGMENTS + 1 }, (_, k) => u0 + ((u1 - u0) * k) / LINK_CURVE_SEGMENTS);
329
+ return [
330
+ ...coarse.filter(u => u < u0),
331
+ ...fine,
332
+ ...coarse.filter(u => u > u1),
333
+ ].map(u => ({
334
+ x: footX + dir * 2 * g.rx * u * u,
335
+ y: yAt(g, 2 * g.ry * u * Math.sqrt(1 - u * u)),
336
+ }));
337
+ }
338
+ function traceChords(ctx, points) {
339
+ ctx.moveTo(points[0].x, points[0].y);
340
+ for (let k = 1; k < points.length; k++) {
341
+ ctx.lineTo(points[k].x, points[k].y);
342
+ }
343
+ }
268
344
  function tracePath(ctx, g) {
269
345
  const { baseY } = g;
270
346
  if (g.kind === KIND_STEM) {
@@ -286,14 +362,14 @@ function tracePath(ctx, g) {
286
362
  [right, 1],
287
363
  [left, -1],
288
364
  ]) {
289
- const points = legPoints(g, footX, legDir);
290
- ctx.moveTo(points[0].x, points[0].y);
291
- for (let k = 1; k < points.length; k++) {
292
- ctx.lineTo(points[k].x, points[k].y);
293
- }
365
+ traceChords(ctx, legPoints(g, footX, legDir));
294
366
  }
295
367
  return;
296
368
  }
369
+ if (linkIsFar(g.rx, g.screenW)) {
370
+ traceChords(ctx, farDomePoints(g, left, right));
371
+ return;
372
+ }
297
373
  const mid = (left + right) / 2;
298
374
  if (g.reverse) {
299
375
  ctx.moveTo(mid + g.rx, baseY);
@@ -458,7 +534,9 @@ function nearestRising(g, px, py) {
458
534
  return { x, y: baseY, dist: Math.hypot(px - x, py - baseY) };
459
535
  }
460
536
  const near = ellipseNearest(px - mid, py - baseY, g.rx, g.ry);
461
- return { x: mid + near.x, y: baseY + near.y, dist: near.dist };
537
+ // The drawn half rises from the baseline. A cursor on the baseline is as
538
+ // near the mirrored half, and the solve may name that one.
539
+ return { x: mid + near.x, y: baseY - Math.abs(near.y), dist: near.dist };
462
540
  }
463
541
  export const linkMark = {
464
542
  id: 'link',
@@ -110,7 +110,7 @@ export declare function createMarkBackend<TRegion, TState extends FrameDimension
110
110
  /**
111
111
  * What the frame is cleared to, when transparent is the wrong answer. The
112
112
  * GPU side hands it to `beginFrame` and the Canvas2D side fills it after
113
- * `prepareCanvas`, so both backends composite over the same colour — which
113
+ * `prepareCanvas`, so both backends composite over the same color — which
114
114
  * synteny needs, its indel wedges being pre-blended against the band's
115
115
  * ground rather than composited over it.
116
116
  */
@@ -63,7 +63,7 @@ export function drawPlannedPasses(hal, plan, regionKey) {
63
63
  // its sampler unconditionally, and a textured pass missing a texture draws
64
64
  // nothing on either GPU HAL. A mark declaring no `texture` at all is the same case —
65
65
  // `pointMark` carries a sampler for the shapes that ramp, and a point of a
66
- // constant colour draws it without one.
66
+ // constant color draws it without one.
67
67
  const INERT_RAMP = new Uint8Array(COLOR_RAMP_LUT_ENTRIES * 4);
68
68
  /**
69
69
  * The texture each textured pass holds, by identity — the mirror of the one
@@ -1,9 +1,9 @@
1
1
  import type { MarkColorScale, MarkThreshold } from './types.ts';
2
2
  /**
3
- * A colour channel as a shape reads it: `color`, the packed ABGR the worker
3
+ * A color channel as a shape reads it: `color`, the packed ABGR the worker
4
4
  * resolved — one per instance, or one number for all of them — or
5
5
  * `colorValue`, the raw values a frame's {@link MarkColorScale} turns into
6
- * colours here. A shape reading a scale declares both optional and asks
6
+ * colors here. A shape reading a scale declares both optional and asks
7
7
  * {@link colorBits} for the GPU and {@link paintColors} for Canvas2D.
8
8
  */
9
9
  export interface ColorChannel {
@@ -19,13 +19,13 @@ interface RampBake {
19
19
  scale: MarkColorScale;
20
20
  colors: Uint32Array;
21
21
  }
22
- /** Whether a colour scale is a threshold rather than a ramp. */
22
+ /** Whether a color scale is a threshold rather than a ramp. */
23
23
  export declare function isThreshold(scale: MarkColorScale): scale is MarkThreshold;
24
24
  type Rgba = [number, number, number, number];
25
25
  type Bands = [Rgba, Rgba, Rgba, Rgba, Rgba, Rgba, Rgba, Rgba, Rgba];
26
26
  /**
27
- * `markColor.slang`'s uniforms for a frame's colour scale, or for none: the
28
- * mode, a ramp's domain and middle, and a threshold's cuts and band colours.
27
+ * `markColor.slang`'s uniforms for a frame's color scale, or for none: the
28
+ * mode, a ramp's domain and middle, and a threshold's cuts and band colors.
29
29
  * Every valued shape's uniform block carries the set.
30
30
  */
31
31
  export declare function rampUniforms(scale: MarkColorScale | undefined): {
@@ -40,8 +40,8 @@ export declare function rampUniforms(scale: MarkColorScale | undefined): {
40
40
  /**
41
41
  * The 4-byte instance lane, for the packer. Under a scale it is the value's
42
42
  * float32 bits viewed as the `uint` the attribute declares — the reinterpret
43
- * `markColor.slang`'s `asfloat` undoes, and the reason a quantitative colour
44
- * adds no lane. A constant colour fills a lane for the pack alone, which the
43
+ * `markColor.slang`'s `asfloat` undoes, and the reason a quantitative color
44
+ * adds no lane. A constant color fills a lane for the pack alone, which the
45
45
  * payload never holds.
46
46
  */
47
47
  export declare function colorBits(c: ColorChannel, count: number): ArrayLike<number>;
@@ -63,12 +63,12 @@ export declare function keepRampValues(values: Float32Array, keep: (value: numbe
63
63
  */
64
64
  export declare function thresholdBandOf(value: number, cuts: readonly number[]): number;
65
65
  /**
66
- * The packed colours a painter fills with. Under a scale the values are baked
66
+ * The packed colors a painter fills with. Under a scale the values are baked
67
67
  * against it once and kept on the payload, so a repaint at an unchanged scale
68
68
  * — every pan and every hover — walks no values and the painters' fill
69
- * batching still sees runs of one colour. The bake reruns when the domain, the
70
- * scale type, the ramp's bytes or a threshold's cuts or colours move. A
71
- * constant colour expands once and is kept the same way.
69
+ * batching still sees runs of one color. The bake reruns when the domain, the
70
+ * scale type, the ramp's bytes or a threshold's cuts or colors move. A
71
+ * constant color expands once and is kept the same way.
72
72
  */
73
73
  export declare function paintColors(c: ColorChannel, count: number, scale: MarkColorScale | undefined): ArrayLike<number>;
74
74
  export {};
@@ -3,7 +3,7 @@ import { rampMidT } from '../shaders/colorRampLut.js.generated.js';
3
3
  import { MAX_COLOR_BANDS, MAX_COLOR_CUTS, RAMP_LINEAR, RAMP_LOG, RAMP_NONE, RAMP_NOT_A_NUMBER_COLOR, RAMP_NO_VALUE_BITS, RAMP_NO_VALUE_COLOR, RAMP_THRESHOLD, } from '../shaders/markColor.generated.js';
4
4
  import { normalizeScore } from '../shaders/scoreScale.js.generated.js';
5
5
  const NO_COLORS = new Uint32Array(0);
6
- /** Whether a colour scale is a threshold rather than a ramp. */
6
+ /** Whether a color scale is a threshold rather than a ramp. */
7
7
  export function isThreshold(scale) {
8
8
  return 'cuts' in scale;
9
9
  }
@@ -31,8 +31,8 @@ function quad(values, from) {
31
31
  ];
32
32
  }
33
33
  /**
34
- * `markColor.slang`'s uniforms for a frame's colour scale, or for none: the
35
- * mode, a ramp's domain and middle, and a threshold's cuts and band colours.
34
+ * `markColor.slang`'s uniforms for a frame's color scale, or for none: the
35
+ * mode, a ramp's domain and middle, and a threshold's cuts and band colors.
36
36
  * Every valued shape's uniform block carries the set.
37
37
  */
38
38
  export function rampUniforms(scale) {
@@ -69,8 +69,8 @@ export function rampUniforms(scale) {
69
69
  /**
70
70
  * The 4-byte instance lane, for the packer. Under a scale it is the value's
71
71
  * float32 bits viewed as the `uint` the attribute declares — the reinterpret
72
- * `markColor.slang`'s `asfloat` undoes, and the reason a quantitative colour
73
- * adds no lane. A constant colour fills a lane for the pack alone, which the
72
+ * `markColor.slang`'s `asfloat` undoes, and the reason a quantitative color
73
+ * adds no lane. A constant color fills a lane for the pack alone, which the
74
74
  * payload never holds.
75
75
  */
76
76
  export function colorBits(c, count) {
@@ -129,12 +129,12 @@ function sameScale(a, b) {
129
129
  midNormOf(a) === midNormOf(b));
130
130
  }
131
131
  /**
132
- * The packed colours a painter fills with. Under a scale the values are baked
132
+ * The packed colors a painter fills with. Under a scale the values are baked
133
133
  * against it once and kept on the payload, so a repaint at an unchanged scale
134
134
  * — every pan and every hover — walks no values and the painters' fill
135
- * batching still sees runs of one colour. The bake reruns when the domain, the
136
- * scale type, the ramp's bytes or a threshold's cuts or colours move. A
137
- * constant colour expands once and is kept the same way.
135
+ * batching still sees runs of one color. The bake reruns when the domain, the
136
+ * scale type, the ramp's bytes or a threshold's cuts or colors move. A
137
+ * constant color expands once and is kept the same way.
138
138
  */
139
139
  export function paintColors(c, count, scale) {
140
140
  const { colorValue, color } = c;
@@ -6,18 +6,21 @@ import type { MarkFrame, MarkColorScale, MarkShape } from './types.ts';
6
6
  import type { MarkValueScale } from './valueScale.ts';
7
7
  /**
8
8
  * The `point` shape's channels: a glyph per instance at the centre of
9
- * `x`..`x2`, on the `y` scale.
9
+ * `x`..`x2`, on the `y` scale, at 0 where `y` is absent. `glyph` is one per
10
+ * instance or one for all of them, as `color` is.
10
11
  */
11
12
  export interface PointChannels extends ColorChannel, RowChannel {
12
13
  x: Uint32Array;
13
14
  x2: Uint32Array;
14
- y: Float32Array;
15
- glyph: Uint8Array;
15
+ y?: Float32Array;
16
+ glyph: Uint8Array | number;
16
17
  count: number;
17
18
  }
18
- /** What the point and rule shapes share: a value scale, rows and a colour scale. */
19
+ /** The channels cut down to instance `i`, for its own painter to trace alone. */
20
+ export declare function pointInstance(c: PointChannels, i: number): PointChannels;
21
+ /** What the point and rule shapes share: a value scale, rows and a color scale. */
19
22
  export interface ValuedMarkParams extends RowParams, MarkValueScale {
20
- /** The quantitative colour scale, for a mark whose colour is a ramp or a threshold. */
23
+ /** The quantitative color scale, for a mark whose color is a ramp or a threshold. */
21
24
  colorScale?: MarkColorScale;
22
25
  /**
23
26
  * How far inside the plot the value range ends, so a mark at a domain
@@ -29,6 +32,8 @@ export interface ValuedMarkParams extends RowParams, MarkValueScale {
29
32
  export interface PointParams extends ValuedMarkParams {
30
33
  /** Glyph diameter in CSS px. */
31
34
  diameterPx: number;
35
+ /** The triangle's height in CSS px, where it is not `diameterPx`. */
36
+ triangleHeightPx?: number;
32
37
  }
33
38
  /**
34
39
  * The point shader's uniforms, which the rule shape draws through too:
@@ -36,6 +41,7 @@ export interface PointParams extends ValuedMarkParams {
36
41
  */
37
42
  export declare function writePointUniforms(scratch: ArrayBuffer, clip: BlockClipResult, block: RenderBlock, frame: MarkFrame, params: ValuedMarkParams, shape: {
38
43
  radiusPx: number;
44
+ triangleRadiusYPx: number;
39
45
  rule: boolean;
40
46
  minWidthPx: number;
41
47
  }): void;