mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2024-12-25 05:07:12 +01:00
An another set of codestyle cleanups for the wfc module.
This commit is contained in:
parent
837e518e5a
commit
dfbae29faa
@ -4,62 +4,37 @@
|
|||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
/**
|
|
||||||
* Represent a 2D array.
|
|
||||||
* The 2D array is stored in a single array, to improve cache usage.
|
|
||||||
*/
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class Array2D {
|
class Array2D {
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
* Height and width of the 2D array.
|
|
||||||
*/
|
|
||||||
std::size_t height;
|
std::size_t height;
|
||||||
std::size_t width;
|
std::size_t width;
|
||||||
|
|
||||||
/**
|
|
||||||
* The array containing the data of the 2D array.
|
|
||||||
*/
|
|
||||||
std::vector<T> data;
|
std::vector<T> data;
|
||||||
|
|
||||||
/**
|
Array2D(std::size_t p_height, std::size_t p_width) {
|
||||||
* Build a 2D array given its height and width.
|
height = p_height;
|
||||||
* All the array elements are initialized to default value.
|
width = p_width;
|
||||||
*/
|
data.resize(width * height);
|
||||||
Array2D(std::size_t height, std::size_t width) noexcept
|
}
|
||||||
:
|
|
||||||
height(height), width(width), data(width * height) {}
|
|
||||||
|
|
||||||
/**
|
Array2D(std::size_t p_height, std::size_t p_width, T p_value) {
|
||||||
* Build a 2D array given its height and width.
|
height = p_height;
|
||||||
* All the array elements are initialized to value.
|
width = p_width;
|
||||||
*/
|
data.resize(width * height, value);
|
||||||
Array2D(std::size_t height, std::size_t width, T value) noexcept
|
}
|
||||||
:
|
|
||||||
height(height), width(width), data(width * height, value) {}
|
|
||||||
|
|
||||||
/**
|
const T &get(std::size_t i, std::size_t j) const {
|
||||||
* Return a const reference to the element in the i-th line and j-th column.
|
|
||||||
* i must be lower than height and j lower than width.
|
|
||||||
*/
|
|
||||||
const T &get(std::size_t i, std::size_t j) const noexcept {
|
|
||||||
assert(i < height && j < width);
|
assert(i < height && j < width);
|
||||||
return data[j + i * width];
|
return data[j + i * width];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
T &get(std::size_t i, std::size_t j) {
|
||||||
* Return a reference to the element in the i-th line and j-th column.
|
|
||||||
* i must be lower than height and j lower than width.
|
|
||||||
*/
|
|
||||||
T &get(std::size_t i, std::size_t j) noexcept {
|
|
||||||
assert(i < height && j < width);
|
assert(i < height && j < width);
|
||||||
return data[j + i * width];
|
return data[j + i * width];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
Array2D<T> reflected() const {
|
||||||
* Return the current 2D array reflected along the x axis.
|
|
||||||
*/
|
|
||||||
Array2D<T> reflected() const noexcept {
|
|
||||||
Array2D<T> result = Array2D<T>(width, height);
|
Array2D<T> result = Array2D<T>(width, height);
|
||||||
for (std::size_t y = 0; y < height; y++) {
|
for (std::size_t y = 0; y < height; y++) {
|
||||||
for (std::size_t x = 0; x < width; x++) {
|
for (std::size_t x = 0; x < width; x++) {
|
||||||
@ -69,10 +44,7 @@ public:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
Array2D<T> rotated() const {
|
||||||
* Return the current 2D array rotated 90° anticlockwise
|
|
||||||
*/
|
|
||||||
Array2D<T> rotated() const noexcept {
|
|
||||||
Array2D<T> result = Array2D<T>(width, height);
|
Array2D<T> result = Array2D<T>(width, height);
|
||||||
for (std::size_t y = 0; y < width; y++) {
|
for (std::size_t y = 0; y < width; y++) {
|
||||||
for (std::size_t x = 0; x < height; x++) {
|
for (std::size_t x = 0; x < height; x++) {
|
||||||
@ -82,12 +54,7 @@ public:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
Array2D<T> get_sub_array(std::size_t y, std::size_t x, std::size_t sub_width, std::size_t sub_height) const {
|
||||||
* Return the sub 2D array starting from (y,x) and with size (sub_width,
|
|
||||||
* sub_height). The current 2D array is considered toric for this operation.
|
|
||||||
*/
|
|
||||||
Array2D<T> get_sub_array(std::size_t y, std::size_t x, std::size_t sub_width,
|
|
||||||
std::size_t sub_height) const noexcept {
|
|
||||||
Array2D<T> sub_array_2d = Array2D<T>(sub_width, sub_height);
|
Array2D<T> sub_array_2d = Array2D<T>(sub_width, sub_height);
|
||||||
for (std::size_t ki = 0; ki < sub_height; ki++) {
|
for (std::size_t ki = 0; ki < sub_height; ki++) {
|
||||||
for (std::size_t kj = 0; kj < sub_width; kj++) {
|
for (std::size_t kj = 0; kj < sub_width; kj++) {
|
||||||
@ -97,10 +64,7 @@ public:
|
|||||||
return sub_array_2d;
|
return sub_array_2d;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
bool operator==(const Array2D<T> &a) const {
|
||||||
* Check if two 2D arrays are equals.
|
|
||||||
*/
|
|
||||||
bool operator==(const Array2D<T> &a) const noexcept {
|
|
||||||
if (height != a.height || width != a.width) {
|
if (height != a.height || width != a.width) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -114,14 +78,11 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Hash function.
|
|
||||||
*/
|
|
||||||
namespace std {
|
namespace std {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class hash<Array2D<T>> {
|
class hash<Array2D<T>> {
|
||||||
public:
|
public:
|
||||||
std::size_t operator()(const Array2D<T> &a) const noexcept {
|
std::size_t operator()(const Array2D<T> &a) const {
|
||||||
std::size_t seed = a.data.size();
|
std::size_t seed = a.data.size();
|
||||||
for (const T &i : a.data) {
|
for (const T &i : a.data) {
|
||||||
seed ^= hash<T>()(i) + (std::size_t)0x9e3779b9 + (seed << 6) + (seed >> 2);
|
seed ^= hash<T>()(i) + (std::size_t)0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||||
@ -129,6 +90,6 @@ public:
|
|||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace std
|
} //namespace std
|
||||||
|
|
||||||
#endif // FAST_WFC_UTILS_ARRAY2D_HPP_
|
#endif
|
||||||
|
@ -4,65 +4,39 @@
|
|||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
/**
|
|
||||||
* Represent a 3D array.
|
|
||||||
* The 3D array is stored in a single array, to improve cache usage.
|
|
||||||
*/
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class Array3D {
|
class Array3D {
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
* The dimensions of the 3D array.
|
|
||||||
*/
|
|
||||||
std::size_t height;
|
std::size_t height;
|
||||||
std::size_t width;
|
std::size_t width;
|
||||||
std::size_t depth;
|
std::size_t depth;
|
||||||
|
|
||||||
/**
|
|
||||||
* The array containing the data of the 3D array.
|
|
||||||
*/
|
|
||||||
std::vector<T> data;
|
std::vector<T> data;
|
||||||
|
|
||||||
/**
|
Array3D(std::size_t p_height, std::size_t p_width, std::size_t p_depth) {
|
||||||
* Build a 2D array given its height, width and depth.
|
height = p_height;
|
||||||
* All the arrays elements are initialized to default value.
|
width = p_width;
|
||||||
*/
|
depth = p_depth;
|
||||||
Array3D(std::size_t height, std::size_t width, std::size_t depth) noexcept
|
data.resize(width * height * depth);
|
||||||
:
|
}
|
||||||
height(height), width(width), depth(depth), data(width * height * depth) {}
|
|
||||||
|
|
||||||
/**
|
Array3D(std::size_t height, std::size_t width, std::size_t depth, T value) {
|
||||||
* Build a 2D array given its height, width and depth.
|
height = p_height;
|
||||||
* All the arrays elements are initialized to value
|
width = p_width;
|
||||||
*/
|
depth = p_depth;
|
||||||
Array3D(std::size_t height, std::size_t width, std::size_t depth,
|
data.resize(width * height * depth, value);
|
||||||
T value) noexcept
|
}
|
||||||
:
|
|
||||||
height(height), width(width), depth(depth), data(width * height * depth, value) {}
|
|
||||||
|
|
||||||
/**
|
const T &get(std::size_t i, std::size_t j, std::size_t k) const {
|
||||||
* Return a const reference to the element in the i-th line, j-th column, and
|
|
||||||
* k-th depth. i must be lower than height, j lower than width, and k lower
|
|
||||||
* than depth.
|
|
||||||
*/
|
|
||||||
const T &get(std::size_t i, std::size_t j, std::size_t k) const noexcept {
|
|
||||||
assert(i < height && j < width && k < depth);
|
assert(i < height && j < width && k < depth);
|
||||||
return data[i * width * depth + j * depth + k];
|
return data[i * width * depth + j * depth + k];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
T &get(std::size_t i, std::size_t j, std::size_t k) {
|
||||||
* Return a reference to the element in the i-th line, j-th column, and k-th
|
|
||||||
* depth. i must be lower than height, j lower than width, and k lower than
|
|
||||||
* depth.
|
|
||||||
*/
|
|
||||||
T &get(std::size_t i, std::size_t j, std::size_t k) noexcept {
|
|
||||||
return data[i * width * depth + j * depth + k];
|
return data[i * width * depth + j * depth + k];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
bool operator==(const Array3D &a) const {
|
||||||
* Check if two 3D arrays are equals.
|
|
||||||
*/
|
|
||||||
bool operator==(const Array3D &a) const noexcept {
|
|
||||||
if (height != a.height || width != a.width || depth != a.depth) {
|
if (height != a.height || width != a.width || depth != a.depth) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
constexpr int directions_x[4] = { 0, -1, 1, 0 };
|
constexpr int directions_x[4] = { 0, -1, 1, 0 };
|
||||||
constexpr int directions_y[4] = { -1, 0, 0, 1 };
|
constexpr int directions_y[4] = { -1, 0, 0, 1 };
|
||||||
|
|
||||||
constexpr unsigned get_opposite_direction(unsigned direction) noexcept {
|
constexpr unsigned get_opposite_direction(unsigned direction) {
|
||||||
return 3 - direction;
|
return 3 - direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,9 +8,6 @@
|
|||||||
#include "array_2d.h"
|
#include "array_2d.h"
|
||||||
#include "wfc.h"
|
#include "wfc.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* Options needed to use the overlapping wfc.
|
|
||||||
*/
|
|
||||||
struct OverlappingWFCOptions {
|
struct OverlappingWFCOptions {
|
||||||
bool periodic_input; // True if the input is toric.
|
bool periodic_input; // True if the input is toric.
|
||||||
bool periodic_output; // True if the output is toric.
|
bool periodic_output; // True if the output is toric.
|
||||||
@ -20,59 +17,33 @@ struct OverlappingWFCOptions {
|
|||||||
bool ground; // True if the ground needs to be set (see init_ground).
|
bool ground; // True if the ground needs to be set (see init_ground).
|
||||||
unsigned pattern_size; // The width and height in pixel of the patterns.
|
unsigned pattern_size; // The width and height in pixel of the patterns.
|
||||||
|
|
||||||
/**
|
//Get the wave height given these options.
|
||||||
* Get the wave height given these options.
|
unsigned get_wave_height() const {
|
||||||
*/
|
|
||||||
unsigned get_wave_height() const noexcept {
|
|
||||||
return periodic_output ? out_height : out_height - pattern_size + 1;
|
return periodic_output ? out_height : out_height - pattern_size + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
//Get the wave width given these options.
|
||||||
* Get the wave width given these options.
|
unsigned get_wave_width() const {
|
||||||
*/
|
|
||||||
unsigned get_wave_width() const noexcept {
|
|
||||||
return periodic_output ? out_width : out_width - pattern_size + 1;
|
return periodic_output ? out_width : out_width - pattern_size + 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Class generating a new image with the overlapping WFC algorithm.
|
|
||||||
*/
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class OverlappingWFC {
|
class OverlappingWFC {
|
||||||
private:
|
private:
|
||||||
/**
|
|
||||||
* The input image. T is usually a color.
|
|
||||||
*/
|
|
||||||
Array2D<T> input;
|
Array2D<T> input;
|
||||||
|
|
||||||
/**
|
|
||||||
* Options needed by the algorithm.
|
|
||||||
*/
|
|
||||||
OverlappingWFCOptions options;
|
OverlappingWFCOptions options;
|
||||||
|
|
||||||
/**
|
|
||||||
* The array of the different patterns extracted from the input.
|
|
||||||
*/
|
|
||||||
std::vector<Array2D<T>> patterns;
|
std::vector<Array2D<T>> patterns;
|
||||||
|
|
||||||
/**
|
|
||||||
* The underlying generic WFC algorithm.
|
|
||||||
*/
|
|
||||||
WFC wfc;
|
WFC wfc;
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor initializing the wfc.
|
|
||||||
* This constructor is called by the other constructors.
|
|
||||||
* This is necessary in order to initialize wfc only once.
|
|
||||||
*/
|
|
||||||
OverlappingWFC(
|
OverlappingWFC(
|
||||||
const Array2D<T> &input, const OverlappingWFCOptions &options,
|
const Array2D<T> &input, const OverlappingWFCOptions &options,
|
||||||
const int &seed,
|
const int &seed,
|
||||||
const std::pair<std::vector<Array2D<T>>, std::vector<double>> &patterns,
|
const std::pair<std::vector<Array2D<T>>, std::vector<double>> &patterns,
|
||||||
const std::vector<std::array<std::vector<unsigned>, 4>>
|
const std::vector<std::array<std::vector<unsigned>, 4>> &propagator) :
|
||||||
&propagator) noexcept
|
|
||||||
:
|
|
||||||
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) {
|
||||||
@ -80,58 +51,32 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor used only to call the other constructor with more computed
|
|
||||||
* parameters.
|
|
||||||
*/
|
|
||||||
OverlappingWFC(const Array2D<T> &input, const OverlappingWFCOptions &options,
|
OverlappingWFC(const Array2D<T> &input, const OverlappingWFCOptions &options,
|
||||||
const int &seed,
|
const int &seed,
|
||||||
const std::pair<std::vector<Array2D<T>>, std::vector<double>>
|
const std::pair<std::vector<Array2D<T>>, std::vector<double>>
|
||||||
&patterns) noexcept
|
&patterns) :
|
||||||
:
|
|
||||||
OverlappingWFC(input, options, seed, patterns,
|
OverlappingWFC(input, options, seed, patterns,
|
||||||
generate_compatible(patterns.first)) {}
|
generate_compatible(patterns.first)) {}
|
||||||
|
|
||||||
/**
|
void init_ground(WFC &wfc, const Array2D<T> &input, const std::vector<Array2D<T>> &patterns, const OverlappingWFCOptions &options) {
|
||||||
* Init the ground of the output image.
|
unsigned ground_pattern_id = get_ground_pattern_id(input, patterns, options);
|
||||||
* The lowest middle pattern is used as a floor (and ceiling when the input is
|
|
||||||
* toric) and is placed at the lowest possible pattern position in the output
|
|
||||||
* image, on all its width. The pattern cannot be used at any other place in
|
|
||||||
* the output image.
|
|
||||||
*/
|
|
||||||
void init_ground(WFC &wfc, const Array2D<T> &input,
|
|
||||||
const std::vector<Array2D<T>> &patterns,
|
|
||||||
const OverlappingWFCOptions &options) noexcept {
|
|
||||||
unsigned ground_pattern_id =
|
|
||||||
get_ground_pattern_id(input, patterns, options);
|
|
||||||
|
|
||||||
// Place the pattern in the ground.
|
|
||||||
for (unsigned j = 0; j < options.get_wave_width(); j++) {
|
for (unsigned j = 0; j < options.get_wave_width(); j++) {
|
||||||
set_pattern(ground_pattern_id, options.get_wave_height() - 1, j);
|
set_pattern(ground_pattern_id, options.get_wave_height() - 1, j);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the pattern from the other positions.
|
|
||||||
for (unsigned i = 0; i < options.get_wave_height() - 1; i++) {
|
for (unsigned i = 0; i < options.get_wave_height() - 1; i++) {
|
||||||
for (unsigned j = 0; j < options.get_wave_width(); j++) {
|
for (unsigned j = 0; j < options.get_wave_width(); j++) {
|
||||||
wfc.remove_wave_pattern(i, j, ground_pattern_id);
|
wfc.remove_wave_pattern(i, j, ground_pattern_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Propagate the information with wfc.
|
|
||||||
wfc.propagate();
|
wfc.propagate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static unsigned get_ground_pattern_id(const Array2D<T> &input, const std::vector<Array2D<T>> &patterns, const OverlappingWFCOptions &options) {
|
||||||
* Return the id of the lowest middle pattern.
|
|
||||||
*/
|
|
||||||
static unsigned
|
|
||||||
get_ground_pattern_id(const Array2D<T> &input,
|
|
||||||
const std::vector<Array2D<T>> &patterns,
|
|
||||||
const OverlappingWFCOptions &options) noexcept {
|
|
||||||
// Get the pattern.
|
// Get the pattern.
|
||||||
Array2D<T> ground_pattern =
|
Array2D<T> ground_pattern = input.get_sub_array(input.height - 1, input.width / 2, options.pattern_size, options.pattern_size);
|
||||||
input.get_sub_array(input.height - 1, input.width / 2,
|
|
||||||
options.pattern_size, options.pattern_size);
|
|
||||||
|
|
||||||
// Retrieve the id of the pattern.
|
// Retrieve the id of the pattern.
|
||||||
for (unsigned i = 0; i < patterns.size(); i++) {
|
for (unsigned i = 0; i < patterns.size(); i++) {
|
||||||
@ -145,12 +90,8 @@ private:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
//Return the list of patterns, as well as their probabilities of apparition.
|
||||||
* Return the list of patterns, as well as their probabilities of apparition.
|
static std::pair<std::vector<Array2D<T>>, std::vector<double>> get_patterns(const Array2D<T> &input, const OverlappingWFCOptions &options) {
|
||||||
*/
|
|
||||||
static std::pair<std::vector<Array2D<T>>, std::vector<double>>
|
|
||||||
get_patterns(const Array2D<T> &input,
|
|
||||||
const OverlappingWFCOptions &options) noexcept {
|
|
||||||
std::unordered_map<Array2D<T>, unsigned> patterns_id;
|
std::unordered_map<Array2D<T>, unsigned> patterns_id;
|
||||||
std::vector<Array2D<T>> patterns;
|
std::vector<Array2D<T>> patterns;
|
||||||
|
|
||||||
@ -202,12 +143,8 @@ private:
|
|||||||
return { patterns, patterns_weight };
|
return { patterns, patterns_weight };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
//Return true if the pattern1 is compatible with pattern2 when pattern2 is at a distance (dy,dx) from pattern1.
|
||||||
* Return true if the pattern1 is compatible with pattern2
|
static bool agrees(const Array2D<T> &pattern1, const Array2D<T> &pattern2, int dy, int dx) {
|
||||||
* when pattern2 is at a distance (dy,dx) from pattern1.
|
|
||||||
*/
|
|
||||||
static bool agrees(const Array2D<T> &pattern1, const Array2D<T> &pattern2,
|
|
||||||
int dy, int dx) noexcept {
|
|
||||||
unsigned xmin = dx < 0 ? 0 : dx;
|
unsigned xmin = dx < 0 ? 0 : dx;
|
||||||
unsigned xmax = dx < 0 ? dx + pattern2.width : pattern1.width;
|
unsigned xmax = dx < 0 ? dx + pattern2.width : pattern1.width;
|
||||||
unsigned ymin = dy < 0 ? 0 : dy;
|
unsigned ymin = dy < 0 ? 0 : dy;
|
||||||
@ -225,16 +162,12 @@ private:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Precompute the function agrees(pattern1, pattern2, dy, dx).
|
||||||
* Precompute the function agrees(pattern1, pattern2, dy, dx).
|
// 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 std::vector<std::array<std::vector<unsigned>, 4>> generate_compatible(const std::vector<Array2D<T>> &patterns) {
|
||||||
*/
|
std::vector<std::array<std::vector<unsigned>, 4>> compatible = std::vector<std::array<std::vector<unsigned>, 4>>(patterns.size());
|
||||||
static std::vector<std::array<std::vector<unsigned>, 4>>
|
|
||||||
generate_compatible(const std::vector<Array2D<T>> &patterns) noexcept {
|
|
||||||
std::vector<std::array<std::vector<unsigned>, 4>> compatible =
|
|
||||||
std::vector<std::array<std::vector<unsigned>, 4>>(patterns.size());
|
|
||||||
|
|
||||||
// Iterate on every dy, dx, pattern1 and pattern2
|
// Iterate on every dy, dx, pattern1 and pattern2
|
||||||
for (unsigned pattern1 = 0; pattern1 < patterns.size(); pattern1++) {
|
for (unsigned pattern1 = 0; pattern1 < patterns.size(); pattern1++) {
|
||||||
@ -251,11 +184,8 @@ private:
|
|||||||
return compatible;
|
return compatible;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Transform a 2D array containing the patterns id to a 2D array containing the pixels.
|
||||||
* Transform a 2D array containing the patterns id to a 2D array containing
|
Array2D<T> to_image(const Array2D<unsigned> &output_patterns) const {
|
||||||
* the pixels.
|
|
||||||
*/
|
|
||||||
Array2D<T> to_image(const Array2D<unsigned> &output_patterns) const noexcept {
|
|
||||||
Array2D<T> output = Array2D<T>(options.out_height, options.out_width);
|
Array2D<T> output = Array2D<T>(options.out_height, options.out_width);
|
||||||
|
|
||||||
if (options.periodic_output) {
|
if (options.periodic_output) {
|
||||||
@ -308,11 +238,9 @@ private:
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Set the pattern at a specific position, given its pattern id
|
||||||
* Set the pattern at a specific position, given its pattern id
|
// pattern_id needs to be a valid pattern id, and i and j needs to be in the wave range
|
||||||
* pattern_id needs to be a valid pattern id, and i and j needs to be in the wave range
|
void set_pattern(unsigned pattern_id, unsigned i, unsigned j) {
|
||||||
*/
|
|
||||||
void set_pattern(unsigned pattern_id, unsigned i, unsigned j) noexcept {
|
|
||||||
for (unsigned p = 0; p < patterns.size(); p++) {
|
for (unsigned p = 0; p < patterns.size(); p++) {
|
||||||
if (pattern_id != p) {
|
if (pattern_id != p) {
|
||||||
wfc.remove_wave_pattern(i, j, p);
|
wfc.remove_wave_pattern(i, j, p);
|
||||||
@ -321,20 +249,13 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
OverlappingWFC(const Array2D<T> &input, const OverlappingWFCOptions &options, int seed) :
|
||||||
* The constructor used by the user.
|
|
||||||
*/
|
|
||||||
OverlappingWFC(const Array2D<T> &input, const OverlappingWFCOptions &options,
|
|
||||||
int seed) noexcept
|
|
||||||
:
|
|
||||||
OverlappingWFC(input, options, seed, get_patterns(input, options)) {}
|
OverlappingWFC(input, options, seed, get_patterns(input, options)) {}
|
||||||
|
|
||||||
/**
|
// Set the pattern at a specific position.
|
||||||
* Set the pattern at a specific position.
|
// Returns false if the given pattern does not exist, or if the
|
||||||
* Returns false if the given pattern does not exist, or if the
|
// coordinates are not in the wave
|
||||||
* coordinates are not in the wave
|
bool set_pattern(const Array2D<T> &pattern, unsigned i, unsigned j) {
|
||||||
*/
|
|
||||||
bool set_pattern(const Array2D<T> &pattern, unsigned i, unsigned j) noexcept {
|
|
||||||
auto pattern_id = get_pattern_id(pattern);
|
auto pattern_id = get_pattern_id(pattern);
|
||||||
|
|
||||||
if (pattern_id == std::nullopt || i >= options.get_wave_height() || j >= options.get_wave_width()) {
|
if (pattern_id == std::nullopt || i >= options.get_wave_height() || j >= options.get_wave_width()) {
|
||||||
@ -345,10 +266,8 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Run the WFC algorithm, and return the result if the algorithm succeeded.
|
||||||
* Run the WFC algorithm, and return the result if the algorithm succeeded.
|
std::optional<Array2D<T>> run() {
|
||||||
*/
|
|
||||||
std::optional<Array2D<T>> run() noexcept {
|
|
||||||
std::optional<Array2D<unsigned>> result = wfc.run();
|
std::optional<Array2D<unsigned>> result = wfc.run();
|
||||||
if (result.has_value()) {
|
if (result.has_value()) {
|
||||||
return to_image(*result);
|
return to_image(*result);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include "propagator.h"
|
#include "propagator.h"
|
||||||
#include "wave.h"
|
#include "wave.h"
|
||||||
|
|
||||||
void Propagator::init_compatible() noexcept {
|
void Propagator::init_compatible() {
|
||||||
std::array<int, 4> value;
|
std::array<int, 4> value;
|
||||||
// We compute the number of pattern compatible in all directions.
|
// We compute the number of pattern compatible in all directions.
|
||||||
for (unsigned y = 0; y < wave_height; y++) {
|
for (unsigned y = 0; y < wave_height; y++) {
|
||||||
@ -18,7 +18,7 @@ void Propagator::init_compatible() noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Propagator::propagate(Wave &wave) noexcept {
|
void Propagator::propagate(Wave &wave) {
|
||||||
// We propagate every element while there is element to propagate.
|
// We propagate every element while there is element to propagate.
|
||||||
while (propagating.size() != 0) {
|
while (propagating.size() != 0) {
|
||||||
// The cell and pattern that has been set to false.
|
// The cell and pattern that has been set to false.
|
||||||
|
@ -1,72 +1,45 @@
|
|||||||
#ifndef FAST_WFC_PROPAGATOR_HPP_
|
#ifndef FAST_WFC_PROPAGATOR_HPP_
|
||||||
#define FAST_WFC_PROPAGATOR_HPP_
|
#define FAST_WFC_PROPAGATOR_HPP_
|
||||||
|
|
||||||
#include "direction.h"
|
|
||||||
#include "array_3d.h"
|
#include "array_3d.h"
|
||||||
|
#include "direction.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class Wave;
|
class Wave;
|
||||||
|
|
||||||
/**
|
|
||||||
* Propagate information about patterns in the wave.
|
|
||||||
*/
|
|
||||||
class Propagator {
|
class Propagator {
|
||||||
public:
|
public:
|
||||||
using PropagatorState = std::vector<std::array<std::vector<unsigned>, 4>>;
|
using PropagatorState = std::vector<std::array<std::vector<unsigned>, 4>>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
|
||||||
* The size of the patterns.
|
|
||||||
*/
|
|
||||||
const std::size_t patterns_size;
|
const std::size_t patterns_size;
|
||||||
|
|
||||||
/**
|
|
||||||
* propagator[pattern1][direction] contains all the patterns that can
|
|
||||||
* be placed in next to pattern1 in the direction direction.
|
|
||||||
*/
|
|
||||||
PropagatorState propagator_state;
|
PropagatorState propagator_state;
|
||||||
|
|
||||||
/**
|
|
||||||
* The wave width and height.
|
|
||||||
*/
|
|
||||||
const unsigned wave_width;
|
const unsigned wave_width;
|
||||||
const unsigned wave_height;
|
const unsigned wave_height;
|
||||||
|
|
||||||
/**
|
|
||||||
* True if the wave and the output is toric.
|
|
||||||
*/
|
|
||||||
const bool periodic_output;
|
const bool periodic_output;
|
||||||
|
|
||||||
/**
|
// All the tuples (y, x, pattern) that should be propagated.
|
||||||
* All the tuples (y, x, pattern) that should be propagated.
|
// The tuple should be propagated when wave.get(y, x, pattern) is set to
|
||||||
* The tuple should be propagated when wave.get(y, x, pattern) is set to
|
// false.
|
||||||
* false.
|
|
||||||
*/
|
|
||||||
std::vector<std::tuple<unsigned, unsigned, unsigned>> propagating;
|
std::vector<std::tuple<unsigned, unsigned, unsigned>> propagating;
|
||||||
|
|
||||||
/**
|
// compatible.get(y, x, pattern)[direction] contains the number of patterns
|
||||||
* 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
|
||||||
* 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
|
||||||
* 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
|
||||||
* 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
|
||||||
* compatible.get(y, x, pattern) has every element negative or null
|
|
||||||
*/
|
|
||||||
Array3D<std::array<int, 4>> compatible;
|
Array3D<std::array<int, 4>> compatible;
|
||||||
|
|
||||||
/**
|
void init_compatible();
|
||||||
* Initialize compatible.
|
|
||||||
*/
|
|
||||||
void init_compatible() noexcept;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
* Constructor building the propagator and initializing compatible.
|
|
||||||
*/
|
|
||||||
Propagator(unsigned wave_height, unsigned wave_width, bool periodic_output,
|
Propagator(unsigned wave_height, unsigned wave_width, bool periodic_output,
|
||||||
PropagatorState propagator_state) noexcept
|
PropagatorState propagator_state) :
|
||||||
:
|
|
||||||
patterns_size(propagator_state.size()),
|
patterns_size(propagator_state.size()),
|
||||||
propagator_state(propagator_state),
|
propagator_state(propagator_state),
|
||||||
wave_width(wave_width),
|
wave_width(wave_width),
|
||||||
@ -76,21 +49,14 @@ public:
|
|||||||
init_compatible();
|
init_compatible();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void add_to_propagator(unsigned y, unsigned x, unsigned pattern) {
|
||||||
* Add an element to the propagator.
|
|
||||||
* This function is called when wave.get(y, x, pattern) is set to false.
|
|
||||||
*/
|
|
||||||
void add_to_propagator(unsigned y, unsigned x, unsigned pattern) noexcept {
|
|
||||||
// All the direction are set to 0, since the pattern cannot be set in (y,x).
|
// All the direction are set to 0, since the pattern cannot be set in (y,x).
|
||||||
std::array<int, 4> temp = {};
|
std::array<int, 4> temp = {};
|
||||||
compatible.get(y, x, pattern) = temp;
|
compatible.get(y, x, pattern) = temp;
|
||||||
propagating.emplace_back(y, x, pattern);
|
propagating.emplace_back(y, x, pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void propagate(Wave &wave);
|
||||||
* Propagate the information given with add_to_propagator.
|
|
||||||
*/
|
|
||||||
void propagate(Wave &wave) noexcept;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FAST_WFC_PROPAGATOR_HPP_
|
#endif // FAST_WFC_PROPAGATOR_HPP_
|
||||||
|
@ -7,10 +7,8 @@
|
|||||||
#include "array_2d.h"
|
#include "array_2d.h"
|
||||||
#include "wfc.h"
|
#include "wfc.h"
|
||||||
|
|
||||||
/**
|
// The distinct symmetries of a tile.
|
||||||
* The distinct symmetries of a tile.
|
// It represents how the tile behave when it is rotated or reflected
|
||||||
* It represents how the tile behave when it is rotated or reflected
|
|
||||||
*/
|
|
||||||
enum class Symmetry {
|
enum class Symmetry {
|
||||||
X,
|
X,
|
||||||
T,
|
T,
|
||||||
@ -21,8 +19,8 @@ enum class Symmetry {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the number of possible distinct orientations for a tile.
|
// Return the number of possible distinct orientations for a tile.
|
||||||
* An orientation is a combination of rotations and reflections.
|
// An orientation is a combination of rotations and reflections.
|
||||||
*/
|
*/
|
||||||
constexpr unsigned nb_of_possible_orientations(const Symmetry &symmetry) {
|
constexpr unsigned nb_of_possible_orientations(const Symmetry &symmetry) {
|
||||||
switch (symmetry) {
|
switch (symmetry) {
|
||||||
@ -39,21 +37,16 @@ constexpr unsigned nb_of_possible_orientations(const Symmetry &symmetry) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// A tile that can be placed on the board.
|
||||||
* A tile that can be placed on the board.
|
|
||||||
*/
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct Tile {
|
struct Tile {
|
||||||
std::vector<Array2D<T>> data; // The different orientations of the tile
|
std::vector<Array2D<T>> data; // The different orientations of the tile
|
||||||
Symmetry symmetry; // The symmetry of the tile
|
Symmetry symmetry; // The symmetry of the tile
|
||||||
double weight; // Its weight on the distribution of presence of tiles
|
double weight; // Its weight on the distribution of presence of tiles
|
||||||
|
|
||||||
/**
|
// Generate the map associating an orientation id to the orientation
|
||||||
* Generate the map associating an orientation id to the orientation
|
// id obtained when rotating 90° anticlockwise the tile.
|
||||||
* id obtained when rotating 90° anticlockwise the tile.
|
static std::vector<unsigned> generate_rotation_map(const Symmetry &symmetry) {
|
||||||
*/
|
|
||||||
static std::vector<unsigned>
|
|
||||||
generate_rotation_map(const Symmetry &symmetry) noexcept {
|
|
||||||
switch (symmetry) {
|
switch (symmetry) {
|
||||||
case Symmetry::X:
|
case Symmetry::X:
|
||||||
return { 0 };
|
return { 0 };
|
||||||
@ -69,12 +62,10 @@ struct Tile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Generate the map associating an orientation id to the orientation
|
||||||
* Generate the map associating an orientation id to the orientation
|
// id obtained when reflecting the tile along the x axis.
|
||||||
* id obtained when reflecting the tile along the x axis.
|
|
||||||
*/
|
|
||||||
static std::vector<unsigned>
|
static std::vector<unsigned>
|
||||||
generate_reflection_map(const Symmetry &symmetry) noexcept {
|
generate_reflection_map(const Symmetry &symmetry) {
|
||||||
switch (symmetry) {
|
switch (symmetry) {
|
||||||
case Symmetry::X:
|
case Symmetry::X:
|
||||||
return { 0 };
|
return { 0 };
|
||||||
@ -92,15 +83,12 @@ struct Tile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Generate the map associating an orientation id and an action to the
|
||||||
* Generate the map associating an orientation id and an action to the
|
// resulting orientation id.
|
||||||
* resulting orientation id.
|
// Actions 0, 1, 2, and 3 are 0°, 90°, 180°, and 270° anticlockwise rotations.
|
||||||
* Actions 0, 1, 2, and 3 are 0°, 90°, 180°, and 270° anticlockwise rotations.
|
// Actions 4, 5, 6, and 7 are actions 0, 1, 2, and 3 preceded by a reflection
|
||||||
* Actions 4, 5, 6, and 7 are actions 0, 1, 2, and 3 preceded by a reflection
|
// on the x axis.
|
||||||
* on the x axis.
|
static std::vector<std::vector<unsigned>> generate_action_map(const Symmetry &symmetry) {
|
||||||
*/
|
|
||||||
static std::vector<std::vector<unsigned>>
|
|
||||||
generate_action_map(const Symmetry &symmetry) noexcept {
|
|
||||||
std::vector<unsigned> rotation_map = generate_rotation_map(symmetry);
|
std::vector<unsigned> rotation_map = generate_rotation_map(symmetry);
|
||||||
std::vector<unsigned> reflection_map = generate_reflection_map(symmetry);
|
std::vector<unsigned> reflection_map = generate_reflection_map(symmetry);
|
||||||
size_t size = rotation_map.size();
|
size_t size = rotation_map.size();
|
||||||
@ -126,11 +114,9 @@ struct Tile {
|
|||||||
return action_map;
|
return action_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Generate all distincts rotations of a 2D array given its symmetries;
|
||||||
* Generate all distincts rotations of a 2D array given its symmetries;
|
|
||||||
*/
|
|
||||||
static std::vector<Array2D<T>> generate_oriented(Array2D<T> data,
|
static std::vector<Array2D<T>> generate_oriented(Array2D<T> data,
|
||||||
Symmetry symmetry) noexcept {
|
Symmetry symmetry) {
|
||||||
std::vector<Array2D<T>> oriented;
|
std::vector<Array2D<T>> oriented;
|
||||||
oriented.push_back(data);
|
oriented.push_back(data);
|
||||||
|
|
||||||
@ -161,80 +147,43 @@ struct Tile {
|
|||||||
return oriented;
|
return oriented;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Create a tile with its differents orientations, its symmetries and its
|
||||||
* Create a tile with its differents orientations, its symmetries and its
|
// weight on the distribution of tiles.
|
||||||
* weight on the distribution of tiles.
|
Tile(std::vector<Array2D<T>> data, Symmetry symmetry, double weight) :
|
||||||
*/
|
|
||||||
Tile(std::vector<Array2D<T>> data, Symmetry symmetry, double weight) noexcept
|
|
||||||
:
|
|
||||||
data(data), symmetry(symmetry), weight(weight) {}
|
data(data), symmetry(symmetry), weight(weight) {}
|
||||||
|
|
||||||
/*
|
// Create a tile with its base orientation, its symmetries and its
|
||||||
* Create a tile with its base orientation, its symmetries and its
|
// weight on the distribution of tiles.
|
||||||
* weight on the distribution of tiles.
|
// The other orientations are generated with its first one.
|
||||||
* The other orientations are generated with its first one.
|
Tile(Array2D<T> data, Symmetry symmetry, double weight) :
|
||||||
*/
|
|
||||||
Tile(Array2D<T> data, Symmetry symmetry, double weight) noexcept
|
|
||||||
:
|
|
||||||
data(generate_oriented(data, symmetry)), symmetry(symmetry), weight(weight) {}
|
data(generate_oriented(data, symmetry)), symmetry(symmetry), weight(weight) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Options needed to use the tiling wfc.
|
|
||||||
*/
|
|
||||||
struct TilingWFCOptions {
|
struct TilingWFCOptions {
|
||||||
bool periodic_output;
|
bool periodic_output;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
// Class generating a new image with the tiling WFC algorithm.
|
||||||
* Class generating a new image with the tiling WFC algorithm.
|
|
||||||
*/
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class TilingWFC {
|
class TilingWFC {
|
||||||
private:
|
private:
|
||||||
/**
|
|
||||||
* The distincts tiles.
|
|
||||||
*/
|
|
||||||
std::vector<Tile<T>> tiles;
|
std::vector<Tile<T>> tiles;
|
||||||
|
|
||||||
/**
|
|
||||||
* Map ids of oriented tiles to tile and orientation.
|
|
||||||
*/
|
|
||||||
std::vector<std::pair<unsigned, unsigned>> id_to_oriented_tile;
|
std::vector<std::pair<unsigned, unsigned>> id_to_oriented_tile;
|
||||||
|
|
||||||
/**
|
|
||||||
* Map tile and orientation to oriented tile id.
|
|
||||||
*/
|
|
||||||
std::vector<std::vector<unsigned>> oriented_tile_ids;
|
std::vector<std::vector<unsigned>> oriented_tile_ids;
|
||||||
|
|
||||||
/**
|
|
||||||
* Otions needed to use the tiling wfc.
|
|
||||||
*/
|
|
||||||
TilingWFCOptions options;
|
TilingWFCOptions options;
|
||||||
|
|
||||||
/**
|
|
||||||
* The underlying generic WFC algorithm.
|
|
||||||
*/
|
|
||||||
WFC wfc;
|
WFC wfc;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
* The number of vertical tiles
|
|
||||||
*/
|
|
||||||
unsigned height;
|
unsigned height;
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of horizontal tiles
|
|
||||||
*/
|
|
||||||
unsigned width;
|
unsigned width;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
// Generate mapping from id to oriented tiles and vice versa.
|
||||||
* Generate mapping from id to oriented tiles and vice versa.
|
|
||||||
*/
|
|
||||||
static std::pair<std::vector<std::pair<unsigned, unsigned>>,
|
static std::pair<std::vector<std::pair<unsigned, unsigned>>,
|
||||||
std::vector<std::vector<unsigned>>>
|
std::vector<std::vector<unsigned>>>
|
||||||
generate_oriented_tile_ids(const std::vector<Tile<T>> &tiles) noexcept {
|
generate_oriented_tile_ids(const std::vector<Tile<T>> &tiles) {
|
||||||
std::vector<std::pair<unsigned, unsigned>> id_to_oriented_tile;
|
std::vector<std::pair<unsigned, unsigned>> id_to_oriented_tile;
|
||||||
std::vector<std::vector<unsigned>> oriented_tile_ids;
|
std::vector<std::vector<unsigned>> oriented_tile_ids;
|
||||||
|
|
||||||
@ -251,9 +200,7 @@ private:
|
|||||||
return { id_to_oriented_tile, oriented_tile_ids };
|
return { id_to_oriented_tile, oriented_tile_ids };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Generate the propagator which will be used in the wfc algorithm.
|
||||||
* Generate the propagator which will be used in the wfc algorithm.
|
|
||||||
*/
|
|
||||||
static std::vector<std::array<std::vector<unsigned>, 4>> generate_propagator(
|
static std::vector<std::array<std::vector<unsigned>, 4>> generate_propagator(
|
||||||
const std::vector<std::tuple<unsigned, unsigned, unsigned, unsigned>>
|
const std::vector<std::tuple<unsigned, unsigned, unsigned, unsigned>>
|
||||||
&neighbors,
|
&neighbors,
|
||||||
@ -313,9 +260,7 @@ private:
|
|||||||
return propagator;
|
return propagator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Get probability of presence of tiles.
|
||||||
* Get probability of presence of tiles.
|
|
||||||
*/
|
|
||||||
static std::vector<double>
|
static std::vector<double>
|
||||||
get_tiles_weights(const std::vector<Tile<T>> &tiles) {
|
get_tiles_weights(const std::vector<Tile<T>> &tiles) {
|
||||||
std::vector<double> frequencies;
|
std::vector<double> frequencies;
|
||||||
@ -327,9 +272,7 @@ private:
|
|||||||
return frequencies;
|
return frequencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Translate the generic WFC result into the image result
|
||||||
* Translate the generic WFC result into the image result
|
|
||||||
*/
|
|
||||||
Array2D<T> id_to_tiling(Array2D<unsigned> ids) {
|
Array2D<T> id_to_tiling(Array2D<unsigned> ids) {
|
||||||
unsigned size = tiles[0].data[0].height;
|
unsigned size = tiles[0].data[0].height;
|
||||||
Array2D<T> tiling(size * ids.height, size * ids.width);
|
Array2D<T> tiling(size * ids.height, size * ids.width);
|
||||||
@ -348,7 +291,7 @@ private:
|
|||||||
return tiling;
|
return tiling;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_tile(unsigned tile_id, unsigned i, unsigned j) noexcept {
|
void set_tile(unsigned tile_id, unsigned i, unsigned j) {
|
||||||
for (unsigned p = 0; p < id_to_oriented_tile.size(); p++) {
|
for (unsigned p = 0; p < id_to_oriented_tile.size(); p++) {
|
||||||
if (tile_id != p) {
|
if (tile_id != p) {
|
||||||
wfc.remove_wave_pattern(i, j, p);
|
wfc.remove_wave_pattern(i, j, p);
|
||||||
@ -357,9 +300,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
// Construct the TilingWFC class to generate a tiled image.
|
||||||
* Construct the TilingWFC class to generate a tiled image.
|
|
||||||
*/
|
|
||||||
TilingWFC(
|
TilingWFC(
|
||||||
const std::vector<Tile<T>> &tiles,
|
const std::vector<Tile<T>> &tiles,
|
||||||
const std::vector<std::tuple<unsigned, unsigned, unsigned, unsigned>>
|
const std::vector<std::tuple<unsigned, unsigned, unsigned, unsigned>>
|
||||||
@ -377,12 +318,10 @@ public:
|
|||||||
height(height),
|
height(height),
|
||||||
width(width) {}
|
width(width) {}
|
||||||
|
|
||||||
/**
|
// Set the tile at a specific position.
|
||||||
* Set the tile at a specific position.
|
// Returns false if the given tile and orientation does not exist,
|
||||||
* Returns false if the given tile and orientation does not exist,
|
// or if the coordinates are not in the wave
|
||||||
* or if the coordinates are not in the wave
|
bool set_tile(unsigned tile_id, unsigned orientation, unsigned i, unsigned j) {
|
||||||
*/
|
|
||||||
bool set_tile(unsigned tile_id, unsigned orientation, unsigned i, unsigned j) noexcept {
|
|
||||||
if (tile_id >= oriented_tile_ids.size() || orientation >= oriented_tile_ids[tile_id].size() || i >= height || j >= width) {
|
if (tile_id >= oriented_tile_ids.size() || orientation >= oriented_tile_ids[tile_id].size() || i >= height || j >= width) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -392,9 +331,7 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Run the tiling wfc and return the result if the algorithm succeeded
|
||||||
* Run the tiling wfc and return the result if the algorithm succeeded
|
|
||||||
*/
|
|
||||||
std::optional<Array2D<T>> run() {
|
std::optional<Array2D<T>> run() {
|
||||||
auto a = wfc.run();
|
auto a = wfc.run();
|
||||||
if (a == std::nullopt) {
|
if (a == std::nullopt) {
|
||||||
|
@ -4,11 +4,9 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
/**
|
// Return distribution * log(distribution).
|
||||||
* Return distribution * log(distribution).
|
|
||||||
*/
|
|
||||||
std::vector<double>
|
std::vector<double>
|
||||||
get_plogp(const std::vector<double> &distribution) noexcept {
|
get_plogp(const std::vector<double> &distribution) {
|
||||||
std::vector<double> plogp;
|
std::vector<double> plogp;
|
||||||
for (unsigned i = 0; i < distribution.size(); i++) {
|
for (unsigned i = 0; i < distribution.size(); i++) {
|
||||||
plogp.push_back(distribution[i] * log(distribution[i]));
|
plogp.push_back(distribution[i] * log(distribution[i]));
|
||||||
@ -16,10 +14,8 @@ get_plogp(const std::vector<double> &distribution) noexcept {
|
|||||||
return plogp;
|
return plogp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Return min(v) / 2.
|
||||||
* Return min(v) / 2.
|
double get_min_abs_half(const std::vector<double> &v) {
|
||||||
*/
|
|
||||||
double get_min_abs_half(const std::vector<double> &v) noexcept {
|
|
||||||
double min_abs_half = std::numeric_limits<double>::infinity();
|
double min_abs_half = std::numeric_limits<double>::infinity();
|
||||||
for (unsigned i = 0; i < v.size(); i++) {
|
for (unsigned i = 0; i < v.size(); i++) {
|
||||||
min_abs_half = std::min(min_abs_half, std::abs(v[i] / 2.0));
|
min_abs_half = std::min(min_abs_half, std::abs(v[i] / 2.0));
|
||||||
@ -30,8 +26,7 @@ double get_min_abs_half(const std::vector<double> &v) noexcept {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Wave::Wave(unsigned height, unsigned width,
|
Wave::Wave(unsigned height, unsigned width,
|
||||||
const std::vector<double> &patterns_frequencies) noexcept
|
const std::vector<double> &patterns_frequencies) :
|
||||||
:
|
|
||||||
patterns_frequencies(patterns_frequencies),
|
patterns_frequencies(patterns_frequencies),
|
||||||
plogp_patterns_frequencies(get_plogp(patterns_frequencies)),
|
plogp_patterns_frequencies(get_plogp(patterns_frequencies)),
|
||||||
min_abs_half_plogp(get_min_abs_half(plogp_patterns_frequencies)),
|
min_abs_half_plogp(get_min_abs_half(plogp_patterns_frequencies)),
|
||||||
@ -58,7 +53,7 @@ Wave::Wave(unsigned height, unsigned width,
|
|||||||
memoisation.entropy = std::vector<double>(width * height, entropy_base);
|
memoisation.entropy = std::vector<double>(width * height, entropy_base);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wave::set(unsigned index, unsigned pattern, bool value) noexcept {
|
void Wave::set(unsigned index, unsigned pattern, bool value) {
|
||||||
bool old_value = data.get(index, pattern);
|
bool old_value = data.get(index, pattern);
|
||||||
// If the value isn't changed, nothing needs to be done.
|
// If the value isn't changed, nothing needs to be done.
|
||||||
if (old_value == value) {
|
if (old_value == value) {
|
||||||
@ -80,7 +75,7 @@ void Wave::set(unsigned index, unsigned pattern, bool value) noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Wave::get_min_entropy(std::minstd_rand &gen) const noexcept {
|
int Wave::get_min_entropy(std::minstd_rand &gen) const {
|
||||||
if (is_impossible) {
|
if (is_impossible) {
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
@ -5,110 +5,75 @@
|
|||||||
#include <random>
|
#include <random>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
/**
|
// Struct containing the values needed to compute the entropy of all the cells.
|
||||||
* Struct containing the values needed to compute the entropy of all the cells.
|
// This struct is updated every time the wave is changed.
|
||||||
* This struct is updated every time the wave is changed.
|
// p'(pattern) is equal to patterns_frequencies[pattern] if wave.get(cell,
|
||||||
* p'(pattern) is equal to patterns_frequencies[pattern] if wave.get(cell,
|
// pattern) is set to true, otherwise 0.
|
||||||
* pattern) is set to true, otherwise 0.
|
|
||||||
*/
|
|
||||||
struct EntropyMemoisation {
|
struct EntropyMemoisation {
|
||||||
std::vector<double> plogp_sum; // The sum of p'(pattern) * log(p'(pattern)).
|
std::vector<double> plogp_sum; // The sum of p'(pattern)// log(p'(pattern)).
|
||||||
std::vector<double> sum; // The sum of p'(pattern).
|
std::vector<double> sum; // The sum of p'(pattern).
|
||||||
std::vector<double> log_sum; // The log of sum.
|
std::vector<double> log_sum; // The log of sum.
|
||||||
std::vector<unsigned> nb_patterns; // The number of patterns present
|
std::vector<unsigned> nb_patterns; // The number of patterns present
|
||||||
std::vector<double> entropy; // The entropy of the cell.
|
std::vector<double> entropy; // The entropy of the cell.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
// Contains the pattern possibilities in every cell.
|
||||||
* Contains the pattern possibilities in every cell.
|
// Also contains information about cell entropy.
|
||||||
* Also contains information about cell entropy.
|
|
||||||
*/
|
|
||||||
class Wave {
|
class Wave {
|
||||||
private:
|
private:
|
||||||
/**
|
// The patterns frequencies p given to wfc.
|
||||||
* The patterns frequencies p given to wfc.
|
|
||||||
*/
|
|
||||||
const std::vector<double> patterns_frequencies;
|
const std::vector<double> patterns_frequencies;
|
||||||
|
|
||||||
/**
|
// The precomputation of p * log(p).
|
||||||
* The precomputation of p * log(p).
|
|
||||||
*/
|
|
||||||
const std::vector<double> plogp_patterns_frequencies;
|
const std::vector<double> plogp_patterns_frequencies;
|
||||||
|
|
||||||
/**
|
// The precomputation of min (p * log(p)) / 2.
|
||||||
* The precomputation of min (p * log(p)) / 2.
|
// This is used to define the maximum value of the noise.
|
||||||
* This is used to define the maximum value of the noise.
|
|
||||||
*/
|
|
||||||
const double min_abs_half_plogp;
|
const double min_abs_half_plogp;
|
||||||
|
|
||||||
/**
|
|
||||||
* The memoisation of important values for the computation of entropy.
|
|
||||||
*/
|
|
||||||
EntropyMemoisation memoisation;
|
EntropyMemoisation memoisation;
|
||||||
|
|
||||||
/**
|
// This value is set to true if there is a contradiction in the wave (all elements set to false in a 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;
|
bool is_impossible;
|
||||||
|
|
||||||
/**
|
// The number of distinct patterns.
|
||||||
* The number of distinct patterns.
|
|
||||||
*/
|
|
||||||
const size_t nb_patterns;
|
const size_t nb_patterns;
|
||||||
|
|
||||||
/**
|
// The actual wave. data.get(index, pattern) is equal to 0 if the pattern can
|
||||||
* The actual wave. data.get(index, pattern) is equal to 0 if the pattern can
|
// be placed in the cell index.
|
||||||
* be placed in the cell index.
|
|
||||||
*/
|
|
||||||
Array2D<uint8_t> data;
|
Array2D<uint8_t> data;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
// The size of the wave.
|
||||||
* The size of the wave.
|
|
||||||
*/
|
|
||||||
const unsigned width;
|
const unsigned width;
|
||||||
const unsigned height;
|
const unsigned height;
|
||||||
const unsigned size;
|
const unsigned size;
|
||||||
|
|
||||||
/**
|
// Initialize the wave with every cell being able to have every pattern.
|
||||||
* Initialize the wave with every cell being able to have every pattern.
|
Wave(unsigned height, unsigned width, const std::vector<double> &patterns_frequencies);
|
||||||
*/
|
|
||||||
Wave(unsigned height, unsigned width,
|
|
||||||
const std::vector<double> &patterns_frequencies) noexcept;
|
|
||||||
|
|
||||||
/**
|
// Return true if pattern can be placed in cell index.
|
||||||
* Return true if pattern can be placed in cell index.
|
bool get(unsigned index, unsigned pattern) const {
|
||||||
*/
|
|
||||||
bool get(unsigned index, unsigned pattern) const noexcept {
|
|
||||||
return data.get(index, pattern);
|
return data.get(index, pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Return true if pattern can be placed in cell (i,j)
|
||||||
* Return true if pattern can be placed in cell (i,j)
|
bool get(unsigned i, unsigned j, unsigned pattern) const {
|
||||||
*/
|
|
||||||
bool get(unsigned i, unsigned j, unsigned pattern) const noexcept {
|
|
||||||
return get(i * width + j, pattern);
|
return get(i * width + j, pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Set the value of pattern in cell index.
|
||||||
* Set the value of pattern in cell index.
|
void set(unsigned index, unsigned pattern, bool value);
|
||||||
*/
|
|
||||||
void set(unsigned index, unsigned pattern, bool value) noexcept;
|
|
||||||
|
|
||||||
/**
|
// Set the value of pattern in cell (i,j).
|
||||||
* Set the value of pattern in cell (i,j).
|
void set(unsigned i, unsigned j, unsigned pattern, bool value) {
|
||||||
*/
|
|
||||||
void set(unsigned i, unsigned j, unsigned pattern, bool value) noexcept {
|
|
||||||
set(i * width + j, pattern, value);
|
set(i * width + j, pattern, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Return the index of the cell with lowest entropy different of 0.
|
||||||
* Return the index of the cell with lowest entropy different of 0.
|
// If there is a contradiction in the wave, return -2.
|
||||||
* If there is a contradiction in the wave, return -2.
|
// If every cell is decided, return -1.
|
||||||
* If every cell is decided, return -1.
|
int get_min_entropy(std::minstd_rand &gen) const;
|
||||||
*/
|
|
||||||
int get_min_entropy(std::minstd_rand &gen) const noexcept;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FAST_WFC_WAVE_HPP_
|
#endif // FAST_WFC_WAVE_HPP_
|
||||||
|
@ -2,9 +2,7 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
std::vector<double> &normalize(std::vector<double> &v) {
|
std::vector<double> &normalize(std::vector<double> &v) {
|
||||||
double sum_weights = 0.0;
|
double sum_weights = 0.0;
|
||||||
for (double weight : v) {
|
for (double weight : v) {
|
||||||
@ -20,7 +18,7 @@ std::vector<double> &normalize(std::vector<double> &v) {
|
|||||||
}
|
}
|
||||||
} //namespace
|
} //namespace
|
||||||
|
|
||||||
Array2D<unsigned> WFC::wave_to_output() const noexcept {
|
Array2D<unsigned> WFC::wave_to_output() const {
|
||||||
Array2D<unsigned> output_patterns(wave.height, wave.width);
|
Array2D<unsigned> output_patterns(wave.height, wave.width);
|
||||||
for (unsigned i = 0; i < wave.size; i++) {
|
for (unsigned i = 0; i < wave.size; i++) {
|
||||||
for (unsigned k = 0; k < nb_patterns; k++) {
|
for (unsigned k = 0; k < nb_patterns; k++) {
|
||||||
@ -35,11 +33,10 @@ Array2D<unsigned> WFC::wave_to_output() const noexcept {
|
|||||||
WFC::WFC(bool periodic_output, int seed,
|
WFC::WFC(bool periodic_output, int seed,
|
||||||
std::vector<double> patterns_frequencies,
|
std::vector<double> patterns_frequencies,
|
||||||
Propagator::PropagatorState propagator, unsigned wave_height,
|
Propagator::PropagatorState propagator, unsigned wave_height,
|
||||||
unsigned wave_width) noexcept
|
unsigned wave_width) :
|
||||||
:
|
|
||||||
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) {}
|
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) {}
|
||||||
|
|
||||||
std::optional<Array2D<unsigned>> WFC::run() noexcept {
|
std::optional<Array2D<unsigned>> WFC::run() {
|
||||||
while (true) {
|
while (true) {
|
||||||
// Define the value of an undefined cell.
|
// Define the value of an undefined cell.
|
||||||
ObserveStatus result = observe();
|
ObserveStatus result = observe();
|
||||||
@ -56,7 +53,7 @@ std::optional<Array2D<unsigned>> WFC::run() noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WFC::ObserveStatus WFC::observe() noexcept {
|
WFC::ObserveStatus WFC::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(gen);
|
||||||
|
|
||||||
|
@ -4,84 +4,56 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
#include "propagator.h"
|
|
||||||
#include "array_2d.h"
|
#include "array_2d.h"
|
||||||
|
#include "propagator.h"
|
||||||
#include "wave.h"
|
#include "wave.h"
|
||||||
|
|
||||||
/**
|
// Class containing the generic WFC algorithm.
|
||||||
* Class containing the generic WFC algorithm.
|
|
||||||
*/
|
|
||||||
class WFC {
|
class WFC {
|
||||||
private:
|
private:
|
||||||
/**
|
// The random number generator.
|
||||||
* The random number generator.
|
|
||||||
*/
|
|
||||||
std::minstd_rand gen;
|
std::minstd_rand gen;
|
||||||
|
|
||||||
/**
|
// The distribution of the patterns as given in input.
|
||||||
* The distribution of the patterns as given in input.
|
|
||||||
*/
|
|
||||||
const std::vector<double> patterns_frequencies;
|
const std::vector<double> patterns_frequencies;
|
||||||
|
|
||||||
/**
|
|
||||||
* The wave, indicating which patterns can be put in which cell.
|
|
||||||
*/
|
|
||||||
Wave wave;
|
Wave wave;
|
||||||
|
|
||||||
/**
|
// The number of distinct patterns.
|
||||||
* The number of distinct patterns.
|
|
||||||
*/
|
|
||||||
const size_t nb_patterns;
|
const size_t nb_patterns;
|
||||||
|
|
||||||
/**
|
// The propagator, used to propagate the information in the wave.
|
||||||
* The propagator, used to propagate the information in the wave.
|
|
||||||
*/
|
|
||||||
Propagator propagator;
|
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<unsigned> wave_to_output() const;
|
||||||
*/
|
|
||||||
Array2D<unsigned> wave_to_output() const noexcept;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
// Basic constructor initializing the algorithm.
|
||||||
* Basic constructor initializing the algorithm.
|
|
||||||
*/
|
|
||||||
WFC(bool periodic_output, int seed, std::vector<double> patterns_frequencies,
|
WFC(bool periodic_output, int seed, std::vector<double> patterns_frequencies,
|
||||||
Propagator::PropagatorState propagator, unsigned wave_height,
|
Propagator::PropagatorState propagator, unsigned wave_height,
|
||||||
unsigned wave_width)
|
unsigned wave_width);
|
||||||
noexcept;
|
|
||||||
|
|
||||||
/**
|
// Run the algorithm, and return a result if it succeeded.
|
||||||
* Run the algorithm, and return a result if it succeeded.
|
std::optional<Array2D<unsigned>> run();
|
||||||
*/
|
|
||||||
std::optional<Array2D<unsigned>> run() noexcept;
|
|
||||||
|
|
||||||
/**
|
// Return value of observe.
|
||||||
* Return value of observe.
|
|
||||||
*/
|
|
||||||
enum ObserveStatus {
|
enum ObserveStatus {
|
||||||
success, // WFC has finished and has succeeded.
|
success, // WFC has finished and has succeeded.
|
||||||
failure, // WFC has finished and failed.
|
failure, // WFC has finished and failed.
|
||||||
to_continue // WFC isn't finished.
|
to_continue // WFC isn't finished.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
// Define the value of the cell with lowest entropy.
|
||||||
* Define the value of the cell with lowest entropy.
|
ObserveStatus observe();
|
||||||
*/
|
|
||||||
ObserveStatus observe() noexcept;
|
|
||||||
|
|
||||||
/**
|
// Propagate the information of the wave.
|
||||||
* Propagate the information of the wave.
|
void propagate() { propagator.propagate(wave); }
|
||||||
*/
|
|
||||||
void propagate() noexcept { propagator.propagate(wave); }
|
|
||||||
|
|
||||||
/**
|
// Remove pattern from cell (i,j).
|
||||||
* Remove pattern from cell (i,j).
|
void remove_wave_pattern(unsigned i, unsigned j, unsigned pattern) {
|
||||||
*/
|
|
||||||
void remove_wave_pattern(unsigned i, unsigned j, unsigned pattern) noexcept {
|
|
||||||
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);
|
propagator.add_to_propagator(i, j, pattern);
|
||||||
|
Loading…
Reference in New Issue
Block a user