@genome-spy/core 0.29.0 → 0.30.2
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.es.js +16373 -0
- package/dist/index.js +43 -43
- package/package.json +10 -7
- package/src/data/collector.js +0 -183
- package/src/data/collector.test.js +0 -84
- package/src/data/dataFlow.js +0 -148
- package/src/data/dataFlow.test.js +0 -5
- package/src/data/facetNode.js +0 -17
- package/src/data/flow.test.js +0 -72
- package/src/data/flowBatch.d.ts +0 -40
- package/src/data/flowNode.js +0 -283
- package/src/data/flowNode.test.js +0 -50
- package/src/data/flowOptimizer.js +0 -123
- package/src/data/flowOptimizer.test.js +0 -193
- package/src/data/flowTestUtils.js +0 -63
- package/src/data/formats/fasta.js +0 -32
- package/src/data/formats/fasta.test.js +0 -27
- package/src/data/sources/dataSource.js +0 -22
- package/src/data/sources/dataSourceFactory.js +0 -24
- package/src/data/sources/dataUtils.js +0 -78
- package/src/data/sources/dynamicCallbackSource.js +0 -57
- package/src/data/sources/dynamicSource.js +0 -37
- package/src/data/sources/inlineSource.js +0 -67
- package/src/data/sources/inlineSource.test.js +0 -56
- package/src/data/sources/namedSource.js +0 -79
- package/src/data/sources/sequenceSource.js +0 -46
- package/src/data/sources/sequenceSource.test.js +0 -46
- package/src/data/sources/urlSource.js +0 -74
- package/src/data/transforms/aggregate.js +0 -70
- package/src/data/transforms/clone.js +0 -40
- package/src/data/transforms/clone.test.js +0 -11
- package/src/data/transforms/coverage.js +0 -187
- package/src/data/transforms/coverage.test.js +0 -123
- package/src/data/transforms/filter.js +0 -37
- package/src/data/transforms/filter.test.js +0 -18
- package/src/data/transforms/filterScoredLabels.js +0 -134
- package/src/data/transforms/flattenCompressedExons.js +0 -57
- package/src/data/transforms/flattenDelimited.js +0 -74
- package/src/data/transforms/flattenDelimited.test.js +0 -87
- package/src/data/transforms/flattenSequence.js +0 -39
- package/src/data/transforms/flattenSequence.test.js +0 -34
- package/src/data/transforms/formula.js +0 -39
- package/src/data/transforms/formula.test.js +0 -19
- package/src/data/transforms/identifier.js +0 -108
- package/src/data/transforms/identifier.test.js +0 -83
- package/src/data/transforms/linearizeGenomicCoordinate.js +0 -101
- package/src/data/transforms/measureText.js +0 -44
- package/src/data/transforms/pileup.js +0 -128
- package/src/data/transforms/pileup.test.js +0 -70
- package/src/data/transforms/project.js +0 -41
- package/src/data/transforms/project.test.js +0 -32
- package/src/data/transforms/regexExtract.js +0 -61
- package/src/data/transforms/regexExtract.test.js +0 -67
- package/src/data/transforms/regexFold.js +0 -141
- package/src/data/transforms/regexFold.test.js +0 -160
- package/src/data/transforms/sample.js +0 -101
- package/src/data/transforms/sample.test.js +0 -38
- package/src/data/transforms/stack.js +0 -137
- package/src/data/transforms/stack.test.js +0 -91
- package/src/data/transforms/transformFactory.js +0 -60
- package/src/embedApi.d.ts +0 -67
- package/src/encoder/accessor.js +0 -82
- package/src/encoder/accessor.test.js +0 -47
- package/src/encoder/encoder.js +0 -394
- package/src/encoder/encoder.test.js +0 -98
- package/src/fonts/Lato-Regular.json +0 -1267
- package/src/fonts/Lato-Regular.png +0 -0
- package/src/fonts/OFL.txt +0 -93
- package/src/fonts/README.md +0 -3
- package/src/fonts/bmFont.d.ts +0 -58
- package/src/fonts/bmFontManager.js +0 -357
- package/src/fonts/bmFontMetrics.js +0 -108
- package/src/genome/genome.js +0 -317
- package/src/genome/genome.test.js +0 -188
- package/src/genome/genomeStore.js +0 -54
- package/src/genome/locusFormat.js +0 -31
- package/src/genome/scaleIndex.d.ts +0 -38
- package/src/genome/scaleIndex.js +0 -166
- package/src/genome/scaleIndex.test.js +0 -78
- package/src/genome/scaleLocus.d.ts +0 -11
- package/src/genome/scaleLocus.js +0 -108
- package/src/genome/scaleLocus.test.js +0 -4
- package/src/genomeSpy.js +0 -784
- package/src/gl/arrayBuilder.js +0 -199
- package/src/gl/dataToVertices.js +0 -636
- package/src/gl/includes/common.glsl +0 -63
- package/src/gl/includes/picking.fragment.glsl +0 -1
- package/src/gl/includes/picking.vertex.glsl +0 -27
- package/src/gl/includes/sampleFacet.glsl +0 -107
- package/src/gl/includes/scales.glsl +0 -112
- package/src/gl/link.fragment.glsl +0 -18
- package/src/gl/link.vertex.glsl +0 -111
- package/src/gl/point.fragment.glsl +0 -123
- package/src/gl/point.vertex.glsl +0 -129
- package/src/gl/rect.fragment.glsl +0 -51
- package/src/gl/rect.vertex.glsl +0 -114
- package/src/gl/rule.fragment.glsl +0 -52
- package/src/gl/rule.vertex.glsl +0 -89
- package/src/gl/text.fragment.glsl +0 -31
- package/src/gl/text.vertex.glsl +0 -246
- package/src/gl/webGLHelper.js +0 -489
- package/src/img/bowtie.svg +0 -1
- package/src/img/genomespy-favicon.svg +0 -34
- package/src/index.html +0 -11
- package/src/index.js +0 -128
- package/src/marks/link.js +0 -175
- package/src/marks/mark.js +0 -975
- package/src/marks/markUtils.js +0 -125
- package/src/marks/pointMark.js +0 -251
- package/src/marks/rectMark.js +0 -241
- package/src/marks/rule.js +0 -250
- package/src/marks/text.js +0 -278
- package/src/node_modules/.vitest/results.json +0 -1
- package/src/scale/colorUtils.js +0 -184
- package/src/scale/glslScaleGenerator.js +0 -488
- package/src/scale/scale.js +0 -451
- package/src/scale/scale.test.js +0 -324
- package/src/scale/ticks.js +0 -203
- package/src/scale/ticks.test.js +0 -40
- package/src/singlePageApp.js +0 -13
- package/src/spec/axis.d.ts +0 -296
- package/src/spec/channel.d.ts +0 -430
- package/src/spec/data.d.ts +0 -196
- package/src/spec/font.d.ts +0 -15
- package/src/spec/genome.d.ts +0 -35
- package/src/spec/mark.d.ts +0 -429
- package/src/spec/root.d.ts +0 -17
- package/src/spec/sampleView.d.ts +0 -180
- package/src/spec/scale.d.ts +0 -273
- package/src/spec/title.d.ts +0 -102
- package/src/spec/tooltip.d.ts +0 -9
- package/src/spec/transform.d.ts +0 -479
- package/src/spec/view.d.ts +0 -201
- package/src/styles/genome-spy.scss +0 -153
- package/src/tooltip/dataTooltipHandler.js +0 -64
- package/src/tooltip/refseqGeneTooltipHandler.js +0 -78
- package/src/tooltip/tooltipHandler.ts +0 -12
- package/src/types/filetypes.d.ts +0 -14
- package/src/types/flatqueue.d.ts +0 -53
- package/src/types/glsl.d.ts +0 -4
- package/src/types/internmap.d.ts +0 -22
- package/src/types/object.d.ts +0 -21
- package/src/types/vega-loader.d.ts +0 -1
- package/src/types/vega-scale.d.ts +0 -60
- package/src/utils/addBaseUrl.js +0 -19
- package/src/utils/addBaseUrl.test.js +0 -22
- package/src/utils/animator.js +0 -83
- package/src/utils/arrayUtils.js +0 -61
- package/src/utils/binnedIndex.js +0 -167
- package/src/utils/binnedIndex.test.js +0 -155
- package/src/utils/clamp.js +0 -8
- package/src/utils/cloner.js +0 -34
- package/src/utils/cloner.test.js +0 -24
- package/src/utils/coalesce.js +0 -11
- package/src/utils/coalesce.test.js +0 -16
- package/src/utils/concatIterables.js +0 -26
- package/src/utils/concatIterables.test.js +0 -8
- package/src/utils/debounce.js +0 -37
- package/src/utils/domainArray.js +0 -216
- package/src/utils/domainArray.test.js +0 -130
- package/src/utils/eerp.js +0 -13
- package/src/utils/expression.js +0 -32
- package/src/utils/field.js +0 -28
- package/src/utils/formatObject.js +0 -31
- package/src/utils/indexer.js +0 -43
- package/src/utils/indexer.test.js +0 -47
- package/src/utils/inertia.js +0 -124
- package/src/utils/interactionEvent.js +0 -33
- package/src/utils/iterateNestedMaps.js +0 -21
- package/src/utils/iterateNestedMaps.test.js +0 -33
- package/src/utils/kWayMerge.js +0 -42
- package/src/utils/kWayMerge.test.js +0 -26
- package/src/utils/layout/flexLayout.js +0 -368
- package/src/utils/layout/flexLayout.test.js +0 -311
- package/src/utils/layout/grid.js +0 -95
- package/src/utils/layout/grid.test.js +0 -71
- package/src/utils/layout/padding.js +0 -120
- package/src/utils/layout/point.js +0 -23
- package/src/utils/layout/rectangle.js +0 -288
- package/src/utils/layout/rectangle.test.js +0 -172
- package/src/utils/mergeObjects.js +0 -99
- package/src/utils/mergeObjects.test.js +0 -42
- package/src/utils/numberExtractor.js +0 -24
- package/src/utils/numberExtractor.test.js +0 -6
- package/src/utils/point.js +0 -14
- package/src/utils/propertyCacher.js +0 -70
- package/src/utils/propertyCacher.test.js +0 -85
- package/src/utils/propertyCoalescer.js +0 -42
- package/src/utils/propertyCoalescer.test.js +0 -22
- package/src/utils/reservationMap.js +0 -103
- package/src/utils/reservationMap.test.js +0 -20
- package/src/utils/scaleNull.js +0 -19
- package/src/utils/setOperations.js +0 -75
- package/src/utils/smoothstep.js +0 -10
- package/src/utils/throttle.js +0 -34
- package/src/utils/topK.js +0 -76
- package/src/utils/topK.test.js +0 -64
- package/src/utils/transition.js +0 -74
- package/src/utils/ui/tooltip.js +0 -189
- package/src/utils/url.js +0 -22
- package/src/utils/variableTools.js +0 -24
- package/src/utils/variableTools.test.js +0 -13
- package/src/view/axisResolution.js +0 -140
- package/src/view/axisResolution.test.js +0 -201
- package/src/view/axisView.js +0 -747
- package/src/view/concatView.js +0 -45
- package/src/view/containerView.js +0 -159
- package/src/view/facetView.js +0 -491
- package/src/view/flowBuilder.js +0 -367
- package/src/view/flowBuilder.test.js +0 -125
- package/src/view/gridView.js +0 -786
- package/src/view/implicitRootView.js +0 -14
- package/src/view/importView.js +0 -19
- package/src/view/layerView.js +0 -74
- package/src/view/rendering.d.ts +0 -44
- package/src/view/renderingContext/compositeViewRenderingContext.js +0 -51
- package/src/view/renderingContext/deferredViewRenderingContext.js +0 -176
- package/src/view/renderingContext/layoutRecorderViewRenderingContext.js +0 -128
- package/src/view/renderingContext/simpleViewRenderingContext.js +0 -64
- package/src/view/renderingContext/svgViewRenderingContext.js +0 -125
- package/src/view/renderingContext/viewRenderingContext.js +0 -41
- package/src/view/scaleResolution.js +0 -791
- package/src/view/scaleResolution.test.js +0 -572
- package/src/view/scaleResolutionApi.d.ts +0 -40
- package/src/view/testUtils.js +0 -51
- package/src/view/title.js +0 -165
- package/src/view/unitView.js +0 -382
- package/src/view/view.js +0 -612
- package/src/view/view.test.js +0 -214
- package/src/view/viewContext.d.ts +0 -62
- package/src/view/viewFactory.js +0 -181
- package/src/view/viewFactory.test.js +0 -17
- package/src/view/viewUtils.js +0 -327
- package/src/view/zoom.js +0 -89
package/src/encoder/encoder.js
DELETED
|
@@ -1,394 +0,0 @@
|
|
|
1
|
-
import { isDiscrete } from "vega-scale";
|
|
2
|
-
import createIndexer from "../utils/indexer";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @typedef {Object} EncoderMetadata
|
|
6
|
-
* @prop {boolean} constant True if the accessor returns the same value for all objects
|
|
7
|
-
* @prop {boolean} constantValue True the encoder returns a "value" without a scale
|
|
8
|
-
* @prop {(value: import("../spec/channel").Scalar) => import("../spec/channel").Scalar} invert
|
|
9
|
-
* @prop {VegaScale} [scale]
|
|
10
|
-
* @prop {import("./accessor").Accessor} accessor
|
|
11
|
-
* @prop {function(any):number} [indexer] Converts ordinal values to index numbers
|
|
12
|
-
* @prop {import("../view/viewUtils").ChannelDef} channelDef
|
|
13
|
-
* @prop {function(function):void} applyMetadata Copies metadata to the target function
|
|
14
|
-
*
|
|
15
|
-
* @typedef {(function(object):(string|number)) & EncoderMetadata} Encoder
|
|
16
|
-
* @typedef {(function(object):number) & EncoderMetadata} NumberEncoder
|
|
17
|
-
*
|
|
18
|
-
* @typedef {object} ScaleMetadata
|
|
19
|
-
* @prop {string} type Scale type
|
|
20
|
-
* @prop {boolean} [fp64] Whether to use emulated 64 bit floating point in WebGL
|
|
21
|
-
*
|
|
22
|
-
* @typedef {(
|
|
23
|
-
import("d3-scale").ScaleContinuousNumeric<any, any> |
|
|
24
|
-
import("d3-scale").ScaleLinear<any, any> |
|
|
25
|
-
import("d3-scale").ScalePower<any, any> |
|
|
26
|
-
import("d3-scale").ScaleLogarithmic<any, any> |
|
|
27
|
-
import("d3-scale").ScaleSymLog<any, any> |
|
|
28
|
-
import("d3-scale").ScaleIdentity |
|
|
29
|
-
import("d3-scale").ScaleTime<any, any> |
|
|
30
|
-
import("d3-scale").ScaleSequential<any> |
|
|
31
|
-
import("d3-scale").ScaleDiverging<any> |
|
|
32
|
-
import("d3-scale").ScaleQuantize<any> |
|
|
33
|
-
import("d3-scale").ScaleQuantile<any> |
|
|
34
|
-
import("d3-scale").ScaleThreshold<any, any> |
|
|
35
|
-
import("d3-scale").ScaleOrdinal<any, any> |
|
|
36
|
-
import("d3-scale").ScaleBand<any> |
|
|
37
|
-
import("d3-scale").ScalePoint<any>
|
|
38
|
-
)} D3Scale
|
|
39
|
-
*
|
|
40
|
-
* @typedef {(D3Scale | import("../genome/scaleIndex").ScaleIndex | import("../genome/scaleLocus").ScaleLocus) & ScaleMetadata} VegaScale
|
|
41
|
-
*
|
|
42
|
-
* @typedef {import("../spec/channel").Channel} Channel
|
|
43
|
-
*/
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Creates an object that contains encoders for every channel of a mark
|
|
47
|
-
*
|
|
48
|
-
* TODO: This should actually receive the mark as parameter
|
|
49
|
-
*
|
|
50
|
-
* TODO: This method should have a test. But how to mock Mark...
|
|
51
|
-
*
|
|
52
|
-
* @param {import("../marks/mark").default} mark
|
|
53
|
-
* @param {import("../spec/channel").Encoding} [encoding] Taken from the mark if not provided
|
|
54
|
-
* @returns {Partial<Record<Channel, Encoder>>}
|
|
55
|
-
*/
|
|
56
|
-
export default function createEncoders(mark, encoding) {
|
|
57
|
-
/** @type {Partial<Record<Channel, Encoder>>} */
|
|
58
|
-
const encoders = {};
|
|
59
|
-
|
|
60
|
-
if (!encoding) {
|
|
61
|
-
encoding = mark.encoding;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
for (const [channel, channelDef] of Object.entries(encoding)) {
|
|
65
|
-
if (!channelDef) {
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const resolution = mark.unitView.getScaleResolution(
|
|
70
|
-
(isChannelDefWithScale(channelDef) &&
|
|
71
|
-
channelDef.resolutionChannel) ||
|
|
72
|
-
channel
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
encoders[channel] = createEncoder(
|
|
76
|
-
encoding[channel],
|
|
77
|
-
resolution?.getScale(),
|
|
78
|
-
mark.unitView.getAccessor(channel),
|
|
79
|
-
channel
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return encoders;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
*
|
|
88
|
-
* @param {import("../spec/channel").ChannelDef} channelDef
|
|
89
|
-
* @param {any} scale
|
|
90
|
-
* @param {import("./accessor").Accessor} accessor
|
|
91
|
-
* @param {Channel} channel
|
|
92
|
-
* @returns {Encoder}
|
|
93
|
-
*/
|
|
94
|
-
export function createEncoder(channelDef, scale, accessor, channel) {
|
|
95
|
-
/** @type {Encoder} */
|
|
96
|
-
let encoder;
|
|
97
|
-
|
|
98
|
-
if (isValueDef(channelDef)) {
|
|
99
|
-
const value = channelDef.value;
|
|
100
|
-
encoder = /** @type {Encoder} */ ((datum) => value);
|
|
101
|
-
encoder.constant = true;
|
|
102
|
-
encoder.constantValue = true;
|
|
103
|
-
encoder.accessor = undefined;
|
|
104
|
-
} else if (accessor) {
|
|
105
|
-
if (channel == "text") {
|
|
106
|
-
// TODO: Define somewhere channels that don't use a scale
|
|
107
|
-
encoder = /** @type {Encoder} */ ((datum) => undefined);
|
|
108
|
-
encoder.accessor = accessor;
|
|
109
|
-
encoder.constant = accessor.constant;
|
|
110
|
-
} else {
|
|
111
|
-
if (!scale) {
|
|
112
|
-
throw new Error(
|
|
113
|
-
`Missing scale! "${channel}": ${JSON.stringify(channelDef)}`
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
encoder = /** @type {Encoder} */ (
|
|
118
|
-
(datum) => scale(accessor(datum))
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
if (isDiscrete(scale.type)) {
|
|
122
|
-
// TODO: pass the found values back to the scale/resolution
|
|
123
|
-
const indexer = createIndexer();
|
|
124
|
-
indexer.addAll(scale.domain());
|
|
125
|
-
encoder.indexer = indexer;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
encoder.constant = accessor.constant;
|
|
129
|
-
encoder.accessor = accessor;
|
|
130
|
-
encoder.scale = scale;
|
|
131
|
-
}
|
|
132
|
-
} else {
|
|
133
|
-
throw new Error(
|
|
134
|
-
`Missing value or accessor (field, expr, datum) on channel "${channel}": ${JSON.stringify(
|
|
135
|
-
channelDef
|
|
136
|
-
)}`
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// TODO: Modifier should be inverted too
|
|
141
|
-
encoder.invert = scale
|
|
142
|
-
? (value) => scale.invert(value)
|
|
143
|
-
: (value) => {
|
|
144
|
-
throw new Error(
|
|
145
|
-
"No scale available, cannot invert: " +
|
|
146
|
-
JSON.stringify(channelDef)
|
|
147
|
-
);
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
// Just to provide a convenient access to the config
|
|
151
|
-
encoder.channelDef = channelDef;
|
|
152
|
-
|
|
153
|
-
/** @param {Encoder} target */
|
|
154
|
-
encoder.applyMetadata = (target) => {
|
|
155
|
-
for (const prop in encoder) {
|
|
156
|
-
if (prop in encoder) {
|
|
157
|
-
// @ts-ignore
|
|
158
|
-
target[prop] = encoder[prop];
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
return target;
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
return encoder;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* TODO: Move to a more generic place
|
|
169
|
-
*
|
|
170
|
-
* @param {import("../spec/channel").ChannelDef} channelDef
|
|
171
|
-
* @returns {channelDef is import("../spec/channel").ValueDef}
|
|
172
|
-
*/
|
|
173
|
-
export function isValueDef(channelDef) {
|
|
174
|
-
return channelDef && "value" in channelDef;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* @param {import("../spec/channel").ChannelDef} channelDef
|
|
179
|
-
* @returns {channelDef is import("../spec/channel").FieldDefBase<string>}
|
|
180
|
-
*/
|
|
181
|
-
export function isFieldDef(channelDef) {
|
|
182
|
-
return channelDef && "field" in channelDef;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* @param {import("../spec/channel").ChannelDef} channelDef
|
|
187
|
-
* @returns {channelDef is import("../spec/channel").DatumDef}
|
|
188
|
-
*/
|
|
189
|
-
export function isDatumDef(channelDef) {
|
|
190
|
-
return channelDef && "datum" in channelDef;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* @param {import("../spec/channel").ChannelDef} channelDef
|
|
195
|
-
* @returns {channelDef is import("../spec/channel").ChannelDefWithScale}
|
|
196
|
-
*/
|
|
197
|
-
export function isChannelDefWithScale(channelDef) {
|
|
198
|
-
// TODO: Not accurate, fix
|
|
199
|
-
return (
|
|
200
|
-
isFieldDef(channelDef) ||
|
|
201
|
-
isDatumDef(channelDef) ||
|
|
202
|
-
isExprDef(channelDef) ||
|
|
203
|
-
isChromPosDef(channelDef)
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* @param {import("../view/unitView").default} view
|
|
209
|
-
* @param {Channel} channel
|
|
210
|
-
*/
|
|
211
|
-
export function getChannelDefWithScale(view, channel) {
|
|
212
|
-
const channelDef = view.mark.encoding[channel];
|
|
213
|
-
if (isChannelDefWithScale(channelDef)) {
|
|
214
|
-
return channelDef;
|
|
215
|
-
} else {
|
|
216
|
-
throw new Error("Not a channel def with scale!");
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* @param {import("../spec/channel").ChannelDef} channelDef
|
|
222
|
-
* @returns {channelDef is import("../spec/channel").ChromPosDef}
|
|
223
|
-
*/
|
|
224
|
-
export function isChromPosDef(channelDef) {
|
|
225
|
-
return channelDef && "chrom" in channelDef;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* @param {import("../spec/channel").ChannelDef} channelDef
|
|
230
|
-
* @returns {channelDef is import("../spec/channel").ExprDef}
|
|
231
|
-
*/
|
|
232
|
-
export function isExprDef(channelDef) {
|
|
233
|
-
return channelDef && "expr" in channelDef;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* @type {import("../spec/channel").PrimaryPositionalChannel[]}
|
|
238
|
-
*/
|
|
239
|
-
export const primaryPositionalChannels = ["x", "y"];
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* @type {import("../spec/channel").SecondaryPositionalChannel[]}
|
|
243
|
-
*/
|
|
244
|
-
export const secondaryPositionalChannels = ["x2", "y2"];
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* @type {import("../spec/channel").PositionalChannel[]}
|
|
248
|
-
*/
|
|
249
|
-
export const positionalChannels = [
|
|
250
|
-
...primaryPositionalChannels,
|
|
251
|
-
...secondaryPositionalChannels,
|
|
252
|
-
];
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* @param {Channel} channel
|
|
256
|
-
* @returns {channel is import("../spec/channel").PrimaryPositionalChannel}
|
|
257
|
-
*/
|
|
258
|
-
export function isPrimaryPositionalChannel(channel) {
|
|
259
|
-
// @ts-expect-error
|
|
260
|
-
return primaryPositionalChannels.includes(channel);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* @param {Channel} channel
|
|
265
|
-
* @returns {channel is import("../spec/channel").PositionalChannel}
|
|
266
|
-
*/
|
|
267
|
-
export function isPositionalChannel(channel) {
|
|
268
|
-
// @ts-expect-error
|
|
269
|
-
return positionalChannels.includes(channel);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Map primary channels to secondarys
|
|
274
|
-
*
|
|
275
|
-
* @type {Partial<Record<Channel, import("../spec/channel").SecondaryPositionalChannel>>}
|
|
276
|
-
*/
|
|
277
|
-
export const secondaryChannels = {
|
|
278
|
-
x: "x2",
|
|
279
|
-
y: "y2",
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* Map secondary channels to primaries
|
|
284
|
-
*
|
|
285
|
-
* @type {Partial<Record<Channel, Channel>>}
|
|
286
|
-
*/
|
|
287
|
-
export const primaryChannels = Object.fromEntries(
|
|
288
|
-
Object.entries(secondaryChannels).map((entry) => [entry[1], entry[0]])
|
|
289
|
-
);
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
*
|
|
293
|
-
* @param {string} channel
|
|
294
|
-
*/
|
|
295
|
-
export function isSecondaryChannel(channel) {
|
|
296
|
-
return channel in primaryChannels;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Return the matching secondary channel or throws if one does not exist.
|
|
301
|
-
*
|
|
302
|
-
* @param {Channel} primaryChannel
|
|
303
|
-
*/
|
|
304
|
-
export function getSecondaryChannel(primaryChannel) {
|
|
305
|
-
const secondary = secondaryChannels[primaryChannel];
|
|
306
|
-
if (secondary) {
|
|
307
|
-
return secondary;
|
|
308
|
-
} else {
|
|
309
|
-
throw new Error(`${primaryChannel} has no secondary channel!`);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Finds the primary channel for the provided channel, which may be
|
|
315
|
-
* the primary or secondary.
|
|
316
|
-
*
|
|
317
|
-
* @param {Channel} channel
|
|
318
|
-
*/
|
|
319
|
-
export function getPrimaryChannel(channel) {
|
|
320
|
-
return primaryChannels[channel] ?? channel;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Returns an array that contains the given channel and its secondary channel if one exists.
|
|
325
|
-
*
|
|
326
|
-
* @param {Channel} channel
|
|
327
|
-
*/
|
|
328
|
-
export function getChannelWithSecondarys(channel) {
|
|
329
|
-
return secondaryChannels[channel]
|
|
330
|
-
? [channel, secondaryChannels[channel]]
|
|
331
|
-
: [channel];
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* @param {Channel} channel
|
|
336
|
-
*/
|
|
337
|
-
export function isColorChannel(channel) {
|
|
338
|
-
return ["color", "fill", "stroke"].includes(getPrimaryChannel(channel));
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Returns true if the channel has a discrete range.
|
|
343
|
-
*
|
|
344
|
-
* @param {Channel} channel
|
|
345
|
-
*/
|
|
346
|
-
export function isDiscreteChannel(channel) {
|
|
347
|
-
return ["shape", "squeeze"].includes(channel);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* Returns valid discrete values for a discrete channel.
|
|
352
|
-
*
|
|
353
|
-
* @param {Channel} channel
|
|
354
|
-
* @returns {any[]}
|
|
355
|
-
*/
|
|
356
|
-
export function getDiscreteRange(channel) {
|
|
357
|
-
// TODO: This is not easily extendable. Figure out a more dynamic approach.
|
|
358
|
-
switch (channel) {
|
|
359
|
-
case "shape":
|
|
360
|
-
return [
|
|
361
|
-
"circle",
|
|
362
|
-
"square",
|
|
363
|
-
"triangle-up",
|
|
364
|
-
"cross",
|
|
365
|
-
"diamond",
|
|
366
|
-
"triangle-down",
|
|
367
|
-
"triangle-right",
|
|
368
|
-
"triangle-left",
|
|
369
|
-
];
|
|
370
|
-
default:
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* @param {Channel} channel
|
|
376
|
-
* @returns {function(any):number}
|
|
377
|
-
*/
|
|
378
|
-
export function getDiscreteRangeMapper(channel) {
|
|
379
|
-
if (!isDiscreteChannel(channel)) {
|
|
380
|
-
throw new Error("Not a discrete channel: " + channel);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
const valueMap = new Map(
|
|
384
|
-
getDiscreteRange(channel).map((value, i) => [value, i])
|
|
385
|
-
);
|
|
386
|
-
|
|
387
|
-
return (value) => {
|
|
388
|
-
const mapped = valueMap.get(value);
|
|
389
|
-
if (mapped !== undefined) {
|
|
390
|
-
return mapped;
|
|
391
|
-
}
|
|
392
|
-
throw new Error(`Invalid value for "${channel}" channel: ${value}`);
|
|
393
|
-
};
|
|
394
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "vitest";
|
|
2
|
-
import AccessorFactory from "./accessor";
|
|
3
|
-
import { scale as vegaScale } from "vega-scale";
|
|
4
|
-
|
|
5
|
-
import { createEncoder } from "./encoder";
|
|
6
|
-
|
|
7
|
-
describe("Encoder", () => {
|
|
8
|
-
/** @type {Record<string, import("../view/viewUtils").ChannelDef>} */
|
|
9
|
-
const encodingSpecs = {
|
|
10
|
-
x: { value: 0 },
|
|
11
|
-
y: { field: "a" },
|
|
12
|
-
z: { datum: 2 },
|
|
13
|
-
size: { value: 5 },
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const scaleLinear = vegaScale("linear");
|
|
17
|
-
|
|
18
|
-
/** @type {Record<string, import("./encoder").VegaScale>} */
|
|
19
|
-
const scales = {
|
|
20
|
-
y: scaleLinear().domain([0, 10]),
|
|
21
|
-
z: scaleLinear().domain([0, 20]),
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const accessorFactory = new AccessorFactory();
|
|
25
|
-
|
|
26
|
-
/** @param {Record<string, import("../view/viewUtils").ChannelDef>} encoding */
|
|
27
|
-
function createEncoders(encoding) {
|
|
28
|
-
/** @type {Record<string, import("./encoder").Encoder>} */
|
|
29
|
-
const encoders = {};
|
|
30
|
-
for (const [channel, channelDef] of Object.entries(encoding)) {
|
|
31
|
-
encoders[channel] = createEncoder(
|
|
32
|
-
channelDef,
|
|
33
|
-
scales[channel],
|
|
34
|
-
accessorFactory.createAccessor(encodingSpecs[channel]),
|
|
35
|
-
channel
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
return encoders;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const datum = {
|
|
42
|
-
a: 5,
|
|
43
|
-
b: 6,
|
|
44
|
-
c: "Pink Floyd",
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
test("Throws on a broken spec", () =>
|
|
48
|
-
expect(() => createEncoders({ x: {} })).toThrow());
|
|
49
|
-
|
|
50
|
-
const encoders = createEncoders(encodingSpecs);
|
|
51
|
-
|
|
52
|
-
test("The encoder object contains all channels", () =>
|
|
53
|
-
expect(
|
|
54
|
-
["x", "y", "z", "size"].every(
|
|
55
|
-
(channel) => typeof encoders[channel] === "function"
|
|
56
|
-
)
|
|
57
|
-
).toBeTruthy());
|
|
58
|
-
|
|
59
|
-
test("Returns a value", () => expect(encoders.x(datum)).toEqual(0));
|
|
60
|
-
|
|
61
|
-
test("Encodes and returns a constant using a scale", () =>
|
|
62
|
-
expect(encoders.z(datum)).toBeCloseTo(0.1));
|
|
63
|
-
|
|
64
|
-
test("Accesses a field and uses a scale", () =>
|
|
65
|
-
expect(encoders.y(datum)).toBeCloseTo(0.5));
|
|
66
|
-
|
|
67
|
-
/*
|
|
68
|
-
test("Accesses a field on a secondary channel and uses the scale from the primary", () =>
|
|
69
|
-
expect(encoders.y2(datum)).toBeCloseTo(0.6));
|
|
70
|
-
*/
|
|
71
|
-
|
|
72
|
-
test("Constant encoder is annotated", () => {
|
|
73
|
-
expect(encoders.y.constant).toBeFalsy();
|
|
74
|
-
expect(encoders.z.constant).toBeTruthy();
|
|
75
|
-
expect(encoders.size.constant).toBeTruthy();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test("Constant value encoder is annotated", () => {
|
|
79
|
-
expect(encoders.y.constantValue).toBeFalsy();
|
|
80
|
-
expect(encoders.z.constantValue).toBeFalsy();
|
|
81
|
-
expect(encoders.size.constantValue).toBeTruthy();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test("Inverts a value", () => {
|
|
85
|
-
expect(encoders.y.invert(0.5)).toBeCloseTo(5);
|
|
86
|
-
expect(encoders.z.invert(0.5)).toBeCloseTo(10);
|
|
87
|
-
// A value, no scale, can't invert
|
|
88
|
-
expect(() => encoders.size.invert(123)).toThrow();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test("Accessors are provided", () => {
|
|
92
|
-
expect(encoders.y.accessor).toBeDefined();
|
|
93
|
-
expect(encoders.z.accessor).toBeDefined();
|
|
94
|
-
expect(encoders.x.accessor).toBeUndefined();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// TODO: Test indexer
|
|
98
|
-
});
|