cosmol-viewer 0.1.4.dev3__tar.gz → 0.1.5.dev1__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.
Files changed (32) hide show
  1. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/Cargo.lock +38 -53
  2. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/Cargo.toml +6 -6
  3. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/PKG-INFO +1 -1
  4. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/Cargo.toml +1 -2
  5. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/src/lib.rs +33 -24
  6. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/src/parser/sdf.rs +6 -0
  7. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/src/scene.rs +7 -5
  8. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/src/shader/canvas.rs +17 -6
  9. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/src/shapes/molecules.rs +64 -27
  10. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/src/shapes/sphere.rs +4 -2
  11. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/src/shapes/stick.rs +2 -2
  12. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/src/utils.rs +29 -6
  13. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/python/cosmol_viewer.pyi +4 -2
  14. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/python/src/shapes.rs +8 -5
  15. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/wasm/Cargo.toml +4 -5
  16. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/wasm/src/lib.rs +31 -4
  17. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/pyproject.toml +1 -1
  18. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/README.md +0 -0
  19. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/src/parser/mod.rs +0 -0
  20. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/src/shader/bg_fragment.glsl +0 -0
  21. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/src/shader/bg_vertex.glsl +0 -0
  22. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/src/shader/fragment.glsl +0 -0
  23. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/src/shader/mod.rs +0 -0
  24. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/src/shader/vertex.glsl +0 -0
  25. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/src/shader/vertex_sphere.glsl +0 -0
  26. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/src/shader/vertex_stick.glsl +0 -0
  27. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/core/src/shapes/mod.rs +0 -0
  28. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/python/Cargo.toml +0 -0
  29. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/python/README.md +0 -0
  30. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/python/build.rs +0 -0
  31. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/python/src/lib.rs +0 -0
  32. {cosmol_viewer-0.1.4.dev3 → cosmol_viewer-0.1.5.dev1}/crates/python/src/parser.rs +0 -0
@@ -759,7 +759,7 @@ dependencies = [
759
759
 
760
760
  [[package]]
761
761
  name = "cosmol_viewer"
762
- version = "0.1.4-nightly.3"
762
+ version = "0.1.5-nightly.1"
763
763
  dependencies = [
764
764
  "bytemuck",
765
765
  "cosmol_viewer_core",
@@ -772,7 +772,7 @@ dependencies = [
772
772
 
773
773
  [[package]]
774
774
  name = "cosmol_viewer_core"
775
- version = "0.1.4-nightly.3"
775
+ version = "0.1.5-nightly.1"
776
776
  dependencies = [
777
777
  "bytemuck",
778
778
  "eframe",
@@ -784,7 +784,6 @@ dependencies = [
784
784
  "serde",
785
785
  "serde_json",
786
786
  "serde_repr",
787
- "wasm-bindgen-futures",
788
787
  ]
789
788
 
790
789
  [[package]]
@@ -802,7 +801,7 @@ dependencies = [
802
801
 
803
802
  [[package]]
804
803
  name = "cosmol_viewer_wasm"
805
- version = "0.1.4-nightly.3"
804
+ version = "0.0.0"
806
805
  dependencies = [
807
806
  "base64",
808
807
  "cosmol_viewer_core",
@@ -923,9 +922,9 @@ checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
923
922
 
924
923
  [[package]]
925
924
  name = "ecolor"
926
- version = "0.33.0"
925
+ version = "0.33.2"
927
926
  source = "registry+https://github.com/rust-lang/crates.io-index"
928
- checksum = "adf31f99fad93fe83c1055b92b5c1b135f1ecfa464789817c372000e768d4bd1"
927
+ checksum = "084980ebede2fb1ad6c4f54285b3e489052ef2b6aa4016e4c19349417adc75c5"
929
928
  dependencies = [
930
929
  "bytemuck",
931
930
  "emath",
@@ -933,9 +932,9 @@ dependencies = [
933
932
 
934
933
  [[package]]
935
934
  name = "eframe"
936
- version = "0.33.0"
935
+ version = "0.33.2"
937
936
  source = "registry+https://github.com/rust-lang/crates.io-index"
938
- checksum = "b829d302a09deb4acde242262a1840ba14fadd0371980ebf713060077a1987bc"
937
+ checksum = "4f60ee3c69cd01d5725b4ef476ed6a3a6009968c57843a2f1beb96f021e4a54b"
939
938
  dependencies = [
940
939
  "ahash",
941
940
  "bytemuck",
@@ -968,9 +967,9 @@ dependencies = [
968
967
 
969
968
  [[package]]
970
969
  name = "egui"
971
- version = "0.33.0"
970
+ version = "0.33.2"
972
971
  source = "registry+https://github.com/rust-lang/crates.io-index"
973
- checksum = "ab9b5d3376c79439f53a78bf7da1e3c0b862ffa3e29f46ab0f3e107430f2e576"
972
+ checksum = "b75645894de4ca1695ab3ab7201c7953bb95c1725aafeefa6822dc901ad2a81b"
974
973
  dependencies = [
975
974
  "accesskit",
976
975
  "ahash",
@@ -986,9 +985,9 @@ dependencies = [
986
985
 
987
986
  [[package]]
988
987
  name = "egui-wgpu"
989
- version = "0.33.0"
988
+ version = "0.33.2"
990
989
  source = "registry+https://github.com/rust-lang/crates.io-index"
991
- checksum = "cef1fe83ba30b3d045814b2d811804f2a7e50a832034c975408f71c20df596e4"
990
+ checksum = "acdaac35a6e0ff458aaa5776b2bda578e38334a9d1258f7795042a70372745b7"
992
991
  dependencies = [
993
992
  "ahash",
994
993
  "bytemuck",
@@ -1006,9 +1005,9 @@ dependencies = [
1006
1005
 
1007
1006
  [[package]]
1008
1007
  name = "egui-winit"
1009
- version = "0.33.0"
1008
+ version = "0.33.2"
1010
1009
  source = "registry+https://github.com/rust-lang/crates.io-index"
1011
- checksum = "bb4ea8cb063c00d8f23ce11279c01eb63a195a72be0e21d429148246dab7983e"
1010
+ checksum = "4772ed5f16fa8ec2ba295e58f62b58ee83fcf49e67ec13d2b7ddf4e9a2dea34e"
1012
1011
  dependencies = [
1013
1012
  "accesskit_winit",
1014
1013
  "arboard",
@@ -1028,9 +1027,9 @@ dependencies = [
1028
1027
 
1029
1028
  [[package]]
1030
1029
  name = "egui_extras"
1031
- version = "0.33.0"
1030
+ version = "0.33.2"
1032
1031
  source = "registry+https://github.com/rust-lang/crates.io-index"
1033
- checksum = "bdced1964ad8a02a116b1307f7b4f73dbe408c5f53dcdd488f527609f261da60"
1032
+ checksum = "550e844e608e356f4ad6843c510aa9bb5838b427e4700ed0056e9746ceeed866"
1034
1033
  dependencies = [
1035
1034
  "ahash",
1036
1035
  "egui",
@@ -1042,9 +1041,9 @@ dependencies = [
1042
1041
 
1043
1042
  [[package]]
1044
1043
  name = "egui_glow"
1045
- version = "0.33.0"
1044
+ version = "0.33.2"
1046
1045
  source = "registry+https://github.com/rust-lang/crates.io-index"
1047
- checksum = "668c0d4f726cc33838f0915f6b8c00af0ca0910e975ab58cf34b3e39c614552c"
1046
+ checksum = "17b94ff67a1d18933fff2519f5f57c388f932c093036c381fb9ae2853b3e1e09"
1048
1047
  dependencies = [
1049
1048
  "bytemuck",
1050
1049
  "egui",
@@ -1065,9 +1064,9 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
1065
1064
 
1066
1065
  [[package]]
1067
1066
  name = "emath"
1068
- version = "0.33.0"
1067
+ version = "0.33.2"
1069
1068
  source = "registry+https://github.com/rust-lang/crates.io-index"
1070
- checksum = "c615516cdceec867065f20d7db13d8eb8aedd65c9e32cc0c7c379380fa42e6e8"
1069
+ checksum = "0e561352ae95c22ad179fb56c38d6e6eecd86cf4925cf5c70e738dd01df9b620"
1071
1070
  dependencies = [
1072
1071
  "bytemuck",
1073
1072
  ]
@@ -1121,9 +1120,9 @@ dependencies = [
1121
1120
 
1122
1121
  [[package]]
1123
1122
  name = "epaint"
1124
- version = "0.33.0"
1123
+ version = "0.33.2"
1125
1124
  source = "registry+https://github.com/rust-lang/crates.io-index"
1126
- checksum = "9926b9500ccb917adb070207ec722dd8ea78b8321f94a85ebec776f501f2930c"
1125
+ checksum = "a909ce8cee63e8350fb0c251ad39277a5b24f19add60787e84a3b3ab3f2bd83a"
1127
1126
  dependencies = [
1128
1127
  "ab_glyph",
1129
1128
  "ahash",
@@ -1139,9 +1138,9 @@ dependencies = [
1139
1138
 
1140
1139
  [[package]]
1141
1140
  name = "epaint_default_fonts"
1142
- version = "0.33.0"
1141
+ version = "0.33.2"
1143
1142
  source = "registry+https://github.com/rust-lang/crates.io-index"
1144
- checksum = "66054d943c66715c6003a27a3dc152d87cadf714ef2597ccd79f550413009b97"
1143
+ checksum = "ad9649446c23368ae138716910e3e28143995691b598fbb9de16b42b0722cbcc"
1145
1144
 
1146
1145
  [[package]]
1147
1146
  name = "equator"
@@ -1859,9 +1858,9 @@ dependencies = [
1859
1858
 
1860
1859
  [[package]]
1861
1860
  name = "js-sys"
1862
- version = "0.3.81"
1861
+ version = "0.3.82"
1863
1862
  source = "registry+https://github.com/rust-lang/crates.io-index"
1864
- checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305"
1863
+ checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65"
1865
1864
  dependencies = [
1866
1865
  "once_cell",
1867
1866
  "wasm-bindgen",
@@ -3708,9 +3707,9 @@ dependencies = [
3708
3707
 
3709
3708
  [[package]]
3710
3709
  name = "wasm-bindgen"
3711
- version = "0.2.104"
3710
+ version = "0.2.105"
3712
3711
  source = "registry+https://github.com/rust-lang/crates.io-index"
3713
- checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d"
3712
+ checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60"
3714
3713
  dependencies = [
3715
3714
  "cfg-if",
3716
3715
  "once_cell",
@@ -3719,25 +3718,11 @@ dependencies = [
3719
3718
  "wasm-bindgen-shared",
3720
3719
  ]
3721
3720
 
3722
- [[package]]
3723
- name = "wasm-bindgen-backend"
3724
- version = "0.2.104"
3725
- source = "registry+https://github.com/rust-lang/crates.io-index"
3726
- checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19"
3727
- dependencies = [
3728
- "bumpalo",
3729
- "log",
3730
- "proc-macro2",
3731
- "quote",
3732
- "syn",
3733
- "wasm-bindgen-shared",
3734
- ]
3735
-
3736
3721
  [[package]]
3737
3722
  name = "wasm-bindgen-futures"
3738
- version = "0.4.54"
3723
+ version = "0.4.55"
3739
3724
  source = "registry+https://github.com/rust-lang/crates.io-index"
3740
- checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c"
3725
+ checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0"
3741
3726
  dependencies = [
3742
3727
  "cfg-if",
3743
3728
  "js-sys",
@@ -3748,9 +3733,9 @@ dependencies = [
3748
3733
 
3749
3734
  [[package]]
3750
3735
  name = "wasm-bindgen-macro"
3751
- version = "0.2.104"
3736
+ version = "0.2.105"
3752
3737
  source = "registry+https://github.com/rust-lang/crates.io-index"
3753
- checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119"
3738
+ checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2"
3754
3739
  dependencies = [
3755
3740
  "quote",
3756
3741
  "wasm-bindgen-macro-support",
@@ -3758,22 +3743,22 @@ dependencies = [
3758
3743
 
3759
3744
  [[package]]
3760
3745
  name = "wasm-bindgen-macro-support"
3761
- version = "0.2.104"
3746
+ version = "0.2.105"
3762
3747
  source = "registry+https://github.com/rust-lang/crates.io-index"
3763
- checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7"
3748
+ checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc"
3764
3749
  dependencies = [
3750
+ "bumpalo",
3765
3751
  "proc-macro2",
3766
3752
  "quote",
3767
3753
  "syn",
3768
- "wasm-bindgen-backend",
3769
3754
  "wasm-bindgen-shared",
3770
3755
  ]
3771
3756
 
3772
3757
  [[package]]
3773
3758
  name = "wasm-bindgen-shared"
3774
- version = "0.2.104"
3759
+ version = "0.2.105"
3775
3760
  source = "registry+https://github.com/rust-lang/crates.io-index"
3776
- checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1"
3761
+ checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76"
3777
3762
  dependencies = [
3778
3763
  "unicode-ident",
3779
3764
  ]
@@ -3889,9 +3874,9 @@ dependencies = [
3889
3874
 
3890
3875
  [[package]]
3891
3876
  name = "web-sys"
3892
- version = "0.3.81"
3877
+ version = "0.3.82"
3893
3878
  source = "registry+https://github.com/rust-lang/crates.io-index"
3894
- checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120"
3879
+ checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1"
3895
3880
  dependencies = [
3896
3881
  "js-sys",
3897
3882
  "wasm-bindgen",
@@ -1,6 +1,6 @@
1
1
  [workspace.package]
2
2
  edition = "2024"
3
- version = "0.1.4-nightly.3"
3
+ version = "0.1.5-nightly.1"
4
4
  authors = ["9028 wjt@cosmol.org"]
5
5
  repository = "https://github.com/COSMol-repl/COSMol-viewer"
6
6
  homepage = "https://github.com/COSMol-repl/COSMol-viewer"
@@ -13,13 +13,13 @@ resolver = "2"
13
13
  members = ["crates/python"]
14
14
 
15
15
  [workspace.dependencies]
16
- cosmol_viewer = { version = "0.1.4-nightly.3", path = "cosmol_viewer"}
17
- cosmol_viewer_core = { version = "0.1.4-nightly.3", path = "crates/core" }
16
+ cosmol_viewer = { version = "0.1.5-nightly.1", path = "cosmol_viewer"}
17
+ cosmol_viewer_core = { version = "0.1.5-nightly.1", path = "crates/core" }
18
18
 
19
- eframe = { version = "0.33.0", features = ["wayland","x11"] }
20
19
  pyo3 = { version = "0.27.1", features = ["extension-module", "abi3-py37"] }
21
- egui-winit = { version = "0.33.0", features = ["wayland","x11"] }
22
- egui_extras = { version = "0.33.0" }
20
+ eframe = { version = "0.33.2", features = ["wayland","x11"] }
21
+ egui-winit = { version = "0.33.2", features = ["wayland","x11"] }
22
+ egui_extras = { version = "0.33.2" }
23
23
  serde = { version = "1.0.228" , features = ["derive"] }
24
24
  serde_json = "1.0.145"
25
25
  base64 = "0.22.1"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cosmol-viewer
3
- Version: 0.1.4.dev3
3
+ Version: 0.1.5.dev1
4
4
  Summary: Molecular visualization tools
5
5
  Author-email: 95028 <wjt@cosmol.org>
6
6
  Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
@@ -12,9 +12,8 @@ serde_json.workspace = true
12
12
  egui_extras.workspace = true
13
13
  serde.workspace = true
14
14
  glam = { version = "0.30.8" , features = ["serde"] }
15
- bytemuck = "1.23.2"
15
+ bytemuck = "1.24.0"
16
16
  serde_repr = "0.1.20"
17
- wasm-bindgen-futures = "0.4.54"
18
17
  once_cell = "1.21.3"
19
18
  image = "0.25.8"
20
19
 
@@ -1,7 +1,5 @@
1
1
  mod shader;
2
- use std::{
3
- sync::{Arc, Mutex},
4
- };
2
+ use std::sync::{Arc, Mutex};
5
3
 
6
4
  pub mod parser;
7
5
  pub mod utils;
@@ -14,16 +12,16 @@ use eframe::{
14
12
 
15
13
  use shader::Canvas;
16
14
 
17
- pub use crate::utils::Shape;
15
+ pub use crate::utils::{Logger, RustLogger, Shape};
18
16
  pub mod shapes;
19
17
  use crate::{scene::Scene, utils::Frames};
20
18
 
21
19
  pub mod scene;
22
20
  use image::{ImageBuffer, Rgba};
23
21
 
24
- pub struct AppWrapper(pub Arc<Mutex<Option<App>>>);
22
+ pub struct AppWrapper<L: Logger>(pub Arc<Mutex<Option<App<L>>>>);
25
23
 
26
- impl eframe::App for AppWrapper {
24
+ impl<L: Logger> eframe::App for AppWrapper<L> {
27
25
  fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
28
26
  if let Some(app) = &mut *self.0.lock().unwrap() {
29
27
  app.update(ctx, frame);
@@ -31,36 +29,41 @@ impl eframe::App for AppWrapper {
31
29
  }
32
30
  }
33
31
 
34
- pub struct App {
35
- canvas: Canvas,
32
+ pub struct App<L: Logger> {
33
+ canvas: Canvas<L>,
36
34
  gl: Option<Arc<eframe::glow::Context>>,
37
35
  pub ctx: egui::Context,
38
36
  screenshot_requested: bool,
39
37
  screenshot_result: Option<(Arc<egui::ColorImage>, egui::TextureHandle)>,
38
+ logger: L,
40
39
  }
41
40
 
42
- impl App {
43
- pub fn new(cc: &eframe::CreationContext<'_>, scene: Scene) -> Self {
41
+ impl<L: Logger> App<L> {
42
+ pub fn new(cc: &eframe::CreationContext<'_>, scene: Scene, logger: L) -> Self {
43
+ logger.log("Creating new viewer app...");
44
44
  let gl = cc.gl.clone();
45
- let canvas = Canvas::new(gl.as_ref().unwrap().clone(), scene).unwrap();
45
+ let canvas = Canvas::new(gl.as_ref().unwrap().clone(), scene, logger).unwrap();
46
46
  App {
47
47
  gl,
48
48
  canvas,
49
49
  ctx: cc.egui_ctx.clone(),
50
50
  screenshot_requested: false,
51
51
  screenshot_result: None,
52
+ logger,
52
53
  }
53
54
  }
54
55
 
55
- pub fn new_play(cc: &eframe::CreationContext<'_>, scene: Frames) -> Self {
56
+ pub fn new_play(cc: &eframe::CreationContext<'_>, scene: Frames, logger: L) -> Self {
57
+ logger.log("Creating new viewer app...");
56
58
  let gl = cc.gl.clone();
57
- let canvas = Canvas::new_play(gl.as_ref().unwrap().clone(), scene).unwrap();
59
+ let canvas = Canvas::new_play(gl.as_ref().unwrap().clone(), scene, logger).unwrap();
58
60
  App {
59
61
  gl,
60
62
  canvas,
61
63
  ctx: cc.egui_ctx.clone(),
62
64
  screenshot_requested: false,
63
65
  screenshot_result: None,
66
+ logger,
64
67
  }
65
68
  }
66
69
 
@@ -93,7 +96,7 @@ fn color_image_to_rgba_bytes(image: &egui::ColorImage) -> Vec<u8> {
93
96
  image.pixels.iter().flat_map(|c| c.to_array()).collect()
94
97
  }
95
98
 
96
- impl eframe::App for App {
99
+ impl<L: Logger> eframe::App for App<L> {
97
100
  fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
98
101
  #[cfg(not(target_arch = "wasm32"))]
99
102
  egui_extras::install_image_loaders(ctx);
@@ -141,16 +144,16 @@ impl eframe::App for App {
141
144
  }
142
145
 
143
146
  pub struct NativeGuiViewer {
144
- pub app: Arc<Mutex<Option<App>>>,
147
+ pub app: Arc<Mutex<Option<App<RustLogger>>>>,
145
148
  }
146
149
 
147
150
  impl NativeGuiViewer {
148
151
  pub fn render(scene: &Scene, width: f32, height: f32) -> Self {
152
+ use std::time::Duration;
149
153
  use std::{
150
154
  sync::{Arc, Mutex},
151
155
  thread,
152
156
  };
153
- use std::time::Duration;
154
157
 
155
158
  #[cfg(not(target_arch = "wasm32"))]
156
159
  use eframe::{
@@ -160,14 +163,14 @@ impl NativeGuiViewer {
160
163
 
161
164
  // let viewport_size = scene.viewport.unwrap_or([800, 500]);
162
165
 
163
- let app: Arc<Mutex<Option<App>>> = Arc::new(Mutex::new(None));
166
+ let app: Arc<Mutex<Option<App<RustLogger>>>> = Arc::new(Mutex::new(None));
164
167
  let app_clone = Arc::clone(&app);
165
168
 
166
169
  let scene = Arc::new(Mutex::new(scene.clone()));
167
170
  #[cfg(not(target_arch = "wasm32"))]
168
171
  thread::spawn(move || {
169
- use std::process;
170
172
  use eframe::{EventLoopBuilderHook, run_native};
173
+ use std::process;
171
174
  let event_loop_builder: Option<EventLoopBuilderHook> =
172
175
  Some(Box::new(|event_loop_builder| {
173
176
  #[cfg(target_family = "windows")]
@@ -200,7 +203,7 @@ impl NativeGuiViewer {
200
203
  native_options,
201
204
  Box::new(move |cc| {
202
205
  let mut guard = app_clone.lock().unwrap();
203
- *guard = Some(App::new(cc, scene.lock().unwrap().clone()));
206
+ *guard = Some(App::new(cc, scene.lock().unwrap().clone(), RustLogger));
204
207
  Ok(Box::new(AppWrapper(app_clone.clone())))
205
208
  }),
206
209
  );
@@ -260,7 +263,14 @@ impl NativeGuiViewer {
260
263
  }
261
264
  }
262
265
 
263
- pub fn play(frames: Vec<Scene>, interval: f32, loops: i64, width: f32, height: f32, smooth: bool) {
266
+ pub fn play(
267
+ frames: Vec<Scene>,
268
+ interval: f32,
269
+ loops: i64,
270
+ width: f32,
271
+ height: f32,
272
+ smooth: bool,
273
+ ) {
264
274
  use std::{
265
275
  sync::{Arc, Mutex},
266
276
  thread,
@@ -279,7 +289,7 @@ impl NativeGuiViewer {
279
289
  egui::{Vec2, ViewportBuilder},
280
290
  };
281
291
 
282
- let app: Arc<Mutex<Option<App>>> = Arc::new(Mutex::new(None));
292
+ let app: Arc<Mutex<Option<App<RustLogger>>>> = Arc::new(Mutex::new(None));
283
293
  let app_clone = Arc::clone(&app);
284
294
 
285
295
  #[cfg(not(target_arch = "wasm32"))]
@@ -319,14 +329,13 @@ impl NativeGuiViewer {
319
329
  native_options,
320
330
  Box::new(move |cc| {
321
331
  let mut guard = app_clone.lock().unwrap();
322
- *guard = Some(App::new_play(cc, frames));
332
+ *guard = Some(App::new_play(cc, frames, RustLogger));
323
333
  Ok(Box::new(AppWrapper(app_clone.clone())))
324
334
  }),
325
335
  );
326
336
  process::exit(0);
327
337
  });
328
338
 
329
- loop {
330
- }
339
+ loop {}
331
340
  }
332
341
  }
@@ -1,3 +1,5 @@
1
+ pub use crate::utils::{Logger, RustLogger};
2
+
1
3
  #[derive(Debug, Clone)]
2
4
  pub struct Atom {
3
5
  pub atom: String,
@@ -24,6 +26,10 @@ pub struct ParserOptions {
24
26
  }
25
27
 
26
28
  pub fn parse_sdf(sdf: &str, options: &ParserOptions) -> MoleculeData {
29
+ _parse_sdf(sdf, options, RustLogger)
30
+ }
31
+
32
+ pub fn _parse_sdf(sdf: &str, options: &ParserOptions, logger: impl Logger) -> MoleculeData {
27
33
  let lines: Vec<&str> = sdf.lines().collect();
28
34
  if lines.len() > 3 && lines[3].len() > 38 {
29
35
  let version = lines[3][34..39].trim();
@@ -1,9 +1,12 @@
1
+ use crate::utils::Logger;
1
2
  use std::collections::HashMap;
2
3
 
3
4
  use serde::{Deserialize, Serialize};
4
5
 
5
6
  use crate::{
6
- shader::CameraState, utils::{self, InstanceData, Interpolatable, IntoInstanceGroups, ShapeKind, ToMesh}, Shape
7
+ Shape,
8
+ shader::CameraState,
9
+ utils::{self, InstanceData, Interpolatable, IntoInstanceGroups, ShapeKind, ToMesh},
7
10
  };
8
11
 
9
12
  #[derive(Deserialize, Serialize, Clone)]
@@ -71,7 +74,6 @@ impl InstanceGroups {
71
74
  }
72
75
  }
73
76
 
74
-
75
77
  impl Scene {
76
78
  pub fn _get_meshes(&self) -> Vec<utils::MeshData> {
77
79
  self.named_shapes
@@ -136,13 +138,13 @@ impl Scene {
136
138
  }
137
139
 
138
140
  impl Interpolatable for Scene {
139
- fn interpolate(&self, other: &Self, t: f32) -> Self {
141
+ fn interpolate(&self, other: &Self, t: f32, logger: impl Logger) -> Self {
140
142
  let named_shapes = self
141
143
  .named_shapes
142
144
  .iter()
143
145
  .map(|(k, v)| {
144
146
  let other_shape = &other.named_shapes[k];
145
- (k.clone(), v.interpolate(other_shape, t))
147
+ (k.clone(), v.interpolate(other_shape, t, logger))
146
148
  })
147
149
  .collect();
148
150
 
@@ -150,7 +152,7 @@ impl Interpolatable for Scene {
150
152
  .unnamed_shapes
151
153
  .iter()
152
154
  .zip(&other.unnamed_shapes)
153
- .map(|(a, b)| a.interpolate(b, t))
155
+ .map(|(a, b)| a.interpolate(b, t, logger))
154
156
  .collect();
155
157
 
156
158
  Self {
@@ -17,31 +17,38 @@ use crate::scene::StickInstance;
17
17
  use crate::shapes::Sphere;
18
18
  use crate::shapes::Stick;
19
19
  use crate::utils::Frames;
20
- use crate::utils::Interpolatable;
20
+ use crate::utils::{Interpolatable, Logger};
21
21
 
22
- pub struct Canvas {
22
+ pub struct Canvas<L: Logger> {
23
23
  shader: Arc<Mutex<Shader>>,
24
24
  camera_state: CameraState,
25
25
  frames: Option<Frames>,
26
26
  interpolate_enabled: bool,
27
+ logger: L,
27
28
  }
28
29
 
29
- impl Canvas {
30
- pub fn new<'a>(gl: Arc<eframe::glow::Context>, scene: Scene) -> Option<Self> {
30
+ impl<L: Logger> Canvas<L> {
31
+ pub fn new<'a>(gl: Arc<eframe::glow::Context>, scene: Scene, logger: L) -> Option<Self> {
31
32
  Some(Self {
32
33
  shader: Arc::new(Mutex::new(Shader::new(&gl, scene)?)),
33
34
  camera_state: CameraState::new(1.0),
34
35
  frames: None,
35
36
  interpolate_enabled: false,
37
+ logger,
36
38
  })
37
39
  }
38
40
 
39
- pub fn new_play<'a>(gl: Arc<eframe::glow::Context>, frames: Frames) -> Option<Self> {
41
+ pub fn new_play<'a>(gl: Arc<eframe::glow::Context>, frames: Frames, logger: L) -> Option<Self> {
42
+ if frames.frames.is_empty() {
43
+ logger.error("No frames provided");
44
+ return None;
45
+ }
40
46
  Some(Self {
41
47
  shader: Arc::new(Mutex::new(Shader::new(&gl, frames.frames[0].clone())?)),
42
48
  camera_state: CameraState::new(1.0),
43
49
  interpolate_enabled: frames.smooth,
44
50
  frames: Some(frames),
51
+ logger,
45
52
  })
46
53
  }
47
54
 
@@ -100,7 +107,11 @@ impl Canvas {
100
107
  } else {
101
108
  // 这里是原先的插值 / 非插值逻辑
102
109
  if self.interpolate_enabled {
103
- frames.frames[frame_a_index].interpolate(&frames.frames[frame_b_index], t)
110
+ frames.frames[frame_a_index].interpolate(
111
+ &frames.frames[frame_b_index],
112
+ t,
113
+ self.logger,
114
+ )
104
115
  } else {
105
116
  frames.frames[frame_a_index].clone()
106
117
  }
@@ -6,7 +6,9 @@ use crate::{
6
6
  parser::sdf::MoleculeData,
7
7
  scene::{InstanceGroups, SphereInstance},
8
8
  shapes::{sphere::Sphere, stick::Stick},
9
- utils::{Interaction, Interpolatable, IntoInstanceGroups, MeshData, VisualShape, VisualStyle},
9
+ utils::{
10
+ Interaction, Interpolatable, IntoInstanceGroups, Logger, MeshData, VisualShape, VisualStyle,
11
+ },
10
12
  };
11
13
 
12
14
  use std::{collections::HashMap, str::FromStr};
@@ -102,7 +104,29 @@ pub struct Molecules {
102
104
  }
103
105
 
104
106
  impl Interpolatable for Molecules {
105
- fn interpolate(&self, other: &Self, t: f32) -> Self {
107
+ fn interpolate(&self, other: &Self, t: f32, logger: impl Logger) -> Self {
108
+ // 检查原子数量是否匹配
109
+ if self.atoms.len() != other.atoms.len() {
110
+ logger.error(format!(
111
+ "Interpolation aborted: atom count differs (self: {}, other: {}). \
112
+ Smooth interpolation requires scenes with identical atom structures.",
113
+ self.atoms.len(),
114
+ other.atoms.len()
115
+ ));
116
+ panic!("Smooth interpolation requires matching atom structures.");
117
+ }
118
+
119
+ // 检查键数量是否匹配(可选,根据需要)
120
+ if self.bonds.len() != other.bonds.len() {
121
+ logger.error(format!(
122
+ "Interpolation aborted: bond topology differs (self: {}, other: {}). \
123
+ Smooth interpolation cannot proceed with different bonding graphs.",
124
+ self.bonds.len(),
125
+ other.bonds.len()
126
+ ));
127
+ panic!("Smooth interpolation requires matching bond topology.");
128
+ }
129
+
106
130
  // 原子坐标插值
107
131
  let atoms: Vec<[f32; 3]> = self
108
132
  .atoms
@@ -192,11 +216,9 @@ impl Molecules {
192
216
  }
193
217
  }
194
218
 
195
- /// Centers the molecule by translating all atoms so that the geometric center
196
- /// is at the origin (0.0, 0.0, 0.0).
197
- pub fn centered(mut self) -> Self {
219
+ pub fn get_center(&self) -> [f32; 3] {
198
220
  if self.atoms.is_empty() {
199
- return self;
221
+ return [0.0; 3];
200
222
  }
201
223
 
202
224
  // 1. 累加所有原子坐标
@@ -213,16 +235,27 @@ impl Molecules {
213
235
  center[1] /= count;
214
236
  center[2] /= count;
215
237
 
216
- // 3. 所有原子坐标减去中心
217
- for pos in &mut self.atoms {
218
- pos[0] -= center[0];
219
- pos[1] -= center[1];
220
- pos[2] -= center[2];
238
+ center
239
+ }
240
+
241
+ /// Centers the molecule by translating all atoms so that the geometric center
242
+ /// is at the origin (0.0, 0.0, 0.0).
243
+ pub fn centered(mut self) -> Self {
244
+ let center = self.get_center();
245
+ for atom in &mut self.atoms {
246
+ atom[0] -= center[0];
247
+ atom[1] -= center[1];
248
+ atom[2] -= center[2];
221
249
  }
222
250
 
223
251
  self
224
252
  }
225
253
 
254
+ pub fn reset_color(mut self) -> Self {
255
+ self.style_mut().color = None;
256
+ self
257
+ }
258
+
226
259
  pub fn to_mesh(&self, scale: f32) -> MeshData {
227
260
  return MeshData::default();
228
261
 
@@ -480,22 +513,26 @@ impl IntoInstanceGroups for Molecules {
480
513
  let d = 0.22;
481
514
 
482
515
  // 颜色和半径与原来一致
483
- let color_a = match self
484
- .atom_types
485
- .get(a as usize)
486
- .unwrap_or(&AtomType::Unknown)
487
- {
488
- AtomType::C => [0.75, 0.75, 0.75],
489
- other => other.color(),
490
- };
491
- let color_b = match self
492
- .atom_types
493
- .get(b as usize)
494
- .unwrap_or(&AtomType::Unknown)
495
- {
496
- AtomType::C => [0.75, 0.75, 0.75],
497
- other => other.color(),
498
- };
516
+ let color_a = self.style.color.unwrap_or(
517
+ match self
518
+ .atom_types
519
+ .get(a as usize)
520
+ .unwrap_or(&AtomType::Unknown)
521
+ {
522
+ AtomType::C => [0.75, 0.75, 0.75],
523
+ other => other.color(),
524
+ },
525
+ );
526
+ let color_b = self.style.color.unwrap_or(
527
+ match self
528
+ .atom_types
529
+ .get(b as usize)
530
+ .unwrap_or(&AtomType::Unknown)
531
+ {
532
+ AtomType::C => [0.75, 0.75, 0.75],
533
+ other => other.color(),
534
+ },
535
+ );
499
536
 
500
537
  // 根据键类型生成多个 stick
501
538
  let (num_sticks, radius) = match bond_type {
@@ -3,7 +3,9 @@ use serde::{Deserialize, Serialize};
3
3
  use crate::{
4
4
  Shape,
5
5
  scene::{Instance, InstanceGroups, Scene, SphereInstance},
6
- utils::{Interaction, Interpolatable, IntoInstanceGroups, MeshData, VisualShape, VisualStyle},
6
+ utils::{
7
+ Interaction, Interpolatable, IntoInstanceGroups, Logger, MeshData, VisualShape, VisualStyle,
8
+ },
7
9
  };
8
10
 
9
11
  use once_cell::sync::Lazy;
@@ -31,7 +33,7 @@ pub struct Sphere {
31
33
  }
32
34
 
33
35
  impl Interpolatable for Sphere {
34
- fn interpolate(&self, other: &Self, t: f32) -> Self {
36
+ fn interpolate(&self, other: &Self, t: f32, logger: impl Logger) -> Self {
35
37
  Self {
36
38
  center: [
37
39
  self.center[0] * (1.0 - t) + other.center[0] * t,
@@ -7,7 +7,7 @@ use crate::{
7
7
  Shape,
8
8
  scene::{Scene, StickInstance},
9
9
  shapes::sphere::MeshTemplate,
10
- utils::{Interaction, Interpolatable, MeshData, VisualShape, VisualStyle},
10
+ utils::{Interaction, Interpolatable, Logger, MeshData, VisualShape, VisualStyle},
11
11
  };
12
12
 
13
13
  static STICK_TEMPLATE_CACHE: Lazy<Mutex<HashMap<u32, MeshTemplate>>> =
@@ -25,7 +25,7 @@ pub struct Stick {
25
25
  }
26
26
 
27
27
  impl Interpolatable for Stick {
28
- fn interpolate(&self, other: &Self, t: f32) -> Self {
28
+ fn interpolate(&self, other: &Self, t: f32, logger: impl Logger) -> Self {
29
29
  Self {
30
30
  start: [
31
31
  self.start[0] * (1.0 - t) + other.start[0] * t,
@@ -6,6 +6,27 @@ use crate::{
6
6
  shapes::{Molecules, Sphere, Stick},
7
7
  };
8
8
 
9
+ pub trait Logger: Send + Sync + Copy {
10
+ fn log(&self, message: impl std::fmt::Display);
11
+ fn error(&self, message: impl std::fmt::Display);
12
+ fn warn(&self, message: impl std::fmt::Display);
13
+ }
14
+
15
+ #[derive(Clone, Copy)]
16
+ pub struct RustLogger;
17
+
18
+ impl Logger for RustLogger {
19
+ fn log(&self, message: impl std::fmt::Display) {
20
+ println!("[LOG] {}", message);
21
+ }
22
+ fn warn(&self, message: impl std::fmt::Display) {
23
+ eprintln!("[WARN] {}", message);
24
+ }
25
+ fn error(&self, message: impl std::fmt::Display) {
26
+ eprintln!("[ERROR] {}", message);
27
+ }
28
+ }
29
+
9
30
  #[derive(Serialize, Deserialize, Debug, Clone, Default, Copy)]
10
31
  pub struct VisualStyle {
11
32
  pub color: Option<[f32; 3]>,
@@ -25,7 +46,7 @@ pub struct Interaction {
25
46
 
26
47
  pub trait Interpolatable {
27
48
  /// t ∈ [0.0, 1.0],返回两个实例之间的插值
28
- fn interpolate(&self, other: &Self, t: f32) -> Self;
49
+ fn interpolate(&self, other: &Self, t: f32, logger: impl Logger) -> Self;
29
50
  }
30
51
 
31
52
  // -------------------- 图元结构体 --------------------------
@@ -53,11 +74,13 @@ pub struct InstanceData {
53
74
  }
54
75
 
55
76
  impl Interpolatable for Shape {
56
- fn interpolate(&self, other: &Self, t: f32) -> Self {
77
+ fn interpolate(&self, other: &Self, t: f32, logger: impl Logger) -> Self {
57
78
  match (self, other) {
58
- (Shape::Sphere(a), Shape::Sphere(b)) => Shape::Sphere(a.interpolate(b, t)),
59
- (Shape::Stick(a), Shape::Stick(b)) => Shape::Stick(a.interpolate(b, t)),
60
- (Shape::Molecules(a), Shape::Molecules(b)) => Shape::Molecules(a.interpolate(b, t)),
79
+ (Shape::Sphere(a), Shape::Sphere(b)) => Shape::Sphere(a.interpolate(b, t, logger)),
80
+ (Shape::Stick(a), Shape::Stick(b)) => Shape::Stick(a.interpolate(b, t, logger)),
81
+ (Shape::Molecules(a), Shape::Molecules(b)) => {
82
+ Shape::Molecules(a.interpolate(b, t, logger))
83
+ }
61
84
  _ => self.clone(), // 如果类型不匹配,可以选择不插值或做默认处理
62
85
  }
63
86
  }
@@ -75,7 +98,7 @@ impl IntoInstanceGroups for Shape {
75
98
  let m_groups = m.to_instance_group(scale);
76
99
  groups.merge(m_groups);
77
100
  }
78
- _ => {},
101
+ _ => {}
79
102
  }
80
103
  groups
81
104
  }
@@ -1,4 +1,4 @@
1
- from typing import Optional, Union, List
1
+ from typing import Optional, Union, List
2
2
 
3
3
  def parse_sdf(
4
4
  sdf: str,
@@ -201,7 +201,7 @@ class Viewer:
201
201
  - loops: Number of loops to repeat (-1 for infinite).
202
202
  - width: The viewport width in pixels.
203
203
  - height: The viewport height in pixels.
204
- - smooth: Whether to smooth the animation by
204
+ - smooth: Whether to smooth the animation by
205
205
  interpolating between frames.
206
206
 
207
207
  # Returns
@@ -306,10 +306,12 @@ class Molecules:
306
306
  """
307
307
 
308
308
  def __init__(self, molecule_data: "MoleculeData") -> None: ...
309
+ def get_center(self) -> List[float]: ...
309
310
  def centered(self) -> "Molecules": ...
310
311
  def color(self, color: List[float]) -> "Molecules": ...
311
312
  def color_rgba(self, color: List[float]) -> "Molecules": ...
312
313
  def opacity(self, opacity: float) -> "Molecules": ...
314
+ def reset_color(self) -> "Molecules": ...
313
315
 
314
316
  class MoleculeData:
315
317
  """
@@ -7,13 +7,13 @@ use pyo3::{PyRefMut, pyclass, pymethods};
7
7
 
8
8
  #[pyclass(name = "Sphere")]
9
9
  /// Sphere(center: [f32; 3], radius: f32)
10
- ///
10
+ ///
11
11
  /// A sphere in the scene.
12
- ///
12
+ ///
13
13
  /// # Arguments
14
14
  /// * `center` - The center of the sphere.
15
15
  /// * `radius` - The radius of the sphere.
16
- ///
16
+ ///
17
17
  /// # Examples
18
18
  /// ```
19
19
  /// scene = Scene()
@@ -21,7 +21,7 @@ use pyo3::{PyRefMut, pyclass, pymethods};
21
21
  /// scene.add_shape(sphere, id)
22
22
  /// viewer = Viewer.render(scene)
23
23
  /// ```
24
- ///
24
+ ///
25
25
  #[derive(Clone)]
26
26
  pub struct PySphere {
27
27
  pub inner: Sphere,
@@ -123,6 +123,10 @@ impl PyMolecules {
123
123
  }
124
124
  }
125
125
 
126
+ pub fn get_center(slf: PyRefMut<'_, Self>) -> [f32; 3] {
127
+ slf.inner.clone().get_center()
128
+ }
129
+
126
130
  pub fn centered(mut slf: PyRefMut<'_, Self>) -> PyRefMut<'_, Self> {
127
131
  slf.inner = slf.inner.clone().centered();
128
132
  slf
@@ -138,7 +142,6 @@ impl PyMolecules {
138
142
  slf
139
143
  }
140
144
 
141
-
142
145
  pub fn opacity(mut slf: PyRefMut<'_, Self>, opacity: f32) -> PyRefMut<'_, Self> {
143
146
  slf.inner = slf.inner.clone().opacity(opacity);
144
147
  slf
@@ -1,7 +1,6 @@
1
1
  [package]
2
2
  name = "cosmol_viewer_wasm"
3
- version.workspace = true
4
- edition = "2024"
3
+ edition.workspace = true
5
4
  publish = false
6
5
 
7
6
  [dependencies]
@@ -12,9 +11,9 @@ log = "0.4.28"
12
11
 
13
12
  uuid = { version = "1.18.1", features = ["v4"], optional = true }
14
13
  eframe = { workspace = true, optional = true }
15
- wasm-bindgen-futures = { version = "0.4.54", optional = true }
16
- web-sys = { version = "0.3.81", features = ["HtmlCanvasElement"], optional = true }
17
- wasm-bindgen = { version = "0.2.104", optional = true }
14
+ wasm-bindgen-futures = { version = "0.4.55", optional = true }
15
+ web-sys = { version = "0.3.82", features = ["HtmlCanvasElement", "console","Window"], optional = true }
16
+ wasm-bindgen = { version = "0.2.105", optional = true }
18
17
  gloo-timers = { version = "0.3.0", features = ["futures-core", "futures"], optional = true }
19
18
  pyo3 = { workspace = true, optional = true }
20
19
  base64 = { workspace = true, optional = true }
@@ -1,6 +1,6 @@
1
1
  use cosmol_viewer_core::App;
2
2
  use cosmol_viewer_core::scene::Scene;
3
- use cosmol_viewer_core::utils::Frames;
3
+ use cosmol_viewer_core::utils::{Frames, Logger};
4
4
  #[cfg(feature = "js_bridge")]
5
5
  use serde::Serialize;
6
6
  use std::sync::Arc;
@@ -292,11 +292,38 @@ impl WebRunner {
292
292
  }
293
293
  }
294
294
 
295
+ #[cfg(feature = "wasm")]
296
+ #[derive(Clone, Copy)]
297
+ pub struct WasmLogger;
298
+
299
+ #[cfg(feature = "wasm")]
300
+ impl Logger for WasmLogger {
301
+ fn log(&self, message: impl std::fmt::Display) {
302
+ web_sys::console::log_1(&JsValue::from_str(&message.to_string()));
303
+ }
304
+
305
+ fn warn(&self, message: impl std::fmt::Display) {
306
+ web_sys::console::warn_1(&JsValue::from_str(&message.to_string()));
307
+ }
308
+
309
+ fn error(&self, message: impl std::fmt::Display) {
310
+ let msg = message.to_string();
311
+
312
+ // Send to console
313
+ web_sys::console::error_1(&JsValue::from_str(&msg));
314
+
315
+ // Show browser alert
316
+ if let Some(window) = web_sys::window() {
317
+ window.alert_with_message(&msg).ok();
318
+ }
319
+ }
320
+ }
321
+
295
322
  #[cfg(feature = "wasm")]
296
323
  #[wasm_bindgen]
297
324
  pub struct WebHandle {
298
325
  runner: WebRunner,
299
- app: Arc<Mutex<Option<App>>>,
326
+ app: Arc<Mutex<Option<App<WasmLogger>>>>,
300
327
  }
301
328
 
302
329
  #[cfg(feature = "wasm")]
@@ -337,7 +364,7 @@ impl WebHandle {
337
364
  use cosmol_viewer_core::AppWrapper;
338
365
 
339
366
  let mut guard = app.lock().unwrap();
340
- *guard = Some(App::new(cc, scene));
367
+ *guard = Some(App::new(cc, scene, WasmLogger));
341
368
  Ok(Box::new(AppWrapper(app.clone())))
342
369
  }),
343
370
  )
@@ -384,7 +411,7 @@ impl WebHandle {
384
411
  use cosmol_viewer_core::AppWrapper;
385
412
 
386
413
  let mut guard = app.lock().unwrap();
387
- *guard = Some(App::new_play(cc, frames));
414
+ *guard = Some(App::new_play(cc, frames, WasmLogger));
388
415
  Ok(Box::new(AppWrapper(app.clone())))
389
416
  }),
390
417
  )
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cosmol-viewer"
3
- version = "0.1.4.dev3"
3
+ version = "0.1.5.dev1"
4
4
  description = "Molecular visualization tools"
5
5
  authors = [{name = "95028", email = "wjt@cosmol.org"}]
6
6
  readme = "README.md"