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 entry.geometry_presentation = presentation.clone();
307 entry
308 .renderer
309 .set_geometry_scene_presentation(presentation)
310 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
311 Ok(())
312 })
313 }
314
315 pub(super) fn pick_geometry_scene_region_impl(
316 surface_id: u32,
317 x: f32,
318 y: f32,
319 ) -> BuiltinResult<Option<GeometryScenePickResult>> {
320 SURFACES.with(|slot| {
321 let mut map = slot.borrow_mut();
322 let entry = map.get_mut(&surface_id).ok_or_else(|| {
323 web_error(format!(
324 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
325 ))
326 })?;
327 let Some(BoundSurfaceTarget::GeometryScene(handle)) = entry.bound_target else {
328 return Ok(None);
329 };
330 let rebuild = entry
331 .geometry_pick_index
332 .as_ref()
333 .map(|cached| cached.handle != handle)
334 .unwrap_or(true);
335 if rebuild {
336 let scene =
337 crate::builtins::plotting::clone_geometry_scene(handle).ok_or_else(|| {
338 web_error(format!("geometry scene handle {handle} does not exist"))
339 })?;
340 entry.geometry_pick_index = Some(CachedGeometryPickIndex {
341 handle,
342 index: GeometryScenePickIndex::build(&scene),
343 });
344 }
345 Ok(entry.geometry_pick_index.as_ref().and_then(|cached| {
346 entry
347 .renderer
348 .pick_geometry_scene_region(&cached.index, [x, y])
349 }))
350 })
351 }
352
353 pub(super) fn present_surface_impl(surface_id: u32) -> BuiltinResult<()> {
354 SURFACES.with(|slot| {
355 let mut map = slot.borrow_mut();
356 let entry = map.get_mut(&surface_id).ok_or_else(|| {
357 web_error(format!(
358 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
359 ))
360 })?;
361 let target = entry
362 .bound_target
363 .ok_or_else(|| web_error("Plotting surface is not bound to a render target."))?;
364 let BoundSurfaceTarget::Figure(handle) = target else {
365 entry
366 .renderer
367 .render_current_scene()
368 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
369 return Ok(());
370 };
371 let current_rev = current_figure_revision(FigureHandle::from(handle));
373 if entry.last_revision != current_rev {
374 let figure = clone_figure(FigureHandle::from(handle))
375 .ok_or_else(|| web_error(format!("figure handle {handle} does not exist")))?;
376 if !figure.visible {
377 entry
378 .renderer
379 .clear_surface()
380 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
381 entry.last_revision = current_rev;
382 return Ok(());
383 }
384 entry
385 .renderer
386 .render_figure(figure)
387 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
388 entry.last_revision = current_rev;
389 }
390 entry
391 .renderer
392 .render_current_scene()
393 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
394 Ok(())
395 })
396 }
397
398 pub(super) fn handle_surface_event_impl(
399 surface_id: u32,
400 event: PlotEvent,
401 ) -> BuiltinResult<()> {
402 SURFACES.with(|slot| {
403 let mut map = slot.borrow_mut();
404 let entry = map.get_mut(&surface_id).ok_or_else(|| {
405 web_error(format!(
406 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
407 ))
408 })?;
409 match &event {
410 PlotEvent::MousePress { .. }
411 | PlotEvent::MouseRelease { .. }
412 | PlotEvent::MouseWheel { .. } => {
413 debug!("plot-web: surface_event(surface_id={surface_id}, event={event:?})");
414 }
415 PlotEvent::MouseMove { .. } | PlotEvent::Resize { .. } => {}
416 PlotEvent::KeyPress { .. } | PlotEvent::KeyRelease { .. } => {}
417 }
418 let _ = entry.renderer.handle_event(event);
421 entry
423 .renderer
424 .render_current_scene()
425 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
426 Ok(())
427 })
428 }
429
430 pub(super) fn fit_surface_extents_impl(surface_id: u32) -> BuiltinResult<()> {
431 SURFACES.with(|slot| {
432 let mut map = slot.borrow_mut();
433 let entry = map.get_mut(&surface_id).ok_or_else(|| {
434 web_error(format!(
435 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
436 ))
437 })?;
438 entry.renderer.fit_extents();
439 entry
440 .renderer
441 .render_current_scene()
442 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
443 Ok(())
444 })
445 }
446
447 pub(super) fn reset_surface_camera_impl(surface_id: u32) -> BuiltinResult<()> {
448 SURFACES.with(|slot| {
449 let mut map = slot.borrow_mut();
450 let entry = map.get_mut(&surface_id).ok_or_else(|| {
451 web_error(format!(
452 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
453 ))
454 })?;
455 entry.renderer.reset_camera_position();
456 entry
457 .renderer
458 .render_current_scene()
459 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
460 Ok(())
461 })
462 }
463
464 pub(super) fn get_surface_camera_state_impl(
465 surface_id: u32,
466 ) -> BuiltinResult<PlotSurfaceCameraState> {
467 SURFACES.with(|slot| {
468 let map = slot.borrow();
469 let entry = map.get(&surface_id).ok_or_else(|| {
470 web_error(format!(
471 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
472 ))
473 })?;
474 Ok(convert_camera_state(entry.renderer.camera_state()))
475 })
476 }
477
478 pub(super) fn set_surface_camera_state_impl(
479 surface_id: u32,
480 state: PlotSurfaceCameraState,
481 ) -> BuiltinResult<()> {
482 SURFACES.with(|slot| {
483 let mut map = slot.borrow_mut();
484 let entry = map.get_mut(&surface_id).ok_or_else(|| {
485 web_error(format!(
486 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
487 ))
488 })?;
489 entry
490 .renderer
491 .set_camera_state(&convert_camera_state_back(state));
492 entry
493 .renderer
494 .render_current_scene()
495 .map_err(|err| web_error(format!("Plotting failed: {err}")))?;
496 Ok(())
497 })
498 }
499
500 pub(super) fn take_surface_host_actions_impl(
501 surface_id: u32,
502 ) -> BuiltinResult<Vec<PlotSurfaceHostAction>> {
503 SURFACES.with(|slot| {
504 let mut map = slot.borrow_mut();
505 let entry = map.get_mut(&surface_id).ok_or_else(|| {
506 web_error(format!(
507 "Plotting surface {surface_id} not registered. Call createPlotSurface() first."
508 ))
509 })?;
510 Ok(entry
511 .renderer
512 .take_host_actions()
513 .into_iter()
514 .map(convert_host_action)
515 .collect())
516 })
517 }
518
519 pub fn render_current_scene(handle: u32) -> BuiltinResult<()> {
520 debug!("plot-web: render_current_scene(handle={handle})");
521 let needs_autobind = SURFACES.with(|slot| {
525 let map = slot.borrow();
526 !map.values()
527 .any(|entry| entry.bound_target == Some(BoundSurfaceTarget::Figure(handle)))
528 });
529 if needs_autobind {
530 let maybe_unbound_surface = SURFACES.with(|slot| {
531 let map = slot.borrow();
532 map.iter()
533 .filter_map(|(surface_id, entry)| {
534 if entry.bound_target.is_none() {
535 Some(*surface_id)
536 } else {
537 None
538 }
539 })
540 .min()
541 });
542 if let Some(surface_id) = maybe_unbound_surface {
543 let _ = bind_surface_to_figure_impl(surface_id, handle);
545 }
546 }
547
548 let surface_ids: Vec<u32> = SURFACES.with(|slot| {
550 slot.borrow()
551 .iter()
552 .filter_map(|(surface_id, entry)| {
553 if entry.bound_target == Some(BoundSurfaceTarget::Figure(handle)) {
554 Some(*surface_id)
555 } else {
556 None
557 }
558 })
559 .collect()
560 });
561 if surface_ids.is_empty() {
562 return Ok(());
564 }
565 for surface_id in surface_ids {
566 present_surface_impl(surface_id)?;
568 }
569 Ok(())
570 }
571
572 pub fn invalidate_surface_revisions() {
573 SURFACES.with(|slot| {
574 let mut map = slot.borrow_mut();
575 for entry in map.values_mut() {
576 entry.last_revision = None;
577 }
578 });
579 }
580
581 pub(super) use runmat_plot::web::WebRenderer as RendererType;
583
584 fn convert_host_action(
585 action: runmat_plot::web::WebSurfaceHostAction,
586 ) -> PlotSurfaceHostAction {
587 match action {
588 runmat_plot::web::WebSurfaceHostAction::CreateFeaStudy => {
589 PlotSurfaceHostAction::CreateFeaStudy
590 }
591 }
592 }
593}
594
595#[cfg(not(all(target_arch = "wasm32", feature = "plot-web")))]
596pub(crate) mod wasm {
597 use super::*;
598
599 pub struct RendererPlaceholder;
600
601 pub(super) fn install_surface_impl(
602 _surface_id: u32,
603 _renderer: RendererPlaceholder,
604 ) -> BuiltinResult<()> {
605 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
606 }
607
608 pub(super) fn detach_surface_impl(_surface_id: u32) {}
609
610 pub(super) fn clear_closed_figure_surfaces_impl(_handle: u32) -> BuiltinResult<()> {
611 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
612 }
613
614 pub fn web_renderer_ready() -> bool {
615 false
616 }
617
618 pub(super) use RendererPlaceholder as RendererType;
619
620 pub(super) fn resize_surface_impl(
621 _surface_id: u32,
622 _width: u32,
623 _height: u32,
624 _pixels_per_point: f32,
625 ) -> BuiltinResult<()> {
626 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
627 }
628
629 pub fn render_current_scene(_handle: u32) -> BuiltinResult<()> {
630 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
631 }
632
633 pub fn invalidate_surface_revisions() {}
634
635 pub(super) fn bind_surface_to_figure_impl(_surface_id: u32, _handle: u32) -> BuiltinResult<()> {
636 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
637 }
638
639 pub(super) fn present_surface_impl(_surface_id: u32) -> BuiltinResult<()> {
640 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
641 }
642
643 pub(super) fn present_figure_on_surface_impl(
644 _surface_id: u32,
645 _handle: u32,
646 ) -> BuiltinResult<()> {
647 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
648 }
649
650 pub(super) fn present_geometry_scene_on_surface_impl(
651 _surface_id: u32,
652 _handle: u32,
653 _scene: runmat_plot::GeometryScene,
654 ) -> BuiltinResult<()> {
655 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
656 }
657
658 pub(super) fn set_geometry_scene_presentation_impl(
659 _surface_id: u32,
660 _presentation: runmat_plot::GeometryScenePresentation,
661 ) -> BuiltinResult<()> {
662 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
663 }
664
665 pub(super) fn pick_geometry_scene_region_impl(
666 _surface_id: u32,
667 _x: f32,
668 _y: f32,
669 ) -> BuiltinResult<Option<runmat_plot::GeometryScenePickResult>> {
670 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
671 }
672
673 pub(super) fn fit_surface_extents_impl(_surface_id: u32) -> BuiltinResult<()> {
674 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
675 }
676
677 pub(super) fn reset_surface_camera_impl(_surface_id: u32) -> BuiltinResult<()> {
678 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
679 }
680
681 pub(super) fn get_surface_camera_state_impl(
682 _surface_id: u32,
683 ) -> BuiltinResult<PlotSurfaceCameraState> {
684 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
685 }
686
687 pub(super) fn set_surface_camera_state_impl(
688 _surface_id: u32,
689 _state: PlotSurfaceCameraState,
690 ) -> BuiltinResult<()> {
691 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
692 }
693
694 pub(super) fn set_theme_config_impl(
695 _theme: runmat_plot::styling::PlotThemeConfig,
696 ) -> BuiltinResult<()> {
697 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
698 }
699
700 pub(super) fn current_theme_config_impl() -> runmat_plot::styling::PlotThemeConfig {
701 runmat_plot::styling::PlotThemeConfig::default()
702 }
703
704 pub(super) fn take_surface_host_actions_impl(
705 _surface_id: u32,
706 ) -> BuiltinResult<Vec<PlotSurfaceHostAction>> {
707 Err(web_error(ERR_PLOTTING_UNAVAILABLE))
708 }
709}
710
711pub use wasm::invalidate_surface_revisions;
712pub use wasm::render_current_scene;
713pub use wasm::web_renderer_ready;
714
715pub fn install_surface(surface_id: u32, renderer: wasm::RendererType) -> BuiltinResult<()> {
716 wasm::install_surface_impl(surface_id, renderer)
717}
718
719pub fn detach_surface(surface_id: u32) {
720 wasm::detach_surface_impl(surface_id)
721}
722
723pub fn clear_closed_figure_surfaces(handle: u32) -> BuiltinResult<()> {
724 wasm::clear_closed_figure_surfaces_impl(handle)
725}
726
727pub fn resize_surface(
728 surface_id: u32,
729 width: u32,
730 height: u32,
731 pixels_per_point: f32,
732) -> BuiltinResult<()> {
733 wasm::resize_surface_impl(surface_id, width, height, pixels_per_point)
734}
735
736pub fn bind_surface_to_figure(surface_id: u32, handle: u32) -> BuiltinResult<()> {
737 wasm::bind_surface_to_figure_impl(surface_id, handle)
738}
739
740pub fn present_surface(surface_id: u32) -> BuiltinResult<()> {
741 wasm::present_surface_impl(surface_id)
742}
743
744#[cfg(all(target_arch = "wasm32", feature = "plot-web"))]
745pub fn handle_plot_surface_event(
746 surface_id: u32,
747 event: runmat_plot::core::PlotEvent,
748) -> BuiltinResult<()> {
749 wasm::handle_surface_event_impl(surface_id, event)
750}
751
752pub fn present_figure_on_surface(surface_id: u32, handle: u32) -> BuiltinResult<()> {
753 wasm::present_figure_on_surface_impl(surface_id, handle)
754}
755
756pub fn present_geometry_scene_on_surface(
757 surface_id: u32,
758 handle: u32,
759 scene: runmat_plot::GeometryScene,
760) -> BuiltinResult<()> {
761 wasm::present_geometry_scene_on_surface_impl(surface_id, handle, scene)
762}
763
764pub fn set_geometry_scene_presentation(
765 surface_id: u32,
766 presentation: runmat_plot::GeometryScenePresentation,
767) -> BuiltinResult<()> {
768 wasm::set_geometry_scene_presentation_impl(surface_id, presentation)
769}
770
771pub fn pick_geometry_scene_region(
772 surface_id: u32,
773 x: f32,
774 y: f32,
775) -> BuiltinResult<Option<runmat_plot::GeometryScenePickResult>> {
776 wasm::pick_geometry_scene_region_impl(surface_id, x, y)
777}
778
779pub fn fit_surface_extents(surface_id: u32) -> BuiltinResult<()> {
780 wasm::fit_surface_extents_impl(surface_id)
781}
782
783pub fn reset_surface_camera(surface_id: u32) -> BuiltinResult<()> {
784 wasm::reset_surface_camera_impl(surface_id)
785}
786
787pub fn get_surface_camera_state(surface_id: u32) -> BuiltinResult<PlotSurfaceCameraState> {
788 wasm::get_surface_camera_state_impl(surface_id)
789}
790
791pub fn set_surface_camera_state(
792 surface_id: u32,
793 state: PlotSurfaceCameraState,
794) -> BuiltinResult<()> {
795 wasm::set_surface_camera_state_impl(surface_id, state)
796}
797
798pub fn take_surface_host_actions(surface_id: u32) -> BuiltinResult<Vec<PlotSurfaceHostAction>> {
799 wasm::take_surface_host_actions_impl(surface_id)
800}
801
802pub fn set_plot_theme_config(theme: runmat_plot::styling::PlotThemeConfig) -> BuiltinResult<()> {
803 wasm::set_theme_config_impl(theme)
804}
805
806pub fn current_plot_theme_config() -> runmat_plot::styling::PlotThemeConfig {
807 wasm::current_theme_config_impl()
808}
809
810#[cfg(all(target_arch = "wasm32", feature = "plot-web"))]
811fn convert_camera_state(state: runmat_plot::web::PlotSurfaceCameraState) -> PlotSurfaceCameraState {
812 PlotSurfaceCameraState {
813 active_axes: state.active_axes,
814 axes: state
815 .axes
816 .into_iter()
817 .map(|camera| PlotCameraState {
818 position: camera.position,
819 target: camera.target,
820 up: camera.up,
821 zoom: camera.zoom,
822 aspect_ratio: camera.aspect_ratio,
823 projection: match camera.projection {
824 runmat_plot::web::PlotCameraProjection::Perspective { fov, near, far } => {
825 PlotCameraProjection::Perspective { fov, near, far }
826 }
827 runmat_plot::web::PlotCameraProjection::Orthographic {
828 left,
829 right,
830 bottom,
831 top,
832 near,
833 far,
834 } => PlotCameraProjection::Orthographic {
835 left,
836 right,
837 bottom,
838 top,
839 near,
840 far,
841 },
842 },
843 })
844 .collect(),
845 }
846}
847
848#[cfg(all(target_arch = "wasm32", feature = "plot-web"))]
849fn convert_camera_state_back(
850 state: PlotSurfaceCameraState,
851) -> runmat_plot::web::PlotSurfaceCameraState {
852 runmat_plot::web::PlotSurfaceCameraState {
853 active_axes: state.active_axes,
854 axes: state
855 .axes
856 .into_iter()
857 .map(|camera| runmat_plot::web::PlotCameraState {
858 position: camera.position,
859 target: camera.target,
860 up: camera.up,
861 zoom: camera.zoom,
862 aspect_ratio: camera.aspect_ratio,
863 projection: match camera.projection {
864 PlotCameraProjection::Perspective { fov, near, far } => {
865 runmat_plot::web::PlotCameraProjection::Perspective { fov, near, far }
866 }
867 PlotCameraProjection::Orthographic {
868 left,
869 right,
870 bottom,
871 top,
872 near,
873 far,
874 } => runmat_plot::web::PlotCameraProjection::Orthographic {
875 left,
876 right,
877 bottom,
878 top,
879 near,
880 far,
881 },
882 },
883 })
884 .collect(),
885 }
886}
887
888