@jbrowse/core
Version:
JBrowse 2 core libraries used by plugins
105 lines (104 loc) • 5.06 kB
JavaScript
import { getSnapshot, isStateTreeNode } from '@jbrowse/mobx-state-tree';
import { BlockSet, ContentBlock, ElidedBlock, InterRegionPaddingBlock, } from "./blockTypes.js";
import { assembleLocStringFast } from "./index.js";
import { intersection2 } from "./range.js";
export default function calculateDynamicBlocks(model, padding = true, elision = true) {
const { offsetPx, displayedRegions, bpPerPx, width, minimumBlockWidth, interRegionPaddingWidth, } = model;
if (!width) {
throw new Error('view has no width, cannot calculate displayed blocks');
}
const invBpPerPx = 1 / bpPerPx;
const blocks = new BlockSet();
let displayedRegionLeftPx = 0;
const windowLeftPx = offsetPx;
const windowRightPx = windowLeftPx + width;
for (let regionNumber = 0; regionNumber < displayedRegions.length; regionNumber++) {
const region = displayedRegions[regionNumber];
const { assemblyName, refName, start: regionStart, end: regionEnd, reversed, } = region;
const displayedRegionRightPx = displayedRegionLeftPx + (regionEnd - regionStart) * invBpPerPx;
const regionWidthPx = (regionEnd - regionStart) * invBpPerPx;
const parentRegion = isStateTreeNode(region) ? getSnapshot(region) : region;
const [leftPx, rightPx] = intersection2(windowLeftPx, windowRightPx, displayedRegionLeftPx, displayedRegionRightPx);
if (leftPx !== undefined && rightPx !== undefined) {
let start;
let end;
let isLeftEndOfDisplayedRegion;
let isRightEndOfDisplayedRegion;
let blockOffsetPx;
if (reversed) {
start = Math.max(regionStart, regionEnd - (rightPx - displayedRegionLeftPx) * bpPerPx);
end = regionEnd - (leftPx - displayedRegionLeftPx) * bpPerPx;
isLeftEndOfDisplayedRegion = end === regionEnd;
isRightEndOfDisplayedRegion = start === regionStart;
blockOffsetPx = displayedRegionLeftPx + (regionEnd - end) * invBpPerPx;
}
else {
start = (leftPx - displayedRegionLeftPx) * bpPerPx + regionStart;
end = Math.min(regionEnd, (rightPx - displayedRegionLeftPx) * bpPerPx + regionStart);
isLeftEndOfDisplayedRegion = start === regionStart;
isRightEndOfDisplayedRegion = end === regionEnd;
blockOffsetPx =
displayedRegionLeftPx + (start - regionStart) * invBpPerPx;
}
const widthPx = (end - start) * invBpPerPx;
const blockData = {
assemblyName,
refName,
start,
end,
reversed,
offsetPx: blockOffsetPx,
parentRegion,
regionNumber,
widthPx,
isLeftEndOfDisplayedRegion,
isRightEndOfDisplayedRegion,
key: `${assembleLocStringFast({
assemblyName,
refName,
start,
end,
reversed,
})}-${regionNumber}${reversed ? '-reversed' : ''}`,
};
if (padding && blocks.length === 0 && isLeftEndOfDisplayedRegion) {
blocks.push(new InterRegionPaddingBlock({
key: `${blockData.key}-beforeFirstRegion`,
widthPx: -offsetPx,
offsetPx: blockData.offsetPx + offsetPx,
variant: 'boundary',
}));
}
if (elision && regionWidthPx < minimumBlockWidth) {
blocks.push(new ElidedBlock(blockData));
}
else {
blocks.push(new ContentBlock(blockData));
}
if (padding) {
if (regionWidthPx >= minimumBlockWidth &&
blockData.isRightEndOfDisplayedRegion &&
regionNumber < displayedRegions.length - 1) {
blocks.push(new InterRegionPaddingBlock({
key: `${blockData.key}-rightpad`,
widthPx: interRegionPaddingWidth,
offsetPx: blockData.offsetPx + blockData.widthPx,
}));
displayedRegionLeftPx += interRegionPaddingWidth;
}
if (regionNumber === displayedRegions.length - 1 &&
blockData.isRightEndOfDisplayedRegion) {
blockOffsetPx = blockData.offsetPx + blockData.widthPx;
blocks.push(new InterRegionPaddingBlock({
key: `${blockData.key}-afterLastRegion`,
widthPx: width - blockOffsetPx + offsetPx,
offsetPx: blockOffsetPx,
variant: 'boundary',
}));
}
}
}
displayedRegionLeftPx += (regionEnd - regionStart) * invBpPerPx;
}
return blocks;
}