/* Chrysalide - Outil d'analyse de fichiers binaires
* edge.c - liens entre les noeuds d'un graphique
*
* Copyright (C) 2013-2017 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* Chrysalide 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.
*
* Chrysalide 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
#include
#include
/* Lien graphique entre deux noeuds graphiques (instance) */
struct _GGraphEdge
{
GObject parent; /* A laisser en premier */
EdgeColor color; /* Couleur du rendu */
const GdkPoint *start; /* Point de départ du lien */
GdkPoint *mid[2]; /* Etapes intermédiaires */
size_t m_count; /* Quantité de ces étapes */
const GdkPoint *end; /* Point d'arrivée du lien */
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 */
};
/* Dimensions des flêches */
#define ARROW_LENGHT 10
#define ARROW_DEGREES 10
/* 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)
{
}
/******************************************************************************
* *
* 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_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 : start = point de départ de la flêche. *
* mid = points intermédiares variables. *
* count = nombre de ces points fournis. *
* end = point d'arrivée de la flêche. *
* 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(const GdkPoint *start, const GdkPoint **mid, size_t count, const GdkPoint *end, EdgeColor color)
{
GGraphEdge *result; /* Structure à retourner */
result = g_object_new(G_TYPE_GRAPH_EDGE, NULL);
result->color = color;
assert(count == 1 || count == 2);
result->start = start;
memcpy(result->mid, mid, count * sizeof(GdkPoint));
result->m_count = count;
result->end = end;
return G_GRAPH_EDGE(result);
}
/******************************************************************************
* *
* Paramètres : edge = ligne de rendu à consulter. *
* x1 = abscisse du point de départ de la ligne. [OUT] *
* x2 = abscisse du point d'arrivée de la ligne. [OUT] *
* *
* Description : Fournit les abscisses des points extrèmes de la ligne. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_graph_edge_get_x_borders(const GGraphEdge *edge, gint *x1, gint *x2)
{
*x1 = edge->start->x;
*x2 = edge->end->x;
}
/******************************************************************************
* *
* Paramètres : edge = ligne de rendu à définir dans les détails. *
* *
* Description : Détermine les positions finales d'un lien graphique. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_graph_edge_resolve(GGraphEdge *edge)
{
edge->count = 2 + 2 * edge->m_count;
edge->points = (GdkPoint *)calloc(edge->count, sizeof(GdkPoint));
edge->points[0] = *edge->start;
edge->points[1].x = edge->start->x;
edge->points[1].y = edge->mid[0]->y;
if (edge->m_count == 1)
{
edge->points[2].x = edge->end->x;
edge->points[2].y = edge->mid[0]->y;
}
else
{
memcpy(&edge->points[2], edge->mid, edge->m_count * sizeof(GdkPoint));
edge->points[4].x = edge->end->x;
edge->points[4].y = edge->mid[3]->y;
}
edge->points[edge->count - 1] = *edge->end;
}
/******************************************************************************
* *
* Paramètres : edge = ligne de rendu à modifier dans ses positions. *
* dx = déplacement à effectuer sur l'axe des abscisses. *
* dy = déplacement à effectuer sur l'axe des ordonnées. *
* *
* Description : Opère un décalage du lien dans une direction donnée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_graph_edge_offset(GGraphEdge *edge, gint dx, gint dy)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < edge->count; i++)
{
edge->points[i].x += dx;
edge->points[i].y += dy;
}
}
/******************************************************************************
* *
* 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);
}