axes3d.rs - source

gnuplot/

axes3d.rs

1// 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 {}