GtkLevelBar < ?

This commit is contained in:
Jean Sirmai 2024-04-30 18:59:44 +02:00
parent feb8fd2ecf
commit 39cf432f6a
Signed by: jean
GPG Key ID: FB3115C340E057E3
22 changed files with 4294 additions and 17 deletions

150
0_application.c Normal file
View File

@ -0,0 +1,150 @@
/*
* Gem-graph OpenGL experiments
*
* Desc: User interface functions
*
* Copyright (C) 2023 Arthur Menges <arthur.menges@a-lec.org>
* Copyright (C) 2023 Adrien Bourmault <neox@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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 *[]) { "<primary>q", NULL });
gtk_application_set_accels_for_action(GTK_APPLICATION(self),
"app.editmode",
(const char *[]) { "<primary>e", NULL });
gtk_application_set_accels_for_action(GTK_APPLICATION(self),
"app.runmode",
(const char *[]) { "<primary>r", NULL });
gtk_application_set_accels_for_action(GTK_APPLICATION(self),
"app.presentmode",
(const char *[]) { "<primary>p", NULL });
gtk_application_set_accels_for_action(GTK_APPLICATION(self),
"app.savefile",
(const char *[]) { "<primary>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);
}

236
0_window.c Normal file
View File

@ -0,0 +1,236 @@
/*
* Gem-graph OpenGL experiments
*
* Desc: User interface functions
*
* Copyright (C) 2023 Arthur Menges <arthur.menges@a-lec.org>
* Copyright (C) 2023 Adrien Bourmault <neox@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <unistd.h>
#include <gtk-4.0/gtk/gtk.h>
#include <glib-2.0/glib.h>
#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);
}
}

BIN
2024-04-30.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

BIN
exec.o

Binary file not shown.

323
gg/GtkGLArea.c Normal file
View File

@ -0,0 +1,323 @@
/*
* Gem-graph OpenGL experiments
*
* Desc: User interface functions
*
* Copyright (C) 2023 Adrien Bourmault <neox@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <unistd.h>
#include <gtk-4.0/gtk/gtk.h>
#include <glib-2.0/glib.h>
#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;
}

150
gg/application.c Normal file
View File

@ -0,0 +1,150 @@
/*
* Gem-graph OpenGL experiments
*
* Desc: User interface functions
*
* Copyright (C) 2023 Arthur Menges <arthur.menges@a-lec.org>
* Copyright (C) 2023 Adrien Bourmault <neox@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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 *[]) { "<primary>q", NULL });
gtk_application_set_accels_for_action(GTK_APPLICATION(self),
"app.editmode",
(const char *[]) { "<primary>e", NULL });
gtk_application_set_accels_for_action(GTK_APPLICATION(self),
"app.runmode",
(const char *[]) { "<primary>r", NULL });
gtk_application_set_accels_for_action(GTK_APPLICATION(self),
"app.presentmode",
(const char *[]) { "<primary>p", NULL });
gtk_application_set_accels_for_action(GTK_APPLICATION(self),
"app.savefile",
(const char *[]) { "<primary>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);
}

285
gg/arrows.c Normal file
View File

@ -0,0 +1,285 @@
/*
* Gem-graph OpenGL experiments
*
* Desc: OpenGL utils header
*
* Copyright (C) 2023 Jean Sirmai <jean@a-lec.org>
* Copyright (C) 2023 Arien Bourmault <neox@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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;
}

119
gg/base.h Normal file
View File

@ -0,0 +1,119 @@
/*
* Gem-graph OpenGL experiments
*
* Desc: Base header
*
* Copyright (C) 2023 Arthur Menges <arthur.menges@a-lec.org>
* Copyright (C) 2023 Adrien Bourmault <neox@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <glib-2.0/glib.h>
//#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

295
gg/displays.c Normal file
View File

@ -0,0 +1,295 @@
/*
* Gem-graph OpenGL experiments
*
* Desc: GL functions
*
* Copyright (C) 2023 Adrien Bourmault <neox@a-lec.org>
* Copyright (C) 2023 Jean Sirmai <jean@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <time.h>
#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();
}

207
gg/draw.c Normal file
View File

@ -0,0 +1,207 @@
/*
* Gem-graph OpenGL experiments
*
* Desc: GL functions
*
* Copyright (C) 2023 Adrien Bourmault <neox@a-lec.org>
* Copyright (C) 2023 Jean Sirmai <jean@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
/*
* Writes values to describe a vertex at (x,y,z) intoq the vertex buffer
*
* @param coords GLfloat(x,y,z)
*
* @return void
*/
#include <stdlib.h>
#include <time.h>
#include <cglm/cglm.h>
#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");
}

369
gg/events.c Normal file
View File

@ -0,0 +1,369 @@
/*
* Gem-graph OpenGL experiments
*
* Desc: User interface functions
*
* Copyright (C) 2023 Arthur Menges <arthur.menges@a-lec.org>
* Copyright (C) 2023 Adrien Bourmault <neox@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#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);
}

328
gg/graphics.c Normal file
View File

@ -0,0 +1,328 @@
/*
* Gem-graph OpenGL experiments
*
* Desc: GL functions
*
* Copyright (C) 2023 Arthur Menges <arthur.menges@a-lec.org>
* Copyright (C) 2023 Adrien Bourmault <neox@a-lec.org>
* Copyright (C) 2023 Jean Sirmai <jean@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <time.h>
#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");
}

314
gg/graphics.h Normal file
View File

@ -0,0 +1,314 @@
/*
* Gem-graph OpenGL experiments
*
* Desc: OpenGL utils header
*
* Copyright (C) 2023 Arthur Menges <arthur.menges@a-lec.org>
* Copyright (C) 2023 Adrien Bourmault <neox@a-lec.org>
* Copyright (C) 2023 Jean Sirmai <jean@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "base.h"
#include <unistd.h>
#include <stdbool.h>
#include <epoxy/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
#include <cglm/cglm.h>
#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);

162
gg/grid.c Normal file
View File

@ -0,0 +1,162 @@
/*
* Gem-graph
*
* Desc: OpenGL grid functions
*
* Copyright (C) 2023 Jean Sirmai <jean@a-lec.org>
* Copyright (C) 2023 Adrien Bourmault <neox@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <time.h>
#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;
}

175
gg/init.c Normal file
View File

@ -0,0 +1,175 @@
/*
* Gem-graph OpenGL experiments
*
* Desc: GL functions
*
* Copyright (C) 2023 Arthur Menges <arthur.menges@a-lec.org>
* Copyright (C) 2023 Adrien Bourmault <neox@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
/* -------------------------------------------------------------------------- */
#include <stdlib.h>
#include <time.h>
#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;
}

483
gg/parsing.c Normal file
View File

@ -0,0 +1,483 @@
/*
* Gem-graph client
*
* Desc: Model parsing functions
*
* Copyright (C) 2023 Jean Sirmai <jean@a-lec.org>
* Copyright (C) 2024 Adrien Bourmault <neox@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <limits.h>
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/xmlreader.h> // 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;
}

39
gg/parsing.h Normal file
View File

@ -0,0 +1,39 @@
/*
* Gem-graph OpenGL experiments
*
* Desc: Model parsing header
*
* Copyright (C) 2023 Arthur Menges <arthur.menges@a-lec.org>
* Copyright (C) 2023 Adrien Bourmault <neox@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <unistd.h>
#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);

118
gg/random_walk.xml Normal file
View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<gem-graph-model version="0.2.1">
<identity>
<name>Modèle de test</name>
<owner>Léontine Patinette</owner>
<owner_id>2</owner_id>
<date>1630000000</date>
<version>1.0</version>
<g_ref id="Cause" date="1630000000" author="toujours" lang="en">Ref</g_ref>
</identity>
<parameters id="texte" date="0" author="Léontine Trottine.">
<simulation>
<max_thread>0</max_thread>
<max_cycles>9</max_cycles>
</simulation>
<space-param>
<!-- loop_on_zero="true" anything moved before zero reenters at the end of space-->
<!-- loop_on_zero="false" anything moved before zero is lost-->
<!-- loop_on_max="true" anything moved beyond max reenters at the zero of space-->
<!-- loop_on_max="false" anything moved beyond max is lost-->
<dimension x="29"/>
<!-- Site_multiplicity = number of sites in a space unit. -->
<!-- Each site points towards a neighbouring space unit. -->
<!-- Several arrows can be stacked in the same site. -->
<site_multiplicity>3</site_multiplicity>
</space-param>
</parameters>
<!-- Model objects definition -->
<objects id="only dimers in this model" date="1630000000" author="inanimés,... âme ?">
<object id="a dimer" date="1630000000" author="Alphonse">
<arrow site="1" weight="1" x="0"/>
<arrow site="2" weight="1" x="1"/>
</object>
</objects>
<!-- Saved space description (initial space is sequence 0) -->
<savedstates id="texte" date="1630000000" author="Qui courrait dans l'herbe'">
<state id="initial" date="1630000000" author="Une souris verte">
<arrow site="0" weight="1" x="0"/>
<arrow site="1" weight="1" x="1"/>
<arrow site="0" weight="1" x="10"/>
<arrow site="1" weight="1" x="11"/>
<arrow site="0" weight="1" x="20"/>
<arrow site="1" weight="1" x="21"/>
<!-- Three dimers 1-1 are drawn in the global space
at locations (0,1) (10,11) and (20,21) -->
</state>
</savedstates>
<!-- Model transitions definition (rules) -->
<!-- In this version : <xs:sequence minOccurs="0" maxOccurs="unbounded">-->
<!-- Transitions should be edited by hand and written in a 'human-readable' format -->
<conditions id="random walk of dimers" date="1630000000" author="Zazard le lézard !">
<condition site="1" weight="1" node_id="1" parent="0" x="0"/>
<condition site="2" weight="1" node_id="2" parent="1" x="1"/>
<!-- as soon as conditions 1 and 2 are satisfied, a dimer is identified at atation (0,1). -->
<condition site="1" weight="0" node_id="3" parent="2" x="2"/>
<!-- as soon as condition 3 is satisfied,
the neighbouring space unit to East of the dimer is empty
and the dimer identified by conditions (1,2) can be moved to East. -->
<condition site="2" weight="0" node_id="4" parent="2" x="-1"/>
<!-- as soon as condition 4 is satisfied,
the neighbouring space unit to West of the dimer is empty
and the dimer identified by conditions (1,2) can be moved to West. -->
</conditions>
<transitions>
<transition id="move_a_dimer_to_east" date="1630000000" author="Pas moi..."
parent="3" probability="1">
<arrow site="1" weight="0" x="0"/>
<arrow site="2" weight="0" x="1"/>
<arrow site="1" weight="1" x="1"/>
<arrow site="2" weight="1" x="2"/>
</transition>
<transition id="move_a_dimer_to_west" date="1630000000" author="Ni moi !"
parent="4" probability="1">
<arrow site="1" weight="0" x="0"/>
<arrow site="2" weight="0" x="1"/>
<arrow site="2" weight="1" x="0"/>
<arrow site="1" weight="1" x="1"/>
</transition>
</transitions>
</gem-graph-model>

232
gg/ui.h Normal file
View File

@ -0,0 +1,232 @@
/*
* Gem-graph OpenGL experiments
*
* Desc: User interface header
*
* Copyright (C) 2023 Arthur Menges <arthur.menges@a-lec.org>
* Copyright (C) 2023 Adrien Bourmault <neox@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <unistd.h>
#include <gtk-4.0/gtk/gtk.h>
#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);

236
gg/window.c Normal file
View File

@ -0,0 +1,236 @@
/*
* Gem-graph OpenGL experiments
*
* Desc: User interface functions
*
* Copyright (C) 2023 Arthur Menges <arthur.menges@a-lec.org>
* Copyright (C) 2023 Adrien Bourmault <neox@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <unistd.h>
#include <gtk-4.0/gtk/gtk.h>
#include <glib-2.0/glib.h>
#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);
}
}

88
hot.c
View File

@ -5,29 +5,33 @@
#include "cold.h" #include "cold.h"
GtkWidget *get_space_page_new(){ 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()); GtkScrolledWindow *scrolled = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new());
gtk_scrolled_window_set_min_content_width (scrolled, 600); gtk_scrolled_window_set_min_content_width (scrolled, 600);
gtk_scrolled_window_set_min_content_height (scrolled, 600); gtk_scrolled_window_set_min_content_height (scrolled, 600);
GtkAdjustment *width = 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); // GtkAdjustment *height = gtk_adjustment_new (600, 300, 1000, 1, 1, 1000);
// (value, lower, upper, step_increment, page_increment, page_size) // (value, lower, upper, step_increment, page_increment, page_size)
GtkWidget *viewport = gtk_viewport_new (width, height); GtkWidget *GLarea = gtk_gl_area_new();
gtk_scrolled_window_set_child (scrolled, viewport); 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 ("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 ("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 ("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 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 // GtkWidget *scale_X = gtk_scale_button_new (0, 360, 10, NULL); < à étudier
// (double min, double max, double step, const char** icons) // (double min, double max, double step, const char** icons)
GtkAdjustment *X_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, 10, 0, 0); GtkAdjustment *Y_adjust = gtk_adjustment_new (0, 0, 380, 1, 0, 0);
GtkAdjustment *Z_adjust = gtk_adjustment_new (0, 0, 380, 10, 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_X = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, X_adjust);
GtkWidget *scroll_Y = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, Y_adjust); GtkWidget *scroll_Y = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, Y_adjust);
GtkWidget *scroll_Z = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, Z_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_Y);
gtk_box_append (XYZ_box, scroll_Z); gtk_box_append (XYZ_box, scroll_Z);
gtk_grid_attach (GTK_GRID(space_grid), GTK_WIDGET(scrolled), 0, 0, 2, 1); GtkWidget *bottom_grid = gtk_grid_new();
gtk_grid_attach (GTK_GRID(space_grid), GTK_WIDGET(controls_box), 0, 1, 1, 1); gtk_grid_attach (GTK_GRID(bottom_grid), GTK_WIDGET(controls_box), 0, 0, 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(bottom_grid), gtk_button_new_with_label ("Objects / Situations (transparences, styles)"), 0, 1, 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(bottom_grid), gtk_button_new_with_label ("zoom, +/- grid,\npresentation,\nstyles,..."), 1, 0, 1, 2);
gtk_grid_attach (GTK_GRID(space_grid), GTK_WIDGET(XYZ_box), 2, 0, 1, 3);
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; 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
*/

2
warm.c
View File

@ -47,7 +47,7 @@ GtkWidget *get_rules_page_new(){
gtk_paned_set_start_child (GTK_PANED(hpaned), GTK_WIDGET (frame1)); gtk_paned_set_start_child (GTK_PANED(hpaned), GTK_WIDGET (frame1));
gtk_paned_set_end_child (GTK_PANED(hpaned), GTK_WIDGET (frame2)); gtk_paned_set_end_child (GTK_PANED(hpaned), GTK_WIDGET (frame2));
gtk_widget_set_size_request (hpaned, 600, 600); 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_widget_set_size_request (frame2, 50, 100);
// gtk_grid_attach (GTK_GRID (rules_grid), hpaned, 0, 0, 1, 1); // gtk_grid_attach (GTK_GRID (rules_grid), hpaned, 0, 0, 1, 1);