gnuplot/
axes3d.rs1// Copyright (c) 2013-2014 by SiegeLord
2//
3// All rights reserved. Distributed under LGPL 3.0. For full terms see the file LICENSE.
4
5use crate::axes_common::*;
6use crate::datatype::*;
7use crate::options::*;
8use crate::util::OneWayOwned;
9use crate::writer::Writer;
10use std::borrow::Borrow;
11
12enum View
13{
14 PitchYaw(f64, f64),
15 Map,
16}
17
18impl View
19{
20 fn write_out(&self, writer: &mut dyn Writer)
21 {
22 match self
23 {
24 Self::PitchYaw(pitch, yaw) =>
25 {
26 writeln!(writer, "set view {:.12e},{:.12e}", pitch, yaw);
27 }
28 Self::Map =>
29 {
30 writer.write_str("set view map\n");
31 }
32 }
33 }
34
35 fn reset_state(&self, writer: &mut dyn Writer)
36 {
37 writer.write_str("unset view\n");
38 }
39}
40
41/// 3D axes that is used for drawing 3D plots
42pub struct Axes3D
43{
44 common: AxesCommonData,
45 z_axis: AxisData,
46 contour_base: bool,
47 contour_surface: bool,
48 contour_auto: AutoOption<u32>,
49 contour_levels: Option<Vec<f64>>,
50 contour_style: ContourStyle,
51 contour_label: AutoOption<String>,
52 view: Option<View>,
53}
54
55impl Axes3D
56{
57 pub(crate) fn new() -> Axes3D
58 {
59 Axes3D {
60 common: AxesCommonData::new(),
61 z_axis: AxisData::new(TickAxis::Z),
62 contour_base: false,
63 contour_surface: false,
64 contour_auto: Auto,
65 contour_levels: None,
66 contour_style: Linear,
67 contour_label: Auto,
68 view: None,
69 }
70 }
71
72 /// Draws a 3D surface from a rectangular array of data by connecting the individual datapoints with polygons.
73 ///
74 /// #Arguments:
75 /// * `mat` - Row-major 2D array signifying the Z coordinate of the datapoints. The X and Y coordinates of the datapoints are determined automatically,
76 /// and optionally scaled using the `dimensions` argument.
77 /// * `num_rows` - Number of rows in the data array
78 /// * `num_cols` - Number of columns in the data array
79 /// * `dimensions` - Optional X and Y coordinates of the first and last data points (with the rest of the coordinates spaced evenly between).
80 /// By default this will be `(0, 0)` and `(num_rows - 1, num_cols - 1)`.
81 /// * `options` - Array of PlotOption controlling the appearance of the surface. Relevant options are:
82 /// * `Caption` - Specifies the caption for this dataset. Use an empty string to hide it (default).
83 pub fn surface<'l, T: DataType, X: IntoIterator<Item = T>>(
84 &'l mut self, mat: X, num_rows: usize, num_cols: usize,
85 dimensions: Option<(f64, f64, f64, f64)>, options: &[PlotOption<&str>],
86 ) -> &'l mut Self
87 {
88 self.common.elems.push(PlotElement::new_plot_matrix(
89 Pm3D,
90 true,
91 mat,
92 num_rows,
93 num_cols,
94 dimensions,
95 options.to_one_way_owned(),
96 ));
97 self
98 }
99
100 /// Plot a 3D scatter-plot with a point standing in for each data point
101 /// # Arguments
102 /// * `x` - x values
103 /// * `y` - y values
104 /// * `z` - z values
105 /// * `options` - Array of PlotOption<&str> controlling the appearance of the plot element. The relevant options are:
106 /// * `Caption` - Specifies the caption for this dataset. Use an empty string to hide it (default).
107 /// * `PointSymbol` - Sets symbol for each point
108 /// * `PointSize` - Sets the size of each point
109 /// * `Color` - Sets the color
110 pub fn points<
111 'l,
112 Tx: DataType,
113 X: IntoIterator<Item = Tx>,
114 Ty: DataType,
115 Y: IntoIterator<Item = Ty>,
116 Tz: DataType,
117 Z: IntoIterator<Item = Tz>,
118 >(
119 &'l mut self, x: X, y: Y, z: Z, options: &[PlotOption<&str>],
120 ) -> &'l mut Self
121 {
122 let (data, num_rows, num_cols) = generate_data!(options, x, y, z);
123 self.common.elems.push(PlotElement::new_plot(
124 Points, data, num_rows, num_cols, options,
125 ));
126 self
127 }
128
129 /// Plot a 3D scatter-plot with lines connecting each data point
130 /// # Arguments
131 /// * `x` - x values
132 /// * `y` - y values
133 /// * `z` - z values
134 /// * `options` - Array of PlotOption<&str> controlling the appearance of the plot element. The relevant options are:
135 /// * `Caption` - Specifies the caption for this dataset. Use an empty string to hide it (default).
136 /// * `PointSymbol` - Sets symbol for each point
137 /// * `PointSize` - Sets the size of each point
138 /// * `Color` - Sets the color
139 pub fn lines<
140 'l,
141 Tx: DataType,
142 X: IntoIterator<Item = Tx>,
143 Ty: DataType,
144 Y: IntoIterator<Item = Ty>,
145 Tz: DataType,
146 Z: IntoIterator<Item = Tz>,
147 >(
148 &'l mut self, x: X, y: Y, z: Z, options: &[PlotOption<&str>],
149 ) -> &'l mut Self
150 {
151 let (data, num_rows, num_cols) = generate_data!(options, x, y, z);
152 self.common.elems.push(PlotElement::new_plot(
153 Lines, data, num_rows, num_cols, options,
154 ));
155 self
156 }
157
158 /// A combination of lines and points methods (drawn in that order).
159 /// # Arguments
160 /// * `x` - x values
161 /// * `y` - y values
162 /// * `z` - z values
163 /// * `options` - Array of PlotOption<&str> controlling the appearance of the plot element
164 pub fn lines_points<
165 'l,
166 Tx: DataType,
167 X: IntoIterator<Item = Tx>,
168 Ty: DataType,
169 Y: IntoIterator<Item = Ty>,
170 Tz: DataType,
171 Z: IntoIterator<Item = Tz>,
172 >(
173 &'l mut self, x: X, y: Y, z: Z, options: &[PlotOption<&str>],
174 ) -> &'l mut Self
175 {
176 let (data, num_rows, num_cols) = generate_data!(options, x, y, z);
177 self.common.elems.push(PlotElement::new_plot(
178 LinesPoints,
179 data,
180 num_rows,
181 num_cols,
182 options,
183 ));
184 self
185 }
186
187 /// Sets the 3D view.
188 ///
189 /// #Arguments:
190 /// * `pitch` - Pitch, in degrees. Value of 0 is looking straight down on the XY plane, Z pointing out of the screen.
191 /// * `yaw` - Yaw, in degrees. Value of 0 is looking at the XZ plane, Y point into the screen.
192 pub fn set_view(&mut self, pitch: f64, yaw: f64) -> &mut Self
193 {
194 self.view = Some(View::PitchYaw(pitch, yaw));
195 self
196 }
197
198 /// Sets the view to be a map. Useful for images and contour plots.
199 pub fn set_view_map(&mut self) -> &mut Self
200 {
201 self.view = Some(View::Map);
202 self
203 }
204
205 /// Set the label for the Z axis
206 ///
207 /// # Arguments
208 /// * `text` - Text of the label. Pass an empty string to hide the label
209 /// * `options` - Array of LabelOption controlling the appearance of the label. Relevant options are:
210 /// * `Offset` - Specifies the offset of the label
211 /// * `Font` - Specifies the font of the label
212 /// * `TextColor` - Specifies the color of the label
213 /// * `Rotate` - Specifies the rotation of the label
214 /// * `Align` - Specifies how to align the label
215 pub fn set_z_label<'l>(&'l mut self, text: &str, options: &[LabelOption<&str>])
216 -> &'l mut Self
217 {
218 self.z_axis
219 .label
220 .set(text.into(), options.to_one_way_owned());
221 self
222 }
223
224 /// Sets the properties of x axis.
225 ///
226 /// # Arguments
227 ///
228 /// * `show` - Whether or not draw the axis
229 /// * `options` - Array of PlotOption<&str> controlling the appearance of the axis. Relevant options are:
230 /// * `Color` - Specifies the color of the border
231 /// * `LineStyle` - Specifies the style of the border
232 /// * `LineWidth` - Specifies the width of the border
233 pub fn set_x_axis<'l>(&'l mut self, show: bool, options: &[PlotOption<&str>]) -> &'l mut Self
234 {
235 self.common.x_axis.show = show;
236 self.common.x_axis.options = options.to_one_way_owned();
237 self
238 }
239
240 /// Like `set_x_axis` but for the y axis.
241 pub fn set_y_axis<'l>(&'l mut self, show: bool, options: &[PlotOption<&str>]) -> &'l mut Self
242 {
243 self.common.y_axis.show = show;
244 self.common.y_axis.options = options.to_one_way_owned();
245 self
246 }
247
248 /// Like `set_x_axis` but for the z axis.
249 pub fn set_z_axis<'l>(&'l mut self, show: bool, options: &[PlotOption<&str>]) -> &'l mut Self
250 {
251 self.z_axis.show = show;
252 self.z_axis.options = options.to_one_way_owned();
253 self
254 }
255
256 /// Like `set_x_ticks` but for the Z axis.
257 pub fn set_z_ticks<'l>(
258 &'l mut self, tick_placement: Option<(AutoOption<f64>, u32)>,
259 tick_options: &[TickOption<&str>], label_options: &[LabelOption<&str>],
260 ) -> &'l mut Self
261 {
262 self.z_axis.set_ticks(
263 tick_placement,
264 tick_options.to_one_way_owned(),
265 label_options.to_one_way_owned(),
266 );
267 self
268 }
269
270 /// Like `set_x_ticks_custom` but for the the Y axis.
271 pub fn set_z_ticks_custom<
272 'l,
273 T: DataType,
274 S: ToString,
275 TickT: Borrow<Tick<T, S>>,
276 TL: IntoIterator<Item = TickT>,
277 >(
278 &'l mut self, ticks: TL, tick_options: &[TickOption<&str>],
279 label_options: &[LabelOption<&str>],
280 ) -> &'l mut Self
281 {
282 self.z_axis.set_ticks_custom(
283 ticks.into_iter().map(|e| e.borrow().to_one_way_owned()),
284 tick_options.to_one_way_owned(),
285 label_options.to_one_way_owned(),
286 );
287 self
288 }
289
290 /// Set the range of values for the Z axis
291 ///
292 /// # Arguments
293 /// * `min` - Minimum Z value
294 /// * `max` - Maximum Z value
295 pub fn set_z_range(&mut self, min: AutoOption<f64>, max: AutoOption<f64>) -> &mut Self
296 {
297 self.z_axis.set_range(min, max);
298 self
299 }
300
301 /// Sets z axis to reverse.
302 pub fn set_z_reverse(&mut self, reverse: bool) -> &mut Self
303 {
304 self.z_axis.set_reverse(reverse);
305 self
306 }
307
308 /// Sets the Z axis be logarithmic. Note that the range must be non-negative for this to be valid.
309 ///
310 /// # Arguments
311 /// * `base` - If Some, then specifies base of the logarithm, if None makes the axis not be logarithmic
312 pub fn set_z_log(&mut self, base: Option<f64>) -> &mut Self
313 {
314 self.z_axis.set_log(base);
315 self
316 }
317
318 /// Shows the grid on the Z axis.
319 ///
320 /// # Arguments
321 /// * `show` - Whether to show the grid.
322 pub fn set_z_grid(&mut self, show: bool) -> &mut Self
323 {
324 self.z_axis.set_grid(show);
325 self
326 }
327
328 /// Sets the Z axis be time. Note that the range must be non-negative for this to be valid.
329 ///
330 /// If true, the axis is interpreted as seconds from the Unix epoch. Use the `Format` TickOption to
331 /// specify the formatting of the ticks (see strftime format spec for valid values).
332 ///
333 /// # Arguments
334 /// * `is_time` - Whether this axis is time or not.
335 pub fn set_z_time(&mut self, is_time: bool) -> &mut Self
336 {
337 self.z_axis.set_time(is_time);
338 self
339 }
340
341 /// Show contours (lines of equal Z value) at automatically determined levels.
342 ///
343 /// # Arguments
344 /// * `base` - Show contours on the base of the plot (XY plane)
345 /// * `surface` - Show the contours on the surface itself
346 /// * `style` - Style of the contours
347 /// * `label` - Auto sets the label automatically and enables the legend, Fix() allows you specify a format string (using C style formatting),
348 /// otherwise an empty string disables the legend and labels.
349 /// * `levels` - Auto picks some default number of levels, otherwise you can pass a set nominal number instead. The number is nominal as
350 /// contours are placed at nice values of Z, and thus there may be fewer of them than this number.
351 pub fn show_contours(
352 &mut self, base: bool, surface: bool, style: ContourStyle, label: AutoOption<&str>,
353 levels: AutoOption<u32>,
354 ) -> &mut Self
355 {
356 self.contour_base = base;
357 self.contour_surface = surface;
358 self.contour_style = style;
359 self.contour_auto = levels;
360 self.contour_levels = None;
361 self.contour_label = label.map(|l| l.to_string());
362 self
363 }
364
365 /// Show contours (lines of equal Z value) at specific levels.
366 ///
367 /// # Arguments
368 /// * `base` - Show contours on the base of the plot (XY plane)
369 /// * `surface` - Show the contours on the surface itself
370 /// * `style` - Style of the contours
371 /// * `label` - Auto sets the label automatically and enables the legend, Fix() allows you specify a format string (using C style formatting),
372 /// otherwise an empty string disables the legend and labels.
373 /// * `levels` - A set of levels.
374 pub fn show_contours_custom<T: DataType, TC: IntoIterator<Item = T>>(
375 &mut self, base: bool, surface: bool, style: ContourStyle, label: AutoOption<&str>,
376 levels: TC,
377 ) -> &mut Self
378 {
379 self.contour_base = base;
380 self.contour_surface = surface;
381 self.contour_style = style;
382 self.contour_auto = Auto;
383 self.contour_levels = Some(levels.into_iter().map(|l| l.get()).collect());
384 self.contour_label = label.map(|l| l.to_string());
385 self
386 }
387
388 pub(crate) fn reset_state(&self, writer: &mut dyn Writer)
389 {
390 self.common.reset_state(writer);
391 if let Some(v) = self.view.as_ref()
392 {
393 v.reset_state(writer)
394 };
395 }
396
397 pub(crate) fn write_out(
398 &self, data_directory: Option<&str>, w: &mut dyn Writer, auto_layout: bool,
399 version: GnuplotVersion,
400 )
401 {
402 fn clamp<T: PartialOrd>(val: T, min: T, max: T) -> T
403 {
404 if val < min
405 {
406 min
407 }
408 else if val > max
409 {
410 max
411 }
412 else
413 {
414 val
415 }
416 }
417
418 if self.contour_base || self.contour_surface
419 {
420 write!(w, "set contour ");
421 write!(
422 w,
423 "{}",
424 match (self.contour_base, self.contour_surface)
425 {
426 (true, false) => "base",
427 (false, true) => "surface",
428 (true, true) => "both",
429 _ => unreachable!(),
430 }
431 );
432 writeln!(w);
433
434 match self.contour_label
435 {
436 Auto => writeln!(w, "set clabel"),
437 Fix(ref s) =>
438 {
439 if s.is_empty()
440 {
441 writeln!(w, "unset clabel")
442 }
443 else
444 {
445 writeln!(w, r#"set clabel "{}""#, s)
446 }
447 }
448 };
449
450 fn set_cntrparam<F: FnOnce(&mut dyn Writer)>(w: &mut dyn Writer, wr: F)
451 {
452 write!(w, "set cntrparam ");
453 wr(w);
454 writeln!(w);
455 }
456
457 set_cntrparam(w, |w| {
458 write!(
459 w,
460 "{}",
461 match self.contour_style
462 {
463 Linear => "linear ",
464 Cubic(..) => "cubicspline",
465 Spline(..) => "bspline",
466 }
467 );
468 });
469
470 set_cntrparam(w, |w| {
471 let pt = match self.contour_style
472 {
473 Cubic(pt) => Some(pt),
474 Spline(pt, _) => Some(pt),
475 _ => None,
476 };
477
478 if let Some(pt) = pt
479 {
480 write!(w, "points {}", clamp(pt, 2, 100));
481 };
482 });
483
484 set_cntrparam(w, |w| {
485 let ord = match self.contour_style
486 {
487 Spline(_, ord) => Some(ord),
488 _ => None,
489 };
490
491 if let Some(ord) = ord
492 {
493 write!(w, "order {}", clamp(ord, 2, 10));
494 };
495 });
496
497 set_cntrparam(w, |w| {
498 write!(w, "levels ");
499 match self.contour_levels
500 {
501 Some(ref ls) =>
502 {
503 write!(w, "discrete ");
504 let mut left = ls.len();
505 for &l in ls.iter()
506 {
507 write!(w, "{:.12e}", l);
508 if left > 1
509 {
510 write!(w, ",");
511 }
512 left -= 1;
513 }
514 }
515 None =>
516 {
517 match self.contour_auto
518 {
519 Auto => write!(w, "auto "),
520 Fix(f) => write!(w, "{}", f),
521 };
522 }
523 };
524 });
525 }
526
527 self.common.write_out_commands(w, auto_layout, version);
528 self.z_axis.write_out_commands(w, version);
529 let mut grid_axes = vec![];
530 if self.common.x_axis.grid
531 {
532 grid_axes.push(self.common.x_axis.axis);
533 }
534 if self.common.y_axis.grid
535 {
536 grid_axes.push(self.common.y_axis.axis);
537 }
538 if self.common.cb_axis.grid
539 {
540 grid_axes.push(self.common.cb_axis.axis);
541 }
542 if self.z_axis.grid
543 {
544 grid_axes.push(self.z_axis.axis);
545 }
546 if let Some(v) = self.view.as_ref()
547 {
548 v.write_out(w)
549 };
550 self.common.write_grid_options(w, &grid_axes, version);
551 self.common
552 .write_out_elements("splot", data_directory, w, version);
553 }
554}
555
556impl AxesCommonPrivate for Axes3D
557{
558 fn get_common_data(&self) -> &AxesCommonData
559 {
560 &self.common
561 }
562
563 fn get_common_data_mut(&mut self) -> &mut AxesCommonData
564 {
565 &mut self.common
566 }
567}
568
569impl AxesCommon for Axes3D {}