final version
[lp74.git] / src / display.rs
blobea4253d96d25d2df4ec4712fcd9cdba1f46e04f0
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     let wsize = w.size();
112     let process = match Command::new("dot")
113         .arg("-Tpng")
114         .arg(format!("-Gsize={},{}!", wsize.width, wsize.height - 50))
115         .arg("-Gdpi=2")
116         .stdin(Stdio::piped())
117         .stdout(Stdio::piped())
118         .spawn() {
119         Ok(p) => p,
120         Err(_) => return None,
121     };
122     process.stdin.unwrap().write_all(s.dot_graph_code()
123                                      .as_bytes())
124         .unwrap_or_else(|e| panic!("piping failed: {}", e));
125     let mut png = Vec::new();
126     process.stdout.unwrap().read_to_end(&mut png)
127         .unwrap_or_else(|_| panic!("final read failed"));
128     let rgba_img = load_from_memory_with_format(&png,
129                                                 ImageFormat::PNG)
130         .unwrap_or_else(|e| panic!("PNG image loading failed: {}", e))
131         .to_rgba();
132     let factory = &mut w.factory;
133     let settings = piston_window::TextureSettings::new();
134     Some(Texture::from_image(factory, &rgba_img, &settings)
135              .unwrap())
138 pub fn final_size(WSize { header_h: hh, side_w: _, w: ww, h: wh_ }: WSize,
139               (iw, ih): (f64, f64)) -> [f64; 4]
141     let wh = wh_ - hh;
142     let wr = ww / wh;
143     let ir = iw / ih;
144     if wr > ir {
145         let w = iw * (wh / ih);
146         [(ww / 2.0) - (w / 2.0), hh, w, wh]
147     } else {
148         let h = ih * (ww / iw);
149         [0.0, (wh / 2.0) - (h / 2.0) + hh, ww, h]
150     }
153 macro_rules! line {
154     ($color:expr, $c:expr, $gl:expr, ($x:expr, $z:expr),
155     ($ax:expr, $az:expr), ($bx:expr, $bz:expr)) => {
156         Line::new($color, 1.0)
157             .draw([$x + $ax, $z + $az,
158                   $x + $bx, $z + $bz],
159                   &DrawState::default(),
160                   $c.transform,
161                   $gl)
162     }
165 macro_rules! black_line {
166     ($c:expr, $gl:expr, ($x:expr, $z:expr),
167     ($ax:expr, $az:expr), ($bx:expr, $bz:expr)) => {
168         line!(BLACK, $c, $gl, ($x, $z), ($ax, $az), ($bx, $bz))
169     }
172 macro_rules! red_line {
173     ($c:expr, $gl:expr, ($x:expr, $z:expr),
174     ($ax:expr, $az:expr), ($bx:expr, $bz:expr)) => {
175         line!(RED, $c, $gl, ($x, $z), ($ax, $az), ($bx, $bz))
176     }
179 pub fn show_space<G: graphics::Graphics>(s: &Space,
180                  wsize: &WSize,
181                  c: graphics::Context, gl: &mut G) {
182     let (xctr, zctr) = ((wsize.w - wsize.side_w) / 2.0, (wsize.h - wsize.header_h) / 2.0);
183     let (xcur, ycur, zcur) = (s.row_size_before_current(Axis::X) as f64,
184                               (s.row_size_before_current(Axis::Y) / 2) as f64,
185                               s.row_size_before_current(Axis::Z) as f64);
186     let (wshift, hshift) = (xctr - (xcur + ycur) + wsize.side_w,
187                             zctr - (zcur + ycur) + wsize.header_h);
188     let mut wy = (s.row_size(Axis::Y) / 3) as f64;
189     for iy in (0..s.faces[0].len()).rev() {
190         let ysize = (s.size[Space::axis_idx(Axis::Y)][iy] / 3) as f64;
191         let mut wx = wshift;
192         for ix in 0..s.faces.len() {
193             let xsize = s.size[Space::axis_idx(Axis::X)][ix] as f64;
194             let mut wz = hshift;
195             for iz in 0..s.faces[0][0].len() {
196                 let zsize = s.size[Space::axis_idx(Axis::Z)][iz] as f64;
197                 match s.faces[ix][iy][iz] {
198                     Some(ss) => {
199                         let x = wx + wy;
200                         let z = wz + wy;
201                         if let Some((r, g, b)) = ss[Space::ori_idx(Ori::Back)] {
202                             Rectangle::new([r, g, b, 0.6])
203                                 .draw([0.0, 0.0, xsize, zsize],
204                                       &DrawState::default(),
205                                       c.transform.trans(x, z),
206                                       gl);
207                             Rectangle::new_border(BLACK, 1.0)
208                                 .draw([0.0, 0.0, xsize, zsize],
209                                       &DrawState::default(),
210                                       c.transform.trans(x, z),
211                                       gl);
212                         }
213                         if let Some((r, g, b)) = ss[Space::ori_idx(Ori::Top)] {
214                             Polygon::new([r, g, b, 0.6])
215                                 .draw(&[[x, z],
216                                       [x + xsize, z],
217                                       [x + xsize - ysize, z - ysize],
218                                       [x - ysize, z - ysize]],
219                                       &DrawState::default(),
220                                       c.transform,
221                                       gl);
222                             black_line!(c, gl, (x, z), (0.0, 0.0), (xsize, 0.0));
223                             black_line!(c, gl, (x, z), (xsize, 0.0), (xsize - ysize, -ysize));
224                             black_line!(c, gl, (x, z), (xsize - ysize, -ysize), (-ysize, -ysize));
225                             black_line!(c, gl, (x, z), (-ysize, -ysize), (0.0, 0.0));
226                         }
227                         if let Some((r, g, b)) = ss[Space::ori_idx(Ori::Side)] {
228                             Polygon::new([r, g, b, 0.6])
229                                 .draw(&[[x, z + zsize],
230                                       [x, z],
231                                       [x - ysize, z - ysize],
232                                       [x - ysize, z + zsize - ysize]],
233                                       &DrawState::default(),
234                                       c.transform,
235                                       gl);
236                             black_line!(c, gl, (x, z), (0.0, zsize), (0.0, 0.0));
237                             black_line!(c, gl, (x, z), (0.0, 0.0), (-ysize, -ysize));
238                             black_line!(c, gl, (x, z), (-ysize, -ysize), (-ysize, zsize - ysize));
239                             black_line!(c, gl, (x, z), (-ysize, zsize - ysize), (0.0, zsize));
240                         }
241                         if (ix, iy, iz) == s.current_node_coord() {
242                             Ellipse::new(RED)
243                                 .draw([-3.0, -3.0, 7.0, 7.0],
244                                       &DrawState::default(),
245                                       c.transform.trans(x, z),
246                                       gl);
247                             match s.current {
248                                 Pos::E((_, a)) => match a {
249                                     Axis::X => Line::new(RED, 1.0)
250                                         .draw([x, z, x + xsize, z],
251                                               &DrawState::default(),
252                                               c.transform,
253                                               gl),
254                                     Axis::Y => Line::new(RED, 1.0)
255                                         .draw([x, z, x - ysize, z - ysize],
256                                               &DrawState::default(),
257                                               c.transform,
258                                               gl),
259                                     Axis::Z => Line::new(RED, 1.0)
260                                         .draw([x, z, x, z + zsize],
261                                               &DrawState::default(),
262                                               c.transform,
263                                               gl),
264                                 },
265                                 Pos::F((_, o)) => match o {
266                                     Ori::Back => Rectangle::new_border(RED, 1.0)
267                                         .draw([0.0, 0.0, xsize, zsize],
268                                               &DrawState::default(),
269                                               c.transform.trans(x, z),
270                                               gl),
271                                     Ori::Top => {
272                                         red_line!(c, gl, (x, z), (0.0, 0.0), (xsize, 0.0));
273                                         red_line!(c, gl, (x, z), (xsize, 0.0), (xsize - ysize, -ysize));
274                                         red_line!(c, gl, (x, z), (xsize - ysize, -ysize), (-ysize, -ysize));
275                                         red_line!(c, gl, (x, z), (-ysize, -ysize), (0.0, 0.0));
276                                     },
277                                     Ori::Side => {
278                                         red_line!(c, gl, (x, z), (0.0, zsize), (0.0, 0.0));
279                                         red_line!(c, gl, (x, z), (0.0, 0.0), (-ysize, -ysize));
280                                         red_line!(c, gl, (x, z), (-ysize, -ysize), (-ysize, zsize - ysize));
281                                         red_line!(c, gl, (x, z), (-ysize, zsize - ysize), (0.0, zsize));
282                                     },
283                                 },
284                             }
285                         }
286                     },
287                     None => {},
288                 }
289                 wz += zsize;
290             }
291             wx += xsize;
292         }
293         wy -= ysize;
294     }
297 pub fn set_ui(ui: &mut UiCell, s: &mut State,
298           d: &mut Data, c: &mut Colors, wsize: &WSize)
300     match s {
301         &mut State::Design => {
302             header_ui(ui, c, wsize);
303             side_ui(ui, c, wsize);
304             std_ui(ui, s, d, c);
305         },
306         &mut State::Visual(_) => {
307             header_ui(ui, c, wsize);
308             visual_ui(ui, s, c);
309         },
310         &mut State::Extensions => /*extensions_ui(ui, s, c)*/ {},
311         &mut State::Err(_) => error_ui(ui, s, c),
312     }
315 fn error_ui(ui: &mut UiCell, gs: &mut State, c: &mut Colors)
317     {
318         Canvas::new()
319             .color(c.hide)
320             .set(C_MASTER, ui);
321         Canvas::new().middle_of(C_MASTER)
322             .color(c.c1).frame_color(c.c2)
323             .w_h(400.0, 120.0).pad(20.0)
324             .set(C_ERR, ui);
325         Text::new(match *gs {
326             State::Err(ref msg) => msg,
327             _=> "",
328         })
329             .color(c.font).mid_top_of(C_ERR)
330             .set(TXT_ERR, ui);
331     }
332     make_button(c)
333         .label("OK").mid_bottom_of(C_ERR)
334         .react(|| {
335             *gs = State::Design;
336         })
337         .set(BTN_OK_ERR, ui);
340 fn header_ui(ui: &mut UiCell, c: &mut Colors, wsize: &WSize)
342     Canvas::new()
343         .color(c.c1).frame_color(c.c2)
344         .y((wsize.h/2.0)-(wsize.header_h/2.0)).h(wsize.header_h)
345         .pad_left(20.0).pad_right(20.0)
346         .set(C_HEADER, ui);
349 fn side_ui(ui: &mut UiCell, c: &mut Colors, wsize: &WSize)
351     Canvas::new().color(c.c1)
352         .color(c.c1).frame_color(c.c2)
353         .x_y(-((wsize.w/2.0)-(wsize.side_w/2.0)), -(wsize.header_h/2.0))
354         .w_h(wsize.side_w, wsize.h - wsize.header_h)
355         .pad(20.0)
356         .set(C_SIDE, ui);
359 fn visual_ui(ui: &mut UiCell, gs: &mut State, c: &mut Colors)
361     make_button(c) 
362         .mid_right_of(C_HEADER)
363         .label("Extensions").mid_right_of(C_HEADER)
364         .react(|| {
365             *gs = State::Extensions;
366         })
367         .set(BTN_EXTENSIONS, ui);
368     make_disable_button!(c)
369         .mid_right_of(C_HEADER)
370         .left_from(BTN_EXTENSIONS, 0.0)
371         .label("Visual")
372         .set(DBTN_VISUAL, ui);
373     make_button(c) 
374         .mid_right_of(C_HEADER)
375         .label("Design").left_from(DBTN_VISUAL, 0.0)
376         .react(|| {
377             *gs = State::Design;
378         })
379         .set(BTN_DESIGN, ui);
381 fn std_ui(ui: &mut UiCell, gs: &mut State, d: &mut Data, c: &mut Colors)
383     let prev_id = if d.history.has_prev() {
384         Button::new()
385             .mid_left_of(C_HEADER).w_h(50.0, 30.0)
386             .color(c.c1).frame_color(c.c2)
387             .label_color(c.font)
388             .label("←").react(|| {
389                 d.load_prev();
390             })
391             .label_font_size(32)
392             .set(BTN_PREV, ui);
393         BTN_PREV
394     } else {
395         make_disable_button!(c)
396             .label("←")
397             .mid_left_of(C_HEADER).w_h(50.0, 30.0)
398             .label_font_size(32)
399             .set(DBTN_PREV, ui);
400         DBTN_PREV
401     };
402     let next_id = if d.history.has_next() {
403         Button::new()
404             .mid_left_of(C_HEADER).right_from(prev_id, 30.0)
405             .w_h(50.0, 30.0)
406             .color(c.c1).frame_color(c.c2)
407             .label_color(c.font)
408             .label("→").react(|| {
409                 d.load_next();
410             })
411             .label_font_size(32)
412             .set(BTN_NEXT, ui);
413         BTN_NEXT
414     } else {
415         make_disable_button!(c)
416             .label("→")
417             .mid_left_of(C_HEADER).right_from(prev_id, 30.0)
418             .w_h(50.0, 30.0)
419             .label_font_size(32)
420             .set(DBTN_NEXT, ui);
421         DBTN_NEXT
422     };
423     {
424         let mut select = vec![String::from("Edge (E)"),
425                               String::from("Face (F)")];
426         let mut select_idx = None;
427             DropDownList::new(&mut select, &mut select_idx)
428             .w_h(BUTTON_WIDTH, BUTTON_HEIGHT)
429                 .mid_left_of(C_HEADER).right_from(next_id, 30.0)
430             .max_visible_items(2)
431             .color(c.c1)
432             .frame_color(c.c2)
433             .label("Select")
434             .label_color(c.font)
435             .react(|selected_idx: &mut Option<usize>, new_idx, string: &str| {
436                 *selected_idx = Some(new_idx);
437                 d.space.set_pos_kind(match string {
438                     "Edge (E)" => PosKind::Edge,
439                     _ => PosKind::Face,
440                 });
441             })
442             .set(POS_KIND_SELECT, ui);
443     }
444     make_button(c)
445         .mid_left_of(C_HEADER).right_from(POS_KIND_SELECT, 30.0)
446         .label("Rotate (R)")
447         .react(|| {
448             d.space.rotate();
449         }).set(BTN_ROTATE, ui);
450     make_button(c)
451         .mid_right_of(C_HEADER)
452         .label("Extensions")
453         .react(|| {
454             *gs = State::Extensions;
455         }).set(BTN_EXTENSIONS, ui);
456     make_button(c)
457         .mid_right_of(C_HEADER)
458         .left_from(BTN_EXTENSIONS, 0.0)
459         .label("Visual")
460         .react(|| {
461             *gs = State::Visual(None);
462         }).set(BTN_VISUAL, ui);
463     make_disable_button!(c) 
464         .mid_right_of(C_HEADER)
465         .label("Design").left_from(BTN_VISUAL, 0.0)
466         .set(DBTN_DESIGN, ui);
467     match d.space.current {
468         Pos::F(((x, y, z), o)) => {
469             if let Some(n) = d.space.faces[x][y][z] {
470                     if let Some((r, g, b)) = n[Space::ori_idx(o)] {
471                     make_disable_button!(c)
472                         .mid_top_of(C_SIDE)
473                         .label("Add (A)")
474                         .set(DBTN_ADD, ui);
475                     make_button(c)
476                         .down(20.0).label("Delete (D)")
477                         .react(|| {
478                             d.space.remove_face(((x, y, z), o));
479                         }).set(BTN_ADD, ui);
480                     for i in 0..3 {
481                         let color = match i {
482                             0 => color::rgb(r, 0.0, 0.0),
483                             1 => color::rgb(0.0, g, 0.0),
484                             _ => color::rgb(0.0, 0.0, b),
485                         };
486                     
487                         let value = match i {
488                             0 => r,
489                             1 => g,
490                             _ => b,
491                         };
492                     
493                         Slider::new(value, 0.0, 1.0)
494                             .and(|slider| if i == 0 { slider.down(40.0) } else { slider.right(10.0) })
495                             .w_h(30.0, 80.0)
496                             .color(color)
497                             .label(&format!("{:.*}", 2, value))
498                             .label_color(color.plain_contrast())
499                             .frame_color(c.c2)
500                             .react(|v| d.space.set_current_face_color_if_any(match i {
501                                 0 => (v, g, b),
502                                 1 => (r, v, b),
503                                 _ => (r, g, v),
504                             }))
505                             .set(COLOR_SLIDER + i, ui);
506                             }
507                     {
508                         let color = color::rgb(r, g, b);
509                                                 let mut colors = vec![
510                             "Black".to_string(),
511                             "Gray".to_string(),
512                             "White".to_string(),
513                             "Brown".to_string(),
514                             "Red".to_string(),
515                             "Orange".to_string(),
516                             "Yellow".to_string(),
517                             "Green".to_string(),
518                             "Blue".to_string(),
519                             "Purple".to_string()
520                         ];
521                         let mut selected_idx = None;
522                                                 DropDownList::new(&mut colors, &mut selected_idx)
523                                                     .w_h(BUTTON_WIDTH, BUTTON_HEIGHT)
524                             .mid_top_of(C_SIDE).down(20.0)
525                                                     .color(color)
526                                                     .frame_color(c.c2)
527                                                     .label("Colors")
528                                                     .label_color(color.plain_contrast())
529                                                     .react(|selected_idx: &mut Option<usize>, new_idx, string: &str| {
530                                                         *selected_idx = Some(new_idx);
531                                                         d.space.set_current_face_color_if_any(match string {
532                                                             "Black" => (0.0, 0.0, 0.0),
533                                                             "White" => (1.0, 1.0, 1.0),
534                                                             "Red"   => (1.0, 0.0, 0.0),
535                                                             "Green" => (0.0, 1.0, 0.0),
536                                                             "Blue"  => (0.0, 0.0, 1.0),
537                                     "Yellow" => (color::YELLOW.red(),
538                                                  color::YELLOW.green(),
539                                                  color::YELLOW.blue()),
540                                     "Purple" => (color::PURPLE.red(),
541                                                  color::PURPLE.green(),
542                                                  color::PURPLE.blue()),
543                                     "Orange" => (color::ORANGE.red(),
544                                                  color::ORANGE.green(),
545                                                  color::ORANGE.blue()),
546                                     "Gray" => (color::GRAY.red(),
547                                                color::GRAY.green(),
548                                                color::GRAY.blue()),
549                                     "Brown" => (color::BROWN.red(),
550                                                 color::BROWN.green(),
551                                                 color::BROWN.blue()),
552                                                             _       => (1.0, 1.0, 1.0),
553                                                         });
554                                                     })
555                                                     .set(COLOR_SELECT, ui);
556                     }
557                 } else {
558                     make_button(c)
559                         .mid_top_of(C_SIDE).label("Add (A)")
560                         .react(|| {
561                             d.space.add_face(((x, y, z), o));
562                         }).set(BTN_ADD, ui);
563                     make_disable_button!(c)
564                         .down(20.0).label("Delete (D)")
565                         .set(DBTN_DEL, ui);
566                     for i in 0..3 {
567                         Slider::new(0.0, 0.0, 1.0)
568                             .and(|slider| if i == 0 {
569                                 slider.down(40.0)
570                             } else {
571                                 slider.right(10.0)
572                             })
573                             .w_h(30.0, 80.0)
574                             .color(c.c1)
575                             .label_color(c.font)
576                             .frame_color(c.c2)
577                             .react(|_| {})
578                             .set(DCOLOR_SLIDER + i, ui);
579                     }
580                     make_disable_button!(c)
581                         .mid_top_of(C_SIDE).down(20.0)
582                         .label("Color")
583                         .set(DBTN_SELECT_COLOR, ui);
584                 }
585             }
586         },
587         Pos::E(e) => {
588             Text::new("Length")
589                 .top_left_of(C_SIDE)
590                 .down(115.0)
591                 .font_size(20)
592                 .color(c.font)
593                 .set(LENGTH_TXT, ui);
594             let length = d.space.get_edge_size(e) as f64;
595             Slider::new(length, 1.0, 1024.0)
596                 .top_right_of(C_SIDE)
597                 .w_h(30.0, 200.0)
598                 .color(c.c1)
599                 .label(&format!("{:.*}", 0, length))
600                 .label_color(c.font)
601                 .frame_color(c.c2)
602                 .react(|v| d.space.edge_size(e, v as usize))
603                 .set(SIZE_SLIDER, ui);
604         },
605         }