jbrowse-plugin-mafviewer 1.0.3 → 1.0.5

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.
@@ -48,6 +48,10 @@ export default function stateModelFactory(
48
48
  * #property
49
49
  */
50
50
  rowProportion: 0.8,
51
+ /**
52
+ * #property
53
+ */
54
+ showAllLetters: false,
51
55
  }),
52
56
  )
53
57
  .volatile(() => ({
@@ -66,6 +70,12 @@ export default function stateModelFactory(
66
70
  setRowProportion(n: number) {
67
71
  self.rowProportion = n
68
72
  },
73
+ /**
74
+ * #action
75
+ */
76
+ setShowAllLetters(f: boolean) {
77
+ self.showAllLetters = f
78
+ },
69
79
  }))
70
80
  .views(self => ({
71
81
  /**
@@ -75,11 +85,9 @@ export default function stateModelFactory(
75
85
  const r = self.adapterConfig.samples as
76
86
  | string[]
77
87
  | { id: string; label: string; color?: string }[]
78
- if (isStrs(r)) {
79
- return r.map(elt => ({ id: elt, label: elt, color: undefined }))
80
- } else {
81
- return r
82
- }
88
+ return isStrs(r)
89
+ ? r.map(elt => ({ id: elt, label: elt, color: undefined }))
90
+ : r
83
91
  },
84
92
 
85
93
  /**
@@ -114,12 +122,20 @@ export default function stateModelFactory(
114
122
  * #method
115
123
  */
116
124
  renderProps() {
125
+ const {
126
+ showAllLetters,
127
+ rendererConfig,
128
+ samples,
129
+ rowHeight,
130
+ rowProportion,
131
+ } = self
117
132
  return {
118
133
  ...superRenderProps(),
119
- config: self.rendererConfig,
120
- samples: self.samples,
121
- rowHeight: self.rowHeight,
122
- rowProportion: self.rowProportion,
134
+ config: rendererConfig,
135
+ samples,
136
+ rowHeight,
137
+ rowProportion,
138
+ showAllLetters,
123
139
  }
124
140
  },
125
141
  /**
@@ -137,17 +153,26 @@ export default function stateModelFactory(
137
153
  ])
138
154
  },
139
155
  },
156
+ {
157
+ label: 'Show all letters',
158
+ type: 'checkbox',
159
+ checked: self.showAllLetters,
160
+ onClick: () => {
161
+ self.setShowAllLetters(!self.showAllLetters)
162
+ },
163
+ },
140
164
  ]
141
165
  },
142
166
  }
143
167
  })
144
168
  .actions(self => {
169
+ // eslint-disable-next-line @typescript-eslint/unbound-method
145
170
  const { renderSvg: superRenderSvg } = self
146
171
  return {
147
172
  /**
148
173
  * #action
149
174
  */
150
- async renderSvg(opts: ExportSvgDisplayOptions): Promise<any> {
175
+ async renderSvg(opts: ExportSvgDisplayOptions) {
151
176
  const { renderSvg } = await import('./renderSvg')
152
177
  return renderSvg(self, opts, superRenderSvg)
153
178
  },
@@ -18,6 +18,13 @@ export function getContrastBaseMap(theme: Theme) {
18
18
  )
19
19
  }
20
20
 
21
+ interface RenderArgs extends RenderArgsDeserialized {
22
+ samples: { id: string; color?: string }[]
23
+ rowHeight: number
24
+ rowProportion: number
25
+ showAllLetters: boolean
26
+ }
27
+
21
28
  export function getColorBaseMap(theme: Theme) {
22
29
  const { bases } = theme.palette
23
30
  return {
@@ -32,31 +39,29 @@ function makeImageData({
32
39
  renderArgs,
33
40
  }: {
34
41
  ctx: CanvasRenderingContext2D
35
- renderArgs: RenderArgsDeserialized & {
36
- samples: { id: string; color?: string }[]
37
- rowHeight: number
38
- rowProportion: number
39
- }
42
+ renderArgs: RenderArgs & { features: Map<string, Feature> }
40
43
  }) {
41
44
  const {
42
45
  regions,
43
46
  bpPerPx,
44
47
  rowHeight,
48
+ showAllLetters,
45
49
  theme: configTheme,
46
50
  samples,
47
51
  rowProportion,
52
+ features,
48
53
  } = renderArgs
49
54
  const [region] = regions
50
- const features = renderArgs.features as Map<string, Feature>
51
- const h = rowHeight
55
+ const h = rowHeight * rowProportion
52
56
  const theme = createJBrowseTheme(configTheme)
53
57
  const colorForBase = getColorBaseMap(theme)
54
58
  const contrastForBase = getContrastBaseMap(theme)
55
59
  const sampleToRowMap = new Map(samples.map((s, i) => [s.id, i]))
56
60
  const scale = 1 / bpPerPx
57
61
  const f = 0.4
58
- const h2 = h * rowProportion
59
- const offset = h2 / 2
62
+ const h2 = rowHeight / 2
63
+ const hp2 = h / 2
64
+ const offset = (rowHeight - h) / 2
60
65
 
61
66
  // sample as alignments
62
67
  ctx.font = 'bold 10px Courier New,monospace'
@@ -74,7 +79,7 @@ function makeImageData({
74
79
  throw new Error(`unknown sample encountered: ${sample}`)
75
80
  }
76
81
 
77
- const t = h * row
82
+ const t = rowHeight * row
78
83
 
79
84
  // gaps
80
85
  ctx.beginPath()
@@ -91,30 +96,32 @@ function makeImageData({
91
96
  }
92
97
  ctx.stroke()
93
98
 
94
- // matches
95
- ctx.beginPath()
96
- ctx.fillStyle = 'lightgrey'
97
- for (let i = 0, o = 0; i < alignment.length; i++) {
98
- if (seq[i] !== '-') {
99
- const c = alignment[i]
100
- const l = leftPx + scale * o
101
- if (seq[i] === c && c !== '-') {
102
- ctx.rect(l, offset + t, scale + f, h2)
99
+ if (!showAllLetters) {
100
+ // matches
101
+ ctx.beginPath()
102
+ ctx.fillStyle = 'lightgrey'
103
+ for (let i = 0, o = 0; i < alignment.length; i++) {
104
+ if (seq[i] !== '-') {
105
+ const c = alignment[i]
106
+ const l = leftPx + scale * o
107
+ if (seq[i] === c && c !== '-') {
108
+ ctx.rect(l, offset + t, scale + f, h)
109
+ }
110
+ o++
103
111
  }
104
- o++
105
112
  }
113
+ ctx.fill()
106
114
  }
107
- ctx.fill()
108
115
 
109
116
  // mismatches
110
117
  for (let i = 0, o = 0; i < alignment.length; i++) {
111
118
  const c = alignment[i]
112
119
  if (seq[i] !== '-') {
113
- if (seq[i] !== c && c !== '-') {
120
+ if ((showAllLetters || seq[i] !== c) && c !== '-') {
114
121
  const l = leftPx + scale * o
115
122
  ctx.fillStyle =
116
- colorForBase[c as keyof typeof colorForBase] ?? 'purple'
117
- ctx.fillRect(l, offset + t, scale + f, h2)
123
+ colorForBase[c as keyof typeof colorForBase] ?? 'black'
124
+ ctx.fillRect(l, offset + t, scale + f, h)
118
125
  }
119
126
  o++
120
127
  }
@@ -128,9 +135,9 @@ function makeImageData({
128
135
  const l = leftPx + scale * o
129
136
  const offset = (scale - charSize.w) / 2 + 1
130
137
  const c = alignment[i]
131
- if (seq[i] !== c && c !== '-') {
132
- ctx.fillStyle = contrastForBase[c] ?? 'black'
133
- ctx.fillText(origAlignment[i], l + offset, h2 + t + 3)
138
+ if ((showAllLetters || seq[i] !== c) && c !== '-') {
139
+ ctx.fillStyle = contrastForBase[c] ?? 'white'
140
+ ctx.fillText(origAlignment[i], l + offset, hp2 + t + 3)
134
141
  }
135
142
  o++
136
143
  }
@@ -154,7 +161,7 @@ function makeImageData({
154
161
  throw new Error(`unknown sample encountered: ${sample}`)
155
162
  }
156
163
 
157
- const t = h * row
164
+ const t = rowHeight * row
158
165
 
159
166
  ctx.beginPath()
160
167
  ctx.fillStyle = 'purple'
@@ -166,11 +173,11 @@ function makeImageData({
166
173
  }
167
174
  i++
168
175
  }
169
- if (ins.length) {
170
- const l = leftPx + scale * o - 2
171
- ctx.rect(l, offset + t, 2, h2)
172
- ctx.rect(l - 2, offset + t, 6, 1)
173
- ctx.rect(l - 2, offset + t + h2, 6, 1)
176
+ if (ins.length > 0) {
177
+ const l = leftPx + scale * o - 1
178
+ ctx.rect(l, offset + t + 1, 1, h - 1)
179
+ ctx.rect(l - 2, offset + t, 5, 1)
180
+ ctx.rect(l - 2, offset + t + h - 1, 5, 1)
174
181
  }
175
182
  o++
176
183
  }
@@ -190,16 +197,10 @@ export default class LinearMafRenderer extends FeatureRendererType {
190
197
  end: Math.ceil(end + bpExpansion),
191
198
  }
192
199
  }
193
- async render(
194
- renderProps: RenderArgsDeserialized & {
195
- samples: { id: string; color?: string }[]
196
- rowHeight: number
197
- rowProportion: number
198
- },
199
- ) {
200
+ async render(renderProps: RenderArgs) {
200
201
  const { regions, bpPerPx, samples, rowHeight } = renderProps
201
202
  const [region] = regions
202
- const height = samples.length * rowHeight
203
+ const height = samples.length * rowHeight + 100
203
204
  const width = (region.end - region.start) / bpPerPx
204
205
  const features = await this.getFeatures(renderProps)
205
206
  const res = await renderToAbstractCanvas(width, height, renderProps, ctx =>
@@ -2,7 +2,10 @@ import { PrerenderedCanvas } from '@jbrowse/core/ui'
2
2
  import { observer } from 'mobx-react'
3
3
  import React from 'react'
4
4
 
5
- const LinearMafRendering = observer(function (props: any) {
5
+ const LinearMafRendering = observer(function (props: {
6
+ width: number
7
+ height: number
8
+ }) {
6
9
  return <PrerenderedCanvas {...props} />
7
10
  })
8
11
 
@@ -43,6 +43,7 @@ export default function MultiMAFWidget({ model }: { model: AddTrackModel }) {
43
43
  const [error, setError] = useState<unknown>()
44
44
  const [trackName, setTrackName] = useState('MAF track')
45
45
  const [choice, setChoice] = useState('BigMafAdapter')
46
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
47
  const rootModel = getRoot<any>(model)
47
48
  return (
48
49
  <Paper className={classes.paper}>
@@ -58,24 +58,15 @@ export default class BigMafAdapter extends BaseFeatureDataAdapter {
58
58
  const data = (feature.get('field5') as string).split(',')
59
59
  const alignments = {} as Record<string, OrganismRecord>
60
60
  const alns = data.map(elt => elt.split(':')[5])
61
- // const aln = alns[0]
62
- // const alns2 = data.map(() => '')
63
- // remove extraneous data in other alignments
64
- // reason being: cannot represent missing data in main species that are in others)
65
- // for (let i = 0; i < aln.length; i++) {
66
- // if (aln[i] !== '-') {
67
- // for (let j = 0; j < data.length; j++) {
68
- // alns2[j] += alns[j][i]
69
- // }
70
- // }
71
- // }
72
- for (let j = 0; j < data.length; j++) {
73
- const elt = data[j]
61
+
62
+ for (const [j, elt] of data.entries()) {
74
63
  const ad = elt.split(':')
75
- const [org, chr] = ad[0].split('.')
64
+ const idx = ad[0].lastIndexOf('.')
65
+ const org = ad[0].slice(0, idx)
66
+ const last = ad[0].slice(idx + 1)
76
67
 
77
68
  alignments[org] = {
78
- chr,
69
+ chr: last,
79
70
  start: +ad[1],
80
71
  srcSize: +ad[2],
81
72
  strand: ad[3] === '-' ? -1 : 1,