/* Chrysalide - Outil d'analyse de fichiers binaires
* ranks.c - calcul de l'ordonnée des différents classements
*
* Copyright (C) 2013 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* OpenIDA is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* OpenIDA 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar. If not, see .
*/
#include "ranks.h"
#include
#include
#include "params.h"
/* ---------------------- 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
{
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 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
{
GObject parent; /* A laisser en premier */
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) */
struct _GGraphRanksClass
{
GObjectClass parent; /* A laisser en premier */
};
/* Initialise la classe de calcul de l'ordonnée des classements. */
static void g_graph_ranks_class_init(GGraphRanksClass *);
/* Initialise un calcul de l'ordonnée des classements. */
static void g_graph_ranks_init(GGraphRanks *);
/* Supprime toutes les références externes. */
static void g_graph_ranks_dispose(GGraphRanks *);
/* Procède à la libération totale de la mémoire. */
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);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe de calcul de l'ordonnée des classements.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_ranks_class_init(GGraphRanksClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_graph_ranks_dispose;
object->finalize = (GObjectFinalizeFunc)g_graph_ranks_finalize;
}
/******************************************************************************
* *
* Paramètres : ranks = instance à initialiser. *
* *
* Description : Initialise un calcul de l'ordonnée des classements. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_ranks_init(GGraphRanks *ranks)
{
}
/******************************************************************************
* *
* Paramètres : node = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_ranks_dispose(GGraphRanks *ranks)
{
G_OBJECT_CLASS(g_graph_ranks_parent_class)->dispose(G_OBJECT(ranks));
}
/******************************************************************************
* *
* Paramètres : ranks = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_ranks_finalize(GGraphRanks *ranks)
{
if (ranks->props != NULL)
free(ranks->props);
G_OBJECT_CLASS(g_graph_ranks_parent_class)->finalize(G_OBJECT(ranks));
}
/******************************************************************************
* *
* Paramètres : count = nombre de rangs dans le classement au total. *
* *
* Description : Prépare de quoi se définir au mieux les ordonnées des noeuds.*
* *
* Retour : Adresse de la structure mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
GGraphRanks *g_graph_ranks_new(unsigned int count)
{
GGraphRanks *result; /* Structure à retourner */
result = g_object_new(G_TYPE_GRAPH_RANKS, NULL);
result->count = count;
result->props = (rank_props *)calloc(count, sizeof(rank_props));
return result;
}
/******************************************************************************
* *
* Paramètres : ranks = ensemble des rangs de classement à manipuler. *
* index = indice du rang visé. *
* height = hauteur minimale requise pour le rang donné. *
* *
* Description : Note une hauteur minimale requise pour un rang du classement.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_graph_ranks_set_min_height(GGraphRanks *ranks, unsigned int index, gint height)
{
/*BUG_ON(index >= ranks->count) */
ranks->props[index].height = MAX(ranks->props[index].height, height);
}
/******************************************************************************
* *
* 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. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
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 */
last_bottom = GRAPH_VERTICAL_MARGIN;
y = 0;
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;
}
/******************************************************************************
* *
* Paramètres : ranks = ensemble des rangs de classement à consulter. *
* index = indice du rang visé. *
* *
* Description : Fournit la position verticale correspondant à un rang donné. *
* *
* Retour : Ordonnée à utiliser. *
* *
* Remarques : - *
* *
******************************************************************************/
gint g_graph_ranks_get_y_for_rank(const GGraphRanks *ranks, unsigned int index)
{
/*BUG_ON(index >= ranks->count) */
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);
}