Select Git revision
field.c 4.56 KiB
#include <stdbool.h>
#include "draw.h"
#include "field.h"
#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;
double field = K * c.q / vec2_norm_sqr(relative_position);
*e = vec2_mul(field, vec2_normalize(relative_position));
return true;
}
bool compute_total_normalized_e(charge_t *charges, int num_charges, vec2 p, double eps, vec2 *e) {
*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;
}
bool is_in_screen(coordinates_t pos) {
return pos.column < WID && pos.row < HEI;
}
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;
// * 5 à supprimer lorsque le code est optimisé
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;
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);
}
}