|
@@ -39,6 +39,23 @@ impl V3 {
|
|
|
]
|
|
|
}
|
|
|
|
|
|
+ fn rotated_neighbors(self, axis: u8,) -> [V3; 6] {
|
|
|
+
|
|
|
+ // [2]
|
|
|
+ // [1][S][0]
|
|
|
+ // [3]
|
|
|
+
|
|
|
+ [
|
|
|
+ self,
|
|
|
+ self,
|
|
|
+ self,
|
|
|
+ self,
|
|
|
+ self,
|
|
|
+ self,
|
|
|
+ ]
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
fn offsets(self) -> [V3; 6] {
|
|
|
[
|
|
|
V3::new(1, 0, 0),
|
|
@@ -56,7 +73,6 @@ impl fmt::Debug for V3 {
|
|
|
write!(f, "V[{},{},{}]", self.x*8, self.y*8, self.z*8)
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
impl Add<V3> for V3 {
|
|
|
type Output = V3;
|
|
|
fn add(self, other: V3) -> V3 {
|
|
@@ -67,7 +83,6 @@ impl Add<V3> for V3 {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
impl Add<i32> for V3 {
|
|
|
type Output = V3;
|
|
|
fn add(self, other: i32) -> V3 {
|
|
@@ -78,7 +93,6 @@ impl Add<i32> for V3 {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
impl Mul<i32> for V3 {
|
|
|
type Output = V3;
|
|
|
fn mul(self, other: i32) -> V3 {
|
|
@@ -89,7 +103,6 @@ impl Mul<i32> for V3 {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
impl Neg<> for V3 {
|
|
|
type Output = V3;
|
|
|
fn neg(self) -> V3 {
|
|
@@ -100,7 +113,6 @@ impl Neg<> for V3 {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
impl<'a, 'b> Add<&'b V3> for &'a V3 {
|
|
|
type Output = V3;
|
|
|
|
|
@@ -112,7 +124,6 @@ impl<'a, 'b> Add<&'b V3> for &'a V3 {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
impl<'a> Mul<i32> for &'a V3 {
|
|
|
type Output = V3;
|
|
|
|
|
@@ -124,8 +135,6 @@ impl<'a> Mul<i32> for &'a V3 {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
impl<'a> Neg<> for &'a V3 {
|
|
|
type Output = V3;
|
|
|
|
|
@@ -138,31 +147,14 @@ impl<'a> Neg<> for &'a V3 {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// impl Sub<V3> for V3 {
|
|
|
-// type Output = V3;
|
|
|
-// fn sub(self, other: V3) -> V3 {
|
|
|
-// V3 {
|
|
|
-// x: self.x - other.x,
|
|
|
-// y: self.y - other.y,
|
|
|
-// z: self.z - other.z,
|
|
|
-// }
|
|
|
-// }
|
|
|
-// }
|
|
|
-
|
|
|
-// #[derive(Clone, Debug)]
|
|
|
-// struct Voxel {
|
|
|
-// id: String,
|
|
|
-// // adjacency: HashMap<&'static str, u16>,
|
|
|
-// adjacency: HashMap<V3, Vec<String>>,
|
|
|
-// // offsets: [V3; 6]
|
|
|
-
|
|
|
-// }
|
|
|
|
|
|
/// A graphics tile as ex- or imported from a 3d program
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
|
struct GfxTile {
|
|
|
name: String,
|
|
|
pos: Vec<f32>,
|
|
|
+ #[serde(default)]
|
|
|
+ rot: Vec<f32>
|
|
|
}
|
|
|
|
|
|
fn unitized_grid(vol: &HashMap<V3, String>, divisor: i32) -> HashMap<V3, String> {
|
|
@@ -238,23 +230,17 @@ fn load_export(path: &'static str) -> HashMap<V3, String> {
|
|
|
let loaded_tiles: Vec<GfxTile> = serde_json::from_reader(reader).expect("Could not parse JSON");
|
|
|
loaded_tiles
|
|
|
.iter()
|
|
|
- .map(|x| {
|
|
|
+ .map(|t| {
|
|
|
+ dbg!(&t);
|
|
|
(
|
|
|
- V3::new(x.pos[0] as i32, x.pos[1] as i32, x.pos[2] as i32),
|
|
|
- x.name.clone(),
|
|
|
+ V3::new(t.pos[0] as i32, t.pos[1] as i32, t.pos[2] as i32),
|
|
|
+ t.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();
|
|
|
|
|
|
-// for (pos, tile) in grid {
|
|
|
-
|
|
|
-// }
|
|
|
-// adjacents
|
|
|
-// }
|
|
|
|
|
|
fn get_unsolved_neighbors(cell: &V3, grid: &HashMap<V3, String>) -> Vec<V3> {
|
|
|
let mut neighbors = vec![];
|
|
@@ -267,16 +253,45 @@ fn get_unsolved_neighbors(cell: &V3, grid: &HashMap<V3, String>) -> Vec<V3> {
|
|
|
neighbors
|
|
|
}
|
|
|
|
|
|
-fn template_meets_constraints(constraint: &HashMap<V3, String>, template: &HashMap<V3, Vec<String>>) -> bool {
|
|
|
-
|
|
|
+fn solve(constraint: &HashMap<V3, String>, training_data: &HashMap<String, HashMap<V3, Vec<String>>>) -> Option<String> {
|
|
|
|
|
|
- for (c_pos, c_name) in constraint {
|
|
|
- // let _x = template.get(&c_pos).map(|x| x.contains(c_name));
|
|
|
- if let Some(possible_tiles) = template.get(&c_pos) {
|
|
|
-
|
|
|
+
|
|
|
+ let mut valid_templates = vec![];
|
|
|
+ for template in training_data {
|
|
|
+
|
|
|
+ let c: Vec<bool> = constraint
|
|
|
+ .iter()
|
|
|
+ .map(|(pos, name)| template.1.get(&pos).map(|x| x.contains(name)) )
|
|
|
+ .filter(|e| e.is_some())
|
|
|
+ .map(|e| e.unwrap())
|
|
|
+ .collect();
|
|
|
+
|
|
|
+ if c.is_empty() {
|
|
|
+ valid_templates.push(None);
|
|
|
+ } else {
|
|
|
+ if c.iter().all(|x| x == &true) {
|
|
|
+ valid_templates.push(Some(template.0))
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+ // valid_templates.push(c);
|
|
|
+
|
|
|
}
|
|
|
- true
|
|
|
+
|
|
|
+
|
|
|
+ let valid_tiles: Vec<_> = valid_templates.iter().filter(|x| x.is_some()).map(|x| x.unwrap()).collect();
|
|
|
+
|
|
|
+ // This was not solvable
|
|
|
+ if valid_tiles.is_empty() {
|
|
|
+ return None
|
|
|
+ } else {
|
|
|
+
|
|
|
+ Some(valid_tiles.choose(&mut rand::thread_rng()).unwrap().to_string())
|
|
|
+ // Some("grass".to_string())
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
}
|
|
|
|
|
|
fn wfc_solver(
|
|
@@ -288,100 +303,51 @@ fn wfc_solver(
|
|
|
|
|
|
for (pos, _name) in new_grid.clone() {
|
|
|
for unsolved_neighbor in get_unsolved_neighbors(&pos, &new_grid) {
|
|
|
- println!("Unsolved {:?}", unsolved_neighbor);
|
|
|
+
|
|
|
+ debug!("Unsolved {:?}", unsolved_neighbor);
|
|
|
+
|
|
|
let mut contraint_map: HashMap<V3, String> = HashMap::new();
|
|
|
|
|
|
for tile_neighbor in &unsolved_neighbor.offsets() {
|
|
|
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());
|
|
|
-
|
|
|
+ debug!("\tNeighbor {:?} exists, is {}. Inserting as constraint.", abs_neighbor_pos, tn);
|
|
|
contraint_map.insert(tile_neighbor.clone(), tn.to_string());
|
|
|
- // if let Some(constraints) = training_data.get(tn).unwrap().get(&-tile_neighbor) {
|
|
|
- // // println!("\t{} points to me, accepts {:?}", tn, constraints);
|
|
|
- // println!(
|
|
|
- // "\t\tlooking up in {} {:?}",
|
|
|
- // tn, tile_neighbor
|
|
|
- // );
|
|
|
- // let cs = constraints.choose(&mut rand::thread_rng()).unwrap();
|
|
|
- // println!("\t\tAdded a randomly chosen constraint: {:?}", cs);
|
|
|
- // // new_grid.insert(unsolved_neighbor, cs.to_string());
|
|
|
- // contraint_map.insert(tile_neighbor.clone(), cs.to_string());
|
|
|
-
|
|
|
- // } else {
|
|
|
- // // println!("\t\tNo training data in tile {} at offset {:?}", tn, tile_neighbor);
|
|
|
- // }
|
|
|
-
|
|
|
}
|
|
|
}
|
|
|
- // Constraint map is ready here
|
|
|
- // println!("All constraints: {:?}", contraint_map);
|
|
|
- let num_constraints = contraint_map.len();
|
|
|
-
|
|
|
- println!("\tSolving {} contraints in {:?}", num_constraints, contraint_map );
|
|
|
+ // Constraint map is ready here
|
|
|
+ debug!("\tSolving {} contraints in {:?}", contraint_map.len(), contraint_map );
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ match solve(&contraint_map, &training_data) {
|
|
|
+ Some(tile) => {
|
|
|
+ if tile == "BACKTRACK" {
|
|
|
+ for n in unsolved_neighbor.neighbors().iter(){
|
|
|
+ new_grid.remove(&n);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ new_grid.insert(unsolved_neighbor, tile);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ None => {
|
|
|
+ // for n in unsolved_neighbor.neighbors().iter(){
|
|
|
+ // new_grid.remove(&n);
|
|
|
+ // }
|
|
|
+ }
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
|
|
|
-
|
|
|
- //let mut compatible = 0;
|
|
|
- for (template_name, compatible_tiles) in training_data {
|
|
|
-
|
|
|
- //if compatible == num_constraints {break;}
|
|
|
- println!("\t\tProbing template {:?}", template_name );
|
|
|
- let _compat = template_meets_constraints(&contraint_map, compatible_tiles);
|
|
|
-
|
|
|
-
|
|
|
- //new_grid.insert(unsolved_neighbor, template_tile.to_string());
|
|
|
-
|
|
|
- // // TODO: iterate through all combos at all the constraint positions
|
|
|
- // for (p, constraint) in &contraint_map {
|
|
|
- // println!("\t\t\tWill {:?} {} fit?", p, constraint );
|
|
|
- // if let Some(tiles_for_this_pos) = compatible_tiles.get(&p) {
|
|
|
- // println!("\t\t\tAt the above pos, this is what the template offers: {:?}", tiles_for_this_pos );
|
|
|
- // if tiles_for_this_pos.contains(&constraint) {
|
|
|
- // compatible += 1;
|
|
|
- // if compatible == num_constraints {
|
|
|
- // println!("\tSolved with {}.", template_tile);
|
|
|
- // new_grid.insert(unsolved_neighbor, template_tile.to_string());
|
|
|
- // break;
|
|
|
- // }
|
|
|
- // }
|
|
|
- // }
|
|
|
- // }
|
|
|
-
|
|
|
-
|
|
|
- // println!("{}", compatible);
|
|
|
- }
|
|
|
|
|
|
|
|
|
- // for (pos, tilename) in contraint_map {
|
|
|
- // println!("\tSolving {} constraint", tilename );
|
|
|
- // // Now find a suitable tile in training data
|
|
|
- // let mut compatible = 0;
|
|
|
- // for (template_tile, compatible_tiles) in training_data {
|
|
|
- // if let Some(tiles_for_this_pos) = compatible_tiles.get(&pos) {
|
|
|
- // if tiles_for_this_pos.contains(&tilename) {
|
|
|
- // println!("\t{} template supports {} @{:?}", template_tile, tilename, pos, );
|
|
|
- // compatible += 1;
|
|
|
- // if compatible == num_constraints {
|
|
|
- // println!("\t{} fits all criteria. Locking in at {:?}", template_tile, unsolved_neighbor*8);
|
|
|
- // new_grid.insert(unsolved_neighbor, template_tile.to_string());
|
|
|
-
|
|
|
- // break;
|
|
|
-
|
|
|
- // }
|
|
|
- // }
|
|
|
-
|
|
|
- // }
|
|
|
- // }
|
|
|
-
|
|
|
- // }
|
|
|
-
|
|
|
- // println!("Training data: {:?}", trained_data_for_this_tile);
|
|
|
+
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- println!("grid: {:?}", new_grid);
|
|
|
+ // println!("grid: {:?}", new_grid);
|
|
|
|
|
|
new_grid
|
|
|
}
|
|
@@ -402,31 +368,20 @@ fn main() {
|
|
|
|
|
|
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{
|
|
|
- // println!("{:?}{:?}", pos, fitting_tiles);
|
|
|
- // }
|
|
|
- // }
|
|
|
-
|
|
|
- // let new_grid: HashMap<V3, String> = HashMap::new();
|
|
|
+
|
|
|
let mut base_map: HashMap<V3, String> = HashMap::new();
|
|
|
+
|
|
|
// init start conditions
|
|
|
base_map.insert(V3::new(0, 0, 0), String::from("grass"));
|
|
|
|
|
|
- for i in 1..3 {
|
|
|
+ for i in 1..8 {
|
|
|
info!(
|
|
|
- "=========================== Iteration {} ===========================\n\n\n",
|
|
|
+ "=========================== Iteration {} ===========================",
|
|
|
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 {
|