@genome-spy/core 0.15.0 → 0.16.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.
Files changed (67) hide show
  1. package/LICENSE +7 -7
  2. package/README.md +16 -0
  3. package/dist/genome-spy-schema.json +816 -624
  4. package/dist/index.js +42 -42
  5. package/dist/style.css +1 -1
  6. package/package.json +3 -2
  7. package/src/data/sources/dataUtils.js +50 -3
  8. package/src/data/sources/dynamicCallbackSource.js +2 -1
  9. package/src/data/sources/dynamicSource.js +2 -1
  10. package/src/data/sources/inlineSource.js +3 -5
  11. package/src/data/sources/namedSource.js +3 -7
  12. package/src/data/sources/urlSource.js +1 -1
  13. package/src/data/transforms/aggregate.js +1 -0
  14. package/src/data/transforms/flattenDelimited.js +6 -0
  15. package/src/data/transforms/regexFold.js +1 -1
  16. package/src/data/transforms/stack.js +3 -3
  17. package/src/embedApi.d.ts +59 -0
  18. package/src/encoder/accessor.js +6 -6
  19. package/src/encoder/encoder.js +47 -22
  20. package/src/genome/scaleIndex.d.ts +38 -0
  21. package/src/genome/scaleIndex.js +18 -52
  22. package/src/genome/scaleLocus.d.ts +11 -0
  23. package/src/genome/scaleLocus.js +12 -16
  24. package/src/genomeSpy.js +1 -1
  25. package/src/gl/dataToVertices.js +14 -6
  26. package/src/gl/includes/fp64-utils.js +10 -0
  27. package/src/gl/includes/scales.glsl +2 -0
  28. package/src/gl/webGLHelper.js +3 -1
  29. package/src/index.js +6 -28
  30. package/src/marks/link.js +1 -12
  31. package/src/marks/mark.js +27 -5
  32. package/src/marks/markUtils.js +41 -25
  33. package/src/marks/pointMark.js +5 -2
  34. package/src/marks/rule.js +11 -2
  35. package/src/scale/glslScaleGenerator.js +16 -29
  36. package/src/scale/scale.js +10 -0
  37. package/src/scale/ticks.js +11 -6
  38. package/src/spec/channel.d.ts +343 -43
  39. package/src/spec/data.d.ts +14 -3
  40. package/src/spec/scale.d.ts +18 -1
  41. package/src/spec/view.d.ts +12 -6
  42. package/src/tooltip/refseqGeneTooltipHandler.js +1 -0
  43. package/src/types/filetypes.d.ts +10 -0
  44. package/src/types/internmap.d.ts +22 -0
  45. package/src/types/vega-loader.d.ts +1 -0
  46. package/src/utils/arrayUtils.js +12 -6
  47. package/src/utils/cloner.js +5 -3
  48. package/src/utils/concatIterables.js +2 -2
  49. package/src/utils/domainArray.js +0 -8
  50. package/src/utils/propertyCoalescer.js +9 -4
  51. package/src/view/axisResolution.js +11 -6
  52. package/src/view/axisView.js +8 -5
  53. package/src/view/decoratorView.js +6 -3
  54. package/src/view/facetView.js +3 -0
  55. package/src/view/flowBuilder.js +2 -1
  56. package/src/view/renderingContext/svgViewRenderingContext.js +7 -3
  57. package/src/view/scaleResolution.js +52 -32
  58. package/src/view/testUtils.js +7 -4
  59. package/src/view/unitView.js +15 -9
  60. package/src/view/view.js +10 -8
  61. package/src/view/viewFactory.js +2 -0
  62. package/src/view/viewUtils.js +4 -4
  63. package/src/options.d.ts +0 -15
  64. package/src/utils/fisheye.js +0 -60
  65. package/src/utils/html.js +0 -23
  66. package/src/utils/html.test.js +0 -13
  67. package/src/view/channel.js +0 -5
@@ -170,7 +170,18 @@ export default class UnitView extends ContainerView {
170
170
  : new AxisResolution(targetChannel);
171
171
  }
172
172
 
173
- view.resolutions[type][targetChannel].pushUnitView(this, channel);
173
+ // Looks silly, but keeps type checking happy
174
+ if (isPositionalChannel(channel)) {
175
+ view.resolutions[type][targetChannel].pushUnitView(
176
+ this,
177
+ channel
178
+ );
179
+ } else if (type == "scale") {
180
+ view.resolutions[type][targetChannel].pushUnitView(
181
+ this,
182
+ channel
183
+ );
184
+ }
174
185
  }
175
186
  }
176
187
 
@@ -223,16 +234,11 @@ export default class UnitView extends ContainerView {
223
234
  }
224
235
 
225
236
  const channelDef = this.mark.encoding[channel];
237
+ // TODO: Broken. Fix.
226
238
  if (!isChannelDefWithScale(channelDef)) {
227
239
  throw new Error("The channel has no scale, cannot get domain!");
228
240
  }
229
241
 
230
- const type = channelDef.type;
231
- if (!type) {
232
- throw new Error(`No data type for channel "${channel}"!`);
233
- // TODO: Support defaults
234
- }
235
-
236
242
  return channelDef;
237
243
  }
238
244
 
@@ -252,7 +258,7 @@ export default class UnitView extends ContainerView {
252
258
  channelDef.resolutionChannel ?? channel
253
259
  );
254
260
  return createDomain(
255
- channelDef.type,
261
+ channelDef.type ?? "nominal",
256
262
  // Chrom/pos must be linearized first
257
263
  scaleResolution.fromComplexInterval(specDomain)
258
264
  );
@@ -274,7 +280,7 @@ export default class UnitView extends ContainerView {
274
280
  */
275
281
  extractDataDomain(channel) {
276
282
  const channelDef = this._validateDomainQuery(channel);
277
- const type = channelDef.type;
283
+ const type = channelDef.type ?? "nominal"; // TODO: Should check that this is a channel without scale
278
284
 
279
285
  /** @param {Channel} channel */
280
286
  const extract = (channel) => {
package/src/view/view.js CHANGED
@@ -142,13 +142,10 @@ export default class View {
142
142
  */
143
143
  getSizeFromSpec() {
144
144
  /**
145
- *
146
145
  * @param {"width" | "height"} dimension
147
146
  * @return {SizeDef}
148
147
  */
149
148
  const handleSize = (dimension) => {
150
- /** @type {SizeDef} */
151
- let sizeDef;
152
149
  let value = this.spec[dimension];
153
150
 
154
151
  if (isStepSize(value)) {
@@ -172,22 +169,27 @@ export default class View {
172
169
  );
173
170
  }
174
171
 
172
+ // TODO: Type guards maybe?
173
+ const _scale =
174
+ /** @type {import("d3-scale").ScaleBand<any> | import("../genome/scaleLocus").ScaleLocus | import("../genome/scaleIndex").ScaleIndex} */ (
175
+ scale
176
+ );
177
+
175
178
  steps = bandSpace(
176
179
  steps,
177
- scale.paddingInner(),
178
- scale.paddingOuter()
180
+ _scale.paddingInner(),
181
+ _scale.paddingOuter()
179
182
  );
180
183
 
181
- sizeDef = { px: steps * stepSize, grow: 0 };
184
+ return { px: steps * stepSize, grow: 0 };
182
185
  } else {
183
186
  throw new Error(
184
187
  "Cannot use 'step' size with missing scale!"
185
188
  );
186
189
  }
187
190
  } else {
188
- sizeDef = (value && parseSizeDef(value)) || { px: 0, grow: 1 };
191
+ return (value && parseSizeDef(value)) ?? { px: 0, grow: 1 };
189
192
  }
190
- return sizeDef;
191
193
  };
192
194
 
193
195
  return this._cache(
@@ -123,8 +123,10 @@ export function isLayerSpec(spec) {
123
123
  export function isFacetSpec(spec) {
124
124
  return (
125
125
  "facet" in spec &&
126
+ // @ts-expect-error
126
127
  isObject(spec.facet) &&
127
128
  "spec" in spec &&
129
+ // @ts-expect-error
128
130
  isObject(spec.spec)
129
131
  );
130
132
  }
@@ -37,8 +37,8 @@ import { rollup } from "d3-array";
37
37
  * @typedef {import("../spec/root").RootSpec} RootSpec
38
38
  * @typedef {import("../spec/root").RootConfig} RootConfig
39
39
  *
40
- * @typedef {import("../spec/channel").FacetFieldDef} FacetFieldDef
41
40
  * @typedef {import("../spec/view").FacetMapping} FacetMapping
41
+ * @typedef {import("../spec/channel").FacetFieldDef} FacetFieldDef
42
42
  */
43
43
 
44
44
  /**
@@ -253,14 +253,14 @@ export async function initializeData(root, existingFlow) {
253
253
  * @param {View} view
254
254
  */
255
255
  export function findEncodedFields(view) {
256
- /** @type {{view: UnitView, channel: string, field: string, type: string}[]} */
256
+ /** @type {{view: UnitView, channel: import("../spec/channel").Channel, field: import("../spec/channel").Field, type: import("../spec/channel").Type}[]} */
257
257
  const fieldInfos = [];
258
258
 
259
259
  view.visit((view) => {
260
260
  if (view instanceof UnitView) {
261
261
  const encoding = view.getEncoding();
262
262
  for (const [channel, def] of Object.entries(encoding)) {
263
- if (isFieldDef(def)) {
263
+ if (isFieldDef(def) && "type" in def) {
264
264
  fieldInfos.push({
265
265
  view,
266
266
  channel,
@@ -292,7 +292,7 @@ async function loadExternalViewSpec(spec, baseUrl, viewContext) {
292
292
  const url = spec.import.url;
293
293
 
294
294
  const importedSpec = JSON.parse(
295
- await loader.load(url).catch((e) => {
295
+ await loader.load(url).catch((/** @type {Error} */ e) => {
296
296
  throw new Error(
297
297
  `Could not load imported view spec: ${url} \nReason: ${e.message}`
298
298
  );
package/src/options.d.ts DELETED
@@ -1,15 +0,0 @@
1
- import { TooltipHandler } from "./tooltip/tooltipHandler";
2
-
3
- export interface EmbedOptions {
4
- /**
5
- * A function that allows retrieval of named data sources.
6
- *
7
- * TODO: Support dynamic updates, i.e., pushing new data.
8
- */
9
- namedDataProvider?: (name: string) => any[];
10
-
11
- /**
12
- * Custom tooltip handlers. Use "default" to override the default handler
13
- */
14
- tooltipHandlers?: Record<string, TooltipHandler>;
15
- }
@@ -1,60 +0,0 @@
1
- /**
2
- * Based on:
3
- * fisheye d3-plugin (c) Mike Bostock
4
- * https://github.com/d3/d3-plugins/blob/master/fisheye/
5
- */
6
- export default function () {
7
- let radius = 200;
8
- let distortion = 2;
9
- let k0 = 1,
10
- k1 = 1;
11
- let focus = 0;
12
-
13
- /**
14
- *
15
- * @param {number} x
16
- */
17
- function fisheye(x) {
18
- const dx = x - focus;
19
- const dd = Math.abs(dx);
20
- if (!dd || dd >= radius) return x;
21
- const k = ((k0 * (1 - Math.exp(-dd * k1))) / dd) * 0.75 + 0.25;
22
- return focus + dx * k;
23
- }
24
-
25
- function rescale() {
26
- k0 = Math.exp(distortion);
27
- k0 = (k0 / (k0 - 1)) * radius;
28
- k1 = distortion / radius;
29
- return fisheye;
30
- }
31
-
32
- /**
33
- * @param {number} _
34
- */
35
- fisheye.radius = function (_) {
36
- if (!arguments.length) return radius;
37
- radius = +_;
38
- return rescale();
39
- };
40
-
41
- /**
42
- * @param {number} _
43
- */
44
- fisheye.distortion = function (_) {
45
- if (!arguments.length) return distortion;
46
- distortion = +_;
47
- return rescale();
48
- };
49
-
50
- /**
51
- * @param {number} _
52
- */
53
- fisheye.focus = function (_) {
54
- if (!arguments.length) return focus;
55
- focus = _;
56
- return fisheye;
57
- };
58
-
59
- return rescale();
60
- }
package/src/utils/html.js DELETED
@@ -1,23 +0,0 @@
1
- /* https://stackoverflow.com/a/41699140/1547896 */
2
-
3
- export function escapeHtml(str) {
4
- var map = {
5
- "&": "&amp;",
6
- "<": "&lt;",
7
- ">": "&gt;",
8
- '"': "&quot;",
9
- "'": "&#039;",
10
- };
11
- return str.replace(/[&<>"']/g, (m) => map[m]);
12
- }
13
-
14
- export function decodeHtml(str) {
15
- var map = {
16
- "&amp;": "&",
17
- "&lt;": "<",
18
- "&gt;": ">",
19
- "&quot;": '"',
20
- "&#039;": "'",
21
- };
22
- return str.replace(/&amp;|&lt;|&gt;|&quot;|&#039;/g, (m) => map[m]);
23
- }
@@ -1,13 +0,0 @@
1
- import * as html from "./html";
2
-
3
- test("Escape HTML", () => {
4
- expect(html.escapeHtml('< "x" & "y" >')).toEqual(
5
- "&lt; &quot;x&quot; &amp; &quot;y&quot; &gt;"
6
- );
7
- });
8
-
9
- test("Decode HTML", () => {
10
- expect(
11
- html.decodeHtml("&lt; &quot;x&quot; &amp; &quot;y&quot; &gt;")
12
- ).toEqual('< "x" & "y" >');
13
- });
@@ -1,5 +0,0 @@
1
- export default class Channel {
2
- constructor() {
3
- this.type = null; // x, y, etc
4
- }
5
- }