pandemonium_engine/modules/wfc/wave_form_collapse.h

240 lines
7.5 KiB
C++

#ifndef WAVE_FORM_COLLAPSE_H
#define WAVE_FORM_COLLAPSE_H
/*************************************************************************/
/* wave_form_collapse.h */
/*************************************************************************/
/* This file is part of: */
/* PANDEMONIUM ENGINE */
/* https://github.com/Relintai/pandemonium_engine */
/*************************************************************************/
/* Copyright (c) 2022-present Péter Magyar. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "core/int_types.h"
#include "core/math/random_pcg.h"
#include "array_2d.h"
#include "array_3d.h"
#include "core/containers/vector.h"
#include "core/object/reference.h"
class WaveFormCollapse : public Reference {
GDCLASS(WaveFormCollapse, Reference);
public:
enum Symmetry {
SYMMETRY_X = 0,
SYMMETRY_T,
SYMMETRY_I,
SYMMETRY_L,
SYMMETRY_BACKSLASH,
SYMMETRY_P
};
enum ObserveStatus {
OBSERVE_STATUS_SUCCESS = 0,
OBSERVE_STATUS_FAILURE,
OBSERVE_STATUS_TO_CONTINUE
};
struct PropagatorStateEntry {
Vector<int> directions[4];
};
struct PropagatingEntry {
int data[3];
PropagatingEntry() {
for (int i = 0; i < 3; ++i) {
data[i] = 0;
}
}
PropagatingEntry(int x, int y, int 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;
}
}
};
static const int DIRECTIONS_X[4];
static const int DIRECTIONS_Y[4];
public:
int get_wave_width() const;
void set_wave_width(const int val);
int get_wave_height() const;
void set_wave_height(const int val);
bool get_periodic_output() const;
void set_periodic_output(const bool val);
void set_seed(const int seed);
void set_wave_size(int p_width, int p_height);
void init_wave();
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);
virtual void set_input(const PoolIntArray &p_data, int p_width, int p_height);
virtual Array2D<int> run();
PoolIntArray generate_image_index_data();
ObserveStatus observe();
void remove_wave_pattern(int i, int j, int pattern) {
if (wave_get(i, j, pattern)) {
wave_set(i, j, pattern, false);
add_to_propagator(i, j, pattern);
}
}
// Return true if pattern can be placed in cell index.
bool wave_get(int index, int pattern) const {
return _wave_data.get(index, pattern);
}
// Return true if pattern can be placed in cell (i,j)
bool wave_get(int i, int j, int pattern) const {
return wave_get(i * _wave_width + j, pattern);
}
// Set the value of pattern in cell index.
void wave_set(int index, int pattern, bool value);
// Set the value of pattern in cell (i,j).
void wave_set(int i, int j, int 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(int y, int x, int 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));
}
constexpr int get_opposite_direction(int direction) {
return 3 - direction;
}
void normalize(Vector<double> &v);
Vector<double> get_plogp(const Vector<double> &distribution);
double get_min_abs_half(const Vector<double> &v);
void propagate();
virtual void initialize();
WaveFormCollapse();
~WaveFormCollapse();
protected:
static void _bind_methods();
Array2D<int> _input;
bool _periodic_output;
//Wave
int _wave_width;
int _wave_height;
int _wave_size;
private:
RandomPCG _gen;
// The number of distinct patterns.
size_t _nb_patterns;
// 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
// are defined.
Array2D<int> wave_to_output() const;
// 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<int> _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. wave_data.get(index, pattern) is equal to false if the pattern can be placed in the cell index.
Array2D<bool> _wave_data;
//Propagator
Vector<PropagatorStateEntry> _propagator_state;
// 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();
};
VARIANT_ENUM_CAST(WaveFormCollapse::Symmetry);
#endif