1 use std::cmp::Ordering; 2 3 use plotters_backend::DrawingBackend; 4 5 use crate::chart::ChartContext; 6 use crate::coord::{ 7 cartesian::Cartesian3d, 8 ranged1d::{KeyPointHint, Ranged}, 9 CoordTranslate, 10 }; 11 use crate::drawing::DrawingAreaErrorKind; 12 use crate::element::{EmptyElement, PathElement, Polygon, Text}; 13 use crate::style::{ 14 text_anchor::{HPos, Pos, VPos}, 15 ShapeStyle, TextStyle, 16 }; 17 18 use super::Coord3D; 19 20 pub(crate) struct KeyPoints3d<X: Ranged, Y: Ranged, Z: Ranged> { 21 pub(crate) x_points: Vec<X::ValueType>, 22 pub(crate) y_points: Vec<Y::ValueType>, 23 pub(crate) z_points: Vec<Z::ValueType>, 24 } 25 26 impl<'a, DB, X: Ranged, Y: Ranged, Z: Ranged> ChartContext<'a, DB, Cartesian3d<X, Y, Z>> 27 where 28 DB: DrawingBackend, 29 X::ValueType: Clone, 30 Y::ValueType: Clone, 31 Z::ValueType: Clone, 32 { get_key_points<XH: KeyPointHint, YH: KeyPointHint, ZH: KeyPointHint>( &self, x_hint: XH, y_hint: YH, z_hint: ZH, ) -> KeyPoints3d<X, Y, Z>33 pub(crate) fn get_key_points<XH: KeyPointHint, YH: KeyPointHint, ZH: KeyPointHint>( 34 &self, 35 x_hint: XH, 36 y_hint: YH, 37 z_hint: ZH, 38 ) -> KeyPoints3d<X, Y, Z> { 39 let coord = self.plotting_area().as_coord_spec(); 40 let x_points = coord.logic_x.key_points(x_hint); 41 let y_points = coord.logic_y.key_points(y_hint); 42 let z_points = coord.logic_z.key_points(z_hint); 43 KeyPoints3d { 44 x_points, 45 y_points, 46 z_points, 47 } 48 } 49 #[allow(clippy::type_complexity)] draw_axis_ticks( &mut self, axis: [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2], labels: &[( [Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3], String, )], tick_size: i32, style: ShapeStyle, font: TextStyle, ) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>>50 pub(crate) fn draw_axis_ticks( 51 &mut self, 52 axis: [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2], 53 labels: &[( 54 [Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3], 55 String, 56 )], 57 tick_size: i32, 58 style: ShapeStyle, 59 font: TextStyle, 60 ) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>> { 61 let coord = self.plotting_area().as_coord_spec(); 62 let begin = coord.translate(&Coord3D::build_coord([ 63 &axis[0][0], 64 &axis[0][1], 65 &axis[0][2], 66 ])); 67 let end = coord.translate(&Coord3D::build_coord([ 68 &axis[1][0], 69 &axis[1][1], 70 &axis[1][2], 71 ])); 72 let axis_dir = (end.0 - begin.0, end.1 - begin.1); 73 let (x_range, y_range) = self.plotting_area().get_pixel_range(); 74 let x_mid = (x_range.start + x_range.end) / 2; 75 let y_mid = (y_range.start + y_range.end) / 2; 76 77 let x_dir = if begin.0 < x_mid { 78 (-tick_size, 0) 79 } else { 80 (tick_size, 0) 81 }; 82 83 let y_dir = if begin.1 < y_mid { 84 (0, -tick_size) 85 } else { 86 (0, tick_size) 87 }; 88 89 let x_score = (x_dir.0 * axis_dir.0 + x_dir.1 * axis_dir.1).abs(); 90 let y_score = (y_dir.0 * axis_dir.0 + y_dir.1 * axis_dir.1).abs(); 91 92 let dir = if x_score < y_score { x_dir } else { y_dir }; 93 94 for (pos, text) in labels { 95 let logic_pos = Coord3D::build_coord([&pos[0], &pos[1], &pos[2]]); 96 let mut font = font.clone(); 97 98 match dir.0.cmp(&0) { 99 Ordering::Less => font.pos = Pos::new(HPos::Right, VPos::Center), 100 Ordering::Greater => font.pos = Pos::new(HPos::Left, VPos::Center), 101 _ => (), 102 } 103 104 match dir.1.cmp(&0) { 105 Ordering::Less => font.pos = Pos::new(HPos::Center, VPos::Bottom), 106 Ordering::Greater => font.pos = Pos::new(HPos::Center, VPos::Top), 107 _ => (), 108 } 109 110 let element = EmptyElement::at(logic_pos) 111 + PathElement::new(vec![(0, 0), dir], style) 112 + Text::new(text.to_string(), (dir.0 * 2, dir.1 * 2), font); 113 self.plotting_area().draw(&element)?; 114 } 115 Ok(()) 116 } 117 #[allow(clippy::type_complexity)] draw_axis( &mut self, idx: usize, panels: &[[[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2]; 3], style: ShapeStyle, ) -> Result< [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2], DrawingAreaErrorKind<DB::ErrorType>, >118 pub(crate) fn draw_axis( 119 &mut self, 120 idx: usize, 121 panels: &[[[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2]; 3], 122 style: ShapeStyle, 123 ) -> Result< 124 [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2], 125 DrawingAreaErrorKind<DB::ErrorType>, 126 > { 127 let coord = self.plotting_area().as_coord_spec(); 128 let x_range = coord.logic_x.range(); 129 let y_range = coord.logic_y.range(); 130 let z_range = coord.logic_z.range(); 131 132 let ranges: [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 2]; 3] = [ 133 [Coord3D::X(x_range.start), Coord3D::X(x_range.end)], 134 [Coord3D::Y(y_range.start), Coord3D::Y(y_range.end)], 135 [Coord3D::Z(z_range.start), Coord3D::Z(z_range.end)], 136 ]; 137 138 let (start, end) = { 139 let mut start = [&ranges[0][0], &ranges[1][0], &ranges[2][0]]; 140 let mut end = [&ranges[0][1], &ranges[1][1], &ranges[2][1]]; 141 142 let mut plan = vec![]; 143 144 for i in 0..3 { 145 if i == idx { 146 continue; 147 } 148 start[i] = &panels[i][0][i]; 149 end[i] = &panels[i][0][i]; 150 for j in 0..3 { 151 if i != idx && i != j && j != idx { 152 for k in 0..2 { 153 start[j] = &panels[i][k][j]; 154 end[j] = &panels[i][k][j]; 155 plan.push((start, end)); 156 } 157 } 158 } 159 } 160 plan.into_iter() 161 .min_by_key(|&(s, e)| { 162 let d = coord.projected_depth(s[0].get_x(), s[1].get_y(), s[2].get_z()); 163 let d = d + coord.projected_depth(e[0].get_x(), e[1].get_y(), e[2].get_z()); 164 let (_, y1) = coord.translate(&Coord3D::build_coord(s)); 165 let (_, y2) = coord.translate(&Coord3D::build_coord(e)); 166 let y = y1 + y2; 167 (d, y) 168 }) 169 .unwrap() 170 }; 171 172 self.plotting_area().draw(&PathElement::new( 173 vec![Coord3D::build_coord(start), Coord3D::build_coord(end)], 174 style, 175 ))?; 176 177 Ok([ 178 [start[0].clone(), start[1].clone(), start[2].clone()], 179 [end[0].clone(), end[1].clone(), end[2].clone()], 180 ]) 181 } 182 183 #[allow(clippy::type_complexity)] draw_axis_panels( &mut self, bold_points: &KeyPoints3d<X, Y, Z>, light_points: &KeyPoints3d<X, Y, Z>, panel_style: ShapeStyle, bold_grid_style: ShapeStyle, light_grid_style: ShapeStyle, ) -> Result< [[[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2]; 3], DrawingAreaErrorKind<DB::ErrorType>, >184 pub(crate) fn draw_axis_panels( 185 &mut self, 186 bold_points: &KeyPoints3d<X, Y, Z>, 187 light_points: &KeyPoints3d<X, Y, Z>, 188 panel_style: ShapeStyle, 189 bold_grid_style: ShapeStyle, 190 light_grid_style: ShapeStyle, 191 ) -> Result< 192 [[[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2]; 3], 193 DrawingAreaErrorKind<DB::ErrorType>, 194 > { 195 let mut r_iter = (0..3).map(|idx| { 196 self.draw_axis_panel( 197 idx, 198 bold_points, 199 light_points, 200 panel_style, 201 bold_grid_style, 202 light_grid_style, 203 ) 204 }); 205 Ok([ 206 r_iter.next().unwrap()?, 207 r_iter.next().unwrap()?, 208 r_iter.next().unwrap()?, 209 ]) 210 } 211 #[allow(clippy::type_complexity)] draw_axis_panel( &mut self, idx: usize, bold_points: &KeyPoints3d<X, Y, Z>, light_points: &KeyPoints3d<X, Y, Z>, panel_style: ShapeStyle, bold_grid_style: ShapeStyle, light_grid_style: ShapeStyle, ) -> Result< [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2], DrawingAreaErrorKind<DB::ErrorType>, >212 fn draw_axis_panel( 213 &mut self, 214 idx: usize, 215 bold_points: &KeyPoints3d<X, Y, Z>, 216 light_points: &KeyPoints3d<X, Y, Z>, 217 panel_style: ShapeStyle, 218 bold_grid_style: ShapeStyle, 219 light_grid_style: ShapeStyle, 220 ) -> Result< 221 [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2], 222 DrawingAreaErrorKind<DB::ErrorType>, 223 > { 224 let coord = self.plotting_area().as_coord_spec(); 225 let x_range = coord.logic_x.range(); 226 let y_range = coord.logic_y.range(); 227 let z_range = coord.logic_z.range(); 228 229 let ranges: [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 2]; 3] = [ 230 [Coord3D::X(x_range.start), Coord3D::X(x_range.end)], 231 [Coord3D::Y(y_range.start), Coord3D::Y(y_range.end)], 232 [Coord3D::Z(z_range.start), Coord3D::Z(z_range.end)], 233 ]; 234 235 let (mut panel, start, end) = { 236 let vert_a = [&ranges[0][0], &ranges[1][0], &ranges[2][0]]; 237 let mut vert_b = [&ranges[0][1], &ranges[1][1], &ranges[2][1]]; 238 let mut vert_c = vert_a; 239 let vert_d = vert_b; 240 241 vert_b[idx] = &ranges[idx][0]; 242 vert_c[idx] = &ranges[idx][1]; 243 244 let (vert_a, vert_b) = 245 if coord.projected_depth(vert_a[0].get_x(), vert_a[1].get_y(), vert_a[2].get_z()) 246 >= coord.projected_depth( 247 vert_c[0].get_x(), 248 vert_c[1].get_y(), 249 vert_c[2].get_z(), 250 ) 251 { 252 (vert_a, vert_b) 253 } else { 254 (vert_c, vert_d) 255 }; 256 257 let mut m = vert_a; 258 m[(idx + 1) % 3] = vert_b[(idx + 1) % 3]; 259 let mut n = vert_a; 260 n[(idx + 2) % 3] = vert_b[(idx + 2) % 3]; 261 262 ( 263 vec![ 264 Coord3D::build_coord(vert_a), 265 Coord3D::build_coord(m), 266 Coord3D::build_coord(vert_b), 267 Coord3D::build_coord(n), 268 ], 269 vert_a, 270 vert_b, 271 ) 272 }; 273 self.plotting_area() 274 .draw(&Polygon::new(panel.clone(), panel_style))?; 275 panel.push(panel[0].clone()); 276 self.plotting_area() 277 .draw(&PathElement::new(panel, bold_grid_style))?; 278 279 for (kps, style) in vec![ 280 (light_points, light_grid_style), 281 (bold_points, bold_grid_style), 282 ] 283 .into_iter() 284 { 285 for idx in (0..3).filter(|&i| i != idx) { 286 let kps: Vec<_> = match idx { 287 0 => kps.x_points.iter().map(|x| Coord3D::X(x.clone())).collect(), 288 1 => kps.y_points.iter().map(|y| Coord3D::Y(y.clone())).collect(), 289 _ => kps.z_points.iter().map(|z| Coord3D::Z(z.clone())).collect(), 290 }; 291 for kp in kps.iter() { 292 let mut kp_start = start; 293 let mut kp_end = end; 294 kp_start[idx] = kp; 295 kp_end[idx] = kp; 296 self.plotting_area().draw(&PathElement::new( 297 vec![Coord3D::build_coord(kp_start), Coord3D::build_coord(kp_end)], 298 style, 299 ))?; 300 } 301 } 302 } 303 304 Ok([ 305 [start[0].clone(), start[1].clone(), start[2].clone()], 306 [end[0].clone(), end[1].clone(), end[2].clone()], 307 ]) 308 } 309 } 310