diff --git a/hex-grid/src/main.rs b/hex-grid/src/main.rs
index b3b88b6..61f0b54 100644
--- a/hex-grid/src/main.rs
+++ b/hex-grid/src/main.rs
@@ -10,94 +10,24 @@ Luminescent Dreams Tools is distributed in the hope that it will be useful, but
You should have received a copy of the GNU General Public License along with Lumeto. If not, see .
*/
-use cairo::{Context, Path};
+use cairo::Context;
use coordinates::{hex_map::parse_data, AxialAddr};
use gio::resources_lookup_data;
use glib::{subclass::InitializingObject, Object};
-use gtk::{
- gdk_pixbuf::Pixbuf, gio, prelude::*, subclass::prelude::*, Application, CompositeTemplate,
- DrawingArea, Label,
-};
-use image::{io::Reader as ImageReader, DynamicImage};
+use gtk::{gio, prelude::*, subclass::prelude::*, Application, DrawingArea};
+use image::io::Reader as ImageReader;
use std::{cell::RefCell, io::Cursor, rc::Rc};
mod labeled_field;
+mod palette_entry;
+mod tile;
+mod utilities;
const APP_ID: &'static str = "com.luminescent-dreams.hex-grid";
const HEX_RADIUS: f64 = 50.;
const MAP_RADIUS: usize = 3;
const DRAWING_ORIGIN: (f64, f64) = (1024. / 2., 768. / 2.);
-#[derive(Clone, Debug)]
-enum Terrain {
- Badlands,
- DeepWater,
- Desert,
- Empty,
- Grasslands,
- Mountain,
- ShallowWater,
- Swamp,
-}
-
-impl Default for Terrain {
- fn default() -> Self {
- Self::Empty
- }
-}
-
-impl From<&str> for Terrain {
- fn from(s: &str) -> Self {
- match s {
- "m" => Self::Mountain,
- "g" => Self::Grasslands,
- "sw" => Self::ShallowWater,
- "dw" => Self::DeepWater,
- "b" => Self::Badlands,
- "d" => Self::Desert,
- "s" => Self::Swamp,
- _ => Self::Empty,
- }
- }
-}
-
-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");
@@ -162,49 +92,9 @@ impl ObjectSubclass for HexGridWindowPrivate {
.spacing(8)
.build();
- /*
- let canvas_address_row = gtk::Box::builder()
- .hexpand(true)
- .spacing(8)
- .homogeneous(true)
- .build();
-
- canvas_address_row.append(>k::Label::builder().label("Canvas Address").build());
-
- let canvas_address = gtk::Label::builder()
- .label("-----")
- .margin_start(4)
- .margin_end(4)
- .margin_top(4)
- .margin_bottom(4)
- .build();
-
- canvas_address_row.append(&canvas_address);
- */
-
let canvas_address = labeled_field::LabeledField::new("Canvas Address", "-----");
let hex_address = labeled_field::LabeledField::new("Hex Address", "-----");
- /*
- let hex_address_row = gtk::Box::builder()
- .hexpand(true)
- .spacing(8)
- .homogeneous(true)
- .build();
-
- hex_address_row.append(>k::Label::builder().label("Hex Address").build());
-
- let hex_address = gtk::Label::builder()
- .label("-----")
- .margin_start(4)
- .margin_end(4)
- .margin_top(4)
- .margin_bottom(4)
- .build();
-
- hex_address_row.append(&hex_address);
- */
-
let palette = gtk::Box::builder()
.spacing(8)
.orientation(gtk::Orientation::Vertical)
@@ -243,7 +133,8 @@ 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",
@@ -255,28 +146,28 @@ impl ObjectImpl for HexGridWindowPrivate {
.unwrap();
let image = reader.decode().unwrap();
- let badlands = Tile::new(&image, Terrain::Badlands);
- let deep_water = Tile::new(&image, Terrain::DeepWater);
- let desert = Tile::new(&image, Terrain::Desert);
- let grasslands = Tile::new(&image, Terrain::Grasslands);
- let mountain = Tile::new(&image, Terrain::Mountain);
- let shallow_water = Tile::new(&image, Terrain::ShallowWater);
- let swamp = Tile::new(&image, Terrain::Swamp);
+ let badlands = tile::Tile::new(&image, tile::Terrain::Badlands);
+ let deep_water = tile::Tile::new(&image, tile::Terrain::DeepWater);
+ let desert = tile::Tile::new(&image, tile::Terrain::Desert);
+ let grasslands = tile::Tile::new(&image, tile::Terrain::Grasslands);
+ let mountain = tile::Tile::new(&image, tile::Terrain::Mountain);
+ let shallow_water = tile::Tile::new(&image, tile::Terrain::ShallowWater);
+ let swamp = tile::Tile::new(&image, tile::Terrain::Swamp);
- let badlands_image = gtk::Image::from_pixbuf(Some(&badlands.image));
- self.palette.append(&badlands_image);
self.palette
- .append(>k::Image::from_pixbuf(Some(&deep_water.image)));
+ .append(&palette_entry::PaletteEntry::new(&badlands));
self.palette
- .append(>k::Image::from_pixbuf(Some(&desert.image)));
+ .append(&palette_entry::PaletteEntry::new(&deep_water));
self.palette
- .append(>k::Image::from_pixbuf(Some(&grasslands.image)));
+ .append(&palette_entry::PaletteEntry::new(&desert));
self.palette
- .append(>k::Image::from_pixbuf(Some(&mountain.image)));
+ .append(&palette_entry::PaletteEntry::new(&grasslands));
self.palette
- .append(>k::Image::from_pixbuf(Some(&shallow_water.image)));
+ .append(&palette_entry::PaletteEntry::new(&mountain));
self.palette
- .append(>k::Image::from_pixbuf(Some(&swamp.image)));
+ .append(&palette_entry::PaletteEntry::new(&shallow_water));
+ self.palette
+ .append(&palette_entry::PaletteEntry::new(&swamp));
let motion_controller = gtk::EventControllerMotion::new();
{
@@ -324,13 +215,13 @@ impl ObjectImpl for HexGridWindowPrivate {
let translate_y = center_y - (3. as f64).sqrt() * HEX_RADIUS / 2.;
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,
+ tile::Terrain::Mountain => &mountain,
+ tile::Terrain::Grasslands => &grasslands,
+ tile::Terrain::ShallowWater => &shallow_water,
+ tile::Terrain::DeepWater => &deep_water,
+ tile::Terrain::Badlands => &badlands,
+ tile::Terrain::Desert => &desert,
+ tile::Terrain::Swamp => &swamp,
_ => panic!("unhandled terrain type"),
};
tile.render_on_context(context, translate_x, translate_y);
@@ -361,7 +252,7 @@ impl HexGridWindow {
fn draw_hexagon(context: &Context, center_x: f64, center_y: f64, radius: f64) {
let ul_x = center_x - radius;
let ul_y = center_y - (3. as f64).sqrt() * radius / 2.;
- let points: Vec<(f64, f64)> = hexagon(radius * 2., (3. as f64).sqrt() * radius);
+ let points: Vec<(f64, f64)> = utilities::hexagon(radius * 2., (3. as f64).sqrt() * radius);
context.new_path();
context.move_to(ul_x + points[0].0, ul_y + points[0].1);
context.line_to(ul_x + points[1].0, ul_y + points[1].1);
@@ -372,51 +263,6 @@ fn draw_hexagon(context: &Context, center_x: f64, center_y: f64, radius: f64) {
context.close_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(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")
-}
-
-fn hexagon(width: f64, height: f64) -> Vec<(f64, f64)> {
- let center_x = width / 2.;
- let center_y = height / 2.;
- let radius = width / 2.;
-
- vec![
- (center_x + radius, center_y),
- (
- center_x + radius / 2.,
- center_y + (3. as f64).sqrt() * radius / 2.,
- ),
- (
- center_x - radius / 2.,
- center_y + (3. as f64).sqrt() * radius / 2.,
- ),
- (center_x - radius, center_y),
- (
- center_x - radius / 2.,
- center_y - (3. as f64).sqrt() * radius / 2.,
- ),
- (
- center_x + radius / 2.,
- center_y - (3. as f64).sqrt() * radius / 2.,
- ),
- ]
-}
-
fn axial_round(q_f64: f64, r_f64: f64) -> (i32, i32) {
let s_f64 = -q_f64 - r_f64;
let mut q = q_f64.round();
@@ -434,15 +280,3 @@ fn axial_round(q_f64: f64, r_f64: f64) -> (i32, i32) {
}
unsafe { (q.to_int_unchecked::(), r.to_int_unchecked::()) }
}
-
-fn pixbuf_from_image_tile(image: image::DynamicImage) -> Pixbuf {
- 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,
- )
-}
diff --git a/hex-grid/src/palette_entry.rs b/hex-grid/src/palette_entry.rs
new file mode 100644
index 0000000..6942202
--- /dev/null
+++ b/hex-grid/src/palette_entry.rs
@@ -0,0 +1,52 @@
+use crate::tile;
+use glib::Object;
+use gtk::{prelude::*, subclass::prelude::*};
+use std::{cell::RefCell, rc::Rc};
+
+pub struct PaletteEntryPrivate {
+ terrain: Rc>,
+}
+
+#[glib::object_subclass]
+impl ObjectSubclass for PaletteEntryPrivate {
+ const NAME: &'static str = "PaletteEntry";
+ type Type = PaletteEntry;
+ type ParentType = gtk::ListBoxRow;
+
+ fn new() -> Self {
+ Self {
+ terrain: Rc::new(RefCell::new(tile::Terrain::Empty)),
+ }
+ }
+}
+
+impl ObjectImpl for PaletteEntryPrivate {}
+impl WidgetImpl for PaletteEntryPrivate {}
+impl ListBoxRowImpl for PaletteEntryPrivate {
+ fn activate(&self) {
+ println!("row activated: {:?}", self.terrain);
+ }
+}
+
+glib::wrapper! {
+ pub struct PaletteEntry(ObjectSubclass) @extends gtk::ListBoxRow, gtk::Widget;
+}
+
+impl PaletteEntry {
+ pub fn new(tile: &tile::Tile) -> Self {
+ let row: Self = Object::builder().build();
+ *row.imp().terrain.borrow_mut() = tile.terrain.clone();
+ let layout = gtk::Box::builder().spacing(8).build();
+
+ let image = gtk::Image::from_pixbuf(Some(&tile.image));
+ image.set_width_request(100);
+ image.set_height_request(88);
+
+ layout.append(&image);
+ layout.append(>k::Label::new(Some(&String::from(&tile.terrain))));
+
+ row.set_child(Some(&layout));
+
+ row
+ }
+}
diff --git a/hex-grid/src/tile.rs b/hex-grid/src/tile.rs
new file mode 100644
index 0000000..ced3870
--- /dev/null
+++ b/hex-grid/src/tile.rs
@@ -0,0 +1,108 @@
+use crate::utilities;
+use cairo::Context;
+use gtk::{gdk_pixbuf::Pixbuf, prelude::*};
+use image::DynamicImage;
+
+#[derive(Clone, Debug)]
+pub enum Terrain {
+ Badlands,
+ DeepWater,
+ Desert,
+ Empty,
+ Grasslands,
+ Mountain,
+ ShallowWater,
+ Swamp,
+}
+
+impl Default for Terrain {
+ fn default() -> Self {
+ Self::Empty
+ }
+}
+
+impl From<&str> for Terrain {
+ fn from(s: &str) -> Self {
+ match s {
+ "m" => Self::Mountain,
+ "g" => Self::Grasslands,
+ "sw" => Self::ShallowWater,
+ "dw" => Self::DeepWater,
+ "b" => Self::Badlands,
+ "d" => Self::Desert,
+ "s" => Self::Swamp,
+ _ => Self::Empty,
+ }
+ }
+}
+
+impl From for Terrain {
+ fn from(s: String) -> Self {
+ Self::from(s.as_ref())
+ }
+}
+
+impl From<&Terrain> for String {
+ fn from(t: &Terrain) -> Self {
+ match t {
+ Terrain::Badlands => "Badlands",
+ Terrain::DeepWater => "Deep Water",
+ Terrain::Desert => "Desert",
+ Terrain::Empty => "Empty",
+ Terrain::Grasslands => "Grasslands",
+ Terrain::Mountain => "Mountain",
+ Terrain::ShallowWater => "Shallow Water",
+ Terrain::Swamp => "Swamp",
+ }
+ .to_owned()
+ }
+}
+
+pub struct Tile {
+ pub terrain: Terrain,
+ pub image: Pixbuf,
+}
+
+impl Tile {
+ pub 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 }
+ }
+
+ pub fn render_on_context(&self, context: &Context, translate_x: f64, translate_y: f64) {
+ context.save().unwrap();
+ context.append_path(&utilities::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 pixbuf_from_image_tile(image: image::DynamicImage) -> Pixbuf {
+ 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,
+ )
+}
diff --git a/hex-grid/src/utilities.rs b/hex-grid/src/utilities.rs
new file mode 100644
index 0000000..613dce6
--- /dev/null
+++ b/hex-grid/src/utilities.rs
@@ -0,0 +1,46 @@
+use cairo::{Context, Path};
+
+pub 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(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")
+}
+
+pub fn hexagon(width: f64, height: f64) -> Vec<(f64, f64)> {
+ let center_x = width / 2.;
+ let center_y = height / 2.;
+ let radius = width / 2.;
+
+ vec![
+ (center_x + radius, center_y),
+ (
+ center_x + radius / 2.,
+ center_y + (3. as f64).sqrt() * radius / 2.,
+ ),
+ (
+ center_x - radius / 2.,
+ center_y + (3. as f64).sqrt() * radius / 2.,
+ ),
+ (center_x - radius, center_y),
+ (
+ center_x - radius / 2.,
+ center_y - (3. as f64).sqrt() * radius / 2.,
+ ),
+ (
+ center_x + radius / 2.,
+ center_y - (3. as f64).sqrt() * radius / 2.,
+ ),
+ ]
+}