@jbrowse/render-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/esm/OverlayCanvas.d.ts +29 -0
- package/esm/OverlayCanvas.js +43 -0
- package/esm/RenderCanvas.d.ts +76 -0
- package/esm/RenderCanvas.js +38 -0
- package/esm/RenderLifecycleMixin.d.ts +180 -0
- package/esm/RenderLifecycleMixin.js +279 -0
- package/esm/ScrollLockedOverlay.d.ts +28 -0
- package/esm/ScrollLockedOverlay.js +42 -0
- package/esm/blockClipUtils.d.ts +27 -0
- package/esm/blockClipUtils.js +101 -0
- package/esm/canvas2dUtils.d.ts +372 -0
- package/esm/canvas2dUtils.js +564 -0
- package/esm/canvasContext.d.ts +68 -0
- package/esm/canvasContext.js +74 -0
- package/esm/colorRampLut.d.ts +17 -0
- package/esm/colorRampLut.js +25 -0
- package/esm/coverageBand.d.ts +107 -0
- package/esm/coverageBand.js +109 -0
- package/esm/coverageBandBuffers.d.ts +30 -0
- package/esm/coverageBandBuffers.js +13 -0
- package/esm/createRenderingBackend.d.ts +52 -0
- package/esm/createRenderingBackend.js +58 -0
- package/esm/displayPhase.d.ts +155 -0
- package/esm/displayPhase.js +97 -0
- package/esm/globalRenderingBackend.d.ts +90 -0
- package/esm/globalRenderingBackend.js +53 -0
- package/esm/gpuDevice.d.ts +91 -0
- package/esm/gpuDevice.js +277 -0
- package/esm/graphicsCapabilities.d.ts +98 -0
- package/esm/graphicsCapabilities.js +165 -0
- package/esm/hal/createHal.d.ts +22 -0
- package/esm/hal/createHal.js +91 -0
- package/esm/hal/deviceGpuCache.d.ts +36 -0
- package/esm/hal/deviceGpuCache.js +120 -0
- package/esm/hal/gpuHalBase.d.ts +55 -0
- package/esm/hal/gpuHalBase.js +81 -0
- package/esm/hal/index.d.ts +7 -0
- package/esm/hal/index.js +5 -0
- package/esm/hal/mockHal.d.ts +153 -0
- package/esm/hal/mockHal.js +297 -0
- package/esm/hal/oomReporter.d.ts +15 -0
- package/esm/hal/oomReporter.js +22 -0
- package/esm/hal/passIds.d.ts +22 -0
- package/esm/hal/passIds.js +36 -0
- package/esm/hal/regionRegistry.d.ts +11 -0
- package/esm/hal/regionRegistry.js +66 -0
- package/esm/hal/types.d.ts +116 -0
- package/esm/hal/types.js +1 -0
- package/esm/hal/webgl2Hal.d.ts +47 -0
- package/esm/hal/webgl2Hal.js +490 -0
- package/esm/hal/webgpuHal.d.ts +169 -0
- package/esm/hal/webgpuHal.js +722 -0
- package/esm/installUpload.d.ts +91 -0
- package/esm/installUpload.js +94 -0
- package/esm/instanceCache.d.ts +43 -0
- package/esm/instanceCache.js +53 -0
- package/esm/instancePass.d.ts +50 -0
- package/esm/instancePass.js +34 -0
- package/esm/keyedRenderingBackend.d.ts +41 -0
- package/esm/keyedRenderingBackend.js +19 -0
- package/esm/mapUploadSync.d.ts +31 -0
- package/esm/mapUploadSync.js +45 -0
- package/esm/namedReactions.d.ts +29 -0
- package/esm/namedReactions.js +62 -0
- package/esm/perRegionRenderingBackend.d.ts +114 -0
- package/esm/perRegionRenderingBackend.js +93 -0
- package/esm/recoveryBudget.d.ts +40 -0
- package/esm/recoveryBudget.js +56 -0
- package/esm/regionDataMap.d.ts +60 -0
- package/esm/regionDataMap.js +98 -0
- package/esm/renderBlock.d.ts +25 -0
- package/esm/renderBlock.js +10 -0
- package/esm/renderingBackendBase.d.ts +71 -0
- package/esm/renderingBackendBase.js +50 -0
- package/esm/shaders/antialias.js.generated.d.ts +4 -0
- package/esm/shaders/antialias.js.generated.js +25 -0
- package/esm/shaders/capsule.generated.d.ts +1 -0
- package/esm/shaders/capsule.generated.js +3 -0
- package/esm/shaders/colorRampLut.generated.d.ts +1 -0
- package/esm/shaders/colorRampLut.generated.js +3 -0
- package/esm/shaders/coverageBand.generated.d.ts +2 -0
- package/esm/shaders/coverageBand.generated.js +4 -0
- package/esm/shaders/coverageBar.generated.d.ts +4 -0
- package/esm/shaders/coverageBar.generated.js +6 -0
- package/esm/shaders/coverageBar.iface.generated.d.ts +85 -0
- package/esm/shaders/coverageBar.iface.generated.js +114 -0
- package/esm/shaders/coverageIndicator.generated.d.ts +4 -0
- package/esm/shaders/coverageIndicator.generated.js +6 -0
- package/esm/shaders/coverageIndicator.iface.generated.d.ts +85 -0
- package/esm/shaders/coverageIndicator.iface.generated.js +114 -0
- package/esm/shaders/coverageInterbase.generated.d.ts +4 -0
- package/esm/shaders/coverageInterbase.generated.js +6 -0
- package/esm/shaders/coverageInterbase.iface.generated.d.ts +93 -0
- package/esm/shaders/coverageInterbase.iface.generated.js +134 -0
- package/esm/shaders/coverageMod.generated.d.ts +4 -0
- package/esm/shaders/coverageMod.generated.js +6 -0
- package/esm/shaders/coverageMod.iface.generated.d.ts +97 -0
- package/esm/shaders/coverageMod.iface.generated.js +144 -0
- package/esm/shaders/coverageSnp.generated.d.ts +4 -0
- package/esm/shaders/coverageSnp.generated.js +6 -0
- package/esm/shaders/coverageSnp.iface.generated.d.ts +97 -0
- package/esm/shaders/coverageSnp.iface.generated.js +144 -0
- package/esm/shaders/hpmath.js.generated.d.ts +3 -0
- package/esm/shaders/hpmath.js.generated.js +48 -0
- package/esm/shaders/pointGlyph.generated.d.ts +1 -0
- package/esm/shaders/pointGlyph.generated.js +3 -0
- package/esm/shaders/pointGlyph.js.generated.d.ts +1 -0
- package/esm/shaders/pointGlyph.js.generated.js +7 -0
- package/esm/shaders/rowRect.generated.d.ts +3 -0
- package/esm/shaders/rowRect.generated.js +5 -0
- package/esm/shaders/rowRect.js.generated.d.ts +2 -0
- package/esm/shaders/rowRect.js.generated.js +13 -0
- package/esm/shaders/scoreScale.js.generated.d.ts +1 -0
- package/esm/shaders/scoreScale.js.generated.js +46 -0
- package/esm/slangPass.d.ts +49 -0
- package/esm/slangPass.js +22 -0
- package/esm/useEventCallback.d.ts +1 -0
- package/esm/useEventCallback.js +19 -0
- package/esm/useGraphicsCapabilities.d.ts +17 -0
- package/esm/useGraphicsCapabilities.js +38 -0
- package/esm/useRenderingBackend.d.ts +83 -0
- package/esm/useRenderingBackend.js +422 -0
- package/esm/useTabVisibilityRerender.d.ts +13 -0
- package/esm/useTabVisibilityRerender.js +39 -0
- package/esm/webgpuUtils.d.ts +23 -0
- package/esm/webgpuUtils.js +76 -0
- package/package.json +91 -0
- package/src/shaders/antialias.js.generated.ts +33 -0
- package/src/shaders/antialias.slang +112 -0
- package/src/shaders/capsule.generated.ts +4 -0
- package/src/shaders/capsule.slang +62 -0
- package/src/shaders/colorPack.slang +26 -0
- package/src/shaders/colorRampLut.generated.ts +4 -0
- package/src/shaders/colorRampLut.slang +51 -0
- package/src/shaders/coverageBand.generated.ts +6 -0
- package/src/shaders/coverageBand.slang +276 -0
- package/src/shaders/coverageBar.generated.ts +10 -0
- package/src/shaders/coverageBar.iface.generated.ts +172 -0
- package/src/shaders/coverageBar.slang +37 -0
- package/src/shaders/coverageIndicator.generated.ts +10 -0
- package/src/shaders/coverageIndicator.iface.generated.ts +172 -0
- package/src/shaders/coverageIndicator.slang +61 -0
- package/src/shaders/coverageInterbase.generated.ts +10 -0
- package/src/shaders/coverageInterbase.iface.generated.ts +198 -0
- package/src/shaders/coverageInterbase.slang +63 -0
- package/src/shaders/coverageMod.generated.ts +10 -0
- package/src/shaders/coverageMod.iface.generated.ts +211 -0
- package/src/shaders/coverageMod.slang +36 -0
- package/src/shaders/coverageSnp.generated.ts +10 -0
- package/src/shaders/coverageSnp.iface.generated.ts +211 -0
- package/src/shaders/coverageSnp.slang +52 -0
- package/src/shaders/diagonalGrid.slang +25 -0
- package/src/shaders/glyphEdgeAlpha.test.ts +241 -0
- package/src/shaders/hpmath.js.generated.ts +47 -0
- package/src/shaders/hpmath.slang +226 -0
- package/src/shaders/pointGlyph.generated.ts +4 -0
- package/src/shaders/pointGlyph.js.generated.ts +9 -0
- package/src/shaders/pointGlyph.slang +93 -0
- package/src/shaders/rowRect.generated.ts +8 -0
- package/src/shaders/rowRect.js.generated.ts +17 -0
- package/src/shaders/rowRect.slang +160 -0
- package/src/shaders/scoreScale.js.generated.ts +51 -0
- package/src/shaders/scoreScale.slang +68 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// Shared "row of colored rects" primitive: one instance is a rectangle
|
|
2
|
+
// spanning [startBp, endBp) in absolute genomic coords, drawn on the row given
|
|
3
|
+
// by `rowIndex` and filled with a per-instance packed ABGR color.
|
|
4
|
+
//
|
|
5
|
+
// Two renderers draw exactly this: the MAF alignment renderer (run-merged
|
|
6
|
+
// per-base cells) and the multi-row feature painter / "chromosome painting"
|
|
7
|
+
// (features are already intervals). They shipped as byte-identical copies of
|
|
8
|
+
// one another joined only by a "probably belongs there too" comment; owning the
|
|
9
|
+
// geometry here makes a change to the primitive land in both by construction.
|
|
10
|
+
//
|
|
11
|
+
// Row placement comes from uniforms rather than the instance data, so a
|
|
12
|
+
// rowHeight / rowProportion change re-renders without a refetch.
|
|
13
|
+
//
|
|
14
|
+
// What the callers legitimately differ on is the min-width floor: both the
|
|
15
|
+
// width they write into `minCellPx` and the *unit* they measure it in, via
|
|
16
|
+
// `minCellDenomPx` (CSS px vs device px) — see GpuMafRenderer's note. Those are
|
|
17
|
+
// uniform-write decisions, not a reason for two shaders. The unit is also
|
|
18
|
+
// unsettled, and device px additionally makes MAF's floor depend on the
|
|
19
|
+
// reader's monitor: `agent-docs/ideas/maf-subpixel-cells.md`.
|
|
20
|
+
//! export-consts: MIN_DRAWN_ROW_PX, MIN_DRAWN_CELL_PX, MULTI_ROW_MIN_CELL_PX
|
|
21
|
+
//! js-export: drawnRowHeightPx, rowBandOffsetPx
|
|
22
|
+
module rowRect;
|
|
23
|
+
|
|
24
|
+
import colorPack;
|
|
25
|
+
import hpmath;
|
|
26
|
+
|
|
27
|
+
public struct RowRectInstance {
|
|
28
|
+
public uint startBp : ATTR0;
|
|
29
|
+
public uint endBp : ATTR1;
|
|
30
|
+
public uint rowIndex : ATTR2;
|
|
31
|
+
public uint color : ATTR3; // packed ABGR
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
public struct RowRectUniforms {
|
|
35
|
+
public float3 bpRangeX; // [bpStartHi, bpStartLo, ±clippedLengthBp]
|
|
36
|
+
public float canvasHeight; // CSS px
|
|
37
|
+
// The width `minCellPx` is measured against, and so the thing that decides
|
|
38
|
+
// what unit `minCellPx` IS. Named for that rather than for the viewport
|
|
39
|
+
// because the three callers do not agree: multi-row and wiggle density pass
|
|
40
|
+
// a CSS width (a 1 CSS px floor), MAF passes a device one (1 device px, so
|
|
41
|
+
// half a CSS px on a retina screen and a floor that moves with the monitor).
|
|
42
|
+
// That disagreement is live and parked, not settled —
|
|
43
|
+
// agent-docs/ideas/maf-subpixel-cells.md — so the name carries it to every
|
|
44
|
+
// call site instead of a comment forty lines away.
|
|
45
|
+
public float minCellDenomPx;
|
|
46
|
+
// Narrowest a cell is painted, in whatever unit `minCellDenomPx` is in.
|
|
47
|
+
// MIN_DRAWN_CELL_PX is the floor a caller takes when it has no reason to
|
|
48
|
+
// widen further; the multi-row painter does (see MULTI_ROW_MIN_CELL_PX).
|
|
49
|
+
public float minCellPx;
|
|
50
|
+
public float zero; // always 0.0 — prevents hp-split collapse
|
|
51
|
+
public float rowHeight; // CSS px per row
|
|
52
|
+
public float rowProportion; // rect height = rowHeight * rowProportion
|
|
53
|
+
// Rows-area scroll offset in CSS px. The canvas is the viewport, not the
|
|
54
|
+
// content: a caller whose rows overflow it paints row i at
|
|
55
|
+
// `rowHeight*i - scrollTop` and lets the off-screen rows clip. Callers that
|
|
56
|
+
// size their canvas to the content (the multi-row painter) write 0.
|
|
57
|
+
public float scrollTop;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
public struct RowRectVsOut {
|
|
61
|
+
public float4 position : SV_Position;
|
|
62
|
+
public float4 color : COLOR0;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Thinnest a row is ever painted. Vertical twin of MIN_DRAWN_CELL_PX below: a
|
|
66
|
+
// row thinner than a pixel can miss every pixel center and disappear, so
|
|
67
|
+
// without this rows drop out as they get denser instead of just getting
|
|
68
|
+
// thinner. Floored, neighbours overlap instead.
|
|
69
|
+
static const float MIN_DRAWN_ROW_PX = 1.0;
|
|
70
|
+
|
|
71
|
+
// Thinnest a cell can be painted and still be drawable at all, for the same
|
|
72
|
+
// reason as the row floor above on the other axis. A caller writes this into
|
|
73
|
+
// `minCellPx` when a cell being *visible* is not its problem, only a cell
|
|
74
|
+
// existing; one whose whole encoding is carried by sub-pixel cells asks for
|
|
75
|
+
// more (MULTI_ROW_MIN_CELL_PX).
|
|
76
|
+
//
|
|
77
|
+
// One caller mirrors it on the Canvas2D side and one deliberately does not: the
|
|
78
|
+
// multi-row painter floors, MAF draws cells at their natural sub-pixel width.
|
|
79
|
+
// That asymmetry is real and parked in `agent-docs/ideas/maf-subpixel-cells.md`;
|
|
80
|
+
// sharing the constant does not decide it, it just stops the value being
|
|
81
|
+
// anonymous in the places that DO agree.
|
|
82
|
+
static const float MIN_DRAWN_CELL_PX = 1.0;
|
|
83
|
+
|
|
84
|
+
// What the multi-row painter ("chromosome painting") writes into `minCellPx`.
|
|
85
|
+
//
|
|
86
|
+
// Wider than a pixel because this display has no second channel to fall back
|
|
87
|
+
// on. MAF's cells tile the row, so a sub-pixel cell sits in a run of its
|
|
88
|
+
// neighbours and the run is what is read; a painting's features are sparse
|
|
89
|
+
// intervals, and at chromosome zoom a RepeatMasker element is a single tick
|
|
90
|
+
// against white paper with nothing beside it. At one pixel — anti-aliased
|
|
91
|
+
// across two, so neither reaches full opacity — the tick reads as a smudge, and
|
|
92
|
+
// the eye scanning for structure across the rows finds none.
|
|
93
|
+
//
|
|
94
|
+
// Both floors distort width; that is what a floor is. This one distorts more,
|
|
95
|
+
// and only where the alternative was showing nothing legible: a feature wide
|
|
96
|
+
// enough to matter is already past it.
|
|
97
|
+
static const float MULTI_ROW_MIN_CELL_PX = 2.0;
|
|
98
|
+
|
|
99
|
+
public struct RowBand {
|
|
100
|
+
public float height; // what the rect is painted at
|
|
101
|
+
public float offset; // inset from the row's top, negative once floored
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// The band a row's rects occupy, as two scalars so `//! js-export` can lift
|
|
105
|
+
// them: rowBand.ts is the Canvas2D painter's, the indel glyphs' and the hover
|
|
106
|
+
// box's definition of the same band, and a row drawn here but hit-tested there
|
|
107
|
+
// has to land in the same place. The struct below is only packaging for the
|
|
108
|
+
// vertex stage.
|
|
109
|
+
//
|
|
110
|
+
// Centered, so a floored band overhangs its row evenly on both sides rather
|
|
111
|
+
// than pushing every row down.
|
|
112
|
+
public float drawnRowHeightPx(float rowHeight, float rowProportion) {
|
|
113
|
+
return max(rowHeight * rowProportion, MIN_DRAWN_ROW_PX);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public float rowBandOffsetPx(float rowHeight, float rowProportion) {
|
|
117
|
+
return (rowHeight - drawnRowHeightPx(rowHeight, rowProportion)) * 0.5;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public RowBand rowBandPx(float rowHeight, float rowProportion) {
|
|
121
|
+
RowBand band;
|
|
122
|
+
band.height = drawnRowHeightPx(rowHeight, rowProportion);
|
|
123
|
+
band.offset = rowBandOffsetPx(rowHeight, rowProportion);
|
|
124
|
+
return band;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// The shape's geometry alone — row band, hp clip-x span, min-width widening —
|
|
128
|
+
// so a consumer whose colour is not a per-instance packed value (wiggle
|
|
129
|
+
// density: a per-instance score through a uniform scale) composes the same
|
|
130
|
+
// rect without routing a resolved colour through the instance buffer.
|
|
131
|
+
// `rowIndex` is float here because the row transform is float arithmetic;
|
|
132
|
+
// each caller converts at its own boundary (`RowRectInstance` stores uint,
|
|
133
|
+
// wiggle's fill record stores float).
|
|
134
|
+
public float4 rowRectClipPos(
|
|
135
|
+
uint startBp, uint endBp, float rowIndex, uint vid, RowRectUniforms u
|
|
136
|
+
) {
|
|
137
|
+
float2 local = quadLocal(vid);
|
|
138
|
+
|
|
139
|
+
RowBand band = rowBandPx(u.rowHeight, u.rowProportion);
|
|
140
|
+
float rowY = band.offset + u.rowHeight * rowIndex - u.scrollTop;
|
|
141
|
+
|
|
142
|
+
float sx1 = hpToClipX(hpSplitUint(startBp), u.bpRangeX, u.zero);
|
|
143
|
+
float sx2 = hpToClipX(hpSplitUint(endBp), u.bpRangeX, u.zero);
|
|
144
|
+
sx2 = extendToMinWidthX(sx1, sx2, u.minCellPx, u.minCellDenomPx);
|
|
145
|
+
|
|
146
|
+
float yPx = lerp(rowY, rowY + band.height, local.y);
|
|
147
|
+
|
|
148
|
+
return float4(
|
|
149
|
+
lerp(sx1, sx2, local.x), yPxToClipY(yPx, u.canvasHeight), 0.0, 1.0);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public RowRectVsOut rowRectVertex(
|
|
153
|
+
RowRectInstance inst, uint vid, RowRectUniforms u
|
|
154
|
+
) {
|
|
155
|
+
RowRectVsOut o;
|
|
156
|
+
o.position = rowRectClipPos(
|
|
157
|
+
inst.startBp, inst.endBp, float(inst.rowIndex), vid, u);
|
|
158
|
+
o.color = unpackRGBA(inst.color);
|
|
159
|
+
return o;
|
|
160
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// AUTO-GENERATED by packages/shader-tools/src/shader-codegen from scoreScale.slang.
|
|
2
|
+
// Do not edit. Run `pnpm gen:shaders` to regenerate.
|
|
3
|
+
|
|
4
|
+
// Scalar twins of scoreScale.slang, transliterated from slangc's WGSL so
|
|
5
|
+
// the Canvas2D and SVG paths run the shader's own math. See adr-051.
|
|
6
|
+
|
|
7
|
+
function _clamp(x: number, lo: number, hi: number) {
|
|
8
|
+
return _min(_max(x, lo), hi)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function _max(a: number, b: number) {
|
|
12
|
+
return b > a || Number.isNaN(a) ? b : a
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function _min(a: number, b: number) {
|
|
16
|
+
return b < a || Number.isNaN(a) ? b : a
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function symlogTransform(x: number, c: number): number {
|
|
20
|
+
return ((((Math.sign(x)) | 0)) * Math.log((1.0 + Math.abs((x / c)))))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function normalizeScore(score: number, domainMin: number, domainMax: number, scaleType: number, symlogConstant: number): number {
|
|
24
|
+
if ((scaleType == 2)) {
|
|
25
|
+
let tMin = symlogTransform(domainMin, symlogConstant)
|
|
26
|
+
let tRange = (symlogTransform(domainMax, symlogConstant) - tMin)
|
|
27
|
+
if ((tRange <= 0.0)) {
|
|
28
|
+
return 0.0
|
|
29
|
+
}
|
|
30
|
+
return _clamp(((symlogTransform(score, symlogConstant) - tMin) / tRange), 0.0, 1.0)
|
|
31
|
+
}
|
|
32
|
+
if ((scaleType == 1)) {
|
|
33
|
+
let floorV: number
|
|
34
|
+
if ((domainMin > 0.0)) {
|
|
35
|
+
floorV = domainMin
|
|
36
|
+
} else {
|
|
37
|
+
floorV = 1.0
|
|
38
|
+
}
|
|
39
|
+
let logMin = Math.log2(floorV)
|
|
40
|
+
let logRange = (Math.log2(_max(domainMax, floorV)) - logMin)
|
|
41
|
+
if ((logRange <= 0.0)) {
|
|
42
|
+
return 0.0
|
|
43
|
+
}
|
|
44
|
+
return _clamp(((Math.log2(_max(score, floorV)) - logMin) / logRange), 0.0, 1.0)
|
|
45
|
+
}
|
|
46
|
+
let range = (domainMax - domainMin)
|
|
47
|
+
if ((range <= 0.0)) {
|
|
48
|
+
return 0.0
|
|
49
|
+
}
|
|
50
|
+
return _clamp(((score - domainMin) / range), 0.0, 1.0)
|
|
51
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// The score → [0,1] normalizer every score-scaled track is placed by, and the
|
|
2
|
+
// scale-type vocabulary its branches switch on.
|
|
3
|
+
//
|
|
4
|
+
// Two real consumers with non-obvious math, which is ADR-040's bar: the wiggle
|
|
5
|
+
// renderer's four modes (`wiggleCommon.slang`, via `scoreToY`) and the coverage
|
|
6
|
+
// band's depth axis (`coverageBand.slang`, via `normalizeDepthScalar`). They
|
|
7
|
+
// carried a branch-for-branch copy each, and the copies did not agree: the
|
|
8
|
+
// coverage one compared `scaleType` against bare 2 and 1 rather than the
|
|
9
|
+
// constants below, and answered 0 on a degenerate domain where wiggle's divided
|
|
10
|
+
// by a 1e-6 floor and saturated. This module is one answer to both.
|
|
11
|
+
//
|
|
12
|
+
// The degenerate domain answers 0, which is what the JS twin
|
|
13
|
+
// (`makeScoreNormalizer`, packages/wiggle-core/src/normalize.ts) has always
|
|
14
|
+
// answered. Returning 0 avoids the NaN the 1e-6 floor was there for just as
|
|
15
|
+
// well, so the divergence bought nothing and cost a documented exception.
|
|
16
|
+
//
|
|
17
|
+
// `SCALE_TYPE_LINEAR` is deliberately absent: it is "neither of these", and a
|
|
18
|
+
// third constant would be a value nothing compares against.
|
|
19
|
+
//! js-export: normalizeScore
|
|
20
|
+
//! js-skip: symlogTransform — the JS twin (wiggle-core normalize.ts) spells it Math.log1p, which is accurate near zero where the shader's log(1.0 + x) is not; a generated twin would be a faithful copy of the worse of the two. What both backends must agree on is normalizeScore, which is exported and swept by normalizeScoreParity.test.ts
|
|
21
|
+
module scoreScale;
|
|
22
|
+
|
|
23
|
+
// The values the `scaleType` uniform is compared against. Emitted into
|
|
24
|
+
// @jbrowse/wiggle-core through `wiggleCommon.slang`'s `export-consts` — the
|
|
25
|
+
// constant evaluator resolves through imports, and wiggle-core is where
|
|
26
|
+
// `WiggleScaleType` is declared, so the numbers stay next to the type they
|
|
27
|
+
// name rather than next to the module that happens to author them.
|
|
28
|
+
public static const int SCALE_TYPE_LOG = 1;
|
|
29
|
+
public static const int SCALE_TYPE_SYMLOG = 2;
|
|
30
|
+
|
|
31
|
+
public float symlogTransform(float x, float c) {
|
|
32
|
+
return sign(x) * log(1.0 + abs(x / c));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public float normalizeScore(float score, float domainMin, float domainMax, int scaleType, float symlogConstant) {
|
|
36
|
+
if (scaleType == SCALE_TYPE_SYMLOG) {
|
|
37
|
+
// Defined at and either side of zero, so unlike the log branch there is
|
|
38
|
+
// nothing to floor: the domain arrives as-is and 0 lands where it belongs
|
|
39
|
+
// instead of on the baseline with everything else below the floor. On a
|
|
40
|
+
// coverage band that is the whole reason symlog is offered — a depth of 0
|
|
41
|
+
// is real, common, and what log cannot separate from a depth of 1.
|
|
42
|
+
float tMin = symlogTransform(domainMin, symlogConstant);
|
|
43
|
+
float tMax = symlogTransform(domainMax, symlogConstant);
|
|
44
|
+
float tRange = tMax - tMin;
|
|
45
|
+
if (tRange <= 0.0) { return 0.0; }
|
|
46
|
+
float t = symlogTransform(score, symlogConstant);
|
|
47
|
+
return clamp((t - tMin) / tRange, 0.0, 1.0);
|
|
48
|
+
}
|
|
49
|
+
if (scaleType == SCALE_TYPE_LOG) {
|
|
50
|
+
// Floor at the domain's own min, not at 1, so a domain sitting entirely
|
|
51
|
+
// below 1 (mappability, a methylation fraction, any normalized ratio) still
|
|
52
|
+
// has a range to spread over instead of collapsing to a flat baseline. Same
|
|
53
|
+
// rule and same reasoning as JS `makeScoreNormalizer`, which keeps its own
|
|
54
|
+
// hand-written copy because it hoists this arithmetic out of a per-feature
|
|
55
|
+
// loop — so this one is `//! js-export`ed as the ORACLE the two are swept
|
|
56
|
+
// against (`normalizeScoreParity.test.ts`), not to retire it.
|
|
57
|
+
float floorV = domainMin > 0.0 ? domainMin : 1.0;
|
|
58
|
+
float logMin = log2(floorV);
|
|
59
|
+
float logMax = log2(max(domainMax, floorV));
|
|
60
|
+
float logRange = logMax - logMin;
|
|
61
|
+
if (logRange <= 0.0) { return 0.0; }
|
|
62
|
+
float logScore = log2(max(score, floorV));
|
|
63
|
+
return clamp((logScore - logMin) / logRange, 0.0, 1.0);
|
|
64
|
+
}
|
|
65
|
+
float range = domainMax - domainMin;
|
|
66
|
+
if (range <= 0.0) { return 0.0; }
|
|
67
|
+
return clamp((score - domainMin) / range, 0.0, 1.0);
|
|
68
|
+
}
|