@platforma-sdk/ui-vue 1.41.19 → 1.42.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/.turbo/turbo-build.log +19 -19
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +11 -0
- package/dist/components/PlMultiSequenceAlignment/Legend.vue.d.ts +2 -2
- package/dist/components/PlMultiSequenceAlignment/Legend.vue.d.ts.map +1 -1
- package/dist/components/PlMultiSequenceAlignment/Legend.vue2.js +11 -11
- package/dist/components/PlMultiSequenceAlignment/Legend.vue3.js +8 -8
- package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue.d.ts +12 -6
- package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue.d.ts.map +1 -1
- package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue2.js +80 -65
- package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue2.js.map +1 -1
- package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue3.js +14 -12
- package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue3.js.map +1 -1
- package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue.d.ts +36 -1
- package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue.d.ts.map +1 -1
- package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue2.js +146 -111
- package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue2.js.map +1 -1
- package/dist/components/PlMultiSequenceAlignment/Toolbar.vue.d.ts +2 -0
- package/dist/components/PlMultiSequenceAlignment/Toolbar.vue.d.ts.map +1 -1
- package/dist/components/PlMultiSequenceAlignment/Toolbar.vue2.js +71 -68
- package/dist/components/PlMultiSequenceAlignment/Toolbar.vue2.js.map +1 -1
- package/dist/components/PlMultiSequenceAlignment/chemical-properties.d.ts +42 -6
- package/dist/components/PlMultiSequenceAlignment/chemical-properties.d.ts.map +1 -1
- package/dist/components/PlMultiSequenceAlignment/chemical-properties.js +96 -130
- package/dist/components/PlMultiSequenceAlignment/chemical-properties.js.map +1 -1
- package/dist/components/PlMultiSequenceAlignment/data.d.ts +3 -9
- package/dist/components/PlMultiSequenceAlignment/data.d.ts.map +1 -1
- package/dist/components/PlMultiSequenceAlignment/data.js +198 -212
- package/dist/components/PlMultiSequenceAlignment/data.js.map +1 -1
- package/dist/components/PlMultiSequenceAlignment/markup.d.ts +7 -5
- package/dist/components/PlMultiSequenceAlignment/markup.d.ts.map +1 -1
- package/dist/components/PlMultiSequenceAlignment/markup.js +47 -26
- package/dist/components/PlMultiSequenceAlignment/markup.js.map +1 -1
- package/dist/components/PlMultiSequenceAlignment/types.d.ts +1 -1
- package/dist/components/PlMultiSequenceAlignment/types.d.ts.map +1 -1
- package/dist/lib/ui/uikit/dist/components/PlAccordion/{ExpandTransition.vue.js → ExpandTransition.vue2.js} +1 -1
- package/dist/lib/ui/uikit/dist/components/PlAccordion/ExpandTransition.vue2.js.map +1 -0
- package/dist/lib/ui/uikit/dist/components/PlAccordion/PlAccordionSection.vue2.js +1 -1
- package/dist/lib/ui/uikit/dist/lib/model/common/dist/index.js.map +1 -1
- package/dist/lib/ui/uikit/dist/sdk/model/dist/index.js +1 -1
- package/dist/node_modules/.pnpm/@vueuse_core@13.3.0_vue@3.5.13_typescript@5.6.3_/node_modules/@vueuse/core/index.js +111 -165
- package/dist/node_modules/.pnpm/@vueuse_core@13.3.0_vue@3.5.13_typescript@5.6.3_/node_modules/@vueuse/core/index.js.map +1 -1
- package/dist/node_modules/.pnpm/@vueuse_shared@13.3.0_vue@3.5.13_typescript@5.6.3_/node_modules/@vueuse/shared/index.js +1 -1
- package/dist/sdk/model/dist/index.js +1 -1
- package/package.json +5 -5
- package/src/components/PlMultiSequenceAlignment/Legend.vue +4 -3
- package/src/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue +66 -46
- package/src/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue +85 -34
- package/src/components/PlMultiSequenceAlignment/README.md +10 -8
- package/src/components/PlMultiSequenceAlignment/Toolbar.vue +4 -1
- package/src/components/PlMultiSequenceAlignment/chemical-properties.ts +154 -161
- package/src/components/PlMultiSequenceAlignment/data.ts +65 -85
- package/src/components/PlMultiSequenceAlignment/markup.ts +47 -15
- package/src/components/PlMultiSequenceAlignment/types.ts +1 -1
- package/dist/lib/ui/uikit/dist/components/PlAccordion/ExpandTransition.vue.js.map +0 -1
|
@@ -1,6 +1,101 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { HighlightLegend, ResidueCounts } from './types';
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export function highlightByChemicalProperties(
|
|
4
|
+
{ sequences, residueCounts }: {
|
|
5
|
+
sequences: string[];
|
|
6
|
+
residueCounts: ResidueCounts;
|
|
7
|
+
},
|
|
8
|
+
): { blob: Blob; legend: HighlightLegend } {
|
|
9
|
+
const lines: {
|
|
10
|
+
category: ChemicalCategory;
|
|
11
|
+
column: number;
|
|
12
|
+
start: number;
|
|
13
|
+
length: number;
|
|
14
|
+
}[] = [];
|
|
15
|
+
const chemicalProperties = getAlignmentChemicalProperties({
|
|
16
|
+
residueCounts,
|
|
17
|
+
rowCount: sequences.length,
|
|
18
|
+
});
|
|
19
|
+
const width = sequences.at(0)?.length ?? 0;
|
|
20
|
+
const height = sequences.length;
|
|
21
|
+
for (let column = 0; column < width; column += 1) {
|
|
22
|
+
for (let row = 0; row < height; row += 1) {
|
|
23
|
+
const residue = sequences[row][column];
|
|
24
|
+
const category = chemicalProperties
|
|
25
|
+
.at(column)
|
|
26
|
+
?.find(({ residues }) => residues.includes(residue))
|
|
27
|
+
?.category;
|
|
28
|
+
if (!category) continue;
|
|
29
|
+
const lastLine = lines.at(-1);
|
|
30
|
+
if (
|
|
31
|
+
lastLine
|
|
32
|
+
&& lastLine.category === category
|
|
33
|
+
&& lastLine.column === column
|
|
34
|
+
&& lastLine.start + lastLine.length === row
|
|
35
|
+
) {
|
|
36
|
+
lastLine.length += 1;
|
|
37
|
+
} else {
|
|
38
|
+
lines.push({ category, column, start: row, length: 1 });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const linesByCategory = Map.groupBy(lines, ({ category }) => category);
|
|
43
|
+
const blob = new Blob(
|
|
44
|
+
(function*() {
|
|
45
|
+
const viewBox = `0 0 ${width * 2} ${height}`;
|
|
46
|
+
yield `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBox}" stroke-width="2" preserveAspectRatio="none">`;
|
|
47
|
+
for (const [category, lines] of linesByCategory) {
|
|
48
|
+
const color = chemicalPropertiesColorScheme[category]?.color;
|
|
49
|
+
if (!color) continue;
|
|
50
|
+
yield `<path stroke="${color}" d="`;
|
|
51
|
+
let x = 0, y = 0;
|
|
52
|
+
for (const { column, start, length } of lines) {
|
|
53
|
+
yield `m${column * 2 + 1 - x},${start - y}v${length}`;
|
|
54
|
+
x = column * 2 + 1;
|
|
55
|
+
y = start + length;
|
|
56
|
+
}
|
|
57
|
+
yield '"/>';
|
|
58
|
+
}
|
|
59
|
+
yield '</svg>';
|
|
60
|
+
})().toArray(),
|
|
61
|
+
{ type: 'image/svg+xml' },
|
|
62
|
+
);
|
|
63
|
+
const legend = Object.fromEntries(
|
|
64
|
+
Object.entries(chemicalPropertiesColorScheme)
|
|
65
|
+
.filter(([color]) => linesByCategory.has(color as ChemicalCategory)),
|
|
66
|
+
);
|
|
67
|
+
return { blob, legend };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const getAlignmentChemicalProperties = (
|
|
71
|
+
{ residueCounts, rowCount }: {
|
|
72
|
+
residueCounts: ResidueCounts;
|
|
73
|
+
rowCount: number;
|
|
74
|
+
},
|
|
75
|
+
): ColumnChemicalProperties[] =>
|
|
76
|
+
residueCounts.map((column) => {
|
|
77
|
+
const matchedRules = new Set<string>();
|
|
78
|
+
return categoryCriterion.filter(({ residues, rules }) =>
|
|
79
|
+
residues.split('').some((residue) => residue in column)
|
|
80
|
+
&& (!rules || rules.split('').map((ruleName) =>
|
|
81
|
+
[ruleName, ruleDefinitions[ruleName as RuleName]] as const,
|
|
82
|
+
).some(([ruleName, { residues, threshold }]) => {
|
|
83
|
+
if (matchedRules.has(ruleName)) return true;
|
|
84
|
+
const groupCount = residues.split('')
|
|
85
|
+
.reduce((acc, residue) => acc + (column[residue] ?? 0), 0);
|
|
86
|
+
const matches = groupCount >= rowCount * threshold;
|
|
87
|
+
if (matches) matchedRules.add(ruleName);
|
|
88
|
+
return matches;
|
|
89
|
+
})),
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
type ColumnChemicalProperties = {
|
|
94
|
+
residues: string;
|
|
95
|
+
category: ChemicalCategory;
|
|
96
|
+
}[];
|
|
97
|
+
|
|
98
|
+
const chemicalPropertiesColorScheme = {
|
|
4
99
|
hydrophobic: {
|
|
5
100
|
label: 'Hydrophobic',
|
|
6
101
|
color: '#99CCFF',
|
|
@@ -33,174 +128,72 @@ export const chemicalPropertiesColorMap: ColorMap = {
|
|
|
33
128
|
label: 'Aromatic',
|
|
34
129
|
color: '#A2F5FA',
|
|
35
130
|
},
|
|
36
|
-
};
|
|
131
|
+
} satisfies HighlightLegend;
|
|
37
132
|
|
|
38
|
-
export type ChemicalCategory = keyof typeof
|
|
133
|
+
export type ChemicalCategory = keyof typeof chemicalPropertiesColorScheme;
|
|
39
134
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
colorMap: ColorMap;
|
|
45
|
-
},
|
|
46
|
-
): Promise<Blob> {
|
|
47
|
-
const canvas = new OffscreenCanvas(
|
|
48
|
-
sequences[0]?.length ?? 0,
|
|
49
|
-
sequences.length,
|
|
50
|
-
);
|
|
51
|
-
const context = canvas.getContext('2d')!;
|
|
52
|
-
const chemicalProperties = getAlignmentChemicalProperties({
|
|
53
|
-
residueCounts,
|
|
54
|
-
rowCount: sequences.length,
|
|
55
|
-
});
|
|
56
|
-
for (const [rowIndex, sequence] of sequences.entries()) {
|
|
57
|
-
for (const [columnIndex, residue] of sequence.split('').entries()) {
|
|
58
|
-
const category = chemicalProperties
|
|
59
|
-
.at(columnIndex)
|
|
60
|
-
?.find(({ residues }) => residues.includes(residue))
|
|
61
|
-
?.category;
|
|
62
|
-
if (!category) continue;
|
|
63
|
-
const color = colorMap[category]?.color;
|
|
64
|
-
if (!color) continue;
|
|
65
|
-
context.fillStyle = colorMap[category].color;
|
|
66
|
-
context.fillRect(columnIndex, rowIndex, 1, 1);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return canvas.convertToBlob();
|
|
70
|
-
}
|
|
135
|
+
/*
|
|
136
|
+
* Below taken mostly from
|
|
137
|
+
* https://www.rbvi.ucsf.edu/chimera/1.2065/docs/ContributedSoftware/multalignviewer/colprot.par}
|
|
138
|
+
*/
|
|
71
139
|
|
|
72
|
-
const
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
140
|
+
const ruleDefinitions = {
|
|
141
|
+
'%': { residues: 'WLVIMAFCYHP', threshold: 0.6 },
|
|
142
|
+
'#': { residues: 'WLVIMAFCYHP', threshold: 0.8 },
|
|
143
|
+
'-': { residues: 'ED', threshold: 0.5 },
|
|
144
|
+
'+': { residues: 'KR', threshold: 0.6 },
|
|
145
|
+
'g': { residues: 'G', threshold: 0.5 },
|
|
146
|
+
'n': { residues: 'N', threshold: 0.5 },
|
|
147
|
+
'q': { residues: 'QE', threshold: 0.5 },
|
|
148
|
+
'p': { residues: 'P', threshold: 0.5 },
|
|
149
|
+
't': { residues: 'TS', threshold: 0.5 },
|
|
150
|
+
'A': { residues: 'A', threshold: 0.85 },
|
|
151
|
+
'C': { residues: 'C', threshold: 0.85 },
|
|
152
|
+
'D': { residues: 'D', threshold: 0.85 },
|
|
153
|
+
'E': { residues: 'E', threshold: 0.85 },
|
|
154
|
+
'F': { residues: 'F', threshold: 0.85 },
|
|
155
|
+
'G': { residues: 'G', threshold: 0.85 },
|
|
156
|
+
'H': { residues: 'H', threshold: 0.85 },
|
|
157
|
+
'I': { residues: 'I', threshold: 0.85 },
|
|
158
|
+
'K': { residues: 'K', threshold: 0.85 },
|
|
159
|
+
'L': { residues: 'L', threshold: 0.85 },
|
|
160
|
+
'M': { residues: 'M', threshold: 0.85 },
|
|
161
|
+
'N': { residues: 'N', threshold: 0.85 },
|
|
162
|
+
'P': { residues: 'P', threshold: 0.85 },
|
|
163
|
+
'Q': { residues: 'Q', threshold: 0.85 },
|
|
164
|
+
'R': { residues: 'R', threshold: 0.85 },
|
|
165
|
+
'S': { residues: 'S', threshold: 0.85 },
|
|
166
|
+
'T': { residues: 'T', threshold: 0.85 },
|
|
167
|
+
'V': { residues: 'V', threshold: 0.85 },
|
|
168
|
+
'W': { residues: 'W', threshold: 0.85 },
|
|
169
|
+
'Y': { residues: 'Y', threshold: 0.85 },
|
|
170
|
+
};
|
|
100
171
|
|
|
101
|
-
type
|
|
102
|
-
residues: string;
|
|
103
|
-
category: ChemicalCategory;
|
|
104
|
-
}[];
|
|
172
|
+
type RuleName = keyof typeof ruleDefinitions;
|
|
105
173
|
|
|
106
|
-
/** @see {@link https://www.jalview.org/help/html/colourSchemes/clustal.html} */
|
|
107
174
|
const categoryCriterion: Criteria[] = [
|
|
108
|
-
{
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
rules: [
|
|
127
|
-
{ groups: ['KR'], threshold: 0.6 },
|
|
128
|
-
{ groups: ['QE'], threshold: 0.5 },
|
|
129
|
-
{ groups: ['ED'], threshold: 0.5 },
|
|
130
|
-
{ groups: [...'EQD'], threshold: 0.85 },
|
|
131
|
-
],
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
residues: 'D',
|
|
135
|
-
category: 'negativeCharge',
|
|
136
|
-
rules: [
|
|
137
|
-
{ groups: ['KR'], threshold: 0.6 },
|
|
138
|
-
{ groups: [...'DEN'], threshold: 0.85 },
|
|
139
|
-
{ groups: ['ED'], threshold: 0.5 },
|
|
140
|
-
],
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
residues: 'N',
|
|
144
|
-
category: 'polar',
|
|
145
|
-
rules: [
|
|
146
|
-
{ groups: ['N'], threshold: 0.5 },
|
|
147
|
-
{ groups: [...'ND'], threshold: 0.85 },
|
|
148
|
-
],
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
residues: 'Q',
|
|
152
|
-
category: 'polar',
|
|
153
|
-
rules: [
|
|
154
|
-
{ groups: ['KR'], threshold: 0.6 },
|
|
155
|
-
{ groups: ['QE'], threshold: 0.5 },
|
|
156
|
-
{ groups: [...'QTKR'], threshold: 0.85 },
|
|
157
|
-
],
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
residues: 'ST',
|
|
161
|
-
category: 'polar',
|
|
162
|
-
rules: [
|
|
163
|
-
{ groups: ['WLVIMAFCYHP'], threshold: 0.6 },
|
|
164
|
-
{ groups: ['TS'], threshold: 0.5 },
|
|
165
|
-
{ groups: [...'ST'], threshold: 0.85 },
|
|
166
|
-
],
|
|
167
|
-
},
|
|
168
|
-
{
|
|
169
|
-
residues: 'C',
|
|
170
|
-
category: 'cysteine',
|
|
171
|
-
rules: [
|
|
172
|
-
{ groups: ['C'], threshold: 0.85 },
|
|
173
|
-
],
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
residues: 'G',
|
|
177
|
-
category: 'glycine',
|
|
178
|
-
rules: [
|
|
179
|
-
{ groups: ['G'], threshold: 0 },
|
|
180
|
-
],
|
|
181
|
-
},
|
|
182
|
-
{
|
|
183
|
-
residues: 'P',
|
|
184
|
-
category: 'proline',
|
|
185
|
-
rules: [
|
|
186
|
-
{ groups: ['P'], threshold: 0 },
|
|
187
|
-
],
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
residues: 'HY',
|
|
191
|
-
category: 'aromatic',
|
|
192
|
-
rules: [
|
|
193
|
-
{ groups: ['WLVIMAFCYHP'], threshold: 0.6 },
|
|
194
|
-
{ groups: [...'WYACPQFHILMV'], threshold: 0.85 },
|
|
195
|
-
],
|
|
196
|
-
},
|
|
175
|
+
{ residues: 'G', category: 'glycine', rules: '' },
|
|
176
|
+
{ residues: 'P', category: 'proline', rules: '' },
|
|
177
|
+
{ residues: 'T', category: 'polar', rules: 'tST%#' },
|
|
178
|
+
{ residues: 'S', category: 'polar', rules: 'tST#' },
|
|
179
|
+
{ residues: 'N', category: 'polar', rules: 'nND' },
|
|
180
|
+
{ residues: 'Q', category: 'polar', rules: 'qQE+KR' },
|
|
181
|
+
// criteria below has to go before the other criteria for C,
|
|
182
|
+
// otherwise it will never match
|
|
183
|
+
{ residues: 'C', category: 'cysteine', rules: 'C' },
|
|
184
|
+
{ residues: 'WLVIMF', category: 'hydrophobic', rules: '%#ACFHILMVWYPp' },
|
|
185
|
+
// below there was an 's' rule too,
|
|
186
|
+
// but no definition of such rule was provided
|
|
187
|
+
{ residues: 'A', category: 'hydrophobic', rules: '%#ACFHILMVWYPpTSG' },
|
|
188
|
+
{ residues: 'C', category: 'hydrophobic', rules: '%#AFHILMVWYSPp' },
|
|
189
|
+
{ residues: 'HY', category: 'aromatic', rules: '%#ACFHILMVWYPp' },
|
|
190
|
+
{ residues: 'E', category: 'negativeCharge', rules: '-DEqQ' },
|
|
191
|
+
{ residues: 'D', category: 'negativeCharge', rules: '-DEnN' },
|
|
192
|
+
{ residues: 'KR', category: 'positiveCharge', rules: '+KRQ' },
|
|
197
193
|
];
|
|
198
194
|
|
|
199
195
|
type Criteria = {
|
|
200
196
|
residues: string;
|
|
201
197
|
category: ChemicalCategory;
|
|
202
|
-
rules:
|
|
203
|
-
groups: string[];
|
|
204
|
-
threshold: number;
|
|
205
|
-
}[];
|
|
198
|
+
rules: string;
|
|
206
199
|
};
|
|
@@ -24,20 +24,15 @@ import {
|
|
|
24
24
|
pTableValue,
|
|
25
25
|
} from '@platforma-sdk/model';
|
|
26
26
|
import { ref, watch } from 'vue';
|
|
27
|
+
import { highlightByChemicalProperties } from './chemical-properties';
|
|
27
28
|
import {
|
|
28
|
-
|
|
29
|
-
colorizeSequencesByChemicalProperties,
|
|
30
|
-
} from './chemical-properties';
|
|
31
|
-
import {
|
|
32
|
-
colorizeSequencesByMarkup,
|
|
33
|
-
type Markup,
|
|
29
|
+
highlightByMarkup,
|
|
34
30
|
markupAlignedSequence,
|
|
35
|
-
markupColors,
|
|
36
31
|
parseMarkup,
|
|
37
32
|
} from './markup';
|
|
38
33
|
import { multiSequenceAlignment } from './multi-sequence-alignment';
|
|
39
34
|
import { getResidueCounts } from './residue-counts';
|
|
40
|
-
import type {
|
|
35
|
+
import type { HighlightLegend, ResidueCounts } from './types';
|
|
41
36
|
|
|
42
37
|
const getPFrameDriver = () => getRawPlatformaInstance().pFrameDriver;
|
|
43
38
|
|
|
@@ -70,12 +65,13 @@ async function getSequenceColumnsOptions({
|
|
|
70
65
|
|
|
71
66
|
const pFrameDriver = getPFrameDriver();
|
|
72
67
|
const columns = await pFrameDriver.listColumns(pFrame);
|
|
73
|
-
const options = columns
|
|
68
|
+
const options = columns.values()
|
|
74
69
|
.filter((column) => sequenceColumnPredicate(column))
|
|
75
70
|
.map(({ spec, columnId }) => ({
|
|
76
71
|
label: spec.annotations?.['pl7.app/label'] ?? 'Unlabelled column',
|
|
77
72
|
value: columnId,
|
|
78
|
-
}))
|
|
73
|
+
}))
|
|
74
|
+
.toArray();
|
|
79
75
|
const defaults = options.map(({ value }) => value);
|
|
80
76
|
return { options, defaults };
|
|
81
77
|
}
|
|
@@ -93,14 +89,16 @@ async function getLabelColumnsOptions({
|
|
|
93
89
|
const columns = await pFrameDriver.listColumns(pFrame);
|
|
94
90
|
const optionMap = new Map<CanonicalizedJson<PTableColumnId>, string>();
|
|
95
91
|
|
|
96
|
-
const sequenceColumnsAxes = new Map(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
.
|
|
103
|
-
|
|
92
|
+
const sequenceColumnsAxes = new Map(
|
|
93
|
+
sequenceColumnIds.values().flatMap((id) => {
|
|
94
|
+
const column = columns.find(({ columnId }) => columnId === id);
|
|
95
|
+
if (!column) {
|
|
96
|
+
throw new Error(`Couldn't find sequence column (ID: \`${id}\`).`);
|
|
97
|
+
}
|
|
98
|
+
return column.spec.axesSpec.values()
|
|
99
|
+
.map((spec) => [canonicalizeJson(getAxisId(spec)), spec]);
|
|
100
|
+
}),
|
|
101
|
+
);
|
|
104
102
|
|
|
105
103
|
for (const [axisIdJson, axisSpec] of sequenceColumnsAxes.entries()) {
|
|
106
104
|
const axisId = parseJson(axisIdJson);
|
|
@@ -119,10 +117,9 @@ async function getLabelColumnsOptions({
|
|
|
119
117
|
|
|
120
118
|
const { hits: compatibleColumns } = await pFrameDriver.findColumns(pFrame, {
|
|
121
119
|
columnFilter: {},
|
|
122
|
-
compatibleWith:
|
|
123
|
-
|
|
124
|
-
(
|
|
125
|
-
),
|
|
120
|
+
compatibleWith: sequenceColumnsAxes.keys()
|
|
121
|
+
.map((axisIdJson) => parseJson(axisIdJson))
|
|
122
|
+
.toArray(),
|
|
126
123
|
strictlyCompatible: false,
|
|
127
124
|
});
|
|
128
125
|
|
|
@@ -135,17 +132,18 @@ async function getLabelColumnsOptions({
|
|
|
135
132
|
);
|
|
136
133
|
}
|
|
137
134
|
|
|
138
|
-
const options =
|
|
139
|
-
([value, label]) => ({ label, value: parseJson(value) })
|
|
140
|
-
|
|
135
|
+
const options = optionMap.entries()
|
|
136
|
+
.map(([value, label]) => ({ label, value: parseJson(value) }))
|
|
137
|
+
.toArray();
|
|
141
138
|
|
|
142
|
-
const defaults = options
|
|
139
|
+
const defaults = options.values()
|
|
143
140
|
.filter(({ value }) => {
|
|
144
141
|
if (value.type === 'axis') return true;
|
|
145
142
|
const column = columns.find(({ columnId }) => columnId === value.id);
|
|
146
143
|
return column && isLabelColumn(column.spec);
|
|
147
144
|
})
|
|
148
|
-
.map(({ value }) => value)
|
|
145
|
+
.map(({ value }) => value)
|
|
146
|
+
.toArray();
|
|
149
147
|
return { options, defaults };
|
|
150
148
|
}
|
|
151
149
|
|
|
@@ -168,7 +166,7 @@ async function getMarkupColumnsOptions({
|
|
|
168
166
|
`Couldn't find sequence column (ID: \`${sequenceColumnIds[0]}\`).`,
|
|
169
167
|
);
|
|
170
168
|
}
|
|
171
|
-
return columns
|
|
169
|
+
return columns.values()
|
|
172
170
|
.filter((column) =>
|
|
173
171
|
column.spec.annotations?.['pl7.app/sequence/isAnnotation'] === 'true'
|
|
174
172
|
&& isJsonEqual(sequenceColumn.spec.axesSpec, column.spec.axesSpec)
|
|
@@ -178,14 +176,14 @@ async function getMarkupColumnsOptions({
|
|
|
178
176
|
).map(({ columnId, spec }) => ({
|
|
179
177
|
value: columnId,
|
|
180
178
|
label: spec.annotations?.['pl7.app/label'] ?? 'Unlabelled column',
|
|
181
|
-
}))
|
|
179
|
+
}))
|
|
180
|
+
.toArray();
|
|
182
181
|
}
|
|
183
182
|
|
|
184
183
|
async function getMultipleAlignmentData({
|
|
185
184
|
pframe,
|
|
186
185
|
sequenceColumnIds,
|
|
187
186
|
labelColumnIds,
|
|
188
|
-
markupColumnId,
|
|
189
187
|
selection,
|
|
190
188
|
colorScheme,
|
|
191
189
|
alignmentParams,
|
|
@@ -193,7 +191,6 @@ async function getMultipleAlignmentData({
|
|
|
193
191
|
pframe: PFrameHandle | undefined;
|
|
194
192
|
sequenceColumnIds: PObjectId[] | undefined;
|
|
195
193
|
labelColumnIds: PTableColumnId[] | undefined;
|
|
196
|
-
markupColumnId: PObjectId | undefined;
|
|
197
194
|
selection: PlSelectionModel | undefined;
|
|
198
195
|
colorScheme: PlMultiSequenceAlignmentColorSchemeOption;
|
|
199
196
|
alignmentParams: PlMultiSequenceAlignmentSettings['alignmentParams'];
|
|
@@ -249,19 +246,21 @@ async function getMultipleAlignmentData({
|
|
|
249
246
|
});
|
|
250
247
|
|
|
251
248
|
// and markup
|
|
252
|
-
if (
|
|
253
|
-
secondaryEntry.push({ type: 'column', column:
|
|
249
|
+
if (colorScheme.type === 'markup') {
|
|
250
|
+
secondaryEntry.push({ type: 'column', column: colorScheme.columnId });
|
|
254
251
|
}
|
|
255
252
|
|
|
256
253
|
const sorting: PTableSorting[] = Array.from(
|
|
257
|
-
new Set(
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
.
|
|
264
|
-
|
|
254
|
+
new Set(
|
|
255
|
+
sequenceColumnIds.values().flatMap((id) => {
|
|
256
|
+
const column = columns.find(({ columnId }) => columnId === id);
|
|
257
|
+
if (!column) {
|
|
258
|
+
throw new Error(`Couldn't find sequence column (ID: ${id})`);
|
|
259
|
+
}
|
|
260
|
+
return column.spec.axesSpec
|
|
261
|
+
.map((spec) => canonicalizeJson(getAxisId(spec)));
|
|
262
|
+
}),
|
|
263
|
+
),
|
|
265
264
|
)
|
|
266
265
|
.sort()
|
|
267
266
|
.map((id) => ({
|
|
@@ -320,9 +319,6 @@ async function getMultipleAlignmentData({
|
|
|
320
319
|
return column;
|
|
321
320
|
});
|
|
322
321
|
|
|
323
|
-
const markupColumn = markupColumnId
|
|
324
|
-
&& table.find(({ spec }) => spec.id === markupColumnId);
|
|
325
|
-
|
|
326
322
|
const alignedSequences = await Promise.all(
|
|
327
323
|
sequenceColumns.map((column) =>
|
|
328
324
|
multiSequenceAlignment(
|
|
@@ -360,17 +356,26 @@ async function getMultipleAlignmentData({
|
|
|
360
356
|
const result: MultipleAlignmentData = {
|
|
361
357
|
sequences: concatenatedSequences,
|
|
362
358
|
sequenceNames,
|
|
363
|
-
|
|
359
|
+
labelRows: labels,
|
|
364
360
|
exceedsLimit,
|
|
365
361
|
residueCounts,
|
|
366
362
|
};
|
|
367
363
|
|
|
368
|
-
if (
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
364
|
+
if (colorScheme.type === 'chemical-properties') {
|
|
365
|
+
result.highlightImage = highlightByChemicalProperties({
|
|
366
|
+
sequences: concatenatedSequences,
|
|
367
|
+
residueCounts,
|
|
368
|
+
});
|
|
369
|
+
} else if (colorScheme.type === 'markup') {
|
|
370
|
+
const markupColumn = table.find(({ spec }) =>
|
|
371
|
+
spec.id === colorScheme.columnId,
|
|
372
372
|
);
|
|
373
|
-
|
|
373
|
+
if (!markupColumn) {
|
|
374
|
+
throw new Error(
|
|
375
|
+
`Couldn't find markup column (ID: \`${colorScheme.columnId}\`).`,
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
const markupRows = Array.from(
|
|
374
379
|
{ length: rowCount },
|
|
375
380
|
(_, row) => {
|
|
376
381
|
const markup = parseMarkup(
|
|
@@ -381,36 +386,15 @@ async function getMultipleAlignmentData({
|
|
|
381
386
|
return markupAlignedSequence(sequences[row][0], markup);
|
|
382
387
|
},
|
|
383
388
|
);
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
if (colorScheme.type === 'chemical-properties') {
|
|
388
|
-
const colorMap = chemicalPropertiesColorMap;
|
|
389
|
-
result.highlightImage = {
|
|
390
|
-
blob: await colorizeSequencesByChemicalProperties({
|
|
391
|
-
sequences: concatenatedSequences,
|
|
392
|
-
residueCounts,
|
|
393
|
-
colorMap,
|
|
394
|
-
}),
|
|
395
|
-
colorMap,
|
|
396
|
-
};
|
|
397
|
-
} else if (colorScheme.type === 'markup') {
|
|
398
|
-
const colorMap = Object.fromEntries(
|
|
399
|
-
Object.entries(
|
|
400
|
-
result.markup?.labels ?? {},
|
|
401
|
-
).map(([id, label], index) => [
|
|
402
|
-
id,
|
|
403
|
-
{ label, color: markupColors[index % markupColors.length] },
|
|
404
|
-
]),
|
|
389
|
+
const labels: Record<string, string> = JSON.parse(
|
|
390
|
+
markupColumn.spec.spec.annotations
|
|
391
|
+
?.['pl7.app/sequence/annotation/mapping'] ?? '{}',
|
|
405
392
|
);
|
|
406
|
-
result.highlightImage = {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
}),
|
|
412
|
-
colorMap,
|
|
413
|
-
};
|
|
393
|
+
result.highlightImage = highlightByMarkup({
|
|
394
|
+
markupRows,
|
|
395
|
+
columnCount: concatenatedSequences.at(0)?.length ?? 0,
|
|
396
|
+
labels,
|
|
397
|
+
});
|
|
414
398
|
}
|
|
415
399
|
|
|
416
400
|
return result;
|
|
@@ -450,15 +434,11 @@ function refreshOnDeepChange<T, P>(cb: (params: P) => Promise<T>) {
|
|
|
450
434
|
type MultipleAlignmentData = {
|
|
451
435
|
sequences: string[];
|
|
452
436
|
sequenceNames: string[];
|
|
453
|
-
|
|
437
|
+
labelRows: string[][];
|
|
454
438
|
residueCounts: ResidueCounts;
|
|
455
|
-
markup?: {
|
|
456
|
-
labels: Record<string, string>;
|
|
457
|
-
data: Markup[];
|
|
458
|
-
};
|
|
459
439
|
highlightImage?: {
|
|
460
440
|
blob: Blob;
|
|
461
|
-
|
|
441
|
+
legend: HighlightLegend;
|
|
462
442
|
};
|
|
463
443
|
exceedsLimit: boolean;
|
|
464
444
|
};
|