higlass 1.13.5 → 1.13.6
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/README.md +48 -54
- package/app/globals.d.ts +1 -1
- package/app/missing-types.d.ts +4 -1
- package/app/scripts/AddTrackDialog.jsx +3 -3
- package/app/scripts/AddTrackPositionMenu.jsx +2 -2
- package/app/scripts/Annotations1dTrack.js +1 -1
- package/app/scripts/Annotations2dTrack.js +3 -5
- package/app/scripts/Autocomplete.jsx +14 -21
- package/app/scripts/AxisPixi.js +6 -10
- package/app/scripts/BarTrack.js +3 -3
- package/app/scripts/BedLikeTrack.js +12 -13
- package/app/scripts/Button.jsx +1 -1
- package/app/scripts/CNVIntervalTrack.js +1 -1
- package/app/scripts/CenterTrack.jsx +8 -7
- package/app/scripts/Chromosome2DAnnotations.js +1 -1
- package/app/scripts/Chromosome2DLabels.js +1 -1
- package/app/scripts/ChromosomeGrid.js +49 -38
- package/app/scripts/ChromosomeInfo.js +1 -1
- package/app/scripts/CombinedTrack.js +3 -1
- package/app/scripts/ConfigTrackMenu.jsx +1 -1
- package/app/scripts/ConfigViewMenu.jsx +2 -2
- package/app/scripts/ContextMenuContainer.jsx +1 -2
- package/app/scripts/ContextMenuItem.jsx +1 -0
- package/app/scripts/CrossRule.js +1 -1
- package/app/scripts/CustomTrackDialog.jsx +2 -2
- package/app/scripts/Dialog.jsx +2 -2
- package/app/scripts/DragListeningDiv.jsx +1 -1
- package/app/scripts/DraggableDiv.jsx +2 -3
- package/app/scripts/ExportLinkDialog.jsx +1 -1
- package/app/scripts/GalleryTracks.jsx +77 -78
- package/app/scripts/GenomePositionSearchBox.jsx +10 -9
- package/app/scripts/HeatmapOptions.jsx +4 -2
- package/app/scripts/HeatmapTiledPixiTrack.js +23 -32
- package/app/scripts/HiGlassComponent.jsx +75 -98
- package/app/scripts/Horizontal1dHeatmapTrack.js +1 -1
- package/app/scripts/Horizontal2DDomainsTrack.js +1 -1
- package/app/scripts/HorizontalChromosomeLabels.js +28 -22
- package/app/scripts/HorizontalGeneAnnotationsTrack.js +1 -1
- package/app/scripts/HorizontalHeatmapTrack.js +2 -2
- package/app/scripts/HorizontalMultivecTrack.js +6 -7
- package/app/scripts/HorizontalRule.js +1 -2
- package/app/scripts/HorizontalTiled1DPixiTrack.js +4 -4
- package/app/scripts/HorizontalTiledPlot.jsx +9 -9
- package/app/scripts/LeftTrackModifier.js +4 -0
- package/app/scripts/ListWrapper.jsx +1 -2
- package/app/scripts/MapboxTilesTrack.js +1 -2
- package/app/scripts/Modal.jsx +2 -2
- package/app/scripts/MoveableTrack.jsx +10 -12
- package/app/scripts/NestedContextMenu.jsx +2 -1
- package/app/scripts/OSMTileIdsTrack.js +1 -1
- package/app/scripts/OverlayTrack.js +4 -4
- package/app/scripts/PixiTrack.js +27 -13
- package/app/scripts/PlotTypeChooser.jsx +3 -4
- package/app/scripts/SearchField.js +5 -5
- package/app/scripts/SeriesListItems.jsx +3 -4
- package/app/scripts/SeriesListMenu.jsx +6 -11
- package/app/scripts/SeriesListSubmenuMixin.jsx +1 -1
- package/app/scripts/SketchInlinePicker.jsx +2 -2
- package/app/scripts/SortableList.jsx +1 -1
- package/app/scripts/Tiled1DPixiTrack.js +5 -1
- package/app/scripts/TiledPixiTrack.js +232 -99
- package/app/scripts/TiledPlot.jsx +33 -43
- package/app/scripts/TilesetFinder.jsx +12 -4
- package/app/scripts/Track.js +1 -1
- package/app/scripts/TrackArea.jsx +4 -0
- package/app/scripts/TrackControl.jsx +2 -2
- package/app/scripts/TrackRenderer.jsx +30 -31
- package/app/scripts/ValueIntervalTrack.js +1 -1
- package/app/scripts/VerticalRule.js +2 -2
- package/app/scripts/VerticalTiledPlot.jsx +7 -7
- package/app/scripts/ViewConfigEditor.jsx +1 -1
- package/app/scripts/ViewContextMenu.jsx +4 -4
- package/app/scripts/ViewHeader.jsx +6 -7
- package/app/scripts/ViewportTracker2D.js +1 -1
- package/app/scripts/api.js +5 -6
- package/app/scripts/configs/available-track-types.js +1 -1
- package/app/scripts/configs/positions-by-datatype.js +2 -2
- package/app/scripts/configs/themes.js +0 -1
- package/app/scripts/configs/tracks-info-by-type.js +11 -8
- package/app/scripts/configs/tracks-info.js +2 -2
- package/app/scripts/d3-context-menu.js +3 -4
- package/app/scripts/data-fetchers/DataFetcher.js +35 -36
- package/app/scripts/data-fetchers/genbank-fetcher.js +6 -10
- package/app/scripts/data-fetchers/local-tile-fetcher.js +2 -6
- package/app/scripts/hglib.jsx +1 -1
- package/app/scripts/hocs/with-modal.jsx +32 -10
- package/app/scripts/hocs/with-pub-sub.js +12 -3
- package/app/scripts/hocs/with-theme.jsx +21 -14
- package/app/scripts/icons.jsx +3 -2
- package/app/scripts/mixwith.js +2 -2
- package/app/scripts/plugins/get-data-fetcher.js +2 -3
- package/app/scripts/services/chrom-info.js +32 -4
- package/app/scripts/services/element-resize-listener.js +2 -2
- package/app/scripts/services/index.js +0 -1
- package/app/scripts/services/tile-proxy.js +368 -281
- package/app/scripts/services/worker.js +30 -28
- package/app/scripts/test-helpers/test-helpers.jsx +3 -3
- package/app/scripts/types.ts +50 -44
- package/app/scripts/utils/LruCache.js +3 -2
- package/app/scripts/utils/assert.js +19 -0
- package/app/scripts/utils/background-task-scheduler.js +2 -0
- package/app/scripts/utils/color-domain-to-rgba-array.js +13 -3
- package/app/scripts/utils/dict-items.js +1 -0
- package/app/scripts/utils/dict-keys.js +1 -0
- package/app/scripts/utils/dict-values.js +1 -0
- package/app/scripts/utils/expand-combined-tracks.js +11 -7
- package/app/scripts/utils/fill-in-min-widths.js +47 -21
- package/app/scripts/utils/flatten.js +0 -1
- package/app/scripts/utils/get-aggregation-function.js +1 -1
- package/app/scripts/utils/get-default-track-for-datatype.js +36 -10
- package/app/scripts/utils/get-higlass-components.js +27 -3
- package/app/scripts/utils/get-track-position-by-uid.js +8 -1
- package/app/scripts/utils/get-xylofon.js +12 -9
- package/app/scripts/utils/has-parent.js +5 -5
- package/app/scripts/utils/hex-string-to-int.js +1 -1
- package/app/scripts/utils/interval-tree.js +222 -177
- package/app/scripts/utils/load-chrom-infos.js +4 -1
- package/app/scripts/utils/pixi-text-to-svg.js +5 -9
- package/app/scripts/utils/range-query-2d.js +3 -3
- package/app/scripts/utils/reduce.js +12 -5
- package/app/scripts/utils/segments-to-rows.js +14 -11
- package/app/scripts/utils/show-mouse-position.js +16 -0
- package/app/scripts/utils/svg-line.js +7 -8
- package/app/scripts/utils/type-guards.js +16 -7
- package/app/scripts/utils/visit-positioned-tracks.js +7 -5
- package/app/styles/d3-context-menu.css +0 -1
- package/app/styles/prism.css +1 -0
- package/dist/hglib.js +78064 -77770
- package/dist/hglib.min.js +109 -109
- package/dist/higlass.mjs +82736 -82442
- package/package.json +13 -18
|
@@ -1,15 +1,43 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
// Configs
|
|
3
2
|
import { DEFAULT_TRACKS_FOR_DATATYPE } from '../configs';
|
|
4
3
|
|
|
4
|
+
/** @typedef {typeof DEFAULT_TRACKS_FOR_DATATYPE} DataTypeMapping */
|
|
5
|
+
/** @typedef {keyof DataTypeMapping} DataType */
|
|
6
|
+
/** @typedef {"top" | "bottom" | "left" | "right" | "center"} TrackPosition */
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @template {string} D
|
|
10
|
+
* @template {string} P
|
|
11
|
+
* @typedef {D extends DataType ? P extends keyof DataTypeMapping[D] ? DataTypeMapping[D][P] : undefined : undefined} ExtractDataType
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @template {DataType | string & {}} D
|
|
16
|
+
* @template {TrackPosition} P
|
|
17
|
+
*
|
|
18
|
+
* @param {D} datatype
|
|
19
|
+
* @param {P} position
|
|
20
|
+
* @returns {ExtractDataType<D, P>}
|
|
21
|
+
*/
|
|
22
|
+
function findDefaultTrackType(datatype, position) {
|
|
23
|
+
// @ts-expect-error - ok with undefined check
|
|
24
|
+
return DEFAULT_TRACKS_FOR_DATATYPE[datatype] !== undefined
|
|
25
|
+
? // @ts-expect-error - ok with undefined check
|
|
26
|
+
DEFAULT_TRACKS_FOR_DATATYPE[datatype][position]
|
|
27
|
+
: undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
5
30
|
/**
|
|
6
31
|
* Gets the default track as defined in utils/default-tracks-for-datatype.js
|
|
7
32
|
*
|
|
8
|
-
* @
|
|
9
|
-
* @
|
|
10
|
-
* @
|
|
11
|
-
*
|
|
12
|
-
* @
|
|
33
|
+
* @template {DataType | string & {}} D
|
|
34
|
+
* @template {TrackPosition} P
|
|
35
|
+
* @template {ReadonlyArray<{ type: string }>} ATracks
|
|
36
|
+
*
|
|
37
|
+
* @param {D} datatype - The datatype to get the default track for
|
|
38
|
+
* @param {P} position - top, bottom, left, right, center
|
|
39
|
+
* @param {ATracks} availableTracks - List of tracks to choose from, typically obtained from AVAILABLE_TRACK_TYPES(...)
|
|
40
|
+
* @return {ATracks[number] | undefined} An element of availableTracks or undefined
|
|
13
41
|
*/
|
|
14
42
|
const getDefaultTrackForDatatype = (datatype, position, availableTracks) => {
|
|
15
43
|
if (availableTracks.length === 0) {
|
|
@@ -21,10 +49,8 @@ const getDefaultTrackForDatatype = (datatype, position, availableTracks) => {
|
|
|
21
49
|
}
|
|
22
50
|
|
|
23
51
|
let usedTrack = availableTracks[0];
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
? DEFAULT_TRACKS_FOR_DATATYPE[datatype][position]
|
|
27
|
-
: undefined;
|
|
52
|
+
|
|
53
|
+
const defaultTrackType = findDefaultTrackType(datatype, position);
|
|
28
54
|
|
|
29
55
|
if (defaultTrackType !== undefined) {
|
|
30
56
|
// If we found a default track type for this datatype,
|
|
@@ -1,19 +1,43 @@
|
|
|
1
|
-
|
|
1
|
+
/** @import * as t from '../types' */
|
|
2
|
+
/** @import HiGlassComponent from '../HiGlassComponent' */
|
|
3
|
+
/** @import { TrackRenderer } from '../TrackRenderer' */
|
|
4
|
+
/** @import { TiledPlot } from '../TiledPlot' */
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {HiGlassComponent} hgc
|
|
8
|
+
* @param {string} viewUid
|
|
9
|
+
* @param {string | undefined} trackUid
|
|
10
|
+
* @returns {t.TrackObject | undefined}
|
|
11
|
+
*/
|
|
2
12
|
export const getTrackObjectFromHGC = (hgc, viewUid, trackUid) => {
|
|
13
|
+
/** @type {string} */
|
|
3
14
|
let newViewUid = viewUid;
|
|
4
|
-
|
|
15
|
+
/** @type {string} */
|
|
16
|
+
let newTrackUid;
|
|
5
17
|
|
|
6
18
|
if (!trackUid) {
|
|
7
19
|
// didn't specify a trackUid so use the viewUid as the trackUid
|
|
8
20
|
// and use the first plot
|
|
9
21
|
newTrackUid = viewUid;
|
|
10
22
|
newViewUid = Object.values(hgc.state.views)[0].uid;
|
|
23
|
+
} else {
|
|
24
|
+
newTrackUid = trackUid;
|
|
11
25
|
}
|
|
12
26
|
|
|
13
|
-
return hgc.tiledPlots[newViewUid].trackRenderer
|
|
27
|
+
return hgc.tiledPlots[newViewUid].trackRenderer?.getTrackObject(newTrackUid);
|
|
14
28
|
};
|
|
15
29
|
|
|
30
|
+
/**
|
|
31
|
+
* @param {HiGlassComponent} hgc
|
|
32
|
+
* @param {string} viewUid
|
|
33
|
+
* @returns {TrackRenderer | null}
|
|
34
|
+
*/
|
|
16
35
|
export const getTrackRenderer = (hgc, viewUid) =>
|
|
17
36
|
hgc.tiledPlots[viewUid].trackRenderer;
|
|
18
37
|
|
|
38
|
+
/**
|
|
39
|
+
* @param {HiGlassComponent} hgc
|
|
40
|
+
* @param {string} viewUid
|
|
41
|
+
* @returns {TiledPlot}
|
|
42
|
+
*/
|
|
19
43
|
export const getTiledPlot = (hgc, viewUid) => hgc.tiledPlots[viewUid];
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import visitPositionedTracks from './visit-positioned-tracks';
|
|
3
2
|
|
|
3
|
+
/** @import { TrackPosition } from '../types' */
|
|
4
|
+
/** @import { PositionedTracks } from './visit-positioned-tracks' */
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Get a track's orientation by its UID.
|
|
8
|
+
*
|
|
9
|
+
* @param {PositionedTracks} positionedTracks
|
|
10
|
+
* @param {string} uid
|
|
11
|
+
* @returns {TrackPosition | null}
|
|
6
12
|
*/
|
|
7
13
|
const getTrackPositionByUid = (positionedTracks, uid) => {
|
|
14
|
+
/** @type {TrackPosition | null} */
|
|
8
15
|
let position = null;
|
|
9
16
|
|
|
10
17
|
visitPositionedTracks(positionedTracks, (track, trackPosition) => {
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
/* global XYLOPHON:false */
|
|
3
|
-
|
|
4
1
|
import map from './map';
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
// Trevor(2025-02-18): Really not sure what the purpose of this module is.
|
|
4
|
+
|
|
5
|
+
const getXylofon = () =>
|
|
6
|
+
/** @type {const} */ ([
|
|
7
|
+
window,
|
|
8
|
+
map((c) => c.charCodeAt(0))(
|
|
9
|
+
// @ts-expect-error - A global added by `vite.config.js`.
|
|
10
|
+
XYLOPHON,
|
|
11
|
+
)
|
|
12
|
+
.map((number) => (number <= 999 ? `00${number}`.slice(-3) : number))
|
|
13
|
+
.join(''),
|
|
14
|
+
]);
|
|
12
15
|
|
|
13
16
|
export default getXylofon;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
/**
|
|
3
2
|
* Test whether a DOM element is the parent of another DOM element.
|
|
4
3
|
*
|
|
5
|
-
* @param {
|
|
6
|
-
* @param {
|
|
7
|
-
*
|
|
8
|
-
* @return {Boolean} If `true` `el` has `target` as a parent.
|
|
4
|
+
* @param {HTMLElement} el - Potential child element.
|
|
5
|
+
* @param {HTMLElement} target - Target parent element which is tested to have `el` as a child.
|
|
6
|
+
* @return {boolean} If `true` `el` has `target` as a parent.
|
|
9
7
|
*/
|
|
10
8
|
const hasParent = (el, target) => {
|
|
9
|
+
/** @type {HTMLElement | null} */
|
|
11
10
|
let _el = el;
|
|
12
11
|
|
|
13
12
|
while (_el && _el !== target && _el.tagName !== 'HTML') {
|
|
13
|
+
// @ts-expect-error - we know ParentElement is also DOM or null
|
|
14
14
|
_el = _el.parentNode;
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -10,6 +10,6 @@
|
|
|
10
10
|
* @param {string} str - HEX string
|
|
11
11
|
* @return {number} An (integer) HEX number
|
|
12
12
|
*/
|
|
13
|
-
const hexStrToInt = (str) => parseInt(str.replace(/^#/, ''), 16);
|
|
13
|
+
const hexStrToInt = (str) => Number.parseInt(str.replace(/^#/, ''), 16);
|
|
14
14
|
|
|
15
15
|
export default hexStrToInt;
|
|
@@ -1,47 +1,221 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/** @typedef {[start: number, end: number]} Interval */
|
|
2
|
+
|
|
3
|
+
class IntervalTreeNode {
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
-
* @
|
|
5
|
+
* @param {number} start
|
|
6
|
+
* @param {number} end
|
|
7
|
+
* @param {IntervalTreeNode} [left]
|
|
8
|
+
* @param {IntervalTreeNode} [right]
|
|
6
9
|
*/
|
|
7
|
-
|
|
10
|
+
constructor(start, end, left, right) {
|
|
11
|
+
/**
|
|
12
|
+
* Node interval.
|
|
13
|
+
* @type {Interval}
|
|
14
|
+
*/
|
|
15
|
+
this.interval = [start, end];
|
|
16
|
+
/**
|
|
17
|
+
* Max endpoint in subtree which starts from this node.
|
|
18
|
+
* @type {number}
|
|
19
|
+
*/
|
|
20
|
+
this.max = Number.NEGATIVE_INFINITY;
|
|
21
|
+
/**
|
|
22
|
+
* Parent node.
|
|
23
|
+
* @type {IntervalTreeNode | null}
|
|
24
|
+
*/
|
|
25
|
+
this.parentNode = null;
|
|
26
|
+
/**
|
|
27
|
+
* Left child node.
|
|
28
|
+
* @type {IntervalTreeNode | null}
|
|
29
|
+
*/
|
|
30
|
+
this.left = left ?? null;
|
|
31
|
+
/**
|
|
32
|
+
* Right child node.
|
|
33
|
+
* @type {IntervalTreeNode | null}
|
|
34
|
+
*/
|
|
35
|
+
this.right = right ?? null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Interval tree.
|
|
41
|
+
*
|
|
42
|
+
* @public
|
|
43
|
+
* @constructor
|
|
44
|
+
*/
|
|
45
|
+
export class IntervalTree {
|
|
46
|
+
constructor() {
|
|
47
|
+
/**
|
|
48
|
+
* Root node of the tree.
|
|
49
|
+
* @type {IntervalTreeNode | null}
|
|
50
|
+
*/
|
|
51
|
+
this.root = null;
|
|
52
|
+
}
|
|
8
53
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
54
|
+
* Add new interval to the tree.
|
|
55
|
+
*
|
|
56
|
+
* @public
|
|
57
|
+
* @param {Interval} interval - Array with start and end points of the interval.
|
|
11
58
|
*/
|
|
12
|
-
|
|
59
|
+
add(interval) {
|
|
60
|
+
if (!this.root) {
|
|
61
|
+
this.root = new IntervalTreeNode(interval[0], interval[1]);
|
|
62
|
+
this.root.max = interval[1];
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
addHelper(this.root, interval);
|
|
66
|
+
}
|
|
13
67
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
68
|
+
* Checks or point belongs to at least one intarval from the tree.<br><br>
|
|
69
|
+
* Complexity: O(log N).
|
|
70
|
+
*
|
|
71
|
+
* @public
|
|
72
|
+
* @method
|
|
73
|
+
* @param {Number} point Point which should be checked.
|
|
74
|
+
* @return {Boolean} True if point belongs to one of the intervals.
|
|
16
75
|
*/
|
|
17
|
-
|
|
76
|
+
contains(point) {
|
|
77
|
+
return contains(point, this.root);
|
|
78
|
+
}
|
|
18
79
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
80
|
+
* Checks or interval belongs to at least one intarval from the tree.<br><br>
|
|
81
|
+
* Complexity: O(log N).
|
|
82
|
+
*
|
|
83
|
+
* @public
|
|
84
|
+
* @method
|
|
85
|
+
* @param {Interval} interval Interval which should be checked.
|
|
86
|
+
* @return {boolean} True if interval intersects with one of the intervals.
|
|
21
87
|
*/
|
|
22
|
-
|
|
88
|
+
intersects(interval) {
|
|
89
|
+
return intersectsHelper(interval, this.root);
|
|
90
|
+
}
|
|
23
91
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
92
|
+
* Returns height of the tree.
|
|
93
|
+
*
|
|
94
|
+
* @public
|
|
95
|
+
* @method
|
|
96
|
+
* @return {Number} Height of the tree.
|
|
26
97
|
*/
|
|
27
|
-
|
|
28
|
-
|
|
98
|
+
height() {
|
|
99
|
+
return heightHelper(this.root);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Returns node with the max endpoint in subtree.
|
|
103
|
+
*
|
|
104
|
+
* @public
|
|
105
|
+
* @method
|
|
106
|
+
* @param {IntervalTreeNode} node Root node of subtree.
|
|
107
|
+
* @return {IntervalTreeNode | null} IntervalTreeNode with the largest endpoint.
|
|
108
|
+
*/
|
|
109
|
+
findMax(node) {
|
|
110
|
+
const stack = [node];
|
|
111
|
+
let current;
|
|
112
|
+
let max = Number.NEGATIVE_INFINITY;
|
|
113
|
+
let maxNode;
|
|
114
|
+
while (stack.length) {
|
|
115
|
+
current = stack.pop();
|
|
116
|
+
if (current?.left) {
|
|
117
|
+
stack.push(current.left);
|
|
118
|
+
}
|
|
119
|
+
if (current?.right) {
|
|
120
|
+
stack.push(current.right);
|
|
121
|
+
}
|
|
122
|
+
// @ts-expect-error - ok assertion
|
|
123
|
+
if (current?.interval[1] > max) {
|
|
124
|
+
// @ts-expect-error - only ok if above assertion succeeds
|
|
125
|
+
max = current.interval[1];
|
|
126
|
+
maxNode = current;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return maxNode ?? null;
|
|
130
|
+
}
|
|
29
131
|
|
|
30
|
-
/**
|
|
31
|
-
* Interval tree.
|
|
32
|
-
*
|
|
33
|
-
* @public
|
|
34
|
-
* @constructor
|
|
35
|
-
*/
|
|
36
|
-
export default function IntervalTree() {
|
|
37
132
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
133
|
+
* Adjust the max value.
|
|
134
|
+
*
|
|
135
|
+
* @param {Interval} interval
|
|
136
|
+
* @param {IntervalTreeNode | null} node
|
|
40
137
|
*/
|
|
41
|
-
|
|
138
|
+
_removeHelper(interval, node) {
|
|
139
|
+
if (!node) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (node.interval[0] === interval[0] && node.interval[1] === interval[1]) {
|
|
143
|
+
// When left and right children exists
|
|
144
|
+
if (node.left && node.right) {
|
|
145
|
+
let replacement = node.left;
|
|
146
|
+
while (replacement.left) {
|
|
147
|
+
replacement = replacement.left;
|
|
148
|
+
}
|
|
149
|
+
const temp = replacement.interval;
|
|
150
|
+
replacement.interval = node.interval;
|
|
151
|
+
node.interval = temp;
|
|
152
|
+
this._removeHelper(replacement.interval, node);
|
|
153
|
+
} else {
|
|
154
|
+
// When only left or right child exists
|
|
155
|
+
/** @type {"left" | "right"} */
|
|
156
|
+
let side = 'left';
|
|
157
|
+
if (node.right) {
|
|
158
|
+
side = 'right';
|
|
159
|
+
}
|
|
160
|
+
const parentNode = node.parentNode;
|
|
161
|
+
if (parentNode) {
|
|
162
|
+
if (parentNode.left === node) {
|
|
163
|
+
parentNode.left = node[side];
|
|
164
|
+
} else {
|
|
165
|
+
parentNode.right = node[side];
|
|
166
|
+
}
|
|
167
|
+
if (node[side]) {
|
|
168
|
+
// @ts-expect-error - ok to set to a node
|
|
169
|
+
node[side].parentNode = parentNode;
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
this.root = node[side];
|
|
173
|
+
// last node removed
|
|
174
|
+
if (this.root) {
|
|
175
|
+
this.root.parentNode = null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Adjust the max value
|
|
180
|
+
const p = node.parentNode;
|
|
181
|
+
if (p) {
|
|
182
|
+
let maxNode = this.findMax(p);
|
|
183
|
+
const max = maxNode?.interval[1] ?? Number.NEGATIVE_INFINITY;
|
|
184
|
+
while (maxNode) {
|
|
185
|
+
if (maxNode.max === node.interval[1]) {
|
|
186
|
+
maxNode.max = max;
|
|
187
|
+
maxNode = maxNode.parentNode;
|
|
188
|
+
} else {
|
|
189
|
+
maxNode = null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
// could be optimized
|
|
195
|
+
this._removeHelper(interval, node.left);
|
|
196
|
+
this._removeHelper(interval, node.right);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Remove interval from the tree.
|
|
201
|
+
*
|
|
202
|
+
* @public
|
|
203
|
+
* @method
|
|
204
|
+
* @param {Interval} interval - Array with start and end of the interval.
|
|
205
|
+
*/
|
|
206
|
+
remove(interval) {
|
|
207
|
+
return this._removeHelper(interval, this.root);
|
|
208
|
+
}
|
|
42
209
|
}
|
|
43
210
|
|
|
211
|
+
/**
|
|
212
|
+
* @param {IntervalTreeNode} node
|
|
213
|
+
* @param {"left"| "right"} side
|
|
214
|
+
* @param {Interval} interval
|
|
215
|
+
* @returns {void}
|
|
216
|
+
*/
|
|
44
217
|
function addNode(node, side, interval) {
|
|
218
|
+
/** @type {IntervalTreeNode | null} */
|
|
45
219
|
let child = new IntervalTreeNode(interval[0], interval[1]);
|
|
46
220
|
child.max = interval[1];
|
|
47
221
|
child.parentNode = node;
|
|
@@ -56,6 +230,11 @@ function addNode(node, side, interval) {
|
|
|
56
230
|
}
|
|
57
231
|
}
|
|
58
232
|
|
|
233
|
+
/**
|
|
234
|
+
* @param {IntervalTreeNode} node
|
|
235
|
+
* @param {Interval} interval
|
|
236
|
+
* @returns {void}
|
|
237
|
+
*/
|
|
59
238
|
function addHelper(node, interval) {
|
|
60
239
|
if (node.interval[0] > interval[0]) {
|
|
61
240
|
if (node.left) {
|
|
@@ -71,20 +250,10 @@ function addHelper(node, interval) {
|
|
|
71
250
|
}
|
|
72
251
|
|
|
73
252
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* @
|
|
77
|
-
* @param {Array} intreval Array with start and end points of the interval.
|
|
253
|
+
* @param {number} point
|
|
254
|
+
* @param {IntervalTreeNode | null} node
|
|
255
|
+
* @returns {boolean}
|
|
78
256
|
*/
|
|
79
|
-
IntervalTree.prototype.add = function add(interval) {
|
|
80
|
-
if (!this.root) {
|
|
81
|
-
this.root = new IntervalTreeNode(interval[0], interval[1]);
|
|
82
|
-
this.root.max = interval[1];
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
addHelper(this.root, interval);
|
|
86
|
-
};
|
|
87
|
-
|
|
88
257
|
function contains(point, node) {
|
|
89
258
|
if (!node) {
|
|
90
259
|
return false;
|
|
@@ -94,7 +263,7 @@ function contains(point, node) {
|
|
|
94
263
|
}
|
|
95
264
|
let result = false;
|
|
96
265
|
let temp;
|
|
97
|
-
['left', 'right'].forEach((key) => {
|
|
266
|
+
/** @type {const} */ (['left', 'right']).forEach((key) => {
|
|
98
267
|
temp = node[key];
|
|
99
268
|
if (temp) {
|
|
100
269
|
if (temp.max > point) {
|
|
@@ -106,18 +275,10 @@ function contains(point, node) {
|
|
|
106
275
|
}
|
|
107
276
|
|
|
108
277
|
/**
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* @public
|
|
113
|
-
* @method
|
|
114
|
-
* @param {Number} point Point which should be checked.
|
|
115
|
-
* @return {Boolean} True if point belongs to one of the intervals.
|
|
278
|
+
* @param {Interval} a
|
|
279
|
+
* @param {Interval} b
|
|
280
|
+
* @returns {boolean}
|
|
116
281
|
*/
|
|
117
|
-
IntervalTree.prototype.contains = function _contains(point) {
|
|
118
|
-
return contains(point, this.root);
|
|
119
|
-
};
|
|
120
|
-
|
|
121
282
|
function intersects(a, b) {
|
|
122
283
|
return (
|
|
123
284
|
// The first case checks for completely overlapping
|
|
@@ -132,6 +293,11 @@ function intersects(a, b) {
|
|
|
132
293
|
);
|
|
133
294
|
}
|
|
134
295
|
|
|
296
|
+
/**
|
|
297
|
+
* @param {Interval} interval
|
|
298
|
+
* @param {IntervalTreeNode | null} node
|
|
299
|
+
* @returns {boolean}
|
|
300
|
+
*/
|
|
135
301
|
function intersectsHelper(interval, node) {
|
|
136
302
|
if (!node) {
|
|
137
303
|
return false;
|
|
@@ -141,7 +307,7 @@ function intersectsHelper(interval, node) {
|
|
|
141
307
|
}
|
|
142
308
|
let result = false;
|
|
143
309
|
let temp;
|
|
144
|
-
['left', 'right'].forEach((side) => {
|
|
310
|
+
/** @type {const} */ (['left', 'right']).forEach((side) => {
|
|
145
311
|
temp = node[side];
|
|
146
312
|
if (temp && temp.max >= interval[0]) {
|
|
147
313
|
result = result || intersectsHelper(interval, temp);
|
|
@@ -151,133 +317,12 @@ function intersectsHelper(interval, node) {
|
|
|
151
317
|
}
|
|
152
318
|
|
|
153
319
|
/**
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
* @public
|
|
158
|
-
* @method
|
|
159
|
-
* @param {Array} interval Interval which should be checked.
|
|
160
|
-
* @return {Boolean} True if interval intersects with one of the intervals.
|
|
320
|
+
* @param {IntervalTreeNode | null} node
|
|
321
|
+
* @returns {number}
|
|
161
322
|
*/
|
|
162
|
-
IntervalTree.prototype.intersects = function _intersects(interval) {
|
|
163
|
-
return intersectsHelper(interval, this.root);
|
|
164
|
-
};
|
|
165
|
-
|
|
166
323
|
function heightHelper(node) {
|
|
167
324
|
if (!node) {
|
|
168
325
|
return 0;
|
|
169
326
|
}
|
|
170
327
|
return 1 + Math.max(heightHelper(node.left), heightHelper(node.right));
|
|
171
328
|
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Returns height of the tree.
|
|
175
|
-
*
|
|
176
|
-
* @public
|
|
177
|
-
* @method
|
|
178
|
-
* @return {Number} Height of the tree.
|
|
179
|
-
*/
|
|
180
|
-
IntervalTree.prototype.height = function height() {
|
|
181
|
-
return heightHelper(this.root);
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Returns node with the max endpoint in subtree.
|
|
186
|
-
*
|
|
187
|
-
* @public
|
|
188
|
-
* @method
|
|
189
|
-
* @param {IntervalTreeNode} node Root node of subtree.
|
|
190
|
-
* @return {IntervalTreeNode} IntervalTreeNode with the largest endpoint.
|
|
191
|
-
*/
|
|
192
|
-
IntervalTree.prototype.findMax = function findMax(node) {
|
|
193
|
-
const stack = [node];
|
|
194
|
-
let current;
|
|
195
|
-
let max = -Infinity;
|
|
196
|
-
let maxNode;
|
|
197
|
-
while (stack.length) {
|
|
198
|
-
current = stack.pop();
|
|
199
|
-
if (current.left) {
|
|
200
|
-
stack.push(current.left);
|
|
201
|
-
}
|
|
202
|
-
if (current.right) {
|
|
203
|
-
stack.push(current.right);
|
|
204
|
-
}
|
|
205
|
-
if (current.interval[1] > max) {
|
|
206
|
-
max = current.interval[1];
|
|
207
|
-
maxNode = current;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
return maxNode;
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
// adjust the max value
|
|
214
|
-
IntervalTree.prototype._removeHelper = function _removeHelper(interval, node) {
|
|
215
|
-
if (!node) {
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
if (node.interval[0] === interval[0] && node.interval[1] === interval[1]) {
|
|
219
|
-
// When left and right children exists
|
|
220
|
-
if (node.left && node.right) {
|
|
221
|
-
let replacement = node.left;
|
|
222
|
-
while (replacement.left) {
|
|
223
|
-
replacement = replacement.left;
|
|
224
|
-
}
|
|
225
|
-
const temp = replacement.interval;
|
|
226
|
-
replacement.interval = node.interval;
|
|
227
|
-
node.interval = temp;
|
|
228
|
-
this._removeHelper(replacement.interval, node);
|
|
229
|
-
} else {
|
|
230
|
-
// When only left or right child exists
|
|
231
|
-
let side = 'left';
|
|
232
|
-
if (node.right) {
|
|
233
|
-
side = 'right';
|
|
234
|
-
}
|
|
235
|
-
const parentNode = node.parentNode;
|
|
236
|
-
if (parentNode) {
|
|
237
|
-
if (parentNode.left === node) {
|
|
238
|
-
parentNode.left = node[side];
|
|
239
|
-
} else {
|
|
240
|
-
parentNode.right = node[side];
|
|
241
|
-
}
|
|
242
|
-
if (node[side]) {
|
|
243
|
-
node[side].parentNode = parentNode;
|
|
244
|
-
}
|
|
245
|
-
} else {
|
|
246
|
-
this.root = node[side];
|
|
247
|
-
// last node removed
|
|
248
|
-
if (this.root) {
|
|
249
|
-
this.root.parentNode = null;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
// Adjust the max value
|
|
254
|
-
const p = node.parentNode;
|
|
255
|
-
if (p) {
|
|
256
|
-
let maxNode = this.findMax(p);
|
|
257
|
-
const max = maxNode.interval[1];
|
|
258
|
-
while (maxNode) {
|
|
259
|
-
if (maxNode.max === node.interval[1]) {
|
|
260
|
-
maxNode.max = max;
|
|
261
|
-
maxNode = maxNode.parentNode;
|
|
262
|
-
} else {
|
|
263
|
-
maxNode = false;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
} else {
|
|
268
|
-
// could be optimized
|
|
269
|
-
this._removeHelper(interval, node.left);
|
|
270
|
-
this._removeHelper(interval, node.right);
|
|
271
|
-
}
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Remove interval from the tree.
|
|
276
|
-
*
|
|
277
|
-
* @public
|
|
278
|
-
* @method
|
|
279
|
-
* @param {Array} intreval Array with start and end of the interval.
|
|
280
|
-
*/
|
|
281
|
-
IntervalTree.prototype.remove = function remove(interval) {
|
|
282
|
-
return this._removeHelper(interval, this.root);
|
|
283
|
-
};
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import { chromInfo } from '../services';
|
|
3
2
|
|
|
4
3
|
import objVals from './obj-vals';
|
|
5
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @param {Record<string, { chromInfoPath: string }>} views
|
|
7
|
+
* @returns {void}
|
|
8
|
+
*/
|
|
6
9
|
const loadChromInfos = (views) =>
|
|
7
10
|
objVals(views)
|
|
8
11
|
.map((view) => view.chromInfoPath)
|