@jbrowse/render-core 5.0.0-beta.11 → 5.0.0-beta.14

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 (190) 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 +0 -1
  14. package/esm/coverageBand.js +0 -5
  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 +18 -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/barMark.js +4 -2
  29. package/esm/marks/colorFill.d.ts +4 -4
  30. package/esm/marks/colorFill.js +4 -4
  31. package/esm/marks/connectorMark.d.ts +48 -0
  32. package/esm/marks/connectorMark.js +285 -0
  33. package/esm/marks/drawAgainstHit.d.ts +1 -1
  34. package/esm/marks/drawAgainstHit.js +4 -4
  35. package/esm/marks/glyphPaint.d.ts +5 -4
  36. package/esm/marks/glyphPaint.js +17 -7
  37. package/esm/marks/index.d.ts +8 -5
  38. package/esm/marks/index.js +6 -4
  39. package/esm/marks/lineMark.d.ts +47 -7
  40. package/esm/marks/lineMark.js +53 -39
  41. package/esm/marks/linkMark.d.ts +43 -6
  42. package/esm/marks/linkMark.js +118 -21
  43. package/esm/marks/markBackend.d.ts +1 -1
  44. package/esm/marks/markBackend.js +1 -1
  45. package/esm/marks/markPlan.d.ts +4 -3
  46. package/esm/marks/markPlan.js +5 -4
  47. package/esm/marks/markRamp.d.ts +11 -11
  48. package/esm/marks/markRamp.js +9 -9
  49. package/esm/marks/pointMark.d.ts +11 -5
  50. package/esm/marks/pointMark.js +34 -9
  51. package/esm/marks/rowLane.d.ts +4 -2
  52. package/esm/marks/rowLane.js +7 -3
  53. package/esm/marks/rowTable.d.ts +5 -5
  54. package/esm/marks/rowTable.js +4 -4
  55. package/esm/marks/ruleMark.js +1 -0
  56. package/esm/marks/spanMark.d.ts +8 -7
  57. package/esm/marks/spanMark.js +3 -3
  58. package/esm/marks/types.d.ts +8 -5
  59. package/esm/marks/types.js +8 -1
  60. package/esm/perRegionRenderingBackend.d.ts +1 -1
  61. package/esm/perRegionRenderingBackend.js +1 -1
  62. package/esm/regionDataMap.d.ts +2 -2
  63. package/esm/regionDataMap.js +2 -2
  64. package/esm/shaders/barMark.glsl.generated.d.ts +1 -1
  65. package/esm/shaders/barMark.glsl.generated.js +1 -1
  66. package/esm/shaders/barMark.wgsl.generated.d.ts +1 -1
  67. package/esm/shaders/barMark.wgsl.generated.js +1 -1
  68. package/esm/shaders/capsule.js.generated.d.ts +1 -1
  69. package/esm/shaders/capsule.js.generated.js +2 -2
  70. package/esm/shaders/connectorMark.consts.generated.d.ts +11 -0
  71. package/esm/shaders/connectorMark.consts.generated.js +13 -0
  72. package/esm/shaders/{coverageIndicator.generated.d.ts → connectorMark.generated.d.ts} +2 -1
  73. package/esm/shaders/connectorMark.generated.js +8 -0
  74. package/esm/shaders/connectorMark.glsl.generated.d.ts +2 -0
  75. package/esm/shaders/connectorMark.glsl.generated.js +4 -0
  76. package/esm/shaders/connectorMark.iface.generated.d.ts +88 -0
  77. package/esm/shaders/connectorMark.iface.generated.js +1194 -0
  78. package/esm/shaders/connectorMark.js.generated.d.ts +5 -0
  79. package/esm/shaders/connectorMark.js.generated.js +47 -0
  80. package/esm/shaders/connectorMark.wgsl.generated.d.ts +1 -0
  81. package/esm/shaders/connectorMark.wgsl.generated.js +3 -0
  82. package/esm/shaders/coverageInterbase.glsl.generated.d.ts +1 -1
  83. package/esm/shaders/coverageInterbase.glsl.generated.js +1 -1
  84. package/esm/shaders/coverageInterbase.wgsl.generated.d.ts +1 -1
  85. package/esm/shaders/coverageInterbase.wgsl.generated.js +1 -1
  86. package/esm/shaders/curveDistance.js.generated.d.ts +4 -0
  87. package/esm/shaders/curveDistance.js.generated.js +113 -0
  88. package/esm/shaders/insertionGlyph.generated.d.ts +6 -0
  89. package/esm/shaders/insertionGlyph.generated.js +8 -0
  90. package/esm/shaders/lineCenterMark.glsl.generated.d.ts +2 -2
  91. package/esm/shaders/lineCenterMark.glsl.generated.js +2 -2
  92. package/esm/shaders/lineCenterMark.iface.generated.d.ts +11 -9
  93. package/esm/shaders/lineCenterMark.iface.generated.js +20 -18
  94. package/esm/shaders/lineCenterMark.wgsl.generated.d.ts +1 -1
  95. package/esm/shaders/lineCenterMark.wgsl.generated.js +1 -1
  96. package/esm/shaders/lineCommon.js.generated.d.ts +1 -0
  97. package/esm/shaders/lineCommon.js.generated.js +20 -0
  98. package/esm/shaders/lineStepMark.glsl.generated.d.ts +2 -2
  99. package/esm/shaders/lineStepMark.glsl.generated.js +2 -2
  100. package/esm/shaders/lineStepMark.iface.generated.d.ts +11 -9
  101. package/esm/shaders/lineStepMark.iface.generated.js +20 -18
  102. package/esm/shaders/lineStepMark.wgsl.generated.d.ts +1 -1
  103. package/esm/shaders/lineStepMark.wgsl.generated.js +1 -1
  104. package/esm/shaders/linkMark.consts.generated.d.ts +1 -0
  105. package/esm/shaders/linkMark.consts.generated.js +1 -0
  106. package/esm/shaders/linkMark.glsl.generated.d.ts +2 -2
  107. package/esm/shaders/linkMark.glsl.generated.js +2 -2
  108. package/esm/shaders/linkMark.js.generated.d.ts +4 -2
  109. package/esm/shaders/linkMark.js.generated.js +33 -4
  110. package/esm/shaders/linkMark.wgsl.generated.d.ts +1 -1
  111. package/esm/shaders/linkMark.wgsl.generated.js +1 -1
  112. package/esm/shaders/pointMark.glsl.generated.d.ts +1 -1
  113. package/esm/shaders/pointMark.glsl.generated.js +1 -1
  114. package/esm/shaders/pointMark.iface.generated.d.ts +10 -8
  115. package/esm/shaders/pointMark.iface.generated.js +18 -16
  116. package/esm/shaders/pointMark.wgsl.generated.d.ts +1 -1
  117. package/esm/shaders/pointMark.wgsl.generated.js +1 -1
  118. package/esm/shaders/spanMark.glsl.generated.d.ts +1 -1
  119. package/esm/shaders/spanMark.glsl.generated.js +1 -1
  120. package/esm/shaders/spanMark.wgsl.generated.d.ts +1 -1
  121. package/esm/shaders/spanMark.wgsl.generated.js +1 -1
  122. package/esm/slangPass.d.ts +5 -3
  123. package/esm/slangPass.js +2 -2
  124. package/package.json +7 -4
  125. package/src/shaders/arcHull.test.ts +87 -0
  126. package/src/shaders/barMark.glsl.generated.ts +1 -1
  127. package/src/shaders/barMark.slang +2 -2
  128. package/src/shaders/barMark.wgsl.generated.ts +1 -1
  129. package/src/shaders/capsule.js.generated.ts +2 -2
  130. package/src/shaders/capsule.slang +69 -24
  131. package/src/shaders/clipStrip.slang +1 -1
  132. package/src/shaders/colorPack.slang +2 -2
  133. package/src/shaders/colorRampLut.slang +7 -7
  134. package/src/shaders/connectorMark.consts.generated.ts +24 -0
  135. package/src/shaders/connectorMark.generated.ts +12 -0
  136. package/src/shaders/connectorMark.glsl.generated.ts +6 -0
  137. package/src/shaders/connectorMark.iface.generated.ts +1270 -0
  138. package/src/shaders/connectorMark.js.generated.ts +52 -0
  139. package/src/shaders/connectorMark.slang +287 -0
  140. package/src/shaders/connectorMark.wgsl.generated.ts +4 -0
  141. package/src/shaders/coverageBand.slang +10 -12
  142. package/src/shaders/coverageInterbase.glsl.generated.ts +1 -1
  143. package/src/shaders/coverageInterbase.slang +24 -21
  144. package/src/shaders/coverageInterbase.wgsl.generated.ts +1 -1
  145. package/src/shaders/coverageSnp.slang +1 -1
  146. package/src/shaders/curveDistance.js.generated.ts +112 -0
  147. package/src/shaders/curveDistance.slang +104 -1
  148. package/src/shaders/farDome.test.ts +117 -0
  149. package/src/shaders/hpClampFloor.test.ts +0 -4
  150. package/src/shaders/insertionGlyph.generated.ts +14 -0
  151. package/src/shaders/insertionGlyph.slang +59 -0
  152. package/src/shaders/lineCenterMark.glsl.generated.ts +2 -2
  153. package/src/shaders/lineCenterMark.iface.generated.ts +21 -18
  154. package/src/shaders/lineCenterMark.slang +19 -23
  155. package/src/shaders/lineCenterMark.wgsl.generated.ts +1 -1
  156. package/src/shaders/lineCommon.js.generated.ts +20 -0
  157. package/src/shaders/lineCommon.slang +18 -8
  158. package/src/shaders/lineStepMark.glsl.generated.ts +2 -2
  159. package/src/shaders/lineStepMark.iface.generated.ts +21 -18
  160. package/src/shaders/lineStepMark.slang +10 -6
  161. package/src/shaders/lineStepMark.wgsl.generated.ts +1 -1
  162. package/src/shaders/linkMark.consts.generated.ts +2 -0
  163. package/src/shaders/linkMark.glsl.generated.ts +2 -2
  164. package/src/shaders/linkMark.js.generated.ts +30 -4
  165. package/src/shaders/linkMark.slang +67 -24
  166. package/src/shaders/linkMark.wgsl.generated.ts +1 -1
  167. package/src/shaders/markColor.slang +10 -10
  168. package/src/shaders/pointMark.glsl.generated.ts +1 -1
  169. package/src/shaders/pointMark.iface.generated.ts +19 -16
  170. package/src/shaders/pointMark.slang +12 -8
  171. package/src/shaders/pointMark.wgsl.generated.ts +1 -1
  172. package/src/shaders/rowRect.slang +3 -3
  173. package/src/shaders/rowTable.slang +12 -8
  174. package/src/shaders/scoreScale.slang +1 -1
  175. package/src/shaders/spanMark.glsl.generated.ts +1 -1
  176. package/src/shaders/spanMark.slang +5 -5
  177. package/src/shaders/spanMark.wgsl.generated.ts +1 -1
  178. package/src/shaders/viewRegion.slang +13 -0
  179. package/esm/shaders/coverageIndicator.generated.js +0 -7
  180. package/esm/shaders/coverageIndicator.glsl.generated.d.ts +0 -2
  181. package/esm/shaders/coverageIndicator.glsl.generated.js +0 -4
  182. package/esm/shaders/coverageIndicator.iface.generated.d.ts +0 -85
  183. package/esm/shaders/coverageIndicator.iface.generated.js +0 -114
  184. package/esm/shaders/coverageIndicator.wgsl.generated.d.ts +0 -1
  185. package/esm/shaders/coverageIndicator.wgsl.generated.js +0 -3
  186. package/src/shaders/coverageIndicator.generated.ts +0 -11
  187. package/src/shaders/coverageIndicator.glsl.generated.ts +0 -6
  188. package/src/shaders/coverageIndicator.iface.generated.ts +0 -172
  189. package/src/shaders/coverageIndicator.slang +0 -61
  190. package/src/shaders/coverageIndicator.wgsl.generated.ts +0 -4
@@ -18,8 +18,9 @@ export interface MarkPlan<TRegion, TState extends MarkFrame> {
18
18
  }
19
19
  /**
20
20
  * Resolve which marks draw this frame. Throws for an enabled mark the plan
21
- * form cannot carry — one with a `band` or a `paintsBlock` — since a plan
22
- * neither scissors nor asks the block anything, and without the throw the GPU
23
- * would draw what Canvas2D declines.
21
+ * form cannot carry — one with a `band`, a `paintsBlock` or a per-draw
22
+ * `verticesPerInstance` — since a plan neither scissors, asks the block
23
+ * anything nor sizes a draw, and without the throw the GPU would draw what
24
+ * Canvas2D declines, or a chevron a line.
24
25
  */
25
26
  export declare function planMarks<TRegion, TState extends MarkFrame>(marks: readonly Mark<TRegion, TState>[], state: TState): MarkPlan<TRegion, TState>;
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Resolve which marks draw this frame. Throws for an enabled mark the plan
3
- * form cannot carry — one with a `band` or a `paintsBlock` — since a plan
4
- * neither scissors nor asks the block anything, and without the throw the GPU
5
- * would draw what Canvas2D declines.
3
+ * form cannot carry — one with a `band`, a `paintsBlock` or a per-draw
4
+ * `verticesPerInstance` — since a plan neither scissors, asks the block
5
+ * anything nor sizes a draw, and without the throw the GPU would draw what
6
+ * Canvas2D declines, or a chevron a line.
6
7
  */
7
8
  export function planMarks(marks, state) {
8
9
  const enabled = [];
@@ -10,7 +11,7 @@ export function planMarks(marks, state) {
10
11
  for (const mark of marks) {
11
12
  if (!mark.enabled || mark.enabled(state)) {
12
13
  if (!mark.planned) {
13
- throw new Error(`mark ${mark.pass.id} declares a band or a block gate, which a frame plan does not carry; draw it through drawMarks`);
14
+ throw new Error(`mark ${mark.pass.id} declares a band, a block gate or a per-draw vertex count, which a frame plan does not carry; draw it through drawMarks`);
14
15
  }
15
16
  enabled.push(mark);
16
17
  passes.push(mark.planned);
@@ -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;
@@ -7,10 +7,24 @@ import { slangPass } from '../slangPass.js';
7
7
  import { abgrToCssRgba } from './colorFill.js';
8
8
  import { appendGlyph, glyphBox } from './glyphPaint.js';
9
9
  import { inkAtPoint, nearestInk } from './markHit.js';
10
- import { colorBits, paintColors, rampUniforms } from './markRamp.js';
10
+ import { colorBits, instanceColor, paintColors, rampUniforms, } from './markRamp.js';
11
11
  import { valueWindow } from './nearestMarkHit.js';
12
12
  import { bandHeightPx, rowColor, rowLane, rowSlot, rowTableKeys, rowTableTextures, } from './rowLane.js';
13
13
  import { valueScaleUniforms } from './valueScale.js';
14
+ const NO_VALUES = new Float32Array(0);
15
+ /** The channels cut down to instance `i`, for its own painter to trace alone. */
16
+ export function pointInstance(c, i) {
17
+ const { glyph } = c;
18
+ return {
19
+ x: c.x.subarray(i, i + 1),
20
+ x2: c.x2.subarray(i, i + 1),
21
+ y: c.y?.subarray(i, i + 1),
22
+ glyph: typeof glyph === 'number' ? glyph : glyph.subarray(i, i + 1),
23
+ row: c.row?.subarray(i, i + 1),
24
+ color: instanceColor(c.color, i),
25
+ count: 1,
26
+ };
27
+ }
14
28
  /**
15
29
  * The point shader's uniforms, which the rule shape draws through too:
16
30
  * `radiusPx` is half a glyph or half a rule's thickness.
@@ -29,6 +43,7 @@ export function writePointUniforms(scratch, clip, block, frame, params, shape) {
29
43
  // stretched ellipses on hi-DPI.
30
44
  viewportWidth: clip.scissorW,
31
45
  radiusPx: shape.radiusPx,
46
+ triangleRadiusYPx: shape.triangleRadiusYPx,
32
47
  rowHeight: bandHeightPx(params, frame.canvasHeight),
33
48
  rowOffsetPx: params.rowOffsetPx ?? 0,
34
49
  reverse: params.reverse ? 1 : 0,
@@ -43,11 +58,21 @@ export const pointMark = {
43
58
  id: 'point',
44
59
  pass: {
45
60
  ...slangPass({ id: 'point', mod: shader }),
46
- pack: c => shader.packInstances({ ...c, color: colorBits(c, c.count), row: rowLane(c.row, c.count) }, c.count),
61
+ pack: c => shader.packInstances({
62
+ x: c.x,
63
+ x2: c.x2,
64
+ y: c.y ?? (c.count === 0 ? NO_VALUES : new Float32Array(c.count)),
65
+ glyph: typeof c.glyph === 'number'
66
+ ? new Uint8Array(c.count).fill(c.glyph)
67
+ : c.glyph,
68
+ color: colorBits(c, c.count),
69
+ row: rowLane(c.row, c.count),
70
+ }, c.count),
47
71
  },
48
72
  writeUniforms(scratch, clip, block, frame, params) {
49
73
  writePointUniforms(scratch, clip, block, frame, params, {
50
74
  radiusPx: params.diameterPx / 2,
75
+ triangleRadiusYPx: (params.triangleHeightPx ?? params.diameterPx) / 2,
51
76
  rule: false,
52
77
  minWidthPx: 0,
53
78
  });
@@ -59,7 +84,7 @@ export const pointMark = {
59
84
  return;
60
85
  }
61
86
  const color = paintColors(channels, count, params.colorScale);
62
- const { diameterPx, domain, insetPx = 0, rowOffsetPx: top = 0, rowTable: table, } = params;
87
+ const { diameterPx, triangleHeightPx = diameterPx, domain, insetPx = 0, rowOffsetPx: top = 0, rowTable: table, } = params;
63
88
  const reverse = params.reverse ? 1 : 0;
64
89
  const band = bandHeightPx(params, frame.canvasHeight);
65
90
  const domainMin = domain[0];
@@ -81,8 +106,8 @@ export const pointMark = {
81
106
  ctx.fillStyle = abgrToCssRgba(abgr);
82
107
  }
83
108
  path.add();
84
- const yPx = pointRowYPx(rowBandTopPx(top, band, slot), band, reverse, pointYPx(y[i], domainMin, domainMax, band, st, insetPx, c));
85
- appendGlyph(ctx, glyph[i], (bpToPx(x[i]) + bpToPx(x2[i])) / 2, yPx, diameterPx);
109
+ const yPx = pointRowYPx(rowBandTopPx(top, band, slot), band, reverse, pointYPx(y ? y[i] : 0, domainMin, domainMax, band, st, insetPx, c));
110
+ appendGlyph(ctx, typeof glyph === 'number' ? glyph : glyph[i], (bpToPx(x[i]) + bpToPx(x2[i])) / 2, yPx, diameterPx, triangleHeightPx);
86
111
  }
87
112
  path.flush();
88
113
  },
@@ -93,12 +118,12 @@ export const pointMark = {
93
118
  if (slot === undefined) {
94
119
  return undefined;
95
120
  }
96
- const { diameterPx, domain, insetPx = 0 } = params;
121
+ const { diameterPx, triangleHeightPx, domain, insetPx = 0 } = params;
97
122
  const bpToPx = makeBpMapper(block);
98
123
  const band = bandHeightPx(params, frame.canvasHeight);
99
124
  const { valueScaleType: st, valueSymlogConstant: c } = valueScaleUniforms(params);
100
- const cy = pointRowYPx(rowBandTopPx(params.rowOffsetPx ?? 0, band, slot), band, params.reverse ? 1 : 0, pointYPx(y[i], domain[0], domain[1], band, st, insetPx, c));
101
- return glyphBox(glyph[i], (bpToPx(x[i]) + bpToPx(x2[i])) / 2, cy, diameterPx);
125
+ const cy = pointRowYPx(rowBandTopPx(params.rowOffsetPx ?? 0, band, slot), band, params.reverse ? 1 : 0, pointYPx(y ? y[i] : 0, domain[0], domain[1], band, st, insetPx, c));
126
+ return glyphBox(typeof glyph === 'number' ? glyph : glyph[i], (bpToPx(x[i]) + bpToPx(x2[i])) / 2, cy, diameterPx, triangleHeightPx);
102
127
  },
103
128
  // Its own rather than the one `ink` implies: a dense plot stacks glyphs
104
129
  // whose boxes all contain the cursor at distance 0, and the one under the
@@ -117,7 +142,7 @@ export const pointMark = {
117
142
  if (slot === undefined) {
118
143
  return undefined;
119
144
  }
120
- const cy = pointRowYPx(rowBandTopPx(top, band, slot), band, reverse, pointYPx(y[i], domainMin, domainMax, band, st, insetPx, c));
145
+ const cy = pointRowYPx(rowBandTopPx(top, band, slot), band, reverse, pointYPx(y ? y[i] : 0, domainMin, domainMax, band, st, insetPx, c));
121
146
  return inkAtPoint(xPx, yPx, (bpToPx(x[i]) + bpToPx(x2[i])) / 2, cy);
122
147
  });
123
148
  },
@@ -15,7 +15,7 @@ export interface RowParams {
15
15
  rowOffsetPx?: number;
16
16
  /**
17
17
  * The table `row` is read through as a key: its drawn slot, hidden, or a
18
- * colour override. Absent, `row` is the slot.
18
+ * color override. Absent, `row` is the slot.
19
19
  */
20
20
  rowTable?: RowTable;
21
21
  }
@@ -35,5 +35,7 @@ export declare function bandHeightPx(params: RowParams, canvasHeight: number): n
35
35
  export declare function keySlot(key: number, table: RowTable | undefined): number | undefined;
36
36
  /** The slot instance `i` is drawn on, undefined where the table hides its key. */
37
37
  export declare function rowSlot(row: Uint32Array | undefined, i: number, table: RowTable | undefined): number | undefined;
38
- /** The colour instance `i` is drawn in: its key's override, else `color`. */
38
+ /** Instance `i`'s key's color override, undefined where it keeps its own. */
39
+ export declare function rowColorOverride(row: Uint32Array | undefined, i: number, table: RowTable | undefined): number | undefined;
40
+ /** The color instance `i` is drawn in: its key's override, else `color`. */
39
41
  export declare function rowColor(color: number, row: Uint32Array | undefined, i: number, table: RowTable | undefined): number;
@@ -33,9 +33,13 @@ export function keySlot(key, table) {
33
33
  export function rowSlot(row, i, table) {
34
34
  return keySlot(rowKey(row, i), table);
35
35
  }
36
- /** The colour instance `i` is drawn in: its key's override, else `color`. */
37
- export function rowColor(color, row, i, table) {
36
+ /** Instance `i`'s key's color override, undefined where it keeps its own. */
37
+ export function rowColorOverride(row, i, table) {
38
38
  const key = rowKey(row, i);
39
39
  const override = table && key < table.keys ? table.color[key] : NO_ROW_COLOR;
40
- return override >>> 24 === 0 ? color : override;
40
+ return override >>> 24 === 0 ? undefined : override;
41
+ }
42
+ /** The color instance `i` is drawn in: its key's override, else `color`. */
43
+ export function rowColor(color, row, i, table) {
44
+ return rowColorOverride(row, i, table) ?? color;
41
45
  }
@@ -1,15 +1,15 @@
1
1
  import type { MarkTexels } from './types.ts';
2
2
  /** The `slot` a hidden key holds. */
3
3
  export declare const HIDDEN_ROW = 4294967295;
4
- /** The `color` a key with no override holds: alpha 0, the instance's own colour. */
4
+ /** The `color` a key with no override holds: alpha 0, the instance's own color. */
5
5
  export declare const NO_ROW_COLOR = 0;
6
6
  /**
7
7
  * The row axis as a pass reads it: each key's drawn slot or `HIDDEN_ROW`, its
8
- * packed ABGR colour override or `NO_ROW_COLOR`, and the RGBA8 texture the
8
+ * packed ABGR color override or `NO_ROW_COLOR`, and the RGBA8 texture the
9
9
  * vertex stage samples for the same answers (`shaders/rowTable.slang` has the
10
10
  * layout). One object, so the shader, the painter, the ink and the hit test
11
11
  * cannot place a key differently. Immutable: a reorder, focus, hide or
12
- * recolour builds a new one, and the backend uploads on identity.
12
+ * recolor builds a new one, and the backend uploads on identity.
13
13
  */
14
14
  export interface RowTable {
15
15
  readonly keys: number;
@@ -20,7 +20,7 @@ export interface RowTable {
20
20
  /**
21
21
  * The table for `slot[key]` and `color[key]`, both indexed by key: `slot` is
22
22
  * `HIDDEN_ROW` where the key draws nothing, `color` is `NO_ROW_COLOR` where the
23
- * instance keeps its own colour, an override alpha 0 being none. Both arrays
23
+ * instance keeps its own color, an override alpha 0 being none. Both arrays
24
24
  * are held, not copied. Each key's texel sits where the shader's own twins
25
25
  * put it.
26
26
  */
@@ -29,7 +29,7 @@ export declare function buildRowTable(slot: Uint32Array, color?: Uint32Array): R
29
29
  * The keys a display's rows carry across every region: a name's key is
30
30
  * assigned the first time it is asked for and never moves, so a region encoded
31
31
  * against the registry stays valid as later regions add names and the table
32
- * alone follows the reader's order, focus and colours. Grows only.
32
+ * alone follows the reader's order, focus and colors. Grows only.
33
33
  */
34
34
  export declare class RowKeys {
35
35
  private readonly index;
@@ -1,20 +1,20 @@
1
1
  import { rowTablePlaneHeight, rowTableTexelX, rowTableTexelY, rowTableWidth, } from '../shaders/rowTable.js.generated.js';
2
2
  /** The `slot` a hidden key holds. */
3
3
  export const HIDDEN_ROW = 0xffffffff;
4
- /** The `color` a key with no override holds: alpha 0, the instance's own colour. */
4
+ /** The `color` a key with no override holds: alpha 0, the instance's own color. */
5
5
  export const NO_ROW_COLOR = 0;
6
6
  const MAX_SLOT = 0xffffff;
7
7
  /**
8
8
  * The table for `slot[key]` and `color[key]`, both indexed by key: `slot` is
9
9
  * `HIDDEN_ROW` where the key draws nothing, `color` is `NO_ROW_COLOR` where the
10
- * instance keeps its own colour, an override alpha 0 being none. Both arrays
10
+ * instance keeps its own color, an override alpha 0 being none. Both arrays
11
11
  * are held, not copied. Each key's texel sits where the shader's own twins
12
12
  * put it.
13
13
  */
14
14
  export function buildRowTable(slot, color = new Uint32Array(slot.length)) {
15
15
  const keys = slot.length;
16
16
  if (color.length !== keys) {
17
- throw new Error(`buildRowTable: ${keys} slots but ${color.length} colours; both are per key`);
17
+ throw new Error(`buildRowTable: ${keys} slots but ${color.length} colors; both are per key`);
18
18
  }
19
19
  const width = rowTableWidth(keys);
20
20
  const plane = rowTablePlaneHeight(keys);
@@ -51,7 +51,7 @@ export function buildRowTable(slot, color = new Uint32Array(slot.length)) {
51
51
  * The keys a display's rows carry across every region: a name's key is
52
52
  * assigned the first time it is asked for and never moves, so a region encoded
53
53
  * against the registry stays valid as later regions add names and the table
54
- * alone follows the reader's order, focus and colours. Grows only.
54
+ * alone follows the reader's order, focus and colors. Grows only.
55
55
  */
56
56
  export class RowKeys {
57
57
  index = new Map();
@@ -59,6 +59,7 @@ export const ruleMark = {
59
59
  writeUniforms(scratch, clip, block, frame, params) {
60
60
  writePointUniforms(scratch, clip, block, frame, params, {
61
61
  radiusPx: params.sizePx / 2,
62
+ triangleRadiusYPx: params.sizePx / 2,
62
63
  rule: true,
63
64
  minWidthPx: params.minWidthPx,
64
65
  });
@@ -2,8 +2,8 @@ import type { ColorChannel } from './markRamp.ts';
2
2
  import type { RowTable } from './rowTable.ts';
3
3
  import type { MarkColorScale, MarkShape } from './types.ts';
4
4
  /**
5
- * The `span` shape's channels: a coloured rectangle from `x` to `x2` on the
6
- * band belonging to `row`, its colour packed or, under a colour scale, the
5
+ * The `span` shape's channels: a colored rectangle from `x` to `x2` on the
6
+ * band belonging to `row`, its color packed or, under a color scale, the
7
7
  * value it resolves from.
8
8
  */
9
9
  export interface SpanChannels extends ColorChannel {
@@ -14,7 +14,7 @@ export interface SpanChannels extends ColorChannel {
14
14
  count: number;
15
15
  }
16
16
  export interface SpanParams {
17
- /** The quantitative colour scale, for a span whose colour is a ramp or a threshold. */
17
+ /** The quantitative color scale, for a span whose color is a ramp or a threshold. */
18
18
  colorScale?: MarkColorScale;
19
19
  /** CSS px per row. */
20
20
  rowHeight: number;
@@ -26,16 +26,17 @@ export interface SpanParams {
26
26
  */
27
27
  minWidthPx: number;
28
28
  /**
29
- * CSS px the **painter alone** adds to each span's right edge, closing the
30
- * hairline two antialiased `fillRect`s leave where tiling runs meet on a
31
- * fractional pixel. The GPU pass needs none, and the ink excludes it.
29
+ * CSS px the **painter alone** adds to each span's edge facing the next span
30
+ * painted — the left edge on a reversed block — closing the hairline two
31
+ * antialiased `fillRect`s leave where tiling runs meet on a fractional pixel.
32
+ * The GPU pass needs none, and the ink excludes it.
32
33
  */
33
34
  seamPx: number;
34
35
  /** Rows-area scroll offset in CSS px; 0 for a canvas sized to its content. */
35
36
  scrollTop: number;
36
37
  /**
37
38
  * The table `row` is read through as a key: its drawn slot, hidden, or a
38
- * colour override. Absent, `row` is the slot.
39
+ * color override. Absent, `row` is the slot.
39
40
  */
40
41
  rowTable?: RowTable;
41
42
  }
@@ -25,7 +25,7 @@ function spanFrame(block, params) {
25
25
  height: drawnRowHeightPx(rowHeight, rowProportion),
26
26
  };
27
27
  }
28
- function placeSpan(c, g, i) {
28
+ function placeSpan(c, g, i, seamPx = 0) {
29
29
  const slot = rowSlot(c.row, i, g.table);
30
30
  if (slot === undefined) {
31
31
  return false;
@@ -33,7 +33,7 @@ function placeSpan(c, g, i) {
33
33
  const xa = projectBp(g, c.x[i]);
34
34
  const xb = projectBp(g, c.x2[i]);
35
35
  const width = Math.max(g.minWidthPx, Math.abs(xb - xa));
36
- g.left = spanLeft(xa, xb, width);
36
+ g.left = spanLeft(xa, xb, width + seamPx);
37
37
  g.top = g.bandOffsetPx + g.rowHeight * slot - g.scrollTop;
38
38
  g.width = width;
39
39
  return true;
@@ -66,7 +66,7 @@ export const spanMark = {
66
66
  const g = spanFrame(block, params);
67
67
  const setFill = makeAbgrFill(ctx);
68
68
  for (let i = 0; i < count; i++) {
69
- if (placeSpan(channels, g, i)) {
69
+ if (placeSpan(channels, g, i, seamPx)) {
70
70
  setFill(rowColor(color[i], row, i, g.table));
71
71
  ctx.fillRect(g.left, g.top, g.width + seamPx, g.height);
72
72
  }
@@ -32,7 +32,7 @@ export interface MarkContext2D extends ClipContext2D {
32
32
  drawImage?(image: CanvasImageSource, sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number): void;
33
33
  }
34
34
  /**
35
- * A canvas a mark's pass samples in place of a colour ramp. Hand a fresh object
35
+ * A canvas a mark's pass samples in place of a color ramp. Hand a fresh object
36
36
  * per repaint: the backend uploads on identity.
37
37
  */
38
38
  export interface MarkImage {
@@ -60,7 +60,7 @@ export type MarkTextures = Readonly<Record<string, MarkTexture | undefined>>;
60
60
  export type MarkFrame = FrameDimensions;
61
61
  export type MarkValueScaleType = 'linear' | 'log' | 'symlog';
62
62
  /**
63
- * The colour scale a ramp channel's raw `colorValue`s resolve through each
63
+ * The color scale a ramp channel's raw `colorValue`s resolve through each
64
64
  * frame, with the 256-entry RGBA LUT the pass's sampler binds: straight, its
65
65
  * middle stop at the middle, and `mid`, where a diverging ramp puts that stop,
66
66
  * read through a uniform.
@@ -76,13 +76,13 @@ export interface MarkRamp {
76
76
  * A threshold a value channel's raw `colorValue`s resolve through each frame:
77
77
  * the ascending cut points, at most `MAX_COLOR_CUTS` of them, and one packed
78
78
  * ABGR per interval between them, `cuts.length + 1` in all. The cuts and
79
- * colours ride uniforms, so an edit moves them and uploads no instance bytes.
79
+ * colors ride uniforms, so an edit moves them and uploads no instance bytes.
80
80
  */
81
81
  export interface MarkThreshold {
82
82
  cuts: readonly number[];
83
83
  colors: Uint32Array;
84
84
  }
85
- /** The scale a quantitative colour channel resolves through: a ramp or a threshold. */
85
+ /** The scale a quantitative color channel resolves through: a ramp or a threshold. */
86
86
  export type MarkColorScale = MarkRamp | MarkThreshold;
87
87
  /** A horizontal strip of the canvas, in CSS px down from its top edge. */
88
88
  export interface MarkBand {
@@ -220,7 +220,10 @@ export interface Mark<TRegion, TState extends MarkFrame> {
220
220
  * frame and every other consumer per block, before any lens.
221
221
  */
222
222
  readonly enabled?: (state: TState) => boolean;
223
- /** The pass a frame plan draws; undefined with a `band` or `paintsBlock`. */
223
+ /**
224
+ * The pass a frame plan draws; undefined with a `band`, a `paintsBlock` or
225
+ * a per-draw `verticesPerInstance`, none of which a plan carries.
226
+ */
224
227
  readonly planned?: PlannedPass;
225
228
  drawRegion(hal: GpuHal, scratch: ArrayBuffer, block: RenderBlock, clip: BlockClipResult, region: TRegion, state: TState, regionKey: number, staged?: StagedUniforms, textures?: TextureBinder): void;
226
229
  paintBlock(ctx: MarkContext2D, region: TRegion, block: RenderBlock, state: TState): void;
@@ -37,7 +37,9 @@ export function defineMark(spec) {
37
37
  throw new Error(`mark ${shape.id} draws off ${lender.id}'s buffer but declares a different instance struct`);
38
38
  }
39
39
  const bufferOf = lender?.id;
40
- const planned = band || shape.paintsBlock ? undefined : { id: shape.pass.id, bufferOf };
40
+ const planned = band || shape.paintsBlock || shape.verticesPerInstance
41
+ ? undefined
42
+ : { id: shape.pass.id, bufferOf };
41
43
  // `channels` gates `params`: a union payload's params lens may only read its
42
44
  // own kind of region.
43
45
  function resolve(region, block, state) {
@@ -104,6 +106,11 @@ export function defineMark(spec) {
104
106
  hal.setScissor(clip.pxX, scissor.top, clip.pxW, scissor.height);
105
107
  }
106
108
  if (staged?.writer !== shape.writeUniforms || staged.params !== params) {
109
+ // A generated packer drops whatever lands past the end of a short
110
+ // buffer, which draws the pass from zeros with nothing said.
111
+ if (scratch.byteLength < shape.pass.uniformByteSize) {
112
+ throw new Error(`${shape.pass.id} writes ${shape.pass.uniformByteSize} bytes of uniforms into a ${scratch.byteLength}-byte scratch`);
113
+ }
107
114
  shape.writeUniforms(scratch, clip, block, state, p);
108
115
  hal.writeUniforms(scratch);
109
116
  if (staged) {
@@ -66,7 +66,7 @@ export declare abstract class Canvas2DPerRegionRenderingBackend<UploadData, Rend
66
66
  * `prepareCanvas`'s `clearRect` as the whole of it, which is transparent.
67
67
  * Synteny is the one display that overrides: its indel wedges pre-blend
68
68
  * against a KNOWN ground and only agree with the base ribbon beside them over
69
- * a destination that really is that colour.
69
+ * a destination that really is that color.
70
70
  */
71
71
  protected clearColor(_state: RenderState): ClearColor | undefined;
72
72
  /**
@@ -37,7 +37,7 @@ export class Canvas2DPerRegionRenderingBackend extends Canvas2DRenderingBackendB
37
37
  * `prepareCanvas`'s `clearRect` as the whole of it, which is transparent.
38
38
  * Synteny is the one display that overrides: its indel wedges pre-blend
39
39
  * against a KNOWN ground and only agree with the base ribbon beside them over
40
- * a destination that really is that colour.
40
+ * a destination that really is that color.
41
41
  */
42
42
  clearColor(_state) {
43
43
  return undefined;
@@ -7,8 +7,8 @@ export declare function isCheckedAtTheStore(map: ReadonlyMap<unknown, unknown>):
7
7
  * ADR-060.
8
8
  *
9
9
  * **Shallow, and that follows from an invariant the codebase already states**:
10
- * per-region values are freshly constructed and never mutated (agent-docs
11
- * CLAUDE.md; backends diff by reference identity). Nothing inside an entry can
10
+ * per-region values are freshly constructed and never mutated (ADR-060;
11
+ * backends diff by reference identity). Nothing inside an entry can
12
12
  * therefore change, so the deep enhancer's field-level atoms can never fire —
13
13
  * they are unreachable reactivity, not a safety margin. What they do cost is
14
14
  * paid on every insert and every read:
@@ -17,8 +17,8 @@ export function isCheckedAtTheStore(map) {
17
17
  * ADR-060.
18
18
  *
19
19
  * **Shallow, and that follows from an invariant the codebase already states**:
20
- * per-region values are freshly constructed and never mutated (agent-docs
21
- * CLAUDE.md; backends diff by reference identity). Nothing inside an entry can
20
+ * per-region values are freshly constructed and never mutated (ADR-060;
21
+ * backends diff by reference identity). Nothing inside an entry can
22
22
  * therefore change, so the deep enhancer's field-level atoms can never fire —
23
23
  * they are unreachable reactivity, not a safety margin. What they do cost is
24
24
  * paid on every insert and every read: