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