@jbrowse/wiggle-core 4.3.0 → 5.0.0-beta.10
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/README.md +76 -107
- package/esm/AutoscaleGroupDialog.d.ts +7 -0
- package/esm/AutoscaleGroupDialog.js +26 -0
- package/esm/ScoreAxisMixin.d.ts +134 -0
- package/esm/ScoreAxisMixin.js +213 -0
- package/esm/ScoreFieldConfigMixin.d.ts +72 -0
- package/esm/ScoreFieldConfigMixin.js +25 -0
- package/esm/ScorePlotChrome.d.ts +45 -0
- package/esm/ScorePlotChrome.js +47 -0
- package/esm/ScorePlotSvgFrame.d.ts +27 -0
- package/esm/ScorePlotSvgFrame.js +23 -0
- package/esm/ScoreScaleMixin.d.ts +113 -50
- package/esm/ScoreScaleMixin.js +148 -70
- package/esm/SetMinMaxDialog.d.ts +3 -2
- package/esm/SetMinMaxDialog.js +20 -6
- package/esm/SetScoreRulesDialog.d.ts +14 -0
- package/esm/SetScoreRulesDialog.js +45 -0
- package/esm/WiggleScoreConfigMixin.d.ts +89 -0
- package/esm/WiggleScoreConfigMixin.js +54 -0
- package/esm/autoscale.d.ts +54 -14
- package/esm/autoscale.js +105 -180
- package/esm/autoscaleGroup.d.ts +30 -0
- package/esm/autoscaleGroup.js +97 -0
- package/esm/chrome.d.ts +5 -0
- package/esm/chrome.js +7 -0
- package/esm/computeYTicks.d.ts +1 -1
- package/esm/computeYTicks.js +4 -3
- package/esm/constants.d.ts +1 -2
- package/esm/constants.js +1 -10
- package/esm/dataTypes.d.ts +4 -6
- package/esm/displayModel.d.ts +1 -2
- package/esm/gapBreak.d.ts +2 -3
- package/esm/gapBreak.js +2 -3
- package/esm/index.d.ts +24 -25
- package/esm/index.js +20 -18
- package/esm/normalize.d.ts +1 -26
- package/esm/normalize.js +1 -86
- package/esm/pointMarker.d.ts +7 -2
- package/esm/pointMarker.js +14 -18
- package/esm/pointSizeMenu.d.ts +30 -11
- package/esm/pointSizeMenu.js +42 -19
- package/esm/renderingBackendTypes.d.ts +13 -4
- package/esm/renderingBackendTypes.js +4 -1
- package/esm/rowLabelOffset.d.ts +8 -0
- package/esm/rowLabelOffset.js +16 -0
- package/esm/scale.d.ts +12 -8
- package/esm/scale.js +27 -14
- package/esm/scoreFieldConfigSchemaFields.d.ts +10 -0
- package/esm/scoreFieldConfigSchemaFields.js +8 -0
- package/esm/scoreMenuItems.d.ts +35 -16
- package/esm/scoreMenuItems.js +95 -62
- package/esm/scoreRuleMarks.d.ts +14 -39
- package/esm/scoreRuleMarks.js +12 -45
- package/esm/transferables.js +4 -9
- package/esm/valueScaleConfigSchema.d.ts +253 -0
- package/esm/valueScaleConfigSchema.js +270 -0
- package/esm/visibleStatsDomain.d.ts +20 -8
- package/esm/visibleStatsDomain.js +32 -27
- package/esm/wiggleRenderModes.generated.d.ts +1 -2
- package/esm/wiggleRenderModes.generated.js +1 -2
- package/package.json +14 -9
- package/esm/CrossHatches.d.ts +0 -11
- package/esm/CrossHatches.js +0 -32
- package/esm/ScoreRules.d.ts +0 -11
- package/esm/ScoreRules.js +0 -37
- package/esm/YScaleBar.d.ts +0 -6
- package/esm/YScaleBar.js +0 -46
- package/esm/YScaleBarOverlay.d.ts +0 -6
- package/esm/YScaleBarOverlay.js +0 -24
- package/esm/scoreAxisConfigSchemaFields.d.ts +0 -44
- package/esm/scoreAxisConfigSchemaFields.js +0 -54
- package/esm/yScaleTicks.d.ts +0 -59
- package/esm/yScaleTicks.js +0 -59
package/esm/autoscale.js
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
import { quantileExtent } from '@jbrowse/core/util/quantileExtent';
|
|
2
|
+
/** The wiggle packer's arrays as a span, under a summary mode. */
|
|
3
|
+
export function datasetSpan({ data, visStart, visEnd }, summaryScoreMode) {
|
|
4
|
+
const { low, high } = boundArrays(summaryScoreMode);
|
|
5
|
+
return {
|
|
6
|
+
count: data.numFeatures,
|
|
7
|
+
starts: data.featurePositions,
|
|
8
|
+
ends: data.featurePositions,
|
|
9
|
+
stride: 2,
|
|
10
|
+
endOffset: 1,
|
|
11
|
+
low: low(data),
|
|
12
|
+
high: high(data),
|
|
13
|
+
visStart,
|
|
14
|
+
visEnd,
|
|
15
|
+
sortedBins: true,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
17
18
|
/**
|
|
18
19
|
* #api
|
|
19
20
|
* Per-feature scalar score array for a summary mode: the min/max summary array
|
|
@@ -32,9 +33,8 @@ function overlaps(fStart, fEnd, visStart, visEnd) {
|
|
|
32
33
|
}
|
|
33
34
|
// Which per-feature array each end of the domain comes from. Whiskers spreads
|
|
34
35
|
// the two ends across the min/max summary arrays; every other mode draws both
|
|
35
|
-
// from a single scalar. One table because
|
|
36
|
-
//
|
|
37
|
-
// bound were measured off different arrays is a domain that clips its own data.
|
|
36
|
+
// from a single scalar. One table because the extent and the clipped bound have
|
|
37
|
+
// to read the same arrays, or a domain clips its own data.
|
|
38
38
|
function boundArrays(summaryScoreMode) {
|
|
39
39
|
const useWhiskers = summaryScoreMode === 'whiskers';
|
|
40
40
|
return {
|
|
@@ -46,18 +46,15 @@ function boundArrays(summaryScoreMode) {
|
|
|
46
46
|
: getEffectiveScores(data, summaryScoreMode),
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
// First index whose feature STARTS at or after `bp`. `featurePositions` is
|
|
54
|
-
// sorted by start — the same property `findFeatureAtBp` binary-searches on.
|
|
55
|
-
function lowerBoundByStart(featurePositions, numFeatures, bp) {
|
|
49
|
+
// First index whose instance STARTS at or after `bp`. `starts` is sorted by
|
|
50
|
+
// start — the same property `findFeatureAtBp` binary-searches on.
|
|
51
|
+
function lowerBoundByStart(span, bp) {
|
|
52
|
+
const { starts, stride } = span;
|
|
56
53
|
let lo = 0;
|
|
57
|
-
let hi =
|
|
54
|
+
let hi = span.count;
|
|
58
55
|
while (lo < hi) {
|
|
59
56
|
const mid = (lo + hi) >> 1;
|
|
60
|
-
if (
|
|
57
|
+
if (starts[mid * stride] >= bp) {
|
|
61
58
|
hi = mid;
|
|
62
59
|
}
|
|
63
60
|
else {
|
|
@@ -67,186 +64,114 @@ function lowerBoundByStart(featurePositions, numFeatures, bp) {
|
|
|
67
64
|
return lo;
|
|
68
65
|
}
|
|
69
66
|
/**
|
|
70
|
-
* The half-open index range that can overlap `[visStart, visEnd)`.
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* `localpercentile` (the default) walks it two or three times over. Both bounds
|
|
75
|
-
* come from a binary search on the sorted starts instead.
|
|
76
|
-
*
|
|
77
|
-
* The upper bound needs nothing but that sortedness: a feature starting at or
|
|
78
|
-
* after `visEnd` cannot reach back into the window. The lower bound also leans
|
|
79
|
-
* on wiggle features being non-overlapping bins — bigWig summary levels and
|
|
80
|
-
* bedGraph both are — walking back over any run that does reach in. The callers
|
|
81
|
-
* still test `overlaps` per feature inside the range, so a dataset that broke
|
|
82
|
-
* that assumption could only lose a long early feature, never gain one.
|
|
67
|
+
* The half-open index range that can overlap `[visStart, visEnd)`. Over
|
|
68
|
+
* `sortedBins` a binary search bounds it, walking back over any bin that
|
|
69
|
+
* reaches into the window; any other span is walked whole, and every caller
|
|
70
|
+
* tests `overlaps` per instance inside the range.
|
|
83
71
|
*/
|
|
84
|
-
function visibleIndexRange(
|
|
85
|
-
|
|
86
|
-
|
|
72
|
+
function visibleIndexRange(span) {
|
|
73
|
+
const { visStart, visEnd, ends, stride, endOffset, sortedBins } = span;
|
|
74
|
+
if (!sortedBins || visStart === undefined || visEnd === undefined) {
|
|
75
|
+
return { from: 0, to: span.count };
|
|
87
76
|
}
|
|
88
|
-
const to = lowerBoundByStart(
|
|
89
|
-
let from = lowerBoundByStart(
|
|
90
|
-
while (from > 0 &&
|
|
77
|
+
const to = lowerBoundByStart(span, visEnd);
|
|
78
|
+
let from = lowerBoundByStart(span, visStart);
|
|
79
|
+
while (from > 0 && ends[(from - 1) * stride + endOffset] > visStart) {
|
|
91
80
|
from--;
|
|
92
81
|
}
|
|
93
82
|
return { from, to };
|
|
94
83
|
}
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
84
|
+
// Half-open overlap test against the span's own window, for the drawn
|
|
85
|
+
// instances the index range admits.
|
|
86
|
+
function spanOverlaps(span, i) {
|
|
87
|
+
const { visStart, visEnd, starts, ends, stride, endOffset, drawnKeys } = span;
|
|
88
|
+
return ((drawnKeys === undefined || drawnKeys[span.row?.[i] ?? 0] === 1) &&
|
|
89
|
+
(visStart === undefined ||
|
|
90
|
+
visEnd === undefined ||
|
|
91
|
+
overlaps(starts[i * stride], ends[i * stride + endOffset], visStart, visEnd)));
|
|
92
|
+
}
|
|
93
|
+
// The extremes of the visible instances, in one pass. Exported (not #api —
|
|
94
|
+
// internal plumbing shared with the quantitative displays) so a caller needing
|
|
95
|
+
// both a domain and the raw extent computes them once and feeds them to
|
|
96
|
+
// `autoscaleDomainFromSpans` instead of walking the arrays twice.
|
|
97
|
+
export function computeSpanStats(spans) {
|
|
101
98
|
let min = Infinity;
|
|
102
99
|
let max = -Infinity;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
for (const { data, visStart, visEnd } of datasets) {
|
|
107
|
-
const { featureScores, featurePositions, numFeatures } = data;
|
|
108
|
-
// Selecting the arrays once per dataset keeps the mode check out of the
|
|
109
|
-
// per-feature loop.
|
|
110
|
-
const minScores = low(data);
|
|
111
|
-
const maxScores = high(data);
|
|
112
|
-
const { from, to } = visibleIndexRange(featurePositions, numFeatures, visStart, visEnd);
|
|
100
|
+
for (const span of spans) {
|
|
101
|
+
const { low, high } = span;
|
|
102
|
+
const { from, to } = visibleIndexRange(span);
|
|
113
103
|
for (let i = from; i < to; i++) {
|
|
114
|
-
if (
|
|
115
|
-
visEnd !== undefined &&
|
|
116
|
-
!overlaps(featurePositions[i * 2], featurePositions[i * 2 + 1], visStart, visEnd)) {
|
|
104
|
+
if (!spanOverlaps(span, i)) {
|
|
117
105
|
continue;
|
|
118
106
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
107
|
+
// Non-finite values are skipped rather than folded in: a wig file may
|
|
108
|
+
// carry a NaN, and one of them poisons min/max, collapsing the whole
|
|
109
|
+
// domain to the [0, 1] stub the callers fall back to.
|
|
110
|
+
const lo = low[i];
|
|
111
|
+
if (Number.isFinite(lo)) {
|
|
112
|
+
min = Math.min(min, lo);
|
|
113
|
+
}
|
|
114
|
+
const hi = high[i];
|
|
115
|
+
if (Number.isFinite(hi)) {
|
|
116
|
+
max = Math.max(max, hi);
|
|
117
|
+
}
|
|
129
118
|
}
|
|
130
119
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
120
|
+
return Number.isFinite(min) && Number.isFinite(max)
|
|
121
|
+
? { scoreMin: min, scoreMax: max }
|
|
122
|
+
: undefined;
|
|
123
|
+
}
|
|
124
|
+
/** `computeSpanStats` over the wiggle packer's datasets. */
|
|
125
|
+
export function computeScoreStats(summaryScoreMode, datasets) {
|
|
126
|
+
return computeSpanStats(datasets.map(d => datasetSpan(d, summaryScoreMode)));
|
|
137
127
|
}
|
|
138
128
|
/**
|
|
139
129
|
* #api
|
|
140
|
-
*
|
|
141
|
-
*
|
|
130
|
+
* The domain the visible instances autoscale to: at a `quantile` of 1 their
|
|
131
|
+
* extremes, `stats`; below it `quantileExtent`'s ends, the bottom read off
|
|
132
|
+
* the `low`s and the top off the `high`s less the zeros where any is
|
|
133
|
+
* positive, so whiskers open to their spread, a sparse window's empty bins
|
|
134
|
+
* lower no top, and a window of nothing above 0 keeps the top it has.
|
|
142
135
|
*/
|
|
143
|
-
export function
|
|
144
|
-
if (
|
|
145
|
-
|
|
146
|
-
return [
|
|
147
|
-
scoreMin >= 0 ? 0 : scoreMean - numStdDev * scoreStdDev,
|
|
148
|
-
scoreMean + numStdDev * scoreStdDev,
|
|
149
|
-
];
|
|
136
|
+
export function autoscaleDomainFromSpans({ stats, quantile, spans, }) {
|
|
137
|
+
if (quantile >= 1) {
|
|
138
|
+
return [stats.scoreMin, stats.scoreMax];
|
|
150
139
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
// of that side's mass falls is returned — clipping the outermost `1 - quantile`
|
|
157
|
-
// as outliers. Returns 0 when the side is empty. A fixed histogram keeps this an
|
|
158
|
-
// O(n) pass with no sort, approximate to ~1/NUM_HISTOGRAM_BINS of maxMag, far
|
|
159
|
-
// finer than the display needs.
|
|
160
|
-
function sideMagnitudePercentile(datasets, scoresFor, positiveSide, maxMag, quantile) {
|
|
161
|
-
if (maxMag <= 0) {
|
|
162
|
-
return 0;
|
|
163
|
-
}
|
|
164
|
-
const bins = new Int32Array(NUM_HISTOGRAM_BINS);
|
|
165
|
-
const scale = NUM_HISTOGRAM_BINS / maxMag;
|
|
166
|
-
let count = 0;
|
|
167
|
-
for (const { data, visStart, visEnd } of datasets) {
|
|
168
|
-
const { featurePositions, numFeatures } = data;
|
|
169
|
-
const scores = scoresFor(data);
|
|
170
|
-
const { from, to } = visibleIndexRange(featurePositions, numFeatures, visStart, visEnd);
|
|
140
|
+
const lows = [];
|
|
141
|
+
const highs = [];
|
|
142
|
+
for (const span of spans) {
|
|
143
|
+
const { low, high } = span;
|
|
144
|
+
const { from, to } = visibleIndexRange(span);
|
|
171
145
|
for (let i = from; i < to; i++) {
|
|
172
|
-
if (
|
|
173
|
-
visEnd !== undefined &&
|
|
174
|
-
!overlaps(featurePositions[i * 2], featurePositions[i * 2 + 1], visStart, visEnd)) {
|
|
146
|
+
if (!spanOverlaps(span, i)) {
|
|
175
147
|
continue;
|
|
176
148
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const bin = Math.min(NUM_HISTOGRAM_BINS - 1, Math.floor(mag * scale));
|
|
180
|
-
bins[bin]++;
|
|
181
|
-
count++;
|
|
182
|
-
}
|
|
149
|
+
lows.push(low[i]);
|
|
150
|
+
highs.push(high[i]);
|
|
183
151
|
}
|
|
184
152
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
let cumulative = 0;
|
|
190
|
-
for (let bin = 0; bin < NUM_HISTOGRAM_BINS; bin++) {
|
|
191
|
-
cumulative += bins[bin];
|
|
192
|
-
// +1 so the target bin is included rather than clipped.
|
|
193
|
-
if (cumulative >= target) {
|
|
194
|
-
return ((bin + 1) / NUM_HISTOGRAM_BINS) * maxMag;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
return maxMag;
|
|
198
|
-
}
|
|
199
|
-
// Builds a `[low, high]` domain by clipping each side of the score distribution
|
|
200
|
-
// to its central `quantile` fraction (e.g. 0.99 → clip the outermost 1% of each
|
|
201
|
-
// sign). Unlike localsd it makes no normality assumption, so it stays robust on
|
|
202
|
-
// the heavily skewed score distributions typical of coverage/wiggle data.
|
|
203
|
-
//
|
|
204
|
-
// The two signs are clipped INDEPENDENTLY, anchored at 0. A single combined
|
|
205
|
-
// percentile spends its whole budget on the dominant side, so on strongly
|
|
206
|
-
// one-sided signed data (e.g. phyloP: mostly-positive conservation with a
|
|
207
|
-
// sparse, small negative tail) the minority tail's 1st percentile lands at or
|
|
208
|
-
// above 0 and the negative extent collapses to a flat band. Measuring each
|
|
209
|
-
// side's percentile from 0 outward keeps a small-but-real opposite tail visible.
|
|
210
|
-
function percentileDomainFromHistogram(stats, summaryScoreMode, quantile, datasets) {
|
|
211
|
-
const { scoreMin, scoreMax } = stats;
|
|
212
|
-
if (scoreMax - scoreMin <= 0) {
|
|
213
|
-
return [scoreMin, scoreMax];
|
|
214
|
-
}
|
|
215
|
-
const arrays = boundArrays(summaryScoreMode);
|
|
216
|
-
const high = scoreMax > 0
|
|
217
|
-
? sideMagnitudePercentile(datasets, arrays.high, true, scoreMax, quantile)
|
|
218
|
-
: 0;
|
|
219
|
-
const negExtent = scoreMin < 0
|
|
220
|
-
? sideMagnitudePercentile(datasets, arrays.low, false, -scoreMin, quantile)
|
|
221
|
-
: 0;
|
|
222
|
-
// Anchor the low bound at 0 for all-positive data (matching localsd's origin
|
|
223
|
-
// convention); otherwise extend it to the negative side's clipped extent.
|
|
224
|
-
return [scoreMin < 0 ? -negExtent : 0, high];
|
|
153
|
+
const tops = highs.some(v => v > 0) ? highs.filter(v => v !== 0) : highs;
|
|
154
|
+
const min = quantileExtent(lows, lows.length, quantile)[0];
|
|
155
|
+
const max = quantileExtent(tops, tops.length, quantile)[1];
|
|
156
|
+
return [Number.isFinite(min) ? min : 0, Number.isFinite(max) ? max : 0];
|
|
225
157
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
: domainFromStats(stats, autoscaleType, numStdDev);
|
|
158
|
+
/** `autoscaleDomainFromSpans` over the wiggle packer's datasets. */
|
|
159
|
+
export function autoscaleDomainFromStats({ stats, quantile, summaryScoreMode, visibleEntries, }) {
|
|
160
|
+
return autoscaleDomainFromSpans({
|
|
161
|
+
stats,
|
|
162
|
+
quantile,
|
|
163
|
+
spans: visibleEntries.map(d => datasetSpan(d, summaryScoreMode)),
|
|
164
|
+
});
|
|
234
165
|
}
|
|
235
166
|
/**
|
|
236
167
|
* #api
|
|
237
|
-
*
|
|
238
|
-
* `
|
|
168
|
+
* The score domain of the visible feature arrays, following `quantile` as
|
|
169
|
+
* `scales.y.domainQuantile` says.
|
|
239
170
|
*/
|
|
240
|
-
export function computeAutoscaleDomain(
|
|
241
|
-
const
|
|
171
|
+
export function computeAutoscaleDomain(quantile, summaryScoreMode, visibleEntries) {
|
|
172
|
+
const spans = visibleEntries.map(d => datasetSpan(d, summaryScoreMode));
|
|
173
|
+
const stats = computeSpanStats(spans);
|
|
242
174
|
return stats
|
|
243
|
-
?
|
|
244
|
-
stats,
|
|
245
|
-
autoscaleType,
|
|
246
|
-
summaryScoreMode,
|
|
247
|
-
numStdDev,
|
|
248
|
-
numQuantile,
|
|
249
|
-
visibleEntries,
|
|
250
|
-
})
|
|
175
|
+
? autoscaleDomainFromSpans({ stats, quantile, spans })
|
|
251
176
|
: undefined;
|
|
252
177
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { IAnyStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
2
|
+
/** A display whose value scale can autoscale beside others. */
|
|
3
|
+
export interface AutoscaleGroupMember {
|
|
4
|
+
autoscaleGroup: string | undefined;
|
|
5
|
+
autoscaleRange: [number, number] | undefined;
|
|
6
|
+
}
|
|
7
|
+
/** A member the score menu can move into or out of a group. */
|
|
8
|
+
export interface AutoscalePeer extends AutoscaleGroupMember {
|
|
9
|
+
setAutoscaleGroup: (group?: string) => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Every display in `node`'s view whose value scale names `group`, `node`
|
|
13
|
+
* among them.
|
|
14
|
+
*/
|
|
15
|
+
export declare function autoscaleGroupMembers(node: IAnyStateTreeNode, group: string): AutoscaleGroupMember[];
|
|
16
|
+
/** The span every defined range covers; undefined where none is. */
|
|
17
|
+
export declare function unionRanges(ranges: readonly ([number, number] | undefined)[]): [number, number] | undefined;
|
|
18
|
+
/** The other displays in `node`'s view with a value scale, by track name. */
|
|
19
|
+
export declare function autoscalePeers(node: IAnyStateTreeNode): {
|
|
20
|
+
display: AutoscalePeer;
|
|
21
|
+
key: string;
|
|
22
|
+
name: string;
|
|
23
|
+
}[];
|
|
24
|
+
/**
|
|
25
|
+
* Put `self` and `chosen` in one group, and take the group off every peer that
|
|
26
|
+
* held it and is not chosen. The group keeps `self`'s name, or a chosen
|
|
27
|
+
* peer's, and is named afresh where neither has one; choosing nobody takes
|
|
28
|
+
* `self` out of its group.
|
|
29
|
+
*/
|
|
30
|
+
export declare function autoscaleWith(self: AutoscalePeer, peers: readonly AutoscalePeer[], chosen: ReadonlySet<AutoscalePeer>): void;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { getConf } from '@jbrowse/core/configuration';
|
|
2
|
+
import { getContainingTrack, getContainingView, } from '@jbrowse/core/util/mstUtils';
|
|
3
|
+
import { hasParent, isStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
4
|
+
function isMember(thing) {
|
|
5
|
+
return (typeof thing === 'object' &&
|
|
6
|
+
thing !== null &&
|
|
7
|
+
'autoscaleGroup' in thing &&
|
|
8
|
+
'autoscaleRange' in thing);
|
|
9
|
+
}
|
|
10
|
+
function isPeer(thing) {
|
|
11
|
+
return isMember(thing) && 'setAutoscaleGroup' in thing;
|
|
12
|
+
}
|
|
13
|
+
function displaysOf(track) {
|
|
14
|
+
return typeof track === 'object' &&
|
|
15
|
+
track !== null &&
|
|
16
|
+
'displays' in track &&
|
|
17
|
+
Array.isArray(track.displays)
|
|
18
|
+
? track.displays
|
|
19
|
+
: [];
|
|
20
|
+
}
|
|
21
|
+
function containingView(node) {
|
|
22
|
+
if (!isStateTreeNode(node) || !hasParent(node)) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
return getContainingView(node);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// A display standing outside any view, as a unit test builds one, shares its
|
|
33
|
+
// axis with nothing.
|
|
34
|
+
function viewDisplays(node) {
|
|
35
|
+
const view = containingView(node);
|
|
36
|
+
const tracks = view && 'tracks' in view && Array.isArray(view.tracks) ? view.tracks : [];
|
|
37
|
+
return tracks.flatMap(displaysOf);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Every display in `node`'s view whose value scale names `group`, `node`
|
|
41
|
+
* among them.
|
|
42
|
+
*/
|
|
43
|
+
export function autoscaleGroupMembers(node, group) {
|
|
44
|
+
return viewDisplays(node).filter((display) => isMember(display) && display.autoscaleGroup === group);
|
|
45
|
+
}
|
|
46
|
+
/** The span every defined range covers; undefined where none is. */
|
|
47
|
+
export function unionRanges(ranges) {
|
|
48
|
+
let union;
|
|
49
|
+
for (const range of ranges) {
|
|
50
|
+
if (range) {
|
|
51
|
+
union = union
|
|
52
|
+
? [Math.min(union[0], range[0]), Math.max(union[1], range[1])]
|
|
53
|
+
: [range[0], range[1]];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return union;
|
|
57
|
+
}
|
|
58
|
+
/** The other displays in `node`'s view with a value scale, by track name. */
|
|
59
|
+
export function autoscalePeers(node) {
|
|
60
|
+
return viewDisplays(node).flatMap(display => {
|
|
61
|
+
if (!isPeer(display) || display === node) {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
const track = getContainingTrack(display);
|
|
65
|
+
return [{ display, key: track.id, name: String(getConf(track, 'name')) }];
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function freeGroupName(taken) {
|
|
69
|
+
let n = 1;
|
|
70
|
+
while (taken.has(`group${n}`)) {
|
|
71
|
+
n++;
|
|
72
|
+
}
|
|
73
|
+
return `group${n}`;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Put `self` and `chosen` in one group, and take the group off every peer that
|
|
77
|
+
* held it and is not chosen. The group keeps `self`'s name, or a chosen
|
|
78
|
+
* peer's, and is named afresh where neither has one; choosing nobody takes
|
|
79
|
+
* `self` out of its group.
|
|
80
|
+
*/
|
|
81
|
+
export function autoscaleWith(self, peers, chosen) {
|
|
82
|
+
const previous = self.autoscaleGroup;
|
|
83
|
+
const group = chosen.size === 0
|
|
84
|
+
? undefined
|
|
85
|
+
: (previous ??
|
|
86
|
+
[...chosen].find(p => p.autoscaleGroup)?.autoscaleGroup ??
|
|
87
|
+
freeGroupName(new Set(peers.map(p => p.autoscaleGroup))));
|
|
88
|
+
self.setAutoscaleGroup(group);
|
|
89
|
+
for (const peer of peers) {
|
|
90
|
+
if (chosen.has(peer)) {
|
|
91
|
+
peer.setAutoscaleGroup(group);
|
|
92
|
+
}
|
|
93
|
+
else if (group !== undefined && peer.autoscaleGroup === previous) {
|
|
94
|
+
peer.setAutoscaleGroup(undefined);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
package/esm/chrome.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { AxisGutter, CrossHatchLines, CrossHatches, ScoreDomainCaption, ScoreRuleLines, ScoreRules, YScaleBar, YScaleBarOverlay, } from '@jbrowse/display-ui';
|
|
2
|
+
export { makeResolutionSubMenuItem } from './ResolutionStepper.tsx';
|
|
3
|
+
export { makePointSizeSubMenu, pointSizeAccess } from './pointSizeMenu.tsx';
|
|
4
|
+
export type { PointSizeAccess } from './pointSizeMenu.tsx';
|
|
5
|
+
export { default as SetMinMaxDialog } from './SetMinMaxDialog.tsx';
|
package/esm/chrome.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// What draws a score plot, kept out of the package entry so the entry's score
|
|
2
|
+
// math and config mixins stay real in the RPC worker. A display imports these;
|
|
3
|
+
// an adapter or an RPC method never does.
|
|
4
|
+
export { AxisGutter, CrossHatchLines, CrossHatches, ScoreDomainCaption, ScoreRuleLines, ScoreRules, YScaleBar, YScaleBarOverlay, } from '@jbrowse/display-ui';
|
|
5
|
+
export { makeResolutionSubMenuItem } from './ResolutionStepper.js';
|
|
6
|
+
export { makePointSizeSubMenu, pointSizeAccess } from './pointSizeMenu.js';
|
|
7
|
+
export { default as SetMinMaxDialog } from './SetMinMaxDialog.js';
|
package/esm/computeYTicks.d.ts
CHANGED
package/esm/computeYTicks.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { YSCALEBAR_LABEL_OFFSET } from '
|
|
1
|
+
import { YSCALEBAR_LABEL_OFFSET } from '@jbrowse/display-ui/yAxisConstants';
|
|
2
|
+
import { axisPlotBox } from '@jbrowse/display-ui/yScaleTicks';
|
|
2
3
|
import { getScale } from './scale.js';
|
|
3
|
-
import { axisPlotBox } from './yScaleTicks.js';
|
|
4
4
|
// Builds Y-axis tick positions for a wiggle-family display: tick values come
|
|
5
5
|
// from d3's scale.ticks(4) at normal heights, or fall back to the domain
|
|
6
6
|
// endpoints for short tracks / when the user opts into minimal ticks.
|
|
@@ -18,7 +18,8 @@ export function computeYTicks(opts) {
|
|
|
18
18
|
const { yTop, yBottom } = axisPlotBox(height, offset);
|
|
19
19
|
// A window whose scores are all one value (an all-zero coverage stretch) has
|
|
20
20
|
// a domain of no width. d3 answers the midpoint for every input there while
|
|
21
|
-
// `makeScoreNormalizer`
|
|
21
|
+
// `makeScoreNormalizer` puts that value on the baseline, and the renderer is
|
|
22
|
+
// the one to agree with.
|
|
22
23
|
if (domainMin === domainMax) {
|
|
23
24
|
return { items: [{ value: domainMin, y: yBottom }], yTop, yBottom };
|
|
24
25
|
}
|
package/esm/constants.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
export declare const ONSCREEN_AXIS_LEFT_PX = 50;
|
|
1
|
+
export { YSCALEBAR_LABEL_OFFSET } from '@jbrowse/display-ui/yAxisConstants';
|
package/esm/constants.js
CHANGED
|
@@ -1,10 +1 @@
|
|
|
1
|
-
|
|
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;
|
|
1
|
+
export { YSCALEBAR_LABEL_OFFSET } from '@jbrowse/display-ui/yAxisConstants';
|
package/esm/dataTypes.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ZoomRange } from '@jbrowse/core/data_adapters/BaseAdapter/zoomRange';
|
|
1
2
|
export interface SourceInfo {
|
|
2
3
|
name: string;
|
|
3
4
|
color?: string;
|
|
@@ -13,14 +14,11 @@ export interface WiggleFeatureArrays {
|
|
|
13
14
|
featureMaxScores: Float32Array;
|
|
14
15
|
numFeatures: number;
|
|
15
16
|
hasSummaryScores: boolean;
|
|
16
|
-
posFeaturePositions: Uint32Array;
|
|
17
|
-
posFeatureScores: Float32Array;
|
|
18
|
-
posNumFeatures: number;
|
|
19
|
-
negFeaturePositions: Uint32Array;
|
|
20
|
-
negFeatureScores: Float32Array;
|
|
21
|
-
negNumFeatures: number;
|
|
22
17
|
}
|
|
23
18
|
export type WiggleSourceData = SourceInfo & WiggleFeatureArrays;
|
|
24
19
|
export interface WiggleDataResult {
|
|
25
20
|
sources: WiggleSourceData[];
|
|
21
|
+
zoomRange?: ZoomRange;
|
|
22
|
+
/** The bounds the adapter's values lie in by definition (`getValueDomain`). */
|
|
23
|
+
valueDomain?: [number, number];
|
|
26
24
|
}
|
package/esm/displayModel.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { WiggleDataResult } from './dataTypes.ts';
|
|
2
2
|
import type { WiggleRenderingBackend } from './renderingBackendTypes.ts';
|
|
3
|
-
import type { YScaleTicks } from './yScaleTicks.ts';
|
|
4
3
|
import type { RegionHost } from '@jbrowse/display-kit/regionHost';
|
|
4
|
+
import type { YScaleTicks } from '@jbrowse/display-ui';
|
|
5
5
|
import type { DisplayPhase } from '@jbrowse/render-core/displayPhase';
|
|
6
6
|
import type { RenderLifecycleModel } from '@jbrowse/render-core/useRenderingBackend';
|
|
7
7
|
export interface WiggleGpuDisplayModel<TRenderingBackend = WiggleRenderingBackend, TData = WiggleDataResult> extends RenderLifecycleModel<TRenderingBackend> {
|
|
@@ -20,5 +20,4 @@ export interface WiggleGpuDisplayModel<TRenderingBackend = WiggleRenderingBacken
|
|
|
20
20
|
regionTooLargeReason: string;
|
|
21
21
|
zoomCanReleaseGate: boolean;
|
|
22
22
|
forceLoad: () => void;
|
|
23
|
-
showCrossHatches: boolean;
|
|
24
23
|
}
|
package/esm/gapBreak.d.ts
CHANGED
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
* draw. Returns `Infinity` when there is nothing to decide, so a caller can
|
|
6
6
|
* always compare against it unguarded.
|
|
7
7
|
*
|
|
8
|
-
* The limit is a multiple of the series'
|
|
9
|
-
* absolute distance,
|
|
10
|
-
* data types: reduced BigWig bins get wider as you zoom out, so any fixed bp
|
|
8
|
+
* The limit is a multiple of the series' *mean* spacing rather than an
|
|
9
|
+
* absolute distance, so one number works across zoom levels and data types: reduced BigWig bins get wider as you zoom out, so any fixed bp
|
|
11
10
|
* threshold would be either useless at one end or destructive at the other. It
|
|
12
11
|
* also lets the same rule serve a bp axis (wiggle's linecenter) and a px one —
|
|
13
12
|
* the caller picks the space, this only cares that the units are consistent.
|
package/esm/gapBreak.js
CHANGED
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
* draw. Returns `Infinity` when there is nothing to decide, so a caller can
|
|
6
6
|
* always compare against it unguarded.
|
|
7
7
|
*
|
|
8
|
-
* The limit is a multiple of the series'
|
|
9
|
-
* absolute distance,
|
|
10
|
-
* data types: reduced BigWig bins get wider as you zoom out, so any fixed bp
|
|
8
|
+
* The limit is a multiple of the series' *mean* spacing rather than an
|
|
9
|
+
* absolute distance, so one number works across zoom levels and data types: reduced BigWig bins get wider as you zoom out, so any fixed bp
|
|
11
10
|
* threshold would be either useless at one end or destructive at the other. It
|
|
12
11
|
* also lets the same rule serve a bp axis (wiggle's linecenter) and a px one —
|
|
13
12
|
* the caller picks the space, this only cares that the units are consistent.
|