diff --git a/CelestialObject.c b/CelestialObject.c index 7f51146a9250a8bedf0c7a3dd57f6f663ee8c45e..61d9cff28abe6bdd2e17fa00d133a84536a677ae 100644 --- a/CelestialObject.c +++ b/CelestialObject.c @@ -6,8 +6,7 @@ #include "drawing.h" -#define G 6.67e-11 -#define SUN_MASS 1.989e30 +const int32_t PREVIOUS_POSITIONS_LENGTH = 200; CelestialObject *celestial_object_create(char *name, double mass, double semi_major_axis, double eccentricity, uint32_t drawing_disc_radius, uint32_t drawing_color) { CelestialObject *object = (CelestialObject *)malloc(sizeof(CelestialObject)); @@ -21,39 +20,34 @@ CelestialObject *celestial_object_create(char *name, double mass, double semi_ma object->semi_major_axis = semi_major_axis; object->eccentricity = eccentricity; + object->drawing_disc_radius = drawing_disc_radius; object->drawing_color = drawing_color; - object->points = (Vector2 *)malloc(sizeof(Vector2) * 200); - object->points_length = 0; + + object->previous_positions = (Vector2 *)malloc(sizeof(Vector2) * PREVIOUS_POSITIONS_LENGTH); + object->previous_positions_length = 0; return object; } -void celestial_object_draw(CelestialObject *object, Vector2 reference_frame, double zoom_factor) { - Vector2 scaled_position = scale_position(zoom_factor, object->current_position, reference_frame); +uint32_t get_zoomed_drawing_disc_radius(CelestialObject *object, double zoom_factor) { + if (zoom_factor < 1) { + return object->drawing_disc_radius * zoom_factor; + } + + return object->drawing_disc_radius; +} +void celestial_object_draw(CelestialObject *object, Vector2 reference_frame, double zoom_factor) { uint32_t color = object->drawing_color; glColor3ub((color & 0xFF0000) >> 16, (color & 0x00FF00) >> 8, (color & 0x0000FF) >> 0); - if (zoom_factor < 1) - draw_disc(scaled_position, object->drawing_disc_radius * zoom_factor); - else - draw_disc(scaled_position, object->drawing_disc_radius); + Vector2 scaled_position = scale_position(zoom_factor, object->current_position, reference_frame); + draw_disc(scaled_position, get_zoomed_drawing_disc_radius(object, zoom_factor)); if (strcmp(object->name, "Moon") != 0) { - if (zoom_factor < 1) - glLineWidth(4.0 * zoom_factor); - else - glLineWidth(4.0); - - for (int32_t j = 0; j < object->points_length - 1; j += 1) { - Vector2 p1 = scale_position(zoom_factor, object->points[j], reference_frame); - Vector2 p2 = scale_position(zoom_factor, object->points[j + 1], reference_frame); - draw_line(p1, p2); - } + draw_scaled_lines(object->previous_positions, object->previous_positions_length, reference_frame, zoom_factor); } - - // celestial_object_draw_name(object, reference_frame,zoom_factor); } void celestial_object_draw_name(CelestialObject *object, Vector2 reference_frame, double zoom_factor) { @@ -63,11 +57,6 @@ void celestial_object_draw_name(CelestialObject *object, Vector2 reference_frame return; } - uint32_t drawing_disc_radius = object->drawing_disc_radius; - - if (zoom_factor < 1) { - drawing_disc_radius *= zoom_factor; - } - - draw_text(object->name, vector2_add(scaled_position, vector2_create(drawing_disc_radius + 8, 7))); + uint32_t zoomed_drawing_disc_radius = get_zoomed_drawing_disc_radius(object, zoom_factor); + draw_text(object->name, vector2_add(scaled_position, vector2_create(zoomed_drawing_disc_radius + 5, -10))); } diff --git a/CelestialObject.h b/CelestialObject.h index bd7f28ff76ec05906620069a936af51ff6a1ddf9..55925d7be25c629127c965a82b87a3ead343b938 100644 --- a/CelestialObject.h +++ b/CelestialObject.h @@ -4,6 +4,7 @@ #include "Vector2.h" typedef struct CelestialObject { + char name[100]; double mass; Vector2 previous_position; Vector2 current_position; @@ -11,9 +12,8 @@ typedef struct CelestialObject { double eccentricity; uint32_t drawing_disc_radius; uint32_t drawing_color; - Vector2 *points; - int32_t points_length; - char name[100]; + Vector2 *previous_positions; + int32_t previous_positions_length; } CelestialObject; CelestialObject *celestial_object_create(char *name, double mass, double semi_major_axis, double eccentricity, uint32_t drawing_disc_radius, uint32_t drawing_color); diff --git a/Makefile b/Makefile index 25019ee4e9b4d46db44836a9318bc24ceab3c652..eecedfe98605be8b4627f643a98ef99a6616db71 100644 --- a/Makefile +++ b/Makefile @@ -19,4 +19,4 @@ $(TARGET): main.o Vector2.o CelestialObject.o PlanetarySystem.o drawing.o $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) clean: - -rm -f *.o $(TARGET) + -rm -f *.o $(TARGET) $(TARGET).exe diff --git a/PlanetarySystem.c b/PlanetarySystem.c index 3418957a16b34597a8739f6a23078f113816279e..85d4983f01502b27f8c560d8ab85102cd850e20f 100644 --- a/PlanetarySystem.c +++ b/PlanetarySystem.c @@ -2,6 +2,7 @@ #include <GL/glut.h> #include <math.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -10,8 +11,8 @@ #include "Vector2.h" #include "drawing.h" -const uint32_t SCREEN_WIDTH = 800; -const uint32_t SCREEN_HEIGHT = 800; +const int32_t SCREEN_WIDTH = 800; +const int32_t SCREEN_HEIGHT = 800; const double G = 6.67430 * 1E-11; @@ -40,7 +41,8 @@ PlanetarySystem *planetary_system_create() { system->objects_length = 9; system->objects = (CelestialObject **)malloc(sizeof(PlanetarySystem *) * system->objects_length); system->zoom_factor = 1; - system->reference_frame_object_index = 0; + system->reference_frame_index = 0; + system->show_names = true; // The moon is initialized with real life values. // Naboo, Jupiter and Endor are all fake planets. @@ -113,36 +115,52 @@ void planetary_system_update(PlanetarySystem *planetary_system, double interval) object->previous_position = object->current_position; object->current_position = new_position; - int32_t length = object->points_length; + object->previous_positions[0] = object->current_position; - if (length > 0 && vector2_norm(vector2_substract(object->points[0], object->current_position)) < 1E9) + int32_t length = object->previous_positions_length; + + if (length > 1 && vector2_norm(vector2_substract(object->previous_positions[1], object->current_position)) < 1E9) continue; - for (int32_t j = (length == 200) ? length - 1 : length; j >= 1; j -= 1) { - object->points[j] = object->points[j - 1]; + for (int32_t j = (length == 200) ? length - 1 : length; j >= 2; j -= 1) { + object->previous_positions[j] = object->previous_positions[j - 1]; } - object->points[0] = object->current_position; + object->previous_positions[1] = object->current_position; - if (length < 200) - object->points_length += 1; + if (length < 200) { + object->previous_positions_length += 1; + } } } -void planetary_system_draw(PlanetarySystem *ps) { - CelestialObject *object_reference_frame = ps->objects[ps->reference_frame_object_index]; - Vector2 reference_frame = object_reference_frame->current_position; +Vector2 planetary_system_get_reference_frame(PlanetarySystem *system) { + CelestialObject *object_reference_frame = system->objects[system->reference_frame_index]; + return object_reference_frame->current_position; +} - for (int32_t i = 0; i < ps->objects_length; i += 1) { - CelestialObject *object = ps->objects[i]; - celestial_object_draw(object, reference_frame, ps->zoom_factor); - } +void planetary_system_draw(PlanetarySystem *system) { + Vector2 reference_frame = planetary_system_get_reference_frame(system); - for (int32_t i = 0; i < ps->objects_length; i += 1) { - CelestialObject *object = ps->objects[i]; - celestial_object_draw_name(object, reference_frame, ps->zoom_factor); + for (int32_t i = 0; i < system->objects_length; i += 1) { + CelestialObject *object = system->objects[i]; + celestial_object_draw(object, reference_frame, system->zoom_factor); } + planetary_system_draw_object_names(system); + char text[200]; - sprintf(text, "Focused Object : %s", object_reference_frame->name); - draw_text(text, vector2_create(8, 64)); + sprintf(text, "Focused Object : %s", system->objects[system->reference_frame_index]->name); + draw_text(text, vector2_create(5, 15)); +} + +void planetary_system_draw_object_names(PlanetarySystem *system) { + if (!system->show_names) { + return; + } + + Vector2 reference_frame = planetary_system_get_reference_frame(system); + + for (int32_t i = 0; i < system->objects_length; i += 1) { + celestial_object_draw_name(system->objects[i], reference_frame, system->zoom_factor); + } } diff --git a/PlanetarySystem.h b/PlanetarySystem.h index 29019531a590d6f17f107f13e111747667f85e47..768cee71c56b9dc1f851285504610e7266be6ed9 100644 --- a/PlanetarySystem.h +++ b/PlanetarySystem.h @@ -1,23 +1,26 @@ #ifndef PLANETARY_SYSTEM_H #define PLANETARY_SYSTEM_H +#include <stdbool.h> #include <stdint.h> #include "CelestialObject.h" -extern const uint32_t SCREEN_WIDTH; -extern const uint32_t SCREEN_HEIGHT; +extern const int32_t SCREEN_WIDTH; +extern const int32_t SCREEN_HEIGHT; typedef struct PlanetarySystem { int32_t objects_length; CelestialObject **objects; - double interval; double zoom_factor; - int32_t reference_frame_object_index; + int32_t reference_frame_index; + bool show_names; } PlanetarySystem; PlanetarySystem *planetary_system_create(); void planetary_system_update(PlanetarySystem *planetary_system, double interval); +Vector2 planetary_system_get_reference_frame(PlanetarySystem *system); void planetary_system_draw(PlanetarySystem *planetary_system); +void planetary_system_draw_object_names(PlanetarySystem *system); #endif diff --git a/Vector2.c b/Vector2.c index 459a285a32ff701c18d201431ecd31509f50213d..1f8931f78d8c23bf10267b081dbf263cba9d199b 100644 --- a/Vector2.c +++ b/Vector2.c @@ -1,9 +1,9 @@ #include "Vector2.h" #include <math.h> +#include <stdbool.h> #include <stdint.h> #include <stdio.h> -#include <stdlib.h> Vector2 vector2_create(double x, double y) { return (Vector2){.x = x, .y = y}; @@ -46,11 +46,7 @@ Vector2 vector2_normalize(Vector2 v) { return vector2_create(v.x / norm, v.y / norm); } -bool vector2_is_similiar(Vector2 a, Vector2 b, double epsilon) { - return vector2_norm(vector2_substract(a, b)) < epsilon; -} - -Vector2 vector2_fit_canvas(Vector2 v, uint32_t width, uint32_t height) { +Vector2 vector2_fit_canvas(Vector2 v, int32_t width, int32_t height) { double x = width / 2.0 * (1 + v.x); double y = height / 2.0 * (1 + v.y); return vector2_create(x, y); diff --git a/Vector2.h b/Vector2.h index 59e09d219f04d95537afa6b2ce8793ad099a8abf..31f8483305ce698bacaef6db335506d287ceb35c 100644 --- a/Vector2.h +++ b/Vector2.h @@ -19,8 +19,7 @@ double vector2_dot_product(Vector2 a, Vector2 b); double vector2_norm_sqr(Vector2 v); double vector2_norm(Vector2 v); Vector2 vector2_normalize(Vector2 v); -bool vector2_is_similiar(Vector2 a, Vector2 b, double epsilon); -Vector2 vector2_fit_canvas(Vector2 v, uint32_t width, uint32_t height); +Vector2 vector2_fit_canvas(Vector2 v, int32_t width, int32_t height); void vector2_print(Vector2 v); #endif diff --git a/drawing.c b/drawing.c index b8eefeb65e5607fc22224d1b38d08a6d6781ea07..4d817dff1982b1b709890bad433bb1d9bd971f0d 100644 --- a/drawing.c +++ b/drawing.c @@ -26,12 +26,25 @@ void draw_line(Vector2 a, Vector2 b) { glEnd(); } +void draw_scaled_lines(Vector2 *points, int32_t points_length, Vector2 reference_frame, double zoom_factor) { + glLineWidth(4.0); + + if (zoom_factor < 1) + glLineWidth(4.0 * zoom_factor); + + for (int32_t j = 0; j < points_length - 1; j += 1) { + Vector2 p1 = scale_position(zoom_factor, points[j], reference_frame); + Vector2 p2 = scale_position(zoom_factor, points[j + 1], reference_frame); + draw_line(p1, p2); + } +} + void draw_text(char *text, Vector2 position) { glColor3f(1.0, 1.0, 1.0); - glRasterPos2f(position.x, position.y); + glRasterPos2f(position.x, position.y + 15); for (int32_t i = 0; i < (int32_t)strlen(text); i++) { - glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, text[i]); + glutBitmapCharacter(GLUT_BITMAP_9_BY_15, text[i]); } } diff --git a/drawing.h b/drawing.h index b9de5c9cee093c4a22b3eb6b108bf8991f249096..ca03066b2c375618cc8570f648c8c2ea28d4549b 100644 --- a/drawing.h +++ b/drawing.h @@ -7,6 +7,7 @@ void draw_disc(Vector2 position, uint32_t radius); void draw_line(Vector2 a, Vector2 b); +void draw_scaled_lines(Vector2 *points, int32_t points_length, Vector2 reference_frame, double zoom_factor); void draw_text(char *text, Vector2 position); Vector2 scale_position(double zoom_factor, Vector2 object_position, Vector2 reference_frame_position); diff --git a/main.c b/main.c index 39b543c4521a705ab20f5d6aa600d965f7298b12..eeb37f7be7b4df15f99400fc6467fff6f6a654a7 100644 --- a/main.c +++ b/main.c @@ -3,17 +3,32 @@ #include <stdint.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> +#include <sys/time.h> #include <time.h> #include "PlanetarySystem.h" -#include "Vector2.h" #include "drawing.h" -#define WINDOW_NAME "Solar System" -#define REFRESH_RATE 200 -#define TIME_ELASPING_PER_SECOND 3600 * 24 * 10 -#define TIME_ELASPING_PER_UPDATE 100 +// Name of the window. +const char WINDOW_NAME[] = "Solar System"; +// Refresh rate in Hz. +const int32_t REFRESH_RATE = 200; +// Number of microseconds in 1 second. +const int32_t ONE_SECOND_IN_MICROSECONDS = 1E6; +// Number of milliseconds in 1 second. +const int32_t ONE_SECOND_IN_MILLISECONDS = 1000; +// Number of seconds in a day. +const int32_t ONE_DAY_IN_SECONDS = 86400; +// Zoom multiplier at each wheel movement. +const double ZOOM_MULTIPLIER = 1.1; +// Default simulation speed in days. +const int32_t DEFAULT_SIMULATION_SPEED_IN_DAYS = 10; +// Maximum speed of the simulation in days. +const int32_t SIMULATION_MAXIMUM_SPEED_IN_DAYS = 500; +// Time that elapses in the planetary system per second. +const int32_t DEFAULT_SIMULATION_SPEED_IN_SECONDS = DEFAULT_SIMULATION_SPEED_IN_DAYS * ONE_DAY_IN_SECONDS; +// Maximum time that elapses in the planetary system at each call of the planetary_system_update function, this value must be small enough for the planetary system to work. +const int32_t PLANETARY_SYSTEM_INTERVAL = 100; // https://stackoverflow.com/questions/3417837/what-is-the-best-way-to-suppress-a-unused-variable-x-warning #ifdef UNUSED @@ -26,79 +41,99 @@ #endif PlanetarySystem *planetary_system; -int32_t elapsed_time = 0; -int32_t true_time_shift = 0; -int32_t time_elasping_per_second = TIME_ELASPING_PER_SECOND; - -void draw_timer() { - glutPostRedisplay(); - glutTimerFunc(1000 / REFRESH_RATE, draw_timer, 0); +int32_t simulation_speed = DEFAULT_SIMULATION_SPEED_IN_SECONDS; +int64_t start_time; +int64_t previous_time; + +int64_t get_current_time() { + struct timeval time; + gettimeofday(&time, NULL); + return (int64_t)time.tv_sec * (int64_t)ONE_SECOND_IN_MICROSECONDS + (int64_t)time.tv_usec; } -void update_timer() { - if (elapsed_time == 0) - true_time_shift = time(NULL) % 1000; - elapsed_time += 1; +void update() { + int64_t current_time = get_current_time(); + int64_t elapsed_time = current_time - previous_time; + double remaining_time = (double)elapsed_time / ONE_SECOND_IN_MICROSECONDS * simulation_speed; - for (int32_t i = 0; i < time_elasping_per_second / REFRESH_RATE / TIME_ELASPING_PER_UPDATE; i += 1) { - planetary_system_update(planetary_system, TIME_ELASPING_PER_UPDATE); + while (remaining_time >= PLANETARY_SYSTEM_INTERVAL) { + planetary_system_update(planetary_system, PLANETARY_SYSTEM_INTERVAL); + remaining_time -= PLANETARY_SYSTEM_INTERVAL; } - glutTimerFunc(1000 / REFRESH_RATE, update_timer, 0); + if (remaining_time > 0) { + planetary_system_update(planetary_system, remaining_time); + } + + previous_time = current_time; +} + +void update_timer() { + update(); + glutTimerFunc(ONE_SECOND_IN_MILLISECONDS / REFRESH_RATE, update_timer, 0); } void draw() { glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); - if (elapsed_time % REFRESH_RATE == 0 && elapsed_time != 0) { - char title[100]; - sprintf(title, "Solar System (%d : %ld)", elapsed_time / REFRESH_RATE, (time(NULL) - true_time_shift) % 1000); - glutSetWindowTitle(title); - } + char title[100]; + double elapsed_time = (get_current_time() - start_time) / ONE_SECOND_IN_MICROSECONDS; + sprintf(title, "Solar System (%.2lf)", elapsed_time); + glutSetWindowTitle(title); planetary_system_draw(planetary_system); char text[100]; - sprintf(text, "Simulation Speed : %d days per second", time_elasping_per_second / 86400); - draw_text(text, vector2_create(8, 32)); + sprintf(text, "Simulation Speed : %d days per second", simulation_speed / ONE_DAY_IN_SECONDS); + draw_text(text, vector2_create(5, 0)); glutSwapBuffers(); } +void draw_timer() { + glutPostRedisplay(); + glutTimerFunc(ONE_SECOND_IN_MILLISECONDS / REFRESH_RATE, draw_timer, 0); +} + void handle_keyboard_input(unsigned char key, int UNUSED(x), int UNUSED(y)) { if (key == 27) { + // TODO : Free exit(0); } + + if (key == 116) { + planetary_system->show_names = !planetary_system->show_names; + } } void handle_special_keyboard_input(int key, int UNUSED(x), int UNUSED(y)) { if (key == GLUT_KEY_LEFT) { - planetary_system->reference_frame_object_index -= 1; + planetary_system->reference_frame_index -= 1; - if (planetary_system->reference_frame_object_index < 0) { - planetary_system->reference_frame_object_index = planetary_system->objects_length - 1; + if (planetary_system->reference_frame_index < 0) { + planetary_system->reference_frame_index = planetary_system->objects_length - 1; } } if (key == GLUT_KEY_RIGHT) { - planetary_system->reference_frame_object_index += 1; - planetary_system->reference_frame_object_index %= planetary_system->objects_length; + planetary_system->reference_frame_index += 1; + planetary_system->reference_frame_index %= planetary_system->objects_length; } if (key == GLUT_KEY_UP) { - time_elasping_per_second += 86400; + simulation_speed += ONE_DAY_IN_SECONDS; - if (time_elasping_per_second > 86400 * 300) { - time_elasping_per_second = 86400 * 300; + if (simulation_speed > ONE_DAY_IN_SECONDS * SIMULATION_MAXIMUM_SPEED_IN_DAYS) { + simulation_speed = ONE_DAY_IN_SECONDS * SIMULATION_MAXIMUM_SPEED_IN_DAYS; } } if (key == GLUT_KEY_DOWN) { - time_elasping_per_second -= 86400; + simulation_speed -= ONE_DAY_IN_SECONDS; - if (time_elasping_per_second < 0) { - time_elasping_per_second = 0; + if (simulation_speed < 0) { + simulation_speed = 0; } } } @@ -108,9 +143,9 @@ void handle_mouse_input(int button, int state, int UNUSED(x), int UNUSED(y)) { if (state == GLUT_UP) return; if (button == 3) { - planetary_system->zoom_factor *= 1.1; + planetary_system->zoom_factor *= ZOOM_MULTIPLIER; } else { - planetary_system->zoom_factor /= 1.1; + planetary_system->zoom_factor /= ZOOM_MULTIPLIER; } } } @@ -134,8 +169,12 @@ int main(int argc, char *argv[]) { glutSpecialFunc(handle_special_keyboard_input); glutMouseFunc(handle_mouse_input); - glutTimerFunc(1000 / REFRESH_RATE, update_timer, 0); - glutTimerFunc(1000 / REFRESH_RATE, draw_timer, 0); + start_time = get_current_time(); + previous_time = start_time; + + glutTimerFunc(ONE_SECOND_IN_MILLISECONDS / REFRESH_RATE, update_timer, 0); + glutTimerFunc(ONE_SECOND_IN_MILLISECONDS / REFRESH_RATE, draw_timer, 0); + glutMainLoop(); return EXIT_SUCCESS;