@jbrowse/render-core 5.0.0-beta.8 → 5.0.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/RenderCanvas.d.ts +2 -2
- package/esm/RenderLifecycleMixin.d.ts +7 -6
- package/esm/RenderLifecycleMixin.js +7 -6
- package/esm/canvas2dUtils.d.ts +58 -17
- package/esm/canvas2dUtils.js +82 -30
- package/esm/computePipeline.d.ts +4 -3
- package/esm/computePipeline.js +21 -14
- package/esm/coverageBand.d.ts +3 -3
- package/esm/coverageBand.js +2 -2
- package/esm/coverageBandBuffers.d.ts +2 -3
- package/esm/createRenderingBackend.d.ts +1 -1
- package/esm/displayPhase.d.ts +79 -109
- package/esm/displayPhase.js +42 -83
- package/esm/gpuDevice.d.ts +11 -10
- package/esm/gpuDevice.js +10 -11
- package/esm/graphicsCapabilities.d.ts +6 -7
- package/esm/graphicsCapabilities.js +3 -4
- package/esm/hal/createHal.js +1 -1
- package/esm/hal/deviceGpuCache.d.ts +25 -30
- package/esm/hal/deviceGpuCache.js +85 -97
- package/esm/hal/index.d.ts +2 -2
- package/esm/hal/index.js +1 -1
- package/esm/hal/mockHal.d.ts +8 -6
- package/esm/hal/mockHal.js +7 -2
- package/esm/hal/passIds.d.ts +8 -7
- package/esm/hal/passIds.js +11 -10
- package/esm/hal/types.d.ts +28 -12
- package/esm/hal/webgl2Hal.d.ts +11 -3
- package/esm/hal/webgl2Hal.js +116 -145
- package/esm/hal/webgpuHal.d.ts +8 -8
- package/esm/hal/webgpuHal.js +97 -124
- package/esm/instancePass.d.ts +2 -1
- package/esm/mapUploadSync.d.ts +2 -2
- package/esm/mapUploadSync.js +2 -2
- package/esm/marks/barMark.d.ts +6 -3
- package/esm/marks/barMark.js +19 -13
- package/esm/marks/drawAgainstHit.d.ts +58 -39
- package/esm/marks/drawAgainstHit.js +136 -96
- package/esm/marks/glyphPaint.d.ts +14 -2
- package/esm/marks/glyphPaint.js +16 -2
- package/esm/marks/index.d.ts +4 -2
- package/esm/marks/index.js +3 -2
- package/esm/marks/markBackend.d.ts +2 -2
- package/esm/marks/markBackend.js +2 -2
- package/esm/marks/markHit.d.ts +2 -2
- package/esm/marks/markHit.js +2 -2
- package/esm/marks/markPlan.d.ts +2 -2
- package/esm/marks/markPlan.js +2 -2
- package/esm/marks/markRamp.d.ts +1 -3
- package/esm/marks/markRamp.js +1 -5
- package/esm/marks/nearestMarkHit.d.ts +49 -0
- package/esm/marks/nearestMarkHit.js +126 -0
- package/esm/marks/pointMark.d.ts +9 -2
- package/esm/marks/pointMark.js +36 -31
- package/esm/marks/rowLane.d.ts +17 -0
- package/esm/marks/rowLane.js +11 -0
- package/esm/marks/spanMark.d.ts +6 -26
- package/esm/marks/spanMark.js +34 -31
- package/esm/marks/types.d.ts +51 -181
- package/esm/marks/types.js +91 -131
- package/esm/namedReactions.d.ts +3 -4
- package/esm/namedReactions.js +3 -4
- package/esm/scoreScale.d.ts +20 -0
- package/esm/scoreScale.js +77 -0
- package/esm/shaders/barMark.generated.d.ts +2 -3
- package/esm/shaders/barMark.generated.js +4 -3
- package/esm/shaders/barMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/barMark.glsl.generated.js +4 -0
- package/esm/shaders/barMark.iface.generated.d.ts +12 -6
- package/esm/shaders/barMark.iface.generated.js +27 -15
- package/esm/shaders/barMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/barMark.wgsl.generated.js +3 -0
- package/esm/shaders/coverageBar.generated.d.ts +2 -3
- package/esm/shaders/coverageBar.generated.js +4 -3
- package/esm/shaders/coverageBar.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageBar.glsl.generated.js +4 -0
- package/esm/shaders/coverageBar.iface.generated.js +1 -1
- package/esm/shaders/coverageBar.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageBar.wgsl.generated.js +3 -0
- package/esm/shaders/coverageIndicator.generated.d.ts +2 -3
- package/esm/shaders/coverageIndicator.generated.js +4 -3
- package/esm/shaders/coverageIndicator.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageIndicator.glsl.generated.js +4 -0
- package/esm/shaders/coverageIndicator.iface.generated.js +1 -1
- package/esm/shaders/coverageIndicator.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageIndicator.wgsl.generated.js +3 -0
- package/esm/shaders/coverageInterbase.generated.d.ts +2 -3
- package/esm/shaders/coverageInterbase.generated.js +4 -3
- package/esm/shaders/coverageInterbase.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageInterbase.glsl.generated.js +4 -0
- package/esm/shaders/coverageInterbase.iface.generated.js +1 -1
- package/esm/shaders/coverageInterbase.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageInterbase.wgsl.generated.js +3 -0
- package/esm/shaders/coverageMod.generated.d.ts +2 -3
- package/esm/shaders/coverageMod.generated.js +4 -3
- package/esm/shaders/coverageMod.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageMod.glsl.generated.js +4 -0
- package/esm/shaders/coverageMod.iface.generated.js +1 -1
- package/esm/shaders/coverageMod.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageMod.wgsl.generated.js +3 -0
- package/esm/shaders/coverageSnp.generated.d.ts +2 -3
- package/esm/shaders/coverageSnp.generated.js +4 -3
- package/esm/shaders/coverageSnp.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageSnp.glsl.generated.js +4 -0
- package/esm/shaders/coverageSnp.iface.generated.js +1 -1
- package/esm/shaders/coverageSnp.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageSnp.wgsl.generated.js +3 -0
- package/esm/shaders/pointMark.generated.d.ts +2 -3
- package/esm/shaders/pointMark.generated.js +4 -3
- package/esm/shaders/pointMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/pointMark.glsl.generated.js +4 -0
- package/esm/shaders/pointMark.iface.generated.d.ts +11 -3
- package/esm/shaders/pointMark.iface.generated.js +23 -9
- package/esm/shaders/pointMark.js.generated.d.ts +1 -1
- package/esm/shaders/pointMark.js.generated.js +51 -29
- package/esm/shaders/pointMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/pointMark.wgsl.generated.js +3 -0
- package/esm/shaders/scoreScale.js.generated.js +46 -22
- package/esm/shaders/spanMark.generated.d.ts +2 -3
- package/esm/shaders/spanMark.generated.js +4 -3
- package/esm/shaders/spanMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/spanMark.glsl.generated.js +4 -0
- package/esm/shaders/spanMark.iface.generated.js +1 -1
- package/esm/shaders/spanMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/spanMark.wgsl.generated.js +3 -0
- package/esm/slangPass.d.ts +9 -10
- package/esm/slangPass.js +9 -3
- package/esm/useGraphicsCapabilities.d.ts +1 -1
- package/esm/useGraphicsCapabilities.js +1 -1
- package/esm/webgpuUtils.d.ts +3 -11
- package/esm/webgpuUtils.js +12 -33
- package/package.json +3 -2
- package/src/shaders/barMark.generated.ts +6 -5
- package/src/shaders/barMark.glsl.generated.ts +6 -0
- package/src/shaders/barMark.iface.generated.ts +31 -15
- package/src/shaders/barMark.slang +11 -3
- package/src/shaders/barMark.wgsl.generated.ts +4 -0
- package/src/shaders/colorPack.slang +3 -2
- package/src/shaders/coverageBand.slang +5 -11
- package/src/shaders/coverageBar.generated.ts +6 -5
- package/src/shaders/coverageBar.glsl.generated.ts +6 -0
- package/src/shaders/coverageBar.iface.generated.ts +1 -1
- package/src/shaders/coverageBar.slang +1 -1
- package/src/shaders/coverageBar.wgsl.generated.ts +4 -0
- package/src/shaders/coverageIndicator.generated.ts +6 -5
- package/src/shaders/coverageIndicator.glsl.generated.ts +6 -0
- package/src/shaders/coverageIndicator.iface.generated.ts +1 -1
- package/src/shaders/coverageIndicator.wgsl.generated.ts +4 -0
- package/src/shaders/coverageInterbase.generated.ts +6 -5
- package/src/shaders/coverageInterbase.glsl.generated.ts +6 -0
- package/src/shaders/coverageInterbase.iface.generated.ts +1 -1
- package/src/shaders/coverageInterbase.wgsl.generated.ts +4 -0
- package/src/shaders/coverageMod.generated.ts +6 -5
- package/src/shaders/coverageMod.glsl.generated.ts +6 -0
- package/src/shaders/coverageMod.iface.generated.ts +1 -1
- package/src/shaders/coverageMod.wgsl.generated.ts +4 -0
- package/src/shaders/coverageSnp.generated.ts +6 -5
- package/src/shaders/coverageSnp.glsl.generated.ts +6 -0
- package/src/shaders/coverageSnp.iface.generated.ts +1 -1
- package/src/shaders/coverageSnp.wgsl.generated.ts +4 -0
- package/src/shaders/hpmath.slang +18 -6
- package/src/shaders/pointMark.generated.ts +6 -6
- package/src/shaders/pointMark.glsl.generated.ts +6 -0
- package/src/shaders/pointMark.iface.generated.ts +28 -9
- package/src/shaders/pointMark.js.generated.ts +48 -29
- package/src/shaders/pointMark.slang +18 -5
- package/src/shaders/pointMark.wgsl.generated.ts +4 -0
- package/src/shaders/scoreScale.js.generated.ts +43 -22
- package/src/shaders/scoreScale.slang +37 -53
- package/src/shaders/spanMark.generated.ts +6 -5
- package/src/shaders/spanMark.glsl.generated.ts +6 -0
- package/src/shaders/spanMark.iface.generated.ts +1 -1
- package/src/shaders/spanMark.wgsl.generated.ts +4 -0
- package/src/shaders/valueScale.slang +8 -45
package/esm/marks/types.d.ts
CHANGED
|
@@ -5,11 +5,9 @@ import type { InstancePass } from '../instancePass.ts';
|
|
|
5
5
|
import type { RenderBlock } from '../renderBlock.ts';
|
|
6
6
|
import type { FrameDimensions } from '../renderingBackendBase.ts';
|
|
7
7
|
/**
|
|
8
|
-
* The 2D-context subset a shape's painter needs,
|
|
9
|
-
* `
|
|
10
|
-
*
|
|
11
|
-
* satisfy it, so a display passes its `Ctx2D` straight in — which is also what
|
|
12
|
-
* makes one painter the on-screen fallback and the SVG export path.
|
|
8
|
+
* The 2D-context subset a shape's painter needs, which both a canvas and
|
|
9
|
+
* `@jbrowse/core`'s `SvgCanvas` satisfy: one painter is the fallback and the
|
|
10
|
+
* SVG export.
|
|
13
11
|
*/
|
|
14
12
|
export interface MarkContext2D extends ClipContext2D {
|
|
15
13
|
fillStyle: string | CanvasGradient | CanvasPattern;
|
|
@@ -34,9 +32,8 @@ export interface MarkContext2D extends ClipContext2D {
|
|
|
34
32
|
drawImage?(image: CanvasImageSource, sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number): void;
|
|
35
33
|
}
|
|
36
34
|
/**
|
|
37
|
-
* A canvas a mark's pass samples in place of a colour ramp
|
|
38
|
-
*
|
|
39
|
-
* backend uploads on identity, and a canvas element's identity never moves.
|
|
35
|
+
* A canvas a mark's pass samples in place of a colour ramp. Hand a fresh object
|
|
36
|
+
* per repaint: the backend uploads on identity.
|
|
40
37
|
*/
|
|
41
38
|
export interface MarkImage {
|
|
42
39
|
image: HTMLCanvasElement | OffscreenCanvas;
|
|
@@ -46,14 +43,10 @@ export interface MarkImage {
|
|
|
46
43
|
/** What a mark's pass binds: a 256-entry RGBA ramp, or a canvas. */
|
|
47
44
|
export type MarkTexture = Uint8Array | MarkImage;
|
|
48
45
|
export type MarkFrame = FrameDimensions;
|
|
49
|
-
/** How a declared value scale reads its domain. */
|
|
50
46
|
export type MarkValueScaleType = 'linear' | 'log';
|
|
51
47
|
/**
|
|
52
|
-
* The
|
|
53
|
-
*
|
|
54
|
-
* type; and the 256-entry RGBA LUT the pass's sampler binds. A shape that
|
|
55
|
-
* reads it takes the raw values in `colorValue` and resolves them per frame,
|
|
56
|
-
* so a pan that widens the domain uploads no instance bytes.
|
|
48
|
+
* The colour scale a ramp channel's raw `colorValue`s resolve through each
|
|
49
|
+
* frame, with the 256-entry RGBA LUT the pass's sampler binds.
|
|
57
50
|
*/
|
|
58
51
|
export interface MarkRamp {
|
|
59
52
|
domain: [number, number];
|
|
@@ -80,20 +73,10 @@ export interface InkRect {
|
|
|
80
73
|
height: number;
|
|
81
74
|
}
|
|
82
75
|
/**
|
|
83
|
-
* A shape: one
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* count. `TParams` is everything else the drawing needs, which reaches the GPU
|
|
88
|
-
* as uniforms and the painter as arguments; the two must be the same values or
|
|
89
|
-
* the backends diverge, which is what having one object per shape prevents.
|
|
90
|
-
*
|
|
91
|
-
* There is no `MarkShape` constructor. A shape is written out once and
|
|
92
|
-
* admitted by a consumer rather than by completeness — the ADR-040 bar. It
|
|
93
|
-
* lives here when two displays share it (`span`) or its geometry is generic
|
|
94
|
-
* (`point`); a display whose shader's generated twins feed its own hit test
|
|
95
|
-
* keeps the shape beside them (variants' `cellMark`) and still declares it
|
|
96
|
-
* through `defineMark`.
|
|
76
|
+
* A shape: one `.slang` pass, a uniform writer and a Canvas2D painter over the
|
|
77
|
+
* same channel arrays, and the geometry its hit test reads. `TParams` reaches
|
|
78
|
+
* the GPU as uniforms and the painter as arguments, so both backends draw the
|
|
79
|
+
* same values.
|
|
97
80
|
*/
|
|
98
81
|
export interface MarkShape<TChannels, TParams> {
|
|
99
82
|
readonly id: string;
|
|
@@ -101,196 +84,83 @@ export interface MarkShape<TChannels, TParams> {
|
|
|
101
84
|
writeUniforms(scratch: ArrayBuffer, clip: BlockClipResult, block: RenderBlock, frame: MarkFrame, params: TParams): void;
|
|
102
85
|
paintBlock(ctx: MarkContext2D, channels: TChannels, block: RenderBlock, frame: MarkFrame, params: TParams): void;
|
|
103
86
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* says no, and so does `hitNearest`: a block with no ink on it has nothing
|
|
107
|
-
* to be near. Optional: a shape whose every block can carry ink leaves it
|
|
108
|
-
* off.
|
|
109
|
-
*
|
|
110
|
-
* Placement is one use: the canvas continuation marker exists only where a
|
|
111
|
-
* block meets a canvas edge, and an interior block would otherwise shade a
|
|
112
|
-
* whole pileup's worth of vertices to draw nothing. Settings are the other,
|
|
113
|
-
* and the general one: the coverage band's layers read `hasDomain` off
|
|
114
|
-
* `params` so an unresolved autoscale draws no depth-scaled layer rather
|
|
115
|
-
* than bars of arbitrary height, and `showInterbase` off it so the histogram
|
|
116
|
-
* and its triangles turn off together.
|
|
87
|
+
* Whether the block carries any ink under these params. Both backends and the
|
|
88
|
+
* hit test skip a block it declines.
|
|
117
89
|
*/
|
|
118
90
|
paintsBlock?(block: RenderBlock, frame: MarkFrame, params: TParams): boolean;
|
|
119
91
|
/**
|
|
120
|
-
* The rect `paintBlock` fills for instance `i`,
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* declared, and a display's highlight guide reads it for the instances it
|
|
125
|
-
* names. A shape whose ink is not a box (a ribbon, an arc, a capsule) leaves
|
|
126
|
-
* it off and keeps its own hit test.
|
|
127
|
-
*
|
|
128
|
-
* Painter-only overdraw — `span`'s seam — is not ink; `drawAgainstHit.ts`
|
|
129
|
-
* holds the two to each other within a pixel.
|
|
92
|
+
* The rect `paintBlock` fills for instance `i`, undefined where it paints
|
|
93
|
+
* nothing. `defineMark` derives `hitNearest` from it where none is declared,
|
|
94
|
+
* and a highlight guide draws it. Painter-only overdraw, `span`'s seam, is
|
|
95
|
+
* not ink.
|
|
130
96
|
*/
|
|
131
97
|
ink?(channels: TChannels, block: RenderBlock, frame: MarkFrame, params: TParams, i: number): InkRect | undefined;
|
|
132
98
|
/**
|
|
133
99
|
* The nearest drawn ink to `(xPx, yPx)` among `candidates`, or undefined if
|
|
134
|
-
* nothing beats `maxDistSq`.
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* worker-built index (GWAS's Flatbush over (bp, value)) hands in what the
|
|
138
|
-
* index answered, and one without hands in every instance. What the shape
|
|
139
|
-
* owns is where its ink actually is — the half that drifts from the painter.
|
|
140
|
-
*
|
|
141
|
-
* Distance 0 means the point is on the ink `paintBlock` fills, edges
|
|
142
|
-
* included, and a caller wanting containment alone passes a bound that
|
|
143
|
-
* admits nothing else (`Number.MIN_VALUE`). Only a strictly nearer candidate
|
|
144
|
-
* replaces the best, so on a tie the first candidate wins — a caller that
|
|
145
|
-
* iterates back to front gets the last-painted mark, which is the one on
|
|
146
|
-
* top. `drawAgainstHit.ts` is the sweep that holds a shape to this.
|
|
147
|
-
*
|
|
148
|
-
* Optional, because a shape earns it from a consumer like any other member.
|
|
149
|
-
* MAF answers a hit from row/column arithmetic over the alignment rather than
|
|
150
|
-
* from `span`'s channels, so it reads none. A shape with `ink` and no hit test
|
|
151
|
-
* gets `inkHitNearest` — the nearest of its boxes — through `shapeHitNearest`.
|
|
100
|
+
* nothing beats `maxDistSq`. Distance 0 is on the ink, edges included. Only a
|
|
101
|
+
* strictly nearer candidate replaces the best, so candidates handed back to
|
|
102
|
+
* front answer a tie with the mark on top.
|
|
152
103
|
*/
|
|
153
104
|
hitNearest?(channels: TChannels, block: RenderBlock, frame: MarkFrame, params: TParams, xPx: number, yPx: number, candidates: Iterable<number>, maxDistSq: number): MarkHit | undefined;
|
|
105
|
+
/**
|
|
106
|
+
* The values an instance can hold and still put ink within `radiusPx` of
|
|
107
|
+
* canvas y `yPx`, which a display's index over (bp, value) is searched with.
|
|
108
|
+
* A shape with no value axis leaves it off.
|
|
109
|
+
*/
|
|
110
|
+
valueWindow?(yPx: number, radiusPx: number, frame: MarkFrame, params: TParams): [number, number];
|
|
154
111
|
}
|
|
155
112
|
/**
|
|
156
|
-
*
|
|
157
|
-
* of the
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
* nothing but the block, the clip, the frame and what that lens returned.
|
|
161
|
-
*
|
|
162
|
-
* A display declaring several marks over one writer and one lens is the common
|
|
163
|
-
* shape, not an edge case — the coverage band is five, the feature glyphs are
|
|
164
|
-
* five — and every mark after the first was restating the previous one's write.
|
|
165
|
-
*
|
|
166
|
-
* **The caller owns one of these per block and hands the same one to every mark
|
|
167
|
-
* of that block.** A stale one drawn against the next block's clip is silently
|
|
168
|
-
* the wrong geometry, so it is a fresh object per block rather than a field
|
|
169
|
-
* that has to be remembered to reset. A caller that writes uniforms of its own
|
|
170
|
-
* between marks (alignments' per-section pileup write) starts a new one after
|
|
171
|
-
* it, or hands none at all.
|
|
113
|
+
* The uniform writer and `params` lens whose struct is in the HAL's slot, so a
|
|
114
|
+
* later mark of the same block over both draws without writing it again. Hand
|
|
115
|
+
* every mark of a block one fresh object: a stale one draws against the
|
|
116
|
+
* previous block's clip.
|
|
172
117
|
*/
|
|
173
118
|
export interface StagedUniforms {
|
|
174
119
|
writer: unknown;
|
|
175
120
|
params: unknown;
|
|
176
121
|
}
|
|
177
|
-
/**
|
|
178
|
-
* A mark's pass as a frame plan draws it: what `hal.drawPass(id, regionKey,
|
|
179
|
-
* bufferOf)` takes and nothing else, resolved once per frame so the per-block
|
|
180
|
-
* loop reads two fields per mark.
|
|
181
|
-
*/
|
|
122
|
+
/** What `hal.drawPass` takes for one mark of a frame plan. */
|
|
182
123
|
export interface PlannedPass {
|
|
183
124
|
readonly id: string;
|
|
184
125
|
readonly bufferOf: string | undefined;
|
|
185
126
|
}
|
|
186
127
|
/**
|
|
187
|
-
* A shape bound to
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
* The shape's own generics are erased here on purpose: a display draws a list
|
|
191
|
-
* of marks over heterogeneous shapes, and only the binding knows how to reach
|
|
192
|
-
* either one's channels.
|
|
128
|
+
* A shape bound to a display's region payload and render state, its generics
|
|
129
|
+
* erased so one list holds several shapes.
|
|
193
130
|
*/
|
|
194
131
|
export interface Mark<TRegion, TState extends MarkFrame> {
|
|
195
132
|
readonly pass: InstancePass<TRegion>;
|
|
196
|
-
/**
|
|
197
|
-
* The pass id whose instance buffer this mark draws from, for a mark that
|
|
198
|
-
* is registered but never uploaded to: the canvas chevrons ride the line
|
|
199
|
-
* buffer and the continuation markers ride rect's. `defineMark` refuses the
|
|
200
|
-
* pairing unless the two passes declare one instance struct.
|
|
201
|
-
*/
|
|
133
|
+
/** The pass whose uploaded instance buffer this mark draws from. */
|
|
202
134
|
readonly bufferOf?: string;
|
|
203
|
-
/**
|
|
204
|
-
* The 256-entry RGBA colour ramp this mark's pass samples this frame, or
|
|
205
|
-
* undefined when it samples none. `createMarkBackend` uploads it per pass and
|
|
206
|
-
* only when the bytes' identity moves, and binds an inert table for the
|
|
207
|
-
* undefined case — a shader owns its sampler unconditionally, and a textured
|
|
208
|
-
* pass with no texture never draws on the WebGPU HAL.
|
|
209
|
-
*/
|
|
135
|
+
/** What the pass samples this frame; undefined binds an inert table. */
|
|
210
136
|
readonly texture?: (state: TState, region: TRegion) => MarkTexture | undefined;
|
|
211
137
|
/**
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* frame; `drawRegion`, `paintBlock` and `hitNearest` ask it per block, so a
|
|
215
|
-
* list drawn through `drawMarks` gates like one drawn through a plan — and
|
|
216
|
-
* so does the hit test, which is what stops a switched-off layer answering a
|
|
217
|
-
* hover over blank pixels. A question about the block is `paintsBlock`, on
|
|
218
|
-
* the shape.
|
|
138
|
+
* Whether the mark draws at all under `state`. `planMarks` asks once per
|
|
139
|
+
* frame and every other consumer per block, before any lens.
|
|
219
140
|
*/
|
|
220
141
|
readonly enabled?: (state: TState) => boolean;
|
|
221
|
-
/**
|
|
222
|
-
* The pass a frame plan draws for this mark, or undefined for a mark the plan
|
|
223
|
-
* form cannot carry: one with a `band` (a plan's caller owns the scissor) or a
|
|
224
|
-
* `paintsBlock` (a plan asks the block nothing).
|
|
225
|
-
*/
|
|
142
|
+
/** The pass a frame plan draws; undefined with a `band` or `paintsBlock`. */
|
|
226
143
|
readonly planned?: PlannedPass;
|
|
227
144
|
drawRegion(hal: GpuHal, scratch: ArrayBuffer, block: RenderBlock, clip: BlockClipResult, region: TRegion, state: TState, regionKey: number, staged?: StagedUniforms): void;
|
|
228
145
|
paintBlock(ctx: MarkContext2D, region: TRegion, block: RenderBlock, state: TState): void;
|
|
229
146
|
hitNearest?(region: TRegion, block: RenderBlock, state: TState, xPx: number, yPx: number, candidates: Iterable<number>, maxDistSq: number): MarkHit | undefined;
|
|
230
|
-
/**
|
|
231
|
-
* The box instance `i` painted under the mark's gates, clipped to its band
|
|
232
|
-
* where it has one: what a highlight guide draws for the instance a display
|
|
233
|
-
* names. Undefined off a shape with no `ink`, and for an instance nothing
|
|
234
|
-
* drew.
|
|
235
|
-
*/
|
|
147
|
+
/** The box instance `i` painted under the mark's gates, clipped to a band. */
|
|
236
148
|
ink?(region: TRegion, block: RenderBlock, state: TState, i: number): InkRect | undefined;
|
|
149
|
+
/** The shape's `valueWindow` under the mark's gates, if they open. */
|
|
150
|
+
valueWindow?(region: TRegion, block: RenderBlock, state: TState, yPx: number, radiusPx: number): [number, number] | undefined;
|
|
237
151
|
}
|
|
238
152
|
/**
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
* and a
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
* of the region's arrays feed the shape's lanes, and `params` names which of
|
|
245
|
-
* the render state's values reach the uniforms — and which of the region's,
|
|
246
|
-
* for a value the payload carries rather than the frame (the variant matrix's
|
|
247
|
-
* column count). Both run once per block per frame, never per instance.
|
|
248
|
-
*
|
|
249
|
-
* **`channels` answers `undefined` for a region this mark has nothing in.**
|
|
250
|
-
* That is how one mark list serves a display whose cells are a union — synteny
|
|
251
|
-
* draws ribbon cells, outline cells and glyph-lane cells off one list, and a
|
|
252
|
-
* region key holds exactly one of the three. The mark then packs an empty
|
|
253
|
-
* buffer (which every HAL reads as the release), draws nothing and paints
|
|
254
|
-
* nothing, so the kind test is stated once per mark instead of once per
|
|
255
|
-
* backend.
|
|
256
|
-
*
|
|
257
|
-
* **`params` is handed the block**, because the key a display's per-track
|
|
258
|
-
* state is filed under is the block's `displayedRegionIndex` and nothing else
|
|
259
|
-
* in the lens's reach says which track this is. Every method the lens feeds
|
|
260
|
-
* already receives the block; the lens receiving it is what lets it stay a
|
|
261
|
-
* pick rather than pushing the lookup into the shape.
|
|
262
|
-
*
|
|
263
|
-
* `bufferOf` names the mark whose uploaded buffer this one draws from, so the
|
|
264
|
-
* declaration says what the HAL's `drawPass(id, region, bufferPassId)` says:
|
|
265
|
-
* the chevron mark's channels are the line mark's, packed once under the line
|
|
266
|
-
* pass.
|
|
267
|
-
*
|
|
268
|
-
* `texture` is the colour ramp the mark's pass samples, for a shape that
|
|
269
|
-
* resolves a per-instance scalar through a LUT (wiggle density, and the same
|
|
270
|
-
* 256-entry table HiC and LD bind). It is state, not a channel: the backend
|
|
271
|
-
* uploads it once per pass and re-uploads only when the bytes' identity moves,
|
|
272
|
-
* so a ramp change costs one texture and no instance byte.
|
|
273
|
-
*
|
|
274
|
-
* **Per pass per FRAME, though the lens is handed a region too.** LD reads its
|
|
275
|
-
* ramp off the payload's own metric — a pre-computed file with no D' column
|
|
276
|
-
* downgrades the request, and the metric that arrived is the authoritative one
|
|
277
|
-
* — which is exact because LD holds one region. A display with several regions
|
|
278
|
-
* wanting different ramps for one pass would re-upload the texture per region
|
|
279
|
-
* per frame; correct, since each recorded draw keeps the table bound when it
|
|
280
|
-
* was encoded, but a texture per region per frame is not what this is for.
|
|
281
|
-
*
|
|
282
|
-
* `band` is the strip of the canvas the mark is clipped to, for a display that
|
|
283
|
-
* stacks bands on one canvas (MAF's coverage strip over its rows viewport).
|
|
284
|
-
* The GPU scissors to it and Canvas2D clips to it, a zero-height band draws
|
|
285
|
-
* nothing, and the shape still places Y against the whole canvas — the band is
|
|
286
|
-
* a clip, never an offset, which is what lets one `scrollTop` serve both
|
|
287
|
-
* backends. A mark without one paints wherever its shape puts ink.
|
|
153
|
+
* Binds a shape to a display's payload and render state through two picks run
|
|
154
|
+
* once per block per frame: `channels` names the region's arrays for the
|
|
155
|
+
* shape's lanes, and answers undefined for a region the mark has nothing in,
|
|
156
|
+
* which then packs, draws and paints nothing; `params` names what reaches the
|
|
157
|
+
* uniforms.
|
|
288
158
|
*
|
|
289
|
-
* `
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
159
|
+
* `bufferOf` draws off another mark's uploaded buffer of the same instance
|
|
160
|
+
* struct. `texture` binds per pass per frame and re-uploads when its identity
|
|
161
|
+
* moves, so ramps that differ by region re-upload per region. `band` clips the
|
|
162
|
+
* mark to a strip without offsetting it. `enabled` turns the mark off for a
|
|
163
|
+
* whole frame, for every consumer.
|
|
294
164
|
*/
|
|
295
165
|
export declare function defineMark<TRegion, TState extends MarkFrame, TChannels, TParams>(spec: {
|
|
296
166
|
shape: MarkShape<TChannels, TParams>;
|
package/esm/marks/types.js
CHANGED
|
@@ -1,66 +1,23 @@
|
|
|
1
1
|
import { devicePxBand, withClip } from '../canvas2dUtils.js';
|
|
2
2
|
import { shapeHitNearest } from './markHit.js';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* and a
|
|
4
|
+
* Binds a shape to a display's payload and render state through two picks run
|
|
5
|
+
* once per block per frame: `channels` names the region's arrays for the
|
|
6
|
+
* shape's lanes, and answers undefined for a region the mark has nothing in,
|
|
7
|
+
* which then packs, draws and paints nothing; `params` names what reaches the
|
|
8
|
+
* uniforms.
|
|
7
9
|
*
|
|
8
|
-
* `
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* **`channels` answers `undefined` for a region this mark has nothing in.**
|
|
15
|
-
* That is how one mark list serves a display whose cells are a union — synteny
|
|
16
|
-
* draws ribbon cells, outline cells and glyph-lane cells off one list, and a
|
|
17
|
-
* region key holds exactly one of the three. The mark then packs an empty
|
|
18
|
-
* buffer (which every HAL reads as the release), draws nothing and paints
|
|
19
|
-
* nothing, so the kind test is stated once per mark instead of once per
|
|
20
|
-
* backend.
|
|
21
|
-
*
|
|
22
|
-
* **`params` is handed the block**, because the key a display's per-track
|
|
23
|
-
* state is filed under is the block's `displayedRegionIndex` and nothing else
|
|
24
|
-
* in the lens's reach says which track this is. Every method the lens feeds
|
|
25
|
-
* already receives the block; the lens receiving it is what lets it stay a
|
|
26
|
-
* pick rather than pushing the lookup into the shape.
|
|
27
|
-
*
|
|
28
|
-
* `bufferOf` names the mark whose uploaded buffer this one draws from, so the
|
|
29
|
-
* declaration says what the HAL's `drawPass(id, region, bufferPassId)` says:
|
|
30
|
-
* the chevron mark's channels are the line mark's, packed once under the line
|
|
31
|
-
* pass.
|
|
32
|
-
*
|
|
33
|
-
* `texture` is the colour ramp the mark's pass samples, for a shape that
|
|
34
|
-
* resolves a per-instance scalar through a LUT (wiggle density, and the same
|
|
35
|
-
* 256-entry table HiC and LD bind). It is state, not a channel: the backend
|
|
36
|
-
* uploads it once per pass and re-uploads only when the bytes' identity moves,
|
|
37
|
-
* so a ramp change costs one texture and no instance byte.
|
|
38
|
-
*
|
|
39
|
-
* **Per pass per FRAME, though the lens is handed a region too.** LD reads its
|
|
40
|
-
* ramp off the payload's own metric — a pre-computed file with no D' column
|
|
41
|
-
* downgrades the request, and the metric that arrived is the authoritative one
|
|
42
|
-
* — which is exact because LD holds one region. A display with several regions
|
|
43
|
-
* wanting different ramps for one pass would re-upload the texture per region
|
|
44
|
-
* per frame; correct, since each recorded draw keeps the table bound when it
|
|
45
|
-
* was encoded, but a texture per region per frame is not what this is for.
|
|
46
|
-
*
|
|
47
|
-
* `band` is the strip of the canvas the mark is clipped to, for a display that
|
|
48
|
-
* stacks bands on one canvas (MAF's coverage strip over its rows viewport).
|
|
49
|
-
* The GPU scissors to it and Canvas2D clips to it, a zero-height band draws
|
|
50
|
-
* nothing, and the shape still places Y against the whole canvas — the band is
|
|
51
|
-
* a clip, never an offset, which is what lets one `scrollTop` serve both
|
|
52
|
-
* backends. A mark without one paints wherever its shape puts ink.
|
|
53
|
-
*
|
|
54
|
-
* `enabled` is the setting that turns the mark off for a whole frame — the
|
|
55
|
-
* pileup's "show mismatches" — and it is one gate for the three consumers: a
|
|
56
|
-
* mark that does not draw does not answer a hover either. It reads the state
|
|
57
|
-
* alone so `planMarks` can resolve it once per frame; a gate that needs the
|
|
58
|
-
* block is the shape's `paintsBlock`.
|
|
10
|
+
* `bufferOf` draws off another mark's uploaded buffer of the same instance
|
|
11
|
+
* struct. `texture` binds per pass per frame and re-uploads when its identity
|
|
12
|
+
* moves, so ramps that differ by region re-upload per region. `band` clips the
|
|
13
|
+
* mark to a strip without offsetting it. `enabled` turns the mark off for a
|
|
14
|
+
* whole frame, for every consumer.
|
|
59
15
|
*/
|
|
60
16
|
export function defineMark(spec) {
|
|
61
17
|
const { shape, channels, params, band, texture, enabled } = spec;
|
|
62
18
|
const hitNearest = shapeHitNearest(shape);
|
|
63
19
|
const shapeInk = shape.ink?.bind(shape);
|
|
20
|
+
const shapeValueWindow = shape.valueWindow?.bind(shape);
|
|
64
21
|
const lender = spec.bufferOf?.pass;
|
|
65
22
|
if (lender &&
|
|
66
23
|
(lender.instanceStride !== shape.pass.instanceStride ||
|
|
@@ -70,6 +27,26 @@ export function defineMark(spec) {
|
|
|
70
27
|
}
|
|
71
28
|
const bufferOf = lender?.id;
|
|
72
29
|
const planned = band || shape.paintsBlock ? undefined : { id: shape.pass.id, bufferOf };
|
|
30
|
+
// `channels` gates `params`: a union payload's params lens may only read its
|
|
31
|
+
// own kind of region.
|
|
32
|
+
function resolve(region, block, state) {
|
|
33
|
+
if (enabled && !enabled(state)) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const strip = band?.(state);
|
|
37
|
+
if (strip && !bandIsOpen(strip)) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
const c = channels(region);
|
|
41
|
+
if (c === undefined) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
const p = params(state, region, block);
|
|
45
|
+
if (shape.paintsBlock && !shape.paintsBlock(block, state, p)) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
return { channels: c, params: p, strip };
|
|
49
|
+
}
|
|
73
50
|
return {
|
|
74
51
|
pass: {
|
|
75
52
|
...shape.pass,
|
|
@@ -82,107 +59,92 @@ export function defineMark(spec) {
|
|
|
82
59
|
texture,
|
|
83
60
|
enabled,
|
|
84
61
|
planned,
|
|
62
|
+
// `resolve`'s gates inline: its picks leaving as a record or through
|
|
63
|
+
// closure state measured 0.80-0.86x of these locals on
|
|
64
|
+
// markUniformDedupe.bench.ts.
|
|
85
65
|
drawRegion(hal, scratch, block, clip, region, state, regionKey, staged) {
|
|
86
66
|
if (enabled && !enabled(state)) {
|
|
87
67
|
return;
|
|
88
68
|
}
|
|
89
69
|
const strip = band?.(state);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
70
|
+
if (strip && !bandIsOpen(strip)) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (channels(region) === undefined) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const p = params(state, region, block);
|
|
77
|
+
if (shape.paintsBlock && !shape.paintsBlock(block, state, p)) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const scissor = strip && devicePxBand(strip.top, strip.height, clip.scaleY, clip.pxH);
|
|
81
|
+
if (scissor?.height === 0) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (scissor) {
|
|
85
|
+
hal.setScissor(clip.pxX, scissor.top, clip.pxW, scissor.height);
|
|
86
|
+
}
|
|
87
|
+
if (staged?.writer !== shape.writeUniforms || staged.params !== params) {
|
|
88
|
+
shape.writeUniforms(scratch, clip, block, state, p);
|
|
89
|
+
hal.writeUniforms(scratch);
|
|
90
|
+
if (staged) {
|
|
91
|
+
staged.writer = shape.writeUniforms;
|
|
92
|
+
staged.params = params;
|
|
112
93
|
}
|
|
113
94
|
}
|
|
95
|
+
hal.drawPass(shape.pass.id, regionKey, bufferOf);
|
|
96
|
+
if (scissor) {
|
|
97
|
+
hal.setScissor(clip.pxX, 0, clip.pxW, clip.pxH);
|
|
98
|
+
}
|
|
114
99
|
},
|
|
115
100
|
paintBlock(ctx, region, block, state) {
|
|
116
|
-
|
|
101
|
+
const open = resolve(region, block, state);
|
|
102
|
+
if (!open) {
|
|
117
103
|
return;
|
|
118
104
|
}
|
|
119
|
-
const strip =
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (strip) {
|
|
128
|
-
withClip(ctx, 0, strip.top, state.canvasWidth, strip.height, paint);
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
paint();
|
|
132
|
-
}
|
|
133
|
-
}
|
|
105
|
+
const { strip } = open;
|
|
106
|
+
if (strip) {
|
|
107
|
+
withClip(ctx, 0, strip.top, state.canvasWidth, strip.height, () => {
|
|
108
|
+
shape.paintBlock(ctx, open.channels, block, state, open.params);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
shape.paintBlock(ctx, open.channels, block, state, open.params);
|
|
134
113
|
}
|
|
135
114
|
},
|
|
136
115
|
hitNearest: hitNearest
|
|
137
116
|
? (region, block, state, xPx, yPx, candidates, maxDistSq) => {
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
// The three gates `paintBlock` takes, in its order — the band on its
|
|
142
|
-
// HEIGHT, as there, so a band both backends decline answers nothing
|
|
143
|
-
// either. The cursor being inside the strip is a fourth, and the ink
|
|
144
|
-
// being inside it the fifth: a band is a CLIP, so ink outside one was
|
|
145
|
-
// never drawn and cannot be the nearest thing to anything.
|
|
146
|
-
//
|
|
147
|
-
// The fifth gate REJECTS rather than re-runs, and that is the one
|
|
148
|
-
// conservative edge here: the shape picks its winner without knowing
|
|
149
|
-
// about the band, so a clipped-away nearest masks a farther candidate
|
|
150
|
-
// whose ink IS inside the strip, and the mark answers nothing instead
|
|
151
|
-
// of that one. Safe in the direction that matters — it never claims a
|
|
152
|
-
// hover over pixels neither backend drew — and closing it would mean
|
|
153
|
-
// pushing the band down into every shape's own ink test, where the
|
|
154
|
-
// band is deliberately not.
|
|
155
|
-
if ((!enabled || enabled(state)) &&
|
|
156
|
-
(!strip || strip.height > 0) &&
|
|
157
|
-
c !== undefined &&
|
|
158
|
-
!bandExcludes(strip, yPx)) {
|
|
159
|
-
const p = params(state, region, block);
|
|
160
|
-
if (!shape.paintsBlock || shape.paintsBlock(block, state, p)) {
|
|
161
|
-
hit = hitNearest(c, block, state, p, xPx, yPx, candidates, maxDistSq);
|
|
162
|
-
}
|
|
117
|
+
const open = resolve(region, block, state);
|
|
118
|
+
if (!open || bandExcludes(open.strip, yPx)) {
|
|
119
|
+
return undefined;
|
|
163
120
|
}
|
|
164
|
-
|
|
121
|
+
const hit = hitNearest(open.channels, block, state, open.params, xPx, yPx, candidates, maxDistSq);
|
|
122
|
+
// Rejecting a nearest hit the band clipped away, rather than asking
|
|
123
|
+
// again, can mask a farther candidate inside the strip.
|
|
124
|
+
return hit && !bandExcludes(open.strip, hit.y) ? hit : undefined;
|
|
165
125
|
}
|
|
166
126
|
: undefined,
|
|
167
127
|
ink: shapeInk
|
|
168
128
|
? (region, block, state, i) => {
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
if ((enabled && !enabled(state)) ||
|
|
172
|
-
(strip && strip.height <= 0) ||
|
|
173
|
-
c === undefined) {
|
|
174
|
-
return undefined;
|
|
175
|
-
}
|
|
176
|
-
const p = params(state, region, block);
|
|
177
|
-
if (shape.paintsBlock && !shape.paintsBlock(block, state, p)) {
|
|
129
|
+
const open = resolve(region, block, state);
|
|
130
|
+
if (!open) {
|
|
178
131
|
return undefined;
|
|
179
132
|
}
|
|
180
|
-
const r = shapeInk(
|
|
181
|
-
return r && strip ? clipToBand(r, strip) : r;
|
|
133
|
+
const r = shapeInk(open.channels, block, state, open.params, i);
|
|
134
|
+
return r && open.strip ? clipToBand(r, open.strip) : r;
|
|
135
|
+
}
|
|
136
|
+
: undefined,
|
|
137
|
+
valueWindow: shapeValueWindow
|
|
138
|
+
? (region, block, state, yPx, radiusPx) => {
|
|
139
|
+
const open = resolve(region, block, state);
|
|
140
|
+
return open && shapeValueWindow(yPx, radiusPx, state, open.params);
|
|
182
141
|
}
|
|
183
142
|
: undefined,
|
|
184
143
|
};
|
|
185
144
|
}
|
|
145
|
+
function bandIsOpen(strip) {
|
|
146
|
+
return strip.height > 0 && Number.isFinite(strip.top + strip.height);
|
|
147
|
+
}
|
|
186
148
|
function clipToBand(r, strip) {
|
|
187
149
|
const top = Math.max(r.top, strip.top);
|
|
188
150
|
const bottom = Math.min(r.top + r.height, strip.top + strip.height);
|
|
@@ -190,8 +152,6 @@ function clipToBand(r, strip) {
|
|
|
190
152
|
? { left: r.left, top, width: r.width, height: bottom - top }
|
|
191
153
|
: undefined;
|
|
192
154
|
}
|
|
193
|
-
// What a mark with nothing in this region packs. An empty pack IS the release
|
|
194
|
-
// — every HAL deletes the pass's prior buffer before it looks at the count.
|
|
195
155
|
const NOTHING = new ArrayBuffer(0);
|
|
196
156
|
function bandExcludes(strip, yPx) {
|
|
197
157
|
return (strip !== undefined && (yPx < strip.top || yPx > strip.top + strip.height));
|
package/esm/namedReactions.d.ts
CHANGED
|
@@ -12,10 +12,9 @@ export declare function recordNamedReaction(self: object, name: string, disposer
|
|
|
12
12
|
*
|
|
13
13
|
* The two halves belong together: `reactionDependencies` can only see a
|
|
14
14
|
* reaction that was recorded, so an installer that calls `autorun` and
|
|
15
|
-
* `addDisposer` directly
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* so there is no second spelling to forget.
|
|
15
|
+
* `addDisposer` directly leaves its dependency set out of the tests that are
|
|
16
|
+
* supposed to pin it, and those tests still pass. Every installer goes through
|
|
17
|
+
* here so there is no second spelling to forget.
|
|
19
18
|
*/
|
|
20
19
|
export declare function namedAutorun(self: IAnyStateTreeNode, body: () => void, options: IAutorunOptions & {
|
|
21
20
|
name: string;
|
package/esm/namedReactions.js
CHANGED
|
@@ -23,10 +23,9 @@ export function recordNamedReaction(self, name, disposer) {
|
|
|
23
23
|
*
|
|
24
24
|
* The two halves belong together: `reactionDependencies` can only see a
|
|
25
25
|
* reaction that was recorded, so an installer that calls `autorun` and
|
|
26
|
-
* `addDisposer` directly
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* so there is no second spelling to forget.
|
|
26
|
+
* `addDisposer` directly leaves its dependency set out of the tests that are
|
|
27
|
+
* supposed to pin it, and those tests still pass. Every installer goes through
|
|
28
|
+
* here so there is no second spelling to forget.
|
|
30
29
|
*/
|
|
31
30
|
export function namedAutorun(self, body, options) {
|
|
32
31
|
const disposer = autorun(body, options);
|