jbrowse-plugin-mafviewer 1.1.4 → 1.2.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.
- package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.d.ts +5 -4
- package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js +103 -82
- package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js.map +1 -1
- package/dist/BgzipTaffyAdapter/util.d.ts +1 -0
- package/dist/BgzipTaffyAdapter/util.js +22 -0
- package/dist/BgzipTaffyAdapter/util.js.map +1 -0
- package/dist/BigMafAdapter/BigMafAdapter.d.ts +3 -2
- package/dist/BigMafAdapter/BigMafAdapter.js +4 -3
- package/dist/BigMafAdapter/BigMafAdapter.js.map +1 -1
- package/dist/LinearMafDisplay/components/ColorLegend.js +1 -1
- package/dist/LinearMafDisplay/components/ColorLegend.js.map +1 -1
- package/dist/LinearMafDisplay/components/ReactComponent.js +1 -1
- package/dist/LinearMafDisplay/components/ReactComponent.js.map +1 -1
- package/dist/LinearMafDisplay/components/SvgWrapper.js +2 -2
- package/dist/LinearMafDisplay/components/SvgWrapper.js.map +1 -1
- package/dist/LinearMafDisplay/components/YScaleBars.js +2 -2
- package/dist/LinearMafDisplay/components/YScaleBars.js.map +1 -1
- package/dist/LinearMafDisplay/renderSvg.d.ts +2 -2
- package/dist/LinearMafDisplay/renderSvg.js +0 -1
- package/dist/LinearMafDisplay/renderSvg.js.map +1 -1
- package/dist/LinearMafDisplay/stateModel.d.ts +19 -17
- package/dist/LinearMafDisplay/stateModel.js +17 -10
- package/dist/LinearMafDisplay/stateModel.js.map +1 -1
- package/dist/LinearMafRenderer/LinearMafRenderer.d.ts +8 -4
- package/dist/LinearMafRenderer/LinearMafRenderer.js +14 -148
- package/dist/LinearMafRenderer/LinearMafRenderer.js.map +1 -1
- package/dist/LinearMafRenderer/configSchema.d.ts +6 -1
- package/dist/LinearMafRenderer/configSchema.js +6 -1
- package/dist/LinearMafRenderer/configSchema.js.map +1 -1
- package/dist/LinearMafRenderer/makeImageData.d.ts +20 -0
- package/dist/LinearMafRenderer/makeImageData.js +144 -0
- package/dist/LinearMafRenderer/makeImageData.js.map +1 -0
- package/dist/LinearMafRenderer/util.d.ts +6 -1
- package/dist/LinearMafRenderer/util.js +17 -0
- package/dist/LinearMafRenderer/util.js.map +1 -1
- package/dist/MafAddTrackWorkflow/AddTrackWorkflow.d.ts +1 -1
- package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js.map +1 -1
- package/dist/MafTabixAdapter/MafTabixAdapter.d.ts +9 -6
- package/dist/MafTabixAdapter/MafTabixAdapter.js +74 -56
- package/dist/MafTabixAdapter/MafTabixAdapter.js.map +1 -1
- package/dist/MafTabixAdapter/configSchema.d.ts +7 -0
- package/dist/MafTabixAdapter/configSchema.js +7 -0
- package/dist/MafTabixAdapter/configSchema.js.map +1 -1
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +7 -8
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +4 -4
- package/package.json +2 -2
- package/src/BgzipTaffyAdapter/BgzipTaffyAdapter.ts +123 -86
- package/src/BgzipTaffyAdapter/util.ts +25 -0
- package/src/BigMafAdapter/BigMafAdapter.ts +10 -4
- package/src/LinearMafDisplay/components/ColorLegend.tsx +1 -2
- package/src/LinearMafDisplay/components/ReactComponent.tsx +1 -1
- package/src/LinearMafDisplay/components/SvgWrapper.tsx +2 -2
- package/src/LinearMafDisplay/components/YScaleBars.tsx +3 -2
- package/src/LinearMafDisplay/renderSvg.tsx +5 -5
- package/src/LinearMafDisplay/stateModel.ts +30 -24
- package/src/LinearMafRenderer/LinearMafRenderer.ts +21 -176
- package/src/LinearMafRenderer/configSchema.ts +6 -1
- package/src/LinearMafRenderer/makeImageData.ts +211 -0
- package/src/LinearMafRenderer/util.ts +28 -1
- package/src/MafAddTrackWorkflow/AddTrackWorkflow.tsx +2 -1
- package/src/MafTabixAdapter/MafTabixAdapter.ts +92 -55
- package/src/MafTabixAdapter/configSchema.ts +7 -0
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { FeatureRendererType } from '@jbrowse/core/pluggableElementTypes'
|
|
2
2
|
import { RenderArgsDeserialized } from '@jbrowse/core/pluggableElementTypes/renderers/BoxRendererType'
|
|
3
|
-
import { createJBrowseTheme } from '@jbrowse/core/ui'
|
|
4
3
|
import {
|
|
5
|
-
Feature,
|
|
6
4
|
Region,
|
|
7
|
-
featureSpanPx,
|
|
8
5
|
renderToAbstractCanvas,
|
|
6
|
+
updateStatus,
|
|
9
7
|
} from '@jbrowse/core/util'
|
|
10
8
|
|
|
11
|
-
import {
|
|
9
|
+
import { makeImageData } from './makeImageData'
|
|
12
10
|
|
|
13
11
|
interface Sample {
|
|
14
12
|
id: string
|
|
@@ -20,172 +18,9 @@ interface RenderArgs extends RenderArgsDeserialized {
|
|
|
20
18
|
rowProportion: number
|
|
21
19
|
showAllLetters: boolean
|
|
22
20
|
mismatchRendering: boolean
|
|
21
|
+
statusCallback?: (arg: string) => void
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
function makeImageData({
|
|
26
|
-
ctx,
|
|
27
|
-
renderArgs,
|
|
28
|
-
}: {
|
|
29
|
-
ctx: CanvasRenderingContext2D
|
|
30
|
-
renderArgs: RenderArgs & { features: Map<string, Feature> }
|
|
31
|
-
}) {
|
|
32
|
-
const {
|
|
33
|
-
regions,
|
|
34
|
-
bpPerPx,
|
|
35
|
-
rowHeight,
|
|
36
|
-
showAllLetters,
|
|
37
|
-
theme: configTheme,
|
|
38
|
-
mismatchRendering,
|
|
39
|
-
samples,
|
|
40
|
-
rowProportion,
|
|
41
|
-
features,
|
|
42
|
-
} = renderArgs
|
|
43
|
-
const region = regions[0]!
|
|
44
|
-
const h = rowHeight * rowProportion
|
|
45
|
-
const theme = createJBrowseTheme(configTheme)
|
|
46
|
-
const colorForBase = getColorBaseMap(theme)
|
|
47
|
-
const contrastForBase = getContrastBaseMap(theme)
|
|
48
|
-
const sampleToRowMap = new Map(samples.map((s, i) => [s.id, i]))
|
|
49
|
-
const scale = 1 / bpPerPx
|
|
50
|
-
const f = 0.4
|
|
51
|
-
const h2 = rowHeight / 2
|
|
52
|
-
const hp2 = h / 2
|
|
53
|
-
const offset = (rowHeight - h) / 2
|
|
54
|
-
|
|
55
|
-
// sample as alignments
|
|
56
|
-
ctx.font = 'bold 10px Courier New,monospace'
|
|
57
|
-
|
|
58
|
-
for (const feature of features.values()) {
|
|
59
|
-
const [leftPx] = featureSpanPx(feature, region, bpPerPx)
|
|
60
|
-
const vals = feature.get('alignments') as Record<string, { data: string }>
|
|
61
|
-
const seq = feature.get('seq').toLowerCase()
|
|
62
|
-
for (const [sample, val] of Object.entries(vals)) {
|
|
63
|
-
const origAlignment = val.data
|
|
64
|
-
const alignment = origAlignment.toLowerCase()
|
|
65
|
-
|
|
66
|
-
const row = sampleToRowMap.get(sample)
|
|
67
|
-
if (row === undefined) {
|
|
68
|
-
continue
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const t = rowHeight * row
|
|
72
|
-
|
|
73
|
-
// gaps
|
|
74
|
-
ctx.beginPath()
|
|
75
|
-
ctx.fillStyle = 'black'
|
|
76
|
-
for (let i = 0, o = 0; i < alignment.length; i++) {
|
|
77
|
-
if (seq[i] !== '-') {
|
|
78
|
-
if (alignment[i] === '-') {
|
|
79
|
-
const l = leftPx + scale * o
|
|
80
|
-
ctx.moveTo(l, t + h2)
|
|
81
|
-
ctx.lineTo(l + scale + f, t + h2)
|
|
82
|
-
}
|
|
83
|
-
o++
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
ctx.stroke()
|
|
87
|
-
|
|
88
|
-
if (!showAllLetters) {
|
|
89
|
-
// matches
|
|
90
|
-
ctx.beginPath()
|
|
91
|
-
ctx.fillStyle = 'lightgrey'
|
|
92
|
-
for (let i = 0, o = 0; i < alignment.length; i++) {
|
|
93
|
-
if (seq[i] !== '-') {
|
|
94
|
-
const c = alignment[i]
|
|
95
|
-
const l = leftPx + scale * o
|
|
96
|
-
if (seq[i] === c && c !== '-') {
|
|
97
|
-
ctx.rect(l, offset + t, scale + f, h)
|
|
98
|
-
}
|
|
99
|
-
o++
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
ctx.fill()
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// mismatches
|
|
106
|
-
for (let i = 0, o = 0; i < alignment.length; i++) {
|
|
107
|
-
const c = alignment[i]
|
|
108
|
-
if (seq[i] !== '-') {
|
|
109
|
-
if (c !== '-') {
|
|
110
|
-
const l = leftPx + scale * o
|
|
111
|
-
if (seq[i] !== c && c !== ' ') {
|
|
112
|
-
ctx.fillStyle = mismatchRendering
|
|
113
|
-
? // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
114
|
-
(colorForBase[c as keyof typeof colorForBase] ?? 'black')
|
|
115
|
-
: 'orange'
|
|
116
|
-
ctx.fillRect(l, offset + t, scale + f, h)
|
|
117
|
-
} else if (showAllLetters) {
|
|
118
|
-
ctx.fillStyle = mismatchRendering
|
|
119
|
-
? // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
120
|
-
(colorForBase[c as keyof typeof colorForBase] ?? 'black')
|
|
121
|
-
: 'lightblue'
|
|
122
|
-
ctx.fillRect(l, offset + t, scale + f, h)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
o++
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// font
|
|
130
|
-
const charSizeW = 10
|
|
131
|
-
if (scale >= charSizeW) {
|
|
132
|
-
for (let i = 0, o = 0; i < alignment.length; i++) {
|
|
133
|
-
if (seq[i] !== '-') {
|
|
134
|
-
const l = leftPx + scale * o
|
|
135
|
-
const offset = (scale - charSizeW) / 2 + 1
|
|
136
|
-
const c = alignment[i]!
|
|
137
|
-
if ((showAllLetters || seq[i] !== c) && c !== '-') {
|
|
138
|
-
ctx.fillStyle = mismatchRendering
|
|
139
|
-
? (contrastForBase[c] ?? 'white')
|
|
140
|
-
: 'black'
|
|
141
|
-
ctx.fillText(origAlignment[i] || '', l + offset, hp2 + t + 3)
|
|
142
|
-
}
|
|
143
|
-
o++
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// second pass for insertions, has slightly improved look since the
|
|
151
|
-
// insertions are always 'on top' of the other features
|
|
152
|
-
for (const feature of features.values()) {
|
|
153
|
-
const [leftPx] = featureSpanPx(feature, region, bpPerPx)
|
|
154
|
-
const vals = feature.get('alignments') as Record<string, { data: string }>
|
|
155
|
-
const seq = feature.get('seq').toLowerCase()
|
|
156
|
-
|
|
157
|
-
for (const [sample, val] of Object.entries(vals)) {
|
|
158
|
-
const origAlignment = val.data
|
|
159
|
-
const alignment = origAlignment.toLowerCase()
|
|
160
|
-
const row = sampleToRowMap.get(sample)
|
|
161
|
-
if (row === undefined) {
|
|
162
|
-
continue
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const t = rowHeight * row
|
|
166
|
-
|
|
167
|
-
ctx.beginPath()
|
|
168
|
-
ctx.fillStyle = 'purple'
|
|
169
|
-
for (let i = 0, o = 0; i < alignment.length; i++) {
|
|
170
|
-
let ins = ''
|
|
171
|
-
while (seq[i] === '-') {
|
|
172
|
-
if (alignment[i] !== '-' && alignment[i] !== ' ') {
|
|
173
|
-
ins += alignment[i]
|
|
174
|
-
}
|
|
175
|
-
i++
|
|
176
|
-
}
|
|
177
|
-
if (ins.length > 0) {
|
|
178
|
-
const l = leftPx + scale * o - 1
|
|
179
|
-
ctx.rect(l, offset + t + 1, 1, h - 1)
|
|
180
|
-
ctx.rect(l - 2, offset + t, 5, 1)
|
|
181
|
-
ctx.rect(l - 2, offset + t + h - 1, 5, 1)
|
|
182
|
-
}
|
|
183
|
-
o++
|
|
184
|
-
}
|
|
185
|
-
ctx.fill()
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
24
|
export default class LinearMafRenderer extends FeatureRendererType {
|
|
190
25
|
getExpandedRegion(region: Region) {
|
|
191
26
|
const { start, end } = region
|
|
@@ -199,7 +34,13 @@ export default class LinearMafRenderer extends FeatureRendererType {
|
|
|
199
34
|
}
|
|
200
35
|
}
|
|
201
36
|
async render(renderProps: RenderArgs) {
|
|
202
|
-
const {
|
|
37
|
+
const {
|
|
38
|
+
statusCallback = () => {},
|
|
39
|
+
regions,
|
|
40
|
+
bpPerPx,
|
|
41
|
+
samples,
|
|
42
|
+
rowHeight,
|
|
43
|
+
} = renderProps
|
|
203
44
|
const region = regions[0]!
|
|
204
45
|
const height = samples.length * (rowHeight + 1) + 100
|
|
205
46
|
const width = (region.end - region.start) / bpPerPx
|
|
@@ -208,13 +49,15 @@ export default class LinearMafRenderer extends FeatureRendererType {
|
|
|
208
49
|
width,
|
|
209
50
|
height,
|
|
210
51
|
renderProps,
|
|
211
|
-
ctx => {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
52
|
+
async ctx => {
|
|
53
|
+
await updateStatus('Rendering alignment', statusCallback, () => {
|
|
54
|
+
makeImageData({
|
|
55
|
+
ctx,
|
|
56
|
+
renderArgs: {
|
|
57
|
+
...renderProps,
|
|
58
|
+
features,
|
|
59
|
+
},
|
|
60
|
+
})
|
|
218
61
|
})
|
|
219
62
|
return undefined
|
|
220
63
|
},
|
|
@@ -228,8 +71,10 @@ export default class LinearMafRenderer extends FeatureRendererType {
|
|
|
228
71
|
return {
|
|
229
72
|
...results,
|
|
230
73
|
...res,
|
|
74
|
+
features: new Map(),
|
|
231
75
|
width,
|
|
232
76
|
height,
|
|
77
|
+
containsNoTransferables: true,
|
|
233
78
|
}
|
|
234
79
|
}
|
|
235
80
|
}
|
|
@@ -7,7 +7,12 @@ function x() {} // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
|
7
7
|
|
|
8
8
|
const configSchema = ConfigurationSchema(
|
|
9
9
|
'LinearMafRenderer',
|
|
10
|
-
{
|
|
10
|
+
{
|
|
11
|
+
baseColor: {
|
|
12
|
+
type: 'color',
|
|
13
|
+
defaultValue: 'lightgrey',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
11
16
|
{
|
|
12
17
|
/**
|
|
13
18
|
* #baseConfiguration
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { RenderArgsDeserialized } from '@jbrowse/core/pluggableElementTypes/renderers/BoxRendererType'
|
|
2
|
+
import { createJBrowseTheme } from '@jbrowse/core/ui'
|
|
3
|
+
import { Feature, featureSpanPx } from '@jbrowse/core/util'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
fillRect,
|
|
7
|
+
getCharWidthHeight,
|
|
8
|
+
getColorBaseMap,
|
|
9
|
+
getContrastBaseMap,
|
|
10
|
+
} from './util'
|
|
11
|
+
|
|
12
|
+
interface Sample {
|
|
13
|
+
id: string
|
|
14
|
+
color?: string
|
|
15
|
+
}
|
|
16
|
+
interface RenderArgs extends RenderArgsDeserialized {
|
|
17
|
+
samples: Sample[]
|
|
18
|
+
rowHeight: number
|
|
19
|
+
rowProportion: number
|
|
20
|
+
showAllLetters: boolean
|
|
21
|
+
mismatchRendering: boolean
|
|
22
|
+
features: Map<string, Feature>
|
|
23
|
+
statusCallback?: (arg: string) => void
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function makeImageData({
|
|
27
|
+
ctx,
|
|
28
|
+
renderArgs,
|
|
29
|
+
}: {
|
|
30
|
+
ctx: CanvasRenderingContext2D
|
|
31
|
+
renderArgs: RenderArgs
|
|
32
|
+
}) {
|
|
33
|
+
const {
|
|
34
|
+
regions,
|
|
35
|
+
bpPerPx,
|
|
36
|
+
rowHeight,
|
|
37
|
+
showAllLetters,
|
|
38
|
+
theme: configTheme,
|
|
39
|
+
mismatchRendering,
|
|
40
|
+
samples,
|
|
41
|
+
rowProportion,
|
|
42
|
+
features,
|
|
43
|
+
statusCallback,
|
|
44
|
+
} = renderArgs
|
|
45
|
+
const region = regions[0]!
|
|
46
|
+
const canvasWidth = (region.end - region.start) / bpPerPx
|
|
47
|
+
const h = rowHeight * rowProportion
|
|
48
|
+
const theme = createJBrowseTheme(configTheme)
|
|
49
|
+
const colorForBase = getColorBaseMap(theme)
|
|
50
|
+
const contrastForBase = getContrastBaseMap(theme)
|
|
51
|
+
|
|
52
|
+
const { charHeight } = getCharWidthHeight()
|
|
53
|
+
const sampleToRowMap = new Map(samples.map((s, i) => [s.id, i]))
|
|
54
|
+
const scale = 1 / bpPerPx
|
|
55
|
+
const f = 0.4
|
|
56
|
+
const h2 = rowHeight / 2
|
|
57
|
+
const hp2 = h / 2
|
|
58
|
+
const offset = (rowHeight - h) / 2
|
|
59
|
+
|
|
60
|
+
// sample as alignments
|
|
61
|
+
ctx.font = 'bold 10px Courier New,monospace'
|
|
62
|
+
|
|
63
|
+
for (const feature of features.values()) {
|
|
64
|
+
const [leftPx] = featureSpanPx(feature, region, bpPerPx)
|
|
65
|
+
const vals = feature.get('alignments') as Record<string, { data: string }>
|
|
66
|
+
const seq = feature.get('seq').toLowerCase()
|
|
67
|
+
const r = Object.entries(vals)
|
|
68
|
+
for (const [sample, val] of r) {
|
|
69
|
+
const origAlignment = val.data
|
|
70
|
+
const alignment = origAlignment.toLowerCase()
|
|
71
|
+
|
|
72
|
+
const row = sampleToRowMap.get(sample)
|
|
73
|
+
if (row === undefined) {
|
|
74
|
+
continue
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const t = rowHeight * row
|
|
78
|
+
|
|
79
|
+
// gaps
|
|
80
|
+
ctx.beginPath()
|
|
81
|
+
ctx.fillStyle = 'black'
|
|
82
|
+
for (let i = 0, o = 0; i < alignment.length; i++) {
|
|
83
|
+
if (seq[i] !== '-') {
|
|
84
|
+
if (alignment[i] === '-') {
|
|
85
|
+
const l = leftPx + scale * o
|
|
86
|
+
ctx.moveTo(l, t + h2)
|
|
87
|
+
ctx.lineTo(l + scale + f, t + h2)
|
|
88
|
+
}
|
|
89
|
+
o++
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
ctx.stroke()
|
|
93
|
+
|
|
94
|
+
if (!showAllLetters) {
|
|
95
|
+
// matches
|
|
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
|
+
fillRect(ctx, l, offset + t, scale + f, h, canvasWidth)
|
|
103
|
+
}
|
|
104
|
+
o++
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// mismatches
|
|
110
|
+
for (let i = 0, o = 0; i < alignment.length; i++) {
|
|
111
|
+
const c = alignment[i]
|
|
112
|
+
if (seq[i] !== '-') {
|
|
113
|
+
if (c !== '-') {
|
|
114
|
+
const l = leftPx + scale * o
|
|
115
|
+
if (seq[i] !== c && c !== ' ') {
|
|
116
|
+
fillRect(
|
|
117
|
+
ctx,
|
|
118
|
+
l,
|
|
119
|
+
offset + t,
|
|
120
|
+
scale + f,
|
|
121
|
+
h,
|
|
122
|
+
canvasWidth,
|
|
123
|
+
mismatchRendering
|
|
124
|
+
? // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
125
|
+
(colorForBase[c as keyof typeof colorForBase] ?? 'black')
|
|
126
|
+
: 'orange',
|
|
127
|
+
)
|
|
128
|
+
} else if (showAllLetters) {
|
|
129
|
+
fillRect(
|
|
130
|
+
ctx,
|
|
131
|
+
l,
|
|
132
|
+
offset + t,
|
|
133
|
+
scale + f,
|
|
134
|
+
h,
|
|
135
|
+
canvasWidth,
|
|
136
|
+
mismatchRendering
|
|
137
|
+
? // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
138
|
+
(colorForBase[c as keyof typeof colorForBase] ?? 'black')
|
|
139
|
+
: 'lightblue',
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
o++
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// font
|
|
148
|
+
const charSizeW = 10
|
|
149
|
+
if (scale >= charSizeW) {
|
|
150
|
+
for (let i = 0, o = 0; i < alignment.length; i++) {
|
|
151
|
+
if (seq[i] !== '-') {
|
|
152
|
+
const l = leftPx + scale * o
|
|
153
|
+
const offset = (scale - charSizeW) / 2 + 1
|
|
154
|
+
const c = alignment[i]!
|
|
155
|
+
if ((showAllLetters || seq[i] !== c) && c !== '-') {
|
|
156
|
+
ctx.fillStyle = mismatchRendering
|
|
157
|
+
? (contrastForBase[c] ?? 'white')
|
|
158
|
+
: 'black'
|
|
159
|
+
if (rowHeight > charHeight) {
|
|
160
|
+
ctx.fillText(origAlignment[i] || '', l + offset, hp2 + t + 3)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
o++
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// second pass for insertions, has slightly improved look since the
|
|
171
|
+
// insertions are always 'on top' of the other features
|
|
172
|
+
for (const feature of features.values()) {
|
|
173
|
+
const [leftPx] = featureSpanPx(feature, region, bpPerPx)
|
|
174
|
+
const vals = feature.get('alignments') as Record<string, { data: string }>
|
|
175
|
+
const seq = feature.get('seq').toLowerCase()
|
|
176
|
+
|
|
177
|
+
for (const [sample, val] of Object.entries(vals)) {
|
|
178
|
+
const origAlignment = val.data
|
|
179
|
+
const alignment = origAlignment.toLowerCase()
|
|
180
|
+
const row = sampleToRowMap.get(sample)
|
|
181
|
+
if (row === undefined) {
|
|
182
|
+
continue
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const t = rowHeight * row
|
|
186
|
+
|
|
187
|
+
ctx.beginPath()
|
|
188
|
+
ctx.fillStyle = 'purple'
|
|
189
|
+
for (let i = 0, o = 0; i < alignment.length; i++) {
|
|
190
|
+
let ins = ''
|
|
191
|
+
while (seq[i] === '-') {
|
|
192
|
+
if (alignment[i] !== '-' && alignment[i] !== ' ') {
|
|
193
|
+
ins += alignment[i]
|
|
194
|
+
}
|
|
195
|
+
i++
|
|
196
|
+
}
|
|
197
|
+
if (ins.length > 0) {
|
|
198
|
+
const l = leftPx + scale * o - 1
|
|
199
|
+
|
|
200
|
+
ctx.rect(l, offset + t, 1, h)
|
|
201
|
+
if (bpPerPx < 1) {
|
|
202
|
+
ctx.rect(l - 2, offset + t, 5, 1)
|
|
203
|
+
ctx.rect(l - 2, offset + t + h - 1, 5, 1)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
o++
|
|
207
|
+
}
|
|
208
|
+
ctx.fill()
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { measureText } from '@jbrowse/core/util'
|
|
2
|
+
import type { Theme } from '@mui/material'
|
|
2
3
|
|
|
3
4
|
export function getContrastBaseMap(theme: Theme) {
|
|
4
5
|
return Object.fromEntries(
|
|
@@ -18,3 +19,29 @@ export function getColorBaseMap(theme: Theme) {
|
|
|
18
19
|
t: bases.T.main,
|
|
19
20
|
}
|
|
20
21
|
}
|
|
22
|
+
|
|
23
|
+
export function fillRect(
|
|
24
|
+
ctx: CanvasRenderingContext2D,
|
|
25
|
+
l: number,
|
|
26
|
+
t: number,
|
|
27
|
+
w: number,
|
|
28
|
+
h: number,
|
|
29
|
+
cw: number,
|
|
30
|
+
color?: string,
|
|
31
|
+
) {
|
|
32
|
+
if (l + w < 0 || l > cw) {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
if (color) {
|
|
36
|
+
ctx.fillStyle = color
|
|
37
|
+
}
|
|
38
|
+
ctx.fillRect(l, t, w, h)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// get width and height of chars the height is an approximation: width letter M
|
|
42
|
+
// is approximately the height
|
|
43
|
+
export function getCharWidthHeight() {
|
|
44
|
+
const charWidth = measureText('A')
|
|
45
|
+
const charHeight = measureText('M') - 2
|
|
46
|
+
return { charWidth, charHeight }
|
|
47
|
+
}
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
isSessionModelWithWidgets,
|
|
8
8
|
isSessionWithAddTracks,
|
|
9
9
|
} from '@jbrowse/core/util'
|
|
10
|
-
import { AddTrackModel } from '@jbrowse/plugin-data-management'
|
|
11
10
|
import {
|
|
12
11
|
Button,
|
|
13
12
|
FormControl,
|
|
@@ -21,6 +20,8 @@ import {
|
|
|
21
20
|
import { getRoot } from 'mobx-state-tree'
|
|
22
21
|
import { makeStyles } from 'tss-react/mui'
|
|
23
22
|
|
|
23
|
+
import type { AddTrackModel } from '@jbrowse/plugin-data-management'
|
|
24
|
+
|
|
24
25
|
const useStyles = makeStyles()(theme => ({
|
|
25
26
|
textbox: {
|
|
26
27
|
width: '100%',
|