# HG changeset patch # User unC0Rr # Date 1676207942 -3600 # Node ID 9bd828451d7705d1f1a16d5cfbe5e4a30903dc06 # Parent 60b5639cc3a5d4980bbc7eba99eedc037a9659c3 Fix several issues with transformations, more work on getting generated image diff -r 60b5639cc3a5 -r 9bd828451d77 rust/integral-geometry/src/lib.rs --- a/rust/integral-geometry/src/lib.rs Fri Feb 03 15:59:18 2023 +0100 +++ b/rust/integral-geometry/src/lib.rs Sun Feb 12 14:19:02 2023 +0100 @@ -164,6 +164,11 @@ } #[inline] + pub fn is_square(&self) -> bool { + self.width == self.height + } + + #[inline] pub const fn contains(&self, other: Self) -> bool { self.width >= other.width && self.height >= other.height } diff -r 60b5639cc3a5 -r 9bd828451d77 rust/landgen/src/wavefront_collapse/generator.rs --- a/rust/landgen/src/wavefront_collapse/generator.rs Fri Feb 03 15:59:18 2023 +0100 +++ b/rust/landgen/src/wavefront_collapse/generator.rs Sun Feb 12 14:19:02 2023 +0100 @@ -7,11 +7,13 @@ use std::fs::File; use std::io::BufReader; -pub struct WavefrontCollapseLandGenerator {} +pub struct WavefrontCollapseLandGenerator { + pub size: Size, +} impl WavefrontCollapseLandGenerator { - pub fn new() -> Self { - Self {} + pub fn new(size: &Size) -> Self { + Self { size: *size } } pub fn load_template( @@ -49,16 +51,18 @@ } } - let top_edge = Edge::new("edge".to_owned(), false); - let right_edge = Edge::new("edge".to_owned(), false); - let bottom_edge = Edge::new("edge".to_owned(), false); - let left_edge = Edge::new("edge".to_owned(), false); + let top_edge = Edge::new("ef".to_owned(), false); + let right_edge = top_edge.reversed(); + let bottom_edge = Edge::new("ee".to_owned(), true); + let left_edge = bottom_edge.clone(); let tile = TileImage::::new(tiles_image, top_edge, right_edge, bottom_edge, left_edge); result.push(tile.clone()); - result.push(tile.mirrored()); + result.push(tile.rotated90()); + result.push(tile.rotated180()); + result.push(tile.rotated270()); result } @@ -82,22 +86,22 @@ let mut top = default_connection.clone(); for p in 0..i { - if tiles[p].left_edge() == tile.right_edge() { + if tiles[p].left_edge().is_compatible(tile.right_edge()) { rules[p].left.insert(Tile::Numbered(i)); right.insert(Tile::Numbered(p)); } - if tiles[p].right_edge() == tile.left_edge() { + if tiles[p].right_edge().is_compatible(tile.left_edge()) { rules[p].right.insert(Tile::Numbered(i)); left.insert(Tile::Numbered(p)); } - if tiles[p].top_edge() == tile.bottom_edge() { + if tiles[p].top_edge().is_compatible(tile.bottom_edge()) { rules[p].top.insert(Tile::Numbered(i)); bottom.insert(Tile::Numbered(p)); } - if tiles[p].bottom_edge() == tile.top_edge() { + if tiles[p].bottom_edge().is_compatible(tile.top_edge()) { rules[p].bottom.insert(Tile::Numbered(i)); top.insert(Tile::Numbered(p)); } @@ -115,7 +119,18 @@ let mut wfc = WavefrontCollapse::default(); wfc.set_rules(rules); - wfc.generate_map(&Size::new(40, 20), |_| {}, random_numbers); + let wfc_size = if let Some(first_tile) = tiles.first() { + let tile_size = first_tile.size(); + + Size::new( + self.size.width / tile_size.width, + self.size.height / tile_size.height, + ) + } else { + Size::new(1, 1) + }; + + wfc.generate_map(&wfc_size, |_| {}, random_numbers); let grid = wfc.grid(); @@ -127,7 +142,30 @@ println!(); } - todo!("build result") + let mut result = land2d::Land2D::new(&self.size, parameters.zero); + + for row in 0..wfc_size.height { + for column in 0..wfc_size.width { + if let Some(Tile::Numbered(tile_index)) = wfc.grid().get(row, column) { + let tile = &tiles[*tile_index]; + + for tile_row in 0..tile.size().height { + for tile_column in 0..tile.size().width { + result.map( + (row * tile.size().height + tile_row) as i32, + (column * tile.size().width + tile_column) as i32, + |p| { + *p = + *tile.get(tile_row, tile_column).unwrap_or(¶meters.zero) + }, + ); + } + } + } + } + } + + result } } @@ -136,12 +174,36 @@ use super::WavefrontCollapseLandGenerator; use crate::{LandGenerationParameters, LandGenerator}; use integral_geometry::Size; + use std::fs::File; + use std::io::BufWriter; + use std::path::Path; use vec2d::Vec2D; #[test] fn test_generation() { - let wfc_gen = WavefrontCollapseLandGenerator::new(); - let landgen_params = LandGenerationParameters::new(0u8, 255u8, 0, true, true); - wfc_gen.generate_land(&landgen_params, &mut std::iter::repeat(1u32)); + let wfc_gen = WavefrontCollapseLandGenerator::new(&Size::new(2048, 1024)); + let landgen_params = LandGenerationParameters::new(0u32, 0xff000000u32, 0, true, true); + let land = wfc_gen.generate_land(&landgen_params, &mut std::iter::repeat(0u32)); + + let path = Path::new(r"output.png"); + let file = File::create(path).unwrap(); + let ref mut w = BufWriter::new(file); + + let mut encoder = png::Encoder::new(w, land.width() as u32, land.height() as u32); // Width is 2 pixels and height is 1. + encoder.set_color(png::ColorType::Rgba); + encoder.set_depth(png::BitDepth::Eight); + encoder.set_source_gamma(png::ScaledFloat::from_scaled(45455)); // 1.0 / 2.2, scaled by 100000 + encoder.set_source_gamma(png::ScaledFloat::new(1.0 / 2.2)); // 1.0 / 2.2, unscaled, but rounded + let source_chromaticities = png::SourceChromaticities::new( + // Using unscaled instantiation here + (0.31270, 0.32900), + (0.64000, 0.33000), + (0.30000, 0.60000), + (0.15000, 0.06000), + ); + encoder.set_source_chromaticities(source_chromaticities); + let mut writer = encoder.write_header().unwrap(); + + writer.write_image_data(land.raw_pixel_bytes()).unwrap(); // Save } } diff -r 60b5639cc3a5 -r 9bd828451d77 rust/landgen/src/wavefront_collapse/tile_image.rs --- a/rust/landgen/src/wavefront_collapse/tile_image.rs Fri Feb 03 15:59:18 2023 +0100 +++ b/rust/landgen/src/wavefront_collapse/tile_image.rs Sun Feb 12 14:19:02 2023 +0100 @@ -1,4 +1,5 @@ -use super::transform::RotationTransform; +use super::transform::Transform; +use integral_geometry::Size; use std::rc::Rc; use vec2d::Vec2D; @@ -25,12 +26,16 @@ reverse: !self.symmetrical && !self.reverse, } } + + pub fn is_compatible(&self, other: &Self) -> bool { + self.id == other.id && (self.reverse != other.reverse || self.symmetrical) + } } #[derive(Clone)] pub struct TileImage { image: Rc>, - transform: RotationTransform, + transform: Transform, top: Edge, right: Edge, bottom: Edge, @@ -47,7 +52,7 @@ ) -> Self { Self { image: Rc::new(image), - transform: RotationTransform::default(), + transform: Transform::default(), top, right, bottom, @@ -91,7 +96,7 @@ pub fn rotated180(&self) -> Self { Self { image: self.image.clone(), - transform: self.transform.rotate90(), + transform: self.transform.rotate180(), top: self.bottom.clone(), right: self.left.clone(), bottom: self.top.clone(), @@ -102,7 +107,7 @@ pub fn rotated270(&self) -> Self { Self { image: self.image.clone(), - transform: self.transform.rotate90(), + transform: self.transform.rotate270(), top: self.left.clone(), right: self.top.clone(), bottom: self.right.clone(), @@ -125,6 +130,48 @@ pub fn top_edge(&self) -> &Edge { &self.top } + + pub fn size(&self) -> Size { + match self.transform { + Transform::Rotate0(_) => self.image.size(), + Transform::Rotate90(_) => Size::new(self.image.size().height, self.image.size().width), + } + } + + pub fn get(&self, row: usize, column: usize) -> Option<&T> { + match self.transform { + Transform::Rotate0(_) => { + let image_row = if self.transform.is_flipped() { + self.image.height().wrapping_sub(1).wrapping_sub(row) + } else { + row + }; + + let image_column = if self.transform.is_mirrored() { + self.image.width().wrapping_sub(1).wrapping_sub(column) + } else { + column + }; + + self.image.get(image_row, image_column) + }, + Transform::Rotate90(_) => { + let image_row = if self.transform.is_flipped() { + column + } else { + self.image.height().wrapping_sub(1).wrapping_sub(column) + }; + + let image_column = if self.transform.is_mirrored() { + self.image.width().wrapping_sub(1).wrapping_sub(row) + } else { + row + }; + + self.image.get(image_row, image_column) + }, + } + } } #[cfg(test)] diff -r 60b5639cc3a5 -r 9bd828451d77 rust/landgen/src/wavefront_collapse/transform.rs --- a/rust/landgen/src/wavefront_collapse/transform.rs Fri Feb 03 15:59:18 2023 +0100 +++ b/rust/landgen/src/wavefront_collapse/transform.rs Sun Feb 12 14:19:02 2023 +0100 @@ -7,16 +7,14 @@ } #[derive(Debug, PartialEq, Clone, Copy)] -pub enum RotationTransform { +pub enum Transform { Rotate0(SymmetryTransform), Rotate90(SymmetryTransform), - Rotate180(SymmetryTransform), - Rotate270(SymmetryTransform), } -impl Default for RotationTransform { +impl Default for Transform { fn default() -> Self { - RotationTransform::Rotate0(SymmetryTransform::Id) + Transform::Rotate0(SymmetryTransform::Id) } } @@ -40,78 +38,89 @@ FlipMirror => Mirror, } } + + pub fn is_mirrored(&self) -> bool { + match self { + Id => false, + Flip => false, + Mirror => true, + FlipMirror => true, + } + } + + pub fn is_flipped(&self) -> bool { + match self { + Id => false, + Flip => true, + Mirror => false, + FlipMirror => true, + } + } } -impl RotationTransform { +impl Transform { pub fn new() -> Self { Self::default() } - pub fn mirror(self) -> RotationTransform { + pub fn mirror(self) -> Transform { match self { - RotationTransform::Rotate0(s) => RotationTransform::Rotate0(s.mirror()), - RotationTransform::Rotate90(s) => RotationTransform::Rotate270(s.mirror()).simplified(), - RotationTransform::Rotate180(s) => { - RotationTransform::Rotate180(s.mirror()).simplified() - } - RotationTransform::Rotate270(s) => RotationTransform::Rotate90(s.mirror()), + Transform::Rotate0(s) => Transform::Rotate0(s.mirror()), + Transform::Rotate90(s) => Transform::Rotate90(s.flip()), } } - pub fn flip(self) -> RotationTransform { + pub fn flip(self) -> Transform { match self { - RotationTransform::Rotate0(s) => RotationTransform::Rotate0(s.flip()), - RotationTransform::Rotate90(s) => RotationTransform::Rotate90(s.flip()), - RotationTransform::Rotate180(s) => RotationTransform::Rotate180(s.flip()).simplified(), - RotationTransform::Rotate270(s) => RotationTransform::Rotate270(s.flip()).simplified(), + Transform::Rotate0(s) => Transform::Rotate0(s.flip()), + Transform::Rotate90(s) => Transform::Rotate90(s.mirror()), + } + } + + pub fn rotate90(self) -> Transform { + match self { + Transform::Rotate0(s) => Transform::Rotate90(s), + Transform::Rotate90(s) => Transform::Rotate0(s.flip().mirror()), } } - pub fn rotate90(self) -> RotationTransform { + pub fn rotate180(self) -> Transform { match self { - RotationTransform::Rotate0(s) => RotationTransform::Rotate90(s), - RotationTransform::Rotate90(s) => RotationTransform::Rotate180(s).simplified(), - RotationTransform::Rotate180(s) => RotationTransform::Rotate270(s).simplified(), - RotationTransform::Rotate270(s) => RotationTransform::Rotate0(s), + Transform::Rotate0(s) => Transform::Rotate0(s.flip().mirror()), + Transform::Rotate90(s) => Transform::Rotate90(s.flip().mirror()), } } - pub fn rotate180(self) -> RotationTransform { + pub fn rotate270(self) -> Transform { match self { - RotationTransform::Rotate0(s) => RotationTransform::Rotate180(s).simplified(), - RotationTransform::Rotate90(s) => RotationTransform::Rotate270(s).simplified(), - RotationTransform::Rotate180(s) => RotationTransform::Rotate0(s), - RotationTransform::Rotate270(s) => RotationTransform::Rotate90(s), + Transform::Rotate0(s) => Transform::Rotate90(s.flip().mirror()), + Transform::Rotate90(s) => Transform::Rotate0(s), } } - pub fn rotate270(self) -> RotationTransform { + pub fn is_mirrored(&self) -> bool { match self { - RotationTransform::Rotate0(s) => RotationTransform::Rotate270(s).simplified(), - RotationTransform::Rotate90(s) => RotationTransform::Rotate0(s), - RotationTransform::Rotate180(s) => RotationTransform::Rotate90(s), - RotationTransform::Rotate270(s) => RotationTransform::Rotate180(s).simplified(), + Transform::Rotate0(s) => s.is_mirrored(), + Transform::Rotate90(s) => s.is_mirrored(), } } - fn simplified(self) -> Self { + pub fn is_flipped(&self) -> bool { match self { - RotationTransform::Rotate0(s) => RotationTransform::Rotate0(s), - RotationTransform::Rotate90(s) => RotationTransform::Rotate90(s), - RotationTransform::Rotate180(s) => RotationTransform::Rotate0(s.flip().mirror()), - RotationTransform::Rotate270(s) => RotationTransform::Rotate90(s.flip().mirror()), + Transform::Rotate0(s) => s.is_flipped(), + Transform::Rotate90(s) => s.is_flipped(), } } } #[cfg(test)] mod tests { - use super::{RotationTransform::*, SymmetryTransform::*, *}; + use super::{SymmetryTransform::*, Transform::*, *}; // I totally wrote all of this myself and didn't use ChatGPT #[test] fn test_default() { - let rt = RotationTransform::new(); + let rt = Transform::new(); assert_eq!(rt, Rotate0(Id)); } @@ -124,7 +133,7 @@ #[test] fn test_flip() { - let rt = Rotate180(Mirror); + let rt = Transform::new().rotate180().mirror(); let flipped = rt.flip(); assert_eq!(flipped, Rotate0(Id)); } @@ -145,69 +154,54 @@ #[test] fn test_rotate270() { - let rt = Rotate180(Flip); + let rt = Transform::new().rotate180().flip(); let rotated = rt.rotate270(); assert_eq!(rotated, Rotate90(Flip)); } #[test] - fn test_simplified() { - let rt = Rotate180(Id); - let simplified = rt.simplified(); - assert_eq!(simplified, Rotate0(FlipMirror)); + fn test_rotate180_2() { + let rt = Transform::new().rotate180(); + assert_eq!(rt, Rotate0(FlipMirror)); } #[test] fn test_rotation_chain() { assert_eq!( - RotationTransform::default(), - RotationTransform::default() + Transform::default(), + Transform::default() .rotate90() .rotate90() .rotate90() .rotate90() ); assert_eq!( - RotationTransform::default().rotate90(), - RotationTransform::default() - .rotate180() - .rotate90() - .rotate180() + Transform::default().rotate90(), + Transform::default().rotate180().rotate90().rotate180() ); assert_eq!( - RotationTransform::default().rotate180(), - RotationTransform::default() - .rotate180() - .rotate270() - .rotate90() + Transform::default().rotate180(), + Transform::default().rotate180().rotate270().rotate90() ); } #[test] fn test_combinations_chain() { assert_eq!( - RotationTransform::default(), - RotationTransform::default() - .flip() - .rotate180() - .flip() - .rotate180() + Transform::default(), + Transform::default().flip().rotate180().flip().rotate180() ); assert_eq!( - RotationTransform::default(), - RotationTransform::default() + Transform::default(), + Transform::default() .mirror() .rotate180() .mirror() .rotate180() ); assert_eq!( - RotationTransform::default(), - RotationTransform::default() - .rotate90() - .flip() - .rotate90() - .mirror() + Transform::default(), + Transform::default().rotate90().flip().rotate90().mirror().rotate180() ); } } diff -r 60b5639cc3a5 -r 9bd828451d77 rust/landgen/src/wavefront_collapse/wavefront_collapse.rs --- a/rust/landgen/src/wavefront_collapse/wavefront_collapse.rs Fri Feb 03 15:59:18 2023 +0100 +++ b/rust/landgen/src/wavefront_collapse/wavefront_collapse.rs Sun Feb 12 14:19:02 2023 +0100 @@ -148,7 +148,7 @@ ); println!("Rules are: {:?}", self.rules); - todo!("no collapse possible - what to do?") + //todo!("no collapse possible - what to do?") } } }