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