1#[cfg(not(all(target_arch = "wasm32", feature = "plot-web")))]
2use super::common::ERR_PLOTTING_UNAVAILABLE;
3
4use crate::{build_runtime_error, BuiltinResult, RuntimeError};
5use serde::{Deserialize, Serialize};
6
7#[derive(Clone, Debug, Serialize, Deserialize)]
8#[serde(rename_all = "camelCase")]
9pub struct PlotSurfaceCameraState {
10 pub active_axes: usize,
11 pub axes: Vec<PlotCameraState>,
12}
13
14#[derive(Clone, Debug, Serialize, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct PlotCameraState {
17 pub position: [f32; 3],
18 pub target: [f32; 3],
19 pub up: [f32; 3],
20 pub zoom: f32,
21 pub aspect_ratio: f32,
22 pub projection: PlotCameraProjection,
23}
24
25#[derive(Clone, Debug, Serialize, Deserialize)]
26#[serde(tag = "kind", rename_all = "camelCase")]
27pub enum PlotCameraProjection {
28 Perspective {
29 fov: f32,
30 near: f32,
31 far: f32,
32 },
33 Orthographic {
34 left: f32,
35 right: f32,
36 bottom: f32,
37 top: f32,
38 near: f32,
39 far: f32,
40 },
41}
42
43#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
44#[serde(tag = "kind", rename_all = "camelCase")]
45pub enum PlotSurfaceHostAction {
46 CreateFeaStudy,
47}
48
49fn web_error(message: impl Into<String>) -> RuntimeError {
50 build_runtime_error(message)
51 .with_identifier("RunMat:plot:WebError")
52 .build()
53}
54
55#[allow(dead_code)]
56fn web_error_with_source(
57 message: impl Into<String>,
58 source: impl std::error::Error + Send + Sync + 'static,
59) -> RuntimeError {
60 build_runtime_error(message)
61 .with_identifier("RunMat:plot:WebError")
62 .with_source(source)
63 .build()
64}
65
66#[cfg(all(target_arch = "wasm32", feature = "plot-web"))]
67pub(crate) mod wasm {
68 use super::*;
69 use crate::builtins::plotting::state::{clone_figure, current_figure_revision, FigureHandle};
70 use log::debug;
71 use runmat_plot::core::PlotEvent;
72 use runmat_plot::styling::PlotThemeConfig;
73 use runmat_plot::web::WebRenderer;
74 use runmat_plot::{GeometryScenePickIndex, GeometryScenePickResult, GeometryScenePresentation};
75 use runmat_thread_local::runmat_thread_local;
76 use std::cell::RefCell;
77 use std::collections::HashMap;
78
79 runmat_thread_local! {
80 static SURFACES: RefCell<HashMap<u32, SurfaceEntry>> = RefCell::new(HashMap::new());
81 static ACTIVE_THEME: RefCell<PlotThemeConfig> = RefCell::new(PlotThemeConfig::default());
82 }
83
84 struct SurfaceEntry {
85 renderer: WebRenderer,
86 bound_target: Option<BoundSurfaceTarget>,
87 last_revision: Option<u64>,
88 geometry_presentation: GeometryScenePresentation,
89 geometry_pick_index: Option<CachedGeometryPickIndex>,
90 }
91
92 struct CachedGeometryPickIndex {
93 handle: u32,
94 index: GeometryScenePickIndex,
95 }
96
97 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
98 enum BoundSurfaceTarget {
99 Figure(u32),
100 GeometryScene(u32),
101 }
102
103 pub(super) fn install_surface_impl(
104 surface_id: u32,
105 mut renderer: WebRenderer,
106 ) -> BuiltinResult<()> {
107 ACTIVE_THEME.with(|theme| {
108 renderer.set_theme_config(theme.borrow().clone());
109 });
110 SURFACES.with(|slot| {
111 slot.borrow_mut().insert(
112 surface_id,
113 SurfaceEntry {
114 renderer,
115 bound_target: None,
116 last_revision: None,
117 geometry_presentation: GeometryScenePresentation::default(),
118 geometry_pick_index: None,
119 },
120 );
121 });
122 SURFACES.with(|slot| {
123 let keys: Vec<u32> = slot.borrow().keys().copied().collect();
124 debug!(
125 "plot-web: installed surface surface_id={surface_id} (active_surfaces={keys:?})"
126 );
127 });
128 Ok(())
129 }
130
131 pub(super) fn detach_surface_impl(surface_id: u32) {
132 SURFACES.with(|slot| {
133 slot.borrow_mut().remove(&surface_id);
134 });
135 SURFACES.with(|slot| {
136 let keys: Vec<u32> = slot.borrow().keys().copied().collect();
137 debug!("plot-web: detached surface surface_id={surface_id} (active_surfaces={keys:?})");
138 });
139 }
140
141 pub(super) fn clear_closed_figure_surfaces_impl(handle: u32) -> BuiltinResult<()> {
142 SURFACES.with(|slot| {
143 let mut map = slot.borrow_mut();
144 for entry in map.values_mut() {
145 if entry.bound_target == Some(BoundSurfaceTarget::Figure(handle)) {
146 entry.bound_target = None;
147 entry.last_revision = None;
148 entry.geometry_pick_index = None;
149 entry
150 .renderer
151 .clear_surface()
152 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
153 }
154 }
155 Ok(())
156 })
157 }
158
159 pub fn web_renderer_ready() -> bool {
160 SURFACES.with(|slot| !slot.borrow().is_empty())
161 }
162
163 pub(super) fn resize_surface_impl(
164 surface_id: u32,
165 width: u32,
166 height: u32,
167 pixels_per_point: f32,
168 ) -> BuiltinResult<()> {
169 SURFACES.with(|slot| {
170 let mut map = slot.borrow_mut();
171 let entry = map.get_mut(&surface_id).ok_or_else(|| {
172 web_error(format!(
173 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
174 ))
175 })?;
176 entry.renderer.set_pixels_per_point(pixels_per_point);
177 entry
178 .renderer
179 .resize_surface(width, height)
180 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
181 Ok(())
182 })
183 }
184
185 pub(super) fn bind_surface_to_figure_impl(surface_id: u32, handle: u32) -> BuiltinResult<()> {
186 SURFACES.with(|slot| {
187 let mut map = slot.borrow_mut();
188 let entry = map.get_mut(&surface_id).ok_or_else(|| {
189 web_error(format!(
190 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
191 ))
192 })?;
193 entry.bound_target = Some(BoundSurfaceTarget::Figure(handle));
194 entry.last_revision = None;
196 Ok(())
197 })
198 }
199
200 pub(super) fn set_theme_config_impl(theme: PlotThemeConfig) -> BuiltinResult<()> {
201 debug!(
202 "plot-web: runtime set_theme_config_impl variant={:?} custom_colors={}",
203 theme.variant,
204 theme.custom_colors.is_some()
205 );
206 ACTIVE_THEME.with(|slot| {
207 *slot.borrow_mut() = theme.clone();
208 });
209 SURFACES.with(|slot| {
210 let mut map = slot.borrow_mut();
211 debug!("plot-web: applying theme to {} surfaces", map.len());
212 for entry in map.values_mut() {
213 entry.renderer.set_theme_config(theme.clone());
214 match entry.bound_target {
215 Some(BoundSurfaceTarget::Figure(handle)) => {
216 if let Some(figure) = clone_figure(FigureHandle::from(handle)) {
217 entry
218 .renderer
219 .render_figure(figure)
220 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
221 }
222 }
223 Some(BoundSurfaceTarget::GeometryScene(_)) => {
224 entry
225 .renderer
226 .render_current_scene()
227 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
228 }
229 None => {}
230 }
231 }
232 Ok(())
233 })
234 }
235
236 pub(super) fn current_theme_config_impl() -> PlotThemeConfig {
237 ACTIVE_THEME.with(|slot| slot.borrow().clone())
238 }
239
240 pub(super) fn present_figure_on_surface_impl(
241 surface_id: u32,
242 handle: u32,
243 ) -> BuiltinResult<()> {
244 SURFACES.with(|slot| {
246 let mut map = slot.borrow_mut();
247 let entry = map.get_mut(&surface_id).ok_or_else(|| {
248 web_error(format!(
249 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
250 ))
251 })?;
252 if entry.bound_target != Some(BoundSurfaceTarget::Figure(handle)) {
253 entry.bound_target = Some(BoundSurfaceTarget::Figure(handle));
254 entry.last_revision = None;
255 entry.geometry_pick_index = None;
256 }
257 Ok::<(), RuntimeError>(())
258 })?;
259 present_surface_impl(surface_id)
260 }
261
262 pub(super) fn present_geometry_scene_on_surface_impl(
263 surface_id: u32,
264 handle: u32,
265 scene: runmat_plot::GeometryScene,
266 ) -> BuiltinResult<()> {
267 SURFACES.with(|slot| {
268 let mut map = slot.borrow_mut();
269 let entry = map.get_mut(&surface_id).ok_or_else(|| {
270 web_error(format!(
271 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
272 ))
273 })?;
274 if entry.bound_target != Some(BoundSurfaceTarget::GeometryScene(handle))
275 || entry.last_revision != Some(scene.revision)
276 {
277 entry.geometry_pick_index = None;
278 }
279 entry.bound_target = Some(BoundSurfaceTarget::GeometryScene(handle));
280 entry.last_revision = Some(scene.revision);
281 let presentation = entry.geometry_presentation.clone();
282 entry
283 .renderer
284 .render_geometry_scene_with_presentation(scene, Some(presentation))
285 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
286 Ok(())
287 })
288 }
289
290 pub(super) fn set_geometry_scene_presentation_impl(
291 surface_id: u32,
292 presentation: GeometryScenePresentation,
293 ) -> BuiltinResult<()> {
294 SURFACES.with(|slot| {
295 let mut map = slot.borrow_mut();
296 let entry = map.get_mut(&surface_id).ok_or_else(|| {
297 web_error(format!(
298 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
299 ))
300 })?;
301 let Some(BoundSurfaceTarget::GeometryScene(_)) = entry.bound_target else {
302 return Err(web_error(
303 "Plotting surface is not bound to a geometry scene.",
304 ));
305 };
306 log::info!(
307 "geometry_scene.presentation_runtime surface_id={} selected_region_id={} selected_region_count={} display_mode={:?}",
308 surface_id,
309 presentation.selected_region_id.as_deref().unwrap_or("none"),
310 presentation.selected_region_ids.len(),
311 presentation.display_mode,
312 );
313 entry.geometry_presentation = presentation.clone();
314 entry
315 .renderer
316 .set_geometry_scene_presentation(presentation)
317 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
318 Ok(())
319 })
320 }
321
322 pub(super) fn pick_geometry_scene_region_impl(
323 surface_id: u32,
324 x: f32,
325 y: f32,
326 ) -> BuiltinResult<Option<GeometryScenePickResult>> {
327 SURFACES.with(|slot| {
328 let mut map = slot.borrow_mut();
329 let entry = map.get_mut(&surface_id).ok_or_else(|| {
330 web_error(format!(
331 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
332 ))
333 })?;
334 let Some(BoundSurfaceTarget::GeometryScene(handle)) = entry.bound_target else {
335 return Ok(None);
336 };
337 let rebuild = entry
338 .geometry_pick_index
339 .as_ref()
340 .map(|cached| cached.handle != handle)
341 .unwrap_or(true);
342 if rebuild {
343 let scene =
344 crate::builtins::plotting::clone_geometry_scene(handle).ok_or_else(|| {
345 web_error(format!("geometry scene handle {handle} does not exist"))
346 })?;
347 entry.geometry_pick_index = Some(CachedGeometryPickIndex {
348 handle,
349 index: GeometryScenePickIndex::build(&scene),
350 });
351 }
352 Ok(entry.geometry_pick_index.as_ref().and_then(|cached| {
353 entry
354 .renderer
355 .pick_geometry_scene_region(&cached.index, [x, y])
356 }))
357 })
358 }
359
360 pub(super) fn present_surface_impl(surface_id: u32) -> BuiltinResult<()> {
361 SURFACES.with(|slot| {
362 let mut map = slot.borrow_mut();
363 let entry = map.get_mut(&surface_id).ok_or_else(|| {
364 web_error(format!(
365 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
366 ))
367 })?;
368 let target = entry
369 .bound_target
370 .ok_or_else(|| web_error("Plotting surface is not bound to a render target."))?;
371 let BoundSurfaceTarget::Figure(handle) = target else {
372 entry
373 .renderer
374 .render_current_scene()
375 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
376 return Ok(());
377 };
378 let current_rev = current_figure_revision(FigureHandle::from(handle));
380 if entry.last_revision != current_rev {
381 let figure = clone_figure(FigureHandle::from(handle))
382 .ok_or_else(|| web_error(format!("figure handle {handle} does not exist")))?;
383 if !figure.visible {
384 entry
385 .renderer
386 .clear_surface()
387 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
388 entry.last_revision = current_rev;
389 return Ok(());
390 }
391 entry
392 .renderer
393 .render_figure(figure)
394 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
395 entry.last_revision = current_rev;
396 }
397 entry
398 .renderer
399 .render_current_scene()
400 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
401 Ok(())
402 })
403 }
404
405 pub(super) fn handle_surface_event_impl(
406 surface_id: u32,
407 event: PlotEvent,
408 ) -> BuiltinResult<()> {
409 SURFACES.with(|slot| {
410 let mut map = slot.borrow_mut();
411 let entry = map.get_mut(&surface_id).ok_or_else(|| {
412 web_error(format!(
413 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
414 ))
415 })?;
416 match &event {
417 PlotEvent::MousePress { .. }
418 | PlotEvent::MouseRelease { .. }
419 | PlotEvent::MouseWheel { .. } => {
420 debug!("plot-web: surface_event(surface_id={surface_id}, event={event:?})");
421 }
422 PlotEvent::MouseMove { .. } | PlotEvent::Resize { .. } => {}
423 PlotEvent::KeyPress { .. } | PlotEvent::KeyRelease { .. } => {}
424 }
425 let _ = entry.renderer.handle_event(event);
428 entry
430 .renderer
431 .render_current_scene()
432 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
433 Ok(())
434 })
435 }
436
437 pub(super) fn fit_surface_extents_impl(surface_id: u32) -> BuiltinResult<()> {
438 SURFACES.with(|slot| {
439 let mut map = slot.borrow_mut();
440 let entry = map.get_mut(&surface_id).ok_or_else(|| {
441 web_error(format!(
442 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
443 ))
444 })?;
445 entry.renderer.fit_extents();
446 entry
447 .renderer
448 .render_current_scene()
449 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
450 Ok(())
451 })
452 }
453
454 pub(super) fn reset_surface_camera_impl(surface_id: u32) -> BuiltinResult<()> {
455 SURFACES.with(|slot| {
456 let mut map = slot.borrow_mut();
457 let entry = map.get_mut(&surface_id).ok_or_else(|| {
458 web_error(format!(
459 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
460 ))
461 })?;
462 entry.renderer.reset_camera_position();
463 entry
464 .renderer
465 .render_current_scene()
466 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
467 Ok(())
468 })
469 }
470
471 pub(super) fn get_surface_camera_state_impl(
472 surface_id: u32,
473 ) -> BuiltinResult<PlotSurfaceCameraState> {
474 SURFACES.with(|slot| {
475 let map = slot.borrow();
476 let entry = map.get(&surface_id).ok_or_else(|| {
477 web_error(format!(
478 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
479 ))
480 })?;
481 Ok(convert_camera_state(entry.renderer.camera_state()))
482 })
483 }
484
485 pub(super) fn set_surface_camera_state_impl(
486 surface_id: u32,
487 state: PlotSurfaceCameraState,
488 ) -> BuiltinResult<()> {
489 SURFACES.with(|slot| {
490 let mut map = slot.borrow_mut();
491 let entry = map.get_mut(&surface_id).ok_or_else(|| {
492 web_error(format!(
493 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
494 ))
495 })?;
496 entry
497 .renderer
498 .set_camera_state(&convert_camera_state_back(state));
499 entry
500 .renderer
501 .render_current_scene()
502 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
503 Ok(())
504 })
505 }
506
507 pub(super) fn take_surface_host_actions_impl(
508 surface_id: u32,
509 ) -> BuiltinResult<Vec<PlotSurfaceHostAction>> {
510 SURFACES.with(|slot| {
511 let mut map = slot.borrow_mut();
512 let entry = map.get_mut(&surface_id).ok_or_else(|| {
513 web_error(format!(
514 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
515 ))
516 })?;
517 Ok(entry
518 .renderer
519 .take_host_actions()
520 .into_iter()
521 .map(convert_host_action)
522 .collect())
523 })
524 }
525
526 pub fn render_current_scene(handle: u32) -> BuiltinResult<()> {
527 debug!("plot-web: render_current_scene(handle={handle})");
528 let needs_autobind = SURFACES.with(|slot| {
532 let map = slot.borrow();
533 !map.values()
534 .any(|entry| entry.bound_target == Some(BoundSurfaceTarget::Figure(handle)))
535 });
536 if needs_autobind {
537 let maybe_unbound_surface = SURFACES.with(|slot| {
538 let map = slot.borrow();
539 map.iter()
540 .filter_map(|(surface_id, entry)| {
541 if entry.bound_target.is_none() {
542 Some(*surface_id)
543 } else {
544 None
545 }
546 })
547 .min()
548 });
549 if let Some(surface_id) = maybe_unbound_surface {
550 let _ = bind_surface_to_figure_impl(surface_id, handle);
552 }
553 }
554
555 let surface_ids: Vec<u32> = SURFACES.with(|slot| {
557 slot.borrow()
558 .iter()
559 .filter_map(|(surface_id, entry)| {
560 if entry.bound_target == Some(BoundSurfaceTarget::Figure(handle)) {
561 Some(*surface_id)
562 } else {
563 None
564 }
565 })
566 .collect()
567 });
568 if surface_ids.is_empty() {
569 return Ok(());
571 }
572 for surface_id in surface_ids {
573 present_surface_impl(surface_id)?;
575 }
576 Ok(())
577 }
578
579 pub fn invalidate_surface_revisions() {
580 SURFACES.with(|slot| {
581 let mut map = slot.borrow_mut();
582 for entry in map.values_mut() {
583 entry.last_revision = None;
584 }
585 });
586 }
587
588 pub(super) use runmat_plot::web::WebRenderer as RendererType;
590
591 fn convert_host_action(
592 action: runmat_plot::web::WebSurfaceHostAction,
593 ) -> PlotSurfaceHostAction {
594 match action {
595 runmat_plot::web::WebSurfaceHostAction::CreateFeaStudy => {
596 PlotSurfaceHostAction::CreateFeaStudy
597 }
598 }
599 }
600}
601
602#[cfg(not(all(target_arch = "wasm32", feature = "plot-web")))]
603pub(crate) mod wasm {
604 use super::*;
605
606 pub struct RendererPlaceholder;
607
608 pub(super) fn install_surface_impl(
609 _surface_id: u32,
610 _renderer: RendererPlaceholder,
611 ) -> BuiltinResult<()> {
612 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
613 }
614
615 pub(super) fn detach_surface_impl(_surface_id: u32) {}
616
617 pub(super) fn clear_closed_figure_surfaces_impl(_handle: u32) -> BuiltinResult<()> {
618 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
619 }
620
621 pub fn web_renderer_ready() -> bool {
622 false
623 }
624
625 pub(super) use RendererPlaceholder as RendererType;
626
627 pub(super) fn resize_surface_impl(
628 _surface_id: u32,
629 _width: u32,
630 _height: u32,
631 _pixels_per_point: f32,
632 ) -> BuiltinResult<()> {
633 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
634 }
635
636 pub fn render_current_scene(_handle: u32) -> BuiltinResult<()> {
637 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
638 }
639
640 pub fn invalidate_surface_revisions() {}
641
642 pub(super) fn bind_surface_to_figure_impl(_surface_id: u32, _handle: u32) -> BuiltinResult<()> {
643 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
644 }
645
646 pub(super) fn present_surface_impl(_surface_id: u32) -> BuiltinResult<()> {
647 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
648 }
649
650 pub(super) fn present_figure_on_surface_impl(
651 _surface_id: u32,
652 _handle: u32,
653 ) -> BuiltinResult<()> {
654 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
655 }
656
657 pub(super) fn present_geometry_scene_on_surface_impl(
658 _surface_id: u32,
659 _handle: u32,
660 _scene: runmat_plot::GeometryScene,
661 ) -> BuiltinResult<()> {
662 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
663 }
664
665 pub(super) fn set_geometry_scene_presentation_impl(
666 _surface_id: u32,
667 _presentation: runmat_plot::GeometryScenePresentation,
668 ) -> BuiltinResult<()> {
669 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
670 }
671
672 pub(super) fn pick_geometry_scene_region_impl(
673 _surface_id: u32,
674 _x: f32,
675 _y: f32,
676 ) -> BuiltinResult<Option<runmat_plot::GeometryScenePickResult>> {
677 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
678 }
679
680 pub(super) fn fit_surface_extents_impl(_surface_id: u32) -> BuiltinResult<()> {
681 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
682 }
683
684 pub(super) fn reset_surface_camera_impl(_surface_id: u32) -> BuiltinResult<()> {
685 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
686 }
687
688 pub(super) fn get_surface_camera_state_impl(
689 _surface_id: u32,
690 ) -> BuiltinResult<PlotSurfaceCameraState> {
691 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
692 }
693
694 pub(super) fn set_surface_camera_state_impl(
695 _surface_id: u32,
696 _state: PlotSurfaceCameraState,
697 ) -> BuiltinResult<()> {
698 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
699 }
700
701 pub(super) fn set_theme_config_impl(
702 _theme: runmat_plot::styling::PlotThemeConfig,
703 ) -> BuiltinResult<()> {
704 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
705 }
706
707 pub(super) fn current_theme_config_impl() -> runmat_plot::styling::PlotThemeConfig {
708 runmat_plot::styling::PlotThemeConfig::default()
709 }
710
711 pub(super) fn take_surface_host_actions_impl(
712 _surface_id: u32,
713 ) -> BuiltinResult<Vec<PlotSurfaceHostAction>> {
714 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
715 }
716}
717
718pub use wasm::invalidate_surface_revisions;
719pub use wasm::render_current_scene;
720pub use wasm::web_renderer_ready;
721
722pub fn install_surface(surface_id: u32, renderer: wasm::RendererType) -> BuiltinResult<()> {
723 wasm::install_surface_impl(surface_id, renderer)
724}
725
726pub fn detach_surface(surface_id: u32) {
727 wasm::detach_surface_impl(surface_id)
728}
729
730pub fn clear_closed_figure_surfaces(handle: u32) -> BuiltinResult<()> {
731 wasm::clear_closed_figure_surfaces_impl(handle)
732}
733
734pub fn resize_surface(
735 surface_id: u32,
736 width: u32,
737 height: u32,
738 pixels_per_point: f32,
739) -> BuiltinResult<()> {
740 wasm::resize_surface_impl(surface_id, width, height, pixels_per_point)
741}
742
743pub fn bind_surface_to_figure(surface_id: u32, handle: u32) -> BuiltinResult<()> {
744 wasm::bind_surface_to_figure_impl(surface_id, handle)
745}
746
747pub fn present_surface(surface_id: u32) -> BuiltinResult<()> {
748 wasm::present_surface_impl(surface_id)
749}
750
751#[cfg(all(target_arch = "wasm32", feature = "plot-web"))]
752pub fn handle_plot_surface_event(
753 surface_id: u32,
754 event: runmat_plot::core::PlotEvent,
755) -> BuiltinResult<()> {
756 wasm::handle_surface_event_impl(surface_id, event)
757}
758
759pub fn present_figure_on_surface(surface_id: u32, handle: u32) -> BuiltinResult<()> {
760 wasm::present_figure_on_surface_impl(surface_id, handle)
761}
762
763pub fn present_geometry_scene_on_surface(
764 surface_id: u32,
765 handle: u32,
766 scene: runmat_plot::GeometryScene,
767) -> BuiltinResult<()> {
768 wasm::present_geometry_scene_on_surface_impl(surface_id, handle, scene)
769}
770
771pub fn set_geometry_scene_presentation(
772 surface_id: u32,
773 presentation: runmat_plot::GeometryScenePresentation,
774) -> BuiltinResult<()> {
775 wasm::set_geometry_scene_presentation_impl(surface_id, presentation)
776}
777
778pub fn pick_geometry_scene_region(
779 surface_id: u32,
780 x: f32,
781 y: f32,
782) -> BuiltinResult<Option<runmat_plot::GeometryScenePickResult>> {
783 wasm::pick_geometry_scene_region_impl(surface_id, x, y)
784}
785
786pub fn fit_surface_extents(surface_id: u32) -> BuiltinResult<()> {
787 wasm::fit_surface_extents_impl(surface_id)
788}
789
790pub fn reset_surface_camera(surface_id: u32) -> BuiltinResult<()> {
791 wasm::reset_surface_camera_impl(surface_id)
792}
793
794pub fn get_surface_camera_state(surface_id: u32) -> BuiltinResult<PlotSurfaceCameraState> {
795 wasm::get_surface_camera_state_impl(surface_id)
796}
797
798pub fn set_surface_camera_state(
799 surface_id: u32,
800 state: PlotSurfaceCameraState,
801) -> BuiltinResult<()> {
802 wasm::set_surface_camera_state_impl(surface_id, state)
803}
804
805pub fn take_surface_host_actions(surface_id: u32) -> BuiltinResult<Vec<PlotSurfaceHostAction>> {
806 wasm::take_surface_host_actions_impl(surface_id)
807}
808
809pub fn set_plot_theme_config(theme: runmat_plot::styling::PlotThemeConfig) -> BuiltinResult<()> {
810 wasm::set_theme_config_impl(theme)
811}
812
813pub fn current_plot_theme_config() -> runmat_plot::styling::PlotThemeConfig {
814 wasm::current_theme_config_impl()
815}
816
817#[cfg(all(target_arch = "wasm32", feature = "plot-web"))]
818fn convert_camera_state(state: runmat_plot::web::PlotSurfaceCameraState) -> PlotSurfaceCameraState {
819 PlotSurfaceCameraState {
820 active_axes: state.active_axes,
821 axes: state
822 .axes
823 .into_iter()
824 .map(|camera| PlotCameraState {
825 position: camera.position,
826 target: camera.target,
827 up: camera.up,
828 zoom: camera.zoom,
829 aspect_ratio: camera.aspect_ratio,
830 projection: match camera.projection {
831 runmat_plot::web::PlotCameraProjection::Perspective { fov, near, far } => {
832 PlotCameraProjection::Perspective { fov, near, far }
833 }
834 runmat_plot::web::PlotCameraProjection::Orthographic {
835 left,
836 right,
837 bottom,
838 top,
839 near,
840 far,
841 } => PlotCameraProjection::Orthographic {
842 left,
843 right,
844 bottom,
845 top,
846 near,
847 far,
848 },
849 },
850 })
851 .collect(),
852 }
853}
854
855#[cfg(all(target_arch = "wasm32", feature = "plot-web"))]
856fn convert_camera_state_back(
857 state: PlotSurfaceCameraState,
858) -> runmat_plot::web::PlotSurfaceCameraState {
859 runmat_plot::web::PlotSurfaceCameraState {
860 active_axes: state.active_axes,
861 axes: state
862 .axes
863 .into_iter()
864 .map(|camera| runmat_plot::web::PlotCameraState {
865 position: camera.position,
866 target: camera.target,
867 up: camera.up,
868 zoom: camera.zoom,
869 aspect_ratio: camera.aspect_ratio,
870 projection: match camera.projection {
871 PlotCameraProjection::Perspective { fov, near, far } => {
872 runmat_plot::web::PlotCameraProjection::Perspective { fov, near, far }
873 }
874 PlotCameraProjection::Orthographic {
875 left,
876 right,
877 bottom,
878 top,
879 near,
880 far,
881 } => runmat_plot::web::PlotCameraProjection::Orthographic {
882 left,
883 right,
884 bottom,
885 top,
886 near,
887 far,
888 },
889 },
890 })
891 .collect(),
892 }
893}
894
895