use concurrency for face map generation
[lp74.git] / src / display.rs
blob01d50db368e94088a7e67b6a0ffe6bef81f09610
1 extern crate conrod;
2 extern crate find_folder;
3 extern crate piston_window;
4 extern crate graphics;
5 extern crate rand;
6 extern crate input;
7 extern crate image;
8 extern crate gfx_device_gl;
10 use data::*;
12 use graphics::{Line, Rectangle, Polygon, Ellipse, DrawState, Transformed};
13 use conrod::{
14     Color, Button, Colorable, Labelable, Frameable, Positionable,
15     Sizeable, Graphics, Canvas, Widget, Text, DButton, Slider, DropDownList
17 use conrod::color;
18 use piston_window::{
19     Glyphs, PistonWindow,
20     Window, Texture
22 use std::process::{Command, Stdio};
23 use image::{ImageFormat, load_from_memory_with_format};
24 use std::io::Write;
25 use std::io::Read;
28 pub type Backend = (
29     <piston_window::G2d<'static> as Graphics>::Texture,
30     Glyphs
32 pub type Ui = conrod::Ui<Backend>;
33 pub type UiCell<'a> = conrod::UiCell<'a, Backend>;
35 const BUTTON_WIDTH: f64 = 110.0;
36 const BUTTON_HEIGHT: f64 = 35.0;
38 const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
39 const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
41 pub struct WSize {
42     pub w: f64,
43     pub h: f64,
44     pub header_h: f64,
45     pub side_w: f64,
48 widget_ids! {
49     C_MASTER,
50     C_HEADER,
51     C_SIDE,
52     BTN_ADD,
53     DBTN_ADD,
54     BTN_DEL,
55     DBTN_DEL,
56     BTN_PREV,
57     DBTN_PREV,
58     BTN_NEXT,
59     DBTN_NEXT,
60     C_ERR,
61     TXT_ERR,
62     BTN_OK_ERR,
63     BTN_CANCEL_ERR,
64     BTN_DESIGN,
65     DBTN_DESIGN,
66     BTN_VISUAL,
67     DBTN_VISUAL,
68         COLOR_SLIDER with 3,
69         DCOLOR_SLIDER with 3,
70     SIZE_SLIDER,
71     POS_KIND_SELECT,
72     BTN_ROTATE,
73         COLOR_SELECT,
74     DBTN_SELECT_COLOR,
75     LENGTH_TXT,
76     BTN_EXTENSIONS,
77     //DBTN_EXTENSIONS,
80 pub struct Colors {
81     pub c1: Color,
82     pub c2: Color,
83     pub font: Color,
84     pub hide: Color,
87 pub enum State {
88     Design,
89     Visual(Option<Texture<gfx_device_gl::Resources>>),
90     Extensions,
91     Err(String),
94 fn make_button<'a, F>(c: &Colors) -> Button<'a, F>
95     where F: FnOnce()
97     Button::new().w_h(BUTTON_WIDTH, BUTTON_HEIGHT).color(c.c1)
98         .label_color(c.font).frame_color(c.c2)
101 macro_rules! make_disable_button {
102     ($c:ident) => {
103         DButton::new()
104             .color($c.c1).w_h(BUTTON_WIDTH, BUTTON_HEIGHT).frame_color($c.c2)
105     }
108 pub fn generate_faces_map(w: &mut PistonWindow,s: &Space) ->
109     Option<Texture<gfx_device_gl::Resources>>
111     println!("Generating graph... ");
112     let wsize = w.size();
113     println!("-Gsize={},{}!", wsize.width, wsize.height - 50);
114     let process = match Command::new("dot")
115         .arg("-Tpng")
116         .arg(format!("-Gsize={},{}!", wsize.width, wsize.height - 50))
117         .arg("-Gdpi=2")
118         .stdin(Stdio::piped())
119         .stdout(Stdio::piped())
120         .spawn() {
121         Ok(p) => p,
122         Err(_) => return None,
123     };
124     process.stdin.unwrap().write_all(s.dot_graph_code()
125                                      .as_bytes())
126         .unwrap_or_else(|e| panic!("piping failed: {}", e));
127     let mut png = Vec::new();
128     process.stdout.unwrap().read_to_end(&mut png)
129         .unwrap_or_else(|_| panic!("final read failed"));
130     let rgba_img = load_from_memory_with_format(&png,
131                                                 ImageFormat::PNG)
132         .unwrap_or_else(|e| panic!("PNG image loading failed: {}", e))
133         .to_rgba();
134     let factory = &mut w.factory;
135     let settings = piston_window::TextureSettings::new();
136     println!("done");
137     Some(Texture::from_image(factory, &rgba_img, &settings)
138              .unwrap())
141 pub fn final_size(WSize { header_h: hh, side_w: _, w: ww, h: wh_ }: WSize,
142               (iw, ih): (f64, f64)) -> [f64; 4]
144     let wh = wh_ - hh;
145     let wr = ww / wh;
146     let ir = iw / ih;
147     if wr > ir {
148         let w = iw * (wh / ih);
149         [(ww / 2.0) - (w / 2.0), hh, w, wh]
150     } else {
151         let h = ih * (ww / iw);
152         [0.0, (wh / 2.0) - (h / 2.0) + hh, ww, h]
153     }
156 macro_rules! line {
157     ($color:expr, $c:expr, $gl:expr, ($x:expr, $z:expr),
158     ($ax:expr, $az:expr), ($bx:expr, $bz:expr)) => {
159         Line::new($color, 1.0)
160             .draw([$x + $ax, $z + $az,
161                   $x + $bx, $z + $bz],
162                   &DrawState::default(),
163                   $c.transform,
164                   $gl)
165     }
168 macro_rules! black_line {
169     ($c:expr, $gl:expr, ($x:expr, $z:expr),
170     ($ax:expr, $az:expr), ($bx:expr, $bz:expr)) => {
171         line!(BLACK, $c, $gl, ($x, $z), ($ax, $az), ($bx, $bz))
172     }
175 macro_rules! red_line {
176     ($c:expr, $gl:expr, ($x:expr, $z:expr),
177     ($ax:expr, $az:expr), ($bx:expr, $bz:expr)) => {
178         line!(RED, $c, $gl, ($x, $z), ($ax, $az), ($bx, $bz))
179     }
182 pub fn show_space<G: graphics::Graphics>(s: &Space,
183                  wsize: &WSize,
184                  c: graphics::Context, gl: &mut G) {
185     let (xctr, zctr) = ((wsize.w - wsize.side_w) / 2.0, (wsize.h - wsize.header_h) / 2.0);
186     let (xcur, ycur, zcur) = (s.row_size_before_current(Axis::X) as f64,
187                               (s.row_size_before_current(Axis::Y) / 2) as f64,
188                               s.row_size_before_current(Axis::Z) as f64);
189     let (wshift, hshift) = (xctr - (xcur + ycur) + wsize.side_w,
190                             zctr - (zcur + ycur) + wsize.header_h);
191     let mut wy = (s.row_size(Axis::Y) / 3) as f64;
192     for iy in (0..s.faces[0].len()).rev() {
193         let ysize = (s.size[Space::axis_idx(Axis::Y)][iy] / 3) as f64;
194         let mut wx = wshift;
195         for ix in 0..s.faces.len() {
196             let xsize = s.size[Space::axis_idx(Axis::X)][ix] as f64;
197             let mut wz = hshift;
198             for iz in 0..s.faces[0][0].len() {
199                 let zsize = s.size[Space::axis_idx(Axis::Z)][iz] as f64;
200                 match s.faces[ix][iy][iz] {
201                     Some(ss) => {
202                         let x = wx + wy;
203                         let z = wz + wy;
204                         if let Some((r, g, b)) = ss[Space::ori_idx(Ori::Back)] {
205                             Rectangle::new([r, g, b, 0.6])
206                                 .draw([0.0, 0.0, xsize, zsize],
207                                       &DrawState::default(),
208                                       c.transform.trans(x, z),
209                                       gl);
210                             Rectangle::new_border(BLACK, 1.0)
211                                 .draw([0.0, 0.0, xsize, zsize],
212                                       &DrawState::default(),
213                                       c.transform.trans(x, z),
214                                       gl);
215                         }
216                         if let Some((r, g, b)) = ss[Space::ori_idx(Ori::Top)] {
217                             Polygon::new([r, g, b, 0.6])
218                                 .draw(&[[x, z],
219                                       [x + xsize, z],
220                                       [x + xsize - ysize, z - ysize],
221                                       [x - ysize, z - ysize]],
222                                       &DrawState::default(),
223                                       c.transform,
224                                       gl);
225                             black_line!(c, gl, (x, z), (0.0, 0.0), (xsize, 0.0));
226                             black_line!(c, gl, (x, z), (xsize, 0.0), (xsize - ysize, -ysize));
227                             black_line!(c, gl, (x, z), (xsize - ysize, -ysize), (-ysize, -ysize));
228                             black_line!(c, gl, (x, z), (-ysize, -ysize), (0.0, 0.0));
229                         }
230                         if let Some((r, g, b)) = ss[Space::ori_idx(Ori::Side)] {
231                             Polygon::new([r, g, b, 0.6])
232                                 .draw(&[[x, z + zsize],
233                                       [x, z],
234                                       [x - ysize, z - ysize],
235                                       [x - ysize, z + zsize - ysize]],
236                                       &DrawState::default(),
237                                       c.transform,
238                                       gl);
239                             black_line!(c, gl, (x, z), (0.0, zsize), (0.0, 0.0));
240                             black_line!(c, gl, (x, z), (0.0, 0.0), (-ysize, -ysize));
241                             black_line!(c, gl, (x, z), (-ysize, -ysize), (-ysize, zsize - ysize));
242                             black_line!(c, gl, (x, z), (-ysize, zsize - ysize), (0.0, zsize));
243                         }
244                         if (ix, iy, iz) == s.current_node_coord() {
245                             Ellipse::new(RED)
246                                 .draw([-3.0, -3.0, 7.0, 7.0],
247                                       &DrawState::default(),
248                                       c.transform.trans(x, z),
249                                       gl);
250                             match s.current {
251                                 Pos::E((_, a)) => match a {
252                                     Axis::X => Line::new(RED, 1.0)
253                                         .draw([x, z, x + xsize, z],
254                                               &DrawState::default(),
255                                               c.transform,
256                                               gl),
257                                     Axis::Y => Line::new(RED, 1.0)
258                                         .draw([x, z, x - ysize, z - ysize],
259                                               &DrawState::default(),
260                                               c.transform,
261                                               gl),
262                                     Axis::Z => Line::new(RED, 1.0)
263                                         .draw([x, z, x, z + zsize],
264                                               &DrawState::default(),
265                                               c.transform,
266                                               gl),
267                                 },
268                                 Pos::F((_, o)) => match o {
269                                     Ori::Back => Rectangle::new_border(RED, 1.0)
270                                         .draw([0.0, 0.0, xsize, zsize],
271                                               &DrawState::default(),
272                                               c.transform.trans(x, z),
273                                               gl),
274                                     Ori::Top => {
275                                         red_line!(c, gl, (x, z), (0.0, 0.0), (xsize, 0.0));
276                                         red_line!(c, gl, (x, z), (xsize, 0.0), (xsize - ysize, -ysize));
277                                         red_line!(c, gl, (x, z), (xsize - ysize, -ysize), (-ysize, -ysize));
278                                         red_line!(c, gl, (x, z), (-ysize, -ysize), (0.0, 0.0));
279                                     },
280                                     Ori::Side => {
281                                         red_line!(c, gl, (x, z), (0.0, zsize), (0.0, 0.0));
282                                         red_line!(c, gl, (x, z), (0.0, 0.0), (-ysize, -ysize));
283                                         red_line!(c, gl, (x, z), (-ysize, -ysize), (-ysize, zsize - ysize));
284                                         red_line!(c, gl, (x, z), (-ysize, zsize - ysize), (0.0, zsize));
285                                     },
286                                 },
287                             }
288                         }
289                     },
290                     None => {},
291                 }
292                 wz += zsize;
293             }
294             wx += xsize;
295         }
296         wy -= ysize;
297     }
300 pub fn set_ui(ui: &mut UiCell, s: &mut State,
301           d: &mut Data, c: &mut Colors, wsize: &WSize)
303     match s {
304         &mut State::Design => {
305             header_ui(ui, c, wsize);
306             side_ui(ui, c, wsize);
307             std_ui(ui, s, d, c);
308         },
309         &mut State::Visual(_) => {
310             header_ui(ui, c, wsize);
311             visual_ui(ui, s, c);
312         },
313         &mut State::Extensions => /*extensions_ui(ui, s, c)*/ {},
314         &mut State::Err(_) => error_ui(ui, s, c),
315     }
318 fn error_ui(ui: &mut UiCell, gs: &mut State, c: &mut Colors)
320     {
321         Canvas::new()
322             .color(c.hide)
323             .set(C_MASTER, ui);
324         Canvas::new().middle_of(C_MASTER)
325             .color(c.c1).frame_color(c.c2)
326             .w_h(400.0, 120.0).pad(20.0)
327             .set(C_ERR, ui);
328         Text::new(match *gs {
329             State::Err(ref msg) => msg,
330             _=> "",
331         })
332             .color(c.font).mid_top_of(C_ERR)
333             .set(TXT_ERR, ui);
334     }
335     make_button(c)
336         .label("OK").mid_bottom_of(C_ERR)
337         .react(|| {
338             println!("OK");
339             *gs = State::Design;
340         })
341         .set(BTN_OK_ERR, ui);
344 fn header_ui(ui: &mut UiCell, c: &mut Colors, wsize: &WSize)
346     Canvas::new()
347         .color(c.c1).frame_color(c.c2)
348         .y((wsize.h/2.0)-(wsize.header_h/2.0)).h(wsize.header_h)
349         .pad_left(20.0).pad_right(20.0)
350         .set(C_HEADER, ui);
353 fn side_ui(ui: &mut UiCell, c: &mut Colors, wsize: &WSize)
355     Canvas::new().color(c.c1)
356         .color(c.c1).frame_color(c.c2)
357         .x_y(-((wsize.w/2.0)-(wsize.side_w/2.0)), -(wsize.header_h/2.0))
358         .w_h(wsize.side_w, wsize.h - wsize.header_h)
359         .pad(20.0)
360         .set(C_SIDE, ui);
363 fn visual_ui(ui: &mut UiCell, gs: &mut State, c: &mut Colors)
365     make_button(c) 
366         .mid_right_of(C_HEADER)
367         .label("Extensions").mid_right_of(C_HEADER)
368         .react(|| {
369             println!("Extensions");
370             *gs = State::Extensions;
371         })
372         .set(BTN_EXTENSIONS, ui);
373     make_disable_button!(c)
374         .mid_right_of(C_HEADER)
375         .left_from(BTN_EXTENSIONS, 0.0)
376         .label("Visual")
377         .set(DBTN_VISUAL, ui);
378     make_button(c) 
379         .mid_right_of(C_HEADER)
380         .label("Design").left_from(DBTN_VISUAL, 0.0)
381         .react(|| {
382             println!("Design");
383             *gs = State::Design;
384         })
385         .set(BTN_DESIGN, ui);
387 fn std_ui(ui: &mut UiCell, gs: &mut State, d: &mut Data, c: &mut Colors)
389     let prev_id = if d.history.has_prev() {
390         Button::new()
391             .mid_left_of(C_HEADER).w_h(50.0, 30.0)
392             .color(c.c1).frame_color(c.c2)
393             .label_color(c.font)
394             .label("<").react(|| {
395                 println!("prev");
396                 d.load_prev();
397             })
398             .set(BTN_PREV, ui);
399         BTN_PREV
400     } else {
401         make_disable_button!(c)
402             .label("<")
403             .mid_left_of(C_HEADER).w_h(50.0, 30.0)
404             .set(DBTN_PREV, ui);
405         DBTN_PREV
406     };
407     let next_id = if d.history.has_next() {
408         Button::new()
409             .mid_left_of(C_HEADER).right_from(prev_id, 30.0)
410             .w_h(50.0, 30.0)
411             .color(c.c1).frame_color(c.c2)
412             .label_color(c.font)
413             .label(">").react(|| {
414                 println!("next");
415                 d.load_next();
416             })
417             .set(BTN_NEXT, ui);
418         BTN_NEXT
419     } else {
420         make_disable_button!(c)
421             .label(">")
422             .mid_left_of(C_HEADER).right_from(prev_id, 30.0)
423             .w_h(50.0, 30.0)
424             .set(DBTN_NEXT, ui);
425         DBTN_NEXT
426     };
427     {
428         let mut select = vec![String::from("Edge (E)"),
429                               String::from("Face (F)")];
430         let mut select_idx = None;
431             DropDownList::new(&mut select, &mut select_idx)
432             .w_h(BUTTON_WIDTH, BUTTON_HEIGHT)
433                 .mid_left_of(C_HEADER).right_from(next_id, 30.0)
434             .max_visible_items(2)
435             .color(c.c1)
436             .frame_color(c.c2)
437             .label("Select")
438             .label_color(c.font)
439             .react(|selected_idx: &mut Option<usize>, new_idx, string: &str| {
440                 *selected_idx = Some(new_idx);
441                 d.space.set_pos_kind(match string {
442                     "Edge (E)" => PosKind::Edge,
443                     _ => PosKind::Face,
444                 });
445             })
446             .set(POS_KIND_SELECT, ui);
447     }
448     make_button(c)
449         .mid_left_of(C_HEADER).right_from(POS_KIND_SELECT, 30.0)
450         .label("Rotate (R)")
451         .react(|| {
452             println!("Rotate");
453             d.space.rotate();
454         }).set(BTN_ROTATE, ui);
455     make_button(c)
456         .mid_right_of(C_HEADER)
457         .label("Extensions")
458         .react(|| {
459             println!("Extensions");
460             *gs = State::Extensions;
461         }).set(BTN_EXTENSIONS, ui);
462     make_button(c)
463         .mid_right_of(C_HEADER)
464         .left_from(BTN_EXTENSIONS, 0.0)
465         .label("Visual")
466         .react(|| {
467             println!("Visual");
468             *gs = State::Visual(None);
469         }).set(BTN_VISUAL, ui);
470     make_disable_button!(c) 
471         .mid_right_of(C_HEADER)
472         .label("Design").left_from(BTN_VISUAL, 0.0)
473         .set(DBTN_DESIGN, ui);
474     //make_button(c)
475     //    .mid_top_of(C_SIDE).down_from(BTN1, 20.0).label("Err")
476     //    .react(|| {
477     //        println!("Err");
478     //        *gs = State::Err(ErrState {
479     //            msg: String::from("Error!")
480     //        });
481     //    }).set(BTN2, ui);
482     match d.space.current {
483         Pos::F(((x, y, z), o)) => {
484             if let Some(n) = d.space.faces[x][y][z] {
485                     if let Some((r, g, b)) = n[Space::ori_idx(o)] {
486                     make_disable_button!(c)
487                         .mid_top_of(C_SIDE)
488                         .label("Add (A)")
489                         .set(DBTN_ADD, ui);
490                     make_button(c)
491                         .down(20.0).label("Delete (D)")
492                         .react(|| {
493                             println!("Delete");
494                             d.space.remove_face(((x, y, z), o));
495                         }).set(BTN_ADD, ui);
496                     for i in 0..3 {
497                         let color = match i {
498                             0 => color::rgb(r, 0.0, 0.0),
499                             1 => color::rgb(0.0, g, 0.0),
500                             _ => color::rgb(0.0, 0.0, b),
501                         };
502                     
503                         let value = match i {
504                             0 => r,
505                             1 => g,
506                             _ => b,
507                         };
508                     
509                         Slider::new(value, 0.0, 1.0)
510                             .and(|slider| if i == 0 { slider.down(40.0) } else { slider.right(10.0) })
511                             .w_h(30.0, 80.0)
512                             .color(color)
513                             .label(&format!("{:.*}", 2, value))
514                             .label_color(color.plain_contrast())
515                             .frame_color(c.c2)
516                             .react(|v| d.space.set_current_face_color_if_any(match i {
517                                 0 => (v, g, b),
518                                 1 => (r, v, b),
519                                 _ => (r, g, v),
520                             }))
521                             .set(COLOR_SLIDER + i, ui);
522                             }
523                     {
524                         let color = color::rgb(r, g, b);
525                                                 let mut colors = vec![
526                             "Black".to_string(),
527                             "White".to_string(),
528                             "Red".to_string(),
529                             "Orange".to_string(),
530                             "Green".to_string(),
531                             "Blue".to_string(),
532                             "Brown".to_string(),
533                             "Gray".to_string(),
534                             "Yellow".to_string(),
535                             "Purple".to_string()
536                         ];
537                         let mut selected_idx = None;
538                                                 DropDownList::new(&mut colors, &mut selected_idx)
539                                                     .w_h(BUTTON_WIDTH, BUTTON_HEIGHT)
540                             .mid_top_of(C_SIDE).down(20.0)
541                                                     .color(color)
542                                                     .frame_color(c.c2)
543                                                     .label("Colors")
544                                                     .label_color(color.plain_contrast())
545                                                     .react(|selected_idx: &mut Option<usize>, new_idx, string: &str| {
546                                                         *selected_idx = Some(new_idx);
547                                                         d.space.set_current_face_color_if_any(match string {
548                                                             "Black" => (0.0, 0.0, 0.0),
549                                                             "White" => (1.0, 1.0, 1.0),
550                                                             "Red"   => (1.0, 0.0, 0.0),
551                                                             "Green" => (0.0, 1.0, 0.0),
552                                                             "Blue"  => (0.0, 0.0, 1.0),
553                                     "Yellow" => (color::YELLOW.red(),
554                                                  color::YELLOW.green(),
555                                                  color::YELLOW.blue()),
556                                     "Purple" => (color::PURPLE.red(),
557                                                  color::PURPLE.green(),
558                                                  color::PURPLE.blue()),
559                                     "Orange" => (color::ORANGE.red(),
560                                                  color::ORANGE.green(),
561                                                  color::ORANGE.blue()),
562                                     "Gray" => (color::GRAY.red(),
563                                                color::GRAY.green(),
564                                                color::GRAY.blue()),
565                                     "Brown" => (color::BROWN.red(),
566                                                 color::BROWN.green(),
567                                                 color::BROWN.blue()),
568                                                             _       => (1.0, 1.0, 1.0),
569                                                         });
570                                                     })
571                                                     .set(COLOR_SELECT, ui);
572                     }
573                 } else {
574                     make_button(c)
575                         .mid_top_of(C_SIDE).label("Add (A)")
576                         .react(|| {
577                             println!("Add");
578                             d.space.add_face(((x, y, z), o));
579                         }).set(BTN_ADD, ui);
580                     make_disable_button!(c)
581                         .down(20.0).label("Delete (D)")
582                         .set(DBTN_DEL, ui);
583                     for i in 0..3 {
584                         Slider::new(0.0, 0.0, 1.0)
585                             .and(|slider| if i == 0 {
586                                 slider.down(40.0)
587                             } else {
588                                 slider.right(10.0)
589                             })
590                             .w_h(30.0, 80.0)
591                             .color(c.c1)
592                             .label_color(c.font)
593                             .frame_color(c.c2)
594                             .react(|_| {})
595                             .set(DCOLOR_SLIDER + i, ui);
596                     }
597                     make_disable_button!(c)
598                         .mid_top_of(C_SIDE).down(20.0)
599                         .label("Color")
600                         .set(DBTN_SELECT_COLOR, ui);
601                 }
602             }
603         },
604         Pos::E(e) => {
605             Text::new("Length")
606                 .top_left_of(C_SIDE)
607                 .down(115.0)
608                 .font_size(20)
609                 .color(c.font)
610                 .set(LENGTH_TXT, ui);
611             let length = d.space.get_edge_size(e) as f64;
612             Slider::new(length, 1.0, 1024.0)
613                 .top_right_of(C_SIDE)
614                 .w_h(30.0, 200.0)
615                 .color(c.c1)
616                 .label(&format!("{:.*}", 0, length))
617                 .label_color(c.font)
618                 .frame_color(c.c2)
619                 .react(|v| d.space.edge_size(e, v as usize))
620                 .set(SIZE_SLIDER, ui);
621         },
622         }