Skip to main content

unlab_gpu/
plot.rs

1//
2// Copyright (c) 2025-2026 Ɓukasz Szpakowski
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at https://mozilla.org/MPL/2.0/.
7//
8//! A plotting module.
9use std::cmp::Ordering;
10use std::collections::BTreeMap;
11use std::collections::HashMap;
12use std::error;
13use std::fmt;
14use std::mem::size_of;
15use std::mem::transmute;
16use std::num::NonZeroU32;
17use std::ops::Range;
18use std::path::PathBuf;
19use std::result;
20use std::slice;
21use std::sync::mpsc::Sender;
22use std::sync::mpsc::channel;
23use std::sync::Arc;
24use plotters::backend::BGRXPixel;
25use plotters::drawing::IntoDrawingArea;
26use plotters::element::DashedPathElement;
27use plotters::element::DottedPathElement;
28use plotters::prelude::*;
29use softbuffer::Context;
30use softbuffer::Surface;
31use crate::winit::application::ApplicationHandler;
32use crate::winit::dpi::PhysicalSize;
33use crate::winit::event::WindowEvent;
34use crate::winit::event_loop::ActiveEventLoop;
35use crate::winit::event_loop::EventLoop;
36use crate::winit::raw_window_handle::DisplayHandle;
37use crate::winit::raw_window_handle::HasDisplayHandle;
38use crate::winit::window::Icon;
39use crate::winit::window::Window;
40use crate::env::*;
41use crate::error::*;
42use crate::interp::*;
43use crate::utils::*;
44use crate::value::*;
45
46/// A structure of floating-point number key.
47///
48/// The floating-point number is used by surfaces which are drawn.
49#[derive(Copy, Clone, Debug)]
50pub struct F32Key
51{
52    value: f32,
53}
54
55impl F32Key
56{
57    /// Creates a floating-point number key.
58    pub fn new(value: f32) -> Self
59    { F32Key { value, } }
60    
61    /// Retursn the floating-point number.
62    pub fn to_f32(&self) -> f32
63    { self.value }
64    
65    /// Converts the floating-point number to a key floating-point number.
66    ///
67    /// This method retuns negated infinity if the floating-point number is NaN, otherwise the
68    /// floating-point number.
69    pub fn to_key_f32(&self) -> f32
70    {
71        if !self.value.is_nan() {
72            self.value
73        } else {
74            -f32::INFINITY
75        }
76    }
77}
78
79impl Eq for F32Key
80{}
81
82impl PartialEq for F32Key
83{
84    fn eq(&self, other: &Self) -> bool
85    { self.to_key_f32() == other.to_key_f32() }
86}
87
88impl Ord for F32Key
89{
90    fn cmp(&self, other: &Self) -> Ordering
91    { self.to_key_f32().partial_cmp(&other.to_key_f32()).unwrap() }
92}
93
94impl PartialOrd for F32Key
95{
96    fn partial_cmp(&self, other: &Self) -> Option<Ordering>
97    { Some(self.cmp(other)) }
98}
99
100/// A chart structure.
101#[derive(Clone, Debug)]
102pub struct Chart
103{
104    /// A chart title.
105    pub title: Option<String>,
106    /// A window identfier.
107    pub window_id: Option<WindowId>,
108    /// If this field is `true`, chart can be shown on the window.
109    pub has_window: bool,
110    /// A path to a chart file.
111    pub file: Option<String>,
112    /// A chart size.
113    pub size: Option<(u32, u32)>,
114}
115
116/// A structure of axes 2D. 
117#[derive(Clone, Debug)]
118pub struct Axes2d
119{
120    /// A X axis.
121    pub x: Range<f32>,
122    /// An Y axis.
123    pub y: Range<f32>,
124}
125
126/// A structure of axes 3D. 
127#[derive(Clone, Debug)]
128pub struct Axes3d
129{
130    /// A X axis.
131    pub x: Range<f32>,
132    /// An Y axis.
133    pub y: Range<f32>,
134    /// A Z axis.
135    pub z: Range<f32>,
136}
137
138/// A structure of histogram value.
139#[derive(Clone, Debug)]
140pub enum HistogramValue
141{
142    /// A boolean value.
143    Bool(bool),
144    /// An integer number.
145    Int(i64),
146    /// A floating-point number.
147    Float(f32),
148    /// A string.
149    String(Box<String>),
150}
151
152impl HistogramValue
153{
154    /// Converts any value to a boolean value.
155    pub fn to_bool(&self) -> bool
156    {
157        match self {
158            HistogramValue::Bool(b) => *b,
159            HistogramValue::Int(n) => *n != 0,
160            HistogramValue::Float(n) => *n != 0.0,
161            HistogramValue::String(_) => true,
162        }
163    }
164
165    /// Converts any value to an integer number.
166    pub fn to_i64(&self) -> i64
167    {
168        match self {
169            HistogramValue::Bool(b) => if *b { 1 } else { 0 },
170            HistogramValue::Int(n) => *n,
171            HistogramValue::Float(n) => *n as i64,
172            HistogramValue::String(_) => 1,
173        }
174    }
175
176    /// Converts any value to a floating-point number.
177    pub fn to_f32(&self) -> f32
178    {
179        match self {
180            HistogramValue::Bool(b) => if *b { 1.0 } else { 0.0 },
181            HistogramValue::Int(n) => *n as f32,
182            HistogramValue::Float(n) => *n,
183            HistogramValue::String(_) => 1.0,
184        }
185    }
186
187    /// Converts the value to a boolean value if the value is an boolean type, otherwise this
188    /// method returns `None`.
189    pub fn to_opt_bool(&self) -> Option<bool>
190    {
191        match self {
192            HistogramValue::Bool(b) => Some(*b),
193            _ => None,
194        }
195    }
196
197    /// Converts the value to an integer number if the value is an integer type or floating-point
198    /// type, otherwise this method returns `None`.
199    pub fn to_opt_i64(&self) -> Option<i64>
200    {
201        match self {
202            HistogramValue::Int(n) => Some(*n),
203            HistogramValue::Float(n) => Some(*n as i64),
204            _ => None,
205        }
206    }
207
208    /// Converts the value to a floating-point number if the value is an integer type or
209    /// floating-point type, otherwise this method returns `None`.
210    pub fn to_opt_f32(&self) -> Option<f32>
211    {
212        match self {
213            HistogramValue::Int(n) => Some(*n as f32),
214            HistogramValue::Float(n) => Some(*n),
215            _ => None,
216        }
217    }
218
219    /// Converts the value to a string if the value is a string type, otherwise this method
220    /// returns `None`.
221    pub fn to_opt_string(&self) -> Option<String>
222    {
223        match self {
224            HistogramValue::String(s) => Some((**s).clone()),
225            _ => None,
226        }
227    }
228}
229
230impl PartialEq for HistogramValue
231{
232    fn eq(&self, other: &Self) -> bool
233    {
234        match (self, other) {
235            (HistogramValue::Bool(b), HistogramValue::Bool(b2)) => b == b2,
236            (HistogramValue::Int(n), HistogramValue::Int(n2)) => n == n2,
237            (HistogramValue::Int(_) | HistogramValue::Float(_), HistogramValue::Int(_) | HistogramValue::Float(_)) => self.to_f32() == other.to_f32(),
238            (HistogramValue::String(s), HistogramValue::String(s2)) => s == s2, 
239            (_, _) => false,
240        }
241    }
242}
243
244impl fmt::Display for HistogramValue
245{
246    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
247    {
248        match self {
249            HistogramValue::Bool(b) => write!(f, "{}", b),
250            HistogramValue::Int(n) => write!(f, "{}", n),
251            HistogramValue::Float(n) => write!(f, "{}", n),
252            HistogramValue::String(s) => write!(f, "{}", *s),
253        }
254    }
255}
256
257/// A structure of histogram axes.
258#[derive(Clone, Debug)]
259pub struct HistogramAxes
260{
261    /// A X axis.
262    pub x: Vec<HistogramValue>,
263    /// An Y axis.
264    pub y: Range<usize>,
265}
266
267/// An enumeration of series 2D.
268#[derive(Clone, Debug)]
269pub enum Series2d
270{
271    /// A line series.
272    Line(Vec<f32>, Vec<f32>, RGBColor, Option<String>),
273    /// A dashed line series.
274    DashedLine(Vec<f32>, Vec<f32>, RGBColor, Option<String>),
275    /// A dotted line series.
276    DottedLine(Vec<f32>, Vec<f32>, RGBColor, Option<String>),
277    /// A circle series.
278    Circle(Vec<f32>, Vec<f32>, RGBColor, Option<String>),
279    /// A cross series.
280    Cross(Vec<f32>, Vec<f32>, RGBColor, Option<String>),
281    /// A point series.
282    Point(Vec<f32>, Vec<f32>, RGBColor, Option<String>),
283    /// A triagle series.
284    Triangle(Vec<f32>, Vec<f32>, RGBColor, Option<String>),
285}
286
287/// An enumeration of series 3D.
288#[derive(Clone, Debug)]
289pub enum Series3d
290{
291    /// A line series.
292    Line(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>),
293    /// A dashed line series.
294    DashedLine(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>),
295    /// A dotted line series.
296    DottedLine(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>),
297    /// A circle series.
298    Circle(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>),
299    /// A cross series.
300    Cross(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>),
301    /// A point series.
302    Point(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>),
303    /// A triagle series.
304    Triangle(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>),
305    /// A surface on a X axis and an Y axis.
306    XYSurface(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>, BTreeMap<F32Key, usize>, BTreeMap<F32Key, usize>),
307    /// A surface on a X axis and a Z axis.
308    XZSurface(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>, BTreeMap<F32Key, usize>, BTreeMap<F32Key, usize>),
309    /// A surface on an Y axis and a Z axis.
310    YZSurface(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>, BTreeMap<F32Key, usize>, BTreeMap<F32Key, usize>),
311}
312
313/// A structure of histogram series.
314#[derive(Clone, Debug)]
315pub struct HistogramSeries(pub Vec<HistogramValue>, pub RGBColor, pub Option<String>);
316
317const DEFAULT_SIZE: (u32, u32) = (640, 480);
318
319const TITLE_FONT_SIZE: i32 = 40;
320const MARGIN: i32 = 10;
321const X_LABEL_AREA_SIZE: i32 = 30;
322const Y_LABEL_AREA_SIZE: i32 = 60;
323
324const LEGEND_WIDTH: i32 = 20;
325const LEGEND_HEIGHT: i32 = 10;
326
327const DASH_SIZE: i32 = 8;
328const DASH_SPACING: i32 = 2;
329const DOT_SHIFT: i32 = 0;
330const DOT_SPACING: i32 = 4;
331const MARKER_SIZE: i32 = 4;
332const POINT_SIZE: i32 = 1;
333
334const SURFACE_MIX: f64 = 0.2;
335const HISTOGRAM_MIX: f64 = 0.6;
336
337static COLORS: [RGBColor; 6] = [RED, BLUE, GREEN, CYAN, YELLOW, MAGENTA];
338
339fn draw_chart2d<T: IntoDrawingArea>(backend: T, chart_desc: &Chart, axes: &Axes2d, serieses: &[Series2d]) -> result::Result<(), Box<dyn error::Error>>
340    where T::ErrorType: 'static
341{
342    let root = backend.into_drawing_area();
343    root.fill(&WHITE)?;
344    let mut chart_builder = ChartBuilder::on(&root);
345    match &chart_desc.title {
346        Some(title) => {
347            chart_builder.caption(title, ("sans-serif", TITLE_FONT_SIZE).into_font());
348        },
349        None => (),
350    }
351    let mut chart = chart_builder
352        .margin(MARGIN)
353        .x_label_area_size(X_LABEL_AREA_SIZE)
354        .y_label_area_size(Y_LABEL_AREA_SIZE)
355        .build_cartesian_2d(axes.x.clone(), axes.y.clone())?;
356    chart.configure_mesh().draw()?;
357    for series in serieses {
358        match series {
359            Series2d::Line(xs, ys, color, label) => {
360                let series_anno = chart.draw_series(LineSeries::new(xs.iter().zip(ys.iter()).map(|(x, y)| (*x, *y)), *color))?;
361                let color2 = *color;
362                match label {
363                    Some(label) => series_anno.label(label.as_str()),
364                    None => series_anno,
365                }.legend(move |(x, y)| PathElement::new(vec![(x, y), (x + LEGEND_WIDTH, y)], &color2));
366            },
367            Series2d::DashedLine(xs, ys, color, label) => {
368                let series_anno = chart.draw_series(DashedLineSeries::new(xs.iter().zip(ys.iter()).map(|(x, y)| (*x, *y)), DASH_SIZE, DASH_SPACING, Into::<ShapeStyle>::into(color)))?;
369                let color2 = *color;
370                match label {
371                    Some(label) => series_anno.label(label.as_str()),
372                    None => series_anno,
373                }.legend(move |(x, y)| DashedPathElement::new(vec![(x, y), (x + LEGEND_WIDTH, y)], DASH_SIZE, DASH_SPACING, Into::<ShapeStyle>::into(&color2)));
374            },
375            Series2d::DottedLine(xs, ys, color, label) => {
376                let color2 = *color;
377                let series_anno = chart.draw_series(DottedLineSeries::new(xs.iter().zip(ys.iter()).map(|(x, y)| (*x, *y)), DOT_SHIFT, DOT_SPACING, move |p| Circle::new(p, POINT_SIZE, Into::<ShapeStyle>::into(&color2).filled())))?;
378                let color3 = *color;
379                match label {
380                    Some(label) => series_anno.label(label.as_str()),
381                    None => series_anno,
382                }.legend(move |(x, y)| DottedPathElement::new(vec![(x, y), (x + LEGEND_WIDTH, y)], DOT_SHIFT, DOT_SPACING, move |p| Circle::new(p, POINT_SIZE, Into::<ShapeStyle>::into(&color3).filled())));
383            },
384            Series2d::Circle(xs, ys, color, label) => {
385                let series_anno = chart.draw_series(PointSeries::<_, _, Circle<_, i32>, i32>::new(xs.iter().zip(ys.iter()).map(|(x, y)| (*x, *y)), MARKER_SIZE, color))?;
386                let color2 = *color;
387                match label {
388                    Some(label) => series_anno.label(label.as_str()),
389                    None => series_anno,
390                }.legend(move |(x, y)| Circle::new((x + LEGEND_WIDTH / 2, y), MARKER_SIZE, &color2));
391            },
392            Series2d::Cross(xs, ys, color, label) => {
393                let series_anno = chart.draw_series(PointSeries::<_, _, Cross<_, i32>, i32>::new(xs.iter().zip(ys.iter()).map(|(x, y)| (*x, *y)), MARKER_SIZE, color))?;
394                let color2 = *color;
395                match label {
396                    Some(label) => series_anno.label(label.as_str()),
397                    None => series_anno,
398                }.legend(move |(x, y)| Cross::new((x + LEGEND_WIDTH / 2, y), MARKER_SIZE, &color2));
399            },
400            Series2d::Point(xs, ys, color, label) => {
401                let series_anno = chart.draw_series(PointSeries::<_, _, Circle<_, i32>, i32>::new(xs.iter().zip(ys.iter()).map(|(x, y)| (*x, *y)), POINT_SIZE, color.filled()))?;
402                let color2 = *color;
403                match label {
404                    Some(label) => series_anno.label(label.as_str()),
405                    None => series_anno,
406                }.legend(move |(x, y)| Circle::new((x + LEGEND_WIDTH / 2, y), POINT_SIZE, color2.filled()));
407            },
408            Series2d::Triangle(xs, ys, color, label) => {
409                let series_anno = chart.draw_series(PointSeries::<_, _, TriangleMarker<_, i32>, i32>::new(xs.iter().zip(ys.iter()).map(|(x, y)| (*x, *y)), MARKER_SIZE, color))?;
410                let color2 = *color;
411                match label {
412                    Some(label) => series_anno.label(label.as_str()),
413                    None => series_anno,
414                }.legend(move |(x, y)| TriangleMarker::new((x + LEGEND_WIDTH / 2, y), MARKER_SIZE, &color2));
415            },
416        }
417    }
418    chart
419        .configure_series_labels()
420        .background_style(&WHITE)
421        .border_style(&BLACK)
422        .draw()?;
423    root.present()?;
424    Ok(())
425}
426
427fn draw_chart3d<T: IntoDrawingArea>(backend: T, chart_desc: &Chart, axes: &Axes3d, serieses: &[Series3d]) -> result::Result<(), Box<dyn error::Error>>
428    where T::ErrorType: 'static
429{
430    let root = backend.into_drawing_area();
431    root.fill(&WHITE)?;
432    let mut chart_builder = ChartBuilder::on(&root);
433    match &chart_desc.title {
434        Some(title) => {
435            chart_builder.caption(title, ("sans-serif", TITLE_FONT_SIZE).into_font());
436        },
437        None => (),
438    }
439    let mut chart = chart_builder
440        .margin(MARGIN)
441        .build_cartesian_3d(axes.x.clone(), axes.y.clone(), axes.z.clone())?;
442    chart.configure_axes().draw()?;
443    for series in serieses {
444        match series {
445            Series3d::Line(xs, ys, zs, color, label) => {
446                let series_anno = chart.draw_series(LineSeries::new(xs.iter().zip(ys.iter()).zip(zs.iter()).map(|((x, y), z)| (*x, *y, *z)), *color))?;
447                let color2 = *color;
448                match label {
449                    Some(label) => series_anno.label(label.as_str()),
450                    None => series_anno,
451                }.legend(move |(x, y)| PathElement::new(vec![(x, y), (x + LEGEND_WIDTH, y)], &color2));
452            },
453            Series3d::DashedLine(xs, ys, zs, color, label) => {
454                let series_anno = chart.draw_series(DashedLineSeries::new(xs.iter().zip(ys.iter()).zip(zs.iter()).map(|((x, y), z)| (*x, *y, *z)), DASH_SIZE, DASH_SPACING, Into::<ShapeStyle>::into(color)))?;
455                let color2 = *color;
456                match label {
457                    Some(label) => series_anno.label(label.as_str()),
458                    None => series_anno,
459                }.legend(move |(x, y)| DashedPathElement::new(vec![(x, y), (x + LEGEND_WIDTH, y)], DASH_SIZE, DASH_SPACING, Into::<ShapeStyle>::into(&color2)));
460            },
461            Series3d::DottedLine(xs, ys, zs, color, label) => {
462                let color2 = *color;
463                let series_anno = chart.draw_series(DottedLineSeries::new(xs.iter().zip(ys.iter()).zip(zs.iter()).map(|((x, y), z)| (*x, *y, *z)), DOT_SHIFT, DOT_SPACING, move |p| Circle::new(p, POINT_SIZE, Into::<ShapeStyle>::into(&color2).filled())))?;
464                let color3 = *color;
465                match label {
466                    Some(label) => series_anno.label(label.as_str()),
467                    None => series_anno,
468                }.legend(move |(x, y)| DottedPathElement::new(vec![(x, y), (x + LEGEND_WIDTH, y)], DOT_SHIFT, DOT_SPACING, move |p| Circle::new(p, POINT_SIZE, Into::<ShapeStyle>::into(&color3).filled())));
469            },
470            Series3d::Circle(xs, ys, zs, color, label) => {
471                let series_anno = chart.draw_series(PointSeries::<_, _, Circle<_, i32>, i32>::new(xs.iter().zip(ys.iter()).zip(zs.iter()).map(|((x, y), z)| (*x, *y, *z)), MARKER_SIZE, color))?;
472                let color2 = *color;
473                match label {
474                    Some(label) => series_anno.label(label.as_str()),
475                    None => series_anno,
476                }.legend(move |(x, y)| Circle::new((x + LEGEND_WIDTH / 2, y), MARKER_SIZE, &color2));
477            },
478            Series3d::Cross(xs, ys, zs, color, label) => {
479                let series_anno = chart.draw_series(PointSeries::<_, _, Cross<_, i32>, i32>::new(xs.iter().zip(ys.iter()).zip(zs.iter()).map(|((x, y), z)| (*x, *y, *z)), MARKER_SIZE, color))?;
480                let color2 = *color;
481                match label {
482                    Some(label) => series_anno.label(label.as_str()),
483                    None => series_anno,
484                }.legend(move |(x, y)| Cross::new((x + LEGEND_WIDTH / 2, y), MARKER_SIZE, &color2));
485            },
486            Series3d::Point(xs, ys, zs, color, label) => {
487                let series_anno = chart.draw_series(PointSeries::<_, _, Circle<_, i32>, i32>::new(xs.iter().zip(ys.iter()).zip(zs.iter()).map(|((x, y), z)| (*x, *y, *z)), POINT_SIZE, color.filled()))?;
488                let color2 = *color;
489                match label {
490                    Some(label) => series_anno.label(label.as_str()),
491                    None => series_anno,
492                }.legend(move |(x, y)| Circle::new((x + LEGEND_WIDTH / 2, y), POINT_SIZE, color2.filled()));
493            },
494            Series3d::Triangle(xs, ys, zs, color, label) => {
495                let series_anno = chart.draw_series(PointSeries::<_, _, TriangleMarker<_, i32>, i32>::new(xs.iter().zip(ys.iter()).zip(zs.iter()).map(|((x, y), z)| (*x, *y, *z)), MARKER_SIZE, color))?;
496                let color2 = *color;
497                match label {
498                    Some(label) => series_anno.label(label.as_str()),
499                    None => series_anno,
500                }.legend(move |(x, y)| TriangleMarker::new((x + LEGEND_WIDTH / 2, y), MARKER_SIZE, &color2));
501            },
502            Series3d::XYSurface(xs, ys, zs, color, label, xis, yis) => {
503                let series_anno = chart.draw_series(SurfaceSeries::xoy(xs.iter().map(|x| *x), ys.iter().map(|y| *y), |x, y| {
504                        let xi = xis.get(&F32Key::new(x));
505                        let yi = yis.get(&F32Key::new(y));
506                        match (xi, yi) {
507                            (Some(xi), Some(yi)) => zs.get(yi * xs.len() + xi).map(|z| *z).unwrap_or(0.0),
508                            (_, _) => 0.0,
509                        }
510                }).style(color.mix(SURFACE_MIX).filled()))?;
511                let color2 = *color;
512                match label {
513                    Some(label) => series_anno.label(label.as_str()),
514                    None => series_anno,
515                }.legend(move |(x, y)| Rectangle::new([(x, y - LEGEND_HEIGHT / 2), (x + LEGEND_WIDTH, y + LEGEND_HEIGHT / 2)], color2.mix(SURFACE_MIX).filled()));
516            },
517            Series3d::XZSurface(xs, ys, zs, color, label, xis, zis) => {
518                let series_anno = chart.draw_series(SurfaceSeries::xoz(xs.iter().map(|x| *x), zs.iter().map(|z| *z), |x, z| {
519                        let xi = xis.get(&F32Key::new(x));
520                        let zi = zis.get(&F32Key::new(z));
521                        match (xi, zi) {
522                            (Some(xi), Some(zi)) => ys.get(zi * xs.len() + xi).map(|y| *y).unwrap_or(0.0),
523                            (_, _) => 0.0,
524                        }
525                }).style(color.mix(SURFACE_MIX).filled()))?;
526                let color2 = *color;
527                match label {
528                    Some(label) => series_anno.label(label.as_str()),
529                    None => series_anno,
530                }.legend(move |(x, y)| Rectangle::new([(x, y - LEGEND_HEIGHT / 2), (x + LEGEND_WIDTH, y + LEGEND_HEIGHT / 2)], color2.mix(SURFACE_MIX).filled()));
531            },
532            Series3d::YZSurface(xs, ys, zs, color, label, yis, zis) => {
533                let series_anno = chart.draw_series(SurfaceSeries::yoz(ys.iter().map(|y| *y), zs.iter().map(|z| *z), |y, z| {
534                        let yi = yis.get(&F32Key::new(y));
535                        let zi = zis.get(&F32Key::new(z));
536                        match (yi, zi) {
537                            (Some(yi), Some(zi)) => xs.get(zi * ys.len() + yi).map(|x| *x).unwrap_or(0.0),
538                            (_, _) => 0.0,
539                        }
540                }).style(color.mix(SURFACE_MIX).filled()))?;
541                let color2 = *color;
542                match label {
543                    Some(label) => series_anno.label(label.as_str()),
544                    None => series_anno,
545                }.legend(move |(x, y)| Rectangle::new([(x, y - LEGEND_HEIGHT / 2), (x + LEGEND_WIDTH, y + LEGEND_HEIGHT / 2)], color2.mix(SURFACE_MIX).filled()));
546            },
547        }
548    }
549    chart
550        .configure_series_labels()
551        .background_style(&WHITE)
552        .border_style(&BLACK)
553        .draw()?;
554    root.present()?;
555    Ok(())
556}
557
558fn draw_histogram<T: IntoDrawingArea>(backend: T, chart_desc: &Chart, axes: &HistogramAxes, serieses: &[HistogramSeries]) -> result::Result<(), Box<dyn error::Error>>
559    where T::ErrorType: 'static
560{
561    let root = backend.into_drawing_area();
562    root.fill(&WHITE)?;
563    let mut chart_builder = ChartBuilder::on(&root);
564    match &chart_desc.title {
565        Some(title) => {
566            chart_builder.caption(title, ("sans-serif", TITLE_FONT_SIZE).into_font());
567        },
568        None => (),
569    }
570    let mut chart = chart_builder
571        .margin(MARGIN)
572        .x_label_area_size(X_LABEL_AREA_SIZE)
573        .y_label_area_size(Y_LABEL_AREA_SIZE)
574        .build_cartesian_2d(axes.x.as_slice().into_segmented(), axes.y.clone())?;
575    chart
576        .configure_mesh()
577        .x_label_formatter(&|x| {
578                match x {
579                    SegmentValue::Exact(x) => format!("{}", x),
580                    SegmentValue::CenterOf(x) => format!("{}", x),
581                    SegmentValue::Last => format!("last"),
582                }
583        }).draw()?;
584    for series in serieses {
585        match series {
586            HistogramSeries(data, color, label) => {
587                let series_anno = chart.draw_series(Histogram::vertical(&chart).style(color.mix(HISTOGRAM_MIX).filled()).data(data.iter().map(|v| (v, 1))))?;
588                let color2 = *color;
589                match label {
590                    Some(label) => series_anno.label(label.as_str()),
591                    None => series_anno,
592                }.legend(move |(x, y)| Rectangle::new([(x, y - LEGEND_HEIGHT / 2), (x + LEGEND_WIDTH, y + LEGEND_HEIGHT / 2)], color2.mix(HISTOGRAM_MIX).filled()));
593            },
594        }
595    }
596    chart
597        .configure_series_labels()
598        .background_style(&WHITE)
599        .border_style(&BLACK)
600        .draw()?;
601    root.present()?;
602    Ok(())
603}
604
605/// A structure of plotting.
606///
607/// The plotting contains chart, axes, and series.
608#[derive(Clone, Debug)]
609pub enum Plot
610{
611    /// A plotting for a `plot` built-in function.
612    Plot(Chart, Axes2d, Vec<Series2d>),
613    /// A plotting for a `plot3` built-in function.
614    Plot3(Chart, Axes3d, Vec<Series3d>),
615    /// A plotting for a `histogram` built-in function.
616    Histogram(Chart, HistogramAxes, Vec<HistogramSeries>),
617}
618
619impl Plot
620{
621    /// Returns the chart.
622    pub fn chart(&self) -> &Chart
623    {
624        match self {
625            Plot::Plot(chart, _, _) => chart,
626            Plot::Plot3(chart, _, _) => chart,
627            Plot::Histogram(chart, _, _) => chart,
628        }
629    }
630    
631    fn draw_with_backend<T: IntoDrawingArea>(&self, backend: T) -> result::Result<(), Box<dyn error::Error>>
632        where T::ErrorType: 'static
633    {
634        match self {
635            Plot::Plot(chart, axes, serieses) => draw_chart2d(backend, chart, axes, serieses.as_slice()),
636            Plot::Plot3(chart, axes, serieses) => draw_chart3d(backend, chart, axes, serieses.as_slice()),
637            Plot::Histogram(chart, axes, serieses) => draw_histogram(backend, chart, axes, serieses.as_slice()),
638        }
639    }
640
641    /// Draws and saves chart to the file.
642    pub fn draw_and_save_to_file(&self) -> result::Result<(), Box<dyn error::Error>>
643    {
644        match &self.chart().file {
645            Some(file) => {
646                let path_buf = PathBuf::from(file);
647                let size = self.chart().size.unwrap_or(DEFAULT_SIZE);
648                match path_buf.extension() {
649                    Some(ext) if ext == "svg" => self.draw_with_backend(SVGBackend::new(path_buf.as_path(), size)),
650                    _ => self.draw_with_backend(BitMapBackend::new(path_buf.as_path(), size)),
651                }
652            },
653            None => Ok(()),
654        }
655    }
656    
657    /// Draws the chart on the buffer.
658    pub fn draw_on_buffer(&self, buf: &mut [u8], size: (u32, u32)) -> result::Result<(), Box<dyn error::Error>>
659    { self.draw_with_backend(BitMapBackend::<BGRXPixel>::with_buffer_and_format(buf, size)?) }
660
661    /// Draws the chart on the window.
662    pub fn draw_on_window(plot: &Arc<Self>, env: &Env) -> Result<Option<Option<WindowId>>>
663    {
664        if plot.chart().has_window {
665            let shared_env_g = rw_lock_read(env.shared_env())?;
666            match shared_env_g.event_loop_proxy() {
667                Some(event_loop_proxy) => {
668                    let (tx, rx) = channel();
669                    match event_loop_proxy.send_event(PlotterAppEvent::Plot(plot.clone(), tx)) {
670                        Ok(()) => (),
671                        Err(err) => return Err(Error::Winit(Box::new(err))),
672                    }
673                    Ok(Some(receiver_recv(&rx)?))
674                },
675                None => Ok(Some(None)),
676            }
677        } else {
678            Ok(None)
679        }
680    }
681}
682
683/// An enumeration of plotter application event.
684///
685/// The plotter application events are used by a thread of main loop to communication with a
686/// thread of plotter application.
687#[derive(Clone, Debug)]
688pub enum PlotterAppEvent
689{
690    /// A plotting event.
691    Plot(Arc<Plot>, Sender<Option<WindowId>>),
692    /// A quit event
693    Quit,
694}
695
696struct WindowState
697{
698    window: Arc<Window>,
699    surface: Surface<DisplayHandle<'static>, Arc<Window>>,
700    plot: Arc<Plot>,
701    size: (u32, u32),
702}
703
704impl WindowState
705{
706    fn new(app: &PlotterApp, window: Arc<Window>, plot: Arc<Plot>, size: (u32, u32)) -> result::Result<WindowState, Box<dyn error::Error>>
707    {
708        let surface = Surface::new(app.context.as_ref().unwrap(), window.clone())?;
709        Ok(WindowState { window, surface, plot, size })
710    }
711    
712    fn resize(&mut self, size: PhysicalSize<u32>)
713    {
714        self.size = (size.width, size.height);
715        match (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) {
716            (Some(width), Some(height)) => self.surface.resize(width, height).unwrap(),
717            (_, _) => (),
718        }
719        self.window.request_redraw();
720    }
721    
722    fn draw(&mut self) -> result::Result<(), Box<dyn error::Error>>
723    {
724        if self.size.0 > 0 && self.size.1 > 0 {
725            let mut buf = self.surface.buffer_mut()?;
726            let plot_buf: &mut [u8] = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, buf.len() * size_of::<u32>()) };
727            self.plot.draw_on_buffer(plot_buf, self.size)?;
728            self.window.pre_present_notify();
729            buf.present()?;
730        }
731        Ok(())
732    }
733}
734
735/// A structure of plotter application.
736///
737/// A plotter application shows windows with charts.
738pub struct PlotterApp
739{
740    icon: Icon,
741    windows: HashMap<WindowId, WindowState>,
742    context: Option<Context<DisplayHandle<'static>>>,
743}
744
745impl PlotterApp
746{
747    /// Creates a plotter application.
748    pub fn new(event_loop: &EventLoop<PlotterAppEvent>) -> Self
749    {
750        let context = Some(Context::new(unsafe { transmute::<DisplayHandle<'_>, DisplayHandle<'static>>(event_loop.display_handle().unwrap()) }).unwrap());
751        let image = image::load_from_memory(include_bytes!("icon.png")).unwrap();
752        let rgba_image = image.into_rgba8();
753        let (width, height) = rgba_image.dimensions();
754        let rgba = rgba_image.into_raw();
755        let icon = Icon::from_rgba(rgba, width, height).unwrap();
756        PlotterApp { icon, windows: HashMap::new(), context, }
757    }
758
759    fn create_window(&mut self, event_loop: &ActiveEventLoop, size: (u32, u32), plot: Arc<Plot>) -> result::Result<WindowId, Box<dyn error::Error>>
760    {
761        let window_attrs = Window::default_attributes().with_title("Unlab-gpu window").with_inner_size(PhysicalSize::new(size.0, size.1)).with_window_icon(Some(self.icon.clone()));
762        let window = event_loop.create_window(window_attrs)?;
763        let window_id = window.id();
764        self.windows.insert(window_id, WindowState::new(self, Arc::new(window), plot, size)?);
765        Ok(window_id)
766    }
767}
768
769impl ApplicationHandler<PlotterAppEvent> for PlotterApp
770{
771    fn resumed(&mut self, _event_loop: &ActiveEventLoop)
772    {}
773    
774    fn window_event(&mut self, _event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent)
775    {
776        let window = match self.windows.get_mut(&window_id) {
777            Some(tmp_window) => tmp_window,
778            None => return,
779        };
780        match event {
781           WindowEvent::Resized(size) => window.resize(size),
782           WindowEvent::CloseRequested => {
783               self.windows.remove(&window_id);
784           },
785           WindowEvent::RedrawRequested => {
786               match window.draw() {
787                   Ok(()) => (),
788                   Err(err) => eprintln!("plotter app error: {}", err),
789               }
790           },
791           _ => (),
792        }
793    }
794
795    fn user_event(&mut self, event_loop: &ActiveEventLoop, event: PlotterAppEvent)
796    {
797        match event {
798            PlotterAppEvent::Plot(plot, tx) => {
799                let window_id = match plot.chart().window_id {
800                    Some(tmp_window_id) => {
801                        match self.windows.get_mut(&tmp_window_id) {
802                            Some(window) => {
803                                window.plot = plot;
804                                window.window.request_redraw();
805                                Some(tmp_window_id)
806                            },
807                            None => None,
808                        }
809                    }
810                    None => {
811                        match self.create_window(event_loop, plot.chart().size.unwrap_or(DEFAULT_SIZE), plot) {
812                            Ok(tmp_window_id) => Some(tmp_window_id),
813                            Err(err) => {
814                                eprintln!("{}", err);
815                                None
816                            },
817                        }
818                    },
819                };
820                match tx.send(window_id) {
821                    Ok(()) => (),
822                    Err(_) => eprintln!("plotter app error: can't send object"),
823                }
824            },
825            PlotterAppEvent::Quit => event_loop.exit(),
826        }
827    }
828    
829    fn exiting(&mut self, _event_loop: &ActiveEventLoop)
830    { self.context = None; }
831}
832
833fn create_size(value: &Value) -> Result<(u32, u32)>
834{
835    match value {
836        Value::Ref(object) => {
837            let object_g = rw_lock_read(&*object)?;
838            match &*object_g {
839                MutObject::Array(elems) => {
840                    if elems.len() != 2 {
841                        return Err(Error::Interp(String::from("invalid numner of elements for size")));
842                    }
843                    let width = match elems.get(0) {
844                        Some(elem) => elem.to_i64(),
845                        None => return Err(Error::Interp(String::from("no element for size"))),
846                    };
847                    let height = match elems.get(1) {
848                        Some(elem) => elem.to_i64(),
849                        None => return Err(Error::Interp(String::from("no element for size"))),
850                    };
851                    if width < 0 {
852                        return Err(Error::Interp(String::from("too small width")));
853                    }
854                    if width > (u32::MAX as i64) {
855                        return Err(Error::Interp(String::from("too large width")));
856                    }
857                    if height < 0 {
858                        return Err(Error::Interp(String::from("too small height")));
859                    }
860                    if height > (u32::MAX as i64) {
861                        return Err(Error::Interp(String::from("too large height")));
862                    }
863                    Ok((width as u32, height as u32))
864                },
865                _ => Err(Error::Interp(String::from("unsupported type for size"))),
866            }
867        },
868        _ => Err(Error::Interp(String::from("unsupported type for size"))),
869    }
870}
871
872fn create_chart(value: &Value) -> Result<Chart>
873{
874    match value {
875        Value::Ref(object) => {
876            let object_g = rw_lock_read(&*object)?;
877            match &*object_g {
878                MutObject::Struct(fields) => {
879                    let title = match fields.get(&String::from("title")) {
880                        Some(field) => {
881                            match field {
882                                Value::None => None,
883                                _ => Some(format!("{}", field)),
884                            }
885                        },
886                        None => None,
887                    };
888                    let window_id = match fields.get(&String::from("windowid")) {
889                        Some(field) => {
890                            match field {
891                                Value::None => None,
892                                Value::Object(object) => {
893                                    match &**object {
894                                        Object::WindowId(tmp_window_id) => Some(*tmp_window_id),
895                                        _ => return Err(Error::Interp(String::from("invalid type for window identifier"))),
896                                    }
897                                },
898                                _ => return Err(Error::Interp(String::from("unsupported type for window identifier"))),
899                            }
900                        },
901                        None => None,
902                    };
903                    let has_window = match fields.get(&String::from("haswindow")) {
904                        Some(field) => {
905                            match field {
906                                Value::None => true,
907                                _ => field.to_bool(),
908                            }
909                        },
910                        None => true,
911                    };
912                    let file = match fields.get(&String::from("file")) {
913                        Some(field) => {
914                            match field {
915                                Value::None => None,
916                                _ => Some(format!("{}", field)),
917                            }
918                        },
919                        None => None,
920                    };
921                    let size = match fields.get(&String::from("size")) {
922                        Some(field) => {
923                            match field {
924                                Value::None => None,
925                                _ => Some(create_size(field)?),
926                            }
927                        },
928                        None => None,
929                    };
930                    Ok(Chart { title, window_id, has_window, file, size, })
931                },
932                _ => Err(Error::Interp(String::from("unsupported type for plotter function"))),
933            }
934        },
935        _ => Err(Error::Interp(String::from("unsupported type for plotter function"))),
936    }
937}
938
939fn create_f32_range(value: &Value) -> Result<Range<f32>>
940{
941    match value {
942        Value::Ref(object) => {
943            let object_g = rw_lock_read(&*object)?;
944            match &*object_g {
945                MutObject::Array(elems) => {
946                    if elems.len() != 2 {
947                        return Err(Error::Interp(String::from("invalid number of elements for range")));
948                    }
949                    let start = match elems.get(0) {
950                        Some(elem) => elem.to_f32(),
951                        None => return Err(Error::Interp(String::from("no element for range"))),
952                    };
953                    let end = match elems.get(1) {
954                        Some(elem) => elem.to_f32(),
955                        None => return Err(Error::Interp(String::from("no element for range"))),
956                    };
957                    if start.is_nan() {
958                        return Err(Error::Interp(String::from("range start is nan")));
959                    }
960                    if start.is_infinite() {
961                        return Err(Error::Interp(String::from("range start is infinite")));
962                    }
963                    if end.is_nan() {
964                        return Err(Error::Interp(String::from("range end is nan")));
965                    }
966                    if end.is_infinite() {
967                        return Err(Error::Interp(String::from("range end is infinite")));
968                    }
969                    Ok(start..end)
970                },
971                _ => Err(Error::Interp(String::from("unsupported type for range"))),
972            }
973        },
974        _ => Err(Error::Interp(String::from("unsupported type for range"))),
975    }
976}
977
978fn create_axes2d(value: &Value) -> Result<Axes2d>
979{
980    match value {
981        Value::Ref(object) => {
982            let object_g = rw_lock_read(&*object)?;
983            match &*object_g {
984                MutObject::Struct(fields) => {
985                    let x = match fields.get(&String::from("x")) {
986                        Some(field) => create_f32_range(field)?,
987                        None => return Err(Error::Interp(String::from("no field x"))),
988                    };
989                    let y = match fields.get(&String::from("y")) {
990                        Some(field) => create_f32_range(field)?,
991                        None => return Err(Error::Interp(String::from("no field y"))),
992                    };
993                    Ok(Axes2d { x, y, })
994                },
995                _ => Err(Error::Interp(String::from("unsupported type for plotter function"))),
996            }
997        },
998        _ => Err(Error::Interp(String::from("unsupported type for plotter function"))),
999    }
1000}
1001
1002fn create_axes3d(value: &Value) -> Result<Axes3d>
1003{
1004    match value {
1005        Value::Ref(object) => {
1006            let object_g = rw_lock_read(&*object)?;
1007            match &*object_g {
1008                MutObject::Struct(fields) => {
1009                    let x = match fields.get(&String::from("x")) {
1010                        Some(field) => create_f32_range(field)?,
1011                        None => return Err(Error::Interp(String::from("no field x"))),
1012                    };
1013                    let y = match fields.get(&String::from("y")) {
1014                        Some(field) => create_f32_range(field)?,
1015                        None => return Err(Error::Interp(String::from("no field y"))),
1016                    };
1017                    let z = match fields.get(&String::from("z")) {
1018                        Some(field) => create_f32_range(field)?,
1019                        None => return Err(Error::Interp(String::from("no field z"))),
1020                    };
1021                    Ok(Axes3d { x, y, z })
1022                },
1023                _ => Err(Error::Interp(String::from("unsupported type for plotter function"))),
1024            }
1025        },
1026        _ => Err(Error::Interp(String::from("unsupported type for plotter function"))),
1027    }
1028}
1029
1030fn create_histogram_values(value: &Value) -> Result<Vec<HistogramValue>>
1031{
1032    match value.iter()? {
1033        Some(iter) => {
1034            let mut values: Vec<HistogramValue> = Vec::new();
1035            for elem in iter {
1036                match elem {
1037                    Ok(elem) => {
1038                        match elem {
1039                            Value::Bool(b) => values.push(HistogramValue::Bool(b)),
1040                            Value::Int(n) => values.push(HistogramValue::Int(n)),
1041                            Value::Float(n) => values.push(HistogramValue::Float(n)),
1042                            _ => values.push(HistogramValue::String(Box::new(format!("{}", elem)))),
1043                        }
1044                    },
1045                    Err(err) => return Err(err),
1046                }
1047            }
1048            Ok(values)
1049        },
1050        None => Err(Error::Interp(String::from("value isn't iterable"))),
1051    }
1052}
1053
1054fn create_usize_range(value: &Value) -> Result<Range<usize>>
1055{
1056    match value {
1057        Value::Ref(object) => {
1058            let object_g = rw_lock_read(&*object)?;
1059            match &*object_g {
1060                MutObject::Array(elems) => {
1061                    if elems.len() != 2 {
1062                        return Err(Error::Interp(String::from("invalid numner of elements for range")));
1063                    }
1064                    let start = match elems.get(0) {
1065                        Some(elem) => elem.to_i64(),
1066                        None => return Err(Error::Interp(String::from("no element for range"))),
1067                    };
1068                    let end = match elems.get(1) {
1069                        Some(elem) => elem.to_i64(),
1070                        None => return Err(Error::Interp(String::from("no element for range"))),
1071                    };
1072                    if start < 0 {
1073                        return Err(Error::Interp(String::from("too small range start")));
1074                    }
1075                    if start > (isize::MAX as i64) {
1076                        return Err(Error::Interp(String::from("too large range start")));
1077                    }
1078                    if end < 0 {
1079                        return Err(Error::Interp(String::from("too small range end")));
1080                    }
1081                    if end > (isize::MAX as i64) {
1082                        return Err(Error::Interp(String::from("too large range end")));
1083                    }
1084                    Ok((start as usize)..(end as usize))
1085                },
1086                _ => Err(Error::Interp(String::from("unsupported type for range"))),
1087            }
1088        },
1089        _ => Err(Error::Interp(String::from("unsupported type for range"))),
1090    }
1091}
1092
1093fn create_histogram_axes(value: &Value) -> Result<HistogramAxes>
1094{
1095    match value {
1096        Value::Ref(object) => {
1097            let object_g = rw_lock_read(&*object)?;
1098            match &*object_g {
1099                MutObject::Struct(fields) => {
1100                    let x = match fields.get(&String::from("x")) {
1101                        Some(field) => create_histogram_values(field)?,
1102                        None => return Err(Error::Interp(String::from("no field x"))),
1103                    };
1104                    let y = match fields.get(&String::from("y")) {
1105                        Some(field) => create_usize_range(field)?,
1106                        None => return Err(Error::Interp(String::from("no field y"))),
1107                    };
1108                    Ok(HistogramAxes { x, y, })
1109                },
1110                _ => Err(Error::Interp(String::from("unsupported type for plotter function"))),
1111            }
1112        },
1113        _ => Err(Error::Interp(String::from("unsupported type for plotter function"))),
1114    }
1115}
1116
1117fn create_f32s(value: &Value) -> Result<Vec<f32>>
1118{
1119    match value.iter()? {
1120        Some(iter) => {
1121            let mut xs: Vec<f32> = Vec::new();
1122            for elem in iter {
1123                match elem {
1124                    Ok(elem) => xs.push(elem.to_f32()),
1125                    Err(err) => return Err(err),
1126                }
1127            }
1128            Ok(xs)
1129        },
1130        None => Err(Error::Interp(String::from("value isn't iterable"))),
1131    }
1132}
1133
1134fn create_f32s_for_fun_value(interp: &mut Interp, env: &mut Env, fun_value: &Value, xs: &[f32]) -> Result<Vec<f32>>
1135{
1136    let mut ys = vec![0.0f32; xs.len()];
1137    for (i, x) in xs.iter().enumerate() {
1138        ys[i] = fun_value.apply(interp, env, &[Value::Float(*x)])?.to_f32();
1139    }
1140    Ok(ys)
1141}
1142
1143#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
1144enum SeriesKind
1145{
1146    Line,
1147    DashedLine,
1148    DottedLine,
1149    Circle,
1150    Cross,
1151    Point,
1152    Triangle,
1153    XYSurface,
1154    XZSurface,
1155    YZSurface,
1156}
1157
1158fn str_to_color(s: &str, color_idx: usize) -> Result<RGBColor>
1159{
1160    if s.is_empty() {
1161        match COLORS.get(color_idx) {
1162            Some(color) => Ok(*color),
1163            None => Err(Error::Interp(String::from("invalid color index"))),
1164        }
1165    } else if s == "r" || s == "red" {
1166        Ok(RED)
1167    } else if s == "g" || s == "green" {
1168        Ok(GREEN)
1169    } else if s == "b" || s == "blue" {
1170        Ok(BLUE)
1171    } else if s == "c" || s == "cyan" {
1172        Ok(CYAN)
1173    } else if s == "m" || s == "magenta" {
1174        Ok(MAGENTA)
1175    } else if s == "y" || s == "yellow" {
1176        Ok(YELLOW)
1177    } else if s == "k" || s == "black" {
1178        Ok(BLACK)
1179    } else if s == "w" || s == "white" {
1180        Ok(WHITE)
1181    } else {
1182        Err(Error::Interp(String::from("invalid color")))
1183    }
1184}
1185
1186fn str_to_opt_string(s: &str) -> Option<String>
1187{
1188    if !s.is_empty() {
1189        Some(String::from(s))
1190    } else {
1191        None
1192    }
1193}
1194
1195fn create_series_tuple(value: &Value, color_idx: usize) -> Result<(SeriesKind, RGBColor, Option<String>)>
1196{
1197    let s = format!("{}", value);
1198    let (t, u) = match s.split_once(",") {
1199        Some((tmp_t, tmp_u)) => (tmp_t, tmp_u),
1200        None => (s.as_str(), ""),
1201    };
1202    let (series_kind, t2) =  if t.starts_with("--") {
1203        (SeriesKind::DashedLine, &t[2..])
1204    } else if t.starts_with("-") {
1205        (SeriesKind::Line, &t[1..])
1206    } else if t.starts_with(":") {
1207        (SeriesKind::DottedLine, &t[1..])
1208    } else if t.starts_with("o") {
1209        (SeriesKind::Circle, &t[1..])
1210    } else if t.starts_with("x") {
1211        (SeriesKind::Cross, &t[1..])
1212    } else if t.starts_with(".") {
1213        (SeriesKind::Point, &t[1..])
1214    } else if t.starts_with("^") {
1215        (SeriesKind::Triangle, &t[1..])
1216    } else if t.starts_with("sxy") {
1217        (SeriesKind::XYSurface, &t[3..])
1218    } else if t.starts_with("sxz") {
1219        (SeriesKind::XZSurface, &t[3..])
1220    } else if t.starts_with("syz") {
1221        (SeriesKind::YZSurface, &t[3..])
1222    } else {
1223        (SeriesKind::Line, t)
1224    };
1225    let color = str_to_color(t2, color_idx)?;
1226    let label = str_to_opt_string(u);
1227    Ok((series_kind, color, label))
1228}
1229
1230fn create_series2d(interp: &mut Interp, env: &mut Env, x_value: &Value, y_value: &Value, s_value: &Value, color_idx: usize) -> Result<Series2d>
1231{
1232    let (series_kind, color, label) = create_series_tuple(s_value, color_idx)?;
1233    let (xs, ys) = match (x_value.is_fun(), y_value.is_fun()) {
1234        (false, false) => (create_f32s(x_value)?, create_f32s(y_value)?),
1235        (false, true) => {
1236            let tmp_xs = create_f32s(x_value)?;
1237            let tmp_ys = create_f32s_for_fun_value(interp, env, y_value, tmp_xs.as_slice())?;
1238            (tmp_xs, tmp_ys)
1239        },
1240        (true, false) => {
1241            let tmp_ys = create_f32s(y_value)?;
1242            let tmp_xs = create_f32s_for_fun_value(interp, env, x_value, tmp_ys.as_slice())?;
1243            (tmp_xs, tmp_ys)
1244        },
1245        (_, _) => return Err(Error::Interp(String::from("unsupported types for plotter function"))),
1246    };
1247    match series_kind {
1248        SeriesKind::Line => Ok(Series2d::Line(xs, ys, color, label)),
1249        SeriesKind::DashedLine => Ok(Series2d::DashedLine(xs, ys, color, label)),
1250        SeriesKind::DottedLine => Ok(Series2d::DottedLine(xs, ys, color, label)),
1251        SeriesKind::Circle => Ok(Series2d::Circle(xs, ys, color, label)),
1252        SeriesKind::Cross => Ok(Series2d::Cross(xs, ys, color, label)),
1253        SeriesKind::Point => Ok(Series2d::Point(xs, ys, color, label)),
1254        SeriesKind::Triangle => Ok(Series2d::Triangle(xs, ys, color, label)),
1255        _ => Err(Error::Interp(String::from("invalid series kind")))
1256    }
1257}
1258
1259fn create_surface_f32s(value: &Value, x_count: usize, y_count: usize, x_name: &str, y_name: &str, z_name: &str) -> Result<Vec<f32>>
1260{
1261    match value.iter()? {
1262        Some(iter) => {
1263            let mut xs: Vec<f32> = Vec::new();
1264            let mut row_count = 0usize;
1265            for row in iter {
1266                match row {
1267                    Ok(row) => {
1268                        let ys = create_f32s(&row)?;
1269                        if ys.len() != x_count {
1270                            return Err(Error::Interp(format!("number of {} columns isn't equal to number of {} elements", z_name, x_name)))
1271                        }
1272                        xs.extend_from_slice(ys.as_slice());
1273                    },
1274                    Err(err) => return Err(err),
1275                }
1276                match row_count.checked_add(1) {
1277                    Some(new_row_count) => row_count = new_row_count,
1278                    None => return Err(Error::Interp(format!("too many {} rows", z_name))),
1279                }
1280            }
1281            if row_count != y_count {
1282                return Err(Error::Interp(format!("number of {} rows isn't equal to number of {} elements", z_name, y_name)))
1283            }
1284            Ok(xs)
1285        },
1286        None => Err(Error::Interp(String::from("value isn't iterable"))),
1287    }
1288}
1289
1290fn checked_mul_row_count_and_col_count(row_count: usize, col_count: usize, name: &str) -> Result<usize>
1291{
1292    if row_count > (isize::MAX as usize) {
1293        return Err(Error::Interp(String::from("too large number of rows")));
1294    }
1295    if col_count > (isize::MAX as usize) {
1296        return Err(Error::Interp(String::from("too large number of columns")));
1297    }
1298    match row_count.checked_mul(col_count) {
1299        Some(len) => {
1300            if len > (isize::MAX as usize) {
1301                return Err(Error::Interp(format!("too large number of {} elements", name)));
1302            }
1303            match (len as isize).checked_mul(size_of::<f32>() as isize) {
1304                Some(_) => Ok(len as usize),
1305                None => Err(Error::Interp(format!("too large number of {} elements", name))),
1306            }
1307        },
1308        None => Err(Error::Interp(format!("too large number of {} elements", name))),
1309    }
1310}
1311
1312fn create_surface_f32s_for_fun_value(interp: &mut Interp, env: &mut Env, fun_value: &Value, xs: &[f32], ys: &[f32], z_name: &str) -> Result<Vec<f32>>
1313{
1314    let len = checked_mul_row_count_and_col_count(ys.len(), xs.len(), z_name)?;
1315    let mut zs = vec![0.0f32; len];
1316    for (yi, y) in ys.iter().enumerate() {
1317        for (xi, x) in xs.iter().enumerate() {
1318            zs[yi * xs.len() + xi] = fun_value.apply(interp, env, &[Value::Float(*x), Value::Float(*y)])?.to_f32();
1319        }
1320    }
1321    Ok(zs)
1322}
1323
1324fn create_indices(xs: &[f32]) -> BTreeMap<F32Key, usize>
1325{
1326    let mut idxs: BTreeMap<F32Key, usize> = BTreeMap::new();
1327    for (i, x) in xs.iter().enumerate() {
1328        idxs.insert(F32Key::new(*x), i);
1329    }
1330    idxs
1331}
1332
1333fn create_series3d(interp: &mut Interp, env: &mut Env, x_value: &Value, y_value: &Value, z_value: &Value, s_value: &Value, color_idx: usize) -> Result<Series3d>
1334{
1335    let (series_kind, color, label) = create_series_tuple(s_value, color_idx)?;
1336    let (xs, ys, zs) = match series_kind {
1337        SeriesKind::XYSurface => {
1338            let xs = create_f32s(x_value)?;
1339            let ys = create_f32s(y_value)?;
1340            let zs = if z_value.is_fun() {
1341                create_surface_f32s_for_fun_value(interp, env, z_value, xs.as_slice(), ys.as_slice(), "z")?
1342            } else {
1343                create_surface_f32s(z_value, xs.len(), ys.len(), "x", "y", "z")?
1344            };
1345            let xis = create_indices(xs.as_slice());
1346            let yis = create_indices(ys.as_slice());
1347            return Ok(Series3d::XYSurface(xs, ys, zs, color, label, xis, yis));
1348        },
1349        SeriesKind::XZSurface => {
1350            let xs = create_f32s(x_value)?;
1351            let zs = create_f32s(z_value)?;
1352            let ys = if y_value.is_fun() {
1353                create_surface_f32s_for_fun_value(interp, env, y_value, xs.as_slice(), zs.as_slice(), "y")?
1354            } else {
1355                create_surface_f32s(y_value, xs.len(), zs.len(), "x", "z", "y")?
1356            };
1357            let xis = create_indices(xs.as_slice());
1358            let zis = create_indices(zs.as_slice());
1359            return Ok(Series3d::XZSurface(xs, ys, zs, color, label, xis, zis));
1360        },
1361        SeriesKind::YZSurface => {
1362            let ys = create_f32s(y_value)?;
1363            let zs = create_f32s(z_value)?;
1364            let xs = if x_value.is_fun() {
1365                create_surface_f32s_for_fun_value(interp, env, x_value, ys.as_slice(), zs.as_slice(), "x")?
1366            } else {
1367                create_surface_f32s(x_value, ys.len(), zs.len(), "y", "z", "x")?
1368            };
1369            let yis = create_indices(ys.as_slice());
1370            let zis = create_indices(zs.as_slice());
1371            return Ok(Series3d::YZSurface(xs, ys, zs, color, label, yis, zis));
1372        },
1373        _ => {
1374            match (x_value.is_fun(), y_value.is_fun(), z_value.is_fun()) {
1375                (false, false, false) => (create_f32s(x_value)?, create_f32s(y_value)?, create_f32s(z_value)?),
1376                (false, true, true) => {
1377                    let tmp_xs = create_f32s(x_value)?;
1378                    let tmp_ys = create_f32s_for_fun_value(interp, env, y_value, tmp_xs.as_slice())?;
1379                    let tmp_zs = create_f32s_for_fun_value(interp, env, z_value, tmp_xs.as_slice())?;
1380                    (tmp_xs, tmp_ys, tmp_zs)
1381                },
1382                (true, false, true) => {
1383                    let tmp_ys = create_f32s(y_value)?;
1384                    let tmp_xs = create_f32s_for_fun_value(interp, env, x_value, tmp_ys.as_slice())?;
1385                    let tmp_zs = create_f32s_for_fun_value(interp, env, z_value, tmp_ys.as_slice())?;
1386                    (tmp_xs, tmp_ys, tmp_zs)
1387                },
1388                (true, true, false) => {
1389                    let tmp_zs = create_f32s(z_value)?;
1390                    let tmp_xs = create_f32s_for_fun_value(interp, env, x_value, tmp_zs.as_slice())?;
1391                    let tmp_ys = create_f32s_for_fun_value(interp, env, y_value, tmp_zs.as_slice())?;
1392                    (tmp_xs, tmp_ys, tmp_zs)
1393                },
1394                (_, _, _) => return Err(Error::Interp(String::from("unsupported types for plotter function"))),
1395            }
1396        },
1397    };
1398    match series_kind {
1399        SeriesKind::Line => Ok(Series3d::Line(xs, ys, zs, color, label)),
1400        SeriesKind::DashedLine => Ok(Series3d::DashedLine(xs, ys, zs, color, label)),
1401        SeriesKind::DottedLine => Ok(Series3d::DottedLine(xs, ys, zs, color, label)),
1402        SeriesKind::Circle => Ok(Series3d::Circle(xs, ys, zs, color, label)),
1403        SeriesKind::Cross => Ok(Series3d::Cross(xs, ys, zs, color, label)),
1404        SeriesKind::Point => Ok(Series3d::Point(xs, ys, zs, color, label)),
1405        SeriesKind::Triangle => Ok(Series3d::Triangle(xs, ys, zs, color, label)),
1406        _ => Err(Error::Interp(String::from("invalid series kind")))
1407    }
1408}
1409
1410fn create_color_and_label(value: &Value, color_idx: usize) -> Result<(RGBColor, Option<String>)>
1411{
1412    let s = format!("{}", value);
1413    let (t, u) = match s.split_once(",") {
1414        Some((tmp_t, tmp_u)) => (tmp_t, tmp_u),
1415        None => (s.as_str(), ""),
1416    };
1417    let color = str_to_color(t, color_idx)?;
1418    let label = str_to_opt_string(u);
1419    Ok((color, label))
1420}
1421
1422fn create_histogram_series(data_value: &Value, s_value: &Value, color_idx: usize) -> Result<HistogramSeries>
1423{
1424    let (color, label) = create_color_and_label(s_value, color_idx)?;
1425    let data = create_histogram_values(data_value)?;
1426    Ok(HistogramSeries(data, color, label))
1427}
1428
1429fn plot_for_plot(plot: &Arc<Plot>, env: &Env) -> Result<Value>
1430{
1431    let window_id = match Plot::draw_on_window(plot, env)? {
1432        Some(Some(tmp_window_id)) => Some(tmp_window_id),
1433        Some(None) => return Ok(Value::Object(Arc::new(Object::Error(String::from("plot"), String::from("can't create or find window"))))),
1434        None => None,
1435    };
1436    match plot.draw_and_save_to_file() {
1437        Ok(()) => (),
1438        Err(err) => return Ok(Value::Object(Arc::new(Object::Error(String::from("plot"), format!("{}", err))))),
1439    }
1440    match window_id {
1441        Some(window_id) => Ok(Value::Object(Arc::new(Object::WindowId(window_id)))),
1442        None => Ok(Value::Bool(true)),
1443    }
1444}
1445
1446/// A `plot` built-in function.
1447pub fn plot(interp: &mut Interp, env: &mut Env, arg_values: &[Value]) -> Result<Value>
1448{
1449    if arg_values.len() < 1 {
1450        return Err(Error::Interp(String::from("invalid number of arguments")));
1451    }
1452    let mut arg_value_iter = arg_values.iter();
1453    let (chart, axes) = match arg_value_iter.next() {
1454        Some(chart_value) => (create_chart(chart_value)?, create_axes2d(chart_value)?),
1455        None => return Err(Error::Interp(String::from("no argument"))),
1456    };
1457    let mut serieses: Vec<Series2d> = Vec::new();
1458    let mut color_idx = 0usize;
1459    loop {
1460        let x_value = match arg_value_iter.next() {
1461            Some(tmp_x_value) => tmp_x_value,
1462            None => break,
1463        };
1464        let y_value = match arg_value_iter.next() {
1465            Some(tmp_y_value) => tmp_y_value,
1466            None => return Err(Error::Interp(String::from("no argument y"))),
1467        };
1468        let s_value = match arg_value_iter.next() {
1469            Some(tmp_s_value) => tmp_s_value,
1470            None => return Err(Error::Interp(String::from("no argument s"))),
1471        };
1472        serieses.push(create_series2d(interp, env, x_value, y_value, s_value, color_idx)?);
1473        color_idx = (color_idx + 1) % COLORS.len();
1474    }
1475    let plot = Arc::new(Plot::Plot(chart, axes, serieses));
1476    plot_for_plot(&plot, env)
1477}
1478
1479/// A `plot3` built-in function.
1480pub fn plot3(interp: &mut Interp, env: &mut Env, arg_values: &[Value]) -> Result<Value>
1481{
1482    if arg_values.len() < 1 {
1483        return Err(Error::Interp(String::from("invalid number of arguments")));
1484    }
1485    let mut arg_value_iter = arg_values.iter();
1486    let (chart, axes) = match arg_value_iter.next() {
1487        Some(chart_value) => (create_chart(chart_value)?, create_axes3d(chart_value)?),
1488        None => return Err(Error::Interp(String::from("no argument"))),
1489    };
1490    let mut serieses: Vec<Series3d> = Vec::new();
1491    let mut color_idx = 0usize;
1492    loop {
1493        let x_value = match arg_value_iter.next() {
1494            Some(tmp_x_value) => tmp_x_value,
1495            None => break,
1496        };
1497        let y_value = match arg_value_iter.next() {
1498            Some(tmp_y_value) => tmp_y_value,
1499            None => return Err(Error::Interp(String::from("no argument y"))),
1500        };
1501        let z_value = match arg_value_iter.next() {
1502            Some(tmp_y_value) => tmp_y_value,
1503            None => return Err(Error::Interp(String::from("no argument z"))),
1504        };
1505        let s_value = match arg_value_iter.next() {
1506            Some(tmp_s_value) => tmp_s_value,
1507            None => return Err(Error::Interp(String::from("no argument s"))),
1508        };
1509        serieses.push(create_series3d(interp, env, x_value, y_value, z_value, s_value, color_idx)?);
1510        color_idx = (color_idx + 1) % COLORS.len();
1511    }
1512    let plot = Arc::new(Plot::Plot3(chart, axes, serieses));
1513    plot_for_plot(&plot, env)
1514}
1515
1516/// A `histogram` built-in function.
1517pub fn histogram(_interp: &mut Interp, env: &mut Env, arg_values: &[Value]) -> Result<Value>
1518{
1519    if arg_values.len() < 1 {
1520        return Err(Error::Interp(String::from("invalid number of arguments")));
1521    }
1522    let mut arg_value_iter = arg_values.iter();
1523    let (chart, axes) = match arg_value_iter.next() {
1524        Some(chart_value) => (create_chart(chart_value)?, create_histogram_axes(chart_value)?),
1525        None => return Err(Error::Interp(String::from("no argument"))),
1526    };
1527    let mut serieses: Vec<HistogramSeries> = Vec::new();
1528    let mut color_idx = 0usize;
1529    loop {
1530        let data_value = match arg_value_iter.next() {
1531            Some(tmp_data_value) => tmp_data_value,
1532            None => break,
1533        };
1534        let s_value = match arg_value_iter.next() {
1535            Some(tmp_s_value) => tmp_s_value,
1536            None => return Err(Error::Interp(String::from("no argument s"))),
1537        };
1538        serieses.push(create_histogram_series(data_value, s_value, color_idx)?);
1539        color_idx = (color_idx + 1) % COLORS.len();
1540    }
1541    let plot = Arc::new(Plot::Histogram(chart, axes, serieses));
1542    plot_for_plot(&plot, env)
1543}