eulumdat_bevy/viewer/
wasm_sync.rs1#[cfg(all(target_arch = "wasm32", feature = "wasm-sync"))]
8use super::SceneType;
9use super::ViewerSettings;
10use bevy::prelude::*;
11use eulumdat::Eulumdat;
12
13#[cfg(all(target_arch = "wasm32", feature = "wasm-sync"))]
14const LDT_STORAGE_KEY: &str = "eulumdat_current_ldt";
15#[cfg(all(target_arch = "wasm32", feature = "wasm-sync"))]
16const LDT_TIMESTAMP_KEY: &str = "eulumdat_ldt_timestamp";
17#[cfg(all(target_arch = "wasm32", feature = "wasm-sync"))]
18const VIEWER_SETTINGS_KEY: &str = "eulumdat_viewer_settings";
19#[cfg(all(target_arch = "wasm32", feature = "wasm-sync"))]
20const VIEWER_SETTINGS_TIMESTAMP_KEY: &str = "eulumdat_viewer_settings_timestamp";
21
22#[derive(Resource, Default)]
24pub struct LdtTimestamp(pub String);
25
26#[derive(Resource, Default)]
28pub struct ViewerSettingsTimestamp(pub String);
29
30#[cfg(all(target_arch = "wasm32", feature = "wasm-sync"))]
32pub fn load_from_local_storage() -> Option<Eulumdat> {
33 let window = web_sys::window()?;
34 let storage = window.local_storage().ok()??;
35 let ldt_string = storage.get_item(LDT_STORAGE_KEY).ok()??;
36
37 web_sys::console::log_1(
38 &format!(
39 "[Bevy] Loading LDT from localStorage, {} bytes",
40 ldt_string.len()
41 )
42 .into(),
43 );
44
45 match Eulumdat::parse(&ldt_string) {
46 Ok(ldt) => {
47 web_sys::console::log_1(
48 &format!(
49 "[Bevy] Parsed LDT: {} lumens, {} cd/klm max",
50 ldt.total_luminous_flux(),
51 ldt.max_intensity()
52 )
53 .into(),
54 );
55 Some(ldt)
56 }
57 Err(e) => {
58 web_sys::console::error_1(&format!("[Bevy] Failed to parse LDT: {:?}", e).into());
59 None
60 }
61 }
62}
63
64#[cfg(not(all(target_arch = "wasm32", feature = "wasm-sync")))]
65pub fn load_from_local_storage() -> Option<Eulumdat> {
66 None
67}
68
69#[cfg(all(target_arch = "wasm32", feature = "wasm-sync"))]
71pub fn get_ldt_timestamp() -> Option<String> {
72 let window = web_sys::window()?;
73 let storage = window.local_storage().ok()??;
74 storage.get_item(LDT_TIMESTAMP_KEY).ok()?
75}
76
77#[cfg(not(all(target_arch = "wasm32", feature = "wasm-sync")))]
78pub fn get_ldt_timestamp() -> Option<String> {
79 None
80}
81
82pub fn load_default_ldt() -> Option<Eulumdat> {
87 #[cfg(all(target_arch = "wasm32", feature = "wasm-sync"))]
89 {
90 load_from_local_storage()
91 }
92
93 #[cfg(all(target_arch = "wasm32", not(feature = "wasm-sync")))]
95 {
96 None
97 }
98
99 #[cfg(not(target_arch = "wasm32"))]
101 {
102 let sample_paths = [
103 "crates/eulumdat-wasm/templates/road_luminaire.ldt",
104 "../eulumdat-wasm/templates/road_luminaire.ldt",
105 "crates/eulumdat-wasm/templates/fluorescent_luminaire.ldt",
106 "../eulumdat-wasm/templates/fluorescent_luminaire.ldt",
107 "templates/road_luminaire.ldt",
108 "sample.ldt",
109 ];
110
111 for path in sample_paths {
112 if let Ok(ldt) = Eulumdat::from_file(path) {
113 return Some(ldt);
114 }
115 }
116 None
117 }
118}
119
120#[cfg(feature = "wasm-sync")]
122#[allow(unused_mut, unused_variables)]
123pub fn poll_ldt_changes(
124 mut settings: ResMut<ViewerSettings>,
125 mut last_timestamp: ResMut<LdtTimestamp>,
126) {
127 #[cfg(target_arch = "wasm32")]
128 {
129 if let Some(new_timestamp) = get_ldt_timestamp() {
130 if new_timestamp != last_timestamp.0 {
131 web_sys::console::log_1(
133 &format!(
134 "[Bevy] LDT timestamp changed: {} -> {}",
135 last_timestamp.0, new_timestamp
136 )
137 .into(),
138 );
139 if let Some(ldt) = load_from_local_storage() {
140 web_sys::console::log_1(
141 &format!("[Bevy] Updating ViewerSettings with new LDT").into(),
142 );
143 settings.ldt_data = Some(ldt);
144 last_timestamp.0 = new_timestamp;
145 }
146 }
147 }
148 }
149}
150
151#[cfg(not(feature = "wasm-sync"))]
153#[allow(unused_mut, unused_variables, dead_code)]
154pub fn poll_ldt_changes(
155 mut settings: ResMut<ViewerSettings>,
156 mut last_timestamp: ResMut<LdtTimestamp>,
157) {
158 }
160
161#[cfg(all(target_arch = "wasm32", feature = "wasm-sync"))]
163pub fn get_viewer_settings_timestamp() -> Option<String> {
164 let window = web_sys::window()?;
165 let storage = window.local_storage().ok()??;
166 storage.get_item(VIEWER_SETTINGS_TIMESTAMP_KEY).ok()?
167}
168
169#[cfg(not(all(target_arch = "wasm32", feature = "wasm-sync")))]
170pub fn get_viewer_settings_timestamp() -> Option<String> {
171 None
172}
173
174#[cfg(all(target_arch = "wasm32", feature = "wasm-sync"))]
176pub fn load_viewer_settings_from_local_storage(current: &ViewerSettings) -> Option<ViewerSettings> {
177 let window = web_sys::window()?;
178 let storage = window.local_storage().ok()??;
179 let json_string = storage.get_item(VIEWER_SETTINGS_KEY).ok()??;
180
181 parse_viewer_settings_json(&json_string, current)
182}
183
184#[cfg(not(all(target_arch = "wasm32", feature = "wasm-sync")))]
185pub fn load_viewer_settings_from_local_storage(
186 _current: &ViewerSettings,
187) -> Option<ViewerSettings> {
188 None
189}
190
191#[cfg(all(target_arch = "wasm32", feature = "wasm-sync"))]
194fn parse_viewer_settings_json(json: &str, current: &ViewerSettings) -> Option<ViewerSettings> {
195 let get_f32 = |key: &str| -> Option<f32> {
199 let pattern = format!("\"{}\":", key);
200 let start = json.find(&pattern)? + pattern.len();
201 let rest = &json[start..];
202 let end = rest.find([',', '}'])?;
203 rest[..end].trim().parse().ok()
204 };
205
206 let get_bool = |key: &str| -> Option<bool> {
207 let pattern = format!("\"{}\":", key);
208 let start = json.find(&pattern)? + pattern.len();
209 let rest = &json[start..];
210 let end = rest.find([',', '}'])?;
211 let value = rest[..end].trim();
212 Some(value == "true")
213 };
214
215 let get_u8 = |key: &str| -> Option<u8> {
216 let pattern = format!("\"{}\":", key);
217 let start = json.find(&pattern)? + pattern.len();
218 let rest = &json[start..];
219 let end = rest.find([',', '}'])?;
220 rest[..end].trim().parse().ok()
221 };
222
223 let scene_type = match get_u8("scene_type")? {
224 0 => SceneType::Room,
225 1 => SceneType::Road,
226 2 => SceneType::Parking,
227 3 => SceneType::Outdoor,
228 _ => SceneType::Room,
229 };
230
231 Some(ViewerSettings {
232 scene_type,
233 room_width: get_f32("room_width").unwrap_or(current.room_width),
234 room_length: get_f32("room_length").unwrap_or(current.room_length),
235 room_height: get_f32("room_height").unwrap_or(current.room_height),
236 mounting_height: get_f32("mounting_height").unwrap_or(current.mounting_height),
237 pendulum_length: get_f32("pendulum_length").unwrap_or(current.pendulum_length),
238 light_intensity: get_f32("light_intensity").unwrap_or(current.light_intensity),
239 show_luminaire: get_bool("show_luminaire").unwrap_or(current.show_luminaire),
240 show_photometric_solid: get_bool("show_photometric_solid")
241 .unwrap_or(current.show_photometric_solid),
242 show_shadows: get_bool("show_shadows").unwrap_or(current.show_shadows),
243 ldt_data: current.ldt_data.clone(),
245 luminaire_tilt: get_f32("luminaire_tilt").unwrap_or(current.luminaire_tilt),
246 lane_width: get_f32("lane_width").unwrap_or(current.lane_width),
247 num_lanes: get_u8("num_lanes").unwrap_or(current.num_lanes as u8) as u32,
248 sidewalk_width: get_f32("sidewalk_width").unwrap_or(current.sidewalk_width),
249 pole_spacing: get_f32("pole_spacing").unwrap_or(current.pole_spacing),
250 })
251}
252
253#[cfg(feature = "wasm-sync")]
255#[allow(unused_mut, unused_variables)]
256pub fn poll_viewer_settings_changes(
257 mut settings: ResMut<ViewerSettings>,
258 mut last_timestamp: ResMut<ViewerSettingsTimestamp>,
259) {
260 #[cfg(target_arch = "wasm32")]
261 {
262 if let Some(new_timestamp) = get_viewer_settings_timestamp() {
263 if new_timestamp != last_timestamp.0 {
264 if let Some(new_settings) = load_viewer_settings_from_local_storage(&settings) {
266 *settings = new_settings;
267 last_timestamp.0 = new_timestamp;
268 }
269 }
270 }
271 }
272}
273
274#[cfg(not(feature = "wasm-sync"))]
276#[allow(unused_mut, unused_variables, dead_code)]
277pub fn poll_viewer_settings_changes(
278 mut settings: ResMut<ViewerSettings>,
279 mut last_timestamp: ResMut<ViewerSettingsTimestamp>,
280) {
281 }