UNPKG

@jbrowse/plugin-linear-genome-view

Version:

JBrowse 2 linear genome view

108 lines (107 loc) 4.19 kB
import { clamp } from '@jbrowse/core/util'; export function collectLayoutsFromRenderings(renderings) { const layoutMaps = []; for (const [, rendering] of renderings) { if (rendering.layout?.getRectangles) { layoutMaps.push(rendering.layout.getRectangles()); } } return layoutMaps; } function calculateFeatureLeftPx(view, assembly, refName, left, right, bpPerPx) { const canonicalRefName = assembly?.getCanonicalRefName(refName) || refName; const leftBpPx = view.bpToPx({ refName: canonicalRefName, coord: left, })?.offsetPx; const rightBpPx = view.bpToPx({ refName: canonicalRefName, coord: right, })?.offsetPx; if (leftBpPx !== undefined) { const rightEstimate = rightBpPx !== undefined ? rightBpPx : leftBpPx + (right - left) / bpPerPx; return Math.min(leftBpPx, rightEstimate); } else if (rightBpPx !== undefined) { const leftEstimate = rightBpPx - (right - left) / bpPerPx; return Math.min(leftEstimate, rightBpPx); } return undefined; } function calculateMultiRegionLeftPx(view, assembly, refName, left, right) { const canonicalRefName = assembly?.getCanonicalRefName(refName) || refName; const visibleRegions = view.displayedRegions.filter(r => r.refName === canonicalRefName && r.start < right && r.end > left); if (visibleRegions.length === 0) { return undefined; } let minLeftPx = Infinity; for (const region of visibleRegions) { const regionStart = Math.max(left, region.start); const regionEnd = Math.min(right, region.end); const startPx = view.bpToPx({ refName: canonicalRefName, coord: regionStart, })?.offsetPx; const endPx = view.bpToPx({ refName: canonicalRefName, coord: regionEnd, })?.offsetPx; if (startPx !== undefined && endPx !== undefined) { minLeftPx = Math.min(minLeftPx, startPx, endPx); } } if (minLeftPx === Infinity) { return undefined; } return minLeftPx; } function getFeatureLeftPx(view, assembly, refName, left, right, bpPerPx) { return (calculateFeatureLeftPx(view, assembly, refName, left, right, bpPerPx) ?? calculateMultiRegionLeftPx(view, assembly, refName, left, right)); } export function deduplicateFeatureLabels(layoutFeatures, view, assembly, bpPerPx) { const featureLabels = new Map(); for (const [key, val] of layoutFeatures.entries()) { if (!val?.[4]) { continue; } const [, topPx, , , feature] = val; const { refName, floatingLabels, totalFeatureHeight, actualTopPx, featureWidth, featureStartBp, featureEndBp, } = feature; const effectiveTopPx = actualTopPx ?? topPx; if (!floatingLabels || floatingLabels.length === 0 || !totalFeatureHeight || featureWidth === undefined || featureStartBp === undefined || featureEndBp === undefined) { continue; } const leftPx = getFeatureLeftPx(view, assembly, refName, featureStartBp, featureEndBp, bpPerPx); if (leftPx === undefined) { continue; } const floorLeftPx = Math.floor(leftPx); const existing = featureLabels.get(key); if (!existing || floorLeftPx < existing.leftPx) { featureLabels.set(key, { leftPx: floorLeftPx, topPx: effectiveTopPx, totalFeatureHeight, floatingLabels, featureWidth, }); } } return featureLabels; } export function calculateFloatingLabelPosition(featureLeftPx, featureRightPx, labelWidth, offsetPx) { const featureWidth = featureRightPx - featureLeftPx; if (labelWidth > featureWidth) { return Math.round(featureLeftPx - offsetPx); } const viewportLeft = Math.max(0, offsetPx); const leftPx = Math.max(featureLeftPx, viewportLeft); const naturalX = leftPx - offsetPx; const maxX = featureRightPx - offsetPx - labelWidth; return Math.round(clamp(naturalX, 0, maxX)); }