@rcsb/rcsb-saguaro
Version:
RCSB 1D Feature Viewer
240 lines (239 loc) • 9.95 kB
JavaScript
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)
};
}