Fix generation in corners by extending outline edge definitions
authorunC0Rr
Tue, 21 Jan 2025 22:10:55 +0100
changeset 16088 2acea266d297
parent 16087 de01be16df95
child 16089 3f73daa3f212
Fix generation in corners by extending outline edge definitions
rust/landgen/Cargo.toml
rust/landgen/src/wavefront_collapse/generator.rs
rust/landgen/src/wavefront_collapse/wavefront_collapse.rs
rust/mapgen/Cargo.toml
rust/mapgen/src/template/wavefront_collapse.rs
share/hedgewars/Data/wfc_templates.yaml
--- a/rust/landgen/Cargo.toml	Sat Jan 18 16:57:26 2025 +0100
+++ b/rust/landgen/Cargo.toml	Tue Jan 21 22:10:55 2025 +0100
@@ -8,7 +8,6 @@
 integral-geometry = { path = "../integral-geometry" }
 land2d = { path = "../land2d" }
 vec2d = { path = "../vec2d" }
-itertools = "0.14"
 png = "0.17"
 rand = "0.8"
 
--- a/rust/landgen/src/wavefront_collapse/generator.rs	Sat Jan 18 16:57:26 2025 +0100
+++ b/rust/landgen/src/wavefront_collapse/generator.rs	Tue Jan 21 22:10:55 2025 +0100
@@ -9,14 +9,14 @@
 use std::io::{BufReader, Result};
 use std::path::{Path, PathBuf};
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct EdgeDescription {
     pub name: String,
     pub reversed: Option<bool>,
     pub symmetrical: Option<bool>,
 }
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct EdgesDescription {
     pub top: EdgeDescription,
     pub right: EdgeDescription,
@@ -24,7 +24,7 @@
     pub left: EdgeDescription,
 }
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct TileDescription {
     pub name: String,
     pub edges: EdgesDescription,
@@ -36,19 +36,26 @@
     pub can_rotate270: Option<bool>,
 }
 
-#[derive(Clone)]
-pub struct NonStrictEdgesDescription {
-    pub top: Option<EdgeDescription>,
-    pub right: Option<EdgeDescription>,
-    pub bottom: Option<EdgeDescription>,
-    pub left: Option<EdgeDescription>,
+#[derive(Debug, Clone)]
+pub struct ComplexEdgeDescription {
+    pub begin: Option<EdgeDescription>,
+    pub fill: Option<EdgeDescription>,
+    pub end: Option<EdgeDescription>,
 }
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
+pub struct NonStrictComplexEdgesDescription {
+    pub top: Option<ComplexEdgeDescription>,
+    pub right: Option<ComplexEdgeDescription>,
+    pub bottom: Option<ComplexEdgeDescription>,
+    pub left: Option<ComplexEdgeDescription>,
+}
+
+#[derive(Debug, Clone)]
 pub struct TemplateDescription {
     pub size: Size,
     pub tiles: Vec<TileDescription>,
-    pub edges: NonStrictEdgesDescription,
+    pub edges: NonStrictComplexEdgesDescription,
     pub wrap: bool,
 }
 
@@ -79,7 +86,7 @@
                 .as_path(),
         )?;
         let decoder = Decoder::new(BufReader::new(file));
-        let mut reader = decoder.read_info().unwrap();
+        let mut reader = decoder.read_info()?;
 
         let info = reader.info();
         let mut tiles_image = vec2d::Vec2D::new(
@@ -88,7 +95,7 @@
         );
 
         let mut buf = vec![0; reader.output_buffer_size()];
-        let info = reader.next_frame(&mut buf).unwrap();
+        let info = reader.next_frame(&mut buf)?;
         let bytes = &buf[..info.buffer_size()];
 
         let mut tiles_image_pixels = tiles_image.as_mut_slice().iter_mut();
@@ -183,14 +190,14 @@
         probability_distribution_factor: i32,
     ) -> Vec<CollapseRule> {
         let [grid_top_edge, grid_right_edge, grid_bottom_edge, grid_left_edge]: [Option<
-            Edge<String>,
+            [Option<Edge<String>>; 3],
         >; 4] = [
             self.template.edges.top.as_ref(),
             self.template.edges.right.as_ref(),
             self.template.edges.bottom.as_ref(),
             self.template.edges.left.as_ref(),
         ]
-        .map(|opt| opt.map(|d| d.into()));
+        .map(|opt| opt.map(|d| [&d.begin, &d.fill, &d.end].map(|e| e.as_ref().map(Into::into))));
 
         let mut rules = Vec::<CollapseRule>::new();
 
@@ -201,34 +208,32 @@
             let mut left = default_connection.clone();
             let mut top = default_connection.clone();
 
+            let iteration = [
+                (&grid_top_edge, tile.top_edge(), &mut top),
+                (&grid_right_edge, tile.right_edge(), &mut right),
+                (&grid_bottom_edge, tile.bottom_edge(), &mut bottom),
+                (&grid_left_edge, tile.left_edge(), &mut left),
+            ];
+
             // compatibility with grid edges
-            if grid_top_edge
-                .as_ref()
-                .map(|e| e.is_compatible(tile.top_edge()))
-                .unwrap_or(true)
-            {
-                top.insert(Tile::Outside);
-            }
-            if grid_right_edge
-                .as_ref()
-                .map(|e| e.is_compatible(tile.right_edge()))
-                .unwrap_or(true)
-            {
-                right.insert(Tile::Outside);
-            }
-            if grid_bottom_edge
-                .as_ref()
-                .map(|e| e.is_compatible(tile.bottom_edge()))
-                .unwrap_or(true)
-            {
-                bottom.insert(Tile::Outside);
-            }
-            if grid_left_edge
-                .as_ref()
-                .map(|e| e.is_compatible(tile.left_edge()))
-                .unwrap_or(true)
-            {
-                left.insert(Tile::Outside);
+            for (edge, tile_edge, set) in iteration {
+                for (is_compatible, tile) in edge
+                    .as_ref()
+                    .map(|e| {
+                        e.clone().map(|ed| {
+                            ed.as_ref()
+                                .map(|e| e.is_compatible(tile_edge))
+                                .unwrap_or(true)
+                        })
+                    })
+                    .unwrap_or([true, true, true])
+                    .into_iter()
+                    .zip([Tile::OutsideBegin, Tile::OutsideFill, Tile::OutsideEnd].into_iter())
+                {
+                    if is_compatible {
+                        set.insert(tile);
+                    }
+                }
             }
 
             // compatibility with itself
@@ -244,21 +249,25 @@
 
             // compatibility with previously defined tiles
             for p in 0..i {
+                // Check left edge
                 if tiles[p].left_edge().is_compatible(tile.right_edge()) {
                     rules[p].left.insert(Tile::Numbered(i));
                     right.insert(Tile::Numbered(p));
                 }
 
+                // Check right edge
                 if tiles[p].right_edge().is_compatible(tile.left_edge()) {
                     rules[p].right.insert(Tile::Numbered(i));
                     left.insert(Tile::Numbered(p));
                 }
 
+                // Check top edge
                 if tiles[p].top_edge().is_compatible(tile.bottom_edge()) {
                     rules[p].top.insert(Tile::Numbered(i));
                     bottom.insert(Tile::Numbered(p));
                 }
 
+                // Check bottom edge
                 if tiles[p].bottom_edge().is_compatible(tile.top_edge()) {
                     rules[p].bottom.insert(Tile::Numbered(i));
                     top.insert(Tile::Numbered(p));
--- a/rust/landgen/src/wavefront_collapse/wavefront_collapse.rs	Sat Jan 18 16:57:26 2025 +0100
+++ b/rust/landgen/src/wavefront_collapse/wavefront_collapse.rs	Tue Jan 21 22:10:55 2025 +0100
@@ -8,16 +8,12 @@
 #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
 pub enum Tile {
     Empty,
-    Outside,
+    OutsideBegin,
+    OutsideFill,
+    OutsideEnd,
     Numbered(usize),
 }
 
-impl Default for Tile {
-    fn default() -> Self {
-        Tile::Outside
-    }
-}
-
 #[derive(Debug)]
 pub struct CollapseRule {
     pub weight: u32,
@@ -83,11 +79,38 @@
             x
         };
 
-        self.grid.get(y, x).copied().unwrap_or_default()
+        self.grid.get(y, x).copied().unwrap_or_else(|| {
+            let x_out = x >= self.grid.width();
+
+            if x_out {
+                let y_at_begin = y == 0;
+                let y_at_end = y.wrapping_add(1) == self.grid.height();
+                if y_at_begin {
+                    Tile::OutsideBegin
+                } else if y_at_end {
+                    Tile::OutsideEnd
+                } else {
+                    Tile::OutsideFill
+                }
+            } else {
+                // if not x, then it is y
+
+                let x_at_begin = x == 0;
+                let x_at_end = x.wrapping_add(1) == self.grid.width();
+
+                if x_at_begin {
+                    Tile::OutsideBegin
+                } else if x_at_end {
+                    Tile::OutsideEnd
+                } else {
+                    Tile::OutsideFill
+                }
+            }
+        })
     }
 
     fn collapse_step(&mut self, random_numbers: &mut impl Rng) -> bool {
-        let mut tiles_to_collapse = (usize::max_value(), Vec::new());
+        let mut tiles_to_collapse = (usize::MAX, Vec::new());
 
         // Iterate through the tiles in the land
         for x in 0..self.grid.width() {
@@ -96,8 +119,8 @@
 
                 if let Tile::Empty = current_tile {
                     // calc entropy
-                    let right_tile = self.get_tile(y, x + 1);
-                    let bottom_tile = self.get_tile(y + 1, x);
+                    let right_tile = self.get_tile(y, x.wrapping_add(1));
+                    let bottom_tile = self.get_tile(y.wrapping_add(1), x);
                     let left_tile = self.get_tile(y, x.wrapping_sub(1));
                     let top_tile = self.get_tile(y.wrapping_sub(1), x);
 
--- a/rust/mapgen/Cargo.toml	Sat Jan 18 16:57:26 2025 +0100
+++ b/rust/mapgen/Cargo.toml	Tue Jan 21 22:10:55 2025 +0100
@@ -2,7 +2,7 @@
 name = "mapgen"
 version = "0.1.0"
 authors = ["Hedgewars Project"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 vec2d = { path = "../vec2d" }
@@ -13,6 +13,6 @@
 
 rand = "0.8"
 serde = "1.0"
-serde_yaml = "0.8"
+serde_yaml = "0.9"
 serde_derive = "1.0"
-png = "0.13"
\ No newline at end of file
+png = "0.13"
--- a/rust/mapgen/src/template/wavefront_collapse.rs	Sat Jan 18 16:57:26 2025 +0100
+++ b/rust/mapgen/src/template/wavefront_collapse.rs	Tue Jan 21 22:10:55 2025 +0100
@@ -5,7 +5,7 @@
 
 use std::collections::hash_map::HashMap;
 
-#[derive(Deserialize)]
+#[derive(Debug, Deserialize)]
 #[serde(remote = "EdgeDescription")]
 pub struct EdgeDesc {
     pub name: String,
@@ -13,7 +13,7 @@
     pub symmetrical: Option<bool>,
 }
 
-#[derive(Deserialize)]
+#[derive(Debug, Deserialize)]
 #[serde(remote = "EdgesDescription")]
 pub struct EdgesDesc {
     #[serde(with = "EdgeDesc")]
@@ -26,7 +26,7 @@
     pub left: EdgeDescription,
 }
 
-#[derive(Deserialize)]
+#[derive(Debug, Deserialize)]
 #[serde(remote = "TileDescription")]
 pub struct TileDesc {
     pub name: String,
@@ -40,20 +40,26 @@
     pub can_rotate270: Option<bool>,
 }
 
-#[derive(Deserialize)]
+#[derive(Debug, Deserialize)]
 pub struct TileDescriptionHelper(#[serde(with = "TileDesc")] TileDescription);
-#[derive(Deserialize)]
+#[derive(Debug, Deserialize)]
 pub struct EdgeDescriptionHelper(#[serde(with = "EdgeDesc")] EdgeDescription);
 
-#[derive(Deserialize)]
-pub struct NonStrictEdgesDesc {
-    pub top: Option<EdgeDescriptionHelper>,
-    pub right: Option<EdgeDescriptionHelper>,
-    pub bottom: Option<EdgeDescriptionHelper>,
-    pub left: Option<EdgeDescriptionHelper>,
+#[derive(Debug, Deserialize)]
+pub struct ComplexEdgeDesc {
+    pub begin: Option<EdgeDescriptionHelper>,
+    pub fill: Option<EdgeDescriptionHelper>,
+    pub end: Option<EdgeDescriptionHelper>,
+}
+#[derive(Debug, Deserialize)]
+pub struct NonStrictComplexEdgesDesc {
+    pub top: Option<ComplexEdgeDesc>,
+    pub right: Option<ComplexEdgeDesc>,
+    pub bottom: Option<ComplexEdgeDesc>,
+    pub left: Option<ComplexEdgeDesc>,
 }
 
-#[derive(Deserialize)]
+#[derive(Debug, Deserialize)]
 pub struct TemplateDesc {
     pub width: usize,
     pub height: usize,
@@ -62,11 +68,11 @@
     pub put_girders: bool,
     pub max_hedgehogs: u8,
     pub wrap: bool,
-    pub edges: Option<NonStrictEdgesDesc>,
+    pub edges: Option<NonStrictComplexEdgesDesc>,
     pub tiles: Vec<TileDescriptionHelper>,
 }
 
-#[derive(Deserialize)]
+#[derive(Debug, Deserialize)]
 pub struct TemplateCollectionDesc {
     pub templates: Vec<TemplateDesc>,
     pub template_types: HashMap<String, Vec<usize>>,
@@ -74,14 +80,14 @@
 
 impl From<&TemplateDesc> for TemplateDescription {
     fn from(desc: &TemplateDesc) -> Self {
-        let [top, right, bottom, left] = if let Some(edges) = &desc.edges {
+        let [top, right, bottom, left]:[Option<ComplexEdgeDescription>; 4] = if let Some(edges) = &desc.edges {
             [
-                edges.top.as_ref(),
-                edges.right.as_ref(),
-                edges.bottom.as_ref(),
-                edges.left.as_ref(),
+                &edges.top,
+                &edges.right,
+                &edges.bottom,
+                &edges.left,
             ]
-            .map(|e| e.map(|EdgeDescriptionHelper(e)| e.clone()))
+            .map(|e| e.as_ref().map(Into::into))
         } else {
             [None, None, None, None]
         };
@@ -94,7 +100,7 @@
                 .map(|TileDescriptionHelper(t)| t.clone())
                 .collect(),
             wrap: desc.wrap,
-            edges: NonStrictEdgesDescription {
+            edges: NonStrictComplexEdgesDescription {
                 top,
                 right,
                 bottom,
@@ -103,3 +109,13 @@
         }
     }
 }
+
+impl From<&ComplexEdgeDesc> for ComplexEdgeDescription {
+    fn from(value: &ComplexEdgeDesc) -> Self {
+        Self {
+            begin: value.begin.as_ref().map(|EdgeDescriptionHelper(e)| e.clone()),
+            fill: value.fill.as_ref().map(|EdgeDescriptionHelper(e)| e.clone()),
+            end: value.end.as_ref().map(|EdgeDescriptionHelper(e)| e.clone()),
+        }
+    }
+}
--- a/share/hedgewars/Data/wfc_templates.yaml	Sat Jan 18 16:57:26 2025 +0100
+++ b/share/hedgewars/Data/wfc_templates.yaml	Tue Jan 21 22:10:55 2025 +0100
@@ -12,9 +12,34 @@
     wrap: true
     edges:
       bottom:
-        name: "ff"
-        symmetrical: true
+        fill:
+          name: "ff"
+          symmetrical: true
+      top:
+        fill:
+          name: "ff"
+          symmetrical: true
     tiles: &template_00_tiles
+      - name: "120_filled.png"
+        edges:
+          top:
+            name: "ff"
+            symmetrical: true
+          right:
+            name: "ff"
+            symmetrical: true
+          bottom:
+            name: "ff"
+            symmetrical: true
+          left:
+            name: "ff"
+            symmetrical: true
+        is_negative: true
+        can_mirror: false
+        can_flip: false
+        can_rotate90: false
+        can_rotate180: false
+        can_rotate270: false
       - name: "120_bar.png"
         edges:
           top:
@@ -33,7 +58,27 @@
         can_flip: false
         can_rotate90: true
         can_rotate180: true
-        can_rotate270: true
+        can_rotate270: true        
+      - name: "120_filled.png"
+        edges:
+          top:
+            name: "ee"
+            symmetrical: true
+          right:
+            name: "ee"
+            symmetrical: true
+          bottom:
+            name: "ee"
+            symmetrical: true
+          left:
+            name: "ee"
+            symmetrical: true
+        is_negative: false
+        can_mirror: false
+        can_flip: false
+        can_rotate90: false
+        can_rotate180: false
+        can_rotate270: false
       - name: "120_corner.png"
         edges:
           top:
@@ -72,46 +117,6 @@
         can_rotate90: true
         can_rotate180: true
         can_rotate270: true
-      - name: "120_filled.png"
-        edges:
-          top:
-            name: "ff"
-            symmetrical: true
-          right:
-            name: "ff"
-            symmetrical: true
-          bottom:
-            name: "ff"
-            symmetrical: true
-          left:
-            name: "ff"
-            symmetrical: true
-        is_negative: true
-        can_mirror: false
-        can_flip: false
-        can_rotate90: false
-        can_rotate180: false
-        can_rotate270: false
-      - name: "120_filled.png"
-        edges:
-          top:
-            name: "ee"
-            symmetrical: true
-          right:
-            name: "ee"
-            symmetrical: true
-          bottom:
-            name: "ee"
-            symmetrical: true
-          left:
-            name: "ee"
-            symmetrical: true
-        is_negative: false
-        can_mirror: false
-        can_flip: false
-        can_rotate90: false
-        can_rotate180: false
-        can_rotate270: false
       - name: "120_two_corners.png"
         edges:
           top:
@@ -141,17 +146,44 @@
     wrap: false
     edges: &open_edges
       top:
-        name: "ee"
-        symmetrical: true
+        begin:
+          name: "ee"
+          symmetrical: true
+        fill:
+          name: "ee"
+          symmetrical: true
+        end:
+          name: "ee"
+          symmetrical: true
       right:
-        name: "ee"
-        symmetrical: true
+        begin:
+          name: "ee"
+          symmetrical: true
+        fill:
+          name: "ee"
+          symmetrical: true
+        end:
+          name: "ee"
+          symmetrical: true
       bottom:
-        name: "ff"
-        symmetrical: true
+        begin:
+          name: "fe"
+          reversed: true
+        fill:
+          name: "ff"
+          symmetrical: true
+        end:
+          name: "fe"
       left:
-        name: "ee"
-        symmetrical: true
+        begin:
+          name: "ee"
+          symmetrical: true
+        fill:
+          name: "ee"
+          symmetrical: true
+        end:
+          name: "ee"
+          symmetrical: true
     tiles: *template_00_tiles
 
   - &template_02