oncoprintjs 6.0.3 → 6.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +301 -61
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +299 -58
- package/dist/index.js.map +1 -1
- package/dist/js/oncoprintmodel.d.ts +11 -1
- package/dist/js/oncoprintwebglcellview.d.ts +17 -1
- package/package.json +2 -2
- package/src/index.tsx +1 -0
- package/src/js/oncoprint.ts +8 -0
- package/src/js/oncoprintmodel.ts +110 -3
- package/src/js/oncoprinttrackinfoview.ts +19 -2
- package/src/js/oncoprinttrackoptionsview.ts +13 -11
- package/src/js/oncoprintwebglcellview.ts +157 -1
|
@@ -30,6 +30,17 @@ type ColumnIdIndex = number;
|
|
|
30
30
|
type PositionVertex = [number, number, number];
|
|
31
31
|
type ColorVertex = [number, number, number, number];
|
|
32
32
|
|
|
33
|
+
type OncoprintGap = {
|
|
34
|
+
origin_x: number;
|
|
35
|
+
origin_y: number;
|
|
36
|
+
data: OncoprintGapConfig;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type OncoprintGapConfig = {
|
|
40
|
+
labelFormatter: () => string;
|
|
41
|
+
tooltipFormatter: () => string;
|
|
42
|
+
};
|
|
43
|
+
|
|
33
44
|
export type OncoprintWebGLContext = WebGLRenderingContext & {
|
|
34
45
|
viewportWidth: number;
|
|
35
46
|
viewportHeight: number;
|
|
@@ -87,6 +98,7 @@ export default class OncoprintWebGLCellView {
|
|
|
87
98
|
private mouseMoveHandler: (evt: MouseMoveEvent) => void;
|
|
88
99
|
|
|
89
100
|
private ctx: OncoprintWebGLContext | null;
|
|
101
|
+
private gap_ctx: CanvasRenderingContext2D | null;
|
|
90
102
|
private ext: ANGLE_instanced_arrays | null;
|
|
91
103
|
private overlay_ctx: CanvasRenderingContext2D | null;
|
|
92
104
|
private column_label_ctx: CanvasRenderingContext2D | null;
|
|
@@ -129,6 +141,7 @@ export default class OncoprintWebGLCellView {
|
|
|
129
141
|
private $container: JQuery,
|
|
130
142
|
private $canvas: JQuery<HTMLCanvasElement>,
|
|
131
143
|
private $overlay_canvas: JQuery<HTMLCanvasElement>,
|
|
144
|
+
private $gap_canvas: JQuery<HTMLCanvasElement>,
|
|
132
145
|
private $column_label_canvas: JQuery<HTMLCanvasElement>,
|
|
133
146
|
private $dummy_scroll_div_contents: JQuery,
|
|
134
147
|
model: OncoprintModel,
|
|
@@ -249,6 +262,7 @@ export default class OncoprintWebGLCellView {
|
|
|
249
262
|
const offset = self.$overlay_canvas.offset();
|
|
250
263
|
const mouseX = evt.pageX - offset.left;
|
|
251
264
|
const mouseY = evt.pageY - offset.top;
|
|
265
|
+
|
|
252
266
|
let overlapping_cells = model.getOverlappingCells(
|
|
253
267
|
mouseX + self.scroll_x,
|
|
254
268
|
mouseY + self.scroll_y
|
|
@@ -294,9 +308,43 @@ export default class OncoprintWebGLCellView {
|
|
|
294
308
|
)
|
|
295
309
|
);
|
|
296
310
|
} else {
|
|
297
|
-
tooltip.hideIfNotAlreadyGoingTo(150);
|
|
298
311
|
overlapping_cells = null;
|
|
299
312
|
}
|
|
313
|
+
|
|
314
|
+
// find a gap which is in range of mouse position
|
|
315
|
+
const overlappingGap = self.gapTooltipTargets.find(
|
|
316
|
+
(t: any) => {
|
|
317
|
+
return (
|
|
318
|
+
_.inRange(mouseX - t.origin_x, 0, 20) &&
|
|
319
|
+
_.inRange(t.origin_y - mouseY, -10, 15)
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
// if there is no gap, turn
|
|
325
|
+
if (overlappingGap === undefined) {
|
|
326
|
+
self.hoveredGap = undefined;
|
|
327
|
+
} else if (self.hoveredGap === overlappingGap) {
|
|
328
|
+
// tooltip should already be showing, so do nothing
|
|
329
|
+
} else {
|
|
330
|
+
// we have a new hovered gap, so show a tooltip
|
|
331
|
+
const clientRect = self.$overlay_canvas[0].getBoundingClientRect();
|
|
332
|
+
self.hoveredGap = overlappingGap;
|
|
333
|
+
tooltip.center = false;
|
|
334
|
+
tooltip.show(
|
|
335
|
+
250,
|
|
336
|
+
clientRect.left + overlappingGap.origin_x,
|
|
337
|
+
clientRect.top + overlappingGap.origin_y - 20,
|
|
338
|
+
$(
|
|
339
|
+
`<span>${overlappingGap.data.tooltipFormatter()}</span>`
|
|
340
|
+
),
|
|
341
|
+
false
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (!overlapping_data && !overlappingGap) {
|
|
346
|
+
tooltip.hideIfNotAlreadyGoingTo(150);
|
|
347
|
+
}
|
|
300
348
|
} else {
|
|
301
349
|
overlapping_cells = null;
|
|
302
350
|
drag_end_x = mouseX;
|
|
@@ -353,6 +401,21 @@ export default class OncoprintWebGLCellView {
|
|
|
353
401
|
});
|
|
354
402
|
}
|
|
355
403
|
|
|
404
|
+
private drawGapLabel(txt: string, x: number, y: number) {
|
|
405
|
+
this.gap_ctx.font = '15pt Arial';
|
|
406
|
+
this.gap_ctx.textAlign = 'right';
|
|
407
|
+
|
|
408
|
+
const origin_x = x * this.supersampling_ratio + 52;
|
|
409
|
+
const origin_y = y * this.supersampling_ratio + 4;
|
|
410
|
+
|
|
411
|
+
this.gap_ctx.fillText(txt, origin_x, origin_y);
|
|
412
|
+
|
|
413
|
+
return {
|
|
414
|
+
origin_x: x,
|
|
415
|
+
origin_y: y,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
356
419
|
private getNewCanvas() {
|
|
357
420
|
const old_canvas = this.$canvas[0];
|
|
358
421
|
const new_canvas = old_canvas.cloneNode() as HTMLCanvasElement;
|
|
@@ -364,6 +427,14 @@ export default class OncoprintWebGLCellView {
|
|
|
364
427
|
this.ext = null;
|
|
365
428
|
}
|
|
366
429
|
|
|
430
|
+
private getGapContext() {
|
|
431
|
+
try {
|
|
432
|
+
return this.$gap_canvas[0].getContext('2d');
|
|
433
|
+
} catch (e) {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
367
438
|
private getWebGLCanvasContext() {
|
|
368
439
|
try {
|
|
369
440
|
const canvas = this.$canvas[0];
|
|
@@ -534,11 +605,14 @@ export default class OncoprintWebGLCellView {
|
|
|
534
605
|
highlightWidth,
|
|
535
606
|
highlightHeight
|
|
536
607
|
);
|
|
608
|
+
|
|
537
609
|
this.overlay_ctx.restore();
|
|
538
610
|
}
|
|
539
611
|
}
|
|
540
612
|
|
|
541
613
|
private getWebGLContextAndSetUpMatrices() {
|
|
614
|
+
this.gap_ctx = this.getGapContext();
|
|
615
|
+
|
|
542
616
|
this.ctx = this.getWebGLCanvasContext();
|
|
543
617
|
if (this.ctx) {
|
|
544
618
|
this.ext = this.ctx.getExtension('ANGLE_instanced_arrays');
|
|
@@ -681,6 +755,13 @@ export default class OncoprintWebGLCellView {
|
|
|
681
755
|
this.dummy_scroll_div_client_size.update();
|
|
682
756
|
this.$canvas[0].height = this.supersampling_ratio * height;
|
|
683
757
|
this.$canvas[0].style.height = height + 'px';
|
|
758
|
+
this.$gap_canvas[0].height = this.supersampling_ratio * height;
|
|
759
|
+
this.$gap_canvas[0].style.height = height + 'px';
|
|
760
|
+
|
|
761
|
+
this.$gap_canvas[0].width =
|
|
762
|
+
this.supersampling_ratio * visible_area_width;
|
|
763
|
+
this.$gap_canvas[0].style.width = visible_area_width + 'px';
|
|
764
|
+
|
|
684
765
|
this.$overlay_canvas[0].height = this.supersampling_ratio * height;
|
|
685
766
|
this.$overlay_canvas[0].style.height = height + 'px';
|
|
686
767
|
this.$column_label_canvas[0].height = this.supersampling_ratio * height;
|
|
@@ -701,6 +782,10 @@ export default class OncoprintWebGLCellView {
|
|
|
701
782
|
this.getColumnLabelsContext();
|
|
702
783
|
}
|
|
703
784
|
|
|
785
|
+
public gapTooltipTargets: OncoprintGap[] = [];
|
|
786
|
+
|
|
787
|
+
public hoveredGap: OncoprintGap;
|
|
788
|
+
|
|
704
789
|
private renderAllTracks(model: OncoprintModel, dont_resize?: boolean) {
|
|
705
790
|
if (this.rendering_suppressed) {
|
|
706
791
|
return;
|
|
@@ -743,11 +828,45 @@ export default class OncoprintWebGLCellView {
|
|
|
743
828
|
this.ctx.clearColor(1.0, 1.0, 1.0, 1.0);
|
|
744
829
|
this.ctx.clear(this.ctx.COLOR_BUFFER_BIT | this.ctx.DEPTH_BUFFER_BIT);
|
|
745
830
|
|
|
831
|
+
this.gap_ctx.clearRect(
|
|
832
|
+
0,
|
|
833
|
+
0,
|
|
834
|
+
this.$gap_canvas[0].width,
|
|
835
|
+
this.$gap_canvas[0].height
|
|
836
|
+
);
|
|
837
|
+
|
|
838
|
+
const gapOffsets = model.getGapOffsets();
|
|
839
|
+
|
|
746
840
|
const tracks = model.getTracks();
|
|
841
|
+
|
|
842
|
+
this.gapTooltipTargets = [];
|
|
843
|
+
|
|
747
844
|
for (let i = 0; i < tracks.length; i++) {
|
|
748
845
|
const track_id = tracks[i];
|
|
749
846
|
const cell_top = model.getCellTops(track_id);
|
|
750
847
|
const cell_height = model.getCellHeight(track_id);
|
|
848
|
+
|
|
849
|
+
if (model.showGaps()) {
|
|
850
|
+
const gaps = this.getGaps(model, track_id);
|
|
851
|
+
if (gaps) {
|
|
852
|
+
gaps.forEach((gap: OncoprintGapConfig, i: number) => {
|
|
853
|
+
const x = gapOffsets[i] - scroll_x - model.getGapSize();
|
|
854
|
+
const y =
|
|
855
|
+
model.getZoomedTrackTops()[track_id] +
|
|
856
|
+
cell_height -
|
|
857
|
+
scroll_y;
|
|
858
|
+
|
|
859
|
+
this.drawGapLabel(gap.labelFormatter(), x, y);
|
|
860
|
+
|
|
861
|
+
this.gapTooltipTargets.push({
|
|
862
|
+
origin_x: x,
|
|
863
|
+
origin_y: y,
|
|
864
|
+
data: gap,
|
|
865
|
+
});
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
751
870
|
if (
|
|
752
871
|
cell_top / zoom_y >= window_bottom ||
|
|
753
872
|
(cell_top + cell_height) / zoom_y < window_top
|
|
@@ -756,6 +875,7 @@ export default class OncoprintWebGLCellView {
|
|
|
756
875
|
continue;
|
|
757
876
|
}
|
|
758
877
|
const buffers = this.getTrackBuffers(track_id);
|
|
878
|
+
|
|
759
879
|
if (buffers.position.numItems === 0) {
|
|
760
880
|
continue;
|
|
761
881
|
}
|
|
@@ -1877,6 +1997,13 @@ export default class OncoprintWebGLCellView {
|
|
|
1877
1997
|
return this.dummy_scroll_div_client_size.get();
|
|
1878
1998
|
}
|
|
1879
1999
|
|
|
2000
|
+
getGaps(model: OncoprintModel, track_id: number) {
|
|
2001
|
+
const custom = model.getTrackCustomOptions(track_id);
|
|
2002
|
+
return _.isEmpty(model.ids_after_a_gap.get())
|
|
2003
|
+
? undefined
|
|
2004
|
+
: custom.find(t => !!t.gapLabelsFn)?.gapLabelsFn(model);
|
|
2005
|
+
}
|
|
2006
|
+
|
|
1880
2007
|
public toSVGGroup(
|
|
1881
2008
|
model: OncoprintModel,
|
|
1882
2009
|
offset_x: number,
|
|
@@ -1887,9 +2014,13 @@ export default class OncoprintWebGLCellView {
|
|
|
1887
2014
|
const tracks = model.getTracks();
|
|
1888
2015
|
const zoomedColumnLeft = model.getZoomedColumnLeft();
|
|
1889
2016
|
// add cell shapes
|
|
2017
|
+
|
|
2018
|
+
const gapOffsets = model.getGapOffsets();
|
|
2019
|
+
|
|
1890
2020
|
for (let i = 0; i < tracks.length; i++) {
|
|
1891
2021
|
const track_id = tracks[i];
|
|
1892
2022
|
const offset_y = cell_tops[track_id];
|
|
2023
|
+
const cell_height = model.getCellHeight(track_id);
|
|
1893
2024
|
const universal_shapes = model.getTrackUniversalShapes(
|
|
1894
2025
|
track_id,
|
|
1895
2026
|
false
|
|
@@ -1898,6 +2029,31 @@ export default class OncoprintWebGLCellView {
|
|
|
1898
2029
|
track_id,
|
|
1899
2030
|
false
|
|
1900
2031
|
);
|
|
2032
|
+
|
|
2033
|
+
const custom = model.getTrackCustomOptions(track_id);
|
|
2034
|
+
if (gapOffsets[0]) {
|
|
2035
|
+
const gaps = _.isEmpty(model.ids_after_a_gap.get())
|
|
2036
|
+
? undefined
|
|
2037
|
+
: custom.find(t => !!t.gapLabelsFn)?.gapLabelsFn(model);
|
|
2038
|
+
|
|
2039
|
+
if (gaps) {
|
|
2040
|
+
gaps.forEach((gap: any, i: number) => {
|
|
2041
|
+
const textElt = makeSvgElement('text', {
|
|
2042
|
+
x: gapOffsets[i] - model.getGapSize() + 25,
|
|
2043
|
+
y: offset_y + cell_height - 3,
|
|
2044
|
+
'font-size': '10',
|
|
2045
|
+
'font-family': 'Arial',
|
|
2046
|
+
'font-weight': 'normal',
|
|
2047
|
+
'text-anchor': 'end',
|
|
2048
|
+
'alignment-baseline': 'top',
|
|
2049
|
+
});
|
|
2050
|
+
|
|
2051
|
+
textElt.textContent = gap.labelFormatter();
|
|
2052
|
+
root.appendChild(textElt);
|
|
2053
|
+
});
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
|
|
1901
2057
|
for (let j = 0; j < identified_shape_list_list.length; j++) {
|
|
1902
2058
|
const id_sl = identified_shape_list_list[j];
|
|
1903
2059
|
const id = id_sl.id;
|