@datagrok/sequence-translator 1.2.9 → 1.3.1
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/CHANGELOG.md +13 -0
- package/dist/package-test.js +1 -1
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/files/polytool-rules/rules_example.json +34 -0
- package/package.json +4 -3
- package/src/apps/pattern/model/event-bus.ts +23 -6
- package/src/apps/pattern/model/translator.ts +27 -1
- package/src/apps/pattern/view/components/bulk-convert/column-input.ts +13 -3
- package/src/apps/pattern/view/components/bulk-convert/table-controls.ts +4 -3
- package/src/apps/pattern/view/components/load-block-controls.ts +4 -2
- package/src/apps/pattern/view/svg-utils/const.ts +15 -1
- package/src/apps/pattern/view/svg-utils/legend-block.ts +92 -0
- package/src/apps/pattern/view/svg-utils/strands-block.ts +335 -0
- package/src/apps/pattern/view/svg-utils/svg-block-base.ts +37 -0
- package/src/apps/pattern/view/svg-utils/svg-display-manager.ts +4 -5
- package/src/apps/pattern/view/svg-utils/svg-element-factory.ts +16 -4
- package/src/apps/pattern/view/svg-utils/svg-renderer.ts +32 -377
- package/src/apps/pattern/view/svg-utils/text-dimensions-calculator.ts +29 -0
- package/src/apps/pattern/view/svg-utils/title-block.ts +53 -0
- package/src/apps/translator/view/ui.ts +1 -1
- package/src/package.ts +22 -6
- package/src/polytool/const.ts +3 -15
- package/src/polytool/pt-conversion.ts +307 -0
- package/src/polytool/pt-dialog.ts +115 -0
- package/src/polytool/pt-enumeration.ts +127 -0
- package/src/polytool/pt-rules.ts +73 -0
- package/src/polytool/utils.ts +7 -0
- package/src/tests/helm-to-nucleotides.ts +0 -5
- package/tsconfig.json +1 -1
- package/src/apps/pattern/view/svg-utils/dimensions-calculator.ts +0 -498
- package/src/polytool/transformation.ts +0 -326
- package/src/polytool/ui.ts +0 -59
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import {NUCLEOTIDES} from '../../../common/model/const';
|
|
2
|
+
import {STRAND, STRANDS, TERMINUS} from '../../model/const';
|
|
3
|
+
import {PatternConfiguration} from '../../model/types';
|
|
4
|
+
import {isOverhangNucleotide} from '../../model/utils';
|
|
5
|
+
import {SVG_CIRCLE_SIZES, SVG_ELEMENT_COLORS, SVG_TEXT_FONT_SIZES} from './const';
|
|
6
|
+
import {SVGBlockBase} from './svg-block-base';
|
|
7
|
+
import {SVGElementFactory} from './svg-element-factory';
|
|
8
|
+
import {TextDimensionsCalculator} from './text-dimensions-calculator';
|
|
9
|
+
import {computeTextColorForNucleobaseLabel, getNucleobaseColorFromStyleMap, getNucleobaseLabelForCircle} from './utils';
|
|
10
|
+
|
|
11
|
+
const NUMERIC_LABEL_PADDING = 5;
|
|
12
|
+
const SENSE_STRAND_HEIGHT = SVG_TEXT_FONT_SIZES.NUCLEOBASE +
|
|
13
|
+
NUMERIC_LABEL_PADDING + SVG_CIRCLE_SIZES.NUCLEOBASE_DIAMETER;
|
|
14
|
+
const SENSE_STRAND_PADDING = 10;
|
|
15
|
+
const LEFT_LABEL_WIDTH = 55;
|
|
16
|
+
const SENSE_STRAND_HORIZONTAL_SHIFT = SENSE_STRAND_PADDING + LEFT_LABEL_WIDTH;
|
|
17
|
+
const RIGHT_LABEL_WIDTH = 20;
|
|
18
|
+
|
|
19
|
+
export class StrandsBlock extends SVGBlockBase {
|
|
20
|
+
private strands: SVGBlockBase[];
|
|
21
|
+
private labels: SVGBlockBase[];
|
|
22
|
+
constructor(
|
|
23
|
+
svgElementFactory: SVGElementFactory,
|
|
24
|
+
config: PatternConfiguration,
|
|
25
|
+
yShift: number
|
|
26
|
+
) {
|
|
27
|
+
super(svgElementFactory, config, yShift);
|
|
28
|
+
const strandTypes = STRANDS.filter((strandType) => config.nucleotideSequences[strandType].length > 0);
|
|
29
|
+
|
|
30
|
+
this.strands = strandTypes
|
|
31
|
+
.map((strand) => new SingleStrandBlock(this.svgElementFactory, config, yShift, strand));
|
|
32
|
+
|
|
33
|
+
this.labels = strandTypes.map(
|
|
34
|
+
(strandType, idx) =>
|
|
35
|
+
new StrandLabel(this.svgElementFactory, config, yShift, strandType, this.strands[idx] as SingleStrandBlock)
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get svgElements(): SVGElement[] {
|
|
40
|
+
const elements = [
|
|
41
|
+
...this.strands,
|
|
42
|
+
...this.labels
|
|
43
|
+
].map((block) => block.svgElements).flat();
|
|
44
|
+
return elements;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getContentWidth(): number {
|
|
48
|
+
return Math.max(...this.labels.map((labelBlock) => labelBlock.getContentWidth()));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
getContentHeight(): number {
|
|
52
|
+
const result = this.strands
|
|
53
|
+
.reduce((acc, strand) => acc + strand.getContentHeight(), 0);
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class SingleStrandBlock extends SVGBlockBase {
|
|
59
|
+
private _svgElements: SVGElement[];
|
|
60
|
+
private nucleotideNumericLabels: (number | null)[];
|
|
61
|
+
constructor(
|
|
62
|
+
protected svgElementFactory: SVGElementFactory,
|
|
63
|
+
protected config: PatternConfiguration,
|
|
64
|
+
protected yShift: number,
|
|
65
|
+
private strand: STRAND
|
|
66
|
+
) {
|
|
67
|
+
super(svgElementFactory, config, yShift);
|
|
68
|
+
|
|
69
|
+
// WARNING: should be computed before creating circles
|
|
70
|
+
this.nucleotideNumericLabels = this.computeNucleotideNumericLabels();
|
|
71
|
+
|
|
72
|
+
if (this.strand === STRAND.ANTISENSE) {
|
|
73
|
+
this.config.phosphorothioateLinkageFlags[this.strand].reverse();
|
|
74
|
+
this.config.nucleotideSequences[this.strand].reverse();
|
|
75
|
+
this.nucleotideNumericLabels.reverse();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this._svgElements = [
|
|
79
|
+
this.createStrandCircles(),
|
|
80
|
+
this.createPTOLinkageStars(),
|
|
81
|
+
].flat();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private computeNucleotideNumericLabels(): (number | null)[] {
|
|
85
|
+
let index = 0;
|
|
86
|
+
const nucleotides = this.config.nucleotideSequences[this.strand];
|
|
87
|
+
const indices = nucleotides.map((nucleotide) => {
|
|
88
|
+
if (isOverhangNucleotide(nucleotide)) return null;
|
|
89
|
+
index++;
|
|
90
|
+
return index;
|
|
91
|
+
});
|
|
92
|
+
return indices;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
get svgElements(): SVGElement[] {
|
|
96
|
+
return this._svgElements;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private getStrandCircleYShift(): number {
|
|
100
|
+
return getStrandCircleYShift(this.strand, this.yShift);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private createStrandCircles(): SVGElement[] {
|
|
104
|
+
const defaultShift = {
|
|
105
|
+
x: SVG_CIRCLE_SIZES.NUCLEOBASE_RADIUS + SENSE_STRAND_HORIZONTAL_SHIFT,
|
|
106
|
+
y: this.getStrandCircleYShift()
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const nucleotides = this.config.nucleotideSequences[this.strand];
|
|
110
|
+
|
|
111
|
+
const elements = nucleotides
|
|
112
|
+
.map((nucleotide, index) => this.createNucleotideElementGroup(nucleotide, index, defaultShift)).flat();
|
|
113
|
+
|
|
114
|
+
return elements;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private createNucleotideElementGroup(
|
|
118
|
+
nucleotide: string,
|
|
119
|
+
index: number,
|
|
120
|
+
defaultShift: {x: number, y: number}
|
|
121
|
+
): SVGElement[] {
|
|
122
|
+
const circleElements = this.createNucleotideCircleElements(nucleotide, index, defaultShift);
|
|
123
|
+
const numericLabel = this.config.nucleotidesWithNumericLabels.includes(nucleotide) ?
|
|
124
|
+
this.createNucleotideNumericLabel(index, defaultShift) : null;
|
|
125
|
+
|
|
126
|
+
return [...circleElements, numericLabel].filter((element) => element !== null) as SVGElement[];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private createNucleotideCircleElements(
|
|
130
|
+
nucleotide: string,
|
|
131
|
+
index: number,
|
|
132
|
+
defaultShift: {x: number, y: number}
|
|
133
|
+
): (SVGElement | null)[] {
|
|
134
|
+
const color = getNucleobaseColorFromStyleMap(nucleotide);
|
|
135
|
+
const centerPosition = {...defaultShift, x: defaultShift.x + index * SVG_CIRCLE_SIZES.NUCLEOBASE_DIAMETER};
|
|
136
|
+
|
|
137
|
+
const circle = this.svgElementFactory
|
|
138
|
+
.createCircleElement(centerPosition, SVG_CIRCLE_SIZES.NUCLEOBASE_RADIUS, color);
|
|
139
|
+
|
|
140
|
+
const nonModifiedNucleotideLetterLabel = this.createNucleotideLetterLabel(index, defaultShift, nucleotide);
|
|
141
|
+
|
|
142
|
+
return [circle, nonModifiedNucleotideLetterLabel];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private getNucleotideCircleCenterPosition(
|
|
146
|
+
index: number,
|
|
147
|
+
defaultShift: {x: number, y: number}
|
|
148
|
+
): {x: number, y: number} {
|
|
149
|
+
return {
|
|
150
|
+
x: defaultShift.x + index * SVG_CIRCLE_SIZES.NUCLEOBASE_DIAMETER,
|
|
151
|
+
y: defaultShift.y
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private createNucleotideLetterLabel(
|
|
156
|
+
index: number,
|
|
157
|
+
defaultShift: {x: number, y: number},
|
|
158
|
+
nucleobase: string
|
|
159
|
+
): SVGElement | null {
|
|
160
|
+
if (!NUCLEOTIDES.includes(nucleobase))
|
|
161
|
+
return null;
|
|
162
|
+
|
|
163
|
+
const text = getNucleobaseLabelForCircle(nucleobase);
|
|
164
|
+
const color = computeTextColorForNucleobaseLabel(nucleobase);
|
|
165
|
+
// position at the very center of the circle
|
|
166
|
+
const position = this.getPositionForNucleotideLabel(index, defaultShift);
|
|
167
|
+
return this.svgElementFactory.createTextElement(
|
|
168
|
+
text,
|
|
169
|
+
position,
|
|
170
|
+
SVG_TEXT_FONT_SIZES.NUCLEOBASE,
|
|
171
|
+
color
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Returns the position for the letter with its center being at the center of the circle */
|
|
176
|
+
private getPositionForNucleotideLabel(
|
|
177
|
+
index: number,
|
|
178
|
+
defaultShift: {x: number, y: number}
|
|
179
|
+
): {x: number, y: number} {
|
|
180
|
+
const circleCenter = this.getNucleotideCircleCenterPosition(index, defaultShift);
|
|
181
|
+
const textDimensions = TextDimensionsCalculator.getTextDimensions('A', SVG_TEXT_FONT_SIZES.NUCLEOBASE);
|
|
182
|
+
return {
|
|
183
|
+
x: circleCenter.x - textDimensions.width / 2,
|
|
184
|
+
// the coefficient 1/3 is fine-tuned to make the text look centered
|
|
185
|
+
y: circleCenter.y + textDimensions.height / 3
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private getNumericLabelYShift(
|
|
190
|
+
defaultShift: {x: number, y: number}
|
|
191
|
+
): number {
|
|
192
|
+
return this.strand === STRAND.SENSE ?
|
|
193
|
+
defaultShift.y - (SVG_CIRCLE_SIZES.NUCLEOBASE_RADIUS + NUMERIC_LABEL_PADDING) :
|
|
194
|
+
defaultShift.y + SVG_CIRCLE_SIZES.NUCLEOBASE_RADIUS + NUMERIC_LABEL_PADDING + SVG_TEXT_FONT_SIZES.COMMENT;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private createNucleotideNumericLabel(
|
|
198
|
+
index: number,
|
|
199
|
+
defaultShift: {x: number, y: number}
|
|
200
|
+
): SVGElement | null {
|
|
201
|
+
const label = this.nucleotideNumericLabels[index];
|
|
202
|
+
if (label === null) return null;
|
|
203
|
+
|
|
204
|
+
const width = TextDimensionsCalculator.getTextDimensions(label.toString(), SVG_TEXT_FONT_SIZES.COMMENT).width;
|
|
205
|
+
|
|
206
|
+
const position = {
|
|
207
|
+
x: defaultShift.x + index * SVG_CIRCLE_SIZES.NUCLEOBASE_DIAMETER - width / 2,
|
|
208
|
+
y: this.getNumericLabelYShift(defaultShift)
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
return this.svgElementFactory.createTextElement(
|
|
212
|
+
label.toString(),
|
|
213
|
+
position,
|
|
214
|
+
SVG_TEXT_FONT_SIZES.COMMENT,
|
|
215
|
+
SVG_ELEMENT_COLORS.TEXT
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private createPTOLinkageStars(): SVGElement[] {
|
|
220
|
+
const ptoFlags = this.config.phosphorothioateLinkageFlags[this.strand];
|
|
221
|
+
|
|
222
|
+
const yShift = this.getStrandCircleYShift() + SVG_CIRCLE_SIZES.NUCLEOBASE_RADIUS * 0.8;
|
|
223
|
+
|
|
224
|
+
const elements = ptoFlags
|
|
225
|
+
.map((ptoFlag, index) => {
|
|
226
|
+
if (!ptoFlag) return null;
|
|
227
|
+
|
|
228
|
+
const centerPosition = {
|
|
229
|
+
x: SENSE_STRAND_HORIZONTAL_SHIFT + index * SVG_CIRCLE_SIZES.NUCLEOBASE_DIAMETER,
|
|
230
|
+
y: yShift
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const color = SVG_ELEMENT_COLORS.LINKAGE_STAR;
|
|
234
|
+
return this.svgElementFactory.createStarElement(centerPosition, color);
|
|
235
|
+
})
|
|
236
|
+
.filter((element) => element !== null) as SVGElement[];
|
|
237
|
+
|
|
238
|
+
return elements;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
getContentWidth(): number {
|
|
242
|
+
const numberOfMonomers = this.config.nucleotideSequences[this.strand].length;
|
|
243
|
+
return numberOfMonomers * SVG_CIRCLE_SIZES.NUCLEOBASE_DIAMETER;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
getContentHeight(): number {
|
|
247
|
+
return SENSE_STRAND_HEIGHT + SENSE_STRAND_PADDING;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
class StrandLabel extends SVGBlockBase {
|
|
252
|
+
private _svgElements: SVGElement[];
|
|
253
|
+
constructor(
|
|
254
|
+
protected svgElementFactory: SVGElementFactory,
|
|
255
|
+
protected config: PatternConfiguration,
|
|
256
|
+
protected yShift: number,
|
|
257
|
+
private strand: STRAND,
|
|
258
|
+
private strandSvgWrapper: SingleStrandBlock
|
|
259
|
+
) {
|
|
260
|
+
super(svgElementFactory, config, yShift);
|
|
261
|
+
this._svgElements = this.createSVGElements();
|
|
262
|
+
// this.strandSvgWrapper.shiftElements({x: this.getLeftLabelWidth(), y: 0});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private createSVGElements(): SVGElement[] {
|
|
266
|
+
const elements = [
|
|
267
|
+
this.createLeftLabel(),
|
|
268
|
+
this.createRightLabel()
|
|
269
|
+
];
|
|
270
|
+
return elements;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private getLeftLabelWidth(): number {
|
|
274
|
+
return LEFT_LABEL_WIDTH;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private getRightLabelWidth(): number {
|
|
278
|
+
return RIGHT_LABEL_WIDTH;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private createLeftLabel(): SVGTextElement {
|
|
282
|
+
const terminus = this.strand === STRAND.SENSE ? TERMINUS.FIVE_PRIME : TERMINUS.THREE_PRIME;
|
|
283
|
+
const text = `${this.strand}: ${terminus} `;
|
|
284
|
+
const textDimensions = TextDimensionsCalculator.getTextDimensions(text, SVG_TEXT_FONT_SIZES.NUCLEOBASE);
|
|
285
|
+
const position = {
|
|
286
|
+
x: SENSE_STRAND_PADDING,
|
|
287
|
+
y: getStrandCircleYShift(this.strand, this.yShift) + textDimensions.height / 3
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
return this.svgElementFactory.createTextElement(
|
|
291
|
+
text,
|
|
292
|
+
position,
|
|
293
|
+
SVG_TEXT_FONT_SIZES.NUCLEOBASE,
|
|
294
|
+
SVG_ELEMENT_COLORS.TEXT
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
private createRightLabel(): SVGTextElement {
|
|
299
|
+
const terminus = this.strand === STRAND.SENSE ? TERMINUS.THREE_PRIME : TERMINUS.FIVE_PRIME;
|
|
300
|
+
const text = ` ${terminus}`;
|
|
301
|
+
const textDimensions = TextDimensionsCalculator.getTextDimensions(text, SVG_TEXT_FONT_SIZES.NUCLEOBASE);
|
|
302
|
+
const position = {
|
|
303
|
+
x: SENSE_STRAND_HORIZONTAL_SHIFT + this.strandSvgWrapper.getContentWidth() + 5,
|
|
304
|
+
y: getStrandCircleYShift(this.strand, this.yShift) + textDimensions.height / 3
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
return this.svgElementFactory.createTextElement(
|
|
308
|
+
text,
|
|
309
|
+
position,
|
|
310
|
+
SVG_TEXT_FONT_SIZES.NUCLEOBASE,
|
|
311
|
+
SVG_ELEMENT_COLORS.TEXT
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
get svgElements(): SVGElement[] {
|
|
316
|
+
return this._svgElements;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
getContentWidth(): number {
|
|
320
|
+
return this.strandSvgWrapper.getContentWidth() + this.getLeftLabelWidth() + this.getRightLabelWidth() + SENSE_STRAND_PADDING;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
getContentHeight(): number {
|
|
324
|
+
return this.strandSvgWrapper.getContentHeight();
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function getStrandCircleYShift(
|
|
329
|
+
strand: STRAND,
|
|
330
|
+
defaultYShift: number
|
|
331
|
+
): number {
|
|
332
|
+
return strand === STRAND.SENSE ?
|
|
333
|
+
defaultYShift + NUMERIC_LABEL_PADDING + SVG_TEXT_FONT_SIZES.COMMENT + SVG_CIRCLE_SIZES.NUCLEOBASE_RADIUS :
|
|
334
|
+
defaultYShift + SENSE_STRAND_HEIGHT + SENSE_STRAND_PADDING + SVG_CIRCLE_SIZES.NUCLEOBASE_RADIUS;
|
|
335
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {PatternConfiguration} from '../../model/types';
|
|
2
|
+
import {SVGElementFactory} from './svg-element-factory';
|
|
3
|
+
|
|
4
|
+
/** Horizontal block within SVG: title, strands, legend */
|
|
5
|
+
export abstract class SVGBlockBase {
|
|
6
|
+
// protected svgElements: SVGElement[] = [];
|
|
7
|
+
constructor(
|
|
8
|
+
protected svgElementFactory: SVGElementFactory,
|
|
9
|
+
protected config: PatternConfiguration,
|
|
10
|
+
protected yShift: number
|
|
11
|
+
) {}
|
|
12
|
+
|
|
13
|
+
abstract get svgElements(): SVGElement[];
|
|
14
|
+
|
|
15
|
+
abstract getContentHeight(): number;
|
|
16
|
+
|
|
17
|
+
shiftElements(shift: {x: number, y: number}): void {
|
|
18
|
+
this.svgElements.forEach((element) => {
|
|
19
|
+
const transform = element.getAttribute('transform') || '';
|
|
20
|
+
const match = transform.match(/translate\(([^,]+),([^,]+)\)/);
|
|
21
|
+
const x = match ? parseFloat(match[1]) : 0;
|
|
22
|
+
const y = match ? parseFloat(match[2]) : 0;
|
|
23
|
+
const newTransform = `translate(${x + shift.x},${y + shift.y})`;
|
|
24
|
+
element.setAttribute('transform', `${transform} ${newTransform}`);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
adjustContentWithinGlobalContainer(globalWidth: number): void {
|
|
29
|
+
const contentWidth = this.getContentWidth();
|
|
30
|
+
if (contentWidth < globalWidth) {
|
|
31
|
+
const shift = (globalWidth - contentWidth) / 2;
|
|
32
|
+
this.shiftElements({x: shift, y: 0});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
abstract getContentWidth(): number;
|
|
37
|
+
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
/* Do not change these import lines to match external modules in webpack configuration */
|
|
2
|
-
import * as grok from 'datagrok-api/grok';
|
|
3
2
|
import * as ui from 'datagrok-api/ui';
|
|
4
|
-
import * as DG from 'datagrok-api/dg';
|
|
5
3
|
|
|
6
|
-
import {PatternConfiguration, StrandType} from '../../model/types';
|
|
7
4
|
import {EventBus} from '../../model/event-bus';
|
|
8
|
-
import {
|
|
5
|
+
import {PatternConfiguration} from '../../model/types';
|
|
9
6
|
//@ts-ignore
|
|
10
7
|
import * as svgExport from 'save-svg-as-png';
|
|
8
|
+
import {NucleotidePatternSVGRenderer} from './svg-renderer';
|
|
11
9
|
|
|
12
10
|
export class SvgDisplayManager {
|
|
13
11
|
private svgDisplayDiv = ui.div([]);
|
|
@@ -16,7 +14,7 @@ export class SvgDisplayManager {
|
|
|
16
14
|
private constructor(
|
|
17
15
|
private eventBus: EventBus
|
|
18
16
|
) {
|
|
19
|
-
eventBus.
|
|
17
|
+
eventBus.updateSvgContainer$.subscribe(() => this.updateSvgContainer());
|
|
20
18
|
eventBus.svgSaveRequested$.subscribe(() => this.saveSvgAsPng());
|
|
21
19
|
}
|
|
22
20
|
|
|
@@ -33,6 +31,7 @@ export class SvgDisplayManager {
|
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
private createSvg(patternConfig: PatternConfiguration) {
|
|
34
|
+
// const renderer = new NucleotidePatternSVGRenderer(patternConfig);
|
|
36
35
|
const renderer = new NucleotidePatternSVGRenderer(patternConfig);
|
|
37
36
|
const svg = renderer.renderPattern();
|
|
38
37
|
return svg;
|
|
@@ -13,7 +13,7 @@ export class SVGElementFactory {
|
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
createCanvas(width: number, height: number): SVGElement {
|
|
17
17
|
const svgElement = this.createElement('svg') as SVGElement;
|
|
18
18
|
this.setAttributes(svgElement, {
|
|
19
19
|
id: 'mySvg',
|
|
@@ -23,7 +23,7 @@ export class SVGElementFactory {
|
|
|
23
23
|
return svgElement;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
createCircleElement(centerPosition: Position, radius: number, color: string): SVGCircleElement {
|
|
27
27
|
const circle = this.createElement('circle') as SVGCircleElement;
|
|
28
28
|
this.setAttributes(circle, {
|
|
29
29
|
cx: centerPosition.x,
|
|
@@ -34,7 +34,7 @@ export class SVGElementFactory {
|
|
|
34
34
|
return circle;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
createTextElement(textContent: string, position: Position, fontSize: number, color: string): SVGTextElement {
|
|
38
38
|
const textElement = this.createElement('text') as SVGTextElement;
|
|
39
39
|
this.setAttributes(textElement, {
|
|
40
40
|
'x': position.x,
|
|
@@ -48,7 +48,7 @@ export class SVGElementFactory {
|
|
|
48
48
|
return textElement;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
createStarElement(centerPosition: Position, color: string): SVGPolygonElement {
|
|
52
52
|
const star = this.createElement('polygon') as SVGPolygonElement;
|
|
53
53
|
const points = this.computeStarVertexCoordinates(centerPosition);
|
|
54
54
|
const pointsAttribute = points.map((point) => point.join(',')).join(' ');
|
|
@@ -79,4 +79,16 @@ export class SVGElementFactory {
|
|
|
79
79
|
|
|
80
80
|
return points;
|
|
81
81
|
}
|
|
82
|
+
|
|
83
|
+
createRectangleElement(topLeftCorner: Position, width: number, height: number, color: string): SVGRectElement {
|
|
84
|
+
const rectangle = this.createElement('rect') as SVGRectElement;
|
|
85
|
+
this.setAttributes(rectangle, {
|
|
86
|
+
x: topLeftCorner.x,
|
|
87
|
+
y: topLeftCorner.y,
|
|
88
|
+
width,
|
|
89
|
+
height,
|
|
90
|
+
fill: color,
|
|
91
|
+
});
|
|
92
|
+
return rectangle;
|
|
93
|
+
}
|
|
82
94
|
}
|