/****************************************************************************** * @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 /** * @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 ); }