UNPKG

higlass

Version:

HiGlass Hi-C / genomic / large data viewer

296 lines (234 loc) 7.71 kB
// @ts-nocheck import TiledPixiTrack from './TiledPixiTrack'; import { tileProxy } from './services'; import { colorToHex } from './utils'; class Horizontal2DDomainsTrack extends TiledPixiTrack { constructor(context, options) { super(context, options); this.drawnRects = new Set(); this.pMain = this.pMobile; } tileToLocalId(tile) { /* * The local tile identifier */ // tile contains [zoomLevel, xPos, yPos] return `${tile.join('.')}`; } tileToRemoteId(tile) { /** * The tile identifier used on the server */ // tile contains [zoomLevel, xPos, yPos] return `${tile.join('.')}`; } localToRemoteId(remoteId) { const idParts = remoteId.split('.'); return idParts.slice(0, idParts.length - 1).join('.'); } calculateZoomLevel() { const xZoomLevel = tileProxy.calculateZoomLevel( this._xScale, this.tilesetInfo.min_pos[0], this.tilesetInfo.max_pos[0], ); const yZoomLevel = tileProxy.calculateZoomLevel( this._xScale, this.tilesetInfo.min_pos[1], this.tilesetInfo.max_pos[1], ); let zoomLevel = Math.max(xZoomLevel, yZoomLevel); zoomLevel = Math.min(zoomLevel, this.maxZoom); if (this.options?.maxZoom) { if (this.options.maxZoom >= 0) { zoomLevel = Math.min(this.options.maxZoom, zoomLevel); } else { console.error('Invalid maxZoom on track:', this); } } return zoomLevel; } setVisibleTiles(tilePositions) { /** * Set which tiles are visible right now. * * @param tiles: A set of tiles which will be considered the currently visible * tile positions. */ this.visibleTiles = tilePositions.map((x) => ({ tileId: this.tileToLocalId(x), remoteId: this.tileToRemoteId(x), })); this.visibleTileIds = new Set(this.visibleTiles.map((x) => x.tileId)); } calculateVisibleTiles() { /** * Taken from HorizontalHeatmapTrack.js */ // if we don't know anything about this dataset, no point // in trying to get tiles if (!this.tilesetInfo) { return; } this.zoomLevel = this.calculateZoomLevel(); // this.zoomLevel = 0; const expandedXScale = this._xScale.copy(); // we need to expand the domain of the X-scale because we are showing diagonal tiles. // to make sure the view is covered up the entire height, we need to expand by // viewHeight * sqrt(2) // on each side expandedXScale.domain([ this._xScale.invert( this._xScale.range()[0] - this.dimensions[1] * Math.sqrt(2), ), this._xScale.invert( this._xScale.range()[1] + this.dimensions[1] * Math.sqrt(2), ), ]); this.xTiles = tileProxy.calculateTiles( this.zoomLevel, expandedXScale, this.tilesetInfo.min_pos[0], this.tilesetInfo.max_pos[0], this.tilesetInfo.max_zoom, this.tilesetInfo.max_width, ); this.yTiles = tileProxy.calculateTiles( this.zoomLevel, expandedXScale, this.tilesetInfo.min_pos[0], this.tilesetInfo.max_pos[0], this.tilesetInfo.max_zoom, this.tilesetInfo.max_width, ); const rows = this.xTiles; const cols = this.yTiles; const zoomLevel = this.zoomLevel; const maxWidth = this.tilesetInfo.max_width; const tileWidth = maxWidth / 2 ** zoomLevel; // if we're mirroring tiles, then we only need tiles along the diagonal const tiles = []; // calculate the ids of the tiles that should be visible for (let i = 0; i < rows.length; i++) { for (let j = i; j < cols.length; j++) { // the length between the bottom of the track and the bottom corner of the tile // draw it out to understand better! const tileBottomPosition = ((j - i - 2) * (this._xScale(tileWidth) - this._xScale(0)) * Math.sqrt(2)) / 2; if (tileBottomPosition > this.dimensions[1]) { // this tile won't be visible so we don't need to fetch it continue; } const newTile = [zoomLevel, rows[i], cols[j]]; newTile.mirrored = false; tiles.push(newTile); } } this.setVisibleTiles(tiles); } initTile(tile) { /** * Create whatever is needed to draw this tile. */ super.initTile(tile); // this.drawTile(tile); this.drawTile(tile); } destroyTile(tile, graphics) {} draw() { // console.log('delayDrawing:', this.delayDrawing, this.dimensions[1]); if (!this.delayDrawing) { this.drawnRects.clear(); } super.draw(); } drawTile(tile) { if (!tile.graphics) { return; } // console.log('Id2DTiled drawTile...'); const graphics = tile.graphics; graphics.clear(); const stroke = colorToHex( this.options.rectangleDomainStrokeColor ? this.options.rectangleDomainStrokeColor : 'black', ); const fill = colorToHex( this.options.rectangleDomainFillColor ? this.options.rectangleDomainFillColor : 'grey', ); graphics.lineStyle(1 / this.pMain.scale.x, stroke, 1); graphics.beginFill(fill, 0.4); graphics.alpha = this.options.rectangleDomainOpacity ? this.options.rectangleDomainOpacity : 0.5; if (!tile.tileData.sort) return; // line needs to be scaled down so that it doesn't become huge for (const td of tile.tileData) { const startX = this._refXScale(td.xStart); const endX = this._refXScale(td.xEnd); const startY = this._refYScale(td.yStart); const endY = this._refYScale(td.yEnd); const uid = td.uid; if (this.drawnRects.has(uid)) { continue; } // we've already drawn this rectangle in another tile this.drawnRects.add(uid); graphics.drawRect(startX, startY, endX - startX, endY - startY); graphics.pivot.x = this._refXScale(0); graphics.pivot.y = this._refYScale(0); graphics.scale.x = -1 / Math.sqrt(2); graphics.rotation = (-3 * Math.PI) / 4; graphics.scale.y = 1 / Math.sqrt(2); graphics.position.x = this._refXScale(0); graphics.position.y = 0; } } setPosition(newPosition) { super.setPosition(newPosition); this.pMain.position.y = this.position[1]; this.pMain.position.x = this.position[0]; } refScalesChanged(refXScale, refYScale) { super.refScalesChanged(refXScale, refYScale); for (const uid in this.fetchedTiles) { const tile = this.fetchedTiles[uid]; if (tile.sprite) { const graphics = tile.graphics; graphics.pivot.x = this._refXScale(0); graphics.pivot.y = this._refYScale(0); graphics.scale.x = -1 / Math.sqrt(2); graphics.rotation = (-3 * Math.PI) / 4; graphics.scale.y = 1 / Math.sqrt(2); graphics.position.x = this._refXScale(0); graphics.position.y = 0; } else { // console.log('skipping...', tile.tileId); } } } zoomed(newXScale, newYScale, k, tx, ty) { super.zoomed(newXScale, newYScale, k, tx, ty); // console.log('zoomed this.pMain.position:', this.pMain.position.x, this.pMain.position.y, // this.pMain.scale.x, this.pMain.scale.y); this.pMain.position.x = tx; this.pMain.position.y = this.position[1] + this.dimensions[1]; // translateY; this.pMain.scale.x = k; // scaleX; this.pMain.scale.y = k; // scaleY; if (this.options.oneDHeatmapFlipped) { this.pMain.scale.y = -k; this.pMain.position.y = this.position[1]; } this.draw(); } leftTrackDraw() { this.draw(); } } export default Horizontal2DDomainsTrack;