Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • main
  • polish
2 results

Target

Select target project
  • boris.stefanov/phys_tp_elec
1 result
Select Git revision
  • main
  • polish
2 results
Show changes
Commits on Source (40)
......@@ -2,3 +2,5 @@
build
*.o
*.pdf
main
*_tests
CC:=gcc
CFLAGS:=-g -O3 -Wall -Wextra -fsanitize=address -fsanitize=leak -std=gnu11
LDFLAGS:=-lm
LDFLAGS:=-lm
VPATH:=utils utils/vec2 utils/gfx src
OBJFILES:= vec2.o utils.o field.o draw.o gfx.o
TESTS:= draw_tests field_tests
all: main
exec: main
./main
main: main.o $(OBJFILES)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) -lSDL2
......
# Compilation
## Projet:
make
./main
## Tests
make tests
./field_tests
./draw_tests
doc/2_charges.png

73.9 KiB

doc/2_charges_+Intensity.png

70.9 KiB

doc/2_charges_-Intensity.png

64 KiB

doc/3_charges.png

53.5 KiB

doc/E_FieldOnePointCharge.png

37.1 KiB

doc/draw_test.png

11.9 KiB

doc/fieldlines.jpg

54 KiB

---
title: Physique - Travail Pratique, Champs Electriques
author: Boris Stefanovic
date: 2022-04-25
title: Champs Electriques - Travail Pratique
author:
- MARTIG Joey
- STEFANOVIC Boris
date: 2022-05-23
geometry: "margin=35mm"
mainfont: DejaVu Sans
fontsize: 10pt
header-includes:
- \usepackage{float}
- \let\origfigure\figure
- \let\endorigfigure\endfigure
- \renewenvironment{figure}[1][2] {\expandafter\origfigure\expandafter[H]} {\endorigfigure}
---
# Introduction
Le champ électrique est un _champ vectoriel_ généré par la présence d'une
charge dans un espace.
Une représentation courante de ce champ vectoriel est le dessin de vecteurs à intervalles réguliers autour de la charge.
Une autre représentation est celle des lignes de champ.
C'est autour de cette dernière que se construit ce projet.
![source: https://commons.wikimedia.org/w/index.php?curid=1044851](E_FieldOnePointCharge.png){width="28%"}\ ![source: https://brilliant.org/wiki/electric-field-lines/](fieldlines.jpg){width="72%"}
## Buts
Le but premier de ce projet est de créer un outil de visualisation de
champs électriques pour en faciliter la compréhension.
Il permet d'observer les effets de différents paramètres
(charge, distance, etc.) sur la "forme" du champ.
On peut facilement imaginer réutiliser cet outil dans un autre projet
de simulation, plus complexe.
# Plan
1. **Théorie** Rappel théorique simplifiant la compréhension du porgramme
2. **Méthodologie** Comment nous nous sommes organisé pour ce travail
3. **Implémentation** Structure du programme et fonctionnements
4. **Résultats** Les résultats obtenus ainsi que les tests effectués
5. **Conclusion** Notre conclusion de ce travail
# Théorie
## Charge électrique
La charge électrique peut être comprise comme le déséquilibre entre le
nombre de protons et d'électrons dans un corps.
Une conséquence de ce fait est l'existance d'une charge élémentaire,
qui ne peut pas être divisée en charge non-nulle d'intensité moindre:
la charge d'un seul électron.
Un corps contenant plus d'électrons que de protons aura une charge
dite négative et un corps contenant plus de protons que d'électrons
aura une charge dite positive.
Tout corps chargé aura tendance à retourner à un état neutre
pour autant qu'il soit à proximité d'une charge opposée
avec laquelle il a la possibilité d'échanger des électrons.
L'unité de la charge électrique est le Coulomb $[C]$.
## Champ électrique
Le champ électrique étant un _champ vectoriel_,
sa représentation exacte nécessiterait, une infinité de vecteurs:
un pour chaque point de l'espace.
Le champ électrique d'une charge en un point de l'espace est l'effet
que la charge aura en ce point.
Celui-ci est représentable sous la forme d'un vecteur unique.
Comme dans le cas d'une force résultante, plusieurs charges
produiront en un point donné une valeur de _champ résultant_,
étant la somme vectorielle des champs de toutes les charges,
en ce point.
Tout objet chargé placé dans un champ électrique subira une force due à ce dernier.
L'intensité de cette force est le produit du champ à la position de l'objet
et de la charge de l'objet.
L'expression du champ électrique $\overrightarrow{E}$
d'une charge $Q$ placée à la position $\overrightarrow{p}$,
au point $\overrightarrow{x}$
est la suivante:
$$ \overrightarrow{E_x} = K * \frac{Q}{{|\overrightarrow{x} - \overrightarrow{p}|}^2} * \frac{\overrightarrow{x} - \overrightarrow{p}}{|\overrightarrow{x} - \overrightarrow{p}|} $$
# Méthodologie
## Espace de simulation
Contrairement à la réalité, nous évoluons dans un espace en 2 dimensions.
Celui-ci est limité à un carré de [0, 1]x[0, 1],
ce qui signifie que tout objet dépassant cette limite
ne sera pas calculé.
Cet espace contiendra les charges ainsi que les lignes de champ.
Cela nous permet de borner le nombre de calculs réalisés.
Pour afficher les objets de cet espace, nous représentons
les positions de notre carré comme des proportions de la
taille de l'écran.
## Dessin des lignes de champ
Une ligne de champ nous permet de visualiser le champ électrique
généré par les charges.
Dans notre cas, nous prenons une position aléatoire dans notre espace,
suffisamment éloignée de toute charge, et calculons
la valeur du champ électrique en ce point.
Cela nous donne une direction, que nous suivons dans les deux sens
pour dessiner la ligne, jusqu'à atteindre une charge ou
sortir du carré.
Le résultat de ce processus nous donne une ligne de champ unique.
Il suffit de répéter ces étapes jusqu'à ce que l'on juge le nombre
de lignes suffisant.
# Implémentation
Le programme peut se décomposer en plusieurs sections. La section d'affichage primaire, qui permet d'afficher des lignes ou des cercles, qui seront utilisés plus tard pour afficher des résultats plus complexes. La section de calculs physique, qui permet de calculer les champs à une certaine position dans notre espace. Puis finalement les fonctions qui se servent des deux sections précédentes afin d'afficher les charges et les lignes de champ.
## Dessin de formes géométriques
Celui-ci permettant de tester et d'utiliser toutes les autres fonctionnalités
du projet, il paraissait sage de commencer par implémenter des
fonctions d'affichage de lignes et de cercles.
```c
void gfx_draw_line(struct gfx_context_t *ctxt,
coordinates_t p0,
coordinates_t p1,
uint32_t color);
void gfx_draw_circle(struct gfx_context_t *ctxt,
coordinates_t c,
uint32_t r,
uint32_t color);
```
## Calcul de grandeurs physiques
Ensuite, nous avons écrit les fonctions de calcul de champ électrique
en un point.
$$ E* \frac{\overrightarrow{qP}}{||\overrightarrow{qP}||} $$
```c
bool compute_e(charge_t c, vec2 p, double eps, vec2 *e);
```
$$ Ei * \frac{\overrightarrow{qiP}}{||\overrightarrow{qiP}||} $$
```c
bool compute_total_normalized_e(charge_t *charges,
int num_charges,
vec2 p,
double eps,
vec2 *e);
```
## Affichage des charges et des lignes de champ
Finalement, nous avons implémenté l'affichage des charges et d'une ligne de champ
passant par un point donné.
```c
static void draw_charges(struct gfx_context_t *context,
charge_t *charges,
int num_charges,
double x0,
double x1,
double y0,
double y1);
static bool draw_field_line(struct gfx_context_t *ctxt,
charge_t *charges,
int num_charges,
double dx,
vec2 pos0,
double x0,
double x1,
double y0,
double y1);
```
## Logique principale de haut niveau
Dans un premier temps, nous créons des charges et les
positionnons dans l'espace de simulation.
Nous dessinons ensuite les lignes de champ dans cet espace,
ce qui nous permet de visualiser les champs électriques.
```c
charge_t charges[NC];
for (int i = 0; i < NC; ++i)
charges[i] = charge_create(
i % 2 == 0 ? rand_one() : -rand_one(),
vec2_create(rand_one(), rand_one())
);
struct gfx_context_t *ctxt = gfx_create("elec", WID, HEI);
draw_everything(ctxt, charges, NC, NLINES, DX, 0.0, 1.0, 0.0, 1.0);
gfx_present(ctxt);
while (gfx_keypressed() != SDLK_ESCAPE) {
gfx_present(ctxt);
}
gfx_destroy(ctxt);
```
# Résultats
Voici un aperçu de résultats que nous avons observés avec
notre programme.
Notons que les charges électriques sont de signes opposés.
On voit très rapidement que les lignes de champ relient les charges.
Cela est effectivement le résultat attendu dans ce cas de figure.
Maintenant, si nous augmentons la valeur absolue d'une des charges,
on remarque que celle-ci a un impact plus important sur le champ
électrique total que l'autre charge.
![](2_charges.png){width="33%"}\ ![](2_charges_+Intensity.png){width="33%"}\ ![](2_charges_-Intensity.png){width="33%"}
La série de tests s'est poursuivie par la vérification du
comportement du modèle lorsque l'on ajoutait un nombre de charges
plus grand que 2 dans l'espace.
Le résultat affiché semble cohérent.
![3 charges](3_charges.png){width="50%"}
En plus de ces tests s'appliquant au modèle physique,
nous avons une batterie de tests permettant de se convaincre de façon
non-exhaustive que les différentes fonctions aient un comportement adéquat.
Dans le cas des fonctions de calcul de grandeurs physiques,
nous avons réalisé des tests unitaires.
Pour l'affichage, le test est visuel et nécessite un bref contrôle humain.
Le rôle de ces tests est de former un premier filtre face aux bugs éventuels.
![draw test](draw_test.png){width="35%"}
# Conclusion
Au cours de ce projet, nous avons pu observer la "forme" du champ électrique dans un espace contenant plusieurs charges. Cela nous permettrait de visualiser, par exemple, la direction du vecteur accélération d'une charge mobile qui se retrouverait dans cet environnement. Les résultats obtenus sont satisfaisants par rapport aux résultats attendus. Surtout lorsque plus de deux charges sont présentent.
Le fonctionnement du programme correspond à la théorie fournie et satisfait donc son but principal étant de permettre la visualisation des champs électriques. Il a été difficile dans le temps imparti de réaliser le programme ainsi que des tests, mais nous avons tout de même réussi à réaliser le travail, ainsi que des tests des fonctions les plus utilisées.
Afin d'améliorer le projet, la première étape serait d'y ajouter les tests unitaires manquants. Pour aller plus loin, il serait envisageable de le réaliser de manière dynamique. Cela demanderait un gros travail d'optimisation, pour obtenir suffisamment de frame par seconde ainsi que l'ajout de déplacement des charges. Ajouter des intéractions serait ludique et permettrait de comprendre plus facilement les lignes de champ. Ce serait faisable en laissant l'utilisateur ajouter des lignes de champ manuellement ou en modifiant en direct les charges.
/**
* @file draw.c
* @author Boris Stefanovic, Joey Martig
* @brief Contient les méthodes d'affichage de lignes et de cercles.
* @date 23.05.2022
*/
#include <stdbool.h>
#include "../utils/gfx/gfx.h"
#include "field.h"
/*
(50, 50) → (75, 50)1 , (50, 50) → (72, 62), (50, 50) → (62, 72)
(50, 50) → (50, 75), (50, 50) → (38, 72), (50, 50) → (28, 62)
(50, 50) → (25, 50), (50, 50) → (28, 38), (50, 50) → (37, 28)
(50, 50) → (50, 25), (50, 50) → (62, 28), (50, 50) → (72, 37)
*/
#include "../utils/utils.h"
void gfx_draw_line(struct gfx_context_t *ctxt, coordinates_t p0, coordinates_t p1, uint32_t color) {
int dx = abs(p1.column - p0.column);
int sx = p0.column < p1.column ? 1 : -1;
int dy = -abs(p1.row - p0.row);
int sy = p0.row < p1.row ? 1 : -1;
int error = dx + dy;
int dx = abs((int) p1.column - (int) p0.column);
int sx = p0.column < p1.column ? 1 : -1;
int dy = -abs((int) p1.row - (int) p0.row);
int sy = p0.row < p1.row ? 1 : -1;
int error = dx + dy;
while (true) {
gfx_putpixel(ctxt, p0.column, p0.row, color);
while (true) {
gfx_putpixel(ctxt, p0.column, p0.row, color);
if (p0.column == p1.column && p0.row == p1.row)
break;
if (p0.column == p1.column && p0.row == p1.row)
break;
int e2 = 2 * error;
int e2 = 2 * error;
if (e2 >= dy) {
if (p0.column == p1.column)
break;
error = error + dy;
p0.column = p0.column + sx;
}
if (e2 >= dy) {
if (p0.column == p1.column)
break;
error = error + dy;
p0.column = p0.column + sx;
}
if (e2 <= dx) {
if (p0.row == p1.row)
break;
error = error + dx;
p0.row = p0.row + sy;
}
}
if (e2 <= dx) {
if (p0.row == p1.row)
break;
error = error + dx;
p0.row = p0.row + sy;
}
}
}
void gfx_draw_circle(struct gfx_context_t *ctxt, coordinates c, uint32_t r, uint32_t color) {
int x = 0;
int y = r;
int d = r - 1;
while(y >= x){
gfx_putpixel(ctxt, c.row + x, c.column + y, color);
gfx_putpixel(ctxt, c.row + y, c.column + x, color);
gfx_putpixel(ctxt, c.row - x, c.column + y, color);
gfx_putpixel(ctxt, c.row - y, c.column + x, color);
gfx_putpixel(ctxt, c.row + x, c.column - y, color);
gfx_putpixel(ctxt, c.row + y, c.column - x, color);
gfx_putpixel(ctxt, c.row - x, c.column - y, color);
gfx_putpixel(ctxt, c.row - y, c.column - x, color);
if(d >= 2*x){
d = d-2*x-1;
x++;
}else if(d < 2 * (r-y)){
d = d + 2*y-1;
y--;
}else {
d = d + 2 * (y-x-1);
y--;
x++;
}
}
void gfx_draw_circle(struct gfx_context_t *ctxt, coordinates_t c, uint32_t r, uint32_t color) {
int x = 0;
int y = r;
int d = r - 1;
while (y >= x) {
gfx_putpixel(ctxt, c.column + x, c.row + y, color);
gfx_putpixel(ctxt, c.column + y, c.row + x, color);
gfx_putpixel(ctxt, c.column - x, c.row + y, color);
gfx_putpixel(ctxt, c.column - y, c.row + x, color);
gfx_putpixel(ctxt, c.column + x, c.row - y, color);
gfx_putpixel(ctxt, c.column + y, c.row - x, color);
gfx_putpixel(ctxt, c.column - x, c.row - y, color);
gfx_putpixel(ctxt, c.column - y, c.row - x, color);
if (d >= 2 * x) {
d = d - 2 * x - 1;
x++;
} else if (d < 2 * ((int) r - y)) {
d = d + 2 * y - 1;
y--;
} else {
d = d + 2 * (y - x - 1);
y--;
x++;
}
}
}
/**
* @file draw.h
* @author Boris Stefanovic, Joey Martig
* @date 23.05.2022
*/
#ifndef DRAW_H
#define DRAW_H
#include "field.h"
void gfx_draw_line(struct gfx_context_t *ctxt, coordinates_t p0, coordinates_t p1, uint32_t color);
void gfx_draw_circle(struct gfx_context_t *ctxt, coordinates_t c, uint32_t r, uint32_t color);
#endif
\ No newline at end of file
#endif
/**
* @file draw_tests.c
* @author Boris Stefanovic, Joey Martig
* @brief Test des méthodes d'affichages de lignes et de cercles.
* @date 23.05.2022
*/
#include <stdlib.h>
#include "draw.h"
......@@ -52,5 +59,6 @@ int main() {
gfx_present(ctxt);
}
gfx_destroy(ctxt);
return EXIT_SUCCESS;
}
#include <math.h>
#include <stdbool.h>
#include <stdlib.h>
/**
* @file field.c
* @author Boris Stefanovic, Joey Martig
* @brief Contient les fonctions de claculs physiques et d'affichage des charges et lignes de champ.
* @date 23.05.2022
*/
#include <stdbool.h>
#include "draw.h"
#include "field.h"
#include "../utils/utils.h"
// Compute E*qP/norm(qP)
// Return false if norm(qP) < eps
/// qP = vectoriel(P-q)
/// Compute the vector value of the field generated by a charge at a given point in space.
#define SIGN_SIZE 10
#define CHARGE_R 25
bool compute_e(charge_t c, vec2 p, double eps, vec2 *e) {
vec2 relative_position = vec2_sub(p, c.pos);
if (vec2_norm(relative_position) < eps) return false;
......@@ -18,26 +21,141 @@ bool compute_e(charge_t c, vec2 p, double eps, vec2 *e) {
return true;
}
// Compute the normalized sum of Ei*qiP/norm(qiP)
// Return false if for some qiP, norm(qiP) < eps
bool compute_total_normalized_e(charge_t *charges, int num_charges, vec2 p, double eps, vec2 *e) {
return EXIT_SUCCESS;
*e = vec2_create_zero();
vec2 ei = vec2_create_zero();
for (int i = 0; i < num_charges; ++i) {
if (!compute_e(charges[i], p, eps, &ei)) return false;
*e = vec2_add(*e, ei);
}
*e = vec2_normalize(*e);
return true;
}
double compute_delta_x() {
double result = pow(WID, 2) + pow(HEI, 2);
result = sqrt(result);
return 1 / result;
}
// Compute and then draw all the points belonging to a field line,
// starting from pos0.
// Returns false if pos0 is not a valid position
// (for example if pos0 is too close to a charge).
static bool
draw_field_line(struct gfx_context_t *ctxt, charge_t *charges, int num_charges, double dx, vec2 pos0, double x0,
double x1, double y0, double y1) {
return EXIT_SUCCESS;
// Vérifie si la position se trouve sur l'écran (entre 0 et HEI-1 ou WID-1)
bool is_in_screen(coordinates_t pos) {
return pos.column < WID && pos.row < HEI;
}
// Draw all the charges
// A circle with minus sign for negative charges
// A circle with a plus sign for positive charges
static void
draw_charges(struct gfx_context_t *context, charge_t *charges, int num_charges, double x0, double x1, double y0,
double y1) {
// Si la valeur sort de l'écran, force sa position à la limite la plus proche (-10 -> 0)
void force_in_screen(coordinates_t *pos){
if (!is_in_screen(*pos)){
pos->column = (int)pos->column <= 0 ? 0 : pos->column;
pos->column = (int)pos->column >= WID ? WID-1 : pos->column;
pos->row = (int)pos->row <= 0 ? 0 : pos->row;
pos->row = (int)pos->row >= HEI ? HEI-1 : pos->row;
}
}
bool draw_field_line_point(struct gfx_context_t *ctxt, charge_t *charges, int num_charges, double x0, double x1, double y0, double y1, vec2 pos, vec2 *pos_next, double delta) {
vec2 e;
compute_total_normalized_e(charges, num_charges, pos, 0.01, &e);
*pos_next = vec2_add(pos, vec2_mul(delta, vec2_normalize(e)));
if (pos_next->x <= 0 || pos_next->x >= 1 || pos_next->y <= 0 || pos_next->y >= 1)
return false;
coordinates_t coordinate_pos = position_to_coordinates(WID, HEI, x0, x1, y0, y1, pos);
coordinates_t coordinate_pos_next = position_to_coordinates(WID, HEI, x0, x1, y0, y1, *pos_next);
if (coordinate_pos_next.column <= 0 || coordinate_pos_next.row <= 0)
return false;
gfx_draw_line(ctxt, coordinate_pos, coordinate_pos_next, COLOR_YELLOW);
return !is_in_screen(coordinate_pos);
}
bool line_reach_charge(vec2 pos, charge_t *charges, int num_charges, double dx) {
for (int j = 0; j < num_charges; ++j) {
if (vec2_norm(vec2_sub(pos, charges[j].pos)) < dx / HEI) {
return true;
}
}
return false;
}
/// Compute and then draw all the points belonging to a field line,
/// starting from pos0.
/// Returns false if pos0 is not a valid position
/// (for example if pos0 is too close to a charge).
static bool draw_field_line(struct gfx_context_t *ctxt, charge_t *charges, int num_charges, double dx, vec2 pos0, double x0, double x1, double y0, double y1) {
vec2 pos_positive = vec2_create(pos0.x, pos0.y);
vec2 pos_negative = vec2_create(pos0.x, pos0.y);
vec2 pos_next_positive;
vec2 pos_next_negative;
bool stop_positive = false;
bool stop_negative = false;
double delta = compute_delta_x();
int max_for = 100000;
for (int i = 0; i < max_for && (!stop_positive || !stop_negative); i++) {
stop_positive = line_reach_charge(pos_positive, charges, num_charges, dx);
stop_negative = line_reach_charge(pos_negative, charges, num_charges, dx);
if (!stop_positive) {
stop_positive = draw_field_line_point(ctxt, charges, num_charges, x0, x1, y0, y1, pos_positive, &pos_next_positive, delta);
pos_positive = pos_next_positive;
}
if (!stop_negative) {
stop_negative = draw_field_line_point(ctxt, charges, num_charges, x0, x1, y0, y1, pos_negative, &pos_next_negative, -delta);
pos_negative = pos_next_negative;
}
}
return !stop_positive || !stop_negative;
}
/// Draw all the charges
/// A circle with minus sign for negative charges
/// A circle with a plus sign for positive charges
static void draw_charges(struct gfx_context_t *context, charge_t *charges, int num_charges, double x0, double x1, double y0, double y1) {
for (int i = 0; i < num_charges; ++i) {
coordinates_t charge_center = position_to_coordinates(WID, HEI, x0, x1, y0, y1, charges[i].pos);
gfx_draw_circle(context, charge_center, CHARGE_R, COLOR_WHITE);
coordinates_t sign_src = charge_center;
coordinates_t sign_dst = charge_center;
uint32_t sign_color = COLOR_RED;
if (charges[i].q > 0) {
sign_color = COLOR_BLUE;
sign_src.row -= SIGN_SIZE;
sign_dst.row += SIGN_SIZE;
gfx_draw_line(context, sign_src, sign_dst, sign_color);
sign_src.row = charge_center.row;
sign_dst.row = charge_center.row;
}
sign_src.column -= SIGN_SIZE;
sign_dst.column += SIGN_SIZE;
force_in_screen(&sign_src);
force_in_screen(&sign_dst);
gfx_draw_line(context, sign_src, sign_dst, sign_color);
}
}
void draw_everything(
struct gfx_context_t *ctxt,
charge_t *charges,
int num_charges,
int num_lines,
double dx,
double x0, double x1,
double y0, double y1) {
draw_charges(ctxt, charges, num_charges, x0, x1, y0, y1);
for (int i = 0; i < num_lines; ++i) {
vec2 pos0 = vec2_create(rand_one(), rand_one());
draw_field_line(ctxt, charges, num_charges, dx, pos0, x0, x1, y0, y1);
}
}
/**
* @file field.h
* @author Boris Stefanovic, Joey Martig
* @date 23.05.2022
*/
#ifndef _PHYSIQUE_H_
#define _PHYSIQUE_H_
#include "../utils/vec2/vec2.h"
#include <stdio.h>
#include "../utils/gfx/gfx.h"
#include "../utils/vec2/vec2.h"
#include "../utils/utils.h"
#include <stdio.h>
typedef struct _charge_t {
double q;
vec2 pos;
} charge_t;
#define SIDE_LEN 1000
#define WID SIDE_LEN
#define HEI WID
// Compute E*qP/norm(qP)
// Return false if norm(qP) < eps
/// Compute E*qP/norm(qP)
/// Return false if norm(qP) < eps
/// Compute the vector value of the field generated by a charge at a given point in space.
/// qP = vectoriel(P-q)
bool compute_e(charge_t c, vec2 p, double eps, vec2 *e);
// Compute the normalized sum of Ei*qiP/norm(qiP)
// Return false if for some qiP, norm(qiP) < eps
/// Compute the normalized sum of Ei*qiP/norm(qiP)
/// Return false if for some qiP, norm(qiP) < eps
bool compute_total_normalized_e(charge_t *charges, int num_charges, vec2 p, double eps, vec2 *e);
// Compute and then draw all the points belonging to a field line,
// starting from pos0.
// Returns false if pos0 is not a valid position
// (for example if pos0 is too close to a charge).
static bool draw_field_line(struct gfx_context_t *ctxt, charge_t *charges, int num_charges, double dx, vec2 pos0, double x0, double x1, double y0, double y1);
// Draw all the charges
// A circle with minus sign for negative charges
// A circle with a plus sign for positive charges
static void draw_charges(struct gfx_context_t *context, charge_t *charges, int num_charges, double x0, double x1, double y0, double y1);
bool line_reach_charge(vec2 pos, charge_t *charges, int num_charges, double dx);
bool is_in_screen(coordinates_t pos);
void force_in_screen(coordinates_t *pos);
void draw_everything(
struct gfx_context_t *ctxt,
charge_t *charges,
int num_charges,
int num_lines,
double dx,
double x0, double x1,
double y0, double y1);
#endif
/**
* @file field_tests.c
* @author Boris Stefanovic, Joey Martig
* @brief Test unitaires des méthodes field.h
* @date 23.05.2022
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <stdbool.h>
#include "field.h"
typedef struct _test_result
{
bool passed;
const char *name;
} test_result;
typedef test_result (*unit_test_t)(void);
void print_in_color(char *color, char *text)
{
printf("\033%s", color);
printf("%s", text);
printf("\033[0m");
}
void print_in_red(char *text)
{
print_in_color("[0;31m", text);
}
void print_in_green(char *text)
{
print_in_color("[0;32m", text);
}
bool dbl_eq(double a, double b)
{
return fabs(a - b) < 1e-6;
}
test_result t_compute_e_0()
{
double test_charge_q[] = {0.25, -0.25, -0.75, 0.9, 0.5};
vec2 test_position[] = {
vec2_create(0.25, 0.6),
vec2_create(0.01, 0.01),
vec2_create(0.9, 0.9),
vec2_create(0.5, 0.5),
vec2_create(0.25, 0.25)
};
vec2 awaited_result[] = {
vec2_create(0, 224700000000.000122),
vec2_create(3320060992.530107, 6778457859.748968),
vec2_create(-9855843283.957649, -6065134328.589322),
vec2_create(129427200000, 0),
vec2_create(0, -71904000000)
};
uint32_t nb_tests = sizeof(test_charge_q) / sizeof(double);
bool passed = true;
for (uint32_t i = 0; i < nb_tests; i++)
{
charge_t c = charge_create(test_charge_q[i], vec2_create(0.25, 0.5));
vec2 res = vec2_create_zero();
compute_e(c, test_position[i], 0.01, &res);
if (!vec2_is_approx_equal(res, awaited_result[i], 0.0001))
{
passed = false;
break;
}
}
return (test_result){.passed = passed, .name = "Test compute_e 0"};
}
test_result t_compute_total_normalized_e_0()
{
double test_charge_q[] = {0.25, -0.25, -0.75, 0.9, 0.5};
vec2 test_position[] = {
vec2_create(0.25, 0.6),
vec2_create(0.01, 0.01),
vec2_create(0.9, 0.9),
vec2_create(0.5, 0.5),
vec2_create(0.25, 0.25)
};
vec2 awaited_result[] = {
vec2_create(0.037974, 0.999279),
vec2_create(-0.178064, -0.984019),
vec2_create(-0.108527, -0.994093),
vec2_create(1.000000, 0.000000),
vec2_create(0.192772, -0.981244)
};
uint32_t nb_tests = sizeof(test_charge_q) / sizeof(double);
bool passed = true;
for (uint32_t i = 0; i < nb_tests; i++)
{
charge_t c[2] = {
charge_create(-0.25, vec2_create(0.75, 0.5)),
charge_create(0.25, vec2_create(0.25, 0.5))
};
vec2 res = vec2_create_zero();
compute_total_normalized_e(c, 2, test_position[i], 0.01, &res);
if (!vec2_is_approx_equal(res, awaited_result[i], 0.0001))
{
passed = false;
break;
}
}
return (test_result){.passed = passed, .name = "Test compute_total_normalized 0"};
}
test_result t_line_reach_charge_0()
{
charge_t c[2] = {
charge_create(-0.25, vec2_create(0.75, 0.5)),
charge_create(0.25, vec2_create(0.25, 0.5))
};
vec2 test_position[] = {
vec2_create(0.231, 0.481),
vec2_create(0.731, 0.519),
vec2_create(0.78, 0.5),
vec2_create(0.75, 0.5),
vec2_create(0.25, 0.5),
vec2_create(0.24, 0.49)
};
bool results[] = {false, false, false, true, true, true};
uint32_t nb_tests = sizeof(results) / sizeof(bool);
bool passed = true;
for (uint32_t i = 0; i < nb_tests; i++)
{
if (line_reach_charge(test_position[i], c, 2, 25) != results[i]){
passed = false;
break;
}
}
return (test_result){.passed = passed, .name = "Test line_reach_charge 0"};
}
test_result t_is_in_screen_0()
{
bool passed = true;
coordinates_t p[] = {
coordinates_create(99 / WID * 100, 1 / HEI * 100),
coordinates_create(50 / WID * 100, 50 / HEI * 100),
coordinates_create(1 / WID * 100, 99 / HEI * 100),
coordinates_create(WID, HEI),
coordinates_create(-1, HEI / 2),
coordinates_create(WID / 2, -1),
coordinates_create(-1, -1),
coordinates_create(WID + 10, HEI/2),
coordinates_create(WID / 2, HEI + 10),
coordinates_create(WID + 10, HEI + 10)
};
bool results[] = {true, true, true, false, false, false, false, false, false, false};
uint32_t nb_tests = sizeof(results) / sizeof(bool);
for (size_t i = 0; i < nb_tests; i++)
{
if(is_in_screen(p[i]) != results[i]){
passed = false;
break;
}
}
return (test_result){.passed = passed, .name = "Test is_in_screen 0"};
}
test_result t_force_in_screen_0()
{
bool passed = true;
coordinates_t p[] = {
coordinates_create(99 / WID * 100, 1 / HEI * 100),
coordinates_create(50 / WID * 100, 50 / HEI * 100),
coordinates_create(1 / WID * 100, 99 / HEI * 100),
coordinates_create(WID, HEI),
coordinates_create(-1, HEI / 2),
coordinates_create(WID / 2, -1),
coordinates_create(-1, -1),
coordinates_create(WID + 10, HEI/2),
coordinates_create(WID / 2, HEI + 10),
coordinates_create(WID + 10, HEI + 10)
};
coordinates_t p_results[] = {
coordinates_create(99 / WID * 100, 1 / HEI * 100),
coordinates_create(50 / WID * 100, 50 / HEI * 100),
coordinates_create(1 / WID * 100, 99 / HEI * 100),
coordinates_create(WID-1, HEI-1),
coordinates_create(0, HEI / 2),
coordinates_create(WID / 2, 0),
coordinates_create(0, 0),
coordinates_create(WID -1, HEI/2),
coordinates_create(WID / 2, HEI -1),
coordinates_create(WID -1, HEI -1)
};
uint32_t nb_tests = sizeof(p_results) / sizeof(coordinates_t);
for (size_t i = 0; i < nb_tests; i++)
{
force_in_screen(&p[i]);
if(p[i].column != p_results[i].column || p[i].row != p_results[i].row){
passed = false;
break;
}
}
return (test_result){.passed = passed, .name = "Test is_in_screen 0"};
}
// Add or remove your test function name here
const unit_test_t tests[] = {t_compute_e_0, t_compute_total_normalized_e_0, t_line_reach_charge_0, t_is_in_screen_0, t_force_in_screen_0};
int main()
{
uint32_t nb_tests = sizeof(tests) / sizeof(unit_test_t);
char message[256];
bool all_passed = true;
int main() {
printf("Field_tests\n");
return EXIT_SUCCESS;
for (uint32_t i = 0; i < nb_tests; i++)
{
printf("Running test n°%d: ...\n", i);
test_result r = tests[i]();
if (r.passed)
{
sprintf(message, "\t- %s : OK", r.name);
print_in_green(message);
}
else
{
all_passed = false;
sprintf(message, "\t- %s : FAILED", r.name);
print_in_red(message);
}
printf("\n");
}
if (all_passed)
print_in_green("\nTests suite result : OK\n");
else
print_in_red("\nTests suite result : FAILED\n");
}
/**
* @file main.c
* @author Boris Stefanovic, Joey Martig
* @brief Main du programme de simulation de charges électriques et lignes de champs.
* @date 23.05.2022
*/
#include <stdlib.h>
#include "draw.h"
#include "field.h"
#define NC 3
#define DX 25
#define NLINES 50
int main() {
charge_t charges[NC];
for (int i = 0; i < NC; ++i)
charges[i] = charge_create(i % 2 == 0 ? rand_one() : -rand_one(), vec2_create(rand_one(), rand_one()));
struct gfx_context_t *ctxt = gfx_create("elec", WID, HEI);
draw_everything(ctxt, charges, NC, NLINES, DX, 0.0, 1.0, 0.0, 1.0);
gfx_present(ctxt);
while (gfx_keypressed() != SDLK_ESCAPE);
gfx_destroy(ctxt);
return EXIT_SUCCESS;
}
......@@ -31,7 +31,7 @@ struct gfx_context_t
extern void gfx_putpixel(
struct gfx_context_t *ctxt, uint32_t column, uint32_t row, uint32_t color);
struct gfx_context_t *ctxt, uint32_t column, uint32_t row, uint32_t color);
extern void gfx_clear(struct gfx_context_t *ctxt, uint32_t color);
extern struct gfx_context_t *gfx_create(char *text, uint32_t width, uint32_t height);
extern void gfx_destroy(struct gfx_context_t *ctxt);
......
#include "vec2/vec2.h"
#include "utils.h"
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include "vec2/vec2.h"
#include "utils.h"
coordinates_t coordinates_create(int row_, int column_) {
coordinates_t c = {.row = row_, .column = column_};
return c;
}
coordinates_t coordinates_create(int row_, int column_)
{
coordinates_t c = {.row = row_, .column = column_};
return c;
coordinates_t position_to_coordinates(int width, int height, double x0, double x1, double y0, double y1, vec2 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));
}
// Transform a position in the univers [x0,y0]x[x1,y1] to a screen position
coordinates_t position_to_coordinates(int width, int height, double x0, double x1, double y0, double y1, vec2 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));
int rand_num() {
static bool rand_initialised = false;
if (!rand_initialised) {
srand(time(NULL));
rand_initialised = true;
}
return rand();
}
double rand_one()
{
return (double)rand() / (double)RAND_MAX;
double rand_one() {
return (double) rand_num() / (double) RAND_MAX;
}
charge_t charge_create(double q, vec2 pos)
{
charge_t c = {.q = q, .pos = pos};
return c;
}
\ No newline at end of file
charge_t charge_create(double q, vec2 pos) {
charge_t c = {.q = q, .pos = pos};
return c;
}