UNPKG

@rcsb/rcsb-saguaro

Version:
240 lines (239 loc) 9.95 kB
import { RcsbLineDisplay } from "./RcsbLineDisplay"; import { area, curveStep, curveCardinal, curveBasis, curveLinear } from "d3-shape"; import { RcsbD3AreaManager } from "../RcsbD3/RcsbD3DisplayManager/RcsbD3AreaManager"; import { largestTriangleOneBucket } from "@d3fc/d3fc-sample"; import { RcsbD3LineManager } from "../RcsbD3/RcsbD3DisplayManager/RcsbD3LineManager"; import { pointer } from "d3-selection"; export class RcsbAreaDisplay extends RcsbLineDisplay { constructor(boardId, trackId) { super(boardId, trackId); this.area = area().curve(curveStep); this.multiLine = new Array(); this.blockAreaFlag = false; this.multiAreaFlag = false; this.areaManager = new RcsbD3AreaManager(); this.SUFFIX_ID = "area_"; this.mouseclick = (event) => { const svgNode = this.g.node(); if (svgNode != null) { const x = pointer(event, svgNode)[0]; const position = Math.round(this.xScale.invert(x)); const region = { begin: position, end: position }; this.getBoardHighlight()(region, event.shiftKey ? 'add' : 'set', 'select', false); this.elementSubject.mouseclick.next({ d: region, e: event }); } }; this.trackSubject.mousemove.subscribe(({ e, n }) => this.mousemove(e, n)); this.trackSubject.mouseleave.subscribe((e) => this.mouseleave(e)); } setInterpolationType(type) { super.setInterpolationType(type); if (type === "cardinal" /* InterpolationTypes.CARDINAL */) this.area = area().curve(curveCardinal); else if (type === "step" /* InterpolationTypes.STEP */) this.area = area().curve(curveStep); else if (type === "basis" /* InterpolationTypes.BASIS */) this.area = area().curve(curveBasis); else if (type === "linear" /* InterpolationTypes.LINEAR */) this.area = area().curve(curveLinear); } setBlockArea(flag) { this.blockAreaFlag = flag; } setMultiArea(flag) { this.multiAreaFlag = flag; } setArea() { this.setLine(); this.area .x((d) => { var _a; return (_a = this.xScale(d.begin)) !== null && _a !== void 0 ? _a : 0; }) .y1((d) => { var _a, _b; if (d.values instanceof Array) return (_a = this.yScale(d.values[1])) !== null && _a !== void 0 ? _a : 0; return (_b = this.yScale(d.value)) !== null && _b !== void 0 ? _b : 0; }) .y0((d) => { var _a, _b; if (d.values instanceof Array) return (_a = this.yScale(d.values[0])) !== null && _a !== void 0 ? _a : 0; return (_b = this.yScale(0)) !== null && _b !== void 0 ? _b : 0; }); } updateArea() { this.updateLine(); this.area .x((d) => { var _a; return (_a = this.xScale(d.begin)) !== null && _a !== void 0 ? _a : 0; }); } mousemove(event, n) { const svgNode = this.g.node(); if (svgNode != null) { if (this.innerData[n]) { this.index = n; this.elementSubject.mouseenter.next({ d: this.innerData[this.index], e: event }); } else if (n > 0) { this.elementSubject.mouseenter.next({ d: { begin: n }, e: event }); } else { this.mouseleave(event); } } } mouseleave(event) { this.elementSubject.mouseleave.next({ d: { begin: 0 }, e: event }); } geoPlot(data) { var _a, _b; if (!this.definedScale) { this.setScale(); this.setArea(); } if (typeof this._displayColor === "string") { this.multiLine = [{ points: this.downSampling(data), color: this._displayColor }]; } else if (typeof this._displayColor === "object") { this.multiLine = this.downSamplingSplit(data, buildColorThreshold(this._displayColor)); } if (!this.blockAreaFlag) this.areaManager.plotAxis({ trackG: this.g, x1: this.xScale.range()[0], x2: this.xScale.range()[1], y1: (_a = this.yScale(0)) !== null && _a !== void 0 ? _a : 0, y2: (_b = this.yScale(0)) !== null && _b !== void 0 ? _b : 0 }); if (this.multiLine.length == 1 && !this.blockAreaFlag) { const index = 0; const e = this.multiLine[index]; const borderConfig = { points: e.points, line: this.line, color: e.color, trackG: this.g, id: this.SUFFIX_ID + "line_" + index }; RcsbD3LineManager.plot(borderConfig); } const areaConfig = []; this.multiLine.forEach((e, index) => { areaConfig.push({ points: e.points, color: e.color, trackG: this.g, area: this.area, id: this.SUFFIX_ID + index, opacity: (this.blockAreaFlag ? e.alpha : ((this.multiLine.length > 1 || this.blockAreaFlag) ? 1 : .2)), mouseclick: this.mouseclick.bind(this) }); }); this.areaManager.plot(areaConfig); } move() { this.updateArea(); this.multiLine.forEach((e, index) => { const areaConfig = { points: e.points, trackG: this.g, area: this.area, id: this.SUFFIX_ID + index }; this.areaManager.move(areaConfig); if (this.multiLine.length == 1) { const borderConfig = { points: e.points, line: this.line, trackG: this.g, id: this.SUFFIX_ID + "line_" + index }; RcsbD3LineManager.move(borderConfig); } }); this.setDataUpdated(false); } downSamplingSplit(points, gradient) { const tmp = new Array(); const lineColorArray = new Array(); const domain = { min: Number.MAX_SAFE_INTEGER, max: Number.MIN_SAFE_INTEGER }; points.forEach(p => { if (p.begin < domain.min) domain.min = p.begin - 0.5; if (p.begin > domain.max) domain.max = p.begin + 0.5; }); domain.min = Math.max(domain.min, this.xScale.domain()[0]); domain.max = Math.min(domain.max, this.xScale.domain()[1]); gradient.colors.forEach((c, i) => { tmp[i] = { points: new Array(), color: c }; }); const thr = this.maxPoints; for (let n = Math.ceil(domain.min); n < domain.max; n++) { this.innerData.push(null); gradient.colors.forEach((c, i) => { tmp[i].points[n] = { begin: n, value: 0, values: [0, 0] }; }); } points.forEach((p) => { this.innerData[p.begin] = p; if (p.begin > domain.min && p.begin < domain.max) { if (this.multiAreaFlag) { gradient.colors.forEach((c, n) => { if (p.values) tmp[n].points[p.begin] = Object.assign(Object.assign({}, p), { values: [(n - 1) < 0 ? 0 : p.values[n - 1], p.values[n]] }); }); } else { const thrIndex = searchClassThreshold(p.value, gradient.thresholds); tmp[thrIndex].points[p.begin] = this.blockAreaFlag ? Object.assign(Object.assign({}, p), { value: 1 }) : p; } } }); tmp.forEach((lineColor, index) => { var _a; let out = []; const filterPoints = lineColor.points.filter(p => (p != null && p.begin > domain.min && p.begin < domain.max)); filterPoints.forEach((p, n) => { var _a, _b; if (!(((_a = out[out.length - 1]) === null || _a === void 0 ? void 0 : _a.value) == p.value && p.value == ((_b = filterPoints[n + 1]) === null || _b === void 0 ? void 0 : _b.value)) || this.multiAreaFlag) out.push(p); }); out.unshift({ begin: domain.min, value: 0, values: [0, 0] }); out.unshift({ begin: this.xScale.domain()[0], value: 0, values: [0, 0] }); out.push({ begin: domain.max, value: 0, values: [0, 0] }); out.push({ begin: this.xScale.domain()[1], value: 0, values: [0, 0] }); if (out.length > thr) { const bucketSize = out.length / thr; const sampler = largestTriangleOneBucket(); sampler.bucketSize(bucketSize); sampler.x((d) => { return d.begin; }); sampler.y((d) => { return d.value; }); out = sampler(out); } lineColorArray.push({ points: out, color: lineColor.color, alpha: (_a = gradient.thresholds[index]) !== null && _a !== void 0 ? _a : 1 }); }); return lineColorArray.reverse(); } } function searchClassThreshold(x, thresholds) { if (x < thresholds[0]) return 0; for (let i = 0; i < thresholds.length - 1; i++) { if (thresholds[i + 1] > x && x >= thresholds[i]) return i + 1; } return thresholds.length; } function buildColorThreshold(displayColor) { if (displayColor.colors instanceof Array) return { thresholds: displayColor.thresholds, colors: displayColor.colors }; return { thresholds: displayColor.thresholds, colors: Array(displayColor.thresholds.length + 1).fill(displayColor.colors) }; }