@jbrowse/plugin-wiggle 2.7.0 → 2.7.2
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/dist/DensityRenderer/DensityRenderer.js +2 -2
- package/dist/LinePlotRenderer/LinePlotRenderer.js +2 -2
- package/dist/MultiDensityRenderer/MultiDensityRenderer.js +5 -8
- package/dist/MultiLineRenderer/MultiLineRenderer.js +5 -9
- package/dist/MultiLinearWiggleDisplay/components/SetColorDialog.js +2 -102
- package/dist/MultiLinearWiggleDisplay/components/SourcesGrid.d.ts +8 -0
- package/dist/MultiLinearWiggleDisplay/components/SourcesGrid.js +138 -0
- package/dist/MultiRowLineRenderer/MultiRowLineRenderer.js +5 -5
- package/dist/MultiRowXYPlotRenderer/MultiRowXYPlotRenderer.js +5 -9
- package/dist/MultiXYPlotRenderer/MultiXYPlotRenderer.js +5 -8
- package/dist/Tooltip.js +4 -1
- package/dist/XYPlotRenderer/XYPlotRenderer.js +2 -2
- package/dist/drawDensity.d.ts +17 -0
- package/dist/drawDensity.js +66 -0
- package/dist/drawLine.d.ts +19 -0
- package/dist/drawLine.js +73 -0
- package/dist/drawXY.d.ts +19 -0
- package/dist/{drawxy.js → drawXY.js} +11 -147
- package/dist/util.d.ts +1 -0
- package/dist/util.js +17 -1
- package/esm/DensityRenderer/DensityRenderer.js +1 -1
- package/esm/LinePlotRenderer/LinePlotRenderer.js +1 -1
- package/esm/MultiDensityRenderer/MultiDensityRenderer.js +4 -7
- package/esm/MultiLineRenderer/MultiLineRenderer.js +4 -8
- package/esm/MultiLinearWiggleDisplay/components/SetColorDialog.js +2 -102
- package/esm/MultiLinearWiggleDisplay/components/SourcesGrid.d.ts +8 -0
- package/esm/MultiLinearWiggleDisplay/components/SourcesGrid.js +110 -0
- package/esm/MultiRowLineRenderer/MultiRowLineRenderer.js +4 -4
- package/esm/MultiRowXYPlotRenderer/MultiRowXYPlotRenderer.js +4 -8
- package/esm/MultiXYPlotRenderer/MultiXYPlotRenderer.js +4 -7
- package/esm/Tooltip.js +5 -2
- package/esm/XYPlotRenderer/XYPlotRenderer.js +1 -1
- package/esm/drawDensity.d.ts +17 -0
- package/esm/drawDensity.js +62 -0
- package/esm/drawLine.d.ts +19 -0
- package/esm/drawLine.js +69 -0
- package/esm/drawXY.d.ts +19 -0
- package/esm/{drawxy.js → drawXY.js} +2 -136
- package/esm/util.d.ts +1 -0
- package/esm/util.js +15 -0
- package/package.json +2 -2
- package/dist/drawxy.d.ts +0 -49
- package/esm/drawxy.d.ts +0 -49
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { groupBy } from '@jbrowse/core/util';
|
|
2
|
-
import { drawLine } from '../
|
|
2
|
+
import { drawLine } from '../drawLine';
|
|
3
3
|
import WiggleBaseRenderer from '../WiggleBaseRenderer';
|
|
4
4
|
export default class MultiRowLineRenderer extends WiggleBaseRenderer {
|
|
5
5
|
// @ts-expect-error
|
|
6
6
|
async draw(ctx, props) {
|
|
7
7
|
const { bpPerPx, sources, regions, features } = props;
|
|
8
8
|
const [region] = regions;
|
|
9
|
-
const groups = groupBy(
|
|
9
|
+
const groups = groupBy(features.values(), f => f.get('source'));
|
|
10
10
|
const height = props.height / sources.length;
|
|
11
11
|
const width = (region.end - region.start) / bpPerPx;
|
|
12
12
|
let feats = [];
|
|
@@ -14,7 +14,7 @@ export default class MultiRowLineRenderer extends WiggleBaseRenderer {
|
|
|
14
14
|
sources.forEach(source => {
|
|
15
15
|
const { reducedFeatures } = drawLine(ctx, {
|
|
16
16
|
...props,
|
|
17
|
-
features: groups[source.name],
|
|
17
|
+
features: groups[source.name] || [],
|
|
18
18
|
height,
|
|
19
19
|
colorCallback: () => source.color || 'blue',
|
|
20
20
|
});
|
|
@@ -24,7 +24,7 @@ export default class MultiRowLineRenderer extends WiggleBaseRenderer {
|
|
|
24
24
|
ctx.lineTo(width, height);
|
|
25
25
|
ctx.stroke();
|
|
26
26
|
ctx.translate(0, height);
|
|
27
|
-
feats =
|
|
27
|
+
feats = feats.concat(reducedFeatures);
|
|
28
28
|
});
|
|
29
29
|
ctx.restore();
|
|
30
30
|
return { reducedFeatures: feats };
|
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
import { groupBy } from '@jbrowse/core/util';
|
|
2
|
-
import { drawXY } from '../
|
|
2
|
+
import { drawXY } from '../drawXY';
|
|
3
3
|
import WiggleBaseRenderer from '../WiggleBaseRenderer';
|
|
4
4
|
export default class MultiXYPlotRenderer extends WiggleBaseRenderer {
|
|
5
5
|
// @ts-expect-error
|
|
6
6
|
async draw(ctx, props) {
|
|
7
7
|
const { bpPerPx, sources, regions, features } = props;
|
|
8
8
|
const [region] = regions;
|
|
9
|
-
const groups = groupBy(
|
|
9
|
+
const groups = groupBy(features.values(), f => f.get('source'));
|
|
10
10
|
const height = props.height / sources.length;
|
|
11
11
|
const width = (region.end - region.start) / bpPerPx;
|
|
12
12
|
let feats = [];
|
|
13
13
|
ctx.save();
|
|
14
14
|
sources.forEach(source => {
|
|
15
|
-
const features = groups[source.name];
|
|
16
|
-
if (!features) {
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
15
|
const { reducedFeatures } = drawXY(ctx, {
|
|
20
16
|
...props,
|
|
21
|
-
features,
|
|
17
|
+
features: groups[source.name] || [],
|
|
22
18
|
height,
|
|
23
19
|
colorCallback: () => source.color || 'blue',
|
|
24
20
|
});
|
|
@@ -28,7 +24,7 @@ export default class MultiXYPlotRenderer extends WiggleBaseRenderer {
|
|
|
28
24
|
ctx.lineTo(width, height);
|
|
29
25
|
ctx.stroke();
|
|
30
26
|
ctx.translate(0, height);
|
|
31
|
-
feats =
|
|
27
|
+
feats = feats.concat(reducedFeatures);
|
|
32
28
|
});
|
|
33
29
|
ctx.restore();
|
|
34
30
|
return { reducedFeatures: feats };
|
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
import { groupBy } from '@jbrowse/core/util';
|
|
2
|
-
import { drawXY } from '../
|
|
2
|
+
import { drawXY } from '../drawXY';
|
|
3
3
|
import { YSCALEBAR_LABEL_OFFSET } from '../util';
|
|
4
4
|
import WiggleBaseRenderer from '../WiggleBaseRenderer';
|
|
5
5
|
export default class MultiXYPlotRenderer extends WiggleBaseRenderer {
|
|
6
6
|
// @ts-expect-error
|
|
7
7
|
async draw(ctx, props) {
|
|
8
8
|
const { sources, features } = props;
|
|
9
|
-
const groups = groupBy(
|
|
9
|
+
const groups = groupBy(features.values(), f => f.get('source'));
|
|
10
10
|
let feats = [];
|
|
11
11
|
for (const source of sources) {
|
|
12
|
-
const features = groups[source.name];
|
|
13
|
-
if (!features) {
|
|
14
|
-
continue;
|
|
15
|
-
}
|
|
12
|
+
const features = groups[source.name] || [];
|
|
16
13
|
const { reducedFeatures } = drawXY(ctx, {
|
|
17
14
|
...props,
|
|
18
15
|
features,
|
|
19
16
|
offset: YSCALEBAR_LABEL_OFFSET,
|
|
20
17
|
colorCallback: () => source.color || 'blue',
|
|
21
18
|
});
|
|
22
|
-
feats =
|
|
19
|
+
feats = feats.concat(reducedFeatures);
|
|
23
20
|
}
|
|
24
21
|
return { reducedFeatures: feats };
|
|
25
22
|
}
|
package/esm/Tooltip.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useMemo, useState } from 'react';
|
|
2
2
|
import { observer } from 'mobx-react';
|
|
3
|
-
import { alpha, Portal } from '@mui/material';
|
|
3
|
+
import { alpha, Portal, useTheme } from '@mui/material';
|
|
4
4
|
import { makeStyles } from 'tss-react/mui';
|
|
5
5
|
// locals
|
|
6
6
|
import { YSCALEBAR_LABEL_OFFSET, round } from './util';
|
|
@@ -33,6 +33,8 @@ const useStyles = makeStyles()(theme => ({
|
|
|
33
33
|
},
|
|
34
34
|
}));
|
|
35
35
|
const Tooltip = observer(function Tooltip({ model, height, clientMouseCoord, offsetMouseCoord, clientRect, TooltipContents, useClientY, }) {
|
|
36
|
+
var _a, _b;
|
|
37
|
+
const theme = useTheme();
|
|
36
38
|
const { featureUnderMouse } = model;
|
|
37
39
|
const [width, setWidth] = useState(0);
|
|
38
40
|
const [anchorEl, setAnchorEl] = useState(null);
|
|
@@ -56,8 +58,9 @@ const Tooltip = observer(function Tooltip({ model, height, clientMouseCoord, off
|
|
|
56
58
|
},
|
|
57
59
|
}), [clientRect === null || clientRect === void 0 ? void 0 : clientRect.top, clientMouseCoord, width, useClientY]);
|
|
58
60
|
const { styles, attributes } = usePopper(virtElement, anchorEl);
|
|
61
|
+
const popperTheme = (_a = theme === null || theme === void 0 ? void 0 : theme.components) === null || _a === void 0 ? void 0 : _a.MuiPopper;
|
|
59
62
|
return featureUnderMouse ? (React.createElement(React.Fragment, null,
|
|
60
|
-
React.createElement(Portal, null,
|
|
63
|
+
React.createElement(Portal, { container: (_b = popperTheme === null || popperTheme === void 0 ? void 0 : popperTheme.defaultProps) === null || _b === void 0 ? void 0 : _b.container },
|
|
61
64
|
React.createElement("div", { ref: setAnchorEl, className: classes.tooltip,
|
|
62
65
|
// zIndex needed to go over widget drawer
|
|
63
66
|
style: { ...styles.popper, zIndex: 100000 }, ...attributes.popper },
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readConfObject } from '@jbrowse/core/configuration';
|
|
2
|
-
import { drawXY } from '../
|
|
2
|
+
import { drawXY } from '../drawXY';
|
|
3
3
|
import WiggleBaseRenderer from '../WiggleBaseRenderer';
|
|
4
4
|
import { YSCALEBAR_LABEL_OFFSET } from '../util';
|
|
5
5
|
export default class XYPlotRenderer extends WiggleBaseRenderer {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
2
|
+
import { Feature, Region } from '@jbrowse/core/util';
|
|
3
|
+
import { ScaleOpts } from './util';
|
|
4
|
+
export declare function drawDensity(ctx: CanvasRenderingContext2D, props: {
|
|
5
|
+
features: Map<string, Feature> | Feature[];
|
|
6
|
+
regions: Region[];
|
|
7
|
+
bpPerPx: number;
|
|
8
|
+
scaleOpts: ScaleOpts;
|
|
9
|
+
height: number;
|
|
10
|
+
ticks: {
|
|
11
|
+
values: number[];
|
|
12
|
+
};
|
|
13
|
+
displayCrossHatches: boolean;
|
|
14
|
+
config: AnyConfigurationModel;
|
|
15
|
+
}): {
|
|
16
|
+
reducedFeatures: Feature[];
|
|
17
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { readConfObject, } from '@jbrowse/core/configuration';
|
|
2
|
+
import { featureSpanPx } from '@jbrowse/core/util';
|
|
3
|
+
// locals
|
|
4
|
+
import { fillRectCtx, getScale } from './util';
|
|
5
|
+
const fudgeFactor = 0.3;
|
|
6
|
+
const clipHeight = 2;
|
|
7
|
+
export function drawDensity(ctx, props) {
|
|
8
|
+
const { features, regions, bpPerPx, scaleOpts, height, config } = props;
|
|
9
|
+
const [region] = regions;
|
|
10
|
+
const pivot = readConfObject(config, 'bicolorPivot');
|
|
11
|
+
const pivotValue = readConfObject(config, 'bicolorPivotValue');
|
|
12
|
+
const negColor = readConfObject(config, 'negColor');
|
|
13
|
+
const posColor = readConfObject(config, 'posColor');
|
|
14
|
+
const color = readConfObject(config, 'color');
|
|
15
|
+
const clipColor = readConfObject(config, 'clipColor');
|
|
16
|
+
const crossing = pivot !== 'none' && scaleOpts.scaleType !== 'log';
|
|
17
|
+
const scale = getScale({
|
|
18
|
+
...scaleOpts,
|
|
19
|
+
pivotValue: crossing ? pivotValue : undefined,
|
|
20
|
+
range: crossing ? [negColor, 'white', posColor] : ['white', posColor],
|
|
21
|
+
});
|
|
22
|
+
const scale2 = getScale({ ...scaleOpts, range: [0, height] });
|
|
23
|
+
const cb = color === '#f0f'
|
|
24
|
+
? (_, score) => scale(score)
|
|
25
|
+
: (feature, score) => readConfObject(config, 'color', { feature, score });
|
|
26
|
+
const [niceMin, niceMax] = scale2.domain();
|
|
27
|
+
let prevLeftPx = -Infinity;
|
|
28
|
+
let hasClipping = false;
|
|
29
|
+
const reducedFeatures = [];
|
|
30
|
+
for (const feature of features.values()) {
|
|
31
|
+
const [leftPx, rightPx] = featureSpanPx(feature, region, bpPerPx);
|
|
32
|
+
// create reduced features, avoiding multiple features per px
|
|
33
|
+
if (Math.floor(leftPx) !== Math.floor(prevLeftPx)) {
|
|
34
|
+
reducedFeatures.push(feature);
|
|
35
|
+
prevLeftPx = leftPx;
|
|
36
|
+
}
|
|
37
|
+
const score = feature.get('score');
|
|
38
|
+
hasClipping = hasClipping || score > niceMax || score < niceMin;
|
|
39
|
+
const w = rightPx - leftPx + fudgeFactor;
|
|
40
|
+
ctx.fillStyle = cb(feature, score);
|
|
41
|
+
ctx.fillRect(leftPx, 0, w, height);
|
|
42
|
+
}
|
|
43
|
+
// second pass: draw clipping
|
|
44
|
+
// avoid persisting the red fillstyle with save/restore
|
|
45
|
+
ctx.save();
|
|
46
|
+
if (hasClipping) {
|
|
47
|
+
ctx.fillStyle = clipColor;
|
|
48
|
+
for (const feature of features.values()) {
|
|
49
|
+
const [leftPx, rightPx] = featureSpanPx(feature, region, bpPerPx);
|
|
50
|
+
const w = rightPx - leftPx + fudgeFactor;
|
|
51
|
+
const score = feature.get('score');
|
|
52
|
+
if (score > niceMax) {
|
|
53
|
+
fillRectCtx(leftPx, 0, w, clipHeight, ctx);
|
|
54
|
+
}
|
|
55
|
+
else if (score < niceMin && scaleOpts.scaleType !== 'log') {
|
|
56
|
+
fillRectCtx(leftPx, 0, w, clipHeight, ctx);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
ctx.restore();
|
|
61
|
+
return { reducedFeatures };
|
|
62
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
2
|
+
import { Feature, Region } from '@jbrowse/core/util';
|
|
3
|
+
import { ScaleOpts } from './util';
|
|
4
|
+
export declare function drawLine(ctx: CanvasRenderingContext2D, props: {
|
|
5
|
+
features: Map<string, Feature> | Feature[];
|
|
6
|
+
regions: Region[];
|
|
7
|
+
bpPerPx: number;
|
|
8
|
+
scaleOpts: ScaleOpts;
|
|
9
|
+
height: number;
|
|
10
|
+
ticks: {
|
|
11
|
+
values: number[];
|
|
12
|
+
};
|
|
13
|
+
displayCrossHatches: boolean;
|
|
14
|
+
colorCallback: (f: Feature, score: number) => string;
|
|
15
|
+
config: AnyConfigurationModel;
|
|
16
|
+
offset?: number;
|
|
17
|
+
}): {
|
|
18
|
+
reducedFeatures: Feature[];
|
|
19
|
+
};
|
package/esm/drawLine.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { readConfObject, } from '@jbrowse/core/configuration';
|
|
2
|
+
import { clamp, featureSpanPx } from '@jbrowse/core/util';
|
|
3
|
+
// locals
|
|
4
|
+
import { getScale } from './util';
|
|
5
|
+
const fudgeFactor = 0.3;
|
|
6
|
+
const clipHeight = 2;
|
|
7
|
+
export function drawLine(ctx, props) {
|
|
8
|
+
const { features, regions, bpPerPx, scaleOpts, height: unadjustedHeight, ticks: { values }, displayCrossHatches, colorCallback, config, offset = 0, } = props;
|
|
9
|
+
const [region] = regions;
|
|
10
|
+
const width = (region.end - region.start) / bpPerPx;
|
|
11
|
+
// the adjusted height takes into account YSCALEBAR_LABEL_OFFSET from the
|
|
12
|
+
// wiggle display, and makes the height of the actual drawn area add
|
|
13
|
+
// "padding" to the top and bottom of the display
|
|
14
|
+
const height = unadjustedHeight - offset * 2;
|
|
15
|
+
const clipColor = readConfObject(config, 'clipColor');
|
|
16
|
+
const scale = getScale({ ...scaleOpts, range: [0, height] });
|
|
17
|
+
const [niceMin, niceMax] = scale.domain();
|
|
18
|
+
const toY = (n) => clamp(height - (scale(n) || 0), 0, height) + offset;
|
|
19
|
+
let lastVal;
|
|
20
|
+
let prevLeftPx = -Infinity;
|
|
21
|
+
const reducedFeatures = [];
|
|
22
|
+
for (const feature of features.values()) {
|
|
23
|
+
const [leftPx, rightPx] = featureSpanPx(feature, region, bpPerPx);
|
|
24
|
+
// create reduced features, avoiding multiple features per px
|
|
25
|
+
if (Math.floor(leftPx) !== Math.floor(prevLeftPx)) {
|
|
26
|
+
reducedFeatures.push(feature);
|
|
27
|
+
prevLeftPx = leftPx;
|
|
28
|
+
}
|
|
29
|
+
const score = feature.get('score');
|
|
30
|
+
const lowClipping = score < niceMin;
|
|
31
|
+
const highClipping = score > niceMax;
|
|
32
|
+
const w = rightPx - leftPx + fudgeFactor;
|
|
33
|
+
const c = colorCallback(feature, score);
|
|
34
|
+
ctx.beginPath();
|
|
35
|
+
ctx.strokeStyle = c;
|
|
36
|
+
const startPos = lastVal !== undefined ? lastVal : score;
|
|
37
|
+
if (!region.reversed) {
|
|
38
|
+
ctx.moveTo(leftPx, toY(startPos));
|
|
39
|
+
ctx.lineTo(leftPx, toY(score));
|
|
40
|
+
ctx.lineTo(rightPx, toY(score));
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
ctx.moveTo(rightPx, toY(startPos));
|
|
44
|
+
ctx.lineTo(rightPx, toY(score));
|
|
45
|
+
ctx.lineTo(leftPx, toY(score));
|
|
46
|
+
}
|
|
47
|
+
ctx.stroke();
|
|
48
|
+
lastVal = score;
|
|
49
|
+
if (highClipping) {
|
|
50
|
+
ctx.fillStyle = clipColor;
|
|
51
|
+
ctx.fillRect(leftPx, offset, w, clipHeight);
|
|
52
|
+
}
|
|
53
|
+
else if (lowClipping && scaleOpts.scaleType !== 'log') {
|
|
54
|
+
ctx.fillStyle = clipColor;
|
|
55
|
+
ctx.fillRect(leftPx, height - clipHeight, w, height);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (displayCrossHatches) {
|
|
59
|
+
ctx.lineWidth = 1;
|
|
60
|
+
ctx.strokeStyle = 'rgba(200,200,200,0.5)';
|
|
61
|
+
values.forEach(tick => {
|
|
62
|
+
ctx.beginPath();
|
|
63
|
+
ctx.moveTo(0, Math.round(toY(tick)));
|
|
64
|
+
ctx.lineTo(width, Math.round(toY(tick)));
|
|
65
|
+
ctx.stroke();
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return { reducedFeatures };
|
|
69
|
+
}
|
package/esm/drawXY.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
2
|
+
import { Feature, Region } from '@jbrowse/core/util';
|
|
3
|
+
import { ScaleOpts } from './util';
|
|
4
|
+
export declare function drawXY(ctx: CanvasRenderingContext2D, props: {
|
|
5
|
+
features: Map<string, Feature> | Feature[];
|
|
6
|
+
bpPerPx: number;
|
|
7
|
+
regions: Region[];
|
|
8
|
+
scaleOpts: ScaleOpts;
|
|
9
|
+
height: number;
|
|
10
|
+
ticks: {
|
|
11
|
+
values: number[];
|
|
12
|
+
};
|
|
13
|
+
config: AnyConfigurationModel;
|
|
14
|
+
displayCrossHatches: boolean;
|
|
15
|
+
offset?: number;
|
|
16
|
+
colorCallback: (f: Feature, score: number) => string;
|
|
17
|
+
}): {
|
|
18
|
+
reducedFeatures: Feature[];
|
|
19
|
+
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { readConfObject, } from '@jbrowse/core/configuration';
|
|
2
2
|
import { colord } from '@jbrowse/core/util/colord';
|
|
3
3
|
import { clamp, featureSpanPx } from '@jbrowse/core/util';
|
|
4
|
+
// locals
|
|
5
|
+
import { fillRectCtx, getOrigin, getScale } from './util';
|
|
4
6
|
function lighten(color, amount) {
|
|
5
7
|
const hslColor = color.toHsl();
|
|
6
8
|
const l = hslColor.l * (1 + amount);
|
|
@@ -11,23 +13,6 @@ function darken(color, amount) {
|
|
|
11
13
|
const l = hslColor.l * (1 - amount);
|
|
12
14
|
return colord({ ...hslColor, l: clamp(l, 0, 100) });
|
|
13
15
|
}
|
|
14
|
-
// locals
|
|
15
|
-
import { getOrigin, getScale } from './util';
|
|
16
|
-
// avoid drawing negative width features for SVG exports
|
|
17
|
-
function fillRectCtx(x, y, width, height, ctx, color) {
|
|
18
|
-
if (width < 0) {
|
|
19
|
-
x += width;
|
|
20
|
-
width = -width;
|
|
21
|
-
}
|
|
22
|
-
if (height < 0) {
|
|
23
|
-
y += height;
|
|
24
|
-
height = -height;
|
|
25
|
-
}
|
|
26
|
-
if (color) {
|
|
27
|
-
ctx.fillStyle = color;
|
|
28
|
-
}
|
|
29
|
-
ctx.fillRect(x, y, width, height);
|
|
30
|
-
}
|
|
31
16
|
const fudgeFactor = 0.3;
|
|
32
17
|
const clipHeight = 2;
|
|
33
18
|
export function drawXY(ctx, props) {
|
|
@@ -173,122 +158,3 @@ export function drawXY(ctx, props) {
|
|
|
173
158
|
}
|
|
174
159
|
return { reducedFeatures };
|
|
175
160
|
}
|
|
176
|
-
export function drawLine(ctx, props) {
|
|
177
|
-
const { features, regions, bpPerPx, scaleOpts, height: unadjustedHeight, ticks: { values }, displayCrossHatches, colorCallback, config, offset = 0, } = props;
|
|
178
|
-
const [region] = regions;
|
|
179
|
-
const width = (region.end - region.start) / bpPerPx;
|
|
180
|
-
// the adjusted height takes into account YSCALEBAR_LABEL_OFFSET from the
|
|
181
|
-
// wiggle display, and makes the height of the actual drawn area add
|
|
182
|
-
// "padding" to the top and bottom of the display
|
|
183
|
-
const height = unadjustedHeight - offset * 2;
|
|
184
|
-
const clipColor = readConfObject(config, 'clipColor');
|
|
185
|
-
const scale = getScale({ ...scaleOpts, range: [0, height] });
|
|
186
|
-
const [niceMin, niceMax] = scale.domain();
|
|
187
|
-
const toY = (n) => clamp(height - (scale(n) || 0), 0, height) + offset;
|
|
188
|
-
let lastVal;
|
|
189
|
-
let prevLeftPx = -Infinity;
|
|
190
|
-
const reducedFeatures = [];
|
|
191
|
-
for (const feature of features.values()) {
|
|
192
|
-
const [leftPx, rightPx] = featureSpanPx(feature, region, bpPerPx);
|
|
193
|
-
// create reduced features, avoiding multiple features per px
|
|
194
|
-
if (Math.floor(leftPx) !== Math.floor(prevLeftPx)) {
|
|
195
|
-
reducedFeatures.push(feature);
|
|
196
|
-
prevLeftPx = leftPx;
|
|
197
|
-
}
|
|
198
|
-
const score = feature.get('score');
|
|
199
|
-
const lowClipping = score < niceMin;
|
|
200
|
-
const highClipping = score > niceMax;
|
|
201
|
-
const w = rightPx - leftPx + fudgeFactor;
|
|
202
|
-
const c = colorCallback(feature, score);
|
|
203
|
-
ctx.beginPath();
|
|
204
|
-
ctx.strokeStyle = c;
|
|
205
|
-
const startPos = lastVal !== undefined ? lastVal : score;
|
|
206
|
-
if (!region.reversed) {
|
|
207
|
-
ctx.moveTo(leftPx, toY(startPos));
|
|
208
|
-
ctx.lineTo(leftPx, toY(score));
|
|
209
|
-
ctx.lineTo(rightPx, toY(score));
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
ctx.moveTo(rightPx, toY(startPos));
|
|
213
|
-
ctx.lineTo(rightPx, toY(score));
|
|
214
|
-
ctx.lineTo(leftPx, toY(score));
|
|
215
|
-
}
|
|
216
|
-
ctx.stroke();
|
|
217
|
-
lastVal = score;
|
|
218
|
-
if (highClipping) {
|
|
219
|
-
ctx.fillStyle = clipColor;
|
|
220
|
-
ctx.fillRect(leftPx, offset, w, clipHeight);
|
|
221
|
-
}
|
|
222
|
-
else if (lowClipping && scaleOpts.scaleType !== 'log') {
|
|
223
|
-
ctx.fillStyle = clipColor;
|
|
224
|
-
ctx.fillRect(leftPx, height - clipHeight, w, height);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
if (displayCrossHatches) {
|
|
228
|
-
ctx.lineWidth = 1;
|
|
229
|
-
ctx.strokeStyle = 'rgba(200,200,200,0.5)';
|
|
230
|
-
values.forEach(tick => {
|
|
231
|
-
ctx.beginPath();
|
|
232
|
-
ctx.moveTo(0, Math.round(toY(tick)));
|
|
233
|
-
ctx.lineTo(width, Math.round(toY(tick)));
|
|
234
|
-
ctx.stroke();
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
return { reducedFeatures };
|
|
238
|
-
}
|
|
239
|
-
export function drawDensity(ctx, props) {
|
|
240
|
-
const { features, regions, bpPerPx, scaleOpts, height, config } = props;
|
|
241
|
-
const [region] = regions;
|
|
242
|
-
const pivot = readConfObject(config, 'bicolorPivot');
|
|
243
|
-
const pivotValue = readConfObject(config, 'bicolorPivotValue');
|
|
244
|
-
const negColor = readConfObject(config, 'negColor');
|
|
245
|
-
const posColor = readConfObject(config, 'posColor');
|
|
246
|
-
const color = readConfObject(config, 'color');
|
|
247
|
-
const clipColor = readConfObject(config, 'clipColor');
|
|
248
|
-
const crossing = pivot !== 'none' && scaleOpts.scaleType !== 'log';
|
|
249
|
-
const scale = getScale({
|
|
250
|
-
...scaleOpts,
|
|
251
|
-
pivotValue: crossing ? pivotValue : undefined,
|
|
252
|
-
range: crossing ? [negColor, 'white', posColor] : ['white', posColor],
|
|
253
|
-
});
|
|
254
|
-
const scale2 = getScale({ ...scaleOpts, range: [0, height] });
|
|
255
|
-
const cb = color === '#f0f'
|
|
256
|
-
? (_, score) => scale(score)
|
|
257
|
-
: (feature, score) => readConfObject(config, 'color', { feature, score });
|
|
258
|
-
const [niceMin, niceMax] = scale2.domain();
|
|
259
|
-
let prevLeftPx = -Infinity;
|
|
260
|
-
let hasClipping = false;
|
|
261
|
-
const reducedFeatures = [];
|
|
262
|
-
for (const feature of features.values()) {
|
|
263
|
-
const [leftPx, rightPx] = featureSpanPx(feature, region, bpPerPx);
|
|
264
|
-
// create reduced features, avoiding multiple features per px
|
|
265
|
-
if (Math.floor(leftPx) !== Math.floor(prevLeftPx)) {
|
|
266
|
-
reducedFeatures.push(feature);
|
|
267
|
-
prevLeftPx = leftPx;
|
|
268
|
-
}
|
|
269
|
-
const score = feature.get('score');
|
|
270
|
-
hasClipping = hasClipping || score > niceMax || score < niceMin;
|
|
271
|
-
const w = rightPx - leftPx + fudgeFactor;
|
|
272
|
-
ctx.fillStyle = cb(feature, score);
|
|
273
|
-
ctx.fillRect(leftPx, 0, w, height);
|
|
274
|
-
}
|
|
275
|
-
// second pass: draw clipping
|
|
276
|
-
// avoid persisting the red fillstyle with save/restore
|
|
277
|
-
ctx.save();
|
|
278
|
-
if (hasClipping) {
|
|
279
|
-
ctx.fillStyle = clipColor;
|
|
280
|
-
for (const feature of features.values()) {
|
|
281
|
-
const [leftPx, rightPx] = featureSpanPx(feature, region, bpPerPx);
|
|
282
|
-
const w = rightPx - leftPx + fudgeFactor;
|
|
283
|
-
const score = feature.get('score');
|
|
284
|
-
if (score > niceMax) {
|
|
285
|
-
fillRectCtx(leftPx, 0, w, clipHeight, ctx);
|
|
286
|
-
}
|
|
287
|
-
else if (score < niceMin && scaleOpts.scaleType !== 'log') {
|
|
288
|
-
fillRectCtx(leftPx, 0, w, clipHeight, ctx);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
ctx.restore();
|
|
293
|
-
return { reducedFeatures };
|
|
294
|
-
}
|
package/esm/util.d.ts
CHANGED
|
@@ -72,3 +72,4 @@ export declare function quantitativeStatsAutorun(self: {
|
|
|
72
72
|
}): void;
|
|
73
73
|
export declare function toP(s?: number): number;
|
|
74
74
|
export declare function round(value: number): number;
|
|
75
|
+
export declare function fillRectCtx(x: number, y: number, width: number, height: number, ctx: CanvasRenderingContext2D, color?: string): void;
|
package/esm/util.js
CHANGED
|
@@ -221,3 +221,18 @@ export function toP(s = 0) {
|
|
|
221
221
|
export function round(value) {
|
|
222
222
|
return Math.round(value * 1e5) / 1e5;
|
|
223
223
|
}
|
|
224
|
+
// avoid drawing negative width features for SVG exports
|
|
225
|
+
export function fillRectCtx(x, y, width, height, ctx, color) {
|
|
226
|
+
if (width < 0) {
|
|
227
|
+
x += width;
|
|
228
|
+
width = -width;
|
|
229
|
+
}
|
|
230
|
+
if (height < 0) {
|
|
231
|
+
y += height;
|
|
232
|
+
height = -height;
|
|
233
|
+
}
|
|
234
|
+
if (color) {
|
|
235
|
+
ctx.fillStyle = color;
|
|
236
|
+
}
|
|
237
|
+
ctx.fillRect(x, y, width, height);
|
|
238
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/plugin-wiggle",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.2",
|
|
4
4
|
"description": "JBrowse 2 wiggle adapters, tracks, etc.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jbrowse",
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"distModule": "esm/index.js",
|
|
68
68
|
"srcModule": "src/index.ts",
|
|
69
69
|
"module": "esm/index.js",
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "9052b295f2d322e729254457ed9fe2231fb22cce"
|
|
71
71
|
}
|
package/dist/drawxy.d.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
2
|
-
import { Feature, Region } from '@jbrowse/core/util';
|
|
3
|
-
import { ScaleOpts } from './util';
|
|
4
|
-
export declare function drawXY(ctx: CanvasRenderingContext2D, props: {
|
|
5
|
-
features: Map<string, Feature> | Feature[];
|
|
6
|
-
bpPerPx: number;
|
|
7
|
-
regions: Region[];
|
|
8
|
-
scaleOpts: ScaleOpts;
|
|
9
|
-
height: number;
|
|
10
|
-
ticks: {
|
|
11
|
-
values: number[];
|
|
12
|
-
};
|
|
13
|
-
config: AnyConfigurationModel;
|
|
14
|
-
displayCrossHatches: boolean;
|
|
15
|
-
offset?: number;
|
|
16
|
-
colorCallback: (f: Feature, score: number) => string;
|
|
17
|
-
}): {
|
|
18
|
-
reducedFeatures: Feature[];
|
|
19
|
-
};
|
|
20
|
-
export declare function drawLine(ctx: CanvasRenderingContext2D, props: {
|
|
21
|
-
features: Map<string, Feature> | Feature[];
|
|
22
|
-
regions: Region[];
|
|
23
|
-
bpPerPx: number;
|
|
24
|
-
scaleOpts: ScaleOpts;
|
|
25
|
-
height: number;
|
|
26
|
-
ticks: {
|
|
27
|
-
values: number[];
|
|
28
|
-
};
|
|
29
|
-
displayCrossHatches: boolean;
|
|
30
|
-
colorCallback: (f: Feature, score: number) => string;
|
|
31
|
-
config: AnyConfigurationModel;
|
|
32
|
-
offset?: number;
|
|
33
|
-
}): {
|
|
34
|
-
reducedFeatures: Feature[];
|
|
35
|
-
};
|
|
36
|
-
export declare function drawDensity(ctx: CanvasRenderingContext2D, props: {
|
|
37
|
-
features: Map<string, Feature> | Feature[];
|
|
38
|
-
regions: Region[];
|
|
39
|
-
bpPerPx: number;
|
|
40
|
-
scaleOpts: ScaleOpts;
|
|
41
|
-
height: number;
|
|
42
|
-
ticks: {
|
|
43
|
-
values: number[];
|
|
44
|
-
};
|
|
45
|
-
displayCrossHatches: boolean;
|
|
46
|
-
config: AnyConfigurationModel;
|
|
47
|
-
}): {
|
|
48
|
-
reducedFeatures: Feature[];
|
|
49
|
-
};
|
package/esm/drawxy.d.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
2
|
-
import { Feature, Region } from '@jbrowse/core/util';
|
|
3
|
-
import { ScaleOpts } from './util';
|
|
4
|
-
export declare function drawXY(ctx: CanvasRenderingContext2D, props: {
|
|
5
|
-
features: Map<string, Feature> | Feature[];
|
|
6
|
-
bpPerPx: number;
|
|
7
|
-
regions: Region[];
|
|
8
|
-
scaleOpts: ScaleOpts;
|
|
9
|
-
height: number;
|
|
10
|
-
ticks: {
|
|
11
|
-
values: number[];
|
|
12
|
-
};
|
|
13
|
-
config: AnyConfigurationModel;
|
|
14
|
-
displayCrossHatches: boolean;
|
|
15
|
-
offset?: number;
|
|
16
|
-
colorCallback: (f: Feature, score: number) => string;
|
|
17
|
-
}): {
|
|
18
|
-
reducedFeatures: Feature[];
|
|
19
|
-
};
|
|
20
|
-
export declare function drawLine(ctx: CanvasRenderingContext2D, props: {
|
|
21
|
-
features: Map<string, Feature> | Feature[];
|
|
22
|
-
regions: Region[];
|
|
23
|
-
bpPerPx: number;
|
|
24
|
-
scaleOpts: ScaleOpts;
|
|
25
|
-
height: number;
|
|
26
|
-
ticks: {
|
|
27
|
-
values: number[];
|
|
28
|
-
};
|
|
29
|
-
displayCrossHatches: boolean;
|
|
30
|
-
colorCallback: (f: Feature, score: number) => string;
|
|
31
|
-
config: AnyConfigurationModel;
|
|
32
|
-
offset?: number;
|
|
33
|
-
}): {
|
|
34
|
-
reducedFeatures: Feature[];
|
|
35
|
-
};
|
|
36
|
-
export declare function drawDensity(ctx: CanvasRenderingContext2D, props: {
|
|
37
|
-
features: Map<string, Feature> | Feature[];
|
|
38
|
-
regions: Region[];
|
|
39
|
-
bpPerPx: number;
|
|
40
|
-
scaleOpts: ScaleOpts;
|
|
41
|
-
height: number;
|
|
42
|
-
ticks: {
|
|
43
|
-
values: number[];
|
|
44
|
-
};
|
|
45
|
-
displayCrossHatches: boolean;
|
|
46
|
-
config: AnyConfigurationModel;
|
|
47
|
-
}): {
|
|
48
|
-
reducedFeatures: Feature[];
|
|
49
|
-
};
|