diff --git a/0_application.c b/0_application.c new file mode 100644 index 0000000..11e5d47 --- /dev/null +++ b/0_application.c @@ -0,0 +1,150 @@ +/* + * Gem-graph OpenGL experiments + * + * Desc: User interface functions + * + * Copyright (C) 2023 Arthur Menges + * Copyright (C) 2023 Adrien Bourmault + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "../../include/base.h" +#include "../../include/ui.h" + +struct _GemGraphClientApplication +{ + GtkApplication parent_instance; +}; + +G_DEFINE_TYPE (GemGraphClientApplication, + gem_graph_client_application, + GTK_TYPE_APPLICATION) + + +static GemGraphClientApplication *application; + +/* -------------------------------------------------------------------------- */ + +void ui_enable_action(const char *name) { + g_simple_action_set_enabled( + (GSimpleAction *)g_action_map_lookup_action( + G_ACTION_MAP(application), + name), + true); +} + +void ui_disable_action(const char *name) { + g_simple_action_set_enabled( + (GSimpleAction *)g_action_map_lookup_action( + G_ACTION_MAP(application), + name), + false); +} + +/* + * Window actual presentation on screen + * + */ +static void gem_graph_client_application_activate(GApplication *app) +{ + GtkWindow *window; + + g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(app)); + + window = gtk_application_get_active_window(GTK_APPLICATION (app)); + if (window == NULL) + window = g_object_new(GEM_GRAPH_CLIENT_TYPE_WINDOW, + "application", app, + NULL); + + ui_set_stack(HOME_MODE); + + gtk_window_present(window); +} + +/* + * Action records are registered here + * + */ +static void gem_graph_client_application_init(GemGraphClientApplication *self) +{ + g_action_map_add_action_entries(G_ACTION_MAP(self), + app_actions, + G_N_ELEMENTS(app_actions), + self); + + // Setup shortcuts + gtk_application_set_accels_for_action(GTK_APPLICATION(self), + "app.quit", + (const char *[]) { "q", NULL }); + gtk_application_set_accels_for_action(GTK_APPLICATION(self), + "app.editmode", + (const char *[]) { "e", NULL }); + gtk_application_set_accels_for_action(GTK_APPLICATION(self), + "app.runmode", + (const char *[]) { "r", NULL }); + gtk_application_set_accels_for_action(GTK_APPLICATION(self), + "app.presentmode", + (const char *[]) { "p", NULL }); + gtk_application_set_accels_for_action(GTK_APPLICATION(self), + "app.savefile", + (const char *[]) { "s", NULL }); + + application = self; + + // + // Disable unneeded/inoperant actions + // + ui_disable_action("savefile"); + ui_disable_action("closefile"); + ui_disable_action("editmode"); + ui_disable_action("runmode"); + ui_disable_action("presentmode"); + ui_disable_action("togglesidebar"); +} + +void ui_send_notification(const char *message) +{ + g_print("NOTIFICATION: %s\n", message); + + g_application_send_notification(G_APPLICATION(application), + "notification", + g_notification_new(message) + ); +} + +/* -------------------------------------------------------------------------- */ + +static void gem_graph_client_application_class_init( + GemGraphClientApplicationClass *klass) +{ + GApplicationClass *app_class = G_APPLICATION_CLASS(klass); + + app_class->activate = gem_graph_client_application_activate; +} + +GemGraphClientApplication *gem_graph_client_application_new( + const char *application_id, + GApplicationFlags flags) +{ + g_return_val_if_fail(application_id != NULL, NULL); + + return g_object_new(GEM_GRAPH_CLIENT_TYPE_APPLICATION, + "application-id", application_id, + "flags", flags, + NULL); +} diff --git a/0_window.c b/0_window.c new file mode 100644 index 0000000..a7ed883 --- /dev/null +++ b/0_window.c @@ -0,0 +1,236 @@ +/* + * Gem-graph OpenGL experiments + * + * Desc: User interface functions + * + * Copyright (C) 2023 Arthur Menges + * Copyright (C) 2023 Adrien Bourmault + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "../../include/base.h" +#include "../../include/ui.h" + +/* -------------------------------------------------------------------------- */ + +static GemGraphClientWindow *window; + +/* -------------------------------------------------------------------------- */ + +struct _GemGraphClientWindow +{ + GtkApplicationWindow parent_instance; + + /* Template widgets */ + GtkHeaderBar *main_titlebar; + GtkStack *main_stack; + GtkStack *side_stack; + GtkPaned *main_paned; + GtkMenuButton *main_button_mode; + GtkToggleButton *main_button_sidebar; + GtkRevealer *toast_revealer; + GtkToggleButton *toast_close_button; + GtkLabel *toast_text; + GtkBox *control_zone; + GtkBox *run_glarea_box; + GtkBox *edition_glarea_box; + GtkBox *presentation_glarea_box; +}; + +G_DEFINE_FINAL_TYPE (GemGraphClientWindow, + gem_graph_client_window, + GTK_TYPE_APPLICATION_WINDOW) + +static void gem_graph_client_window_class_init(GemGraphClientWindowClass *klass) +{ + gchar *contents; + gsize len; + GError *err; + GBytes *bytes; + + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + + if (g_file_get_contents("src/ui/gemgraph.ui", &contents, &len, &err) == FALSE) + g_error("error reading gemgraph.ui: %s", err->message); + + bytes = g_bytes_new_take(contents, len); + gtk_widget_class_set_template(GTK_WIDGET_CLASS(klass), bytes); + + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + main_titlebar); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + main_stack); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + side_stack); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + main_paned); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + main_button_mode); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + main_button_sidebar); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + toast_revealer); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + toast_close_button); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + toast_text); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + control_zone); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + run_glarea_box); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + edition_glarea_box); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + presentation_glarea_box); +} + +static void gem_graph_client_window_init(GemGraphClientWindow *self) +{ + gtk_widget_init_template(GTK_WIDGET(self)); + window = self; + + // Launch with sidebar off + ui_toggle_sidebar(); +} + +/* -------------------------------------------------------------------------- */ + +void ui_set_stack(int mode) +{ + //g_printerr("[debug] ui_set_stack()\n"); + + if (window->main_stack == NULL) { + g_printerr("Can't find self->main_stack !\n"); + return; + } + if (window->side_stack == NULL) { + g_printerr("Can't find self->side_stack !\n"); + return; + } + + // Switch on the first letter of the mode, because switch is soooo simple :) + switch(mode) { + case EDIT_MODE: + gtk_stack_set_visible_child_full(window->main_stack, + "edition", + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_stack_set_visible_child_full(window->side_stack, + "edition", + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_menu_button_set_icon_name(window->main_button_mode, + "document-edit-symbolic"); + ui_setup_glarea(EDIT_MODE, GTK_WIDGET(window->edition_glarea_box)); + break; + case RUN_MODE: + gtk_stack_set_visible_child_full(window->main_stack, + "run", + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_stack_set_visible_child_full(window->side_stack, + "run", + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_menu_button_set_icon_name(window->main_button_mode, + "system-run-symbolic"); + ui_setup_glarea(RUN_MODE, GTK_WIDGET(window->run_glarea_box)); + break; + case PRESENTATION_MODE: + gtk_stack_set_visible_child_full(window->main_stack, + "presentation", + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_stack_set_visible_child_full(window->side_stack, + "presentation", + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_menu_button_set_icon_name(window->main_button_mode, + "x-office-presentation-symbolic"); + ui_setup_glarea(PRESENTATION_MODE, + GTK_WIDGET(window->presentation_glarea_box)); + break; + case HOME_MODE: + gtk_stack_set_visible_child_full(window->main_stack, + "home", + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_stack_set_visible_child_full(window->side_stack, + "home", + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_paned_set_position(window->main_paned, 0); + gtk_menu_button_set_icon_name(window->main_button_mode, + "user-home-symbolic"); + break; + default: + break; + } +} + +void ui_send_internal_notification(const char *message) +{ + if (window->toast_revealer == NULL) { + g_printerr("Can't find self->toast_overlay !\n"); + return; + } + + if (window->toast_text == NULL) { + g_printerr("Can't find self->toast_overlay !\n"); + return; + } + + gtk_label_set_label(window->toast_text, message); + gtk_revealer_set_reveal_child(window->toast_revealer, true); + g_printerr("%s\n", message); +} + +void ui_close_internal_notification(void) +{ + if (window->toast_revealer == NULL) { + g_printerr("Can't find self->toast_overlay !\n"); + return; + } + + if (window->toast_text == NULL) { + g_printerr("Can't find self->toast_overlay !\n"); + return; + } + + gtk_revealer_set_reveal_child(window->toast_revealer, false); +} + + +void ui_toggle_sidebar(void) +{ + int position = gtk_paned_get_position(window->main_paned); + + if (position != 0) { + gtk_paned_set_position(window->main_paned, 0); + } else { + gtk_paned_set_position(window->main_paned, 400); + } +} diff --git a/2024-04-30.png b/2024-04-30.png new file mode 100644 index 0000000..ff01e7c Binary files /dev/null and b/2024-04-30.png differ diff --git a/exec.o b/exec.o index f81c011..a34b54e 100755 Binary files a/exec.o and b/exec.o differ diff --git a/gg/GtkGLArea.c b/gg/GtkGLArea.c new file mode 100644 index 0000000..0635bfc --- /dev/null +++ b/gg/GtkGLArea.c @@ -0,0 +1,323 @@ +/* + * Gem-graph OpenGL experiments + * + * Desc: User interface functions + * + * Copyright (C) 2023 Adrien Bourmault + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "../../include/base.h" +#include "../../include/ui.h" +#include "../../include/graphics.h" + +struct stack_index_t { + long stack_id; + void *container_widget; + void *gl_area; +}; + +static struct stack_index_t *stack_index = NULL; +size_t stack_index_size = 0; + + +/* + * Look for stack entry and returns stack_id + * + * @params container_widget, generally the GtkBox that contains the GLArea + * + * @returns stack_id + */ +long ui_get_graphic_stack(void *container_widget) +{ + // look for stack_index entry + for (int i = 0; i < stack_index_size; i++) { + if (stack_index[i].container_widget == (void *)container_widget) { + return stack_index[i].stack_id; + } + } + return -1; +} + +/* + * Look for stack entry and returns stack_id + * + * @params container_widget, generally the GtkBox that contains the GLArea + * + * @returns stack_id + */ +long ui_is_graphic_stack_ready(void *container_widget) +{ + // look for stack_index entry + for (int i = 0; i < stack_index_size; i++) { + if (stack_index[i].container_widget == (void *)container_widget) { + return stack_index[i].stack_id; + } + } + return -1; +} + +/* + * Look for stack entry and initializes OpenGL for it + * + * @params container_widget, generally the GtkBox that contains the GLArea + * + * @returns bool, true if success + */ +bool ui_init_graphic_stack(void *container_widget, GError *error_buffer) +{ + //g_printerr("[debug] ui_init_graphic_stack()\n"); + + //g_printerr("[debug] ui_init_graphic_stack() : target is %p\n", container_widget); + + // look for stack_index entry + for (int i = 0; i < stack_index_size; i++) { + //g_printerr("[debug] ui_init_graphic_stack() : i is %d\n", i); + //g_printerr("[debug] ui_init_graphic_stack() : target would be %p\n", + //stack_index[i].container_widget); + if (stack_index[i].container_widget == (void *)container_widget) { + stack_index[i].stack_id = graphics_init(&error_buffer); + //g_printerr("[debug] ui_init_graphic_stack() : stack_id is %d\n", + //stack_index[i].stack_id); + if (stack_index[i].stack_id >= 0) + return true; + else + return false; + } + } + return false; +} + +/* + * Look for stack entry and shutdowns OpenGL for it + * + * @params container_widget, generally the GtkBox that contains the GLArea + * + * @returns bool, true if success + */ +bool ui_shutdown_graphic_stack(void *container_widget, GError *error_buffer) +{ + // look for stack_index entry + for (int i = 0; i < stack_index_size; i++) { + if (stack_index[i].container_widget == (void *)container_widget) { + if (graphics_shutdown(stack_index[i].stack_id, + &error_buffer) == false) { + return false; + } + stack_index[i].stack_id = 0; + return true; + } + } + return false; +} + + +void ui_clean_stack_index(void) +{ + // look for stack_index entry + for (int i = 0; i < stack_index_size; i++) { + stack_index[i].stack_id = 0; + } + return; +} + +/* + * Look for stack entry and triggers OpenGL for drawing + * + * @params container_widget, generally the GtkBox that contains the GLArea + * + * @returns bool, true if success + */ +bool ui_render_stack(GtkWidget *container_widget) +{ + // look for stack_index entry + for (int i = 0; i < stack_index_size; i++) { + if (stack_index[i].container_widget == (void *)container_widget) { + graphics_draw(stack_index[i].stack_id); + return true; + } + } + return false; +} + +/* + * Look for stack entry and triggers OpenGL for drawing + * + * @params container_widget, generally the GtkBox that contains the GLArea + * + * @returns bool, true if success + */ +bool ui_update_axis_stack(GtkWidget *container_widget, int axis, int value) +{ + // look for stack_index entry + for (int i = 0; i < stack_index_size; i++) { + if (stack_index[i].container_widget == (void *)container_widget) { + graphic_stack[stack_index[i].stack_id].rotation_angles[axis] = value; + gtk_widget_queue_draw((GtkWidget*)(stack_index[i].gl_area)); + return true; + } + } + return false; +} + +/* + * Look for every stack entry and shutdowns OpenGL for it + * + * @params void + * + * @returns bool, true if success + */ +void ui_shutdown_all_graphic_stacks(void) +{ + // look for stack_index entry + for (int i = 0; i < stack_index_size; i++) { + graphics_shutdown(stack_index[i].stack_id, NULL); + } + return; +} + +/* + * Creates a slider widget + * + * @params axis, meaning which axis we're building (for label) + * + * @returns GtkWidget*, pointer to the new widget + */ +GtkWidget *create_axis_slider(int axis) +{ + GtkWidget *box, *label, *slider; + GtkAdjustment *adj; + const char *text; + + box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + + switch (axis) { + case X_AXIS: + text = "X"; + break; + + case Y_AXIS: + text = "Y"; + break; + + case Z_AXIS: + text = "Z"; + break; + + default: + g_assert_not_reached(); + } + + label = gtk_label_new(text); + gtk_box_append(GTK_BOX(box), label); + gtk_widget_show(label); + + adj = gtk_adjustment_new(0.0, 0.0, 360.0, 1.0, 12.0, 0.0); + g_signal_connect(adj, "value-changed", + G_CALLBACK(on_axis_value_change), + (gpointer) label); + slider = gtk_scale_new(GTK_ORIENTATION_HORIZONTAL, adj); + gtk_box_append(GTK_BOX(box), slider); + gtk_widget_set_hexpand(slider, TRUE); + gtk_widget_show(slider); + + gtk_widget_show(box); + + return box; +} + +/* + * Creates GLArea and indexes it + * + * @params target_mode, meaning which ui_stack we're on + * target_widget, meaning the box that will host the GLArea + * + * @returns bool, true if success + */ +bool ui_setup_glarea(int target_mode, GtkWidget *target_widget) +{ + GtkWidget *gl_area; + + ////g_printerr("[debug] ui_setup_glarea()\n"); + + assert(target_widget); + + ////g_printerr("[debug] ui_setup_glarea() : target is %p\n", target_widget); + + if (stack_index == NULL) { + stack_index = g_malloc(sizeof(struct stack_index_t)); + stack_index_size = 1; + } else { + // look for stack_index entry + for (int i = 0; i < stack_index_size; i++) { + if (stack_index[i].container_widget == (void *)target_widget) { + return false; + } + } + // create entry + stack_index = + g_realloc(stack_index, + ++stack_index_size * sizeof(struct stack_index_t)); + } + + gl_area = GTK_WIDGET(gtk_gl_area_new()); + assert(gl_area); + + //gtk_widget_set_size_request(gl_area, 1000, 1000); + gtk_gl_area_set_auto_render(GTK_GL_AREA(gl_area), true); + gtk_widget_set_hexpand(gl_area, TRUE); + gtk_widget_set_vexpand(gl_area, TRUE); + //gtk_widget_set_halign(gl_area, GTK_ALIGN_CENTER); + //gtk_widget_set_valign(gl_area, GTK_ALIGN_CENTER); + + // The main "draw" call for GtkGLArea + g_signal_connect(GTK_GL_AREA(gl_area), + "render", + G_CALLBACK(on_glarea_render), NULL); + + g_signal_connect(gl_area, + "realize", + G_CALLBACK(on_glarea_realize), NULL); + + g_signal_connect(gl_area, + "unrealize", + G_CALLBACK(on_glarea_unrealize), NULL); + + stack_index[stack_index_size-1].container_widget = + (void*)target_widget; + + stack_index[stack_index_size-1].gl_area = (void*)gl_area; + + ////g_printerr("[debug] ui_setup_glarea() : set target to %p\n", target_widget); + + ////g_printerr("[debug] ui_setup_glarea() : stack_index (@0x%p) had %ld elements\n", + //stack_index, + //stack_index_size); + + gtk_box_append(GTK_BOX(target_widget), gl_area); + gtk_widget_show(GTK_WIDGET(gl_area)); + + // Create sliders + for(int i = 0; i < N_AXIS; i++) + gtk_box_append(GTK_BOX(target_widget), create_axis_slider(i)); + + return true; +} diff --git a/gg/application.c b/gg/application.c new file mode 100644 index 0000000..11e5d47 --- /dev/null +++ b/gg/application.c @@ -0,0 +1,150 @@ +/* + * Gem-graph OpenGL experiments + * + * Desc: User interface functions + * + * Copyright (C) 2023 Arthur Menges + * Copyright (C) 2023 Adrien Bourmault + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "../../include/base.h" +#include "../../include/ui.h" + +struct _GemGraphClientApplication +{ + GtkApplication parent_instance; +}; + +G_DEFINE_TYPE (GemGraphClientApplication, + gem_graph_client_application, + GTK_TYPE_APPLICATION) + + +static GemGraphClientApplication *application; + +/* -------------------------------------------------------------------------- */ + +void ui_enable_action(const char *name) { + g_simple_action_set_enabled( + (GSimpleAction *)g_action_map_lookup_action( + G_ACTION_MAP(application), + name), + true); +} + +void ui_disable_action(const char *name) { + g_simple_action_set_enabled( + (GSimpleAction *)g_action_map_lookup_action( + G_ACTION_MAP(application), + name), + false); +} + +/* + * Window actual presentation on screen + * + */ +static void gem_graph_client_application_activate(GApplication *app) +{ + GtkWindow *window; + + g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(app)); + + window = gtk_application_get_active_window(GTK_APPLICATION (app)); + if (window == NULL) + window = g_object_new(GEM_GRAPH_CLIENT_TYPE_WINDOW, + "application", app, + NULL); + + ui_set_stack(HOME_MODE); + + gtk_window_present(window); +} + +/* + * Action records are registered here + * + */ +static void gem_graph_client_application_init(GemGraphClientApplication *self) +{ + g_action_map_add_action_entries(G_ACTION_MAP(self), + app_actions, + G_N_ELEMENTS(app_actions), + self); + + // Setup shortcuts + gtk_application_set_accels_for_action(GTK_APPLICATION(self), + "app.quit", + (const char *[]) { "q", NULL }); + gtk_application_set_accels_for_action(GTK_APPLICATION(self), + "app.editmode", + (const char *[]) { "e", NULL }); + gtk_application_set_accels_for_action(GTK_APPLICATION(self), + "app.runmode", + (const char *[]) { "r", NULL }); + gtk_application_set_accels_for_action(GTK_APPLICATION(self), + "app.presentmode", + (const char *[]) { "p", NULL }); + gtk_application_set_accels_for_action(GTK_APPLICATION(self), + "app.savefile", + (const char *[]) { "s", NULL }); + + application = self; + + // + // Disable unneeded/inoperant actions + // + ui_disable_action("savefile"); + ui_disable_action("closefile"); + ui_disable_action("editmode"); + ui_disable_action("runmode"); + ui_disable_action("presentmode"); + ui_disable_action("togglesidebar"); +} + +void ui_send_notification(const char *message) +{ + g_print("NOTIFICATION: %s\n", message); + + g_application_send_notification(G_APPLICATION(application), + "notification", + g_notification_new(message) + ); +} + +/* -------------------------------------------------------------------------- */ + +static void gem_graph_client_application_class_init( + GemGraphClientApplicationClass *klass) +{ + GApplicationClass *app_class = G_APPLICATION_CLASS(klass); + + app_class->activate = gem_graph_client_application_activate; +} + +GemGraphClientApplication *gem_graph_client_application_new( + const char *application_id, + GApplicationFlags flags) +{ + g_return_val_if_fail(application_id != NULL, NULL); + + return g_object_new(GEM_GRAPH_CLIENT_TYPE_APPLICATION, + "application-id", application_id, + "flags", flags, + NULL); +} diff --git a/gg/arrows.c b/gg/arrows.c new file mode 100644 index 0000000..56b0e5f --- /dev/null +++ b/gg/arrows.c @@ -0,0 +1,285 @@ +/* + * Gem-graph OpenGL experiments + * + * Desc: OpenGL utils header + * + * Copyright (C) 2023 Jean Sirmai + * Copyright (C) 2023 Arien Bourmault + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "../../include/base.h" +#include "../../include/graphics.h" + +/* I'm standing on Earth (any planet or star or spinning spheroid, in fact) + * and looking towards its North pole + * + * X - X = EAST - WEST = rouge - cyan + * Y - Y = ZENITH - NADIR = vert - magenta + * Z - Z = NORTH - SOUTH = bleu - jaune + */ + +int draw_one_arrow_vertex (const int stack_id, + int space_X_int, + int space_Y_int, + int space_Z_int, + int weight, + int site, + int arrow_x, + int arrow_y, + int arrow_z) +{ + float max = fmax(space_X_int, space_Y_int); + max = fmax(max, space_Z_int); + + float i = arrow_x, j = arrow_y, k = arrow_z; + + float vx = (2 * i / space_X_int - 1) * space_X_int / max + (1 / max), + vy = (2 * j / space_Y_int - 1) * space_Y_int / max + (1 / max), + vz = (2 * k / space_Z_int - 1) * space_Z_int / max + (1 / max); + + graphics_draw_vertex(stack_id, vx, vy, vz); + graphics_draw_color(stack_id, 0.4f, 0.4f, 0.4f); + + // réduit légèrement les longueurs des flèches + // pour qu'elles s'arrêtent avant les faces des cubes + GLfloat arrow_tip_padding = (1 / max) / 10; + + switch(site){ + case EAST: + graphics_draw_vertex (stack_id, vx - (site % 2 - 1) * (1 / max) + (site % 2 - 1) * arrow_tip_padding, vy, vz); + graphics_draw_color (stack_id, 1.0f, 0.0f, 0.0f); + break; + case WEST: + graphics_draw_vertex (stack_id, vx - (site % 2) * (1 / max) + (site % 2) * arrow_tip_padding, vy, vz); + graphics_draw_color (stack_id, 0.0f, 1.0f, 1.0f); + break; + case ZENITH: + graphics_draw_vertex (stack_id, vx, vy - (site % 2 - 1) * (1 / max) + (site % 2 - 1) * arrow_tip_padding, vz); + graphics_draw_color(stack_id, 0.0f, 0.6f, 0.1f); + break; + case NADIR: + graphics_draw_vertex (stack_id, vx, vy - (site % 2) * (1 / max) + (site % 2) * arrow_tip_padding, vz); + graphics_draw_color(stack_id, 0.6f, 0.1f, 0.7f); + break; + case SOUTH: + graphics_draw_vertex (stack_id, vx, vy, vz - (site % 2 + 1) * (1 / max) + (site % 2 + 1) * arrow_tip_padding); + graphics_draw_color(stack_id, 0.05f, 0.4f, 1.0f); + break; + case NORTH: + graphics_draw_vertex (stack_id, vx, vy, vz - (site % 2 - 2) * (1 / max) + (site % 2 - 2) * arrow_tip_padding); + graphics_draw_color(stack_id, 1.0f, 1.0f, 0.0f); + break; + default: break; + } + + return 2*3; +} + + +int draw_one_arrow_line(const int stack_id, int offset_vertex) +{ + graphics_draw_line (stack_id, offset_vertex + 0, offset_vertex + 1); + return 2; +} + + +/* + * Depends on set_arrow() + * Exchanges current and required site values + * when the address of the arrow to be set is already occupied + */ +static int rewrite_arrow (const int stack_id, + int address, + int load, + int site, + int x, + int y, + int z) +{ + struct graphic_stack_t *stack = &graphic_stack[stack_id]; + printf("WARNING in rewrite_arrow() <> address or address / 5 ? (et pourquoi ?)\n"); + stack->arrows_ptr[address].load = load; + return 1; +} + +/* + * Depends on set_arrow() + * Creates the arrow to be set with the required load at the required address + */ +static inline int create_arrow (int stack_id, + int arrows_nb, + int space_X, + int space_Y, + int space_Z, + int load, + int site, + int x, + int y, + int z) +{ + struct graphic_stack_t *stack = &graphic_stack[stack_id]; + void *newptr = g_realloc(stack->arrows_ptr, (arrows_nb + 1) * sizeof(struct arrow_t)); + + if (newptr) + stack->arrows_ptr = newptr; + else + perror("In create arrow, can't allocate new arrow buffer !\n"); + + stack->arrows_ptr[arrows_nb].load = load; + stack->arrows_ptr[arrows_nb].site = site; + stack->arrows_ptr[arrows_nb].x = x; + stack->arrows_ptr[arrows_nb].y = y; + stack->arrows_ptr[arrows_nb].z = z; + + draw_one_arrow_vertex(stack_id, space_X, space_Y, space_Z, load, site, x, y, z); + draw_one_arrow_line (stack_id, stack->buffer_vertex_size / 3 - 2); + + arrows_nb ++; + + return arrows_nb; +} + +/* + * Depends on set_arrow() + * Erases the arrow at the required address + */ +static inline int erase_arrow (const int stack_id, + int arrows_nb, + int arrow_address_in_list, + GLuint site, + GLint x, + GLint y, + GLint z) +{ + struct graphic_stack_t *stack = &graphic_stack[stack_id]; + if (arrows_nb == 0) assert (stack->buffer_lines_size == + stack->buffer_lines_0_arrow); + if (arrows_nb == 0) { + stack->buffer_lines_size = + stack->buffer_lines_0_arrow; + return 0; + } + + assert (arrows_nb); + arrows_nb --; + + if (arrow_address_in_list < arrows_nb) + { + stack->arrows_ptr[arrow_address_in_list].load = + stack->arrows_ptr[arrows_nb].load; + stack->arrows_ptr[arrow_address_in_list].site = + stack->arrows_ptr[arrows_nb].site; + stack->arrows_ptr[arrow_address_in_list].x = + stack->arrows_ptr[arrows_nb].x; + stack->arrows_ptr[arrow_address_in_list].y = + stack->arrows_ptr[arrows_nb].y; + stack->arrows_ptr[arrow_address_in_list].z = + stack->arrows_ptr[arrows_nb].z; + } + + for (long i = 0; i < 6; i++) + stack->buffer_vertex_origin [stack->buffer_vertex_0_arrow + arrow_address_in_list * 6 + i] + = stack->buffer_vertex_origin [stack->buffer_vertex_size - 6 + i]; + + for (long i = 0; i < 6; i++) + stack->buffer_colors_origin [stack->buffer_colors_0_arrow + arrow_address_in_list * 6 + i] + = stack->buffer_colors_origin [stack->buffer_colors_size - 6 + i]; + + stack->buffer_vertex_size -= 6; // <<< l'inverse de ce qui est fait dans : graphics_draw_vertex() + stack->buffer_colors_size -= 6; // <<< l'inverse de ce qui est fait dans : graphics_draw_colors() + stack->buffer_lines_size -= 2; // <<< l'inverse de ce qui est fait dans : graphics_draw_line() + + void *new_arrows_vertex_ptr = g_realloc(stack->buffer_vertex_origin, stack->buffer_vertex_size * sizeof(GLfloat)); + if (new_arrows_vertex_ptr) stack->buffer_vertex_origin = new_arrows_vertex_ptr; + else perror("In graphics.erase_arrow(), can't re_allocate for arrows vertex buffer.\n"); + + void *new_arrows_colors_ptr = g_realloc(stack->buffer_colors_origin, stack->buffer_colors_size * sizeof(GLfloat)); + if (new_arrows_colors_ptr) stack->buffer_colors_origin = new_arrows_colors_ptr; + else perror("In graphics.erase_arrow(), can't re_allocate for arrows colors buffer.\n"); + + /* Il ne faut pas réécrire ce qui suit: ces lignes dessinent maintenant à partir d'autres vertex */ + /* void *new_arrows_lines_ptr = g_realloc(stack->buffer_lines_origin, stack->buffer_lines_size * sizeof(GLfloat)); */ + /* if (new_arrows_lines_ptr) stack->buffer_lines_origin = new_arrows_lines_ptr; */ + /* else perror("In graphics.erase_arrow(), can't re_allocate for arrows lines buffer.\n"); */ + + return arrows_nb; +} + + + +/* + * Creates or deletes an arrow or modify the load of an existing one + * Acts both by writing the list of arrows : (struct arrow_t *arrows_ptr, int arrows_nb) + * and by drawing in the global space + * + * @param arrows_ptr and arrows_nb before operation, + * required load and address (site, x, y, z) + * + * IF there is no arrow at the required address, + * AND IF the load is > 0, an arrow will be created. + * + * IF there is an arrow at the required address + * AND IF the load is = 0, the existing arrow will be deleted. + * + * IF there is an arrow at the required address + * AND IF the load is > 0, the load of the existing arrow will be modified. + * + * May not call any of these three functions IF : + * - Current_weight of an arrow located at the requested address == requested_weight + * - No arrow was found at the requested addres AND current_weight == requested_weight + * + * @return arrows_nb after operation + */ +int set_arrow (int stack_id, + int arrows_nb, + int space_X, + int space_Y, + int space_Z, + int requested_weight, + int site, + int arrow_x, + int arrow_y, + int arrow_z) +{ + struct graphic_stack_t *stack = &graphic_stack[stack_id]; + int address = -1, current_weight = -1; + + for (int i = 0; i < arrows_nb; i++) + if ((site == stack->arrows_ptr[i].site) + && (arrow_x == stack->arrows_ptr[i].x) + && (arrow_y == stack->arrows_ptr[i].y) + && (arrow_z == stack->arrows_ptr[i].z)) + { + address = i; + current_weight = stack->arrows_ptr[i].load; + break; + } + assert (address <= arrows_nb); + + if (address == -1 && requested_weight > 0) + return create_arrow (stack_id, arrows_nb, space_X, space_Y, space_Z, requested_weight, site, arrow_x, arrow_y, arrow_z); + if (address >= 0 && requested_weight == 0) // address >= 0 if and only if arrows_nb > 0 + return erase_arrow(stack_id, arrows_nb, address, site, arrow_x, arrow_y, arrow_z); + if (address >= 0 && current_weight != requested_weight) { + rewrite_arrow(stack_id, address, requested_weight, site, arrow_x, arrow_y, arrow_z); + return arrows_nb; + } + + return arrows_nb; +} + diff --git a/gg/base.h b/gg/base.h new file mode 100644 index 0000000..85d6d77 --- /dev/null +++ b/gg/base.h @@ -0,0 +1,119 @@ +/* + * Gem-graph OpenGL experiments + * + * Desc: Base header + * + * Copyright (C) 2023 Arthur Menges + * Copyright (C) 2023 Adrien Bourmault + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#define G_APPLICATION_DEFAULT_FLAGS 0 + +// Graphical axis +enum +{ + X_AXIS, + Y_AXIS, + Z_AXIS, + + N_AXIS +}; + +// Gem-graph modes +enum +{ + HOME_MODE, + RUN_MODE, + EDIT_MODE, + PRESENTATION_MODE, + + N_MODE +}; + +/* + * Structure describing an arrow + */ +struct arrow_t { + uint load; + uint site; + uint x; + uint y; + uint z; +}; + +/* + * Read a file from filename into a provided buffer + * + * @param filename, file name + * contents, target ptr + * + * @return void + */ +static inline char *read_file(char *filename) +{ + int fd; + int filesize; + char *contents; + + fd = open(filename, O_RDONLY); + if(fd < 0) { + printf("Couldn't read file: %s\n",filename); + return NULL; + } + + filesize = lseek(fd, 0, SEEK_END) + 1 ; + contents = g_malloc(filesize * sizeof(char)); + assert (contents); + + lseek(fd, 0, SEEK_SET); + read(fd,contents,filesize); + + contents[filesize-1] = '\0'; + + close(fd); + + return contents; +} + +/* I'm standing on Earth (any planet or star or spinning spheroid, in fact) + * and looking towards its North pole + * + * X - X = EAST - WEST = rouge - cyan + * Y - Y = ZENITH - NADIR = vert - magenta + * Z - Z = NORTH - SOUTH = bleu - jaune + */ + +#define EAST 0 // + x rouge +#define WEST 1 // - x cyan +#define ZENITH 2 // + y vert +#define NADIR 3 // - y magenta +#define SOUTH 4 // + z bleu +#define NORTH 5 // - z jaune diff --git a/gg/displays.c b/gg/displays.c new file mode 100644 index 0000000..fee294c --- /dev/null +++ b/gg/displays.c @@ -0,0 +1,295 @@ +/* + * Gem-graph OpenGL experiments + * + * Desc: GL functions + * + * Copyright (C) 2023 Adrien Bourmault + * Copyright (C) 2023 Jean Sirmai + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "../../include/graphics.h" + +/* Prints the arrows[] array + * + * For each arrow the following parameters are displayed : + * - rank in the array + * - load (or weight) + * - coordinates in space (site, x, y, z) + */ +void print_arrows_array (struct arrow_t *arrows, int arrows_nb, int invoked_by) +{ + printf(" [rank] load | site x y z ("); + switch (invoked_by) { + case 0: printf("after deletion) arrows_nb : %d", arrows_nb); break; + case 1: printf("after creation) arrows_nb : %d", arrows_nb); break; + case 2: printf("after modification) arrows_nb : %d", arrows_nb); break; + case 3: printf("address >= 0 && current_weight == requested_weight)"); break; + case 4: printf("address == -1 && requested_weight == 0)"); break; + case 5: printf("print_user_choices)"); break; + case 6: printf("print_evolution)"); break; + case 7: printf("before deletion) arrows_nb : %d", arrows_nb); break; + } + for (int i = 0; i < arrows_nb; i++) + printf("\n [%4d] = %2d | %2d, %2d, %2d, %2d", i, arrows[i].load,\ + arrows[i].site, arrows[i].x, arrows[i].y, arrows[i].z); + if (arrows_nb == 0) printf("\n [NULL] ---- | ---- --- --- ---"); + printf("\n"); +} + + +/* Prints the initial user choices : + * - space dimension size and appearance (grids) + * - arrows[] array + * NB The space may be empty or saturated with arrows or any value in between + * Only one arrow per possible coordinates with a load max equal to ? TODO + */ +void print_user_choices(struct arrow_t *arrows, int max_arrows_nb, int arrows_nb, + int space_size_x, int space_size_y, int space_size_z, + int show_array, int show_space_design) +{ + printf("model + user constraints :\tspace size x,y,z (%d,%d,%d)\tinitial (max) arrows nb : %d",\ + space_size_x, space_size_y, space_size_z, max_arrows_nb); + + if (show_space_design) printf(" (grilles alternées)"); + printf("\n"); + + if (show_array) print_arrows_array (arrows, arrows_nb, 5); +} + +/* Prints the evolution after adding / removing arrows : + * - arrows[] array + * NB The space may be empty or saturated with arrows or any value in between + * Only one arrow per possible coordinates with a load max equal to ? TODO + * + * + * print_evolution (arrows, added, mem, deleted, print_arrows_data); + */ +void print_evolution (struct arrow_t *arrows, int arrows_nb, int modified, int deleted, int show_array) +{ + printf("evolution\t\t\t\t\t\t\tarrows nb > %6d\t (%d added / %d deleted) (modified : %d)\n",\ + arrows_nb + modified - deleted * 2, modified - deleted, deleted, modified); + if (show_array) print_arrows_array (arrows, arrows_nb, 6); +} + +/* + * Prints the arrows[] array + * + * For each arrow the following parameters are displayed : + * - rank in the array + * - weight (or load) + * - coordinates in space (site, x, y, z) + */ +void show_arrows_array_head(int one_batch_size, long nb_batches_specified, int verbose) { + printf("\n [rank] load | site x y z"); + if (verbose) printf(" one batch size = %d nb_batches_specified = %ld",\ + one_batch_size, nb_batches_specified); +} +void show_one_arrow_in_array(struct arrow_t *arrows, int i) { + printf("\n [%4d] = %2d | %2d, %2d, %2d, %2d",\ + i, arrows[i].load, arrows[i].site, arrows[i].x, arrows[i].y, arrows[i].z); +} +void show_empty_arrows_array() {printf("\n [NULL] ---- | ---- --- --- ---");} + +void show_arrows_array (struct arrow_t *arrows, int arrows_nb, int x, int y, int z) +{ + show_arrows_array_head (0,0,0); + for (int i = 0; i < arrows_nb; i++) + show_one_arrow_in_array(arrows, i); + if (arrows_nb == 0) show_empty_arrows_array(); +} + + + +/* + * Prints the initial user choices : + * - space dimension size and appearance (grids) + * - arrows[] array + * NB The space may be empty or saturated with arrows or any value in between + * To assert : Only one arrow per possible coordinates with a load max equal to ? + */ +void show_user_choices(long copy_nb_arrows_specified, + int space_size_x, int space_size_y, int space_size_z, + int prefer, int arbitrary, int one_batch_size) +{ + printf("\nnb arrows specified = %ld", copy_nb_arrows_specified); + printf(" space size (x,y,z) = (%d,%d,%d)", space_size_x, space_size_y, space_size_z); + printf(" arbitrary = %d one_batch_size = %d", arbitrary, one_batch_size); + if (prefer == 0) printf(" prefer = %d <> show all grids", prefer); + if (prefer == 1) printf(" prefer = %d <> show no grid", prefer); + if (prefer > 1) printf(" show grids according rule (%d)", prefer); +} + + +/* + * Prints the result of the function set_arrow() + * and indicates the reasons of the choice (call) this function makes (see this function) + */ +#define TEST 0 +void show_user_action(struct arrow_t *arrows, int arrows_nb, int address, int requested_weight, + int current_weight, int site, int x, int y, int z) +{ + if (address == -1 && requested_weight > 0) { // *(arrows + i * 5 + 0) + printf("no such arrow found (%2d,%2d,%2d,%2d)", site, x, y, z); //arrows[address + 1], arrows[address + 2], arrows[address + 3], arrows[address + 4]); + if (! TEST) printf("\n "); else printf(" "); + printf("=> CREATE"); return;} + + if (address >= 0 && requested_weight == 0) { + printf("arrow (%2d,%2d,%2d,%2d) found at address %2d; current_weight = %2d;", site, x, y, z, address, current_weight); // arrows[address + 1], arrows[address + 2], arrows[address + 3], arrows[address + 4], address/5, current_weight); + if (! TEST) printf("\n "); else printf(" "); + printf("=> ERASE"); return;} + + if (address >= 0 && current_weight != requested_weight) { + printf("arrow (%2d,%2d,%2d,%2d) found at address %2d; current_weight = %2d;", site, x, y, z, address, current_weight); // arrows[address + 1], arrows[address + 2], arrows[address + 3], arrows[address + 4], address/5, current_weight); + if (! TEST) printf("\n "); else printf(" "); + printf("=> MODIFY"); return;} + + if (address >= 0 && current_weight == requested_weight){ + printf("arrow (%2d,%2d,%2d,%2d) found at address %2d;", site, x, y, z, address); // arrows[address + 1], arrows[address + 2], arrows[address + 3], arrows[address + 4], address/5); + if (! TEST) printf("\n "); else printf(" "); + printf("requested_weight == current_weight => END"); return;} + + if (address == -1 && requested_weight == 0) { + printf("no such arrow found (%2d,%2d,%2d,%2d)", site, x, y, z); // arrows[address + 1], arrows[address + 2], arrows[address + 3], arrows[address + 4]); + if (! TEST) printf("\n "); else printf(" "); + printf("=> END"); return;} +} + + + +/* + * Prints vertex and lines buffers_states (sizes) at major one_batch_sizes and at the end of a session. + * Major one_batch_sizes are creation of grids and creation of arrows + * Arithmetic verification is provided at each one_batch_size + */ +void show_buffers_states(int space_X, int space_Y, int space_Z, + long nb_batches_specified, int one_batch_size, + int offset_after_grids, int buffer_vertex_size, + int buffer_lines_size_after_cubes, int buffer_lines_size) +{ + int offset_after_arrows = buffer_vertex_size / 3, difference = offset_after_arrows - offset_after_grids; + + printf("\n - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + + printf("\n buffer_vertex_offset_after grids = %9d\t%9d = (x+1)*(y+1)*(z+1); <--> (x,y,z = %d,%d,%d)",\ + offset_after_grids, (space_X + 1)*(space_Y + 1)*(space_Z + 1), space_X, space_Y, space_Z); + + printf("\n buffer_vertex_offset_after arrows = %9d\t%9d = %d + %d; <--> %d = 12 x %d (%d cubes)",\ + offset_after_arrows, offset_after_arrows, offset_after_grids,\ + difference, difference, difference / 12, difference / 12); + + printf("\n buffer_lines_offset after cubes = %9d\t%9d = 2 * (%dx%d + %dx%d + %dx%d); <--> 2 * (x*y + x*z + y*z)",\ + buffer_lines_size_after_cubes, ((space_X+1) * (space_Y+1) + (space_X+1) * (space_Z+1) + (space_Y+1) * (space_Z+1)) * 2, + space_X+1, space_Y+1, space_X+1, space_Z+1, space_Y+1, space_Z+1); + + printf("\n buffer_lines_offset after arrows = %9d\t%9d = %d + %d; <--> %d = 14 x %d (%d arrows drawned)",\ + buffer_lines_size, buffer_lines_size, buffer_lines_size_after_cubes,\ + buffer_lines_size - buffer_lines_size_after_cubes,\ + buffer_lines_size - buffer_lines_size_after_cubes, (buffer_lines_size - buffer_lines_size_after_cubes) / 14,\ + (buffer_lines_size - buffer_lines_size_after_cubes) / 14); + + if (nb_batches_specified * one_batch_size == 0) printf("\n"); + else printf("\n WARNING Arrows of the same address, whatever their weight, are drawn in superimposition. Check their list."); + + printf("\n It happens that NOT all the specified arrows (%ld) = (%ld x %d) are drawned (%d) ! WHY ? (d = %ld)\n",\ + nb_batches_specified * one_batch_size, nb_batches_specified, one_batch_size, (buffer_lines_size - buffer_lines_size_after_cubes) / 14,\ + nb_batches_specified * one_batch_size - (buffer_lines_size - buffer_lines_size_after_cubes) / 14); +} + + + +/* + * Prints the result of the function set_arrow() + * and indicates the reasons of the choice (call) this function makes (see this function) + */ +void print_user_action(struct arrow_t *arrows, int arrows_nb, int address, int requested_weight, + int current_weight, int site, int x, int y, int z) +{ + if (address == -1 && requested_weight > 0) { // *(arrows + i * 5 + 0) + printf("no such arrow found (%2d,%2d,%2d,%2d)", site, x, y, z); //arrows[address + 1], arrows[address + 2], arrows[address + 3], arrows[address + 4]); + if (! TEST) printf("\n "); else printf(" "); + printf("=> CREATE"); return;} + + if (address >= 0 && requested_weight == 0) { + printf("arrow (%2d,%2d,%2d,%2d) found at address %2d; current_weight = %2d;", site, x, y, z, address, current_weight); // arrows[address + 1], arrows[address + 2], arrows[address + 3], arrows[address + 4], address/5, current_weight); + if (! TEST) printf("\n "); else printf(" "); + printf("=> ERASE"); return;} + + if (address >= 0 && current_weight != requested_weight) { + printf("arrow (%2d,%2d,%2d,%2d) found at address %2d; current_weight = %2d;", site, x, y, z, address, current_weight); // arrows[address + 1], arrows[address + 2], arrows[address + 3], arrows[address + 4], address/5, current_weight); + if (! TEST) printf("\n "); else printf(" "); + printf("=> MODIFY"); return;} + + if (address >= 0 && current_weight == requested_weight){ + printf("arrow (%2d,%2d,%2d,%2d) found at address %2d;", site, x, y, z, address); // arrows[address + 1], arrows[address + 2], arrows[address + 3], arrows[address + 4], address/5); + if (! TEST) printf("\n "); else printf(" "); + printf("requested_weight == current_weight => END"); return;} + + if (address == -1 && requested_weight == 0) { + printf("no such arrow found (%2d,%2d,%2d,%2d)", site, x, y, z); // arrows[address + 1], arrows[address + 2], arrows[address + 3], arrows[address + 4]); + if (! TEST) printf("\n "); else printf(" "); + printf("=> END"); return;} +} + + + + + +static inline void print_buffers_array_head () {printf(" [rank] load | site x y z [address] buffer-vertex-1 buffer-vertex-2 buffer-lines\n");} +static inline void print_empty_buffers_array () {printf(" [NULL] ---- | ---- --- --- --- -1 --- --- ---\n");} + +static void print_one_arrow_in_buffers (struct arrow_t *arrows, + GLfloat *buffer_vertex_origin, long buffer_vertex_0_arrow, + GLfloat *buffer_lines_origin, long buffer_lines_0_arrow, + int i, int arrows_nb, int address) +{ + printf(" [%4d] = %2d | %2d, %2d, %2d, %2d, (%2d) %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f %4f %4f\n",\ + i, + arrows[i].load, arrows[i].site, arrows[i].x, arrows[i].y, arrows[i].z, + address, + + buffer_vertex_origin [buffer_vertex_0_arrow + i * 6 + 0], + buffer_vertex_origin [buffer_vertex_0_arrow + i * 6 + 1], + buffer_vertex_origin [buffer_vertex_0_arrow + i * 6 + 2], + + buffer_vertex_origin [buffer_vertex_0_arrow + i * 6 + 3], + buffer_vertex_origin [buffer_vertex_0_arrow + i * 6 + 4], + buffer_vertex_origin [buffer_vertex_0_arrow + i * 6 + 5], + + buffer_lines_origin [buffer_lines_0_arrow + i * 2 + 0], + buffer_lines_origin [buffer_lines_0_arrow + i * 2 + 1]); +} + +void print_vertex_and_lines_buffers (struct arrow_t *arrows_ptr, int arrows_nb, + GLfloat *buffer_vertex_origin, long buffer_vertex_0_arrow, + GLfloat *buffer_lines_origin, long buffer_lines_0_arrow, + int address, int requested_weight, int current_weight, int site, int x, int y, int z) +{ + /* for (int i = 0; i < 6; i++) printf("%5.2f ", buffer_vertex_origin [buffer_vertex_0_arrow + i]); printf("\n"); */ + /* for (int i = 0; i < 6; i++) printf("%5.2f ", buffer_lines_origin [buffer_lines_0_arrow + i]); printf("\n"); */ + print_buffers_array_head (); + for (int i = 0; i < arrows_nb; i++) + print_one_arrow_in_buffers(arrows_ptr, buffer_vertex_origin, buffer_vertex_0_arrow, + buffer_lines_origin, buffer_lines_0_arrow, i, arrows_nb, address); + if (arrows_nb == 0) print_empty_buffers_array(); +} + + + + diff --git a/gg/draw.c b/gg/draw.c new file mode 100644 index 0000000..7b9732c --- /dev/null +++ b/gg/draw.c @@ -0,0 +1,207 @@ +/* + * Gem-graph OpenGL experiments + * + * Desc: GL functions + * + * Copyright (C) 2023 Adrien Bourmault + * Copyright (C) 2023 Jean Sirmai + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/* + * Writes values to describe a vertex at (x,y,z) intoq the vertex buffer + * + * @param coords GLfloat(x,y,z) + * + * @return void + */ + +#include +#include +#include +#include "../../include/base.h" +#include "../../include/ui.h" +#include "../../include/graphics.h" + +void graphics_draw_vertex (const int stack_id, + GLfloat x, + GLfloat y, + GLfloat z) +{ + //g_printerr("stack_id is %d\n", stack_id); + //g_printerr("stack_id is %d\n", stack_id); + //g_printerr("graphic_stack is at %p\n", graphic_stack); + //g_printerr("graphic_stack[stack_id] is at %p\n", graphic_stack + stack_id); + volatile struct graphic_stack_t *stack = &graphic_stack[stack_id]; + + //g_printerr("Currently stack->buffer_vertex_origin @ %p\n", stack->buffer_vertex_origin); + + //assert (stack->buffer_vertex_origin); + + stack->buffer_vertex_origin = + g_realloc (stack->buffer_vertex_origin, + (stack->buffer_vertex_size + 3) * sizeof(GLfloat)); + //print_stack(stack_id); + + stack->buffer_vertex_origin[stack->buffer_vertex_size + 0] = x; + stack->buffer_vertex_origin[stack->buffer_vertex_size + 1] = y; + stack->buffer_vertex_origin[stack->buffer_vertex_size + 2] = z; + + stack->buffer_vertex_size += 3; +} + +/* + * Writes values to describe a color (r,g,b) into the color buffer + * + * @param color GLfloat(r,g,b) + * + * @return void + */ +void graphics_draw_color (const int stack_id, + GLfloat r, + GLfloat g, + GLfloat b) +{ + struct graphic_stack_t *stack = &graphic_stack[stack_id]; + + stack->buffer_colors_origin = g_realloc (stack->buffer_colors_origin, + (stack->buffer_colors_size + 3) * sizeof(GLfloat)); + + assert (stack->buffer_colors_origin); + + stack->buffer_colors_origin[stack->buffer_colors_size + 0] = r; + stack->buffer_colors_origin[stack->buffer_colors_size + 1] = g; + stack->buffer_colors_origin[stack->buffer_colors_size + 2] = b; + + stack->buffer_colors_size += 3; +} + +/* + * Writes values to describe a line from a to b into the line buffer + * + * @param coords GLuint (a,b) + * + * @return void + */ +void graphics_draw_line (const int stack_id, + GLuint a, + GLuint b) +{ + struct graphic_stack_t *stack = &graphic_stack[stack_id]; + + stack->buffer_lines_origin = g_realloc (stack->buffer_lines_origin, + (stack->buffer_lines_size + 2) * sizeof(GLuint)); + + assert (stack->buffer_lines_origin); + + stack->buffer_lines_origin[stack->buffer_lines_size + 0] = a; + stack->buffer_lines_origin[stack->buffer_lines_size + 1] = b; + + stack->buffer_lines_size += 2; +} + +/* + * Writes values to describe an (a,b,c) plan (triangle) into the plan buffer + * + * @param coords GLuint (a,b,c) + * + * @return void + */ +void graphics_draw_plan (const int stack_id, + GLuint a, + GLuint b, + GLuint c) +{ + struct graphic_stack_t *stack = &graphic_stack[stack_id]; + + stack->buffer_plans_origin = g_realloc (stack->buffer_plans_origin, + (stack->buffer_plans_size + 3) * sizeof(GLuint)); + + assert (stack->buffer_plans_origin); + + stack->buffer_plans_origin[stack->buffer_plans_size + 0] = a; + stack->buffer_plans_origin[stack->buffer_plans_size + 1] = b; + stack->buffer_plans_origin[stack->buffer_plans_size + 2] = c; + + stack->buffer_plans_size += 3; +} + +/* + * Draws the current buffer to a gl_area + * + * @param gl_area, ptr to the gl_area widget + * + * @return void + */ +void graphics_draw(const int stack_id) +{ + struct graphic_stack_t *stack = &graphic_stack[stack_id]; + + //g_printerr("[debug] graphics_draw() started\n"); + + //print_stack(stack_id); + + GLint cur_viewport[4]; + glGetIntegerv(GL_VIEWPORT, cur_viewport); + + mat4 m = GLM_MAT4_IDENTITY_INIT; + glm_rotate_x(m, glm_rad(stack->rotation_angles[X_AXIS]), m); + glm_rotate_y(m, glm_rad(stack->rotation_angles[Y_AXIS]), m); + glm_rotate_z(m, glm_rad(stack->rotation_angles[Z_AXIS]), m); + + mat4 v = GLM_MAT4_IDENTITY_INIT; // XXX define zoom and translations here ? + + mat4 p = GLM_MAT4_IDENTITY_INIT; + //glm_ortho(-1.0f, +1.0f, -1.0f, +1.0f, -1.0f, +1.0f, p); + glm_ortho_default((float)cur_viewport[2] / (float)cur_viewport[3], p); + //glm_perspective_default((float)cur_viewport[2] / (float)cur_viewport[3], p); + + /* Use our shaders */ + glUseProgram(stack->program); + + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + /* Update the "mvp" matrix we use in the shader */ + glUniformMatrix4fv(stack->m, 1, GL_FALSE, (float *)m); + glUniformMatrix4fv(stack->v, 1, GL_FALSE, (float *)v); + glUniformMatrix4fv(stack->p, 1, GL_FALSE, (float *)p); + + /* Use the vertices in our buffer */ + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, stack->position_buffer); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0,(void*)0); + + // couleurs + glEnableVertexAttribArray(1); + glBindBuffer(GL_ARRAY_BUFFER, stack->color_buffer); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0,(void*)0); + + //glEnable(GL_DEPTH_TEST); + + glDrawElements(GL_LINES, stack->buffer_lines_size, GL_UNSIGNED_INT, stack->buffer_lines_origin); + glDrawElements(GL_TRIANGLES, stack->buffer_plans_size, GL_UNSIGNED_INT, stack->buffer_plans_origin); + + /* We finished using the buffers and program */ + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glUseProgram(0); + + glFlush(); + //g_printerr("[debug] graphics_draw() ended\n"); +} diff --git a/gg/events.c b/gg/events.c new file mode 100644 index 0000000..90c69dc --- /dev/null +++ b/gg/events.c @@ -0,0 +1,369 @@ +/* + * Gem-graph OpenGL experiments + * + * Desc: User interface functions + * + * Copyright (C) 2023 Arthur Menges + * Copyright (C) 2023 Adrien Bourmault + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include + +#include "../../include/base.h" +#include "../../include/graphics.h" +#include "../../include/parsing.h" +#include "../../include/ui.h" + + + +void on_about_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + static const char *authors[] = { "Adrien Bourmault (neox@a-lec.org)", + "Jean Sirmai (jean@a-lec.org)", + "Arthur Menges (arthur.menges@a-lec.org)", + NULL}; + GemGraphClientApplication *self = user_data; + GtkWindow *window = NULL; + + g_assert (GEM_GRAPH_CLIENT_IS_APPLICATION(self)); + + window = gtk_application_get_active_window(GTK_APPLICATION (self)); + + gtk_show_about_dialog(window, + "program-name", "Gem-graph", + "logo-icon-name", "application-x-executable", + "authors", authors, + "version", "0.1.0", + "copyright", "Copyright © 2023 Libre en Communs", + NULL); +} + +void on_quit_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GemGraphClientApplication *self = user_data; + + g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); + + g_application_quit(G_APPLICATION(self)); +} + +void on_preferences_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GemGraphClientApplication *self = user_data; + + g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); + + ui_send_internal_notification("Not implemented !"); +} + +void on_togglesidebar_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GemGraphClientApplication *self = user_data; + + g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); + + ui_toggle_sidebar(); +} + +void on_editmode_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GemGraphClientApplication *self = user_data; + + g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); + + ui_set_stack(EDIT_MODE); +} + +void on_runmode_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GemGraphClientApplication *self = user_data; + + g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); + + ui_set_stack(RUN_MODE); +} + +void on_presentmode_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GemGraphClientApplication *self = user_data; + + g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); + + ui_set_stack(PRESENTATION_MODE); +} + +void on_openfile_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GemGraphClientApplication *self = user_data; + + g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); + + // Create a new file selection dialog, using the "open" mode + GtkFileChooserNative *native = + gtk_file_chooser_native_new("Open File", + GTK_WINDOW(self), + GTK_FILE_CHOOSER_ACTION_OPEN, + "_Open", + "_Cancel"); + + // Connect the "response" signal of the file selection dialog; + // this signal is emitted when the user selects a file, or when + // they cancel the operation + g_signal_connect (native, + "response", + G_CALLBACK(on_openfile_response), + self); + + // Present the dialog to the user + gtk_native_dialog_show (GTK_NATIVE_DIALOG (native)); +} + +void on_closefile_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GemGraphClientApplication *self = user_data; + + g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); + + model_shutdown(); + + ui_disable_action("closefile"); + ui_disable_action("savefile"); + ui_disable_action("runmode"); + ui_disable_action("editmode"); + ui_disable_action("presentmode"); + ui_disable_action("togglesidebar"); + ui_set_stack(HOME_MODE); +} + +void on_savefile_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GemGraphClientApplication *self = user_data; + + g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); + +} + +void on_toast_close_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GemGraphClientApplication *self = user_data; + + g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); + + ui_close_internal_notification(); +} + +/* -------------------------------------------------------------------------- */ + +/* + * Graphical/view related events + */ + +void on_axis_value_change(GtkAdjustment *adjustment, gpointer data) +{ + + GtkWidget *slider = gtk_widget_get_parent(GTK_WIDGET(data)); + GtkWidget *container_widget = gtk_widget_get_parent(GTK_WIDGET(slider)); + + const gchar *label_text = gtk_label_get_label(GTK_LABEL(data)); + + // THANKS ASCIIIII/Unicode/Whateverrr ! + int axis = label_text[0] - 'X'; + + g_assert(axis >= 0 && axis < N_AXIS); + + /* Update the rotation angle */ + ui_update_axis_stack(container_widget, + axis, + gtk_adjustment_get_value(adjustment)); + + /* Update the contents of the GL drawing area */ +} + +gboolean on_glarea_render(GtkGLArea *area, GdkGLContext *context) +{ + // Check if the widget is a glarea + if(gtk_gl_area_get_error(area) != NULL) { + ui_send_internal_notification("An OpenGL error occured !"); + return false; + } + + if (ui_render_stack(gtk_widget_get_parent(GTK_WIDGET(area))) == false) { + ui_send_internal_notification( + "Failed to render corresponding graphic stack !"); + return false; + } + + return true; +} + + +/* We need to set up our state when we realize the GtkGLArea widget */ +void on_glarea_realize(GtkWidget *widget) +{ + GError *internal_error = NULL; + + // Make the GL context current to be able to call the GL API + gtk_gl_area_make_current(GTK_GL_AREA(widget)); + + // Check if the widget is a glarea + if(gtk_gl_area_get_error(GTK_GL_AREA(widget)) != NULL) { + ui_send_internal_notification("An OpenGL error occured !"); + return; + } + + // Link graphical stack to widget + if (ui_init_graphic_stack(gtk_widget_get_parent(widget), + internal_error) == false) { + ui_send_internal_notification( + "Failed to link the graphic stack to widgets !"); + return; + } + + gtk_gl_area_set_auto_render(GTK_GL_AREA(widget), true); +} + +/* We should tear down the state when unrealizing */ +void on_glarea_unrealize(GtkWidget *widget) +{ + GError *internal_error = NULL; + + // Make the GL context current to be able to call the GL API + gtk_gl_area_make_current(GTK_GL_AREA(widget)); + + // Check if the widget is a glarea + if(gtk_gl_area_get_error(GTK_GL_AREA(widget)) != NULL) { + ui_send_internal_notification("An OpenGL error occured !"); + return; + } + + // Destroy graphic stack + if (ui_shutdown_graphic_stack(gtk_widget_get_parent(widget), + internal_error) == false) { + ui_send_internal_notification( + "Failed to shutdown the graphic stack !"); + return; + } +} + +void on_close_window(GtkWidget *widget) +{ + + ui_shutdown_all_graphic_stacks(); + ui_clean_stack_index(); +} + +/* -------------------------------------------------------------------------- */ + +/* + * Responses + */ +void on_openfile_response(GtkNativeDialog *native, + int response, + GemGraphClientWindow *self) +{ + g_autoptr(GFile) file; + GtkFileChooser *chooser; + + if (response == GTK_RESPONSE_ACCEPT) { + chooser = GTK_FILE_CHOOSER(native); + file = gtk_file_chooser_get_file(chooser); + + // Load asynchroneously not to block control flow + g_file_load_contents_async (file, + NULL, + (GAsyncReadyCallback)ui_model_loading, + self); + } + + g_object_unref(native); +} + +void ui_model_loading(GObject *source_object, + GAsyncResult *result, + GemGraphClientWindow *self) +{ + GFile *file = G_FILE(source_object); + + char *content = NULL; + size_t length = 0; + + GError *error = NULL; + char *basename = g_file_get_basename(file); + + // Gives you the contents of the file as a byte array, or + // set the error argument + g_file_load_contents_finish(file, + result, + &content, + &length, + NULL, + &error); + + // In case of error, print a warning to the standard error output + if (error != NULL) { + g_printerr("Unable to open “%s”: %s\n", + g_file_peek_path(file), + error->message); + ui_send_internal_notification("Unable to open file !"); + g_free(error); + g_free(basename); + g_free(content); + return; + } + + if (model_init(content, length, basename) == false) { + ui_send_internal_notification("Error while loading the model"); + g_free(basename); + g_free(content); + return; + } + + ui_enable_action("closefile"); + ui_enable_action("savefile"); + ui_enable_action("runmode"); + ui_enable_action("editmode"); + ui_enable_action("presentmode"); + ui_enable_action("togglesidebar"); + ui_set_stack(RUN_MODE); + + g_free(basename); + g_free(content); +} + diff --git a/gg/graphics.c b/gg/graphics.c new file mode 100644 index 0000000..49e3d5a --- /dev/null +++ b/gg/graphics.c @@ -0,0 +1,328 @@ +/* + * Gem-graph OpenGL experiments + * + * Desc: GL functions + * + * Copyright (C) 2023 Arthur Menges + * Copyright (C) 2023 Adrien Bourmault + * Copyright (C) 2023 Jean Sirmai + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "../../include/base.h" +#include "../../include/ui.h" +#include "../../include/parsing.h" +#include "../../include/graphics.h" + +#define TEST 0 + +struct graphic_stack_t *graphic_stack = NULL; +size_t graphic_stack_size = 0; +int *free_stack_slot = NULL; +size_t free_stack_slot_size = 0; + +/* + * Prints verbose human-readable debug messages + * + * @param source, XXX + * type, XXX + * id, XXX + * severity, XXX + * length, XXX + * msg, XXX + * data, XXX + * + * @return void + */ +static void graphics_debug_callback(GLenum source, GLenum type, GLuint id, + GLenum severity, GLsizei length, + const GLchar *msg, const void *data) +{ + const char *errsource; + const char *errtype; + const char *errseverity; + const GLubyte *string; + GLenum code; + + switch (source) { + case GL_DEBUG_SOURCE_API: errsource = "API"; break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: errsource = "WINDOW SYSTEM"; break; + case GL_DEBUG_SOURCE_SHADER_COMPILER: errsource = "SHADER COMPILER"; break; + case GL_DEBUG_SOURCE_THIRD_PARTY: errsource = "THIRD PARTY"; break; + case GL_DEBUG_SOURCE_APPLICATION: errsource = "APPLICATION"; break; + case GL_DEBUG_SOURCE_OTHER: errsource = "UNKNOWN"; break; + default: errsource = "UNKNOWN"; break; + } + + switch (type) { + case GL_DEBUG_TYPE_ERROR: errtype = "ERROR"; break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: errtype = "DEPRECATED BEHAVIOR";break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: errtype = "UNDEFINED BEHAVIOR"; break; + case GL_DEBUG_TYPE_PORTABILITY: errtype = "PORTABILITY"; break; + case GL_DEBUG_TYPE_PERFORMANCE: errtype = "PERFORMANCE"; break; + case GL_DEBUG_TYPE_OTHER: errtype = "OTHER"; break; + case GL_DEBUG_TYPE_MARKER: errtype = "MARKER"; break; + default: errtype = "UNKNOWN"; break; + } + + switch (severity) { + case GL_DEBUG_SEVERITY_HIGH: errseverity = "CRITICAL"; break; + case GL_DEBUG_SEVERITY_MEDIUM: errseverity = "ERROR"; break; + case GL_DEBUG_SEVERITY_LOW: errseverity = "WARNING"; break; + case GL_DEBUG_SEVERITY_NOTIFICATION: errseverity = "INFO"; break; + default: errseverity = "UNKNOWN"; break; + } + + code = glGetError(); + string = gluErrorString(code); + + g_printerr("[%s] %s (%s) from %s: %s\n", + errseverity, string, errtype, errsource, msg); +} + +/* -------------------------------------------------------------------------- */ + +/* + * Initializes graphical stack + * + * @param gl_area, ptr to the gl_area widget + * + * @return id if initialized + */ +int graphics_init(void *error_buffer) +{ + int cur_id = 0; + struct graphic_stack_t *stack; + + /* g_printerr("[debug] graphics_init()\n"); */ + + if (graphic_stack == NULL) { + graphic_stack = g_malloc0(sizeof(struct graphic_stack_t)); + graphic_stack_size = 1; + /* g_printerr("[debug] graphics_init(): init graphic_stack @ %p\n", graphic_stack); */ + } else { + // Check if there are free slots + if (free_stack_slot_size) { + // We get the last free slot registered + cur_id = free_stack_slot[free_stack_slot_size-1]; + // Unregister it (ofc) + free_stack_slot = g_realloc(free_stack_slot, + free_stack_slot_size-- * + sizeof(int)); + } else { + cur_id = graphic_stack_size; + graphic_stack = g_realloc(graphic_stack, + ++graphic_stack_size * + sizeof(struct graphic_stack_t)); + } + } + + memset(&graphic_stack[cur_id], 0, sizeof(struct graphic_stack_t)); + + /* g_printerr("[debug] graphics_init() : graphic_stack (@0x%p) has %ld elements\n", */ + /* graphic_stack, */ + /* graphic_stack_size); */ + + stack = &graphic_stack[cur_id]; + stack->id = cur_id; + + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glEnable(GL_MULTISAMPLE); + + if (!graphics_init_shaders(cur_id)) + return -1; + + //print_stack(cur_id); + + graphics_init_buffers(cur_id); + + glDebugMessageCallback(graphics_debug_callback, NULL); + + //print_stack(cur_id); + + return cur_id; +} + +/* + * Shutdowns a gl_area + * + * @param gl_area, ptr to the gl_area widget + * + * @return true if success + */ +bool graphics_shutdown(const int id, void *error_buffer) +{ + struct graphic_stack_t *stack; + + if (id >= graphic_stack_size || + graphic_stack_size == 0 || + graphic_stack == NULL) + return false; + + stack = &graphic_stack[id]; + + //XXX + free(stack->arrows_ptr); + stack->arrows_ptr = NULL; + stack->arrows_nb = 0; + + glDeleteBuffers(1, &stack->position_buffer); + glDeleteBuffers(1, &stack->color_buffer); + glDeleteProgram(stack->program); + + g_free(stack->buffer_vertex_origin); + g_free(stack->buffer_colors_origin); + g_free(stack->buffer_lines_origin); + g_free(stack->buffer_plans_origin); + + if (graphic_stack_size == 1) { + free(graphic_stack); + graphic_stack = NULL; + graphic_stack_size = 0; + } else { + memset(&graphic_stack[id], 0, sizeof(struct graphic_stack_t)); + free_stack_slot = g_realloc(free_stack_slot, + ++free_stack_slot_size * + sizeof(int)); + free_stack_slot[free_stack_slot_size-1] = id; + } + + return true; +} + +/* TODO + * #pragma omp parallel schedule(static, 12) + * void __attribute__((optimize("no-unroll-loops"))) main_test_graphics (void) {} + * +// assert : space dimensions (x,y,z) > 0 +// assert : arrows localization within space and sites +// assert : no more than one arrow per address +// notify : weights are replaced, NOT added (could be !) + * + * Init space and arrows (= initial state) + * and allows ulterior creations, suppressions or modifications of the arrows[] array + * + * draws the space() + * triggers set_arrows() that modifies the list () and draws arrows + * + * Initialisation du générateur pseudo-aléatoire + * Attention, les vertex centraux de chaque unité d'espace (cube) + * peuvent être redondants (max 6) +*/ +void graphics_model_setup (const int stack_id) +{ + /*------------------------------------------------------------------------*/ + + /* I N I T I A L D A T A S P E C I F I C A T I O N */ + + /*------------------------------------------------------------------------*/ + + struct graphic_stack_t *stack = &graphic_stack[stack_id]; + char dimension; + long space_X; + long space_Y; + long space_Z; + long announced_arrows_nb; + int density_max; + char multiplicity; + + dimension = model_get_dim(); + + g_print("[GRAPH DEBUG] dim = %d\n", dimension); + + space_X = 1; + space_Y = 1; + space_Z = 1; + + switch(dimension) { + case 3: + space_Z = model_get_dim_value("z"); + case 2: + space_Y = model_get_dim_value("y"); + + // even in 1D, we must be able to see a grid + if (!space_Y) + space_Y = 1; + case 1: + space_X = model_get_dim_value("x"); + if (!space_X) + space_X = 1; + default: + break; + } + + g_print("[GRAPH DEBUG] x = %ld\n", space_X); + g_print("[GRAPH DEBUG] y = %ld\n", space_Y); + g_print("[GRAPH DEBUG] z = %ld\n", space_Z); + + density_max = space_X * space_Y * space_Z; + stack->arrows_nb = 0; + + multiplicity = model_get_multiplicity(); + g_print("[GRAPH DEBUG] site_multiplicity = %ld\n", multiplicity); + + /*------------------------------------------------------------------------*/ + + /* S P A C E D R A W I N G */ + + /*------------------------------------------------------------------------*/ + + draw_space_ridges_vertex (stack_id, stack->buffer_vertex_size, + space_X, space_Y, space_Z); + draw_space_ridges_lines (stack_id); + draw_grids_on_space_faces_vertex (stack_id, space_X, space_Y, space_Z); + draw_grids_on_space_faces_lines (stack_id, stack->buffer_lines_size, + space_X, space_Y, space_Z); + + stack->buffer_vertex_0_arrow = stack->buffer_vertex_size; + stack->buffer_colors_0_arrow = stack->buffer_colors_size; + stack->buffer_lines_0_arrow = stack->buffer_lines_size; + + /*------------------------------------------------------------------------*/ + + /* A R R O W S D R A W I N G */ + + /*------------------------------------------------------------------------*/ + + char state_id[30] = {0}; + struct arrow_t arrow = {0}; + + assert(model_get_next_state(&state_id)); + + g_print("[GRAPH DEBUG] first state is = %s\n", state_id); + + announced_arrows_nb = model_get_state_arrows_count(state_id); + + g_print("[GRAPH DEBUG] announced_arrows_nb is = %ld\n", announced_arrows_nb); + + while (model_get_next_arrow(&arrow, &state_id, dimension)) { + g_print("[GRAPH DEBUG] cur arrow has x = %d\n", arrow.x); + stack->arrows_nb = + set_arrow (stack_id, stack->arrows_nb, space_X, space_Y, space_Z, + arrow.load, // load + arrow.site, // site + arrow.x, // x + arrow.y, // y + arrow.z); // z + } + + if (stack->arrows_nb != announced_arrows_nb) + g_printerr("ARGH : all the arrows have not been parsed !\n"); +} diff --git a/gg/graphics.h b/gg/graphics.h new file mode 100644 index 0000000..f30f7b1 --- /dev/null +++ b/gg/graphics.h @@ -0,0 +1,314 @@ +/* + * Gem-graph OpenGL experiments + * + * Desc: OpenGL utils header + * + * Copyright (C) 2023 Arthur Menges + * Copyright (C) 2023 Adrien Bourmault + * Copyright (C) 2023 Jean Sirmai + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#pragma once +#include "base.h" +#include +#include +#include +#include +#include +#include + +#define VERTEX_SHADER_FILE "src/graphics/shaders/shader.vert" +#define FRAG_SHADER_FILE "src/graphics/shaders/shader.frag" +#define GL_TARGET_MAJOR_VERSION 0 +#define GL_TARGET_MINOR_VERSION 4 + +/* + * Structure describing a gl_area and its parameters, used to create a table + * of Gem-graph client current gl_areas + */ +struct graphic_stack_t { + + int id; + int mode; + + float rotation_angles[N_AXIS]; // Rotation angles on each axis + + GLuint vao; // init_buffers + GLuint position_buffer; // shutdown, draw + GLuint color_buffer; // shutdown, draw + GLuint program; // shutdown, init_shaders, draw + GLuint m; // init_shaders, draw + GLuint v; // init_shaders, draw + GLuint p; // init_shaders, draw + + struct arrow_t *arrows_ptr; + long arrows_nb; + + GLfloat *buffer_vertex_origin; + GLfloat *buffer_colors_origin; + GLuint *buffer_lines_origin; + GLuint *buffer_plans_origin; + + long buffer_vertex_size; + long buffer_colors_size; + long buffer_lines_size; + long buffer_plans_size; + + long buffer_vertex_0_arrow; + long buffer_colors_0_arrow; + long buffer_lines_0_arrow; + long buffer_plans_0_arrow; +}; + +/* + * Dynamic array of ptrs to dynamically allocated gl_area_entry + */ +extern struct graphic_stack_t *graphic_stack; + +/* + * Initializes a gl_area + * + * @param gl_area, ptr to the gl_area widget + * + * @return true if initialized + */ +int graphics_init(void *error_buffer); + +/* + * Draws the current buffer to a gl_area + * + * @param gl_area, ptr to the gl_area widget + * + * @return void + */ +void graphics_draw(const int stack_id); + +/* + * Shutdowns a gl_area + * + * @param gl_area, ptr to the gl_area widget + * + * @return true if success + */ +bool graphics_shutdown(const int stack_id, void *error_buffer); + +/* + * Initializes the shaders of a gl_area and link them to a program + * + * @param gl_area, ptr to the gl_area widget + * + * @return true if initialized + */ +bool graphics_init_shaders(const int stack_id); + +/* Initializes the buffer of a gl_area + * Calls according to the user preferences + * @param gl_area, ptr to the gl_area widget + * @return void + */ +void graphics_init_buffers(const int stack_id); + +/* + * Draws a vertex (x, y, z) + * if (console) prints (x, y, z) values to console + * + * @param GLfloat x, GLfloat y, GLfloat z + * + * @return void + */ +void graphics_draw_vertex (const int stack_id, + GLfloat x, + GLfloat y, + GLfloat z); + +/* + * Draws the color (r, g, b) associated to a vertex + * if (console) prints (r, g, b) values to console + * + * @param GLfloat r, GLfloat g, GLfloat b + * + * @return void + */ +void graphics_draw_color (const int stack_id, GLfloat r, GLfloat g, GLfloat b); + +/* + * Writes values to describe a line from a to b into the line buffer + * + * @param coords GLuint (a,b) + * + * @return void + */ +void graphics_draw_line (const int stack_id, GLuint a, GLuint b); + +/* + * Writes values to describe an (a,b,c) plan (triangle) into the plan buffer + * + * @param coords GLuint (a,b,c) + * + * @return void + */ +void graphics_draw_plan (const int stack_id, GLuint a, GLuint b, GLuint c); + +/* + * Created and compile a shader + * + * @param type, shader type + * src, source code of shader + * + * @return shader id + */ +static inline GLuint create_shader(const int stack_id, int type, const char *src) +{ + GLuint shader; + int status; + + shader = glCreateShader(type); + glShaderSource(shader, 1, &src, NULL); + glCompileShader(shader); + + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if(status == GL_FALSE) { + int log_len; + char *buffer; + + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len); + + buffer = g_malloc(log_len + 1); + assert (buffer); + glGetShaderInfoLog(shader, log_len, NULL, buffer); + + g_warning("Compile failure in %s shader:\n%s", + type == GL_VERTEX_SHADER ? "vertex" : "fragment", + buffer); + + g_free(buffer); + + glDeleteShader(shader); + + return 0; + } + + return shader; +} + +static inline void print_stack(int stack_id) +{ + static int n = 0; + + printf("\n[n=%d]***************", n); + + printf("id = %d\tmode = %d\n", + graphic_stack[stack_id].id, + graphic_stack[stack_id].mode); + + printf("rotation_angles = "); + for (int i = 0; i < N_AXIS; i++) { + printf("%f\t", graphic_stack[stack_id].rotation_angles[i]); // Rotation angles on each axis + } + printf("\n"); + + printf("vao = %d\n", graphic_stack[stack_id].vao); + printf("position_buffer = %d\n", graphic_stack[stack_id].position_buffer); + printf("color_buffer = %d\n", graphic_stack[stack_id].color_buffer); + printf("program = %d\n", graphic_stack[stack_id].program); + printf("m = %d\n", graphic_stack[stack_id].m); + printf("v = %d\n", graphic_stack[stack_id].v); + printf("p = %d\n", graphic_stack[stack_id].p); + + printf("arrows_ptr = %p\n", graphic_stack[stack_id].arrows_ptr); + printf("arrows_nb = %ld\n", graphic_stack[stack_id].arrows_nb); + + printf("buffer_vertex_origin = %p\n", graphic_stack[stack_id].buffer_vertex_origin); + printf("buffer_colors_origin = %p\n", graphic_stack[stack_id].buffer_colors_origin); + printf("buffer_lines_origin = %p\n", graphic_stack[stack_id].buffer_lines_origin); + printf("buffer_plans_origin = %p\n", graphic_stack[stack_id].buffer_plans_origin); + + printf("buffer_vertex_size = %ld\n", graphic_stack[stack_id].buffer_vertex_size); + printf("buffer_colors_size = %ld\n", graphic_stack[stack_id].buffer_colors_size); + printf("buffer_lines_size = %ld\n", graphic_stack[stack_id].buffer_lines_size); + printf("buffer_plans_size = %ld\n", graphic_stack[stack_id].buffer_plans_size); + + printf("buffer_vertex_0_arrow = %p\n", graphic_stack[stack_id].buffer_vertex_0_arrow); + printf("buffer_colors_0_arrow = %p\n", graphic_stack[stack_id].buffer_colors_0_arrow); + printf("buffer_lines_0_arrow = %p\n", graphic_stack[stack_id].buffer_lines_0_arrow); + printf("buffer_plans_0_arrow = %p\n", graphic_stack[stack_id].buffer_plans_0_arrow); + + printf("********************\n"); + n++; +} + + +void graphics_model_setup (const int stack_id); + +int draw_one_arrow_vertex (const int stack_id, + int space_X, + int space_Y, + int space_Z, + int weight, + int site, + int x, + int y, + int z); + +int draw_one_arrow_line(const int stack_id, + int offset_vertex); + +/* + * Writes grid ridges to vertex and color buffers + * + * @param coords long (x,y,z), step_x, step_y, step_z + * + * @return void + */ +int draw_space_ridges_vertex (const int stack_id, + long offset_vertex, + long x, + long y, + long z); + +int draw_space_ridges_lines (const int stack_id); + +/* + * Writes grid lines on space faces + * + * @param coords long (x,y,z) + * + * @return void + */ +long draw_grids_on_space_faces_vertex (const int stack_id, + long x, + long y, + long z); + +long draw_grids_on_space_faces_lines (const int stack_id, + long offset_vertex, + long x, + long y, + long z); + +int set_arrow (int stack_id, + int arrows_nb, + int space_X, + int space_Y, + int space_Z, + int requested_weight, + int site, + int arrow_x, + int arrow_y, + int arrow_z); + diff --git a/gg/grid.c b/gg/grid.c new file mode 100644 index 0000000..e0fad55 --- /dev/null +++ b/gg/grid.c @@ -0,0 +1,162 @@ +/* + * Gem-graph + * + * Desc: OpenGL grid functions + * + * Copyright (C) 2023 Jean Sirmai + * Copyright (C) 2023 Adrien Bourmault + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "../../include/base.h" +#include "../../include/ui.h" +#include "../../include/graphics.h" + +int draw_space_ridges_vertex (const int stack_id, + long offset_vertex, + long x, + long y, + long z) +{ + GLfloat max = fmax(x, y); max = fmax(max, z); + + graphics_draw_vertex (stack_id, offset_vertex - x / max, offset_vertex - y / max, - z / max); + + graphics_draw_vertex (stack_id, offset_vertex + x / max, offset_vertex - y / max, - z / max); + graphics_draw_vertex (stack_id, offset_vertex - x / max, offset_vertex + y / max, - z / max); + graphics_draw_vertex (stack_id, offset_vertex - x / max, offset_vertex - y / max, + z / max); + + graphics_draw_vertex (stack_id, offset_vertex + x / max, offset_vertex + y / max, - z / max); + graphics_draw_vertex (stack_id, offset_vertex + x / max, offset_vertex - y / max, + z / max); + graphics_draw_vertex (stack_id, offset_vertex - x / max, offset_vertex + y / max, + z / max); + + graphics_draw_vertex (stack_id, offset_vertex + x / max, + y / max, + z / max); + + graphics_draw_color (stack_id, 0.8f, 0.6f, 0.5f); + graphics_draw_color (stack_id, 0.8f, 0.6f, 0.5f); + graphics_draw_color (stack_id, 0.8f, 0.6f, 0.5f); + graphics_draw_color (stack_id, 0.8f, 0.6f, 0.5f); + graphics_draw_color (stack_id, 0.8f, 0.6f, 0.5f); + graphics_draw_color (stack_id, 0.8f, 0.6f, 0.5f); + graphics_draw_color (stack_id, 0.8f, 0.6f, 0.5f); + graphics_draw_color (stack_id, 0.8f, 0.6f, 0.5f); + + return 8; +} + +int draw_space_ridges_lines (const int stack_id) +{ + graphics_draw_line (stack_id, 0, 1); graphics_draw_line (stack_id, 7, 4); + graphics_draw_line (stack_id, 0, 2); graphics_draw_line (stack_id, 7, 5); + graphics_draw_line (stack_id, 0, 3); graphics_draw_line (stack_id, 7, 6); + + graphics_draw_line (stack_id, 1, 4); graphics_draw_line (stack_id, 2, 4); + graphics_draw_line (stack_id, 1, 5); graphics_draw_line (stack_id, 3, 5); + graphics_draw_line (stack_id, 2, 6); graphics_draw_line (stack_id, 3, 6); + + return 12; +} + +long draw_grids_on_space_faces_vertex (const int stack_id, + long x, + long y, + long z) +{ + float i, max = fmax(x, y); max = fmax(max, z); + + for (i = 1; i < x; i++) { + + graphics_draw_vertex (stack_id, (2 * i / x - 1) * x / max, - y / max, - z / max); + graphics_draw_vertex (stack_id, (2 * i / x - 1) * x / max, - y / max, z / max); + graphics_draw_vertex (stack_id, (2 * i / x - 1) * x / max, y / max, z / max); + graphics_draw_vertex (stack_id, (2 * i / x - 1) * x / max, y / max, - z / max); + + graphics_draw_color (stack_id, 0.55f, 0.55f, 0.55f); + graphics_draw_color (stack_id, 0.55f, 0.55f, 0.55f); + graphics_draw_color (stack_id, 0.55f, 0.55f, 0.55f); + graphics_draw_color (stack_id, 0.55f, 0.55f, 0.55f); + } + + /* offset_vertex += (x - 1) * 4; */ /* offset_colors += (x - 1) * 4; */ + + for (i = 1; i < y; i++) { + + graphics_draw_vertex (stack_id, - x / max, (2 * i / y - 1) * y / max, - z / max); + graphics_draw_vertex (stack_id, - x / max, (2 * i / y - 1) * y / max, z / max); + graphics_draw_vertex (stack_id, x / max, (2 * i / y - 1) * y / max, z / max); + graphics_draw_vertex (stack_id, x / max, (2 * i / y - 1) * y / max, - z / max); + + graphics_draw_color (stack_id, 0.55f, 0.55f, 0.55f); + graphics_draw_color (stack_id, 0.55f, 0.55f, 0.55f); + graphics_draw_color (stack_id, 0.55f, 0.55f, 0.55f); + graphics_draw_color (stack_id, 0.55f, 0.55f, 0.55f); + } + + /* offset_vertex += (y - 1) * 4; */ /* offset_colors += (y - 1) * 4; */ + + for (i = 1; i < z; i++) { + + graphics_draw_vertex (stack_id, - x / max, - y / max, (2 * i / z - 1) * z / max); + graphics_draw_vertex (stack_id, - x / max, y / max, (2 * i / z - 1) * z / max); + graphics_draw_vertex (stack_id, x / max, y / max, (2 * i / z - 1) * z / max); + graphics_draw_vertex (stack_id, x / max, - y / max, (2 * i / z - 1) * z / max); + + graphics_draw_color (stack_id, 0.55f, 0.55f, 0.55f); + graphics_draw_color (stack_id, 0.55f, 0.55f, 0.55f); + graphics_draw_color (stack_id, 0.55f, 0.55f, 0.55f); + graphics_draw_color (stack_id, 0.55f, 0.55f, 0.55f); + } + + return (x + y + z - 3) * 3; +} + + +long draw_grids_on_space_faces_lines (const int stack_id, + long offset_vertex, + long x, + long y, + long z) +{ + offset_vertex = offset_vertex / 3; + + for (int i = 0; i < x - 1; i ++) { + + graphics_draw_line (stack_id, offset_vertex + i * 4 + 1, offset_vertex + i * 4 + 2); + graphics_draw_line (stack_id, offset_vertex + i * 4 + 2, offset_vertex + i * 4 + 3); + } + + offset_vertex += (x - 1) * 4; + + for (int i = 0; i < y - 1; i ++) { + + graphics_draw_line (stack_id, offset_vertex + i * 4 + 2, offset_vertex + i * 4 + 3); + graphics_draw_line (stack_id, offset_vertex + i * 4 + 3, offset_vertex + i * 4 + 0); + } + + offset_vertex += (y - 1) * 4; + + for (int i = 0; i < z - 1; i ++) { + + graphics_draw_line (stack_id, offset_vertex + i * 4 + 0, offset_vertex + i * 4 + 1); + graphics_draw_line (stack_id, offset_vertex + i * 4 + 3, offset_vertex + i * 4 + 0); + } + + return (x + y + z - 3) * 4; +} + diff --git a/gg/init.c b/gg/init.c new file mode 100644 index 0000000..8928f98 --- /dev/null +++ b/gg/init.c @@ -0,0 +1,175 @@ +/* + * Gem-graph OpenGL experiments + * + * Desc: GL functions + * + * Copyright (C) 2023 Arthur Menges + * Copyright (C) 2023 Adrien Bourmault + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/* -------------------------------------------------------------------------- */ + +#include +#include +#include "../../include/base.h" +#include "../../include/ui.h" +#include "../../include/graphics.h" + +/* Initializes the buffer of a gl_area + * Calls according to the user preferences + * @param gl_area, ptr to the gl_area widget + * @return void + */ +void graphics_init_buffers(const int stack_id) +{ + struct graphic_stack_t *stack = &graphic_stack[stack_id]; + + //XXX + graphics_model_setup(stack_id); + + GLuint vao, vertex_buffer, color_buffer; + + glGenBuffers(1, &vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, + stack->buffer_vertex_size * + sizeof(stack->buffer_vertex_origin[0]), + stack->buffer_vertex_origin, + GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + + // colors + glGenBuffers(1, &color_buffer); + glBindBuffer(GL_ARRAY_BUFFER, color_buffer); + glBufferData(GL_ARRAY_BUFFER, stack->buffer_colors_size * + sizeof(stack->buffer_colors_origin[0]), + stack->buffer_colors_origin, + GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + // We only use one VAO, so we always keep it bound + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + stack->vao = vao; + stack->position_buffer = vertex_buffer; + stack->color_buffer = color_buffer; +} + +/* + * Initializes the shaders of a gl_area and link them to a program + * + * @param gl_area, ptr to the gl_area widget + * + * @return true if initialized + */ +bool graphics_init_shaders(const int stack_id) +{ + struct graphic_stack_t *stack = &graphic_stack[stack_id]; + char *vertex_shader; + char *fragment_shader; + int status; + GLuint vertex, fragment; + GLuint program = 0; + GLuint m = 0; + GLuint v = 0; + GLuint p = 0; + + // Load vertex shader file + vertex_shader = read_file(VERTEX_SHADER_FILE); + if (vertex_shader == NULL) + return false; + vertex = create_shader(stack_id, GL_VERTEX_SHADER, vertex_shader); + + if(vertex == 0) { + stack->program = 0; + g_free(vertex_shader); + return false; + } + + // Load fragment shader file + fragment_shader = read_file(FRAG_SHADER_FILE); + if (fragment_shader == NULL) + return false; + fragment = create_shader(stack_id, GL_FRAGMENT_SHADER, fragment_shader); + + if(fragment == 0) { + glDeleteShader(vertex); + stack->program = 0; + g_free(vertex_shader); + g_free(fragment_shader); + return false; + } + + // Link shaders to program + program = glCreateProgram(); + glAttachShader(program, vertex); + glAttachShader(program, fragment); + + glLinkProgram(program); + + glGetProgramiv(program, GL_LINK_STATUS, &status); + + if(status == GL_FALSE) { + int log_len; + char *buffer; + + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_len); + + buffer = g_malloc(log_len + 1); + assert(buffer); + glGetProgramInfoLog(program, log_len, NULL, buffer); + + g_warning("Linking failure:\n%s", buffer); + + g_free(buffer); + + glDeleteProgram(program); + program = 0; + + glDeleteShader(vertex); + glDeleteShader(fragment); + + g_free(vertex_shader); + g_free(fragment_shader); + + return false; + } + + /* Get the location of the "mvp" uniform */ + m = glGetUniformLocation(program, "model_matrix"); + v = glGetUniformLocation(program, "view_matrix"); + p = glGetUniformLocation(program, "projection_matrix"); + + glDetachShader(program, vertex); + glDetachShader(program, fragment); + + glDeleteShader(vertex); + glDeleteShader(fragment); + + stack->program = program; + stack->m = m; + stack->v = v; + stack->p = p; + + g_free(vertex_shader); + g_free(fragment_shader); + + return true; +} diff --git a/gg/parsing.c b/gg/parsing.c new file mode 100644 index 0000000..916fe85 --- /dev/null +++ b/gg/parsing.c @@ -0,0 +1,483 @@ +/* + * Gem-graph client + * + * Desc: Model parsing functions + * + * Copyright (C) 2023 Jean Sirmai + * Copyright (C) 2024 Adrien Bourmault + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include // http://xmlsoft.org/examples/#parse1.c + // https://gnome.pages.gitlab.gnome.org/libxml2/devhelp/general.html + +#include "../../include/base.h" + +#define READ_SITE 1 << 0 +#define READ_WEIGHT 1 << 1 +#define READ_X 1 << 2 +#define READ_Y 1 << 3 +#define READ_Z 1 << 4 +#define SUCCESSFUL_READ_ARROW_X (READ_SITE | READ_WEIGHT | READ_X) +#define SUCCESSFUL_READ_ARROW_XY (READ_SITE | READ_WEIGHT | READ_X | READ_Y) +#define SUCCESSFUL_READ_ARROW_XYZ (READ_SITE | READ_WEIGHT | READ_X | READ_Y | READ_Z) + +static xmlDocPtr model; +static xmlHashTablePtr model_hashtable; + +bool model_init(const char *content, size_t length, const char *basename) +{ + xmlNode *node; + + /* + * this initialize the library and check potential ABI mismatches + * between the version it was compiled for and the actual shared + * library used. + */ + LIBXML_TEST_VERSION + + model = xmlReadMemory(content, length, basename, NULL, 0); + + if (model == NULL ) { + return false; + } + + node = xmlDocGetRootElement(model); + + if (node == NULL) { + g_printerr("Empty XML model !\n"); + xmlFreeDoc(model); + return false; + } + + if (xmlStrcmp(node->name, (xmlChar *) "gem-graph-model")) { + g_printerr("document of the wrong type, root node != gem-graph-model\n"); + xmlFreeDoc(model); + return false; + } + + model_hashtable = xmlHashCreate(0); + + if (model_hashtable == NULL) { + g_printerr("Can't create model hash table !\n"); + xmlFreeDoc(model); + return false; + } + + return true; +} + +bool model_shutdown(void) +{ + xmlFreeDoc(model); + xmlHashFree(model_hashtable, NULL); + + // Cleanup function for the XML library + xmlCleanupParser(); + + // This is to debug memory for regression tests + xmlMemoryDump(); + + return true; +} + +/******************************************************************************/ + +static inline xmlNodePtr getNextChild(xmlNodePtr node, xmlChar *last) +{ + while (node != NULL && xmlStrcmp(node->name, last)) { + // //printf(" <>--- line n°%lu <%s>\n", xmlGetLineNo(node), node->name); + node = node->next; + } + return node; +} + +static inline xmlChar* splitStrAtSlash(xmlChar *toSplit) +{ + toSplit = (xmlChar *)xmlStrchr(toSplit, '/'); + toSplit = xmlStrsub (toSplit, 1, xmlStrlen(toSplit)); + return toSplit; +} + +static inline xmlChar* getFirstTag(xmlChar *path) +{ + xmlChar *preop = path; + path = (xmlChar *)xmlStrchr(path, '/'); + path = xmlStrsub (path, 1, xmlStrlen(path)); + + //printf("%s = %s + / + %s\n", preop,\ + xmlStrsub (preop, 0, xmlStrlen(preop) - xmlStrlen(path) - 1), path); + + return xmlStrsub (preop, 0, xmlStrlen(preop) - xmlStrlen(path) - 1); +} + +static inline xmlChar* getLastTag(xmlChar *path) +{ + while ((ulong)xmlStrchr (path, '/')) + path = splitStrAtSlash((xmlChar *)path); + + // //printf("last tag in the path = <%s>\n", path); + return path; // which is no more the given path but only its last tag ! +} + +/******************************************************************************/ + +static xmlNodePtr model_get_node(xmlChar *path) +{ + xmlNodePtr node; + xmlChar *extrait; + xmlChar *reste, *last, *affich; + + // Lookup for node from path in hash table + node = xmlHashLookup(model_hashtable, path); + + // Found a node in hash table + if (node) { + return node; + + // no node found in hash table + } else { + reste = path; + affich = reste; + last = getLastTag(reste); + node = xmlDocGetRootElement(model); + + while (xmlStrchr (reste, '/')) { + extrait = getFirstTag(reste); + reste = splitStrAtSlash((xmlChar *)reste); + node = node->xmlChildrenNode; + node = getNextChild(node, extrait); + } + + if(node && xmlStrcmp(node->name, last)) { + node = node->xmlChildrenNode; + + while (node && xmlStrcmp(node->name, last)) { + node = node->next; + } + xmlHashAddEntry (model_hashtable, path, node); + } + + return node; + + } + + return NULL; +} + +/******************************************************************************/ + +static inline long model_get_node_long_attrib(xmlNodePtr node, char *id) +{ + xmlAttr *attribute; + xmlChar* value; + long ret_value; + + if (node && node->properties) { + attribute = node->properties; + while(attribute && attribute->name && attribute->children) { + if (!xmlStrcmp(attribute->name, (const xmlChar *)id)) { + value = xmlNodeListGetString(node->doc, attribute->children, 1); + ret_value = strtol((char *)value, NULL, 0); + xmlFree(value); + return ret_value; + } + attribute = attribute->next; + } + } + return 0; +} + +static inline bool model_get_node_str_attrib(xmlNodePtr node, + char *id, + char *dest) +{ + xmlAttr *attribute; + xmlChar* value; + + if (node && node->properties) { + attribute = node->properties; + while(attribute && attribute->name && attribute->children) { + if (!xmlStrcmp(attribute->name, (const xmlChar *)id)) { + value = xmlNodeListGetString(node->doc, attribute->children, 1); + strcpy(dest, value); + xmlFree(value); + return true; + } + attribute = attribute->next; + } + } + return false; +} + +/******************************************************************************/ + +char model_get_dim(void) +{ + xmlAttr *attribute; + xmlChar* value; + xmlNodePtr node = model_get_node( + (xmlChar *)"parameters/space-param/dimension"); + + if (xmlHasProp (node, (xmlChar *) "z")) return 3; + if (xmlHasProp (node, (xmlChar *) "y")) return 2; + if (xmlHasProp (node, (xmlChar *) "x")) return 1; + return 0; +} + +long model_get_dim_value(const char *axis) +{ + xmlAttr *attribute; + xmlChar *value; + long ret_value; + xmlNodePtr node = model_get_node( + (xmlChar *)"parameters/space-param/dimension"); + + return model_get_node_long_attrib(node, axis); +} + +char model_get_multiplicity(void) +{ + xmlAttr *attribute; + xmlChar* value; + xmlNodePtr node = model_get_node( + (xmlChar *)"parameters/space-param/site_multiplicity"); + + if (node->children) + if (node->children->content) + return (char)strtol((char *)node->children->content, NULL, 0); + + return 0; +} + +bool model_get_next_state(char *new_state_id) +{ + static xmlNodePtr cur_node = NULL; + xmlAttr *attribute; + xmlChar *value; + + if (cur_node == NULL) { + // Get first state + cur_node = model_get_node((xmlChar *)"savedstates/state"); + + } else { + // Get next state + if (cur_node->next) + cur_node = cur_node->next; + else + return false; + } + + // Lookup in properties + if (model_get_node_str_attrib(cur_node, "id", new_state_id)) + return true; + + cur_node = NULL; + return false; +} + +long model_get_state_arrows_count(const char *state_id) +{ + xmlNodePtr cur_node = NULL; + xmlAttr *attribute; + long value = 0; + bool found = false; + char temp_char[25]; + uint check = 0; // bit field checker + + //printf("NEW CALL : cur_node = %p\n", cur_node); + + assert(state_id); + + // Get first state node + cur_node = model_get_node((xmlChar *)"savedstates/state"); + + // Lookup in properties + while (cur_node && cur_node->properties) { + attribute = cur_node->properties; + + // Look for the id attribute + if (model_get_node_str_attrib(cur_node, "id", &temp_char)) { + if (!xmlStrcmp(temp_char, (const xmlChar *)state_id)) { + found = true; + break; + } + } + cur_node = cur_node->next; + } + + // Check if the state has been found + if (!found) { + cur_node = NULL; + return -1; + } + + + // Count arrows + if (cur_node->children) { + cur_node = cur_node->children; + while (cur_node) { + if (!xmlStrcmp(cur_node->name, (const xmlChar *)"arrow")) + value++; + cur_node = cur_node->next; + } + } else { + return -1; + } + return value; +} + +bool model_get_next_arrow(struct arrow_t *new_arrow, + const char *state_id, + char dimension) +{ + static xmlNodePtr cur_node = NULL; + xmlAttr *attribute; + xmlChar *value; + bool found = false; + char temp_char[25]; + uint check = 0; // bit field checker + + //printf("NEW CALL : cur_node = %p\n", cur_node); + + assert(new_arrow); + assert(state_id); + + if (cur_node == NULL) { + // Get first state node + cur_node = model_get_node((xmlChar *)"savedstates/state"); + + // Lookup in properties + while (cur_node && cur_node->properties) { + attribute = cur_node->properties; + + // Look for the id attribute + if (model_get_node_str_attrib(cur_node, "id", &temp_char)) { + if (!xmlStrcmp(temp_char, (const xmlChar *)state_id)) { + found = true; + break; + } + } + cur_node = cur_node->next; + } + + // Check if the state has been found + if (!found) { + cur_node = NULL; + return false; + } + + // Get first arrow + if (cur_node->children) { + cur_node = cur_node->children; + + found = false; + while (cur_node && cur_node->name) { + if (!xmlStrcmp(cur_node->name, (const xmlChar *)"arrow")) { + found = true; + break; + } + cur_node = cur_node->next; + } + } + + // Check if the state has been found + if (!found) { + cur_node = NULL; + return false; + } + + } else { + // Get next arrow + found = false; + while (cur_node->next) { + cur_node = cur_node->next; + if (!xmlStrcmp(cur_node->name, (const xmlChar *)"arrow")) { + found = true; + break; + } + } + + // Check if the state has been found + if (!found) { + cur_node = NULL; + return false; + } + } + + //printf("DURING CALL : cur_node = %p\n", cur_node); + //printf("DURING CALL : cur_node->name = %s\n", cur_node->name); + + // Lookup in properties + if (cur_node && cur_node->properties) { + attribute = cur_node->properties; + + while(attribute && attribute->name && attribute->children) { + //printf("attr name : %s\n", attribute->name); + if (!xmlStrcmp(attribute->name, (const xmlChar *)"site")) { + value = xmlNodeListGetString(cur_node->doc, attribute->children, 1); + new_arrow->site = strtol((char *)value, NULL, 0); + xmlFree(value); + check |= READ_SITE; + } + if (!xmlStrcmp(attribute->name, (const xmlChar *)"weight")) { + value = xmlNodeListGetString(cur_node->doc, attribute->children, 1); + new_arrow->load = strtol((char *)value, NULL, 0); + xmlFree(value); + check |= READ_WEIGHT; + } + if (!xmlStrcmp(attribute->name, (const xmlChar *)"x")) { + value = xmlNodeListGetString(cur_node->doc, attribute->children, 1); + new_arrow->x = strtol((char *)value, NULL, 0); + xmlFree(value); + check |= READ_X; + } + if (!xmlStrcmp(attribute->name, (const xmlChar *)"y")) { + value = xmlNodeListGetString(cur_node->doc, attribute->children, 1); + new_arrow->y = strtol((char *)value, NULL, 0); + xmlFree(value); + check |= READ_Y; + } + if (!xmlStrcmp(attribute->name, (const xmlChar *)"z")) { + value = xmlNodeListGetString(cur_node->doc, attribute->children, 1); + new_arrow->z = strtol((char *)value, NULL, 0); + xmlFree(value); + check |= READ_Z; + } + attribute = attribute->next; + } + + switch(dimension) { + case 3: + return (bool)(check & SUCCESSFUL_READ_ARROW_XYZ); + case 2: + return (bool)(check & SUCCESSFUL_READ_ARROW_XY); + case 1: + return (bool)(check & SUCCESSFUL_READ_ARROW_X); + } + } + cur_node = NULL; + return false; +} diff --git a/gg/parsing.h b/gg/parsing.h new file mode 100644 index 0000000..8ac296e --- /dev/null +++ b/gg/parsing.h @@ -0,0 +1,39 @@ +/* + * Gem-graph OpenGL experiments + * + * Desc: Model parsing header + * + * Copyright (C) 2023 Arthur Menges + * Copyright (C) 2023 Adrien Bourmault + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +#include "../include/base.h" + +bool model_init(const char *content, size_t length, const char *basename); +bool model_shutdown(void); + +char model_get_dim(void); +long model_get_dim_value(const char *axis); +char model_get_multiplicity(void); +bool model_get_next_state(char *new_state_id); +bool model_get_next_arrow(struct arrow_t *new_arrow, + const char *state_id, + char dimension); diff --git a/gg/random_walk.xml b/gg/random_walk.xml new file mode 100644 index 0000000..12ae465 --- /dev/null +++ b/gg/random_walk.xml @@ -0,0 +1,118 @@ + + + + + Modèle de test + + Léontine Patinette + + 2 + + 1630000000 + + 1.0 + + Ref + + + + + 0 + 9 + + + + + + + + + + + + + + + + 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gg/ui.h b/gg/ui.h new file mode 100644 index 0000000..343198b --- /dev/null +++ b/gg/ui.h @@ -0,0 +1,232 @@ +/* + * Gem-graph OpenGL experiments + * + * Desc: User interface header + * + * Copyright (C) 2023 Arthur Menges + * Copyright (C) 2023 Adrien Bourmault + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "../include/base.h" + +G_BEGIN_DECLS + +#define GEM_GRAPH_CLIENT_TYPE_WINDOW (gem_graph_client_window_get_type()) + +G_DECLARE_FINAL_TYPE (GemGraphClientWindow, + gem_graph_client_window, + GEM_GRAPH_CLIENT, + WINDOW, + GtkApplicationWindow) + +G_END_DECLS + +G_BEGIN_DECLS + +#define GEM_GRAPH_CLIENT_TYPE_APPLICATION (gem_graph_client_application_get_type()) + +G_DECLARE_FINAL_TYPE (GemGraphClientApplication, + gem_graph_client_application, + GEM_GRAPH_CLIENT, + APPLICATION, + GtkApplication) + +GemGraphClientApplication *gem_graph_client_application_new(const char *application_id, + GApplicationFlags flags); + +G_END_DECLS + + +void ui_enable_action(const char *name); + +void ui_disable_action(const char *name); + +// +// Actions +// +void on_about_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data); + +void on_quit_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data); + +void on_preferences_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data); + +void on_togglesidebar_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data); + +void on_editmode_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data); + +void on_runmode_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data); + +void on_presentmode_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data); + +void on_openfile_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data); + +void on_closefile_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data); + +void on_savefile_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data); + +void on_toast_close_action(GSimpleAction *action, + GVariant *parameter, + gpointer user_data); + + +static const GActionEntry app_actions[] = { + { "quit", on_quit_action, NULL, NULL, NULL }, + { "about", on_about_action, NULL, NULL, NULL }, + { "preferences", on_preferences_action, NULL, NULL, NULL }, + { "togglesidebar", on_togglesidebar_action, NULL, NULL, NULL }, + { "editmode", on_editmode_action, NULL, NULL, NULL }, + { "runmode", on_runmode_action, NULL, NULL, NULL }, + { "presentmode", on_presentmode_action, NULL, NULL, NULL }, + { "openfile", on_openfile_action, NULL, NULL, NULL }, + { "closefile", on_closefile_action, NULL, NULL, NULL }, + { "savefile", on_savefile_action, NULL, NULL, NULL }, + { "toastclose", on_toast_close_action, NULL, NULL, NULL }, +}; + +// +// Actions responses +// +void on_openfile_response(GtkNativeDialog *native, + int response, + GemGraphClientWindow *self); + +void ui_model_loading(GObject *source_object, + GAsyncResult *result, + GemGraphClientWindow *self); + +// +// General events +// +void on_axis_value_change(GtkAdjustment *adjustment, gpointer data); + +gboolean on_glarea_render(GtkGLArea * area, GdkGLContext * context); + +void on_glarea_realize(GtkWidget *widget); + +void on_glarea_unrealize(GtkWidget *widget); + +void on_close_window(GtkWidget *widget); + +/* -------------------------------------------------------------------------- */ + +// +// Window primitives +// + +void ui_set_stack(int mode); + +void ui_send_internal_notification(const char *message); +void ui_close_internal_notification(void); +void ui_toggle_sidebar(); + +// +// Graphical stuff +// + +/* + * Creates GLArea and indexes it + * + * @params target_mode, meaning which ui_stack we're on + * target_widget, meaning the box that will host the GLArea + * + * @returns bool, true if success + */ +bool ui_setup_glarea(int target_mode, GtkWidget *target_widget); + + +/* + * Look for stack entry and returns stack_id + * + * @params container_widget, generally the GtkBox that contains the GLArea + * + * @returns stack_id + */ +long ui_get_graphic_stack(void *container_widget); + +/* + * Look for stack entry and initializes OpenGL for it + * + * @params container_widget, generally the GtkBox that contains the GLArea + * + * @returns bool, true if success + */ +bool ui_init_graphic_stack(void *container_widget, GError *error_buffer); + +/* + * Look for stack entry and shutdowns OpenGL for it + * + * @params container_widget, generally the GtkBox that contains the GLArea + * + * @returns bool, true if success + */ +bool ui_shutdown_graphic_stack(void *container_widget, GError *error_buffer); + + +void ui_clean_stack_index(void); + +/* + * Look for stack entry and triggers OpenGL for drawing + * + * @params container_widget, generally the GtkBox that contains the GLArea + * + * @returns bool, true if success + */ +bool ui_render_stack(GtkWidget *container_widget); + +/* + * Look for every stack entry and shutdowns OpenGL for it + * + * @params void + * + * @returns bool, true if success + */ +void ui_shutdown_all_graphic_stacks(void); + +/* + * Look for stack entry and triggers OpenGL for drawing + * + * @params container_widget, generally the GtkBox that contains the GLArea + * + * @returns bool, true if success + */ +bool ui_update_axis_stack(GtkWidget *container_widget, int axis, int value); + diff --git a/gg/window.c b/gg/window.c new file mode 100644 index 0000000..a7ed883 --- /dev/null +++ b/gg/window.c @@ -0,0 +1,236 @@ +/* + * Gem-graph OpenGL experiments + * + * Desc: User interface functions + * + * Copyright (C) 2023 Arthur Menges + * Copyright (C) 2023 Adrien Bourmault + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "../../include/base.h" +#include "../../include/ui.h" + +/* -------------------------------------------------------------------------- */ + +static GemGraphClientWindow *window; + +/* -------------------------------------------------------------------------- */ + +struct _GemGraphClientWindow +{ + GtkApplicationWindow parent_instance; + + /* Template widgets */ + GtkHeaderBar *main_titlebar; + GtkStack *main_stack; + GtkStack *side_stack; + GtkPaned *main_paned; + GtkMenuButton *main_button_mode; + GtkToggleButton *main_button_sidebar; + GtkRevealer *toast_revealer; + GtkToggleButton *toast_close_button; + GtkLabel *toast_text; + GtkBox *control_zone; + GtkBox *run_glarea_box; + GtkBox *edition_glarea_box; + GtkBox *presentation_glarea_box; +}; + +G_DEFINE_FINAL_TYPE (GemGraphClientWindow, + gem_graph_client_window, + GTK_TYPE_APPLICATION_WINDOW) + +static void gem_graph_client_window_class_init(GemGraphClientWindowClass *klass) +{ + gchar *contents; + gsize len; + GError *err; + GBytes *bytes; + + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + + if (g_file_get_contents("src/ui/gemgraph.ui", &contents, &len, &err) == FALSE) + g_error("error reading gemgraph.ui: %s", err->message); + + bytes = g_bytes_new_take(contents, len); + gtk_widget_class_set_template(GTK_WIDGET_CLASS(klass), bytes); + + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + main_titlebar); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + main_stack); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + side_stack); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + main_paned); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + main_button_mode); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + main_button_sidebar); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + toast_revealer); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + toast_close_button); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + toast_text); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + control_zone); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + run_glarea_box); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + edition_glarea_box); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + presentation_glarea_box); +} + +static void gem_graph_client_window_init(GemGraphClientWindow *self) +{ + gtk_widget_init_template(GTK_WIDGET(self)); + window = self; + + // Launch with sidebar off + ui_toggle_sidebar(); +} + +/* -------------------------------------------------------------------------- */ + +void ui_set_stack(int mode) +{ + //g_printerr("[debug] ui_set_stack()\n"); + + if (window->main_stack == NULL) { + g_printerr("Can't find self->main_stack !\n"); + return; + } + if (window->side_stack == NULL) { + g_printerr("Can't find self->side_stack !\n"); + return; + } + + // Switch on the first letter of the mode, because switch is soooo simple :) + switch(mode) { + case EDIT_MODE: + gtk_stack_set_visible_child_full(window->main_stack, + "edition", + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_stack_set_visible_child_full(window->side_stack, + "edition", + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_menu_button_set_icon_name(window->main_button_mode, + "document-edit-symbolic"); + ui_setup_glarea(EDIT_MODE, GTK_WIDGET(window->edition_glarea_box)); + break; + case RUN_MODE: + gtk_stack_set_visible_child_full(window->main_stack, + "run", + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_stack_set_visible_child_full(window->side_stack, + "run", + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_menu_button_set_icon_name(window->main_button_mode, + "system-run-symbolic"); + ui_setup_glarea(RUN_MODE, GTK_WIDGET(window->run_glarea_box)); + break; + case PRESENTATION_MODE: + gtk_stack_set_visible_child_full(window->main_stack, + "presentation", + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_stack_set_visible_child_full(window->side_stack, + "presentation", + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_menu_button_set_icon_name(window->main_button_mode, + "x-office-presentation-symbolic"); + ui_setup_glarea(PRESENTATION_MODE, + GTK_WIDGET(window->presentation_glarea_box)); + break; + case HOME_MODE: + gtk_stack_set_visible_child_full(window->main_stack, + "home", + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_stack_set_visible_child_full(window->side_stack, + "home", + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_paned_set_position(window->main_paned, 0); + gtk_menu_button_set_icon_name(window->main_button_mode, + "user-home-symbolic"); + break; + default: + break; + } +} + +void ui_send_internal_notification(const char *message) +{ + if (window->toast_revealer == NULL) { + g_printerr("Can't find self->toast_overlay !\n"); + return; + } + + if (window->toast_text == NULL) { + g_printerr("Can't find self->toast_overlay !\n"); + return; + } + + gtk_label_set_label(window->toast_text, message); + gtk_revealer_set_reveal_child(window->toast_revealer, true); + g_printerr("%s\n", message); +} + +void ui_close_internal_notification(void) +{ + if (window->toast_revealer == NULL) { + g_printerr("Can't find self->toast_overlay !\n"); + return; + } + + if (window->toast_text == NULL) { + g_printerr("Can't find self->toast_overlay !\n"); + return; + } + + gtk_revealer_set_reveal_child(window->toast_revealer, false); +} + + +void ui_toggle_sidebar(void) +{ + int position = gtk_paned_get_position(window->main_paned); + + if (position != 0) { + gtk_paned_set_position(window->main_paned, 0); + } else { + gtk_paned_set_position(window->main_paned, 400); + } +} diff --git a/hot.c b/hot.c index b815275..36b92d9 100644 --- a/hot.c +++ b/hot.c @@ -5,29 +5,33 @@ #include "cold.h" GtkWidget *get_space_page_new(){ - GtkWidget *space_grid = gtk_grid_new(); +// https://docs.gtk.org/gtk4/visual_index.html < widgets gallery GtkScrolledWindow *scrolled = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new()); gtk_scrolled_window_set_min_content_width (scrolled, 600); gtk_scrolled_window_set_min_content_height (scrolled, 600); - GtkAdjustment *width = gtk_adjustment_new (600, 300, 1000, 1, 1, 1000); - GtkAdjustment *height = gtk_adjustment_new (600, 300, 1000, 1, 1, 1000); +// GtkAdjustment *width = gtk_adjustment_new (600, 300, 1000, 1, 1, 1000); +// GtkAdjustment *height = gtk_adjustment_new (600, 300, 1000, 1, 1, 1000); // (value, lower, upper, step_increment, page_increment, page_size) - GtkWidget *viewport = gtk_viewport_new (width, height); - gtk_scrolled_window_set_child (scrolled, viewport); + GtkWidget *GLarea = gtk_gl_area_new(); + gtk_scrolled_window_set_child (scrolled, GLarea); +// https://docs.gtk.org/gtk4/class.GLArea.html - GtkBox *controls_box = GTK_BOX(gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0)); // spacing = 2 + GtkBox *controls_box = GTK_BOX(gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0)); gtk_box_append (controls_box, gtk_button_new_with_label ("RUN / STOP")); gtk_box_append (controls_box, gtk_button_new_with_label ("slow down / speed up")); gtk_box_append (controls_box, gtk_button_new_with_label ("step by step")); - gtk_box_append (controls_box, gtk_button_new_with_label ("buffer")); +// GtkLevelBar *buffer = GTK_LEVEL_BAR (gtk_level_bar_new ()); +// GtkWidget *buffer = gtk_level_bar_new_for_interval (0, 100000); +// gtk_level_bar_set_mode (buffer, GTK_LEVEL_BAR_MODE_CONTINUOUS); //_DISCRETE + gtk_box_append (controls_box, gtk_button_new_with_label ("---- buffer ----")); GtkBox *XYZ_box = GTK_BOX(gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2)); // spacing = 2 // GtkWidget *scale_X = gtk_scale_button_new (0, 360, 10, NULL); < à étudier // (double min, double max, double step, const char** icons) - GtkAdjustment *X_adjust = gtk_adjustment_new (0, 0, 380, 10, 0, 0); - GtkAdjustment *Y_adjust = gtk_adjustment_new (0, 0, 380, 10, 0, 0); - GtkAdjustment *Z_adjust = gtk_adjustment_new (0, 0, 380, 10, 0, 0); + GtkAdjustment *X_adjust = gtk_adjustment_new (0, 0, 380, 1, 0, 0); + GtkAdjustment *Y_adjust = gtk_adjustment_new (0, 0, 380, 1, 0, 0); + GtkAdjustment *Z_adjust = gtk_adjustment_new (0, 0, 380, 1, 0, 0); GtkWidget *scroll_X = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, X_adjust); GtkWidget *scroll_Y = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, Y_adjust); GtkWidget *scroll_Z = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, Z_adjust); @@ -36,12 +40,64 @@ GtkWidget *get_space_page_new(){ gtk_box_append (XYZ_box, scroll_Y); gtk_box_append (XYZ_box, scroll_Z); - gtk_grid_attach (GTK_GRID(space_grid), GTK_WIDGET(scrolled), 0, 0, 2, 1); - gtk_grid_attach (GTK_GRID(space_grid), GTK_WIDGET(controls_box), 0, 1, 1, 1); - gtk_grid_attach (GTK_GRID(space_grid), gtk_button_new_with_label ("Objects / Situations (transparences, styles)"), 0, 2, 1, 1); - gtk_grid_attach (GTK_GRID(space_grid), gtk_button_new_with_label ("zoom, +/- grid,\npresentation,\nstyles,..."), 1, 1, 1, 2); - gtk_grid_attach (GTK_GRID(space_grid), GTK_WIDGET(XYZ_box), 2, 0, 1, 3); + GtkWidget *bottom_grid = gtk_grid_new(); + gtk_grid_attach (GTK_GRID(bottom_grid), GTK_WIDGET(controls_box), 0, 0, 1, 1); + gtk_grid_attach (GTK_GRID(bottom_grid), gtk_button_new_with_label ("Objects / Situations (transparences, styles)"), 0, 1, 1, 1); + gtk_grid_attach (GTK_GRID(bottom_grid), gtk_button_new_with_label ("zoom, +/- grid,\npresentation,\nstyles,..."), 1, 0, 1, 2); + + GtkWidget *space_grid = gtk_grid_new(); + gtk_grid_attach (GTK_GRID(space_grid), GTK_WIDGET(scrolled), 0, 0, 1, 1); + gtk_grid_attach (GTK_GRID(space_grid), GTK_WIDGET(XYZ_box), 1, 0, 1, 1); + gtk_grid_attach (GTK_GRID(space_grid), GTK_WIDGET(bottom_grid), 0, 1, 2, 1); // ?! échec x_size +// gtk_grid_attach (GTK_GRID(space_grid), gtk_button_new_with_label ("?"), 1, 1, 1, 1); + return space_grid; } - +/* +gg/draw.c: * Draws the current buffer to a gl_area +gg/draw.c: * @param gl_area, ptr to the gl_area widget +gg/init.c: * Initializes the buffer of a gl_area +gg/init.c: * @param gl_area, ptr to the gl_area widget +gg/init.c: * Initializes the shaders of a gl_area and link them to a program +gg/init.c: * @param gl_area, ptr to the gl_area widget +gg/graphics.c: * @param gl_area, ptr to the gl_area widget +gg/graphics.c: * Shutdowns a gl_area +gg/graphics.c: * @param gl_area, ptr to the gl_area widget +gg/GtkGLArea.c: void *gl_area; +gg/GtkGLArea.c: gtk_widget_queue_draw((GtkWidget*)(stack_index[i].gl_area)); +gg/GtkGLArea.c: GtkWidget *gl_area; +gg/GtkGLArea.c: gl_area = GTK_WIDGET(gtk_gl_area_new()); +gg/GtkGLArea.c: assert(gl_area); +gg/GtkGLArea.c: //gtk_widget_set_size_request(gl_area, 1000, 1000); +gg/GtkGLArea.c: gtk_gl_area_set_auto_render(GTK_GL_AREA(gl_area), true); +gg/GtkGLArea.c: gtk_widget_set_hexpand(gl_area, TRUE); +gg/GtkGLArea.c: gtk_widget_set_vexpand(gl_area, TRUE); +gg/GtkGLArea.c: //gtk_widget_set_halign(gl_area, GTK_ALIGN_CENTER); +gg/GtkGLArea.c: //gtk_widget_set_valign(gl_area, GTK_ALIGN_CENTER); +gg/GtkGLArea.c: g_signal_connect(GTK_GL_AREA(gl_area), +gg/GtkGLArea.c: g_signal_connect(gl_area, +gg/GtkGLArea.c: g_signal_connect(gl_area, +gg/GtkGLArea.c: stack_index[stack_index_size-1].gl_area = (void*)gl_area; +gg/GtkGLArea.c: gtk_box_append(GTK_BOX(target_widget), gl_area); +gg/GtkGLArea.c: gtk_widget_show(GTK_WIDGET(gl_area)); +gg/events.c: if(gtk_gl_area_get_error(area) != NULL) { +gg/events.c: gtk_gl_area_make_current(GTK_GL_AREA(widget)); +gg/events.c: if(gtk_gl_area_get_error(GTK_GL_AREA(widget)) != NULL) { +gg/events.c: gtk_gl_area_set_auto_render(GTK_GL_AREA(widget), true); +gg/events.c: gtk_gl_area_make_current(GTK_GL_AREA(widget)); +gg/events.c: if(gtk_gl_area_get_error(GTK_GL_AREA(widget)) != NULL) { +gg/graphics.h: * Structure describing a gl_area and its parameters, used to create a table +gg/graphics.h: * of Gem-graph client current gl_areas +gg/graphics.h: * Dynamic array of ptrs to dynamically allocated gl_area_entry +gg/graphics.h: * Initializes a gl_area +gg/graphics.h: * @param gl_area, ptr to the gl_area widget +gg/graphics.h: * Draws the current buffer to a gl_area +gg/graphics.h: * @param gl_area, ptr to the gl_area widget +gg/graphics.h: * Shutdowns a gl_area +gg/graphics.h: * @param gl_area, ptr to the gl_area widget +gg/graphics.h: * Initializes the shaders of a gl_area and link them to a program +gg/graphics.h: * @param gl_area, ptr to the gl_area widget +gg/graphics.h: * Initializes the buffer of a gl_area +gg/graphics.h: * @param gl_area, ptr to the gl_area widget +*/ diff --git a/warm.c b/warm.c index 6913140..99ce544 100644 --- a/warm.c +++ b/warm.c @@ -47,7 +47,7 @@ GtkWidget *get_rules_page_new(){ gtk_paned_set_start_child (GTK_PANED(hpaned), GTK_WIDGET (frame1)); gtk_paned_set_end_child (GTK_PANED(hpaned), GTK_WIDGET (frame2)); gtk_widget_set_size_request (hpaned, 600, 600); - gtk_widget_set_size_request (frame1, 300, 0); // < utile seulement pour la largeur min/max + gtk_widget_set_size_request (frame1, 100, 0); // < utile seulement pour la largeur min/max // gtk_widget_set_size_request (frame2, 50, 100); // gtk_grid_attach (GTK_GRID (rules_grid), hpaned, 0, 0, 1, 1);