diff --git a/README.md b/README.md index 25469e2131d0571dd5c798e48d0f79a1520ee0a9..b841ca1027fc5d5ce70e611ce7059af6cb99d83e 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,8 @@ cd src make ./program ``` + +## Windows + +mingw32-make.exe +pacman -S mingw-w64-x86_64-SDL2 diff --git a/src/Makefile b/src/Makefile index 826de5743f4faa8b221b34ecf115dd620c9a1c93..585571d8ebe39a947a7899e0f27884d63fba7457 100644 --- a/src/Makefile +++ b/src/Makefile @@ -11,7 +11,7 @@ endif default: $(TARGET) -charge.o: charge.c charge.h +Charge.o: Charge.c Charge.h $(CC) ${CFLAGS} -c $< main.o: main.c @@ -23,10 +23,19 @@ utils.o: utils.c utils.h vector2.o: vector2.c vector2.h $(CC) ${CFLAGS} -c $< +Rectangle.o: Rectangle.c Rectangle.h + $(CC) ${CFLAGS} -c $< + +Simulation.o: Simulation.c Simulation.h + $(CC) ${CFLAGS} -c $< + +random_number.o: random_number.c random_number.h + $(CC) ${CFLAGS} -c $< + gfx.o: ./gfx/gfx.c ./gfx/gfx.h $(CC) ${CFLAGS} -c $< -$(TARGET): charge.o main.o utils.o vector2.o gfx.o +$(TARGET): Charge.o main.o utils.o vector2.o gfx.o Rectangle.o Simulation.o random_number.o $(CC) -Wall -o $@ $^ $(LIBS) clean: diff --git a/src/README b/src/README deleted file mode 100644 index 233e008f35cc9cb0b6ab5a056696a7c9e6c13641..0000000000000000000000000000000000000000 --- a/src/README +++ /dev/null @@ -1,2 +0,0 @@ -mingw32-make.exe -pacman -S mingw-w64-x86_64-SDL2 diff --git a/src/Rectangle.c b/src/Rectangle.c new file mode 100644 index 0000000000000000000000000000000000000000..c7ecc26680297ea214299eb67e83aa7ec6e45081 --- /dev/null +++ b/src/Rectangle.c @@ -0,0 +1,17 @@ +#include "Rectangle.h" + +#include <stdlib.h> + +#include "vector2.h" + +Rectangle *rectangle_init(int x0, int y0, int x1, int y1) { + Rectangle *rectangle = (Rectangle *)malloc(sizeof(Rectangle)); + rectangle->top_left = vector2_create(x0, y0); + rectangle->bottom_right = vector2_create(x1, y1); + return rectangle; +} + +void rectangle_destroy(Rectangle **rectangle) { + free(*rectangle); + *rectangle = NULL; +} diff --git a/src/Rectangle.h b/src/Rectangle.h new file mode 100644 index 0000000000000000000000000000000000000000..aacb71d9a6547820102162e2698d9d6ec3f65fa0 --- /dev/null +++ b/src/Rectangle.h @@ -0,0 +1,14 @@ +#ifndef RECTANGLE_H +#define RECTANGLE_H + +#include "vector2.h" + +typedef struct Rectangle { + vector2_t top_left; + vector2_t bottom_right; +} Rectangle; + +Rectangle *rectangle_init(int x0, int y0, int x1, int y1); +void rectangle_destroy(Rectangle **rectangle); + +#endif diff --git a/src/Simulation.c b/src/Simulation.c new file mode 100644 index 0000000000000000000000000000000000000000..eaa128acd7def04e9a54ff3c6f67ef2b270a7a08 --- /dev/null +++ b/src/Simulation.c @@ -0,0 +1,154 @@ +#include "Simulation.h" + +#include <stdlib.h> + +#include "Charge.h" +#include "gfx/gfx.h" +#include "random_number.h" +#include "utils.h" +#include "vector2.h" + +static const int MIN_CHARGES = 2; +static const int MAX_CHARGES = 5; +const int CHARGE_CIRCLE_RADIUS = 20; +const double K = 8.988e9; +const double ELEMENTARY_CHARGE = 1.602e-19; + +static Charge generate_random_charge() { + vector2_t position = vector2_create(random_number_between_0_and_1(), random_number_between_0_and_1()); + int sign = rand() % 2 == 0 ? 1 : -1; + return charge_init(ELEMENTARY_CHARGE * sign, position); +} + +static Charge *generate_random_charges(int *charges_length) { + *charges_length = random_number_between(MIN_CHARGES, MAX_CHARGES); + Charge *charges = (Charge *)malloc(sizeof(Charge) * *charges_length); + + for (int i = 0; i < *charges_length; i++) { + charges[i] = generate_random_charge(); + // TODO : check if charge is not close to border or close to another charge + } + + return charges; +} + +static void draw_charge(Charge charge, struct gfx_context_t *ctxt, Rectangle *universe) { + coordinates_t c = position_to_coordinates(SCREEN_WIDTH, SCREEN_HEIGHT, universe, charge.pos); + + int radius = CHARGE_CIRCLE_RADIUS; + gfx_draw_circle(ctxt, c, radius, COLOR_WHITE); + + int color = charge.q > 0 ? COLOR_RED : COLOR_BLUE; + int half_length = (int)(radius * .6); + gfx_draw_line(ctxt, coordinates_create(c.row, c.column - half_length), coordinates_create(c.row, c.column + half_length), color); + + if (charge.q > 0) { + gfx_draw_line(ctxt, coordinates_create(c.row - half_length, c.column), coordinates_create(c.row + half_length, c.column), color); + } +} + +static void draw_charges(Simulation *simulation, struct gfx_context_t *ctxt) { + for (int i = 0; i < simulation->charges_length; i++) { + draw_charge(simulation->charges[i], ctxt, simulation->universe); + } +} + +static bool compute_e(Charge charge, vector2_t p, double eps, vector2_t *e) { + vector2_t r = vector2_substract(charge.pos, p); + double e_intensity = K * fabs(charge.q) / vector2_norm_sqr(r); + *e = vector2_multiply(vector2_normalize(r), e_intensity); + + if (charge.q > 0) { + *e = vector2_multiply(*e, -1); + } + + return vector2_norm(r) >= eps; +} + +static bool compute_total_normalized_e(int charges_length, Charge *charges, vector2_t p, double eps, vector2_t *e) { + *e = vector2_create_zero(); + + for (int i = 0; i < charges_length; i += 1) { + vector2_t e_i; + if (!compute_e(charges[i], p, eps, &e_i)) { + return false; + } + + *e = vector2_add(*e, e_i); + } + + *e = vector2_normalize(*e); + return true; +} + +static bool is_out_of_bounds(Rectangle *universe, vector2_t point) { + return point.x < universe->top_left.x || point.x > universe->bottom_right.x || point.y < universe->top_left.y || point.y > universe->bottom_right.y; +} + +static bool compute_next_point(Rectangle *universe, int charges_length, Charge *charges, vector2_t current_pos, double eps, double dx, int direction, vector2_t *next_point) { + vector2_t electric_field; + if (!compute_total_normalized_e(charges_length, charges, current_pos, eps, &electric_field)) { + return false; + } + + *next_point = vector2_add(current_pos, vector2_multiply(vector2_multiply(electric_field, dx), direction)); + + if (is_out_of_bounds(universe, *next_point)) { + return false; + } + + return true; +} + +static void draw_field_line_with_direction(struct gfx_context_t *ctxt, Rectangle *universe, int charges_length, Charge *charges, double dx, vector2_t pos0, int direction) { + vector2_t current_point = pos0; + + while (true) { + vector2_t next_point; + if (!compute_next_point(universe, charges_length, charges, current_point, 0.027, dx, direction, &next_point)) { + break; + } + + coordinates_t current_coordinates = position_to_coordinates(SCREEN_WIDTH, SCREEN_HEIGHT, universe, current_point); + coordinates_t next_coordinates = position_to_coordinates(SCREEN_WIDTH, SCREEN_HEIGHT, universe, next_point); + gfx_draw_line(ctxt, current_coordinates, next_coordinates, COLOR_WHITE); + current_point = next_point; + } +} + +static void draw_field_line(struct gfx_context_t *ctxt, Rectangle *universe, int charges_length, Charge *charges, double dx, vector2_t pos0) { + draw_field_line_with_direction(ctxt, universe, charges_length, charges, dx, pos0, -1); + draw_field_line_with_direction(ctxt, universe, charges_length, charges, dx, pos0, 1); +} + +// --- + +Simulation *simulation_init(Rectangle *universe, double delta_x) { + Simulation *simulation = (Simulation *)malloc(sizeof(Simulation)); + simulation->universe = universe; + simulation->charges = generate_random_charges(&simulation->charges_length); + simulation->delta_x = delta_x; + return simulation; +} + +void simulation_draw(Simulation *simulation, struct gfx_context_t *ctxt) { + draw_charges(simulation, ctxt); + + // // Drawing of field lines from randomly placed points. + // int number_random_points = 100; + // for (int i = 0; i < number_random_points; i += 1) { + // draw_field_line(canvas, charges, charges_length, delta_x, vector2_create(random_number_between_0_and_1(), random_number_between_0_and_1()), x0, x1, y0, y1); + // } + + // Drawing of the field lines from points placed around each of the particles (the display is more homogeneous with this technique). + for (int32_t i = 0; i < simulation->charges_length; i += 1) { + vector2_t pos = simulation->charges[i].pos; + + double angle = 0; + while (angle < 2 * M_PI) { + angle += 2 * M_PI / 64; + vector2_t pos0 = vector2_add(pos, vector2_create(cos(angle) * 0.1, sin(angle) * 0.1)); + draw_field_line(ctxt, simulation->universe, simulation->charges_length, simulation->charges, simulation->delta_x, pos0); + } + } +} diff --git a/src/Simulation.h b/src/Simulation.h new file mode 100644 index 0000000000000000000000000000000000000000..bed10866594efd614f6155c90cd3f0e6a53b44ae --- /dev/null +++ b/src/Simulation.h @@ -0,0 +1,19 @@ +#ifndef SIMULATION_H +#define SIMULATION_H + +#include "Charge.h" +#include "Rectangle.h" +#include "gfx/gfx.h" + +typedef struct Simulation { + Rectangle *universe; + int charges_length; + Charge *charges; + double delta_x; +} Simulation; + +Simulation *simulation_init(Rectangle *universe, double delta_x); +void simulation_destroy(Simulation **simulation); +void simulation_draw(Simulation *simulation, struct gfx_context_t *ctxt); + +#endif diff --git a/src/charge.c b/src/charge.c index 795460c5ace080331fe27d96ee8ea0d3df39475c..061c76f6c499cadc0841277eac9da3a3b991aa31 100644 --- a/src/charge.c +++ b/src/charge.c @@ -1,110 +1,7 @@ -#include "charge.h" +#include "Charge.h" -#include <math.h> -#include <stdio.h> -#include <stdlib.h> - -#include "gfx/gfx.h" -#include "utils.h" #include "vector2.h" -const double K = 8.988e9; -const double ELEMENTARY_CHARGE = 1.602e-19; -const double THRESHOLD_CHARGES_DISTANCES = 0.5; -const double EPSILON = 0.05; - -double compute_delta_x(int width, int height) { - return 1.0 / sqrt((width * width) + (height * height)); -} - -bool compute_next_point(charge_t *charges, int num_charges, vector2_t current_pos, double eps, vector2_t *new_pos, double dx, int orientation) { - vector2_t electric_field; - if (compute_total_normalized_e(charges, num_charges, current_pos, eps, &electric_field)) { - *new_pos = vector2_add(current_pos, vector2_multiply(vector2_multiply(electric_field, dx), orientation)); - return true; - } - - return false; -} - -bool is_out_of_bounds(vector2_t position, double x0, double x1, double y0, double y1) { - return position.x < x0 || position.x > x1 || position.y < y0 || position.y > y1; -} - -charge_t charge_create(double q, vector2_t pos) { - charge_t c = {.q = q, .pos = pos}; - return c; -} - -bool compute_e(charge_t c, vector2_t p, double eps, vector2_t *e) { - *e = vector2_create_zero(); - vector2_t r = vector2_substract(c.pos, p); - double norm_r = vector2_norm(r); - double E_i = K * (fabs(c.q) / (norm_r * norm_r)); - *e = vector2_multiply(vector2_normalize(r), E_i); - - if (c.q > 0) { - *e = vector2_multiply(*e, -1); - } - - return norm_r >= eps; -} - -bool compute_total_normalized_e(charge_t *charges, int num_charges, vector2_t p, double eps, vector2_t *e) { - *e = vector2_create_zero(); - - for (int i = 0; i < num_charges; i += 1) { - vector2_t tmp; - if (!compute_e(charges[i], p, eps, &tmp)) { - return false; - } - - *e = vector2_add(*e, tmp); - } - - *e = vector2_normalize(*e); - return true; -} - -void draw_field_line(struct gfx_context_t *ctxt, charge_t *charges, int num_charges, double dx, vector2_t pos0, double x0, double x1, double y0, double y1, int orientation) { - vector2_t current_pos = pos0; - - while (true) { - vector2_t new_pos; - - if (!compute_next_point(charges, num_charges, current_pos, 0.027, &new_pos, dx, orientation)) { - break; - } - - if (is_out_of_bounds(new_pos, x0, x1, y0, y1)) { - break; - } - - coordinates_t current_coordinates = position_to_coordinates(SCREEN_WIDTH, SCREEN_HEIGHT, x0, x1, y0, y1, current_pos); - coordinates_t new_coordinates = position_to_coordinates(SCREEN_WIDTH, SCREEN_HEIGHT, x0, x1, y0, y1, new_pos); - gfx_draw_line(ctxt, current_coordinates, new_coordinates, COLOR_WHITE); - current_pos = new_pos; - } -} - -void draw_field_lines(struct gfx_context_t *ctxt, charge_t *charges, int num_charges, double dx, vector2_t pos0, double x0, double x1, double y0, double y1){ - draw_field_line(ctxt, charges, num_charges, dx, pos0, x0, x1, y0, y1, -1); - draw_field_line(ctxt, charges, num_charges, dx, pos0, x0, x1, y0, y1, 1); -} - -void draw_charges(struct gfx_context_t *ctxt, charge_t *charges, int num_charges, double x0, double x1, double y0, double y1) { - for (int32_t i = 0; i < num_charges; i += 1) { - coordinates_t c = position_to_coordinates(SCREEN_WIDTH, SCREEN_HEIGHT, x0, x1, y0, y1, charges[i].pos); - - int32_t radius = 20; - gfx_draw_circle(ctxt, c, radius, COLOR_WHITE); - - int32_t color = charges[i].q > 0 ? COLOR_RED : COLOR_BLUE; - int32_t half_length = (int32_t)(radius * .6); - gfx_draw_line(ctxt, coordinates_create(c.row, c.column - half_length), coordinates_create(c.row, c.column + half_length), color); - - if (charges[i].q > 0) { - gfx_draw_line(ctxt, coordinates_create(c.row - half_length, c.column), coordinates_create(c.row + half_length, c.column), color); - } - } +Charge charge_init(double q, vector2_t pos) { + return (Charge){.q = q, .pos = pos}; } diff --git a/src/charge.h b/src/charge.h index a2a29b7303626b138f30eba6b188d75334c28563..e2af4bccbc40919d708e8f0a94f4a48bf8f5231d 100644 --- a/src/charge.h +++ b/src/charge.h @@ -1,28 +1,13 @@ #ifndef CHARGE_H #define CHARGE_H -#include <stdbool.h> -#include <stdint.h> - -#include "gfx/gfx.h" -#include "utils.h" #include "vector2.h" -extern const double K; -extern const double ELEMENTARY_CHARGE; - -typedef struct _charge_t { +typedef struct Charge { double q; vector2_t pos; -} charge_t; - -double compute_delta_x(int width, int height); +} Charge; -charge_t charge_create(double q, vector2_t pos); +Charge charge_init(double q, vector2_t pos); -bool compute_e(charge_t c, vector2_t p, double eps, vector2_t *e); -bool compute_total_normalized_e(charge_t *charges, int num_charges, vector2_t p, double eps, vector2_t *e); -void draw_field_line(struct gfx_context_t *ctxt, charge_t *charges, int num_charges, double dx, vector2_t pos0, double x0, double x1, double y0, double y1, int orientation); -void draw_charges(struct gfx_context_t *ctxt, charge_t *charges, int num_charges, double x0, double x1, double y0, double y1); -void draw_field_lines(struct gfx_context_t *ctxt, charge_t *charges, int num_charges, double dx, vector2_t pos0, double x0, double x1, double y0, double y1); #endif diff --git a/src/main.c b/src/main.c index 9d4c58fcbcc6354b46098ea95e481b86b98f2630..cb890c8a0c28d098750fc3751db6122cc43d1762 100644 --- a/src/main.c +++ b/src/main.c @@ -1,12 +1,21 @@ #include <math.h> -#include <stdio.h> +// #include <stdio.h> #include <stdlib.h> #include <time.h> -#include "charge.h" +#include "Rectangle.h" +#include "Simulation.h" #include "gfx/gfx.h" #include "utils.h" -#include "vector2.h" + +static const int UNIVERSE_X0 = 0; +static const int UNIVERSE_Y0 = 0; +static const int UNIVERSE_X1 = 1; +static const int UNIVERSE_Y1 = 1; + +double compute_delta_x(int width, int height) { + return 1.0 / sqrt((width * width) + (height * height)); +} int main(int argc, char *argv[]) { srand(time(NULL)); @@ -17,49 +26,11 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - double x0 = 0; - double y0 = 0; - double x1 = 1; - double y1 = 1; - - int num_charges = 2; - charge_t *charges = (charge_t *)malloc(sizeof(charge_t) * num_charges); - charges[0] = charge_create(ELEMENTARY_CHARGE, vector2_create(.25, .8)); - charges[1] = charge_create(-ELEMENTARY_CHARGE * 5, vector2_create(.85, .2)); - // charges[2] = charge_create(-ELEMENTARY_CHARGE * 4, vector2_create(.5, .25)); - // charges[3] = charge_create(ELEMENTARY_CHARGE * 5, vector2_create(.7, .75)); - - double dx = compute_delta_x(SCREEN_WIDTH, SCREEN_HEIGHT); - gfx_clear(canvas, COLOR_BLACK); - draw_charges(canvas, charges, num_charges, x0, x1, y0, y1); - // // Default strategy. - // int num_points = 100; - // for (int i = 0; i < num_points; i += 1) { - // draw_field_lines(canvas, charges, num_charges, dx, vector2_create(rand_one(), rand_one()), x0, x1, y0, y1); - // } - - // // Alternative strategy 1. - // for (int32_t i = 1; i < SCREEN_HEIGHT; i += 1) { - // for (int32_t j = 1; j < SCREEN_WIDTH; j += 1) { - // double x = 1.0 / (SCREEN_WIDTH * .05) * j; - // double y = 1.0 / (SCREEN_HEIGHT * .05) * i; - // draw_field_lines(canvas, charges, num_charges, dx, vector2_create(x, y), x0, x1, y0, y1); - // } - // } - - // Alternative strategy 2. - for (int32_t i = 0; i < num_charges; i += 1) { - vector2_t pos = charges[i].pos; - - double angle = 0; - while (angle < 2 * M_PI) { - angle += 2 * M_PI / 64; - vector2_t pos0 = vector2_add(pos, vector2_create(cos(angle) * 0.1, sin(angle) * 0.1)); - draw_field_lines(canvas, charges, num_charges, dx, pos0, x0, x1, y0, y1); - } - } + Rectangle *universe = rectangle_init(UNIVERSE_X0, UNIVERSE_Y0, UNIVERSE_X1, UNIVERSE_Y1); + Simulation *simulation = simulation_init(universe, compute_delta_x(SCREEN_WIDTH, SCREEN_HEIGHT)); + simulation_draw(simulation, canvas); gfx_present(canvas); diff --git a/src/random_number.c b/src/random_number.c new file mode 100644 index 0000000000000000000000000000000000000000..22f2ddc64f3354e7af8bd9e06c8e8aa1b5e19f83 --- /dev/null +++ b/src/random_number.c @@ -0,0 +1,11 @@ +#include "random_number.h" + +#include <stdlib.h> + +int random_number_between(int min, int max) { + return min + rand() % (max - min + 1); +} + +double random_number_between_0_and_1() { + return (double)rand() / RAND_MAX; +} diff --git a/src/random_number.h b/src/random_number.h new file mode 100644 index 0000000000000000000000000000000000000000..029eb6bd8698f1b30b6755906004468cf76c6680 --- /dev/null +++ b/src/random_number.h @@ -0,0 +1,7 @@ +#ifndef RANDOM_NUMBER_H +#define RANDOM_NUMBER_H + +int random_number_between(int min, int max); +double random_number_between_0_and_1(); + +#endif diff --git a/src/utils.c b/src/utils.c index 9d2bfc8307b8b04080ffcee52a8ed64f6e1aee3b..95e3691f2dde7bd3988d5c1b1d16d56850438f0d 100644 --- a/src/utils.c +++ b/src/utils.c @@ -3,6 +3,7 @@ #include <math.h> #include <stdlib.h> +#include "Rectangle.h" #include "vector2.h" const int SCREEN_WIDTH = 750; @@ -13,12 +14,8 @@ coordinates_t coordinates_create(int row_, int column_) { return c; } -coordinates_t position_to_coordinates(int width, int height, double x0, double x1, double y0, double y1, vector2_t pos) { - double dx = x1 - x0; - double dy = y1 - y0; - return coordinates_create((int)round(height * (pos.y - y0) / dy), (int)round(width * (pos.x - x0) / dx)); -} - -double rand_one() { - return (double)rand() / (double)RAND_MAX; +coordinates_t position_to_coordinates(int width, int height, Rectangle *universe, vector2_t pos) { + double dx = universe->bottom_right.x - universe->top_left.x; + double dy = universe->bottom_right.y - universe->top_left.y; + return coordinates_create((int)round(height * (pos.y - universe->top_left.y) / dy), (int)round(width * (pos.x - universe->top_left.x) / dx)); } diff --git a/src/utils.h b/src/utils.h index 0266052dc26efc916e0719bf4eb54b540c459eb5..25b9acd2d85cb8037d8d1789354b7cf39adbe2ba 100644 --- a/src/utils.h +++ b/src/utils.h @@ -3,6 +3,7 @@ #include <stdint.h> +#include "Rectangle.h" #include "vector2.h" extern const int SCREEN_WIDTH; @@ -14,7 +15,6 @@ typedef struct _coordinates_t { } coordinates_t; coordinates_t coordinates_create(int row_, int column_); -coordinates_t position_to_coordinates(int width, int height, double x0, double x1, double y0, double y1, vector2_t pos); -double rand_one(); +coordinates_t position_to_coordinates(int width, int height, Rectangle *universe, vector2_t pos); #endif