@genome-spy/core 0.30.0 → 0.30.3

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.
Files changed (234) hide show
  1. package/dist/index.es.js +16379 -0
  2. package/dist/index.js +43 -43
  3. package/package.json +10 -7
  4. package/src/data/collector.js +0 -183
  5. package/src/data/collector.test.js +0 -84
  6. package/src/data/dataFlow.js +0 -148
  7. package/src/data/dataFlow.test.js +0 -5
  8. package/src/data/facetNode.js +0 -17
  9. package/src/data/flow.test.js +0 -72
  10. package/src/data/flowBatch.d.ts +0 -40
  11. package/src/data/flowNode.js +0 -283
  12. package/src/data/flowNode.test.js +0 -50
  13. package/src/data/flowOptimizer.js +0 -123
  14. package/src/data/flowOptimizer.test.js +0 -193
  15. package/src/data/flowTestUtils.js +0 -63
  16. package/src/data/formats/fasta.js +0 -32
  17. package/src/data/formats/fasta.test.js +0 -27
  18. package/src/data/sources/dataSource.js +0 -22
  19. package/src/data/sources/dataSourceFactory.js +0 -24
  20. package/src/data/sources/dataUtils.js +0 -78
  21. package/src/data/sources/dynamicCallbackSource.js +0 -57
  22. package/src/data/sources/dynamicSource.js +0 -37
  23. package/src/data/sources/inlineSource.js +0 -67
  24. package/src/data/sources/inlineSource.test.js +0 -56
  25. package/src/data/sources/namedSource.js +0 -79
  26. package/src/data/sources/sequenceSource.js +0 -46
  27. package/src/data/sources/sequenceSource.test.js +0 -46
  28. package/src/data/sources/urlSource.js +0 -74
  29. package/src/data/transforms/aggregate.js +0 -70
  30. package/src/data/transforms/clone.js +0 -40
  31. package/src/data/transforms/clone.test.js +0 -11
  32. package/src/data/transforms/coverage.js +0 -187
  33. package/src/data/transforms/coverage.test.js +0 -123
  34. package/src/data/transforms/filter.js +0 -37
  35. package/src/data/transforms/filter.test.js +0 -18
  36. package/src/data/transforms/filterScoredLabels.js +0 -134
  37. package/src/data/transforms/flattenCompressedExons.js +0 -57
  38. package/src/data/transforms/flattenDelimited.js +0 -74
  39. package/src/data/transforms/flattenDelimited.test.js +0 -87
  40. package/src/data/transforms/flattenSequence.js +0 -39
  41. package/src/data/transforms/flattenSequence.test.js +0 -34
  42. package/src/data/transforms/formula.js +0 -39
  43. package/src/data/transforms/formula.test.js +0 -19
  44. package/src/data/transforms/identifier.js +0 -108
  45. package/src/data/transforms/identifier.test.js +0 -83
  46. package/src/data/transforms/linearizeGenomicCoordinate.js +0 -101
  47. package/src/data/transforms/measureText.js +0 -44
  48. package/src/data/transforms/pileup.js +0 -128
  49. package/src/data/transforms/pileup.test.js +0 -70
  50. package/src/data/transforms/project.js +0 -41
  51. package/src/data/transforms/project.test.js +0 -32
  52. package/src/data/transforms/regexExtract.js +0 -61
  53. package/src/data/transforms/regexExtract.test.js +0 -67
  54. package/src/data/transforms/regexFold.js +0 -141
  55. package/src/data/transforms/regexFold.test.js +0 -160
  56. package/src/data/transforms/sample.js +0 -101
  57. package/src/data/transforms/sample.test.js +0 -38
  58. package/src/data/transforms/stack.js +0 -137
  59. package/src/data/transforms/stack.test.js +0 -91
  60. package/src/data/transforms/transformFactory.js +0 -60
  61. package/src/embedApi.d.ts +0 -67
  62. package/src/encoder/accessor.js +0 -82
  63. package/src/encoder/accessor.test.js +0 -47
  64. package/src/encoder/encoder.js +0 -394
  65. package/src/encoder/encoder.test.js +0 -98
  66. package/src/fonts/Lato-Regular.json +0 -1267
  67. package/src/fonts/Lato-Regular.png +0 -0
  68. package/src/fonts/OFL.txt +0 -93
  69. package/src/fonts/README.md +0 -3
  70. package/src/fonts/bmFont.d.ts +0 -58
  71. package/src/fonts/bmFontManager.js +0 -357
  72. package/src/fonts/bmFontMetrics.js +0 -108
  73. package/src/genome/genome.js +0 -317
  74. package/src/genome/genome.test.js +0 -188
  75. package/src/genome/genomeStore.js +0 -54
  76. package/src/genome/locusFormat.js +0 -31
  77. package/src/genome/scaleIndex.d.ts +0 -38
  78. package/src/genome/scaleIndex.js +0 -166
  79. package/src/genome/scaleIndex.test.js +0 -78
  80. package/src/genome/scaleLocus.d.ts +0 -11
  81. package/src/genome/scaleLocus.js +0 -108
  82. package/src/genome/scaleLocus.test.js +0 -4
  83. package/src/genomeSpy.js +0 -785
  84. package/src/gl/arrayBuilder.js +0 -199
  85. package/src/gl/dataToVertices.js +0 -636
  86. package/src/gl/includes/common.glsl +0 -63
  87. package/src/gl/includes/picking.fragment.glsl +0 -1
  88. package/src/gl/includes/picking.vertex.glsl +0 -27
  89. package/src/gl/includes/sampleFacet.glsl +0 -107
  90. package/src/gl/includes/scales.glsl +0 -112
  91. package/src/gl/link.fragment.glsl +0 -18
  92. package/src/gl/link.vertex.glsl +0 -111
  93. package/src/gl/point.fragment.glsl +0 -123
  94. package/src/gl/point.vertex.glsl +0 -129
  95. package/src/gl/rect.fragment.glsl +0 -51
  96. package/src/gl/rect.vertex.glsl +0 -114
  97. package/src/gl/rule.fragment.glsl +0 -52
  98. package/src/gl/rule.vertex.glsl +0 -89
  99. package/src/gl/text.fragment.glsl +0 -31
  100. package/src/gl/text.vertex.glsl +0 -246
  101. package/src/gl/webGLHelper.js +0 -504
  102. package/src/img/bowtie.svg +0 -1
  103. package/src/img/genomespy-favicon.svg +0 -34
  104. package/src/index.html +0 -11
  105. package/src/index.js +0 -128
  106. package/src/marks/link.js +0 -175
  107. package/src/marks/mark.js +0 -975
  108. package/src/marks/markUtils.js +0 -125
  109. package/src/marks/pointMark.js +0 -251
  110. package/src/marks/rectMark.js +0 -241
  111. package/src/marks/rule.js +0 -250
  112. package/src/marks/text.js +0 -278
  113. package/src/node_modules/.vitest/results.json +0 -1
  114. package/src/scale/colorUtils.js +0 -184
  115. package/src/scale/glslScaleGenerator.js +0 -502
  116. package/src/scale/scale.js +0 -451
  117. package/src/scale/scale.test.js +0 -324
  118. package/src/scale/ticks.js +0 -203
  119. package/src/scale/ticks.test.js +0 -40
  120. package/src/singlePageApp.js +0 -13
  121. package/src/spec/axis.d.ts +0 -296
  122. package/src/spec/channel.d.ts +0 -430
  123. package/src/spec/data.d.ts +0 -196
  124. package/src/spec/font.d.ts +0 -15
  125. package/src/spec/genome.d.ts +0 -35
  126. package/src/spec/mark.d.ts +0 -429
  127. package/src/spec/root.d.ts +0 -17
  128. package/src/spec/sampleView.d.ts +0 -180
  129. package/src/spec/scale.d.ts +0 -273
  130. package/src/spec/title.d.ts +0 -102
  131. package/src/spec/tooltip.d.ts +0 -9
  132. package/src/spec/transform.d.ts +0 -479
  133. package/src/spec/view.d.ts +0 -201
  134. package/src/styles/genome-spy.scss +0 -153
  135. package/src/tooltip/dataTooltipHandler.js +0 -64
  136. package/src/tooltip/refseqGeneTooltipHandler.js +0 -78
  137. package/src/tooltip/tooltipHandler.ts +0 -12
  138. package/src/types/filetypes.d.ts +0 -14
  139. package/src/types/flatqueue.d.ts +0 -53
  140. package/src/types/glsl.d.ts +0 -4
  141. package/src/types/internmap.d.ts +0 -22
  142. package/src/types/object.d.ts +0 -21
  143. package/src/types/vega-loader.d.ts +0 -1
  144. package/src/types/vega-scale.d.ts +0 -60
  145. package/src/utils/addBaseUrl.js +0 -19
  146. package/src/utils/addBaseUrl.test.js +0 -22
  147. package/src/utils/animator.js +0 -83
  148. package/src/utils/arrayUtils.js +0 -61
  149. package/src/utils/binnedIndex.js +0 -167
  150. package/src/utils/binnedIndex.test.js +0 -155
  151. package/src/utils/clamp.js +0 -8
  152. package/src/utils/cloner.js +0 -34
  153. package/src/utils/cloner.test.js +0 -24
  154. package/src/utils/coalesce.js +0 -11
  155. package/src/utils/coalesce.test.js +0 -16
  156. package/src/utils/concatIterables.js +0 -26
  157. package/src/utils/concatIterables.test.js +0 -8
  158. package/src/utils/debounce.js +0 -37
  159. package/src/utils/domainArray.js +0 -216
  160. package/src/utils/domainArray.test.js +0 -130
  161. package/src/utils/eerp.js +0 -13
  162. package/src/utils/expression.js +0 -32
  163. package/src/utils/field.js +0 -28
  164. package/src/utils/formatObject.js +0 -31
  165. package/src/utils/indexer.js +0 -43
  166. package/src/utils/indexer.test.js +0 -47
  167. package/src/utils/inertia.js +0 -124
  168. package/src/utils/interactionEvent.js +0 -33
  169. package/src/utils/iterateNestedMaps.js +0 -21
  170. package/src/utils/iterateNestedMaps.test.js +0 -33
  171. package/src/utils/kWayMerge.js +0 -42
  172. package/src/utils/kWayMerge.test.js +0 -26
  173. package/src/utils/layout/flexLayout.js +0 -368
  174. package/src/utils/layout/flexLayout.test.js +0 -311
  175. package/src/utils/layout/grid.js +0 -95
  176. package/src/utils/layout/grid.test.js +0 -71
  177. package/src/utils/layout/padding.js +0 -120
  178. package/src/utils/layout/point.js +0 -23
  179. package/src/utils/layout/rectangle.js +0 -288
  180. package/src/utils/layout/rectangle.test.js +0 -172
  181. package/src/utils/mergeObjects.js +0 -99
  182. package/src/utils/mergeObjects.test.js +0 -42
  183. package/src/utils/numberExtractor.js +0 -24
  184. package/src/utils/numberExtractor.test.js +0 -6
  185. package/src/utils/point.js +0 -14
  186. package/src/utils/propertyCacher.js +0 -70
  187. package/src/utils/propertyCacher.test.js +0 -85
  188. package/src/utils/propertyCoalescer.js +0 -42
  189. package/src/utils/propertyCoalescer.test.js +0 -22
  190. package/src/utils/reservationMap.js +0 -103
  191. package/src/utils/reservationMap.test.js +0 -20
  192. package/src/utils/scaleNull.js +0 -19
  193. package/src/utils/setOperations.js +0 -75
  194. package/src/utils/smoothstep.js +0 -10
  195. package/src/utils/throttle.js +0 -34
  196. package/src/utils/topK.js +0 -76
  197. package/src/utils/topK.test.js +0 -64
  198. package/src/utils/transition.js +0 -74
  199. package/src/utils/ui/tooltip.js +0 -189
  200. package/src/utils/url.js +0 -22
  201. package/src/utils/variableTools.js +0 -24
  202. package/src/utils/variableTools.test.js +0 -13
  203. package/src/view/axisResolution.js +0 -140
  204. package/src/view/axisResolution.test.js +0 -201
  205. package/src/view/axisView.js +0 -747
  206. package/src/view/concatView.js +0 -45
  207. package/src/view/containerView.js +0 -159
  208. package/src/view/facetView.js +0 -491
  209. package/src/view/flowBuilder.js +0 -367
  210. package/src/view/flowBuilder.test.js +0 -125
  211. package/src/view/gridView.js +0 -786
  212. package/src/view/implicitRootView.js +0 -14
  213. package/src/view/importView.js +0 -19
  214. package/src/view/layerView.js +0 -74
  215. package/src/view/rendering.d.ts +0 -44
  216. package/src/view/renderingContext/compositeViewRenderingContext.js +0 -51
  217. package/src/view/renderingContext/deferredViewRenderingContext.js +0 -176
  218. package/src/view/renderingContext/layoutRecorderViewRenderingContext.js +0 -128
  219. package/src/view/renderingContext/simpleViewRenderingContext.js +0 -64
  220. package/src/view/renderingContext/svgViewRenderingContext.js +0 -125
  221. package/src/view/renderingContext/viewRenderingContext.js +0 -41
  222. package/src/view/scaleResolution.js +0 -797
  223. package/src/view/scaleResolution.test.js +0 -572
  224. package/src/view/scaleResolutionApi.d.ts +0 -40
  225. package/src/view/testUtils.js +0 -51
  226. package/src/view/title.js +0 -165
  227. package/src/view/unitView.js +0 -382
  228. package/src/view/view.js +0 -612
  229. package/src/view/view.test.js +0 -214
  230. package/src/view/viewContext.d.ts +0 -62
  231. package/src/view/viewFactory.js +0 -181
  232. package/src/view/viewFactory.test.js +0 -17
  233. package/src/view/viewUtils.js +0 -327
  234. package/src/view/zoom.js +0 -89
@@ -1,747 +0,0 @@
1
- import { validTicks, tickValues, tickFormat, tickCount } from "../scale/ticks";
2
- import LayerView from "./layerView";
3
- import { isNumber } from "vega-util";
4
- import smoothstep from "../utils/smoothstep";
5
- import {
6
- shallowArrayEquals,
7
- shallowArrayEqualsWithAccessors,
8
- } from "../utils/arrayUtils";
9
- import { FlexDimensions } from "../utils/layout/flexLayout";
10
- import DynamicCallbackSource from "../data/sources/dynamicCallbackSource";
11
-
12
- const CHROM_LAYER_NAME = "chromosome_ticks_and_labels";
13
-
14
- /**
15
- * @typedef {import("../spec/channel").PrimaryPositionalChannel} PositionalChannel
16
- * @typedef {import("../spec/view").GeometricDimension} GeometricDimension
17
- */
18
-
19
- /** @type {Record<PositionalChannel, GeometricDimension>} */
20
- const CHANNEL_DIMENSIONS = {
21
- x: "width",
22
- y: "height",
23
- };
24
-
25
- /**
26
- * @param {PositionalChannel} channel
27
- * @returns {PositionalChannel}
28
- */
29
- function getPerpendicularChannel(channel) {
30
- return channel == "x" ? "y" : "x";
31
- }
32
-
33
- /** @type {Record<PositionalChannel, AxisOrient[]>} */
34
- export const CHANNEL_ORIENTS = {
35
- x: ["bottom", "top"],
36
- y: ["left", "right"],
37
- };
38
-
39
- /** @type {Record<AxisOrient, PositionalChannel>} */
40
- const ORIENT_CHANNELS = Object.fromEntries(
41
- Object.entries(CHANNEL_ORIENTS)
42
- .map(([channel, slots]) => slots.map((slot) => [slot, channel]))
43
- .flat(1)
44
- );
45
- /**
46
- * @param {AxisOrient} slot
47
- */
48
- function orient2channel(slot) {
49
- return ORIENT_CHANNELS[slot];
50
- }
51
-
52
- /**
53
- * An internal view that renders an axis.
54
- *
55
- * TODO: Implement grid
56
- *
57
- * @typedef {import("../spec/view").LayerSpec} LayerSpec
58
- * @typedef {import("./view").default} View
59
- * @typedef {import("../spec/axis").Axis} Axis
60
- * @typedef {import("../spec/axis").GenomeAxis} GenomeAxis
61
- * @typedef {import("../spec/axis").AxisOrient} AxisOrient
62
- * @typedef {import("../utils/layout/flexLayout").SizeDef} SizeDef
63
- *
64
- * @typedef {Axis & { extent: number }} AugmentedAxis
65
- */
66
- export default class AxisView extends LayerView {
67
- /**
68
- * @param {Axis} axisProps
69
- * @param {import("./viewUtils").ViewContext} context
70
- * @param {string} type Data type (quantitative, ..., locus)
71
- * @param {import("./containerView").default} parent
72
- */
73
- constructor(axisProps, type, context, parent) {
74
- // Now the presence of genomeAxis is based on field type, not scale type.
75
- // TODO: Use scale instead. However, it would make the initialization much more
76
- // complex because scales are not available before scale resolution.
77
- const genomeAxis = type == "locus";
78
-
79
- // TODO: Compute extent
80
-
81
- /** @type {Axis | GenomeAxis} */
82
- const fullAxisProps = {
83
- ...(genomeAxis ? defaultGenomeAxisProps : defaultAxisProps),
84
- ...getDefaultAngleAndAlign(type, axisProps),
85
- ...axisProps,
86
- };
87
-
88
- super(
89
- genomeAxis
90
- ? createGenomeAxis(fullAxisProps)
91
- : createAxis(fullAxisProps),
92
- context,
93
- parent,
94
- `axis_${axisProps.orient}`
95
- );
96
-
97
- this.axisProps = fullAxisProps;
98
-
99
- /** Axis should be updated before next render */
100
- this.axisUpdateRequested = true;
101
-
102
- this._addBroadcastHandler("layout", () => {
103
- this.axisUpdateRequested = true;
104
- });
105
-
106
- /** @type {any[]} */
107
- this.previousScaleDomain = [];
108
-
109
- /** @type {number} TODO: Take from scal*/
110
- this.axisLength = undefined;
111
-
112
- /** @type {TickDatum[]} */
113
- this.ticks = [];
114
-
115
- this.tickSource = new DynamicCallbackSource(() => this.ticks);
116
-
117
- if (genomeAxis) {
118
- const channel = orient2channel(this.axisProps.orient);
119
- const genome = this.getScaleResolution(channel).getGenome();
120
- this.findChildByName(CHROM_LAYER_NAME).getDynamicDataSource = () =>
121
- new DynamicCallbackSource(() => genome.chromosomes);
122
- }
123
-
124
- this.blockEncodingInheritance = true;
125
- }
126
-
127
- getOrient() {
128
- return this.axisProps.orient;
129
- }
130
-
131
- getSize() {
132
- /** @type {SizeDef} */
133
- const perpendicularSize = { px: this.getPerpendicularSize() };
134
-
135
- /** @type {SizeDef} */
136
- const mainSize = { grow: 1 };
137
-
138
- if (ORIENT_CHANNELS[this.axisProps.orient] == "x") {
139
- return new FlexDimensions(mainSize, perpendicularSize);
140
- } else {
141
- return new FlexDimensions(perpendicularSize, mainSize);
142
- }
143
- }
144
-
145
- getPerpendicularSize() {
146
- return getExtent(this.axisProps);
147
- }
148
-
149
- getDynamicDataSource() {
150
- return this.tickSource;
151
- }
152
-
153
- _updateAxisData() {
154
- // TODO: This could be a transform that generates ticks on the fly
155
- // Would allow for unlimited customization.
156
-
157
- const channel = orient2channel(this.axisProps.orient);
158
- const scale = this.getScaleResolution(channel).getScale();
159
- const currentScaleDomain = scale.domain();
160
-
161
- if (
162
- shallowArrayEquals(currentScaleDomain, this.previousScaleDomain) &&
163
- !this.axisUpdateRequested
164
- ) {
165
- // TODO: Instead of scale comparison, register an observer to Resolution
166
- return;
167
- }
168
- this.previousScaleDomain = currentScaleDomain;
169
-
170
- const oldTicks = this.ticks;
171
- const newTicks = generateTicks(
172
- this.axisProps,
173
- scale,
174
- this.axisLength,
175
- oldTicks
176
- );
177
-
178
- if (newTicks !== oldTicks) {
179
- this.ticks = newTicks;
180
- this.tickSource.loadSynchronously();
181
- }
182
-
183
- this.axisUpdateRequested = false;
184
- }
185
-
186
- onBeforeRender() {
187
- super.onBeforeRender();
188
- this._updateAxisData();
189
- }
190
-
191
- /**
192
- * @param {import("./renderingContext/viewRenderingContext").default} context
193
- * @param {import("../utils/layout/rectangle").default} coords
194
- * @param {import("./view").RenderingOptions} [options]
195
- */
196
- render(context, coords, options = {}) {
197
- if (!this.isVisible()) {
198
- return;
199
- }
200
-
201
- this.axisLength =
202
- coords[CHANNEL_DIMENSIONS[orient2channel(this.getOrient())]];
203
-
204
- super.render(context, coords, options);
205
- }
206
-
207
- isPickingSupported() {
208
- return false;
209
- }
210
- }
211
-
212
- /**
213
- * @param {Axis} axisProps
214
- */
215
- function getExtent(axisProps) {
216
- const mainChannel = orient2channel(axisProps.orient);
217
-
218
- /** @type {number} */
219
- let extent = (axisProps.ticks && axisProps.tickSize) || 0;
220
-
221
- if (axisProps.labels) {
222
- extent += axisProps.labelPadding;
223
- if (mainChannel == "x") {
224
- extent += axisProps.labelFontSize;
225
- } else {
226
- extent += 30; // TODO: Measure label lengths!
227
- }
228
- }
229
- if (axisProps.title) {
230
- extent += axisProps.titlePadding + axisProps.titleFontSize;
231
- }
232
-
233
- // TODO: Include chrom ticks and labels!
234
-
235
- extent = Math.min(
236
- axisProps.maxExtent || Infinity,
237
- Math.max(axisProps.minExtent || 0, extent)
238
- );
239
-
240
- return extent;
241
- }
242
-
243
- /**
244
- * @param {Axis} axisProps
245
- * @param {any} scale
246
- * @param {number} axisLength Length of axis in pixels
247
- * @param {TickDatum[]} [oldTicks] Reuse the old data if the tick values are identical
248
- * @returns {TickDatum[]}
249
- *
250
- * @typedef {object} TickDatum
251
- * @prop {number} value
252
- * @prop {string} label
253
- */
254
- function generateTicks(axisProps, scale, axisLength, oldTicks = []) {
255
- /**
256
- * Make ticks more dense in small plots
257
- *
258
- * @param {number} length
259
- */
260
- const tickSpacing = (length) => 25 + 60 * smoothstep(100, 700, length);
261
-
262
- let count = isNumber(axisProps.tickCount)
263
- ? axisProps.tickCount
264
- : Math.round(axisLength / tickSpacing(axisLength));
265
-
266
- count = tickCount(scale, count, axisProps.tickMinStep);
267
-
268
- const values = axisProps.values
269
- ? validTicks(scale, axisProps.values, count)
270
- : tickValues(scale, count);
271
-
272
- if (
273
- shallowArrayEqualsWithAccessors(
274
- values,
275
- oldTicks,
276
- (v) => v,
277
- (d) => d.value
278
- )
279
- ) {
280
- return oldTicks;
281
- } else {
282
- const format = tickFormat(scale, count, axisProps.format);
283
-
284
- return values.map((x) => ({ value: x, label: format(x) }));
285
- }
286
- }
287
-
288
- // Based on: https://vega.github.io/vega-lite/docs/axis.html
289
- // TODO: The defaults should be taken from config (theme)
290
- /** @type {Axis} */
291
- const defaultAxisProps = {
292
- values: null,
293
-
294
- minExtent: 20,
295
- maxExtent: Infinity,
296
- offset: 0, // TODO: Implement
297
-
298
- domain: true,
299
- domainWidth: 1,
300
- domainColor: "gray",
301
- domainDash: null,
302
- domainDashOffset: 0,
303
- domainCap: "square", // Make 1px caps crisp
304
-
305
- ticks: true,
306
- tickSize: 5,
307
- tickWidth: 1,
308
- tickColor: "gray",
309
- tickDash: null,
310
- tickDashOffset: 0,
311
- tickCap: "square", // Make 1px caps crisp
312
-
313
- // TODO: tickBand
314
-
315
- tickCount: null,
316
- tickMinStep: null,
317
-
318
- labels: true,
319
- labelAlign: "center",
320
- labelBaseline: "middle",
321
- labelPadding: 4,
322
- labelFontSize: 10,
323
- labelLimit: 180, // TODO
324
- labelColor: "black",
325
- format: null,
326
-
327
- titleColor: "black",
328
- titleFont: "sans-serif",
329
- titleFontSize: 10,
330
- titlePadding: 3,
331
-
332
- // TODO: titleX, titleY, titleAngle, titleAlign, etc
333
- };
334
-
335
- /**
336
- * @param {string} type
337
- * @param {Axis} axisProps
338
- */
339
- function getDefaultAngleAndAlign(type, axisProps) {
340
- const orient = axisProps.orient;
341
- const discrete = type == "nominal" || type == "ordinal";
342
-
343
- /** @type {import("../spec/font").Align} */
344
- let align = "center";
345
- /** @type {import("../spec/font").Baseline} */
346
- let baseline = "middle";
347
-
348
- /** @type {number} */
349
- let angle =
350
- axisProps.labelAngle ??
351
- ((orient == "top" || orient == "bottom") && discrete ? -90 : 0);
352
-
353
- // TODO: Setting labelAngle of left or right axis to 90 or -90 should center the labels
354
-
355
- switch (orient) {
356
- case "left":
357
- align = "right";
358
- break;
359
- case "right":
360
- align = "left";
361
- break;
362
- case "top":
363
- case "bottom":
364
- if (Math.abs(angle) > 30) {
365
- align = angle > 0 === (orient == "bottom") ? "left" : "right";
366
- baseline = "middle";
367
- } else {
368
- baseline = orient == "top" ? "alphabetic" : "top";
369
- }
370
- break;
371
- default:
372
- }
373
-
374
- return {
375
- labelAlign: align,
376
- labelAngle: angle,
377
- labelBaseline: baseline,
378
- };
379
- }
380
-
381
- /**
382
- * @param {Axis} axisProps
383
- * @returns {LayerSpec}
384
- */
385
- function createAxis(axisProps) {
386
- // TODO: Ensure that no channels except the positional ones are shared
387
-
388
- const ap = { ...axisProps, extent: getExtent(axisProps) };
389
-
390
- const main = orient2channel(ap.orient);
391
- const secondary = getPerpendicularChannel(main);
392
-
393
- const offsetDirection =
394
- ap.orient == "bottom" || ap.orient == "right" ? 1 : -1;
395
-
396
- const anchor = ap.orient == "bottom" || ap.orient == "left" ? 1 : 0;
397
-
398
- /**
399
- * @return {import("../spec/view").UnitSpec}
400
- */
401
- const createDomain = () => ({
402
- name: "domain",
403
- data: { values: [0] },
404
- mark: {
405
- type: "rule",
406
- clip: false,
407
- strokeDash: ap.domainDash,
408
- strokeCap: ap.domainCap,
409
- color: ap.domainColor,
410
- [secondary]: anchor,
411
- size: ap.domainWidth,
412
- },
413
- });
414
-
415
- /**
416
- * @return {import("../spec/view").UnitSpec}
417
- */
418
- const createLabels = () => ({
419
- name: "labels",
420
- mark: {
421
- type: "text",
422
- clip: false,
423
- align: ap.labelAlign,
424
- angle: ap.labelAngle,
425
- baseline: ap.labelBaseline,
426
- [secondary + "Offset"]:
427
- (ap.tickSize + ap.labelPadding) * offsetDirection,
428
- [secondary]: anchor,
429
- size: ap.labelFontSize,
430
- color: ap.labelColor,
431
- minBufferSize: 1500, // to prevent GPU buffer reallocation when zooming
432
- },
433
- encoding: {
434
- [main]: { field: "value", type: "quantitative" },
435
- text: { field: "label" },
436
- },
437
- });
438
-
439
- /**
440
- * @return {import("../spec/view").UnitSpec}
441
- */
442
- const createTicks = () => ({
443
- name: "ticks",
444
- mark: {
445
- type: "rule",
446
- clip: false,
447
- strokeDash: ap.tickDash,
448
- strokeCap: ap.tickCap,
449
- color: ap.tickColor,
450
- size: ap.tickWidth,
451
- minBufferSize: 300,
452
- },
453
- encoding: {
454
- [secondary]: { value: anchor },
455
- [secondary + "2"]: {
456
- value: anchor - (ap.tickSize / ap.extent) * (anchor ? 1 : -1),
457
- },
458
- },
459
- });
460
-
461
- /**
462
- * @return {import("../spec/view").UnitSpec}
463
- */
464
- const createTitle = () => ({
465
- name: "title",
466
- data: { values: [0] },
467
- mark: {
468
- type: "text",
469
- clip: false,
470
- align: "center",
471
- baseline: ap.orient == "bottom" ? "bottom" : "top",
472
- angle: [0, 90, 0, -90][
473
- ["top", "right", "bottom", "left"].indexOf(ap.orient)
474
- ],
475
- text: ap.title,
476
- color: ap.titleColor,
477
- [main]: 0.5,
478
- [secondary]: 1 - anchor,
479
- },
480
- });
481
-
482
- /**
483
- * @return {import("../spec/view").LayerSpec}
484
- */
485
- const createTicksAndLabels = () => {
486
- /** @type {LayerSpec} */
487
- const spec = {
488
- name: "ticks_and_labels",
489
- encoding: {
490
- [main]: { field: "value", type: "quantitative" },
491
- },
492
- layer: [],
493
- };
494
-
495
- if (ap.ticks) {
496
- spec.layer.push(createTicks());
497
- }
498
-
499
- if (ap.labels) {
500
- spec.layer.push(createLabels());
501
- }
502
-
503
- return spec;
504
- };
505
-
506
- /** @type {LayerSpec} */
507
- const axisSpec = {
508
- [CHANNEL_DIMENSIONS[
509
- getPerpendicularChannel(orient2channel(ap.orient))
510
- ]]: ap.extent,
511
- data: { dynamicCallbackSource: true },
512
- layer: [],
513
- };
514
-
515
- if (ap.domain) {
516
- axisSpec.layer.push(createDomain());
517
- }
518
-
519
- if (ap.ticks || ap.labels) {
520
- axisSpec.layer.push(createTicksAndLabels());
521
- }
522
-
523
- if (ap.title) {
524
- axisSpec.layer.push(createTitle());
525
- }
526
-
527
- return axisSpec;
528
- }
529
-
530
- /** @type {import("../spec/axis").GenomeAxis} */
531
- const defaultGenomeAxisProps = {
532
- ...defaultAxisProps,
533
-
534
- chromTicks: true,
535
- chromTickSize: 18,
536
- chromTickWidth: 1,
537
- chromTickColor: "#989898",
538
- chromTickDash: [4, 2],
539
- chromTickDashOffset: 1,
540
-
541
- chromLabels: true,
542
- chromLabelFontSize: 13,
543
- chromLabelFontWeight: "normal",
544
- chromLabelFontStyle: "normal",
545
- chromLabelColor: "black",
546
- chromLabelAlign: "left",
547
- chromLabelPadding: 7,
548
- // TODO: chromLabelAngle
549
- };
550
-
551
- /**
552
- * @param {GenomeAxis} axisProps
553
- * @returns {LayerSpec}
554
- */
555
- export function createGenomeAxis(axisProps) {
556
- const ap = { ...axisProps, extent: getExtent(axisProps) };
557
-
558
- const main = orient2channel(ap.orient);
559
- const secondary = getPerpendicularChannel(main);
560
-
561
- const anchor = ap.orient == "bottom" || ap.orient == "left" ? 1 : 0;
562
-
563
- /**
564
- * @return {import("../spec/view").UnitSpec}
565
- */
566
- const createChromosomeTicks = () => ({
567
- name: "chromosome_ticks",
568
- mark: {
569
- type: "rule",
570
- strokeDash: axisProps.chromTickDash,
571
- strokeDashOffset: axisProps.chromTickDashOffset,
572
- [secondary]: anchor,
573
- [secondary + "2"]:
574
- anchor - (ap.chromTickSize / ap.extent) * (anchor ? 1 : -1),
575
- color: axisProps.chromTickColor,
576
- size: ap.chromTickWidth,
577
- },
578
- });
579
-
580
- /**
581
- * @return {import("../spec/view").UnitSpec}
582
- */
583
- const createChromosomeLabels = () => {
584
- /** @type {Partial<import("../spec/mark").MarkConfig>} */
585
- let chromLabelMarkProps;
586
- switch (ap.orient) {
587
- case "top":
588
- chromLabelMarkProps = {
589
- y: 0,
590
- angle: 0,
591
- paddingX: 4,
592
- dy: -ap.chromLabelPadding,
593
- viewportEdgeFadeWidthLeft: 20,
594
- viewportEdgeFadeWidthRight: 20,
595
- viewportEdgeFadeDistanceRight: -10,
596
- viewportEdgeFadeDistanceLeft: -20,
597
- };
598
- break;
599
- case "bottom":
600
- chromLabelMarkProps = {
601
- y: 1,
602
- angle: 0,
603
- paddingX: 4,
604
- dy: ap.chromLabelPadding + ap.chromLabelFontSize * 0.73, // A hack to align baseline with other labels
605
- viewportEdgeFadeWidthLeft: 20,
606
- viewportEdgeFadeWidthRight: 20,
607
- viewportEdgeFadeDistanceRight: -10,
608
- viewportEdgeFadeDistanceLeft: -20,
609
- };
610
- break;
611
- case "left":
612
- chromLabelMarkProps = {
613
- x: 1,
614
- angle: -90,
615
- paddingY: 4,
616
- dy: -ap.chromLabelPadding,
617
- viewportEdgeFadeWidthBottom: 20,
618
- viewportEdgeFadeWidthTop: 20,
619
- viewportEdgeFadeDistanceBottom: -20,
620
- viewportEdgeFadeDistanceTop: -10,
621
- };
622
- break;
623
- case "right":
624
- chromLabelMarkProps = {
625
- x: 0,
626
- angle: 90,
627
- align: "right",
628
- paddingY: 4,
629
- dy: -ap.chromLabelPadding,
630
- };
631
- break;
632
- default:
633
- chromLabelMarkProps = {};
634
- }
635
-
636
- /** @type {import("../spec/view").UnitSpec} */
637
- const labels = {
638
- name: "chromosome_labels",
639
- mark: {
640
- type: "text",
641
- size: ap.chromLabelFontSize,
642
- font: ap.chromLabelFont,
643
- fontWeight: ap.chromLabelFontWeight,
644
- fontStyle: ap.chromLabelFontStyle,
645
- color: ap.chromLabelColor,
646
- align: axisProps.chromLabelAlign,
647
- baseline: "alphabetic",
648
- clip: false,
649
- ...chromLabelMarkProps,
650
- },
651
- encoding: {
652
- [main + "2"]: { field: "continuousEnd", type: "locus" },
653
- text: { field: "name" },
654
- },
655
- };
656
- return labels;
657
- };
658
-
659
- /** @type {Axis} */
660
- let fixedAxisProps;
661
- switch (ap.orient) {
662
- case "bottom":
663
- case "top":
664
- fixedAxisProps = {};
665
- break;
666
- case "left":
667
- fixedAxisProps = {
668
- labelAngle: -90,
669
- labelAlign: "center",
670
- labelPadding: 6,
671
- };
672
- break;
673
- case "right":
674
- fixedAxisProps = {
675
- labelAngle: 90,
676
- labelAlign: "center",
677
- labelPadding: 6,
678
- };
679
- break;
680
- default:
681
- fixedAxisProps = {};
682
- }
683
-
684
- // Create an ordinary axis
685
- const axisSpec = createAxis({
686
- ...axisProps,
687
- ...fixedAxisProps,
688
- // TODO: Allow the user to override fixedAxisProps
689
- });
690
-
691
- if (axisProps.chromTicks || axisProps.chromLabels) {
692
- /** @type {import("../spec/view").LayerSpec} */
693
- const chromLayerSpec = {
694
- // TODO: Configuration
695
- name: CHROM_LAYER_NAME,
696
- data: { dynamicCallbackSource: true },
697
- encoding: {
698
- // TODO: { chrom: "name", type: "locus" } // without pos = pos is 0
699
- [main]: { field: "continuousStart", type: "locus", band: 0 },
700
- },
701
- layer: [],
702
- };
703
-
704
- if (axisProps.chromTicks) {
705
- chromLayerSpec.layer.push(createChromosomeTicks());
706
- }
707
-
708
- if (axisProps.chromLabels) {
709
- chromLayerSpec.layer.push(createChromosomeLabels());
710
-
711
- /** @type {import("../spec/mark").MarkConfig} */
712
- let labelMarkSpec;
713
-
714
- // TODO: Simplify the following mess
715
- axisSpec.layer
716
- .filter((view) => view.name == "ticks_and_labels")
717
- .forEach((/** @type {LayerSpec} */ view) =>
718
- view.layer
719
- .filter((view) => view.name == "labels")
720
- .forEach(
721
- (
722
- /** @type {import("../spec/view").UnitSpec} */ view
723
- ) => {
724
- labelMarkSpec =
725
- /** @type {import("../spec/mark").MarkConfig} */ (
726
- view.mark
727
- );
728
- }
729
- )
730
- );
731
-
732
- if (labelMarkSpec) {
733
- if (ap.orient == "top" || ap.orient == "bottom") {
734
- labelMarkSpec.viewportEdgeFadeWidthLeft = 30;
735
- labelMarkSpec.viewportEdgeFadeDistanceLeft = 40;
736
- } else {
737
- labelMarkSpec.viewportEdgeFadeWidthBottom = 30;
738
- labelMarkSpec.viewportEdgeFadeDistanceBottom = 40;
739
- }
740
- }
741
- }
742
-
743
- axisSpec.layer.push(chromLayerSpec);
744
- }
745
-
746
- return axisSpec;
747
- }