diff options
Diffstat (limited to 'src/gtkext/graph/ranks.c')
-rw-r--r-- | src/gtkext/graph/ranks.c | 463 |
1 files changed, 446 insertions, 17 deletions
diff --git a/src/gtkext/graph/ranks.c b/src/gtkext/graph/ranks.c index 6121907..65e40fd 100644 --- a/src/gtkext/graph/ranks.c +++ b/src/gtkext/graph/ranks.c @@ -32,16 +32,68 @@ +/* ---------------------- MANIPULATION DES PORTES HORIZONTALES ---------------------- */ + + +/* Etendue horizontale d'un lien */ +typedef struct _reserved_hspan +{ + gint x1; /* Départ d'une ligne */ + gint x2; /* Arrivée de cette même ligne */ + +} reserved_hspan; + +/* Lignes de liens d'un même niveau */ +typedef struct _links_spans +{ + reserved_hspan *hspans; /* Lignes sans chevauchement */ + size_t count; /* Nombre de ces lignes */ + +} links_spans; + + +/* Initialise les futures portées d'un même niveau. */ +static void init_links_spans(links_spans *); + +/* Regarde si des portées horizontales se chevauchent ou non. */ +static bool is_intersection_with_spans(const links_spans *, const reserved_hspan *); + +/* Ajoute une réservation à un ensemble de portées horizontales. */ +static void extend_links_spans(links_spans *, const reserved_hspan *); + + + +/* ------------------------ GESTION D'UN ETAGE DE CLASSEMENT ------------------------ */ + + /* Information relatives à un rang de classement */ typedef struct _rank_props { - gint top_margin; /* Espace supérieur pour liens */ - gint height; /* Hauteur minimale */ + links_spans *top_spans; /* Niveaux de lignes supérieurs*/ + hspan_slot_t top_count; /* Nbre des niveaux de lignes */ + links_spans *bottom_spans; /* Niveaux de lignes inférieurs*/ + hspan_slot_t bottom_count; /* Nbre des niveaux de lignes */ - gint final_y; /* Ordonnée finale */ + gint height; /* Hauteur minimale */ + gint y; /* Ordonnée finale */ } rank_props; + +/* Réserve une portion de hauteur pour un lien. */ +static hspan_slot_t reserve_span_in_rank_props(rank_props *, gint, gint, bool); + +/* Calcule la position verticale du rang de classement. */ +static gint compute_rank_props_y_pos(rank_props *, gint *, gint); + +/* Fournit la position verticale obtenue par réservation. */ +static gint get_y_for_rank_props_hspan_slot(const rank_props *, hspan_slot_t, bool); + + + +/* ------------------------- CLASSEMENT VERTICAL DES NOEUDS ------------------------- */ + + /* Calcul de l'ordonnée des différents classements (instance) */ struct _GGraphRanks { @@ -50,6 +102,8 @@ struct _GGraphRanks rank_props *props; /* Propriétés des rangs */ unsigned int count; /* Nombre de rangs présents */ + gint height; /* Hauteur totale du graphique */ + }; /* Calcul de l'ordonnée des différents classements (classe) */ @@ -74,6 +128,296 @@ static void g_graph_ranks_finalize(GGraphRanks *); +/* ---------------------------------------------------------------------------------- */ +/* MANIPULATION DES PORTES HORIZONTALES */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : spans = liste à initialiser. * +* * +* Description : Initialise les futures portées d'un même niveau. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void init_links_spans(links_spans *spans) +{ + spans->hspans = NULL; + spans->count = 0; + +} + + +/****************************************************************************** +* * +* Paramètres : spans = liste de portées déjà en place. * +* hspan = nouvelle portée à insérer quelque part. * +* * +* Description : Regarde si des portées horizontales se chevauchent ou non. * +* * +* Retour : true si les portées peuvent cohabiter, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool is_intersection_with_spans(const links_spans *spans, const reserved_hspan *hspan) +{ + bool result; /* Bilan d'analyse à retourner */ + size_t i; /* Boucle de parcours */ + + result = false; + + for (i = 0; i < spans->count && !result; i++) + { + if (hspan->x1 < spans->hspans[i].x1) + result = hspan->x2 >= spans->hspans[i].x1; + + else if (hspan->x1 > spans->hspans[i].x2) + result = hspan->x2 <= spans->hspans[i].x2; + + else + { + result = spans->hspans[i].x1 < hspan->x1 && hspan->x1 < spans->hspans[i].x2; + result |= spans->hspans[i].x1 < hspan->x2 && hspan->x2 < spans->hspans[i].x2; + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : spans = liste de portées déjà en place. * +* hspan = nouvelle portée à insérer quelque part. * +* * +* Description : Ajoute une réservation à un ensemble de portées horizontales.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void extend_links_spans(links_spans *spans, const reserved_hspan *new) +{ + spans->hspans = (reserved_hspan *)realloc(spans->hspans, + ++spans->count * sizeof(reserved_hspan)); + + if (new->x1 <= new->x2) + spans->hspans[spans->count - 1] = *new; + else + { + spans->hspans[spans->count - 1].x1 = new->x2; + spans->hspans[spans->count - 1].x2 = new->x1; + } + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* GESTION D'UN ETAGE DE CLASSEMENT */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : props = propriétés de rang à mettre à jour. * +* * +* Description : Supprime les portions de hauteur pour un lien. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void reset_spans_in_rank_props(rank_props *props) +{ + hspan_slot_t i; /* Boucle de parcours */ + + for (i = 0; i < props->top_count; i++) + if (props->top_spans[i].hspans != NULL) + free(props->top_spans[i].hspans); + + if (props->top_spans != NULL) + { + free(props->top_spans); + props->top_spans = NULL; + props->top_count = 0; + } + + for (i = 0; i < props->bottom_count; i++) + if (props->bottom_spans[i].hspans != NULL) + free(props->bottom_spans[i].hspans); + + if (props->bottom_spans != NULL) + { + free(props->bottom_spans); + props->bottom_spans = NULL; + props->bottom_count = 0; + } + + props->y = 0; + props->height = 0; + +} + + +/****************************************************************************** +* * +* Paramètres : props = propriétés de rang à mettre à jour. * +* x1 = début de la portée à réserver. * +* x2 = fin de la portée à réserver. * +* top = désignation de la zone à traiter. * +* * +* Description : Réserve une portion de hauteur pour un lien. * +* * +* Retour : Indice de la hauteur adaptée et réservée. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static hspan_slot_t reserve_span_in_rank_props(rank_props *props, gint x1, gint x2, bool top) +{ + hspan_slot_t result; /* Indice à retourner */ + reserved_hspan hspan; /* Portée à constituer */ + links_spans **list; /* Liste à parcourir */ + hspan_slot_t *count; /* Taille de cette liste */ + + hspan.x1 = x1; + hspan.x2 = x2; + + if (top) + { + list = &props->top_spans; + count = &props->top_count; + } + else + { + list = &props->bottom_spans; + count = &props->bottom_count; + } + + for (result = 0; result < *count; result++) + if (!is_intersection_with_spans(&(*list)[result], &hspan)) + break; + + if (result == *count) + { + *list = (links_spans *)realloc(*list, ++(*count) * sizeof(links_spans)); + init_links_spans(&(*list)[result]); + } + + extend_links_spans(&(*list)[result], &hspan); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : props = propriétés de rang à mettre à jour. * +* y = ordonnée courante à faire évoluer. * +* last_bottom = espace inférieur requis du niveau précédent. * +* * +* Description : Calcule la position verticale du rang de classement. * +* * +* Retour : Espace inférieur nécessaire au niveau de classement. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static gint compute_rank_props_y_pos(rank_props *props, gint *y, gint last_bottom) +{ + gint result; /* Espace Sud à retourner */ + gint top_margin; /* Espace Nord nécessaire */ + + /* Espace supérieur pour les liens */ + + top_margin = last_bottom; + + if (props->top_count > 0) + { + top_margin += props->top_count * EDGE_VERTICAL_MIN_SEP; + top_margin += EDGE_SLOT_VERT_MARGIN; + } + + /** + * L'espace vertical entre les noeuds n'a lieu d'être qu'en présence de noeuds ! + */ + top_margin = MAX(top_margin, (*y > 0 && props->height > 0 ? NODE_VERTICAL_MARGIN : 0)); + + /* Position des noeuds */ + + props->y = *y + top_margin; + *y = props->y + props->height; + + /* Espace inférieur pour les liens */ + + /** + * Fournit une marge minimum : si des liens autres que boucles partent de ce rang, + * il ne sont pas enregistrés à ce niveau, mais ont besoin d'espace quand même. + * On prend néanmoins garde aux rangs terminaux, potentiellement vides. + */ + result = (props->height > 0 ? EDGE_SLOT_VERT_MARGIN : 0); + + if (props->bottom_count > 0) + result += props->bottom_count * EDGE_VERTICAL_MIN_SEP; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : props = propriétés de rang à consulter. * +* slot = numérod de la réservation. * +* top = désignation de la zone à traiter. * +* * +* Description : Fournit la position verticale obtenue par réservation. * +* * +* Retour : Ordonnée à utiliser. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static gint get_y_for_rank_props_hspan_slot(const rank_props *props, hspan_slot_t slot, bool top) +{ + gint result; /* Ordonnée à retourner */ + + result = EDGE_SLOT_VERT_MARGIN + slot * EDGE_VERTICAL_MIN_SEP; + + if (top) + result = props->y - result; + else + result += props->y + props->height; + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CLASSEMENT VERTICAL DES NOEUDS */ +/* ---------------------------------------------------------------------------------- */ + + /* Indique le type définit par la GLib pour le calcul de l'ordonnée des classements. */ G_DEFINE_TYPE(GGraphRanks, g_graph_ranks, G_TYPE_OBJECT); @@ -176,19 +520,12 @@ static void g_graph_ranks_finalize(GGraphRanks *ranks) GGraphRanks *g_graph_ranks_new(unsigned int count) { GGraphRanks *result; /* Structure à retourner */ - size_t i; /* Boucle de parcours */ result = g_object_new(G_TYPE_GRAPH_RANKS, NULL); result->count = count; result->props = (rank_props *)calloc(count, sizeof(rank_props)); - for (i = 0; i < count; i++) - if (i == 0) - result->props[i].top_margin = GRAPH_TOP_MARGIN; - else - result->props[i].top_margin = NODE_TOP_MARGIN; - return result; } @@ -219,6 +556,72 @@ void g_graph_ranks_set_min_height(GGraphRanks *ranks, unsigned int index, gint h /****************************************************************************** * * +* Paramètres : ranks = ensemble des rangs de classement à consulter. * +* * +* Description : Fournit la hauteur requise pour l'affichage des rangs. * +* * +* Retour : Hauteur nécessaire en pixel. * +* * +* Remarques : - * +* * +******************************************************************************/ + +gint g_graph_ranks_get_height(const GGraphRanks *ranks) +{ + return ranks->height; + +} + + +/****************************************************************************** +* * +* Paramètres : ranks = ensemble des rangs à mettre à jour. * +* * +* Description : Supprime toutes les réservations faites pour les liens. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_graph_ranks_reset_reservations(GGraphRanks *ranks) +{ + unsigned int i; /* Boucle de parcours */ + + for (i = 0; i < ranks->count; i++) + reset_spans_in_rank_props(&ranks->props[i]); + +} + + +/****************************************************************************** +* * +* Paramètres : ranks = ensemble des rangs à mettre à jour. * +* index = indice du rang visé. * +* x1 = début de la portée à réserver. * +* x2 = fin de la portée à réserver. * +* top = désignation de la zone à traiter. * +* * +* Description : Réserve une portion de hauteur pour un lien. * +* * +* Retour : Indice de la hauteur adaptée et réservée. * +* * +* Remarques : - * +* * +******************************************************************************/ + +hspan_slot_t g_graph_ranks_reserve_span(GGraphRanks *ranks, unsigned int index, gint x1, gint x2, bool top) +{ + /*BUG_ON(index >= ranks->count) */ + + return reserve_span_in_rank_props(&ranks->props[index], x1, x2, top); + +} + + +/****************************************************************************** +* * * Paramètres : ranks = ensemble des rangs de classement à manipuler. * * * * Description : Calcule l'ordonnée finale de chaque rang du classement. * @@ -231,16 +634,18 @@ void g_graph_ranks_set_min_height(GGraphRanks *ranks, unsigned int index, gint h void g_graph_ranks_compute_y_line(GGraphRanks *ranks) { + gint last_bottom; /* Espace minimal à transmettre*/ + gint y; /* Ordonnée courante à proposer*/ size_t i; /* Boucle de parcours */ - for (i = 0; i < ranks->count; i++) - { - ranks->props[i].final_y = ranks->props[i].top_margin; + last_bottom = GRAPH_VERTICAL_MARGIN; + y = 0; - if (i > 0) - ranks->props[i].final_y += ranks->props[i - 1].final_y + ranks->props[i - 1].height; - } + for (i = 0; i < ranks->count; i++) + last_bottom = compute_rank_props_y_pos(&ranks->props[i], &y, last_bottom); + + ranks->height = y + last_bottom + GRAPH_VERTICAL_MARGIN; } @@ -262,6 +667,30 @@ gint g_graph_ranks_get_y_for_rank(const GGraphRanks *ranks, unsigned int index) { /*BUG_ON(index >= ranks->count) */ - return ranks->props[index].final_y; + return ranks->props[index].y; + +} + + +/****************************************************************************** +* * +* Paramètres : ranks = ensemble des rangs de classement à consulter. * +* index = indice du rang visé. * +* slot = numérod de la réservation. * +* top = désignation de la zone à traiter. * +* * +* Description : Fournit la position verticale obtenue par réservation. * +* * +* Retour : Ordonnée à utiliser. * +* * +* Remarques : - * +* * +******************************************************************************/ + +gint g_graph_ranks_get_y_for_hspan_slot(const GGraphRanks *ranks, unsigned int index, hspan_slot_t slot, bool top) +{ + /*BUG_ON(index >= ranks->count) */ + + return get_y_for_rank_props_hspan_slot(&ranks->props[index], slot, top); } |