--- 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(¶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
}
}
--- 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?")
}
}
}