@jbrowse/plugin-hic 2.10.3 → 2.11.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.
@@ -8,17 +8,16 @@ const BaseAdapter_1 = require("@jbrowse/core/data_adapters/BaseAdapter");
8
8
  const rxjs_1 = require("@jbrowse/core/util/rxjs");
9
9
  const io_1 = require("@jbrowse/core/util/io");
10
10
  const hic_straw_1 = __importDefault(require("hic-straw"));
11
- // wraps generic-filehandle so the read function only takes a position and length
12
- // in some ways, generic-filehandle wishes it was just this but it has
11
+ // wraps generic-filehandle so the read function only takes a position and
12
+ // length in some ways, generic-filehandle wishes it was just this but it has
13
13
  // to adapt to the node.js fs promises API
14
14
  class GenericFilehandleWrapper {
15
15
  constructor(filehandle) {
16
16
  this.filehandle = filehandle;
17
17
  }
18
18
  async read(position, length) {
19
- const { buffer: b, bytesRead } = await this.filehandle.read(Buffer.allocUnsafe(length), 0, length, position);
20
- // xref https://stackoverflow.com/a/31394257/2129219
21
- return b.buffer.slice(b.byteOffset, b.byteOffset + bytesRead);
19
+ const { buffer } = await this.filehandle.read(Buffer.alloc(length), 0, length, position);
20
+ return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
22
21
  }
23
22
  }
24
23
  function openFilehandleWrapper(location, pluginManager) {
@@ -67,9 +66,9 @@ class HicAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
67
66
  const res = await this.getResolution(bpPerPx / (resolution || 1000), opts);
68
67
  statusCallback('Downloading .hic data');
69
68
  const records = await this.hic.getContactRecords('KR', { start, chr, end }, { start, chr, end }, 'BP', res);
70
- records.forEach(record => {
69
+ for (const record of records) {
71
70
  observer.next(record);
72
- });
71
+ }
73
72
  statusCallback('');
74
73
  observer.complete();
75
74
  }, opts.signal); // eslint-disable-line @typescript-eslint/no-explicit-any
@@ -10,20 +10,25 @@ const operators_1 = require("rxjs/operators");
10
10
  const configuration_1 = require("@jbrowse/core/configuration");
11
11
  const dataAdapterCache_1 = require("@jbrowse/core/data_adapters/dataAdapterCache");
12
12
  const colord_1 = require("@jbrowse/core/util/colord");
13
+ const rxjs_1 = require("rxjs");
14
+ const d3_scale_1 = require("d3-scale");
15
+ const d3_scale_chromatic_1 = require("d3-scale-chromatic");
16
+ const d3_interpolate_1 = require("d3-interpolate");
13
17
  class HicRenderer extends ServerSideRendererType_1.default {
14
18
  constructor() {
15
19
  super(...arguments);
16
20
  this.supportsSVG = true;
17
21
  }
18
22
  async makeImageData(ctx, props) {
19
- const { features, config, bpPerPx, signal, resolution, sessionId, adapterConfig, regions, } = props;
23
+ const { features, config, bpPerPx, signal, resolution, sessionId, adapterConfig, useLogScale, colorScheme, regions, } = props;
20
24
  const [region] = regions;
21
25
  const { dataAdapter } = await (0, dataAdapterCache_1.getAdapter)(this.pluginManager, sessionId, adapterConfig);
22
26
  const res = await dataAdapter.getResolution(bpPerPx / resolution);
27
+ const width = (region.end - region.start) / bpPerPx;
23
28
  const w = res / (bpPerPx * Math.sqrt(2));
24
29
  const baseColor = (0, colord_1.colord)((0, configuration_1.readConfObject)(config, 'baseColor'));
30
+ const offset = Math.floor(region.start / res);
25
31
  if (features.length) {
26
- const offset = features[0].bin1;
27
32
  let maxScore = 0;
28
33
  let minBin = 0;
29
34
  let maxBin = 0;
@@ -34,14 +39,34 @@ class HicRenderer extends ServerSideRendererType_1.default {
34
39
  maxBin = Math.max(Math.max(bin1, bin2), maxBin);
35
40
  }
36
41
  await (0, util_1.abortBreakPoint)(signal);
37
- function horizontallyFlip() {
42
+ const colorSchemes = {
43
+ juicebox: ['rgba(0,0,0,0)', 'red'],
44
+ fall: (0, d3_interpolate_1.interpolateRgbBasis)([
45
+ 'rgb(255, 255, 255)',
46
+ 'rgb(255, 255, 204)',
47
+ 'rgb(255, 237, 160)',
48
+ 'rgb(254, 217, 118)',
49
+ 'rgb(254, 178, 76)',
50
+ 'rgb(253, 141, 60)',
51
+ 'rgb(252, 78, 42)',
52
+ 'rgb(227, 26, 28)',
53
+ 'rgb(189, 0, 38)',
54
+ 'rgb(128, 0, 38)',
55
+ 'rgb(0, 0, 0)',
56
+ ]),
57
+ viridis: d3_scale_chromatic_1.interpolateViridis,
58
+ };
59
+ const m = useLogScale ? maxScore : maxScore / 20;
60
+ // @ts-expect-error
61
+ const x1 = colorSchemes[colorScheme] || colorSchemes.juicebox;
62
+ const scale = useLogScale
63
+ ? (0, d3_scale_1.scaleSequentialLog)(x1).domain([1, m])
64
+ : (0, d3_scale_1.scaleSequential)(x1).domain([0, m]);
65
+ ctx.save();
66
+ if (region.reversed === true) {
38
67
  ctx.scale(-1, 1);
39
- const width = (region.end - region.start) / bpPerPx;
40
68
  ctx.translate(-width, 0);
41
69
  }
42
- if (region.reversed === true) {
43
- horizontallyFlip();
44
- }
45
70
  ctx.rotate(-Math.PI / 4);
46
71
  let start = Date.now();
47
72
  for (const { bin1, bin2, counts } of features) {
@@ -49,6 +74,8 @@ class HicRenderer extends ServerSideRendererType_1.default {
49
74
  count: counts,
50
75
  maxScore,
51
76
  baseColor,
77
+ scale,
78
+ useLogScale,
52
79
  });
53
80
  ctx.fillRect((bin1 - offset) * w, (bin2 - offset) * w, w, w);
54
81
  if (+Date.now() - start > 400) {
@@ -56,6 +83,7 @@ class HicRenderer extends ServerSideRendererType_1.default {
56
83
  start = +Date.now();
57
84
  }
58
85
  }
86
+ ctx.restore();
59
87
  }
60
88
  }
61
89
  async render(renderProps) {
@@ -86,10 +114,9 @@ class HicRenderer extends ServerSideRendererType_1.default {
86
114
  async getFeatures(args) {
87
115
  const { regions, sessionId, adapterConfig } = args;
88
116
  const { dataAdapter } = await (0, dataAdapterCache_1.getAdapter)(this.pluginManager, sessionId, adapterConfig);
89
- const features = await dataAdapter
117
+ const features = await (0, rxjs_1.firstValueFrom)(dataAdapter
90
118
  .getFeatures(regions[0], args)
91
- .pipe((0, operators_1.toArray)())
92
- .toPromise();
119
+ .pipe((0, operators_1.toArray)()));
93
120
  // cast to any to avoid return-type conflict, because the
94
121
  // types of features returned by our getFeatures are quite
95
122
  // different from the base interface
@@ -21,8 +21,8 @@ const HicRenderer = (0, configuration_1.ConfigurationSchema)('HicRenderer', {
21
21
  color: {
22
22
  type: 'color',
23
23
  description: 'the color of each feature in a hic alignment',
24
- defaultValue: `jexl:colorString(hsl(alpha(baseColor,min(1,count/(maxScore/20)))))`,
25
- contextVariable: ['count', 'maxScore', 'baseColor'],
24
+ defaultValue: `jexl:interpolate(count,scale)`,
25
+ contextVariable: ['count', 'maxScore', 'baseColor', 'scale'],
26
26
  },
27
27
  /**
28
28
  * #slot
@@ -81,24 +81,19 @@ declare const _default: (configSchema: AnyConfigurationSchemaType) => import("mo
81
81
  };
82
82
  mouseover: {
83
83
  type: string;
84
- description: string;
84
+ description: string; /**
85
+ * #getter
86
+ */
85
87
  defaultValue: string;
86
88
  contextVariable: string[];
87
89
  };
88
90
  }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, "displayId">>;
89
91
  } & {
90
- /**
91
- * #property
92
- */
93
92
  type: import("mobx-state-tree").ISimpleType<"LinearHicDisplay">;
94
- /**
95
- * #property
96
- */
97
93
  configuration: AnyConfigurationSchemaType;
98
- /**
99
- * #property
100
- */
101
94
  resolution: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<number>, [undefined]>;
95
+ useLogScale: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
96
+ colorScheme: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>;
102
97
  }, {
103
98
  rendererTypeName: string;
104
99
  error: unknown;
@@ -111,11 +106,17 @@ declare const _default: (configSchema: AnyConfigurationSchemaType) => import("mo
111
106
  rpcDriverName: string | undefined;
112
107
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
113
108
  rendererTypeName: string;
114
- error: unknown;
109
+ error: unknown; /**
110
+ * #property
111
+ */
115
112
  message: string | undefined;
116
113
  } & import("mobx-state-tree").IStateTreeNode<import("mobx-state-tree").IModelType<{
117
- id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
118
- type: import("mobx-state-tree").ISimpleType<string>;
114
+ id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>; /**
115
+ * #getter
116
+ */
117
+ type: import("mobx-state-tree").ISimpleType<string>; /**
118
+ * #method
119
+ */
119
120
  rpcDriverName: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>;
120
121
  }, {
121
122
  rendererTypeName: string;
@@ -131,7 +132,9 @@ declare const _default: (configSchema: AnyConfigurationSchemaType) => import("mo
131
132
  type: string;
132
133
  rpcDriverName: string | undefined;
133
134
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
134
- rendererTypeName: string;
135
+ rendererTypeName: string; /**
136
+ * #action
137
+ */
135
138
  error: unknown;
136
139
  message: string | undefined;
137
140
  } & import("mobx-state-tree").IStateTreeNode<import("mobx-state-tree").IModelType<{
@@ -166,7 +169,9 @@ declare const _default: (configSchema: AnyConfigurationSchemaType) => import("mo
166
169
  setHeight(displayHeight: number): number;
167
170
  resizeHeight(distance: number): number;
168
171
  } & {
169
- featureDensityStatsP: Promise<import("@jbrowse/core/data_adapters/BaseAdapter").FeatureDensityStats> | undefined;
172
+ featureDensityStatsP: Promise<import("@jbrowse/core/data_adapters/BaseAdapter").FeatureDensityStats> | undefined; /**
173
+ * #getter
174
+ */
170
175
  featureDensityStats: import("@jbrowse/core/data_adapters/BaseAdapter").FeatureDensityStats | undefined;
171
176
  currStatsBpPerPx: number;
172
177
  } & {
@@ -241,11 +246,34 @@ declare const _default: (configSchema: AnyConfigurationSchemaType) => import("mo
241
246
  * #action
242
247
  */
243
248
  setResolution(n: number): void;
249
+ /**
250
+ * #action
251
+ */
252
+ setUseLogScale(f: boolean): void;
253
+ /**
254
+ * #action
255
+ */
256
+ setColorScheme(f?: string): void;
244
257
  } & {
245
258
  /**
246
259
  * #getter
247
260
  */
248
- trackMenuItems(): import("@jbrowse/core/ui").MenuItem[];
261
+ trackMenuItems(): (import("@jbrowse/core/ui").MenuDivider | import("@jbrowse/core/ui").MenuSubHeader | import("@jbrowse/core/ui").NormalMenuItem | import("@jbrowse/core/ui").CheckboxMenuItem | import("@jbrowse/core/ui").RadioMenuItem | import("@jbrowse/core/ui").SubMenuItem | {
262
+ label: string;
263
+ type: string;
264
+ checked: boolean;
265
+ onClick: () => void;
266
+ subMenu?: undefined;
267
+ } | {
268
+ label: string;
269
+ type: string;
270
+ subMenu: {
271
+ label: string;
272
+ onClick: () => void;
273
+ }[];
274
+ checked?: undefined;
275
+ onClick?: undefined;
276
+ })[];
249
277
  }, {
250
278
  type: string;
251
279
  } & Partial<import("mobx-state-tree/dist/internal").ExtractCFromProps<{
@@ -329,7 +357,9 @@ declare const _default: (configSchema: AnyConfigurationSchemaType) => import("mo
329
357
  };
330
358
  mouseover: {
331
359
  type: string;
332
- description: string;
360
+ description: string; /**
361
+ * #getter
362
+ */
333
363
  defaultValue: string;
334
364
  contextVariable: string[];
335
365
  };
@@ -23,6 +23,14 @@ exports.default = (configSchema) => mobx_state_tree_1.types
23
23
  * #property
24
24
  */
25
25
  resolution: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.number, 1),
26
+ /**
27
+ * #property
28
+ */
29
+ useLogScale: false,
30
+ /**
31
+ * #property
32
+ */
33
+ colorScheme: mobx_state_tree_1.types.maybe(mobx_state_tree_1.types.string),
26
34
  }))
27
35
  .views(self => {
28
36
  const { renderProps: superRenderProps } = self;
@@ -43,13 +51,21 @@ exports.default = (configSchema) => mobx_state_tree_1.types
43
51
  * #method
44
52
  */
45
53
  renderProps() {
46
- const config = self.rendererType.configSchema.create((0, configuration_1.getConf)(self, 'renderer') || {}, (0, mobx_state_tree_1.getEnv)(self));
54
+ const config = self.rendererType.configSchema.create({
55
+ ...(0, configuration_1.getConf)(self, 'renderer'),
56
+ // add specific jexl color callback when using pre-defined color schemes
57
+ ...(self.colorScheme
58
+ ? { color: 'jexl:interpolate(count,scale)' }
59
+ : {}),
60
+ }, (0, mobx_state_tree_1.getEnv)(self));
47
61
  return {
48
62
  ...superRenderProps(),
49
63
  config,
50
64
  rpcDriverName: self.rpcDriverName,
51
65
  displayModel: self,
52
66
  resolution: self.resolution,
67
+ useLogScale: self.useLogScale,
68
+ colorScheme: self.colorScheme,
53
69
  };
54
70
  },
55
71
  };
@@ -61,6 +77,18 @@ exports.default = (configSchema) => mobx_state_tree_1.types
61
77
  setResolution(n) {
62
78
  self.resolution = n;
63
79
  },
80
+ /**
81
+ * #action
82
+ */
83
+ setUseLogScale(f) {
84
+ self.useLogScale = f;
85
+ },
86
+ /**
87
+ * #action
88
+ */
89
+ setColorScheme(f) {
90
+ self.colorScheme = f;
91
+ },
64
92
  }))
65
93
  .views(self => {
66
94
  const { trackMenuItems: superTrackMenuItems } = self;
@@ -71,20 +99,44 @@ exports.default = (configSchema) => mobx_state_tree_1.types
71
99
  trackMenuItems() {
72
100
  return [
73
101
  ...superTrackMenuItems(),
102
+ {
103
+ label: 'Use log scale',
104
+ type: 'checkbox',
105
+ checked: self.useLogScale,
106
+ onClick: () => self.setUseLogScale(!self.useLogScale),
107
+ },
108
+ {
109
+ label: 'Color scheme',
110
+ type: 'subMenu',
111
+ subMenu: [
112
+ {
113
+ label: 'Fall',
114
+ onClick: () => self.setColorScheme('fall'),
115
+ },
116
+ {
117
+ label: 'Viridis',
118
+ onClick: () => self.setColorScheme('viridis'),
119
+ },
120
+ {
121
+ label: 'Juicebox',
122
+ onClick: () => self.setColorScheme('juicebox'),
123
+ },
124
+ {
125
+ label: 'Clear',
126
+ onClick: () => self.setColorScheme(undefined),
127
+ },
128
+ ],
129
+ },
74
130
  {
75
131
  label: 'Resolution',
76
132
  subMenu: [
77
133
  {
78
134
  label: 'Finer resolution',
79
- onClick: () => {
80
- self.setResolution(self.resolution * 2);
81
- },
135
+ onClick: () => self.setResolution(self.resolution * 2),
82
136
  },
83
137
  {
84
138
  label: 'Coarser resolution',
85
- onClick: () => {
86
- self.setResolution(self.resolution / 2);
87
- },
139
+ onClick: () => self.setResolution(self.resolution / 2),
88
140
  },
89
141
  ],
90
142
  },
package/dist/index.js CHANGED
@@ -5,11 +5,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const Plugin_1 = __importDefault(require("@jbrowse/core/Plugin"));
7
7
  const colord_1 = require("@jbrowse/core/util/colord");
8
+ const tracks_1 = require("@jbrowse/core/util/tracks");
9
+ // locals
8
10
  const HicRenderer_1 = __importDefault(require("./HicRenderer"));
9
11
  const HicTrack_1 = __importDefault(require("./HicTrack"));
10
12
  const LinearHicDisplay_1 = __importDefault(require("./LinearHicDisplay"));
11
13
  const HicAdapter_1 = __importDefault(require("./HicAdapter"));
12
- const tracks_1 = require("@jbrowse/core/util/tracks");
13
14
  class HicPlugin extends Plugin_1.default {
14
15
  constructor() {
15
16
  super(...arguments);
@@ -35,16 +36,15 @@ class HicPlugin extends Plugin_1.default {
35
36
  else if (adapterHint === adapterName) {
36
37
  return obj;
37
38
  }
38
- return adapterGuesser(file, index, adapterHint);
39
+ else {
40
+ return adapterGuesser(file, index, adapterHint);
41
+ }
39
42
  };
40
43
  });
41
44
  pluginManager.addToExtensionPoint('Core-guessTrackTypeForLocation', (trackTypeGuesser) => {
42
- return (adapterName) => {
43
- if (adapterName === 'HicAdapter') {
44
- return 'HicTrack';
45
- }
46
- return trackTypeGuesser(adapterName);
47
- };
45
+ return (adapterName) => adapterName === 'HicAdapter'
46
+ ? 'HicTrack'
47
+ : trackTypeGuesser(adapterName);
48
48
  });
49
49
  }
50
50
  configure(pluginManager) {
@@ -52,6 +52,7 @@ class HicPlugin extends Plugin_1.default {
52
52
  jexl.addFunction('alpha', (color, n) => color.alpha(n));
53
53
  jexl.addFunction('hsl', (color) => (0, colord_1.colord)(color.toHsl()));
54
54
  jexl.addFunction('colorString', (color) => color.toHex());
55
+ jexl.addFunction('interpolate', (count, scale) => scale(count));
55
56
  }
56
57
  }
57
58
  exports.default = HicPlugin;
@@ -2,17 +2,16 @@ import { BaseFeatureDataAdapter, } from '@jbrowse/core/data_adapters/BaseAdapter
2
2
  import { ObservableCreate } from '@jbrowse/core/util/rxjs';
3
3
  import { openLocation } from '@jbrowse/core/util/io';
4
4
  import HicStraw from 'hic-straw';
5
- // wraps generic-filehandle so the read function only takes a position and length
6
- // in some ways, generic-filehandle wishes it was just this but it has
5
+ // wraps generic-filehandle so the read function only takes a position and
6
+ // length in some ways, generic-filehandle wishes it was just this but it has
7
7
  // to adapt to the node.js fs promises API
8
8
  class GenericFilehandleWrapper {
9
9
  constructor(filehandle) {
10
10
  this.filehandle = filehandle;
11
11
  }
12
12
  async read(position, length) {
13
- const { buffer: b, bytesRead } = await this.filehandle.read(Buffer.allocUnsafe(length), 0, length, position);
14
- // xref https://stackoverflow.com/a/31394257/2129219
15
- return b.buffer.slice(b.byteOffset, b.byteOffset + bytesRead);
13
+ const { buffer } = await this.filehandle.read(Buffer.alloc(length), 0, length, position);
14
+ return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
16
15
  }
17
16
  }
18
17
  export function openFilehandleWrapper(location, pluginManager) {
@@ -60,9 +59,9 @@ export default class HicAdapter extends BaseFeatureDataAdapter {
60
59
  const res = await this.getResolution(bpPerPx / (resolution || 1000), opts);
61
60
  statusCallback('Downloading .hic data');
62
61
  const records = await this.hic.getContactRecords('KR', { start, chr, end }, { start, chr, end }, 'BP', res);
63
- records.forEach(record => {
62
+ for (const record of records) {
64
63
  observer.next(record);
65
- });
64
+ }
66
65
  statusCallback('');
67
66
  observer.complete();
68
67
  }, opts.signal); // eslint-disable-line @typescript-eslint/no-explicit-any
@@ -5,20 +5,25 @@ import { toArray } from 'rxjs/operators';
5
5
  import { readConfObject } from '@jbrowse/core/configuration';
6
6
  import { getAdapter } from '@jbrowse/core/data_adapters/dataAdapterCache';
7
7
  import { colord } from '@jbrowse/core/util/colord';
8
+ import { firstValueFrom } from 'rxjs';
9
+ import { scaleSequential, scaleSequentialLog } from 'd3-scale';
10
+ import { interpolateViridis } from 'd3-scale-chromatic';
11
+ import { interpolateRgbBasis } from 'd3-interpolate';
8
12
  export default class HicRenderer extends ServerSideRendererType {
9
13
  constructor() {
10
14
  super(...arguments);
11
15
  this.supportsSVG = true;
12
16
  }
13
17
  async makeImageData(ctx, props) {
14
- const { features, config, bpPerPx, signal, resolution, sessionId, adapterConfig, regions, } = props;
18
+ const { features, config, bpPerPx, signal, resolution, sessionId, adapterConfig, useLogScale, colorScheme, regions, } = props;
15
19
  const [region] = regions;
16
20
  const { dataAdapter } = await getAdapter(this.pluginManager, sessionId, adapterConfig);
17
21
  const res = await dataAdapter.getResolution(bpPerPx / resolution);
22
+ const width = (region.end - region.start) / bpPerPx;
18
23
  const w = res / (bpPerPx * Math.sqrt(2));
19
24
  const baseColor = colord(readConfObject(config, 'baseColor'));
25
+ const offset = Math.floor(region.start / res);
20
26
  if (features.length) {
21
- const offset = features[0].bin1;
22
27
  let maxScore = 0;
23
28
  let minBin = 0;
24
29
  let maxBin = 0;
@@ -29,14 +34,34 @@ export default class HicRenderer extends ServerSideRendererType {
29
34
  maxBin = Math.max(Math.max(bin1, bin2), maxBin);
30
35
  }
31
36
  await abortBreakPoint(signal);
32
- function horizontallyFlip() {
37
+ const colorSchemes = {
38
+ juicebox: ['rgba(0,0,0,0)', 'red'],
39
+ fall: interpolateRgbBasis([
40
+ 'rgb(255, 255, 255)',
41
+ 'rgb(255, 255, 204)',
42
+ 'rgb(255, 237, 160)',
43
+ 'rgb(254, 217, 118)',
44
+ 'rgb(254, 178, 76)',
45
+ 'rgb(253, 141, 60)',
46
+ 'rgb(252, 78, 42)',
47
+ 'rgb(227, 26, 28)',
48
+ 'rgb(189, 0, 38)',
49
+ 'rgb(128, 0, 38)',
50
+ 'rgb(0, 0, 0)',
51
+ ]),
52
+ viridis: interpolateViridis,
53
+ };
54
+ const m = useLogScale ? maxScore : maxScore / 20;
55
+ // @ts-expect-error
56
+ const x1 = colorSchemes[colorScheme] || colorSchemes.juicebox;
57
+ const scale = useLogScale
58
+ ? scaleSequentialLog(x1).domain([1, m])
59
+ : scaleSequential(x1).domain([0, m]);
60
+ ctx.save();
61
+ if (region.reversed === true) {
33
62
  ctx.scale(-1, 1);
34
- const width = (region.end - region.start) / bpPerPx;
35
63
  ctx.translate(-width, 0);
36
64
  }
37
- if (region.reversed === true) {
38
- horizontallyFlip();
39
- }
40
65
  ctx.rotate(-Math.PI / 4);
41
66
  let start = Date.now();
42
67
  for (const { bin1, bin2, counts } of features) {
@@ -44,6 +69,8 @@ export default class HicRenderer extends ServerSideRendererType {
44
69
  count: counts,
45
70
  maxScore,
46
71
  baseColor,
72
+ scale,
73
+ useLogScale,
47
74
  });
48
75
  ctx.fillRect((bin1 - offset) * w, (bin2 - offset) * w, w, w);
49
76
  if (+Date.now() - start > 400) {
@@ -51,6 +78,7 @@ export default class HicRenderer extends ServerSideRendererType {
51
78
  start = +Date.now();
52
79
  }
53
80
  }
81
+ ctx.restore();
54
82
  }
55
83
  }
56
84
  async render(renderProps) {
@@ -81,10 +109,9 @@ export default class HicRenderer extends ServerSideRendererType {
81
109
  async getFeatures(args) {
82
110
  const { regions, sessionId, adapterConfig } = args;
83
111
  const { dataAdapter } = await getAdapter(this.pluginManager, sessionId, adapterConfig);
84
- const features = await dataAdapter
112
+ const features = await firstValueFrom(dataAdapter
85
113
  .getFeatures(regions[0], args)
86
- .pipe(toArray())
87
- .toPromise();
114
+ .pipe(toArray()));
88
115
  // cast to any to avoid return-type conflict, because the
89
116
  // types of features returned by our getFeatures are quite
90
117
  // different from the base interface
@@ -19,8 +19,8 @@ const HicRenderer = ConfigurationSchema('HicRenderer', {
19
19
  color: {
20
20
  type: 'color',
21
21
  description: 'the color of each feature in a hic alignment',
22
- defaultValue: `jexl:colorString(hsl(alpha(baseColor,min(1,count/(maxScore/20)))))`,
23
- contextVariable: ['count', 'maxScore', 'baseColor'],
22
+ defaultValue: `jexl:interpolate(count,scale)`,
23
+ contextVariable: ['count', 'maxScore', 'baseColor', 'scale'],
24
24
  },
25
25
  /**
26
26
  * #slot
@@ -81,24 +81,19 @@ declare const _default: (configSchema: AnyConfigurationSchemaType) => import("mo
81
81
  };
82
82
  mouseover: {
83
83
  type: string;
84
- description: string;
84
+ description: string; /**
85
+ * #getter
86
+ */
85
87
  defaultValue: string;
86
88
  contextVariable: string[];
87
89
  };
88
90
  }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, "displayId">>;
89
91
  } & {
90
- /**
91
- * #property
92
- */
93
92
  type: import("mobx-state-tree").ISimpleType<"LinearHicDisplay">;
94
- /**
95
- * #property
96
- */
97
93
  configuration: AnyConfigurationSchemaType;
98
- /**
99
- * #property
100
- */
101
94
  resolution: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<number>, [undefined]>;
95
+ useLogScale: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
96
+ colorScheme: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>;
102
97
  }, {
103
98
  rendererTypeName: string;
104
99
  error: unknown;
@@ -111,11 +106,17 @@ declare const _default: (configSchema: AnyConfigurationSchemaType) => import("mo
111
106
  rpcDriverName: string | undefined;
112
107
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
113
108
  rendererTypeName: string;
114
- error: unknown;
109
+ error: unknown; /**
110
+ * #property
111
+ */
115
112
  message: string | undefined;
116
113
  } & import("mobx-state-tree").IStateTreeNode<import("mobx-state-tree").IModelType<{
117
- id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
118
- type: import("mobx-state-tree").ISimpleType<string>;
114
+ id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>; /**
115
+ * #getter
116
+ */
117
+ type: import("mobx-state-tree").ISimpleType<string>; /**
118
+ * #method
119
+ */
119
120
  rpcDriverName: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>;
120
121
  }, {
121
122
  rendererTypeName: string;
@@ -131,7 +132,9 @@ declare const _default: (configSchema: AnyConfigurationSchemaType) => import("mo
131
132
  type: string;
132
133
  rpcDriverName: string | undefined;
133
134
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
134
- rendererTypeName: string;
135
+ rendererTypeName: string; /**
136
+ * #action
137
+ */
135
138
  error: unknown;
136
139
  message: string | undefined;
137
140
  } & import("mobx-state-tree").IStateTreeNode<import("mobx-state-tree").IModelType<{
@@ -166,7 +169,9 @@ declare const _default: (configSchema: AnyConfigurationSchemaType) => import("mo
166
169
  setHeight(displayHeight: number): number;
167
170
  resizeHeight(distance: number): number;
168
171
  } & {
169
- featureDensityStatsP: Promise<import("@jbrowse/core/data_adapters/BaseAdapter").FeatureDensityStats> | undefined;
172
+ featureDensityStatsP: Promise<import("@jbrowse/core/data_adapters/BaseAdapter").FeatureDensityStats> | undefined; /**
173
+ * #getter
174
+ */
170
175
  featureDensityStats: import("@jbrowse/core/data_adapters/BaseAdapter").FeatureDensityStats | undefined;
171
176
  currStatsBpPerPx: number;
172
177
  } & {
@@ -241,11 +246,34 @@ declare const _default: (configSchema: AnyConfigurationSchemaType) => import("mo
241
246
  * #action
242
247
  */
243
248
  setResolution(n: number): void;
249
+ /**
250
+ * #action
251
+ */
252
+ setUseLogScale(f: boolean): void;
253
+ /**
254
+ * #action
255
+ */
256
+ setColorScheme(f?: string): void;
244
257
  } & {
245
258
  /**
246
259
  * #getter
247
260
  */
248
- trackMenuItems(): import("@jbrowse/core/ui").MenuItem[];
261
+ trackMenuItems(): (import("@jbrowse/core/ui").MenuDivider | import("@jbrowse/core/ui").MenuSubHeader | import("@jbrowse/core/ui").NormalMenuItem | import("@jbrowse/core/ui").CheckboxMenuItem | import("@jbrowse/core/ui").RadioMenuItem | import("@jbrowse/core/ui").SubMenuItem | {
262
+ label: string;
263
+ type: string;
264
+ checked: boolean;
265
+ onClick: () => void;
266
+ subMenu?: undefined;
267
+ } | {
268
+ label: string;
269
+ type: string;
270
+ subMenu: {
271
+ label: string;
272
+ onClick: () => void;
273
+ }[];
274
+ checked?: undefined;
275
+ onClick?: undefined;
276
+ })[];
249
277
  }, {
250
278
  type: string;
251
279
  } & Partial<import("mobx-state-tree/dist/internal").ExtractCFromProps<{
@@ -329,7 +357,9 @@ declare const _default: (configSchema: AnyConfigurationSchemaType) => import("mo
329
357
  };
330
358
  mouseover: {
331
359
  type: string;
332
- description: string;
360
+ description: string; /**
361
+ * #getter
362
+ */
333
363
  defaultValue: string;
334
364
  contextVariable: string[];
335
365
  };
@@ -21,6 +21,14 @@ export default (configSchema) => types
21
21
  * #property
22
22
  */
23
23
  resolution: types.optional(types.number, 1),
24
+ /**
25
+ * #property
26
+ */
27
+ useLogScale: false,
28
+ /**
29
+ * #property
30
+ */
31
+ colorScheme: types.maybe(types.string),
24
32
  }))
25
33
  .views(self => {
26
34
  const { renderProps: superRenderProps } = self;
@@ -41,13 +49,21 @@ export default (configSchema) => types
41
49
  * #method
42
50
  */
43
51
  renderProps() {
44
- const config = self.rendererType.configSchema.create(getConf(self, 'renderer') || {}, getEnv(self));
52
+ const config = self.rendererType.configSchema.create({
53
+ ...getConf(self, 'renderer'),
54
+ // add specific jexl color callback when using pre-defined color schemes
55
+ ...(self.colorScheme
56
+ ? { color: 'jexl:interpolate(count,scale)' }
57
+ : {}),
58
+ }, getEnv(self));
45
59
  return {
46
60
  ...superRenderProps(),
47
61
  config,
48
62
  rpcDriverName: self.rpcDriverName,
49
63
  displayModel: self,
50
64
  resolution: self.resolution,
65
+ useLogScale: self.useLogScale,
66
+ colorScheme: self.colorScheme,
51
67
  };
52
68
  },
53
69
  };
@@ -59,6 +75,18 @@ export default (configSchema) => types
59
75
  setResolution(n) {
60
76
  self.resolution = n;
61
77
  },
78
+ /**
79
+ * #action
80
+ */
81
+ setUseLogScale(f) {
82
+ self.useLogScale = f;
83
+ },
84
+ /**
85
+ * #action
86
+ */
87
+ setColorScheme(f) {
88
+ self.colorScheme = f;
89
+ },
62
90
  }))
63
91
  .views(self => {
64
92
  const { trackMenuItems: superTrackMenuItems } = self;
@@ -69,20 +97,44 @@ export default (configSchema) => types
69
97
  trackMenuItems() {
70
98
  return [
71
99
  ...superTrackMenuItems(),
100
+ {
101
+ label: 'Use log scale',
102
+ type: 'checkbox',
103
+ checked: self.useLogScale,
104
+ onClick: () => self.setUseLogScale(!self.useLogScale),
105
+ },
106
+ {
107
+ label: 'Color scheme',
108
+ type: 'subMenu',
109
+ subMenu: [
110
+ {
111
+ label: 'Fall',
112
+ onClick: () => self.setColorScheme('fall'),
113
+ },
114
+ {
115
+ label: 'Viridis',
116
+ onClick: () => self.setColorScheme('viridis'),
117
+ },
118
+ {
119
+ label: 'Juicebox',
120
+ onClick: () => self.setColorScheme('juicebox'),
121
+ },
122
+ {
123
+ label: 'Clear',
124
+ onClick: () => self.setColorScheme(undefined),
125
+ },
126
+ ],
127
+ },
72
128
  {
73
129
  label: 'Resolution',
74
130
  subMenu: [
75
131
  {
76
132
  label: 'Finer resolution',
77
- onClick: () => {
78
- self.setResolution(self.resolution * 2);
79
- },
133
+ onClick: () => self.setResolution(self.resolution * 2),
80
134
  },
81
135
  {
82
136
  label: 'Coarser resolution',
83
- onClick: () => {
84
- self.setResolution(self.resolution / 2);
85
- },
137
+ onClick: () => self.setResolution(self.resolution / 2),
86
138
  },
87
139
  ],
88
140
  },
package/esm/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import Plugin from '@jbrowse/core/Plugin';
2
2
  import { colord } from '@jbrowse/core/util/colord';
3
+ import { getFileName, } from '@jbrowse/core/util/tracks';
4
+ // locals
3
5
  import HicRendererF from './HicRenderer';
4
6
  import HicTrackF from './HicTrack';
5
7
  import LinearHicDisplayF from './LinearHicDisplay';
6
8
  import HicAdapterF from './HicAdapter';
7
- import { getFileName, } from '@jbrowse/core/util/tracks';
8
9
  export default class HicPlugin extends Plugin {
9
10
  constructor() {
10
11
  super(...arguments);
@@ -30,16 +31,15 @@ export default class HicPlugin extends Plugin {
30
31
  else if (adapterHint === adapterName) {
31
32
  return obj;
32
33
  }
33
- return adapterGuesser(file, index, adapterHint);
34
+ else {
35
+ return adapterGuesser(file, index, adapterHint);
36
+ }
34
37
  };
35
38
  });
36
39
  pluginManager.addToExtensionPoint('Core-guessTrackTypeForLocation', (trackTypeGuesser) => {
37
- return (adapterName) => {
38
- if (adapterName === 'HicAdapter') {
39
- return 'HicTrack';
40
- }
41
- return trackTypeGuesser(adapterName);
42
- };
40
+ return (adapterName) => adapterName === 'HicAdapter'
41
+ ? 'HicTrack'
42
+ : trackTypeGuesser(adapterName);
43
43
  });
44
44
  }
45
45
  configure(pluginManager) {
@@ -47,5 +47,6 @@ export default class HicPlugin extends Plugin {
47
47
  jexl.addFunction('alpha', (color, n) => color.alpha(n));
48
48
  jexl.addFunction('hsl', (color) => colord(color.toHsl()));
49
49
  jexl.addFunction('colorString', (color) => color.toHex());
50
+ jexl.addFunction('interpolate', (count, scale) => scale(count));
50
51
  }
51
52
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/plugin-hic",
3
- "version": "2.10.3",
3
+ "version": "2.11.0",
4
4
  "description": "JBrowse 2 hic adapters, tracks, etc.",
5
5
  "keywords": [
6
6
  "jbrowse",
@@ -36,6 +36,8 @@
36
36
  "clean": "rimraf dist esm *.tsbuildinfo"
37
37
  },
38
38
  "dependencies": {
39
+ "d3-interpolate": "^2.0.1",
40
+ "d3-scale-chromatic": "^2.0.0",
39
41
  "hic-straw": "^2.0.3"
40
42
  },
41
43
  "peerDependencies": {
@@ -54,5 +56,5 @@
54
56
  "publishConfig": {
55
57
  "access": "public"
56
58
  },
57
- "gitHead": "c8fc800cd17decd72b2e971c7a6add3b95214e72"
59
+ "gitHead": "3d43a820b9274a6160aa4dc15616147f390d9094"
58
60
  }