UNPKG

@jbrowse/plugin-wiggle

Version:

JBrowse 2 wiggle adapters, tracks, etc.

152 lines (151 loc) 6.11 kB
import { readConfObject } from '@jbrowse/core/configuration'; import { clamp } from '@jbrowse/core/util'; import { checkStopToken2, createStopTokenChecker, } from '@jbrowse/core/util/stopToken'; import { WIGGLE_CLIP_HEIGHT, WIGGLE_FUDGE_FACTOR, getScale } from "./util.js"; export function drawLine(ctx, props) { const { features, regions, bpPerPx, scaleOpts, height: unadjustedHeight, ticks: { values }, displayCrossHatches, colorCallback, config, offset = 0, staticColor, stopToken, lastCheck = createStopTokenChecker(stopToken), } = props; const region = regions[0]; const regionStart = region.start; const regionEnd = region.end; const reversed = region.reversed; const invBpPerPx = 1 / bpPerPx; const width = (regionEnd - regionStart) * invBpPerPx; const height = unadjustedHeight - offset * 2; const clipColor = readConfObject(config, 'clipColor'); const scale = getScale({ ...scaleOpts, range: [0, height] }); const domain = scale.domain(); const niceMin = domain[0]; const niceMax = domain[1]; const domainSpan = niceMax - niceMin; const isLog = scaleOpts.scaleType === 'log'; const linearRatio = domainSpan !== 0 ? height / domainSpan : 0; const log2 = Math.log(2); const logMin = Math.log(niceMin) / log2; const logMax = Math.log(niceMax) / log2; const logSpan = logMax - logMin; const logRatio = logSpan !== 0 ? height / logSpan : 0; const toY = isLog ? (n) => clamp(height - (Math.log(n) / log2 - logMin) * logRatio, 0, height) + offset : (n) => clamp(height - (n - niceMin) * linearRatio, 0, height) + offset; let lastVal; let prevLeftPx = Number.NEGATIVE_INFINITY; const reducedFeatures = []; if (staticColor) { ctx.beginPath(); ctx.strokeStyle = staticColor; const clippingFeatures = []; for (const feature of features.values()) { checkStopToken2(lastCheck); const fStart = feature.get('start'); const fEnd = feature.get('end'); const leftPx = reversed ? (regionEnd - fEnd) * invBpPerPx : (fStart - regionStart) * invBpPerPx; const rightPx = reversed ? (regionEnd - fStart) * invBpPerPx : (fEnd - regionStart) * invBpPerPx; if ((leftPx | 0) !== (prevLeftPx | 0) || rightPx - leftPx > 1) { reducedFeatures.push(feature); prevLeftPx = leftPx; } const score = feature.get('score'); const scoreY = toY(score); if (score > niceMax) { clippingFeatures.push({ leftPx, w: rightPx - leftPx + WIGGLE_FUDGE_FACTOR, high: true, }); } else if (score < niceMin && !isLog) { clippingFeatures.push({ leftPx, w: rightPx - leftPx + WIGGLE_FUDGE_FACTOR, high: false, }); } const startY = lastVal !== undefined ? toY(lastVal) : scoreY; if (!reversed) { ctx.moveTo(leftPx, startY); ctx.lineTo(leftPx, scoreY); ctx.lineTo(rightPx, scoreY); } else { ctx.moveTo(rightPx, startY); ctx.lineTo(rightPx, scoreY); ctx.lineTo(leftPx, scoreY); } lastVal = score; } ctx.stroke(); if (clippingFeatures.length > 0) { ctx.fillStyle = clipColor; for (const { leftPx, w, high } of clippingFeatures) { if (high) { ctx.fillRect(leftPx, offset, w, WIGGLE_CLIP_HEIGHT); } else { ctx.fillRect(leftPx, height - WIGGLE_CLIP_HEIGHT, w, WIGGLE_CLIP_HEIGHT); } } } } else { for (const feature of features.values()) { checkStopToken2(lastCheck); const fStart = feature.get('start'); const fEnd = feature.get('end'); const leftPx = reversed ? (regionEnd - fEnd) * invBpPerPx : (fStart - regionStart) * invBpPerPx; const rightPx = reversed ? (regionEnd - fStart) * invBpPerPx : (fEnd - regionStart) * invBpPerPx; if ((leftPx | 0) !== (prevLeftPx | 0) || rightPx - leftPx > 1) { reducedFeatures.push(feature); prevLeftPx = leftPx; } const score = feature.get('score'); const scoreY = toY(score); const w = rightPx - leftPx + WIGGLE_FUDGE_FACTOR; const c = colorCallback(feature, score); ctx.beginPath(); ctx.strokeStyle = c; const startY = lastVal !== undefined ? toY(lastVal) : scoreY; if (!reversed) { ctx.moveTo(leftPx, startY); ctx.lineTo(leftPx, scoreY); ctx.lineTo(rightPx, scoreY); } else { ctx.moveTo(rightPx, startY); ctx.lineTo(rightPx, scoreY); ctx.lineTo(leftPx, scoreY); } ctx.stroke(); lastVal = score; if (score > niceMax) { ctx.fillStyle = clipColor; ctx.fillRect(leftPx, offset, w, WIGGLE_CLIP_HEIGHT); } else if (score < niceMin && !isLog) { ctx.fillStyle = clipColor; ctx.fillRect(leftPx, height - WIGGLE_CLIP_HEIGHT, w, WIGGLE_CLIP_HEIGHT); } } } if (displayCrossHatches) { ctx.lineWidth = 1; ctx.strokeStyle = 'rgba(200,200,200,0.5)'; for (const tick of values) { ctx.beginPath(); ctx.moveTo(0, Math.round(toY(tick))); ctx.lineTo(width, Math.round(toY(tick))); ctx.stroke(); } } return { reducedFeatures, }; }