1use 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#[derive(Copy, Clone, Debug)]
50pub struct F32Key
51{
52 value: f32,
53}
54
55impl F32Key
56{
57 pub fn new(value: f32) -> Self
59 { F32Key { value, } }
60
61 pub fn to_f32(&self) -> f32
63 { self.value }
64
65 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#[derive(Clone, Debug)]
102pub struct Chart
103{
104 pub title: Option<String>,
106 pub window_id: Option<WindowId>,
108 pub has_window: bool,
110 pub file: Option<String>,
112 pub size: Option<(u32, u32)>,
114}
115
116#[derive(Clone, Debug)]
118pub struct Axes2d
119{
120 pub x: Range<f32>,
122 pub y: Range<f32>,
124}
125
126#[derive(Clone, Debug)]
128pub struct Axes3d
129{
130 pub x: Range<f32>,
132 pub y: Range<f32>,
134 pub z: Range<f32>,
136}
137
138#[derive(Clone, Debug)]
140pub enum HistogramValue
141{
142 Bool(bool),
144 Int(i64),
146 Float(f32),
148 String(Box<String>),
150}
151
152impl HistogramValue
153{
154 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 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 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 pub fn to_opt_bool(&self) -> Option<bool>
190 {
191 match self {
192 HistogramValue::Bool(b) => Some(*b),
193 _ => None,
194 }
195 }
196
197 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 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 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#[derive(Clone, Debug)]
259pub struct HistogramAxes
260{
261 pub x: Vec<HistogramValue>,
263 pub y: Range<usize>,
265}
266
267#[derive(Clone, Debug)]
269pub enum Series2d
270{
271 Line(Vec<f32>, Vec<f32>, RGBColor, Option<String>),
273 DashedLine(Vec<f32>, Vec<f32>, RGBColor, Option<String>),
275 DottedLine(Vec<f32>, Vec<f32>, RGBColor, Option<String>),
277 Circle(Vec<f32>, Vec<f32>, RGBColor, Option<String>),
279 Cross(Vec<f32>, Vec<f32>, RGBColor, Option<String>),
281 Point(Vec<f32>, Vec<f32>, RGBColor, Option<String>),
283 Triangle(Vec<f32>, Vec<f32>, RGBColor, Option<String>),
285}
286
287#[derive(Clone, Debug)]
289pub enum Series3d
290{
291 Line(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>),
293 DashedLine(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>),
295 DottedLine(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>),
297 Circle(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>),
299 Cross(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>),
301 Point(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>),
303 Triangle(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>),
305 XYSurface(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>, BTreeMap<F32Key, usize>, BTreeMap<F32Key, usize>),
307 XZSurface(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>, BTreeMap<F32Key, usize>, BTreeMap<F32Key, usize>),
309 YZSurface(Vec<f32>, Vec<f32>, Vec<f32>, RGBColor, Option<String>, BTreeMap<F32Key, usize>, BTreeMap<F32Key, usize>),
311}
312
313#[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#[derive(Clone, Debug)]
609pub enum Plot
610{
611 Plot(Chart, Axes2d, Vec<Series2d>),
613 Plot3(Chart, Axes3d, Vec<Series3d>),
615 Histogram(Chart, HistogramAxes, Vec<HistogramSeries>),
617}
618
619impl Plot
620{
621 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 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 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 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#[derive(Clone, Debug)]
688pub enum PlotterAppEvent
689{
690 Plot(Arc<Plot>, Sender<Option<WindowId>>),
692 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
735pub struct PlotterApp
739{
740 icon: Icon,
741 windows: HashMap<WindowId, WindowState>,
742 context: Option<Context<DisplayHandle<'static>>>,
743}
744
745impl PlotterApp
746{
747 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
1446pub 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
1479pub 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
1516pub 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}