cosmol-viewer 0.1.0__tar.gz → 0.1.1.dev2__tar.gz

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.

Potentially problematic release.


This version of cosmol-viewer might be problematic. Click here for more details.

Files changed (30) hide show
  1. {cosmol_viewer-0.1.0 → cosmol_viewer-0.1.1.dev2}/Cargo.lock +143 -9
  2. cosmol_viewer-0.1.1.dev2/Cargo.toml +22 -0
  3. {cosmol_viewer-0.1.0 → cosmol_viewer-0.1.1.dev2}/PKG-INFO +1 -1
  4. {cosmol_viewer-0.1.0/cosmol_viewer_core → cosmol_viewer-0.1.1.dev2/crates/core}/Cargo.toml +4 -0
  5. cosmol_viewer-0.1.1.dev2/crates/core/src/lib.rs +72 -0
  6. cosmol_viewer-0.1.1.dev2/crates/core/src/parser/mod.rs +2 -0
  7. cosmol_viewer-0.1.1.dev2/crates/core/src/parser/sdf.rs +197 -0
  8. cosmol_viewer-0.1.1.dev2/crates/core/src/scene.rs +63 -0
  9. cosmol_viewer-0.1.0/cosmol_viewer_core/src/shader/app.rs → cosmol_viewer-0.1.1.dev2/crates/core/src/shader/canvas.rs +111 -62
  10. {cosmol_viewer-0.1.0/cosmol_viewer_core → cosmol_viewer-0.1.1.dev2/crates/core}/src/shader/fragment.glsl +2 -4
  11. cosmol_viewer-0.1.1.dev2/crates/core/src/shader/mod.rs +3 -0
  12. {cosmol_viewer-0.1.0/cosmol_viewer_core → cosmol_viewer-0.1.1.dev2/crates/core}/src/shader/vertex.glsl +1 -1
  13. cosmol_viewer-0.1.1.dev2/crates/core/src/shapes/mod.rs +3 -0
  14. cosmol_viewer-0.1.1.dev2/crates/core/src/shapes/molecules.rs +276 -0
  15. cosmol_viewer-0.1.0/cosmol_viewer_core/src/utils.rs → cosmol_viewer-0.1.1.dev2/crates/core/src/shapes/sphere.rs +31 -62
  16. cosmol_viewer-0.1.1.dev2/crates/core/src/shapes/stick.rs +153 -0
  17. cosmol_viewer-0.1.1.dev2/crates/core/src/utils.rs +85 -0
  18. {cosmol_viewer-0.1.0/cosmol_viewer_python → cosmol_viewer-0.1.1.dev2/crates/python}/Cargo.toml +6 -2
  19. cosmol_viewer-0.1.1.dev2/crates/python/build.rs +22 -0
  20. cosmol_viewer-0.1.1.dev2/crates/python/src/lib.rs +342 -0
  21. cosmol_viewer-0.1.1.dev2/crates/python/src/parser.rs +24 -0
  22. cosmol_viewer-0.1.1.dev2/crates/python/src/shapes.rs +97 -0
  23. {cosmol_viewer-0.1.0 → cosmol_viewer-0.1.1.dev2}/pyproject.toml +6 -7
  24. cosmol_viewer-0.1.0/Cargo.toml +0 -15
  25. cosmol_viewer-0.1.0/cosmol_viewer_core/src/lib.rs +0 -113
  26. cosmol_viewer-0.1.0/cosmol_viewer_core/src/shader/mod.rs +0 -3
  27. cosmol_viewer-0.1.0/cosmol_viewer_python/build.rs +0 -22
  28. cosmol_viewer-0.1.0/cosmol_viewer_python/src/lib.rs +0 -150
  29. {cosmol_viewer-0.1.0/cosmol_viewer_core → cosmol_viewer-0.1.1.dev2/crates/core}/src/shader/bg_fragment.glsl +0 -0
  30. {cosmol_viewer-0.1.0/cosmol_viewer_core → cosmol_viewer-0.1.1.dev2/crates/core}/src/shader/bg_vertex.glsl +0 -0
@@ -431,6 +431,15 @@ version = "0.22.1"
431
431
  source = "registry+https://github.com/rust-lang/crates.io-index"
432
432
  checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
433
433
 
434
+ [[package]]
435
+ name = "bincode"
436
+ version = "1.3.3"
437
+ source = "registry+https://github.com/rust-lang/crates.io-index"
438
+ checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
439
+ dependencies = [
440
+ "serde",
441
+ ]
442
+
434
443
  [[package]]
435
444
  name = "bit-set"
436
445
  version = "0.8.0"
@@ -688,35 +697,75 @@ dependencies = [
688
697
  "libc",
689
698
  ]
690
699
 
700
+ [[package]]
701
+ name = "cosmol_viewer"
702
+ version = "0.1.1-nightly.2"
703
+ dependencies = [
704
+ "bytemuck",
705
+ "cosmol_viewer_core",
706
+ "eframe",
707
+ "egui_extras",
708
+ "glam",
709
+ "hex",
710
+ "ipc-channel",
711
+ "serde",
712
+ "serde_json",
713
+ "sha2",
714
+ "wasm-bindgen-futures",
715
+ "web-sys",
716
+ ]
717
+
691
718
  [[package]]
692
719
  name = "cosmol_viewer_core"
693
- version = "0.1.0"
720
+ version = "0.1.1-nightly.2"
721
+ dependencies = [
722
+ "bytemuck",
723
+ "eframe",
724
+ "egui_extras",
725
+ "glam",
726
+ "serde",
727
+ "serde_json",
728
+ "wasm-bindgen-futures",
729
+ "web-sys",
730
+ ]
731
+
732
+ [[package]]
733
+ name = "cosmol_viewer_gui"
734
+ version = "0.1.1-nightly.2"
694
735
  dependencies = [
695
736
  "bytemuck",
737
+ "cosmol_viewer_core",
696
738
  "eframe",
697
739
  "egui_extras",
698
740
  "glam",
741
+ "ipc-channel",
699
742
  "serde",
743
+ "serde_json",
744
+ "wasm-bindgen-futures",
745
+ "web-sys",
700
746
  ]
701
747
 
702
748
  [[package]]
703
749
  name = "cosmol_viewer_python"
704
- version = "0.1.0"
750
+ version = "0.0.0"
705
751
  dependencies = [
706
752
  "base64 0.22.1",
707
753
  "cosmol_viewer_core",
708
754
  "eframe",
709
755
  "egui_extras",
710
756
  "glam",
757
+ "hex",
758
+ "ipc-channel",
711
759
  "pyo3",
712
760
  "serde_json",
761
+ "sha2",
713
762
  "uuid",
714
763
  "wasm-bindgen",
715
764
  ]
716
765
 
717
766
  [[package]]
718
767
  name = "cosmol_viewer_wasm"
719
- version = "0.1.0"
768
+ version = "0.1.1-nightly.2"
720
769
  dependencies = [
721
770
  "cosmol_viewer_core",
722
771
  "eframe",
@@ -747,6 +796,15 @@ dependencies = [
747
796
  "cfg-if",
748
797
  ]
749
798
 
799
+ [[package]]
800
+ name = "crossbeam-channel"
801
+ version = "0.5.15"
802
+ source = "registry+https://github.com/rust-lang/crates.io-index"
803
+ checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
804
+ dependencies = [
805
+ "crossbeam-utils",
806
+ ]
807
+
750
808
  [[package]]
751
809
  name = "crossbeam-utils"
752
810
  version = "0.8.21"
@@ -1132,6 +1190,12 @@ version = "0.9.0"
1132
1190
  source = "registry+https://github.com/rust-lang/crates.io-index"
1133
1191
  checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
1134
1192
 
1193
+ [[package]]
1194
+ name = "fnv"
1195
+ version = "1.0.7"
1196
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1197
+ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
1198
+
1135
1199
  [[package]]
1136
1200
  name = "foldhash"
1137
1201
  version = "0.1.5"
@@ -1603,6 +1667,24 @@ version = "2.0.6"
1603
1667
  source = "registry+https://github.com/rust-lang/crates.io-index"
1604
1668
  checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
1605
1669
 
1670
+ [[package]]
1671
+ name = "ipc-channel"
1672
+ version = "0.20.0"
1673
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1674
+ checksum = "5b1c98b70019c830a1fc39cecfe1f60ff99c4122f0a189697c810c90ec545c14"
1675
+ dependencies = [
1676
+ "bincode",
1677
+ "crossbeam-channel",
1678
+ "fnv",
1679
+ "libc",
1680
+ "mio",
1681
+ "rand 0.9.1",
1682
+ "serde",
1683
+ "tempfile",
1684
+ "uuid",
1685
+ "windows",
1686
+ ]
1687
+
1606
1688
  [[package]]
1607
1689
  name = "itoa"
1608
1690
  version = "1.0.15"
@@ -1826,6 +1908,17 @@ dependencies = [
1826
1908
  "simd-adler32",
1827
1909
  ]
1828
1910
 
1911
+ [[package]]
1912
+ name = "mio"
1913
+ version = "1.0.4"
1914
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1915
+ checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
1916
+ dependencies = [
1917
+ "libc",
1918
+ "wasi 0.11.1+wasi-snapshot-preview1",
1919
+ "windows-sys 0.59.0",
1920
+ ]
1921
+
1829
1922
  [[package]]
1830
1923
  name = "naga"
1831
1924
  version = "24.0.0"
@@ -2318,7 +2411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
2318
2411
  checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
2319
2412
  dependencies = [
2320
2413
  "phf_shared",
2321
- "rand",
2414
+ "rand 0.8.5",
2322
2415
  ]
2323
2416
 
2324
2417
  [[package]]
@@ -2579,8 +2672,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
2579
2672
  checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
2580
2673
  dependencies = [
2581
2674
  "libc",
2582
- "rand_chacha",
2583
- "rand_core",
2675
+ "rand_chacha 0.3.1",
2676
+ "rand_core 0.6.4",
2677
+ ]
2678
+
2679
+ [[package]]
2680
+ name = "rand"
2681
+ version = "0.9.1"
2682
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2683
+ checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
2684
+ dependencies = [
2685
+ "rand_chacha 0.9.0",
2686
+ "rand_core 0.9.3",
2584
2687
  ]
2585
2688
 
2586
2689
  [[package]]
@@ -2590,7 +2693,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
2590
2693
  checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
2591
2694
  dependencies = [
2592
2695
  "ppv-lite86",
2593
- "rand_core",
2696
+ "rand_core 0.6.4",
2697
+ ]
2698
+
2699
+ [[package]]
2700
+ name = "rand_chacha"
2701
+ version = "0.9.0"
2702
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2703
+ checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
2704
+ dependencies = [
2705
+ "ppv-lite86",
2706
+ "rand_core 0.9.3",
2594
2707
  ]
2595
2708
 
2596
2709
  [[package]]
@@ -2602,6 +2715,15 @@ dependencies = [
2602
2715
  "getrandom 0.2.16",
2603
2716
  ]
2604
2717
 
2718
+ [[package]]
2719
+ name = "rand_core"
2720
+ version = "0.9.3"
2721
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2722
+ checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
2723
+ dependencies = [
2724
+ "getrandom 0.3.3",
2725
+ ]
2726
+
2605
2727
  [[package]]
2606
2728
  name = "raw-window-handle"
2607
2729
  version = "0.6.2"
@@ -2805,6 +2927,17 @@ dependencies = [
2805
2927
  "digest",
2806
2928
  ]
2807
2929
 
2930
+ [[package]]
2931
+ name = "sha2"
2932
+ version = "0.10.9"
2933
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2934
+ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
2935
+ dependencies = [
2936
+ "cfg-if",
2937
+ "cpufeatures",
2938
+ "digest",
2939
+ ]
2940
+
2808
2941
  [[package]]
2809
2942
  name = "shlex"
2810
2943
  version = "1.3.0"
@@ -3027,8 +3160,9 @@ dependencies = [
3027
3160
 
3028
3161
  [[package]]
3029
3162
  name = "test"
3030
- version = "0.1.0"
3163
+ version = "0.1.1-nightly.2"
3031
3164
  dependencies = [
3165
+ "cosmol_viewer",
3032
3166
  "cosmol_viewer_core",
3033
3167
  "eframe",
3034
3168
  "egui_extras",
@@ -4242,7 +4376,7 @@ dependencies = [
4242
4376
  "hex",
4243
4377
  "nix",
4244
4378
  "ordered-stream",
4245
- "rand",
4379
+ "rand 0.8.5",
4246
4380
  "serde",
4247
4381
  "serde_repr",
4248
4382
  "sha1",
@@ -0,0 +1,22 @@
1
+ [workspace.package]
2
+ edition = "2024"
3
+ version = "0.1.1-nightly.2"
4
+ authors = ["9028 wjt@cosmol.org"]
5
+ repository = "https://github.com/COSMol-repl/COSMol-viewer"
6
+ homepage = "https://github.com/COSMol-repl/COSMol-viewer"
7
+ keywords = ["molecular", "visualization"]
8
+
9
+ [workspace]
10
+ resolver = "2"
11
+ members = ["crates/python"]
12
+
13
+ [workspace.dependencies]
14
+ cosmol_viewer = {path = "cosmol_viewer"}
15
+ cosmol_viewer_core = { path = "crates/core" }
16
+ eframe = { version = "0.31.1"}
17
+ egui_extras = { version = "0.31.1", features = ["svg"] }
18
+ glam = { version = "0.30.3" , features = ["serde"] }
19
+ serde_json = "1.0.140"
20
+ sha2 = "0.10.9"
21
+ hex = "0.4.3"
22
+ ipc-channel = "0.20.0"
@@ -1,5 +1,5 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cosmol-viewer
3
- Version: 0.1.0
3
+ Version: 0.1.1.dev2
4
4
  Summary: Molecular visualization tools
5
5
  Author-email: 95028 <wjt@cosmol.org>
@@ -2,10 +2,14 @@
2
2
  name = "cosmol_viewer_core"
3
3
  version.workspace = true
4
4
  edition = "2024"
5
+ publish = false
5
6
 
6
7
  [dependencies]
7
8
  eframe.workspace = true
9
+ serde_json.workspace = true
8
10
  glam.workspace = true
9
11
  egui_extras.workspace = true
10
12
  serde = { version = "1.0.219" , features = ["derive"] }
11
13
  bytemuck = "1.23.1"
14
+ web-sys = "0.3.77"
15
+ wasm-bindgen-futures = "0.4.50"
@@ -0,0 +1,72 @@
1
+ mod shader;
2
+ use std::{
3
+ sync::{Arc, Mutex},
4
+ };
5
+
6
+ pub mod utils;
7
+ pub mod parser;
8
+
9
+ use eframe::egui::{self, Color32, Stroke};
10
+
11
+ use shader::Canvas;
12
+
13
+ pub use crate::utils::{Shape};
14
+ pub mod shapes;
15
+ use crate::{scene::Scene};
16
+
17
+ pub mod scene;
18
+
19
+ pub struct AppWrapper(pub Arc<Mutex<Option<App>>>);
20
+
21
+ impl eframe::App for AppWrapper {
22
+ fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
23
+ if let Some(app) = &mut *self.0.lock().unwrap() {
24
+ app.update(ctx, frame);
25
+ }
26
+ }
27
+ }
28
+
29
+ pub struct App {
30
+ canvas: Canvas,
31
+ gl: Option<Arc<eframe::glow::Context>>,
32
+ pub ctx: egui::Context,
33
+ }
34
+
35
+ impl App {
36
+ // #[cfg(not(target_arch = "wasm32"))]
37
+ pub fn new(cc: &eframe::CreationContext<'_>, scene: Scene) -> Self {
38
+ let gl = cc.gl.clone();
39
+ let canvas = Canvas::new(gl.as_ref().unwrap().clone(), scene).unwrap();
40
+ App {
41
+ gl,
42
+ canvas,
43
+ ctx: cc.egui_ctx.clone(),
44
+ }
45
+ }
46
+
47
+ pub fn update_scene(&mut self, scene: Scene) {
48
+ self.canvas.update_scene(scene);
49
+ }
50
+ }
51
+
52
+ impl eframe::App for App {
53
+ fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
54
+ egui_extras::install_image_loaders(ctx);
55
+ egui::CentralPanel::default()
56
+ .frame(
57
+ egui::Frame::default()
58
+ .fill(Color32::from_rgb(48, 48, 48))
59
+ .inner_margin(0.0)
60
+ .outer_margin(0.0)
61
+ .stroke(Stroke::new(0.0, Color32::from_rgb(30, 200, 30))),
62
+ )
63
+ .show(ctx, |ui| {
64
+ ui.set_width(ui.available_width());
65
+ ui.set_height(ui.available_height());
66
+
67
+ self.canvas.custom_painting(ui);
68
+ });
69
+ }
70
+ }
71
+
72
+
@@ -0,0 +1,2 @@
1
+ pub mod sdf;
2
+
@@ -0,0 +1,197 @@
1
+ #[derive(Debug, Clone)]
2
+ pub struct Atom {
3
+ pub atom: String,
4
+ pub elem: String,
5
+ pub x: f32,
6
+ pub y: f32,
7
+ pub z: f32,
8
+ pub serial: usize,
9
+ pub index: usize,
10
+ pub hetflag: bool,
11
+ pub bonds: Vec<usize>,
12
+ pub bond_order: Vec<f32>,
13
+ pub properties: std::collections::HashMap<String, String>,
14
+ }
15
+
16
+ pub type Molecule = Vec<Atom>;
17
+ pub type MoleculeData = Vec<Molecule>;
18
+
19
+ #[derive(Default)]
20
+ pub struct ParserOptions {
21
+ pub keep_h: bool,
22
+ pub multimodel: bool,
23
+ pub onemol: bool,
24
+ }
25
+
26
+ pub fn parse_sdf(sdf: &str, options: &ParserOptions) -> MoleculeData {
27
+ let lines: Vec<&str> = sdf.lines().collect();
28
+ if lines.len() > 3 && lines[3].len() > 38 {
29
+ let version = lines[3][34..39].trim();
30
+ match version {
31
+ "V3000" => parse_v3000(lines, options),
32
+ _ => parse_v2000(lines, options),
33
+ }
34
+ } else {
35
+ vec![vec![]]
36
+ }
37
+ }
38
+
39
+ fn parse_v2000(mut lines: Vec<&str>, options: &ParserOptions) -> MoleculeData {
40
+ let mut molecules = vec![vec![]];
41
+ let mut current = 0;
42
+
43
+ while lines.len() >= 4 {
44
+ let header = lines[3];
45
+ let atom_count = header[0..3].trim().parse::<usize>().unwrap_or(0);
46
+ let bond_count = header[3..6].trim().parse::<usize>().unwrap_or(0);
47
+
48
+ if atom_count == 0 || lines.len() < 4 + atom_count + bond_count {
49
+ break;
50
+ }
51
+
52
+ let mut serial_to_index = vec![None; atom_count];
53
+ let mut offset = 4;
54
+ let start = molecules[current].len();
55
+
56
+ for i in 0..atom_count {
57
+ let line = lines[offset + i];
58
+ let elem = line[31..34].trim();
59
+ let elem_cap = capitalize(elem);
60
+ if elem_cap != "H" || options.keep_h {
61
+ let atom = Atom {
62
+ atom: elem_cap.clone(),
63
+ elem: elem_cap,
64
+ x: line[0..10].trim().parse().unwrap_or(0.0),
65
+ y: line[10..20].trim().parse().unwrap_or(0.0),
66
+ z: line[20..30].trim().parse().unwrap_or(0.0),
67
+ serial: start + i,
68
+ index: molecules[current].len(),
69
+ hetflag: true,
70
+ bonds: vec![],
71
+ bond_order: vec![],
72
+ properties: std::collections::HashMap::new(),
73
+ };
74
+ serial_to_index[i] = Some(molecules[current].len());
75
+ molecules[current].push(atom);
76
+ }
77
+ }
78
+
79
+ offset += atom_count;
80
+
81
+ for i in 0..bond_count {
82
+ let line = lines[offset + i];
83
+ let from = line[0..3].trim().parse::<usize>().unwrap_or(0).saturating_sub(1);
84
+ let to = line[3..6].trim().parse::<usize>().unwrap_or(0).saturating_sub(1);
85
+ let order = line[6..].trim().parse::<f32>().unwrap_or(1.0);
86
+ if let (Some(f), Some(t)) = (serial_to_index.get(from).and_then(|x| *x), serial_to_index.get(to).and_then(|x| *x)) {
87
+ molecules[current][f].bonds.push(t);
88
+ molecules[current][f].bond_order.push(order);
89
+ molecules[current][t].bonds.push(f);
90
+ molecules[current][t].bond_order.push(order);
91
+ }
92
+ }
93
+
94
+ let mut next_offset = offset + bond_count;
95
+ if options.multimodel {
96
+ if !options.onemol {
97
+ molecules.push(vec![]);
98
+ current += 1;
99
+ }
100
+ while next_offset < lines.len() && lines[next_offset] != "$$$$" {
101
+ next_offset += 1;
102
+ }
103
+ lines.drain(0..=next_offset);
104
+ } else {
105
+ break;
106
+ }
107
+ }
108
+
109
+ molecules
110
+ }
111
+
112
+ fn parse_v3000(mut lines: Vec<&str>, options: &ParserOptions) -> MoleculeData {
113
+ let mut molecules = vec![vec![]];
114
+ let mut current = 0;
115
+
116
+ while lines.len() >= 8 {
117
+ if !lines[4].starts_with("M V30 BEGIN CTAB") || !lines[5].starts_with("M V30 COUNTS") {
118
+ break;
119
+ }
120
+
121
+ let counts: Vec<_> = lines[5][13..].split_whitespace().collect();
122
+ let atom_count = counts.get(0).and_then(|s| s.parse::<usize>().ok()).unwrap_or(0);
123
+ let bond_count = counts.get(1).and_then(|s| s.parse::<usize>().ok()).unwrap_or(0);
124
+ let mut offset = 7;
125
+
126
+ let mut serial_to_index = vec![None; atom_count];
127
+ let start = molecules[current].len();
128
+
129
+ for i in 0..atom_count {
130
+ let line = lines[offset + i];
131
+ let parts: Vec<_> = line[6..].split_whitespace().collect();
132
+ if parts.len() > 4 {
133
+ let elem_cap = capitalize(parts[1]);
134
+ if elem_cap != "H" || options.keep_h {
135
+ let atom = Atom {
136
+ atom: elem_cap.clone(),
137
+ elem: elem_cap,
138
+ x: parts[2].parse().unwrap_or(0.0),
139
+ y: parts[3].parse().unwrap_or(0.0),
140
+ z: parts[4].parse().unwrap_or(0.0),
141
+ serial: start + i,
142
+ index: molecules[current].len(),
143
+ hetflag: true,
144
+ bonds: vec![],
145
+ bond_order: vec![],
146
+ properties: std::collections::HashMap::new(),
147
+ };
148
+ serial_to_index[i] = Some(molecules[current].len());
149
+ molecules[current].push(atom);
150
+ }
151
+ }
152
+ }
153
+
154
+ offset += atom_count + 1; // skip "END ATOM"
155
+ offset += 1; // BEGIN BOND
156
+
157
+ for i in 0..bond_count {
158
+ let line = lines[offset + i];
159
+ let parts: Vec<_> = line[6..].split_whitespace().collect();
160
+ if parts.len() > 3 {
161
+ let from = parts[2].parse::<usize>().unwrap_or(0).saturating_sub(1);
162
+ let to = parts[3].parse::<usize>().unwrap_or(0).saturating_sub(1);
163
+ let order = parts[1].parse::<f32>().unwrap_or(1.0);
164
+ if let (Some(f), Some(t)) = (serial_to_index.get(from).and_then(|x| *x), serial_to_index.get(to).and_then(|x| *x)) {
165
+ molecules[current][f].bonds.push(t);
166
+ molecules[current][f].bond_order.push(order);
167
+ molecules[current][t].bonds.push(f);
168
+ molecules[current][t].bond_order.push(order);
169
+ }
170
+ }
171
+ }
172
+
173
+ let mut next_offset = offset + bond_count;
174
+ if options.multimodel {
175
+ if !options.onemol {
176
+ molecules.push(vec![]);
177
+ current += 1;
178
+ }
179
+ while next_offset < lines.len() && lines[next_offset] != "$$$$" {
180
+ next_offset += 1;
181
+ }
182
+ lines.drain(0..=next_offset);
183
+ } else {
184
+ break;
185
+ }
186
+ }
187
+
188
+ molecules
189
+ }
190
+
191
+ fn capitalize(s: &str) -> String {
192
+ let mut chars = s.chars();
193
+ match chars.next() {
194
+ Some(first) => first.to_ascii_uppercase().to_string() + &chars.as_str().to_ascii_lowercase(),
195
+ None => String::new(),
196
+ }
197
+ }
@@ -0,0 +1,63 @@
1
+ use std::collections::HashMap;
2
+
3
+ use serde::{Deserialize, Serialize};
4
+
5
+ use crate::{shader::CameraState, utils::{self, ToMesh}, Shape};
6
+
7
+
8
+ #[derive(Deserialize, Serialize, Clone)]
9
+ pub struct Scene {
10
+ pub background_color: [f32; 3],
11
+ pub camera_state: CameraState,
12
+ pub named_shapes: HashMap<String, Shape>,
13
+ pub unnamed_shapes: Vec<Shape>,
14
+ pub scale: f32,
15
+ }
16
+
17
+ impl Scene {
18
+ pub fn _get_meshes(&self) -> Vec<utils::MeshData> {
19
+ self.named_shapes
20
+ .values()
21
+ .chain(self.unnamed_shapes.iter())
22
+ .map(|s| s.to_mesh(self.scale))
23
+ .collect()
24
+ }
25
+
26
+ pub fn new() -> Self {
27
+ Scene {
28
+ background_color: [1.0, 1.0, 1.0],
29
+ camera_state: CameraState::new(1.0),
30
+ named_shapes: HashMap::new(),
31
+ unnamed_shapes: Vec::new(),
32
+ scale: 1.0,
33
+ }
34
+ }
35
+
36
+ pub fn scale(&mut self, scale: f32) {
37
+ self.scale = scale;
38
+ }
39
+
40
+ pub fn add_shape<S: Into<Shape>>(&mut self, shape: S, id: Option<&str>) {
41
+ let shape = shape.into();
42
+ if let Some(id) = id {
43
+ self.named_shapes.insert(id.into(), shape);
44
+ } else {
45
+ self.unnamed_shapes.push(shape);
46
+ }
47
+ }
48
+
49
+ pub fn delete_shape(&mut self, id: &str) {
50
+ if self.named_shapes.remove(id).is_none() {
51
+ panic!("Sphere with ID '{}' not found", id);
52
+ }
53
+ }
54
+
55
+ pub fn update_shape<S: Into<Shape>>(&mut self, id: &str, shape: S) {
56
+ let shape = shape.into();
57
+ if let Some(existing_shape) = self.named_shapes.get_mut(id) {
58
+ *existing_shape = shape;
59
+ } else {
60
+ panic!("Shape with ID '{}' not found", id);
61
+ }
62
+ }
63
+ }