Fix several issues with transformations, more work on getting generated image transitional_engine
authorunC0Rr
Sun, 12 Feb 2023 14:19:02 +0100
branchtransitional_engine
changeset 15948 9bd828451d77
parent 15947 60b5639cc3a5
child 15949 659c92124c26
Fix several issues with transformations, more work on getting generated image
rust/integral-geometry/src/lib.rs
rust/landgen/src/wavefront_collapse/generator.rs
rust/landgen/src/wavefront_collapse/tile_image.rs
rust/landgen/src/wavefront_collapse/transform.rs
rust/landgen/src/wavefront_collapse/wavefront_collapse.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
     }
--- 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<T: Copy + PartialEq + Default>(
@@ -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::<T, String>::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(&parameters.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
     }
 }
--- 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<T, I: PartialEq + Clone> {
     image: Rc<Vec2D<T>>,
-    transform: RotationTransform,
+    transform: Transform,
     top: Edge<I>,
     right: Edge<I>,
     bottom: Edge<I>,
@@ -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<I> {
         &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)]
--- 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()
         );
     }
 }
--- 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?")
                     }
                 }
             }