diff --git a/.gitignore b/.gitignore index 40b3a75..bad5b79 100644 --- a/.gitignore +++ b/.gitignore @@ -40,7 +40,7 @@ MANIFEST .vscode # Files saved -saves/ +saves/* # PyInstaller # Usually these files are written by a python script from a template diff --git a/C/Makefile b/C/Makefile index f6aa5c2..9a46b8e 100644 --- a/C/Makefile +++ b/C/Makefile @@ -47,8 +47,10 @@ $(COMP)%.o : $(SRC)%.c $(CC) -c $< -o $@ +# Main -------------------------------------- - +main: $(COMP)main.o $(COMP)mapGenerator.o $(COMP)map.o $(COMP)chunk.o $(COMP)layer.o $(COMP)gradientGrid.o $(COMP)loadingBar.o $(COMP)interpreter.o + $(CC) $^ -o $(BIN)$@ $(LFLAGS) # Tests ------------------------------------- @@ -58,22 +60,25 @@ test_unicode: $(COMP)test_unicode.o test_loadingBar: $(COMP)test_loadingBar.o $(COMP)loadingBar.o $(CC) $^ -o $(BIN)$@ -test_gradientGrid: $(COMP)test_gradientGrid.o $(COMP)gradientGrid.o $(COMP)loadingBar.o +test_gradientGrid: $(COMP)test_gradientGrid.o $(COMP)gradientGrid.o $(COMP)loadingBar.o $(COMP)interpreter.o $(CC) $^ -o $(BIN)$@ $(LFLAGS) -test_layer: $(COMP)test_layer.o $(COMP)layer.o $(COMP)gradientGrid.o $(COMP)loadingBar.o +test_layer: $(COMP)test_layer.o $(COMP)layer.o $(COMP)gradientGrid.o $(COMP)loadingBar.o $(COMP)interpreter.o $(CC) $^ -o $(BIN)$@ $(LFLAGS) -test_chunk: $(COMP)test_chunk.o $(COMP)chunk.o $(COMP)layer.o $(COMP)gradientGrid.o $(COMP)loadingBar.o +test_chunk: $(COMP)test_chunk.o $(COMP)chunk.o $(COMP)layer.o $(COMP)gradientGrid.o $(COMP)loadingBar.o $(COMP)interpreter.o $(CC) $^ -o $(BIN)$@ $(LFLAGS) -test_map: $(COMP)test_map.o $(COMP)map.o $(COMP)chunk.o $(COMP)layer.o $(COMP)gradientGrid.o $(COMP)loadingBar.o +test_map: $(COMP)test_map.o $(COMP)map.o $(COMP)chunk.o $(COMP)layer.o $(COMP)gradientGrid.o $(COMP)loadingBar.o $(COMP)interpreter.o $(CC) $^ -o $(BIN)$@ $(LFLAGS) -test_mapGenerator: $(COMP)test_mapGenerator.o $(COMP)mapGenerator.o $(COMP)map.o $(COMP)chunk.o $(COMP)layer.o $(COMP)gradientGrid.o $(COMP)loadingBar.o +test_mapGenerator: $(COMP)test_mapGenerator.o $(COMP)mapGenerator.o $(COMP)map.o $(COMP)chunk.o $(COMP)layer.o $(COMP)gradientGrid.o $(COMP)loadingBar.o $(COMP)interpreter.o + $(CC) $^ -o $(BIN)$@ $(LFLAGS) + +test_interpreter: $(COMP)test_interpreter.o $(COMP)interpreter.o $(COMP)mapGenerator.o $(COMP)map.o $(COMP)chunk.o $(COMP)layer.o $(COMP)gradientGrid.o $(COMP)loadingBar.o $(CC) $^ -o $(BIN)$@ $(LFLAGS) -test_all : test_unicode test_loadingBar test_gradientGrid test_layer test_chunk test_map test_mapGenerator +test_all : test_unicode test_loadingBar test_gradientGrid test_layer test_chunk test_map test_mapGenerator test_interpreter # Valgrind ---------------------------------- diff --git a/C/headers/chunk.h b/C/headers/chunk.h index c899e8c..e29d9db 100644 --- a/C/headers/chunk.h +++ b/C/headers/chunk.h @@ -2,8 +2,8 @@ * @file chunk.h * @author Zyno and BlueNZ * @brief Header to chunk structure and functions - * @version 0.2 - * @date 2024-06-19 + * @version 0.3 + * @date 2024-07-29 * */ @@ -100,6 +100,13 @@ chunk* initChunk(int width, int height, int number_of_layers, double layers_fact +/** + * @brief Generates a new base altitude to be set to a chunk. + * + * @return double : the base_altitude value. + */ +double generateBaseAltitude(); + /** * @brief Regenerates the given chunk final altitude values from its layers and layers factors. * @@ -107,6 +114,8 @@ chunk* initChunk(int width, int height, int number_of_layers, double layers_fact * @param display_loading (unsigned int) : the given value defines the behaviour. * * If `0` the loading bars won't be printed. * * If `> 0` the loading bars will be printed with a number of indent equal to `display_loading - 1`. + * + * @note each layer will see its values free'd at this point. They can still be regenerated if needed using the dedicated function. */ void regenerateChunk(chunk* chunk, unsigned int display_loading); @@ -174,26 +183,17 @@ chunk* newChunkFromGradients(int width, int height, int number_of_layers, gradie chunk* newChunk(int number_of_layers, int gradGrids_width[number_of_layers], int gradGrids_height[number_of_layers], int size_factors[number_of_layers], double layers_factors[number_of_layers], unsigned int display_loading); -//TODO ? signature could be changed to avoid passing useless parameters -> `chunk_width` and `chunk_height` instead of `gradGrids_width`, `gradGrids_height` and `size_factors` /** * @brief Generates a new virtual chunk structure with the given parameters. It does not possess any altitude values and is used as a way to store * the data of boundary conditions with its `base_altitude` parameter. * - * @param number_of_layers (int) : the number of layers passed. - * @param gradGrids_width (int[number_of_layers]) : the array of gradientGrid width to compute the final width this virtual chunk should have. - * @param gradGrids_height (int[number_of_layers]) : the array of gradientGrid height to compute the final height this virtual chunk should have. - * @param size_factors (int[number_of_layers]) : the array of size factors to compute the final dimensions of this virtual chunk. - * @param layers_factors (double[number_of_layers]) : the array of layers factors to be stored in the virtual chunk. - * @return chunk* : the pointer to the newly generated virtual chunk structure. + * @param chunk_width (int) : the width size of the virtuak chunk if it had altitude values. + * @param chunk_height (int) : the height size of the virtuak chunk if it had altitude values. + * @param regenerate (bool) : should the base altitude value be generated. * - * @warning The `size_factors` array should match the `gradGrids_width` and `gradGrids_height` arrays such that - * `(gradGrid_dimensions - 1) * size_factor = constant`. In case it would not be the case, the chunk structure will still - * be generated but its dimensions will be wrong : it will simply use the first value. - * - * @note The arrays does not need to be dynamically allocated and their content will be copied in the structure. + * @return chunk* : the pointer to the newly generated virtual chunk structure. */ -chunk* newVirtualChunk(int number_of_layers, int gradGrids_width[number_of_layers], int gradGrids_height[number_of_layers], int size_factors[number_of_layers], - double layers_factors[number_of_layers]); +chunk* newVirtualChunk(int chunk_width, int chunk_height, bool regenerate); /** @@ -223,6 +223,23 @@ chunk* newAdjacentChunk(chunk* north_chunk, chunk* west_chunk, unsigned int disp chunk* copyChunk(chunk* p_chunk); +/** + * @brief Encodes a chunk struct in a binary format. + * + * @param chk (chunk*) : a pointer to the chunk struct. + * @return bytes : the byte string representing the encoded struct. + */ +bytes bytesChunk(chunk* chk); + +/** + * @brief Decodes a chunk struct from a formatted byte string. + * + * @param bytes (bytes) : the formatted byte string. + * @return tuple_obj_bytes : the decoded chunk and the byte string (with the start index updated). + */ +tuple_obj_bytes nextChunk(bytes bytes); + + /** * @brief Writes the chunk structure to a file at the given path. diff --git a/C/headers/gradientGrid.h b/C/headers/gradientGrid.h index bfa0f56..530f084 100644 --- a/C/headers/gradientGrid.h +++ b/C/headers/gradientGrid.h @@ -10,6 +10,8 @@ #ifndef GRADIENT_GRID #define GRADIENT_GRID +#include "interpreter.h" + // ----- Structure definition ----- /** @@ -145,6 +147,23 @@ gradientGrid* readGradientGridFile(char path[]); +/** + * @brief Encodes a gradient grid struct in a binary format. + * + * @param gradGrid (gradientGrid*) : a pointer to the gradient grid struct. + * @return bytes : the byte string representing the encoded struct. + */ +bytes bytesGradientGrid(gradientGrid* grid); + +/** + * @brief Decodes a gradient grid struct from a formatted byte string. + * + * @param bytes (bytes) : the formatted byte string. + * @return tuple_obj_bytes : the decoded gradient grid and the byte string (with the start index updated). + */ +tuple_obj_bytes nextGradientGrid(bytes bytes); + + /** * @brief Prints in the terminal the given gradient grid. * diff --git a/C/headers/interpreter.h b/C/headers/interpreter.h new file mode 100644 index 0000000..7206c4b --- /dev/null +++ b/C/headers/interpreter.h @@ -0,0 +1,178 @@ +/** + * @file interpreter.h + * @author BlueNZ + * @brief Header to interpreter functions + * @version 0.1 + * @date 2024-07-27 + * + */ + +#ifndef INTERP +#define INTERP + +// ------- Structure definition ------- // + +typedef unsigned char byte; //type used as bytes +typedef char* object; //an alias for pointers + +/** + * @brief a simple implementation of byte strings + */ +typedef struct bytes { + byte* bytes; // the byte array + int size; // to keep track of the size of the byte array + int start; // where to look for the current byte in the array +} bytes; + +/** + * @brief a placeholder to return both an object (a number or a struct) and a byte string (bytes) + */ +typedef struct tuple_obj_bytes { + object object; // a pointer to the object + bytes bytes; // the byte string +} tuple_obj_bytes; + + +// ------- Constants ------- // + +#define BYTE_TRUE (byte) 1 +#define BYTE_FALSE (byte) 0 +#define INT_BITS_NBR 24 //24 should be a multiple of 8 +#define FLOAT_BITS_EXP 5 //5 should be < 8 +#define FLOAT_BITS_MANTISS 18 +#define FLOAT_BITS_NBR (1 + FLOAT_BITS_EXP + FLOAT_BITS_MANTISS) //24 should be a multiple of 8 + +#define GRID_ENCODING (byte) 1 +#define LAYER_ENCODING (byte) 2 +#define CHUNK_ENCODING (byte) 3 +#define MAP_ENCODING (byte) 4 +#define COMPLETE_MAP_ENCODING (byte) 5 + +#define BYTES_VERSION (byte) 0 + + + +// ------- Functions ------- // + +/** + * @brief 'Fast' int powers. + * + * @param nbr (int) : the nbr to be exponantiated + * @param exp (int) : the exponant + * @return int : the result of nbr^exp. + */ +int intpow(int nbr, int exp); + + + +/** + * @brief Deallocates the byte array of a bytes struct + * + * @param b (bytes) : the bytes struct + */ +void freeBytes(bytes b); + +/** + * @brief Auxiliary function to print bytes (update a string with the hexadecimal representation of a byte). + * + * @param c (unsigned char) : the byte to print + * @param res (char*) : the string to update + * @return char* : the updated string + */ +char* hex(unsigned char c, char* res); + +/** + * @brief Prints a bytes struct + * + * @param bytes (bytes) : the bytes to print + * @param start (char*) : a string to print beforehand + * @param end (char*) : a string to print after ("\n" for instance) + */ +void printBytes(bytes bytes, char* start, char* end); + +/** + * @brief Updates a byte string starting from a given index with the values of another byte string + * + * @param b (bytes) : the bytes to update + * @param bb (bytes) : the bytes to use for updating + * @param start (int) : the starting index in b + */ +void concatBytes(bytes b, bytes bb, int start); + + + +/** + * @brief Encodes an unsigned 8bits int. + * + * @param nbr (__uint8_t) : the unsigned int. + * @return bytes : the byte string representing the encoded number. + */ +bytes bytesUint8(__uint8_t nbr); + +/** + * @brief Encodes an int. + * + * @param nbr (int) : the int. + * @return bytes : the byte string representing the encoded number. + */ +bytes bytesInt(int nbr); + +/** + * @brief Encodes a double. + * + * @param nbr (double) : the double. + * @return bytes : the byte string representing the encoded number. + */ +bytes bytesDouble(double nbr); + + + +/** + * @brief Decodes an encoded unsigned 8bits int. + * + * @param bytes (bytes) : the encoded byte string. + * @return tuple_obj_bytes : the byte string with updated start index and a pointer to the number decoded. + */ +tuple_obj_bytes nextUint8(bytes bytes); + +/** + * @brief Decodes an encoded int. + * + * @param bytes (bytes) : the encoded byte string. + * @return tuple_obj_bytes : the byte string with updated start index and a pointer to the number decoded. + */ +tuple_obj_bytes nextInt(bytes bytes); + +/** + * @brief Decodes an encoded double. + * + * @param bytes (bytes) : the encoded byte string. + * @return tuple_obj_bytes : the byte string with updated start index and a pointer to the number decoded. + */ +tuple_obj_bytes nextDouble(bytes bytes); + + + +/** + * @brief Writes a bytes string in a specified file + * + * @param bytes (bytes) : the byte string. + * @param path (char*) : the folder path. + * @param name (char*) : the file name (should end with '.data'). + */ +void writeBytesFile(bytes bytes, char* path, char* name); + +/** + * @brief Read bytes from a specified file + * + * @param path (char*) : the file path. + * @return (bytes) : read bytes + */ +bytes readBytesFile(char* path); + + + + + + +#endif \ No newline at end of file diff --git a/C/headers/layer.h b/C/headers/layer.h index fcc0d48..45feb46 100644 --- a/C/headers/layer.h +++ b/C/headers/layer.h @@ -10,7 +10,10 @@ #ifndef LAYER #define LAYER +#include + #include "gradientGrid.h" +#include "interpreter.h" // ----- Structure definition ----- @@ -95,6 +98,7 @@ double* getLayerValue(layer* layer, int width_idx, int height_idx); * * @param gradient_grid (gradientGrid*) : the pointer to the gradientGrid structure to build the layer from. * @param size_factor (int) : the size_factor to rescale the layer's dimensions. + * @param altitude (bool) : should altitude values be initialised and generated. * @param display_loading (unsigned int) : the given value defines the behaviour. * * If `0` the loading bars won't be printed. * * If `> 0` the loading bars will be printed with a number of indent equal to `display_loading - 1`. @@ -103,7 +107,7 @@ double* getLayerValue(layer* layer, int width_idx, int height_idx); * * @note Please be aware that the layer's dimensions are `(gradGrid_dimensions - 1) * size_factor` */ -layer* newLayerFromGradient(gradientGrid* gradient_grid, int size_factor, unsigned int display_loading); +layer* newLayerFromGradient(gradientGrid* gradient_grid, int size_factor, bool altitude, unsigned int display_loading); /** * @brief Generates a new layer structure from scratch with the given parameters. @@ -132,6 +136,26 @@ layer* copyLayer(layer * p_layer); +/** + * @brief Encodes a layer struct in a binary format. + * + * @param layer (layer*) : a pointer to the layer struct. + * @param altitude (bool) : should altitude values be encoded. + * @return bytes : the byte string representing the encoded struct. + */ +bytes bytesLayer(layer* layer, bool altitude); + +/** + * @brief Decodes a layer struct from a formatted byte string. + * + * @param bytes (bytes) : the formatted byte string. + * @param altitude (bool) : should altitude values be initialised and decoded (or regenerated) or not. + * @return tuple_obj_bytes : the decoded layer and the byte string (with the start index updated). + */ +tuple_obj_bytes nextLayer(bytes bytes, bool altitude); + + + /** * @brief Writes the layer structure to a file at the given path. * diff --git a/C/headers/map.h b/C/headers/map.h index 4401471..da5d3a9 100644 --- a/C/headers/map.h +++ b/C/headers/map.h @@ -11,6 +11,7 @@ #define MAP #include "chunk.h" +#include "interpreter.h" // ----- Structure definition ----- @@ -31,7 +32,7 @@ struct map int chunk_width; /**< the width of each chunk*/ int chunk_height; /**< the height of each chunk*/ - double* map_values; /**< the array of altitude values. Its size is `map_width * chunk_width` x `map_height * chunk_height`*/ + // double* map_values; /**< the array of altitude values. Its size is `map_width * chunk_width` x `map_height * chunk_height`*/ }; typedef struct map map; @@ -39,12 +40,13 @@ typedef struct map map; // ----- Functions ----- /** - * @brief Get the pointer to the altitude value at given width and height indexes in the given map structure + * @brief Get the pointer to the altitude value at given width and height indexes in the given map structure. + * This points to the altitude value in the correct chunk. * * @param map (map*) : the pointer to the map structure. * @param width_idx (int) : the width index of the wanted value. * @param height_idx (int) : the height index of the wanted value. - * @return double* : the pointer to the map altitude value. + * @return double* : the pointer to the map altitude value in the correct chunk structure. */ double* getMapValue(map* map, int width_idx, int height_idx); @@ -72,6 +74,17 @@ chunk* getVirtualChunk(map* map, int width_idx, int height_idx); +/** + * @brief Get the full map altitude values built from concatenation of each chunk altitude value. + * + * @param map (map*) : the pointer to the map to get the full map altitude from. + * @return double* : the pointer to the full altitude array. It is of size + * `map.map_width * map.chunk_width * map.map_height * map.chunk_height`. + */ +double* getFullMap(map* map); + + + /** * @brief Interpolates the given values in 2d in order to get a smooth 2d interpolation. * @@ -86,7 +99,7 @@ chunk* getVirtualChunk(map* map, int width_idx, int height_idx); double interpolate2D(double a1, double a2, double a3, double a4, double x, double y); /** - * @brief Modifies the given map to process each chunk's base altitude and adapt the final altitude values of the given map. + * @brief Modifies the chunks of the given map to process each chunk's base altitude and adapt the final altitude values of each chunk. * * @param p_map (map*) : the pointer to the original untreated map. * @param display_loading (unsigned int) : the given value defines the behaviour. @@ -105,6 +118,7 @@ map* addMeanAltitude(map* p_map, unsigned int display_loading); * @param map_height (int) : number of chunks in height. * @param chunks (chunk**) : array of size `map_width * map_height` of pointers to the chunk structures. * @param virtual_chunks (chunk**) : array of size `(map_height+2 + map_width+2)*2 - 4` of pointers to the virtual chunk structures. + * @param regenerate (bool) : should altitude values be initialised and regenerated (adds base altitude to its chunks' altitude values). * @param display_loading (unsigned int) : the given value defines the behaviour. * * If `0` the loading bars won't be printed. * * If `> 0` the loading bars will be printed with a number of indent equal to `display_loading - 1`. @@ -112,7 +126,7 @@ map* addMeanAltitude(map* p_map, unsigned int display_loading); * * @note The chunks array does not need to be dynamically allocated and its content will be copied in the structure. */ -map* newMapFromChunks(int map_width, int map_height, chunk* chunks[map_width * map_height], chunk* virtual_chunks[(map_height+2+map_width+2)*2-4], unsigned int display_loading); +map* newMapFromChunks(int map_width, int map_height, chunk* chunks[map_width * map_height], chunk* virtual_chunks[(map_height+2+map_width+2)*2-4], bool regenerate, unsigned int display_loading); /** * @brief Creates a new map from scratch with the given parameters. @@ -150,6 +164,24 @@ map* copyMap(map* p_map) ; +/** + * @brief Encodes a map struct in a binary format. + * + * @param map (map*) : a pointer to the map struct. + * @return bytes : the byte string representing the encoded struct. + */ +bytes bytesMap(map* map); + +/** + * @brief Decodes a map struct from a formatted byte string. + * + * @param bytes (bytes) : the formatted byte string. + * @return tuple_obj_bytes : the decoded map and the byte string (with the start index updated). + */ +tuple_obj_bytes nextMap(bytes bytes); + + + /** * @brief Writes the map structure into a file at the given path. * diff --git a/C/headers/mapGenerator.h b/C/headers/mapGenerator.h index 01e3a71..2365d1f 100644 --- a/C/headers/mapGenerator.h +++ b/C/headers/mapGenerator.h @@ -2,8 +2,8 @@ * @file mapGenerator.h * @author Zyno and BlueNZ * @brief Header to mapGenerator structure and functions - * @version 0.2 - * @date 2024-06-19 + * @version 0.3 + * @date 2024-07-31 * */ @@ -11,6 +11,7 @@ #define MAP_GENERATOR #include "map.h" +#include "interpreter.h" // ----- Structure definition ----- @@ -20,13 +21,9 @@ */ struct color { - int red_int; /**< the red int value in [0, 255]*/ - int green_int; /**< the green int value in [0, 255]*/ - int blue_int; /**< the blue int value in [0, 255]*/ - - float red_float; /**< the red float value in [0, 1]*/ - float green_float; /**< the green float value in [0, 1]*/ - float blue_float; /**< the blue float value in [0, 1]*/ + __uint8_t red; /**< the red integer value in [0, 255] */ + __uint8_t green; /**< the green integer value in [0, 255]*/ + __uint8_t blue; /**< the blue integer value in [0, 255] */ }; typedef struct color color; @@ -233,6 +230,34 @@ void printCompleteMap(completeMap* completeMap); +/** + * @brief Encodes a color struct in a binary format. + * + * @param c (color) : a color struct. + * @return bytes : the byte string representing the encoded struct. + */ +bytes bytesColor(color c); + +tuple_obj_bytes nextColor(bytes bytes); + +/** + * @brief Encodes a compltet map struct in a binary format. + * + * @param cmap (completeMap*) : a pointer to the complete map struct. + * @return bytes : the byte string representing the encoded struct. + */ +bytes bytesCompleteMap(completeMap* cmap); + +/** + * @brief Decodes a complete map struct from a formatted byte string. + * + * @param bytes (bytes) : the formatted byte string. + * @return tuple_obj_bytes : the decoded complete map and the byte string (with the start index updated). + */ +tuple_obj_bytes nextCompleteMap(bytes bytes); + + + /** * @brief Writes the sea_map in a file at the given path. * @@ -242,14 +267,14 @@ void printCompleteMap(completeMap* completeMap); void writeSeaMapFile(completeMap* completeMap, char path[]); /** - * @brief TODO : Writes both color_map structures in two different files. + * @brief Writes the color_map in a file at the given path. * * @param width (int) : the width of the color map * @param height (int) : the height of the color map. * @param color_map (color**) : the array of pointers to the color structures representing the color map. * @param path (char[]) : the path where the file shall be written. */ -void writeColorMapFiles(int width, int height, color* color_map[width * height], char path[]); +void writeColorMapFile(int width, int height, color* color_map[width * height], char path[]); /** * @brief Writes every required files to save the completeMap structure. diff --git a/C/src/chunk.c b/C/src/chunk.c index 5d6a6b4..1543e8d 100644 --- a/C/src/chunk.c +++ b/C/src/chunk.c @@ -2,8 +2,8 @@ * @file chunk.c * @author Zyno and BlueNZ * @brief chunk structure and functions implementation - * @version 0.2 - * @date 2024-06-19 + * @version 0.3 + * @date 2024-07-27 * */ @@ -86,6 +86,13 @@ chunk* initChunk(int width, int height, int number_of_layers, double layers_fact +double generateBaseAltitude() +{ + return -0.5+2*rand()*1./RAND_MAX; +} + + + void regenerateChunk(chunk* chunk, unsigned int display_loading) { clock_t start_time = clock(); @@ -137,7 +144,16 @@ void regenerateChunk(chunk* chunk, unsigned int display_loading) *value /= divisor; } } - chunk->base_altitude=-0.5+2*rand()*1./RAND_MAX; + + chunk->base_altitude=generateBaseAltitude(); + + // Frees layer altitude arrays + + for (int k = 0; k < nblayers; k++) + { + free(layers[k]->values); + layers[k]->values = NULL; + } } @@ -181,7 +197,7 @@ chunk* newChunkFromGradients(int width, int height, int number_of_layers, gradie } // Size_factors should match gradient_grids dimensions - 1 - layers[i] = newLayerFromGradient(gradient_grids[i], size_factors[i], g_loading); + layers[i] = newLayerFromGradient(gradient_grids[i], size_factors[i], true, g_loading); if (display_loading != 0) @@ -256,32 +272,25 @@ chunk* newChunk(int number_of_layers, int gradGrids_width[number_of_layers], int -//TODO ? signature could be changed to avoid passing useless parameters -> `chunk_width` and `chunk_height` instead of `gradGrids_width`, `gradGrids_height` and `size_factors` -chunk* newVirtualChunk(int number_of_layers, int gradGrids_width[number_of_layers], int gradGrids_height[number_of_layers], int size_factors[number_of_layers], double layers_factors[number_of_layers]) +chunk* newVirtualChunk(int chunk_width, int chunk_height, bool regenerate) { chunk* new_chunk = calloc(1, sizeof(chunk)); - new_chunk->number_of_layers = number_of_layers; + new_chunk->number_of_layers = 0; // size_factors should match gradient_grids dimensions - 1 - int width = (gradGrids_width[0] - 1) * size_factors[0]; - int height = (gradGrids_height[0] - 1) * size_factors[0]; + int width = chunk_width; + int height = chunk_height; new_chunk->width = width; new_chunk->height = height; - // copy layer factors to ensure dynamic allocation - double* factors = calloc(number_of_layers, sizeof(double)); - for (int i = 0; i < number_of_layers; i++) - { - factors[i] = layers_factors[i]; - } - new_chunk->layers_factors = factors; + new_chunk->layers_factors = NULL; new_chunk->chunk_values = NULL; new_chunk->layers = NULL; - new_chunk->base_altitude=-0.5+2*rand()*1./RAND_MAX; + if (regenerate) new_chunk->base_altitude=generateBaseAltitude(); return new_chunk; } @@ -474,6 +483,163 @@ chunk* copyChunk(chunk* p_chunk) +bytes bytesChunk(chunk* chk) { + bytes bytes_str; + + if (chk->chunk_values==NULL) { //virtual chunk + bytes_str.bytes = malloc(2 + 2*INT_BITS_NBR/8 + FLOAT_BITS_NBR/8); + bytes_str.size = 2 + 2*INT_BITS_NBR/8 + FLOAT_BITS_NBR/8; + bytes_str.start = 0; + + bytes_str.bytes[0] = CHUNK_ENCODING; + + bytes a = bytesDouble(chk->base_altitude); + concatBytes(bytes_str, a, 1); + freeBytes(a); + a = bytesInt(chk->width); + concatBytes(bytes_str, a, 1+FLOAT_BITS_NBR/8); + freeBytes(a); + a = bytesInt(chk->height); + concatBytes(bytes_str, a, 1+FLOAT_BITS_NBR/8+INT_BITS_NBR/8); + freeBytes(a); + + bytes_str.bytes[1+FLOAT_BITS_NBR/8+2*INT_BITS_NBR/8] = BYTE_TRUE; + } + else { + bytes* bytes_layers[chk->number_of_layers]; + int layer_size=0; + for (int i=0; inumber_of_layers; i++) { + bytes bytes_lay = (bytesLayer(chk->layers[i],false)); + bytes_layers[i] = malloc(sizeof(bytes_lay)); + *bytes_layers[i] = bytes_lay; + layer_size += bytes_lay.size; + } + + bytes_str.bytes = malloc(2 + 3*INT_BITS_NBR/8 + (chk->height*chk->width+1+chk->number_of_layers)*FLOAT_BITS_NBR/8 + layer_size); + bytes_str.size = 2 + 3*INT_BITS_NBR/8 + (chk->height*chk->width+1+chk->number_of_layers)*FLOAT_BITS_NBR/8 + layer_size; + bytes_str.start = 0; + + bytes_str.bytes[0] = CHUNK_ENCODING; + + bytes a = bytesDouble(chk->base_altitude); + concatBytes(bytes_str, a, 1); + freeBytes(a); + a = bytesInt(chk->height); + concatBytes(bytes_str, a, 1+FLOAT_BITS_NBR/8); + freeBytes(a); + a = bytesInt(chk->width); + concatBytes(bytes_str, a, 1+FLOAT_BITS_NBR/8+INT_BITS_NBR/8); + freeBytes(a); + + bytes_str.bytes[1+FLOAT_BITS_NBR/8+2*INT_BITS_NBR/8] = BYTE_FALSE; + + a = bytesInt(chk->number_of_layers); + concatBytes(bytes_str, a, 2+FLOAT_BITS_NBR/8+2*INT_BITS_NBR/8); + freeBytes(a); + + for (int i=0; inumber_of_layers; i++) { + a = bytesDouble(chk->layers_factors[i]); + concatBytes(bytes_str, a, 2+(i+1)*(FLOAT_BITS_NBR/8)+3*(INT_BITS_NBR/8)); + freeBytes(a); + } + + layer_size=0; + for (int i=0; inumber_of_layers; i++) { + concatBytes(bytes_str, *(bytes_layers[i]), 2+(chk->number_of_layers+1)*FLOAT_BITS_NBR/8+3*INT_BITS_NBR/8+layer_size); + layer_size += (bytes_layers[i])->size; + freeBytes(*(bytes_layers[i])); + free(bytes_layers[i]); + } + + for (int i=0; iheight; i++) { + for (int j=0; jwidth; j++) { + a = bytesDouble(*getChunkValue(chk,j,i)); + concatBytes(bytes_str, a, 2+(chk->number_of_layers+1+i*chk->width+j)*FLOAT_BITS_NBR/8+3*INT_BITS_NBR/8+layer_size); + freeBytes(a); + } + } + } + + return bytes_str; + +} + +tuple_obj_bytes nextChunk(bytes bytes) { + tuple_obj_bytes res; + + if (bytes.bytes[bytes.start]==CHUNK_ENCODING) { + bytes.start += 1; + + tuple_obj_bytes a = nextDouble(bytes); + double base_altitude = *((double*)a.object); + bytes = a.bytes; + free(a.object); + a = nextInt(bytes); + int h = *((int*)a.object); + bytes = a.bytes; + free(a.object); + a = nextInt(bytes); + int w = *((int*)a.object); + bytes = a.bytes; + free(a.object); + + chunk* obj; + + if (bytes.bytes[bytes.start]==BYTE_TRUE) { + bytes.start+=1; + obj = newVirtualChunk(w,h,false); + obj->base_altitude=base_altitude; + } + else { + bytes.start+=1; + obj = newVirtualChunk(w,h,false); + obj->base_altitude=base_altitude; + + a = nextInt(bytes); + int nbr = *((int*)a.object); + bytes = a.bytes; + free(a.object); + obj->number_of_layers = nbr; + + double* layer_factors = calloc(nbr, sizeof(double)); + for (int i=0; ilayers_factors = layer_factors; + + layer** layers = calloc(nbr, sizeof(layer*)); + for (int i=0; ilayers = layers; + + double* chunk_values = calloc(w * h, sizeof(double)); + obj->chunk_values = chunk_values; + for (int i=0; i0) { - for (int k = 0; k < nb_layer; k++) - { - if (k < nb_layer - 1) - { - printf("%lf, ", factors[k]); - } - else + printf("It has %d layers with factors : {", nb_layer); + + for (int k = 0; k < nb_layer; k++) { - printf("%lf}\n", factors[k]); + if (k < nb_layer - 1) + { + printf("%lf, ", factors[k]); + } + else + { + printf("%lf}\n", factors[k]); + } } - } - printf("\n"); + printf("\n"); - for (int i = 0; i < height; i++) - { - for (int j = 0; j < width; j++) + for (int i = 0; i < height; i++) { - double* value = getChunkValue(chunk, j, i); + for (int j = 0; j < width; j++) + { + double* value = getChunkValue(chunk, j, i); - printf("%lf ", *value); + printf("%lf ", *value); + } + printf("\n"); } - printf("\n"); } + else printf("virtual chunk\n"); printf("-------------------------------------------\n"); } diff --git a/C/src/gradientGrid.c b/C/src/gradientGrid.c index 18798b1..2acd44a 100644 --- a/C/src/gradientGrid.c +++ b/C/src/gradientGrid.c @@ -2,8 +2,8 @@ * @file gradientGrid.c * @author Zyno and BlueNZ * @brief gradientGrid structure implementation - * @version 0.2 - * @date 2024-06-19 + * @version 0.3 + * @date 2024-07-28 * */ @@ -13,6 +13,7 @@ #include #include "loadingBar.h" +#include "interpreter.h" #include "gradientGrid.h" void setRandomSeed(unsigned int seed) @@ -300,6 +301,75 @@ vector* copyVect(vector* vect) +bytes bytesGradientGrid(gradientGrid* grid) { + bytes bytes_str; + bytes_str.bytes = malloc(2*(grid->height*grid->width)*FLOAT_BITS_NBR/8+2*INT_BITS_NBR/8+1); + bytes_str.size = 2*(grid->height*grid->width)*FLOAT_BITS_NBR/8+2*INT_BITS_NBR/8+1; + bytes_str.start = 0; + + bytes_str.bytes[0] = GRID_ENCODING; + + bytes a = bytesInt(grid->height); + concatBytes(bytes_str, a, 1); + freeBytes(a); + a = bytesInt(grid->width); + concatBytes(bytes_str, a, 1+INT_BITS_NBR/8); + freeBytes(a); + + for (int i=0; iheight; i++) { + for (int j=0; jwidth; j++) { + vector vect = *getVector(grid,j,i); + a = bytesDouble(vect.x); + concatBytes(bytes_str, a, 1 + 2 * INT_BITS_NBR/8 + 2 * (i * grid->width + j) * FLOAT_BITS_NBR/8); + freeBytes(a); + a = bytesDouble(vect.y); + concatBytes(bytes_str, a, 1 + 2 * INT_BITS_NBR/8 + (2 * (i * grid->width + j) + 1) * FLOAT_BITS_NBR/8); + freeBytes(a); + } + } + + return bytes_str; + +} + +tuple_obj_bytes nextGradientGrid(bytes bytes) { + tuple_obj_bytes res; + + if (bytes.bytes[bytes.start]==GRID_ENCODING) { + bytes.start += 1; + + tuple_obj_bytes a = nextInt(bytes); + int h = *((int*)a.object); + bytes = a.bytes; + free(a.object); + a = nextInt(bytes); + int w = *((int*)a.object); + bytes = a.bytes; + free(a.object); + + gradientGrid* obj = newGradGrid(w,h); + + for (int i=0; ix = *((double*)a.object); + bytes = a.bytes; + free(a.object); + a = nextDouble(bytes); + vect->y = *((double*)a.object); + bytes = a.bytes; + free(a.object); + } + } + + res.object = (object) obj; + res.bytes = bytes; + } + + return res; +} + void writeGradientGridFile(gradientGrid* gradGrid, char path[]) { FILE* f = NULL; diff --git a/C/src/interpreter.c b/C/src/interpreter.c new file mode 100644 index 0000000..b363469 --- /dev/null +++ b/C/src/interpreter.c @@ -0,0 +1,254 @@ +/** + * @file interpreter.c + * @author BlueNZ + * @brief interpreter functions implementation + * @version 0.1 + * @date 2024-07-27 + * + */ + +#include +#include +#include + +#include "map.h" +#include "interpreter.h" + + +// ------- General functions ------- // + +int intpow(int nbr, int exp) { + if (exp<=0) return 1; + else if (exp==1) return nbr; + else if (exp%2==0) return intpow(nbr*nbr,exp/2); + else return nbr*intpow(nbr,exp-1); +} + + + + + +// ------- Binary functions ------- // + +void freeBytes(bytes b) { + free(b.bytes); +} + +char* hex(unsigned char c, char* res) { + int a = c/16; + int b = c%16; + sprintf(res,"%x%x",a,b); + return res; +} + +void printBytes(bytes bytes, char* start, char* end) { + char* bytes_str=malloc(bytes.size * 2+4); + bytes_str[0] = 'b'; + bytes_str[1] = '\''; + bytes_str[bytes.size * 2+2] = '\''; + bytes_str[bytes.size * 2+3] = '\0'; + + for (int i=0; i-intpow(2,FLOAT_BITS_EXP-1) && nbr=intpow(2,FLOAT_BITS_MANTISS)) { + exp+=1; + nbr/=2; + } + } + exp+=16; + + res.bytes[0] = sign + (byte) (exp * intpow(2,8-(1+FLOAT_BITS_EXP)) + ((int)nbr)/intpow(2,FLOAT_BITS_MANTISS-(8-(1+FLOAT_BITS_EXP)))); + for (int i=8; i=0) *obj = sign * ((double)temp) * intpow(2,exp); + else *obj = sign * ((double)temp) / intpow(2,-exp); + + res.object = (object) obj; + res.bytes = bytes; + res.bytes.start += FLOAT_BITS_NBR/8; + + return res; +} + + +void writeBytesFile(bytes bytes, char* path, char* name) { + struct stat st = {0}; + + // Generates a directory if it does not exist + if (stat(path, &st) == -1) + { + // 0700 is rwx permission for owner only + mkdir(path, 0700); + } + + // Generates the sea map file + char file_path[200] = ""; + snprintf(file_path, sizeof(file_path), "%s/%s", path, name); + + // Creates and/or opens the binary file (overrides already existing content) + FILE* f = NULL; + f = fopen(file_path, "wb"); + + if (f != NULL) + { + // Writes bytes (unsigned char) in the file + fwrite(bytes.bytes, bytes.size, 1, f); + fclose(f); + } +} + +bytes readBytesFile(char* path) { + // Opens file + FILE* f = NULL; + f = fopen(path, "rb"); + bytes b; + + if (f != NULL) + { + // Get file size + fseek(f, 0, SEEK_END); // seek to end of file + long size = ftell(f); // get current file pointer + fseek(f, 0, SEEK_SET); // seek back to beginning of file + + // Read bytes (unsigned char) in the file + b.size = size; + b.start = 0; + b.bytes = malloc(size*sizeof(byte)); + fread(b.bytes, b.size, 1, f); + + fclose(f); + } + + return b; +} \ No newline at end of file diff --git a/C/src/layer.c b/C/src/layer.c index d01e153..53a047a 100644 --- a/C/src/layer.c +++ b/C/src/layer.c @@ -2,8 +2,8 @@ * @file layer.c * @author Zyno and BlueNZ * @brief layer structure and functions implementation - * @version 0.2 - * @date 2024-06-19 + * @version 0.3 + * @date 2024-07-28 * */ @@ -103,7 +103,10 @@ double* getLayerValue(layer* layer, int width_idx, int height_idx) return NULL; } - layer_value = (layer->values) + height_idx * width + width_idx; + if (layer->values != NULL) + { + layer_value = (layer->values) + height_idx * width + width_idx; + } } return layer_value; @@ -113,7 +116,7 @@ double* getLayerValue(layer* layer, int width_idx, int height_idx) -layer* newLayerFromGradient(gradientGrid* gradient_grid, int size_factor, unsigned int display_loading) +layer* newLayerFromGradient(gradientGrid* gradient_grid, int size_factor, bool altitude, unsigned int display_loading) { clock_t start_time = clock(); @@ -126,7 +129,8 @@ layer* newLayerFromGradient(gradientGrid* gradient_grid, int size_factor, unsign // Initialization layer* new_layer = calloc(1, sizeof(layer)); - double* values = calloc(width * height, sizeof(double)); + double* values = NULL; + if (altitude) values = calloc(width * height, sizeof(double)); new_layer->width = width; new_layer->height = height; @@ -137,21 +141,23 @@ layer* newLayerFromGradient(gradientGrid* gradient_grid, int size_factor, unsign new_layer->values = values; // Setting correct double values - for (int i = 0; i < height; i++) - { - for (int j = 0; j < width; j++) + if (altitude) { + for (int i = 0; i < height; i++) { - double* value = getLayerValue(new_layer, j, i); + for (int j = 0; j < width; j++) + { + double* value = getLayerValue(new_layer, j, i); - *value = perlin((double) j/size_factor, (double) i/size_factor, gradient_grid); + *value = perlin((double) j/size_factor, (double) i/size_factor, gradient_grid); - if (display_loading != 0) - { - int nb_indents = display_loading - 1; + if (display_loading != 0) + { + int nb_indents = display_loading - 1; - char base_str[100] = "Generating layer... "; + char base_str[100] = "Generating layer... "; - predefined_loading_bar(j + i * width, width * height - 1, NUMBER_OF_SEGMENTS, base_str, nb_indents, start_time); + predefined_loading_bar(j + i * width, width * height - 1, NUMBER_OF_SEGMENTS, base_str, nb_indents, start_time); + } } } } @@ -170,7 +176,7 @@ layer* newLayer(int gradGrid_width, int gradGrid_height, int size_factor, unsign gradientGrid* gradient_grid = newRandomGradGrid(gradGrid_width, gradGrid_height, g_loading); // Generating layer from the new gradientGrid - return newLayerFromGradient(gradient_grid, size_factor, g_loading); + return newLayerFromGradient(gradient_grid, size_factor, true, g_loading); } @@ -203,6 +209,83 @@ layer* copyLayer(layer * p_layer) +bytes bytesLayer(layer* layer, bool altitude) { + bytes bytes_grid = bytesGradientGrid(layer->gradient_grid); + + bytes bytes_str; + bytes_str.bytes = malloc(2+INT_BITS_NBR/8+(altitude?(layer->height*layer->width):(0))*FLOAT_BITS_NBR/8+bytes_grid.size); + bytes_str.size = 2+INT_BITS_NBR/8+(altitude?(layer->height*layer->width):(0))*FLOAT_BITS_NBR/8+bytes_grid.size; + bytes_str.start = 0; + + bytes_str.bytes[0] = LAYER_ENCODING; + + bytes a = bytesInt(layer->size_factor); + concatBytes(bytes_str, a, 1); + freeBytes(a); + concatBytes(bytes_str, bytes_grid, 1+FLOAT_BITS_NBR/8); + + if (altitude) { + bytes_str.bytes[1 + bytes_grid.size + INT_BITS_NBR/8] = BYTE_TRUE; + for (int i=0; iheight; i++) { + for (int j=0; jwidth; j++) { + a = bytesDouble(*getLayerValue(layer,j,i)); + concatBytes(bytes_str, a, 2 + INT_BITS_NBR/8 + bytes_grid.size + (i * layer->width + j) * FLOAT_BITS_NBR/8); + freeBytes(a); + } + } + } + else bytes_str.bytes[1 + bytes_grid.size + FLOAT_BITS_NBR/8] = BYTE_FALSE; + + freeBytes(bytes_grid); + + return bytes_str; + +} + +tuple_obj_bytes nextLayer(bytes bytes, bool altitude) { + tuple_obj_bytes res; + + if (bytes.bytes[bytes.start]==LAYER_ENCODING) { + bytes.start += 1; + + tuple_obj_bytes a = nextInt(bytes); + int sf = *((int*)a.object); + bytes = a.bytes; + free(a.object); + + tuple_obj_bytes temp = nextGradientGrid(bytes); + gradientGrid* grid = ((gradientGrid*)temp.object); + bytes = temp.bytes; + + layer * obj; + + if (bytes.bytes[bytes.start]==BYTE_TRUE) { + bytes.start += 1; + obj = newLayerFromGradient(grid,sf,false,0); + if (altitude) obj->values = calloc(obj->width * obj->height, sizeof(double)); + for (int i=0; iheight; i++) { + for (int j=0; jwidth; j++) { + a = nextDouble(bytes); + if (altitude) *getLayerValue(obj,j,i) = *((double*)a.object); + bytes = a.bytes; + free(a.object); + } + } + } + else { + bytes.start += 1; + if (altitude) obj = newLayerFromGradient(grid,sf,true,0); + else obj = newLayerFromGradient(grid,sf,false,0); + } + + res.object = (object) obj; + res.bytes = bytes; + } + + return res; +} + + void writeLayerFile(layer* layer, char path[]) { @@ -264,16 +347,20 @@ void printLayer(layer* layer) printf("-------------------------------------------\n"); printf("Printing layer of size = (%d, %d)\n\n", height, width); - for (int i = 0; i < height; i++) + if (layer->values!=NULL) { - for (int j = 0; j < width; j++) + for (int i = 0; i < height; i++) { - double* value = getLayerValue(layer, j, i); + for (int j = 0; j < width; j++) + { + double* value = getLayerValue(layer, j, i); - printf("%lf ", *value); + printf("%lf ", *value); + } + printf("\n"); } - printf("\n"); } + else printf("Null values\n"); printf("-------------------------------------------\n"); } diff --git a/C/src/main.c b/C/src/main.c new file mode 100644 index 0000000..6ede2e2 --- /dev/null +++ b/C/src/main.c @@ -0,0 +1,536 @@ +/** + * @file main.c + * @author Zyno and BlueNZ + * @brief main code to generate and save a new complete map + * @version 0.2 + * @date 2024-08-07 + * + */ + +#include +#include +#include + +#include "loadingBar.h" +#include "mapGenerator.h" +#include "interpreter.h" + + +#define INPUT_MAX_SIZE 100 + + + +int main(int argc, char* argv[argc]) +{ + //? --- Initialisation of the different variables ----------------------------------------------------------------------------------------------- + + // Can be set in command line + int map_width = 0; + int map_height = 0; + int nb_layers = 0; + + double sea_level = 0.; + int sea_level_affected = 0; // boolean + + //? --- Manage command line inputs : ./main ---------------------------------------------------- + + if (argc > 1) + { + int temp = 0; + int nb_associated = sscanf(argv[1], "%d", &temp); + + if (nb_associated == 0) + { + printf("%s/!\\ Input Error : input '%s' could not be cast as an integer for map_width /!\\%s\n", RED_COLOR, argv[1], DEFAULT_COLOR); + } + else if (temp <= 0) + { + printf("%s/!\\ Value Error : map_width should be a strictly positive integer but %d was given /!\\%s\n", RED_COLOR, temp, DEFAULT_COLOR); + } + else + { + map_width = temp; + } + } + if (argc > 2) + { + int temp = 0; + int nb_associated = sscanf(argv[2], "%d", &temp); + + if (nb_associated == 0) + { + printf("%s/!\\ Input Error : input '%s' could not be cast as an integer for map_height /!\\%s\n", RED_COLOR, argv[2], DEFAULT_COLOR); + } + else if (temp <= 0) + { + printf("%s/!\\ Value Error : map_height should be a strictly positive integer but %d was given /!\\%s\n", RED_COLOR, temp, DEFAULT_COLOR); + } + else + { + map_height = temp; + } + } + if (argc > 3) + { + int temp = 0; + int nb_associated = sscanf(argv[3], "%d", &temp); + + if (nb_associated == 0) + { + printf("%s/!\\ Input Error : input '%s' could not be cast as an integer for nb_layers /!\\%s\n", RED_COLOR, argv[3], DEFAULT_COLOR); + } + else if (temp <= 0) + { + printf("%s/!\\ Value Error : nb_layers should be a strictly positive integer but %d was given /!\\%s\n", RED_COLOR, temp, DEFAULT_COLOR); + } + else + { + nb_layers = temp; + } + } + if (argc > 4) + { + double temp = 0.; + int nb_associated = sscanf(argv[4], "%lf", &temp); + + if (nb_associated == 0) + { + printf("%s/!\\ Input Error : input '%s' could not be cast as a double for sea_level /!\\%s\n", RED_COLOR, argv[4], DEFAULT_COLOR); + } + else + { + sea_level = temp; + sea_level_affected = 1; + } + } + if (argc > 5) + { + printf("%s/!\\ Warning : the remaining inputs will not be treated : /!\\%s\n", YELLOW_COLOR, DEFAULT_COLOR); + for (int i = 5; i < argc; i++) + { + if (i < argc - 1) + { + printf("'%s', ", argv[i]); + } + else + { + printf("'%s'\n", argv[i]); + } + } + } + + + + + + //? --- Manage missing values with prompt inputs (will have to manage layers whatsoever) -------------------------------------------------------- + + if ( !(map_width > 0) ) + { + int temp = 0; + int nb_associated = 0; + char line[INPUT_MAX_SIZE] = ""; + + while ( !(temp > 0) ) + { + printf("Please enter a valid map_width value for the map generation. It should be > 0. Your input : "); + + fgets(line, INPUT_MAX_SIZE, stdin); + + nb_associated = sscanf(line, " %d", &temp); + if (nb_associated == 0) + { + printf("%s/!\\ Input Error : could not convert line '%s' as an integer value. /!\\%s\n", RED_COLOR, line, DEFAULT_COLOR); + } + else if (temp <= 0) + { + printf("%s/!\\ Value Error : map_width should be a strictly positive integer but %d was given /!\\%s\n", RED_COLOR, temp, DEFAULT_COLOR); + } + } + + map_width = temp; + } + printf("%sMap width is set to %d%s\n\n", GREEN_COLOR, map_width, DEFAULT_COLOR); + + + if ( !(map_height > 0) ) + { + int temp = 0; + int nb_associated = 0; + char line[INPUT_MAX_SIZE] = ""; + + while ( !(temp > 0) ) + { + printf("Please enter a valid map_height value for the map generation. It should be > 0. Your input : "); + + fgets(line, INPUT_MAX_SIZE, stdin); + + nb_associated = sscanf(line, " %d", &temp); + if (nb_associated == 0) + { + printf("%s/!\\ Input Error : could not convert line '%s' as an integer value. /!\\%s\n", RED_COLOR, line, DEFAULT_COLOR); + } + else if (temp <= 0) + { + printf("%s/!\\ Value Error : map_height should be a strictly positive integer but %d was given /!\\%s\n", RED_COLOR, temp, DEFAULT_COLOR); + } + } + + map_height = temp; + } + printf("%sMap height is set to %d%s\n\n", GREEN_COLOR, map_height, DEFAULT_COLOR); + + + if ( !(nb_layers > 0) ) + { + int temp = 0; + int nb_associated = 0; + char line[INPUT_MAX_SIZE] = ""; + + while ( !(temp > 0) ) + { + printf("Please enter a valid nb_layers value for the map generation. It should be > 0. Your input : "); + + fgets(line, INPUT_MAX_SIZE, stdin); + + nb_associated = sscanf(line, " %d", &temp); + if (nb_associated == 0) + { + printf("%s/!\\ Input Error : could not convert line '%s' as an integer value. /!\\%s\n", RED_COLOR, line, DEFAULT_COLOR); + } + else if (temp <= 0) + { + printf("%s/!\\ Value Error : nb_layers should be a strictly positive integer but %d was given /!\\%s\n", RED_COLOR, temp, DEFAULT_COLOR); + } + } + + nb_layers = temp; + } + printf("%sNumber of layers is set to %d%s\n\n", GREEN_COLOR, nb_layers, DEFAULT_COLOR); + + + + + //? --- Manage layers parameters ---------------------------------------------------------------------------------------------------------------- + + + // Initialisation of the missing variables + + int grid_dimensions[nb_layers]; + double layers_factors[nb_layers]; + + for (int i=0; i < nb_layers; i++) + { + printf("Setting up the layer n°%d/%d...\n", i+1, nb_layers); + + int nb_associated = 0; + int temp_i = 0; + double temp_d = 0.; + char line[INPUT_MAX_SIZE] = ""; + + while ( !(temp_i > 0) ) + { + printf("Please enter a valid grid dimension value for gradientGrid %d/%d. It should be > 0. Your input : ", i+1, nb_layers); + + fgets(line, INPUT_MAX_SIZE, stdin); + + nb_associated = sscanf(line, " %d", &temp_i); + if (nb_associated == 0) + { + printf("%s/!\\ Input Error : could not convert line '%s' as an integer value. /!\\%s\n", RED_COLOR, line, DEFAULT_COLOR); + } + else if (temp_i <= 0) + { + printf("%s/!\\ Value Error : a grid dimension should be a strictly positive integer but %d was given /!\\%s\n", RED_COLOR, temp_i, DEFAULT_COLOR); + } + } + + grid_dimensions[i] = temp_i; + printf("Grid dimensions %d/%d set to %d\n", i+1, nb_layers, grid_dimensions[i]); + + + + + // Reset tracking parameters + nb_associated = 0; + snprintf(line, sizeof(line), ""); + + while ( (nb_associated == 0) ) + { + printf("Please enter a valid layer factor value for layer %d/%d. It should be a double value. Your input : ", i+1, nb_layers); + + fgets(line, INPUT_MAX_SIZE, stdin); + + nb_associated = sscanf(line, " %lf", &temp_d); + if (nb_associated == 0) + { + printf("%s/!\\ Input Error : could not convert line '%s' as a double value. /!\\%s\n", RED_COLOR, line, DEFAULT_COLOR); + } + } + + layers_factors[i] = temp_d; + printf("Layer factor %d/%d set to %lf\n\n", i+1, nb_layers, layers_factors[i]); + } + + + + // Summary of the values set + + printf("Gradient grid sizes (-1) are finally set to the following values :\n"); + for (int i = 0; i < nb_layers; i++) + { + if (i == 0) + { + printf("%s{%d, ", GREEN_COLOR, grid_dimensions[i]); + } + else if (i < nb_layers - 1) + { + printf("%d, ", grid_dimensions[i]); + } + else + { + printf("%d}%s\n", grid_dimensions[i], DEFAULT_COLOR); + } + } + int final_size = lcmOfArray(nb_layers, grid_dimensions); + printf("It will lead to square chunks of sizes %s%d%s\n\n", GREEN_COLOR, final_size, DEFAULT_COLOR); + + + + + printf("Layers factors are finally set to the following values :\n"); + for (int i = 0; i < nb_layers; i++) + { + if (i == 0) + { + printf("%s{%lf, ", GREEN_COLOR, layers_factors[i]); + } + else if (i < nb_layers - 1) + { + printf("%lf, ", layers_factors[i]); + } + else + { + printf("%lf}%s\n\n", layers_factors[i], DEFAULT_COLOR); + } + } + + + + //? --- Manage final missing parameter : sea_level ---------------------------------------------------------------------------------------------- + + if ( (sea_level_affected != 1) ) + { + int nb_associated = 0; + char line[INPUT_MAX_SIZE] = ""; + + while ( sea_level_affected != 1 ) + { + printf("Please enter a valid sea_level value for the map generation. It should be a double value. Your input : "); + + fgets(line, INPUT_MAX_SIZE, stdin); + + nb_associated = sscanf(line, " %lf", &sea_level); + if (nb_associated == 0) + { + printf("%s/!\\ Input Error : could not convert line '%s' as a double value. /!\\%s\n", RED_COLOR, line, DEFAULT_COLOR); + } + else + { + sea_level_affected = 1; + } + } + } + printf("%sSea level is set to %lf%s\n\n", GREEN_COLOR, sea_level, DEFAULT_COLOR); + + + + + + //? --- Generation ------------------------------------------------------------------------------------------------------------------------------ + + // Required memory space + long int mem_space_by_chunk = sizeof(chunk) + final_size * final_size * sizeof(double) + nb_layers * (sizeof(double) + sizeof(layer*)) + + sizeof(layer) + final_size * final_size * sizeof(double); + + for (int i = 0; i < nb_layers; i++) + { + mem_space_by_chunk += sizeof(gradientGrid) + grid_dimensions[i] * grid_dimensions[i] * sizeof(vector); + } + + long int total_memory_space_used = sizeof(completeMap) + map_width * final_size * map_height * final_size * (sizeof(double) + sizeof(color*) + sizeof(color)) + + sizeof(map) + map_width * final_size * map_height * final_size * sizeof(double) + + map_width * map_height * (sizeof(chunk*) + mem_space_by_chunk); + + printf("The complete memory space used will be around %s%ld bytes%s.\n", BLUE_COLOR, total_memory_space_used, DEFAULT_COLOR); + + + int user_input = -1; + int temp = -1; + int nb_associated = 0; + char line[INPUT_MAX_SIZE] = ""; + + while ( temp != 0 && temp != 1 ) + { + printf("Do you want to continue ? (1 for Yes, 0 for No) -> "); + + fgets(line, INPUT_MAX_SIZE, stdin); + + nb_associated = sscanf(line, " %d", &temp); + if (nb_associated == 0) + { + printf("%s/!\\ Input Error : could not convert line '%s' as an integer value. /!\\%s\n", RED_COLOR, line, DEFAULT_COLOR); + } + else if (temp != 0 && temp != 1) + { + printf("%s/!\\ Value Error : please enter 0 to cancel or 1 to proceed with the generation. /!\\%s\n", RED_COLOR, DEFAULT_COLOR); + } + } + + user_input = temp; + printf("\n"); + + if (user_input != 1) + { + printf("The process was cancelled.\n"); + return 0; // The process was correctly cancelled. + } + + + + // Whether to display loading bars or not + + int display_loading = -1; // boolean + + // Reset tracking parameters + temp = -1; + nb_associated = 0; + snprintf(line, sizeof(line), ""); + + while ( temp != 0 && temp != 1 ) + { + printf("Do you want to print loading bars during the generation ? (1 for Yes, 0 for No) -> "); + + fgets(line, INPUT_MAX_SIZE, stdin); + + nb_associated = sscanf(line, " %d", &temp); + if (nb_associated == 0) + { + printf("%s/!\\ Input Error : could not convert line '%s' as an integer value. /!\\%s\n", RED_COLOR, line, DEFAULT_COLOR); + } + else if (temp != 0 && temp != 1) + { + printf("%s/!\\ Value Error : please enter 0 to hide loading bars or 1 to show them. /!\\%s\n", RED_COLOR, DEFAULT_COLOR); + } + } + + display_loading = temp; + + + // TODO : Ask for a seed input + + + printf("Generating a complete map with given parameters...\n"); + clock_t start_time = clock(); + + + completeMap* new_complete_map = fullGen(nb_layers, grid_dimensions, layers_factors, map_width, map_height, sea_level, display_loading); + printf("Generation complete!\n"); + + double total_time = (double) (clock() - start_time) / CLOCKS_PER_SEC; + printf("The whole map generation took a total of %lf second(s) in CPU time\n", total_time); + + + // Whether to save the file or not + + int save_map = -1; // boolean + + // Reset tracking parameters + temp = -1; + nb_associated = 0; + snprintf(line, sizeof(line), ""); + + while ( temp != 0 && temp != 1 ) + { + printf("Do you want to save the generated map ? (1 for Yes, 0 for No) -> "); + + fgets(line, INPUT_MAX_SIZE, stdin); + + nb_associated = sscanf(line, " %d", &temp); + if (nb_associated == 0) + { + printf("%s/!\\ Input Error : could not convert line '%s' as an integer value. /!\\%s\n", RED_COLOR, line, DEFAULT_COLOR); + } + else if (temp != 0 && temp != 1) + { + printf("%s/!\\ Value Error : please enter 0 to hide loading bars or 1 to show them. /!\\%s\n", RED_COLOR, DEFAULT_COLOR); + } + } + + save_map = temp; + + + if (save_map==1) { + printf("Save file creation : \n"); + start_time = clock(); + + // Get folder name + temp = -1; + nb_associated = 0; + snprintf(line, sizeof(line), ""); + + char folder_path[100] = "../saves"; + + printf("Folder name (defaults to %s) -> ", folder_path); + fgets(line, INPUT_MAX_SIZE, stdin); + nb_associated = sscanf(line, " %s", folder_path); + if (nb_associated == 0) + { + printf("%s/!\\ Input Error : could not convert line '%s' as a path name. /!\\%s\n", RED_COLOR, line, DEFAULT_COLOR); + } + + // Get file name + temp = -1; + nb_associated = 0; + snprintf(line, sizeof(line), ""); + + char name[100] = "save.data"; + + printf("File name (defaults to %s) -> ", name); + fgets(line, INPUT_MAX_SIZE, stdin); + + nb_associated = sscanf(line, " %s", name); + if (nb_associated == 0) + { + printf("%s/!\\ Input Error : could not convert line '%s' as a file name. /!\\%s\n", RED_COLOR, line, DEFAULT_COLOR); + } + + // Get bytes + bytes b; + bytes cb = bytesCompleteMap(new_complete_map); + b.size = cb.size +1; + b.start = 0; + b.bytes = malloc(b.size * sizeof(byte)); + b.bytes[0] = BYTES_VERSION; + concatBytes(b,cb,1); + + // Write binary file + writeBytesFile(b,folder_path,name); + + // Deallocating + freeBytes(b); + freeBytes(cb); + + total_time = (double) (clock() - start_time) / CLOCKS_PER_SEC; + printf("The map saving took a total of %lf second(s) in CPU time\n", total_time); + } + + + + printf("Deallocating now...\n"); + freeCompleteMap(new_complete_map); + + + + return 0; +} \ No newline at end of file diff --git a/C/src/map.c b/C/src/map.c index 14347fc..dffc169 100644 --- a/C/src/map.c +++ b/C/src/map.c @@ -38,7 +38,10 @@ double* getMapValue(map* map, int width_idx, int height_idx) return NULL; } - map_value = (map->map_values) + height_idx * width + width_idx; + + chunk* current_chunk = getChunk(map, width_idx/map->chunk_width, height_idx/map->chunk_height); + + map_value = getChunkValue(current_chunk, width_idx%map->chunk_width, height_idx%map->chunk_height); } return map_value; @@ -85,6 +88,33 @@ chunk* getVirtualChunk(map* map, int width_idx, int height_idx) +double* getFullMap(map* map) +{ + double* altitude = NULL; + + if (map != NULL) + { + int width = map->map_width * map->chunk_width; + int height = map->map_height * map->chunk_height; + + altitude = calloc(width * height, sizeof(double)); + + for (int i = 0; i < height; i++) + { + for (int j = 0; j < width; j++) + { + altitude[i*width + j] = *getMapValue(map, j, i); + } + } + } + + return altitude; +} + + + + + double interpolate2D(double a1, double a2, double a3, double a4, double x, double y) { return a1 + (a2 - a1) * smoothstep(x) + (a3 - a1) * smoothstep(y) + (a1 + a4 - a2 - a3) * smoothstep(x) * smoothstep(y); @@ -159,18 +189,19 @@ map* addMeanAltitude(map* p_map, unsigned int display_loading) clock_t start_adding_time = clock(); - for (int i=0; i=chunk_width*0.5) && (j!=0 || pj>=chunk_height*0.5)) @@ -196,9 +227,11 @@ map* addMeanAltitude(map* p_map, unsigned int display_loading) } } - display_loading-=2; if (display_loading != 0) { + display_loading-=2; + + double total_time = (double) (clock() - start_time)/CLOCKS_PER_SEC; char final_string[200] = ""; @@ -219,9 +252,9 @@ map* addMeanAltitude(map* p_map, unsigned int display_loading) -map* newMapFromChunks(int map_width, int map_height, chunk* chunks[map_width * map_height], chunk* virtual_chunks[(map_height+2+map_width+2)*2-4], unsigned int display_loading) +map* newMapFromChunks(int map_width, int map_height, chunk* chunks[map_width * map_height], chunk* virtual_chunks[(map_height+2+map_width+2)*2-4], bool regenerate, unsigned int display_loading) { - clock_t start_time = clock(); + // clock_t start_time = clock(); if (chunks != NULL && map_width > 0 && map_height > 0) { @@ -260,35 +293,35 @@ map* newMapFromChunks(int map_width, int map_height, chunk* chunks[map_width * m new_map->chunk_height = chunk_height; - int width = map_width * chunk_width; - int height = map_height * chunk_height; - double* map_values = calloc(width * height, sizeof(double)); + // int width = map_width * chunk_width; + // int height = map_height * chunk_height; + // double* map_values = calloc(width * height, sizeof(double)); - new_map->map_values = map_values; + // new_map->map_values = map_values; - // Copy the correct altitude values - for (int i = 0; i < height; i++) - { - for (int j = 0; j < width; j++) - { - double* value = getMapValue(new_map, j, i); - chunk* current_chunk = getChunk(new_map, j/chunk_width, i/chunk_height); + // // Copy the correct altitude values + // for (int i = 0; i < height; i++) + // { + // for (int j = 0; j < width; j++) + // { + // double* value = getMapValue(new_map, j, i); + // chunk* current_chunk = getChunk(new_map, j/chunk_width, i/chunk_height); - *value = *getChunkValue(current_chunk, j%chunk_width, i%chunk_height); + // *value = *getChunkValue(current_chunk, j%chunk_width, i%chunk_height); - if (display_loading != 0) - { - int nb_indents = display_loading - 1; + // if (display_loading != 0) + // { + // int nb_indents = display_loading - 1; - char base_str[100] = "Generating map values... "; + // char base_str[100] = "Generating map values... "; - predefined_loading_bar(j + i * width, width * height - 1, NUMBER_OF_SEGMENTS, base_str, nb_indents, start_time); - } - } - } + // predefined_loading_bar(j + i * width, width * height - 1, NUMBER_OF_SEGMENTS, base_str, nb_indents, start_time); + // } + // } + // } - new_map = addMeanAltitude(new_map,display_loading); + if (regenerate) new_map = addMeanAltitude(new_map,display_loading); return new_map; } @@ -389,13 +422,16 @@ map* newMap(int number_of_layers, int gradGrids_width[number_of_layers], int gra predefined_loading_bar(j+1,(map_height+2+map_width+2)*2-4, NUMBER_OF_SEGMENTS, base_str, nb_indents, v_start_time); } - current_chunk = newVirtualChunk(number_of_layers, gradGrids_width, gradGrids_height, size_factors, layers_factors); + int width = (gradGrids_width[0] - 1) * size_factors[0]; + int height = (gradGrids_height[0] - 1) * size_factors[0]; + + current_chunk = newVirtualChunk(width, height, true); virtual_chunks[j] = current_chunk; } // Generating the map from the new chunks - map* new_map = newMapFromChunks(map_width, map_height, chunks, virtual_chunks, display_loading); + map* new_map = newMapFromChunks(map_width, map_height, chunks, virtual_chunks, true, display_loading); if (display_loading == 1) { @@ -434,23 +470,130 @@ map* copyMap(map* p_map) } } - int n = res->chunk_width*res->map_width; - int m = res->chunk_height*res->map_height; + // int n = res->chunk_width*res->map_width; + // int m = res->chunk_height*res->map_height; - res->map_values = calloc(n*m, sizeof(double)); + // res->map_values = calloc(n*m, sizeof(double)); - for (int i=0; imap_width * map->map_height]; + int chunk_size=0; + for (int i=0; imap_height; i++) { + for (int j=0; jmap_width; j++) { + bytes bytes_chunk = (bytesChunk(getChunk(map,j,i))); + bytes_chunks[i*map->map_width+j] = calloc(1,sizeof(bytes_chunk)); + *bytes_chunks[i*map->map_width+j] = bytes_chunk; + chunk_size += bytes_chunk.size; } } + + int nbr = (map->map_height+2+map->map_width+2)*2-4; + bytes* bytes_vchunks[nbr]; + int vchunk_size=0; + chunk** virtual_chunks = map->virtual_chunks; //! map->vitual_chunks changes value during execution (???) + for (int i=0; ivirtual_chunks,virtual_chunks); + bytes bytes_vchunk = (bytesChunk(virtual_chunks[i])); + bytes_vchunks[i] = calloc(1,sizeof(bytes_vchunk)); + *bytes_vchunks[i] = bytes_vchunk; + vchunk_size += bytes_vchunk.size; + } + + bytes_str.bytes = calloc(1 + 2*INT_BITS_NBR/8 + chunk_size + vchunk_size, sizeof(byte)); + bytes_str.size = 1 + 2*INT_BITS_NBR/8 + chunk_size + vchunk_size; + bytes_str.start = 0; + + bytes_str.bytes[0] = MAP_ENCODING; + + bytes a = bytesInt(map->map_height); + concatBytes(bytes_str, a, 1); + freeBytes(a); + a = bytesInt(map->map_width); + concatBytes(bytes_str, a, 1+INT_BITS_NBR/8); + freeBytes(a); + + chunk_size=0; + for (int i=0; imap_height; i++) { + for (int j=0; jmap_width; j++) { + concatBytes(bytes_str, *(bytes_chunks[i*map->map_width+j]), 1+2*INT_BITS_NBR/8+chunk_size); + chunk_size += (bytes_chunks[i*map->map_width+j])->size; + freeBytes(*(bytes_chunks[i*map->map_width+j])); + free(bytes_chunks[i*map->map_width+j]); + } + } + + vchunk_size=0; + for (int i=0; isize; + freeBytes(*(bytes_vchunks[i])); + free(bytes_vchunks[i]); + } + + return bytes_str; - return res; } +tuple_obj_bytes nextMap(bytes bytes) { + tuple_obj_bytes res; + + if (bytes.bytes[bytes.start]==MAP_ENCODING) { + bytes.start += 1; + + tuple_obj_bytes a = nextInt(bytes); + int map_height = *((int*)a.object); + bytes = a.bytes; + free(a.object); + a = nextInt(bytes); + int map_width = *((int*)a.object); + bytes = a.bytes; + free(a.object); + + int nbr = (map_height+2+map_width+2)*2-4; + chunk* chunks [map_height*map_width]; + + chunk* vchunks [nbr]; + + for (int i=0; imap_width; int map_height = map->map_height; - if (map->map_values != NULL) - { - free(map->map_values); - } + // if (map->map_values != NULL) + // { + // free(map->map_values); + // } if (map->chunks != NULL) diff --git a/C/src/mapGenerator.c b/C/src/mapGenerator.c index cd6ce87..eda90f7 100644 --- a/C/src/mapGenerator.c +++ b/C/src/mapGenerator.c @@ -2,8 +2,8 @@ * @file mapGenerator.c * @author Zyno and BlueNZ * @brief mapGenerator structure and functions implementations - * @version 0.2 - * @date 2024-06-19 + * @version 0.3 + * @date 2024-07-31 * */ @@ -184,33 +184,21 @@ color* colorize(double value, double sea_level, double min_value, double max_val //? Show red dots on zero values. Could be removed. if (value == 0) { - new_color->red_int = 255; - new_color->green_int = 0; - new_color->blue_int = 0; - - new_color->red_float = 1.; - new_color->green_float = 0; - new_color->blue_float = 0; + new_color->red = 255; + new_color->green = 0; + new_color->blue = 0; } else if (value <= sea_level) { - new_color->red_int = 50; - new_color->green_int = 50; - new_color->blue_int = s; - - new_color->red_float = 1./255 * 50; - new_color->green_float = 1./255 * 50; - new_color->blue_float = 1./255 * s; + new_color->red = 50; + new_color->green = 50; + new_color->blue = s; } else { - new_color->red_int = 50; - new_color->green_int = i; - new_color->blue_int = 50; - - new_color->red_float = 1./255 * 50; - new_color->green_float = 1./255 * i; - new_color->blue_float = 1./255 * 50; + new_color->red = 50; + new_color->green = i; + new_color->blue = 50; } return new_color; @@ -512,6 +500,131 @@ completeMap* fullGen(int number_of_layers, int gradGrids_dimension[number_of_lay + +bytes bytesColor(color c) { + bytes res; + res.bytes = malloc(3); + res.size = 3; + res.start = 0; + + bytes a = bytesUint8(c.red); + concatBytes(res,a,0); + freeBytes(a); + + a = bytesUint8(c.green); + concatBytes(res,a,1); + freeBytes(a); + + a = bytesUint8(c.blue); + concatBytes(res,a,2); + freeBytes(a); + + return res; +} + +tuple_obj_bytes nextColor(bytes bytes) { + tuple_obj_bytes res; + + color* c = malloc(sizeof(color)); + + tuple_obj_bytes a = nextUint8(bytes); + c->red = *((__uint8_t*)a.object); + bytes = a.bytes; + free(a.object); + + a = nextUint8(bytes); + c->green = *((__uint8_t*)a.object); + bytes = a.bytes; + free(a.object); + + a = nextUint8(bytes); + c->blue = *((__uint8_t*)a.object); + bytes = a.bytes; + free(a.object); + + res.object = (object)c; + res.bytes = bytes; + + return res; +} + +bytes bytesCompleteMap(completeMap* cmap) { + bytes bytes_str; + + bytes bytes_map = bytesMap(cmap->map); + + bytes_str.bytes = calloc(1 + bytes_map.size + FLOAT_BITS_NBR/8 + cmap->height*cmap->width * (FLOAT_BITS_NBR/8 + 3), sizeof(byte)); + bytes_str.size = 1 + bytes_map.size + FLOAT_BITS_NBR/8 + cmap->height*cmap->width * (FLOAT_BITS_NBR/8 + 3); + bytes_str.start = 0; + + bytes_str.bytes[0] = COMPLETE_MAP_ENCODING; + + concatBytes(bytes_str,bytes_map,1); + freeBytes(bytes_map); + + bytes a = bytesDouble(cmap->sea_level); + concatBytes(bytes_str, a, 1+bytes_map.size); + freeBytes(a); + + for (int i=0; iheight; i++) { + for (int j=0; jwidth; j++) { + a = bytesDouble(*getCompleteMapSeaValue(cmap,j,i)); + concatBytes(bytes_str, a, 1+bytes_map.size+FLOAT_BITS_NBR/8 + (FLOAT_BITS_NBR/8+3)*(i*cmap->width+j)); + freeBytes(a); + a = bytesColor(*getCompleteMapColor(cmap,j,i)); + concatBytes(bytes_str, a, 1+bytes_map.size+2*FLOAT_BITS_NBR/8 + (FLOAT_BITS_NBR/8+3)*(i*cmap->width+j)); + freeBytes(a); + } + } + + return bytes_str; + +} + +tuple_obj_bytes nextCompleteMap(bytes bytes) { + tuple_obj_bytes res; + + if (bytes.bytes[bytes.start]==COMPLETE_MAP_ENCODING) { + bytes.start += 1; + + completeMap* cmap = calloc(1,sizeof(completeMap)); + + tuple_obj_bytes a = nextMap(bytes); + cmap->map = ((map*)a.object); + bytes = a.bytes; + + cmap->height = cmap->map->chunk_height * cmap->map->map_height; + cmap->width = cmap->map->chunk_width * cmap->map->map_width; + + a = nextDouble(bytes); + cmap->sea_level = *((double*)a.object); + bytes = a.bytes; + free(a.object); + + cmap->color_map = calloc(cmap->height*cmap->width,sizeof(color*)); + cmap->sea_values = calloc(cmap->height*cmap->width,sizeof(double)); + + for (int i=0; iheight; i++) { + for (int j=0; jwidth; j++) { + a = nextDouble(bytes); + *getCompleteMapSeaValue(cmap,j,i) = *((double*)a.object); + bytes = a.bytes; + free(a.object); + a = nextColor(bytes); + cmap->color_map[i*cmap->width+j] = ((color*)a.object); + bytes = a.bytes; + } + } + + res.object = (object) cmap; + res.bytes = bytes; + } + + return res; +} + + + void writeSeaMapFile(completeMap* completeMap, char path[]) { FILE* f = NULL; @@ -565,49 +678,7 @@ void writeSeaMapFile(completeMap* completeMap, char path[]) -void writeColorIntMapFile(color** color_map, int width, int height, char path[]) -{ - FILE* f = NULL; - - f = fopen(path, "w"); - - if (f != NULL) - { - fprintf(f, "Color Int Map\n"); - - // Writing the parameters - fprintf(f, "width=%d\nheight=%d\n", width, height); - - // Writing the vectors - for (int i = 0; i < height; i++) - { - for (int j = 0; j < width; j++) - { - color* color = color_map[j + i * width]; - - if (j != width - 1) - { - fprintf(f, "(%d,%d,%d)\t", color->red_int, color->green_int, color->blue_int); - } - else - { - fprintf(f, "(%d,%d,%d)\n", color->red_int, color->green_int, color->blue_int); - } - } - } - - fclose(f); - } - else - { - printf("%sERROR : could not open file in writing mode at path '%s'%s\n", RED_COLOR, path, DEFAULT_COLOR); - return; - } -} - - - -void writeColorFloatMapFile(color** color_map, int width, int height, char path[]) +void writeColorMapFile(int width, int height, color* color_map[width * height], char path[]) { FILE* f = NULL; @@ -615,7 +686,7 @@ void writeColorFloatMapFile(color** color_map, int width, int height, char path[ if (f != NULL) { - fprintf(f, "Color Float Map\n"); + fprintf(f, "Color Map\n"); // Writing the parameters fprintf(f, "width=%d\nheight=%d\n", width, height); @@ -629,11 +700,11 @@ void writeColorFloatMapFile(color** color_map, int width, int height, char path[ if (j != width - 1) { - fprintf(f, "(%.4f,%.4f,%.4f)\t", color->red_float, color->green_float, color->blue_float); + fprintf(f, "(%3d,%3d,%3d)\t", color->red, color->green, color->blue); } else { - fprintf(f, "(%.4f,%.4f,%.4f)\n", color->red_float, color->green_float, color->blue_float); + fprintf(f, "(%3d,%3d,%3d)\n", color->red, color->green, color->blue); } } } @@ -658,7 +729,7 @@ void writeCompleteMapFiles(completeMap* complete_map, char folder_path[]) // Generates a directory if it does not exist if (stat(folder_path, &st) == -1) { - // Should check what 0700 permissions are exactly, but that's how it works to create a directory. + // 0700 is rwx permission for owner only. mkdir(folder_path, 0700); } @@ -668,16 +739,10 @@ void writeCompleteMapFiles(completeMap* complete_map, char folder_path[]) writeSeaMapFile(complete_map, sea_map_path); - // Generates the color int map file - char color_int_path[200] = ""; - snprintf(color_int_path, sizeof(color_int_path), "%scolor_int_map.txt", folder_path); - writeColorIntMapFile(complete_map->color_map, complete_map->width, complete_map->height, color_int_path); - - - // Generates the color float map file - char color_float_path[200] = ""; - snprintf(color_float_path, sizeof(color_float_path), "%scolor_float_map.txt", folder_path); - writeColorFloatMapFile(complete_map->color_map, complete_map->width, complete_map->height, color_float_path); + // Generates the color map file + char color_path[200] = ""; + snprintf(color_path, sizeof(color_path), "%scolor_map.txt", folder_path); + writeColorMapFile(complete_map->width, complete_map->height, complete_map->color_map, color_path); } diff --git a/C/src/test_chunk.c b/C/src/test_chunk.c index 3e40563..eca82bd 100644 --- a/C/src/test_chunk.c +++ b/C/src/test_chunk.c @@ -50,10 +50,10 @@ int main() layer_height1, layer_width1, layer_height2, layer_width2); - layer* layer1 = newLayerFromGradient(gradGrid1, sizeFactor1, display_loading); + layer* layer1 = newLayerFromGradient(gradGrid1, sizeFactor1, true, display_loading); // printLayer(layer1); - layer* layer2 = newLayerFromGradient(gradGrid2, sizeFactor2, display_loading); + layer* layer2 = newLayerFromGradient(gradGrid2, sizeFactor2, true, display_loading); // printLayer(layer2); diff --git a/C/src/test_interpreter.c b/C/src/test_interpreter.c new file mode 100644 index 0000000..0b88f18 --- /dev/null +++ b/C/src/test_interpreter.c @@ -0,0 +1,191 @@ +/** + * @file test_interpreter.c + * @author BlueNZ + * @brief a testing script for the interpreter functions + * @version 0.1 + * @date 2024-07-27 + * + */ + +#include +#include + +#include "interpreter.h" +#include "gradientGrid.h" +#include "layer.h" +#include "chunk.h" +#include "map.h" +#include "mapGenerator.h" + +int main() { + printf("Testing binary encoding and decoding: \n\n\n"); + + __uint8_t d = 1; + int a=0,e=-5683; + double b=1.3,c=-0.394383; + + printf("\tEncoding some numbers : %d, %d, %d, %f, %f\n",d,a,e,b,c); + + bytes db=bytesUint8(d); + bytes ab=bytesInt(a); + bytes eb=bytesInt(e); + bytes bb=bytesDouble(b); + bytes cb=bytesDouble(c); + + printf("\t\tUint8 : %d, ",d); + printBytes(db,"","\n"); + printf("\t\tint : %d, ",a); + printBytes(ab,"","\n"); + printf("\t\tint : %d, ",e); + printBytes(eb,"","\n"); + printf("\t\tdouble : %lf, ",b); + printBytes(bb,"","\n"); + printf("\t\tdouble : %lf, ",c); + printBytes(cb,"","\n"); + + object od = nextUint8(db).object; + object oa = nextInt(ab).object; + object oe = nextInt(eb).object; + object ob = nextDouble(bb).object; + object oc = nextDouble(cb).object; + + d = *((__uint8_t*)od); + a = *((int*)oa); + e = *((int*)oe); + b = *((double*)ob); + c = *((double*)oc); + + printf("\tDecoding numbers : %d, %d, %d, %f, %f\n\n\n",d,a,e,b,c); + + + + printf("\tTesting for gradients:\n"); + gradientGrid* grid = newRandomGradGrid(1,1,0); + printGradientGrid(grid); + + bytes gridb = bytesGradientGrid(grid); + printBytes(gridb, "", "\n"); + + gradientGrid* gridd = ((gradientGrid*)nextGradientGrid(gridb).object); + printGradientGrid(gridd); + + printf("\n\n"); + + + printf("\tTesting for layers:\n"); + layer* lay = newLayer(2,2,3,0); + printLayer(lay); + + bytes laybf = bytesLayer(lay,false); + printBytes(laybf,"","\n"); + bytes laybt = bytesLayer(lay,true); + printBytes(laybt,"","\n"); + + layer* laydff = ((layer*)nextLayer(laybf,false).object); + printLayer(laydff); + layer* laydft = ((layer*)nextLayer(laybf,true).object); + printLayer(laydft); + layer* laydtf = ((layer*)nextLayer(laybt,false).object); + printLayer(laydtf); + layer* laydtt = ((layer*)nextLayer(laybt,true).object); + printLayer(laydtt); + + + + printf("\n\n\tTesting for chunks:\n"); + int grid_size[] = {2}; + int size_factor[] = {3}; + double layer_factor[] = {1.}; + chunk* chk = newChunk(1,grid_size,grid_size,size_factor,layer_factor,0); + printChunk(chk); + + bytes chkb = bytesChunk(chk); + printBytes(chkb,"","\n"); + + chunk* chkd = ((chunk*)nextChunk(chkb).object); + printChunk(chkd); + + + + printf("\n\n\tTesting for maps:\n"); + map* m = newMap(1,grid_size,grid_size,size_factor,layer_factor,1,1,0); + printMap(m); + + bytes mb = bytesMap(m); + printBytes(mb,"","\n"); + + map* md = ((map*)nextMap(mb).object); + printMap(md); + + + + printf("\n\n\tTesting for colors:\n"); + color col = {255,0,0}; + printf("color: %u - %u - %u\n", col.red, col.green, col.blue); + bytes colb = bytesColor(col); + printBytes(colb,"","\n"); + color* cold = ((color*)nextColor(colb).object); + printf("color: %u - %u - %u\n", cold->red, cold->green, cold->blue); + + printf("\n\n\tTesting for completeMaps:\n"); + completeMap* cmap = newCompleteMap(1,grid_size,grid_size,size_factor,layer_factor,1,1,0,0); + printMap(cmap->map); + bytes cmapb = bytesCompleteMap(cmap); + printBytes(cmapb,"","\n"); + completeMap* cmapd = ((completeMap*)nextCompleteMap(cmapb).object); + printMap(cmapd->map); + + + + printf("\n\n\tTesting file writing and reading...\n"); + writeBytesFile(cmapb, "../saves","test.data"); + bytes cmapbr = readBytesFile("../saves/test.data"); + printBytes(cmapb,"","\n"); + printBytes(cmapbr,"","\n"); + + + + printf("\n\nDeallocating...\n"); + free(od); + free(oa); + free(oe); + free(ob); + free(oc); + freeBytes(db); + freeBytes(ab); + freeBytes(eb); + freeBytes(bb); + freeBytes(cb); + + + freeGradGrid(grid); + freeBytes(gridb); + freeGradGrid(gridd); + + + freeLayer(lay); + freeBytes(laybf); + freeBytes(laybt); + freeLayer(laydff); + freeLayer(laydft); + freeLayer(laydtf); + freeLayer(laydtt); + + freeChunk(chk); + freeBytes(chkb); + freeChunk(chkd); + + freeMap(m); + freeBytes(mb); + freeMap(md); + + freeBytes(colb); + free(cold); + + freeCompleteMap(cmap); + freeBytes(cmapb); + freeCompleteMap(cmapd); + freeBytes(cmapbr); + + return 0; +} \ No newline at end of file diff --git a/C/src/test_layer.c b/C/src/test_layer.c index 979d74e..1c9405b 100644 --- a/C/src/test_layer.c +++ b/C/src/test_layer.c @@ -34,7 +34,7 @@ int main() printf("Creating a layer of size (height x width) = (%d x %d)\n", sizeFactor*(height-1), sizeFactor*(width-1)); - layer* layer1 = newLayerFromGradient(gradGrid, sizeFactor, display_loading); + layer* layer1 = newLayerFromGradient(gradGrid, sizeFactor, true, display_loading); printLayer(layer1); diff --git a/Python/chunk.py b/Python/chunk.py index 6057b0b..40fc8a8 100644 --- a/Python/chunk.py +++ b/Python/chunk.py @@ -8,6 +8,7 @@ import random as rd from gradientGrid import GradientGrid from layer import Layer +import interpreter as interp @@ -17,11 +18,15 @@ class Chunk: Chunk : ------------- The Chunk class used to generate an altitude map by superposition of multiple layers. + + Note : + Layers' altitude values are then free'd. """ #? -------------------------- Static ------------------------- # + CHUNK_ENCODING = b'\x03' ALT_PRINTING_DECIMALS = 4 ALT_PRINTING_FORMAT = " {{: .{}f}} ".format(ALT_PRINTING_DECIMALS) @@ -240,7 +245,7 @@ def generateChunkFromGradientGrids(grids: list[GradientGrid], size_factors: list @staticmethod - def newVirtualChunk(chunk_width: int, chunk_height: int) -> Chunk: + def newVirtualChunk(chunk_width: int, chunk_height: int, base_altitude: int = None) -> Chunk: """Generates a new virtual chunk. Args: @@ -257,7 +262,8 @@ def newVirtualChunk(chunk_width: int, chunk_height: int) -> Chunk: virtual_chunk.width = chunk_width virtual_chunk.height = chunk_height - virtual_chunk.base_altitude = Chunk.generateBaseAltitude() + if base_altitude!=None: virtual_chunk.base_altitude = base_altitude + else: virtual_chunk.base_altitude = Chunk.generateBaseAltitude() return virtual_chunk @@ -265,28 +271,108 @@ def newVirtualChunk(chunk_width: int, chunk_height: int) -> Chunk: @staticmethod - def write(path: str, chunk: Chunk) -> None: - #TODO - pass + def write(chunk: Chunk, virtual: bool=False, path: str=None, append: bool=False) -> bytes: + """Encodes a Chunk object into a binary file or string. + + Args: + chunk (Chunk): the chunk object to encode + virtual (bool): is it a virtual chunk. Defaults to False. + path (str, optional): path to the file. Defaults to None. + append (bool, optional): should it append the binary string to the end of the file. Defaults to False. + + Returns: + bytes: the encoded bytes + """ + bytes_str : bytes = b'' + bytes_str += Chunk.CHUNK_ENCODING + bytes_str += interp.bytesNumber(chunk.base_altitude) + bytes_str += interp.bytesNumber(chunk.height) + bytes_str += interp.bytesNumber(chunk.width) + if virtual: #uninitialised values + bytes_str += interp.BYTES_TRUE + else: + bytes_str += interp.BYTES_FALSE + bytes_str += interp.bytesNumber(len(chunk.layers_factors)) + for x in chunk.layers_factors: + bytes_str += interp.bytesNumber(float(x)) + for x in chunk.layers: + bytes_str += Layer.write(x) + for i in range(chunk.height): + for j in range(chunk.width): + bytes_str += interp.bytesNumber(chunk.altitude[i,j]) + if path!=None: + if append: f=open(path,"ab") + else: f=open(path,"wb") + f.write(bytes_str) + f.close() + return bytes_str @staticmethod - def read(path: str) -> Chunk: - #TODO - return None + def read(path: str, bytes_in : bytes=None) -> tuple[Chunk, bytes]: + """Decodes a Chunk object from a binary file or a bytes string. + + Args: + path (str, optional): path to the binary file. Defaults to None. + bytes_in (bytes, optional): encoded bytes. Defaults to None. + + Returns: + tuple[Chunk, bytes]: the chunk object and remaining bytes + """ + bytes_str : bytes + if path!=None: + f=open(path,'rb') + bytes_str=f.read() + f.close() + if bytes_str[0:1]==Chunk.CHUNK_ENCODING: bytes_str=bytes_str[1:] + else: bytes_str=None + elif bytes_in!=None and bytes_in[0:1]==Chunk.CHUNK_ENCODING: + bytes_str=bytes_in[1:] + else: bytes_str=None + + chunk : Chunk + + if bytes_str!=None: + base_altitude, bytes_str = interp.nextFloat(bytes_str) + height, bytes_str = interp.nextInt(bytes_str) + width, bytes_str = interp.nextInt(bytes_str) + if bytes_str[0:1]==interp.BYTES_TRUE: #is virtual chunk + chunk = Chunk.newVirtualChunk(width, height, base_altitude) + bytes_str = bytes_str[1:] + else: #is proper chunk + bytes_str = bytes_str[1:] + layer_number, bytes_str = interp.nextInt(bytes_str) + layers: list[Layer] = [] + layer_factors: list[float] = [] + for _ in range(layer_number): + x, bytes_str = interp.nextFloat(bytes_str) + layer_factors.append(x) + for _ in range(layer_number): + x, bytes_str = Layer.read(None,bytes_str) + layers.append(x) + chunk = Chunk(layers, layer_factors, False) + chunk.altitude = np.zeros((chunk.height,chunk.width)) + for i in range(chunk.height): + for j in range(chunk.width): + chunk.altitude[i,j], bytes_str = interp.nextFloat(bytes_str) + + else: chunk = None + + return chunk, bytes_str #? ------------------------ Instances ------------------------ # - def __init__(self, layers: list[Layer], layers_factors: list[float]) -> None: + def __init__(self, layers: list[Layer], layers_factors: list[float], regenerate: bool = True) -> None: """Initializes a new Chunk structure from the given list of already existing layers and factors. If layers should be generated in the process, please use one of the designated static methods. Args: - `layers` (list[Layer], optional): the list of existing chunks to be averaged to generate the chunk. - `layers_factors` (list[float], optional): the list of factors to be used in the weighted average to generate the chunk. + `layers` (list[Layer]): the list of existing chunks to be averaged to generate the chunk. + `layers_factors` (list[float]): the list of factors to be used in the weighted average to generate the chunk. + `regenerate`(bool, optional): should altitude values be (re)generated. Defaults to `True`. """ self.layers_factors = None @@ -335,7 +421,7 @@ def __init__(self, layers: list[Layer], layers_factors: list[float]) -> None: self.layers_factors = layers_factors.copy() # Compute altitude - self.regenerate() + self.regenerate(regenerate) @@ -364,8 +450,11 @@ def __str__(self) -> str: - def regenerate(self) -> None: + def regenerate(self, regenerate: bool = True) -> None: """Regenerates the altitude values of the chunk based on its `layers` and `layer_factors` parameters. + + Args: + `regenerate`(bool, optional): should altitude values be (re)generated. Defaults to `True`. """ #* -------------- Verifications : Begin -------------- @@ -416,17 +505,21 @@ def regenerate(self) -> None: #* -------------- Verifications : End -------------- - - self.altitude = np.zeros((self.height, self.width)) + if (regenerate): + self.altitude = np.zeros((self.height, self.width)) + + for i in range(len_fac): + fac = self.layers_factors[i] + layer = self.layers[i] + + self.altitude += fac * layer.altitude + + self.altitude /= factor_sum + self.base_altitude = Chunk.generateBaseAltitude() for i in range(len_fac): - fac = self.layers_factors[i] layer = self.layers[i] - - self.altitude += fac * layer.altitude - - self.altitude /= factor_sum - self.base_altitude = Chunk.generateBaseAltitude() + layer.altitude = None # Free memory now @@ -454,6 +547,9 @@ def regenerate(self) -> None: print(chunk) + print("\nTesting chunk encoding: ") + print(Chunk.write(chunk)) + print(Chunk.read(None,Chunk.write(chunk))[0]) print("Generating a south chunk from gradient grids structures :") diff --git a/Python/gradientGrid.py b/Python/gradientGrid.py index 2c7d2e1..bfba439 100644 --- a/Python/gradientGrid.py +++ b/Python/gradientGrid.py @@ -6,7 +6,7 @@ import numpy as np import random as rd - +import interpreter as interp @@ -28,6 +28,7 @@ class GradientGrid: #? -------------------------- Static ------------------------- # + GRID_ENCODING = b'\x01' MAX_INT_SEED = 1000000000000 # Maximum integer for the automatic random seed generation CURRENT_SEED = "a simple seed" # Current seed used by the randomizer @@ -52,22 +53,76 @@ def setRandomSeed(seed: int | float | str | bytes | bytearray | None = rd.randin @staticmethod - def write(path: str, grid: GradientGrid) -> None: - #TODO - pass + def write(grid: GradientGrid, path: str=None, append: bool=False) -> bytes: + """Encodes a GradientGrid object into a binary file or string. + + Args: + grid (GradientGrid): the grid object to encode + path (str, optional): path to the file. Defaults to None. + append (bool, optional): should it append the binary string to the end of the file. Defaults to False. + + Returns: + bytes: the encoded bytes + """ + bytes_str : bytes = b'' + bytes_str += GradientGrid.GRID_ENCODING + bytes_str += interp.bytesNumber(grid.height) + bytes_str += interp.bytesNumber(grid.width) + for i in range(grid.height): + for j in range(grid.width): + bytes_str += interp.bytesNumber(grid.vectors[i,j,0]) + bytes_str += interp.bytesNumber(grid.vectors[i,j,1]) + if path!=None: + if append: f=open(path,"ab") + else: f=open(path,"wb") + f.write(bytes_str) + f.close() + return bytes_str @staticmethod - def read(path: str) -> GradientGrid: - #TODO - return None + def read(path: str=None, bytes_in : bytes=None) -> tuple[GradientGrid, bytes]: + """Decodes a GradientGrid object from a binary file or a bytes string. + + Args: + path (str, optional): path to the binary file. Defaults to None. + bytes_in (bytes, optional): encoded bytes. Defaults to None. + + Returns: + tuple[GradientGrid, bytes]: the grid object and remaining bytes + """ + bytes_str : bytes + if path!=None: + f=open(path,'rb') + bytes_str=f.read() + f.close() + if bytes_str[0:1]==GradientGrid.GRID_ENCODING: bytes_str=bytes_str[1:] + else: bytes_str=None + elif bytes_in!=None and bytes_in[0:1]==GradientGrid.GRID_ENCODING: + bytes_str=bytes_in[1:] + else: bytes_str=None + + grid : GradientGrid + + if bytes_str!=None: + height, bytes_str = interp.nextInt(bytes_str) + width, bytes_str = interp.nextInt(bytes_str) + grid = GradientGrid(width,height,False) + for i in range(grid.height): + for j in range(grid.width): + grid.vectors[i,j,0], bytes_str = interp.nextFloat(bytes_str) + grid.vectors[i,j,1], bytes_str = interp.nextFloat(bytes_str) + + else: grid = None + + return grid, bytes_str #? ------------------------ Instances ------------------------ # - def __init__(self, width: int = 2, height: int = 2, adjacent_grids: tuple = (None, None, None, None)) -> None: + def __init__(self, width: int = 2, height: int = 2, regenerate: bool = True, adjacent_grids: tuple = (None, None, None, None)) -> None: """Initialize a new random GradientGrid instance with given width and height values. Should be of size `2 x 2` at minimum. A tuple of adjacent GradientGrids can additionally be provided to apply the corresponding boundary conditions with the following order : @@ -76,6 +131,7 @@ def __init__(self, width: int = 2, height: int = 2, adjacent_grids: tuple = (Non Args: `width` (int, optional): the width of the generated random GradientGrid. Defaults to `2`. `height` (int, optional): the height of the generated random GradientGrid. Defaults to `2`. + `regenerate`(bool, optional): if the grid should be (re)generated or not. Defaults to `True`. `adjacent_grids` (tuple, optional): the tuple of adjacent GradientGrids to generate a smooth transition in terrain with correct boundary conditions. The tuple is in order `(NORTH, EAST, SOUTH, WEST)`. @@ -86,7 +142,7 @@ def __init__(self, width: int = 2, height: int = 2, adjacent_grids: tuple = (Non self.width = width self.height = height self.vectors = np.array([[np.zeros((2)) for j in range(self.width)] for i in range(self.height)]) - self.regenerate(adjacent_grids) + if regenerate: self.regenerate(adjacent_grids) @@ -181,7 +237,7 @@ def regenerate(self, adjacent_grids: tuple[GradientGrid] = (None, None, None, No GradientGrid.setRandomSeed("seed") - grid = GradientGrid(5, 5) + grid = GradientGrid(5, 3) print(grid) @@ -190,6 +246,9 @@ def regenerate(self, adjacent_grids: tuple[GradientGrid] = (None, None, None, No grid.regenerate() print(grid) + print("\nTrying to encode and decode the grid :") + print(GradientGrid.write(grid,"../saves/test_completeMap.data")) + print(GradientGrid.read("../saves/test_completeMap.data")[0]) print("Trying to generate a grid at the north of the first one :") diff --git a/Python/interpreter.py b/Python/interpreter.py index 81eff7b..0c3bf99 100644 --- a/Python/interpreter.py +++ b/Python/interpreter.py @@ -321,6 +321,235 @@ class DecodeDictError(InterpreterError): +### ---------- Binary encoding methods ---------- ### + +from numpy import uint8, float64 + +BYTES_TRUE = b'\x01' +BYTES_FALSE = b'\x00' +INT_BITS_NBR = 24 #24 should be a multiple of 8 +FLOAT_BITS_EXP = 5 #5 should be < 8 +FLOAT_BITS_MANTISS = 18 +FLOAT_BITS_NBR = 1 + FLOAT_BITS_EXP + FLOAT_BITS_MANTISS #24 should be a multiple of 8 + +BYTES_VERSION = b'\x00' + +def bytesNumber(x : int | float | float64 | uint8) -> bytes: + """Returns the encoding of a given number. + + Args: + x (int | float | float64 | uint8): the given number + + Returns: + bytes: the encoding corresponding + """ + if type(x)==uint8: + res=bytes([x]) #stored on one byte + return res + elif type(x)==int and int.bit_length(x)0 else 128 #sign bit + x = abs(x) + l=[temp + x // (2**(INT_BITS_NBR-8))] + [x % (2**(INT_BITS_NBR-i)) // (2**(INT_BITS_NBR-i-8)) for i in range(8,INT_BITS_NBR,8)] #[sign bit + first numeral bits to get a byte] + [remaining bytes] + res = bytes(l) + return res + elif type(x)==float or type(x)==float64: + temp = 0 if x>0 else 128 #sign bit + x = abs(x) + exp = 0 + if x<2**(FLOAT_BITS_MANTISS): #negative exponant + while exp>-2**(FLOAT_BITS_EXP-1) and x<2**(FLOAT_BITS_MANTISS-1): + exp-=1 + x*=2 + else: #positive exponant + while exp<2**(FLOAT_BITS_EXP-1)-1 and x>=2**(FLOAT_BITS_MANTISS): + exp+=1 + x/=2 + exp+=16 #offsetting exponant + x=int(x) + l = [temp + exp*2**(8-(1+FLOAT_BITS_EXP)) + x//2**(FLOAT_BITS_MANTISS-(8-(1+FLOAT_BITS_EXP)))] #sign bit + exponant bits + first numeral bits to get a byte + #remaining numeral bytes + l += [x%2**(FLOAT_BITS_MANTISS+(1+FLOAT_BITS_EXP)-i) // (2**(FLOAT_BITS_MANTISS+(1+FLOAT_BITS_EXP)-i-8)) for i in range(8,FLOAT_BITS_MANTISS+(1+FLOAT_BITS_EXP),8)] + res = bytes(l) + return res + +def nextInt(bytes_str : bytes) -> tuple[int, bytes]: + """Returns the next int from the first bytes of a given byte string. + + Args: + bytes_str (bytes): the given byte string + + Returns: + tuple[int, bytes]: the int and the remaining bytes + """ + x = int.from_bytes(bytes_str[:INT_BITS_NBR//8]) #easier to work with ints + if x//2**(INT_BITS_NBR-1)==1: x=-x%2**(INT_BITS_NBR-1) #first bit is a sign bit + return x,bytes_str[INT_BITS_NBR//8:] + +def nextFloat(bytes_str : bytes) -> tuple[float, bytes]: + """Returns the next float from the first bytes of a given byte string. + + Args: + bytes_str (bytes): the given byte string + + Returns: + tuple[float, bytes]: the float and the remaining bytes + """ + x = int.from_bytes(bytes_str[:FLOAT_BITS_NBR//8]) #easier to work with ints + if x//(2**(FLOAT_BITS_NBR-1))==1: s=-1 #first bit is a sign bit + else: s=1 + x=x%(2**(FLOAT_BITS_NBR-1)) #getting rid of first sign bit + exp=x//(2**(FLOAT_BITS_MANTISS)) #obtaining exponants bits + exp-=16 #de-offsetting exponant + x=x%(2**(FLOAT_BITS_MANTISS)) #getting rid of exponants bits + return s*x*2**exp,bytes_str[FLOAT_BITS_NBR//8:] #reconstructing float number + +def nextUInt8(bytes_str : bytes) -> tuple[uint8, bytes]: + """Returns the next uint8 (numpy) from the first byte of a given byte string. + + Args: + bytes_str (bytes): the byte string + + Returns: + tuple[uint8, bytes]: the uint8 decoded and the remaining bytes + """ + x = int.from_bytes(bytes_str[0:1]) + x = uint8(x) + return x,bytes_str[1:] + + +def read(data_in: str|bytes) -> tuple[object, bytes]: + """Decodes an object from a given binary file or string + + Args: + data_in (str | bytes): either path to file to decode, or bytes to be decoded + + Returns: + tuple[object, bytes]: the decoded object and eventual residual bytes + """ + + #importing objects to be matched + from layer import Layer + from gradientGrid import GradientGrid + from chunk import Chunk + from map import Map + from mapGenerator import CompleteMap + + #bytes to be decoded + bytes_str : bytes + if type(data_in)==str: + f=open(data_in,'rb') + bytes_str=f.read() + if bytes_str[0:1]==BYTES_VERSION: + bytes_str=bytes_str[1:] + else: bytes_str==None + f.close() + elif type(data_in)==bytes: + bytes_str=data_in + if bytes_str[0:1]==BYTES_VERSION: + bytes_str=bytes_str[1:] + else: bytes_str==None + else: bytes_str=None + + #decoding bytes + if bytes_str!=None: + isList=bytes_str[0:1]==BYTES_TRUE + bytes_str=bytes_str[1:] + + if isList: + nbr, bytes_str = nextInt(bytes_str) #number of elements in the list + res = [] + for _ in range(nbr): + x, bytes_str = read(bytes_str) + res.append(x) + return res, bytes_str + + elif bytes_str[0:1]==GradientGrid.GRID_ENCODING: + return GradientGrid.read(None,bytes_str) + elif bytes_str[0:1]==Layer.LAYER_ENCODING: + return Layer.read(None,bytes_str) + elif bytes_str[0:1]==Chunk.CHUNK_ENCODING: + return Chunk.read(None,bytes_str) + elif bytes_str[0:1]==Map.MAP_ENCODING: + return Map.read(None,bytes_str) + elif bytes_str[0:1]==CompleteMap.COMPLETE_MAP_ENCODING: + return CompleteMap.read(None,bytes_str) + + else: return None #failed + +def write(obj: object, path: str=None) -> bytes: + """Encodes a given object into a binary file or string. + + Args: + obj (object): the object to be encoded + path (str, optional): the file path where to store encoded bytes. Defaults to None. + + Returns: + bytes: encoded bytes + """ + + #importing object to match + from layer import Layer + from gradientGrid import GradientGrid + from chunk import Chunk + from map import Map + from mapGenerator import CompleteMap + + bytes_str: bytes = b'' + bytes_str += BYTES_VERSION + + if type(obj)==list or type(obj)==tuple: + bytes_str += BYTES_TRUE + bytes_str += bytesNumber(len(obj)) + for x in obj: + bytes_str += write(x) + else: + bytes_str += BYTES_FALSE + + if type(obj)==GradientGrid: + bytes_str+=GradientGrid.write(obj) + elif type(obj)==Layer: + bytes_str+=Layer.write(obj,True) + elif type(obj)==Chunk: + bytes_str+=Chunk.write(obj) + elif type(obj)==Map: + bytes_str+=Map.write(obj) + elif type(obj)==CompleteMap: + bytes_str+=CompleteMap.write(obj) + + if path!=None: + f=open(path,'wb') + f.write(bytes_str) + f.close() + + return bytes_str + +def readAll(data_in: str|bytes) -> list[object]: + """Decodes object from a given binary file or string up to bytes depletion. + + Args: + data_in (str | bytes): path to the file or bytes + + Returns: + list[object]: all decoded ojects + """ + + #bytes to be decoded + bytes_str : bytes + if type(data_in)==str: + f=open(data_in,'rb') + bytes_str=f.read() + f.close() + elif type(data_in)==bytes: + bytes_str=data_in + else: return None + + #decoding + res = [] + while len(bytes_str)>0: + x, bytes_str = read(bytes_str) + res.append(x) + return res + if __name__ == "__main__": @@ -336,9 +565,22 @@ class DecodeDictError(InterpreterError): # print(b.name) # print(b.bio.altitude_range) - t = temp() - t.obj=bytearray("A",'utf-8') + # t = temp() + # t.obj=bytearray("A",'utf-8') + + # print(encode(t)) + # print(decode(encode(t),True).obj) + + + + from gradientGrid import GradientGrid + + grid = GradientGrid(1, 1) + print(grid) + + print("Writing grid in file") + write(grid,"../saves/testing.data") - print(encode(t)) - print(decode(encode(t),True).obj) - \ No newline at end of file + print("Reading map in file") + gridl=read("../saves/testing.data")[0] + print(gridl) \ No newline at end of file diff --git a/Python/layer.py b/Python/layer.py index 684549d..bc50c2b 100644 --- a/Python/layer.py +++ b/Python/layer.py @@ -6,7 +6,7 @@ import numpy as np from gradientGrid import GradientGrid - +import interpreter as interp @@ -22,6 +22,7 @@ class Layer: #? -------------------------- Static ------------------------- # + LAYER_ENCODING = b'\x02' ALT_PRINTING_DECIMALS = 4 ALT_PRINTING_FORMAT = " {{: .{}f}} ".format(ALT_PRINTING_DECIMALS) @@ -169,22 +170,91 @@ def generateLayerFromScratch(grid_width: int, grid_height: int, size_factor: int @staticmethod - def write(path: str, layer: Layer) -> None: - #TODO - pass + def write(layer: Layer, altitude: bool=False, path: str=None, append: bool=False) -> bytes: + """Encodes a Layer object into a binary file or string. + + Args: + layer (Layer): the layer object to encode + altitude (bool, optional): should the altitude be encoded. Defaults to False. + path (str, optional): path to the file. Defaults to None. + append (bool, optional): should it append the binary string to the end of the file. Defaults to False. + + Returns: + bytes: the encoded bytes + """ + bytes_str : bytes = b'' + bytes_str += Layer.LAYER_ENCODING + bytes_str += interp.bytesNumber(layer.size_factor) + bytes_str += GradientGrid.write(layer.grid) + if altitude: #altitude values (optional) + bytes_str += interp.BYTES_TRUE + for i in range(layer.height): + for j in range(layer.width): + bytes_str += interp.bytesNumber(layer.altitude[i,j]) + else: bytes_str += interp.BYTES_FALSE + if path!=None: + if append: f=open(path,"ab") + else: f=open(path,"wb") + f.write(bytes_str) + f.close() + return bytes_str @staticmethod - def read(path: str) -> Layer: - #TODO - return None + def read(path: str = None, bytes_in: bytes=None, altitude: bool=False) -> tuple[Layer, bytes]: + """Decodes a Layer object from a binary file or a bytes string. + + Args: + path (str, optional): path to the binary file. Defaults to None. + bytes_in (bytes, optional): encoded bytes. Defaults to None. + altitude (bool, optional): should eventual altitude values be decoded or regenerated. Defaults to False. + + Returns: + tuple[Layer, bytes]: the layer object and remaining bytes + """ + bytes_str : bytes + if path!=None: + f=open(path,'rb') + bytes_str=f.read() + f.close() + if bytes_str[0:1]==Layer.LAYER_ENCODING: bytes_str=bytes_str[1:] + else: bytes_str=None + elif bytes_in!=None and bytes_in[0:1]==Layer.LAYER_ENCODING: + bytes_str=bytes_in[1:] + else: bytes_str=None + + layer : Layer + + if bytes_str!=None: + size_factor, bytes_str = interp.nextInt(bytes_str) + grid, bytes_str = GradientGrid.read(None,bytes_str) + if bytes_str[0:1]==interp.BYTES_TRUE: #altitude values encoded + bytes_str=bytes_str[1:] + layer = Layer(grid,size_factor,False) + if altitude: #decoding altitude values + layer.altitude=np.zeros((layer.height,layer.width)) + for i in range(layer.height): + for j in range(layer.width): + layer.altitude[i,j], bytes_str = interp.nextFloat(bytes_str) + else: #getting rid of altitude values + for i in range(layer.height): + for j in range(layer.width): + _, bytes_str = interp.nextFloat(bytes_str) + else: #altitude values not encoded, eventually regenerated + bytes_str=bytes_str[1:] + layer = Layer(grid,size_factor,altitude) + + + else: layer = None + + return layer, bytes_str #? ------------------------ Instances ------------------------ # - def __init__(self, grid: GradientGrid, size_factor: int) -> None: + def __init__(self, grid: GradientGrid, size_factor: int, regenerate: bool=True) -> None: """Initializes a new layer instance from an already existing GradientGrid and a size factor. If no gradient grid are already generated, please use the designated static method `generateLayerFromScratch` to generate the appropriate GradientGrid and then generate the layer structure. @@ -193,6 +263,7 @@ def __init__(self, grid: GradientGrid, size_factor: int) -> None: `grid` (GradientGrid): the gradient grid to build the layer from. `size_factor` (int): the size factor to be used to generate the layer. The layer dimensions will be `(gradient_grid_dimensions - 1) * size_factor`. + `regenerate`(bool, optional): should altitude values be (re)generated. Defaults to `True`. """ self.grid = None @@ -212,7 +283,7 @@ def __init__(self, grid: GradientGrid, size_factor: int) -> None: self.grid = grid self.size_factor = size_factor - self.regenerate() + self.regenerate(regenerate) @@ -225,21 +296,28 @@ def __str__(self) -> str: """ final_str = "-----------------------------------------\n" final_str += "Layer of dimensions {} x {} :\n".format(self.width, self.height) - for i in range(self.height): - - for j in range(self.width): - alt = self.altitude[i, j] - final_str += Layer.ALT_PRINTING_FORMAT.format(alt) + + if type(self.altitude)==np.ndarray: - final_str += "\n" + for i in range(self.height): + + for j in range(self.width): + alt = self.altitude[i, j] + final_str += Layer.ALT_PRINTING_FORMAT.format(alt) + + final_str += "\n" + final_str += "-----------------------------------------\n" return final_str - def regenerate(self) -> None: + def regenerate(self, regenerate: bool=True) -> None: """Regenerates the altitude values of the layer based on its `grid` and `size_factor` parameters. + + Args: + `regenerate`(bool, optional): should altitude values be (re)generated. Defaults to `True`. """ if not (type(self.grid) is GradientGrid and type(self.size_factor) is int and self.size_factor > 1): @@ -249,12 +327,15 @@ def regenerate(self) -> None: self.width = (self.grid.width - 1) * self.size_factor self.height = (self.grid.height - 1) * self.size_factor - self.altitude = np.zeros((self.height, self.width)) - for i in range(self.height): - for j in range(self.width): - - self.altitude[i, j] = Layer.perlin(i/self.size_factor, j/self.size_factor, self.grid) + if regenerate: + self.altitude = np.zeros((self.height, self.width)) + for i in range(self.height): + for j in range(self.width): + + self.altitude[i, j] = Layer.perlin(i/self.size_factor, j/self.size_factor, self.grid) + else: + self.altitude = None @@ -279,6 +360,14 @@ def regenerate(self) -> None: layer.regenerate() print(layer) + print("\nTrying to encode and decode the layer :") + print("\tWithout altitude :") + print(Layer.write(layer)) + print(Layer.read(None, Layer.write(layer))[0]) + print("\n\tWith altitude :") + print(Layer.write(layer,True)) + print(Layer.read(None, Layer.write(layer,True), True)[0]) + print("Trying to generate a layer at the north of the first one :") diff --git a/Python/map.py b/Python/map.py index a2a9ddd..8490786 100644 --- a/Python/map.py +++ b/Python/map.py @@ -10,6 +10,7 @@ from gradientGrid import GradientGrid from layer import Layer from chunk import Chunk +import interpreter as interp class Map: @@ -17,11 +18,15 @@ class Map: Map : ------------- The Map class used to generate large terrain by gathering chunks. + + Note : + A Map structure does not contain the complete map array. Use method `getFullMap()` on a map structure to get it. """ #? -------------------------- Static ------------------------- # + MAP_ENCODING = b'\x04' ALT_PRINTING_DECIMALS = 4 ALT_PRINTING_FORMAT = " {{: .{}f}} ".format(ALT_PRINTING_DECIMALS) @@ -109,16 +114,79 @@ def generateMapFromScratch(grid_widths: list[int], grid_heights: list[int], size @staticmethod - def write(path: str, map: Map) -> None: - #TODO - pass + def write(map: Map, path: str=None, append: bool=False) -> bytes: + """Encodes a Map object into a binary file or string. + + Args: + map (Map): the map object to encode + path (str, optional): path to the file. Defaults to None. + append (bool, optional): should it append the binary string to the end of the file. Defaults to False. + + Returns: + bytes: the encoded bytes + """ + bytes_str : bytes = b'' + bytes_str += Map.MAP_ENCODING + bytes_str += interp.bytesNumber(map.map_height) + bytes_str += interp.bytesNumber(map.map_width) + for i in range(map.map_height): #proper chunks + for j in range(map.map_width): + bytes_str += Chunk.write(map.chunks[i][j]) + n = 2 * (map.map_width+2) + 2 * map.map_height #nbr of virtual chunks + for i in range(n): #virtual chunks + bytes_str += Chunk.write(map.virtual_chunks[i],True) + if path!=None: + if append: f=open(path,"ab") + else: f=open(path,"wb") + f.write(bytes_str) + f.close() + return bytes_str @staticmethod - def read(path: str) -> Map: - #TODO - return None + def read(path: str, bytes_in : bytes=None) -> tuple[Map, bytes]: + """Decodes a Map object from a binary file or a bytes string. + + Args: + path (str, optional): path to the binary file. Defaults to None. + bytes_in (bytes, optional): encoded bytes. Defaults to None. + + Returns: + tuple[Map, bytes]: the map object and remaining bytes + """ + bytes_str : bytes + if path!=None: + f=open(path,'rb') + bytes_str=f.read() + f.close() + if bytes_str[0:1]==Map.MAP_ENCODING: bytes_str=bytes_str[1:] + else: bytes_str=None + elif bytes_in!=None and bytes_in[0:1]==Map.MAP_ENCODING: + bytes_str=bytes_in[1:] + else: bytes_str=None + + map : Map + + if bytes_str!=None: + height, bytes_str = interp.nextInt(bytes_str) + width, bytes_str = interp.nextInt(bytes_str) + chunks: list[list[Chunk]] = [] + virtual_chunks: list[Chunk] = [] + for i in range(height): + chunks.append([]) + for _ in range(width): + x, bytes_str = Chunk.read(None, bytes_str) + chunks[i].append(x) + n = 2 * (width+2) + 2 * height #nbr of virtual chunks + for _ in range(n): + x, bytes_str = Chunk.read(None, bytes_str) + virtual_chunks.append(x) + map = Map(width,height,chunks,virtual_chunks,False) + + else: map = None + + return map, bytes_str @@ -156,7 +224,7 @@ def getVirtualChunk(self, width_idx: int, height_idx: int) -> Chunk: - def __init__(self, map_width: int, map_height: int, chunks: list[list[Chunk]], virtual_chunks: list[Chunk]) -> None: + def __init__(self, map_width: int, map_height: int, chunks: list[list[Chunk]], virtual_chunks: list[Chunk], regenerate: bool = True) -> None: """Initializes a new Map structure from the given list of already existing chunks and virtual chunks. If chunks should be generated in the process, please use one of the designated static methods. @@ -166,6 +234,7 @@ def __init__(self, map_width: int, map_height: int, chunks: list[list[Chunk]], v `chunks` (list[list[Chunk]]): two-dimension matrix-like structure of chunks. First dimension is height, second is width. `virtual_chunks` (list[Chunk]): list of virtual chunks to generate the map altitudes. Virtual chunks should be stored in the following order : `Column0, ColumnN, Row0, RowN` without any repetition of the corners. + `regenerate`(bool, optional): should altitude values be (re)generated. Defaults to `True`. """ @@ -175,7 +244,7 @@ def __init__(self, map_width: int, map_height: int, chunks: list[list[Chunk]], v self.chunk_width = 0 self.chunk_height = 0 - self.altitude = None + # self.altitude = None self.chunks = None self.virtual_chunks = None @@ -196,7 +265,7 @@ def __init__(self, map_width: int, map_height: int, chunks: list[list[Chunk]], v self.chunks = copy.deepcopy(chunks) #? To keep both dimensions copied. Or use an np.ndarray ? self.virtual_chunks = virtual_chunks.copy() - self.regenerate() + self.regenerate(regenerate) @@ -210,22 +279,17 @@ def __str__(self) -> str: final_str = "-----------------------------------------\n" final_str += "Map of {} x {} chunks of dimension {} x {}\n"\ .format(self.map_width, self.map_height, self.chunk_width, self.chunk_height) - # final_str += "\nAltitude values :\n" - # for i in range(self.height): - - # for j in range(self.width): - # alt = self.altitude[i, j] - # final_str += Layer.ALT_PRINTING_FORMAT.format(alt) - - # final_str += "\n" final_str += "-----------------------------------------\n" return final_str - def regenerate(self) -> None: + def regenerate(self, regenerate: bool = True) -> None: """Regenerates the altitude values of the map based on its chunks. + + Args: + `regenerate`(bool, optional): should altitude values be (re)generated. Defaults to `True`. """ #* -------------- Verifications : Begin -------------- @@ -234,20 +298,20 @@ def regenerate(self) -> None: #* -------------- Verifications : End -------------- - width = self.map_width * self.chunk_width - height = self.map_height * self.chunk_height + # width = self.map_width * self.chunk_width + # height = self.map_height * self.chunk_height - self.altitude = np.zeros((height, width)) + # self.altitude = np.zeros((height, width)) - # First copies altitudes from chunks - for i in range(height): - for j in range(width): + # # First copies altitudes from chunks + # for i in range(height): + # for j in range(width): - current_chunk = self.chunks[i//self.chunk_height][j//self.chunk_width] - self.altitude[i][j] = current_chunk.altitude[i%self.chunk_height][j%self.chunk_width] + # current_chunk = self.chunks[i//self.chunk_height][j//self.chunk_width] + # self.altitude[i][j] = current_chunk.altitude[i%self.chunk_height][j%self.chunk_width] - - self.applyMeanAltitude() + if regenerate: + self.applyMeanAltitude() @@ -296,10 +360,36 @@ def applyMeanAltitude(self) -> None: alt_i = pi + math.floor((i-0.5)*chunk_height) alt_j = pj + math.floor((j-0.5)*chunk_width) + if alt_i < 0 or alt_j < 0: print("Warning alt i and j are : ", alt_i, alt_j) - self.altitude[alt_i][alt_j] += alt + current_chunk = self.chunks[alt_i//chunk_height][alt_j//chunk_width] + + + current_chunk.altitude[alt_i%chunk_height][alt_j%chunk_width] += alt + + + + def getFullMap(self) -> np.ndarray: + """Get the full map altitude from the contained chunks. + + Returns: + `np.ndarray`: the complete altitude array from the map. + """ + + width = self.map_width * self.chunk_width + height = self.map_height * self.chunk_height + + altitude = np.zeros((height, width)) + + for i in range(height): + for j in range(width): + + current_chunk = self.chunks[i//self.chunk_height][j//self.chunk_width] + altitude[i][j] = current_chunk.altitude[i%self.chunk_height][j%self.chunk_width] + + return altitude @@ -350,4 +440,8 @@ def applyMeanAltitude(self) -> None: map = Map.generateMapFromScratch(grid_dim, grid_dim, size_factors, layer_factors, map_dimensions[0], map_dimensions[1]) print(map) + + print("\nTesting map encoding: ") + print(Map.write(map)) + print(Map.read(None,Map.write(map))[0]) \ No newline at end of file diff --git a/Python/mapGenerator.py b/Python/mapGenerator.py index 030f0b8..5430abc 100644 --- a/Python/mapGenerator.py +++ b/Python/mapGenerator.py @@ -11,6 +11,7 @@ from layer import Layer from chunk import Chunk from map import Map +import interpreter as interp class CompleteMap: """ @@ -22,6 +23,8 @@ class CompleteMap: #? -------------------------- Static ------------------------- # + COMPLETE_MAP_ENCODING = b'\x04' + @staticmethod def colorize(value: float, sea_level: float, min_value: float, max_value: float) -> tuple[float]: """Returns the color associated with the given value for the given parameters. The color is a @@ -42,14 +45,14 @@ def colorize(value: float, sea_level: float, min_value: float, max_value: float) #? Show red dots on zero values. Could be removed. if value == 0: - return (1., 0., 0.) + return (255, 0, 0) elif value <= sea_level: - blue = int((value - min_value) * 200. / (sea_level - min_value)) / 255 - o = 50 / 255 + blue = int((value - min_value) * 200. / (sea_level - min_value)) + o = 50 return (o, o, blue) else: - green = int((value - min_value) * 255. / (max_value - min_value)) / 255 - o = 50 / 255 + green = int((value - min_value) * 255. / (max_value - min_value)) + o = 50 return (o, green, o) @@ -95,26 +98,93 @@ def generateCompleteMapFromScratch(grid_widths: list[int], grid_heights: list[in @staticmethod - def write(path: str, complete_map: CompleteMap) -> None: - #TODO - pass + def write(complete_map: CompleteMap, path: str=None, append: bool=False) -> bytes: + """Encodes a CompleteMap object into a binary file or string. + + Args: + complete_map (CompleteMap): the complete map object to encode + path (str, optional): path to the file. Defaults to None. + append (bool, optional): should it append the binary string to the end of the file. Defaults to False. + + Returns: + bytes: the encoded bytes + """ + bytes_str : bytes = b'' + bytes_str += CompleteMap.COMPLETE_MAP_ENCODING + bytes_str += Map.write(complete_map.map) + bytes_str += interp.bytesNumber(complete_map.sea_level) + height = complete_map.map.map_height * complete_map.map.chunk_height + width = complete_map.map.map_width * complete_map.map.chunk_width + for i in range(height): + for j in range(width): + bytes_str += interp.bytesNumber(complete_map.sea_values[i,j]) + bytes_str += interp.bytesNumber(complete_map.color_map[i,j,0]) + bytes_str += interp.bytesNumber(complete_map.color_map[i,j,1]) + bytes_str += interp.bytesNumber(complete_map.color_map[i,j,2]) + if path!=None: + if append: f=open(path,"ab") + else: f=open(path,"wb") + f.write(bytes_str) + f.close() + return bytes_str @staticmethod - def read(path: str) -> CompleteMap: - #TODO - return None + def read(path: str, bytes_in : bytes=None) -> tuple[CompleteMap, bytes]: + """Decodes a CompleteMap object from a binary file or a bytes string. + + Args: + path (str, optional): path to the binary file. Defaults to None. + bytes_in (bytes, optional): encoded bytes. Defaults to None. + + Returns: + tuple[CompleteMap, bytes]: the complete map object and remaining bytes + """ + bytes_str : bytes + if path!=None: + f=open(path,'rb') + bytes_str=f.read() + f.close() + if bytes_str[0:1]==CompleteMap.COMPLETE_MAP_ENCODING: bytes_str=bytes_str[1:] + else: bytes_str=None + elif bytes_in!=None and bytes_in[0:1]==CompleteMap.COMPLETE_MAP_ENCODING: + bytes_str=bytes_in[1:] + else: bytes_str=None + + complete_map : CompleteMap + + if bytes_str!=None: + map,bytes_str = Map.read(None, bytes_str) + height = map.map_height * map.chunk_height + width = map.map_width * map.chunk_width + sea_level, bytes_str = interp.nextFloat(bytes_str) + sea_values = np.zeros((height,width)) + color_map = np.zeros((height,width,3),np.uint8) + for i in range(height): + for j in range(width): + sea_values[i,j], bytes_str = interp.nextFloat(bytes_str) + color_map[i,j,0], bytes_str = interp.nextUInt8(bytes_str) + color_map[i,j,1], bytes_str = interp.nextUInt8(bytes_str) + color_map[i,j,2], bytes_str = interp.nextUInt8(bytes_str) + complete_map = CompleteMap(map,sea_level,False) + complete_map.sea_values = sea_values + complete_map.color_map = color_map + + else: complete_map = None + + return complete_map, bytes_str #? ------------------------ Instances ------------------------ # - def __init__(self, map: Map, sea_level: float) -> None: + def __init__(self, map: Map, sea_level: float, regenerate: bool = True) -> None: """Initialize a CompleteMap structure based on the given Map and sea altitude. Args: map (Map): the map structure to build the complete map from. sea_level (float): the altitude of the sea. + regenerate (bool, optional): should sea map and color map be (re)generated """ self.map = None @@ -129,13 +199,16 @@ def __init__(self, map: Map, sea_level: float) -> None: self.map = map self.sea_level = sea_level - if type(map.altitude) == np.ndarray: - alt = map.altitude - else: - alt = np.array(map.altitude) - self.sea_values = np.where(alt >= sea_level, alt, sea_level) - - self.generateColorMap() + if regenerate: + alt = self.map.getFullMap() + + if type(alt) == np.ndarray: + pass + else: + alt = np.array(alt) + self.sea_values = np.where(alt >= sea_level, alt, sea_level) + + self.generateColorMap() @@ -146,19 +219,23 @@ def generateColorMap(self) -> None: width = self.map.map_width * self.map.chunk_width height = self.map.map_height * self.map.chunk_height - if type(self.map.altitude) == np.ndarray: - alt = self.map.altitude + alt = self.map.getFullMap() + + if type(alt) == np.ndarray: + pass else: - alt = np.array(self.map.altitude) + alt = np.array(alt) max_alt = np.max(alt) min_alt = np.min(alt) - self.color_map = [[(0, 0, 0) for j in range(width)] for i in range(height)] + color_map = [[(0, 0, 0) for j in range(width)] for i in range(height)] for i in range(height): for j in range(width): - self.color_map[i][j] = CompleteMap.colorize(alt[i][j], self.sea_level, min_alt, max_alt) + color_map[i][j] = CompleteMap.colorize(alt[i][j], self.sea_level, min_alt, max_alt) + + self.color_map = np.array(color_map, dtype=np.uint8) @@ -420,8 +497,30 @@ def plotCompleteMap(complete_map: CompleteMap) -> None: y = np.linspace(0, map_size[1], chunk_size[1] * map_size[1]) x, y = np.meshgrid(x, y) + + if not type(complete_map.color_map) == np.ndarray: + print("Color map should be an numpy array.") + + + + + + if np.issubdtype(complete_map.color_map.dtype, np.integer): + cmap = complete_map.color_map.astype(np.float32) + cmap /= 255 + elif not np.issubdtype(complete_map.color_map.dtype, np.floating): + # WHAT ARE YOU TRYING TO DO ??? + print("Error : cmap can only be integers in [0, 255] or floating point values in [0., 1.]") + print("You tried to use : ", complete_map.color_map.dtype) + cmap = complete_map.color_map + else: + cmap = complete_map.color_map + + + + # print(np.shape(y), np.shape(x), np.shape(complete_map.sea_values), np.shape(complete_map.color_map)) - surf = ax3D.plot_surface(y, x, np.array(complete_map.sea_values), facecolors=np.array(complete_map.color_map)) + surf = ax3D.plot_surface(y, x, np.array(complete_map.sea_values), facecolors=np.array(cmap)) xylim = max(map_size[0], map_size[1]) ax3D.set_xlim(0, xylim) @@ -471,6 +570,10 @@ def plotCompleteMap(complete_map: CompleteMap) -> None: MapGenerator.plotCompleteMap(complete_map) + print("\nTesting completeMap encoding: ") + print(CompleteMap.write(complete_map,"../saves/test_completeMap.data")) + MapGenerator.plotCompleteMap(CompleteMap.read("../saves/test_completeMap.data")[0]) + diff --git a/Python/old_mapGenerator.py b/Python/old_mapGenerator.py index 1697b14..9ae6d37 100644 --- a/Python/old_mapGenerator.py +++ b/Python/old_mapGenerator.py @@ -584,7 +584,7 @@ def loadColorMap(color_map_path : str): return # Description line - if "Color Float Map" not in lines[0]: + if "Color Map" not in lines[0]: print(RED_COLOR + "Color float map does not contain the correct first line" + DEFAULT_COLOR) # Parameters @@ -627,28 +627,28 @@ def loadColorMap(color_map_path : str): vector_parts = parts[j].split(",") try: - r = float(vector_parts[0]) + r = int(vector_parts[0]) except: - print(RED_COLOR + "Could not convert '{}' into a float for red value at indexes i={} j={} !".format(vector_parts[0], i, j) + DEFAULT_COLOR) + print(RED_COLOR + "Could not convert '{}' into an int for red value at indexes i={} j={} !".format(vector_parts[0], i, j) + DEFAULT_COLOR) return try: - g = float(vector_parts[1]) + g = int(vector_parts[1]) except: - print(RED_COLOR + "Could not convert '{}' into a float for green value at indexes i={} j={} !".format(vector_parts[1], i, j) + DEFAULT_COLOR) + print(RED_COLOR + "Could not convert '{}' into an int for green value at indexes i={} j={} !".format(vector_parts[1], i, j) + DEFAULT_COLOR) return try: - b = float(vector_parts[2]) + b = int(vector_parts[2]) except: - print(RED_COLOR + "Could not convert '{}' into a float for blue value at indexes i={} j={} !".format(vector_parts[2], i, j) + DEFAULT_COLOR) + print(RED_COLOR + "Could not convert '{}' into an int for blue value at indexes i={} j={} !".format(vector_parts[2], i, j) + DEFAULT_COLOR) return color_map[i][j] = (r, g, b) - return np.array(color_map) + return np.array(color_map)/255 else: raise FileNotFoundError(RED_COLOR + "File '{}' was not found!".format(color_map_path) + DEFAULT_COLOR) @@ -679,7 +679,7 @@ def loadCompleteMap(folder_path : str): if os.path.isdir(folder_path): sea_map_path = os.path.join(folder_path, "sea_map.txt") - color_map_path = os.path.join(folder_path, "color_float_map.txt") + color_map_path = os.path.join(folder_path, "color_map.txt") sea_map, map_width_in_points, map_height_in_points, sea_level, map_width_in_chunks, map_height_in_chunks = mapGenerator.loadSeaMap(sea_map_path)