UNPKG

higlass

Version:

HiGlass Hi-C / genomic / large data viewer

196 lines (149 loc) 5.11 kB
// @ts-nocheck import { brush } from 'd3-brush'; import slugid from 'slugid'; import SVGTrack from './SVGTrack'; class ViewportTracker2D extends SVGTrack { constructor(context, options) { // create a clipped SVG Path super(context, options); const { registerViewportChanged, removeViewportChanged, setDomainsCallback, } = context; const uid = slugid.nice(); this.uid = uid; this.options = options; // Is there actually a linked _from_ view? Or is this projection "independent"? this.hasFromView = !context.projectionXDomain || !context.projectionYDomain; this.removeViewportChanged = removeViewportChanged; this.setDomainsCallback = setDomainsCallback; this.viewportXDomain = this.hasFromView ? null : context.projectionXDomain; this.viewportYDomain = this.hasFromView ? null : context.projectionYDomain; this.brush = brush().on('brush', this.brushed.bind(this)); this.gBrush = this.gMain .append('g') .attr('id', `brush-${this.uid}`) .call(this.brush); /* // This is used to draw a border that is completely outside of the // drawn rectangle this.gBorder = this.gMain .append('path') .style('pointer-events', 'none'); */ // turn off the ability to select new regions for this brush this.gBrush.selectAll('.overlay').style('pointer-events', 'none'); // turn off the ability to modify the aspect ratio of the brush this.gBrush.selectAll('.handle--n').style('pointer-events', 'none'); this.gBrush.selectAll('.handle--s').style('pointer-events', 'none'); this.gBrush.selectAll('.handle--w').style('pointer-events', 'none'); this.gBrush.selectAll('.handle--e').style('pointer-events', 'none'); // the viewport will call this.viewportChanged immediately upon // hearing registerViewportChanged registerViewportChanged(uid, this.viewportChanged.bind(this)); this.rerender(); this.draw(); } brushed(event) { /** * Should only be called on active brushing, not in response to the * draw event */ const s = event.selection; if (!this._xScale || !this._yScale) { return; } const xDomain = [ this._xScale.invert(s[0][0]), this._xScale.invert(s[1][0]), ]; const yDomain = [ this._yScale.invert(s[0][1]), this._yScale.invert(s[1][1]), ]; if (!this.hasFromView) { this.viewportXDomain = xDomain; this.viewportYDomain = yDomain; } this.setDomainsCallback(xDomain, yDomain); } viewportChanged(viewportXScale, viewportYScale) { const viewportXDomain = viewportXScale.domain(); const viewportYDomain = viewportYScale.domain(); this.viewportXDomain = viewportXDomain; this.viewportYDomain = viewportYDomain; this.draw(); } remove() { // remove the event handler that updates this viewport tracker this.removeViewportChanged(this.uid); super.remove(); } rerender() { // set the fill and stroke colors // console.log('rerender'); this.gBrush .selectAll('.selection') .attr('fill', this.options.projectionFillColor) .attr('stroke', this.options.projectionStrokeColor) .attr('fill-opacity', this.options.projectionFillOpacity) .attr('stroke-opacity', this.options.projectionStrokeOpacity) .attr('stroke-width', this.options.strokeWidth); /* this.gBorder .style('fill', this.options.projectionStrokeColor) .style('opacity', this.options.projectionStrokeOpacity) */ } draw() { if (!this._xScale || !this.yScale) { return; } if (!this.viewportXDomain || !this.viewportYDomain) { return; } const x0 = this._xScale(this.viewportXDomain[0]); const y0 = this._yScale(this.viewportYDomain[0]); const x1 = this._xScale(this.viewportXDomain[1]); const y1 = this._yScale(this.viewportYDomain[1]); const dest = [ [x0, y0], [x1, y1], ]; // user hasn't actively brushed so we don't want to emit a // 'brushed' event this.brush.on('brush', null); this.gBrush.call(this.brush.move, dest); this.brush.on('brush', this.brushed.bind(this)); /* const sW = this.options.strokeWidth; this.gBorder .attr('d', `M${x0} ${y0} H ${x1} V ${y1} H ${x0} V ${y0 - sW} H ${x0 - sW} V ${y1 + sW} H ${x1 + sW} V ${y0 - sW} H ${x0 - sW} V ${y0}`); */ } zoomed(newXScale, newYScale) { this.xScale(newXScale); this.yScale(newYScale); this.draw(); } setPosition(newPosition) { super.setPosition(newPosition); this.draw(); } setDimensions(newDimensions) { super.setDimensions(newDimensions); const xRange = this._xScale.range(); const yRange = this._yScale.range(); const xDiff = xRange[1] - xRange[0]; const yDiff = yRange[1] - yRange[0]; this.brush.extent([ [xRange[0] - xDiff, yRange[0] - yDiff], [xRange[1] + xDiff, yRange[1] + yDiff], ]); this.gBrush.call(this.brush); this.draw(); } } export default ViewportTracker2D;