runmat_runtime/builtins/plotting/
mod.rs1#[path = "core/common.rs"]
4pub(crate) mod common;
5#[path = "core/context.rs"]
6pub mod context;
7#[path = "core/engine.rs"]
8pub(crate) mod engine;
9#[path = "core/gpu_helpers.rs"]
10pub(crate) mod gpu_helpers;
11#[path = "core/perf.rs"]
12pub(crate) mod perf;
13#[path = "core/point.rs"]
14pub(crate) mod point;
15#[path = "core/properties.rs"]
16pub(crate) mod properties;
17#[path = "core/state.rs"]
18pub(crate) mod state;
19#[path = "core/style.rs"]
20pub(crate) mod style;
21#[path = "core/web.rs"]
22pub mod web;
23
24#[path = "type_resolvers.rs"]
25pub(crate) mod type_resolvers;
26
27#[path = "ops/area.rs"]
28pub(crate) mod area;
29#[path = "ops/bar.rs"]
30pub(crate) mod bar;
31#[path = "ops/caxis.rs"]
32pub(crate) mod caxis;
33#[path = "ops/clf.rs"]
34pub(crate) mod clf;
35#[path = "ops/clim.rs"]
36pub(crate) mod clim;
37#[path = "ops/close.rs"]
38pub(crate) mod close;
39#[path = "ops/cmds.rs"]
40pub(crate) mod cmds;
41#[path = "ops/contour.rs"]
42pub(crate) mod contour;
43#[path = "ops/contour3.rs"]
44pub(crate) mod contour3;
45#[path = "ops/contourf.rs"]
46pub(crate) mod contourf;
47#[path = "ops/drawnow.rs"]
48pub(crate) mod drawnow;
49#[path = "ops/errorbar.rs"]
50pub(crate) mod errorbar;
51#[path = "ops/figure.rs"]
52pub(crate) mod figure;
53#[path = "ops/fill3.rs"]
54pub(crate) mod fill3;
55#[path = "ops/gca.rs"]
56pub(crate) mod gca;
57#[path = "ops/gcf.rs"]
58pub(crate) mod gcf;
59#[path = "ops/get.rs"]
60pub(crate) mod get;
61#[path = "ops/heatmap.rs"]
62pub(crate) mod heatmap;
63#[path = "ops/hist.rs"]
64pub mod hist;
65#[path = "ops/histogram.rs"]
66pub(crate) mod histogram;
67#[path = "ops/hold.rs"]
68pub(crate) mod hold;
69#[path = "ops/image.rs"]
70pub(crate) mod image;
71#[path = "ops/imagesc.rs"]
72pub(crate) mod imagesc;
73#[path = "ops/imshow.rs"]
74pub(crate) mod imshow;
75#[path = "ops/isgraphics.rs"]
76pub(crate) mod isgraphics;
77#[path = "ops/ishandle.rs"]
78pub(crate) mod ishandle;
79#[path = "ops/legend.rs"]
80pub(crate) mod legend;
81#[path = "ops/loglog.rs"]
82pub(crate) mod loglog;
83#[path = "ops/mesh.rs"]
84pub(crate) mod mesh;
85#[path = "ops/meshc.rs"]
86pub(crate) mod meshc;
87#[path = "ops/common/mod.rs"]
88pub(crate) mod op_common;
89#[path = "ops/patch.rs"]
90pub(crate) mod patch;
91#[path = "ops/pie.rs"]
92pub(crate) mod pie;
93#[path = "ops/plot.rs"]
94pub(crate) mod plot;
95#[path = "ops/plot3.rs"]
96pub(crate) mod plot3;
97#[path = "ops/polarplot.rs"]
98pub(crate) mod polarplot;
99#[path = "ops/print.rs"]
100pub(crate) mod print;
101#[path = "ops/quiver.rs"]
102pub(crate) mod quiver;
103#[path = "ops/scatter.rs"]
104pub(crate) mod scatter;
105#[path = "ops/scatter3.rs"]
106pub(crate) mod scatter3;
107#[path = "ops/scatterplot.rs"]
108pub(crate) mod scatterplot;
109#[path = "ops/semilogx.rs"]
110pub(crate) mod semilogx;
111#[path = "ops/semilogy.rs"]
112pub(crate) mod semilogy;
113#[path = "ops/set.rs"]
114pub(crate) mod set;
115#[path = "ops/sgtitle.rs"]
116pub(crate) mod sgtitle;
117#[path = "ops/stairs.rs"]
118pub(crate) mod stairs;
119#[path = "ops/stem.rs"]
120pub(crate) mod stem;
121#[path = "ops/subplot.rs"]
122pub(crate) mod subplot;
123#[path = "ops/suptitle.rs"]
124pub(crate) mod suptitle;
125#[path = "ops/surf.rs"]
126pub(crate) mod surf;
127#[path = "ops/surfc.rs"]
128pub(crate) mod surfc;
129#[path = "ops/text.rs"]
130pub(crate) mod text;
131#[path = "ops/title.rs"]
132pub(crate) mod title;
133#[path = "ops/view.rs"]
134pub(crate) mod view;
135#[path = "ops/xlabel.rs"]
136pub(crate) mod xlabel;
137#[path = "ops/xlim.rs"]
138pub(crate) mod xlim;
139#[path = "ops/xline.rs"]
140pub(crate) mod xline;
141#[path = "ops/ylabel.rs"]
142pub(crate) mod ylabel;
143#[path = "ops/ylim.rs"]
144pub(crate) mod ylim;
145#[path = "ops/yline.rs"]
146pub(crate) mod yline;
147#[path = "ops/zlabel.rs"]
148pub(crate) mod zlabel;
149#[path = "ops/zlim.rs"]
150pub(crate) mod zlim;
151
152pub use perf::{
153 set_scatter_target_points, set_scene_export_budget_bytes, set_surface_vertex_budget,
154};
155pub use properties::resolve_plot_handle;
156pub use state::{
157 clear_figure, clone_figure, close_figure, configure_subplot, current_axes_state,
158 current_figure_handle, figure_handles, import_figure, install_figure_observer,
159 new_figure_handle, record_recent_figure, reset_hold_state_for_run, reset_plot_state,
160 reset_recent_figures, select_axes_for_figure, select_figure, set_hold, take_recent_figures,
161 FigureAxesState, FigureError, FigureEventKind, FigureEventView, FigureHandle, HoldMode,
162};
163#[cfg(all(feature = "plot-core", target_arch = "wasm32"))]
164use std::cell::RefCell;
165use std::collections::HashMap;
166#[cfg(feature = "plot-core")]
167use std::sync::atomic::{AtomicU64, Ordering};
168use std::sync::{Mutex, OnceLock};
169use web::present_figure_on_surface as web_present_figure_on_surface;
170use web::present_geometry_scene_on_surface as web_present_geometry_scene_on_surface;
171pub use web::{
172 bind_surface_to_figure, clear_closed_figure_surfaces, detach_surface, fit_surface_extents,
173 get_surface_camera_state, install_surface, invalidate_surface_revisions,
174 pick_geometry_scene_region, present_surface, render_current_scene, reset_surface_camera,
175 resize_surface, set_geometry_scene_presentation, set_plot_theme_config,
176 set_surface_camera_state, web_renderer_ready, PlotCameraProjection, PlotCameraState,
177 PlotSurfaceCameraState,
178};
179
180#[doc(hidden)]
181pub use state::PlotTestLockGuard;
182
183#[doc(hidden)]
184pub fn lock_plot_test_context() -> PlotTestLockGuard {
185 state::lock_plot_test_registry()
186}
187
188#[derive(Debug, Clone, Copy, PartialEq, Eq)]
189pub enum RuntimePlottingMode {
190 Auto,
191 Interactive,
192 Static,
193}
194
195#[cfg(feature = "gui")]
196pub fn set_runtime_plotting_mode(mode: RuntimePlottingMode) {
197 let mapped = match mode {
198 RuntimePlottingMode::Auto => engine::native::RuntimePlottingMode::Auto,
199 RuntimePlottingMode::Interactive => engine::native::RuntimePlottingMode::Interactive,
200 RuntimePlottingMode::Static => engine::native::RuntimePlottingMode::Static,
201 };
202 engine::native::set_runtime_plotting_mode(mapped);
203}
204
205#[cfg(not(feature = "gui"))]
206pub fn set_runtime_plotting_mode(_mode: RuntimePlottingMode) {}
207
208#[cfg(all(target_arch = "wasm32", feature = "plot-web"))]
209pub use web::{handle_plot_surface_event, take_surface_host_actions, PlotSurfaceHostAction};
210
211pub(crate) fn plotting_error(builtin: &str, message: impl Into<String>) -> crate::RuntimeError {
212 crate::build_runtime_error(message)
213 .with_builtin(builtin)
214 .build()
215}
216
217pub(crate) fn plotting_error_with_source(
218 builtin: &str,
219 message: impl Into<String>,
220 source: impl std::error::Error + Send + Sync + 'static,
221) -> crate::RuntimeError {
222 crate::build_runtime_error(message)
223 .with_builtin(builtin)
224 .with_source(source)
225 .build()
226}
227
228#[cfg(feature = "plot-core")]
229pub async fn export_figure_scene(handle: FigureHandle) -> crate::BuiltinResult<Option<Vec<u8>>> {
230 export_figure_scene_with_policy(
231 handle,
232 runmat_plot::event::resolve_scene_export_policy(Some(perf::scene_export_budget_bytes())),
233 )
234 .await
235}
236
237#[cfg(feature = "plot-core")]
238pub async fn export_figure_scene_with_policy(
239 handle: FigureHandle,
240 policy: runmat_plot::event::SceneExportPolicy,
241) -> crate::BuiltinResult<Option<Vec<u8>>> {
242 let Some(figure) = clone_figure(handle) else {
243 return Ok(None);
244 };
245 let scene = runmat_plot::event::FigureScene::capture_for_export(&figure, policy)
246 .await
247 .map_err(|err| {
248 crate::replay_error_with_source(
249 crate::ReplayErrorKind::ExportRejected,
250 "invalid figure scene content",
251 err,
252 )
253 })?;
254 crate::replay::export_figure_scene_payload_with_limits(
255 &scene,
256 crate::replay::limits::ReplayLimits {
257 max_scene_payload_bytes: policy.max_scene_bytes,
258 ..crate::replay::limits::ReplayLimits::default()
259 },
260 )
261 .map(Some)
262}
263
264#[cfg(feature = "plot-core")]
265pub fn import_figure_scene(bytes: &[u8]) -> crate::BuiltinResult<Option<FigureHandle>> {
266 let scene = crate::replay::import_figure_scene_payload(bytes)?;
267 let figure = scene.into_figure().map_err(|err| {
268 crate::replay_error_with_source(
269 crate::ReplayErrorKind::ImportRejected,
270 "invalid figure scene content",
271 std::io::Error::new(std::io::ErrorKind::InvalidData, err),
272 )
273 })?;
274 let handle = import_figure(figure);
275 register_imported_figure(handle.as_u32());
276 Ok(Some(handle))
277}
278
279#[cfg(feature = "plot-core")]
280pub fn import_geometry_scene_payload(bytes: &[u8]) -> crate::BuiltinResult<Option<u32>> {
281 let scene = crate::replay::import_figure_scene_payload(bytes)?;
282 let hash = geometry_scene_payload_hash(bytes);
283 let scene_id = format!("geometry-scene-payload:{hash:016x}");
284 let scene = scene.into_geometry_scene(scene_id, hash).map_err(|err| {
285 crate::replay_error_with_source(
286 crate::ReplayErrorKind::ImportRejected,
287 "invalid geometry scene content",
288 std::io::Error::new(std::io::ErrorKind::InvalidData, err),
289 )
290 })?;
291 import_geometry_scene(scene).map(Some)
292}
293
294#[cfg(feature = "plot-core")]
295pub async fn import_figure_scene_async(bytes: &[u8]) -> crate::BuiltinResult<Option<FigureHandle>> {
296 let scene = crate::replay::import_figure_scene_payload_async(bytes).await?;
297 let figure = scene.into_figure().map_err(|err| {
298 crate::replay_error_with_source(
299 crate::ReplayErrorKind::ImportRejected,
300 "invalid figure scene content",
301 std::io::Error::new(std::io::ErrorKind::InvalidData, err),
302 )
303 })?;
304 let handle = import_figure(figure);
305 register_imported_figure(handle.as_u32());
306 Ok(Some(handle))
307}
308
309#[cfg(feature = "plot-core")]
310pub async fn import_figure_scene_from_path_async(
311 path: &str,
312) -> crate::BuiltinResult<Option<FigureHandle>> {
313 let bytes = runmat_filesystem::read_async(path).await.map_err(|err| {
314 crate::replay_error_with_source(
315 crate::ReplayErrorKind::ImportRejected,
316 format!("failed to read figure scene payload '{path}'"),
317 err,
318 )
319 })?;
320 import_figure_scene_async(&bytes).await
321}
322
323#[cfg(feature = "plot-core")]
324pub fn import_geometry_scene(scene: runmat_plot::GeometryScene) -> crate::BuiltinResult<u32> {
325 let handle = NEXT_GEOMETRY_SCENE_HANDLE.fetch_add(1, Ordering::Relaxed) as u32;
326 insert_geometry_scene(handle, scene)?;
327 register_imported_geometry_scene(handle);
328 Ok(handle)
329}
330
331#[cfg(feature = "plot-core")]
332pub fn clone_geometry_scene(handle: u32) -> Option<runmat_plot::GeometryScene> {
333 get_geometry_scene(handle)
334}
335
336#[cfg(feature = "plot-core")]
337pub fn append_geometry_scene_chunks(
338 handle: u32,
339 chunks: Vec<runmat_plot::GeometrySceneChunk>,
340 overlay: Option<runmat_plot::GeometrySceneOverlay>,
341) -> crate::BuiltinResult<()> {
342 with_geometry_scene_mut(handle, |scene| {
343 if !chunks.is_empty() {
344 scene.append_chunks(chunks);
345 }
346 if let Some(overlay) = overlay {
347 let merged = merge_geometry_scene_overlay(scene, overlay);
348 scene.set_overlay(merged);
349 }
350 })
351}
352
353#[cfg(feature = "plot-core")]
354pub fn close_geometry_scene(handle: u32) -> bool {
355 remove_geometry_scene(handle)
356}
357
358#[cfg(feature = "plot-core")]
359pub fn export_geometry_scene(handle: u32) -> crate::BuiltinResult<Option<Vec<u8>>> {
360 let Some(scene) = clone_geometry_scene(handle) else {
361 return Ok(None);
362 };
363 let scene = runmat_plot::event::FigureScene::from_geometry_scene(&scene);
364 crate::replay::export_figure_scene_payload(&scene).map(Some)
365}
366
367#[cfg(feature = "plot-core")]
368pub fn present_geometry_scene_on_surface(surface_id: u32, handle: u32) -> crate::BuiltinResult<()> {
369 let Some(scene) = clone_geometry_scene(handle) else {
370 return Err(crate::build_runtime_error(format!(
371 "geometry scene handle {handle} does not exist"
372 ))
373 .with_builtin("plot")
374 .build());
375 };
376 web_present_geometry_scene_on_surface(surface_id, handle, scene)?;
377 if take_imported_geometry_scene(handle) {
378 let _ = reset_surface_camera(surface_id);
379 }
380 Ok(())
381}
382
383pub fn present_figure_on_surface(surface_id: u32, handle: u32) -> crate::BuiltinResult<()> {
384 web_present_figure_on_surface(surface_id, handle)?;
385 if take_imported_figure(handle) {
386 let _ = reset_surface_camera(surface_id);
387 }
388 Ok(())
389}
390
391#[cfg(feature = "plot-core")]
392pub fn import_runtime_figure(figure: runmat_plot::plots::Figure) -> u32 {
393 let handle = state::import_figure(figure);
394 register_imported_figure(handle.as_u32());
395 handle.as_u32()
396}
397
398type ImportedFigureRegistry = Mutex<HashMap<u32, ()>>;
399#[cfg(all(feature = "plot-core", not(target_arch = "wasm32")))]
400type GeometrySceneRegistry = Mutex<HashMap<u32, runmat_plot::GeometryScene>>;
401#[cfg(feature = "plot-core")]
402type ImportedGeometrySceneRegistry = Mutex<HashMap<u32, ()>>;
403
404#[cfg(feature = "plot-core")]
405static NEXT_GEOMETRY_SCENE_HANDLE: AtomicU64 = AtomicU64::new(1);
406
407#[cfg(all(feature = "plot-core", target_arch = "wasm32"))]
408thread_local! {
409 static GEOMETRY_SCENE_REGISTRY: RefCell<HashMap<u32, runmat_plot::GeometryScene>> =
410 RefCell::new(HashMap::new());
411}
412
413#[cfg(all(feature = "plot-core", not(target_arch = "wasm32")))]
414fn geometry_scene_registry() -> &'static GeometrySceneRegistry {
415 static REGISTRY: OnceLock<GeometrySceneRegistry> = OnceLock::new();
416 REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
417}
418
419#[cfg(all(feature = "plot-core", not(target_arch = "wasm32")))]
420fn insert_geometry_scene(
421 handle: u32,
422 scene: runmat_plot::GeometryScene,
423) -> crate::BuiltinResult<()> {
424 let mut guard = geometry_scene_registry().lock().map_err(|_| {
425 crate::build_runtime_error("geometry scene registry lock poisoned")
426 .with_builtin("plot")
427 .build()
428 })?;
429 guard.insert(handle, scene);
430 Ok(())
431}
432
433#[cfg(all(feature = "plot-core", target_arch = "wasm32"))]
434fn insert_geometry_scene(
435 handle: u32,
436 scene: runmat_plot::GeometryScene,
437) -> crate::BuiltinResult<()> {
438 GEOMETRY_SCENE_REGISTRY.with(|registry| {
439 registry.borrow_mut().insert(handle, scene);
440 });
441 Ok(())
442}
443
444#[cfg(all(feature = "plot-core", not(target_arch = "wasm32")))]
445fn get_geometry_scene(handle: u32) -> Option<runmat_plot::GeometryScene> {
446 geometry_scene_registry().lock().ok()?.get(&handle).cloned()
447}
448
449#[cfg(all(feature = "plot-core", target_arch = "wasm32"))]
450fn get_geometry_scene(handle: u32) -> Option<runmat_plot::GeometryScene> {
451 GEOMETRY_SCENE_REGISTRY.with(|registry| registry.borrow().get(&handle).cloned())
452}
453
454#[cfg(all(feature = "plot-core", not(target_arch = "wasm32")))]
455fn with_geometry_scene_mut(
456 handle: u32,
457 update: impl FnOnce(&mut runmat_plot::GeometryScene),
458) -> crate::BuiltinResult<()> {
459 let mut guard = geometry_scene_registry().lock().map_err(|_| {
460 crate::build_runtime_error("geometry scene registry lock poisoned")
461 .with_builtin("plot")
462 .build()
463 })?;
464 let scene = guard.get_mut(&handle).ok_or_else(|| {
465 crate::build_runtime_error(format!("geometry scene handle {handle} does not exist"))
466 .with_builtin("plot")
467 .build()
468 })?;
469 update(scene);
470 Ok(())
471}
472
473#[cfg(all(feature = "plot-core", target_arch = "wasm32"))]
474fn with_geometry_scene_mut(
475 handle: u32,
476 update: impl FnOnce(&mut runmat_plot::GeometryScene),
477) -> crate::BuiltinResult<()> {
478 GEOMETRY_SCENE_REGISTRY.with(|registry| {
479 let mut registry = registry.borrow_mut();
480 let scene = registry.get_mut(&handle).ok_or_else(|| {
481 crate::build_runtime_error(format!("geometry scene handle {handle} does not exist"))
482 .with_builtin("plot")
483 .build()
484 })?;
485 update(scene);
486 Ok(())
487 })
488}
489
490#[cfg(all(feature = "plot-core", not(target_arch = "wasm32")))]
491fn remove_geometry_scene(handle: u32) -> bool {
492 geometry_scene_registry()
493 .lock()
494 .ok()
495 .and_then(|mut guard| guard.remove(&handle))
496 .is_some()
497}
498
499#[cfg(all(feature = "plot-core", target_arch = "wasm32"))]
500fn remove_geometry_scene(handle: u32) -> bool {
501 GEOMETRY_SCENE_REGISTRY.with(|registry| registry.borrow_mut().remove(&handle).is_some())
502}
503
504#[cfg(feature = "plot-core")]
505fn geometry_scene_payload_hash(bytes: &[u8]) -> u64 {
506 const FNV_OFFSET_BASIS: u64 = 0xcbf29ce484222325;
507 const FNV_PRIME: u64 = 0x100000001b3;
508 let mut hash = FNV_OFFSET_BASIS;
509 for byte in bytes {
510 hash ^= u64::from(*byte);
511 hash = hash.wrapping_mul(FNV_PRIME);
512 }
513 hash
514}
515
516#[cfg(feature = "plot-core")]
517fn merge_geometry_scene_overlay(
518 scene: &runmat_plot::GeometryScene,
519 incoming: runmat_plot::GeometrySceneOverlay,
520) -> runmat_plot::GeometrySceneOverlay {
521 let Some(mut current) = scene.overlay.clone() else {
522 let mut overlay = incoming;
523 overlay.vertex_count = scene.vertex_count();
524 overlay.triangle_count = scene.triangle_count();
525 return overlay;
526 };
527
528 current.status = incoming.status;
529 current.quality_label = incoming.quality_label.or(current.quality_label);
530 current.format = incoming.format.or(current.format);
531 current.source_label = incoming.source_label.or(current.source_label);
532 current.allow_create_fea_study =
533 current.allow_create_fea_study || incoming.allow_create_fea_study;
534 current.byte_count = incoming.byte_count.or(current.byte_count);
535 current.mesh_count = current.mesh_count.max(incoming.mesh_count);
536 current.vertex_count = scene.vertex_count();
537 current.triangle_count = scene.triangle_count();
538 current.progress_percent = incoming.progress_percent;
539
540 if current.assembly_nodes.is_empty() {
541 current.assembly_nodes = incoming.assembly_nodes;
542 }
543
544 merge_region_summaries(&mut current.regions, incoming.regions);
545 current.region_count = current.regions.len();
546 current.mapped_region_count = current.region_count;
547 merge_warnings(&mut current.warnings, incoming.warnings);
548 current
549}
550
551#[cfg(feature = "plot-core")]
552fn merge_region_summaries(
553 current: &mut Vec<runmat_plot::GeometrySceneRegionSummary>,
554 incoming: Vec<runmat_plot::GeometrySceneRegionSummary>,
555) {
556 let mut positions = HashMap::<String, usize>::with_capacity(current.len() + incoming.len());
557 for (index, region) in current.iter().enumerate() {
558 positions.insert(region.region_id.clone(), index);
559 }
560 for region in incoming {
561 if let Some(index) = positions.get(®ion.region_id).copied() {
562 current[index].triangle_count = current[index]
563 .triangle_count
564 .saturating_add(region.triangle_count);
565 } else {
566 positions.insert(region.region_id.clone(), current.len());
567 current.push(region);
568 }
569 }
570}
571
572#[cfg(feature = "plot-core")]
573fn merge_warnings(current: &mut Vec<String>, incoming: Vec<String>) {
574 for warning in incoming {
575 if !current.iter().any(|item| item == &warning) {
576 current.push(warning);
577 }
578 }
579}
580
581fn imported_figure_registry() -> &'static ImportedFigureRegistry {
582 static REGISTRY: OnceLock<ImportedFigureRegistry> = OnceLock::new();
583 REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
584}
585
586fn register_imported_figure(handle: u32) {
587 if let Ok(mut map) = imported_figure_registry().lock() {
588 map.insert(handle, ());
589 }
590}
591
592fn take_imported_figure(handle: u32) -> bool {
593 imported_figure_registry()
594 .lock()
595 .ok()
596 .and_then(|mut map| map.remove(&handle))
597 .is_some()
598}
599
600#[cfg(feature = "plot-core")]
601fn imported_geometry_scene_registry() -> &'static ImportedGeometrySceneRegistry {
602 static REGISTRY: OnceLock<ImportedGeometrySceneRegistry> = OnceLock::new();
603 REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
604}
605
606#[cfg(feature = "plot-core")]
607pub fn register_imported_geometry_scene(handle: u32) {
608 if let Ok(mut map) = imported_geometry_scene_registry().lock() {
609 map.insert(handle, ());
610 }
611}
612
613#[cfg(feature = "plot-core")]
614fn take_imported_geometry_scene(handle: u32) -> bool {
615 imported_geometry_scene_registry()
616 .lock()
617 .ok()
618 .and_then(|mut map| map.remove(&handle))
619 .is_some()
620}
621
622#[cfg(feature = "plot-core")]
623pub use engine::{
624 render_figure_png_bytes, render_figure_png_bytes_with_axes_cameras,
625 render_figure_png_bytes_with_camera, render_figure_rgba_bytes,
626 render_figure_rgba_bytes_with_axes_cameras, render_figure_rgba_bytes_with_camera,
627 render_figure_snapshot, render_figure_snapshot_with_camera_state,
628 render_geometry_scene_snapshot,
629};
630
631pub mod ops {
632 pub use super::hist;
633}
634
635#[cfg(test)]
636pub(crate) mod tests {
637 use super::state;
638
639 pub(crate) fn ensure_plot_test_env() {
640 state::disable_rendering_for_tests();
641 }
642
643 pub(crate) fn lock_plot_registry() -> state::PlotTestLockGuard {
644 state::lock_plot_test_registry()
645 }
646}