|
|
@@ -2,24 +2,24 @@
|
|
|
extern crate serde_derive;
|
|
|
extern crate serde;
|
|
|
extern crate serde_json;
|
|
|
-use std::io::BufWriter;
|
|
|
-use std::io::BufReader;
|
|
|
use std::fs::File;
|
|
|
+use std::io::BufReader;
|
|
|
+use std::io::BufWriter;
|
|
|
// use std::ops::{Add, Sub, Div};
|
|
|
-use std::ops::{Add, Mul};
|
|
|
use std::collections::HashMap;
|
|
|
-#[macro_use] extern crate log;
|
|
|
+use std::ops::{Add, Mul};
|
|
|
+#[macro_use]
|
|
|
+extern crate log;
|
|
|
extern crate simplelog;
|
|
|
+use rand::seq::SliceRandom;
|
|
|
use simplelog::*;
|
|
|
use std::fmt;
|
|
|
-use rand::seq::SliceRandom;
|
|
|
-
|
|
|
|
|
|
#[derive(Clone, Serialize, Deserialize, Copy, Eq, PartialEq, Hash)]
|
|
|
struct V3 {
|
|
|
x: i32,
|
|
|
y: i32,
|
|
|
- z: i32
|
|
|
+ z: i32,
|
|
|
}
|
|
|
|
|
|
impl V3 {
|
|
|
@@ -27,40 +27,37 @@ impl V3 {
|
|
|
V3 { x: x, y: y, z: z }
|
|
|
}
|
|
|
|
|
|
- fn neighbors(self) -> [V3;6] {
|
|
|
+ fn neighbors(self) -> [V3; 6] {
|
|
|
let o = self.offsets();
|
|
|
[
|
|
|
- self+o[0],
|
|
|
- self+o[1],
|
|
|
- self+o[2],
|
|
|
- self+o[3],
|
|
|
- self+o[4],
|
|
|
- self+o[5]
|
|
|
+ self + o[0],
|
|
|
+ self + o[1],
|
|
|
+ self + o[2],
|
|
|
+ self + o[3],
|
|
|
+ self + o[4],
|
|
|
+ self + o[5],
|
|
|
]
|
|
|
}
|
|
|
|
|
|
- fn offsets(self) -> [V3;6] {
|
|
|
+ fn offsets(self) -> [V3; 6] {
|
|
|
[
|
|
|
- V3::new(1,0,0),
|
|
|
- V3::new(-1,0,0),
|
|
|
- V3::new(0,1,0),
|
|
|
- V3::new(0,-1,0),
|
|
|
- V3::new(0,0,1),
|
|
|
- V3::new(0,0,-1),
|
|
|
+ V3::new(1, 0, 0),
|
|
|
+ V3::new(-1, 0, 0),
|
|
|
+ V3::new(0, 1, 0),
|
|
|
+ V3::new(0, -1, 0),
|
|
|
+ V3::new(0, 0, 1),
|
|
|
+ V3::new(0, 0, -1),
|
|
|
]
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
impl fmt::Debug for V3 {
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
write!(f, "V[{},{},{}]", self.x, self.y, self.z)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl Add<V3> for V3 {
|
|
|
+impl Add<V3> for V3 {
|
|
|
type Output = V3;
|
|
|
fn add(self, other: V3) -> V3 {
|
|
|
V3 {
|
|
|
@@ -71,7 +68,7 @@ impl Add<V3> for V3 {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl Add<i32> for V3 {
|
|
|
+impl Add<i32> for V3 {
|
|
|
type Output = V3;
|
|
|
fn add(self, other: i32) -> V3 {
|
|
|
V3 {
|
|
|
@@ -82,7 +79,7 @@ impl Add<i32> for V3 {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl Mul<i32> for V3 {
|
|
|
+impl Mul<i32> for V3 {
|
|
|
type Output = V3;
|
|
|
fn mul(self, other: i32) -> V3 {
|
|
|
V3 {
|
|
|
@@ -128,7 +125,6 @@ impl<'a> Mul<i32> for &'a V3 {
|
|
|
// }
|
|
|
// }
|
|
|
|
|
|
-
|
|
|
// #[derive(Clone, Debug)]
|
|
|
// struct Voxel {
|
|
|
// id: String,
|
|
|
@@ -145,15 +141,22 @@ struct GfxTile {
|
|
|
pos: Vec<f32>,
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
fn unitized_grid(vol: &HashMap<V3, String>, divisor: i32) -> HashMap<V3, String> {
|
|
|
- vol.iter().map(|(k,v)| (V3{x: k.x/divisor, y: k.y/divisor, z: k.z/divisor},v.clone())).collect()
|
|
|
+ vol.iter()
|
|
|
+ .map(|(k, v)| {
|
|
|
+ (
|
|
|
+ V3 {
|
|
|
+ x: k.x / divisor,
|
|
|
+ y: k.y / divisor,
|
|
|
+ z: k.z / divisor,
|
|
|
+ },
|
|
|
+ v.clone(),
|
|
|
+ )
|
|
|
+ })
|
|
|
+ .collect()
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-fn guess_gridsize(vol: &HashMap<V3, String>) -> i32{
|
|
|
+fn guess_gridsize(vol: &HashMap<V3, String>) -> i32 {
|
|
|
let mut x_positions: Vec<i32> = vol.iter().map(|(k, _v)| k.x).collect();
|
|
|
let mut y_positions: Vec<i32> = vol.iter().map(|(k, _v)| k.y).collect();
|
|
|
x_positions.sort();
|
|
|
@@ -164,7 +167,7 @@ fn guess_gridsize(vol: &HashMap<V3, String>) -> i32{
|
|
|
let mut last: Option<i32> = None;
|
|
|
for px in x_positions {
|
|
|
if let Some(l) = last {
|
|
|
- let delta = px.abs() - l;
|
|
|
+ let delta = px.abs() - l;
|
|
|
let c = dist_count.entry(delta.abs()).or_insert(1);
|
|
|
*c += 1;
|
|
|
}
|
|
|
@@ -182,19 +185,15 @@ fn guess_gridsize(vol: &HashMap<V3, String>) -> i32{
|
|
|
|
|
|
// dbg!(&grid);
|
|
|
grid
|
|
|
-
|
|
|
}
|
|
|
|
|
|
fn train_volume(vol: &HashMap<V3, String>) -> HashMap<String, HashMap<V3, Vec<String>>> {
|
|
|
-
|
|
|
let mut trained: HashMap<String, HashMap<V3, Vec<String>>> = HashMap::new();
|
|
|
|
|
|
for (pos, tilename) in vol {
|
|
|
// lookup neighbors
|
|
|
|
|
|
- let tile = trained.entry(tilename.clone()).or_insert(
|
|
|
- HashMap::new()
|
|
|
- );
|
|
|
+ let tile = trained.entry(tilename.clone()).or_insert(HashMap::new());
|
|
|
|
|
|
for p_o in pos.offsets().iter() {
|
|
|
let p_n = p_o + pos;
|
|
|
@@ -203,26 +202,27 @@ fn train_volume(vol: &HashMap<V3, String>) -> HashMap<String, HashMap<V3, Vec<St
|
|
|
neighbor_tile.push(neighbor.clone());
|
|
|
} else {
|
|
|
tile.insert(p_o.clone(), vec![neighbor.clone()]);
|
|
|
- }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
trained
|
|
|
}
|
|
|
|
|
|
-
|
|
|
fn load_export(path: &'static str) -> HashMap<V3, String> {
|
|
|
let reader = BufReader::new(File::open(path).unwrap());
|
|
|
- let loaded_tiles: Vec<GfxTile> = serde_json::from_reader(reader).unwrap();
|
|
|
+ let loaded_tiles: Vec<GfxTile> = serde_json::from_reader(reader).expect("Could not parse JSON");
|
|
|
loaded_tiles
|
|
|
.iter()
|
|
|
.map(|x| {
|
|
|
- (V3::new(x.pos[0] as i32, x.pos[1] as i32, x.pos[2] as i32), x.name.clone())
|
|
|
+ (
|
|
|
+ V3::new(x.pos[0] as i32, x.pos[1] as i32, x.pos[2] as i32),
|
|
|
+ x.name.clone(),
|
|
|
+ )
|
|
|
})
|
|
|
.collect()
|
|
|
}
|
|
|
|
|
|
-
|
|
|
// fn get_adjacent_tiles(grid: &HashMap<V3, String>, training_data: &HashMap<String, HashMap<V3, Vec<String>>>) -> HashMap<V3, String> {
|
|
|
// let mut adjacents: HashMap<V3, String> = HashMap::new();
|
|
|
|
|
|
@@ -243,46 +243,50 @@ fn get_unsolved_neighbors(cell: &V3, grid: &HashMap<V3, String>) -> Vec<V3> {
|
|
|
neighbors
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-fn wfc_solver(training_data: &HashMap<String, HashMap<V3, Vec<String>>>, base_map: HashMap<V3, String>) -> HashMap<V3, String> {
|
|
|
+fn wfc_solver(
|
|
|
+ training_data: &HashMap<String, HashMap<V3, Vec<String>>>,
|
|
|
+ base_map: HashMap<V3, String>,
|
|
|
+) -> HashMap<V3, String> {
|
|
|
let mut new_grid = base_map;
|
|
|
-
|
|
|
|
|
|
-
|
|
|
- for (pos, name) in new_grid.clone() {
|
|
|
+ for (pos, _name) in new_grid.clone() {
|
|
|
for unsolved_neighbor in get_unsolved_neighbors(&pos, &new_grid) {
|
|
|
+ println!("Unsolved {:?}", unsolved_neighbor);
|
|
|
for tile_neighbor in &unsolved_neighbor.offsets() {
|
|
|
- if let Some(tn) = new_grid.get(&(&unsolved_neighbor+tile_neighbor)) {
|
|
|
+ let abs_neighbor_pos = &unsolved_neighbor + tile_neighbor;
|
|
|
+
|
|
|
+ if let Some(tn) = new_grid.get(&abs_neighbor_pos) {
|
|
|
+ println!("\tNeighbor {:?} exists, is {}", abs_neighbor_pos, tn);
|
|
|
+ // println!("\tTraining data for {} = {:?}", tn, training_data.get(tn).unwrap());
|
|
|
+ let mut contraint_map: HashMap<V3, String> = HashMap::new();
|
|
|
+
|
|
|
if let Some(constraints) = training_data.get(tn).unwrap().get(tile_neighbor) {
|
|
|
// println!("\t{} points to me, accepts {:?}", tn, constraints);
|
|
|
- println!("Solving {:?} - looking into neighbors", unsolved_neighbor);
|
|
|
+ println!(
|
|
|
+ "\t\tSolving {:?} - looking into neighbors",
|
|
|
+ unsolved_neighbor
|
|
|
+ );
|
|
|
let cs = constraints.choose(&mut rand::thread_rng()).unwrap();
|
|
|
- println!("\tchose {:?}", cs);
|
|
|
+ println!("\t\tchose {:?}", cs);
|
|
|
new_grid.insert(unsolved_neighbor, cs.to_string());
|
|
|
-
|
|
|
+ } else {
|
|
|
+ println!("\t\tNo training data in tile {} at offset {:?}", tn, tile_neighbor);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
// println!("Training data: {:?}", trained_data_for_this_tile);
|
|
|
-
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
-
|
|
|
println!("grid: {:?}", new_grid);
|
|
|
|
|
|
new_grid
|
|
|
}
|
|
|
|
|
|
fn main() {
|
|
|
-
|
|
|
let _ = simplelog::SimpleLogger::init(LevelFilter::Info, Config::default());
|
|
|
|
|
|
-
|
|
|
let mut import_volume: HashMap<V3, String> = HashMap::new();
|
|
|
let imported_tiles = load_export("out.json");
|
|
|
let gridsize = guess_gridsize(&imported_tiles);
|
|
|
@@ -290,14 +294,12 @@ fn main() {
|
|
|
let imported_tiles = unitized_grid(&imported_tiles, gridsize);
|
|
|
info!("Tiles imported: {}", imported_tiles.len());
|
|
|
|
|
|
-
|
|
|
for (pos, tile) in imported_tiles {
|
|
|
import_volume.insert(pos, tile);
|
|
|
}
|
|
|
|
|
|
let training_data = train_volume(&import_volume);
|
|
|
info!("Training done: {} tiles", training_data.len());
|
|
|
-
|
|
|
// for (tilename, adjacency_map) in training_data {
|
|
|
// println!("\n{}", tilename);
|
|
|
// for (pos, fitting_tiles) in adjacency_map{
|
|
|
@@ -309,21 +311,25 @@ fn main() {
|
|
|
let mut base_map: HashMap<V3, String> = HashMap::new();
|
|
|
// init start conditions
|
|
|
base_map.insert(V3::new(10, 0, 0), String::from("grass"));
|
|
|
-
|
|
|
- base_map = wfc_solver(&training_data, base_map);
|
|
|
- base_map = wfc_solver(&training_data, base_map);
|
|
|
- base_map = wfc_solver(&training_data, base_map);
|
|
|
+
|
|
|
+ for i in 1..3 {
|
|
|
+ info!(
|
|
|
+ "=========================== Iteration {} ===========================\n\n\n",
|
|
|
+ i
|
|
|
+ );
|
|
|
+ base_map = wfc_solver(&training_data, base_map);
|
|
|
+ }
|
|
|
+
|
|
|
+ //base_map = wfc_solver(&training_data, base_map);
|
|
|
+ //base_map = wfc_solver(&training_data, base_map);
|
|
|
|
|
|
// dbg!(&vol);
|
|
|
// let mut adj_map: HashMap<String, Adjacency> = HashMap::new();
|
|
|
|
|
|
let mut export = vec![];
|
|
|
for (p, n) in &base_map {
|
|
|
- export.push((p*gridsize,n));
|
|
|
+ export.push((p * gridsize, n));
|
|
|
}
|
|
|
let writer = BufWriter::new(File::create("map.json").unwrap());
|
|
|
serde_json::to_writer_pretty(writer, &export).unwrap();
|
|
|
-
|
|
|
}
|
|
|
-
|
|
|
-
|