@genome-spy/core 0.19.1 → 0.22.0
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.js +42 -42
- package/dist/schema.json +213 -21
- package/package.json +3 -3
- package/src/genomeSpy.js +16 -11
- package/src/gl/dataToVertices.js +43 -46
- package/src/gl/includes/common.glsl +12 -12
- package/src/gl/includes/picking.fragment.glsl +0 -2
- package/src/gl/includes/picking.vertex.glsl +0 -2
- package/src/marks/link.js +32 -39
- package/src/marks/mark.js +168 -95
- package/src/marks/pointMark.js +28 -59
- package/src/marks/rectMark.js +38 -33
- package/src/marks/rule.js +31 -21
- package/src/marks/text.js +18 -14
- package/src/spec/mark.d.ts +0 -3
- package/src/spec/title.d.ts +102 -0
- package/src/spec/view.d.ts +6 -4
- package/src/tooltip/dataTooltipHandler.js +3 -2
- package/src/utils/binnedIndex.js +147 -0
- package/src/utils/binnedIndex.test.js +73 -0
- package/src/utils/layout/flexLayout.js +35 -3
- package/src/utils/layout/flexLayout.test.js +14 -0
- package/src/utils/layout/grid.js +95 -0
- package/src/utils/layout/grid.test.js +71 -0
- package/src/utils/layout/padding.js +13 -0
- package/src/utils/layout/rectangle.js +6 -0
- package/src/view/axisView.js +3 -5
- package/src/view/concatView.js +24 -275
- package/src/view/containerView.js +18 -0
- package/src/view/gridView.js +774 -0
- package/src/view/implicitRootView.js +14 -0
- package/src/view/layerView.js +15 -1
- package/src/view/renderingContext/deferredViewRenderingContext.js +3 -1
- package/src/view/renderingContext/simpleViewRenderingContext.js +3 -1
- package/src/view/title.js +165 -0
- package/src/view/unitView.js +9 -5
- package/src/view/view.js +35 -14
- package/src/view/viewContext.d.ts +6 -1
- package/src/view/viewUtils.js +1 -93
- package/src/view/zoom.js +89 -0
- package/src/utils/binnedRangeIndex.js +0 -83
- package/src/view/decoratorView.js +0 -513
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { expect, test, describe } from "vitest";
|
|
2
|
+
|
|
3
|
+
import Grid from "./grid";
|
|
4
|
+
|
|
5
|
+
describe("Grid indexing", () => {
|
|
6
|
+
test("Single row", () => {
|
|
7
|
+
const g = new Grid(3);
|
|
8
|
+
|
|
9
|
+
expect(g.maxCols).toEqual(Infinity);
|
|
10
|
+
expect(g.nCols).toEqual(3);
|
|
11
|
+
expect(g.nRows).toEqual(1);
|
|
12
|
+
expect(g.colIndices).toEqual([[0], [1], [2]]);
|
|
13
|
+
expect(g.rowIndices).toEqual([[0, 1, 2]]);
|
|
14
|
+
expect(g.getCellIndex(1, 0)).toEqual(1);
|
|
15
|
+
expect(g.getCellIndex(1, 1)).toBeUndefined();
|
|
16
|
+
expect(g.getCellCoords(1)).toEqual([1, 0]);
|
|
17
|
+
expect(g.getCellCoords(-1)).toBeUndefined();
|
|
18
|
+
expect(g.getCellCoords(3)).toBeUndefined();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("Single column", () => {
|
|
22
|
+
const g = new Grid(3, 1);
|
|
23
|
+
|
|
24
|
+
expect(g.maxCols).toEqual(1);
|
|
25
|
+
expect(g.nCols).toEqual(1);
|
|
26
|
+
expect(g.nRows).toEqual(3);
|
|
27
|
+
expect(g.colIndices).toEqual([[0, 1, 2]]);
|
|
28
|
+
expect(g.rowIndices).toEqual([[0], [1], [2]]);
|
|
29
|
+
expect(g.getCellIndex(0, 1)).toEqual(1);
|
|
30
|
+
expect(g.getCellIndex(1, 1)).toBeUndefined();
|
|
31
|
+
expect(g.getCellCoords(1)).toEqual([0, 1]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("Two columns", () => {
|
|
35
|
+
const g = new Grid(6, 2);
|
|
36
|
+
|
|
37
|
+
expect(g.maxCols).toEqual(2);
|
|
38
|
+
expect(g.nCols).toEqual(2);
|
|
39
|
+
expect(g.nRows).toEqual(3);
|
|
40
|
+
expect(g.colIndices).toEqual([
|
|
41
|
+
[0, 2, 4],
|
|
42
|
+
[1, 3, 5],
|
|
43
|
+
]);
|
|
44
|
+
expect(g.rowIndices).toEqual([
|
|
45
|
+
[0, 1],
|
|
46
|
+
[2, 3],
|
|
47
|
+
[4, 5],
|
|
48
|
+
]);
|
|
49
|
+
expect(g.getCellIndex(1, 0)).toEqual(1);
|
|
50
|
+
expect(g.getCellIndex(0, 1)).toEqual(2);
|
|
51
|
+
expect(g.getCellIndex(1, 1)).toEqual(3);
|
|
52
|
+
expect(g.getCellCoords(3)).toEqual([1, 1]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("Two columns, second is partial", () => {
|
|
56
|
+
const g = new Grid(5, 2);
|
|
57
|
+
|
|
58
|
+
expect(g.maxCols).toEqual(2);
|
|
59
|
+
expect(g.nCols).toEqual(2);
|
|
60
|
+
expect(g.nRows).toEqual(3);
|
|
61
|
+
expect(g.colIndices).toEqual([
|
|
62
|
+
[0, 2, 4],
|
|
63
|
+
[1, 3],
|
|
64
|
+
]);
|
|
65
|
+
expect(g.rowIndices).toEqual([[0, 1], [2, 3], [4]]);
|
|
66
|
+
expect(g.getCellIndex(1, 0)).toEqual(1);
|
|
67
|
+
expect(g.getCellIndex(0, 1)).toEqual(2);
|
|
68
|
+
expect(g.getCellIndex(1, 2)).toBeUndefined();
|
|
69
|
+
expect(g.getCellCoords(3)).toEqual([1, 1]);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -61,6 +61,19 @@ export default class Padding {
|
|
|
61
61
|
);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
*
|
|
66
|
+
* @param {Padding} padding padding to subtract
|
|
67
|
+
*/
|
|
68
|
+
subtract(padding) {
|
|
69
|
+
return new Padding(
|
|
70
|
+
this.top - padding.top,
|
|
71
|
+
this.right - padding.right,
|
|
72
|
+
this.bottom - padding.bottom,
|
|
73
|
+
this.left - padding.left
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
64
77
|
/**
|
|
65
78
|
*
|
|
66
79
|
* @param {PaddingConfig} config
|
|
@@ -31,6 +31,8 @@ export default class Rectangle {
|
|
|
31
31
|
);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
static ZERO = Rectangle.create(0, 0, 0, 0);
|
|
35
|
+
|
|
34
36
|
/**
|
|
35
37
|
* @param {Prop} prop
|
|
36
38
|
* @param {number | function():number} value
|
|
@@ -279,4 +281,8 @@ export default class Rectangle {
|
|
|
279
281
|
y: (y - this.y) / this.height,
|
|
280
282
|
};
|
|
281
283
|
}
|
|
284
|
+
|
|
285
|
+
toString() {
|
|
286
|
+
return `Rectangle: x: ${this.x}, y: ${this.y}, width: ${this.width}, height: ${this.height}`;
|
|
287
|
+
}
|
|
282
288
|
}
|
package/src/view/axisView.js
CHANGED
|
@@ -31,7 +31,7 @@ function getPerpendicularChannel(channel) {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/** @type {Record<PositionalChannel, AxisOrient[]>} */
|
|
34
|
-
const CHANNEL_ORIENTS = {
|
|
34
|
+
export const CHANNEL_ORIENTS = {
|
|
35
35
|
x: ["bottom", "top"],
|
|
36
36
|
y: ["left", "right"],
|
|
37
37
|
};
|
|
@@ -120,6 +120,8 @@ export default class AxisView extends LayerView {
|
|
|
120
120
|
this.findChildByName(CHROM_LAYER_NAME).getDynamicDataSource = () =>
|
|
121
121
|
new DynamicCallbackSource(() => genome.chromosomes);
|
|
122
122
|
}
|
|
123
|
+
|
|
124
|
+
this.blockEncodingInheritance = true;
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
getOrient() {
|
|
@@ -427,7 +429,6 @@ function createAxis(axisProps) {
|
|
|
427
429
|
size: ap.labelFontSize,
|
|
428
430
|
color: ap.labelColor,
|
|
429
431
|
minBufferSize: 1500, // to prevent GPU buffer reallocation when zooming
|
|
430
|
-
dynamicData: true,
|
|
431
432
|
},
|
|
432
433
|
encoding: {
|
|
433
434
|
[main]: { field: "value", type: "quantitative" },
|
|
@@ -448,7 +449,6 @@ function createAxis(axisProps) {
|
|
|
448
449
|
color: ap.tickColor,
|
|
449
450
|
size: ap.tickWidth,
|
|
450
451
|
minBufferSize: 300,
|
|
451
|
-
dynamicData: true,
|
|
452
452
|
},
|
|
453
453
|
encoding: {
|
|
454
454
|
[secondary]: { value: anchor },
|
|
@@ -574,7 +574,6 @@ export function createGenomeAxis(axisProps) {
|
|
|
574
574
|
anchor - (ap.chromTickSize / ap.extent) * (anchor ? 1 : -1),
|
|
575
575
|
color: axisProps.chromTickColor,
|
|
576
576
|
size: ap.chromTickWidth,
|
|
577
|
-
dynamicData: true,
|
|
578
577
|
},
|
|
579
578
|
});
|
|
580
579
|
|
|
@@ -647,7 +646,6 @@ export function createGenomeAxis(axisProps) {
|
|
|
647
646
|
align: axisProps.chromLabelAlign,
|
|
648
647
|
baseline: "alphabetic",
|
|
649
648
|
clip: false,
|
|
650
|
-
dynamicData: true,
|
|
651
649
|
...chromLabelMarkProps,
|
|
652
650
|
},
|
|
653
651
|
encoding: {
|
package/src/view/concatView.js
CHANGED
|
@@ -1,22 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
mapToPixelCoords,
|
|
5
|
-
getMinimumSize,
|
|
6
|
-
parseSizeDef,
|
|
7
|
-
FlexDimensions,
|
|
8
|
-
} from "../utils/layout/flexLayout";
|
|
9
|
-
import Rectangle from "../utils/layout/rectangle";
|
|
10
|
-
import Padding from "../utils/layout/padding";
|
|
11
|
-
import { peek } from "../utils/arrayUtils";
|
|
1
|
+
import { isConcatSpec, isVConcatSpec } from "./viewFactory";
|
|
2
|
+
import GridView from "./gridView";
|
|
12
3
|
|
|
13
4
|
/**
|
|
14
5
|
* Creates a vertically or horizontally concatenated layout for children.
|
|
15
|
-
*
|
|
16
|
-
* @typedef {import("./view").default} View
|
|
17
|
-
* @typedef { import("../utils/layout/flexLayout").SizeDef} SizeDef
|
|
18
6
|
*/
|
|
19
|
-
export default class ConcatView extends
|
|
7
|
+
export default class ConcatView extends GridView {
|
|
20
8
|
/**
|
|
21
9
|
*
|
|
22
10
|
* @param {import("./viewUtils").AnyConcatSpec} spec
|
|
@@ -25,272 +13,33 @@ export default class ConcatView extends ContainerView {
|
|
|
25
13
|
* @param {string} name
|
|
26
14
|
*/
|
|
27
15
|
constructor(spec, context, parent, name) {
|
|
28
|
-
super(
|
|
16
|
+
super(
|
|
17
|
+
spec,
|
|
18
|
+
context,
|
|
19
|
+
parent,
|
|
20
|
+
name,
|
|
21
|
+
isConcatSpec(spec)
|
|
22
|
+
? spec.columns
|
|
23
|
+
: isVConcatSpec(spec)
|
|
24
|
+
? 1
|
|
25
|
+
: Infinity
|
|
26
|
+
);
|
|
29
27
|
|
|
30
28
|
this.spec = spec;
|
|
29
|
+
}
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
/** @type {import("../spec/view").GeometricDimension } */
|
|
37
|
-
this.mainDimension = isHConcatSpec(spec) ? "width" : "height";
|
|
38
|
-
/** @type {import("../spec/view").GeometricDimension } */
|
|
39
|
-
this.secondaryDimension =
|
|
40
|
-
this.mainDimension == "width" ? "height" : "width";
|
|
41
|
-
|
|
42
|
-
const childSpecs = isHConcatSpec(spec)
|
|
43
|
-
? spec.hconcat
|
|
31
|
+
_createChildren() {
|
|
32
|
+
const spec = this.spec;
|
|
33
|
+
const childSpecs = isConcatSpec(spec)
|
|
34
|
+
? spec.concat
|
|
44
35
|
: isVConcatSpec(spec)
|
|
45
36
|
? spec.vconcat
|
|
46
|
-
: spec.
|
|
37
|
+
: spec.hconcat;
|
|
47
38
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
39
|
+
this.setChildren(
|
|
40
|
+
childSpecs.map((childSpec, i) =>
|
|
41
|
+
this.context.createView(childSpec, this, "grid" + i)
|
|
42
|
+
)
|
|
51
43
|
);
|
|
52
44
|
}
|
|
53
|
-
|
|
54
|
-
/*
|
|
55
|
-
_getEffectiveChildPaddings() {
|
|
56
|
-
return this.children
|
|
57
|
-
.map((view) => view.getEffectivePadding())
|
|
58
|
-
.map((padding) =>
|
|
59
|
-
this.mainDimension == "height"
|
|
60
|
-
? [padding.left, padding.right]
|
|
61
|
-
: [padding.top, padding.bottom]
|
|
62
|
-
)
|
|
63
|
-
}
|
|
64
|
-
*/
|
|
65
|
-
|
|
66
|
-
getEffectivePadding() {
|
|
67
|
-
return this._cache("size/effectivePadding", () => {
|
|
68
|
-
const visibleChildren = this.children.filter((view) =>
|
|
69
|
-
view.isVisible()
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
if (!visibleChildren.length) {
|
|
73
|
-
return this.getPadding();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Max paddings along the secondary dimension
|
|
77
|
-
|
|
78
|
-
const paddings = visibleChildren
|
|
79
|
-
.map((view) => view.getEffectivePadding())
|
|
80
|
-
.map((padding) =>
|
|
81
|
-
this.mainDimension == "height"
|
|
82
|
-
? [padding.left, padding.right]
|
|
83
|
-
: [padding.top, padding.bottom]
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
const maxPaddings = getMaxEffectivePaddings(paddings);
|
|
87
|
-
|
|
88
|
-
const effectiveChildPadding =
|
|
89
|
-
this.mainDimension == "height"
|
|
90
|
-
? new Padding(
|
|
91
|
-
visibleChildren[0].getEffectivePadding().top,
|
|
92
|
-
maxPaddings[1],
|
|
93
|
-
peek(visibleChildren).getEffectivePadding().bottom,
|
|
94
|
-
maxPaddings[0]
|
|
95
|
-
)
|
|
96
|
-
: new Padding(
|
|
97
|
-
maxPaddings[0],
|
|
98
|
-
visibleChildren[0].getEffectivePadding().left,
|
|
99
|
-
maxPaddings[1],
|
|
100
|
-
peek(visibleChildren).getEffectivePadding().right
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
return this.getPadding().add(effectiveChildPadding);
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
getSize() {
|
|
108
|
-
return this._cache("size", () => {
|
|
109
|
-
/** @type {SizeDef} */
|
|
110
|
-
let mainSizeDef;
|
|
111
|
-
if (this.spec[this.mainDimension]) {
|
|
112
|
-
mainSizeDef = parseSizeDef(this.spec[this.mainDimension]);
|
|
113
|
-
} else {
|
|
114
|
-
const childMainSizeDefs = this.children
|
|
115
|
-
.filter((view) => view.isVisible())
|
|
116
|
-
.map((view) => view.getSize()[this.mainDimension]);
|
|
117
|
-
|
|
118
|
-
mainSizeDef = {
|
|
119
|
-
// Grows are summed to support sensible nesting of concatViews
|
|
120
|
-
grow: childMainSizeDefs
|
|
121
|
-
.map((sizeDef) => +sizeDef.grow)
|
|
122
|
-
.reduce((a, b) => a + b, 0),
|
|
123
|
-
px: getMinimumSize(childMainSizeDefs, {
|
|
124
|
-
spacing: this.spec.spacing,
|
|
125
|
-
}),
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const secondarySizeDef = (this.spec[this.secondaryDimension] &&
|
|
130
|
-
parseSizeDef(this.spec[this.secondaryDimension])) || {
|
|
131
|
-
grow: 1,
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
return (
|
|
135
|
-
this.mainDimension == "height"
|
|
136
|
-
? new FlexDimensions(secondarySizeDef, mainSizeDef)
|
|
137
|
-
: new FlexDimensions(mainSizeDef, secondarySizeDef)
|
|
138
|
-
).addPadding(this.getPadding());
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* @param {import("./renderingContext/viewRenderingContext").default} context
|
|
144
|
-
* @param {import("../utils/layout/rectangle").default} coords
|
|
145
|
-
* @param {import("./view").RenderingOptions} [options]
|
|
146
|
-
*/
|
|
147
|
-
render(context, coords, options = {}) {
|
|
148
|
-
if (!this.isVisible()) {
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
coords = coords.shrink(this.getPadding());
|
|
153
|
-
context.pushView(this, coords);
|
|
154
|
-
|
|
155
|
-
// This method produces piles of garbage when used with sample faceting.
|
|
156
|
-
// TODO: Figure out something. Perhaps the rectangles could be cached because
|
|
157
|
-
// they are identical for each sample facet.
|
|
158
|
-
|
|
159
|
-
const visibleChildren = this.children.filter((view) =>
|
|
160
|
-
view.isVisible()
|
|
161
|
-
);
|
|
162
|
-
const childSizeDefs = visibleChildren.map(
|
|
163
|
-
(view) => view.getSize()[this.mainDimension]
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
const mappedCoords = mapToPixelCoords(
|
|
167
|
-
childSizeDefs,
|
|
168
|
-
coords[this.mainDimension],
|
|
169
|
-
{
|
|
170
|
-
spacing: this.spec.spacing,
|
|
171
|
-
devicePixelRatio: this.context.glHelper.dpr,
|
|
172
|
-
}
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
// Align the views.
|
|
176
|
-
const paddings = visibleChildren
|
|
177
|
-
.map((view) => view.getEffectivePadding())
|
|
178
|
-
.map((padding) =>
|
|
179
|
-
this.mainDimension == "height"
|
|
180
|
-
? [padding.left, padding.right]
|
|
181
|
-
: [padding.top, padding.bottom]
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
const maxPaddings = getMaxEffectivePaddings(paddings);
|
|
185
|
-
|
|
186
|
-
for (let i = 0; i < visibleChildren.length; i++) {
|
|
187
|
-
const view = visibleChildren[i];
|
|
188
|
-
const flexCoords = mappedCoords[i];
|
|
189
|
-
|
|
190
|
-
const pa = maxPaddings[0] - paddings[i][0];
|
|
191
|
-
const pb = maxPaddings[1] - paddings[i][1];
|
|
192
|
-
|
|
193
|
-
const secondarySize = coords[this.secondaryDimension] - pa - pb;
|
|
194
|
-
|
|
195
|
-
const childCoords =
|
|
196
|
-
this.mainDimension == "height"
|
|
197
|
-
? new Rectangle(
|
|
198
|
-
() => coords.x + pa,
|
|
199
|
-
() => coords.y + flexCoords.location,
|
|
200
|
-
() => secondarySize,
|
|
201
|
-
() => flexCoords.size
|
|
202
|
-
)
|
|
203
|
-
: new Rectangle(
|
|
204
|
-
() => coords.x + flexCoords.location,
|
|
205
|
-
() => coords.y + pa,
|
|
206
|
-
() => flexCoords.size,
|
|
207
|
-
() => secondarySize
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
view.render(context, childCoords, options);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
context.popView(this);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* @returns {IterableIterator<View>}
|
|
218
|
-
*/
|
|
219
|
-
*[Symbol.iterator]() {
|
|
220
|
-
for (const child of this.children) {
|
|
221
|
-
yield child;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* @param {import("./view").default} child
|
|
227
|
-
* @param {import("./view").default} replacement
|
|
228
|
-
*/
|
|
229
|
-
replaceChild(child, replacement) {
|
|
230
|
-
const i = this.children.indexOf(child);
|
|
231
|
-
if (i >= 0) {
|
|
232
|
-
this.children[i] = replacement;
|
|
233
|
-
} else {
|
|
234
|
-
throw new Error("Not my child view!");
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Adds a child using a spec. Does NOT perform any initializations.
|
|
240
|
-
* Returns the newly added view instance.
|
|
241
|
-
*
|
|
242
|
-
* @param {import("./viewUtils").ViewSpec} viewSpec
|
|
243
|
-
*/
|
|
244
|
-
addChildBySpec(viewSpec) {
|
|
245
|
-
// TODO: Move to containerView
|
|
246
|
-
|
|
247
|
-
// TODO: More robust solution. Will break in future when views can be removed
|
|
248
|
-
const i = this.children.length;
|
|
249
|
-
|
|
250
|
-
const view = this.context.createView(viewSpec, this, "concat" + i);
|
|
251
|
-
this.children.push(view);
|
|
252
|
-
|
|
253
|
-
return view;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Adds a child. Does NOT perform any initializations.
|
|
258
|
-
* Returns the newly added view instance.
|
|
259
|
-
*
|
|
260
|
-
* @param {View} view
|
|
261
|
-
*/
|
|
262
|
-
addChild(view) {
|
|
263
|
-
// TODO: Move to containerView
|
|
264
|
-
|
|
265
|
-
// TODO: More robust solution. Will break in future when views can be removed
|
|
266
|
-
const i = this.children.length;
|
|
267
|
-
|
|
268
|
-
if (!view.name) {
|
|
269
|
-
view.name = "concat" + i;
|
|
270
|
-
}
|
|
271
|
-
view.parent = this;
|
|
272
|
-
this.children.push(view);
|
|
273
|
-
|
|
274
|
-
return view;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* @param {string} channel
|
|
279
|
-
* @param {import("./containerView").ResolutionTarget} resolutionType
|
|
280
|
-
* @returns {import("../spec/view").ResolutionBehavior}
|
|
281
|
-
*/
|
|
282
|
-
getDefaultResolution(channel, resolutionType) {
|
|
283
|
-
// TODO: Default to shared when working with genomic coordinates
|
|
284
|
-
return "independent";
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
*
|
|
290
|
-
* @param {number[][]} paddings
|
|
291
|
-
*/
|
|
292
|
-
function getMaxEffectivePaddings(paddings) {
|
|
293
|
-
return [0, 1].map((i) =>
|
|
294
|
-
paddings.map((p) => p[i]).reduce((a, c) => Math.max(a, c), 0)
|
|
295
|
-
);
|
|
296
45
|
}
|
|
@@ -108,6 +108,24 @@ export default class ContainerView extends View {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
*
|
|
113
|
+
* @param {string} name
|
|
114
|
+
*/
|
|
115
|
+
findDescendantByName(name) {
|
|
116
|
+
/** @type {View} */
|
|
117
|
+
let view;
|
|
118
|
+
|
|
119
|
+
this.visit((v) => {
|
|
120
|
+
if (v.name == name) {
|
|
121
|
+
view = v;
|
|
122
|
+
return VISIT_STOP;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return view;
|
|
127
|
+
}
|
|
128
|
+
|
|
111
129
|
/**
|
|
112
130
|
* @param {import("../spec/channel").Channel | "default"} channel
|
|
113
131
|
* @param {ResolutionTarget} resolutionType
|