cosmol-viewer 0.1.1.dev4__tar.gz → 0.1.1.dev5__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 (28) hide show
  1. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/Cargo.lock +7 -5
  2. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/Cargo.toml +1 -1
  3. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/PKG-INFO +4 -5
  4. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/Cargo.toml +2 -0
  5. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/shader/canvas.rs +20 -25
  6. cosmol_viewer-0.1.1.dev5/crates/core/src/shader/fragment.glsl +34 -0
  7. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/shapes/molecules.rs +9 -9
  8. cosmol_viewer-0.1.1.dev5/crates/core/src/shapes/sphere.rs +246 -0
  9. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/shapes/stick.rs +7 -8
  10. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/python/README.md +2 -4
  11. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/python/build.rs +1 -1
  12. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/python/src/lib.rs +2 -2
  13. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/python/src/shapes.rs +1 -1
  14. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/pyproject.toml +4 -2
  15. cosmol_viewer-0.1.1.dev4/crates/core/src/shader/fragment.glsl +0 -28
  16. cosmol_viewer-0.1.1.dev4/crates/core/src/shapes/sphere.rs +0 -136
  17. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/lib.rs +0 -0
  18. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/parser/mod.rs +0 -0
  19. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/parser/sdf.rs +0 -0
  20. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/scene.rs +0 -0
  21. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/shader/bg_fragment.glsl +0 -0
  22. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/shader/bg_vertex.glsl +0 -0
  23. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/shader/mod.rs +0 -0
  24. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/shader/vertex.glsl +0 -0
  25. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/shapes/mod.rs +0 -0
  26. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/utils.rs +0 -0
  27. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/python/Cargo.toml +0 -0
  28. {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/python/src/parser.rs +0 -0
@@ -699,7 +699,7 @@ dependencies = [
699
699
 
700
700
  [[package]]
701
701
  name = "cosmol_viewer"
702
- version = "0.1.1-nightly.4"
702
+ version = "0.1.1-nightly.5"
703
703
  dependencies = [
704
704
  "bytemuck",
705
705
  "cosmol_viewer_core",
@@ -717,21 +717,23 @@ dependencies = [
717
717
 
718
718
  [[package]]
719
719
  name = "cosmol_viewer_core"
720
- version = "0.1.1-nightly.4"
720
+ version = "0.1.1-nightly.5"
721
721
  dependencies = [
722
722
  "bytemuck",
723
723
  "eframe",
724
724
  "egui_extras",
725
725
  "glam",
726
+ "once_cell",
726
727
  "serde",
727
728
  "serde_json",
729
+ "serde_repr",
728
730
  "wasm-bindgen-futures",
729
731
  "web-sys",
730
732
  ]
731
733
 
732
734
  [[package]]
733
735
  name = "cosmol_viewer_gui"
734
- version = "0.1.1-nightly.4"
736
+ version = "0.1.1-nightly.5"
735
737
  dependencies = [
736
738
  "bytemuck",
737
739
  "cosmol_viewer_core",
@@ -765,7 +767,7 @@ dependencies = [
765
767
 
766
768
  [[package]]
767
769
  name = "cosmol_viewer_wasm"
768
- version = "0.1.1-nightly.4"
770
+ version = "0.1.1-nightly.5"
769
771
  dependencies = [
770
772
  "cosmol_viewer_core",
771
773
  "eframe",
@@ -3160,7 +3162,7 @@ dependencies = [
3160
3162
 
3161
3163
  [[package]]
3162
3164
  name = "test"
3163
- version = "0.1.1-nightly.4"
3165
+ version = "0.1.1-nightly.5"
3164
3166
  dependencies = [
3165
3167
  "cosmol_viewer",
3166
3168
  "cosmol_viewer_core",
@@ -1,6 +1,6 @@
1
1
  [workspace.package]
2
2
  edition = "2024"
3
- version = "0.1.1-nightly.4"
3
+ version = "0.1.1-nightly.5"
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"
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cosmol-viewer
3
- Version: 0.1.1.dev4
3
+ Version: 0.1.1.dev5
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
7
+ Project-URL: Repository, https://github.com/COSMol-repl/COSMol-viewer
7
8
 
8
9
  # COSMol-viewer
9
10
 
@@ -21,14 +22,12 @@ A high-performance molecular visualization library built with Rust and WebGPU, d
21
22
 
22
23
  # Installation
23
24
 
24
- install with
25
25
  ```sh
26
- pip install cosmol-viewer==0.1.1.dev4
26
+ pip install cosmol-viewer==0.1.1.dev5
27
27
  ```
28
28
 
29
29
  # Usage
30
30
 
31
- python:
32
31
  ```python
33
32
  from cosmol_viewer import Scene, Viewer, parse_sdf, Molecules
34
33
 
@@ -37,7 +36,7 @@ with open("molecule.sdf", "r") as f:
37
36
  sdf = f.read()
38
37
  mol = Molecules(parse_sdf(sdf)).centered()
39
38
 
40
- scene = Scene.create_viewer()
39
+ scene = Scene()
41
40
  scene.scale(0.1)
42
41
  scene.add_shape(mol, "mol")
43
42
 
@@ -12,4 +12,6 @@ egui_extras.workspace = true
12
12
  serde = { version = "1.0.219" , features = ["derive"] }
13
13
  bytemuck = "1.23.1"
14
14
  web-sys = "0.3.77"
15
+ serde_repr = "0.1"
15
16
  wasm-bindgen-futures = "0.4.50"
17
+ once_cell = "1.21.3"
@@ -308,14 +308,6 @@ impl Shader {
308
308
  self.dirty = true;
309
309
  }
310
310
 
311
- fn destroy(&self, gl: &glow::Context) {
312
- // use glow::HasContext as _;
313
- // unsafe {
314
- // gl.delete_program(self.program);
315
- // gl.delete_vertex_array(self.vertex_array);
316
- // }
317
- }
318
-
319
311
  fn paint(&mut self, gl: &glow::Context, aspect_ratio: f32, camera_state: CameraState) {
320
312
  use glow::HasContext as _;
321
313
 
@@ -333,7 +325,7 @@ impl Shader {
333
325
  let light = Light {
334
326
  position: [2.0, -3.0, 2.0],
335
327
  color: [1.0, 0.9, 0.9],
336
- intensity: 0.7,
328
+ intensity: 1.0,
337
329
  };
338
330
 
339
331
  unsafe {
@@ -359,7 +351,7 @@ impl Shader {
359
351
 
360
352
  // === 绘制场景 ===
361
353
  gl.enable(glow::DEPTH_TEST);
362
- // gl.depth_mask(false); // ✅ 关键:恢复写入深度缓冲区
354
+ gl.depth_mask(true); // ✅ 关键:恢复写入深度缓冲区
363
355
 
364
356
  // gl.enable(glow::BLEND);
365
357
  // gl.blend_func_separate(
@@ -434,7 +426,7 @@ impl Shader {
434
426
  gl.uniform_3_f32_slice(
435
427
  gl.get_uniform_location(self.program, "u_light_color")
436
428
  .as_ref(),
437
- (light.color).as_ref(),
429
+ (light.color.map(|x| x * light.intensity)).as_ref(),
438
430
  );
439
431
 
440
432
  gl.uniform_1_f32(
@@ -494,28 +486,31 @@ impl CameraState {
494
486
 
495
487
  pub fn rotate_camera(mut camera_state: CameraState, drag_motion: Vec2) -> CameraState {
496
488
  let sensitivity = 0.005;
497
- let yaw = drag_motion.x * sensitivity;
498
- let pitch = drag_motion.y * sensitivity;
489
+ let yaw = -drag_motion.x * sensitivity; // 水平拖动 → 绕 up 旋转
490
+ let pitch = -drag_motion.y * sensitivity; // 垂直拖动 → 绕 right 旋转
499
491
 
500
- // 计算右向量
501
- let right = camera_state.direction.cross(camera_state.up).normalize();
492
+ // 当前方向
493
+ let dir = camera_state.direction;
502
494
 
503
- // 计算绕 Y 的旋转(水平拖动)
504
- let yaw_quat = Quat::from_axis_angle(Vec3::Y, yaw);
495
+ // right = 当前方向 × 当前 up
496
+ let right = dir.cross(camera_state.up).normalize();
505
497
 
506
- // 计算绕右轴的旋转(垂直拖动)
507
- let pitch_quat = Quat::from_axis_angle(right, -pitch);
498
+ // 1. pitch:绕当前 right 轴旋转(垂直)
499
+ let pitch_quat = Quat::from_axis_angle(right, pitch);
500
+ let rotated_dir = pitch_quat * dir;
501
+ let rotated_up = pitch_quat * camera_state.up;
508
502
 
509
- // 最终方向 = 原方向应用 pitch,然后再应用 yaw
510
- let new_direction = yaw_quat * pitch_quat * camera_state.direction;
511
- camera_state.direction = new_direction.normalize();
503
+ // 2. yaw:绕当前“视角 up”旋转(水平)
504
+ let yaw_quat = Quat::from_axis_angle(rotated_up, yaw);
505
+ let final_dir = yaw_quat * rotated_dir;
512
506
 
513
- // 更新 up 向量(可选,视需求而定)
514
- camera_state.up = yaw_quat * pitch_quat * camera_state.up;
507
+ camera_state.direction = final_dir.normalize();
508
+ camera_state.up = (yaw_quat * rotated_up).normalize();
515
509
 
516
- return camera_state;
510
+ camera_state
517
511
  }
518
512
 
513
+
519
514
  #[repr(C)]
520
515
  #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, Debug, Serialize, Deserialize)]
521
516
  pub struct Vertex3d {
@@ -0,0 +1,34 @@
1
+ precision mediump float;
2
+
3
+ uniform vec3 u_light_pos;
4
+ uniform vec3 u_light_color;
5
+ uniform vec3 u_view_pos;
6
+ uniform float u_light_intensity;
7
+
8
+ in vec3 v_normal;
9
+ in vec4 v_color;
10
+ in vec3 v_frag_pos;
11
+
12
+ out vec4 FragColor;
13
+
14
+ void main() {
15
+ vec3 normal = normalize(v_normal);
16
+ vec3 light_dir = normalize(u_light_pos - v_frag_pos);
17
+ vec3 view_dir = normalize(u_view_pos - v_frag_pos);
18
+
19
+ // === 环境光 ===
20
+ vec3 ambient = 0.7 * u_light_color;
21
+
22
+ // === 漫反射 ===
23
+ float diff = max(dot(normal, light_dir), 0.0);
24
+ vec3 diffuse = 0.5 * diff * u_light_color;
25
+
26
+ // === 高光 ===
27
+ vec3 halfway_dir = normalize(light_dir + view_dir); // Blinn 模型
28
+ float spec = pow(max(dot(normal, halfway_dir), 0.0), 64.0); // shininess 可调
29
+ vec3 specular = 0.3 * spec * u_light_color; // 强度可调
30
+
31
+ // === 最终颜色 ===
32
+ vec3 lighting = (ambient + diffuse) * v_color.rgb + specular;
33
+ FragColor = vec4(lighting * u_light_intensity, v_color.a);
34
+ }
@@ -1,10 +1,9 @@
1
- use glam::Mat3;
2
1
  use serde::{Deserialize, Serialize};
2
+ use serde_repr::{Deserialize_repr, Serialize_repr};
3
3
 
4
4
  use crate::{
5
5
  Shape,
6
6
  parser::sdf::MoleculeData,
7
- scene::Scene,
8
7
  shapes::{sphere::Sphere, stick::Stick},
9
8
  utils::{Interaction, MeshData, VisualShape, VisualStyle},
10
9
  };
@@ -50,7 +49,7 @@ impl AtomType {
50
49
  pub fn color(&self) -> [f32; 3] {
51
50
  match self {
52
51
  AtomType::H => [1.0, 1.0, 1.0], // 白色
53
- AtomType::C => [0.2, 0.2, 0.2], // 深灰
52
+ AtomType::C => [0.3, 0.3, 0.3], // 深灰
54
53
  AtomType::N => [0.0, 0.0, 1.0], // 蓝色
55
54
  AtomType::O => [1.0, 0.0, 0.0], // 红色
56
55
  AtomType::F => [0.0, 0.8, 0.0], // 绿
@@ -80,12 +79,13 @@ impl AtomType {
80
79
  }
81
80
  }
82
81
 
83
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
82
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)]
83
+ #[repr(u8)]
84
84
  pub enum BondType {
85
- SINGLE,
86
- DOUBLE,
87
- TRIPLE,
88
- AROMATIC,
85
+ SINGLE = 1,
86
+ DOUBLE = 2,
87
+ TRIPLE = 3,
88
+ AROMATIC = 0,
89
89
  }
90
90
 
91
91
  #[derive(Serialize, Deserialize, Debug, Clone)]
@@ -241,7 +241,7 @@ impl Molecules {
241
241
 
242
242
  let mut stick = Stick::new(pos_a, pos_b, 0.1); // or radius by bond type
243
243
  stick.interaction = self.interaction;
244
- stick = stick.color([0.5, 0.5, 0.5]);
244
+ stick = stick.color([0.7, 0.7, 0.7]);
245
245
 
246
246
  let mesh = stick.to_mesh(1.0);
247
247
 
@@ -0,0 +1,246 @@
1
+ use serde::{Deserialize, Serialize};
2
+
3
+ use crate::{scene::Scene, utils::{Interaction, MeshData, VisualShape, VisualStyle}, Shape};
4
+
5
+ use once_cell::sync::Lazy;
6
+ use std::collections::HashMap;
7
+ use std::sync::Mutex;
8
+
9
+ #[derive(Clone)]
10
+ struct SphereTemplate {
11
+ vertices: Vec<[f32; 3]>,
12
+ normals: Vec<[f32; 3]>,
13
+ indices: Vec<u32>,
14
+ }
15
+
16
+ static SPHERE_TEMPLATE_CACHE: Lazy<Mutex<HashMap<u32, SphereTemplate>>> = Lazy::new(|| {
17
+ Mutex::new(HashMap::new())
18
+ });
19
+
20
+ fn get_or_generate_template(quality: u32) -> SphereTemplate {
21
+ let mut cache = SPHERE_TEMPLATE_CACHE.lock().unwrap();
22
+
23
+ if let Some(template) = cache.get(&quality) {
24
+ return template.clone(); // 直接返回已有的
25
+ }
26
+
27
+ let lat_segments = 10 * quality;
28
+ let lon_segments = 20 * quality;
29
+
30
+ let mut vertices = Vec::new();
31
+ let mut normals = Vec::new();
32
+ let mut indices = Vec::new();
33
+
34
+ for i in 0..=lat_segments {
35
+ let theta = std::f32::consts::PI * (i as f32) / (lat_segments as f32);
36
+ let sin_theta = theta.sin();
37
+ let cos_theta = theta.cos();
38
+
39
+ for j in 0..=lon_segments {
40
+ let phi = 2.0 * std::f32::consts::PI * (j as f32) / (lon_segments as f32);
41
+ let sin_phi = phi.sin();
42
+ let cos_phi = phi.cos();
43
+
44
+ let nx = sin_theta * cos_phi;
45
+ let ny = cos_theta;
46
+ let nz = sin_theta * sin_phi;
47
+
48
+ vertices.push([nx, ny, nz]); // 单位球
49
+ normals.push([nx, ny, nz]);
50
+ }
51
+ }
52
+
53
+ for i in 0..lat_segments {
54
+ for j in 0..lon_segments {
55
+ let first = i * (lon_segments + 1) + j;
56
+ let second = first + lon_segments + 1;
57
+
58
+ indices.push(first);
59
+ indices.push(first + 1);
60
+ indices.push(second);
61
+
62
+ indices.push(second);
63
+ indices.push(first + 1);
64
+ indices.push(second + 1);
65
+ }
66
+ }
67
+
68
+ let template = SphereTemplate {
69
+ vertices,
70
+ normals,
71
+ indices,
72
+ };
73
+
74
+ cache.insert(quality, template.clone());
75
+
76
+ template
77
+ }
78
+
79
+
80
+ #[derive(Serialize, Deserialize, Debug, Clone, Copy)]
81
+ pub struct Sphere {
82
+ pub center: [f32; 3],
83
+ pub radius: f32,
84
+ pub quality: u32,
85
+
86
+ pub style: VisualStyle,
87
+ pub interaction: Interaction,
88
+ }
89
+
90
+ impl Into<Shape> for Sphere {
91
+ fn into(self) -> Shape {
92
+ Shape::Sphere(self)
93
+ }
94
+ }
95
+
96
+ impl Sphere {
97
+ pub fn new(center: [f32; 3], radius: f32) -> Self {
98
+ Self {
99
+ center,
100
+ radius,
101
+ quality: 2,
102
+ style: VisualStyle {
103
+ opacity: 1.0,
104
+ visible: true,
105
+ ..Default::default()
106
+ },
107
+ interaction: Default::default(),
108
+ }
109
+ }
110
+
111
+ pub fn center(mut self, center: [f32; 3])-> Self {
112
+ self.center = center;
113
+ self
114
+ }
115
+
116
+ pub fn set_radius(mut self, radius: f32)-> Self {
117
+ self.radius = radius;
118
+ self
119
+ }
120
+
121
+ pub fn clickable(mut self, val: bool) -> Self {
122
+ self.interaction.clickable = val;
123
+ self
124
+ }
125
+
126
+ // pub fn to_mesh(&self, scale: f32) -> MeshData {
127
+ // let mut vertices = Vec::new();
128
+ // let mut normals = Vec::new();
129
+ // let mut indices = Vec::new();
130
+ // let mut colors = Vec::new();
131
+
132
+ // let lat_segments = 10 * self.quality;
133
+ // let lon_segments = 20 * self.quality;
134
+
135
+ // let r = self.radius;
136
+ // let [cx, cy, cz] = self.center;
137
+
138
+ // // 基础颜色(带透明度)
139
+ // let base_color = self.style.color.unwrap_or([1.0, 1.0, 1.0]);
140
+ // let alpha = self.style.opacity.clamp(0.0, 1.0);
141
+ // let color_rgba = [base_color[0], base_color[1], base_color[2], alpha];
142
+
143
+ // for i in 0..=lat_segments {
144
+ // let theta = std::f32::consts::PI * (i as f32) / (lat_segments as f32);
145
+ // let sin_theta = theta.sin();
146
+ // let cos_theta = theta.cos();
147
+
148
+ // for j in 0..=lon_segments {
149
+ // let phi = 2.0 * std::f32::consts::PI * (j as f32) / (lon_segments as f32);
150
+ // let sin_phi = phi.sin();
151
+ // let cos_phi = phi.cos();
152
+
153
+ // let nx = sin_theta * cos_phi;
154
+ // let ny = cos_theta;
155
+ // let nz = sin_theta * sin_phi;
156
+
157
+ // let x = cx + r * nx;
158
+ // let y = cy + r * ny;
159
+ // let z = cz + r * nz;
160
+
161
+ // vertices.push([x, y, z].map(|x| x * scale));
162
+ // normals.push([nx, ny, nz].map(|x| x * scale));
163
+ // colors.push(color_rgba); // 每个顶点同样颜色
164
+ // }
165
+ // }
166
+
167
+ // for i in 0..lat_segments {
168
+ // for j in 0..lon_segments {
169
+ // let first = i * (lon_segments + 1) + j;
170
+ // let second = first + lon_segments + 1;
171
+
172
+ // indices.push(first);
173
+ // indices.push(first + 1);
174
+ // indices.push(second);
175
+
176
+ // indices.push(second);
177
+ // indices.push(first + 1);
178
+ // indices.push(second + 1);
179
+ // }
180
+ // }
181
+
182
+ // MeshData {
183
+ // vertices,
184
+ // normals,
185
+ // indices,
186
+ // colors: Some(colors),
187
+ // transform: None,
188
+ // is_wireframe: self.style.wireframe,
189
+ // }
190
+ // }
191
+ pub fn to_mesh(&self, scale: f32) -> MeshData {
192
+ let template = get_or_generate_template(self.quality);
193
+
194
+ let [cx, cy, cz] = self.center;
195
+ let r = self.radius;
196
+
197
+ let transformed_vertices: Vec<[f32; 3]> = template.vertices.iter()
198
+ .map(|v| [
199
+ (v[0] * r + cx) * scale,
200
+ (v[1] * r + cy) * scale,
201
+ (v[2] * r + cz) * scale,
202
+ ])
203
+ .collect();
204
+
205
+ let transformed_normals: Vec<[f32; 3]> = template.normals
206
+ .iter()
207
+ .map(|n| n.map(|x| x * scale)) // 你可以不乘 scale,如果只用于方向
208
+ .collect();
209
+
210
+ let base_color = self.style.color.unwrap_or([1.0, 1.0, 1.0]);
211
+ let alpha = self.style.opacity.clamp(0.0, 1.0);
212
+ let color = [base_color[0], base_color[1], base_color[2], alpha];
213
+
214
+ let colors = vec![color; transformed_vertices.len()];
215
+
216
+ MeshData {
217
+ vertices: transformed_vertices,
218
+ normals: transformed_normals,
219
+ indices: template.indices.clone(),
220
+ colors: Some(colors),
221
+ transform: None,
222
+ is_wireframe: self.style.wireframe,
223
+ }
224
+
225
+ }
226
+ }
227
+
228
+ impl VisualShape for Sphere {
229
+ fn style_mut(&mut self) -> &mut VisualStyle {
230
+ &mut self.style
231
+ }
232
+ }
233
+
234
+ pub trait UpdateSphere {
235
+ fn update_sphere(&mut self, id: &str, f: impl FnOnce(&mut Sphere));
236
+ }
237
+
238
+ impl UpdateSphere for Scene {
239
+ fn update_sphere(&mut self, id: &str, f: impl FnOnce(&mut Sphere)) {
240
+ if let Some(Shape::Sphere(sphere)) = self.named_shapes.get_mut(id) {
241
+ f(sphere);
242
+ } else {
243
+ panic!("Sphere with ID '{}' not found or is not a Sphere", id);
244
+ }
245
+ }
246
+ }
@@ -1,4 +1,3 @@
1
- use glam::Mat3;
2
1
  use serde::{Deserialize, Serialize};
3
2
 
4
3
  use crate::{
@@ -40,17 +39,17 @@ impl Stick {
40
39
  }
41
40
  }
42
41
 
43
- pub fn radius(mut self, radius: f32) -> Self{
42
+ pub fn radius(mut self, radius: f32) -> Self {
44
43
  self.radius = radius;
45
44
  self
46
45
  }
47
46
 
48
- pub fn start(mut self, start: [f32; 3]) -> Self{
47
+ pub fn start(mut self, start: [f32; 3]) -> Self {
49
48
  self.start = start;
50
49
  self
51
50
  }
52
51
 
53
- pub fn end(mut self, end: [f32; 3]) -> Self{
52
+ pub fn end(mut self, end: [f32; 3]) -> Self {
54
53
  self.end = end;
55
54
  self
56
55
  }
@@ -96,13 +95,13 @@ impl Stick {
96
95
 
97
96
  for i in 0..segments {
98
97
  let idx = i * 2;
99
- indices.push(idx);
100
- indices.push(idx + 1);
101
98
  indices.push(idx + 2);
102
-
103
99
  indices.push(idx + 1);
104
- indices.push(idx + 3);
100
+ indices.push(idx);
101
+
105
102
  indices.push(idx + 2);
103
+ indices.push(idx + 3);
104
+ indices.push(idx + 1);
106
105
  }
107
106
 
108
107
  // 对齐旋转:Z -> axis
@@ -14,14 +14,12 @@ A high-performance molecular visualization library built with Rust and WebGPU, d
14
14
 
15
15
  # Installation
16
16
 
17
- install with
18
17
  ```sh
19
- pip install cosmol-viewer==0.1.1.dev4
18
+ pip install cosmol-viewer==0.1.1.dev5
20
19
  ```
21
20
 
22
21
  # Usage
23
22
 
24
- python:
25
23
  ```python
26
24
  from cosmol_viewer import Scene, Viewer, parse_sdf, Molecules
27
25
 
@@ -30,7 +28,7 @@ with open("molecule.sdf", "r") as f:
30
28
  sdf = f.read()
31
29
  mol = Molecules(parse_sdf(sdf)).centered()
32
30
 
33
- scene = Scene.create_viewer()
31
+ scene = Scene()
34
32
  scene.scale(0.1)
35
33
  scene.add_shape(mol, "mol")
36
34
 
@@ -1,4 +1,4 @@
1
- use std::{env, process::Command};
1
+ // use std::{env, process::Command};
2
2
 
3
3
  fn main() {
4
4
  // let is_ci = env::var("GITHUB_ACTIONS").is_ok();
@@ -18,8 +18,8 @@ pub struct Scene {
18
18
 
19
19
  #[pymethods]
20
20
  impl Scene {
21
- #[staticmethod]
22
- pub fn create_viewer() -> Self {
21
+ #[new]
22
+ pub fn new() -> Self {
23
23
  Self {
24
24
  inner: _Scene::new(),
25
25
  }
@@ -1,6 +1,6 @@
1
1
  use cosmol_viewer_core::{
2
2
  shapes::{molecules::Molecules, sphere::Sphere, stick::Stick},
3
- utils::{VisualShape, VisualStyle},
3
+ utils::VisualShape,
4
4
  };
5
5
  use pyo3::{PyRefMut, pyclass, pymethods};
6
6
 
@@ -1,9 +1,8 @@
1
1
  [project]
2
2
  name = "cosmol-viewer"
3
- version = "0.1.1.dev4"
3
+ version = "0.1.1.dev5"
4
4
  description = "Molecular visualization tools"
5
5
  authors = [{name = "95028", email = "wjt@cosmol.org"}]
6
- repository = "https://github.com/COSMol-repl/COSMol-viewer"
7
6
 
8
7
  [build-system]
9
8
  requires = ["maturin>=1.0,<2.0"]
@@ -12,3 +11,6 @@ build-backend = "maturin"
12
11
  [tool.maturin]
13
12
  name = "cosmol_viewer"
14
13
  manifest-path = "crates/python/Cargo.toml"
14
+
15
+ [project.urls]
16
+ Repository = "https://github.com/COSMol-repl/COSMol-viewer"
@@ -1,28 +0,0 @@
1
- precision mediump float;
2
-
3
- uniform vec3 u_light_pos;
4
- uniform vec3 u_light_color;
5
- uniform vec3 u_view_pos;
6
- uniform float u_light_intensity;
7
-
8
- in vec3 v_normal;
9
- in vec4 v_color;
10
- in vec3 v_frag_pos;
11
-
12
- out vec4 FragColor;
13
- void main() {
14
- vec3 normal = normalize(v_normal); // 归一化法线
15
- vec3 light_dir = normalize(u_light_pos - v_frag_pos); // 计算从片元到光源的方向
16
- vec3 view_dir = normalize(u_view_pos - v_frag_pos); // 计算从片元到相机的方向
17
- vec3 halfway_dir = normalize(light_dir + view_dir); // halfway 向量用于 Blinn-Phong 高光
18
-
19
- float diff = max(dot(normal, light_dir), 0.0); // 漫反射项(Lambert)
20
- float spec = pow(max(dot(normal, halfway_dir), 0.0), 32.0); // 高光项,32 是 shininess 参数
21
-
22
- vec3 ambient = 0.3 * u_light_color; // 环境光,固定系数0.2
23
- vec3 diffuse = diff * u_light_color; // 漫反射
24
-
25
- vec3 light = (ambient + diffuse) * u_light_intensity; // 叠加光照并乘以强度
26
-
27
- FragColor = vec4(v_color.rgb * light, v_color.a);
28
- }
@@ -1,136 +0,0 @@
1
- use serde::{Deserialize, Serialize};
2
-
3
- use crate::{scene::Scene, utils::{Interaction, MeshData, VisualShape, VisualStyle}, Shape};
4
-
5
- #[derive(Serialize, Deserialize, Debug, Clone, Copy)]
6
- pub struct Sphere {
7
- pub center: [f32; 3],
8
- pub radius: f32,
9
- pub quality: u32,
10
-
11
- pub style: VisualStyle,
12
- pub interaction: Interaction,
13
- }
14
-
15
- impl Into<Shape> for Sphere {
16
- fn into(self) -> Shape {
17
- Shape::Sphere(self)
18
- }
19
- }
20
-
21
- impl Sphere {
22
- pub fn new(center: [f32; 3], radius: f32) -> Self {
23
- Self {
24
- center,
25
- radius,
26
- quality: 2,
27
- style: VisualStyle {
28
- opacity: 1.0,
29
- visible: true,
30
- ..Default::default()
31
- },
32
- interaction: Default::default(),
33
- }
34
- }
35
-
36
- pub fn center(mut self, center: [f32; 3])-> Self {
37
- self.center = center;
38
- self
39
- }
40
-
41
- pub fn set_radius(mut self, radius: f32)-> Self {
42
- self.radius = radius;
43
- self
44
- }
45
-
46
- pub fn clickable(mut self, val: bool) -> Self {
47
- self.interaction.clickable = val;
48
- self
49
- }
50
-
51
- pub fn to_mesh(&self, scale: f32) -> MeshData {
52
- let mut vertices = Vec::new();
53
- let mut normals = Vec::new();
54
- let mut indices = Vec::new();
55
- let mut colors = Vec::new();
56
-
57
- let lat_segments = 10 * self.quality;
58
- let lon_segments = 20 * self.quality;
59
-
60
- let r = self.radius;
61
- let [cx, cy, cz] = self.center;
62
-
63
- // 基础颜色(带透明度)
64
- let base_color = self.style.color.unwrap_or([1.0, 1.0, 1.0]);
65
- let alpha = self.style.opacity.clamp(0.0, 1.0);
66
- let color_rgba = [base_color[0], base_color[1], base_color[2], alpha];
67
-
68
- for i in 0..=lat_segments {
69
- let theta = std::f32::consts::PI * (i as f32) / (lat_segments as f32);
70
- let sin_theta = theta.sin();
71
- let cos_theta = theta.cos();
72
-
73
- for j in 0..=lon_segments {
74
- let phi = 2.0 * std::f32::consts::PI * (j as f32) / (lon_segments as f32);
75
- let sin_phi = phi.sin();
76
- let cos_phi = phi.cos();
77
-
78
- let nx = sin_theta * cos_phi;
79
- let ny = cos_theta;
80
- let nz = sin_theta * sin_phi;
81
-
82
- let x = cx + r * nx;
83
- let y = cy + r * ny;
84
- let z = cz + r * nz;
85
-
86
- vertices.push([x, y, z].map(|x| x * scale));
87
- normals.push([nx, ny, nz].map(|x| x * scale));
88
- colors.push(color_rgba); // 每个顶点同样颜色
89
- }
90
- }
91
-
92
- for i in 0..lat_segments {
93
- for j in 0..lon_segments {
94
- let first = i * (lon_segments + 1) + j;
95
- let second = first + lon_segments + 1;
96
-
97
- indices.push(first);
98
- indices.push(second);
99
- indices.push(first + 1);
100
-
101
- indices.push(second);
102
- indices.push(second + 1);
103
- indices.push(first + 1);
104
- }
105
- }
106
-
107
- MeshData {
108
- vertices,
109
- normals,
110
- indices,
111
- colors: Some(colors),
112
- transform: None,
113
- is_wireframe: self.style.wireframe,
114
- }
115
- }
116
- }
117
-
118
- impl VisualShape for Sphere {
119
- fn style_mut(&mut self) -> &mut VisualStyle {
120
- &mut self.style
121
- }
122
- }
123
-
124
- pub trait UpdateSphere {
125
- fn update_sphere(&mut self, id: &str, f: impl FnOnce(&mut Sphere));
126
- }
127
-
128
- impl UpdateSphere for Scene {
129
- fn update_sphere(&mut self, id: &str, f: impl FnOnce(&mut Sphere)) {
130
- if let Some(Shape::Sphere(sphere)) = self.named_shapes.get_mut(id) {
131
- f(sphere);
132
- } else {
133
- panic!("Sphere with ID '{}' not found or is not a Sphere", id);
134
- }
135
- }
136
- }