Skip to content
Snippets Groups Projects
Select Git revision
  • 8a3e4543c2ce76ee588812a6ed85a71ef93ed9ef
  • main default protected
  • polish
3 results

field.c

Blame
  • 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);
    	}
    }