@jbrowse/wiggle-core 4.3.0
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/LICENSE +201 -0
- package/README.md +292 -0
- package/esm/CrossHatches.d.ts +11 -0
- package/esm/CrossHatches.js +32 -0
- package/esm/ResolutionStepper.d.ts +17 -0
- package/esm/ResolutionStepper.js +56 -0
- package/esm/ScoreRules.d.ts +11 -0
- package/esm/ScoreRules.js +37 -0
- package/esm/ScoreScaleMixin.d.ts +105 -0
- package/esm/ScoreScaleMixin.js +136 -0
- package/esm/SetMinMaxDialog.d.ts +11 -0
- package/esm/SetMinMaxDialog.js +18 -0
- package/esm/YScaleBar.d.ts +6 -0
- package/esm/YScaleBar.js +46 -0
- package/esm/YScaleBarOverlay.d.ts +6 -0
- package/esm/YScaleBarOverlay.js +24 -0
- package/esm/autoscale.d.ts +55 -0
- package/esm/autoscale.js +252 -0
- package/esm/computeYTicks.d.ts +15 -0
- package/esm/computeYTicks.js +44 -0
- package/esm/constants.d.ts +2 -0
- package/esm/constants.js +10 -0
- package/esm/dataTypes.d.ts +26 -0
- package/esm/dataTypes.js +1 -0
- package/esm/displayModel.d.ts +24 -0
- package/esm/displayModel.js +1 -0
- package/esm/formatNumber.d.ts +1 -0
- package/esm/formatNumber.js +6 -0
- package/esm/gapBreak.d.ts +59 -0
- package/esm/gapBreak.js +58 -0
- package/esm/index.d.ts +36 -0
- package/esm/index.js +26 -0
- package/esm/normalize.d.ts +40 -0
- package/esm/normalize.js +106 -0
- package/esm/pointMarker.d.ts +3 -0
- package/esm/pointMarker.js +27 -0
- package/esm/pointSizeMenu.d.ts +13 -0
- package/esm/pointSizeMenu.js +21 -0
- package/esm/renderingBackendTypes.d.ts +39 -0
- package/esm/renderingBackendTypes.js +29 -0
- package/esm/resolveRenderState.d.ts +1 -0
- package/esm/resolveRenderState.js +20 -0
- package/esm/scale.d.ts +60 -0
- package/esm/scale.js +161 -0
- package/esm/scoreAxisConfigSchemaFields.d.ts +44 -0
- package/esm/scoreAxisConfigSchemaFields.js +54 -0
- package/esm/scoreMenuItems.d.ts +37 -0
- package/esm/scoreMenuItems.js +104 -0
- package/esm/scoreRuleMarks.d.ts +73 -0
- package/esm/scoreRuleMarks.js +98 -0
- package/esm/symlogTicks.d.ts +17 -0
- package/esm/symlogTicks.js +92 -0
- package/esm/transferables.d.ts +2 -0
- package/esm/transferables.js +32 -0
- package/esm/vendor/d3-scale.d.ts +33 -0
- package/esm/vendor/d3-scale.js +249 -0
- package/esm/visibleStatsDomain.d.ts +59 -0
- package/esm/visibleStatsDomain.js +33 -0
- package/esm/wiggleRenderModes.generated.d.ts +9 -0
- package/esm/wiggleRenderModes.generated.js +11 -0
- package/esm/yScaleTicks.d.ts +59 -0
- package/esm/yScaleTicks.js +59 -0
- package/package.json +54 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { YScaleTicks } from './yScaleTicks.ts';
|
|
2
|
+
export declare function computeYTicks(opts: {
|
|
3
|
+
height: number;
|
|
4
|
+
domain: [number, number] | number[] | undefined;
|
|
5
|
+
scaleType: string;
|
|
6
|
+
minimalTicks: boolean;
|
|
7
|
+
offset?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Raw config value for symlog; `0` means "derive from the domain". Passed
|
|
10
|
+
* through to `getScale` so the axis is built with the constant the renderer
|
|
11
|
+
* normalizes with — resolving them separately would label ticks at heights
|
|
12
|
+
* the bars are not drawn at.
|
|
13
|
+
*/
|
|
14
|
+
symlogConstant?: number;
|
|
15
|
+
}): YScaleTicks | undefined;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { YSCALEBAR_LABEL_OFFSET } from './constants.js';
|
|
2
|
+
import { getScale } from './scale.js';
|
|
3
|
+
import { axisPlotBox } from './yScaleTicks.js';
|
|
4
|
+
// Builds Y-axis tick positions for a wiggle-family display: tick values come
|
|
5
|
+
// from d3's scale.ticks(4) at normal heights, or fall back to the domain
|
|
6
|
+
// endpoints for short tracks / when the user opts into minimal ticks.
|
|
7
|
+
export function computeYTicks(opts) {
|
|
8
|
+
const { height, domain, scaleType, minimalTicks, offset = YSCALEBAR_LABEL_OFFSET, symlogConstant, } = opts;
|
|
9
|
+
const domainMin = domain?.[0];
|
|
10
|
+
const domainMax = domain?.[1];
|
|
11
|
+
if (domainMin === undefined || domainMax === undefined) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
// The same box the renderer paints into, so a tick lands on its own data. It
|
|
15
|
+
// used to end a pixel short of it, to keep the spine's 1px stroke inside
|
|
16
|
+
// multi-wiggle's row — that belongs in the drawing, and is now
|
|
17
|
+
// `clampStrokeInsideAxis`.
|
|
18
|
+
const { yTop, yBottom } = axisPlotBox(height, offset);
|
|
19
|
+
// A window whose scores are all one value (an all-zero coverage stretch) has
|
|
20
|
+
// a domain of no width. d3 answers the midpoint for every input there while
|
|
21
|
+
// `makeScoreNormalizer` answers 0, and the renderer is the one to agree with.
|
|
22
|
+
if (domainMin === domainMax) {
|
|
23
|
+
return { items: [{ value: domainMin, y: yBottom }], yTop, yBottom };
|
|
24
|
+
}
|
|
25
|
+
// `nice: false`: the caller's domain is the one the renderer is drawing with
|
|
26
|
+
// (every wiggle-family `domain` getter has already been through
|
|
27
|
+
// getNiceDomain), so nicing it a second time would move the axis off the
|
|
28
|
+
// plot. It was a no-op only because every caller pre-nices — hand this a raw
|
|
29
|
+
// [1, 1000] and it re-niced to [1, 1024], drawing a labelled tick at a value
|
|
30
|
+
// the data never reaches.
|
|
31
|
+
const scale = getScale({
|
|
32
|
+
scaleType,
|
|
33
|
+
domain: [domainMin, domainMax],
|
|
34
|
+
range: [yBottom, yTop],
|
|
35
|
+
symlogConstant,
|
|
36
|
+
nice: false,
|
|
37
|
+
});
|
|
38
|
+
const values = height < 100 || minimalTicks ? [domainMin, domainMax] : scale.ticks(4);
|
|
39
|
+
return {
|
|
40
|
+
items: values.map(v => ({ value: v, y: scale(v) })),
|
|
41
|
+
yTop,
|
|
42
|
+
yBottom,
|
|
43
|
+
};
|
|
44
|
+
}
|
package/esm/constants.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Vertical inset the single-wiggle family reserves above and below the plot so
|
|
2
|
+
// the top/bottom y-axis labels aren't clipped. Lives in its own module because
|
|
3
|
+
// both index.ts (which re-exports computeYTicks) and computeYTicks.ts need it —
|
|
4
|
+
// importing it from index.ts would close a cycle.
|
|
5
|
+
export const YSCALEBAR_LABEL_OFFSET = 5;
|
|
6
|
+
// Left indent of the on-screen y-axis overlay (single-wiggle / manhattan) and of
|
|
7
|
+
// multi-wiggle's per-row scalebars. The axis is right-oriented on screen, so its
|
|
8
|
+
// labels grow into the plot from here; the indent keeps them clear of the track
|
|
9
|
+
// label. Export uses its own anchor (the content left edge).
|
|
10
|
+
export const ONSCREEN_AXIS_LEFT_PX = 50;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface SourceInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
color?: string;
|
|
4
|
+
labelColor?: string;
|
|
5
|
+
label?: string;
|
|
6
|
+
group?: string;
|
|
7
|
+
baseUri?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface WiggleFeatureArrays {
|
|
10
|
+
featurePositions: Uint32Array;
|
|
11
|
+
featureScores: Float32Array;
|
|
12
|
+
featureMinScores: Float32Array;
|
|
13
|
+
featureMaxScores: Float32Array;
|
|
14
|
+
numFeatures: number;
|
|
15
|
+
hasSummaryScores: boolean;
|
|
16
|
+
posFeaturePositions: Uint32Array;
|
|
17
|
+
posFeatureScores: Float32Array;
|
|
18
|
+
posNumFeatures: number;
|
|
19
|
+
negFeaturePositions: Uint32Array;
|
|
20
|
+
negFeatureScores: Float32Array;
|
|
21
|
+
negNumFeatures: number;
|
|
22
|
+
}
|
|
23
|
+
export type WiggleSourceData = SourceInfo & WiggleFeatureArrays;
|
|
24
|
+
export interface WiggleDataResult {
|
|
25
|
+
sources: WiggleSourceData[];
|
|
26
|
+
}
|
package/esm/dataTypes.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { WiggleDataResult } from './dataTypes.ts';
|
|
2
|
+
import type { WiggleRenderingBackend } from './renderingBackendTypes.ts';
|
|
3
|
+
import type { YScaleTicks } from './yScaleTicks.ts';
|
|
4
|
+
import type { RegionHost } from '@jbrowse/display-kit/regionHost';
|
|
5
|
+
import type { DisplayPhase } from '@jbrowse/render-core/displayPhase';
|
|
6
|
+
import type { RenderLifecycleModel } from '@jbrowse/render-core/useRenderingBackend';
|
|
7
|
+
export interface WiggleGpuDisplayModel<TRenderingBackend = WiggleRenderingBackend, TData = WiggleDataResult> extends RenderLifecycleModel<TRenderingBackend> {
|
|
8
|
+
rpcDataMap: ReadonlyMap<number, TData>;
|
|
9
|
+
host: RegionHost;
|
|
10
|
+
ticks?: YScaleTicks;
|
|
11
|
+
canvasDrawn: boolean;
|
|
12
|
+
painted: boolean;
|
|
13
|
+
height: number;
|
|
14
|
+
canvasWidthPx: number;
|
|
15
|
+
error: unknown;
|
|
16
|
+
displayPhase: DisplayPhase;
|
|
17
|
+
statusMessage?: string;
|
|
18
|
+
reload: () => void;
|
|
19
|
+
regionTooLarge: boolean;
|
|
20
|
+
regionTooLargeReason: string;
|
|
21
|
+
zoomCanReleaseGate: boolean;
|
|
22
|
+
forceLoad: () => void;
|
|
23
|
+
showCrossHatches: boolean;
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function toP(s?: number, precision?: number): number;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Format a number to `precision` significant figures, coercing the result back
|
|
2
|
+
// to a number so trailing zeros are dropped (5.000 -> 5, 0.8500 -> 0.85).
|
|
3
|
+
// Shared by the wiggle and GWAS tooltips so score/r² formatting stays uniform.
|
|
4
|
+
export function toP(s = 0, precision = 6) {
|
|
5
|
+
return +s.toPrecision(precision);
|
|
6
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #api
|
|
3
|
+
* How far apart two consecutive points of an interpolated (point-to-point) line
|
|
4
|
+
* may be before the span between them counts as a hole rather than a segment to
|
|
5
|
+
* draw. Returns `Infinity` when there is nothing to decide, so a caller can
|
|
6
|
+
* always compare against it unguarded.
|
|
7
|
+
*
|
|
8
|
+
* The limit is a multiple of the series' own *mean* spacing rather than an
|
|
9
|
+
* absolute distance, which is what makes one number work across zoom levels and
|
|
10
|
+
* data types: reduced BigWig bins get wider as you zoom out, so any fixed bp
|
|
11
|
+
* threshold would be either useless at one end or destructive at the other. It
|
|
12
|
+
* also lets the same rule serve a bp axis (wiggle's linecenter) and a px one —
|
|
13
|
+
* the caller picks the space, this only cares that the units are consistent.
|
|
14
|
+
*
|
|
15
|
+
* Mean, not median: it is O(1) from the endpoints, and it errs the safe way. A
|
|
16
|
+
* series' holes inflate their own mean, which raises the limit and so breaks
|
|
17
|
+
* *less* — a line that stays connected is the status quo, whereas a spuriously
|
|
18
|
+
* broken one destroys data the user can see nowhere else. Sorting for a true
|
|
19
|
+
* median would cost O(n log n) per source per region on the encode path, for a
|
|
20
|
+
* threshold this coarse.
|
|
21
|
+
*
|
|
22
|
+
* `count < 3` returns Infinity: two points have no "typical" spacing to be
|
|
23
|
+
* unusual against, so there is nothing to call a hole.
|
|
24
|
+
*/
|
|
25
|
+
export declare function gapBreakLimit({ first, last, count, multiple, }: {
|
|
26
|
+
/** position of the first point, in whatever space the caller measures gaps */
|
|
27
|
+
first: number;
|
|
28
|
+
/** position of the last point, same space */
|
|
29
|
+
last: number;
|
|
30
|
+
/** number of points in the series */
|
|
31
|
+
count: number;
|
|
32
|
+
/** how many mean spacings a gap must exceed; <= 0 disables breaking */
|
|
33
|
+
multiple: number;
|
|
34
|
+
}): number;
|
|
35
|
+
/**
|
|
36
|
+
* #api
|
|
37
|
+
* Default `multiple` for the wiggle interpolated line — the `maxGapMultiple`
|
|
38
|
+
* config slot's default. 0 means the line never breaks: one connected polyline
|
|
39
|
+
* across every hole, which is how the interpolated line behaved before gap
|
|
40
|
+
* breaking existed.
|
|
41
|
+
*
|
|
42
|
+
* OFF BY DEFAULT, deliberately and after having been on. It shipped at 20 and
|
|
43
|
+
* the calibration behind that number still holds — a hole worth breaking on runs
|
|
44
|
+
* orders of magnitude past the mean, and bbi's reduced zoom levels emit
|
|
45
|
+
* fixed-width bins so the series tiles (measured on volvox_microarray.bw at
|
|
46
|
+
* three zooms: 500 bins, every gap exactly 1.0x the mean, no break at any
|
|
47
|
+
* threshold). What changed is the call about whether a reader wants the break at
|
|
48
|
+
* all: "we added this feature awhile back but i dont think i like it now. might
|
|
49
|
+
* consider going back to not skipping". A broken line reads as missing data
|
|
50
|
+
* whether or not
|
|
51
|
+
* data is missing there, and the chord across a hole is at least continuous with
|
|
52
|
+
* what the neighbouring points say.
|
|
53
|
+
*
|
|
54
|
+
* The mechanism stays, whole, because it is the only way to get the other
|
|
55
|
+
* behavior back: set `maxGapMultiple` on the track, 20 being the calibrated
|
|
56
|
+
* value. Nothing about `gapBreakLimit` itself changes — a caller passing a
|
|
57
|
+
* positive multiple gets exactly what it always got.
|
|
58
|
+
*/
|
|
59
|
+
export declare const DEFAULT_GAP_BREAK_MULTIPLE = 0;
|
package/esm/gapBreak.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #api
|
|
3
|
+
* How far apart two consecutive points of an interpolated (point-to-point) line
|
|
4
|
+
* may be before the span between them counts as a hole rather than a segment to
|
|
5
|
+
* draw. Returns `Infinity` when there is nothing to decide, so a caller can
|
|
6
|
+
* always compare against it unguarded.
|
|
7
|
+
*
|
|
8
|
+
* The limit is a multiple of the series' own *mean* spacing rather than an
|
|
9
|
+
* absolute distance, which is what makes one number work across zoom levels and
|
|
10
|
+
* data types: reduced BigWig bins get wider as you zoom out, so any fixed bp
|
|
11
|
+
* threshold would be either useless at one end or destructive at the other. It
|
|
12
|
+
* also lets the same rule serve a bp axis (wiggle's linecenter) and a px one —
|
|
13
|
+
* the caller picks the space, this only cares that the units are consistent.
|
|
14
|
+
*
|
|
15
|
+
* Mean, not median: it is O(1) from the endpoints, and it errs the safe way. A
|
|
16
|
+
* series' holes inflate their own mean, which raises the limit and so breaks
|
|
17
|
+
* *less* — a line that stays connected is the status quo, whereas a spuriously
|
|
18
|
+
* broken one destroys data the user can see nowhere else. Sorting for a true
|
|
19
|
+
* median would cost O(n log n) per source per region on the encode path, for a
|
|
20
|
+
* threshold this coarse.
|
|
21
|
+
*
|
|
22
|
+
* `count < 3` returns Infinity: two points have no "typical" spacing to be
|
|
23
|
+
* unusual against, so there is nothing to call a hole.
|
|
24
|
+
*/
|
|
25
|
+
export function gapBreakLimit({ first, last, count, multiple, }) {
|
|
26
|
+
if (multiple <= 0 || count < 3) {
|
|
27
|
+
return Number.POSITIVE_INFINITY;
|
|
28
|
+
}
|
|
29
|
+
const meanSpacing = (last - first) / (count - 1);
|
|
30
|
+
// A degenerate series (every point at the same position) has no spacing to
|
|
31
|
+
// scale by, and a limit of 0 would break every segment.
|
|
32
|
+
return meanSpacing > 0 ? meanSpacing * multiple : Number.POSITIVE_INFINITY;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* #api
|
|
36
|
+
* Default `multiple` for the wiggle interpolated line — the `maxGapMultiple`
|
|
37
|
+
* config slot's default. 0 means the line never breaks: one connected polyline
|
|
38
|
+
* across every hole, which is how the interpolated line behaved before gap
|
|
39
|
+
* breaking existed.
|
|
40
|
+
*
|
|
41
|
+
* OFF BY DEFAULT, deliberately and after having been on. It shipped at 20 and
|
|
42
|
+
* the calibration behind that number still holds — a hole worth breaking on runs
|
|
43
|
+
* orders of magnitude past the mean, and bbi's reduced zoom levels emit
|
|
44
|
+
* fixed-width bins so the series tiles (measured on volvox_microarray.bw at
|
|
45
|
+
* three zooms: 500 bins, every gap exactly 1.0x the mean, no break at any
|
|
46
|
+
* threshold). What changed is the call about whether a reader wants the break at
|
|
47
|
+
* all: "we added this feature awhile back but i dont think i like it now. might
|
|
48
|
+
* consider going back to not skipping". A broken line reads as missing data
|
|
49
|
+
* whether or not
|
|
50
|
+
* data is missing there, and the chord across a hole is at least continuous with
|
|
51
|
+
* what the neighbouring points say.
|
|
52
|
+
*
|
|
53
|
+
* The mechanism stays, whole, because it is the only way to get the other
|
|
54
|
+
* behavior back: set `maxGapMultiple` on the track, 20 being the calibrated
|
|
55
|
+
* value. Nothing about `gapBreakLimit` itself changes — a caller passing a
|
|
56
|
+
* positive multiple gets exactly what it always got.
|
|
57
|
+
*/
|
|
58
|
+
export const DEFAULT_GAP_BREAK_MULTIPLE = 0;
|
package/esm/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export { scoreAxisConfigSchemaFields } from './scoreAxisConfigSchemaFields.ts';
|
|
2
|
+
export type { ScoreAxisConfigModel } from './scoreAxisConfigSchemaFields.ts';
|
|
3
|
+
export { getNiceDomain, getNiceScale, getScale } from './scale.ts';
|
|
4
|
+
export type { ScaleOpts } from './scale.ts';
|
|
5
|
+
export { toP } from './formatNumber.ts';
|
|
6
|
+
export { SCALE_TYPE_LINEAR, SCALE_TYPE_LOG, SCALE_TYPE_SYMLOG, makeScoreNormalizer, resolveSymlogConstant, scaleTypeFromString, } from './normalize.ts';
|
|
7
|
+
export type { WiggleScaleType } from './normalize.ts';
|
|
8
|
+
export { autoscaleDomainFromStats, computeAutoscaleDomain, computeScoreStats, domainFromStats, getEffectiveScores, } from './autoscale.ts';
|
|
9
|
+
export type { Dataset, FeatureArrays, ScoreStats } from './autoscale.ts';
|
|
10
|
+
export { ONSCREEN_AXIS_LEFT_PX, YSCALEBAR_LABEL_OFFSET } from './constants.ts';
|
|
11
|
+
export { computeYTicks } from './computeYTicks.ts';
|
|
12
|
+
export { resolveRenderState } from './resolveRenderState.ts';
|
|
13
|
+
export { visibleStatsDomain } from './visibleStatsDomain.ts';
|
|
14
|
+
export type { SettledBlocksView, VisibleEntry, VisibleStatsDomainSpec, } from './visibleStatsDomain.ts';
|
|
15
|
+
export { AXIS_GUTTER_WIDTH_PX, axisPlotBox, clampStrokeInsideAxis, leftAxisSpineX, scoreToAxisY, } from './yScaleTicks.ts';
|
|
16
|
+
export type { YScaleTicks } from './yScaleTicks.ts';
|
|
17
|
+
export { CrossHatchLines, default as CrossHatches } from './CrossHatches.tsx';
|
|
18
|
+
export { ScoreRuleLines, default as ScoreRules } from './ScoreRules.tsx';
|
|
19
|
+
export { parseScoreRules, scoreRuleMarks, widenRangeToRules, } from './scoreRuleMarks.ts';
|
|
20
|
+
export type { ScoreRule, ScoreRuleMark } from './scoreRuleMarks.ts';
|
|
21
|
+
export { makeResolutionSubMenuItem } from './ResolutionStepper.tsx';
|
|
22
|
+
export { makeScatterPointSizeMenuItem } from './pointSizeMenu.tsx';
|
|
23
|
+
export { SMALL_POINT_MAX_DIAMETER_PX, appendPointMarker, } from './pointMarker.ts';
|
|
24
|
+
export { default as SetMinMaxDialog } from './SetMinMaxDialog.tsx';
|
|
25
|
+
export { DEFAULT_AUTOSCALE_OPTIONS } from './autoscale.ts';
|
|
26
|
+
export { makeAutoscaleTypeSubMenu, makeCrossHatchItem, makeScaleTypeSubMenu, makeScoreSubMenu, makeSetMinMaxScoreItem, } from './scoreMenuItems.ts';
|
|
27
|
+
export type { ScoreScaleModel } from './scoreMenuItems.ts';
|
|
28
|
+
export { ScoreScaleMixin } from './ScoreScaleMixin.ts';
|
|
29
|
+
export { default as YScaleBar } from './YScaleBar.tsx';
|
|
30
|
+
export { default as YScaleBarOverlay } from './YScaleBarOverlay.tsx';
|
|
31
|
+
export { MIN_FILL_WIDTH_PX, NO_PREV_START, RENDERING_TYPE_DENSITY, RENDERING_TYPE_LINE, RENDERING_TYPE_LINE_CENTER, RENDERING_TYPE_SCATTER, RENDERING_TYPE_XYPLOT, } from './renderingBackendTypes.ts';
|
|
32
|
+
export type { SourceRenderData, WiggleGPURenderState, WiggleRenderingBackend, WiggleRenderingType, } from './renderingBackendTypes.ts';
|
|
33
|
+
export type { SourceInfo, WiggleDataResult, WiggleFeatureArrays, WiggleSourceData, } from './dataTypes.ts';
|
|
34
|
+
export { type WiggleGpuDisplayModel } from './displayModel.ts';
|
|
35
|
+
export { collectWiggleTransferables } from './transferables.ts';
|
|
36
|
+
export { DEFAULT_GAP_BREAK_MULTIPLE, gapBreakLimit } from './gapBreak.ts';
|
package/esm/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export { scoreAxisConfigSchemaFields } from './scoreAxisConfigSchemaFields.js';
|
|
2
|
+
export { getNiceDomain, getNiceScale, getScale } from './scale.js';
|
|
3
|
+
export { toP } from './formatNumber.js';
|
|
4
|
+
export { SCALE_TYPE_LINEAR, SCALE_TYPE_LOG, SCALE_TYPE_SYMLOG, makeScoreNormalizer, resolveSymlogConstant, scaleTypeFromString, } from './normalize.js';
|
|
5
|
+
export { autoscaleDomainFromStats, computeAutoscaleDomain, computeScoreStats, domainFromStats, getEffectiveScores, } from './autoscale.js';
|
|
6
|
+
export { ONSCREEN_AXIS_LEFT_PX, YSCALEBAR_LABEL_OFFSET } from './constants.js';
|
|
7
|
+
export { computeYTicks } from './computeYTicks.js';
|
|
8
|
+
export { resolveRenderState } from './resolveRenderState.js';
|
|
9
|
+
export { visibleStatsDomain } from './visibleStatsDomain.js';
|
|
10
|
+
export { AXIS_GUTTER_WIDTH_PX, axisPlotBox, clampStrokeInsideAxis, leftAxisSpineX, scoreToAxisY, } from './yScaleTicks.js';
|
|
11
|
+
export { CrossHatchLines, default as CrossHatches } from './CrossHatches.js';
|
|
12
|
+
export { ScoreRuleLines, default as ScoreRules } from './ScoreRules.js';
|
|
13
|
+
export { parseScoreRules, scoreRuleMarks, widenRangeToRules, } from './scoreRuleMarks.js';
|
|
14
|
+
export { makeResolutionSubMenuItem } from './ResolutionStepper.js';
|
|
15
|
+
export { makeScatterPointSizeMenuItem } from './pointSizeMenu.js';
|
|
16
|
+
export { SMALL_POINT_MAX_DIAMETER_PX, appendPointMarker, } from './pointMarker.js';
|
|
17
|
+
export { default as SetMinMaxDialog } from './SetMinMaxDialog.js';
|
|
18
|
+
export { DEFAULT_AUTOSCALE_OPTIONS } from './autoscale.js';
|
|
19
|
+
export { makeAutoscaleTypeSubMenu, makeCrossHatchItem, makeScaleTypeSubMenu, makeScoreSubMenu, makeSetMinMaxScoreItem, } from './scoreMenuItems.js';
|
|
20
|
+
export { ScoreScaleMixin } from './ScoreScaleMixin.js';
|
|
21
|
+
export { default as YScaleBar } from './YScaleBar.js';
|
|
22
|
+
export { default as YScaleBarOverlay } from './YScaleBarOverlay.js';
|
|
23
|
+
export { MIN_FILL_WIDTH_PX, NO_PREV_START, RENDERING_TYPE_DENSITY, RENDERING_TYPE_LINE, RENDERING_TYPE_LINE_CENTER, RENDERING_TYPE_SCATTER, RENDERING_TYPE_XYPLOT, } from './renderingBackendTypes.js';
|
|
24
|
+
export {} from './displayModel.js';
|
|
25
|
+
export { collectWiggleTransferables } from './transferables.js';
|
|
26
|
+
export { DEFAULT_GAP_BREAK_MULTIPLE, gapBreakLimit } from './gapBreak.js';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type WiggleScaleType = 0 | 1 | 2;
|
|
2
|
+
export declare const SCALE_TYPE_LINEAR: WiggleScaleType;
|
|
3
|
+
export declare const SCALE_TYPE_LOG: WiggleScaleType;
|
|
4
|
+
export declare const SCALE_TYPE_SYMLOG: WiggleScaleType;
|
|
5
|
+
/**
|
|
6
|
+
* #api
|
|
7
|
+
* Maps the `'log'`/`'symlog'`/`'linear'` string to the numeric
|
|
8
|
+
* `WiggleScaleType`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function scaleTypeFromString(scaleType: string): WiggleScaleType;
|
|
11
|
+
/**
|
|
12
|
+
* #api
|
|
13
|
+
* The symlog constant actually used for a domain. `0` (the config default)
|
|
14
|
+
* means "pick one from the domain": a thousandth of its largest magnitude, so
|
|
15
|
+
* the log-ish part of the curve covers the top three decades of whatever the
|
|
16
|
+
* track holds and the linear knee sits below the data rather than through it.
|
|
17
|
+
*
|
|
18
|
+
* The alternative — d3's default of 1 — is `log(x + 1)`, which is fine for read
|
|
19
|
+
* depth and useless for anything living below 1, because the entire domain then
|
|
20
|
+
* falls in the linear part of the curve. A p-value track configured that way is
|
|
21
|
+
* just a linear track wearing a log label, which is the reason this is resolved
|
|
22
|
+
* rather than hard-coded.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveSymlogConstant(min: number, max: number, configured: number): number;
|
|
25
|
+
/**
|
|
26
|
+
* #api
|
|
27
|
+
* Returns a loop-hoistable function normalizing a score to [0,1].
|
|
28
|
+
*
|
|
29
|
+
* `symlogConstant` is only read for `SCALE_TYPE_SYMLOG`, and is expected to be
|
|
30
|
+
* already resolved by {@link resolveSymlogConstant} — the shader gets the same
|
|
31
|
+
* resolved number as a uniform, so the "auto" rule lives on this side only and
|
|
32
|
+
* the two backends compare like for like.
|
|
33
|
+
*
|
|
34
|
+
* A range that is zero **or negative** normalizes everything to 0, which is the
|
|
35
|
+
* rule `scoreScale.slang` spells as `range <= 0.0`. Testing `range === 0` here
|
|
36
|
+
* instead let a descending domain through to a negative `1 / range`, so every
|
|
37
|
+
* score saturated to 1 on this side while the shader flattened them all to 0 —
|
|
38
|
+
* one view, opposite plots. `normalizeScoreParity.test.ts` sweeps the two.
|
|
39
|
+
*/
|
|
40
|
+
export declare function makeScoreNormalizer(min: number, max: number, scaleType: WiggleScaleType, symlogConstant?: number): (score: number) => number;
|
package/esm/normalize.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { SCALE_TYPE_LOG as GENERATED_SCALE_TYPE_LOG, SCALE_TYPE_SYMLOG as GENERATED_SCALE_TYPE_SYMLOG, } from './wiggleRenderModes.generated.js';
|
|
2
|
+
export const SCALE_TYPE_LINEAR = 0;
|
|
3
|
+
// The values the `scaleType` uniform is compared against, so they are
|
|
4
|
+
// wiggle.slang's own — generated in by `pnpm gen:shaders` (adr-051). LINEAR is
|
|
5
|
+
// simply "neither of these".
|
|
6
|
+
export const SCALE_TYPE_LOG = GENERATED_SCALE_TYPE_LOG;
|
|
7
|
+
export const SCALE_TYPE_SYMLOG = GENERATED_SCALE_TYPE_SYMLOG;
|
|
8
|
+
/**
|
|
9
|
+
* #api
|
|
10
|
+
* Maps the `'log'`/`'symlog'`/`'linear'` string to the numeric
|
|
11
|
+
* `WiggleScaleType`.
|
|
12
|
+
*/
|
|
13
|
+
export function scaleTypeFromString(scaleType) {
|
|
14
|
+
if (scaleType === 'log') {
|
|
15
|
+
return SCALE_TYPE_LOG;
|
|
16
|
+
}
|
|
17
|
+
if (scaleType === 'symlog') {
|
|
18
|
+
return SCALE_TYPE_SYMLOG;
|
|
19
|
+
}
|
|
20
|
+
return SCALE_TYPE_LINEAR;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* #api
|
|
24
|
+
* The symlog constant actually used for a domain. `0` (the config default)
|
|
25
|
+
* means "pick one from the domain": a thousandth of its largest magnitude, so
|
|
26
|
+
* the log-ish part of the curve covers the top three decades of whatever the
|
|
27
|
+
* track holds and the linear knee sits below the data rather than through it.
|
|
28
|
+
*
|
|
29
|
+
* The alternative — d3's default of 1 — is `log(x + 1)`, which is fine for read
|
|
30
|
+
* depth and useless for anything living below 1, because the entire domain then
|
|
31
|
+
* falls in the linear part of the curve. A p-value track configured that way is
|
|
32
|
+
* just a linear track wearing a log label, which is the reason this is resolved
|
|
33
|
+
* rather than hard-coded.
|
|
34
|
+
*/
|
|
35
|
+
export function resolveSymlogConstant(min, max, configured) {
|
|
36
|
+
if (configured > 0) {
|
|
37
|
+
return configured;
|
|
38
|
+
}
|
|
39
|
+
const magnitude = Math.max(Math.abs(min), Math.abs(max));
|
|
40
|
+
return magnitude > 0 ? magnitude / 1000 : 1;
|
|
41
|
+
}
|
|
42
|
+
/** sign(x) * log1p(|x / c|) — the transform d3's scaleSymlog applies. */
|
|
43
|
+
function symlog(x, c) {
|
|
44
|
+
return Math.sign(x) * Math.log1p(Math.abs(x / c));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* #api
|
|
48
|
+
* Returns a loop-hoistable function normalizing a score to [0,1].
|
|
49
|
+
*
|
|
50
|
+
* `symlogConstant` is only read for `SCALE_TYPE_SYMLOG`, and is expected to be
|
|
51
|
+
* already resolved by {@link resolveSymlogConstant} — the shader gets the same
|
|
52
|
+
* resolved number as a uniform, so the "auto" rule lives on this side only and
|
|
53
|
+
* the two backends compare like for like.
|
|
54
|
+
*
|
|
55
|
+
* A range that is zero **or negative** normalizes everything to 0, which is the
|
|
56
|
+
* rule `scoreScale.slang` spells as `range <= 0.0`. Testing `range === 0` here
|
|
57
|
+
* instead let a descending domain through to a negative `1 / range`, so every
|
|
58
|
+
* score saturated to 1 on this side while the shader flattened them all to 0 —
|
|
59
|
+
* one view, opposite plots. `normalizeScoreParity.test.ts` sweeps the two.
|
|
60
|
+
*/
|
|
61
|
+
export function makeScoreNormalizer(min, max, scaleType, symlogConstant = 1) {
|
|
62
|
+
if (scaleType === SCALE_TYPE_SYMLOG) {
|
|
63
|
+
// No flooring, and no clamp of the domain away from zero: symlog is defined
|
|
64
|
+
// across the whole real line, which is the point of offering it.
|
|
65
|
+
const c = symlogConstant;
|
|
66
|
+
const tMin = symlog(min, c);
|
|
67
|
+
const tMax = symlog(max, c);
|
|
68
|
+
const tRange = tMax - tMin;
|
|
69
|
+
if (tRange <= 0) {
|
|
70
|
+
return () => 0;
|
|
71
|
+
}
|
|
72
|
+
const invRange = 1 / tRange;
|
|
73
|
+
return (score) => Math.max(0, Math.min(1, (symlog(score, c) - tMin) * invRange));
|
|
74
|
+
}
|
|
75
|
+
if (scaleType === SCALE_TYPE_LOG) {
|
|
76
|
+
// Floor at the domain's own min, not at 1. A log domain can sit entirely
|
|
77
|
+
// below 1 — a mappability track, a methylation fraction, any normalized
|
|
78
|
+
// ratio — and `getNiceDomain` deliberately keeps it there, pinning min to 1
|
|
79
|
+
// only when max > 1 (an explicit `minScore` bound can also land under 1 at
|
|
80
|
+
// any max). Flooring at 1 collapsed logMin and logMax to 0 for all of
|
|
81
|
+
// those, so `logRange === 0` and every score in the track normalized to 0:
|
|
82
|
+
// a plot flat on the baseline under an axis that, reading the same domain
|
|
83
|
+
// through d3, spread its ticks down the full height.
|
|
84
|
+
//
|
|
85
|
+
// Callers passing min = 0 — the four coverage draws, whose domain starts at
|
|
86
|
+
// no reads — still floor at 1, which is where a depth axis starts.
|
|
87
|
+
const floor = min > 0 ? min : 1;
|
|
88
|
+
const logMin = Math.log2(floor);
|
|
89
|
+
const logMax = Math.log2(Math.max(max, floor));
|
|
90
|
+
const logRange = logMax - logMin;
|
|
91
|
+
if (logRange <= 0) {
|
|
92
|
+
return () => 0;
|
|
93
|
+
}
|
|
94
|
+
const invLogRange = 1 / logRange;
|
|
95
|
+
return (score) => {
|
|
96
|
+
const logScore = Math.log2(Math.max(score, floor));
|
|
97
|
+
return Math.max(0, Math.min(1, (logScore - logMin) * invLogRange));
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const range = max - min;
|
|
101
|
+
if (range <= 0) {
|
|
102
|
+
return () => 0;
|
|
103
|
+
}
|
|
104
|
+
const invRange = 1 / range;
|
|
105
|
+
return (score) => Math.max(0, Math.min(1, (score - min) * invRange));
|
|
106
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { crispSquareTopLeftPx } from '@jbrowse/render-core/shaders/pointGlyph';
|
|
2
|
+
import { SMALL_POINT_MAX_DIAMETER } from '@jbrowse/render-core/shaders/pointGlyphConsts';
|
|
3
|
+
// At/below a 3px diameter an antialiased disc reads as a blurry blob, so scatter
|
|
4
|
+
// points draw as a crisp filled square below this threshold and an AA disc
|
|
5
|
+
// above it. The value comes from pointGlyph.slang via `//! export-consts`, so
|
|
6
|
+
// the two backends can't disagree about where the square/disc split is; the
|
|
7
|
+
// alias keeps this module's long-standing public name.
|
|
8
|
+
export const SMALL_POINT_MAX_DIAMETER_PX = SMALL_POINT_MAX_DIAMETER;
|
|
9
|
+
// Append one scatter point marker to the current path, centered on (cx, y): a
|
|
10
|
+
// crisp filled square at/below SMALL_POINT_MAX_DIAMETER_PX, else an antialiased
|
|
11
|
+
// disc. Shared by the wiggle scatter and GWAS Manhattan Canvas2D paths so the
|
|
12
|
+
// square/disc threshold can't drift between them or from the shader. The caller
|
|
13
|
+
// owns ctx.beginPath()/ctx.fill() so many markers batch into one fill.
|
|
14
|
+
export function appendPointMarker(ctx, cx, y, diameter) {
|
|
15
|
+
const radius = diameter / 2;
|
|
16
|
+
if (diameter <= SMALL_POINT_MAX_DIAMETER_PX) {
|
|
17
|
+
// Snap the top-left to the pixel grid (round-half-up) so the crisp square's
|
|
18
|
+
// fill lands on whole pixels instead of AA-blurring across columns/rows.
|
|
19
|
+
// `crispSquareTopLeftPx` is generated from pointGlyph.slang (adr-051), so
|
|
20
|
+
// this is the shader's snap rather than a re-derivation of it.
|
|
21
|
+
ctx.rect(crispSquareTopLeftPx(cx, diameter), crispSquareTopLeftPx(y, diameter), diameter, diameter);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
ctx.moveTo(cx + radius, y);
|
|
25
|
+
ctx.arc(cx, y, radius, 0, Math.PI * 2);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ConfigModelForFields, ResolvableDisplay } from '@jbrowse/core/configuration';
|
|
2
|
+
import type { MenuItem } from '@jbrowse/core/ui';
|
|
3
|
+
export declare function makeScatterPointSizeMenuItem(self: {
|
|
4
|
+
scatterPointSize: number;
|
|
5
|
+
setScatterPointSize: (n?: number) => void;
|
|
6
|
+
} & ResolvableDisplay<ConfigModelForFields<{
|
|
7
|
+
scatterPointSize: {
|
|
8
|
+
type: 'maybeNumber';
|
|
9
|
+
promotedBase: number;
|
|
10
|
+
};
|
|
11
|
+
}>>, opts: {
|
|
12
|
+
label: string;
|
|
13
|
+
}): MenuItem;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { makePromotableSizeMenu } from '@jbrowse/core/ui';
|
|
2
|
+
// Wires a display's shared `scatterPointSize`/`setScatterPointSize` (from
|
|
3
|
+
// WiggleScoreConfigMixin) to makePromotableSizeMenu. Used by both the wiggle
|
|
4
|
+
// scatter and GWAS Manhattan track menus so the slider/reset/pin behavior can't
|
|
5
|
+
// drift. The `scatterPointSize` slot is promotable, so the row carries the
|
|
6
|
+
// "default for all tracks of this type" pin.
|
|
7
|
+
export function makeScatterPointSizeMenuItem(self, opts) {
|
|
8
|
+
return makePromotableSizeMenu({
|
|
9
|
+
label: opts.label,
|
|
10
|
+
title: 'Point size',
|
|
11
|
+
display: self,
|
|
12
|
+
slot: 'scatterPointSize',
|
|
13
|
+
getValue: () => self.scatterPointSize,
|
|
14
|
+
onChange: n => {
|
|
15
|
+
self.setScatterPointSize(n);
|
|
16
|
+
},
|
|
17
|
+
onReset: () => {
|
|
18
|
+
self.setScatterPointSize(undefined);
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { WiggleScaleType } from './normalize.ts';
|
|
2
|
+
import type { PerRegionRenderingBackend } from '@jbrowse/render-core/perRegionRenderingBackend';
|
|
3
|
+
export type WiggleRenderingType = 0 | 1 | 2 | 3 | 4;
|
|
4
|
+
export declare const RENDERING_TYPE_XYPLOT: WiggleRenderingType;
|
|
5
|
+
export declare const RENDERING_TYPE_DENSITY: WiggleRenderingType;
|
|
6
|
+
export declare const RENDERING_TYPE_LINE: WiggleRenderingType;
|
|
7
|
+
export declare const RENDERING_TYPE_SCATTER: WiggleRenderingType;
|
|
8
|
+
export declare const RENDERING_TYPE_LINE_CENTER: WiggleRenderingType;
|
|
9
|
+
export declare const NO_PREV_START = 4294967295;
|
|
10
|
+
export declare const MIN_FILL_WIDTH_PX = 1.5;
|
|
11
|
+
export interface WiggleGPURenderState {
|
|
12
|
+
domainY: [number, number];
|
|
13
|
+
scaleType: WiggleScaleType;
|
|
14
|
+
/**
|
|
15
|
+
* symlog's linear-region width, already resolved from the domain by
|
|
16
|
+
* `resolveSymlogConstant`. Both backends read this one number so the axis,
|
|
17
|
+
* the shader and the Canvas2D fallback agree; unread by the other scales.
|
|
18
|
+
*/
|
|
19
|
+
symlogConstant: number;
|
|
20
|
+
renderingType: WiggleRenderingType;
|
|
21
|
+
canvasWidth: number;
|
|
22
|
+
canvasHeight: number;
|
|
23
|
+
numRows: number;
|
|
24
|
+
scatterPointSize: number;
|
|
25
|
+
lineWidth: number;
|
|
26
|
+
origin: number;
|
|
27
|
+
densityColorRamp?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface SourceRenderData {
|
|
30
|
+
featurePositions: Uint32Array;
|
|
31
|
+
featureScores: Float32Array;
|
|
32
|
+
numFeatures: number;
|
|
33
|
+
color: [number, number, number];
|
|
34
|
+
rowIndex: number;
|
|
35
|
+
renderingType: WiggleRenderingType;
|
|
36
|
+
colorsAbgr?: Uint32Array;
|
|
37
|
+
gapLimitBp?: number;
|
|
38
|
+
}
|
|
39
|
+
export type WiggleRenderingBackend = PerRegionRenderingBackend<SourceRenderData[], WiggleGPURenderState>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { MIN_FILL_WIDTH_PX as GENERATED_MIN_FILL_WIDTH_PX, NO_PREV_START as GENERATED_NO_PREV_START, RENDERING_TYPE_DENSITY as GENERATED_RENDERING_TYPE_DENSITY, RENDERING_TYPE_LINE as GENERATED_RENDERING_TYPE_LINE, RENDERING_TYPE_LINE_CENTER as GENERATED_RENDERING_TYPE_LINE_CENTER, RENDERING_TYPE_SCATTER as GENERATED_RENDERING_TYPE_SCATTER, RENDERING_TYPE_XYPLOT as GENERATED_RENDERING_TYPE_XYPLOT, } from './wiggleRenderModes.generated.js';
|
|
2
|
+
// The `renderingType` uniform's vocabulary. These are wiggle.slang's own
|
|
3
|
+
// numbers, generated in by `pnpm gen:shaders` (adr-051): the Canvas2D path
|
|
4
|
+
// branches on them to pick a draw function while the GPU path branches on them
|
|
5
|
+
// inside vs_main, so a renumbering that reached only one side would quietly
|
|
6
|
+
// draw a different plot type on each backend. Declared here rather than in the
|
|
7
|
+
// plugin because this is where `WiggleRenderingType` lives, and the annotations
|
|
8
|
+
// are what pin each generated value into that union.
|
|
9
|
+
export const RENDERING_TYPE_XYPLOT = GENERATED_RENDERING_TYPE_XYPLOT;
|
|
10
|
+
export const RENDERING_TYPE_DENSITY = GENERATED_RENDERING_TYPE_DENSITY;
|
|
11
|
+
export const RENDERING_TYPE_LINE = GENERATED_RENDERING_TYPE_LINE;
|
|
12
|
+
export const RENDERING_TYPE_SCATTER = GENERATED_RENDERING_TYPE_SCATTER;
|
|
13
|
+
// Point-to-point line: connects the score at each feature's bp midpoint to its
|
|
14
|
+
// neighbor's, instead of tracing the stepped bar tops that RENDERING_TYPE_LINE
|
|
15
|
+
// draws. Better for sparse/discrete data where the plateaus look wrong.
|
|
16
|
+
export const RENDERING_TYPE_LINE_CENTER = GENERATED_RENDERING_TYPE_LINE_CENTER;
|
|
17
|
+
// `prevStartEnd.x` sentinel meaning "no previous feature" — the source start,
|
|
18
|
+
// or a hole wider than `gapLimitBp`. Larger than any genomic coordinate, so it
|
|
19
|
+
// cannot collide with a real start, and the shader collapses that quad to
|
|
20
|
+
// nothing. wiggle.slang's, generated in, because the encoder writes it and the
|
|
21
|
+
// shader tests for it.
|
|
22
|
+
export const NO_PREV_START = GENERATED_NO_PREV_START;
|
|
23
|
+
// Narrowest an xyplot bar / density column paints, in CSS px, so a bin under a
|
|
24
|
+
// pixel wide still shows. wiggle.slang's, generated in, because both backends
|
|
25
|
+
// apply it: the shader through `extendToMinWidthX` and Canvas2D through
|
|
26
|
+
// `Math.max(WIGGLE_MIN_PX, …)`, which is what re-exports this under that name.
|
|
27
|
+
// It was a `1.5` in each, kept together by a comment in each pointing at the
|
|
28
|
+
// other.
|
|
29
|
+
export const MIN_FILL_WIDTH_PX = GENERATED_MIN_FILL_WIDTH_PX;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function resolveRenderState<T>(domain: [number, number] | undefined, build: (domain: [number, number]) => T): T;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// A region with data but no scores (empty / not covered by the file) has no
|
|
2
|
+
// score range to autoscale, yet it must still render — `renderBlocks` has to run
|
|
3
|
+
// to clear the canvas and flip `canvasDrawn`, or the display spins on the
|
|
4
|
+
// loading overlay forever. The GPU render-state can't be *built* without a
|
|
5
|
+
// domain (it encodes one for vertical scaling), so this stands in. It's inert:
|
|
6
|
+
// nothing is ever plotted against it (there's no data), and every axis/legend a
|
|
7
|
+
// caller draws is gated on the *real* `domain`, so it never surfaces as a fake
|
|
8
|
+
// scale. The specific numbers are irrelevant — [0,1] is just a valid range.
|
|
9
|
+
const EMPTY_PLOT_DOMAIN = [0, 1];
|
|
10
|
+
// The render-state a wiggle-family GPU display builds from its score `domain`,
|
|
11
|
+
// always resolvable (real domain, else EMPTY_PLOT_DOMAIN). This is NOT the
|
|
12
|
+
// loading gate — "no fetch has completed yet" is answered by the boolean
|
|
13
|
+
// `renderBlocks` returns (did a region actually draw), not by a nullable render
|
|
14
|
+
// state and no longer by a per-display size check. `build` absorbs each
|
|
15
|
+
// display's render-state shape (wiggle's encoded WiggleGPURenderState vs
|
|
16
|
+
// manhattan's { domainY, canvasWidth, canvasHeight }), so only the empty-plot
|
|
17
|
+
// domain is shared, not the construction.
|
|
18
|
+
export function resolveRenderState(domain, build) {
|
|
19
|
+
return build(domain ?? EMPTY_PLOT_DOMAIN);
|
|
20
|
+
}
|