/
RcsbD3SequenceManager.ts
103 lines (94 loc) · 4.11 KB
/
RcsbD3SequenceManager.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import {Selection, BaseType, select} from "d3-selection";
import {RcsbD3Constants} from "../RcsbD3Constants";
import {RcsbFvTrackDataElementInterface} from "../../../RcsbDataManager/RcsbDataManager";
import {asyncScheduler, Subscription} from "rxjs";
import {RcsbScaleInterface} from "../RcsbD3ScaleFactory";
export interface PlotSequenceInterface {
elements: Selection<SVGGElement,RcsbFvTrackDataElementInterface,BaseType,undefined>;
xScale: RcsbScaleInterface;
yScale: RcsbScaleInterface;
color?: string;
height:number;
intervalRatio: [number,number];
}
export interface PlotSequenceLineInterface {
xScale: RcsbScaleInterface;
yScale: RcsbScaleInterface;
g:Selection<SVGGElement,any,null,undefined>;
height:number;
color?: string;
}
export interface MoveSequenceInterface {
xScale: RcsbScaleInterface;
intervalRatio: [number,number];
}
export class RcsbD3SequenceManager {
private textElements: Selection<SVGTextElement, RcsbFvTrackDataElementInterface, BaseType, undefined> = select<SVGTextElement, RcsbFvTrackDataElementInterface>(RcsbD3Constants.EMPTY);
private plotTask: Subscription = new Subscription();
plot(config: PlotSequenceInterface){
this.plotTask.unsubscribe();
this.plotTask = asyncScheduler.schedule(()=>{
const xScale = config.xScale;
const yScale = config.yScale;
this.textElements = config.elements.select(RcsbD3Constants.TEXT);
this.textElements
.attr(RcsbD3Constants.X, (d:RcsbFvTrackDataElementInterface) => {
return xScale(d.begin) ?? 0;
})
.attr(RcsbD3Constants.Y, yScale(Math.floor(config.height*0.5)+4) ?? 0)
.transition()
.duration(500)
.attr(RcsbD3Constants.FONT_SIZE, "10")
.attr(RcsbD3Constants.FONT_FAMILY,"Arial")
.attr(RcsbD3Constants.TEXT_ANCHOR, "middle")
.attr(RcsbD3Constants.FILL, (d:RcsbFvTrackDataElementInterface) => {
if (typeof d.color === "string"){
return d.color;
} else if(typeof config.color === "string"){
return config.color;
}else{
console.warn("Config color noy found");
return "#CCCCCC";
}
}).text((d:RcsbFvTrackDataElementInterface) => {
return d.label || "";
}).attr(RcsbD3Constants.FILL_OPACITY,()=>{
return RcsbD3SequenceManager.opacity(xScale,config.intervalRatio)
})
});
}
static plotSequenceLine(config: PlotSequenceLineInterface): void{
config.g.select(RcsbD3Constants.LINE).remove();
config.g.append(RcsbD3Constants.LINE)
.style(RcsbD3Constants.STROKE_WIDTH,2)
.style(RcsbD3Constants.STROKE, "#DDDDDD")
.attr(RcsbD3Constants.STROKE_DASH,"2")
.attr(RcsbD3Constants.X1, config.xScale.range()[0])
.attr(RcsbD3Constants.Y1, config.yScale(config.height*0.5) ?? 0)
.attr(RcsbD3Constants.X2, config.xScale.range()[1])
.attr(RcsbD3Constants.Y2, config.yScale(config.height*0.5) ?? 0);
}
move(config: MoveSequenceInterface){
asyncScheduler.schedule(()=>{
const xScale = config.xScale;
this.textElements
.attr(RcsbD3Constants.X, (d:RcsbFvTrackDataElementInterface) => {
return xScale(d.begin) ?? 0;
})
.attr(RcsbD3Constants.FILL_OPACITY,()=>{
return RcsbD3SequenceManager.opacity(xScale,config.intervalRatio)
});
});
}
private static opacity (xScale: RcsbScaleInterface, intervalRatio: [number,number]): number {
const r = (xScale.range()[1]-xScale.range()[0])/(xScale.domain()[1]-xScale.domain()[0]);
const o_min = 0.2;
const a = intervalRatio[0];
const b = intervalRatio[1];
if(r<b) {
return (1-o_min)/(b-a)*(r-a)+o_min;
} else {
return 1;
}
}
}