diff --git a/charge/charge.h b/charge/charge.h index 1f8343717814a685a99dd785be0f2424e8119a63..097fe21859857c0fa949941563d4d0b62aa85d35 100644 --- a/charge/charge.h +++ b/charge/charge.h @@ -16,6 +16,7 @@ #include <math.h> #include <stdbool.h> #include <stdint.h> +#include <stdio.h> #include <stdlib.h> #define K 8.988e9 diff --git a/draw/draw.c b/draw/draw.c index e372aed509f8c600fe4b5ea45697d453949a5213..e178166fbee66f348d743a45036523ef11c9e2a3 100644 --- a/draw/draw.c +++ b/draw/draw.c @@ -48,6 +48,19 @@ void draw_line(vec2 start, vec2 end) { glEnd(); } +void get_heatmap_color(double value, double *red, double *green, double *blue) { + double aR = 0.0; + double aG = 0.0; + double aB = 0.0; // RGB for our 1st color (blue in this case). + double bR = 0.31; + double bG = 0.23; + double bB = 0.31; // RGB for our 2nd color (red in this case). + + *red = (bR - aR) * value + aR; // Evaluated as -255*value + 255. + *green = (bG - aG) * value + aG; // Evaluates as 0. + *blue = (bB - aB) * value + aB; // Evaluates as 255*value + 0. +} + //===================== // PUBLIC //===================== @@ -66,7 +79,7 @@ void draw_textbox(vec2 pos, char *text) { glEnd(); glBegin(GL_LINE_LOOP); - glColor3f(255, 255, 255); + glColor3f(1, 1, 1); glVertex2d(pos.x, pos.y); glVertex2d(pos.x + textbox_width, pos.y); glVertex2d(pos.x + textbox_width, pos.y + textbox_height); @@ -74,7 +87,7 @@ void draw_textbox(vec2 pos, char *text) { glEnd(); // Text - glColor3f(255, 255, 255); + glColor3f(1, 1, 1); glRasterPos2i(pos.x + 4, pos.y + CHAR_HEIGHT); glutBitmapString(GLUT_BITMAP_8_BY_13, (unsigned char *)text); } @@ -86,7 +99,6 @@ void draw_field_line_segment(charge_t *charges, int num_charges, double dx, vec2 old_coord = position_to_coordinates(w, h, p); vec2 p_coord = position_to_coordinates(w, h, next); - glColor3f(255, 255, 255); draw_line((vec2){p_coord.x, p_coord.y}, (vec2){old_coord.x, old_coord.y}); @@ -103,6 +115,70 @@ void draw_field_line(charge_t *charges, int num_charges, double dx, vec2 pos0, draw_field_line_segment(charges, num_charges, dx, w, h, pos0, pos1, false); } +void draw_heatmap(charge_t *charges, int num_charges, int w, int h) { + vec2 *intensities = malloc(sizeof(vec2) * (w * h)); + double min = DBL_MAX, max = 0; + + int i = 0; + for (double y = 0; y < 1.0 - 1.0 / h; y += 1.0 / h) { + for (double x = 0; x < 1.0 - 1.0 / w; x += 1.0 / w) { + vec2 p = vec2_create(x, y); + compute_total_normalized_e(charges, num_charges, p, EPS, + &intensities[i]); + + double in = vec2_norm(intensities[i]); + if (in < min && in > 1) + min = in; + if (in > max) + max = in; + + i++; + } + } + + for (int i = 0; i < h * w; i++) { + int x = i % w; + int y = i / w; + double r, g, b; + double a = (vec2_norm(intensities[i]) / max) * 20.0; + + get_heatmap_color(a, &r, &g, &b); + glBegin(GL_POINTS); + glColor3f(r, g, b); + glVertex2d(x, y); + glEnd(); + } + + free(intensities); +} + +void draw_vector_field(charge_t *charges, int num_charges, vec2 *points, + int nb_points, int w, int h) { + double min = DBL_MAX, max = 0.0; + vec2 vs[nb_points]; + for (int i = 0; i < nb_points; i++) { + compute_total_normalized_e(charges, num_charges, points[i], EPS, + &vs[i]); + + double in = vec2_norm(vs[i]); + if (in < min && in > 1) + min = in; + if (in > max) + max = in; + } + + for (int i = 0; i < nb_points; i++) { + vec2 a = position_to_coordinates(w, h, points[i]); + + glBegin(GL_LINES); + glColor3f(0.61, 0.87, 0.61); + glVertex2d(a.x, a.y); + a = vec2_add(a, vec2_mul(vec2_div(vs[i], max), 10)); + glVertex2d(a.x, a.y); + glEnd(); + } +} + void draw_charges(charge_t *charges, int num_charges, int w, int h) { int chargePadding = CHARGE_RADIUS / 2; @@ -112,11 +188,11 @@ void draw_charges(charge_t *charges, int num_charges, int w, int h) { // Draw sign according to charge value if (charges[i].q < 0) { - glColor3f(0, 0, 255); + glColor3f(0.54, 0.82, 0.90); draw_line((vec2){chCoord.x - chargePadding, chCoord.y}, (vec2){chCoord.x + chargePadding, chCoord.y}); } else { - glColor3f(255, 0, 0); + glColor3f(1, 0.4, 0.38); draw_line((vec2){chCoord.x, chCoord.y - chargePadding}, (vec2){chCoord.x, chCoord.y + chargePadding}); draw_line((vec2){chCoord.x - chargePadding, chCoord.y}, diff --git a/draw/draw.h b/draw/draw.h index 616bc886952583893bbc2f5d822e436951a44def..57c42642bd7779711b45ceaf66d43ede9eabf6be 100644 --- a/draw/draw.h +++ b/draw/draw.h @@ -16,6 +16,7 @@ #include "../vec2/vec2.h" #include <GL/freeglut.h> #include <GL/gl.h> +#include <float.h> #include <math.h> #include <stdint.h> #include <stdio.h> @@ -61,6 +62,29 @@ void draw_field_line_segment(charge_t *charges, int num_charges, double dx, void draw_field_line(charge_t *charges, int num_charges, double dx, vec2 pos0, int w, int h); +/** + * @brief Draw a heatmap of the intensity + * + * @param charges + * @param num_charges + * @param w + * @param h + */ +void draw_heatmap(charge_t *charges, int num_charges, int w, int h); + +/** + * @brief Draw a vector field + * + * @param charges + * @param num_charges + * @param points + * @param nb_points + * @param w + * @param h + */ +void draw_vector_field(charge_t *charges, int num_charges, vec2 *points, + int nb_points, int w, int h); + /** * @brief Draw all charges * A circle with a minus sign for negative charges diff --git a/main.c b/main.c index 9373cabd5d90026968c6020f9731188b3fd3ea92..ae82fc98583f96190a75a6afb2faae1c5aef18fa 100644 --- a/main.c +++ b/main.c @@ -19,10 +19,22 @@ #include <stdlib.h> #include <time.h> +// https://stackoverflow.com/questions/3417837/what-is-the-best-way-to-suppress-a-unused-variable-x-warning +#ifdef UNUSED +#elif defined(__GNUC__) +#define UNUSED(x) UNUSED_##x __attribute__((unused)) +#elif defined(__LCLINT__) +#define UNUSED(x) /*@unused@*/ x +#else +#define UNUSED(x) x +#endif + #define WINDOW_WIDTH 800 #define WINDOW_HEIGHT 800 #define NB_CHARGES 5 -#define NB_POINTS 200 +#define GRID_SPACING 10 +#define NB_POINTS \ + ((WINDOW_WIDTH / GRID_SPACING) * (WINDOW_WIDTH / GRID_SPACING)) #define MAXIMUM_CHARGES 30 // Can be replace by a dynamic array #define DELTA (1 / sqrt(pow(WINDOW_WIDTH, 2) + pow(WINDOW_HEIGHT, 2))) @@ -31,21 +43,24 @@ vec2 starting_points[NB_POINTS]; vec2 default_charges_pos[MAXIMUM_CHARGES]; double default_charges[MAXIMUM_CHARGES]; +bool toggle_lines_view = true; +bool toggle_vector_view = false; +bool toggle_heatmap_view = false; + /** - * @brief Generate a given set of points in the [0, 1]x[0, 1] universe + * @brief Generate a grid of points * * @param points - * @param nb_points */ -void generate_points(vec2 points[], int nb_points) { - double x = 0; - double y = 0; - - for (int i = 0; i < nb_points; i++) { - x = rand_one(); - y = rand_one(); - - points[i] = vec2_create(x, y); +void generate_points(vec2 points[]) { + int i = 0; + + for (int y = 0; y < WINDOW_HEIGHT; y += GRID_SPACING) { + for (int x = 0; x < WINDOW_WIDTH; x += GRID_SPACING) { + points[i] = vec2_create(x / (double)WINDOW_WIDTH, + y / (double)WINDOW_HEIGHT); + i++; + } } } @@ -62,9 +77,20 @@ void display() { for (int i = 0; i < charge_count; i++) charges[i] = charge_create(default_charges[i], default_charges_pos[i]); - for (int i = 0; i < NB_POINTS; i++) { - draw_field_line(charges, charge_count, DELTA, starting_points[i], - WINDOW_WIDTH, WINDOW_HEIGHT); + if (toggle_heatmap_view) + draw_heatmap(charges, charge_count, WINDOW_WIDTH, WINDOW_HEIGHT); + + if (toggle_lines_view) { + for (int i = 0; i < NB_POINTS; i += 10) { + glColor3f(0.2, 0.2, 0.2); + draw_field_line(charges, charge_count, DELTA, starting_points[i], + WINDOW_WIDTH, WINDOW_HEIGHT); + } + } + + if (toggle_vector_view) { + draw_vector_field(charges, charge_count, starting_points, NB_POINTS, + WINDOW_WIDTH, WINDOW_HEIGHT); } draw_charges(charges, charge_count, WINDOW_WIDTH, WINDOW_HEIGHT); @@ -99,6 +125,29 @@ void handle_mouse_input(int button, int state, int x, int y) { glutPostRedisplay(); } +/** + * @brief Toggle some views + * + * @param key + * @param x + * @param y + */ +void keyboard(unsigned char key, int UNUSED(x), int UNUSED(y)) { + switch (key) { + case 'l': + toggle_lines_view = !toggle_lines_view; + break; + case 'v': + toggle_vector_view = !toggle_vector_view; + break; + case 'h': + toggle_heatmap_view = !toggle_heatmap_view; + break; + } + + glutPostRedisplay(); +} + /** * @brief Main function * @@ -116,7 +165,7 @@ int main(int argc, char **argv) { } // Generate all field lines starting points - generate_points(starting_points, NB_POINTS); + generate_points(starting_points); // Window initialization glutInit(&argc, argv); @@ -133,6 +182,7 @@ int main(int argc, char **argv) { glutDisplayFunc(display); glutMouseFunc(handle_mouse_input); + glutKeyboardFunc(keyboard); glutMainLoop();