jbrowse-plugin-mafviewer 1.0.3 → 1.0.4

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
  },
@@ -36,27 +36,30 @@ function makeImageData({
36
36
  samples: { id: string; color?: string }[]
37
37
  rowHeight: number
38
38
  rowProportion: number
39
+ showAllLetters: boolean
39
40
  }
40
41
  }) {
41
42
  const {
42
43
  regions,
43
44
  bpPerPx,
44
45
  rowHeight,
46
+ showAllLetters,
45
47
  theme: configTheme,
46
48
  samples,
47
49
  rowProportion,
48
50
  } = renderArgs
49
51
  const [region] = regions
50
52
  const features = renderArgs.features as Map<string, Feature>
51
- const h = rowHeight
53
+ const h = rowHeight * rowProportion
52
54
  const theme = createJBrowseTheme(configTheme)
53
55
  const colorForBase = getColorBaseMap(theme)
54
56
  const contrastForBase = getContrastBaseMap(theme)
55
57
  const sampleToRowMap = new Map(samples.map((s, i) => [s.id, i]))
56
58
  const scale = 1 / bpPerPx
57
59
  const f = 0.4
58
- const h2 = h * rowProportion
59
- const offset = h2 / 2
60
+ const h2 = rowHeight / 2
61
+ const hp2 = h / 2
62
+ const offset = (rowHeight - h) / 2
60
63
 
61
64
  // sample as alignments
62
65
  ctx.font = 'bold 10px Courier New,monospace'
@@ -74,7 +77,7 @@ function makeImageData({
74
77
  throw new Error(`unknown sample encountered: ${sample}`)
75
78
  }
76
79
 
77
- const t = h * row
80
+ const t = rowHeight * row
78
81
 
79
82
  // gaps
80
83
  ctx.beginPath()
@@ -91,30 +94,32 @@ function makeImageData({
91
94
  }
92
95
  ctx.stroke()
93
96
 
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)
97
+ if (!showAllLetters) {
98
+ // matches
99
+ ctx.beginPath()
100
+ ctx.fillStyle = 'lightgrey'
101
+ for (let i = 0, o = 0; i < alignment.length; i++) {
102
+ if (seq[i] !== '-') {
103
+ const c = alignment[i]
104
+ const l = leftPx + scale * o
105
+ if (seq[i] === c && c !== '-') {
106
+ ctx.rect(l, offset + t, scale + f, h)
107
+ }
108
+ o++
103
109
  }
104
- o++
105
110
  }
111
+ ctx.fill()
106
112
  }
107
- ctx.fill()
108
113
 
109
114
  // mismatches
110
115
  for (let i = 0, o = 0; i < alignment.length; i++) {
111
116
  const c = alignment[i]
112
117
  if (seq[i] !== '-') {
113
- if (seq[i] !== c && c !== '-') {
118
+ if ((showAllLetters || seq[i] !== c) && c !== '-') {
114
119
  const l = leftPx + scale * o
115
120
  ctx.fillStyle =
116
- colorForBase[c as keyof typeof colorForBase] ?? 'purple'
117
- ctx.fillRect(l, offset + t, scale + f, h2)
121
+ colorForBase[c as keyof typeof colorForBase] ?? 'black'
122
+ ctx.fillRect(l, offset + t, scale + f, h)
118
123
  }
119
124
  o++
120
125
  }
@@ -128,9 +133,9 @@ function makeImageData({
128
133
  const l = leftPx + scale * o
129
134
  const offset = (scale - charSize.w) / 2 + 1
130
135
  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)
136
+ if ((showAllLetters || seq[i] !== c) && c !== '-') {
137
+ ctx.fillStyle = contrastForBase[c] ?? 'white'
138
+ ctx.fillText(origAlignment[i], l + offset, hp2 + t + 3)
134
139
  }
135
140
  o++
136
141
  }
@@ -154,7 +159,7 @@ function makeImageData({
154
159
  throw new Error(`unknown sample encountered: ${sample}`)
155
160
  }
156
161
 
157
- const t = h * row
162
+ const t = rowHeight * row
158
163
 
159
164
  ctx.beginPath()
160
165
  ctx.fillStyle = 'purple'
@@ -166,11 +171,11 @@ function makeImageData({
166
171
  }
167
172
  i++
168
173
  }
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)
174
+ if (ins.length > 0) {
175
+ const l = leftPx + scale * o - 1
176
+ ctx.rect(l, offset + t + 1, 1, h - 1)
177
+ ctx.rect(l - 2, offset + t, 5, 1)
178
+ ctx.rect(l - 2, offset + t + h - 1, 5, 1)
174
179
  }
175
180
  o++
176
181
  }
@@ -199,7 +204,7 @@ export default class LinearMafRenderer extends FeatureRendererType {
199
204
  ) {
200
205
  const { regions, bpPerPx, samples, rowHeight } = renderProps
201
206
  const [region] = regions
202
- const height = samples.length * rowHeight
207
+ const height = samples.length * rowHeight + 100
203
208
  const width = (region.end - region.start) / bpPerPx
204
209
  const features = await this.getFeatures(renderProps)
205
210
  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,19 +58,8 @@ 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
64
  const [org, chr] = ad[0].split('.')
76
65