@sjcrh/proteinpaint-rust 2.152.1-0 → 2.166.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/Cargo.toml +5 -0
- package/README.md +6 -8
- package/package.json +1 -1
- package/src/bigwig.rs +73 -162
- package/src/manhattan_plot.rs +523 -0
- package/src/ollama.rs +6 -0
- package/src/sjprovider.rs +7 -0
package/Cargo.toml
CHANGED
|
@@ -19,6 +19,7 @@ ndarray = "0.16.1"
|
|
|
19
19
|
hdf5 = { package = "hdf5-metno", version = "0.9.0" }
|
|
20
20
|
nalgebra = {version = "0.32.2", features = ["serde-serialize"]}
|
|
21
21
|
plotters = "0.3.4"
|
|
22
|
+
tiny-skia = "0.11"
|
|
22
23
|
colorgrad = "0.6.2"
|
|
23
24
|
statrs = "^0.16.0"
|
|
24
25
|
fishers_exact="^1.0.1"
|
|
@@ -42,6 +43,7 @@ r2d2 = "0.8.10"
|
|
|
42
43
|
rig-core = "0.22.0"
|
|
43
44
|
url = "2.5.7"
|
|
44
45
|
async-stream = "0.3.6"
|
|
46
|
+
base64 = "0.22.1"
|
|
45
47
|
|
|
46
48
|
[profile.release]
|
|
47
49
|
lto = "fat"
|
|
@@ -125,3 +127,6 @@ path="src/readH5.rs"
|
|
|
125
127
|
name="aichatbot"
|
|
126
128
|
path="src/aichatbot.rs"
|
|
127
129
|
|
|
130
|
+
[[bin]]
|
|
131
|
+
name="manhattan_plot"
|
|
132
|
+
path="src/manhattan_plot.rs"
|
package/README.md
CHANGED
|
@@ -5,19 +5,17 @@ This directory holds the source code for rust-compiled utilities.
|
|
|
5
5
|
|
|
6
6
|
## Rust version
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
The current rust version is defined in `rust/rust-toolchain.toml`. When updating the rust version inside the rust docker image the `container/rust/build.sh` script parses the rust version from `rust-toolchain.toml` into `container/rust/Dockerfile` at runtime. This ensures consistency between local PP builds and the docker container in CI and production.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
The Github Actions workflow file `.github/workflows/CD-rust-build.yml` and `.github/workflows/CI-unit.yml` also parses the rust version from the `rust-toolchain.toml` to ensure the correct rust version is used for compiling the current rust code.
|
|
11
11
|
|
|
12
|
-
the
|
|
12
|
+
When bumping the rust version and publish the new rust build env image using:
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
The rust build docker file `container/rust/Dockerfile`.
|
|
14
|
+
https://github.com/stjude/proteinpaint/actions/workflows/CD-publish-rust-bookworm-env-image.yml
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
For publishing updated rust binaries, use this workflow.
|
|
19
17
|
|
|
20
|
-
https://github.com/stjude/proteinpaint/actions/workflows/CD-publish-rust-
|
|
18
|
+
https://github.com/stjude/proteinpaint/actions/workflows/CD-publish-rust-binaries.yml
|
|
21
19
|
|
|
22
20
|
## Code layout
|
|
23
21
|
|
package/package.json
CHANGED
package/src/bigwig.rs
CHANGED
|
@@ -158,19 +158,11 @@ fn main() {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
fn determine_max(n1: f64, n2: f64) -> f64 {
|
|
161
|
-
if n1 >= n2 {
|
|
162
|
-
n1
|
|
163
|
-
} else {
|
|
164
|
-
n2
|
|
165
|
-
}
|
|
161
|
+
if n1 >= n2 { n1 } else { n2 }
|
|
166
162
|
}
|
|
167
163
|
|
|
168
164
|
fn determine_min(n1: f64, n2: f64) -> f64 {
|
|
169
|
-
if n1 < n2 {
|
|
170
|
-
n1
|
|
171
|
-
} else {
|
|
172
|
-
n2
|
|
173
|
-
}
|
|
165
|
+
if n1 < n2 { n1 } else { n2 }
|
|
174
166
|
}
|
|
175
167
|
|
|
176
168
|
#[allow(dead_code)]
|
|
@@ -179,12 +171,8 @@ fn calculate_appropriate_zoom_level(zoom_headers: Vec<ZoomHeader>, difference: f
|
|
|
179
171
|
let mut closest_level = Option::<u32>::None; // Zoom level will be none at base-pair resolution
|
|
180
172
|
let mut unity_added = false;
|
|
181
173
|
let max_entries_parsed_limit = 100000; // Maximum number of entries that should be parsed from bigwig file. A very high number will lead to better accuracy as this will lead to selection of a lower zoom level. In contrast, a lower value will decrease run time at the cost of accuracy.
|
|
182
|
-
|
|
183
|
-
for reduction_level in zoom_headers
|
|
184
|
-
.into_iter()
|
|
185
|
-
.map(|entry| (entry.reduction_level))
|
|
186
|
-
.rev()
|
|
187
|
-
{
|
|
174
|
+
// Parsing out various zoom levels from bigwig file
|
|
175
|
+
for reduction_level in zoom_headers.into_iter().map(|entry| entry.reduction_level).rev() {
|
|
188
176
|
reduction_levels.push(reduction_level as u32);
|
|
189
177
|
}
|
|
190
178
|
if reduction_levels.contains(&1) == false {
|
|
@@ -206,21 +194,14 @@ fn calculate_appropriate_zoom_level(zoom_headers: Vec<ZoomHeader>, difference: f
|
|
|
206
194
|
closest_level
|
|
207
195
|
}
|
|
208
196
|
|
|
209
|
-
fn calculate_appropriate_zoom_level_ucsc(
|
|
210
|
-
zoom_headers: Vec<ZoomHeader>,
|
|
211
|
-
exact_offset: f64,
|
|
212
|
-
) -> Option<u32> {
|
|
197
|
+
fn calculate_appropriate_zoom_level_ucsc(zoom_headers: Vec<ZoomHeader>, exact_offset: f64) -> Option<u32> {
|
|
213
198
|
let mut reduction_levels = Vec::<u32>::new();
|
|
214
199
|
let mut closest_level = Option::<u32>::None; // Zoom level will be none at base-pair resolution
|
|
215
200
|
let desired_reduction: u32 = ((exact_offset as f64) / 2.0).floor() as u32;
|
|
216
201
|
let mut unity_added = false;
|
|
217
202
|
if desired_reduction > 1 {
|
|
218
203
|
// Parsing out various zoom levels from bigwig file
|
|
219
|
-
for reduction_level in zoom_headers
|
|
220
|
-
.into_iter()
|
|
221
|
-
.map(|entry| (entry.reduction_level))
|
|
222
|
-
.rev()
|
|
223
|
-
{
|
|
204
|
+
for reduction_level in zoom_headers.into_iter().map(|entry| entry.reduction_level).rev() {
|
|
224
205
|
reduction_levels.push(reduction_level as u32);
|
|
225
206
|
}
|
|
226
207
|
if reduction_levels.contains(&1) == false {
|
|
@@ -304,11 +285,9 @@ fn calculate_datapoints<
|
|
|
304
285
|
continue;
|
|
305
286
|
} else {
|
|
306
287
|
if (v.start as f64 <= start_region && end_region < v.end as f64)
|
|
307
|
-
|| (v.start as f64 >= start_region
|
|
308
|
-
&& (v.start as f64) < end_region)
|
|
288
|
+
|| (v.start as f64 >= start_region && (v.start as f64) < end_region)
|
|
309
289
|
|| (v.end as f64 >= start_region && (v.end as f64) < end_region)
|
|
310
|
-
|| (start_region >= v.start as f64
|
|
311
|
-
&& (v.end as f64) < end_region)
|
|
290
|
+
|| (start_region >= v.start as f64 && (v.end as f64) < end_region)
|
|
312
291
|
{
|
|
313
292
|
// Calculate sum and number for this region
|
|
314
293
|
//println!("i:{}", i);
|
|
@@ -316,16 +295,10 @@ fn calculate_datapoints<
|
|
|
316
295
|
//println!("v.end:{}", v.end);
|
|
317
296
|
//println!("start_region:{}", start_region);
|
|
318
297
|
//println!("end_region:{}", end_region);
|
|
319
|
-
let start_entry_within_region =
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
datapoints_num[i] += (stop_entry_within_region
|
|
324
|
-
- start_entry_within_region)
|
|
325
|
-
as f64;
|
|
326
|
-
datapoints_sum[i] += (stop_entry_within_region
|
|
327
|
-
- start_entry_within_region)
|
|
328
|
-
as f64
|
|
298
|
+
let start_entry_within_region = determine_max(v.start as f64, start_region);
|
|
299
|
+
let stop_entry_within_region = determine_min(v.end as f64, end_region);
|
|
300
|
+
datapoints_num[i] += (stop_entry_within_region - start_entry_within_region) as f64;
|
|
301
|
+
datapoints_sum[i] += (stop_entry_within_region - start_entry_within_region) as f64
|
|
329
302
|
* ((v.summary.sum as f64) / v.summary.bases_covered as f64);
|
|
330
303
|
//println!(
|
|
331
304
|
// "start_entry_within_region:{}",
|
|
@@ -346,30 +319,21 @@ fn calculate_datapoints<
|
|
|
346
319
|
//println!("v.end:{}", v.end);
|
|
347
320
|
//println!("start_region:{}", start_region);
|
|
348
321
|
//println!("end_region:{}", end_region);
|
|
349
|
-
if ((v.start as f64 <= start_region
|
|
350
|
-
|
|
351
|
-
|| (v.
|
|
352
|
-
|
|
353
|
-
|| (v.end as f64 >= start_region
|
|
354
|
-
&& (v.end as f64) < end_region)
|
|
355
|
-
|| (start_region >= v.start as f64
|
|
356
|
-
&& (v.end as f64) < end_region))
|
|
322
|
+
if ((v.start as f64 <= start_region && end_region < v.end as f64)
|
|
323
|
+
|| (v.start as f64 >= start_region && (v.start as f64) < end_region)
|
|
324
|
+
|| (v.end as f64 >= start_region && (v.end as f64) < end_region)
|
|
325
|
+
|| (start_region >= v.start as f64 && (v.end as f64) < end_region))
|
|
357
326
|
&& iter > 1
|
|
358
327
|
{
|
|
359
328
|
// Calculate sum and number for this region
|
|
360
329
|
//println!("Hello");
|
|
361
|
-
let start_entry_within_region =
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
- start_entry_within_region)
|
|
367
|
-
as f64;
|
|
368
|
-
datapoints_sum[i] += (stop_entry_within_region
|
|
369
|
-
- start_entry_within_region)
|
|
330
|
+
let start_entry_within_region = determine_max(v.start as f64, start_region);
|
|
331
|
+
let stop_entry_within_region = determine_min(v.end as f64, end_region);
|
|
332
|
+
datapoints_num[i] +=
|
|
333
|
+
(stop_entry_within_region - start_entry_within_region) as f64;
|
|
334
|
+
datapoints_sum[i] += (stop_entry_within_region - start_entry_within_region)
|
|
370
335
|
as f64
|
|
371
|
-
* ((v.summary.sum as f64)
|
|
372
|
-
/ v.summary.bases_covered as f64);
|
|
336
|
+
* ((v.summary.sum as f64) / v.summary.bases_covered as f64);
|
|
373
337
|
//println!(
|
|
374
338
|
// "start_entry_within_region inside:{}",
|
|
375
339
|
// start_entry_within_region
|
|
@@ -401,9 +365,7 @@ fn calculate_datapoints<
|
|
|
401
365
|
}
|
|
402
366
|
None => {
|
|
403
367
|
// To be used in nucleotide resolution
|
|
404
|
-
let bigwig_output = reader
|
|
405
|
-
.get_interval(&chrom, start_pos as u32, stop_pos as u32)
|
|
406
|
-
.unwrap();
|
|
368
|
+
let bigwig_output = reader.get_interval(&chrom, start_pos as u32, stop_pos as u32).unwrap();
|
|
407
369
|
let mut i = 0;
|
|
408
370
|
let mut start_region = datapoints_list[i];
|
|
409
371
|
let mut end_region = datapoints_list[i + 1];
|
|
@@ -415,11 +377,9 @@ fn calculate_datapoints<
|
|
|
415
377
|
continue;
|
|
416
378
|
} else {
|
|
417
379
|
if (v.start as f64 <= start_region && end_region < v.end as f64)
|
|
418
|
-
|| (v.start as f64 >= start_region
|
|
419
|
-
&& (v.start as f64) < end_region)
|
|
380
|
+
|| (v.start as f64 >= start_region && (v.start as f64) < end_region)
|
|
420
381
|
|| (v.end as f64 >= start_region && (v.end as f64) < end_region)
|
|
421
|
-
|| (start_region >= v.start as f64
|
|
422
|
-
&& (v.end as f64) < end_region)
|
|
382
|
+
|| (start_region >= v.start as f64 && (v.end as f64) < end_region)
|
|
423
383
|
{
|
|
424
384
|
// Calculate sum and number for this region
|
|
425
385
|
//println!("i:{}", i);
|
|
@@ -427,17 +387,11 @@ fn calculate_datapoints<
|
|
|
427
387
|
//println!("v.end:{}", v.end);
|
|
428
388
|
//println!("start_region:{}", start_region);
|
|
429
389
|
//println!("end_region:{}", end_region);
|
|
430
|
-
let start_entry_within_region =
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
- start_entry_within_region)
|
|
436
|
-
as f64;
|
|
437
|
-
datapoints_sum[i] += (stop_entry_within_region
|
|
438
|
-
- start_entry_within_region)
|
|
439
|
-
as f64
|
|
440
|
-
* v.value as f64;
|
|
390
|
+
let start_entry_within_region = determine_max(v.start as f64, start_region);
|
|
391
|
+
let stop_entry_within_region = determine_min(v.end as f64, end_region);
|
|
392
|
+
datapoints_num[i] += (stop_entry_within_region - start_entry_within_region) as f64;
|
|
393
|
+
datapoints_sum[i] +=
|
|
394
|
+
(stop_entry_within_region - start_entry_within_region) as f64 * v.value as f64;
|
|
441
395
|
//println!(
|
|
442
396
|
// "start_entry_within_region:{}",
|
|
443
397
|
// start_entry_within_region
|
|
@@ -457,27 +411,19 @@ fn calculate_datapoints<
|
|
|
457
411
|
//println!("v.end:{}", v.end);
|
|
458
412
|
//println!("start_region:{}", start_region);
|
|
459
413
|
//println!("end_region:{}", end_region);
|
|
460
|
-
if ((v.start as f64 <= start_region
|
|
461
|
-
|
|
462
|
-
|| (v.
|
|
463
|
-
|
|
464
|
-
|| (v.end as f64 >= start_region
|
|
465
|
-
&& (v.end as f64) < end_region)
|
|
466
|
-
|| (start_region >= v.start as f64
|
|
467
|
-
&& (v.end as f64) < end_region))
|
|
414
|
+
if ((v.start as f64 <= start_region && end_region < v.end as f64)
|
|
415
|
+
|| (v.start as f64 >= start_region && (v.start as f64) < end_region)
|
|
416
|
+
|| (v.end as f64 >= start_region && (v.end as f64) < end_region)
|
|
417
|
+
|| (start_region >= v.start as f64 && (v.end as f64) < end_region))
|
|
468
418
|
&& iter > 1
|
|
469
419
|
{
|
|
470
420
|
// Calculate sum and number for this region
|
|
471
421
|
//println!("Hello");
|
|
472
|
-
let start_entry_within_region =
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
- start_entry_within_region)
|
|
478
|
-
as f64;
|
|
479
|
-
datapoints_sum[i] += (stop_entry_within_region
|
|
480
|
-
- start_entry_within_region)
|
|
422
|
+
let start_entry_within_region = determine_max(v.start as f64, start_region);
|
|
423
|
+
let stop_entry_within_region = determine_min(v.end as f64, end_region);
|
|
424
|
+
datapoints_num[i] +=
|
|
425
|
+
(stop_entry_within_region - start_entry_within_region) as f64;
|
|
426
|
+
datapoints_sum[i] += (stop_entry_within_region - start_entry_within_region)
|
|
481
427
|
as f64
|
|
482
428
|
* v.value as f64;
|
|
483
429
|
//println!(
|
|
@@ -536,11 +482,9 @@ fn calculate_datapoints<
|
|
|
536
482
|
continue;
|
|
537
483
|
} else {
|
|
538
484
|
if (v.start as f64 <= start_region && end_region < v.end as f64)
|
|
539
|
-
|| (v.start as f64 >= start_region
|
|
540
|
-
&& (v.start as f64) < end_region)
|
|
485
|
+
|| (v.start as f64 >= start_region && (v.start as f64) < end_region)
|
|
541
486
|
|| (v.end as f64 >= start_region && (v.end as f64) < end_region)
|
|
542
|
-
|| (start_region >= v.start as f64
|
|
543
|
-
&& (v.end as f64) < end_region)
|
|
487
|
+
|| (start_region >= v.start as f64 && (v.end as f64) < end_region)
|
|
544
488
|
{
|
|
545
489
|
// Calculate sum and number for this region
|
|
546
490
|
//println!("i:{}", i);
|
|
@@ -548,16 +492,10 @@ fn calculate_datapoints<
|
|
|
548
492
|
//println!("v.end:{}", v.end);
|
|
549
493
|
//println!("start_region:{}", start_region);
|
|
550
494
|
//println!("end_region:{}", end_region);
|
|
551
|
-
let start_entry_within_region =
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
datapoints_num[i] += (stop_entry_within_region
|
|
556
|
-
- start_entry_within_region)
|
|
557
|
-
as f64;
|
|
558
|
-
datapoints_sum[i] += (stop_entry_within_region
|
|
559
|
-
- start_entry_within_region)
|
|
560
|
-
as f64
|
|
495
|
+
let start_entry_within_region = determine_max(v.start as f64, start_region);
|
|
496
|
+
let stop_entry_within_region = determine_min(v.end as f64, end_region);
|
|
497
|
+
datapoints_num[i] += (stop_entry_within_region - start_entry_within_region) as f64;
|
|
498
|
+
datapoints_sum[i] += (stop_entry_within_region - start_entry_within_region) as f64
|
|
561
499
|
* ((v.summary.sum as f64) / v.summary.bases_covered as f64);
|
|
562
500
|
//println!(
|
|
563
501
|
// "start_entry_within_region:{}",
|
|
@@ -578,29 +516,20 @@ fn calculate_datapoints<
|
|
|
578
516
|
//println!("v.end:{}", v.end);
|
|
579
517
|
//println!("start_region:{}", start_region);
|
|
580
518
|
//println!("end_region:{}", end_region);
|
|
581
|
-
if ((v.start as f64 <= start_region
|
|
582
|
-
|
|
583
|
-
|| (v.
|
|
584
|
-
|
|
585
|
-
|| (v.end as f64 >= start_region
|
|
586
|
-
&& (v.end as f64) < end_region)
|
|
587
|
-
|| (start_region >= v.start as f64
|
|
588
|
-
&& (v.end as f64) < end_region))
|
|
519
|
+
if ((v.start as f64 <= start_region && end_region < v.end as f64)
|
|
520
|
+
|| (v.start as f64 >= start_region && (v.start as f64) < end_region)
|
|
521
|
+
|| (v.end as f64 >= start_region && (v.end as f64) < end_region)
|
|
522
|
+
|| (start_region >= v.start as f64 && (v.end as f64) < end_region))
|
|
589
523
|
&& iter > 1
|
|
590
524
|
{
|
|
591
525
|
// Calculate sum and number for this region
|
|
592
|
-
let start_entry_within_region =
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
- start_entry_within_region)
|
|
598
|
-
as f64;
|
|
599
|
-
datapoints_sum[i] += (stop_entry_within_region
|
|
600
|
-
- start_entry_within_region)
|
|
526
|
+
let start_entry_within_region = determine_max(v.start as f64, start_region);
|
|
527
|
+
let stop_entry_within_region = determine_min(v.end as f64, end_region);
|
|
528
|
+
datapoints_num[i] +=
|
|
529
|
+
(stop_entry_within_region - start_entry_within_region) as f64;
|
|
530
|
+
datapoints_sum[i] += (stop_entry_within_region - start_entry_within_region)
|
|
601
531
|
as f64
|
|
602
|
-
* ((v.summary.sum as f64)
|
|
603
|
-
/ v.summary.bases_covered as f64);
|
|
532
|
+
* ((v.summary.sum as f64) / v.summary.bases_covered as f64);
|
|
604
533
|
//println!(
|
|
605
534
|
// "start_entry_within_region inside:{}",
|
|
606
535
|
// start_entry_within_region
|
|
@@ -634,9 +563,7 @@ fn calculate_datapoints<
|
|
|
634
563
|
}
|
|
635
564
|
None => {
|
|
636
565
|
// To be used in nucleotide resolution
|
|
637
|
-
let bigwig_output = reader
|
|
638
|
-
.get_interval(&chrom, start_pos as u32, stop_pos as u32)
|
|
639
|
-
.unwrap();
|
|
566
|
+
let bigwig_output = reader.get_interval(&chrom, start_pos as u32, stop_pos as u32).unwrap();
|
|
640
567
|
let mut i = 0;
|
|
641
568
|
let mut start_region = datapoints_list[i];
|
|
642
569
|
let mut end_region = datapoints_list[i + 1];
|
|
@@ -648,11 +575,9 @@ fn calculate_datapoints<
|
|
|
648
575
|
continue;
|
|
649
576
|
} else {
|
|
650
577
|
if (v.start as f64 <= start_region && end_region < v.end as f64)
|
|
651
|
-
|| (v.start as f64 >= start_region
|
|
652
|
-
&& (v.start as f64) < end_region)
|
|
578
|
+
|| (v.start as f64 >= start_region && (v.start as f64) < end_region)
|
|
653
579
|
|| (v.end as f64 >= start_region && (v.end as f64) < end_region)
|
|
654
|
-
|| (start_region >= v.start as f64
|
|
655
|
-
&& (v.end as f64) < end_region)
|
|
580
|
+
|| (start_region >= v.start as f64 && (v.end as f64) < end_region)
|
|
656
581
|
{
|
|
657
582
|
// Calculate sum and number for this region
|
|
658
583
|
//println!("i:{}", i);
|
|
@@ -660,17 +585,11 @@ fn calculate_datapoints<
|
|
|
660
585
|
//println!("v.end:{}", v.end);
|
|
661
586
|
//println!("start_region:{}", start_region);
|
|
662
587
|
//println!("end_region:{}", end_region);
|
|
663
|
-
let start_entry_within_region =
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
- start_entry_within_region)
|
|
669
|
-
as f64;
|
|
670
|
-
datapoints_sum[i] += (stop_entry_within_region
|
|
671
|
-
- start_entry_within_region)
|
|
672
|
-
as f64
|
|
673
|
-
* v.value as f64;
|
|
588
|
+
let start_entry_within_region = determine_max(v.start as f64, start_region);
|
|
589
|
+
let stop_entry_within_region = determine_min(v.end as f64, end_region);
|
|
590
|
+
datapoints_num[i] += (stop_entry_within_region - start_entry_within_region) as f64;
|
|
591
|
+
datapoints_sum[i] +=
|
|
592
|
+
(stop_entry_within_region - start_entry_within_region) as f64 * v.value as f64;
|
|
674
593
|
//println!(
|
|
675
594
|
// "start_entry_within_region:{}",
|
|
676
595
|
// start_entry_within_region
|
|
@@ -690,27 +609,19 @@ fn calculate_datapoints<
|
|
|
690
609
|
//println!("v.end:{}", v.end);
|
|
691
610
|
//println!("start_region:{}", start_region);
|
|
692
611
|
//println!("end_region:{}", end_region);
|
|
693
|
-
if ((v.start as f64 <= start_region
|
|
694
|
-
|
|
695
|
-
|| (v.
|
|
696
|
-
|
|
697
|
-
|| (v.end as f64 >= start_region
|
|
698
|
-
&& (v.end as f64) < end_region)
|
|
699
|
-
|| (start_region >= v.start as f64
|
|
700
|
-
&& (v.end as f64) < end_region))
|
|
612
|
+
if ((v.start as f64 <= start_region && end_region < v.end as f64)
|
|
613
|
+
|| (v.start as f64 >= start_region && (v.start as f64) < end_region)
|
|
614
|
+
|| (v.end as f64 >= start_region && (v.end as f64) < end_region)
|
|
615
|
+
|| (start_region >= v.start as f64 && (v.end as f64) < end_region))
|
|
701
616
|
&& iter > 1
|
|
702
617
|
{
|
|
703
618
|
// Calculate sum and number for this region
|
|
704
619
|
//println!("Hello");
|
|
705
|
-
let start_entry_within_region =
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
- start_entry_within_region)
|
|
711
|
-
as f64;
|
|
712
|
-
datapoints_sum[i] += (stop_entry_within_region
|
|
713
|
-
- start_entry_within_region)
|
|
620
|
+
let start_entry_within_region = determine_max(v.start as f64, start_region);
|
|
621
|
+
let stop_entry_within_region = determine_min(v.end as f64, end_region);
|
|
622
|
+
datapoints_num[i] +=
|
|
623
|
+
(stop_entry_within_region - start_entry_within_region) as f64;
|
|
624
|
+
datapoints_sum[i] += (stop_entry_within_region - start_entry_within_region)
|
|
714
625
|
as f64
|
|
715
626
|
* v.value as f64;
|
|
716
627
|
//println!(
|
|
@@ -762,4 +673,4 @@ fn calculate_datapoints<
|
|
|
762
673
|
}
|
|
763
674
|
output_vec.pop();
|
|
764
675
|
println!("{}", output_vec);
|
|
765
|
-
}
|
|
676
|
+
}
|
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
|
|
2
|
+
use plotters::prelude::*;
|
|
3
|
+
use plotters::style::ShapeStyle;
|
|
4
|
+
use serde::{Deserialize, Serialize};
|
|
5
|
+
use serde_json;
|
|
6
|
+
use std::collections::HashMap;
|
|
7
|
+
use std::convert::TryInto;
|
|
8
|
+
use std::error::Error;
|
|
9
|
+
use std::fs::File;
|
|
10
|
+
use std::io::{self, BufRead, BufReader};
|
|
11
|
+
use tiny_skia::{FillRule, PathBuilder, Pixmap, Transform};
|
|
12
|
+
|
|
13
|
+
// Define the JSON input structure
|
|
14
|
+
#[derive(Deserialize, Debug)]
|
|
15
|
+
struct Input {
|
|
16
|
+
file: String,
|
|
17
|
+
#[serde(rename = "type")]
|
|
18
|
+
plot_type: String,
|
|
19
|
+
#[serde(rename = "chrSizes")]
|
|
20
|
+
chromosomelist: HashMap<String, u64>,
|
|
21
|
+
plot_width: u64,
|
|
22
|
+
plot_height: u64,
|
|
23
|
+
device_pixel_ratio: f64,
|
|
24
|
+
png_dot_radius: u64,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// chromosome info
|
|
28
|
+
#[derive(Serialize)]
|
|
29
|
+
struct ChromInfo {
|
|
30
|
+
start: u64,
|
|
31
|
+
size: u64,
|
|
32
|
+
center: u64,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#[derive(Serialize)]
|
|
36
|
+
struct PointDetail {
|
|
37
|
+
x: u64,
|
|
38
|
+
y: f64,
|
|
39
|
+
color: String,
|
|
40
|
+
r#type: String,
|
|
41
|
+
gene: String,
|
|
42
|
+
chrom: String,
|
|
43
|
+
start: u64,
|
|
44
|
+
end: u64,
|
|
45
|
+
pos: u64,
|
|
46
|
+
q_value: f64,
|
|
47
|
+
nsubj: Option<i64>,
|
|
48
|
+
pixel_x: f64,
|
|
49
|
+
pixel_y: f64,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#[derive(Serialize)]
|
|
53
|
+
struct InteractiveData {
|
|
54
|
+
points: Vec<PointDetail>,
|
|
55
|
+
chrom_data: HashMap<String, ChromInfo>,
|
|
56
|
+
total_genome_length: i64,
|
|
57
|
+
x_buffer: i64,
|
|
58
|
+
y_min: f64,
|
|
59
|
+
y_max: f64,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
#[derive(Serialize)]
|
|
63
|
+
struct Output {
|
|
64
|
+
png: String,
|
|
65
|
+
plot_data: InteractiveData,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Helper function to convert hex color to RGB
|
|
69
|
+
fn hex_to_rgb(hex: &str) -> Option<(u8, u8, u8)> {
|
|
70
|
+
let hex = hex.trim_start_matches('#');
|
|
71
|
+
if hex.len() != 6 {
|
|
72
|
+
return None;
|
|
73
|
+
}
|
|
74
|
+
let r = u8::from_str_radix(&hex[0..2], 16).ok()?;
|
|
75
|
+
let g = u8::from_str_radix(&hex[2..4], 16).ok()?;
|
|
76
|
+
let b = u8::from_str_radix(&hex[4..6], 16).ok()?;
|
|
77
|
+
Some((r, g, b))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Function to Build cumulative chromosome map
|
|
81
|
+
fn cumulative_chrom(
|
|
82
|
+
chrom_size: &HashMap<String, u64>,
|
|
83
|
+
) -> Result<(HashMap<String, ChromInfo>, u64, Vec<String>), Box<dyn Error>> {
|
|
84
|
+
let mut chrom_data: HashMap<String, ChromInfo> = HashMap::new();
|
|
85
|
+
let mut cumulative_pos: u64 = 0;
|
|
86
|
+
|
|
87
|
+
// Sort chromosomes
|
|
88
|
+
let mut sorted_chroms: Vec<String> = chrom_size.keys().cloned().collect();
|
|
89
|
+
sorted_chroms.sort_by_key(|chr| {
|
|
90
|
+
let s = chr.trim_start_matches("chr");
|
|
91
|
+
match s.parse::<u32>() {
|
|
92
|
+
Ok(n) => (0, n),
|
|
93
|
+
Err(_) => match s {
|
|
94
|
+
"X" => (1, 23),
|
|
95
|
+
"Y" => (1, 24),
|
|
96
|
+
"M" | "MT" => (1, 100),
|
|
97
|
+
_ => (2, u32::MAX),
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
for chrom in &sorted_chroms {
|
|
103
|
+
if let Some(&size) = chrom_size.get(chrom) {
|
|
104
|
+
chrom_data.insert(
|
|
105
|
+
chrom.clone(),
|
|
106
|
+
ChromInfo {
|
|
107
|
+
start: cumulative_pos,
|
|
108
|
+
size: size,
|
|
109
|
+
center: cumulative_pos + size / 2,
|
|
110
|
+
},
|
|
111
|
+
);
|
|
112
|
+
cumulative_pos += size;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
Ok((chrom_data, cumulative_pos, sorted_chroms))
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Function to read the GRIN2 file
|
|
119
|
+
fn grin2_file_read(
|
|
120
|
+
grin2_file: &str,
|
|
121
|
+
chrom_data: &HashMap<String, ChromInfo>,
|
|
122
|
+
) -> Result<(Vec<u64>, Vec<f64>, Vec<String>, Vec<PointDetail>, Vec<usize>), Box<dyn Error>> {
|
|
123
|
+
// Default colours
|
|
124
|
+
let mut colors: HashMap<String, String> = HashMap::new();
|
|
125
|
+
colors.insert("gain".into(), "#FF4444".into());
|
|
126
|
+
colors.insert("loss".into(), "#4444FF".into());
|
|
127
|
+
colors.insert("mutation".into(), "#44AA44".into());
|
|
128
|
+
colors.insert("fusion".into(), "#FFA500".into());
|
|
129
|
+
colors.insert("sv".into(), "#9932CC".into());
|
|
130
|
+
|
|
131
|
+
let mut xs = Vec::new();
|
|
132
|
+
let mut ys = Vec::new();
|
|
133
|
+
let mut colors_vec = Vec::new();
|
|
134
|
+
let mut point_details = Vec::new();
|
|
135
|
+
let mut sig_indices: Vec<usize> = Vec::new();
|
|
136
|
+
|
|
137
|
+
let grin2_file = File::open(grin2_file).expect("Failed to open grin2_result_file");
|
|
138
|
+
let mut reader = BufReader::new(grin2_file);
|
|
139
|
+
// get the first line (header line)
|
|
140
|
+
let mut header_line = String::new();
|
|
141
|
+
reader
|
|
142
|
+
.read_line(&mut header_line)
|
|
143
|
+
.expect("Failed to read the first line of grin2_result_file");
|
|
144
|
+
let header: Vec<String> = header_line
|
|
145
|
+
.trim_end()
|
|
146
|
+
.split('\t')
|
|
147
|
+
.map(|s| s.trim().to_string())
|
|
148
|
+
.collect();
|
|
149
|
+
|
|
150
|
+
// define the mutation types from the header of grin2 result file
|
|
151
|
+
let mutation_types = ["gain", "loss", "mutation", "fusion", "sv"];
|
|
152
|
+
let mut mutation_indices: HashMap<&str, (usize, Option<usize>)> = HashMap::new();
|
|
153
|
+
for name in &mutation_types {
|
|
154
|
+
let q_col = format!("q.nsubj.{name}");
|
|
155
|
+
let n_col = format!("nsubj.{name}");
|
|
156
|
+
if let Some(q_idx) = header.iter().position(|h| h == &q_col) {
|
|
157
|
+
let n_idx = header.iter().position(|h| h == &n_col);
|
|
158
|
+
mutation_indices.insert(*name, (q_idx, n_idx));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// extract the index for each required info
|
|
163
|
+
let chrom_idx = header
|
|
164
|
+
.iter()
|
|
165
|
+
.position(|h| h == "chrom")
|
|
166
|
+
.expect("Missing 'chrom' column");
|
|
167
|
+
let gene_idx = header.iter().position(|h| h == "gene").expect("Missing 'gene' column");
|
|
168
|
+
let loc_start_idx = header
|
|
169
|
+
.iter()
|
|
170
|
+
.position(|h| h == "loc.start")
|
|
171
|
+
.expect("Missing 'loc.start' column");
|
|
172
|
+
let loc_end_idx = header
|
|
173
|
+
.iter()
|
|
174
|
+
.position(|h| h == "loc.end")
|
|
175
|
+
.expect("Missing 'loc.end' column");
|
|
176
|
+
|
|
177
|
+
// loop all lines
|
|
178
|
+
let mut mut_num: usize = 0;
|
|
179
|
+
for line_result in reader.lines() {
|
|
180
|
+
let line = match line_result {
|
|
181
|
+
Ok(l) => l,
|
|
182
|
+
Err(e) => {
|
|
183
|
+
eprintln!("Error reading line: {}", e);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
let fields: Vec<&str> = line.trim_end().split('\t').collect();
|
|
189
|
+
let chrom = match fields.get(chrom_idx).map(|s| s.trim()) {
|
|
190
|
+
Some(s) if !s.is_empty() => s,
|
|
191
|
+
_ => continue,
|
|
192
|
+
};
|
|
193
|
+
let chrom_info = match chrom_data.get(chrom) {
|
|
194
|
+
Some(info) => info,
|
|
195
|
+
None => continue,
|
|
196
|
+
};
|
|
197
|
+
let gene_name = fields.get(gene_idx).unwrap_or(&"").to_string();
|
|
198
|
+
let loc_start_str = match fields.get(loc_start_idx).map(|s| s.trim()) {
|
|
199
|
+
Some(s) if !s.is_empty() => s,
|
|
200
|
+
_ => continue,
|
|
201
|
+
};
|
|
202
|
+
let gene_start: u64 = loc_start_str
|
|
203
|
+
.parse()
|
|
204
|
+
.unwrap_or_else(|_| panic!("Invalid integer for loc.start: '{}' in line: {}", loc_start_str, line));
|
|
205
|
+
let loc_end_str = match fields.get(loc_end_idx).map(|s| s.trim()) {
|
|
206
|
+
Some(s) if !s.is_empty() => s,
|
|
207
|
+
_ => continue,
|
|
208
|
+
};
|
|
209
|
+
let gene_end: u64 = loc_end_str
|
|
210
|
+
.parse()
|
|
211
|
+
.unwrap_or_else(|_| panic!("Invalid integer for loc.end: '{}' in line: {}", loc_end_str, line));
|
|
212
|
+
let x_pos = chrom_info.start + gene_start as u64;
|
|
213
|
+
|
|
214
|
+
for (mtype, (q_idx, n_idx_opt)) in &mutation_indices {
|
|
215
|
+
let q_val_str = match fields.get(*q_idx) {
|
|
216
|
+
Some(q) => q,
|
|
217
|
+
None => continue,
|
|
218
|
+
};
|
|
219
|
+
let q_val: f64 = match q_val_str.parse() {
|
|
220
|
+
Ok(v) if v > 0.0 => v,
|
|
221
|
+
_ => continue,
|
|
222
|
+
};
|
|
223
|
+
let neg_log10_q = -q_val.log10();
|
|
224
|
+
let n_subj_count: Option<i64> = n_idx_opt
|
|
225
|
+
.and_then(|i| fields.get(i))
|
|
226
|
+
.and_then(|s| s.parse::<i64>().ok());
|
|
227
|
+
let color = colors.get(*mtype).unwrap_or(&"#888888".to_string()).clone();
|
|
228
|
+
// Add to plotting vectors
|
|
229
|
+
xs.push(x_pos);
|
|
230
|
+
ys.push(neg_log10_q);
|
|
231
|
+
colors_vec.push(color.clone());
|
|
232
|
+
|
|
233
|
+
// only add significant points for interactivity
|
|
234
|
+
if q_val <= 0.05 {
|
|
235
|
+
point_details.push(PointDetail {
|
|
236
|
+
x: x_pos,
|
|
237
|
+
y: neg_log10_q,
|
|
238
|
+
color,
|
|
239
|
+
r#type: mtype.to_string(),
|
|
240
|
+
gene: gene_name.clone(),
|
|
241
|
+
chrom: chrom.to_string(),
|
|
242
|
+
start: gene_start,
|
|
243
|
+
end: gene_end,
|
|
244
|
+
pos: gene_start,
|
|
245
|
+
q_value: q_val,
|
|
246
|
+
nsubj: n_subj_count,
|
|
247
|
+
pixel_x: 0.0,
|
|
248
|
+
pixel_y: 0.0,
|
|
249
|
+
});
|
|
250
|
+
sig_indices.push(mut_num);
|
|
251
|
+
};
|
|
252
|
+
mut_num += 1;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
Ok((xs, ys, colors_vec, point_details, sig_indices))
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Function to create the GRIN2 Manhattan plot
|
|
260
|
+
fn plot_grin2_manhattan(
|
|
261
|
+
grin2_result_file: String,
|
|
262
|
+
chrom_size: HashMap<String, u64>,
|
|
263
|
+
plot_width: u64,
|
|
264
|
+
plot_height: u64,
|
|
265
|
+
device_pixel_ratio: f64,
|
|
266
|
+
png_dot_radius: u64,
|
|
267
|
+
) -> Result<(String, InteractiveData), Box<dyn Error>> {
|
|
268
|
+
// ------------------------------------------------
|
|
269
|
+
// 1. Build cumulative chromosome map
|
|
270
|
+
// ------------------------------------------------
|
|
271
|
+
|
|
272
|
+
let mut chrom_data: HashMap<String, ChromInfo> = HashMap::new();
|
|
273
|
+
let mut cumulative_pos: u64 = 0;
|
|
274
|
+
let mut sorted_chroms: Vec<String> = Vec::new();
|
|
275
|
+
|
|
276
|
+
if let Ok((chr_data, cum_pos, chrom_sort)) = cumulative_chrom(&chrom_size) {
|
|
277
|
+
chrom_data = chr_data;
|
|
278
|
+
cumulative_pos = cum_pos;
|
|
279
|
+
sorted_chroms = chrom_sort;
|
|
280
|
+
};
|
|
281
|
+
let total_genome_length: i64 = cumulative_pos.try_into().unwrap();
|
|
282
|
+
let x_buffer = (total_genome_length as f64 * 0.005) as i64; // 0.5 % buffer
|
|
283
|
+
|
|
284
|
+
// ------------------------------------------------
|
|
285
|
+
// 2. Read file & collect points
|
|
286
|
+
// ------------------------------------------------
|
|
287
|
+
|
|
288
|
+
// Declare all data
|
|
289
|
+
let mut xs = Vec::new();
|
|
290
|
+
let mut ys = Vec::new();
|
|
291
|
+
let mut colors_vec = Vec::new();
|
|
292
|
+
let mut point_details = Vec::new();
|
|
293
|
+
let mut sig_indices = Vec::new();
|
|
294
|
+
|
|
295
|
+
if let Ok((x, y, c, pd, si)) = grin2_file_read(&grin2_result_file, &chrom_data) {
|
|
296
|
+
xs = x;
|
|
297
|
+
ys = y;
|
|
298
|
+
colors_vec = c;
|
|
299
|
+
point_details = pd;
|
|
300
|
+
sig_indices = si;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ------------------------------------------------
|
|
304
|
+
// 3. Y-axis scaling
|
|
305
|
+
// ------------------------------------------------
|
|
306
|
+
let y_padding = png_dot_radius as f64;
|
|
307
|
+
let y_min = 0.0 - y_padding;
|
|
308
|
+
let y_max = if !ys.is_empty() {
|
|
309
|
+
let max_y = ys.iter().cloned().fold(f64::MIN, f64::max);
|
|
310
|
+
if max_y > 40.0 {
|
|
311
|
+
let target = 40.0;
|
|
312
|
+
let scale_factor_y = target / max_y;
|
|
313
|
+
|
|
314
|
+
for y in ys.iter_mut() {
|
|
315
|
+
*y *= scale_factor_y;
|
|
316
|
+
}
|
|
317
|
+
for p in point_details.iter_mut() {
|
|
318
|
+
p.y *= scale_factor_y;
|
|
319
|
+
}
|
|
320
|
+
let scaled_max = ys.iter().cloned().fold(f64::MIN, f64::max);
|
|
321
|
+
scaled_max + 0.35 + y_padding
|
|
322
|
+
} else {
|
|
323
|
+
max_y + 0.35 + y_padding
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
1.0 + y_padding
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// ------------------------------------------------
|
|
330
|
+
// 4. Setup high-DPR bitmap dimensions
|
|
331
|
+
// ------------------------------------------------
|
|
332
|
+
|
|
333
|
+
let dpr = device_pixel_ratio.max(1.0);
|
|
334
|
+
|
|
335
|
+
let png_width = plot_width + 2 * png_dot_radius;
|
|
336
|
+
let png_height = plot_height + 2 * png_dot_radius;
|
|
337
|
+
|
|
338
|
+
let w: u32 = (png_width * device_pixel_ratio as u64)
|
|
339
|
+
.try_into()
|
|
340
|
+
.expect("PNG width too large for u32");
|
|
341
|
+
let h: u32 = (png_height * device_pixel_ratio as u64)
|
|
342
|
+
.try_into()
|
|
343
|
+
.expect("PNG height too large for u32");
|
|
344
|
+
|
|
345
|
+
// Create RGB buffer for Plotters
|
|
346
|
+
let mut buffer = vec![0u8; w as usize * h as usize * 3];
|
|
347
|
+
|
|
348
|
+
// Make Plotters backend that draws into the RGB buffer (scale-aware)
|
|
349
|
+
|
|
350
|
+
let mut pixel_positions: Vec<(f64, f64)> = Vec::with_capacity(xs.len());
|
|
351
|
+
{
|
|
352
|
+
let backend = BitMapBackend::with_buffer(&mut buffer, (w, h));
|
|
353
|
+
let root = backend.into_drawing_area();
|
|
354
|
+
root.fill(&WHITE)?;
|
|
355
|
+
|
|
356
|
+
// ------------------------------------------------
|
|
357
|
+
// 5. Build the chart (no axes, no margins)
|
|
358
|
+
// ------------------------------------------------
|
|
359
|
+
let mut chart = ChartBuilder::on(&root)
|
|
360
|
+
.margin(0)
|
|
361
|
+
.set_all_label_area_size(0)
|
|
362
|
+
.build_cartesian_2d((-x_buffer)..(total_genome_length + x_buffer), y_min..y_max)?;
|
|
363
|
+
|
|
364
|
+
chart
|
|
365
|
+
.configure_mesh()
|
|
366
|
+
.disable_x_mesh()
|
|
367
|
+
.disable_y_mesh()
|
|
368
|
+
.disable_axes()
|
|
369
|
+
.draw()?;
|
|
370
|
+
|
|
371
|
+
// ------------------------------------------------
|
|
372
|
+
// 6. Alternating chromosome backgrounds
|
|
373
|
+
// ------------------------------------------------
|
|
374
|
+
for (i, chrom) in sorted_chroms.iter().enumerate() {
|
|
375
|
+
if let Some(info) = chrom_data.get(chrom) {
|
|
376
|
+
let bg = if i % 2 == 0 { WHITE } else { RGBColor(211, 211, 211) };
|
|
377
|
+
let fill_style: ShapeStyle = bg.mix(0.5).filled();
|
|
378
|
+
let rect = Rectangle::new(
|
|
379
|
+
[
|
|
380
|
+
(info.start as i64, (y_min + y_padding)),
|
|
381
|
+
((info.start + info.size) as i64, (y_max - y_padding)),
|
|
382
|
+
],
|
|
383
|
+
fill_style,
|
|
384
|
+
);
|
|
385
|
+
chart.draw_series(vec![rect])?;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// ------------------------------------------------
|
|
390
|
+
// 7. capture high-DPR pixel mapping for the points
|
|
391
|
+
// we do not draw the points with plotters (will use tiny-skia for AA)
|
|
392
|
+
// but use charts.backend_coord to map data->pixel in the high-DPR backend
|
|
393
|
+
// ------------------------------------------------
|
|
394
|
+
|
|
395
|
+
if !xs.is_empty() {
|
|
396
|
+
for (x, y) in xs.iter().zip(ys.iter()) {
|
|
397
|
+
// convert data coords -> high-DPR pixel coords
|
|
398
|
+
let (px, py) = chart.backend_coord(&(*x as i64, *y));
|
|
399
|
+
pixel_positions.push((px as f64, py as f64));
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
for (i, p) in point_details.iter_mut().enumerate() {
|
|
404
|
+
let (px, py) = pixel_positions[*&sig_indices[i]];
|
|
405
|
+
p.pixel_x = px;
|
|
406
|
+
p.pixel_y = py;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// flush root drawing area
|
|
410
|
+
root.present()?;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Convert Plotters RGB buffer into tiny-skia RGBA pixmap
|
|
414
|
+
let mut pixmap = Pixmap::new(w, h).ok_or("Failed to create pixmap")?;
|
|
415
|
+
{
|
|
416
|
+
let data = pixmap.data_mut();
|
|
417
|
+
let mut src_i = 0usize;
|
|
418
|
+
let mut dst_i = 0usize;
|
|
419
|
+
for _ in 0..(w as usize * h as usize) {
|
|
420
|
+
let r = buffer[src_i];
|
|
421
|
+
let g = buffer[src_i + 1];
|
|
422
|
+
let b = buffer[src_i + 2];
|
|
423
|
+
data[dst_i] = r;
|
|
424
|
+
data[dst_i + 1] = g;
|
|
425
|
+
data[dst_i + 2] = b;
|
|
426
|
+
data[dst_i + 3] = 255u8; // opaque alpha
|
|
427
|
+
src_i += 3;
|
|
428
|
+
dst_i += 4;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Draw anti-aliased circles using tiny-skia into the pixmap
|
|
433
|
+
// radius in HIGH-DPR pixels:
|
|
434
|
+
let radius_high_dpr = (png_dot_radius as f32) * (dpr as f32);
|
|
435
|
+
|
|
436
|
+
// Paint template
|
|
437
|
+
let mut paint = tiny_skia::Paint::default();
|
|
438
|
+
|
|
439
|
+
// for perfomance: reuse a PathBuilder to create circles
|
|
440
|
+
// will create a small path per point
|
|
441
|
+
for i in 0..xs.len() {
|
|
442
|
+
let (px, py) = pixel_positions[i]; // pixel coordinates for this point
|
|
443
|
+
let color_hex = &colors_vec[i];
|
|
444
|
+
|
|
445
|
+
let (r_u8, g_u8, b_u8) = match hex_to_rgb(color_hex) {
|
|
446
|
+
Some(rgb) => rgb,
|
|
447
|
+
None => (136u8, 136u8, 136u8),
|
|
448
|
+
};
|
|
449
|
+
paint.set_color_rgba8(r_u8, g_u8, b_u8, 255u8);
|
|
450
|
+
let mut pb = PathBuilder::new();
|
|
451
|
+
pb.push_circle(px as f32, py as f32, radius_high_dpr);
|
|
452
|
+
|
|
453
|
+
if let Some(path) = pb.finish() {
|
|
454
|
+
pixmap.fill_path(&path, &paint, FillRule::Winding, Transform::identity(), None);
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// encode pixmap to PNG bytes
|
|
459
|
+
let png_bytes = pixmap.encode_png()?;
|
|
460
|
+
let png_data = BASE64.encode(&png_bytes);
|
|
461
|
+
|
|
462
|
+
// ------------------------------------------------
|
|
463
|
+
// 8. Generate interactive data
|
|
464
|
+
// ------------------------------------------------
|
|
465
|
+
let interactive_data = InteractiveData {
|
|
466
|
+
points: point_details,
|
|
467
|
+
chrom_data,
|
|
468
|
+
total_genome_length,
|
|
469
|
+
x_buffer,
|
|
470
|
+
y_min,
|
|
471
|
+
y_max,
|
|
472
|
+
};
|
|
473
|
+
Ok((png_data, interactive_data))
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
477
|
+
let mut input = String::new();
|
|
478
|
+
match io::stdin().read_line(&mut input) {
|
|
479
|
+
Ok(_bytes_read) => {
|
|
480
|
+
let input_json: Input = match serde_json::from_str(&input) {
|
|
481
|
+
Ok(json) => json,
|
|
482
|
+
Err(_err) => {
|
|
483
|
+
panic!("Invalid JSON input");
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
// input data type
|
|
488
|
+
// *** might need to change the key later
|
|
489
|
+
let input_data = &input_json.plot_type;
|
|
490
|
+
|
|
491
|
+
if input_data == "grin2" {
|
|
492
|
+
let grin2_file = &input_json.file;
|
|
493
|
+
let chrom_size = &input_json.chromosomelist;
|
|
494
|
+
let plot_width = &input_json.plot_width;
|
|
495
|
+
let plot_height = &input_json.plot_height;
|
|
496
|
+
let device_pixel_ratio = &input_json.device_pixel_ratio;
|
|
497
|
+
let png_dot_radius = &input_json.png_dot_radius;
|
|
498
|
+
if let Ok((base64_string, plot_data)) = plot_grin2_manhattan(
|
|
499
|
+
grin2_file.clone(),
|
|
500
|
+
chrom_size.clone(),
|
|
501
|
+
plot_width.clone(),
|
|
502
|
+
plot_height.clone(),
|
|
503
|
+
device_pixel_ratio.clone(),
|
|
504
|
+
png_dot_radius.clone(),
|
|
505
|
+
) {
|
|
506
|
+
let output = Output {
|
|
507
|
+
png: base64_string,
|
|
508
|
+
plot_data,
|
|
509
|
+
};
|
|
510
|
+
if let Ok(json) = serde_json::to_string(&output) {
|
|
511
|
+
println!("{}", json);
|
|
512
|
+
}
|
|
513
|
+
} else {
|
|
514
|
+
eprintln!("Failed to generate Manhattan plot");
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
Err(_err) => {
|
|
519
|
+
panic!("Error reading input JSON!");
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
Ok(())
|
|
523
|
+
}
|
package/src/ollama.rs
CHANGED
|
@@ -663,6 +663,7 @@ impl From<rig::completion::ToolDefinition> for ToolDefinition {
|
|
|
663
663
|
}
|
|
664
664
|
|
|
665
665
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
|
666
|
+
#[allow(dead_code)]
|
|
666
667
|
pub struct ToolCall {
|
|
667
668
|
// pub id: String,
|
|
668
669
|
#[serde(default, rename = "type")]
|
|
@@ -872,6 +873,7 @@ impl From<rig::message::ToolCall> for ToolCall {
|
|
|
872
873
|
}
|
|
873
874
|
|
|
874
875
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
|
876
|
+
#[allow(dead_code)]
|
|
875
877
|
pub struct SystemContent {
|
|
876
878
|
#[serde(default)]
|
|
877
879
|
r#type: SystemContentType,
|
|
@@ -880,6 +882,7 @@ pub struct SystemContent {
|
|
|
880
882
|
|
|
881
883
|
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone)]
|
|
882
884
|
#[serde(rename_all = "lowercase")]
|
|
885
|
+
#[allow(dead_code)]
|
|
883
886
|
pub enum SystemContentType {
|
|
884
887
|
#[default]
|
|
885
888
|
Text,
|
|
@@ -905,6 +908,7 @@ impl FromStr for SystemContent {
|
|
|
905
908
|
}
|
|
906
909
|
|
|
907
910
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
|
911
|
+
#[allow(dead_code)]
|
|
908
912
|
pub struct AssistantContent {
|
|
909
913
|
pub text: String,
|
|
910
914
|
}
|
|
@@ -918,6 +922,7 @@ impl FromStr for AssistantContent {
|
|
|
918
922
|
|
|
919
923
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
|
920
924
|
#[serde(tag = "type", rename_all = "lowercase")]
|
|
925
|
+
#[allow(dead_code)]
|
|
921
926
|
pub enum UserContent {
|
|
922
927
|
Text { text: String },
|
|
923
928
|
Image { image_url: ImageUrl },
|
|
@@ -932,6 +937,7 @@ impl FromStr for UserContent {
|
|
|
932
937
|
}
|
|
933
938
|
|
|
934
939
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
|
940
|
+
#[allow(dead_code)]
|
|
935
941
|
pub struct ImageUrl {
|
|
936
942
|
pub url: String,
|
|
937
943
|
#[serde(default)]
|
package/src/sjprovider.rs
CHANGED
|
@@ -729,6 +729,7 @@ impl From<rig::completion::ToolDefinition> for ToolDefinition {
|
|
|
729
729
|
}
|
|
730
730
|
|
|
731
731
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
|
732
|
+
#[allow(dead_code)]
|
|
732
733
|
pub struct ToolCall {
|
|
733
734
|
// pub id: String,
|
|
734
735
|
#[serde(default, rename = "type")]
|
|
@@ -737,11 +738,13 @@ pub struct ToolCall {
|
|
|
737
738
|
}
|
|
738
739
|
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone)]
|
|
739
740
|
#[serde(rename_all = "lowercase")]
|
|
741
|
+
#[allow(dead_code)]
|
|
740
742
|
pub enum ToolType {
|
|
741
743
|
#[default]
|
|
742
744
|
Function,
|
|
743
745
|
}
|
|
744
746
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
|
747
|
+
#[allow(dead_code)]
|
|
745
748
|
pub struct Function {
|
|
746
749
|
pub name: String,
|
|
747
750
|
pub arguments: Value,
|
|
@@ -938,6 +941,7 @@ impl From<rig::message::ToolCall> for ToolCall {
|
|
|
938
941
|
}
|
|
939
942
|
|
|
940
943
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
|
944
|
+
#[allow(dead_code)]
|
|
941
945
|
pub struct SystemContent {
|
|
942
946
|
#[serde(default)]
|
|
943
947
|
r#type: SystemContentType,
|
|
@@ -971,6 +975,7 @@ impl FromStr for SystemContent {
|
|
|
971
975
|
}
|
|
972
976
|
|
|
973
977
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
|
978
|
+
#[allow(dead_code)]
|
|
974
979
|
pub struct AssistantContent {
|
|
975
980
|
pub text: String,
|
|
976
981
|
}
|
|
@@ -984,6 +989,7 @@ impl FromStr for AssistantContent {
|
|
|
984
989
|
|
|
985
990
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
|
986
991
|
#[serde(tag = "type", rename_all = "lowercase")]
|
|
992
|
+
#[allow(dead_code)]
|
|
987
993
|
pub enum UserContent {
|
|
988
994
|
Text { text: String },
|
|
989
995
|
Image { image_url: ImageUrl },
|
|
@@ -998,6 +1004,7 @@ impl FromStr for UserContent {
|
|
|
998
1004
|
}
|
|
999
1005
|
|
|
1000
1006
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
|
1007
|
+
#[allow(dead_code)]
|
|
1001
1008
|
pub struct ImageUrl {
|
|
1002
1009
|
pub url: String,
|
|
1003
1010
|
#[serde(default)]
|