@datagrok/sequence-translator 0.0.1 → 0.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.
- package/.eslintrc.json +39 -0
- package/detectors.js +11 -15
- package/package.json +10 -3
- package/setup.cmd +11 -0
- package/src/axolabsMap.ts +101 -99
- package/src/defineAxolabsPattern.ts +242 -207
- package/src/drawAxolabsPattern.ts +127 -92
- package/src/map.ts +540 -0
- package/src/package-test.ts +12 -0
- package/src/package.ts +635 -357
- package/src/save-sense-antisense.ts +36 -0
- package/src/tests/smiles-tests.ts +454 -0
- package/webpack.config.js +7 -1
- package/scripts/convertFastaToSmiles +0 -24
- package/scripts/drawAxolabsPattern +0 -50
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {axolabsMap} from
|
|
1
|
+
import {axolabsMap} from './axolabsMap';
|
|
2
2
|
|
|
3
3
|
// https://uxdesign.cc/star-rating-make-svg-great-again-d4ce4731347e
|
|
4
|
-
function getPointsToDrawStar(centerX: number, centerY: number) {
|
|
5
|
-
const innerCirclePoints = 5;
|
|
4
|
+
function getPointsToDrawStar(centerX: number, centerY: number): string {
|
|
5
|
+
const innerCirclePoints = 5; // a 5 point star
|
|
6
6
|
const innerRadius = 15 / innerCirclePoints;
|
|
7
7
|
const innerOuterRadiusRatio = 2; // outter circle is x2 the inner
|
|
8
8
|
const outerRadius = innerRadius * innerOuterRadiusRatio;
|
|
@@ -12,9 +12,9 @@ function getPointsToDrawStar(centerX: number, centerY: number) {
|
|
|
12
12
|
|
|
13
13
|
let points = '';
|
|
14
14
|
for (let i = 0; i < totalNumberOfPoints; i++) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const r = (i % 2 == 0) ? outerRadius : innerRadius;
|
|
16
|
+
const currentX = centerX + Math.cos(i * angle + angleOffsetToCenterStar) * r;
|
|
17
|
+
const currentY = centerY + Math.sin(i * angle + angleOffsetToCenterStar) * r;
|
|
18
18
|
points += currentX + ',' + currentY + ' ';
|
|
19
19
|
}
|
|
20
20
|
return points;
|
|
@@ -35,121 +35,137 @@ function getTextWidth(text: string, font: number): number {
|
|
|
35
35
|
return 2 * context.measureText(text).width;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
function getTextInsideCircle(
|
|
39
|
-
|
|
38
|
+
function getTextInsideCircle(
|
|
39
|
+
bases: string[], index: number, nucleotideCounter: number,
|
|
40
|
+
numberOfNucleotides: number, enumerateModifications: string[]): string {
|
|
41
|
+
return (bases[index].slice(-3) == '(o)') || !enumerateModifications.includes(bases[index]) ? '' :
|
|
40
42
|
['A', 'G', 'C', 'U', 'T'].includes(bases[index]) ? bases[index] : String(numberOfNucleotides - nucleotideCounter);
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
function getFontColorVisibleOnBackground(rgbString: string) {
|
|
44
|
-
const rgbIntList = rgbString.match(/\d+/g)!.map(e => Number(e));
|
|
45
|
-
return (rgbIntList[0] * 0.299 + rgbIntList[1] * 0.587 + rgbIntList[2] * 0.114) > 186 ? '#
|
|
45
|
+
function getFontColorVisibleOnBackground(rgbString: string): string {
|
|
46
|
+
const rgbIntList = rgbString.match(/\d+/g)!.map((e) => Number(e));
|
|
47
|
+
return (rgbIntList[0] * 0.299 + rgbIntList[1] * 0.587 + rgbIntList[2] * 0.114) > 186 ? '#33333' : '#ffffff';
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
function getBaseColor(base: string): string {
|
|
49
|
-
return axolabsMap[base][
|
|
51
|
+
return axolabsMap[base]['color'];
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
export function drawAxolabsPattern(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
export function drawAxolabsPattern(
|
|
55
|
+
patternName: string, asExists: boolean, ssBases: string[],
|
|
56
|
+
asBases: string[], ssPtoStatuses: boolean[], asPtoStatuses: boolean[],
|
|
57
|
+
ssThreeModification: string, ssFiveModification: string,
|
|
58
|
+
asThreeModification: string, asFiveModification: string, comment: string,
|
|
59
|
+
enumerateModifications: string[]) {
|
|
56
60
|
function getEquidistantXForLegend(index: number): number {
|
|
57
61
|
return Math.round((index + startFrom) * width / (uniqueBases.length + startFrom) + legendRadius);
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
function getXOfBaseCircles(index: number, rightOverhangs: number): number {
|
|
61
|
-
return widthOfRightModification +
|
|
65
|
+
return widthOfRightModification +
|
|
66
|
+
(resultingNumberOfNucleotidesInStrands - index + rightOverhangs + 1) * baseDiameter;
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
function getShiftToAlignNumberInsideCircle(bases: string[], generalIndex: number, nucleotideIndex: number): number {
|
|
65
|
-
return (nucleotideIndex < 10 || ['A', 'G', 'C', 'U', 'T'].
|
|
70
|
+
return (nucleotideIndex < 10 || ['A', 'G', 'C', 'U', 'T'].
|
|
71
|
+
includes(bases[generalIndex])) ? shiftToAlignOneDigitNumberInsideCircle : shiftToAlignTwoDigitNumberInsideCircle;
|
|
66
72
|
}
|
|
67
73
|
|
|
68
74
|
const svg = {
|
|
69
|
-
xmlns:
|
|
75
|
+
xmlns: 'http://www.w3.org/2000/svg',
|
|
70
76
|
render: function(width: number, height: number) {
|
|
71
77
|
const e = document.createElementNS(this.xmlns, 'svg');
|
|
72
|
-
e.setAttribute(
|
|
73
|
-
e.setAttribute(
|
|
74
|
-
e.setAttribute(
|
|
78
|
+
e.setAttribute('id', 'mySvg');
|
|
79
|
+
e.setAttribute('width', String(width));
|
|
80
|
+
e.setAttribute('height', String(height));
|
|
75
81
|
return e;
|
|
76
82
|
},
|
|
77
83
|
circle: function(x: number, y: number, radius: number, color: string) {
|
|
78
84
|
const e = document.createElementNS(this.xmlns, 'circle');
|
|
79
|
-
e.setAttribute(
|
|
80
|
-
e.setAttribute(
|
|
81
|
-
e.setAttribute(
|
|
82
|
-
e.setAttribute(
|
|
85
|
+
e.setAttribute('cx', String(x));
|
|
86
|
+
e.setAttribute('cy', String(y));
|
|
87
|
+
e.setAttribute('r', String(radius));
|
|
88
|
+
e.setAttribute('fill', color);
|
|
83
89
|
return e;
|
|
84
90
|
},
|
|
85
91
|
text: function(text: string, x: number, y: number, fontSize: number, color: string) {
|
|
86
92
|
const e = document.createElementNS(this.xmlns, 'text');
|
|
87
|
-
e.setAttribute(
|
|
88
|
-
e.setAttribute(
|
|
89
|
-
e.setAttribute(
|
|
90
|
-
e.setAttribute(
|
|
91
|
-
e.setAttribute(
|
|
92
|
-
e.setAttribute(
|
|
93
|
+
e.setAttribute('x', String(x));
|
|
94
|
+
e.setAttribute('y', String(y));
|
|
95
|
+
e.setAttribute('font-size', String(fontSize));
|
|
96
|
+
e.setAttribute('font-weight', 'normal');
|
|
97
|
+
e.setAttribute('font-family', 'Arial');
|
|
98
|
+
e.setAttribute('fill', color);
|
|
93
99
|
e.innerHTML = text;
|
|
94
100
|
return e;
|
|
95
101
|
},
|
|
96
102
|
star: function(x: number, y: number, fill: string) {
|
|
97
|
-
const e = document.createElementNS(this.xmlns,
|
|
98
|
-
e.setAttribute(
|
|
99
|
-
e.setAttribute(
|
|
103
|
+
const e = document.createElementNS(this.xmlns, 'polygon');
|
|
104
|
+
e.setAttribute('points', getPointsToDrawStar(x, y));
|
|
105
|
+
e.setAttribute('fill', fill);
|
|
100
106
|
return e;
|
|
101
|
-
}
|
|
107
|
+
},
|
|
102
108
|
};
|
|
103
109
|
|
|
104
110
|
ssBases = ssBases.reverse();
|
|
105
111
|
ssPtoStatuses = ssPtoStatuses.reverse();
|
|
106
112
|
|
|
107
|
-
const baseRadius = 15
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
113
|
+
const baseRadius = 15;
|
|
114
|
+
const shiftToAlignTwoDigitNumberInsideCircle = -10;
|
|
115
|
+
const shiftToAlignOneDigitNumberInsideCircle = -5;
|
|
116
|
+
const legendRadius = 6;
|
|
117
|
+
const psLinkageRadius = 5;
|
|
118
|
+
const baseFontSize = 17;
|
|
119
|
+
const legendFontSize = 14;
|
|
120
|
+
const psLinkageColor = 'red';
|
|
121
|
+
const fontColor = 'var(--grey-6)';
|
|
122
|
+
const titleFontColor = 'black';
|
|
123
|
+
const modificationsColor = 'red';
|
|
124
|
+
const ssLeftText = 'SS: 5\'';
|
|
125
|
+
const asLeftText = 'AS: 3\'';
|
|
126
|
+
const ssRightText = '3\'';
|
|
127
|
+
const asRightText = '5\'';
|
|
122
128
|
|
|
123
129
|
const ssRightOverhangs = countOverhangsOnTheRightEdge(ssBases);
|
|
124
130
|
const asRightOverhangs = countOverhangsOnTheRightEdge(asBases);
|
|
125
|
-
const resultingNumberOfNucleotidesInStrands =
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
131
|
+
const resultingNumberOfNucleotidesInStrands =
|
|
132
|
+
Math.max(ssBases.length - ssRightOverhangs, asBases.length - asRightOverhangs);
|
|
133
|
+
const baseDiameter = 2 * baseRadius;
|
|
134
|
+
const widthOfBases =
|
|
135
|
+
baseDiameter * (resultingNumberOfNucleotidesInStrands + Math.max(ssRightOverhangs, asRightOverhangs));
|
|
136
|
+
const widthOfLeftModification =
|
|
137
|
+
Math.max(getTextWidth(ssThreeModification, baseFontSize), getTextWidth(asFiveModification, baseFontSize));
|
|
138
|
+
const widthOfRightModification =
|
|
139
|
+
Math.max(getTextWidth(ssFiveModification, baseFontSize), getTextWidth(asThreeModification, baseFontSize));
|
|
140
|
+
const widthOfLeftText = Math.max(getTextWidth(ssLeftText, baseFontSize), getTextWidth(asLeftText, baseFontSize));
|
|
141
|
+
const widthOfRightText = Math.max(getTextWidth(ssRightText, baseFontSize), getTextWidth(asRightText, baseFontSize));
|
|
142
|
+
const width =
|
|
143
|
+
widthOfLeftText + widthOfLeftModification + widthOfBases +
|
|
144
|
+
widthOfRightModification + widthOfRightText + baseDiameter;
|
|
145
|
+
const height = asExists ? 11 * baseRadius : 9 * baseRadius;
|
|
146
|
+
const xOfTitle = baseRadius; // Math.round(width / 4),
|
|
147
|
+
const uniqueBases = asExists ? [...new Set(ssBases.concat(asBases))] : [...new Set(ssBases)];
|
|
148
|
+
const isPtoExist =
|
|
149
|
+
asExists ? [...new Set(ssPtoStatuses.concat(asPtoStatuses))].includes(true) :
|
|
150
|
+
[...new Set(ssPtoStatuses)].includes(true);
|
|
151
|
+
const startFrom = isPtoExist ? 1 : 0;
|
|
152
|
+
const xOfLeftTexts = 0;
|
|
153
|
+
const xOfLeftModifications = xOfLeftTexts + widthOfLeftText - 5;
|
|
154
|
+
const xOfSsRightModifications = ssRightOverhangs * baseDiameter + getXOfBaseCircles(-0.5, 0);
|
|
155
|
+
const xOfAsRightModifications = asRightOverhangs * baseDiameter + getXOfBaseCircles(-0.5, 0);
|
|
156
|
+
const xOfRightTexts =
|
|
157
|
+
Math.max(xOfSsRightModifications, xOfAsRightModifications) +
|
|
158
|
+
widthOfLeftModification + baseDiameter * (Math.max(ssRightOverhangs, asRightOverhangs));
|
|
159
|
+
const yOfTitle = baseRadius;
|
|
160
|
+
const yOfSsTexts = 4 * baseRadius;
|
|
161
|
+
const yOfAsTexts = 7 * baseRadius;
|
|
162
|
+
const yOfComment = asExists ? 11 * baseRadius : 8.5 * baseRadius;
|
|
163
|
+
const yOfSsCircles = 3.5 * baseRadius;
|
|
164
|
+
const yOfAsCircles = 6.5 * baseRadius;
|
|
165
|
+
const yOfCirclesInLegends = asExists ? 9 * baseRadius : 6 * baseRadius;
|
|
166
|
+
const yOfTextLegend = asExists ? 9.5 * baseRadius - 3 : yOfAsCircles - 3;
|
|
167
|
+
|
|
168
|
+
const image = svg.render(width, height);
|
|
153
169
|
|
|
154
170
|
image.append(
|
|
155
171
|
svg.text(ssLeftText, xOfLeftTexts, yOfSsTexts, baseFontSize, fontColor),
|
|
@@ -162,31 +178,40 @@ export function drawAxolabsPattern(patternName: string, asExists: boolean, ssBas
|
|
|
162
178
|
asExists ? svg.text(asFiveModification, xOfAsRightModifications, yOfAsTexts, baseFontSize, modificationsColor) : '',
|
|
163
179
|
svg.text(comment, xOfLeftTexts, yOfComment, legendFontSize, fontColor),
|
|
164
180
|
isPtoExist ? svg.star(baseRadius, yOfCirclesInLegends, psLinkageColor) : '',
|
|
165
|
-
isPtoExist ? svg.text('ps linkage', 2 * baseRadius - 8, yOfTextLegend, legendFontSize, fontColor) : ''
|
|
181
|
+
isPtoExist ? svg.text('ps linkage', 2 * baseRadius - 8, yOfTextLegend, legendFontSize, fontColor) : '',
|
|
166
182
|
);
|
|
167
183
|
|
|
168
184
|
let numberOfSsNucleotides = 0;
|
|
169
|
-
for (let i = 0; i < ssBases.length; i++)
|
|
185
|
+
for (let i = 0; i < ssBases.length; i++) {
|
|
170
186
|
if (ssBases[i].slice(-3) != '(o)')
|
|
171
187
|
numberOfSsNucleotides++;
|
|
188
|
+
}
|
|
172
189
|
let nucleotideCounter = numberOfSsNucleotides;
|
|
173
190
|
for (let i = ssBases.length - 1; i > -1; i--) {
|
|
174
191
|
if (ssBases[i].slice(-3) != '(o)')
|
|
175
192
|
nucleotideCounter--;
|
|
176
193
|
image.append(
|
|
177
194
|
svg.circle(getXOfBaseCircles(i, ssRightOverhangs), yOfSsCircles, baseRadius, getBaseColor(ssBases[i])),
|
|
178
|
-
svg.text(getTextInsideCircle(ssBases, i, nucleotideCounter, numberOfSsNucleotides, enumerateModifications),
|
|
179
|
-
|
|
195
|
+
svg.text(getTextInsideCircle(ssBases, i, nucleotideCounter, numberOfSsNucleotides, enumerateModifications),
|
|
196
|
+
getXOfBaseCircles(i, ssRightOverhangs) +
|
|
197
|
+
getShiftToAlignNumberInsideCircle(ssBases, ssBases.length - i, numberOfSsNucleotides - nucleotideCounter),
|
|
198
|
+
yOfSsTexts, baseFontSize, getFontColorVisibleOnBackground(axolabsMap[ssBases[i]]['color'])),
|
|
199
|
+
ssPtoStatuses[i] ?
|
|
200
|
+
svg.star(getXOfBaseCircles(i, ssRightOverhangs) + baseRadius, yOfSsTexts + psLinkageRadius, psLinkageColor) :
|
|
201
|
+
'',
|
|
180
202
|
);
|
|
181
203
|
}
|
|
182
204
|
image.append(
|
|
183
|
-
ssPtoStatuses[ssBases.length] ?
|
|
205
|
+
ssPtoStatuses[ssBases.length] ?
|
|
206
|
+
svg.star(getXOfBaseCircles(ssBases.length, ssRightOverhangs) +
|
|
207
|
+
baseRadius, yOfSsTexts + psLinkageRadius, psLinkageColor) : '',
|
|
184
208
|
);
|
|
185
209
|
|
|
186
210
|
let numberOfAsNucleotides = 0;
|
|
187
|
-
for (let i = 0; i < asBases.length; i++)
|
|
211
|
+
for (let i = 0; i < asBases.length; i++) {
|
|
188
212
|
if (asBases[i].slice(-3) != '(o)')
|
|
189
213
|
numberOfAsNucleotides++;
|
|
214
|
+
}
|
|
190
215
|
if (asExists) {
|
|
191
216
|
let nucleotideCounter = numberOfAsNucleotides;
|
|
192
217
|
for (let i = asBases.length - 1; i > -1; i--) {
|
|
@@ -194,21 +219,31 @@ export function drawAxolabsPattern(patternName: string, asExists: boolean, ssBas
|
|
|
194
219
|
nucleotideCounter--;
|
|
195
220
|
image.append(
|
|
196
221
|
svg.circle(getXOfBaseCircles(i, asRightOverhangs), yOfAsCircles, baseRadius, getBaseColor(asBases[i])),
|
|
197
|
-
svg.text(getTextInsideCircle(
|
|
198
|
-
|
|
222
|
+
svg.text(getTextInsideCircle(
|
|
223
|
+
asBases, i, numberOfAsNucleotides - nucleotideCounter - 1, numberOfAsNucleotides, enumerateModifications),
|
|
224
|
+
getXOfBaseCircles(i, asRightOverhangs) +
|
|
225
|
+
getShiftToAlignNumberInsideCircle(asBases, i, nucleotideCounter + 1),
|
|
226
|
+
yOfAsTexts, baseFontSize, getFontColorVisibleOnBackground(axolabsMap[asBases[i]]['color'])),
|
|
227
|
+
asPtoStatuses[i] ? svg.star(getXOfBaseCircles(i, asRightOverhangs) +
|
|
228
|
+
baseRadius, yOfAsTexts + psLinkageRadius, psLinkageColor) : '',
|
|
199
229
|
);
|
|
200
230
|
}
|
|
201
231
|
image.append(
|
|
202
|
-
asPtoStatuses[asBases.length] ?
|
|
232
|
+
asPtoStatuses[asBases.length] ?
|
|
233
|
+
svg.star(getXOfBaseCircles(asBases.length, asRightOverhangs) +
|
|
234
|
+
baseRadius, yOfAsTexts + psLinkageRadius, psLinkageColor) : '',
|
|
203
235
|
);
|
|
204
236
|
}
|
|
205
237
|
|
|
206
|
-
|
|
238
|
+
const title = patternName + ' for ' +
|
|
239
|
+
String(numberOfSsNucleotides) + (asExists ? '/' + String(numberOfAsNucleotides) : '') + 'mer';
|
|
207
240
|
image.append(svg.text(title, xOfTitle, yOfTitle, baseFontSize, titleFontColor));
|
|
208
|
-
for (let i = 0; i < uniqueBases.length; i++)
|
|
241
|
+
for (let i = 0; i < uniqueBases.length; i++) {
|
|
209
242
|
image.append(
|
|
210
243
|
svg.circle(getEquidistantXForLegend(i), yOfCirclesInLegends, legendRadius, getBaseColor(uniqueBases[i])),
|
|
211
|
-
svg.text(uniqueBases[i], getEquidistantXForLegend(i) +
|
|
244
|
+
svg.text(uniqueBases[i], getEquidistantXForLegend(i) +
|
|
245
|
+
legendRadius + 4, yOfTextLegend, legendFontSize, fontColor),
|
|
212
246
|
);
|
|
247
|
+
}
|
|
213
248
|
return image;
|
|
214
|
-
}
|
|
249
|
+
}
|