Draw terrains within their relevant positions on the hex grid (#22)
Co-authored-by: Savanni D'Gerinel <savanni@luminescent-dreams.com> Reviewed-on: savanni/tools#22
This commit is contained in:
parent
363a4632b1
commit
4163ccb5c2
|
@ -18,7 +18,7 @@ use gtk::{
|
||||||
gdk_pixbuf::Pixbuf, gio, prelude::*, subclass::prelude::*, Application, CompositeTemplate,
|
gdk_pixbuf::Pixbuf, gio, prelude::*, subclass::prelude::*, Application, CompositeTemplate,
|
||||||
DrawingArea, Label,
|
DrawingArea, Label,
|
||||||
};
|
};
|
||||||
use image::io::Reader as ImageReader;
|
use image::{io::Reader as ImageReader, DynamicImage};
|
||||||
use std::{cell::RefCell, io::Cursor, rc::Rc};
|
use std::{cell::RefCell, io::Cursor, rc::Rc};
|
||||||
|
|
||||||
const APP_ID: &'static str = "com.luminescent-dreams.hex-grid";
|
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.);
|
const DRAWING_ORIGIN: (f64, f64) = (1024. / 2., 768. / 2.);
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum Tile {
|
enum Terrain {
|
||||||
Empty,
|
Empty,
|
||||||
Mountain,
|
Mountain,
|
||||||
Grasslands,
|
Grasslands,
|
||||||
|
@ -38,28 +38,13 @@ enum Tile {
|
||||||
Swamp,
|
Swamp,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tile {
|
impl Default for Terrain {
|
||||||
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 {
|
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Empty
|
Self::Empty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for Tile {
|
impl From<&str> for Terrain {
|
||||||
fn from(s: &str) -> Self {
|
fn from(s: &str) -> Self {
|
||||||
match s {
|
match s {
|
||||||
"m" => Self::Mountain,
|
"m" => Self::Mountain,
|
||||||
|
@ -74,12 +59,43 @@ impl From<&str> for Tile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<String> for Tile {
|
impl From<String> for Terrain {
|
||||||
fn from(s: String) -> Self {
|
fn from(s: String) -> Self {
|
||||||
Self::from(s.as_ref())
|
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() {
|
fn main() {
|
||||||
gio::resources_register_include!("com.luminescent-dreams.hex-grid.gresource")
|
gio::resources_register_include!("com.luminescent-dreams.hex-grid.gresource")
|
||||||
.expect("Failed to register resources");
|
.expect("Failed to register resources");
|
||||||
|
@ -133,7 +149,7 @@ impl ObjectImpl for HexGridWindowPrivate {
|
||||||
.expect("map should be in the bundle")
|
.expect("map should be in the bundle")
|
||||||
.to_vec();
|
.to_vec();
|
||||||
|
|
||||||
let hex_map = parse_data::<Tile>(String::from_utf8(map_text_resource).unwrap().lines());
|
let hex_map = parse_data::<Terrain>(String::from_utf8(map_text_resource).unwrap().lines());
|
||||||
|
|
||||||
let terrain_data = resources_lookup_data(
|
let terrain_data = resources_lookup_data(
|
||||||
"/com/luminescent-dreams/hex-grid/terrain.ppm",
|
"/com/luminescent-dreams/hex-grid/terrain.ppm",
|
||||||
|
@ -143,27 +159,15 @@ impl ObjectImpl for HexGridWindowPrivate {
|
||||||
let reader = ImageReader::new(Cursor::new(terrain_data))
|
let reader = ImageReader::new(Cursor::new(terrain_data))
|
||||||
.with_guessed_format()
|
.with_guessed_format()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let image = reader.decode().unwrap();
|
let image = reader.decode().unwrap();
|
||||||
|
let deep_water = Tile::new(&image, Terrain::DeepWater);
|
||||||
let image_deep_water = pixbuf_from_image_tile(image.clone().crop(0, 0, 100, 88));
|
let shallow_water = Tile::new(&image, Terrain::ShallowWater);
|
||||||
let image_shallow_water = pixbuf_from_image_tile(image.clone().crop(100, 0, 100, 88));
|
let grasslands = Tile::new(&image, Terrain::Grasslands);
|
||||||
let image_grasslands = pixbuf_from_image_tile(image.clone().crop(200, 0, 100, 88));
|
let desert = Tile::new(&image, Terrain::Desert);
|
||||||
let image_desert = pixbuf_from_image_tile(image.clone().crop(300, 0, 100, 88));
|
let mountain = Tile::new(&image, Terrain::Mountain);
|
||||||
let image_mountain = pixbuf_from_image_tile(image.clone().crop(0, 88, 100, 88));
|
let badlands = Tile::new(&image, Terrain::Badlands);
|
||||||
let image_badlands = pixbuf_from_image_tile(image.clone().crop(100, 88, 100, 88));
|
let swamp = Tile::new(&image, Terrain::Swamp);
|
||||||
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 motion_controller = gtk::EventControllerMotion::new();
|
let motion_controller = gtk::EventControllerMotion::new();
|
||||||
{
|
{
|
||||||
|
@ -197,30 +201,16 @@ impl ObjectImpl for HexGridWindowPrivate {
|
||||||
|
|
||||||
context.set_line_width(2.);
|
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()]
|
for coordinate in vec![AxialAddr::origin()]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(AxialAddr::origin().addresses(MAP_RADIUS))
|
.chain(AxialAddr::origin().addresses(MAP_RADIUS))
|
||||||
|
@ -231,15 +221,22 @@ impl ObjectImpl for HexGridWindowPrivate {
|
||||||
+ HEX_RADIUS
|
+ HEX_RADIUS
|
||||||
* ((3. as f64).sqrt() / 2. * (coordinate.q() as f64)
|
* ((3. as f64).sqrt() / 2. * (coordinate.q() as f64)
|
||||||
+ (3. as f64).sqrt() * (coordinate.r() 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 tile = match hex_map.get(&coordinate).unwrap() {
|
||||||
let color = tile.color();
|
Terrain::Mountain => &mountain,
|
||||||
context.set_source_rgb(color.0, color.1, color.2);
|
Terrain::Grasslands => &grasslands,
|
||||||
|
Terrain::ShallowWater => &shallow_water,
|
||||||
draw_hexagon(context, center_x, center_y, HEX_RADIUS);
|
Terrain::DeepWater => &deep_water,
|
||||||
let _ = context.fill();
|
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);
|
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();
|
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();
|
context.new_path();
|
||||||
let points = hexagon(width, height);
|
let points = hexagon(width, height);
|
||||||
context.move_to(points[0].0, points[0].1);
|
context.move_to(translate_x + points[0].0, translate_y + points[0].1);
|
||||||
context.line_to(points[1].0, points[1].1);
|
context.line_to(translate_x + points[1].0, translate_y + points[1].1);
|
||||||
context.line_to(points[2].0, points[2].1);
|
context.line_to(translate_x + points[2].0, translate_y + points[2].1);
|
||||||
context.line_to(points[3].0, points[3].1);
|
context.line_to(translate_x + points[3].0, translate_y + points[3].1);
|
||||||
context.line_to(points[4].0, points[4].1);
|
context.line_to(translate_x + points[4].0, translate_y + points[4].1);
|
||||||
context.line_to(points[5].0, points[5].1);
|
context.line_to(translate_x + points[5].0, translate_y + points[5].1);
|
||||||
context.copy_path().expect("to successfully copy a path")
|
context.copy_path().expect("to successfully copy a path")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue