diff --git a/hex-grid/src/main.rs b/hex-grid/src/main.rs index 37e3bed..7d62cc2 100644 --- a/hex-grid/src/main.rs +++ b/hex-grid/src/main.rs @@ -18,7 +18,7 @@ use gtk::{ gdk_pixbuf::Pixbuf, gio, prelude::*, subclass::prelude::*, Application, CompositeTemplate, DrawingArea, Label, }; -use image::io::Reader as ImageReader; +use image::{io::Reader as ImageReader, DynamicImage}; use std::{cell::RefCell, io::Cursor, rc::Rc}; const APP_ID: &'static str = "com.luminescent-dreams.hex-grid"; @@ -27,7 +27,7 @@ const MAP_RADIUS: usize = 3; const DRAWING_ORIGIN: (f64, f64) = (1024. / 2., 768. / 2.); #[derive(Clone, Debug)] -enum Tile { +enum Terrain { Empty, Mountain, Grasslands, @@ -38,28 +38,13 @@ enum Tile { Swamp, } -impl Tile { - fn color(&self) -> (f64, f64, f64) { - match self { - Self::Empty => (0., 0., 0.), - Self::Mountain => (0.5, 0.5, 0.5), - Self::Grasslands => (0., 1., 0.), - Self::ShallowWater => (0.5, 0.5, 1.), - Self::DeepWater => (0., 0., 1.), - Self::Badlands => (0.5, 0.25, 0.), - Self::Desert => (1., 1., 0.), - Self::Swamp => (0., 0.25, 0.), - } - } -} - -impl Default for Tile { +impl Default for Terrain { fn default() -> Self { Self::Empty } } -impl From<&str> for Tile { +impl From<&str> for Terrain { fn from(s: &str) -> Self { match s { "m" => Self::Mountain, @@ -74,12 +59,43 @@ impl From<&str> for Tile { } } -impl From for Tile { +impl From for Terrain { fn from(s: String) -> Self { Self::from(s.as_ref()) } } +struct Tile { + terrain: Terrain, + image: Pixbuf, +} + +impl Tile { + fn new(source: &DynamicImage, terrain: Terrain) -> Tile { + let image = match terrain { + Terrain::DeepWater => pixbuf_from_image_tile(source.clone().crop(0, 0, 100, 88)), + Terrain::ShallowWater => pixbuf_from_image_tile(source.clone().crop(100, 0, 100, 88)), + Terrain::Grasslands => pixbuf_from_image_tile(source.clone().crop(200, 0, 100, 88)), + Terrain::Desert => pixbuf_from_image_tile(source.clone().crop(300, 0, 100, 88)), + Terrain::Mountain => pixbuf_from_image_tile(source.clone().crop(0, 88, 100, 88)), + Terrain::Badlands => pixbuf_from_image_tile(source.clone().crop(100, 88, 100, 88)), + Terrain::Swamp => pixbuf_from_image_tile(source.clone().crop(0, 176, 100, 88)), + Terrain::Empty => pixbuf_from_image_tile(source.clone().crop(300, 176, 100, 88)), + }; + + Tile { terrain, image } + } + + fn render_on_context(&self, context: &Context, translate_x: f64, translate_y: f64) { + context.save().unwrap(); + context.append_path(&hexagon_path(context, translate_x, translate_y, 100., 88.)); + context.clip(); + context.set_source_pixbuf(&self.image, translate_x, translate_y); + context.paint().expect("paint should succeed"); + context.restore().unwrap(); + } +} + fn main() { gio::resources_register_include!("com.luminescent-dreams.hex-grid.gresource") .expect("Failed to register resources"); @@ -133,7 +149,7 @@ impl ObjectImpl for HexGridWindowPrivate { .expect("map should be in the bundle") .to_vec(); - let hex_map = parse_data::(String::from_utf8(map_text_resource).unwrap().lines()); + let hex_map = parse_data::(String::from_utf8(map_text_resource).unwrap().lines()); let terrain_data = resources_lookup_data( "/com/luminescent-dreams/hex-grid/terrain.ppm", @@ -143,27 +159,15 @@ impl ObjectImpl for HexGridWindowPrivate { let reader = ImageReader::new(Cursor::new(terrain_data)) .with_guessed_format() .unwrap(); + let image = reader.decode().unwrap(); - - let image_deep_water = pixbuf_from_image_tile(image.clone().crop(0, 0, 100, 88)); - let image_shallow_water = pixbuf_from_image_tile(image.clone().crop(100, 0, 100, 88)); - let image_grasslands = pixbuf_from_image_tile(image.clone().crop(200, 0, 100, 88)); - let image_desert = pixbuf_from_image_tile(image.clone().crop(300, 0, 100, 88)); - let image_mountain = pixbuf_from_image_tile(image.clone().crop(0, 88, 100, 88)); - let image_badlands = pixbuf_from_image_tile(image.clone().crop(100, 88, 100, 88)); - let image_swamp = pixbuf_from_image_tile(image.clone().crop(0, 176, 100, 88)); - - /* - let image_pb = Pixbuf::from_bytes( - &glib::Bytes::from(image.as_bytes()), - gtk::gdk_pixbuf::Colorspace::Rgb, - false, - 8, - image.width() as i32, - image.height() as i32, - image.to_rgb8().sample_layout().height_stride as i32, - ); - */ + let deep_water = Tile::new(&image, Terrain::DeepWater); + let shallow_water = Tile::new(&image, Terrain::ShallowWater); + let grasslands = Tile::new(&image, Terrain::Grasslands); + let desert = Tile::new(&image, Terrain::Desert); + let mountain = Tile::new(&image, Terrain::Mountain); + let badlands = Tile::new(&image, Terrain::Badlands); + let swamp = Tile::new(&image, Terrain::Swamp); let motion_controller = gtk::EventControllerMotion::new(); { @@ -197,30 +201,16 @@ impl ObjectImpl for HexGridWindowPrivate { context.set_line_width(2.); - let clip_path = hexagon_path(context, 100., 88.); - context.set_source_pixbuf(&image_deep_water, 0., 0.); - context.append_path(&clip_path); - context.clip(); - context.paint().expect("paint should succeed"); - - context.set_source_pixbuf(&image_shallow_water, 150., 0.); - context.paint().expect("paint should succeed"); - - context.set_source_pixbuf(&image_grasslands, 300., 0.); - context.paint().expect("paint should succeed"); - - context.set_source_pixbuf(&image_desert, 450., 0.); - context.paint().expect("paint should succeed"); - - context.set_source_pixbuf(&image_mountain, 0., 100.); - context.paint().expect("paint should succeed"); - - context.set_source_pixbuf(&image_badlands, 150., 100.); - context.paint().expect("paint should succeed"); - - context.set_source_pixbuf(&image_swamp, 0., 200.); - context.paint().expect("paint should succeed"); /* + deep_water.render_on_context(&context, 0., 0.); + shallow_water.render_on_context(&context, 150., 0.); + grasslands.render_on_context(&context, 300., 0.); + desert.render_on_context(&context, 450., 0.); + mountain.render_on_context(&context, 0., 100.); + badlands.render_on_context(&context, 150., 100.); + swamp.render_on_context(&context, 0., 200.); + */ + for coordinate in vec![AxialAddr::origin()] .into_iter() .chain(AxialAddr::origin().addresses(MAP_RADIUS)) @@ -231,15 +221,22 @@ impl ObjectImpl for HexGridWindowPrivate { + HEX_RADIUS * ((3. as f64).sqrt() / 2. * (coordinate.q() as f64) + (3. as f64).sqrt() * (coordinate.r() as f64)); + let translate_x = center_x - HEX_RADIUS; + let translate_y = center_y - (3. as f64).sqrt() * HEX_RADIUS / 2.; - let tile = hex_map.get(&coordinate).unwrap(); - let color = tile.color(); - context.set_source_rgb(color.0, color.1, color.2); - - draw_hexagon(context, center_x, center_y, HEX_RADIUS); - let _ = context.fill(); + let tile = match hex_map.get(&coordinate).unwrap() { + Terrain::Mountain => &mountain, + Terrain::Grasslands => &grasslands, + Terrain::ShallowWater => &shallow_water, + Terrain::DeepWater => &deep_water, + Terrain::Badlands => &badlands, + Terrain::Desert => &desert, + Terrain::Swamp => &swamp, + _ => panic!("unhandled terrain type"), + }; + tile.render_on_context(context, translate_x, translate_y); + // draw_hexagon(context, center_x, center_y, HEX_RADIUS); } - */ }); } self.drawing_area.add_controller(&motion_controller); @@ -275,15 +272,21 @@ fn draw_hexagon(context: &Context, center_x: f64, center_y: f64, radius: f64) { context.close_path(); } -fn hexagon_path(context: &Context, width: f64, height: f64) -> Path { +fn hexagon_path( + context: &Context, + translate_x: f64, + translate_y: f64, + width: f64, + height: f64, +) -> Path { context.new_path(); let points = hexagon(width, height); - context.move_to(points[0].0, points[0].1); - context.line_to(points[1].0, points[1].1); - context.line_to(points[2].0, points[2].1); - context.line_to(points[3].0, points[3].1); - context.line_to(points[4].0, points[4].1); - context.line_to(points[5].0, points[5].1); + context.move_to(translate_x + points[0].0, translate_y + points[0].1); + context.line_to(translate_x + points[1].0, translate_y + points[1].1); + context.line_to(translate_x + points[2].0, translate_y + points[2].1); + context.line_to(translate_x + points[3].0, translate_y + points[3].1); + context.line_to(translate_x + points[4].0, translate_y + points[4].1); + context.line_to(translate_x + points[5].0, translate_y + points[5].1); context.copy_path().expect("to successfully copy a path") }