Skip to content

Commit 7f8929a

Browse files
authored
Merge pull request #28 from rs4rse/pr-17
Superseded pr17: drag-drop new structure
2 parents dcb7d3e + 38e45a4 commit 7f8929a

7 files changed

Lines changed: 358 additions & 98 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ js-sys = "0.3"
3131
[features]
3232
webgpu = ["bevy/webgpu"]
3333
webgl2 = ["bevy/webgl2"]
34+
dev-track-location = ["bevy/track_location"]
3435

3536
[lib]
3637
# need both, cdylib for wasm, rlib for main.rs

assets/c6h6.xyz

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
12
2+
Benzene C6H6
3+
C 0.000000 1.396792 0.000000
4+
C 1.209657 0.698396 0.000000
5+
C 1.209657 -0.698396 0.000000
6+
C 0.000000 -1.396792 0.000000
7+
C -1.209657 -0.698396 0.000000
8+
C -1.209657 0.698396 0.000000
9+
H 0.000000 2.490291 0.000000
10+
H 2.156659 1.245145 0.000000
11+
H 2.156659 -1.245145 0.000000
12+
H 0.000000 -2.490291 0.000000
13+
H -2.156659 -1.245145 0.000000
14+
H -2.156659 1.245145 0.000000

src/io.rs

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
// io.rs
12
use bevy::prelude::*;
3+
use std::path::PathBuf;
24

5+
use crate::parse::parse_xyz_content;
36
use crate::structure::{Atom, Crystal};
47

5-
// System to load crystal data
6-
pub fn load_crystal(mut commands: Commands) {
7-
// For now, use the default water molecule structure
8-
// In the future, this can be extended to load from embedded assets or user input
8+
// System to load default crystal data
9+
pub(crate) fn load_default_crystal(mut commands: Commands) {
910
println!("Loading default water molecule structure");
1011

1112
let crystal = Crystal {
@@ -33,3 +34,90 @@ pub fn load_crystal(mut commands: Commands) {
3334

3435
commands.insert_resource(crystal);
3536
}
37+
38+
// Resource to handle file drag and drop
39+
#[derive(Resource, Default)]
40+
pub(crate) struct FileDragDrop {
41+
dragged_file: Option<PathBuf>,
42+
loaded_crystal: Option<Crystal>,
43+
}
44+
45+
impl FileDragDrop {
46+
pub(crate) fn dragged_file(&self) -> Option<&PathBuf> {
47+
self.dragged_file.as_ref()
48+
}
49+
}
50+
51+
// System to handle file drag and drop events
52+
pub(crate) fn handle_file_drag_drop(
53+
mut drag_drop_events: EventReader<bevy::window::FileDragAndDrop>,
54+
mut file_drag_drop: ResMut<FileDragDrop>,
55+
) {
56+
for event in drag_drop_events.read() {
57+
match event {
58+
bevy::window::FileDragAndDrop::DroppedFile { path_buf, .. } => {
59+
println!("File dropped: {:?}", path_buf);
60+
61+
if let Some(extension) = path_buf.extension() {
62+
if extension == "xyz" {
63+
file_drag_drop.dragged_file = Some(path_buf.clone());
64+
} else {
65+
println!("Unsupported file type. Please drop an XYZ file.");
66+
}
67+
}
68+
}
69+
bevy::window::FileDragAndDrop::HoveredFile { path_buf, .. } => {
70+
println!("File hovered: {:?}", path_buf);
71+
}
72+
bevy::window::FileDragAndDrop::HoveredFileCanceled { .. } => {
73+
println!("File hover canceled");
74+
}
75+
}
76+
}
77+
}
78+
79+
// System to load crystal from dropped file
80+
pub(crate) fn load_dropped_file(
81+
mut file_drag_drop: ResMut<FileDragDrop>,
82+
mut crystal_loaded: Local<bool>,
83+
) {
84+
if let Some(ref path) = file_drag_drop.dragged_file {
85+
if !*crystal_loaded {
86+
match std::fs::read_to_string(path) {
87+
Ok(contents) => match parse_xyz_content(&contents) {
88+
Ok(crystal) => {
89+
println!("Successfully loaded crystal from: {:?}", path);
90+
file_drag_drop.loaded_crystal = Some(crystal);
91+
*crystal_loaded = true;
92+
}
93+
Err(e) => {
94+
eprintln!("Failed to parse XYZ file: {}", e);
95+
}
96+
},
97+
Err(e) => {
98+
eprintln!("Failed to read file: {}", e);
99+
}
100+
}
101+
}
102+
}
103+
}
104+
105+
// System to update crystal resource when new file is loaded
106+
pub(crate) fn update_crystal_from_file(
107+
mut commands: Commands,
108+
file_drag_drop: Res<FileDragDrop>,
109+
current_crystal: Option<Res<Crystal>>,
110+
) {
111+
if let Some(crystal) = &file_drag_drop.loaded_crystal {
112+
// Only update if this is a new crystal
113+
if let Some(current) = current_crystal {
114+
if current.atoms.len() != crystal.atoms.len() {
115+
commands.insert_resource(crystal.clone());
116+
println!("Crystal updated with {} atoms", crystal.atoms.len());
117+
}
118+
} else {
119+
commands.insert_resource(crystal.clone());
120+
println!("Crystal loaded with {} atoms", crystal.atoms.len());
121+
}
122+
}
123+
}

src/lib.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ pub(crate) mod parse;
1010
pub(crate) mod structure;
1111

1212
use crate::client::{poll_websocket_stream, setup_websocket_stream};
13-
use crate::io::load_crystal;
13+
use crate::io::{handle_file_drag_drop, load_dropped_file, update_crystal_from_file, FileDragDrop};
1414
use crate::structure::{update_crystal_system, UpdateStructure};
15-
use crate::ui::reset_camera_button_interaction;
1615
use crate::ui::{
17-
camera_controls, refresh_atoms_system, setup_cameras, setup_scene, toggle_light_attachment,
16+
camera_controls, handle_load_default_button, refresh_atoms_system,
17+
reset_camera_button_interaction, setup_cameras, setup_file_ui, setup_light,
18+
toggle_light_attachment, update_file_ui, update_scene,
1819
};
1920
use crate::ui::{setup_buttons, spawn_axis};
2021

@@ -36,28 +37,35 @@ pub fn run_app() {
3637
filter: "wgpu=error,bevy_render=info,bevy_ecs=trace".to_string(),
3738
custom_layer: |_| None,
3839
}))
40+
.init_resource::<FileDragDrop>()
3941
.add_event::<UpdateStructure>()
40-
.add_systems(Startup, load_crystal)
41-
.add_systems(Startup, setup_scene.after(load_crystal))
42+
.add_event::<bevy::window::FileDragAndDrop>()
4243
.add_systems(
4344
Startup,
4445
(
4546
setup_cameras,
4647
spawn_axis,
4748
setup_buttons,
49+
setup_file_ui,
4850
setup_websocket_stream,
49-
)
50-
.after(setup_scene),
51+
),
5152
)
53+
.add_systems(Startup, (setup_light).after(setup_cameras))
5254
.add_systems(
5355
Update,
5456
(
5557
poll_websocket_stream,
5658
update_crystal_system,
59+
handle_file_drag_drop,
60+
load_dropped_file,
61+
update_crystal_from_file,
62+
update_file_ui,
5763
refresh_atoms_system,
5864
toggle_light_attachment,
5965
reset_camera_button_interaction,
66+
handle_load_default_button,
6067
camera_controls,
68+
update_scene,
6169
),
6270
)
6371
.run();

src/parse.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ use crate::structure::{Atom, Crystal};
22
use anyhow::{Context, Result};
33

44
// Function to parse XYZ file format from string content
5-
#[allow(dead_code)]
6-
fn parse_xyz_content(contents: &str) -> Result<Crystal> {
5+
pub(crate) fn parse_xyz_content(contents: &str) -> Result<Crystal> {
76
let lines = contents.lines().collect::<Vec<&str>>();
87

98
if lines.len() < 2 {
@@ -16,8 +15,11 @@ fn parse_xyz_content(contents: &str) -> Result<Crystal> {
1615
.parse()
1716
.context("Failed to parse number of atoms")?;
1817

19-
// Second line is a comment (we can skip it)
20-
// Remaining lines contain atom data
18+
// Second line may contain comment or extended XYZ properties
19+
let _comment_line = lines[1].trim();
20+
21+
// Parse extended XYZ properties if present (basic implementation)
22+
// For now, we'll focus on the basic XYZ format
2123

2224
let mut atoms = Vec::new();
2325

src/structure.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub struct Atom {
1212
}
1313

1414
// Structure to hold our crystal data
15-
#[derive(Resource)]
15+
#[derive(Resource, Clone)]
1616
pub struct Crystal {
1717
pub atoms: Vec<Atom>,
1818
}
@@ -31,10 +31,12 @@ pub struct UpdateStructure {
3131

3232
// System to handle incoming structure updates
3333
pub fn update_crystal_system(
34-
mut crystal: ResMut<Crystal>,
34+
crystal: Option<ResMut<Crystal>>,
3535
mut events: EventReader<UpdateStructure>,
3636
) {
37-
for event in events.read() {
38-
crystal.atoms = event.atoms.clone();
37+
if let Some(mut crystal) = crystal {
38+
for event in events.read() {
39+
crystal.atoms.clone_from(&event.atoms);
40+
}
3941
}
4042
}

0 commit comments

Comments
 (0)