plotlars_core/plots/
image.rs1use bon::bon;
2
3use crate::{
4 components::{Axis, Text},
5 ir::layout::LayoutIR,
6 ir::trace::{ImageIR, TraceIR},
7};
8
9#[derive(Clone)]
50#[allow(dead_code)]
51pub struct Image {
52 traces: Vec<TraceIR>,
53 layout: LayoutIR,
54}
55
56#[bon]
57impl Image {
58 #[builder(on(String, into), on(Text, into))]
59 pub fn new(
60 path: &str,
61 plot_title: Option<Text>,
62 x_title: Option<Text>,
63 y_title: Option<Text>,
64 x_axis: Option<&Axis>,
65 y_axis: Option<&Axis>,
66 ) -> Self {
67 let ir_trace = Self::create_ir_trace(path);
69 let traces = vec![ir_trace];
70 let layout = LayoutIR {
71 title: plot_title.clone(),
72 x_title: x_title.clone(),
73 y_title: y_title.clone(),
74 y2_title: None,
75 z_title: None,
76 legend_title: None,
77 legend: None,
78 dimensions: None,
79 bar_mode: None,
80 box_mode: None,
81 box_gap: None,
82 margin_bottom: None,
83 axes_2d: Some(crate::ir::layout::Axes2dIR {
84 x_axis: x_axis.cloned(),
85 y_axis: y_axis.cloned(),
86 y2_axis: None,
87 }),
88 scene_3d: None,
89 polar: None,
90 mapbox: None,
91 grid: None,
92 annotations: vec![],
93 };
94
95 Self { traces, layout }
97 }
98}
99
100#[bon]
101impl Image {
102 #[builder(
103 start_fn = try_builder,
104 finish_fn = try_build,
105 builder_type = ImageTryBuilder,
106 on(String, into),
107 on(Text, into),
108 )]
109 pub fn try_new(
110 path: &str,
111 plot_title: Option<Text>,
112 x_title: Option<Text>,
113 y_title: Option<Text>,
114 x_axis: Option<&Axis>,
115 y_axis: Option<&Axis>,
116 ) -> Result<Self, crate::io::PlotlarsError> {
117 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
118 Self::__orig_new(path, plot_title, x_title, y_title, x_axis, y_axis)
119 }))
120 .map_err(|panic| {
121 let msg = panic
122 .downcast_ref::<String>()
123 .cloned()
124 .or_else(|| panic.downcast_ref::<&str>().map(|s| s.to_string()))
125 .unwrap_or_else(|| "unknown error".to_string());
126 crate::io::PlotlarsError::PlotBuild { message: msg }
127 })
128 }
129}
130
131impl Image {
132 fn create_ir_trace(path: &str) -> TraceIR {
133 let im: image::ImageBuffer<image::Rgb<u8>, Vec<u8>> =
134 image::open(path).unwrap().into_rgb8();
135
136 let (width, height) = im.dimensions();
137 let mut pixels = vec![vec![[0u8; 3]; width as usize]; height as usize];
138
139 for (x, y, pixel) in im.enumerate_pixels() {
140 pixels[y as usize][x as usize] = [pixel[0], pixel[1], pixel[2]];
141 }
142
143 TraceIR::Image(ImageIR { pixels })
144 }
145}
146
147impl crate::Plot for Image {
148 fn ir_traces(&self) -> &[TraceIR] {
149 &self.traces
150 }
151
152 fn ir_layout(&self) -> &LayoutIR {
153 &self.layout
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160 use crate::Plot;
161
162 fn image_path() -> String {
163 let manifest = env!("CARGO_MANIFEST_DIR");
164 format!("{}/../../data/image.png", manifest)
165 }
166
167 #[test]
168 fn test_basic_one_trace() {
169 let plot = Image::builder().path(&image_path()).build();
170 assert_eq!(plot.ir_traces().len(), 1);
171 }
172
173 #[test]
174 fn test_trace_variant() {
175 let plot = Image::builder().path(&image_path()).build();
176 assert!(matches!(plot.ir_traces()[0], TraceIR::Image(_)));
177 }
178}