ferritin_structure_mesh/
structure.rs

1//! Structure.
2//!
3//! Struct for rendering protein structures
4//!
5
6use super::ColorScheme;
7use bevy::prelude::*;
8use bon::Builder;
9use ferritin_core::AtomCollection;
10
11/// Rendering options for protein structures
12#[derive(Clone)]
13pub enum RenderOptions {
14    Wireframe,
15    Cartoon,
16    BallAndStick,
17    Solid,
18    Putty,
19}
20
21/// Structure represents a molecular structure that can be rendered
22#[derive(Builder, Clone)]
23pub struct Structure {
24    pdb: AtomCollection,
25    #[builder(default = RenderOptions::Solid)]
26    rendertype: RenderOptions,
27    #[builder(default = ColorScheme::ByAtomType)]
28    color_scheme: ColorScheme,
29    #[builder(default = StandardMaterial::default())]
30    material: StandardMaterial,
31}
32
33// Basic implementation without feature gates
34impl Structure {
35    // Basic methods that don't depend on bevy or rerun
36}
37
38impl Structure {
39    /// Convert the structure to a mesh using the specified render type
40    pub fn to_mesh(&self) -> Mesh {
41        match self.rendertype {
42            RenderOptions::Wireframe => self.render_wireframe(),
43            RenderOptions::Cartoon => self.render_cartoon(),
44            RenderOptions::BallAndStick => self.render_ballandstick(),
45            RenderOptions::Solid => self.render_spheres(),
46            RenderOptions::Putty => self.render_putty(),
47        }
48    }
49
50    /// Get the material used for rendering
51    pub fn get_material(&self) -> StandardMaterial {
52        self.material.clone()
53    }
54
55    // Rendering method implementations
56    fn render_wireframe(&self) -> Mesh {
57        self.create_sphere_mesh(0.5)
58    }
59
60    fn render_cartoon(&self) -> Mesh {
61        self.create_sphere_mesh(1.0)
62    }
63
64    fn render_ballandstick(&self) -> Mesh {
65        self.create_sphere_mesh(0.3)
66    }
67
68    fn render_spheres(&self) -> Mesh {
69        self.create_sphere_mesh(1.5)
70    }
71
72    fn render_putty(&self) -> Mesh {
73        self.create_sphere_mesh(2.0)
74    }
75
76    /// Create a mesh with spheres at each atom position
77    fn create_sphere_mesh(&self, radius: f32) -> Mesh {
78        let mut positions = Vec::new();
79        let mut normals = Vec::new();
80        let mut uvs = Vec::new();
81        let mut indices = Vec::new();
82
83        let subdivisions = 8;
84
85        for idx in 0..self.pdb.get_size() {
86            let coord = self.pdb.get_coord(idx);
87            let center = Vec3::new(coord[0], coord[1], coord[2]);
88            let base_index = positions.len() as u32;
89
90            // Generate a sphere using UV sphere algorithm
91            for lat in 0..=subdivisions {
92                let theta = lat as f32 * std::f32::consts::PI / subdivisions as f32;
93                let sin_theta = theta.sin();
94                let cos_theta = theta.cos();
95
96                for lon in 0..=subdivisions {
97                    let phi = lon as f32 * 2.0 * std::f32::consts::PI / subdivisions as f32;
98                    let sin_phi = phi.sin();
99                    let cos_phi = phi.cos();
100
101                    let x = sin_theta * cos_phi;
102                    let y = cos_theta;
103                    let z = sin_theta * sin_phi;
104
105                    let normal = Vec3::new(x, y, z);
106                    let pos = center + normal * radius;
107
108                    positions.push([pos.x, pos.y, pos.z]);
109                    normals.push([normal.x, normal.y, normal.z]);
110                    uvs.push([
111                        lon as f32 / subdivisions as f32,
112                        lat as f32 / subdivisions as f32,
113                    ]);
114                }
115            }
116
117            // Generate indices for the sphere
118            for lat in 0..subdivisions {
119                for lon in 0..subdivisions {
120                    let first = base_index + lat * (subdivisions + 1) + lon;
121                    let second = first + subdivisions + 1;
122
123                    indices.push(first);
124                    indices.push(second);
125                    indices.push(first + 1);
126
127                    indices.push(second);
128                    indices.push(second + 1);
129                    indices.push(first + 1);
130                }
131            }
132        }
133
134        // Create mesh with proper vertex attributes
135        let mut mesh = Mesh::new(
136            bevy::mesh::PrimitiveTopology::TriangleList,
137            bevy::asset::RenderAssetUsages::default(),
138        );
139
140        mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
141        mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
142        mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);
143        mesh.insert_indices(bevy::mesh::Indices::U32(indices));
144
145        mesh
146    }
147}