mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2025-01-12 13:51:10 +01:00
Merged Propagator and Wave into the WaveFormCollapse class, ans simplified it's design as much as possible. It still needs more work though.
This commit is contained in:
parent
fdb6ced123
commit
5beae4d5dc
@ -11,6 +11,11 @@ public:
|
|||||||
|
|
||||||
Vector<T> data;
|
Vector<T> data;
|
||||||
|
|
||||||
|
Array2D() {
|
||||||
|
height = 0;
|
||||||
|
width = 0;
|
||||||
|
}
|
||||||
|
|
||||||
Array2D(uint32_t p_height, uint32_t p_width) {
|
Array2D(uint32_t p_height, uint32_t p_width) {
|
||||||
height = p_height;
|
height = p_height;
|
||||||
width = p_width;
|
width = p_width;
|
||||||
@ -24,6 +29,19 @@ public:
|
|||||||
data.fill(p_value);
|
data.fill(p_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void resize(uint32_t p_height, uint32_t p_width) {
|
||||||
|
height = p_height;
|
||||||
|
width = p_width;
|
||||||
|
data.resize(width * height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize_fill(uint32_t p_height, uint32_t p_width, T p_value) {
|
||||||
|
height = p_height;
|
||||||
|
width = p_width;
|
||||||
|
data.resize(width * height);
|
||||||
|
data.fill(p_value);
|
||||||
|
}
|
||||||
|
|
||||||
const T &get(uint32_t i, uint32_t j) const {
|
const T &get(uint32_t i, uint32_t j) const {
|
||||||
CRASH_BAD_INDEX(i, height);
|
CRASH_BAD_INDEX(i, height);
|
||||||
CRASH_BAD_INDEX(j, width);
|
CRASH_BAD_INDEX(j, width);
|
||||||
|
@ -12,6 +12,12 @@ public:
|
|||||||
|
|
||||||
Vector<T> data;
|
Vector<T> data;
|
||||||
|
|
||||||
|
Array3D() {
|
||||||
|
height = 0;
|
||||||
|
width = 0;
|
||||||
|
depth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
Array3D(uint32_t p_height, uint32_t p_width, uint32_t p_depth) {
|
Array3D(uint32_t p_height, uint32_t p_width, uint32_t p_depth) {
|
||||||
height = p_height;
|
height = p_height;
|
||||||
width = p_width;
|
width = p_width;
|
||||||
@ -27,6 +33,21 @@ public:
|
|||||||
data.fill(value);
|
data.fill(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void resize(uint32_t p_height, uint32_t p_width, uint32_t p_depth) {
|
||||||
|
height = p_height;
|
||||||
|
width = p_width;
|
||||||
|
depth = p_depth;
|
||||||
|
data.resize(width * height * depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize_fill(uint32_t p_height, uint32_t p_width, uint32_t p_depth, T value) {
|
||||||
|
height = p_height;
|
||||||
|
width = p_width;
|
||||||
|
depth = p_depth;
|
||||||
|
data.resize(width * height * depth);
|
||||||
|
data.fill(value);
|
||||||
|
}
|
||||||
|
|
||||||
const T &get(uint32_t i, uint32_t j, uint32_t k) const {
|
const T &get(uint32_t i, uint32_t j, uint32_t k) const {
|
||||||
CRASH_BAD_INDEX(i, height);
|
CRASH_BAD_INDEX(i, height);
|
||||||
CRASH_BAD_INDEX(j, width);
|
CRASH_BAD_INDEX(j, width);
|
||||||
|
@ -46,7 +46,7 @@ private:
|
|||||||
const Array2D<T> &input, const OverlappingWFCOptions &options,
|
const Array2D<T> &input, const OverlappingWFCOptions &options,
|
||||||
const int &seed,
|
const int &seed,
|
||||||
const std::pair<Vector<Array2D<T>>, Vector<double>> &patterns,
|
const std::pair<Vector<Array2D<T>>, Vector<double>> &patterns,
|
||||||
const Vector<PropagatorEntry> &propagator) :
|
const Vector<PropagatorStateEntry> &propagator) :
|
||||||
input(input), options(options), patterns(patterns.first), wfc(options.periodic_output, seed, patterns.second, propagator, options.get_wave_height(), options.get_wave_width()) {
|
input(input), options(options), patterns(patterns.first), wfc(options.periodic_output, seed, patterns.second, propagator, options.get_wave_height(), options.get_wave_width()) {
|
||||||
// If necessary, the ground is set.
|
// If necessary, the ground is set.
|
||||||
if (options.ground) {
|
if (options.ground) {
|
||||||
@ -159,8 +159,8 @@ private:
|
|||||||
// If agrees(pattern1, pattern2, dy, dx), then compatible[pattern1][direction]
|
// If agrees(pattern1, pattern2, dy, dx), then compatible[pattern1][direction]
|
||||||
// contains pattern2, where direction is the direction defined by (dy, dx)
|
// contains pattern2, where direction is the direction defined by (dy, dx)
|
||||||
// (see direction.hpp).
|
// (see direction.hpp).
|
||||||
static Vector<PropagatorEntry> generate_compatible(const Vector<Array2D<T>> &patterns) {
|
static Vector<PropagatorStateEntry> generate_compatible(const Vector<Array2D<T>> &patterns) {
|
||||||
Vector<PropagatorEntry> compatible;
|
Vector<PropagatorStateEntry> compatible;
|
||||||
compatible.resize(patterns.size());
|
compatible.resize(patterns.size());
|
||||||
|
|
||||||
// Iterate on every dy, dx, pattern1 and pattern2
|
// Iterate on every dy, dx, pattern1 and pattern2
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
#include "propagator.h"
|
|
||||||
#include "wave.h"
|
|
||||||
|
|
||||||
void Propagator::init_compatible() {
|
|
||||||
CompatibilityEntry value;
|
|
||||||
// We compute the number of pattern compatible in all directions.
|
|
||||||
for (uint32_t y = 0; y < wave_height; y++) {
|
|
||||||
for (uint32_t x = 0; x < wave_width; x++) {
|
|
||||||
for (uint32_t pattern = 0; pattern < patterns_size; pattern++) {
|
|
||||||
for (int direction = 0; direction < 4; direction++) {
|
|
||||||
value.direction[direction] = static_cast<uint32_t>(propagator_state[pattern].directions[get_opposite_direction(direction)].size());
|
|
||||||
}
|
|
||||||
|
|
||||||
compatible.get(y, x, pattern) = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Propagator::propagate(Wave &wave) {
|
|
||||||
// We propagate every element while there is element to propagate.
|
|
||||||
while (propagating.size() != 0) {
|
|
||||||
// The cell and pattern that has been set to false.
|
|
||||||
|
|
||||||
const PropagatingEntry &e = propagating[propagating.size() - 1];
|
|
||||||
|
|
||||||
uint32_t y1 = e.data[0];
|
|
||||||
uint32_t x1 = e.data[1];
|
|
||||||
uint32_t pattern = e.data[2];
|
|
||||||
|
|
||||||
propagating.resize(propagating.size() - 1);
|
|
||||||
|
|
||||||
// We propagate the information in all 4 directions.
|
|
||||||
for (uint32_t direction = 0; direction < 4; direction++) {
|
|
||||||
// We get the next cell in the direction direction.
|
|
||||||
int dx = directions_x[direction];
|
|
||||||
int dy = directions_y[direction];
|
|
||||||
int x2, y2;
|
|
||||||
if (periodic_output) {
|
|
||||||
x2 = ((int)x1 + dx + (int)wave.width) % wave.width;
|
|
||||||
y2 = ((int)y1 + dy + (int)wave.height) % wave.height;
|
|
||||||
} else {
|
|
||||||
x2 = x1 + dx;
|
|
||||||
y2 = y1 + dy;
|
|
||||||
|
|
||||||
if (x2 < 0 || x2 >= (int)wave.width) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y2 < 0 || y2 >= (int)wave.height) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The index of the second cell, and the patterns compatible
|
|
||||||
uint32_t i2 = x2 + y2 * wave.width;
|
|
||||||
const Vector<uint32_t> &patterns = propagator_state[pattern].directions[direction];
|
|
||||||
|
|
||||||
// For every pattern that could be placed in that cell without being in
|
|
||||||
// contradiction with pattern1
|
|
||||||
int size = patterns.size();
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
uint32_t pattern_entry = patterns[i];
|
|
||||||
|
|
||||||
// We decrease the number of compatible patterns in the opposite
|
|
||||||
// direction If the pattern was discarded from the wave, the element
|
|
||||||
// is still negative, which is not a problem
|
|
||||||
CompatibilityEntry &value = compatible.get(y2, x2, pattern_entry);
|
|
||||||
value.direction[direction]--;
|
|
||||||
|
|
||||||
// If the element was set to 0 with this operation, we need to remove
|
|
||||||
// the pattern from the wave, and propagate the information
|
|
||||||
if (value.direction[direction] == 0) {
|
|
||||||
add_to_propagator(y2, x2, pattern_entry);
|
|
||||||
wave.set(i2, pattern_entry, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
#ifndef FAST_WFC_PROPAGATOR_HPP_
|
|
||||||
#define FAST_WFC_PROPAGATOR_HPP_
|
|
||||||
|
|
||||||
#include "array_3d.h"
|
|
||||||
#include "core/vector.h"
|
|
||||||
#include "direction.h"
|
|
||||||
|
|
||||||
class Wave;
|
|
||||||
|
|
||||||
class Propagator {
|
|
||||||
public:
|
|
||||||
struct PropagatorEntry {
|
|
||||||
Vector<uint32_t> directions[4];
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
const uint32_t patterns_size;
|
|
||||||
|
|
||||||
Vector<PropagatorEntry> propagator_state;
|
|
||||||
|
|
||||||
const uint32_t wave_width;
|
|
||||||
const uint32_t wave_height;
|
|
||||||
|
|
||||||
const bool periodic_output;
|
|
||||||
|
|
||||||
struct PropagatingEntry {
|
|
||||||
uint32_t data[3];
|
|
||||||
|
|
||||||
PropagatingEntry() {
|
|
||||||
for (int i = 0; i < 3; ++i) {
|
|
||||||
data[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PropagatingEntry(uint32_t x, uint32_t y, uint32_t z) {
|
|
||||||
data[0] = x;
|
|
||||||
data[1] = y;
|
|
||||||
data[2] = z;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// All the tuples (y, x, pattern) that should be propagated.
|
|
||||||
// The tuple should be propagated when wave.get(y, x, pattern) is set to
|
|
||||||
// false.
|
|
||||||
Vector<PropagatingEntry> propagating;
|
|
||||||
|
|
||||||
struct CompatibilityEntry {
|
|
||||||
int direction[4];
|
|
||||||
|
|
||||||
CompatibilityEntry() {
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
|
||||||
direction[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// compatible.get(y, x, pattern)[direction] contains the number of patterns
|
|
||||||
// present in the wave that can be placed in the cell next to (y,x) in the
|
|
||||||
// opposite direction of direction without being in contradiction with pattern
|
|
||||||
// placed in (y,x). If wave.get(y, x, pattern) is set to false, then
|
|
||||||
// compatible.get(y, x, pattern) has every element negative or null
|
|
||||||
Array3D<CompatibilityEntry> compatible;
|
|
||||||
|
|
||||||
void init_compatible();
|
|
||||||
|
|
||||||
public:
|
|
||||||
Propagator(uint32_t wave_height, uint32_t wave_width, bool periodic_output, Vector<PropagatorEntry> propagator_state) :
|
|
||||||
patterns_size(propagator_state.size()),
|
|
||||||
propagator_state(propagator_state),
|
|
||||||
wave_width(wave_width),
|
|
||||||
wave_height(wave_height),
|
|
||||||
periodic_output(periodic_output),
|
|
||||||
compatible(wave_height, wave_width, patterns_size) {
|
|
||||||
init_compatible();
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_to_propagator(uint32_t y, uint32_t x, uint32_t pattern) {
|
|
||||||
// All the direction are set to 0, since the pattern cannot be set in (y,x).
|
|
||||||
CompatibilityEntry temp;
|
|
||||||
compatible.get(y, x, pattern) = temp;
|
|
||||||
|
|
||||||
propagating.push_back(PropagatingEntry(y, x, pattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
void propagate(Wave &wave);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // FAST_WFC_PROPAGATOR_HPP_
|
|
@ -208,7 +208,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Generate the propagator which will be used in the wfc algorithm.
|
// Generate the propagator which will be used in the wfc algorithm.
|
||||||
static Vector<PropagatorEntry> generate_propagator(
|
static Vector<PropagatorStateEntry> generate_propagator(
|
||||||
const Vector<NeighbourData> &neighbors,
|
const Vector<NeighbourData> &neighbors,
|
||||||
Vector<Tile<T>> tiles,
|
Vector<Tile<T>> tiles,
|
||||||
Vector<std::pair<uint32_t, uint32_t>> id_to_oriented_tile,
|
Vector<std::pair<uint32_t, uint32_t>> id_to_oriented_tile,
|
||||||
@ -250,7 +250,7 @@ private:
|
|||||||
add(7, 0);
|
add(7, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<PropagatorEntry> propagator(nb_oriented_tiles);
|
Vector<PropagatorStateEntry> propagator(nb_oriented_tiles);
|
||||||
|
|
||||||
for (size_t i = 0; i < nb_oriented_tiles; ++i) {
|
for (size_t i = 0; i < nb_oriented_tiles; ++i) {
|
||||||
for (size_t j = 0; j < nb_oriented_tiles; ++j) {
|
for (size_t j = 0; j < nb_oriented_tiles; ++j) {
|
||||||
|
@ -1,132 +0,0 @@
|
|||||||
#include "wave.h"
|
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// Return distribution * log(distribution).
|
|
||||||
Vector<double> get_plogp(const Vector<double> &distribution) {
|
|
||||||
Vector<double> plogp;
|
|
||||||
|
|
||||||
for (int i = 0; i < distribution.size(); i++) {
|
|
||||||
plogp.push_back(distribution[i] * log(distribution[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return plogp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return min(v) / 2.
|
|
||||||
double get_min_abs_half(const Vector<double> &v) {
|
|
||||||
double min_abs_half = std::numeric_limits<double>::infinity();
|
|
||||||
|
|
||||||
for (int i = 0; i < v.size(); i++) {
|
|
||||||
min_abs_half = std::min(min_abs_half, std::abs(v[i] / 2.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
return min_abs_half;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
Wave::Wave(uint32_t height, uint32_t width,
|
|
||||||
const Vector<double> &patterns_frequencies) :
|
|
||||||
patterns_frequencies(patterns_frequencies),
|
|
||||||
plogp_patterns_frequencies(get_plogp(patterns_frequencies)),
|
|
||||||
min_abs_half_plogp(get_min_abs_half(plogp_patterns_frequencies)),
|
|
||||||
is_impossible(false),
|
|
||||||
nb_patterns(patterns_frequencies.size()),
|
|
||||||
data(width * height, nb_patterns, 1),
|
|
||||||
width(width),
|
|
||||||
height(height),
|
|
||||||
size(height * width) {
|
|
||||||
|
|
||||||
// Initialize the memoisation of entropy.
|
|
||||||
double base_entropy = 0;
|
|
||||||
double base_s = 0;
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < nb_patterns; i++) {
|
|
||||||
base_entropy += plogp_patterns_frequencies[i];
|
|
||||||
base_s += patterns_frequencies[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
double log_base_s = log(base_s);
|
|
||||||
double entropy_base = log_base_s - base_entropy / base_s;
|
|
||||||
|
|
||||||
memoisation_plogp_sum.resize(width * height);
|
|
||||||
memoisation_plogp_sum.fill(base_entropy);
|
|
||||||
|
|
||||||
memoisation_sum.resize(width * height);
|
|
||||||
memoisation_sum.fill(base_s);
|
|
||||||
|
|
||||||
memoisation_log_sum.resize(width * height);
|
|
||||||
memoisation_log_sum.fill(log_base_s);
|
|
||||||
|
|
||||||
memoisation_nb_patterns.resize(width * height);
|
|
||||||
memoisation_nb_patterns.fill(static_cast<uint32_t>(nb_patterns));
|
|
||||||
|
|
||||||
memoisation_entropy.resize(width * height);
|
|
||||||
memoisation_entropy.fill(entropy_base);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Wave::set(uint32_t index, uint32_t pattern, bool value) {
|
|
||||||
bool old_value = data.get(index, pattern);
|
|
||||||
|
|
||||||
// If the value isn't changed, nothing needs to be done.
|
|
||||||
if (old_value == value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, the memoisation should be updated.
|
|
||||||
data.get(index, pattern) = value;
|
|
||||||
memoisation_plogp_sum.write[index] -= plogp_patterns_frequencies[pattern];
|
|
||||||
memoisation_sum.write[index] -= patterns_frequencies[pattern];
|
|
||||||
memoisation_log_sum.write[index] = log(memoisation_sum[index]);
|
|
||||||
memoisation_nb_patterns.write[index]--;
|
|
||||||
memoisation_entropy.write[index] = memoisation_log_sum[index] - memoisation_plogp_sum[index] / memoisation_sum[index];
|
|
||||||
|
|
||||||
// If there is no patterns possible in the cell, then there is a contradiction.
|
|
||||||
if (memoisation_nb_patterns[index] == 0) {
|
|
||||||
is_impossible = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int Wave::get_min_entropy(std::minstd_rand &gen) const {
|
|
||||||
if (is_impossible) {
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uniform_real_distribution<> dis(0, min_abs_half_plogp);
|
|
||||||
|
|
||||||
// The minimum entropy (plus a small noise)
|
|
||||||
double min = std::numeric_limits<double>::infinity();
|
|
||||||
int argmin = -1;
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < size; i++) {
|
|
||||||
// If the cell is decided, we do not compute the entropy (which is equal
|
|
||||||
// to 0).
|
|
||||||
double nb_patterns_local = memoisation_nb_patterns[i];
|
|
||||||
|
|
||||||
if (nb_patterns_local == 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, we take the memoised entropy.
|
|
||||||
double entropy = memoisation_entropy[i];
|
|
||||||
|
|
||||||
// We first check if the entropy is less than the minimum.
|
|
||||||
// This is important to reduce noise computation (which is not
|
|
||||||
// negligible).
|
|
||||||
if (entropy <= min) {
|
|
||||||
// Then, we add noise to decide randomly which will be chosen.
|
|
||||||
// noise is smaller than the smallest p * log(p), so the minimum entropy
|
|
||||||
// will always be chosen.
|
|
||||||
double noise = dis(gen);
|
|
||||||
if (entropy + noise < min) {
|
|
||||||
min = entropy + noise;
|
|
||||||
argmin = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return argmin;
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
#ifndef FAST_WFC_WAVE_HPP_
|
|
||||||
#define FAST_WFC_WAVE_HPP_
|
|
||||||
|
|
||||||
#include "array_2d.h"
|
|
||||||
#include <random>
|
|
||||||
#include "core/vector.h"
|
|
||||||
|
|
||||||
// Contains the pattern possibilities in every cell.
|
|
||||||
// Also contains information about cell entropy.
|
|
||||||
class Wave {
|
|
||||||
private:
|
|
||||||
// The patterns frequencies p given to wfc.
|
|
||||||
const Vector<double> patterns_frequencies;
|
|
||||||
|
|
||||||
// The precomputation of p * log(p).
|
|
||||||
const Vector<double> plogp_patterns_frequencies;
|
|
||||||
|
|
||||||
// The precomputation of min (p * log(p)) / 2.
|
|
||||||
// This is used to define the maximum value of the noise.
|
|
||||||
const double min_abs_half_plogp;
|
|
||||||
|
|
||||||
Vector<double> memoisation_plogp_sum; // The sum of p'(pattern)// log(p'(pattern)).
|
|
||||||
Vector<double> memoisation_sum; // The sum of p'(pattern).
|
|
||||||
Vector<double> memoisation_log_sum; // The log of sum.
|
|
||||||
Vector<uint32_t> memoisation_nb_patterns; // The number of patterns present
|
|
||||||
Vector<double> memoisation_entropy; // The entropy of the cell.
|
|
||||||
|
|
||||||
// This value is set to true if there is a contradiction in the wave (all elements set to false in a cell).
|
|
||||||
bool is_impossible;
|
|
||||||
|
|
||||||
// The number of distinct patterns.
|
|
||||||
const size_t nb_patterns;
|
|
||||||
|
|
||||||
// The actual wave. data.get(index, pattern) is equal to 0 if the pattern can
|
|
||||||
// be placed in the cell index.
|
|
||||||
Array2D<uint8_t> data;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// The size of the wave.
|
|
||||||
const uint32_t width;
|
|
||||||
const uint32_t height;
|
|
||||||
const uint32_t size;
|
|
||||||
|
|
||||||
// Initialize the wave with every cell being able to have every pattern.
|
|
||||||
Wave(uint32_t height, uint32_t width, const Vector<double> &patterns_frequencies);
|
|
||||||
|
|
||||||
// Return true if pattern can be placed in cell index.
|
|
||||||
bool get(uint32_t index, uint32_t pattern) const {
|
|
||||||
return data.get(index, pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return true if pattern can be placed in cell (i,j)
|
|
||||||
bool get(uint32_t i, uint32_t j, uint32_t pattern) const {
|
|
||||||
return get(i * width + j, pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the value of pattern in cell index.
|
|
||||||
void set(uint32_t index, uint32_t pattern, bool value);
|
|
||||||
|
|
||||||
// Set the value of pattern in cell (i,j).
|
|
||||||
void set(uint32_t i, uint32_t j, uint32_t pattern, bool value) {
|
|
||||||
set(i * width + j, pattern, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the index of the cell with lowest entropy different of 0.
|
|
||||||
// If there is a contradiction in the wave, return -2.
|
|
||||||
// If every cell is decided, return -1.
|
|
||||||
int get_min_entropy(std::minstd_rand &gen) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // FAST_WFC_WAVE_HPP_
|
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// Normalize a vector so the sum of its elements is equal to 1.0f
|
// Normalize a vector so the sum of its elements is equal to 1.0f
|
||||||
Vector<double> &normalize(Vector<double> &v) {
|
void normalize(Vector<double> &v) {
|
||||||
double sum_weights = 0.0;
|
double sum_weights = 0.0;
|
||||||
int size = v.size();
|
int size = v.size();
|
||||||
const double *vpr = v.ptr();
|
const double *vpr = v.ptr();
|
||||||
@ -17,31 +17,59 @@ Vector<double> &normalize(Vector<double> &v) {
|
|||||||
for (int i = 0; i < size; ++i) {
|
for (int i = 0; i < size; ++i) {
|
||||||
vpw[i] *= inv_sum_weights;
|
vpw[i] *= inv_sum_weights;
|
||||||
}
|
}
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return distribution * log(distribution).
|
||||||
|
Vector<double> get_plogp(const Vector<double> &distribution) {
|
||||||
|
Vector<double> plogp;
|
||||||
|
|
||||||
|
for (int i = 0; i < distribution.size(); i++) {
|
||||||
|
plogp.push_back(distribution[i] * log(distribution[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return plogp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return min(v) / 2.
|
||||||
|
double get_min_abs_half(const Vector<double> &v) {
|
||||||
|
double min_abs_half = std::numeric_limits<double>::infinity();
|
||||||
|
|
||||||
|
for (int i = 0; i < v.size(); i++) {
|
||||||
|
min_abs_half = std::min(min_abs_half, std::abs(v[i] / 2.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return min_abs_half;
|
||||||
|
}
|
||||||
|
|
||||||
} //namespace
|
} //namespace
|
||||||
|
|
||||||
Array2D<uint32_t> WaveFormCollapse::wave_to_output() const {
|
bool WaveFormCollapse::get_eriodic_output() const {
|
||||||
Array2D<uint32_t> output_patterns(wave.height, wave.width);
|
return is_impossible;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < wave.size; i++) {
|
|
||||||
for (uint32_t k = 0; k < nb_patterns; k++) {
|
|
||||||
if (wave.get(i, k)) {
|
|
||||||
output_patterns.data.write[i] = k;
|
|
||||||
}
|
}
|
||||||
}
|
void WaveFormCollapse::set_periodic_output(const bool val) {
|
||||||
}
|
is_impossible = val;
|
||||||
return output_patterns;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//WaveFormCollapse::WaveFormCollapse() {
|
void WaveFormCollapse::set_seed(const int seed) {
|
||||||
//}
|
gen.seed(seed);
|
||||||
|
}
|
||||||
|
|
||||||
WaveFormCollapse::WaveFormCollapse(bool periodic_output, int seed,
|
void WaveFormCollapse::set_size(uint32_t p_width, uint32_t p_height) {
|
||||||
Vector<double> patterns_frequencies,
|
wave_width = p_width;
|
||||||
Vector<Propagator::PropagatorEntry> propagator, uint32_t wave_height, uint32_t wave_width) :
|
wave_height = p_height;
|
||||||
gen(seed), patterns_frequencies(normalize(patterns_frequencies)), wave(wave_height, wave_width, patterns_frequencies), nb_patterns(propagator.size()), propagator(wave.height, wave.width, periodic_output, propagator) {
|
wave_size = p_height * p_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveFormCollapse::set_propagator_state(const Vector<PropagatorStateEntry> &p_propagator_state) {
|
||||||
|
propagator_state = p_propagator_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveFormCollapse::set_pattern_frequencies(const Vector<double> &p_patterns_frequencies, const bool p_normalize) {
|
||||||
|
patterns_frequencies = p_patterns_frequencies;
|
||||||
|
|
||||||
|
if (p_normalize) {
|
||||||
|
normalize(patterns_frequencies);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Array2D<uint32_t> WaveFormCollapse::run() {
|
Array2D<uint32_t> WaveFormCollapse::run() {
|
||||||
@ -56,14 +84,13 @@ Array2D<uint32_t> WaveFormCollapse::run() {
|
|||||||
return wave_to_output();
|
return wave_to_output();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Propagate the information.
|
propagate();
|
||||||
propagator.propagate(wave);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WaveFormCollapse::ObserveStatus WaveFormCollapse::observe() {
|
WaveFormCollapse::ObserveStatus WaveFormCollapse::observe() {
|
||||||
// Get the cell with lowest entropy.
|
// Get the cell with lowest entropy.
|
||||||
int argmin = wave.get_min_entropy(gen);
|
int argmin = wave_get_min_entropy();
|
||||||
|
|
||||||
// If there is a contradiction, the algorithm has failed.
|
// If there is a contradiction, the algorithm has failed.
|
||||||
if (argmin == -2) {
|
if (argmin == -2) {
|
||||||
@ -79,16 +106,16 @@ WaveFormCollapse::ObserveStatus WaveFormCollapse::observe() {
|
|||||||
|
|
||||||
// Choose an element according to the pattern distribution
|
// Choose an element according to the pattern distribution
|
||||||
double s = 0;
|
double s = 0;
|
||||||
for (uint32_t k = 0; k < nb_patterns; k++) {
|
for (int k = 0; k < patterns_frequencies.size(); k++) {
|
||||||
s += wave.get(argmin, k) ? patterns_frequencies[k] : 0;
|
s += wave_get(argmin, k) ? patterns_frequencies[k] : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uniform_real_distribution<> dis(0, s);
|
double random_value = gen.random(0.0, s);
|
||||||
double random_value = dis(gen);
|
|
||||||
size_t chosen_value = nb_patterns - 1;
|
|
||||||
|
|
||||||
for (uint32_t k = 0; k < nb_patterns; k++) {
|
size_t chosen_value = patterns_frequencies.size() - 1;
|
||||||
random_value -= wave.get(argmin, k) ? patterns_frequencies[k] : 0;
|
|
||||||
|
for (int k = 0; k < patterns_frequencies.size(); k++) {
|
||||||
|
random_value -= wave_get(argmin, k) ? patterns_frequencies[k] : 0;
|
||||||
if (random_value <= 0) {
|
if (random_value <= 0) {
|
||||||
chosen_value = k;
|
chosen_value = k;
|
||||||
break;
|
break;
|
||||||
@ -96,15 +123,224 @@ WaveFormCollapse::ObserveStatus WaveFormCollapse::observe() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// And define the cell with the pattern.
|
// And define the cell with the pattern.
|
||||||
for (uint32_t k = 0; k < nb_patterns; k++) {
|
for (int k = 0; k < patterns_frequencies.size(); k++) {
|
||||||
if (wave.get(argmin, k) != (k == chosen_value)) {
|
if (wave_get(argmin, k) != (k == chosen_value)) {
|
||||||
propagator.add_to_propagator(argmin / wave.width, argmin % wave.width, k);
|
add_to_propagator(argmin / wave_width, argmin % wave_width, k);
|
||||||
wave.set(argmin, k, false);
|
wave_set(argmin, k, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return OBSERVE_STATUS_TO_CONTINUE;
|
return OBSERVE_STATUS_TO_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Array2D<uint32_t> WaveFormCollapse::wave_to_output() const {
|
||||||
|
Array2D<uint32_t> output_patterns(wave_height, wave_width);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < wave_size; i++) {
|
||||||
|
for (int k = 0; k < patterns_frequencies.size(); k++) {
|
||||||
|
if (wave_get(i, k)) {
|
||||||
|
output_patterns.data.write[i] = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output_patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveFormCollapse::wave_set(uint32_t index, uint32_t pattern, bool value) {
|
||||||
|
bool old_value = data.get(index, pattern);
|
||||||
|
|
||||||
|
// If the value isn't changed, nothing needs to be done.
|
||||||
|
if (old_value == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, the memoisation should be updated.
|
||||||
|
data.get(index, pattern) = value;
|
||||||
|
|
||||||
|
memoisation_plogp_sum.write[index] -= plogp_patterns_frequencies[pattern];
|
||||||
|
memoisation_sum.write[index] -= patterns_frequencies[pattern];
|
||||||
|
memoisation_log_sum.write[index] = log(memoisation_sum[index]);
|
||||||
|
memoisation_nb_patterns.write[index]--;
|
||||||
|
memoisation_entropy.write[index] = memoisation_log_sum[index] - memoisation_plogp_sum[index] / memoisation_sum[index];
|
||||||
|
|
||||||
|
// If there is no patterns possible in the cell, then there is a contradiction.
|
||||||
|
if (memoisation_nb_patterns[index] == 0) {
|
||||||
|
is_impossible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int WaveFormCollapse::wave_get_min_entropy() const {
|
||||||
|
if (is_impossible) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
RandomPCG pcg;
|
||||||
|
|
||||||
|
// The minimum entropy (plus a small noise)
|
||||||
|
double min = std::numeric_limits<double>::infinity();
|
||||||
|
int argmin = -1;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < wave_size; i++) {
|
||||||
|
// If the cell is decided, we do not compute the entropy (which is equal
|
||||||
|
// to 0).
|
||||||
|
double nb_patterns_local = memoisation_nb_patterns[i];
|
||||||
|
|
||||||
|
if (nb_patterns_local == 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we take the memoised entropy.
|
||||||
|
double entropy = memoisation_entropy[i];
|
||||||
|
|
||||||
|
// We first check if the entropy is less than the minimum.
|
||||||
|
// This is important to reduce noise computation (which is not
|
||||||
|
// negligible).
|
||||||
|
if (entropy <= min) {
|
||||||
|
// Then, we add noise to decide randomly which will be chosen.
|
||||||
|
// noise is smaller than the smallest p * log(p), so the minimum entropy
|
||||||
|
// will always be chosen.
|
||||||
|
double noise = pcg.random(0.0, min_abs_half_plogp);
|
||||||
|
if (entropy + noise < min) {
|
||||||
|
min = entropy + noise;
|
||||||
|
argmin = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return argmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveFormCollapse::init_compatible() {
|
||||||
|
CompatibilityEntry value;
|
||||||
|
|
||||||
|
// We compute the number of pattern compatible in all directions.
|
||||||
|
for (uint32_t y = 0; y < wave_height; y++) {
|
||||||
|
for (uint32_t x = 0; x < wave_width; x++) {
|
||||||
|
for (int pattern = 0; pattern < propagator_state.size(); pattern++) {
|
||||||
|
for (int direction = 0; direction < 4; direction++) {
|
||||||
|
value.direction[direction] = static_cast<uint32_t>(propagator_state[pattern].directions[get_opposite_direction(direction)].size());
|
||||||
|
}
|
||||||
|
|
||||||
|
compatible.get(y, x, pattern) = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveFormCollapse::propagate() {
|
||||||
|
// We propagate every element while there is element to propagate.
|
||||||
|
while (propagating.size() != 0) {
|
||||||
|
// The cell and pattern that has been set to false.
|
||||||
|
|
||||||
|
const PropagatingEntry &e = propagating[propagating.size() - 1];
|
||||||
|
|
||||||
|
uint32_t y1 = e.data[0];
|
||||||
|
uint32_t x1 = e.data[1];
|
||||||
|
uint32_t pattern = e.data[2];
|
||||||
|
|
||||||
|
propagating.resize(propagating.size() - 1);
|
||||||
|
|
||||||
|
// We propagate the information in all 4 directions.
|
||||||
|
for (uint32_t direction = 0; direction < 4; direction++) {
|
||||||
|
// We get the next cell in the direction direction.
|
||||||
|
int dx = directions_x[direction];
|
||||||
|
int dy = directions_y[direction];
|
||||||
|
int x2, y2;
|
||||||
|
|
||||||
|
if (periodic_output) {
|
||||||
|
x2 = ((int)x1 + dx + (int)wave_width) % wave_width;
|
||||||
|
y2 = ((int)y1 + dy + (int)wave_height) % wave_height;
|
||||||
|
} else {
|
||||||
|
x2 = x1 + dx;
|
||||||
|
y2 = y1 + dy;
|
||||||
|
|
||||||
|
if (x2 < 0 || x2 >= (int)wave_width) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y2 < 0 || y2 >= (int)wave_height) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The index of the second cell, and the patterns compatible
|
||||||
|
uint32_t i2 = x2 + y2 * wave_width;
|
||||||
|
const Vector<uint32_t> &patterns = propagator_state[pattern].directions[direction];
|
||||||
|
|
||||||
|
// For every pattern that could be placed in that cell without being in
|
||||||
|
// contradiction with pattern1
|
||||||
|
int size = patterns.size();
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
uint32_t pattern_entry = patterns[i];
|
||||||
|
|
||||||
|
// We decrease the number of compatible patterns in the opposite
|
||||||
|
// direction If the pattern was discarded from the wave, the element
|
||||||
|
// is still negative, which is not a problem
|
||||||
|
CompatibilityEntry &value = compatible.get(y2, x2, pattern_entry);
|
||||||
|
value.direction[direction]--;
|
||||||
|
|
||||||
|
// If the element was set to 0 with this operation, we need to remove
|
||||||
|
// the pattern from the wave, and propagate the information
|
||||||
|
if (value.direction[direction] == 0) {
|
||||||
|
add_to_propagator(y2, x2, pattern_entry);
|
||||||
|
wave_set(i2, pattern_entry, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveFormCollapse::initialize() {
|
||||||
|
//wave
|
||||||
|
data.resize_fill(wave_width * wave_height, patterns_frequencies.size(), 1);
|
||||||
|
|
||||||
|
plogp_patterns_frequencies = get_plogp(patterns_frequencies);
|
||||||
|
min_abs_half_plogp = get_min_abs_half(plogp_patterns_frequencies);
|
||||||
|
|
||||||
|
is_impossible = false;
|
||||||
|
|
||||||
|
// Initialize the memoisation of entropy.
|
||||||
|
double base_entropy = 0;
|
||||||
|
double base_s = 0;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < patterns_frequencies.size(); i++) {
|
||||||
|
base_entropy += plogp_patterns_frequencies[i];
|
||||||
|
base_s += patterns_frequencies[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
double log_base_s = log(base_s);
|
||||||
|
double entropy_base = log_base_s - base_entropy / base_s;
|
||||||
|
|
||||||
|
memoisation_plogp_sum.resize(wave_width * wave_height);
|
||||||
|
memoisation_plogp_sum.fill(base_entropy);
|
||||||
|
|
||||||
|
memoisation_sum.resize(wave_width * wave_height);
|
||||||
|
memoisation_sum.fill(base_s);
|
||||||
|
|
||||||
|
memoisation_log_sum.resize(wave_width * wave_height);
|
||||||
|
memoisation_log_sum.fill(log_base_s);
|
||||||
|
|
||||||
|
memoisation_nb_patterns.resize(wave_width * wave_height);
|
||||||
|
memoisation_nb_patterns.fill(static_cast<uint32_t>(patterns_frequencies.size()));
|
||||||
|
|
||||||
|
memoisation_entropy.resize(wave_width * wave_height);
|
||||||
|
memoisation_entropy.fill(entropy_base);
|
||||||
|
|
||||||
|
//propagator
|
||||||
|
compatible.resize(wave_height, wave_width, propagator_state.size());
|
||||||
|
init_compatible();
|
||||||
|
}
|
||||||
|
|
||||||
|
WaveFormCollapse::WaveFormCollapse() {
|
||||||
|
//todo maybe it should be better as true?
|
||||||
|
periodic_output = false;
|
||||||
|
is_impossible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaveFormCollapse::~WaveFormCollapse() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void WaveFormCollapse::bind_methods() {
|
void WaveFormCollapse::bind_methods() {
|
||||||
}
|
}
|
@ -1,14 +1,16 @@
|
|||||||
#ifndef WAVE_FORM_COLLAPSE_H
|
#ifndef WAVE_FORM_COLLAPSE_H
|
||||||
#define WAVE_FORM_COLLAPSE_H
|
#define WAVE_FORM_COLLAPSE_H
|
||||||
|
|
||||||
#include "core/reference.h"
|
#include "core/int_types.h"
|
||||||
|
#include "core/math/random_pcg.h"
|
||||||
#include <optional>
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
#include "array_2d.h"
|
#include "array_2d.h"
|
||||||
#include "propagator.h"
|
#include "array_3d.h"
|
||||||
#include "wave.h"
|
#include "core/vector.h"
|
||||||
|
|
||||||
|
#include "core/reference.h"
|
||||||
|
|
||||||
|
#include "direction.h"
|
||||||
|
|
||||||
class WaveFormCollapse : public Reference {
|
class WaveFormCollapse : public Reference {
|
||||||
GDCLASS(WaveFormCollapse, Reference);
|
GDCLASS(WaveFormCollapse, Reference);
|
||||||
@ -20,43 +22,157 @@ public:
|
|||||||
OBSERVE_STATUS_TO_CONTINUE
|
OBSERVE_STATUS_TO_CONTINUE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PropagatorStateEntry {
|
||||||
|
Vector<uint32_t> directions[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PropagatingEntry {
|
||||||
|
uint32_t data[3];
|
||||||
|
|
||||||
|
PropagatingEntry() {
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
data[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PropagatingEntry(uint32_t x, uint32_t y, uint32_t z) {
|
||||||
|
data[0] = x;
|
||||||
|
data[1] = y;
|
||||||
|
data[2] = z;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CompatibilityEntry {
|
||||||
|
int direction[4];
|
||||||
|
|
||||||
|
CompatibilityEntry() {
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
direction[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool get_eriodic_output() const;
|
||||||
|
void set_periodic_output(const bool val);
|
||||||
|
|
||||||
|
void set_seed(const int seed);
|
||||||
|
|
||||||
|
void set_size(uint32_t p_width, uint32_t p_height);
|
||||||
|
|
||||||
|
void set_propagator_state(const Vector<PropagatorStateEntry> &p_propagator_state);
|
||||||
|
void set_pattern_frequencies(const Vector<double> &p_patterns_frequencies, const bool p_normalize = true);
|
||||||
|
|
||||||
Array2D<uint32_t> run();
|
Array2D<uint32_t> run();
|
||||||
|
|
||||||
ObserveStatus observe();
|
ObserveStatus observe();
|
||||||
|
|
||||||
void propagate() { propagator.propagate(wave); }
|
//dvoid propagate() { propagator.propagate(wave); }
|
||||||
|
|
||||||
void remove_wave_pattern(uint32_t i, uint32_t j, uint32_t pattern) {
|
void remove_wave_pattern(uint32_t i, uint32_t j, uint32_t pattern) {
|
||||||
if (wave.get(i, j, pattern)) {
|
if (wave_get(i, j, pattern)) {
|
||||||
wave.set(i, j, pattern, false);
|
wave_set(i, j, pattern, false);
|
||||||
propagator.add_to_propagator(i, j, pattern);
|
add_to_propagator(i, j, pattern);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return true if pattern can be placed in cell index.
|
||||||
|
bool wave_get(uint32_t index, uint32_t pattern) const {
|
||||||
|
return data.get(index, pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if pattern can be placed in cell (i,j)
|
||||||
|
bool wave_get(uint32_t i, uint32_t j, uint32_t pattern) const {
|
||||||
|
return wave_get(i * wave_width + j, pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the value of pattern in cell index.
|
||||||
|
void wave_set(uint32_t index, uint32_t pattern, bool value);
|
||||||
|
|
||||||
|
// Set the value of pattern in cell (i,j).
|
||||||
|
void wave_set(uint32_t i, uint32_t j, uint32_t pattern, bool value) {
|
||||||
|
wave_set(i * wave_width + j, pattern, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the index of the cell with lowest entropy different of 0.
|
||||||
|
// If there is a contradiction in the wave, return -2.
|
||||||
|
// If every cell is decided, return -1.
|
||||||
|
int wave_get_min_entropy() const;
|
||||||
|
|
||||||
|
void add_to_propagator(uint32_t y, uint32_t x, uint32_t pattern) {
|
||||||
|
// All the direction are set to 0, since the pattern cannot be set in (y,x).
|
||||||
|
CompatibilityEntry temp;
|
||||||
|
compatible.get(y, x, pattern) = temp;
|
||||||
|
|
||||||
|
propagating.push_back(PropagatingEntry(y, x, pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
void propagate();
|
||||||
|
|
||||||
|
void initialize();
|
||||||
|
|
||||||
WaveFormCollapse();
|
WaveFormCollapse();
|
||||||
WaveFormCollapse(bool periodic_output, int seed, Vector<double> patterns_frequencies,
|
~WaveFormCollapse();
|
||||||
Vector<Propagator::PropagatorEntry> propagator, uint32_t wave_height,
|
|
||||||
uint32_t wave_width);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void bind_methods();
|
static void bind_methods();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::minstd_rand gen;
|
RandomPCG gen;
|
||||||
|
|
||||||
const Vector<double> patterns_frequencies;
|
|
||||||
|
|
||||||
Wave wave;
|
|
||||||
|
|
||||||
// The number of distinct patterns.
|
// The number of distinct patterns.
|
||||||
const size_t nb_patterns;
|
size_t nb_patterns;
|
||||||
|
|
||||||
Propagator propagator;
|
|
||||||
|
|
||||||
// Transform the wave to a valid output (a 2d array of patterns that aren't in
|
// Transform the wave to a valid output (a 2d array of patterns that aren't in
|
||||||
// contradiction). This function should be used only when all cell of the wave
|
// contradiction). This function should be used only when all cell of the wave
|
||||||
// are defined.
|
// are defined.
|
||||||
Array2D<uint32_t> wave_to_output() const;
|
Array2D<uint32_t> wave_to_output() const;
|
||||||
|
|
||||||
|
//Wave
|
||||||
|
uint32_t wave_width;
|
||||||
|
uint32_t wave_height;
|
||||||
|
uint32_t wave_size;
|
||||||
|
|
||||||
|
// The patterns frequencies p given to wfc.
|
||||||
|
Vector<double> patterns_frequencies;
|
||||||
|
|
||||||
|
// The precomputation of p * log(p).
|
||||||
|
Vector<double> plogp_patterns_frequencies;
|
||||||
|
|
||||||
|
// The precomputation of min (p * log(p)) / 2.
|
||||||
|
// This is used to define the maximum value of the noise.
|
||||||
|
double min_abs_half_plogp;
|
||||||
|
|
||||||
|
Vector<double> memoisation_plogp_sum; // The sum of p'(pattern)// log(p'(pattern)).
|
||||||
|
Vector<double> memoisation_sum; // The sum of p'(pattern).
|
||||||
|
Vector<double> memoisation_log_sum; // The log of sum.
|
||||||
|
Vector<uint32_t> memoisation_nb_patterns; // The number of patterns present
|
||||||
|
Vector<double> memoisation_entropy; // The entropy of the cell.
|
||||||
|
|
||||||
|
// This value is set to true if there is a contradiction in the wave (all elements set to false in a cell).
|
||||||
|
bool is_impossible;
|
||||||
|
|
||||||
|
// The actual wave. data.get(index, pattern) is equal to 0 if the pattern can
|
||||||
|
// be placed in the cell index.
|
||||||
|
Array2D<uint8_t> data;
|
||||||
|
|
||||||
|
//Propagator
|
||||||
|
Vector<PropagatorStateEntry> propagator_state;
|
||||||
|
|
||||||
|
bool periodic_output;
|
||||||
|
|
||||||
|
// All the tuples (y, x, pattern) that should be propagated.
|
||||||
|
// The tuple should be propagated when wave.get(y, x, pattern) is set to false.
|
||||||
|
Vector<PropagatingEntry> propagating;
|
||||||
|
|
||||||
|
// compatible.get(y, x, pattern)[direction] contains the number of patterns
|
||||||
|
// present in the wave that can be placed in the cell next to (y,x) in the
|
||||||
|
// opposite direction of direction without being in contradiction with pattern
|
||||||
|
// placed in (y,x). If wave.get(y, x, pattern) is set to false, then
|
||||||
|
// compatible.get(y, x, pattern) has every element negative or null
|
||||||
|
Array3D<CompatibilityEntry> compatible;
|
||||||
|
|
||||||
|
void init_compatible();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user