labfy-investigation/src/models/investigation_graph_layout.c
grayTerminal-sh bf56b09422 Ajouter les relations déplaçables dans le graphe
Affiche les relations comme des nœuds indépendants et persistants.
  Généralise le stockage des positions aux entités et aux relations.
  Finalise la création de relation et le rafraîchissement du graphe.
2026-07-22 10:15:05 +02:00

321 lines
6.2 KiB
C

/******************************************************************************
* @file investigation_graph_layout.c
* @brief Implémentation du modèle en mémoire de disposition du graphe.
******************************************************************************/
#include "models/investigation_graph_layout.h"
#include <math.h>
/**
* @brief Coordonnées privées associées à un nœud.
*/
typedef struct
{
double x;
double y;
} InvestigationGraphLayoutPosition;
/**
* @struct InvestigationGraphLayout
* @brief Représentation privée de la disposition.
*/
struct InvestigationGraphLayout
{
GHashTable *positions_by_node_identifier;
};
/**
* @brief Enregistre une erreur littérale.
*/
static void investigation_graph_layout_set_error_literal(
GError **error,
InvestigationGraphLayoutError error_code,
const char *error_message
)
{
if (error == NULL)
{
return;
}
g_set_error_literal(
error,
INVESTIGATION_GRAPH_LAYOUT_ERROR,
error_code,
error_message
);
}
/**
* @brief Vérifie un identifiant de nœud.
*/
static gboolean investigation_graph_layout_identifier_is_valid(
const char *node_identifier
)
{
return node_identifier != NULL &&
node_identifier[0] != '\0' &&
g_uuid_string_is_valid(
node_identifier
);
}
GQuark investigation_graph_layout_error_quark(void)
{
return g_quark_from_static_string(
"investigation-graph-layout-error-quark"
);
}
InvestigationGraphLayout *investigation_graph_layout_new(void)
{
InvestigationGraphLayout *layout =
NULL;
layout =
g_try_new0(
InvestigationGraphLayout,
1
);
if (layout == NULL)
{
return NULL;
}
layout->positions_by_node_identifier =
g_hash_table_new_full(
g_str_hash,
g_str_equal,
g_free,
g_free
);
if (layout->positions_by_node_identifier == NULL)
{
investigation_graph_layout_free(
layout
);
return NULL;
}
return layout;
}
gboolean investigation_graph_layout_set_position(
InvestigationGraphLayout *layout,
const char *node_identifier,
double x,
double y,
GError **error
)
{
InvestigationGraphLayoutPosition *position =
NULL;
char *node_identifier_copy =
NULL;
g_return_val_if_fail(
error == NULL || *error == NULL,
FALSE
);
if (layout == NULL ||
layout->positions_by_node_identifier == NULL)
{
investigation_graph_layout_set_error_literal(
error,
INVESTIGATION_GRAPH_LAYOUT_ERROR_INVALID_ARGUMENT,
"La disposition du graphe est absente."
);
return FALSE;
}
if (!investigation_graph_layout_identifier_is_valid(
node_identifier
))
{
investigation_graph_layout_set_error_literal(
error,
INVESTIGATION_GRAPH_LAYOUT_ERROR_INVALID_IDENTIFIER,
"L'identifiant du nœud n'est pas un UUID valide."
);
return FALSE;
}
if (!isfinite(x) ||
!isfinite(y))
{
investigation_graph_layout_set_error_literal(
error,
INVESTIGATION_GRAPH_LAYOUT_ERROR_INVALID_COORDINATE,
"Les coordonnées du nœud doivent être des nombres finis."
);
return FALSE;
}
node_identifier_copy =
g_strdup(
node_identifier
);
position =
g_try_new0(
InvestigationGraphLayoutPosition,
1
);
if (node_identifier_copy == NULL ||
position == NULL)
{
g_free(
position
);
g_free(
node_identifier_copy
);
investigation_graph_layout_set_error_literal(
error,
INVESTIGATION_GRAPH_LAYOUT_ERROR_MEMORY,
"Impossible d'allouer la position du nœud."
);
return FALSE;
}
position->x =
x;
position->y =
y;
g_hash_table_replace(
layout->positions_by_node_identifier,
node_identifier_copy,
position
);
return TRUE;
}
gboolean investigation_graph_layout_get_position(
const InvestigationGraphLayout *layout,
const char *node_identifier,
double *x,
double *y
)
{
const InvestigationGraphLayoutPosition *position =
NULL;
if (layout == NULL ||
layout->positions_by_node_identifier == NULL ||
!investigation_graph_layout_identifier_is_valid(
node_identifier
))
{
return FALSE;
}
position =
g_hash_table_lookup(
layout->positions_by_node_identifier,
node_identifier
);
if (position == NULL)
{
return FALSE;
}
if (x != NULL)
{
*x =
position->x;
}
if (y != NULL)
{
*y =
position->y;
}
return TRUE;
}
gboolean investigation_graph_layout_remove_position(
InvestigationGraphLayout *layout,
const char *node_identifier
)
{
if (layout == NULL ||
layout->positions_by_node_identifier == NULL ||
!investigation_graph_layout_identifier_is_valid(
node_identifier
))
{
return FALSE;
}
return g_hash_table_remove(
layout->positions_by_node_identifier,
node_identifier
);
}
void investigation_graph_layout_clear(
InvestigationGraphLayout *layout
)
{
if (layout == NULL ||
layout->positions_by_node_identifier == NULL)
{
return;
}
g_hash_table_remove_all(
layout->positions_by_node_identifier
);
}
guint investigation_graph_layout_get_count(
const InvestigationGraphLayout *layout
)
{
if (layout == NULL ||
layout->positions_by_node_identifier == NULL)
{
return 0U;
}
return g_hash_table_size(
layout->positions_by_node_identifier
);
}
void investigation_graph_layout_free(
InvestigationGraphLayout *layout
)
{
if (layout == NULL)
{
return;
}
g_clear_pointer(
&layout->positions_by_node_identifier,
g_hash_table_unref
);
g_free(
layout
);
}