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.
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/Cargo.lock +7 -5
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/Cargo.toml +1 -1
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/PKG-INFO +4 -5
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/Cargo.toml +2 -0
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/shader/canvas.rs +20 -25
- cosmol_viewer-0.1.1.dev5/crates/core/src/shader/fragment.glsl +34 -0
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/shapes/molecules.rs +9 -9
- cosmol_viewer-0.1.1.dev5/crates/core/src/shapes/sphere.rs +246 -0
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/shapes/stick.rs +7 -8
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/python/README.md +2 -4
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/python/build.rs +1 -1
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/python/src/lib.rs +2 -2
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/python/src/shapes.rs +1 -1
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/pyproject.toml +4 -2
- cosmol_viewer-0.1.1.dev4/crates/core/src/shader/fragment.glsl +0 -28
- cosmol_viewer-0.1.1.dev4/crates/core/src/shapes/sphere.rs +0 -136
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/lib.rs +0 -0
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/parser/mod.rs +0 -0
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/parser/sdf.rs +0 -0
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/scene.rs +0 -0
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/shader/bg_fragment.glsl +0 -0
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/shader/bg_vertex.glsl +0 -0
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/shader/mod.rs +0 -0
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/shader/vertex.glsl +0 -0
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/shapes/mod.rs +0 -0
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/utils.rs +0 -0
- {cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/python/Cargo.toml +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
3165
|
+
version = "0.1.1-nightly.5"
|
|
3164
3166
|
dependencies = [
|
|
3165
3167
|
"cosmol_viewer",
|
|
3166
3168
|
"cosmol_viewer_core",
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cosmol-viewer
|
|
3
|
-
Version: 0.1.1.
|
|
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.
|
|
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
|
|
39
|
+
scene = Scene()
|
|
41
40
|
scene.scale(0.1)
|
|
42
41
|
scene.add_shape(mol, "mol")
|
|
43
42
|
|
|
@@ -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
|
|
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
|
-
|
|
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
|
|
492
|
+
// 当前方向
|
|
493
|
+
let dir = camera_state.direction;
|
|
502
494
|
|
|
503
|
-
//
|
|
504
|
-
let
|
|
495
|
+
// right = 当前方向 × 当前 up
|
|
496
|
+
let right = dir.cross(camera_state.up).normalize();
|
|
505
497
|
|
|
506
|
-
//
|
|
507
|
-
let pitch_quat = Quat::from_axis_angle(right,
|
|
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
|
-
//
|
|
510
|
-
let
|
|
511
|
-
|
|
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
|
-
|
|
514
|
-
camera_state.up = yaw_quat *
|
|
507
|
+
camera_state.direction = final_dir.normalize();
|
|
508
|
+
camera_state.up = (yaw_quat * rotated_up).normalize();
|
|
515
509
|
|
|
516
|
-
|
|
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.
|
|
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,
|
|
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.
|
|
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
|
|
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.
|
|
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
|
|
31
|
+
scene = Scene()
|
|
34
32
|
scene.scale(0.1)
|
|
35
33
|
scene.add_shape(mol, "mol")
|
|
36
34
|
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cosmol-viewer"
|
|
3
|
-
version = "0.1.1.
|
|
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
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cosmol_viewer-0.1.1.dev4 → cosmol_viewer-0.1.1.dev5}/crates/core/src/shader/bg_fragment.glsl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|