From d69adb5666fd6f73edbc6d410afcdf23c24e7c6b Mon Sep 17 00:00:00 2001
From: Jaro <jarorutjes07@gmail.com>
Date: Fri, 8 Nov 2024 19:20:05 +0100
Subject: particle updated

---
 src/crepe/Particle.cpp              |  25 ++++--
 src/crepe/Particle.h                |  24 +++---
 src/crepe/api/ParticleEmitter.cpp   |  35 ++------
 src/crepe/api/ParticleEmitter.h     |  72 ++++++++++------
 src/crepe/system/ParticleSystem.cpp | 159 ++++++++++++++++++++++++++++--------
 src/crepe/system/ParticleSystem.h   |  10 ++-
 6 files changed, 216 insertions(+), 109 deletions(-)

(limited to 'src/crepe')

diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp
index 4810e80..d2b1880 100644
--- a/src/crepe/Particle.cpp
+++ b/src/crepe/Particle.cpp
@@ -1,20 +1,29 @@
 #include "Particle.h"
 
-using namespace crepe;
+#include "api/Transform.h"
 
-Particle::Particle() { this->active = false; }
+using namespace crepe;
 
-void Particle::reset(float lifespan, Position position, Position velocity) {
+void Particle::reset(uint32_t lifespan, Vector2 position, Vector2 velocity, double angle) {
 	this->time_in_life = 0;
 	this->lifespan = lifespan;
 	this->position = position;
 	this->velocity = velocity;
 	this->active = true;
+	this->angle = angle;
+}
+
+void Particle::update() {
+	time_in_life++;
+	if (time_in_life >= lifespan) 
+	{
+		this->active = false;
+		return;
+	}	
+	velocity += force_over_time;
+	position += velocity;
 }
 
-void Particle::update(float deltaTime) {
-	time_in_life += deltaTime;
-	position.x += velocity.x * deltaTime;
-	position.y += velocity.y * deltaTime;
-	if (time_in_life >= lifespan) this->active = false;
+void Particle::stop_movement() {
+	this->velocity = {0,0};
 }
diff --git a/src/crepe/Particle.h b/src/crepe/Particle.h
index 21e691d..f52196c 100644
--- a/src/crepe/Particle.h
+++ b/src/crepe/Particle.h
@@ -1,22 +1,24 @@
 #pragma once
 
-#include "Position.h"
+#include "api/Transform.h"
 
 namespace crepe {
 
 class Particle {
 public:
-	Position position;
-	// FIXME: `Position` is an awkward name for a 2D vector. See FIXME comment in
-	// api/Transform.h for fix proposal.
-	Position velocity;
-	float lifespan;
-	bool active;
+	Vector2 position;
+	Vector2 velocity;
+	Vector2 force_over_time;
+	uint32_t lifespan;
+	bool active = false;
+	uint32_t time_in_life = 0;
+	double angle;
 
-	Particle();
-	void reset(float lifespan, Position position, Position velocity);
-	void update(float deltaTime);
-	float time_in_life;
+	Particle() = default;
+	void reset(uint32_t lifespan, Vector2 position, Vector2 velocity,double angle);
+	void update();
+	void stop_movement();
+	
 };
 
 } // namespace crepe
diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp
index 3b2e2f2..f585a81 100644
--- a/src/crepe/api/ParticleEmitter.cpp
+++ b/src/crepe/api/ParticleEmitter.cpp
@@ -1,36 +1,19 @@
-#include <ctime>
-#include <iostream>
-
 #include "Particle.h"
 #include "ParticleEmitter.h"
 
 using namespace crepe;
 
-ParticleEmitter::ParticleEmitter(game_object_id_t id, uint32_t max_particles,
-								 uint32_t emission_rate, uint32_t speed,
-								 uint32_t speed_offset, uint32_t angle,
-								 uint32_t angleOffset, float begin_lifespan,
-								 float end_lifespan)
-	: Component(id), max_particles(max_particles), emission_rate(emission_rate),
-	  speed(speed), speed_offset(speed_offset), position{0, 0},
-	  begin_lifespan(begin_lifespan), end_lifespan(end_lifespan) {
-	std::srand(
-		static_cast<uint32_t>(std::time(nullptr))); // initialize random seed
-	std::cout << "Create emitter" << std::endl;
-	// FIXME: Why do these expressions start with `360 +`, only to be `% 360`'d
-	// right after? This does not make any sense to me.
-	min_angle = (360 + angle - (angleOffset % 360)) % 360;
-	max_angle = (360 + angle + (angleOffset % 360)) % 360;
-	position.x = 400; // FIXME: what are these magic values?
-	position.y = 400;
-	for (size_t i = 0; i < max_particles; i++) {
-		this->particles.emplace_back();
-	}
+ParticleEmitter::ParticleEmitter(uint32_t game_object_id, const ParticleEmitterData& data) : Component(game_object_id),data(data) {
+    for (size_t i = 0; i < this->data.max_particles; i++) {
+        this->data.particles.emplace_back();
+    }
 }
 
+
 ParticleEmitter::~ParticleEmitter() {
-	std::vector<Particle>::iterator it = this->particles.begin();
-	while (it != this->particles.end()) {
-		it = this->particles.erase(it);
+	std::vector<Particle>::iterator it = this->data.particles.begin();
+	while (it != this->data.particles.end()) {
+		it = this->data.particles.erase(it);
 	}
 }
+
diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h
index 5939723..f931e8c 100644
--- a/src/crepe/api/ParticleEmitter.h
+++ b/src/crepe/api/ParticleEmitter.h
@@ -5,38 +5,58 @@
 
 #include "Component.h"
 #include "Particle.h"
+#include "Transform.h"
+
+class Sprite;
 
 namespace crepe {
 
 class ParticleEmitter : public Component {
 public:
-	ParticleEmitter(game_object_id_t id, uint32_t max_particles,
-					uint32_t emission_rate, uint32_t speed,
-					uint32_t speed_offset, uint32_t angle, uint32_t angleOffset,
-					float begin_lifespan, float end_lifespan);
-	~ParticleEmitter();
-
-	//! position of the emitter
-	Position position;
-	//! maximum number of particles
-	uint32_t max_particles;
-	//! rate of particle emission
-	uint32_t emission_rate;
-	//! base speed of the particles
-	uint32_t speed;
-	//! offset for random speed variation
-	uint32_t speed_offset;
-	//! min angle of particle emission
-	uint32_t min_angle;
-	//! max angle of particle emission
-	uint32_t max_angle;
-	//! begin Lifespan of particle (only visual)
-	float begin_lifespan;
-	//! begin Lifespan of particle
-	float end_lifespan;
+	struct ParticleBoundary{
+		//! boundary width (midpoint is emitter location)
+		double boundary_width = 0.0;
+		//! boundary height (midpoint is emitter location)
+		double boundary_height = 0.0;
+		//! boundary offset from particle emitter location
+		Vector2 boundary_offset;
+		//! reset on exit or stop velocity and set max postion
+		bool reset_on_exit = false;
+	};
 
-	//! collection of particles
-	std::vector<Particle> particles;
+	struct ParticleEmitterData{
+		//! position of the emitter
+		Vector2 position;
+		//! maximum number of particles
+		uint32_t max_particles = 0;
+		//! rate of particle emission per update (Lowest value = 0.001 any lower is ignored)
+		double emission_rate = 0;
+		//! min speed of the particles
+		double min_speed = 0;
+		//! min speed of the particles
+		double max_speed = 0;
+		//! min angle of particle emission
+		double min_angle = 0;
+		//! max angle of particle emission
+		double max_angle = 0;
+		//! begin Lifespan of particle (only visual)
+		double begin_lifespan = 0.0;
+		//! end Lifespan of particle
+		double end_lifespan = 0.0;
+		//! force over time (physics)
+		Vector2 force_over_time;
+		//! particle boundary
+		ParticleBoundary boundary;
+		//! collection of particles
+		std::vector<Particle> particles;
+		//! sprite reference
+		const Sprite* sprite;
+	};	
+public:
+	ParticleEmitter(uint32_t game_object_id, const ParticleEmitterData& data);
+	~ParticleEmitter();
+public:
+	ParticleEmitterData data;
 };
 
 } // namespace crepe
diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp
index 397b586..23534a3 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -1,62 +1,149 @@
 #include <cmath>
 #include <ctime>
+#include <cstdlib>
 
-#include "../ComponentManager.h"
-#include "../api/ParticleEmitter.h"
+#include "api/ParticleEmitter.h"
+#include "api/Vector2.h"
 
+#include "ComponentManager.h"
 #include "ParticleSystem.h"
 
 using namespace crepe;
 
-ParticleSystem::ParticleSystem() : elapsed_time(0.0f) {}
+ParticleSystem::ParticleSystem() {}
 
 void ParticleSystem::update() {
+
+	// Get all emitters
 	ComponentManager & mgr = ComponentManager::get_instance();
 	std::vector<std::reference_wrapper<ParticleEmitter>> emitters
 		= mgr.get_components_by_type<ParticleEmitter>();
-	float delta_time = 0.10;
+
 	for (ParticleEmitter & emitter : emitters) {
-		float update_amount = 1 / static_cast<float>(emitter.emission_rate);
-		for (float i = 0; i < delta_time; i += update_amount) {
-			emit_particle(emitter);
+
+		//	Get transform linked to emitter
+		const Transform& transform = mgr.get_components_by_id<Transform>(emitter.GAME_OBJECT_ID).front().get();
+
+		//	Check if within boundary
+		check_bounds(emitter,transform);
+		
+		// Emit particles based on emission_rate
+		int updates = calculate_update(this->update_count,emitter.data.emission_rate);
+		for (size_t i = 0; i < updates; i++)
+		{
+			emit_particle(emitter,transform);		
 		}
-		for (size_t j = 0; j < emitter.particles.size(); j++) {
-			if (emitter.particles[j].active) {
-				emitter.particles[j].update(delta_time);
+		
+		// Update all particles
+		for (size_t j = 0; j < emitter.data.particles.size(); j++) {
+			if (emitter.data.particles[j].active) {
+				emitter.data.particles[j].update();
 			}
 		}
 	}
+	update_count++;
+	if(update_count == MAX_UPDATE_COUNT) update_count = 0;
 }
 
-void ParticleSystem::emit_particle(ParticleEmitter & emitter) {
-	Position initial_position = {emitter.position.x, emitter.position.y};
-	float random_angle = 0.0f;
-	if (emitter.max_angle < emitter.min_angle) {
-		random_angle = ((emitter.min_angle
-						 + (std::rand()
-							% (static_cast<uint32_t>(emitter.max_angle + 360
-													 - emitter.min_angle + 1))))
-						% 360);
+void ParticleSystem::emit_particle(ParticleEmitter & emitter,const Transform& transform) {
+	constexpr double DEG_TO_RAD = M_PI / 180.0;
+
+	Vector2 initial_position = emitter.data.position + transform.position;
+	double min_angle = emitter.data.min_angle;
+	double max_angle = emitter.data.max_angle;
+	double random_angle;
+	
+	if (min_angle <= max_angle) {
+			// Standard range (e.g., 10 to 20 degrees)
+			double angle_offset = max_angle - min_angle;
+			random_angle = min_angle + static_cast<double>(std::rand() % static_cast<uint32_t>(angle_offset));
 	} else {
-		random_angle = emitter.min_angle
-					   + (std::rand()
-						  % (static_cast<uint32_t>(emitter.max_angle
-												   - emitter.min_angle + 1)));
+			// Wrap-around range (e.g., 350 to 10 degrees)
+			double angle_offset = (360 - min_angle) + max_angle;
+			random_angle = min_angle + static_cast<double>(std::rand() % static_cast<uint32_t>(angle_offset));
+			
+			// Wrap around to keep random_angle within 0-360 degrees
+			if (random_angle >= 360) {
+					random_angle -= 360;
+			}
 	}
-	float angle_in_radians = random_angle * (M_PI / 180.0f);
-	float random_speed_offset = (static_cast<float>(std::rand()) / RAND_MAX)
-									* (2 * emitter.speed_offset)
-								- emitter.speed_offset;
-	float velocity_x
-		= (emitter.speed + random_speed_offset) * std::cos(angle_in_radians);
-	float velocity_y
-		= (emitter.speed + random_speed_offset) * std::sin(angle_in_radians);
-	Position initial_velocity = {velocity_x, velocity_y};
-	for (size_t i = 0; i < emitter.particles.size(); i++) {
-		if (!emitter.particles[i].active) {
-			emitter.particles[i].reset(emitter.end_lifespan, initial_position,
-									   initial_velocity);
+
+	// Generate a random speed between min_speed and max_speed
+	double speed_offset = emitter.data.max_speed - emitter.data.min_speed;
+	double random_speed = emitter.data.min_speed + static_cast<double>(std::rand() % static_cast<uint32_t>(speed_offset));
+
+	// Convert random_angle to radians
+	double angle_radians = random_angle * DEG_TO_RAD;
+
+	Vector2 velocity = {
+        random_speed * std::cos(angle_radians),
+        random_speed * std::sin(angle_radians)
+    };
+
+
+	for (size_t i = 0; i < emitter.data.particles.size(); i++) {
+		if (!emitter.data.particles[i].active) {
+			emitter.data.particles[i].reset(emitter.data.end_lifespan, initial_position,velocity,random_angle);
 			break;
 		}
 	}
 }
+
+int ParticleSystem::calculate_update(int count, double emission) {
+
+		//get interger part of the emission
+		double integer_part = std::floor(emission);
+
+    // Get the fractional part of the emission
+    double fractional_part = emission - integer_part;
+
+    // Convert the fractional part to a denominator value
+    int denominator = static_cast<int>(1.0 / fractional_part);
+
+    // For emissions like 0.01, 0.1, 0.5, etc., calculate the update frequency
+    if (fractional_part > 0) {
+			// Calculate how often the update should be triggered based on the fractional part
+			if (count % denominator == 0) {
+				return 1;
+			} else {
+				return 0;
+			}
+    }
+		
+    // For integer emissions, return the emission directly
+    return static_cast<int>(emission);
+}
+
+void ParticleSystem::check_bounds(ParticleEmitter & emitter,const Transform& transform)
+{
+		Vector2 offset = emitter.data.boundary.boundary_offset + transform.position + emitter.data.position;
+		double half_width = emitter.data.boundary.boundary_width / 2.0;
+		double half_height = emitter.data.boundary.boundary_height / 2.0;
+
+		// Define boundary edges
+		const double left = offset.x - half_width;
+		const double right = offset.x + half_width;
+		const double top = offset.y - half_height;
+		const double bottom = offset.y + half_height;
+
+		std::vector<Particle>& particles = emitter.data.particles;
+		for (Particle& particle : particles)
+		{
+				const Vector2& position = particle.position;
+
+				// Check if particle is within bounds
+				bool within_bounds = (position.x >= left && position.x <= right && position.y >= top && position.y <= bottom);
+				if (!within_bounds)
+				{
+						if (emitter.data.boundary.reset_on_exit)
+						{
+								particle.active = false;
+						}
+						else
+						{
+								particle.velocity = {0, 0};
+								//todo add that particle goes back to boundary
+						}
+				}
+		}
+}
diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h
index 3ac1d3f..3b9cb54 100644
--- a/src/crepe/system/ParticleSystem.h
+++ b/src/crepe/system/ParticleSystem.h
@@ -10,9 +10,15 @@ public:
 	void update();
 
 private:
-	void emit_particle(ParticleEmitter & emitter); //emits a new particle
+	void emit_particle(ParticleEmitter & emitter,const Transform& transform);
+	int calculate_update(int count, double emission);
+	void check_bounds(ParticleEmitter & emitter,const Transform& transform);
 
-	float elapsed_time; //elapsed time since the last emission
+private:
+	//! counter to count updates to determine how many times emit_particle is called.
+	uint32_t update_count = 0;
+	//! determines the lowest amount of emissionrate (1000 = 0.001 = 1 particle per 1000 updates).
+	const uint32_t MAX_UPDATE_COUNT = 100;
 };
 
 } // namespace crepe
-- 
cgit v1.2.3


From 4e6f10f2d6ed593a21b985ccabe305a9cd6212cc Mon Sep 17 00:00:00 2001
From: JAROWMR <jarorutjes07@gmail.com>
Date: Fri, 8 Nov 2024 21:37:28 +0000
Subject: fixed build

---
 src/crepe/api/Vector2.cpp           |  6 +++---
 src/crepe/api/Vector2.h             | 10 +++++-----
 src/crepe/system/ParticleSystem.cpp |  2 +-
 src/example/CMakeLists.txt          |  1 +
 src/example/particles.cpp           |  5 ++---
 5 files changed, 12 insertions(+), 12 deletions(-)

(limited to 'src/crepe')

diff --git a/src/crepe/api/Vector2.cpp b/src/crepe/api/Vector2.cpp
index 09bb59b..09b3fa3 100644
--- a/src/crepe/api/Vector2.cpp
+++ b/src/crepe/api/Vector2.cpp
@@ -3,7 +3,7 @@
 namespace crepe {
 
 // Constructor with initial values
-Vector2::Vector2(float x, float y) : x(x), y(y) {}
+Vector2::Vector2(double x, double y) : x(x), y(y) {}
 
 // Subtracts another vector from this vector and returns the result.
 Vector2 Vector2::operator-(const Vector2 & other) const {
@@ -16,7 +16,7 @@ Vector2 Vector2::operator+(const Vector2 & other) const {
 }
 
 // Multiplies this vector by a scalar and returns the result.
-Vector2 Vector2::operator*(float scalar) const {
+Vector2 Vector2::operator*(double scalar) const {
 	return {x * scalar, y * scalar};
 }
 
@@ -35,7 +35,7 @@ Vector2 & Vector2::operator+=(const Vector2 & other) {
 }
 
 // Adds a scalar value to both components of this vector and updates this vector.
-Vector2 & Vector2::operator+=(float other) {
+Vector2 & Vector2::operator+=(double other) {
 	x += other;
 	y += other;
 	return *this;
diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h
index 741951b..5a57484 100644
--- a/src/crepe/api/Vector2.h
+++ b/src/crepe/api/Vector2.h
@@ -6,15 +6,15 @@ namespace crepe {
 class Vector2 {
 public:
 	//! X component of the vector
-	float x;
+	double x;
 	//! Y component of the vector
-	float y;
+	double y;
 
 	//! Default constructor
 	Vector2() = default;
 
 	//! Constructor with initial values
-	Vector2(float x, float y);
+	Vector2(double x, double y);
 
 	//! Subtracts another vector from this vector and returns the result.
 	Vector2 operator-(const Vector2 & other) const;
@@ -23,7 +23,7 @@ public:
 	Vector2 operator+(const Vector2 & other) const;
 
 	//! Multiplies this vector by a scalar and returns the result.
-	Vector2 operator*(float scalar) const;
+	Vector2 operator*(double scalar) const;
 
 	//! Multiplies this vector by another vector element-wise and updates this vector.
 	Vector2 & operator*=(const Vector2 & other);
@@ -32,7 +32,7 @@ public:
 	Vector2 & operator+=(const Vector2 & other);
 
 	//! Adds a scalar value to both components of this vector and updates this vector.
-	Vector2 & operator+=(float other);
+	Vector2 & operator+=(double other);
 
 	//! Returns the negation of this vector.
 	Vector2 operator-() const;
diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp
index 23534a3..b2fe829 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -22,7 +22,7 @@ void ParticleSystem::update() {
 	for (ParticleEmitter & emitter : emitters) {
 
 		//	Get transform linked to emitter
-		const Transform& transform = mgr.get_components_by_id<Transform>(emitter.GAME_OBJECT_ID).front().get();
+		const Transform& transform = mgr.get_components_by_id<Transform>(emitter.game_object_id).front().get();
 
 		//	Check if within boundary
 		check_bounds(emitter,transform);
diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt
index 36f9d4d..722ffea 100644
--- a/src/example/CMakeLists.txt
+++ b/src/example/CMakeLists.txt
@@ -28,4 +28,5 @@ add_example(proxy)
 add_example(db)
 add_example(ecs)
 add_example(scene_manager)
+add_example(particles)
 
diff --git a/src/example/particles.cpp b/src/example/particles.cpp
index a56ec5c..bcff48f 100644
--- a/src/example/particles.cpp
+++ b/src/example/particles.cpp
@@ -3,7 +3,6 @@
 #include <crepe/system/CollisionSystem.h>
 #include <crepe/api/AssetManager.h>
 #include <crepe/system/PhysicsSystem.h>
-#include <crepe/api/loopManager.h>
 
 #include <crepe/Component.h>
 #include <crepe/api/GameObject.h>
@@ -32,11 +31,11 @@ int main(int argc, char * argv[]) {
 		.max_angle = 0,
 		.begin_lifespan = 0,
 		.end_lifespan = 0,
-		.force_over_time = 0,
+		.force_over_time = Vector2{0,0},
 		.boundary{
 			.boundary_width = 0,
 			.boundary_height = 0,
-			.boundary_offset = {0,0},
+			.boundary_offset = Vector2{0,0},
 			.reset_on_exit = false,
 		},
 		.sprite = nullptr,
-- 
cgit v1.2.3


From b668abb3a0671c1233bb30d038a5a53018aa8620 Mon Sep 17 00:00:00 2001
From: JAROWMR <jarorutjes07@gmail.com>
Date: Sun, 10 Nov 2024 15:25:13 +0100
Subject: added unit test for particles

---
 src/crepe/Particle.h                | 43 ++++++++++++++++++-
 src/crepe/api/ParticleEmitter.cpp   |  2 +-
 src/crepe/api/ParticleEmitter.h     | 45 +++++++++++++++----
 src/crepe/system/ParticleSystem.cpp |  6 +--
 src/example/particles.cpp           |  1 -
 src/test/CMakeLists.txt             |  3 +-
 src/test/ParticleTest.cpp           | 86 +++++++++++++++++++++++++++++++++++++
 7 files changed, 169 insertions(+), 17 deletions(-)
 create mode 100644 src/test/ParticleTest.cpp

(limited to 'src/crepe')

diff --git a/src/crepe/Particle.h b/src/crepe/Particle.h
index f52196c..77c3ea3 100644
--- a/src/crepe/Particle.h
+++ b/src/crepe/Particle.h
@@ -1,22 +1,61 @@
 #pragma once
 
-#include "api/Transform.h"
+#include <cstdint>
+
+#include "api/Vector2.h"
 
 namespace crepe {
 
+/**
+ * \brief Represents a particle in the particle emitter.
+ *
+ * This class stores information about a single particle, including its position,
+ * velocity, lifespan, and other properties. Particles can be updated over time
+ * to simulate movement and can also be reset or stopped.
+ */
 class Particle {
 public:
+	//! Position of the particle in 2D space.
 	Vector2 position;
+	//! Velocity vector indicating the speed and direction of the particle.
 	Vector2 velocity;
+	//! Accumulated force affecting the particle over time.
 	Vector2 force_over_time;
+	//! Total lifespan of the particle in milliseconds.
 	uint32_t lifespan;
+	//! Active state of the particle; true if it is in use, false otherwise.
 	bool active = false;
+	//! The time the particle has been alive, in milliseconds.
 	uint32_t time_in_life = 0;
+	//! The angle at which the particle is oriented or moving.
 	double angle;
 
 	Particle() = default;
-	void reset(uint32_t lifespan, Vector2 position, Vector2 velocity,double angle);
+	/**
+	 * \brief Resets the particle with new properties.
+	 *
+	 * This method initializes the particle with a specific lifespan, position,
+	 * velocity, and angle, marking it as active and resetting its life counter.
+	 *
+	 * \param lifespan  The lifespan of the particle in amount of updates.
+	 * \param position  The starting position of the particle.
+	 * \param velocity  The initial velocity of the particle.
+	 * \param angle     The angle of the particle's trajectory or orientation.
+	 */
+	void reset(uint32_t lifespan, Vector2 position, Vector2 velocity, double angle);
+	/**
+	 * \brief Updates the particle's state.
+	 *
+	 * Advances the particle's position based on its velocity and applies accumulated forces. 
+	 * Deactivates the particle if its lifespan has expired.
+	 */
 	void update();
+	/**
+	 * \brief Stops the particle's movement.
+	 *
+	 * Sets the particle's velocity to zero, effectively halting any further
+	 * movement.
+	 */
 	void stop_movement();
 	
 };
diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp
index f585a81..000bf30 100644
--- a/src/crepe/api/ParticleEmitter.cpp
+++ b/src/crepe/api/ParticleEmitter.cpp
@@ -3,7 +3,7 @@
 
 using namespace crepe;
 
-ParticleEmitter::ParticleEmitter(uint32_t game_object_id, const ParticleEmitterData& data) : Component(game_object_id),data(data) {
+ParticleEmitter::ParticleEmitter(uint32_t game_object_id, const Data& data) : Component(game_object_id),data(data) {
     for (size_t i = 0; i < this->data.max_particles; i++) {
         this->data.particles.emplace_back();
     }
diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h
index f931e8c..2cda1bb 100644
--- a/src/crepe/api/ParticleEmitter.h
+++ b/src/crepe/api/ParticleEmitter.h
@@ -11,24 +11,44 @@ class Sprite;
 
 namespace crepe {
 
+
+/**
+ * \brief Data holder for particle emission parameters.
+ *
+ * The ParticleEmitter class stores configuration data for particle properties,
+ * defining the characteristics and boundaries of particle emissions.
+ */
+
 class ParticleEmitter : public Component {
 public:
-	struct ParticleBoundary{
+	/**
+	 * \brief Defines the boundary within which particles are constrained.
+	 *
+	 * This structure specifies the boundary's size and offset, as well as the
+	 * behavior of particles upon reaching the boundary limits.
+	 */
+	struct Boundary{
 		//! boundary width (midpoint is emitter location)
-		double boundary_width = 0.0;
+		double width = 0.0;
 		//! boundary height (midpoint is emitter location)
-		double boundary_height = 0.0;
+		double height = 0.0;
 		//! boundary offset from particle emitter location
-		Vector2 boundary_offset;
+		Vector2 offset;
 		//! reset on exit or stop velocity and set max postion
 		bool reset_on_exit = false;
 	};
 
-	struct ParticleEmitterData{
+	/**
+	 * \brief Holds parameters that control particle emission.
+	 *
+	 * Contains settings for the emitter’s position, particle speed, angle, lifespan,
+	 * boundary, and the sprite used for rendering particles.
+	 */
+	struct Data{
 		//! position of the emitter
 		Vector2 position;
 		//! maximum number of particles
-		uint32_t max_particles = 0;
+		const uint32_t max_particles = 0;
 		//! rate of particle emission per update (Lowest value = 0.001 any lower is ignored)
 		double emission_rate = 0;
 		//! min speed of the particles
@@ -46,17 +66,24 @@ public:
 		//! force over time (physics)
 		Vector2 force_over_time;
 		//! particle boundary
-		ParticleBoundary boundary;
+		Boundary boundary;
 		//! collection of particles
 		std::vector<Particle> particles;
 		//! sprite reference
 		const Sprite* sprite;
 	};	
 public:
-	ParticleEmitter(uint32_t game_object_id, const ParticleEmitterData& data);
+	/**
+	 * \brief Constructs a ParticleEmitter data holder with specified settings.
+	 *
+	 * \param game_object_id  Identifier for the game object using this emitter.
+	 * \param data            Configuration data defining particle properties.
+	 */
+	ParticleEmitter(uint32_t game_object_id, const Data& data);
 	~ParticleEmitter();
 public:
-	ParticleEmitterData data;
+	//! Configuration data for particle emission settings.
+	Data data;
 };
 
 } // namespace crepe
diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp
index b2fe829..85321f0 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -116,9 +116,9 @@ int ParticleSystem::calculate_update(int count, double emission) {
 
 void ParticleSystem::check_bounds(ParticleEmitter & emitter,const Transform& transform)
 {
-		Vector2 offset = emitter.data.boundary.boundary_offset + transform.position + emitter.data.position;
-		double half_width = emitter.data.boundary.boundary_width / 2.0;
-		double half_height = emitter.data.boundary.boundary_height / 2.0;
+		Vector2 offset = emitter.data.boundary.offset + transform.position + emitter.data.position;
+		double half_width = emitter.data.boundary.width / 2.0;
+		double half_height = emitter.data.boundary.height / 2.0;
 
 		// Define boundary edges
 		const double left = offset.x - half_width;
diff --git a/src/example/particles.cpp b/src/example/particles.cpp
index bcff48f..01e33cf 100644
--- a/src/example/particles.cpp
+++ b/src/example/particles.cpp
@@ -19,7 +19,6 @@ using namespace std;
 
 int main(int argc, char * argv[]) {
 	
-	Color color(0, 0, 0, 0);
 	GameObject *game_object = new GameObject(0, "Name", "Tag", Vector2{0,0},0,0);
 	game_object->add_component<ParticleEmitter>(ParticleEmitter::ParticleEmitterData{
 		.position = {0,0},
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
index 0e4eaed..acab388 100644
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -1,6 +1,7 @@
 target_sources(test_main PUBLIC
 	dummy.cpp
 	# audio.cpp
-	PhysicsTest.cpp
+	# PhysicsTest.cpp
+	ParticleTest.cpp
 )
 
diff --git a/src/test/ParticleTest.cpp b/src/test/ParticleTest.cpp
new file mode 100644
index 0000000..aa63ccf
--- /dev/null
+++ b/src/test/ParticleTest.cpp
@@ -0,0 +1,86 @@
+#include <math.h>
+#include <crepe/ComponentManager.h>
+#include <crepe/api/Config.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/Rigidbody.h>
+#include <crepe/api/Transform.h>
+#include <crepe/system/ParticleSystem.h>
+#include <gtest/gtest.h>
+
+
+using namespace std;
+using namespace std::chrono_literals;
+using namespace crepe;
+
+class ParticlesTest : public ::testing::Test {
+protected:
+	GameObject * game_object;
+	ParticleSystem particle_system;
+	void SetUp() override {
+		ComponentManager & mgr = ComponentManager::get_instance();
+		std::vector<std::reference_wrapper<Transform>> transforms
+			= mgr.get_components_by_id<Transform>(0);
+		if (transforms.empty()) {
+			game_object = new GameObject(0, "", "", Vector2{0, 0}, 0, 0);
+			game_object->add_component<ParticleEmitter>(ParticleEmitter::Data{
+				.position = {0,0},
+				.max_particles = 1,
+				.emission_rate = 0,
+				.min_speed = 0,
+				.max_speed = 0,
+				.min_angle = 0,
+				.max_angle = 0,
+				.begin_lifespan = 0,
+				.end_lifespan = 0,
+				.force_over_time = Vector2{0,0},
+				.boundary{
+					.width = 0,
+					.height = 0,
+					.offset = Vector2{0,0},
+					.reset_on_exit = false,
+				},
+				.sprite = nullptr,
+			});
+		}
+		transforms = mgr.get_components_by_id<Transform>(0);
+		Transform & transform = transforms.front().get();
+		transform.position.x = 0.0;
+		transform.position.y = 0.0;
+		transform.rotation = 0.0;
+		std::vector<std::reference_wrapper<Rigidbody>> rigidbodies
+			= mgr.get_components_by_id<Rigidbody>(0);
+		Rigidbody & rigidbody = rigidbodies.front().get();
+		rigidbody.data.angular_velocity = 0;
+		rigidbody.data.linear_velocity.x = 0;
+		rigidbody.data.linear_velocity.y = 0;
+	}
+};
+
+TEST_F(ParticlesTest, spawnParticle) {
+	Config::get_instance().physics.gravity = 1;
+	ComponentManager & mgr = ComponentManager::get_instance();
+	ParticleEmitter & emitter = mgr.get_components_by_id<ParticleEmitter>(0).front().get();
+	emitter.data.end_lifespan = 5;
+	emitter.data.boundary.height = 100;
+	emitter.data.boundary.width = 100;
+	emitter.data.max_speed = 1;
+	emitter.data.max_angle = 1;
+	particle_system.update(); 
+	//check if nothing happend
+	EXPECT_EQ(emitter.data.particles.size(), 0);
+	emitter.data.emission_rate = 1;
+	particle_system.update();
+	//check if particle spawned
+	EXPECT_EQ(emitter.data.particles.size(), 1);
+	for (auto& particle : emitter.data.particles) {
+        // Check velocity range
+        EXPECT_GE(particle.velocity.x, emitter.data.min_speed);  // Speed should be greater than or equal to min_speed
+        EXPECT_LE(particle.velocity.x, emitter.data.max_speed);  // Speed should be less than or equal to max_speed
+		EXPECT_GE(particle.velocity.y, emitter.data.min_speed);  // Speed should be greater than or equal to min_speed
+        EXPECT_LE(particle.velocity.y, emitter.data.max_speed);  // Speed should be less than or equal to max_speed
+
+        // Check angle range
+        EXPECT_GE(particle.angle, emitter.data.min_angle);  // Angle should be greater than or equal to min_angle
+        EXPECT_LE(particle.angle, emitter.data.max_angle);  // Angle should be less than or equal to max_angle
+    }
+}
-- 
cgit v1.2.3


From b48773d19961be44bf9edd7a326dd85f304828f4 Mon Sep 17 00:00:00 2001
From: JAROWMR <jarorutjes07@gmail.com>
Date: Sun, 10 Nov 2024 16:08:22 +0100
Subject: added tests and fixed bugs in system

---
 src/crepe/system/ParticleSystem.cpp | 17 ++++++++++----
 src/test/ParticleTest.cpp           | 44 ++++++++++++++++++++++++++++++++++---
 2 files changed, 54 insertions(+), 7 deletions(-)

(limited to 'src/crepe')

diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp
index 85321f0..48fe1d7 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -52,8 +52,10 @@ void ParticleSystem::emit_particle(ParticleEmitter & emitter,const Transform& tr
 	double min_angle = emitter.data.min_angle;
 	double max_angle = emitter.data.max_angle;
 	double random_angle;
-	
-	if (min_angle <= max_angle) {
+	if(min_angle == max_angle){
+		random_angle = min_angle;
+	}
+	else if (min_angle <= max_angle) {
 			// Standard range (e.g., 10 to 20 degrees)
 			double angle_offset = max_angle - min_angle;
 			random_angle = min_angle + static_cast<double>(std::rand() % static_cast<uint32_t>(angle_offset));
@@ -70,8 +72,15 @@ void ParticleSystem::emit_particle(ParticleEmitter & emitter,const Transform& tr
 
 	// Generate a random speed between min_speed and max_speed
 	double speed_offset = emitter.data.max_speed - emitter.data.min_speed;
-	double random_speed = emitter.data.min_speed + static_cast<double>(std::rand() % static_cast<uint32_t>(speed_offset));
-
+	double random_speed = 0.0;
+	if(emitter.data.max_speed  == emitter.data.min_speed)
+	{
+		random_speed = emitter.data.min_speed;
+	}
+	else {
+		random_speed = emitter.data.min_speed + static_cast<double>(std::rand() % static_cast<uint32_t>(speed_offset));
+	}
+	 
 	// Convert random_angle to radians
 	double angle_radians = random_angle * DEG_TO_RAD;
 
diff --git a/src/test/ParticleTest.cpp b/src/test/ParticleTest.cpp
index 368a1f6..1b949e3 100644
--- a/src/test/ParticleTest.cpp
+++ b/src/test/ParticleTest.cpp
@@ -74,8 +74,10 @@ TEST_F(ParticlesTest, spawnParticle) {
 	emitter.data.end_lifespan = 5;
 	emitter.data.boundary.height = 100;
 	emitter.data.boundary.width = 100;
-	emitter.data.max_speed = 1;
-	emitter.data.max_angle = 1;
+	emitter.data.max_speed = 0.1;
+	emitter.data.max_angle = 0.1;
+	emitter.data.max_speed = 10;
+	emitter.data.max_angle = 10;
 	particle_system.update(); 
 	//check if nothing happend
 	EXPECT_EQ(emitter.data.particles[0].active, 0);
@@ -89,7 +91,7 @@ TEST_F(ParticlesTest, spawnParticle) {
 	EXPECT_EQ(emitter.data.particles[2].active, 1);
 	particle_system.update();
 	EXPECT_EQ(emitter.data.particles[3].active, 1);
-	
+
 	for (auto& particle : emitter.data.particles) {
         // Check velocity range
         EXPECT_GE(particle.velocity.x, emitter.data.min_speed);  // Speed should be greater than or equal to min_speed
@@ -103,3 +105,39 @@ TEST_F(ParticlesTest, spawnParticle) {
     }
 
 }
+
+TEST_F(ParticlesTest, moveParticleHorizontal) {
+	Config::get_instance().physics.gravity = 1;
+	ComponentManager & mgr = ComponentManager::get_instance();
+	ParticleEmitter & emitter = mgr.get_components_by_id<ParticleEmitter>(0).front().get();
+	emitter.data.end_lifespan = 100;
+	emitter.data.boundary.height = 100;
+	emitter.data.boundary.width = 100;
+	emitter.data.min_speed = 1;
+	emitter.data.max_speed = 1;
+	emitter.data.max_angle = 0;
+	emitter.data.emission_rate = 1;
+	for (int a = 1; a < emitter.data.boundary.width/2; a++) {
+		particle_system.update(); 
+		EXPECT_EQ(emitter.data.particles[0].position.x,a);
+	}
+}
+
+
+TEST_F(ParticlesTest, moveParticleVertical) {
+	Config::get_instance().physics.gravity = 1;
+	ComponentManager & mgr = ComponentManager::get_instance();
+	ParticleEmitter & emitter = mgr.get_components_by_id<ParticleEmitter>(0).front().get();
+	emitter.data.end_lifespan = 100;
+	emitter.data.boundary.height = 100;
+	emitter.data.boundary.width = 100;
+	emitter.data.min_speed = 1;
+	emitter.data.max_speed = 1;
+	emitter.data.min_angle = 90;
+	emitter.data.max_angle = 90;
+	emitter.data.emission_rate = 1;
+	for (int a = 1; a < emitter.data.boundary.width/2; a++) {
+		particle_system.update(); 
+		EXPECT_EQ(emitter.data.particles[0].position.y,a);
+	}
+}
-- 
cgit v1.2.3


From 89b06f2c342a7f5e528dd37d96c5a7ffb90cd16c Mon Sep 17 00:00:00 2001
From: JAROWMR <jarorutjes07@gmail.com>
Date: Sun, 10 Nov 2024 18:01:49 +0100
Subject: added boundary tests and added functionality

---
 src/crepe/system/ParticleSystem.cpp | 101 +++++++++++++++++++-----------------
 src/test/ParticleTest.cpp           |  54 +++++++++++++++++--
 2 files changed, 102 insertions(+), 53 deletions(-)

(limited to 'src/crepe')

diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp
index 48fe1d7..e6dc670 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -24,8 +24,6 @@ void ParticleSystem::update() {
 		//	Get transform linked to emitter
 		const Transform& transform = mgr.get_components_by_id<Transform>(emitter.game_object_id).front().get();
 
-		//	Check if within boundary
-		check_bounds(emitter,transform);
 		
 		// Emit particles based on emission_rate
 		int updates = calculate_update(this->update_count,emitter.data.emission_rate);
@@ -40,7 +38,11 @@ void ParticleSystem::update() {
 				emitter.data.particles[j].update();
 			}
 		}
+
+		//	Check if within boundary
+		check_bounds(emitter,transform);
 	}
+
 	update_count++;
 	if(update_count == MAX_UPDATE_COUNT) update_count = 0;
 }
@@ -100,59 +102,62 @@ void ParticleSystem::emit_particle(ParticleEmitter & emitter,const Transform& tr
 
 int ParticleSystem::calculate_update(int count, double emission) {
 
-		//get interger part of the emission
-		double integer_part = std::floor(emission);
+	//get interger part of the emission
+	double integer_part = std::floor(emission);
 
-    // Get the fractional part of the emission
-    double fractional_part = emission - integer_part;
+	// Get the fractional part of the emission
+	double fractional_part = emission - integer_part;
 
-    // Convert the fractional part to a denominator value
-    int denominator = static_cast<int>(1.0 / fractional_part);
+	// Convert the fractional part to a denominator value
+	int denominator = static_cast<int>(1.0 / fractional_part);
 
-    // For emissions like 0.01, 0.1, 0.5, etc., calculate the update frequency
-    if (fractional_part > 0) {
-			// Calculate how often the update should be triggered based on the fractional part
-			if (count % denominator == 0) {
-				return 1;
-			} else {
-				return 0;
-			}
-    }
-		
-    // For integer emissions, return the emission directly
-    return static_cast<int>(emission);
+	// For emissions like 0.01, 0.1, 0.5, etc., calculate the update frequency
+	if (fractional_part > 0) {
+		// Calculate how often the update should be triggered based on the fractional part
+		if (count % denominator == 0) {
+			return 1;
+		} else {
+			return 0;
+		}
+	}
+	
+	// For integer emissions, return the emission directly
+	return static_cast<int>(emission);
 }
 
 void ParticleSystem::check_bounds(ParticleEmitter & emitter,const Transform& transform)
 {
-		Vector2 offset = emitter.data.boundary.offset + transform.position + emitter.data.position;
-		double half_width = emitter.data.boundary.width / 2.0;
-		double half_height = emitter.data.boundary.height / 2.0;
-
-		// Define boundary edges
-		const double left = offset.x - half_width;
-		const double right = offset.x + half_width;
-		const double top = offset.y - half_height;
-		const double bottom = offset.y + half_height;
-
-		std::vector<Particle>& particles = emitter.data.particles;
-		for (Particle& particle : particles)
+	Vector2 offset = emitter.data.boundary.offset + transform.position + emitter.data.position;
+	double half_width = emitter.data.boundary.width / 2.0;
+	double half_height = emitter.data.boundary.height / 2.0;
+
+	// Define boundary edges
+	const double left = offset.x - half_width;
+	const double right = offset.x + half_width;
+	const double top = offset.y - half_height;
+	const double bottom = offset.y + half_height;
+
+	std::vector<Particle>& particles = emitter.data.particles;
+	for (Particle& particle : particles)
+	{
+		const Vector2& position = particle.position;
+
+		// Check if particle is within bounds
+		bool within_bounds = (position.x >= left && position.x <= right && position.y >= top && position.y <= bottom);
+		if (!within_bounds)
 		{
-				const Vector2& position = particle.position;
-
-				// Check if particle is within bounds
-				bool within_bounds = (position.x >= left && position.x <= right && position.y >= top && position.y <= bottom);
-				if (!within_bounds)
-				{
-						if (emitter.data.boundary.reset_on_exit)
-						{
-								particle.active = false;
-						}
-						else
-						{
-								particle.velocity = {0, 0};
-								//todo add that particle goes back to boundary
-						}
-				}
+			if (emitter.data.boundary.reset_on_exit)
+			{
+				particle.active = false;
+			}
+			else
+			{
+				particle.velocity = {0, 0};
+				if (particle.position.x < left) particle.position.x = left;
+        else if (particle.position.x > right) particle.position.x = right;
+        if (particle.position.y < bottom) particle.position.y = bottom;
+        else if (particle.position.y > top) particle.position.y = top;
+			}
 		}
+	}
 }
diff --git a/src/test/ParticleTest.cpp b/src/test/ParticleTest.cpp
index 1b949e3..8c6a7e7 100644
--- a/src/test/ParticleTest.cpp
+++ b/src/test/ParticleTest.cpp
@@ -1,3 +1,4 @@
+#include "api/Vector2.h"
 #include <math.h>
 #include <crepe/ComponentManager.h>
 #include <crepe/api/Config.h>
@@ -80,17 +81,17 @@ TEST_F(ParticlesTest, spawnParticle) {
 	emitter.data.max_angle = 10;
 	particle_system.update(); 
 	//check if nothing happend
-	EXPECT_EQ(emitter.data.particles[0].active, 0);
+	EXPECT_EQ(emitter.data.particles[0].active, false);
 	emitter.data.emission_rate = 1;
 	//check particle spawnes
 	particle_system.update();
-	EXPECT_EQ(emitter.data.particles[0].active, 1);
+	EXPECT_EQ(emitter.data.particles[0].active, true);
 	particle_system.update();
-	EXPECT_EQ(emitter.data.particles[1].active, 1);
+	EXPECT_EQ(emitter.data.particles[1].active, true);
 	particle_system.update();
-	EXPECT_EQ(emitter.data.particles[2].active, 1);
+	EXPECT_EQ(emitter.data.particles[2].active, true);
 	particle_system.update();
-	EXPECT_EQ(emitter.data.particles[3].active, 1);
+	EXPECT_EQ(emitter.data.particles[3].active, true);
 
 	for (auto& particle : emitter.data.particles) {
         // Check velocity range
@@ -141,3 +142,46 @@ TEST_F(ParticlesTest, moveParticleVertical) {
 		EXPECT_EQ(emitter.data.particles[0].position.y,a);
 	}
 }
+
+TEST_F(ParticlesTest, boundaryParticleReset) {
+	Config::get_instance().physics.gravity = 1;
+	ComponentManager & mgr = ComponentManager::get_instance();
+	ParticleEmitter & emitter = mgr.get_components_by_id<ParticleEmitter>(0).front().get();
+	emitter.data.end_lifespan = 100;
+	emitter.data.boundary.height = 10;
+	emitter.data.boundary.width = 10;
+	emitter.data.boundary.reset_on_exit = true;
+	emitter.data.min_speed = 1;
+	emitter.data.max_speed = 1;
+	emitter.data.min_angle = 90;
+	emitter.data.max_angle = 90;
+	emitter.data.emission_rate = 1;
+	for (int a = 0; a < emitter.data.boundary.width/2+1; a++) {
+		particle_system.update(); 
+	}
+	EXPECT_EQ(emitter.data.particles[0].active,false);
+}
+
+TEST_F(ParticlesTest, boundaryParticleStop) {
+	Config::get_instance().physics.gravity = 1;
+	ComponentManager & mgr = ComponentManager::get_instance();
+	ParticleEmitter & emitter = mgr.get_components_by_id<ParticleEmitter>(0).front().get();
+	emitter.data.end_lifespan = 100;
+	emitter.data.boundary.height = 10;
+	emitter.data.boundary.width = 10;
+	emitter.data.boundary.reset_on_exit = false;
+	emitter.data.min_speed = 1;
+	emitter.data.max_speed = 1;
+	emitter.data.min_angle = 90;
+	emitter.data.max_angle = 90;
+	emitter.data.emission_rate = 1;
+	for (int a = 0; a < emitter.data.boundary.width/2+1; a++) {
+		particle_system.update(); 
+	}
+	const double TOLERANCE = 0.01;
+	EXPECT_NEAR(emitter.data.particles[0].velocity.x, 0, TOLERANCE);
+	EXPECT_NEAR(emitter.data.particles[0].velocity.y, 0, TOLERANCE);
+	if(emitter.data.particles[0].velocity.x != 0)	EXPECT_NEAR(std::abs(emitter.data.particles[0].position.x), emitter.data.boundary.height / 2, TOLERANCE);
+	if(emitter.data.particles[0].velocity.y != 0)	EXPECT_NEAR(std::abs(emitter.data.particles[0].position.y), emitter.data.boundary.width / 2, TOLERANCE);
+}
+
-- 
cgit v1.2.3


From 760351abd1a6609a8fe0de1269ac69ea275f4250 Mon Sep 17 00:00:00 2001
From: JAROWMR <jarorutjes07@gmail.com>
Date: Sun, 10 Nov 2024 18:11:57 +0100
Subject: improved readablity code

---
 src/crepe/Particle.h                |   1 +
 src/crepe/system/ParticleSystem.cpp | 157 ++++++++++++++----------------------
 src/crepe/system/ParticleSystem.h   |  61 ++++++++++++--
 3 files changed, 115 insertions(+), 104 deletions(-)

(limited to 'src/crepe')

diff --git a/src/crepe/Particle.h b/src/crepe/Particle.h
index 77c3ea3..88a015d 100644
--- a/src/crepe/Particle.h
+++ b/src/crepe/Particle.h
@@ -14,6 +14,7 @@ namespace crepe {
  * to simulate movement and can also be reset or stopped.
  */
 class Particle {
+	// TODO: add friend particleSsytem and rendersystem. Unit test will fail.
 public:
 	//! Position of the particle in 2D space.
 	Vector2 position;
diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp
index e6dc670..a0b1e55 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -13,151 +13,112 @@ using namespace crepe;
 ParticleSystem::ParticleSystem() {}
 
 void ParticleSystem::update() {
-
 	// Get all emitters
 	ComponentManager & mgr = ComponentManager::get_instance();
-	std::vector<std::reference_wrapper<ParticleEmitter>> emitters
-		= mgr.get_components_by_type<ParticleEmitter>();
+	std::vector<std::reference_wrapper<ParticleEmitter>> emitters = mgr.get_components_by_type<ParticleEmitter>();
 
 	for (ParticleEmitter & emitter : emitters) {
-
-		//	Get transform linked to emitter
+		// Get transform linked to emitter
 		const Transform& transform = mgr.get_components_by_id<Transform>(emitter.game_object_id).front().get();
 
-		
 		// Emit particles based on emission_rate
-		int updates = calculate_update(this->update_count,emitter.data.emission_rate);
-		for (size_t i = 0; i < updates; i++)
-		{
-			emit_particle(emitter,transform);		
+		int updates = calculate_update(this->update_count, emitter.data.emission_rate);
+		for (size_t i = 0; i < updates; i++) {
+			emit_particle(emitter, transform);
 		}
-		
+
 		// Update all particles
-		for (size_t j = 0; j < emitter.data.particles.size(); j++) {
-			if (emitter.data.particles[j].active) {
-				emitter.data.particles[j].update();
+		for (Particle& particle : emitter.data.particles) {
+			if (particle.active) {
+				particle.update();
 			}
 		}
 
-		//	Check if within boundary
-		check_bounds(emitter,transform);
+		// Check if within boundary
+		check_bounds(emitter, transform);
 	}
 
-	update_count++;
-	if(update_count == MAX_UPDATE_COUNT) update_count = 0;
+	update_count = (update_count + 1) % MAX_UPDATE_COUNT;
 }
 
-void ParticleSystem::emit_particle(ParticleEmitter & emitter,const Transform& transform) {
+void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform& transform) {
 	constexpr double DEG_TO_RAD = M_PI / 180.0;
 
 	Vector2 initial_position = emitter.data.position + transform.position;
-	double min_angle = emitter.data.min_angle;
-	double max_angle = emitter.data.max_angle;
-	double random_angle;
-	if(min_angle == max_angle){
-		random_angle = min_angle;
-	}
-	else if (min_angle <= max_angle) {
-			// Standard range (e.g., 10 to 20 degrees)
-			double angle_offset = max_angle - min_angle;
-			random_angle = min_angle + static_cast<double>(std::rand() % static_cast<uint32_t>(angle_offset));
-	} else {
-			// Wrap-around range (e.g., 350 to 10 degrees)
-			double angle_offset = (360 - min_angle) + max_angle;
-			random_angle = min_angle + static_cast<double>(std::rand() % static_cast<uint32_t>(angle_offset));
-			
-			// Wrap around to keep random_angle within 0-360 degrees
-			if (random_angle >= 360) {
-					random_angle -= 360;
-			}
-	}
+	double random_angle = generate_random_angle(emitter.data.min_angle, emitter.data.max_angle);
 
-	// Generate a random speed between min_speed and max_speed
-	double speed_offset = emitter.data.max_speed - emitter.data.min_speed;
-	double random_speed = 0.0;
-	if(emitter.data.max_speed  == emitter.data.min_speed)
-	{
-		random_speed = emitter.data.min_speed;
-	}
-	else {
-		random_speed = emitter.data.min_speed + static_cast<double>(std::rand() % static_cast<uint32_t>(speed_offset));
-	}
-	 
-	// Convert random_angle to radians
+	double random_speed = generate_random_speed(emitter.data.min_speed, emitter.data.max_speed);
 	double angle_radians = random_angle * DEG_TO_RAD;
 
 	Vector2 velocity = {
-        random_speed * std::cos(angle_radians),
-        random_speed * std::sin(angle_radians)
-    };
+		random_speed * std::cos(angle_radians),
+		random_speed * std::sin(angle_radians)
+	};
 
-
-	for (size_t i = 0; i < emitter.data.particles.size(); i++) {
-		if (!emitter.data.particles[i].active) {
-			emitter.data.particles[i].reset(emitter.data.end_lifespan, initial_position,velocity,random_angle);
+	for (Particle& particle : emitter.data.particles) {
+		if (!particle.active) {
+			particle.reset(emitter.data.end_lifespan, initial_position, velocity, random_angle);
 			break;
 		}
 	}
 }
 
-int ParticleSystem::calculate_update(int count, double emission) {
-
-	//get interger part of the emission
+int ParticleSystem::calculate_update(int count, double emission) const {
 	double integer_part = std::floor(emission);
-
-	// Get the fractional part of the emission
 	double fractional_part = emission - integer_part;
 
-	// Convert the fractional part to a denominator value
-	int denominator = static_cast<int>(1.0 / fractional_part);
-
-	// For emissions like 0.01, 0.1, 0.5, etc., calculate the update frequency
 	if (fractional_part > 0) {
-		// Calculate how often the update should be triggered based on the fractional part
-		if (count % denominator == 0) {
-			return 1;
-		} else {
-			return 0;
-		}
+			int denominator = static_cast<int>(1.0 / fractional_part);
+			return (count % denominator == 0) ? 1 : 0;
 	}
-	
-	// For integer emissions, return the emission directly
+
 	return static_cast<int>(emission);
 }
 
-void ParticleSystem::check_bounds(ParticleEmitter & emitter,const Transform& transform)
-{
+void ParticleSystem::check_bounds(ParticleEmitter & emitter, const Transform& transform) {
 	Vector2 offset = emitter.data.boundary.offset + transform.position + emitter.data.position;
 	double half_width = emitter.data.boundary.width / 2.0;
 	double half_height = emitter.data.boundary.height / 2.0;
 
-	// Define boundary edges
-	const double left = offset.x - half_width;
-	const double right = offset.x + half_width;
-	const double top = offset.y - half_height;
-	const double bottom = offset.y + half_height;
+	const double LEFT = offset.x - half_width;
+	const double RIGHT = offset.x + half_width;
+	const double TOP = offset.y - half_height;
+	const double BOTTOM = offset.y + half_height;
 
-	std::vector<Particle>& particles = emitter.data.particles;
-	for (Particle& particle : particles)
-	{
+	for (Particle& particle : emitter.data.particles) {
 		const Vector2& position = particle.position;
+		bool within_bounds = (position.x >= LEFT && position.x <= RIGHT && position.y >= TOP && position.y <= BOTTOM);
 
-		// Check if particle is within bounds
-		bool within_bounds = (position.x >= left && position.x <= right && position.y >= top && position.y <= bottom);
-		if (!within_bounds)
-		{
-			if (emitter.data.boundary.reset_on_exit)
-			{
+		if (!within_bounds) {
+			if (emitter.data.boundary.reset_on_exit) {
 				particle.active = false;
-			}
-			else
-			{
+			} else {
 				particle.velocity = {0, 0};
-				if (particle.position.x < left) particle.position.x = left;
-        else if (particle.position.x > right) particle.position.x = right;
-        if (particle.position.y < bottom) particle.position.y = bottom;
-        else if (particle.position.y > top) particle.position.y = top;
+				if (position.x < LEFT) particle.position.x = LEFT;
+				else if (position.x > RIGHT) particle.position.x = RIGHT;
+				if (position.y < TOP) particle.position.y = TOP;
+				else if (position.y > BOTTOM) particle.position.y = BOTTOM;
 			}
 		}
 	}
 }
+
+double ParticleSystem::generate_random_angle(double min_angle, double max_angle) const {
+	if (min_angle == max_angle) {
+		return min_angle;
+	} else if (min_angle < max_angle) {
+		return min_angle + static_cast<double>(std::rand() % static_cast<int>(max_angle - min_angle));
+	} else {
+		double angle_offset = (360 - min_angle) + max_angle;
+		double random_angle = min_angle + static_cast<double>(std::rand() % static_cast<int>(angle_offset));
+		return (random_angle >= 360) ? random_angle - 360 : random_angle;
+	}
+}
+
+double ParticleSystem::generate_random_speed(double min_speed, double max_speed) const {
+	if (min_speed == max_speed) {
+		return min_speed;
+	} else {
+		return min_speed + static_cast<double>(std::rand() % static_cast<int>(max_speed - min_speed));
+	}
+}
diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h
index 3b9cb54..df89ed9 100644
--- a/src/crepe/system/ParticleSystem.h
+++ b/src/crepe/system/ParticleSystem.h
@@ -4,21 +4,70 @@
 
 namespace crepe {
 
+/**
+ 	* \brief ParticleSystem class responsible for managing particle emission, updates, and bounds checking.
+ 	*/
 class ParticleSystem {
 public:
+	/**
+		* \brief Default constructor.
+		*/
 	ParticleSystem();
+
+	/**
+		* \brief Updates all particle emitters by emitting particles, updating particle states, and checking bounds.
+		*/
 	void update();
 
 private:
-	void emit_particle(ParticleEmitter & emitter,const Transform& transform);
-	int calculate_update(int count, double emission);
-	void check_bounds(ParticleEmitter & emitter,const Transform& transform);
+	/**
+		* \brief Emits a particle from the specified emitter based on its emission properties.
+		* 
+		* \param emitter Reference to the ParticleEmitter.
+		* \param transform Const reference to the Transform component associated with the emitter.
+		*/
+	void emit_particle(ParticleEmitter & emitter, const Transform& transform);
+
+	/**
+		* \brief Calculates the number of times particles should be emitted based on emission rate and update count.
+		* 
+		* \param count Current update count.
+		* \param emission Emission rate.
+		* \return The number of particles to emit.
+		*/
+	int calculate_update(int count, double emission) const;
+
+	/**
+		* \brief Checks whether particles are within the emitter’s boundary, resets or stops particles if they exit.
+		* 
+		* \param emitter Reference to the ParticleEmitter.
+		* \param transform Const reference to the Transform component associated with the emitter.
+		*/
+	void check_bounds(ParticleEmitter & emitter, const Transform& transform);
+
+	/**
+		* \brief Generates a random angle for particle emission within the specified range.
+		* 
+		* \param min_angle Minimum emission angle in degrees.
+		* \param max_angle Maximum emission angle in degrees.
+		* \return Random angle in degrees.
+		*/
+	double generate_random_angle(double min_angle, double max_angle) const;
+
+	/**
+		* \brief Generates a random speed for particle emission within the specified range.
+		* 
+		* \param min_speed Minimum emission speed.
+		* \param max_speed Maximum emission speed.
+		* \return Random speed.
+		*/
+	double generate_random_speed(double min_speed, double max_speed) const;
 
 private:
-	//! counter to count updates to determine how many times emit_particle is called.
+	//! Counter to count updates to determine how many times emit_particle is called.
 	uint32_t update_count = 0;
-	//! determines the lowest amount of emissionrate (1000 = 0.001 = 1 particle per 1000 updates).
-	const uint32_t MAX_UPDATE_COUNT = 100;
+	//! Determines the lowest amount of emission rate (1000 = 0.001 = 1 particle per 1000 updates).
+	static constexpr uint32_t MAX_UPDATE_COUNT = 100;
 };
 
 } // namespace crepe
-- 
cgit v1.2.3


From 08d48aabac838a641ef918da92d9827b214e5da6 Mon Sep 17 00:00:00 2001
From: JAROWMR <jarorutjes07@gmail.com>
Date: Sun, 10 Nov 2024 18:14:57 +0100
Subject: added comments and code standard

---
 src/crepe/Particle.cpp              | 21 +++++++++++++--------
 src/crepe/system/ParticleSystem.cpp |  2 +-
 2 files changed, 14 insertions(+), 9 deletions(-)

(limited to 'src/crepe')

diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp
index d2b1880..cb2ef0d 100644
--- a/src/crepe/Particle.cpp
+++ b/src/crepe/Particle.cpp
@@ -5,25 +5,30 @@
 using namespace crepe;
 
 void Particle::reset(uint32_t lifespan, Vector2 position, Vector2 velocity, double angle) {
+	// Initialize the particle state
 	this->time_in_life = 0;
 	this->lifespan = lifespan;
 	this->position = position;
 	this->velocity = velocity;
-	this->active = true;
 	this->angle = angle;
+	this->active = true;
+	// Reset force accumulation
+	this->force_over_time = {0, 0};
 }
 
 void Particle::update() {
-	time_in_life++;
-	if (time_in_life >= lifespan) 
-	{
+	// Deactivate particle if it has exceeded its lifespan
+	if (++time_in_life >= lifespan) {
 		this->active = false;
 		return;
-	}	
-	velocity += force_over_time;
-	position += velocity;
+	}
+
+	// Update velocity based on accumulated force and update position
+	this->velocity += force_over_time;
+	this->position += velocity;
 }
 
 void Particle::stop_movement() {
-	this->velocity = {0,0};
+	// Reset velocity to halt movement
+	this->velocity = {0, 0};
 }
diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp
index a0b1e55..33027f8 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -38,7 +38,7 @@ void ParticleSystem::update() {
 		check_bounds(emitter, transform);
 	}
 
-	update_count = (update_count + 1) % MAX_UPDATE_COUNT;
+	this->update_count = (this->update_count + 1) % this->MAX_UPDATE_COUNT;
 }
 
 void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform& transform) {
-- 
cgit v1.2.3


From 0006e959643f0150acd230b174287a85d4cb3255 Mon Sep 17 00:00:00 2001
From: JAROWMR <jarorutjes07@gmail.com>
Date: Sun, 10 Nov 2024 18:30:10 +0100
Subject: added forward declaration and const reference sprite

---
 src/crepe/api/ParticleEmitter.h     |  6 +++---
 src/crepe/system/ParticleSystem.cpp |  1 +
 src/crepe/system/ParticleSystem.h   |  4 ++--
 src/test/ParticleTest.cpp           | 22 ++++++++++++++++------
 4 files changed, 22 insertions(+), 11 deletions(-)

(limited to 'src/crepe')

diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h
index 2cda1bb..e81ed3b 100644
--- a/src/crepe/api/ParticleEmitter.h
+++ b/src/crepe/api/ParticleEmitter.h
@@ -5,9 +5,9 @@
 
 #include "Component.h"
 #include "Particle.h"
-#include "Transform.h"
+#include "Sprite.h"
 
-class Sprite;
+// class Sprite;
 
 namespace crepe {
 
@@ -70,7 +70,7 @@ public:
 		//! collection of particles
 		std::vector<Particle> particles;
 		//! sprite reference
-		const Sprite* sprite;
+		const Sprite& sprite;
 	};	
 public:
 	/**
diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp
index 33027f8..ad6699b 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -4,6 +4,7 @@
 
 #include "api/ParticleEmitter.h"
 #include "api/Vector2.h"
+#include "api/Transform.h"
 
 #include "ComponentManager.h"
 #include "ParticleSystem.h"
diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h
index df89ed9..023a886 100644
--- a/src/crepe/system/ParticleSystem.h
+++ b/src/crepe/system/ParticleSystem.h
@@ -1,9 +1,9 @@
 #pragma once
 
-#include "../api/ParticleEmitter.h"
 
 namespace crepe {
-
+class ParticleEmitter;
+class Transform;
 /**
  	* \brief ParticleSystem class responsible for managing particle emission, updates, and bounds checking.
  	*/
diff --git a/src/test/ParticleTest.cpp b/src/test/ParticleTest.cpp
index 8c6a7e7..7883eca 100644
--- a/src/test/ParticleTest.cpp
+++ b/src/test/ParticleTest.cpp
@@ -5,25 +5,36 @@
 #include <crepe/api/GameObject.h>
 #include <crepe/api/Rigidbody.h>
 #include <crepe/api/Transform.h>
+#include <crepe/api/ParticleEmitter.h>
+#include <crepe/Particle.h>
 #include <crepe/system/ParticleSystem.h>
 #include <gtest/gtest.h>
-
+#include <crepe/api/Sprite.h>
 
 using namespace std;
 using namespace std::chrono_literals;
 using namespace crepe;
 
+
+
 class ParticlesTest : public ::testing::Test {
 protected:
-	GameObject * game_object;
 	ParticleSystem particle_system;
 	void SetUp() override {
 		ComponentManager & mgr = ComponentManager::get_instance();
 		std::vector<std::reference_wrapper<Transform>> transforms
 			= mgr.get_components_by_id<Transform>(0);
 		if (transforms.empty()) {
-			game_object = new GameObject(0, "", "", Vector2{0, 0}, 0, 0);
-			game_object->add_component<ParticleEmitter>(ParticleEmitter::Data{
+		
+			
+			GameObject game_object(0, "", "", Vector2{0, 0}, 0, 0);
+
+			Color color(0, 0, 0, 0);
+			Sprite test_sprite = game_object.add_component<Sprite>(
+			make_shared<Texture>("../asset/texture/img.png"), color,
+			FlipSettings{true, true});
+
+			game_object.add_component<ParticleEmitter>(ParticleEmitter::Data{
 				.position = {0,0},
 				.max_particles = 100,
 				.emission_rate = 0,
@@ -40,7 +51,7 @@ protected:
 					.offset = Vector2{0,0},
 					.reset_on_exit = false,
 				},
-				.sprite = nullptr,
+				.sprite = test_sprite,
 			});
 		}
 		transforms = mgr.get_components_by_id<Transform>(0);
@@ -61,7 +72,6 @@ protected:
 		emitter.data.end_lifespan = 0;
 		emitter.data.force_over_time = Vector2{0, 0};
 		emitter.data.boundary = {0, 0, Vector2{0, 0}, false};
-		emitter.data.sprite = nullptr;
 		for (auto& particle : emitter.data.particles) {
 			particle.active = false;
 		}
-- 
cgit v1.2.3


From 46716724df7697fa789329a62f7a5444ceed5585 Mon Sep 17 00:00:00 2001
From: JAROWMR <jarorutjes07@gmail.com>
Date: Sun, 10 Nov 2024 19:18:58 +0100
Subject: changed includes

---
 src/crepe/Particle.cpp            | 2 --
 src/crepe/api/ParticleEmitter.cpp | 3 ++-
 src/crepe/api/ParticleEmitter.h   | 1 +
 src/crepe/system/ParticleSystem.h | 1 +
 4 files changed, 4 insertions(+), 3 deletions(-)

(limited to 'src/crepe')

diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp
index cb2ef0d..3777db0 100644
--- a/src/crepe/Particle.cpp
+++ b/src/crepe/Particle.cpp
@@ -1,7 +1,5 @@
 #include "Particle.h"
 
-#include "api/Transform.h"
-
 using namespace crepe;
 
 void Particle::reset(uint32_t lifespan, Vector2 position, Vector2 velocity, double angle) {
diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp
index 000bf30..1703026 100644
--- a/src/crepe/api/ParticleEmitter.cpp
+++ b/src/crepe/api/ParticleEmitter.cpp
@@ -1,5 +1,6 @@
-#include "Particle.h"
 #include "ParticleEmitter.h"
+#include "Particle.h"
+
 
 using namespace crepe;
 
diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h
index e81ed3b..037effe 100644
--- a/src/crepe/api/ParticleEmitter.h
+++ b/src/crepe/api/ParticleEmitter.h
@@ -6,6 +6,7 @@
 #include "Component.h"
 #include "Particle.h"
 #include "Sprite.h"
+#include "Vector2.h"
 
 // class Sprite;
 
diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h
index 023a886..a74ea79 100644
--- a/src/crepe/system/ParticleSystem.h
+++ b/src/crepe/system/ParticleSystem.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <cstdint>
 
 namespace crepe {
 class ParticleEmitter;
-- 
cgit v1.2.3


From e42d0877592aa1e88afbe0bc65822cd53a82205d Mon Sep 17 00:00:00 2001
From: JAROWMR <jarorutjes07@gmail.com>
Date: Sun, 10 Nov 2024 19:34:15 +0100
Subject: clang format and tidy

---
 src/crepe/Particle.cpp              |   3 +-
 src/crepe/Particle.h                |   5 +-
 src/crepe/api/ParticleEmitter.cpp   |  12 ++---
 src/crepe/api/ParticleEmitter.h     |  13 ++---
 src/crepe/system/ParticleSystem.cpp |  72 +++++++++++++++----------
 src/crepe/system/ParticleSystem.h   |   4 +-
 src/example/particles.cpp           |  17 +++---
 src/makefile                        |   2 +-
 src/test/ParticleTest.cpp           | 103 ++++++++++++++++++++----------------
 9 files changed, 129 insertions(+), 102 deletions(-)

(limited to 'src/crepe')

diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp
index 3777db0..ab55f37 100644
--- a/src/crepe/Particle.cpp
+++ b/src/crepe/Particle.cpp
@@ -2,7 +2,8 @@
 
 using namespace crepe;
 
-void Particle::reset(uint32_t lifespan, Vector2 position, Vector2 velocity, double angle) {
+void Particle::reset(uint32_t lifespan, Vector2 position, Vector2 velocity,
+					 double angle) {
 	// Initialize the particle state
 	this->time_in_life = 0;
 	this->lifespan = lifespan;
diff --git a/src/crepe/Particle.h b/src/crepe/Particle.h
index 88a015d..06431bb 100644
--- a/src/crepe/Particle.h
+++ b/src/crepe/Particle.h
@@ -15,6 +15,7 @@ namespace crepe {
  */
 class Particle {
 	// TODO: add friend particleSsytem and rendersystem. Unit test will fail.
+
 public:
 	//! Position of the particle in 2D space.
 	Vector2 position;
@@ -43,7 +44,8 @@ public:
 	 * \param velocity  The initial velocity of the particle.
 	 * \param angle     The angle of the particle's trajectory or orientation.
 	 */
-	void reset(uint32_t lifespan, Vector2 position, Vector2 velocity, double angle);
+	void reset(uint32_t lifespan, Vector2 position, Vector2 velocity,
+			   double angle);
 	/**
 	 * \brief Updates the particle's state.
 	 *
@@ -58,7 +60,6 @@ public:
 	 * movement.
 	 */
 	void stop_movement();
-	
 };
 
 } // namespace crepe
diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp
index 1703026..e7f298c 100644
--- a/src/crepe/api/ParticleEmitter.cpp
+++ b/src/crepe/api/ParticleEmitter.cpp
@@ -1,20 +1,18 @@
 #include "ParticleEmitter.h"
 #include "Particle.h"
 
-
 using namespace crepe;
 
-ParticleEmitter::ParticleEmitter(uint32_t game_object_id, const Data& data) : Component(game_object_id),data(data) {
-    for (size_t i = 0; i < this->data.max_particles; i++) {
-        this->data.particles.emplace_back();
-    }
+ParticleEmitter::ParticleEmitter(uint32_t game_object_id, const Data & data)
+	: Component(game_object_id), data(data) {
+	for (size_t i = 0; i < this->data.max_particles; i++) {
+		this->data.particles.emplace_back();
+	}
 }
 
-
 ParticleEmitter::~ParticleEmitter() {
 	std::vector<Particle>::iterator it = this->data.particles.begin();
 	while (it != this->data.particles.end()) {
 		it = this->data.particles.erase(it);
 	}
 }
-
diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h
index 037effe..83a1588 100644
--- a/src/crepe/api/ParticleEmitter.h
+++ b/src/crepe/api/ParticleEmitter.h
@@ -12,7 +12,6 @@
 
 namespace crepe {
 
-
 /**
  * \brief Data holder for particle emission parameters.
  *
@@ -28,7 +27,7 @@ public:
 	 * This structure specifies the boundary's size and offset, as well as the
 	 * behavior of particles upon reaching the boundary limits.
 	 */
-	struct Boundary{
+	struct Boundary {
 		//! boundary width (midpoint is emitter location)
 		double width = 0.0;
 		//! boundary height (midpoint is emitter location)
@@ -45,7 +44,7 @@ public:
 	 * Contains settings for the emitter’s position, particle speed, angle, lifespan,
 	 * boundary, and the sprite used for rendering particles.
 	 */
-	struct Data{
+	struct Data {
 		//! position of the emitter
 		Vector2 position;
 		//! maximum number of particles
@@ -71,8 +70,9 @@ public:
 		//! collection of particles
 		std::vector<Particle> particles;
 		//! sprite reference
-		const Sprite& sprite;
-	};	
+		const Sprite & sprite;
+	};
+
 public:
 	/**
 	 * \brief Constructs a ParticleEmitter data holder with specified settings.
@@ -80,8 +80,9 @@ public:
 	 * \param game_object_id  Identifier for the game object using this emitter.
 	 * \param data            Configuration data defining particle properties.
 	 */
-	ParticleEmitter(uint32_t game_object_id, const Data& data);
+	ParticleEmitter(uint32_t game_object_id, const Data & data);
 	~ParticleEmitter();
+
 public:
 	//! Configuration data for particle emission settings.
 	Data data;
diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp
index ad6699b..4a25b47 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -1,10 +1,10 @@
 #include <cmath>
-#include <ctime>
 #include <cstdlib>
+#include <ctime>
 
 #include "api/ParticleEmitter.h"
-#include "api/Vector2.h"
 #include "api/Transform.h"
+#include "api/Vector2.h"
 
 #include "ComponentManager.h"
 #include "ParticleSystem.h"
@@ -16,20 +16,25 @@ ParticleSystem::ParticleSystem() {}
 void ParticleSystem::update() {
 	// Get all emitters
 	ComponentManager & mgr = ComponentManager::get_instance();
-	std::vector<std::reference_wrapper<ParticleEmitter>> emitters = mgr.get_components_by_type<ParticleEmitter>();
+	std::vector<std::reference_wrapper<ParticleEmitter>> emitters
+		= mgr.get_components_by_type<ParticleEmitter>();
 
 	for (ParticleEmitter & emitter : emitters) {
 		// Get transform linked to emitter
-		const Transform& transform = mgr.get_components_by_id<Transform>(emitter.game_object_id).front().get();
+		const Transform & transform
+			= mgr.get_components_by_id<Transform>(emitter.game_object_id)
+				  .front()
+				  .get();
 
 		// Emit particles based on emission_rate
-		int updates = calculate_update(this->update_count, emitter.data.emission_rate);
+		int updates
+			= calculate_update(this->update_count, emitter.data.emission_rate);
 		for (size_t i = 0; i < updates; i++) {
 			emit_particle(emitter, transform);
 		}
 
 		// Update all particles
-		for (Particle& particle : emitter.data.particles) {
+		for (Particle & particle : emitter.data.particles) {
 			if (particle.active) {
 				particle.update();
 			}
@@ -42,23 +47,25 @@ void ParticleSystem::update() {
 	this->update_count = (this->update_count + 1) % this->MAX_UPDATE_COUNT;
 }
 
-void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform& transform) {
+void ParticleSystem::emit_particle(ParticleEmitter & emitter,
+								   const Transform & transform) {
 	constexpr double DEG_TO_RAD = M_PI / 180.0;
 
 	Vector2 initial_position = emitter.data.position + transform.position;
-	double random_angle = generate_random_angle(emitter.data.min_angle, emitter.data.max_angle);
+	double random_angle
+		= generate_random_angle(emitter.data.min_angle, emitter.data.max_angle);
 
-	double random_speed = generate_random_speed(emitter.data.min_speed, emitter.data.max_speed);
+	double random_speed
+		= generate_random_speed(emitter.data.min_speed, emitter.data.max_speed);
 	double angle_radians = random_angle * DEG_TO_RAD;
 
-	Vector2 velocity = {
-		random_speed * std::cos(angle_radians),
-		random_speed * std::sin(angle_radians)
-	};
+	Vector2 velocity = {random_speed * std::cos(angle_radians),
+						random_speed * std::sin(angle_radians)};
 
-	for (Particle& particle : emitter.data.particles) {
+	for (Particle & particle : emitter.data.particles) {
 		if (!particle.active) {
-			particle.reset(emitter.data.end_lifespan, initial_position, velocity, random_angle);
+			particle.reset(emitter.data.end_lifespan, initial_position,
+						   velocity, random_angle);
 			break;
 		}
 	}
@@ -69,15 +76,17 @@ int ParticleSystem::calculate_update(int count, double emission) const {
 	double fractional_part = emission - integer_part;
 
 	if (fractional_part > 0) {
-			int denominator = static_cast<int>(1.0 / fractional_part);
-			return (count % denominator == 0) ? 1 : 0;
+		int denominator = static_cast<int>(1.0 / fractional_part);
+		return (count % denominator == 0) ? 1 : 0;
 	}
 
 	return static_cast<int>(emission);
 }
 
-void ParticleSystem::check_bounds(ParticleEmitter & emitter, const Transform& transform) {
-	Vector2 offset = emitter.data.boundary.offset + transform.position + emitter.data.position;
+void ParticleSystem::check_bounds(ParticleEmitter & emitter,
+								  const Transform & transform) {
+	Vector2 offset = emitter.data.boundary.offset + transform.position
+					 + emitter.data.position;
 	double half_width = emitter.data.boundary.width / 2.0;
 	double half_height = emitter.data.boundary.height / 2.0;
 
@@ -86,9 +95,10 @@ void ParticleSystem::check_bounds(ParticleEmitter & emitter, const Transform& tr
 	const double TOP = offset.y - half_height;
 	const double BOTTOM = offset.y + half_height;
 
-	for (Particle& particle : emitter.data.particles) {
-		const Vector2& position = particle.position;
-		bool within_bounds = (position.x >= LEFT && position.x <= RIGHT && position.y >= TOP && position.y <= BOTTOM);
+	for (Particle & particle : emitter.data.particles) {
+		const Vector2 & position = particle.position;
+		bool within_bounds = (position.x >= LEFT && position.x <= RIGHT
+							  && position.y >= TOP && position.y <= BOTTOM);
 
 		if (!within_bounds) {
 			if (emitter.data.boundary.reset_on_exit) {
@@ -104,22 +114,30 @@ void ParticleSystem::check_bounds(ParticleEmitter & emitter, const Transform& tr
 	}
 }
 
-double ParticleSystem::generate_random_angle(double min_angle, double max_angle) const {
+double ParticleSystem::generate_random_angle(double min_angle,
+											 double max_angle) const {
 	if (min_angle == max_angle) {
 		return min_angle;
 	} else if (min_angle < max_angle) {
-		return min_angle + static_cast<double>(std::rand() % static_cast<int>(max_angle - min_angle));
+		return min_angle
+			   + static_cast<double>(std::rand()
+									 % static_cast<int>(max_angle - min_angle));
 	} else {
 		double angle_offset = (360 - min_angle) + max_angle;
-		double random_angle = min_angle + static_cast<double>(std::rand() % static_cast<int>(angle_offset));
+		double random_angle = min_angle
+							  + static_cast<double>(
+								  std::rand() % static_cast<int>(angle_offset));
 		return (random_angle >= 360) ? random_angle - 360 : random_angle;
 	}
 }
 
-double ParticleSystem::generate_random_speed(double min_speed, double max_speed) const {
+double ParticleSystem::generate_random_speed(double min_speed,
+											 double max_speed) const {
 	if (min_speed == max_speed) {
 		return min_speed;
 	} else {
-		return min_speed + static_cast<double>(std::rand() % static_cast<int>(max_speed - min_speed));
+		return min_speed
+			   + static_cast<double>(std::rand()
+									 % static_cast<int>(max_speed - min_speed));
 	}
 }
diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h
index a74ea79..3155df1 100644
--- a/src/crepe/system/ParticleSystem.h
+++ b/src/crepe/system/ParticleSystem.h
@@ -27,7 +27,7 @@ private:
 		* \param emitter Reference to the ParticleEmitter.
 		* \param transform Const reference to the Transform component associated with the emitter.
 		*/
-	void emit_particle(ParticleEmitter & emitter, const Transform& transform);
+	void emit_particle(ParticleEmitter & emitter, const Transform & transform);
 
 	/**
 		* \brief Calculates the number of times particles should be emitted based on emission rate and update count.
@@ -44,7 +44,7 @@ private:
 		* \param emitter Reference to the ParticleEmitter.
 		* \param transform Const reference to the Transform component associated with the emitter.
 		*/
-	void check_bounds(ParticleEmitter & emitter, const Transform& transform);
+	void check_bounds(ParticleEmitter & emitter, const Transform & transform);
 
 	/**
 		* \brief Generates a random angle for particle emission within the specified range.
diff --git a/src/example/particles.cpp b/src/example/particles.cpp
index 407df4e..6eab046 100644
--- a/src/example/particles.cpp
+++ b/src/example/particles.cpp
@@ -2,14 +2,13 @@
 #include <crepe/api/AssetManager.h>
 
 #include <crepe/Component.h>
+#include <crepe/api/Color.h>
 #include <crepe/api/GameObject.h>
-#include <crepe/api/Rigidbody.h>
-#include <crepe/api/Transform.h>
 #include <crepe/api/ParticleEmitter.h>
+#include <crepe/api/Rigidbody.h>
 #include <crepe/api/Sprite.h>
 #include <crepe/api/Texture.h>
-#include <crepe/api/Color.h>
-
+#include <crepe/api/Transform.h>
 
 using namespace crepe;
 using namespace std;
@@ -18,10 +17,10 @@ int main(int argc, char * argv[]) {
 	GameObject game_object(0, "", "", Vector2{0, 0}, 0, 0);
 	Color color(0, 0, 0, 0);
 	Sprite test_sprite = game_object.add_component<Sprite>(
-	make_shared<Texture>("../asset/texture/img.png"), color,
-	FlipSettings{true, true});
+		make_shared<Texture>("../asset/texture/img.png"), color,
+		FlipSettings{true, true});
 	game_object.add_component<ParticleEmitter>(ParticleEmitter::Data{
-		.position = {0,0},
+		.position = {0, 0},
 		.max_particles = 100,
 		.emission_rate = 0,
 		.min_speed = 0,
@@ -30,11 +29,11 @@ int main(int argc, char * argv[]) {
 		.max_angle = 0,
 		.begin_lifespan = 0,
 		.end_lifespan = 0,
-		.force_over_time = Vector2{0,0},
+		.force_over_time = Vector2{0, 0},
 		.boundary{
 			.width = 0,
 			.height = 0,
-			.offset = Vector2{0,0},
+			.offset = Vector2{0, 0},
 			.reset_on_exit = false,
 		},
 		.sprite = test_sprite,
diff --git a/src/makefile b/src/makefile
index 59298fd..356179e 100644
--- a/src/makefile
+++ b/src/makefile
@@ -87,7 +87,7 @@ LOEK += example/audio_internal.cpp
 TODO += example/components_internal.cpp
 MAX += example/ecs.cpp
 LOEK += example/log.cpp
-JARO += example/particle.cpp
+JARO += example/particles.cpp
 JARO += example/physics.cpp
 TODO += example/rendering.cpp
 LOEK += example/script.cpp
diff --git a/src/test/ParticleTest.cpp b/src/test/ParticleTest.cpp
index 7883eca..6fe3133 100644
--- a/src/test/ParticleTest.cpp
+++ b/src/test/ParticleTest.cpp
@@ -1,22 +1,20 @@
 #include "api/Vector2.h"
-#include <math.h>
 #include <crepe/ComponentManager.h>
+#include <crepe/Particle.h>
 #include <crepe/api/Config.h>
 #include <crepe/api/GameObject.h>
+#include <crepe/api/ParticleEmitter.h>
 #include <crepe/api/Rigidbody.h>
+#include <crepe/api/Sprite.h>
 #include <crepe/api/Transform.h>
-#include <crepe/api/ParticleEmitter.h>
-#include <crepe/Particle.h>
 #include <crepe/system/ParticleSystem.h>
 #include <gtest/gtest.h>
-#include <crepe/api/Sprite.h>
+#include <math.h>
 
 using namespace std;
 using namespace std::chrono_literals;
 using namespace crepe;
 
-
-
 class ParticlesTest : public ::testing::Test {
 protected:
 	ParticleSystem particle_system;
@@ -25,17 +23,16 @@ protected:
 		std::vector<std::reference_wrapper<Transform>> transforms
 			= mgr.get_components_by_id<Transform>(0);
 		if (transforms.empty()) {
-		
-			
+
 			GameObject game_object(0, "", "", Vector2{0, 0}, 0, 0);
 
 			Color color(0, 0, 0, 0);
 			Sprite test_sprite = game_object.add_component<Sprite>(
-			make_shared<Texture>("../asset/texture/img.png"), color,
-			FlipSettings{true, true});
+				make_shared<Texture>("../asset/texture/img.png"), color,
+				FlipSettings{true, true});
 
 			game_object.add_component<ParticleEmitter>(ParticleEmitter::Data{
-				.position = {0,0},
+				.position = {0, 0},
 				.max_particles = 100,
 				.emission_rate = 0,
 				.min_speed = 0,
@@ -44,11 +41,11 @@ protected:
 				.max_angle = 0,
 				.begin_lifespan = 0,
 				.end_lifespan = 0,
-				.force_over_time = Vector2{0,0},
+				.force_over_time = Vector2{0, 0},
 				.boundary{
 					.width = 0,
 					.height = 0,
-					.offset = Vector2{0,0},
+					.offset = Vector2{0, 0},
 					.reset_on_exit = false,
 				},
 				.sprite = test_sprite,
@@ -72,7 +69,7 @@ protected:
 		emitter.data.end_lifespan = 0;
 		emitter.data.force_over_time = Vector2{0, 0};
 		emitter.data.boundary = {0, 0, Vector2{0, 0}, false};
-		for (auto& particle : emitter.data.particles) {
+		for (auto & particle : emitter.data.particles) {
 			particle.active = false;
 		}
 	}
@@ -81,7 +78,8 @@ protected:
 TEST_F(ParticlesTest, spawnParticle) {
 	Config::get_instance().physics.gravity = 1;
 	ComponentManager & mgr = ComponentManager::get_instance();
-	ParticleEmitter & emitter = mgr.get_components_by_id<ParticleEmitter>(0).front().get();
+	ParticleEmitter & emitter
+		= mgr.get_components_by_id<ParticleEmitter>(0).front().get();
 	emitter.data.end_lifespan = 5;
 	emitter.data.boundary.height = 100;
 	emitter.data.boundary.width = 100;
@@ -89,7 +87,7 @@ TEST_F(ParticlesTest, spawnParticle) {
 	emitter.data.max_angle = 0.1;
 	emitter.data.max_speed = 10;
 	emitter.data.max_angle = 10;
-	particle_system.update(); 
+	particle_system.update();
 	//check if nothing happend
 	EXPECT_EQ(emitter.data.particles[0].active, false);
 	emitter.data.emission_rate = 1;
@@ -103,24 +101,30 @@ TEST_F(ParticlesTest, spawnParticle) {
 	particle_system.update();
 	EXPECT_EQ(emitter.data.particles[3].active, true);
 
-	for (auto& particle : emitter.data.particles) {
-        // Check velocity range
-        EXPECT_GE(particle.velocity.x, emitter.data.min_speed);  // Speed should be greater than or equal to min_speed
-        EXPECT_LE(particle.velocity.x, emitter.data.max_speed);  // Speed should be less than or equal to max_speed
-		EXPECT_GE(particle.velocity.y, emitter.data.min_speed);  // Speed should be greater than or equal to min_speed
-        EXPECT_LE(particle.velocity.y, emitter.data.max_speed);  // Speed should be less than or equal to max_speed
-
-        // Check angle range
-        EXPECT_GE(particle.angle, emitter.data.min_angle);  // Angle should be greater than or equal to min_angle
-        EXPECT_LE(particle.angle, emitter.data.max_angle);  // Angle should be less than or equal to max_angle
-    }
-
+	for (auto & particle : emitter.data.particles) {
+		// Check velocity range
+		EXPECT_GE(particle.velocity.x, emitter.data.min_speed);
+		// Speed should be greater than or equal to min_speed
+		EXPECT_LE(particle.velocity.x, emitter.data.max_speed);
+		// Speed should be less than or equal to max_speed
+		EXPECT_GE(particle.velocity.y, emitter.data.min_speed);
+		// Speed should be greater than or equal to min_speed
+		EXPECT_LE(particle.velocity.y, emitter.data.max_speed);
+		// Speed should be less than or equal to max_speed
+
+		// Check angle range
+		EXPECT_GE(particle.angle, emitter.data.min_angle);
+		// Angle should be greater than or equal to min_angle
+		EXPECT_LE(particle.angle, emitter.data.max_angle);
+		// Angle should be less than or equal to max_angle
+	}
 }
 
 TEST_F(ParticlesTest, moveParticleHorizontal) {
 	Config::get_instance().physics.gravity = 1;
 	ComponentManager & mgr = ComponentManager::get_instance();
-	ParticleEmitter & emitter = mgr.get_components_by_id<ParticleEmitter>(0).front().get();
+	ParticleEmitter & emitter
+		= mgr.get_components_by_id<ParticleEmitter>(0).front().get();
 	emitter.data.end_lifespan = 100;
 	emitter.data.boundary.height = 100;
 	emitter.data.boundary.width = 100;
@@ -128,17 +132,17 @@ TEST_F(ParticlesTest, moveParticleHorizontal) {
 	emitter.data.max_speed = 1;
 	emitter.data.max_angle = 0;
 	emitter.data.emission_rate = 1;
-	for (int a = 1; a < emitter.data.boundary.width/2; a++) {
-		particle_system.update(); 
-		EXPECT_EQ(emitter.data.particles[0].position.x,a);
+	for (int a = 1; a < emitter.data.boundary.width / 2; a++) {
+		particle_system.update();
+		EXPECT_EQ(emitter.data.particles[0].position.x, a);
 	}
 }
 
-
 TEST_F(ParticlesTest, moveParticleVertical) {
 	Config::get_instance().physics.gravity = 1;
 	ComponentManager & mgr = ComponentManager::get_instance();
-	ParticleEmitter & emitter = mgr.get_components_by_id<ParticleEmitter>(0).front().get();
+	ParticleEmitter & emitter
+		= mgr.get_components_by_id<ParticleEmitter>(0).front().get();
 	emitter.data.end_lifespan = 100;
 	emitter.data.boundary.height = 100;
 	emitter.data.boundary.width = 100;
@@ -147,16 +151,17 @@ TEST_F(ParticlesTest, moveParticleVertical) {
 	emitter.data.min_angle = 90;
 	emitter.data.max_angle = 90;
 	emitter.data.emission_rate = 1;
-	for (int a = 1; a < emitter.data.boundary.width/2; a++) {
-		particle_system.update(); 
-		EXPECT_EQ(emitter.data.particles[0].position.y,a);
+	for (int a = 1; a < emitter.data.boundary.width / 2; a++) {
+		particle_system.update();
+		EXPECT_EQ(emitter.data.particles[0].position.y, a);
 	}
 }
 
 TEST_F(ParticlesTest, boundaryParticleReset) {
 	Config::get_instance().physics.gravity = 1;
 	ComponentManager & mgr = ComponentManager::get_instance();
-	ParticleEmitter & emitter = mgr.get_components_by_id<ParticleEmitter>(0).front().get();
+	ParticleEmitter & emitter
+		= mgr.get_components_by_id<ParticleEmitter>(0).front().get();
 	emitter.data.end_lifespan = 100;
 	emitter.data.boundary.height = 10;
 	emitter.data.boundary.width = 10;
@@ -166,16 +171,17 @@ TEST_F(ParticlesTest, boundaryParticleReset) {
 	emitter.data.min_angle = 90;
 	emitter.data.max_angle = 90;
 	emitter.data.emission_rate = 1;
-	for (int a = 0; a < emitter.data.boundary.width/2+1; a++) {
-		particle_system.update(); 
+	for (int a = 0; a < emitter.data.boundary.width / 2 + 1; a++) {
+		particle_system.update();
 	}
-	EXPECT_EQ(emitter.data.particles[0].active,false);
+	EXPECT_EQ(emitter.data.particles[0].active, false);
 }
 
 TEST_F(ParticlesTest, boundaryParticleStop) {
 	Config::get_instance().physics.gravity = 1;
 	ComponentManager & mgr = ComponentManager::get_instance();
-	ParticleEmitter & emitter = mgr.get_components_by_id<ParticleEmitter>(0).front().get();
+	ParticleEmitter & emitter
+		= mgr.get_components_by_id<ParticleEmitter>(0).front().get();
 	emitter.data.end_lifespan = 100;
 	emitter.data.boundary.height = 10;
 	emitter.data.boundary.width = 10;
@@ -185,13 +191,16 @@ TEST_F(ParticlesTest, boundaryParticleStop) {
 	emitter.data.min_angle = 90;
 	emitter.data.max_angle = 90;
 	emitter.data.emission_rate = 1;
-	for (int a = 0; a < emitter.data.boundary.width/2+1; a++) {
-		particle_system.update(); 
+	for (int a = 0; a < emitter.data.boundary.width / 2 + 1; a++) {
+		particle_system.update();
 	}
 	const double TOLERANCE = 0.01;
 	EXPECT_NEAR(emitter.data.particles[0].velocity.x, 0, TOLERANCE);
 	EXPECT_NEAR(emitter.data.particles[0].velocity.y, 0, TOLERANCE);
-	if(emitter.data.particles[0].velocity.x != 0)	EXPECT_NEAR(std::abs(emitter.data.particles[0].position.x), emitter.data.boundary.height / 2, TOLERANCE);
-	if(emitter.data.particles[0].velocity.y != 0)	EXPECT_NEAR(std::abs(emitter.data.particles[0].position.y), emitter.data.boundary.width / 2, TOLERANCE);
+	if (emitter.data.particles[0].velocity.x != 0)
+		EXPECT_NEAR(std::abs(emitter.data.particles[0].position.x),
+					emitter.data.boundary.height / 2, TOLERANCE);
+	if (emitter.data.particles[0].velocity.y != 0)
+		EXPECT_NEAR(std::abs(emitter.data.particles[0].position.y),
+					emitter.data.boundary.width / 2, TOLERANCE);
 }
-
-- 
cgit v1.2.3