/* Chrysalide - Outil d'analyse de fichiers binaires
* edge.c - liens entre les noeuds d'un graphique
*
* Copyright (C) 2013-2014 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 "edge.h"
#include
#include "nodes/virtual.h"
/* Lien graphique entre deux noeuds graphiques (instance) */
struct _GGraphEdge
{
GObject parent; /* A laisser en premier */
GFlowNode *src_node; /* Bloc de départ du lien */
node_slot_t *src_slot; /* Numéro d'ancrage initial */
GFlowNode *dest_node; /* Bloc d'arrivée du lien */
node_slot_t *dest_slot; /* Numéro d'ancrage final */
hspan_slot_t top_step; /* Niveau sup. de destination */
GVirtualNode *container; /* Conteneur pour les tours */
bool is_left; /* Tour par la gauche ? */
vspan_slot_t vert_step; /* Position de contournement */
hspan_slot_t bottom_step; /* Niveau inf. de source */
EdgeColor color; /* Couleur du rendu */
GdkPoint *points; /* Points de la ligne dessinée */
size_t count; /* Quantité de ces points */
};
/* Lien graphique entre deux noeuds graphiques (classe) */
struct _GGraphEdgeClass
{
GObjectClass parent; /* A laisser en premier */
};
/* Initialise la classe des liens graphiques entre deux noeuds. */
static void g_graph_edge_class_init(GGraphEdgeClass *);
/* Initialise une encapsulation de bloc virtuel. */
static void g_graph_edge_init(GGraphEdge *);
/* Supprime toutes les références externes. */
static void g_graph_edge_dispose(GGraphEdge *);
/* Procède à la libération totale de la mémoire. */
static void g_graph_edge_finalize(GGraphEdge *);
/* Dessine une flèche au bout du lien représenté. */
static void draw_link_arrow(cairo_t *, gint, gint, gint, gint);
/* Indique le type définit par la GLib pour les liens graphiques entre noeuds. */
G_DEFINE_TYPE(GGraphEdge, g_graph_edge, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des liens graphiques entre deux noeuds. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_edge_class_init(GGraphEdgeClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_graph_edge_dispose;
object->finalize = (GObjectFinalizeFunc)g_graph_edge_finalize;
}
/******************************************************************************
* *
* Paramètres : edge = instance à initialiser. *
* *
* Description : Initialise un lien graphique entre deux noeuds graphiques. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_edge_init(GGraphEdge *edge)
{
edge->top_step = UNINITIALIZED_HSPAN_SLOT;
edge->bottom_step = UNINITIALIZED_HSPAN_SLOT;
}
/******************************************************************************
* *
* Paramètres : edge = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_edge_dispose(GGraphEdge *edge)
{
g_object_unref(G_OBJECT(edge->src_node));
g_object_unref(G_OBJECT(edge->dest_node));
if (edge->container != NULL)
g_object_unref(G_OBJECT(edge->container));
G_OBJECT_CLASS(g_graph_edge_parent_class)->dispose(G_OBJECT(edge));
}
/******************************************************************************
* *
* Paramètres : edge = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_edge_finalize(GGraphEdge *edge)
{
if (edge->points != NULL)
free(edge->points);
G_OBJECT_CLASS(g_graph_edge_parent_class)->finalize(G_OBJECT(edge));
}
/******************************************************************************
* *
* Paramètres : src_node = bloc de départ du lien. *
* src_slot = point d'ancrage associé. *
* dest_node = bloc d'arrivée du lien. *
* dest_slot = point d'ancrage associé. *
* color = couleur de rendu à l'écran. *
* *
* Description : Etablit un lien graphique entre deux noeuds graphiques. *
* *
* Retour : Adresse de la structure mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
GGraphEdge *g_graph_edge_new(GFlowNode *src_node, node_slot_t *src_slot, GFlowNode *dest_node, node_slot_t *dest_slot, EdgeColor color)
{
GGraphEdge *result; /* Structure à retourner */
result = g_object_new(G_TYPE_GRAPH_EDGE, NULL);
g_object_ref(G_OBJECT(src_node));
g_object_ref(G_OBJECT(dest_node));
result->src_node = src_node;
result->src_slot = src_slot;
result->dest_node = dest_node;
result->dest_slot = dest_slot;
result->color = color;
return G_GRAPH_EDGE(result);
}
/******************************************************************************
* *
* Paramètres : edge = ligne de rendu à mettre à jour. *
* nodes = noeud au sommet de la hiérarchie. *
* ranks = classement global dans lequel s'intégrer. *
* *
* Description : Prend les dispositions nécessaires à l'insertion du lien. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_graph_edge_reserve_vertical_space(GGraphEdge *edge, GGraphNode *nodes, GGraphRanks *ranks)
{
GGraphNode *container; /* Conteneur du lien de boucle */
unsigned int r1; /* Classement de départ */
unsigned int r2; /* Classement d'arrivée */
switch (edge->color)
{
case EGC_BLUE:
container = g_graph_node_find_container(nodes, G_GRAPH_NODE(edge->dest_node));
g_object_ref(G_OBJECT(container));
edge->container = G_VIRTUAL_NODE(container);
edge->is_left = false; /* TODO */
r1 = g_graph_node_get_rank(G_GRAPH_NODE(edge->src_node));
r2 = g_graph_node_get_rank(G_GRAPH_NODE(edge->dest_node));
edge->vert_step = g_virtual_node_reserve_span(edge->container, r1, r2, edge->is_left);
break;
default:
break;
}
}
/******************************************************************************
* *
* Paramètres : edge = ligne de rendu à mettre à jour. *
* ranks = classement global dans lequel s'intégrer. *
* *
* Description : Prend les dispositions nécessaires à l'insertion du lien. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_graph_edge_reserve_horizontal_space(GGraphEdge *edge, GGraphRanks *ranks)
{
GdkPoint x_src; /* Abscisse au niveau du départ*/
GdkPoint x_dest; /* Abscisse au niveau d'arrivée*/
gint x_step; /* Transition verticale */
unsigned int rank; /* Indice de classement */
x_src = g_flow_node_get_point_from_slot(edge->src_node, false, edge->src_slot);
x_dest = g_flow_node_get_point_from_slot(edge->dest_node, true, edge->dest_slot);
switch (edge->color)
{
case EGC_BLUE:
x_step = g_virtual_node_get_x_for_vspan_slot(edge->container,
edge->vert_step, edge->is_left);
rank = g_graph_node_get_rank(G_GRAPH_NODE(edge->src_node));
edge->bottom_step = g_graph_ranks_reserve_span(ranks, rank, x_src.x, x_step, false);
rank = g_graph_node_get_rank(G_GRAPH_NODE(edge->dest_node));
edge->top_step = g_graph_ranks_reserve_span(ranks, rank, x_dest.x, x_step, true);
break;
default:
rank = g_graph_node_get_rank(G_GRAPH_NODE(edge->dest_node));
edge->top_step = g_graph_ranks_reserve_span(ranks, rank, x_src.x, x_dest.x, true);
break;
}
}
/******************************************************************************
* *
* Paramètres : edge = ligne de rendu à mettre à jour. *
* ranks = classement global dans lequel s'intégrer. *
* *
* Description : Etablit le tracé du lien graphique entre deux noeuds. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_graph_edge_compute(GGraphEdge *edge, GGraphRanks *ranks)
{
GdkPoint start; /* Point de départ */
GdkPoint end; /* Point d'arrivée */
gint x_step; /* Transition verticale */
unsigned int rank; /* Indice de classement */
start = g_flow_node_get_point_from_slot(edge->src_node, false, edge->src_slot);
end = g_flow_node_get_point_from_slot(edge->dest_node, true, edge->dest_slot);
/* Point de départ */
edge->count = 2;
edge->points = (GdkPoint *)calloc(edge->count, sizeof(GdkPoint));
edge->points[0] = start;
edge->points[1] = start;
edge->points[1].y += SLOT_VERT_SEP;
/* Points de jonction */
switch (edge->color)
{
case EGC_BLUE:
edge->count += 4;
edge->points = (GdkPoint *)realloc(edge->points, edge->count * sizeof(GdkPoint));
x_step = g_virtual_node_get_x_for_vspan_slot(edge->container,
edge->vert_step, edge->is_left);
rank = g_graph_node_get_rank(G_GRAPH_NODE(edge->src_node));
edge->points[edge->count - 4].x = start.x;
edge->points[edge->count - 4].y = g_graph_ranks_get_y_for_hspan_slot(ranks, rank,
edge->bottom_step,
false);
edge->points[edge->count - 3].x = x_step;
edge->points[edge->count - 3].y = edge->points[edge->count - 4].y;
rank = g_graph_node_get_rank(G_GRAPH_NODE(edge->dest_node));
edge->points[edge->count - 2].x = x_step;
edge->points[edge->count - 2].y = g_graph_ranks_get_y_for_hspan_slot(ranks, rank,
edge->top_step,
true);
edge->points[edge->count - 1].x = end.x;
edge->points[edge->count - 1].y = edge->points[edge->count - 2].y;
break;
default:
edge->count += 2;
edge->points = (GdkPoint *)realloc(edge->points, edge->count * sizeof(GdkPoint));
rank = g_graph_node_get_rank(G_GRAPH_NODE(edge->dest_node));
edge->points[edge->count - 2].x = start.x;
edge->points[edge->count - 2].y = g_graph_ranks_get_y_for_hspan_slot(ranks, rank,
edge->top_step,
true);
edge->points[edge->count - 1].x = end.x;
edge->points[edge->count - 1].y = edge->points[edge->count - 2].y;
break;
}
/* Point d'arrivée */
edge->count += 2;
edge->points = (GdkPoint *)realloc(edge->points, edge->count * sizeof(GdkPoint));
edge->points[edge->count - 2] = end;
edge->points[edge->count - 2].y -= SLOT_VERT_SEP;
edge->points[edge->count - 1] = end;
}
/******************************************************************************
* *
* Paramètres : edge = ligne de rendu à manipuler. *
* cairo = assistant pour le rendu graphique. *
* arrow = indique le besoin en flèche à l'arrivée. *
* *
* Description : Dessine les liens graphiques enregistrés dans le moteur. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_graph_edge_draw(const GGraphEdge *edge, cairo_t *cairo, bool arrow)
{
size_t i; /* Boucle de parcours */
switch (edge->color)
{
default:
case EGC_DEFAULT:
cairo_set_source_rgb(cairo, 0, 0, 0);
break;
case EGC_GREEN:
cairo_set_source_rgb(cairo, 0, 0.6, 0);
break;
case EGC_RED:
cairo_set_source_rgb(cairo, 0.8, 0, 0);
break;
case EGC_BLUE:
cairo_set_source_rgb(cairo, 0, 0, 0.8);
break;
case EGC_DASHED_GRAY:
cairo_set_source_rgb(cairo, 0.4, 0.4, 0.4);
break;
}
switch (edge->color)
{
default:
case EGC_DEFAULT:
case EGC_GREEN:
case EGC_RED:
case EGC_BLUE:
cairo_set_dash(cairo, (double []) { 6.0 }, 0, 0.0);
break;
case EGC_DASHED_GRAY:
cairo_set_dash(cairo, (double []) { 6.0 }, 1, 0.0);
break;
}
/**
* Si on ne veut pas de flèche, on doit se destiner à un aperçu...
* Dans ce cas, pour plus de lisibilité, on double la taille d'impression.
* Et pour faire ressortir les boucles, on double encore les liens associés.
*/
cairo_set_line_width(cairo, arrow ? 1 : (edge->color == EGC_BLUE ? 4 : 2));
cairo_move_to(cairo, edge->points[0].x + 0.5, edge->points[0].y);
for (i = 1; i < edge->count; i++)
cairo_line_to(cairo, edge->points[i].x + 0.5, edge->points[i].y);
cairo_stroke(cairo);
if (arrow)
draw_link_arrow(cairo,
edge->points[edge->count - 2].x,
edge->points[edge->count - 2].y,
edge->points[edge->count - 1].x,
edge->points[edge->count - 1].y);
}
/******************************************************************************
* *
* Paramètres : cairo = gestionnaire de rendu graphique. *
* start_x = abscisse du début du segment final. *
* start_y = ordonnée du début du segment final. *
* end_x = abscisse de fin de segment et pointe de flèche. *
* end_y = ordonnée de fin de segment et pointe de flèche. *
* *
* Description : Dessine une flèche au bout du lien représenté. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void draw_link_arrow(cairo_t *cairo, gint start_x, gint start_y, gint end_x, gint end_y)
{
double angle; /* Angle du triangle à remplir */
double factor_x; /* Direction de la flèche #1 */
double factor_y; /* Direction de la flèche #2 */
double x1; /* Abscisse du point n°1 */
double y1; /* Ordonnée du point n°1 */
double x2; /* Abscisse du point n°2 */
double y2; /* Ordonnée du point n°2 */
angle = atan2(end_y - start_y, end_x - start_x) + M_PI;
factor_x = -1;
factor_y = -1;
x1 = end_x + factor_x * ARROW_LENGHT * cos(angle - ARROW_DEGREES);
y1 = end_y + factor_y * ARROW_LENGHT * sin(angle - ARROW_DEGREES);
x2 = end_x + factor_x * ARROW_LENGHT * cos(angle + ARROW_DEGREES);
y2 = end_y + factor_y * ARROW_LENGHT * sin(angle + ARROW_DEGREES);
cairo_move_to(cairo, end_x, end_y);
cairo_line_to(cairo, x1, y1);
cairo_line_to(cairo, x2, y2);
cairo_move_to(cairo, end_x, end_y);
cairo_fill(cairo);
}