C++ is hard
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
/build/
|
||||||
|
/cmake-build-debug/
|
||||||
|
/cmake-build-debug-mingw/
|
||||||
|
/cmake-build-debug-visual-studio/
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
Generated
+2
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
||||||
Generated
+7
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CMakePythonSetting">
|
||||||
|
<option name="pythonIntegrationState" value="YES" />
|
||||||
|
</component>
|
||||||
|
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||||
|
</project>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/ForestFireCPP.iml" filepath="$PROJECT_DIR$/.idea/ForestFireCPP.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.28)
|
||||||
|
project(ForestFireCPP)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
# Add the vcpkg toolchain file
|
||||||
|
set(CMAKE_TOOLCHAIN_FILE "C:/Users/0piet/vcpkg/scripts/buildsystems/vcpkg.cmake" CACHE STRING "Vcpkg toolchain file")
|
||||||
|
|
||||||
|
find_package(SFML COMPONENTS system window graphics CONFIG REQUIRED)
|
||||||
|
|
||||||
|
add_executable(ForestFireCPP main.cpp)
|
||||||
|
|
||||||
|
target_link_libraries(ForestFireCPP PRIVATE sfml-system sfml-window sfml-graphics)
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# Forest Fire Simulation
|
||||||
|
|
||||||
|
This C++ project simulates the spread of forest fires using a cellular automaton model. It provides a visual representation of fire propagation in a forest environment, taking into account various factors such as wind speed, direction, and different vegetation types.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Interactive grid-based simulation
|
||||||
|
- Customizable simulation parameters
|
||||||
|
- Real-time visualization using SFML graphics library
|
||||||
|
- Different vegetation types (Normal Forest, Dry Grass, Dense Trees, Water)
|
||||||
|
- Wind speed and direction influence on fire spread
|
||||||
|
- Zoom and pan functionality for detailed observation
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
To build and run this project, you'll need:
|
||||||
|
|
||||||
|
- C++17 compatible compiler
|
||||||
|
- CMake (version 3.28 or higher)
|
||||||
|
- SFML library
|
||||||
|
- vcpkg package manager (recommended for easy SFML installation)
|
||||||
|
|
||||||
|
## Building the Project
|
||||||
|
|
||||||
|
1. Clone the repository:
|
||||||
|
```bash
|
||||||
|
git clone
|
||||||
|
```
|
||||||
|
2. Install SFML using vcpkg:
|
||||||
|
```bash
|
||||||
|
vcpkg install sfml:x64-windows
|
||||||
|
```
|
||||||
|
3. Configure the project using CMake:
|
||||||
|
```bash
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake .. -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake
|
||||||
|
```
|
||||||
|
4. Build the project:
|
||||||
|
```bash
|
||||||
|
cmake --build .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the Simulation
|
||||||
|
|
||||||
|
After building the project, run the executable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./ForestFireCPP
|
||||||
|
```
|
||||||
|
|
||||||
|
You will be prompted to enter simulation parameters:
|
||||||
|
|
||||||
|
- Probability of fire spread (0.0 - 1.0)
|
||||||
|
- Probability of spontaneous ignition (0.0 - 1.0)
|
||||||
|
- Wind speed (0.0 - 1.0)
|
||||||
|
- Wind direction in degrees (0 - 360)
|
||||||
|
- Water ratio (0.0 - 1.0)
|
||||||
|
|
||||||
|
After entering the parameters, press Enter to start the simulation.
|
||||||
|
|
||||||
|
## Controls
|
||||||
|
|
||||||
|
- Left-click and drag to pan the view
|
||||||
|
- Mouse wheel to zoom in/out
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions to improve the simulation or add new features are welcome. Please feel free to submit pull requests or open issues for bugs and feature requests.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||||
|
|
||||||
|
## Acknowledgments
|
||||||
|
|
||||||
|
- Inspired by various forest fire simulation models and cellular automaton concepts and my already existing Python project (https://github.com/Quinta0/ForestFire.git)
|
||||||
|
- SFML library for graphics rendering (https://www.sfml-dev.org/)
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
#include <vector>
|
||||||
|
#include <random>
|
||||||
|
#include <cmath>
|
||||||
|
#include <ctime>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
const int GRID_SIZE = 256;
|
||||||
|
const float CELL_SIZE = 5.0f;
|
||||||
|
|
||||||
|
// Vegetation types
|
||||||
|
enum class CellType {
|
||||||
|
NormalForest,
|
||||||
|
DryGrass,
|
||||||
|
DenseTrees,
|
||||||
|
Water,
|
||||||
|
Burning,
|
||||||
|
Burned
|
||||||
|
};
|
||||||
|
|
||||||
|
// Simulation parameters
|
||||||
|
struct SimParams {
|
||||||
|
float p = 0.8f;
|
||||||
|
float pstart = 0.01f;
|
||||||
|
float w_speed = 0.5f;
|
||||||
|
float w_direction = 30.0f;
|
||||||
|
float water_ratio = 0.175f;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Grid class
|
||||||
|
class Grid {
|
||||||
|
public:
|
||||||
|
Grid() : size(0) {}
|
||||||
|
|
||||||
|
Grid(int size, float water_ratio) : size(size), cells(size, std::vector<CellType>(size, CellType::NormalForest)) {
|
||||||
|
initializeGrid(water_ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initializeGrid(float water_ratio) {
|
||||||
|
// Initialize grid similar to Python version
|
||||||
|
cells[size / 2][size / 2] = CellType::Burning;
|
||||||
|
cells[size / 4][size / 4] = CellType::DryGrass;
|
||||||
|
cells[3 * size / 4][3 * size / 4] = CellType::DenseTrees;
|
||||||
|
|
||||||
|
// Implement water initialization
|
||||||
|
int num_water_cells = static_cast<int>(size * size * water_ratio);
|
||||||
|
for (int i = 0; i < num_water_cells; ++i) {
|
||||||
|
int x = rand() % size;
|
||||||
|
int y = rand() % size;
|
||||||
|
cells[y][x] = CellType::Water;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(float p, float pstart, float w_speed, float w_direction) {
|
||||||
|
std::vector<std::vector<CellType>> new_cells = cells;
|
||||||
|
for (int y = 0; y < size; ++y) {
|
||||||
|
for (int x = 0; x < size; ++x) {
|
||||||
|
new_cells[y][x] = updateCell(x, y, p, pstart, w_speed, w_direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cells = new_cells;
|
||||||
|
}
|
||||||
|
|
||||||
|
CellType getCell(int x, int y) const {
|
||||||
|
return cells[y][x];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int size;
|
||||||
|
std::vector<std::vector<CellType>> cells;
|
||||||
|
|
||||||
|
CellType updateCell(int x, int y, float p, float pstart, float w_speed, float w_direction) {
|
||||||
|
CellType current = cells[y][x];
|
||||||
|
if (current == CellType::Burning) return CellType::Burned;
|
||||||
|
if (current == CellType::Burned || current == CellType::Water) return current;
|
||||||
|
|
||||||
|
for (const auto& neighbor : getNeighbors(x, y)) {
|
||||||
|
if (cells[neighbor.second][neighbor.first] == CellType::Burning) {
|
||||||
|
float angle = calculateAngle(x, y, neighbor.first, neighbor.second);
|
||||||
|
float directional_influence = calculateDirectionalInfluence(w_direction, angle);
|
||||||
|
float vegetation_factor = getVegetationFactor(current);
|
||||||
|
// Implement probability calculation and fire spread logic here
|
||||||
|
float adjusted_p = p * vegetation_factor * (1 + 0.1f * w_speed * directional_influence);
|
||||||
|
|
||||||
|
if (static_cast<float>(rand()) / RAND_MAX < adjusted_p) {
|
||||||
|
return CellType::Burning;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static_cast<float>(rand()) / RAND_MAX < pstart) {
|
||||||
|
return CellType::Burning;
|
||||||
|
}
|
||||||
|
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<int, int>> getNeighbors(int x, int y) {
|
||||||
|
std::vector<std::pair<int, int>> neighbors;
|
||||||
|
for (int dx = -1; dx <= 1; ++dx) {
|
||||||
|
for (int dy = -1; dy <= 1; ++dy) {
|
||||||
|
if (dx == 0 && dy == 0) continue;
|
||||||
|
int nx = x + dx, ny = y + dy;
|
||||||
|
if (nx >= 0 && nx < size && ny >= 0 && ny < size) {
|
||||||
|
neighbors.emplace_back(nx, ny);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return neighbors;
|
||||||
|
}
|
||||||
|
|
||||||
|
float calculateAngle(int x1, int y1, int x2, int y2) {
|
||||||
|
return std::atan2(y2 - y1, x2 - x1) * 180 / M_PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
float calculateDirectionalInfluence(float w_direction, float angle) {
|
||||||
|
float angle_diff = std::abs(w_direction - angle);
|
||||||
|
angle_diff = std::min(angle_diff, 360.0f - angle_diff);
|
||||||
|
return 1.0f - (angle_diff / 180.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
float getVegetationFactor(CellType type) {
|
||||||
|
switch (type) {
|
||||||
|
case CellType::NormalForest: return 1.0f;
|
||||||
|
case CellType::DryGrass: return 1.5f;
|
||||||
|
case CellType::DenseTrees: return 0.5f;
|
||||||
|
case CellType::Water: return 0.0f;
|
||||||
|
default: return 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Main application class
|
||||||
|
class ForestFireSim {
|
||||||
|
public:
|
||||||
|
void getSimulationParameters() {
|
||||||
|
std::cout << "Enter simulation parameters:\n";
|
||||||
|
std::cout << "Probability of fire spread (0.0 - 1.0): ";
|
||||||
|
std::cin >> params.p;
|
||||||
|
std::cout << "Probability of spontaneous ignition (0.0 - 1.0): ";
|
||||||
|
std::cin >> params.pstart;
|
||||||
|
std::cout << "Wind speed (0.0 - 1.0): ";
|
||||||
|
std::cin >> params.w_speed;
|
||||||
|
std::cout << "Wind direction in degrees (0 - 360): ";
|
||||||
|
std::cin >> params.w_direction;
|
||||||
|
std::cout << "Water ratio (0.0 - 1.0): ";
|
||||||
|
std::cin >> params.water_ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
ForestFireSim() : window(sf::VideoMode(800, 600), "Forest Fire Simulation"),
|
||||||
|
view(window.getDefaultView()),
|
||||||
|
grid(GRID_SIZE, params.water_ratio) {
|
||||||
|
window.setView(view);
|
||||||
|
getSimulationParameters();
|
||||||
|
grid = Grid(GRID_SIZE, params.water_ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() {
|
||||||
|
while (window.isOpen()) {
|
||||||
|
handleEvents();
|
||||||
|
update();
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void startSimulation() {
|
||||||
|
std::cout << "Press Enter to start the simulation...";
|
||||||
|
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
||||||
|
std::cin.get();
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
sf::RenderWindow window;
|
||||||
|
Grid grid;
|
||||||
|
sf::View view;
|
||||||
|
SimParams params;
|
||||||
|
sf::Vector2f lastMousePos;
|
||||||
|
float zoomLevel = 1.0f;
|
||||||
|
|
||||||
|
void handleEvents() {
|
||||||
|
sf::Event event;
|
||||||
|
while (window.pollEvent(event)) {
|
||||||
|
if (event.type == sf::Event::Closed)
|
||||||
|
window.close();
|
||||||
|
else if (event.type == sf::Event::MouseButtonPressed) {
|
||||||
|
if (event.mouseButton.button == sf::Mouse::Left) {
|
||||||
|
lastMousePos = window.mapPixelToCoords(sf::Vector2i(event.mouseButton.x, event.mouseButton.y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (event.type == sf::Event::MouseMoved) {
|
||||||
|
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
|
||||||
|
sf::Vector2f newMousePos = window.mapPixelToCoords(sf::Vector2i(event.mouseMove.x, event.mouseMove.y));
|
||||||
|
sf::Vector2f deltaPos = lastMousePos - newMousePos;
|
||||||
|
view.move(deltaPos);
|
||||||
|
lastMousePos = newMousePos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (event.type == sf::Event::MouseWheelScrolled) {
|
||||||
|
if (event.mouseWheelScroll.delta > 0)
|
||||||
|
zoomLevel *= 1.1f;
|
||||||
|
else if (event.mouseWheelScroll.delta < 0)
|
||||||
|
zoomLevel /= 1.1f;
|
||||||
|
view.setSize(window.getDefaultView().getSize());
|
||||||
|
view.zoom(zoomLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
grid.update(params.p, params.pstart, params.w_speed, params.w_direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
void render() {
|
||||||
|
window.clear(sf::Color::White);
|
||||||
|
window.setView(view);
|
||||||
|
|
||||||
|
// Draw grid
|
||||||
|
for (int y = 0; y < GRID_SIZE; ++y) {
|
||||||
|
for (int x = 0; x < GRID_SIZE; ++x) {
|
||||||
|
sf::RectangleShape cell(sf::Vector2f(CELL_SIZE, CELL_SIZE));
|
||||||
|
cell.setPosition(x * CELL_SIZE, y * CELL_SIZE);
|
||||||
|
cell.setFillColor(getCellColor(grid.getCell(x, y)));
|
||||||
|
window.draw(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
sf::Color getCellColor(CellType type) {
|
||||||
|
switch (type) {
|
||||||
|
case CellType::NormalForest: return sf::Color::Green;
|
||||||
|
case CellType::DryGrass: return sf::Color::Yellow;
|
||||||
|
case CellType::DenseTrees: return sf::Color(0, 100, 0); // Dark Green
|
||||||
|
case CellType::Water: return sf::Color::Blue;
|
||||||
|
case CellType::Burning: return sf::Color::Red;
|
||||||
|
case CellType::Burned: return sf::Color::Black;
|
||||||
|
default: return sf::Color::White;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
srand(static_cast<unsigned int>(time(0)));
|
||||||
|
ForestFireSim sim;
|
||||||
|
sim.startSimulation();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"default-registry": {
|
||||||
|
"kind": "git",
|
||||||
|
"baseline": "3f530d4827b064f5894e94a8946473d40999826e",
|
||||||
|
"repository": "https://github.com/microsoft/vcpkg"
|
||||||
|
},
|
||||||
|
"registries": [
|
||||||
|
{
|
||||||
|
"kind": "artifact",
|
||||||
|
"location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip",
|
||||||
|
"name": "microsoft"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "forest-fire-cpp",
|
||||||
|
"version-string": "0.1.0",
|
||||||
|
"dependencies": [
|
||||||
|
"sfml"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user