Test Implemetation of Flood Fill.

This commit is contained in:
2025-05-27 22:42:49 -05:00
parent a8fc5dd389
commit 5f607c5ae1
9 changed files with 147 additions and 53 deletions

View File

@@ -55,7 +55,8 @@ if (WIN32)
include/Preferences.hpp
src/Utils.cpp
include/NewMapDialog.hpp
src/Tileset.cpp)
src/Tileset.cpp
src/Layer.cpp)
endif()
set_target_properties(Editor2D PROPERTIES LINKER_LANGUAGE CXX)

View File

@@ -140,6 +140,7 @@ public:
/// This provides a mapping from a 1D flat array to a 2D grid.
/// @return The {x,y} coordinate of the grid-cell that maps to the given index.
Vector2i IndexToCell(int index, int width);
void FloodFill(int x, int y, int tileid);
/// This provides a mapping from a 2D grid to a 1D flat array.
/// @return The {x,y} coordinate of the grid-cell that maps to the given index.
@@ -187,6 +188,9 @@ public:
Vector2i GetGridCellFromMouse();
void CameraUpdate(float elapsed);
int GetTile(const Vector2i& cell);
int GetTile(int x, int y);
void SetTile(int x, int y, int tileID);
void SetTile(const Vector2i& cell, int tileID);

View File

@@ -6,6 +6,19 @@
#include <Colors.hpp>
#include <Utils.hpp>
using namespace JJX;
/// @class Layer
/// @brief Represents a single plane of tiles within a level.
///
/// A layer stores the grid of tile IDs and properties specific to that layer,
/// such as its dimensions, visibility, opacity, and parallax scroll factors.
/// The actual tile data is stored in a separate binary file for efficiency.
/// Our current chosen format is sequential signed 32-bit integers, one for each tile ID.
///
/// TODO: Make this configurable
///
/// TODO: Support endianness management
class Layer
{
public:
@@ -17,19 +30,34 @@ public:
int cell_height;
int** cells;
float parallax_x;
float parallax_y;
/// The default constructor for Layer initializes member variables to default, zero values.
Layer() {}
/// This constructor creates an empty layer of a given size, and cell-size.
Layer(int rows, int cols, int cell_width, int cell_height)
{
this->rows = rows;
this->cols = cols;
this->cell_width = cell_width;
this->cell_height = cell_height;
InitGrid();
}
/// Constructs a Layer object by deserializing metadata from a JSON object.
/// The binary tile data will need to be loaded separately using `ReadFromFile()`
explicit Layer(const json::value& json)
{
Deserialize(json);
}
/// Destructor for Layer cleans up
~Layer() = default;
explicit Layer(const std::filesystem::path& path)
{
auto [json, err] = json::parse(read_file_contents(path));
@@ -54,6 +82,11 @@ public:
{
name = json["name"].as_string();
binary_path = json["binary-path"].as_string();
rows = json["rows"].number.value();
cols = json["cols"].number.value();
cell_width = json["cell-width"].number.value();
cell_height = json["cell-height"].number.value();
}
void InitGrid()
@@ -78,6 +111,17 @@ public:
delete[] cells;
}
/// Resizes the layer's dimensions and reinitializes the tile data.
/// Existing tile data will be lost or truncated.
///
void Resize(int newWidth, int newHeight)
{
}
void LoadFromDataBuffer(const int* buffer)
{
for (int x = 0; x < rows; x++)
@@ -103,23 +147,30 @@ public:
}
}
void WriteToFile()
/// Writes the current tile grid data to a specified raw binary file.
/// The file path should typically be stored in the layer's JSON metadata.
/// @param path
bool WriteBinaryData(const std::filesystem::path& path)
{
int* buffer = new int[rows*cols];
WriteToDataBuffer(buffer);
std::ofstream output;
output.open(binary_path, std::ios::out | std::ios::binary);
output.open(path, std::ios::out | std::ios::binary);
output.write(reinterpret_cast<const char*>(buffer), rows * cols * sizeof(int));
output.close();
delete[] buffer;
return true;
}
void ReadFromFile()
bool Save() { return WriteBinaryData(binary_path); }
bool Load() { return ReadBinaryData(binary_path); }
bool ReadBinaryData(const std::filesystem::path& path)
{
std::ifstream input;
input.open(binary_path, std::ios::binary | std::ios::in);
input.open(path, std::ios::binary | std::ios::in);
input.seekg(0, std::ios::end);
int data_length = input.tellg();
input.seekg(0, std::ios::beg);
@@ -128,12 +179,13 @@ public:
input.read(buffer, data_length);
input.close();
InitGrid();
LoadFromDataBuffer(reinterpret_cast<int*>(buffer));
delete[] buffer;
}
~Layer() { }
return true;
}

View File

@@ -10,7 +10,6 @@
#include <J3ML/LinearAlgebra.hpp>
#include "JGL/types/Texture.h"
using namespace JJX;
struct Quad {
@@ -66,28 +65,25 @@ public:
/// The number of full columns of tiles in the texture atlast.
/// @note This might be redundant if `tiles_per_row` and `tile_count` are used.
int cols;
///
std::vector<json::value> tile_metadata;
std::vector<Tile> tiles;
#pragma endregion
#pragma region computed fields
/// The total number of tiles defined in this tileset.
/// @note This is calculated from `rows * cols` or by iterating
int tile_count;
/// The number of tiles that fit horizontally in a single row of the texture atlast, taking into account `tile_width` and `tile_spacing`.
int tiles_per_row;
std::vector<Quad> quads;
std::vector<Tile> tiles;
#pragma endregion
public:
/// Default constructor for Tileset.
/// Default constructor for Tileset .
/// Initializes member variables to default or zero values.
/// @see CreateDefault().
Tileset()
{
Tileset();
}
/// This constructor is used for the initial definition of a Tileset. i.e. From Editor->Tileset->New
Tileset(const std::string& name, const std::filesystem::path& texture_path, int tex_width, int tex_height, int tile_width, int tile_height, int tile_spacing);
@@ -104,18 +100,15 @@ public:
void Deserialize(const json::value& json);
/// Serializes the current Tileset object's data into a JSON object.
///
/// @return a JJX::json::value object representing the tileset's data.
json::value Serialize() const;
/// Creates and returns a default Tileset object with predefined values.
/// This can be useful for initializing a new, empty tileset in the editor.
static Tileset CreateDefault() {
}
protected:
/// Converts a 1D tile index to 2D cell coordinates (row, column) within the tileset grid.
/// The row and column are 0-based.
/// @param index The 0-based 1D index of the tile.
/// @warning If the index is out of bounds, the returned coordinates might be invalid (e.g. negative)
Vector2i IndexToCell(int index) const;
inline int CellToIndex(int cx, int cy);

View File

@@ -1,9 +0,0 @@
#pragma once
/// A Texture atlas that maps to a series of unique tiles with associated metadata.
class Tilesheet {
public:
Tilesheet(JGL::Texture* texture, int tile_width, int tile_height) {
}
};

View File

@@ -3,12 +3,6 @@
#include <Entity.hpp>
#include <Layer.hpp>
void CreateSampleLayer()
{
}
void CreateSampleLevel()
{
Level level;
@@ -39,20 +33,28 @@ int main()
if (!std::filesystem::exists("level.json"))
CreateSampleLevel();
Tileset tileset = Tileset(std::filesystem::path("tileset.json"));
Tile specialized_tile;
specialized_tile.name = "Special Tile";
specialized_tile.id = 9999;
specialized_tile.quad = {0,0,16,16};
json::object meta;
meta["color"] = json::string("#FFAAFF");
specialized_tile.metadata = meta;
tileset.tiles.push_back(specialized_tile);
Level level = Level(std::filesystem::path("level.json"));
Layer layer(128, 128, 16, 16);
layer.binary_path = "test.lvl";
layer.ReadFromFile();
layer.ReadBinaryData();
level.layers.push_back(layer);
write_file_contents("level.json", json::deparse(level.Serialize()));
write_file_contents("tileset.json", json::deparse(tileset.Serialize()));
std::cout << "Completed all tests" << std::endl;

View File

@@ -20,6 +20,24 @@ Vector2i EditorApp::IndexToCell(int index, int width)
return {x, y};
}
void EditorApp::FloodFill(int x, int y, int tileid)
{
if (GetTile(x, y) == -1)
{
SetTile(x, y, tileid);
if (GetTile(x-1, y) == -1) FloodFill(x-1, y, tileid);
if (GetTile(x+1, y) == -1) FloodFill(x+1, y, tileid);
if (GetTile(x, y-1) == -1) FloodFill(x, y-1, tileid);
if (GetTile(x, y+1) == -1) FloodFill(x, y+1, tileid);
}
}
int EditorApp::CellToIndex(Vector2i cell, int width)
{
return cell.y*width + cell.x;
@@ -400,6 +418,23 @@ void EditorApp::CameraUpdate(float elapsed)
}
int EditorApp::GetTile(const Vector2i& cell)
{
if (cell.x < 0) return -2;
if (cell.y < 0) return -2;
// out of bounds horizontally
if (cell.x > grid_rows) return -2;
// out of bounds vertically
if (cell.y > grid_cols) return -2;
return grid[cell.x][cell.y];
}
int EditorApp::GetTile(int x, int y) { return GetTile({x, y}); }
void EditorApp::SetTile(int x, int y, int tileID) { SetTile({x, y}, tileID);}
void EditorApp::SetTile(const Vector2i& cell, int tileID)
{
@@ -418,6 +453,7 @@ void EditorApp::SetTile(const Vector2i& cell, int tileID)
void EditorApp::PlaceTileBrush(const Vector2i& cell)
{
if (cell.x < 0) return;
if (cell.y < 0) return;
@@ -428,7 +464,11 @@ void EditorApp::PlaceTileBrush(const Vector2i& cell)
// out of bounds vertically
if (cell.y > grid_cols) return;
SetTile(cell, selected_quad);
if (IsKeyDown(Keys::B))
FloodFill(cell.x, cell.y, selected_quad);
else
SetTile(cell, selected_quad);
}
void EditorApp::EraseTile(const Vector2i& cell)

1
src/Layer.cpp Normal file
View File

@@ -0,0 +1 @@
#include <Layer.hpp>

View File

@@ -1,7 +1,13 @@
#include <Tileset.hpp>
Tileset::Tileset():
tile_width(0),tile_height(0), tile_spacing(0),
tex_width(0), tex_height(0), rows(0), cols(0),
tile_count(0), tiles_per_row(0)
{ }
Tileset::Tileset(const std::string& name, const std::filesystem::path& texture_path, int tex_width, int tex_height, int tile_width, int tile_height,
int tile_spacing)
int tile_spacing)
{
this->name = name;
this->file_path = std::format("{}.tileset", name);
@@ -60,11 +66,15 @@ void Tileset::Deserialize(const json::value& json)
this->tex_width = (int)json["texture-width"].number.value();
this->tex_height = (int)json["texture-height"].number.value();
/*auto tilemeta = json["tiledata"].as_array();
for (json::value& v : tilemeta)
if (json.as_object().contains("tiles"))
{
this->tile_metadata.push_back(v);
}*/
auto tilemeta = json["tiles"].as_array();
for (json::value& v : tilemeta)
{
// this->tiles.push_back(v);
}
}
this->rows = J3ML::Math::Floor(this->tex_width / this->tile_width);
this->cols = J3ML::Math::Floor(this->tex_height / this->tile_height);
@@ -104,7 +114,7 @@ json::value Tileset::Serialize() const
tileset["quads"] = quad_array;*/
/*json::array tiles;
json::array tiles;
for (auto& tile : this->tiles)
{
json::object tile_json;
@@ -119,7 +129,7 @@ json::value Tileset::Serialize() const
tile_json["quad"] = quad;
tiles.push_back(tile_json);
}
tileset["tiles"] = tiles;*/
tileset["tiles"] = tiles;
return tileset;
}