@jbrowse/plugin-wiggle
Version:
JBrowse 2 wiggle adapters, tracks, etc.
89 lines (88 loc) • 3.72 kB
JavaScript
import { readConfObject } from '@jbrowse/core/configuration';
import { checkStopToken2, createStopTokenChecker, } from '@jbrowse/core/util/stopToken';
import { fillRectCtx, getScale } from "./util.js";
const fudgeFactor = 0.3;
const clipHeight = 2;
export function drawDensity(ctx, props) {
const { features, regions, bpPerPx, scaleOpts, height, config, stopToken, lastCheck = createStopTokenChecker(stopToken), } = props;
const region = regions[0];
const regionStart = region.start;
const regionEnd = region.end;
const regionReversed = region.reversed;
const inverseBpPerPx = 1 / bpPerPx;
const pivot = readConfObject(config, 'bicolorPivot');
const pivotValue = readConfObject(config, 'bicolorPivotValue');
const negColor = readConfObject(config, 'negColor');
const posColor = readConfObject(config, 'posColor');
const color = readConfObject(config, 'color');
const clipColor = readConfObject(config, 'clipColor');
const crossing = pivot !== 'none' && scaleOpts.scaleType !== 'log';
const scale = getScale({
...scaleOpts,
pivotValue: crossing ? pivotValue : undefined,
range: crossing ? [negColor, '#eee', posColor] : ['#eee', posColor],
});
const scale2 = getScale({ ...scaleOpts, range: [0, height] });
const cb = color === '#f0f'
? (_, score) => scale(score)
: (feature, score) => readConfObject(config, 'color', { feature, score });
const domain = scale2.domain();
const niceMin = domain[0];
const niceMax = domain[1];
let prevLeftPx = Number.NEGATIVE_INFINITY;
let hasClipping = false;
const reducedFeatures = [];
for (const feature of features.values()) {
checkStopToken2(lastCheck);
const fStart = feature.get('start');
const fEnd = feature.get('end');
const leftPx = regionReversed
? (regionEnd - fEnd) * inverseBpPerPx
: (fStart - regionStart) * inverseBpPerPx;
const rightPx = regionReversed
? (regionEnd - fStart) * inverseBpPerPx
: (fEnd - regionStart) * inverseBpPerPx;
if (Math.floor(leftPx) !== Math.floor(prevLeftPx) || rightPx - leftPx > 1) {
reducedFeatures.push(feature);
prevLeftPx = leftPx;
}
const score = feature.get('score');
hasClipping = hasClipping || score > niceMax || score < niceMin;
const w = rightPx - leftPx + fudgeFactor;
if (score >= scaleOpts.domain[0]) {
ctx.fillStyle = cb(feature, score);
ctx.fillRect(leftPx, 0, w, height);
}
else {
ctx.fillStyle = '#eee';
ctx.fillRect(leftPx, 0, w, height);
}
}
ctx.save();
if (hasClipping) {
ctx.fillStyle = clipColor;
for (const feature of features.values()) {
checkStopToken2(lastCheck);
const fStart = feature.get('start');
const fEnd = feature.get('end');
const leftPx = regionReversed
? (regionEnd - fEnd) * inverseBpPerPx
: (fStart - regionStart) * inverseBpPerPx;
const rightPx = regionReversed
? (regionEnd - fStart) * inverseBpPerPx
: (fEnd - regionStart) * inverseBpPerPx;
const w = rightPx - leftPx + fudgeFactor;
const score = feature.get('score');
if (score > niceMax) {
fillRectCtx(leftPx, 0, w, clipHeight, ctx);
}
else if (score < niceMin && scaleOpts.scaleType !== 'log') {
fillRectCtx(leftPx, 0, w, clipHeight, ctx);
}
}
}
ctx.restore();
return {
reducedFeatures,
};
}