add various graphical elements
[lp74.git] / src / main.rs
blobe9bd4f898c123dc2c0f3398017440ca827ae64c1
1 //#![feature(slice_patterns)]
2 #[macro_use] extern crate conrod;
3 extern crate find_folder;
4 extern crate piston_window;
5 extern crate graphics;
6 extern crate rand;
7 extern crate input;
8 extern crate image;
9 extern crate gfx_device_gl;
11 use conrod::{Theme, Color, Button, Colorable, Labelable, Frameable,
12              Positionable, Sizeable, Graphics, Canvas, Widget, Tabs,
13              Circle, Text, DButton, Slider, DropDownList};
14 use conrod::color;
15 use conrod::events::input_provider::InputProvider;
16 use piston_window::{Glyphs, OpenGL, PistonWindow, WindowSettings, UpdateEvent,
17                     EventLoop, Window, Texture, Flip, ImageSize};
18 use input::keyboard::{Key};
20 use data::{Data, Space, Axis, Ori, Pos, Change, PosKind};
21 use view::WSize;
24 use std::io;
25 use std::process::{Command, Output, Stdio, ChildStdin, ChildStdout};
26 use image::{ImageFormat, load_from_memory_with_format};
27 use std::sync::Arc;
28 use graphics::{Transformed, Image, DrawState};
29 use std::io::Write;
30 use std::io::Read;
31 use gfx_device_gl::Resources;
34 mod data;
35 mod view;
37 type Backend = (
38     <piston_window::G2d<'static> as Graphics>::Texture,
39     Glyphs
41 type Ui = conrod::Ui<Backend>;
42 type UiCell<'a> = conrod::UiCell<'a, Backend>;
44 const BUTTON_WIDTH: f64 = 110.0;
45 const BUTTON_HEIGHT: f64 = 35.0;
47 widget_ids! {
48     C_MASTER,
49     C_HEADER,
50     C_SIDE,
51     BTN_ADD,
52     DBTN_ADD,
53     BTN_DEL,
54     DBTN_DEL,
55     BTN_PREV,
56     DBTN_PREV,
57     BTN_NEXT,
58     DBTN_NEXT,
59     CIRCLE1,
60     CIRCLE2,
61     C_ERR,
62     TXT_ERR,
63     BTN_OK_ERR,
64     BTN_CANCEL_ERR,
65     BTN_DESIGN,
66     DBTN_DESIGN,
67     BTN_VISUAL,
68     DBTN_VISUAL,
69         COLOR_SLIDER with 3,
70         DCOLOR_SLIDER with 3,
71     SIZE_SLIDER,
72     POS_KIND_SELECT,
75 struct Colors {
76     c1: Color,
77     c2: Color,
78     font: Color,
79     main: Color,
80     hide: Color,
83 #[derive(Clone)]
84 struct ErrState {
85     msg: String,
88 #[derive(Clone)]
89 enum DesignState {
90     Std,
91 //    Add,
94 //#[derive(Clone)]
95 enum State {
96     Design(DesignState),
97     Visual(Option<Texture<gfx_device_gl::Resources>>),
98     Err(ErrState),
101 fn make_button<'a, F>(c: &Colors) -> Button<'a, F>
102     where F: FnOnce()
104     Button::new().w_h(BUTTON_WIDTH, BUTTON_HEIGHT).color(c.c1)
105         .label_color(c.font).frame_color(c.c2)
108 macro_rules! make_disable_button {
109     ($c:ident) => {
110         DButton::new()
111             .color($c.c1).w_h(BUTTON_WIDTH, BUTTON_HEIGHT).frame_color($c.c2)
112     }
115 fn generate_faces_map(w: &mut PistonWindow,s: &Space) ->
116     Texture<gfx_device_gl::Resources>
118     println!("Generating graph... ");
119     let wsize = w.size();
120     println!("-Gsize={},{}!", wsize.width, wsize.height - 50);
121     let process = Command::new("dot")
122         .arg("-Tpng")
123         .arg(format!("-Gsize={},{}!", wsize.width, wsize.height - 50))
124         .arg("-Gdpi=2")
125         .stdin(Stdio::piped())
126         .stdout(Stdio::piped())
127         .spawn()
128         .unwrap_or_else(|e| panic!("system call failed: {}", e));
129     process.stdin.unwrap().write_all(s.dot_graph_code()
130                                      .as_bytes())
131         .unwrap_or_else(|e| panic!("piping failed: {}", e));
132     let mut png = Vec::new();
133     process.stdout.unwrap().read_to_end(&mut png)
134         .unwrap_or_else(|e| panic!("final read failed: {}"));
135     let rgba_img = load_from_memory_with_format(&png,
136                                                 ImageFormat::PNG)
137         .unwrap_or_else(|e| panic!("PNG image loading failed: {}", e))
138         .to_rgba();
139     //assert!(output.status.success());
140     let factory = &mut w.factory;
141     let settings = piston_window::TextureSettings::new();
142     println!("done");
143     Texture::from_image(factory, &rgba_img, &settings)
144              .unwrap()
147 fn final_size(WSize { header_h: hh, side_w: _, w: ww, h: wh_ }: WSize,
148               (iw, ih): (f64, f64)) -> [f64; 4]
150     let wh = wh_ - hh;
151     let wr = ww / wh;
152     let ir = iw / ih;
153     if wr > ir {
154         let w = iw * (wh / ih);
155         [(ww / 2.0) - (w / 2.0), hh, w, wh]
156     } else {
157         let h = ih * (ww / iw);
158         [0.0, (wh / 2.0) - (h / 2.0) + hh, ww, h]
159     }
162 fn main() {
163     let opengl = OpenGL::V3_2;
164     let mut window: PistonWindow = WindowSettings::new("LP74", [800, 500])
165         .opengl(opengl).exit_on_esc(true).build().unwrap();
167         let mut ui = {
168         let assets = find_folder::Search::KidsThenParents(3, 5)
169             .for_folder("assets").expect("Cannot find assets folder");
170         let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf");
171         let theme = Theme::default();
172         let glyph_cache = Glyphs::new(&font_path, window.factory.clone());
173         Ui::new(glyph_cache.unwrap(), theme)
174     };
176     window.set_ups(120);
177     window.set_max_fps(60);
179     let mut colors = Colors {
180         c1: color::rgb_bytes(57, 63, 63),
181         c2: color::rgb_bytes(30, 34, 34),
182         font: color::WHITE,
183         main: color::WHITE,
184         hide: color::rgba_bytes(57, 63, 63, 0.4),
185     };
187     let mut state = State::Design(DesignState::Std);
188     let mut data = Data::new();
190     while let Some(event) = window.next() {
191         let wsize = WSize {
192             header_h: 50.0,
193             side_w: 150.0,
194             w: window.size().width as f64,
195             h: window.size().height as f64
196         };
198         handle_keys(&ui, &mut data); //must be before handle_event()
199         ui.handle_event(&event);
200         event.update(|_| ui.set_widgets(|ref mut uicell|
201                                         set_ui(uicell,
202                                                &mut state,
203                                                &mut data,
204                                                &mut colors,
205                                                &wsize)));
206         match state {
207             State::Visual(ref mut faces_map) => match *faces_map {
208                 None => {
209                     *faces_map = Some(generate_faces_map(&mut window, &data.space));
210                 },
211                 _ => {},
212             },
213             _ => {},
214         }
215         window.draw_2d(&event, |c, g| {
216             match &state {
217                 &State::Design(_) => {
218                     if data.space.change != Change::None
219                         || data.history.change
220                         || ui.will_redraw() {
221                         data.history.change = false;
222                         piston_window::clear([0.0, 0.0, 0.0, 0.0], g);
223                         draw_model(&wsize, &data, c, g);
224                         ui.needs_redraw();
225                     }
226                     data.save_if_changed();
227                 },
228                 &State::Visual(ref faces_map) => {
229                     if ui.will_redraw() {
230                         if let &Some(ref m) = faces_map {
231                             piston_window::clear([1.0, 1.0, 1.0, 1.0], g);
232                             let image = Image::new()
233                                 .rect(final_size(wsize,
234                                                  (m.get_width() as f64,
235                                                   (m.get_height()) as f64)));
236                             image.draw(m, &DrawState::default(),
237                                        c.transform.trans(0.0, 0.0), g);
238                             ui.needs_redraw();
239                         }
240                     }
241                 },
242                 &State::Err(_) => {},
243             }
244             ui.draw_if_changed(c, g);
245         });
246     }
249 fn draw_model<G: graphics::Graphics>(wsize: &WSize, d: &Data, c: graphics::Context, g: &mut G) {
250     use graphics::*;
251     //rectangle([rand::random(), rand::random(), rand::random(), 1.0], [0.0, 0.0, 200.0, 200.0], c.transform.trans(100.0, 100.0), g);
252     view::view_space(&d.space, wsize, c, g);
255 fn handle_keys(ui: &Ui, d: &mut Data) {
256     let mut keys = ui.global_input.keys_just_pressed().collect::<Vec<_>>();
257     keys.dedup();
258     for k in keys {
259         println!("{:?}", k);
260         match k {
261             Key::R => d.space.rotate(),
262             Key::S => d.space.switch_pos_type(),
263             Key::N => d.load_next_if_any(),
264             Key::P => d.load_prev_if_any(),
265             _ => match d.space.current {
266                 Pos::E((_, a)) => match k {
267                     Key::PageUp => d.space.inc_size(),
268                     Key::PageDown => d.space.dec_size(),
269                     _ => match a {
270                         Axis::X => match k {
271                             Key::Right => d.space.inc_pos(),
272                             Key::Left => d.space.dec_pos(),
273                             _ => {},
274                         },
275                         Axis::Y => match k {
276                             Key::Up => d.space.dec_pos(),
277                             Key::Down => d.space.inc_pos(),
278                             _ => {},
279                         },
280                         Axis::Z => match k {
281                             Key::Up => d.space.dec_pos(),
282                             Key::Down => d.space.inc_pos(),
283                             _ => {},
284                         },
285                     },
286                 },
287                 Pos::F((_, o)) => match k {
288                         Key::A => d.space.set_current_face_if_none(),
289                         _ => match o {
290                             Ori::Top => match k {
291                                 Key::Up => d.space.dec_pos(),
292                                 Key::Down => d.space.inc_pos(),
293                                 _ => {},
294                             },
295                             Ori::Back => match k {
296                                 Key::Down => d.space.dec_pos(),
297                                 Key::Up => d.space.inc_pos(),
298                                 _ => {},
299                             },
300                             Ori::Side => match k {
301                                 Key::Left => d.space.dec_pos(),
302                                 Key::Right => d.space.inc_pos(),
303                                 _ => {},
304                         },
305                     },
306                 },
307             },
308         };
309     }
312 fn set_ui(ui: &mut UiCell, s: &mut State,
313           d: &mut Data, c: &mut Colors, wsize: &WSize)
315     match s {
316         &mut State::Design(DesignState::Std) => {
317             header_ui(ui, s, d, c, wsize);
318             side_ui(ui, s, d, c, wsize);
319             std_ui(ui, s, d, c);
320         },
321         //&mut State::Design(DesignState::Add) => {
322         //    header_ui(ui, s, d, c, wsize);
323         //    side_ui(ui, s, d, c, wsize);
324         //    add_ui(ui, s, d, c)
325         //},
326         &mut State::Visual(_) => {
327             header_ui(ui, s, d, c, wsize);
328             visual_ui(ui, s, d, c);
329         },
330         &mut State::Err(_) => error_ui(ui, s, d, c),
331     }
334 fn error_ui(ui: &mut UiCell, gs: &mut State, d: &mut Data, c: &mut Colors)
336     {
337         let s = match *gs {
338             State::Err(ref mut s) => s,
339             _ => panic!("non Err state"),
340         };
341         Canvas::new()
342             .color(c.hide)
343             .set(C_MASTER, ui);
344         Canvas::new().middle_of(C_MASTER)
345             .color(c.c1).frame_color(c.c2)
346             .w_h(400.0, 120.0).pad(20.0)
347             .set(C_ERR, ui);
348         Text::new(&s.msg)
349             .color(c.font).mid_top_of(C_ERR)
350             .set(TXT_ERR, ui);
351         make_button(c)
352             .label("Cancel").bottom_right_of(C_ERR)
353             .react(|| {
354                 println!("Cancel");
355                 s.msg = String::from("Cannot cancel");
356             })
357             .set(BTN_CANCEL_ERR, ui);
358     }
359     make_button(c)
360         .label("OK").bottom_left_of(C_ERR)
361         .react(|| {
362             println!("OK");
363             *gs = State::Design(DesignState::Std);
364         })
365         .set(BTN_OK_ERR, ui);
368 fn get_win_dim(ui: &UiCell) -> (f64, f64) {
369     (ui.window_dim()[0], ui.window_dim()[1])
372 fn header_ui(ui: &mut UiCell, gs: &mut State,
373              d: &mut Data, c: &mut Colors, wsize: &WSize)
375     Canvas::new()
376         .color(c.c1).frame_color(c.c2)
377         .y((wsize.h/2.0)-(wsize.header_h/2.0)).h(wsize.header_h)
378         .pad_left(20.0).pad_right(20.0)
379         .set(C_HEADER, ui);
382 fn side_ui(ui: &mut UiCell, gs: &mut State,
383            d: &mut Data, c: &mut Colors, wsize: &WSize)
385     Canvas::new().color(c.c1)
386         .color(c.c1).frame_color(c.c2)
387         .x_y(-((wsize.w/2.0)-(wsize.side_w/2.0)), -(wsize.header_h/2.0))
388         .w_h(wsize.side_w, wsize.h - wsize.header_h)
389         .pad(20.0)
390         .set(C_SIDE, ui);
393 fn visual_ui(ui: &mut UiCell, gs: &mut State, d: &mut Data, c: &mut Colors)
395     make_disable_button!(c)
396         .mid_right_of(C_HEADER).label("Visual")
397         .set(DBTN_VISUAL, ui);
398     make_button(c) 
399         .mid_right_of(C_HEADER)
400         .label("Design").left_from(DBTN_VISUAL, 0.0)
401         .react(|| {
402             println!("Design");
403             *gs = State::Design(DesignState::Std);
404         })
405         .set(BTN_DESIGN, ui);
407 fn std_ui(ui: &mut UiCell, gs: &mut State, d: &mut Data, c: &mut Colors)
409     let prev_id = if d.history.has_prev() {
410         Button::new()
411             .mid_left_of(C_HEADER).w_h(50.0, 30.0)
412             .color(c.c1).frame_color(c.c2)
413             .label_color(c.font)
414             .label("<").react(|| {
415                 println!("prev");
416                 d.load_prev();
417             })
418             .set(BTN_PREV, ui);
419         BTN_PREV
420     } else {
421         make_disable_button!(c)
422             .label("<")
423             .mid_left_of(C_HEADER).w_h(50.0, 30.0)
424             .set(DBTN_PREV, ui);
425         DBTN_PREV
426     };
427     let next_id = if d.history.has_next() {
428         Button::new()
429             .mid_left_of(C_HEADER).right_from(prev_id, 30.0)
430             .w_h(50.0, 30.0)
431             .color(c.c1).frame_color(c.c2)
432             .label_color(c.font)
433             .label(">").react(|| {
434                 println!("next");
435                 d.load_next();
436             })
437             .set(BTN_NEXT, ui);
438         BTN_NEXT
439     } else {
440         make_disable_button!(c)
441             .label(">")
442             .mid_left_of(C_HEADER).right_from(prev_id, 30.0)
443             .w_h(50.0, 30.0)
444             .set(DBTN_NEXT, ui);
445         DBTN_NEXT
446     };
447     {
448         let mut select = vec![String::from("Edge"),
449                           String::from("Face")];
450         let mut select_idx = None;
451             DropDownList::new(&mut select, &mut select_idx)
452             .w_h(BUTTON_WIDTH, BUTTON_HEIGHT)
453                 .mid_left_of(C_HEADER).right_from(next_id, 30.0)
454             .max_visible_items(2)
455             .color(c.c1)
456             .frame_color(c.c2)
457             .label("Select")
458             .label_color(c.font)
459             .react(|selected_idx: &mut Option<usize>, new_idx, string: &str| {
460                 *selected_idx = Some(new_idx);
461                 d.space.set_pos_kind(match string {
462                     "Edge" => PosKind::Edge,
463                     _ => PosKind::Face,
464                 });
465             })
466             .set(POS_KIND_SELECT, ui);
467     }
468     make_button(c)
469         .mid_right_of(C_HEADER).label("Visual")
470         .react(|| {
471             println!("Visual");
472             *gs = State::Visual(None);
473         }).set(BTN_VISUAL, ui);
474     make_disable_button!(c) 
475         .mid_right_of(C_HEADER)
476         .label("Design").left_from(BTN_VISUAL, 0.0)
477         .set(DBTN_DESIGN, ui);
478     //make_button(c)
479     //    .mid_top_of(C_SIDE).down_from(BTN1, 20.0).label("Err")
480     //    .react(|| {
481     //        println!("Err");
482     //        *gs = State::Err(ErrState {
483     //            msg: String::from("Error!")
484     //        });
485     //    }).set(BTN2, ui);
486     match d.space.current {
487         Pos::F(((x, y, z), o)) => {
488             if let Some(n) = d.space.faces[x][y][z] {
489                     if let Some((r, g, b)) = n[Space::ori_idx(o)] {
490                     make_disable_button!(c)
491                         .mid_top_of(C_SIDE)
492                         .label("Add")
493                         .set(DBTN_ADD, ui);
494                     make_button(c)
495                         .down(20.0).label("Remove")
496                         .react(|| {
497                             println!("Remove");
498                             d.space.remove_face(((x, y, z), o));
499                         }).set(BTN_ADD, ui);
500                     for i in 0..3 {
501                         let color = match i {
502                             0 => color::rgb(r, 0.0, 0.0),
503                             1 => color::rgb(0.0, g, 0.0),
504                             _ => color::rgb(0.0, 0.0, b),
505                         };
506                     
507                         let value = match i {
508                             0 => r,
509                             1 => g,
510                             _ => b,
511                         };
512                     
513                         Slider::new(value, 0.0, 1.0)
514                             .and(|slider| if i == 0 { slider.down(20.0) } else { slider.right(10.0) })
515                             .w_h(30.0, 80.0)
516                             .color(color)
517                             //.frame(app.frame_width)
518                             .label(&format!("{:.*}", 2, value))
519                             .label_color(c.font)
520                             .frame_color(c.c2)
521                             .react(|v| match i {
522                                 0 => d.space.set_current_face_color_if_any((v, g, b)),
523                                 1 => d.space.set_current_face_color_if_any((r, v, b)),
524                                 _ => d.space.set_current_face_color_if_any((r, g, v)),
525                             })
526                             .set(COLOR_SLIDER + i, ui);
527                             }
528                 } else {
529                     make_button(c)
530                         .mid_top_of(C_SIDE).label("Add")
531                         .react(|| {
532                             println!("Add");
533                             d.space.add_face(((x, y, z), o));
534                         }).set(BTN_ADD, ui);
535                     make_disable_button!(c)
536                         .down(20.0).label("Remove")
537                         .set(DBTN_DEL, ui);
538                     for i in 0..3 {
539                         Slider::new(0.0, 0.0, 1.0)
540                             .and(|slider| if i == 0 {
541                                 slider.down(20.0)
542                             } else {
543                                 slider.right(10.0)
544                             })
545                             .w_h(30.0, 80.0)
546                             .color(c.c1)
547                             .label_color(c.font)
548                             .frame_color(c.c2)
549                             .react(|_| {})
550                             .set(DCOLOR_SLIDER + i, ui);
551                     }
552                 }
553             } else {
554                 panic!("no node here");
555             }
556         },
557         Pos::E(e) => {
558             let value = d.space.get_edge_size(e) as f64;
559             Slider::new(value, 1.0, 1024.0)
560                 .mid_top_of(C_SIDE)
561                 .w_h(30.0, 200.0)
562                 .color(c.c1)
563                 //.frame(app.frame_width)
564                 .label(&format!("{:.*}", 0, value))
565                 .label_color(c.font)
566                 .frame_color(c.c2)
567                 .react(|v| d.space.edge_size(e, v as usize))
568                 .set(SIZE_SLIDER, ui);
569         },
570         }