optionstratlib_visualization/visualization/
surfaces.rs1use crate::visualization::{Graph, GraphData, PlotBuilder, Plottable, Surface3D};
52use optionstratlib_math::surfaces::Surface;
53
54impl Graph for Surface {
58 fn graph_data(&self) -> GraphData {
59 GraphData::GraphSurface(Surface3D {
60 x: self.points.iter().map(|p| p.x).collect(),
61 y: self.points.iter().map(|p| p.y).collect(),
62 z: self.points.iter().map(|p| p.z).collect(),
63 name: "Surface".to_string(),
64 })
65 }
66}
67
68impl Plottable for Surface {
70 fn plot(&self) -> PlotBuilder<Self>
71 where
72 Self: Sized,
73 {
74 PlotBuilder {
75 data: self.clone(),
76 options: self.graph_config(),
77 }
78 }
79}
80
81#[cfg(test)]
82mod tests_extended {
83 use super::*;
84 use crate::visualization::{GraphConfig, GraphData, Series2D, TraceMode};
85 use optionstratlib_math::surfaces::Point3D;
86 use rust_decimal_macros::dec;
87 use std::any::{Any, TypeId};
88 use std::collections::BTreeSet;
89
90 #[derive(Clone)]
91 struct MockChart {
92 pub x_desc: String,
93 pub y_desc: String,
94 }
95
96 impl MockChart {
97 pub fn new() -> Self {
98 MockChart {
99 x_desc: String::new(),
100 y_desc: String::new(),
101 }
102 }
103
104 pub fn configure_mesh(&mut self) -> &mut Self {
105 self
106 }
107
108 pub fn x_label_formatter(&mut self, _formatter: &dyn Fn(f64) -> String) -> &mut Self {
109 self
110 }
111
112 pub fn y_label_formatter(&mut self, _formatter: &dyn Fn(f64) -> String) -> &mut Self {
113 self
114 }
115
116 pub fn x_desc(&mut self, desc: &str) -> &mut Self {
117 self.x_desc = desc.to_string();
118 self
119 }
120
121 pub fn y_desc(&mut self, desc: &str) -> &mut Self {
122 self.y_desc = desc.to_string();
123 self
124 }
125 }
126
127 impl Graph for MockChart {
128 fn graph_data(&self) -> GraphData {
129 GraphData::Series(Series2D {
130 x: vec![dec!(1.0), dec!(2.0)],
131 y: vec![dec!(3.0), dec!(4.0)],
132 name: "Test Series".to_string(),
133 mode: TraceMode::Lines,
134 line_color: None,
135 line_width: None,
136 })
137 }
138
139 fn graph_config(&self) -> GraphConfig {
140 GraphConfig::default()
141 }
142 }
143
144 impl Plottable for MockChart {
145 fn plot(&self) -> PlotBuilder<Self>
146 where
147 Self: Sized + Graph,
148 {
149 PlotBuilder {
150 data: self.clone(),
151 options: self.graph_config(),
152 }
153 }
154 }
155
156 #[test]
157 fn test_configure_chart_mesh() {
158 let mut chart = MockChart::new();
159 chart
160 .configure_mesh()
161 .x_label_formatter(&|v| format!("{v:.2}"))
162 .y_label_formatter(&|v| format!("{v:.2}"))
163 .x_desc("X-axis")
164 .y_desc("Y-axis");
165 assert_eq!(chart.x_desc, "X-axis");
166 assert_eq!(chart.y_desc, "Y-axis");
167 }
168
169 #[test]
170 fn test_plot() {
171 let mut chart = MockChart::new();
172 chart
173 .configure_mesh()
174 .x_label_formatter(&|v| format!("{v:.2}"))
175 .y_label_formatter(&|v| format!("{v:.2}"))
176 .x_desc("X-axis")
177 .y_desc("Y-axis");
178 assert_eq!(chart.x_desc, "X-axis");
179 assert_eq!(chart.y_desc, "Y-axis");
180
181 let plot = chart.plot();
182 assert_eq!(plot.data.graph_data(), chart.graph_data());
183 assert_eq!(plot.options, chart.graph_config());
184 assert_eq!(plot.data.graph_config(), chart.graph_config());
185 assert_eq!(plot.options.width, 1280);
186 assert_eq!(plot.options.height, 720);
187 assert_eq!(plot.options.title, "Graph");
188 assert_eq!(plot.options.x_label, None);
189 assert_eq!(plot.options.y_label, None);
190 assert_eq!(plot.options.z_label, None);
191 assert_eq!(plot.data.x_desc, "X-axis");
192 assert_eq!(plot.data.y_desc, "Y-axis");
193 }
194 #[test]
195 fn test_plot_surface() {
196 let points = BTreeSet::from_iter(vec![
197 Point3D::new(dec!(0.0), dec!(0.0), dec!(1.0)),
198 Point3D::new(dec!(0.5), dec!(0.0), dec!(1.0)),
199 Point3D::new(dec!(1.0), dec!(0.0), dec!(1.0)),
200 Point3D::new(dec!(0.0), dec!(0.5), dec!(1.0)),
201 Point3D::new(dec!(0.5), dec!(0.5), dec!(1.0)),
202 Point3D::new(dec!(1.0), dec!(0.5), dec!(1.0)),
203 Point3D::new(dec!(0.0), dec!(1.0), dec!(1.0)),
204 Point3D::new(dec!(0.5), dec!(1.0), dec!(1.0)),
205 Point3D::new(dec!(1.0), dec!(1.0), dec!(1.0)),
206 ]);
207 let surface = Surface::new(points);
208 let plot = surface.plot();
209 assert_eq!(plot.type_id(), TypeId::of::<PlotBuilder<Surface>>());
211 assert_eq!(plot.data.graph_data(), surface.graph_data());
212 assert_eq!(plot.options, surface.graph_config());
213 assert_eq!(plot.data.graph_config(), surface.graph_config());
214 assert_eq!(plot.options.width, 1280);
215 assert_eq!(plot.options.height, 720);
216 assert_eq!(plot.options.title, "Graph");
217 assert_eq!(plot.options.x_label, None);
218 assert_eq!(plot.options.y_label, None);
219 assert_eq!(plot.options.z_label, None);
220 }
221
222 #[test]
225 fn test_surface_graph_data_is_graph_surface() {
226 let points = BTreeSet::from_iter(vec![
227 Point3D::new(dec!(0.0), dec!(0.0), dec!(0.0)),
228 Point3D::new(dec!(0.0), dec!(1.0), dec!(1.0)),
229 Point3D::new(dec!(1.0), dec!(0.0), dec!(1.0)),
230 Point3D::new(dec!(1.0), dec!(1.0), dec!(2.0)),
231 Point3D::new(dec!(0.5), dec!(0.5), dec!(1.5)),
232 ]);
233 let surface = Surface::new(points);
234 let graph_data = surface.graph_data();
235 assert!(matches!(
236 graph_data,
237 GraphData::GraphSurface(Surface3D { .. })
238 ));
239 }
240}