diff options
| author | tslil clingman <> | 2020-11-01 21:50:21 -0500 |
|---|---|---|
| committer | tslil clingman <> | 2020-11-01 21:50:21 -0500 |
| commit | c120fcdaac1911280f18ddb153068a2589823b7d (patch) | |
| tree | 97e443724a5aaffb050dd55bf1719201ecde44c6 /src | |
| parent | 9ac8a63a5319fb4c846d7b9f777b1572a47d9560 (diff) | |
Working on rendering
Diffstat (limited to 'src')
| -rw-r--r-- | src/gui.rs | 327 |
1 files changed, 288 insertions, 39 deletions
@@ -1,44 +1,302 @@ +/* + This file is part of Takwrap. + + Takwrap is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see <https://www.gnu.org/licenses/>. +*/ + use fltk::{app::*, button::*, draw::*, frame::*, menu::*, text::*, window::*}; +use crate::game::*; + +struct BoardRendererBackend { + xorg: i32, + yorg: i32, + square_size: i32, +} + +impl BoardRendererBackend { + fn initialise(frame: &Frame, frame_pad: i32, game_size: i32) -> BoardRendererBackend { + let x0 = frame.x(); + let y0 = frame.y(); + let xorg = x0 + frame_pad; + let yorg = y0 + frame_pad; + let board_size = i32::min(frame.width(), frame.height()) - frame_pad * 2; + + BoardRendererBackend { + xorg, + yorg, + square_size: board_size / game_size, + } + } +} + #[derive(Clone, Copy)] -enum GUIMessage { - None, - Test, +struct BoardRenderer { + even_square_colour: Color, + odd_square_colour: Color, + black_colour: Color, + white_colour: Color, + stone_size: f32, + game_size: i32, +} + +impl BoardRenderer { + fn default() -> BoardRenderer { + BoardRenderer { + // Sandy brown + odd_square_colour: Color::from_rgb(244, 164, 96), + // Moccasin + even_square_colour: Color::from_rgb(255, 228, 181), + // Colours for pieces + black_colour: Color::from_rgb(178, 34, 34), + white_colour: Color::from_rgb(250, 235, 215), + // Gap between stones and edged of square + stone_size: 0.8, + // I'm not sure where this should go + game_size: 5, + } + } + + fn with_size(mut self, size: i32) -> BoardRenderer { + self.game_size = size; + return self; + } + + fn render_stone( + &self, + backend: &BoardRendererBackend, + player: Player, + stone: Stone, + row: i32, + col: i32, + shift: i32, + ) { + set_draw_color(match player { + Player::Black => self.black_colour, + Player::White => self.white_colour, + }); + + // Gap scales with square size + let stone_gap = (backend.square_size as f32 * (1.00 - self.stone_size)).round() as i32; + let d = backend.square_size - stone_gap * 2; + let shift = (shift * d) / 8; + let x1 = backend.xorg + col * backend.square_size + stone_gap + shift; + let y1 = backend.yorg + row * backend.square_size + stone_gap - shift; + + // Outline with thickness varying by stone size + let setup_border_pen = || { + set_line_style(LineStyle::Solid, i32::max(1, d / 24)); + set_draw_color(Color::Black); + }; + + match stone { + Stone::Capstone => { + draw_pie(x1, y1, d, d, 0.0, 360.0); + setup_border_pen(); + let r = (d as f64) / 2.0; + draw_circle(r + x1 as f64, r + y1 as f64, r); + } + Stone::Flat => { + draw_rectf(x1, y1, d, d); + setup_border_pen(); + draw_rect_with_color(x1, y1, d, d, Color::Black); + } + Stone::Standing => { + // These look bad shifted so we shift it back if was + let df = d as f64; + // and we have to fudge it a little? Some rounding things presumably + let ds = if shift > 0 { df / 8.0 - 0.5 } else { 0.0 }; + let x1 = x1 as f64 - ds; + let y1 = y1 as f64 + ds; + + let verts = [ + (x1 + df * 0.3, y1 + df * 0.075), + (x1 + df * 0.925, y1 + df * 0.7), + (x1 + df * 0.7, y1 + df * 0.925), + (x1 + df * 0.075, y1 + df * 0.3), + ]; + + begin_polygon(); + for (x, y) in verts.iter() { + vertex(*x, *y) + } + end_polygon(); + + setup_border_pen(); + begin_loop(); + for (x, y) in verts.iter() { + vertex(*x, *y); + } + end_loop(); + } + } + set_line_style(LineStyle::Solid, 1); + } + + fn render_icons( + &self, + backend: &BoardRendererBackend, + player: Player, + row: i32, + col: i32, + shift: i32, + ) { + let stone_gap = backend.square_size as f32 * (1.00 - self.stone_size); + let icon_gap = (stone_gap * 0.15).round() as i32; + let icon_width = (stone_gap * 0.7).round() as i32; + let icon_height = icon_width / 2; + + let x1 = backend.xorg + col * backend.square_size + icon_gap; + let y1 = backend.yorg + (row + 1) * backend.square_size - icon_gap; + + set_line_style(LineStyle::Solid, i32::max(1, icon_width / 8)); + set_draw_color(Color::Black); + + set_draw_color(match player { + Player::Black => self.black_colour, + Player::White => self.white_colour, + }); + let y2 = y1 - icon_height * (1 + shift as i32); + draw_rectf(x1, y2, icon_width, icon_height); + draw_rect_with_color(x1, y2, icon_width, icon_height, Color::Black); + + set_line_style(LineStyle::Solid, 1); + } + + fn render_stack(&self, backend: &BoardRendererBackend, row: i32, col: i32, stack: &Stack) { + // Is there a better way than usize::min((...), i32::MAX as usize) as i32 ? + let cutoff = stack.len() as i32 - self.game_size; + + for (shift, piece) in stack.iter().enumerate() { + let shift = shift as i32; // TODO: please don't overflow + if shift < cutoff { + self.render_icons(backend, piece.player, row, col, shift); + } else { + self.render_stone( + backend, + piece.player, + piece.stone, + row, + col, + shift - i32::max(0, cutoff), + ); + } + } + } + + fn render_board(&self, backend: &BoardRendererBackend) { + for x in 0..self.game_size { + for y in 0..self.game_size { + if (x + y) % 2 == 0 { + set_draw_color(self.even_square_colour); + } else { + set_draw_color(self.odd_square_colour); + } + draw_rectf( + backend.xorg + x * backend.square_size, + backend.yorg + y * backend.square_size, + backend.square_size, + backend.square_size, + ); + } + } + } } struct GUIBoard { frame: Frame, - test: bool, + renderer: BoardRenderer, + size: i32, } impl GUIBoard { - fn new(frame: Frame) -> GUIBoard { - let mut g = GUIBoard { frame, test: false }; - g.frame.draw2(g.draw()); - g + fn new(frame: Frame, size: i32) -> GUIBoard { + let renderer = BoardRenderer::default().with_size(size); + let mut g = GUIBoard { + frame, + size, + renderer, + }; + g.frame.draw2(g.render()); + return g; } - fn change(&mut self) { - self.test = !self.test; + fn render(&self) -> impl Fn(&mut Frame) { + let renderer = self.renderer; + let game_size = self.size; - self.frame.draw2(self.draw()); - self.frame.set_damage(true); - } + let stack = vec![ + Piece { + player: Player::Black, + stone: Stone::Flat, + }, + Piece { + player: Player::White, + stone: Stone::Flat, + }, + Piece { + player: Player::White, + stone: Stone::Flat, + }, + Piece { + player: Player::Black, + stone: Stone::Flat, + }, + Piece { + player: Player::White, + stone: Stone::Flat, + }, + Piece { + player: Player::Black, + stone: Stone::Flat, + }, + Piece { + player: Player::Black, + stone: Stone::Flat, + }, + Piece { + player: Player::White, + stone: Stone::Flat, + }, + Piece { + player: Player::Black, + stone: Stone::Flat, + }, + Piece { + player: Player::White, + stone: Stone::Standing, + }, + ]; - fn draw(&self) -> impl Fn(&mut Frame) { - let tc = self.test; move |frame: &mut Frame| { - const BOARD_PAD_INNER: i32 = 5; - let bx = frame.x() + BOARD_PAD_INNER; - let by = frame.y() + BOARD_PAD_INNER; - push_clip(frame.x(), frame.y(), frame.width(), frame.height()); - set_draw_color(if tc { Color::Blue } else { Color::Red }); - draw_rectf(bx, by, 100, 30); - pop_clip(); + let backend = BoardRendererBackend::initialise(frame, 5, game_size); + renderer.render_board(&backend); + + // Test code for now + renderer.render_stack(&backend, 1, 4, &stack); + renderer.render_stone(&backend, Player::Black, Stone::Capstone, 1, 2, 0); + renderer.render_stone(&backend, Player::Black, Stone::Standing, 2, 2, 0); } } } +#[derive(Clone, Copy)] +enum GUIMessage { + None, + Test, +} + pub struct GUI { app: App, window: Window, @@ -65,7 +323,8 @@ impl GUI { let (sender, receiver) = channel::<GUIMessage>(); - let mut menu = SysMenuBar::default().with_size(800, 24); + const MENU_HEIGHT: i32 = 24; + let mut menu = SysMenuBar::default().with_size(800, MENU_HEIGHT); // menu.set_text_font(Font::Helvetica); menu.set_color(Color::Light2); menu.add_emit( @@ -83,28 +342,23 @@ impl GUI { GUIMessage::Test, ); - const LOG_WIDTH: i32 = 10 * 16; - const LOG_PAD: i32 = 0; + const LOG_WIDTH: i32 = 800 - 600 + MENU_HEIGHT; let mut log = TextDisplay::default() - .with_size(LOG_WIDTH, 600 - 2 * LOG_PAD - menu.height()) - .with_pos(800 - LOG_WIDTH - LOG_PAD, menu.height()); + .with_size(LOG_WIDTH, 600 - menu.height()) + .with_pos(800 - LOG_WIDTH, menu.height()); log.set_buffer(Some(TextBuffer::default())); log.insert("test"); - const BOARD_PAD: i32 = 0; let mut board_frame = Frame::default() - .with_size( - 800 - LOG_WIDTH - LOG_PAD * 2 - BOARD_PAD, - 600 - 2 * BOARD_PAD - menu.height(), - ) - .below_of(&menu, BOARD_PAD); + .with_size(800 - LOG_WIDTH, 600 - menu.height()) + .below_of(&menu, 0); board_frame.set_frame(FrameType::EmbossedBox); window.resizable(&mut board_frame); window.end(); window.show(); - let board = GUIBoard::new(board_frame); + let board = GUIBoard::new(board_frame, 5); // button.emit(sender, GUIMessage::Foo); @@ -119,14 +373,9 @@ impl GUI { } pub fn run(&mut self) -> Result<(), fltk::prelude::FltkError> { - // self.window.show(); - while self.app.wait() { if let Some(msg) = self.receiver.recv() { match msg { - GUIMessage::Test => { - self.board.change(); - } _ => (), } } |
