#include #include #include "Egraph.h" #define DEBUG 0 #define VERTICE_OBJ_W 10 #define VERTICE_OBJ_H 10 #define VERTICE_TEXT_W 20 // XXX dynamicaly get the max on each new name #define VERTICE_TEXT_H 10 #define VERTICE_W (VERTICE_OBJ_W + VERTICE_TEXT_W) #define VERTICE_H (VERTICE_OBJ_H + VERTICE_TEXT_H) static void _smart_add(Evas_Object *obj); static void _smart_del(Evas_Object *obj); static void _smart_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y); static void _smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h); static void _edge_v2_add(Egraph *eg, Egraph_Edge *e); static void _edge_add(Egraph *eg, Egraph_Edge *e); static void _edge_v2_del(Egraph *eg, Egraph_Edge *e); static void _edge_del(Egraph *eg, Egraph_Edge *e); static void _vertice_v2_add(Egraph *eg, Egraph_Vertice *v); static void _vertice_add(Egraph *eg, Egraph_Vertice *v); static void _vertice_v2_del(Egraph *eg, Egraph_Vertice *v); static void _vertice_del(Egraph *eg, Egraph_Vertice *v); static void _v2_update(Egraph *eg); static void _v3_update(Egraph *eg); static void _coords2_copy(Egraph *eg); static void _layouting_schedule(Egraph *eg); static Eina_Bool _cb_layouting_start(void *data); static void _cb_layouting_run(void *data, Ecore_Thread *thread); static void _cb_layouting_end(void *data, Ecore_Thread *thread); static void _cb_layouting_cancel(void *data, Ecore_Thread *thread); static int _igraph_query_vid(igraph_t *g, int g_len, int id); static void _cb_vertice_move(void *data, Evas *evas, Evas_Object *obj, void *event_info); static void _reposition(Egraph *eg, int no_animation); static void _matrix_minmax_2v(const igraph_matrix_t *m, int row_count, float *c1min, float *c1max, float *c2min, float *c2max); static const Evas_Smart_Cb_Description _smart_callbacks[] = {{NULL, NULL}}; #define EGRAPH_DATA_GET(o, ptr) \ Egraph * ptr = evas_object_smart_data_get(o) /* defines _egraph_parent_sc and _egraph_smart_class_new */ EVAS_SMART_SUBCLASS_NEW("Egraph", _egraph, Evas_Smart_Class, Evas_Smart_Class, evas_object_smart_clipped_class_get, _smart_callbacks); static void _egraph_smart_set_user(Evas_Smart_Class *sc) { /* specializing these two */ sc->add = _smart_add; sc->del = _smart_del; sc->move = _smart_move; /* clipped smart object has no hook on resizes or calculations */ sc->resize = _smart_resize; } static void _smart_add(Evas_Object *obj) { EVAS_SMART_DATA_ALLOC(obj, Egraph); priv->evas = evas_object_evas_get(obj); _egraph_parent_sc->add(obj); } static void _smart_del(Evas_Object *obj) { EGRAPH_DATA_GET(obj, eg); igraph_destroy(&eg->graph); if (eg->layouting.running == 2) ecore_thread_cancel(eg->layouting.thread); _egraph_parent_sc->del(obj); } static void _smart_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y) { EGRAPH_DATA_GET(obj, eg); _reposition(eg, 1); _egraph_parent_sc->move(obj, x, y); } static void _smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h) { EGRAPH_DATA_GET(obj, eg); _reposition(eg, 1); } Evas_Object * egraph_new(Evas *evas, int directed) { Evas_Object *obj = NULL; Egraph *eg; int i; EINA_SAFETY_ON_NULL_RETURN_VAL(evas, NULL); efx_init(); eina_log_domain_level_set("efx", EINA_LOG_LEVEL_WARN); obj = evas_object_smart_add(evas, _egraph_smart_class_new()); eg = evas_object_smart_data_get(obj); if (!eg) goto err; eg->obj = obj; eg->graph_directed = directed; eg->display_vertices = 1; eg->display_names = 1; eg->display_edges = 1; eg->use_animations = 1; eg->do_improvements = 1; eg->layout = EGRAPH_LAYOUT_DEFAULT; /* needed for igraph attribute handling */ igraph_i_set_attribute_table(&igraph_cattribute_table); eg->vertices = eina_hash_int32_new(NULL); for (i=0; ivertices_freeids[i] = i; if (igraph_empty(&eg->graph, 0, directed)) goto err; if (igraph_empty(&eg->graph2, 0, directed)) goto err; igraph_matrix_init(&eg->coords, 0, 2); igraph_matrix_init(&eg->coords2, 0, 2); eg->graph_wmin = 0; eg->graph_wmax = 200; eg->graph_hmin = 0; eg->graph_hmax = 200; return obj; err: if (obj) evas_object_del(obj); return NULL; } void egraph_layout_set(Evas_Object *obj, Egraph_Layout layout) { EGRAPH_DATA_GET(obj, eg); eg->layout = layout; _layouting_schedule(eg); } void egraph_display_vertices_set(Evas_Object *obj, int set) { EGRAPH_DATA_GET(obj, eg); Eina_Iterator *it; Egraph_Vertice *v; void *data; eg->display_vertices = set; it = eina_hash_iterator_tuple_new(eg->vertices); while (eina_iterator_next(it, &data)) { Eina_Hash_Tuple *t = data; v = t->data; if (v->o) { if (set == 0) evas_object_hide(v->o); else evas_object_show(v->o); } } _reposition(eg, 0); } void egraph_display_names_set(Evas_Object *obj, int set) { EGRAPH_DATA_GET(obj, eg); Eina_Iterator *it; Egraph_Vertice *v; void *data; eg->display_names = set; it = eina_hash_iterator_tuple_new(eg->vertices); while (eina_iterator_next(it, &data)) { Eina_Hash_Tuple *t = data; v = t->data; if (v->o_text) { if (set == 0) evas_object_hide(v->o_text); else evas_object_show(v->o_text); } } } void egraph_display_edges_set(Evas_Object *obj, int set) { EGRAPH_DATA_GET(obj, eg); Eina_List *l; Egraph_Edge *e; eg->display_edges = set; EINA_LIST_FOREACH(eg->edges, l, e) { if (e->o) { if (set == 0) evas_object_hide(e->o); else evas_object_show(e->o); } } } void egraph_use_animations_set(Evas_Object *obj, int set) { EGRAPH_DATA_GET(obj, eg); eg->use_animations = set; } void egraph_do_improvements_set(Evas_Object *obj, int set) { EGRAPH_DATA_GET(obj, eg); eg->do_improvements = set; } Egraph_Edge * egraph_edge_add(Evas_Object *obj, Egraph_Vertice *a, Egraph_Vertice *b, void *data) { EGRAPH_DATA_GET(obj, eg); Egraph_Edge *e; e = calloc(1, sizeof(Egraph_Edge)); if (!e) return NULL; e->v3_new = 1; e->a = a; e->b = b; e->data = data; eg->edges = eina_list_append(eg->edges, e); if (DEBUG) printf("egraph_edge_add %d %d\n", e->a->id, e->b->id); _layouting_schedule(eg); return e; } static void _edge_v2_add(Egraph *eg, Egraph_Edge *e) { int a_pos, b_pos; if (DEBUG) printf("_edge_v2_add %d %d\n", e->a->id, e->b->id); e->v2_new = 1; e->v3_new = 0; a_pos = _igraph_query_vid(&eg->graph2, eg->graph2_vcount, e->a->id); b_pos = _igraph_query_vid(&eg->graph2, eg->graph2_vcount, e->b->id); igraph_add_edge(&eg->graph2, a_pos, b_pos); } static void _edge_add(Egraph *eg, Egraph_Edge *e) { Evas_Object *eobj; int a_pos, b_pos; e->new = 1; e->v2_new = 0; a_pos = _igraph_query_vid(&eg->graph, eg->graph_vcount, e->a->id); b_pos = _igraph_query_vid(&eg->graph, eg->graph_vcount, e->b->id); igraph_add_edge(&eg->graph, a_pos, b_pos); eobj = evas_object_line_add(eg->evas); evas_object_smart_member_add(eobj, eg->obj); evas_object_color_set(eobj, 255, 0, 0, 255); e->o = eobj; e->a->edges = eina_list_append(e->a->edges, e); e->b->edges = eina_list_append(e->b->edges, e); } void egraph_edge_del(Evas_Object *obj, Egraph_Edge *e) { EGRAPH_DATA_GET(obj, eg); e->v3_del = 1; _layouting_schedule(eg); } static void _edge_v2_del(Egraph *eg, Egraph_Edge *e) { igraph_es_t es; int a_pos, b_pos; if (e->v2_del == 1) return; if (DEBUG) printf("_edge_v2_del %d %d\n", e->a->id, e->b->id); e->v2_del = 1; e->v3_del = 0; a_pos = _igraph_query_vid(&eg->graph2, eg->graph2_vcount, e->a->id); b_pos = _igraph_query_vid(&eg->graph2, eg->graph2_vcount, e->b->id); igraph_es_pairs_small(&es, eg->graph_directed, a_pos, b_pos, -1); igraph_delete_edges(&eg->graph2, es); } static void _edge_del(Egraph *eg, Egraph_Edge *e) { igraph_es_t es; int a_pos, b_pos; a_pos = _igraph_query_vid(&eg->graph, eg->graph_vcount, e->a->id); b_pos = _igraph_query_vid(&eg->graph, eg->graph_vcount, e->b->id); igraph_es_pairs_small(&es, eg->graph_directed, a_pos, b_pos, -1); igraph_delete_edges(&eg->graph, es); e->a->edges = eina_list_remove(e->a->edges, e); e->b->edges = eina_list_remove(e->b->edges, e); evas_object_del(e->o); eg->edges = eina_list_remove(eg->edges, e); free(e); } Egraph_Edge * egraph_edge_find(Evas_Object *obj, Egraph_Vertice *a, Egraph_Vertice *b) { EGRAPH_DATA_GET(obj, eg); Egraph_Edge *e; Eina_List *l; EINA_LIST_FOREACH(eg->edges, l, e) if ((e->a == a && e->b == b) || (e->a == b && e->b == a)) return e; return NULL; } Egraph_Vertice * egraph_vertice_add(Evas_Object *obj, const char *name, void *data) { EGRAPH_DATA_GET(obj, eg); Egraph_Vertice *v; u_int32_t id; if (eg->vertices_count == EGRAPH_VERTICES_MAX) { printf("egraph error: maximum number of vertices reached !\n"); return NULL; } v = calloc(1, sizeof(Egraph_Vertice)); if (!v) return NULL; id = eg->vertices_freeids[eg->vertices_count]; v->id = id; eina_hash_add(eg->vertices, &id, v); eg->vertices_count++; v->v3_new = 1; if (DEBUG) printf("egraph_vertice_add %d\n", id); if (name) v->name = strndup(name, EGRAPH_VERTICE_NAME_MAXLEN); v->data = data; _layouting_schedule(eg); return v; } static void _vertice_v2_add(Egraph *eg, Egraph_Vertice *v) { v->v2_new = 1; v->v3_new = 0; igraph_add_vertices(&eg->graph2, 1, 0); SETVAN(&eg->graph2, "id", eg->graph2_vcount, v->id); eg->graph2_vcount++; } static void _vertice_add(Egraph *eg, Egraph_Vertice *v) { Evas_Object *vobj, *text; v->new = 1; v->v2_new = 0; igraph_add_vertices(&eg->graph, 1, 0); SETVAN(&eg->graph, "id", eg->graph_vcount, v->id); eg->graph_vcount++; vobj = evas_object_rectangle_add(eg->evas); evas_object_smart_member_add(vobj, eg->obj); evas_object_color_set(vobj, 0, 0, 255, 255); evas_object_resize(vobj, VERTICE_OBJ_W, VERTICE_OBJ_H); evas_object_event_callback_add(vobj, EVAS_CALLBACK_MOVE, _cb_vertice_move, v); v->o = vobj; text = evas_object_text_add(eg->evas); evas_object_text_style_set(text, EVAS_TEXT_STYLE_PLAIN); evas_object_color_set(text, 200, 255, 0, 255); evas_object_text_font_set(text, "Vera-Bold", VERTICE_TEXT_H); evas_object_smart_member_add(text, eg->obj); v->o_text = text; if (v->name) egraph_vertice_rename(eg->obj, v, v->name); } void egraph_vertice_del(Evas_Object *obj, Egraph_Vertice *v) { EGRAPH_DATA_GET(obj, eg); Egraph_Edge *e; Eina_List *l; v->v3_del = 1; EINA_LIST_FOREACH(eg->edges, l, e) if (e->a == v || e->b == v) egraph_edge_del(obj, e); _layouting_schedule(eg); } static void _vertice_v2_del(Egraph *eg, Egraph_Vertice *v) { Egraph_Edge *e; Eina_List *l; int pos; if (v->v2_del == 1) return; v->v2_del = 1; v->v3_del = 0; if (DEBUG) printf("_vertice_v2_del %d\n", v->id); EINA_LIST_FOREACH(eg->edges, l, e) if (e->a == v || e->b == v) _edge_v2_del(eg, e); pos = _igraph_query_vid(&eg->graph2, eg->graph2_vcount, v->id); igraph_delete_vertices(&eg->graph2, igraph_vss_1(pos)); eg->graph2_vcount--; } static void _vertice_del(Egraph *eg, Egraph_Vertice *v) { Egraph_Edge *e; Eina_List *l; int pos; EINA_LIST_FOREACH(eg->edges, l, e) if (e->a == v || e->b == v) _edge_del(eg, e); pos = _igraph_query_vid(&eg->graph, eg->graph_vcount, v->id); igraph_delete_vertices(&eg->graph, igraph_vss_1(pos)); eg->graph_vcount--; evas_object_del(v->o); if (v->o_text) evas_object_del(v->o_text); eina_hash_del(eg->vertices, &v->id, NULL); eg->vertices_count--; eg->vertices_freeids[eg->vertices_count] = v->id; free(v); } void egraph_vertice_rename(Evas_Object *obj, Egraph_Vertice *v, const char *name) { EGRAPH_DATA_GET(obj, eg); if (eg->display_names == 0) return; if (name) { if (v->name != name) { if (v->name) free(v->name); v->name = strndup(name, EGRAPH_VERTICE_NAME_MAXLEN); } evas_object_text_text_set(v->o_text, name); evas_object_show(v->o_text); } else { evas_object_hide(v->o_text); } } static void _layouting_schedule(Egraph *eg) { if (eg->layouting.running > 0) { eg->layouting.todo = 1; return; } eg->layouting.running = 1; ecore_timer_add(0.0, _cb_layouting_start, eg); /* delayed start */ } static void _v2_update(Egraph *eg) { Eina_List *l, *lprev, *todel, *toadd; Eina_Iterator *it; Egraph_Vertice *v; Egraph_Edge *e; void *data; /* update graph and structs based on v2_add / v2_del */ it = eina_hash_iterator_tuple_new(eg->vertices); todel = NULL; toadd = NULL; while (eina_iterator_next(it, &data)) { Eina_Hash_Tuple *t = data; v = t->data; if (v->v2_del) todel = eina_list_append(todel, v); else if (v->v2_new) toadd = eina_list_append(toadd, v); } eina_iterator_free(it); EINA_LIST_FOREACH(todel, l, v) _vertice_del(eg, v); EINA_LIST_FOREACH(toadd, l, v) _vertice_add(eg, v); todel = eina_list_free(todel); toadd = eina_list_free(toadd); EINA_LIST_FOREACH_SAFE(eg->edges, l, lprev, e) { if (e->v2_del) _edge_del(eg, e); else if (e->v2_new) _edge_add(eg, e); } } static void _v3_update(Egraph *eg) { Eina_List *l, *lprev; Eina_Iterator *it; Egraph_Vertice *v; Egraph_Edge *e; void *data; float ranx, rany; int changes_count = 0; int changes_diff = 0; int i, id; if (DEBUG) printf("_v3_update\n"); /* update graph2 and structs based on v3_add / v3_del */ it = eina_hash_iterator_tuple_new(eg->vertices); while (eina_iterator_next(it, &data)) { Eina_Hash_Tuple *t = data; v = t->data; if (v->v3_del) { _vertice_v2_del(eg, v); changes_diff--; changes_count++; } else if (v->v3_new) { _vertice_v2_add(eg, v); changes_diff++; changes_count++; } } eina_iterator_free(it); if (DEBUG) printf("_v3_update edges\n"); EINA_LIST_FOREACH_SAFE(eg->edges, l, lprev, e) { if (e->v3_del) { _edge_v2_del(eg, e); changes_count++; } else if (e->v3_new) { _edge_v2_add(eg, e); changes_count++; } } /* set correct coords2 size */ if (changes_diff > 0) { for (i=0; igraph2_vcount - changes_diff) + i; igraph_matrix_add_rows(&eg->coords2, 1); ranx = eg->graph_wmin + fmod((float)random() / 1000, (float)((eg->graph_wmax + 1) - eg->graph_wmin)); rany = eg->graph_hmin + fmod((float)random() / 1000, (float)((eg->graph_hmax + 1) - eg->graph_hmin)); if (DEBUG) printf("ranx %6.3f rany %6.3f\n", ranx, rany); igraph_matrix_set(&eg->coords2, id, 0, ranx); igraph_matrix_set(&eg->coords2, id, 1, rany); } } else if (changes_diff < 0) { changes_diff = -changes_diff; for (i=0; igraph2_vcount + changes_diff) - i; igraph_matrix_remove_row(&eg->coords2, 0); } } if (eg->layout == EGRAPH_LAYOUT_FRUCHTERMANREINGOLD) { /* set minimum and maximum for each node */ /* XXX do that in layouting thread ? */ if (DEBUG) printf("g wmin %d wmax %d hmin %d hmax %d\n", eg->graph_wmin, eg->graph_wmax, eg->graph_hmin, eg->graph_hmax); igraph_vector_init(&eg->graph2_wmin, eg->graph2_vcount); igraph_vector_init(&eg->graph2_wmax, eg->graph2_vcount); igraph_vector_init(&eg->graph2_hmin, eg->graph2_vcount); igraph_vector_init(&eg->graph2_hmax, eg->graph2_vcount); igraph_vector_fill(&eg->graph2_wmin, eg->graph_wmin); igraph_vector_fill(&eg->graph2_wmax, eg->graph_wmax); igraph_vector_fill(&eg->graph2_hmin, eg->graph_hmin); igraph_vector_fill(&eg->graph2_hmax, eg->graph_hmax); } eg->layouting.changes_diff = changes_diff; } static Eina_Bool _cb_layouting_start(void *data) { Egraph *eg; eg = data; eg->layouting.todo = 0; _v3_update(eg); eg->layouting.running = 2; eg->layouting.thread = ecore_thread_run(_cb_layouting_run, _cb_layouting_end, _cb_layouting_cancel, eg); return EINA_FALSE; /* no repeat */ } void _cb_layouting_run(void *data, Ecore_Thread *thread) { Egraph *eg; int niter; eg = data; if (DEBUG) printf("[-] _cb_layouting_run begin (%d)\n", eg->layout); switch(eg->layout) { case EGRAPH_LAYOUT_KAMADAKAWAI: if (eg->layouting.improvement) { niter = 300; } else { if (eg->layouting.changes_diff == 0) niter = 1000; else niter = 1000 * eg->layouting.changes_diff; if (niter > 2000) niter = 2000; } /* http://igraph.sourceforge.net/doc/html/ch18s01.html#igraph_layout_kamada_kawai * int igraph_layout_kamada_kawai(const igraph_t *g, igraph_matrix_t *res, * igraph_integer_t niter, igraph_real_t sigma, * igraph_real_t initemp, igraph_real_t coolexp, * igraph_real_t kkconst, igraph_bool_t use_seed, * const igraph_vector_t *minx, * const igraph_vector_t *maxx, * const igraph_vector_t *miny, * const igraph_vector_t *maxy); * Defaults : * igraph_layout_kamada_kawai(&eg->graph2, &eg->coords2, * 1000, eg->vertices_count / 4, 10, 0.99, * eg->vertices_count ^ 2, 1, * NULL, NULL, NULL, NULL); */ igraph_layout_kamada_kawai(&eg->graph2, &eg->coords2, niter, eg->graph2_vcount, 10, 0.99, eg->vertices_count ^ 2, 1, NULL, NULL, NULL, NULL); break; case EGRAPH_LAYOUT_GRAPHOPT: if (eg->layouting.improvement) { niter = 300; } else { if (eg->layouting.changes_diff == 0) niter = 100; else niter = 50 * eg->layouting.changes_diff; if (niter > 500) niter = 500; } /* http://igraph.sourceforge.net/doc/html/ch18s01.html#igraph_layout_graphopt * int igraph_layout_graphopt(const igraph_t *g, igraph_matrix_t *res, * igraph_integer_t niter, * igraph_real_t node_charge, igraph_real_t node_mass, * igraph_real_t spring_length, * igraph_real_t spring_constant, * igraph_real_t max_sa_movement, * igraph_bool_t use_seed); * Defaults : * igraph_layout_graphopt(&eg->graph2, &eg->coords2, * 1000, 0.001, 30, 0, 1, 5, 0); */ igraph_layout_graphopt(&eg->graph2, &eg->coords2, niter, 0.003, 10, 10, 1, 5, 1); break; case EGRAPH_LAYOUT_FRUCHTERMANREINGOLD: niter = 1000; /* http://igraph.sourceforge.net/doc/html/ch18s01.html#igraph_layout_fruchterman_reingold * int igraph_layout_fruchterman_reingold(const igraph_t *graph, igraph_matrix_t *res, * igraph_integer_t niter, igraph_real_t maxdelta, * igraph_real_t area, igraph_real_t coolexp, * igraph_real_t repulserad, igraph_bool_t use_seed, * const igraph_vector_t *weight, * const igraph_vector_t *minx, * const igraph_vector_t *maxx, * const igraph_vector_t *miny, * const igraph_vector_t *maxy); * Defaults: * igraph_layout_fruchterman_reingold(&eg->graph2, &eg->coords2, * 500, eg->graph2_vcount, * sqrt(eg->graph2_vcount) * sqrt(eg->graph2_vcount), 1.5, * eg->graph2_vcount, 0, * NULL, NULL, NULL, NULL, NULL); */ igraph_layout_fruchterman_reingold(&eg->graph2, &eg->coords2, niter, 0.05, sqrt(eg->graph2_vcount), 1.5, sqrt(eg->graph2_vcount) * eg->graph2_vcount, 1, NULL, NULL, NULL, NULL, NULL); //&eg->graph2_wmin, &eg->graph2_wmax, //&eg->graph2_hmin, &eg->graph2_hmax); break; } if (DEBUG) printf("[-] _cb_layouting_run end\n"); } static void _coords2_copy(Egraph *eg) { igraph_matrix_copy(&eg->coords, &eg->coords2); } void _cb_layouting_end(void *data, Ecore_Thread *thread) { Egraph *eg; eg = data; _v2_update(eg); _coords2_copy(eg); _reposition(eg, 0); eg->layouting.running = 0; if (eg->layouting.todo) { eg->layouting.todo = 0; eg->layouting.improvement = 0; _layouting_schedule(eg); } else { if (eg->do_improvements) { if (eg->layouting.improvement < EGRAPH_LAYOUTING_IMPROVEMENTS) { eg->layouting.improvement++; _layouting_schedule(eg); } else { eg->layouting.improvement = 0; } } } } void _cb_layouting_cancel(void *data, Ecore_Thread *thread) { Egraph *eg; eg = data; eg->layouting.running = 0; eg->layouting.todo = 0; /* we are not in a clean state now, but it happends on exit only */ } /* XXX slow ! possible to do edge/vertice delete on repositionning ? */ static int _igraph_query_vid(igraph_t *g, int g_len, int id) { int i; for (i=0; io, &x, &y, &w, &h); if (eg->display_names) { if (eg->display_vertices) evas_object_move(v->o_text, x+3, y+8); else evas_object_move(v->o_text, x, y); evas_object_raise(v->o_text); } EINA_LIST_FOREACH(v->edges, l, e) { if (e->new) { if (eg->display_edges) evas_object_show(e->o); e->new = 0; } evas_object_line_xy_get(e->o, &ax, &ay, &bx, &by); if (e->a == v) { ax = x + w/2; ay = y + h/2; } else { bx = x + w/2; by = y + h/2; } evas_object_line_xy_set(e->o, ax, ay, bx, by); } } static void _reposition(Egraph *eg, int no_animation) { Egraph_Vertice *v; u_int32_t id; float gw_min, gw_max, gh_min, gh_max, factor_w, factor_h; float x, y; int obj_x, obj_y, obj_w, obj_h; int vcur; if (DEBUG) printf("[-] _reposition\n"); if (eg->graph_vcount == 0) return; evas_object_geometry_get(eg->obj, &obj_x, &obj_y, &obj_w, &obj_h); _matrix_minmax_2v(&eg->coords, eg->graph_vcount, &gw_min, &gw_max, &gh_min, &gh_max); eg->graph_wmin = gw_min; eg->graph_wmax = gw_max; eg->graph_hmin = gh_min; eg->graph_hmax = gh_max; if (DEBUG) printf("gw_min %6.3f gw_max %6.3f gh_min %6.3f gh_max %6.3f\n", gw_min, gw_max, gh_min, gh_max); if (gw_max == gw_min) factor_w = 1; else factor_w = (obj_w - VERTICE_W) / (gw_max - gw_min); if (gh_max == gh_min) factor_h = 1; else factor_h = (obj_h - VERTICE_H) / (gh_max - gh_min); if (DEBUG) printf("factor_w %6.3f factor_h %6.3f\n", factor_w, factor_h); for (vcur=0; vcurgraph_vcount; vcur++) { id = VAN(&eg->graph, "id", vcur); x = MATRIX(eg->coords, vcur, 0); y = MATRIX(eg->coords, vcur, 1); if (DEBUG) printf("%d: %6.3f %6.3f id %d\n", vcur, x, y, id); x = obj_x + ((x - gw_min) * factor_w); y = obj_y + ((y - gh_min) * factor_h); if (DEBUG) printf(" inobj: %6.3f %6.3f\n", x, y); v = eina_hash_find(eg->vertices, &id); if (eg->use_animations && !no_animation) efx_move(v->o, EFX_EFFECT_SPEED_DECELERATE, &(Evas_Point){ x, y }, 1.0, NULL, NULL); else evas_object_move(v->o, x, y); evas_object_raise(v->o); if (eg->display_names) { evas_object_raise(v->o_text); } if (v->new) { if (eg->display_vertices) evas_object_show(v->o); if (eg->display_names) evas_object_show(v->o_text); v->new = 0; } } } /** * Compute minimum and maximum of a matrices of 2 colum vectors * * @note igraph is crazy in the coconut */ static void _matrix_minmax_2v(const igraph_matrix_t *m, int row_count, float *c1min, float *c1max, float *c2min, float *c2max) { float val; int row; *c1min=MATRIX(*m, 0, 0); *c1max=MATRIX(*m, 0, 0); *c2min=MATRIX(*m, 0, 1); *c2max=MATRIX(*m, 0, 1); for (row=1; row *c1max) *c1max = val; val = MATRIX(*m, row, 1); if (val < *c2min) *c2min = val; else if (val > *c2max) *c2max = val; } }