@genome-spy/core 0.14.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 +224 -0
- package/dist/style.css +1 -0
- package/package.json +54 -0
- package/src/data/collector.js +178 -0
- package/src/data/collector.test.js +82 -0
- package/src/data/dataFlow.js +109 -0
- package/src/data/dataFlow.test.js +3 -0
- package/src/data/facetNode.js +17 -0
- package/src/data/flow.test.js +71 -0
- package/src/data/flowBatch.d.ts +40 -0
- package/src/data/flowNode.js +283 -0
- package/src/data/flowNode.test.js +49 -0
- package/src/data/flowOptimizer.js +117 -0
- package/src/data/flowOptimizer.test.js +192 -0
- package/src/data/flowTestUtils.js +63 -0
- package/src/data/formats/fasta.js +32 -0
- package/src/data/formats/fasta.test.js +26 -0
- package/src/data/sources/dataSource.js +22 -0
- package/src/data/sources/dataSourceFactory.js +24 -0
- package/src/data/sources/dataUtils.js +31 -0
- package/src/data/sources/dynamicCallbackSource.js +56 -0
- package/src/data/sources/dynamicSource.js +36 -0
- package/src/data/sources/inlineSource.js +69 -0
- package/src/data/sources/inlineSource.test.js +55 -0
- package/src/data/sources/namedSource.js +74 -0
- package/src/data/sources/sequenceSource.js +46 -0
- package/src/data/sources/sequenceSource.test.js +45 -0
- package/src/data/sources/urlSource.js +74 -0
- package/src/data/transforms/aggregate.js +69 -0
- package/src/data/transforms/clone.js +40 -0
- package/src/data/transforms/clone.test.js +10 -0
- package/src/data/transforms/coverage.js +187 -0
- package/src/data/transforms/coverage.test.js +122 -0
- package/src/data/transforms/filter.js +37 -0
- package/src/data/transforms/filter.test.js +17 -0
- package/src/data/transforms/filterScoredLabels.js +134 -0
- package/src/data/transforms/flattenCompressedExons.js +57 -0
- package/src/data/transforms/flattenDelimited.js +68 -0
- package/src/data/transforms/flattenDelimited.test.js +86 -0
- package/src/data/transforms/flattenSequence.js +39 -0
- package/src/data/transforms/flattenSequence.test.js +33 -0
- package/src/data/transforms/formula.js +39 -0
- package/src/data/transforms/formula.test.js +18 -0
- package/src/data/transforms/identifier.js +108 -0
- package/src/data/transforms/identifier.test.js +82 -0
- package/src/data/transforms/linearizeGenomicCoordinate.js +101 -0
- package/src/data/transforms/measureText.js +44 -0
- package/src/data/transforms/pileup.js +128 -0
- package/src/data/transforms/pileup.test.js +69 -0
- package/src/data/transforms/project.js +41 -0
- package/src/data/transforms/project.test.js +31 -0
- package/src/data/transforms/regexExtract.js +61 -0
- package/src/data/transforms/regexExtract.test.js +66 -0
- package/src/data/transforms/regexFold.js +141 -0
- package/src/data/transforms/regexFold.test.js +159 -0
- package/src/data/transforms/sample.js +101 -0
- package/src/data/transforms/sample.test.js +37 -0
- package/src/data/transforms/stack.js +137 -0
- package/src/data/transforms/stack.test.js +90 -0
- package/src/data/transforms/transformFactory.js +60 -0
- package/src/encoder/accessor.js +82 -0
- package/src/encoder/accessor.test.js +46 -0
- package/src/encoder/encoder.js +369 -0
- package/src/encoder/encoder.test.js +97 -0
- package/src/fonts/Lato-Regular.json +1267 -0
- package/src/fonts/Lato-Regular.png +0 -0
- package/src/fonts/OFL.txt +93 -0
- package/src/fonts/README.md +3 -0
- package/src/fonts/bmFont.d.ts +58 -0
- package/src/fonts/bmFontManager.js +357 -0
- package/src/fonts/bmFontMetrics.js +108 -0
- package/src/genome/genome.js +305 -0
- package/src/genome/genome.test.js +152 -0
- package/src/genome/genomeStore.js +54 -0
- package/src/genome/locusFormat.js +31 -0
- package/src/genome/scaleIndex.js +199 -0
- package/src/genome/scaleIndex.test.js +61 -0
- package/src/genome/scaleLocus.js +112 -0
- package/src/genome/scaleLocus.test.js +3 -0
- package/src/genomeSpy.js +753 -0
- package/src/gl/arrayBuilder.js +199 -0
- package/src/gl/dataToVertices.js +621 -0
- package/src/gl/includes/common.glsl +63 -0
- package/src/gl/includes/fp64-arithmetic.glsl +187 -0
- package/src/gl/includes/fp64-utils.js +132 -0
- package/src/gl/includes/picking.fragment.glsl +3 -0
- package/src/gl/includes/picking.vertex.glsl +29 -0
- package/src/gl/includes/sampleFacet.glsl +107 -0
- package/src/gl/includes/scales.glsl +79 -0
- package/src/gl/includes/scales_fp64.glsl +30 -0
- package/src/gl/link.fragment.glsl +18 -0
- package/src/gl/link.vertex.glsl +111 -0
- package/src/gl/point.fragment.glsl +123 -0
- package/src/gl/point.vertex.glsl +128 -0
- package/src/gl/rect.fragment.glsl +51 -0
- package/src/gl/rect.vertex.glsl +114 -0
- package/src/gl/rule.fragment.glsl +52 -0
- package/src/gl/rule.vertex.glsl +89 -0
- package/src/gl/text.fragment.glsl +31 -0
- package/src/gl/text.vertex.glsl +246 -0
- package/src/gl/webGLHelper.js +490 -0
- package/src/img/bowtie.svg +1 -0
- package/src/img/genomespy-favicon.svg +34 -0
- package/src/index.html +11 -0
- package/src/index.js +151 -0
- package/src/marks/link.js +189 -0
- package/src/marks/mark.js +867 -0
- package/src/marks/markUtils.js +109 -0
- package/src/marks/pointMark.js +279 -0
- package/src/marks/rectMark.js +236 -0
- package/src/marks/rule.js +231 -0
- package/src/marks/text.js +274 -0
- package/src/options.d.ts +9 -0
- package/src/scale/colorUtils.js +184 -0
- package/src/scale/glslScaleGenerator.js +462 -0
- package/src/scale/scale.js +441 -0
- package/src/scale/scale.test.js +323 -0
- package/src/scale/ticks.js +198 -0
- package/src/scale/ticks.test.js +39 -0
- package/src/singlePageApp.js +13 -0
- package/src/spec/axis.d.ts +296 -0
- package/src/spec/channel.d.ts +127 -0
- package/src/spec/data.d.ts +185 -0
- package/src/spec/font.d.ts +15 -0
- package/src/spec/genome.d.ts +35 -0
- package/src/spec/mark.d.ts +432 -0
- package/src/spec/root.d.ts +22 -0
- package/src/spec/scale.d.ts +265 -0
- package/src/spec/tooltip.d.ts +9 -0
- package/src/spec/transform.d.ts +479 -0
- package/src/spec/view.d.ts +215 -0
- package/src/styles/genome-spy.scss +153 -0
- package/src/tooltip/dataTooltipHandler.js +59 -0
- package/src/tooltip/refseqGeneTooltipHandler.js +77 -0
- package/src/tooltip/tooltipHandler.ts +12 -0
- package/src/types/filetypes.d.ts +4 -0
- package/src/types/flatqueue.d.ts +53 -0
- package/src/types/glsl.d.ts +4 -0
- package/src/types/object.d.ts +21 -0
- package/src/types/vega-scale.d.ts +60 -0
- package/src/utils/animator.js +83 -0
- package/src/utils/arrayUtils.js +55 -0
- package/src/utils/binnedRangeIndex.js +83 -0
- package/src/utils/clamp.js +8 -0
- package/src/utils/cloner.js +32 -0
- package/src/utils/cloner.test.js +23 -0
- package/src/utils/coalesce.js +11 -0
- package/src/utils/coalesce.test.js +15 -0
- package/src/utils/concatIterables.js +26 -0
- package/src/utils/concatIterables.test.js +7 -0
- package/src/utils/debounce.js +37 -0
- package/src/utils/domainArray.js +224 -0
- package/src/utils/domainArray.test.js +129 -0
- package/src/utils/eerp.js +13 -0
- package/src/utils/expression.js +32 -0
- package/src/utils/field.js +28 -0
- package/src/utils/fisheye.js +60 -0
- package/src/utils/formatObject.js +31 -0
- package/src/utils/html.js +23 -0
- package/src/utils/html.test.js +13 -0
- package/src/utils/indexer.js +43 -0
- package/src/utils/indexer.test.js +46 -0
- package/src/utils/inertia.js +124 -0
- package/src/utils/interactionEvent.js +33 -0
- package/src/utils/iterateNestedMaps.js +21 -0
- package/src/utils/iterateNestedMaps.test.js +32 -0
- package/src/utils/kWayMerge.js +42 -0
- package/src/utils/kWayMerge.test.js +25 -0
- package/src/utils/layout/flexLayout.js +336 -0
- package/src/utils/layout/flexLayout.test.js +296 -0
- package/src/utils/layout/padding.js +107 -0
- package/src/utils/layout/point.js +23 -0
- package/src/utils/layout/rectangle.js +282 -0
- package/src/utils/layout/rectangle.test.js +171 -0
- package/src/utils/mergeObjects.js +99 -0
- package/src/utils/mergeObjects.test.js +41 -0
- package/src/utils/numberExtractor.js +24 -0
- package/src/utils/numberExtractor.test.js +5 -0
- package/src/utils/point.js +14 -0
- package/src/utils/propertyCacher.js +70 -0
- package/src/utils/propertyCacher.test.js +84 -0
- package/src/utils/propertyCoalescer.js +37 -0
- package/src/utils/propertyCoalescer.test.js +21 -0
- package/src/utils/reservationMap.js +103 -0
- package/src/utils/reservationMap.test.js +19 -0
- package/src/utils/scaleNull.js +19 -0
- package/src/utils/setOperations.js +75 -0
- package/src/utils/smoothstep.js +10 -0
- package/src/utils/throttle.js +34 -0
- package/src/utils/topK.js +76 -0
- package/src/utils/topK.test.js +63 -0
- package/src/utils/transition.js +74 -0
- package/src/utils/ui/tooltip.js +189 -0
- package/src/utils/url.js +22 -0
- package/src/utils/variableTools.js +24 -0
- package/src/utils/variableTools.test.js +12 -0
- package/src/view/axisResolution.js +135 -0
- package/src/view/axisResolution.test.js +200 -0
- package/src/view/axisView.js +746 -0
- package/src/view/channel.js +5 -0
- package/src/view/concatView.js +296 -0
- package/src/view/containerView.js +141 -0
- package/src/view/decoratorView.js +510 -0
- package/src/view/facetView.js +488 -0
- package/src/view/flowBuilder.js +362 -0
- package/src/view/flowBuilder.test.js +124 -0
- package/src/view/importView.js +19 -0
- package/src/view/layerView.js +60 -0
- package/src/view/rendering.d.ts +44 -0
- package/src/view/renderingContext/compositeViewRenderingContext.js +51 -0
- package/src/view/renderingContext/deferredViewRenderingContext.js +174 -0
- package/src/view/renderingContext/layoutRecorderViewRenderingContext.js +128 -0
- package/src/view/renderingContext/simpleViewRenderingContext.js +62 -0
- package/src/view/renderingContext/svgViewRenderingContext.js +121 -0
- package/src/view/renderingContext/viewRenderingContext.js +41 -0
- package/src/view/scaleResolution.js +756 -0
- package/src/view/scaleResolution.test.js +571 -0
- package/src/view/scaleResolutionApi.d.ts +40 -0
- package/src/view/testUtils.js +48 -0
- package/src/view/unitView.js +368 -0
- package/src/view/view.js +589 -0
- package/src/view/view.test.js +213 -0
- package/src/view/viewContext.d.ts +57 -0
- package/src/view/viewFactory.js +179 -0
- package/src/view/viewFactory.test.js +16 -0
- package/src/view/viewUtils.js +420 -0
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
import { createAndInitialize } from "./testUtils";
|
|
2
|
+
import createDomain, { toRegularArray as r } from "../utils/domainArray";
|
|
3
|
+
import LayerView from "./layerView";
|
|
4
|
+
import UnitView from "./unitView";
|
|
5
|
+
import { primaryPositionalChannels } from "../encoder/encoder";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import("../spec/channel").Channel} Channel
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// NOTE: The most of these tests don't actually test scaleResolution but the resolution algorithm.
|
|
12
|
+
|
|
13
|
+
describe("Scale resolution", () => {
|
|
14
|
+
test("Channels with just values (no fields or scales) do not resolve", async () => {
|
|
15
|
+
/** @type {import("../spec/view").LayerSpec} */
|
|
16
|
+
const spec = {
|
|
17
|
+
data: { values: [] },
|
|
18
|
+
|
|
19
|
+
resolve: {
|
|
20
|
+
scale: { x: "shared" },
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
layer: [
|
|
24
|
+
{
|
|
25
|
+
mark: "point",
|
|
26
|
+
encoding: {
|
|
27
|
+
color: { value: "red" },
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
mark: "point",
|
|
32
|
+
encoding: {
|
|
33
|
+
color: { value: "green" },
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
const view = await createAndInitialize(spec, LayerView);
|
|
39
|
+
expect(view.children[0].getScaleResolution("color")).toBeUndefined();
|
|
40
|
+
expect(view.children[1].getScaleResolution("color")).toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("Deeply shared scales are shared", async () => {
|
|
44
|
+
/** @type {import("../spec/view").LayerSpec} */
|
|
45
|
+
const spec = {
|
|
46
|
+
data: { values: [] },
|
|
47
|
+
encoding: {
|
|
48
|
+
x: { field: "data", type: "quantitative" },
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
resolve: { scale: { x: "shared" } },
|
|
52
|
+
|
|
53
|
+
layer: [
|
|
54
|
+
{
|
|
55
|
+
resolve: { scale: { x: "shared" } },
|
|
56
|
+
layer: [{ mark: "point" }, { mark: "point" }],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
resolve: { scale: { x: "shared" } },
|
|
60
|
+
layer: [{ mark: "point" }, { mark: "point" }],
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const view = await createAndInitialize(spec, LayerView);
|
|
66
|
+
|
|
67
|
+
expect(view.children[0].children[0].getScaleResolution("x")).toBe(
|
|
68
|
+
view.children[1].children[1].getScaleResolution("x")
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("Shared branches under an independent branch works as expected", async () => {
|
|
73
|
+
/** @type {import("../spec/view").LayerSpec} */
|
|
74
|
+
const spec = {
|
|
75
|
+
data: { values: [] },
|
|
76
|
+
encoding: {
|
|
77
|
+
x: { field: "data", type: "quantitative" },
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
resolve: {
|
|
81
|
+
scale: { x: "independent" },
|
|
82
|
+
// TODO: Axis should be set independent implicitly
|
|
83
|
+
axis: { x: "independent" },
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
layer: [
|
|
87
|
+
{
|
|
88
|
+
resolve: { scale: { x: "shared" } },
|
|
89
|
+
layer: [{ mark: "point" }, { mark: "point" }],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
resolve: { scale: { x: "shared" } },
|
|
93
|
+
layer: [{ mark: "point" }, { mark: "point" }],
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const view = await createAndInitialize(spec, LayerView);
|
|
99
|
+
|
|
100
|
+
expect(view.children[0].children[0].getScaleResolution("x")).not.toBe(
|
|
101
|
+
view.children[1].children[1].getScaleResolution("x")
|
|
102
|
+
);
|
|
103
|
+
expect(view.children[0].children[0].getScaleResolution("x")).toBe(
|
|
104
|
+
view.children[0].children[1].getScaleResolution("x")
|
|
105
|
+
);
|
|
106
|
+
expect(view.children[1].children[0].getScaleResolution("x")).toBe(
|
|
107
|
+
view.children[1].children[1].getScaleResolution("x")
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("Independent branches under a shared branch works as expected", async () => {
|
|
112
|
+
/** @type {import("../spec/view").LayerSpec} */
|
|
113
|
+
const spec = {
|
|
114
|
+
data: { values: [] },
|
|
115
|
+
encoding: {
|
|
116
|
+
x: { field: "data", type: "quantitative" },
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
resolve: {
|
|
120
|
+
scale: { x: "shared" },
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
layer: [
|
|
124
|
+
{
|
|
125
|
+
resolve: {
|
|
126
|
+
scale: { x: "independent" },
|
|
127
|
+
// TODO: Axis should be set independent implicitly
|
|
128
|
+
axis: { x: "independent" },
|
|
129
|
+
},
|
|
130
|
+
layer: [{ mark: "point" }, { mark: "point" }],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
resolve: {
|
|
134
|
+
scale: { x: "independent" },
|
|
135
|
+
// TODO: Axis should be set independent implicitly
|
|
136
|
+
axis: { x: "independent" },
|
|
137
|
+
},
|
|
138
|
+
layer: [{ mark: "point" }, { mark: "point" }],
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const view = await createAndInitialize(spec, LayerView);
|
|
144
|
+
|
|
145
|
+
expect(view.children[0].children[0].getScaleResolution("x")).not.toBe(
|
|
146
|
+
view.children[1].children[0].getScaleResolution("x")
|
|
147
|
+
);
|
|
148
|
+
expect(view.children[0].children[1].getScaleResolution("x")).not.toBe(
|
|
149
|
+
view.children[1].children[1].getScaleResolution("x")
|
|
150
|
+
);
|
|
151
|
+
expect(view.children[0].children[0].getScaleResolution("x")).not.toBe(
|
|
152
|
+
view.children[0].children[1].getScaleResolution("x")
|
|
153
|
+
);
|
|
154
|
+
expect(view.children[1].children[0].getScaleResolution("x")).not.toBe(
|
|
155
|
+
view.children[1].children[1].getScaleResolution("x")
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("Excluded resolution is not pushed towards the root but collects from children.", async () => {
|
|
160
|
+
/** @type {import("../spec/view").LayerSpec} */
|
|
161
|
+
const spec = {
|
|
162
|
+
data: { values: [] },
|
|
163
|
+
encoding: {
|
|
164
|
+
x: { field: "data", type: "quantitative" },
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
resolve: {
|
|
168
|
+
scale: { x: "shared" },
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
layer: [
|
|
172
|
+
{ mark: "point" },
|
|
173
|
+
{ mark: "point" },
|
|
174
|
+
{
|
|
175
|
+
resolve: {
|
|
176
|
+
scale: { x: "excluded" },
|
|
177
|
+
// TODO: Implicit
|
|
178
|
+
axis: { x: "excluded" },
|
|
179
|
+
},
|
|
180
|
+
layer: [{ mark: "point" }, { mark: "point" }],
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const view = await createAndInitialize(spec, LayerView);
|
|
186
|
+
|
|
187
|
+
expect(view.children[0].getScaleResolution("x")).toBe(
|
|
188
|
+
view.children[1].getScaleResolution("x")
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
expect(view.children[2].children[0].getScaleResolution("x")).toBe(
|
|
192
|
+
view.children[2].children[1].getScaleResolution("x")
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
expect(view.children[0].getScaleResolution("x")).not.toBe(
|
|
196
|
+
view.children[2].children[0].getScaleResolution("x")
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("Default resolution is configurable", async () => {
|
|
201
|
+
/** @type {import("../spec/view").LayerSpec} */
|
|
202
|
+
const spec = {
|
|
203
|
+
data: { values: [] },
|
|
204
|
+
encoding: {
|
|
205
|
+
x: { field: "data", type: "quantitative" },
|
|
206
|
+
y: { field: "data", type: "quantitative" },
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
resolve: {
|
|
210
|
+
scale: {
|
|
211
|
+
// The hard default in LayerView is "shared".
|
|
212
|
+
default: "independent",
|
|
213
|
+
x: "shared",
|
|
214
|
+
},
|
|
215
|
+
axis: {
|
|
216
|
+
// TODO: Implicit
|
|
217
|
+
default: "independent",
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
layer: [{ mark: "point" }, { mark: "point" }],
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const view = await createAndInitialize(spec, LayerView);
|
|
225
|
+
|
|
226
|
+
expect(view.children[0].getScaleResolution("x")).toBe(
|
|
227
|
+
view.children[1].getScaleResolution("x")
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
expect(view.children[0].getScaleResolution("y")).not.toBe(
|
|
231
|
+
view.children[1].getScaleResolution("y")
|
|
232
|
+
);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
describe("Domain handling", () => {
|
|
237
|
+
test("Scales are shared and explicit domains merged properly", async () => {
|
|
238
|
+
const view = await createAndInitialize(
|
|
239
|
+
{
|
|
240
|
+
data: { values: [] },
|
|
241
|
+
resolve: { scale: { default: "independent", y: "shared" } },
|
|
242
|
+
layer: [
|
|
243
|
+
{
|
|
244
|
+
mark: "point",
|
|
245
|
+
encoding: {
|
|
246
|
+
y: {
|
|
247
|
+
field: "a",
|
|
248
|
+
type: "quantitative",
|
|
249
|
+
scale: { domain: [1, 2] },
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
mark: "point",
|
|
255
|
+
encoding: {
|
|
256
|
+
y: {
|
|
257
|
+
field: "b",
|
|
258
|
+
type: "quantitative",
|
|
259
|
+
scale: { domain: [4, 5] },
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
},
|
|
265
|
+
LayerView
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
/** @param {import("./view").default} view */
|
|
269
|
+
const d = (view) => view.getScaleResolution("y").getScale().domain();
|
|
270
|
+
|
|
271
|
+
expect(r(d(view))).toEqual([1, 5]);
|
|
272
|
+
expect(r(d(view.children[0]))).toEqual([1, 5]);
|
|
273
|
+
expect(r(d(view.children[1]))).toEqual([1, 5]);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test("Scales are shared and extracted domains merged properly", async () => {
|
|
277
|
+
const view = await createAndInitialize(
|
|
278
|
+
{
|
|
279
|
+
resolve: { scale: { default: "independent", y: "shared" } },
|
|
280
|
+
layer: [
|
|
281
|
+
{
|
|
282
|
+
data: { values: [1, 2] },
|
|
283
|
+
mark: "point",
|
|
284
|
+
encoding: {
|
|
285
|
+
y: {
|
|
286
|
+
field: "data",
|
|
287
|
+
type: "quantitative",
|
|
288
|
+
scale: { zero: false },
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
data: { values: [4, 5] },
|
|
294
|
+
mark: "point",
|
|
295
|
+
encoding: {
|
|
296
|
+
y: { field: "data", type: "quantitative" },
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
},
|
|
301
|
+
LayerView
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
/** @param {import("./view").default} view */
|
|
305
|
+
const d = (view) => view.getScaleResolution("y").getScale().domain();
|
|
306
|
+
|
|
307
|
+
expect(r(d(view))).toEqual([1, 5]);
|
|
308
|
+
expect(r(d(view.children[0]))).toEqual([1, 5]);
|
|
309
|
+
expect(r(d(view.children[1]))).toEqual([1, 5]);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test("Scales of primary and secondary channels are shared and extracted domains merged properly", async () => {
|
|
313
|
+
const view = await createAndInitialize(
|
|
314
|
+
{
|
|
315
|
+
data: {
|
|
316
|
+
values: [
|
|
317
|
+
{ a: 1, b: 4 },
|
|
318
|
+
{ a: 2, b: 5 },
|
|
319
|
+
],
|
|
320
|
+
},
|
|
321
|
+
mark: "point",
|
|
322
|
+
encoding: {
|
|
323
|
+
y: {
|
|
324
|
+
field: "a",
|
|
325
|
+
type: "quantitative",
|
|
326
|
+
scale: { zero: false },
|
|
327
|
+
},
|
|
328
|
+
y2: {
|
|
329
|
+
field: "b",
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
UnitView
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
/** @param {import("./view").default} view */
|
|
337
|
+
const d = (view) => view.getScaleResolution("y").getScale().domain();
|
|
338
|
+
|
|
339
|
+
// FAILS!!!!!!! TODO: FIX!!
|
|
340
|
+
// expect(r(d(view))).toEqual([1, 5]);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test("resolutionChannel property is respected", async () => {
|
|
344
|
+
const view = await createAndInitialize(
|
|
345
|
+
{
|
|
346
|
+
data: { values: [] },
|
|
347
|
+
resolve: { scale: { default: "independent", y: "shared" } },
|
|
348
|
+
layer: [
|
|
349
|
+
{
|
|
350
|
+
mark: "point",
|
|
351
|
+
encoding: {
|
|
352
|
+
y: {
|
|
353
|
+
field: "a",
|
|
354
|
+
type: "quantitative",
|
|
355
|
+
scale: { domain: [1, 2] },
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
mark: "point",
|
|
361
|
+
encoding: {
|
|
362
|
+
x: {
|
|
363
|
+
field: "b",
|
|
364
|
+
type: "quantitative",
|
|
365
|
+
scale: { domain: [4, 5] },
|
|
366
|
+
resolutionChannel: "y",
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
],
|
|
371
|
+
},
|
|
372
|
+
LayerView
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
/** @param {import("./view").default} view */
|
|
376
|
+
const d = (view) => view.getScaleResolution("y").getScale().domain();
|
|
377
|
+
|
|
378
|
+
expect(r(d(view))).toEqual([1, 5]);
|
|
379
|
+
expect(r(d(view.children[0]))).toEqual([1, 5]);
|
|
380
|
+
expect(r(d(view.children[1]))).toEqual([1, 5]);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
test("Channels with quantitative fields include zero in their scale domain by default", async () => {
|
|
384
|
+
const view = await createAndInitialize(
|
|
385
|
+
{
|
|
386
|
+
data: { values: [2, 3] },
|
|
387
|
+
mark: "point",
|
|
388
|
+
encoding: {
|
|
389
|
+
x: { field: "data", type: "quantitative" },
|
|
390
|
+
y: { field: "data", type: "quantitative" },
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
UnitView
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
for (const channel of primaryPositionalChannels) {
|
|
397
|
+
// Extract domain from data
|
|
398
|
+
view.getScaleResolution(channel).reconfigure();
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const d = /** @param {import("../spec/channel").Channel} channel*/ (
|
|
402
|
+
channel
|
|
403
|
+
) => view.getScaleResolution(channel).getScale().domain();
|
|
404
|
+
|
|
405
|
+
expect(d("x")).toEqual([0, 3]);
|
|
406
|
+
expect(d("y")).toEqual([0, 3]);
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
test("Channels with quantitative fields do not include zero in their scale domain if the domain has been defined explicitly", async () => {
|
|
410
|
+
const view = await createAndInitialize(
|
|
411
|
+
{
|
|
412
|
+
data: { values: [2, 3] },
|
|
413
|
+
mark: "point",
|
|
414
|
+
encoding: {
|
|
415
|
+
x: {
|
|
416
|
+
field: "data",
|
|
417
|
+
type: "quantitative",
|
|
418
|
+
scale: { domain: [1, 4] },
|
|
419
|
+
},
|
|
420
|
+
y: {
|
|
421
|
+
field: "data",
|
|
422
|
+
type: "quantitative",
|
|
423
|
+
scale: { domain: [1, 4] },
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
UnitView
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
const d = /** @param {Channel} channel*/ (channel) =>
|
|
431
|
+
view.getScaleResolution(channel).getScale().domain();
|
|
432
|
+
|
|
433
|
+
expect(d("x")).toEqual([1, 4]);
|
|
434
|
+
expect(d("x")).toEqual([1, 4]);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
test("Channels with quantitative fields do not include zero in their scale domain if zero is explicitly false", async () => {
|
|
438
|
+
const view = await createAndInitialize(
|
|
439
|
+
{
|
|
440
|
+
data: { values: [2, 3] },
|
|
441
|
+
mark: "point",
|
|
442
|
+
encoding: {
|
|
443
|
+
x: {
|
|
444
|
+
field: "data",
|
|
445
|
+
type: "quantitative",
|
|
446
|
+
scale: { zero: false },
|
|
447
|
+
},
|
|
448
|
+
y: {
|
|
449
|
+
field: "data",
|
|
450
|
+
type: "quantitative",
|
|
451
|
+
scale: { zero: false },
|
|
452
|
+
},
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
UnitView
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
for (const channel of primaryPositionalChannels) {
|
|
459
|
+
// Extract domain from data
|
|
460
|
+
view.getScaleResolution(channel).reconfigure();
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const d = /** @param {Channel} channel*/ (channel) =>
|
|
464
|
+
view.getScaleResolution(channel).getScale().domain();
|
|
465
|
+
|
|
466
|
+
expect(d("x")).toEqual([2, 3]);
|
|
467
|
+
expect(d("y")).toEqual([2, 3]);
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
describe("Named scales", () => {
|
|
472
|
+
test("Resolution of shared scales with conflicting names fails with an exception", async () => {
|
|
473
|
+
return expect(
|
|
474
|
+
createAndInitialize(
|
|
475
|
+
{
|
|
476
|
+
data: { values: [1, 2] },
|
|
477
|
+
layer: [
|
|
478
|
+
{
|
|
479
|
+
mark: "point",
|
|
480
|
+
encoding: {
|
|
481
|
+
x: {
|
|
482
|
+
field: "data",
|
|
483
|
+
type: "quantitative",
|
|
484
|
+
scale: { name: "scale_1" },
|
|
485
|
+
},
|
|
486
|
+
},
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
mark: "point",
|
|
490
|
+
encoding: {
|
|
491
|
+
x: {
|
|
492
|
+
field: "data",
|
|
493
|
+
type: "quantitative",
|
|
494
|
+
scale: { name: "scale_2" },
|
|
495
|
+
},
|
|
496
|
+
},
|
|
497
|
+
},
|
|
498
|
+
],
|
|
499
|
+
},
|
|
500
|
+
LayerView
|
|
501
|
+
)
|
|
502
|
+
).rejects.toThrow(/conflicting/);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
test("A name is properly registered to the ScaleResolution object", async () => {
|
|
506
|
+
expect(
|
|
507
|
+
await createAndInitialize(
|
|
508
|
+
{
|
|
509
|
+
data: { values: [1, 2] },
|
|
510
|
+
layer: [
|
|
511
|
+
{
|
|
512
|
+
mark: "point",
|
|
513
|
+
encoding: {
|
|
514
|
+
x: {
|
|
515
|
+
field: "data",
|
|
516
|
+
type: "quantitative",
|
|
517
|
+
scale: { name: "scale_1" },
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
mark: "point",
|
|
523
|
+
encoding: {
|
|
524
|
+
x: {
|
|
525
|
+
field: "data",
|
|
526
|
+
type: "quantitative",
|
|
527
|
+
scale: { name: "scale_1" },
|
|
528
|
+
},
|
|
529
|
+
},
|
|
530
|
+
},
|
|
531
|
+
],
|
|
532
|
+
},
|
|
533
|
+
LayerView
|
|
534
|
+
).then((view) => view.getScaleResolution("x"))
|
|
535
|
+
).toHaveProperty("name", "scale_1");
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
test("The scale name must be unique among the scale resolutions", async () => {
|
|
539
|
+
return expect(
|
|
540
|
+
createAndInitialize(
|
|
541
|
+
{
|
|
542
|
+
resolve: { scale: { x: "independent" } },
|
|
543
|
+
data: { values: [1, 2] },
|
|
544
|
+
layer: [
|
|
545
|
+
{
|
|
546
|
+
mark: "point",
|
|
547
|
+
encoding: {
|
|
548
|
+
x: {
|
|
549
|
+
field: "data",
|
|
550
|
+
type: "quantitative",
|
|
551
|
+
scale: { name: "scale_1" },
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
mark: "point",
|
|
557
|
+
encoding: {
|
|
558
|
+
x: {
|
|
559
|
+
field: "data",
|
|
560
|
+
type: "quantitative",
|
|
561
|
+
scale: { name: "scale_1" },
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
},
|
|
565
|
+
],
|
|
566
|
+
},
|
|
567
|
+
LayerView
|
|
568
|
+
)
|
|
569
|
+
).rejects.toThrow(/multiple/);
|
|
570
|
+
});
|
|
571
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ComplexDomain, NumericDomain } from "../spec/scale";
|
|
2
|
+
import ScaleResolution from "./scaleResolution";
|
|
3
|
+
|
|
4
|
+
export interface ScaleResolutionEvent {
|
|
5
|
+
type: "domain";
|
|
6
|
+
|
|
7
|
+
scaleResolution: ScaleResolution;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type ScaleResolutionListener = (event: ScaleResolutionEvent) => void;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A public API for ScaleResolution
|
|
14
|
+
*/
|
|
15
|
+
export interface ScaleResolutionApi {
|
|
16
|
+
addEventListener(type: "domain", listener: ScaleResolutionListener): void;
|
|
17
|
+
|
|
18
|
+
removeEventListener(
|
|
19
|
+
type: "domain",
|
|
20
|
+
listener: ScaleResolutionListener
|
|
21
|
+
): void;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Returns the current, possible zoomed domain.
|
|
25
|
+
*/
|
|
26
|
+
getDomain(): any[];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Returns the current, possible zoomed domain converted into complex objects
|
|
30
|
+
* such as genomic coordinates.
|
|
31
|
+
*/
|
|
32
|
+
getComplexDomain(): NumericDomain | ComplexDomain;
|
|
33
|
+
|
|
34
|
+
isZoomable(): boolean;
|
|
35
|
+
|
|
36
|
+
zoomTo(
|
|
37
|
+
domain: number[] | ComplexDomain,
|
|
38
|
+
duration?: boolean | number
|
|
39
|
+
): Promise<void>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utils for Jest tests
|
|
3
|
+
* TODO: Find a better place and convention
|
|
4
|
+
*
|
|
5
|
+
* @typedef {import("./view").default} View
|
|
6
|
+
* @typedef {import("../spec/view").ViewSpec} ViewSpec
|
|
7
|
+
* @typedef {import("./viewContext").default} ViewContext
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { resolveScalesAndAxes, initializeData } from "./viewUtils";
|
|
11
|
+
import AccessorFactory from "../encoder/accessor";
|
|
12
|
+
import DataFlow from "../data/dataFlow";
|
|
13
|
+
import { ViewFactory } from "./viewFactory";
|
|
14
|
+
|
|
15
|
+
/** @type {<V extends View>(spec: ViewSpec, viewClass: { new(...args: any[]): V }, context?: ViewContext) => V} */
|
|
16
|
+
export function create(spec, viewClass, context = undefined) {
|
|
17
|
+
const viewTypeRegistry = new ViewFactory();
|
|
18
|
+
/** @type {ViewContext} */
|
|
19
|
+
const c = {
|
|
20
|
+
...(context || {}),
|
|
21
|
+
accessorFactory: new AccessorFactory(),
|
|
22
|
+
|
|
23
|
+
createView: function (spec, parent, defaultName) {
|
|
24
|
+
return viewTypeRegistry.createView(spec, c, parent, defaultName);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const view = c.createView(spec, null, "root");
|
|
29
|
+
|
|
30
|
+
if (!(view instanceof viewClass)) {
|
|
31
|
+
throw new Error("ViewClass and the spec do not match!");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return view;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** @type {<V extends View>(spec: ViewSpec, viewClass: { new(...args: any[]): V }, context?: ViewContext) => Promise<V>} */
|
|
38
|
+
export async function createAndInitialize(
|
|
39
|
+
spec,
|
|
40
|
+
viewClass,
|
|
41
|
+
context = undefined
|
|
42
|
+
) {
|
|
43
|
+
context = { ...(context || {}), dataFlow: new DataFlow() };
|
|
44
|
+
const view = create(spec, viewClass, context);
|
|
45
|
+
resolveScalesAndAxes(view);
|
|
46
|
+
await initializeData(view, context.dataFlow);
|
|
47
|
+
return view;
|
|
48
|
+
}
|