1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
use pancurses::{initscr, endwin, noecho};
use line_drawing::Bresenham;
use std::vec::Vec;

pub struct Screen {
  max_y: i32,
  max_x: i32,
  _w: pancurses::Window
}

impl Screen {
  pub fn deinit(self) {endwin();}
}

pub enum GraphPlacement {
  _TopLeft, _TopMiddle, _TopRight,
  _BottomLeft, _BottomMiddle, _BottomRight
}

pub fn init_screen() -> Screen {
  let win = initscr();
  win.keypad(true);
  noecho();
  Screen {max_y: win.get_max_y(), max_x: win.get_max_x(), _w: win}
}

pub fn make_plot_area(screen: &Screen, plot_pos: GraphPlacement) -> pancurses::Window {
  let corner_origin = match plot_pos { // y, x
      GraphPlacement::_TopLeft => [0, 0],
      GraphPlacement::_TopMiddle => [0, screen.max_x / 3],
      GraphPlacement::_TopRight => [0, 2 * (screen.max_x / 3)],
      GraphPlacement::_BottomLeft => [screen.max_y / 2, 0],
      GraphPlacement::_BottomMiddle => [screen.max_y / 2, screen.max_x / 3],
      GraphPlacement::_BottomRight => [screen.max_y / 2, 2 * (screen.max_x / 3)]
  };

  pancurses::newwin(corner_origin[0] + screen.max_y / 2, corner_origin[1] + screen.max_x / 3, corner_origin[0], corner_origin[1])
}

fn draw_border_and_crosshatch(sub_win: &pancurses::Window, panel_height: i32, panel_width: i32) {
  for across_y in 0..panel_height {
    for across_x in 0..panel_width {
      if across_y == 0 || across_y == panel_height - 1 {sub_win.mvprintw(across_y, across_x, "-");}
      else if across_x == 0 || across_x == panel_width - 1 {sub_win.mvprintw(across_y, across_x, "'");}
      else if across_y == panel_height / 2 {sub_win.mvprintw(across_y, across_x, "-");}
      else if across_x == panel_width / 2 {sub_win.mvprintw(across_y, across_x, "'");}
    }
  }
}

fn plot_points(sub_win: &pancurses::Window, point_slice: Vec<[i32; 2]>) {
  let mut t_previous = (point_slice[0][0], point_slice[0][1]);
  let mut a_previous = point_slice[0];

  for a_current in point_slice.iter() {
    let t_current = (a_current[0], a_current[1]);

    let rise = a_previous[1] - a_current[1];
    let run = a_current[0] - a_previous[0];

    let mut slope_segment = "";
    if rise == 0 || run == 0 {slope_segment = "_";}
    else {
      let slope = rise / run;
      if i32::abs(slope) >= 10 {slope_segment = "|";}
      else if i32::abs(slope) as f32 > 0.3 {
        if slope > 0 {slope_segment = "/";}
        else {slope_segment = "\\";}
      }
    }
    for (x, y) in Bresenham::new(t_previous, t_current) {
      sub_win.mvprintw(y, x, slope_segment);
    }
  t_previous = t_current;
  a_previous = *a_current;
  }
}

pub fn draw_function<F: Fn(f32) -> f32>(sub_win: pancurses::Window, function: F) {
  let (begin_y, begin_x) = sub_win.get_beg_yx();
  let (end_y, end_x) = sub_win.get_max_yx();

  draw_border_and_crosshatch(&sub_win, end_y - begin_y, end_x - begin_x);

  let center_x = end_x - begin_x;

  let graph_half_width = center_x - center_x / 2;
  let scale_vert = (end_y - begin_y) / 2;
  let scale_hori = (end_x - begin_x) / 2;

  let mut points_to_plot = Vec::new(); // x, y
    
  for graph_x in -graph_half_width..graph_half_width {
    let x = scale_hori + graph_x;
    let y = scale_vert - function(graph_x as f32) as i32;
    points_to_plot.push([x, y]);
  }

  plot_points(&sub_win, points_to_plot);
  sub_win.refresh();
  sub_win.getch();
}