]> granicus.if.org Git - graphviz/commitdiff
Added cycle_centroid for curved edges
authorBilly Schoenberg <bschoenberg@bschoenberg.local>
Mon, 28 Jan 2019 14:27:59 +0000 (09:27 -0500)
committerBilly Schoenberg <bschoenberg@bschoenberg.local>
Mon, 28 Jan 2019 14:27:59 +0000 (09:27 -0500)
lib/common/routespl.c

index a5c7076c15194d1ad98b5652da35ab527c018c33..6abf72ea1efec05a1520864a3be4c1d86db9d3bb 100644 (file)
@@ -902,6 +902,244 @@ static pointf get_centroid(Agraph_t *g)
     return sum;
 }
 
+#define __CYCLE_CENTROID 
+#ifdef __CYCLE_CENTROID
+//generic vector structure
+typedef struct _tag_vec
+{
+    void** _mem;
+    size_t _elems;
+    size_t _capelems;
+} vec;
+
+static vec* vec_new()
+{
+    vec* pvec = (vec*)malloc(sizeof(vec));
+    pvec->_capelems = 10;
+    pvec->_elems = 0;
+    pvec->_mem = (void**)malloc(pvec->_capelems * sizeof(void*));
+    return pvec;
+}
+
+static void vec_delete(vec* pvec)
+{
+    free(pvec->_mem);
+    free(pvec);
+}
+
+static void vec_push_back(vec* pvec, void* data)
+{
+    if (pvec->_elems == pvec->_capelems) {
+               pvec->_capelems += 10;
+               pvec->_mem = (void**)realloc(pvec->_mem, pvec->_capelems * sizeof(void*));
+       }
+    pvec->_mem[pvec->_elems++] = data;  
+}
+
+static size_t vec_length(vec* pvec)
+{
+    return pvec->_elems;
+}
+
+static void* vec_get(vec* pvec, size_t index)
+{
+    assert(index < pvec->_elems);
+    return pvec->_mem[index];
+}
+
+static void* vec_pop(vec* pvec)
+{
+       if (pvec->_elems > 0)
+               return pvec->_mem[--pvec->_elems];
+       return NULL;
+}
+
+static boolean vec_contains(vec* pvec, void* item) 
+{
+       size_t i;
+
+       for (i=0; i < pvec->_elems; ++i) {
+               if (pvec->_mem[i] == item)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static vec* vec_copy(vec* pvec)
+{
+    vec* nvec = (vec*)malloc(sizeof(vec));
+    nvec->_capelems = pvec->_capelems;
+    nvec->_elems = pvec->_elems;
+    nvec->_mem = (void**)malloc(pvec->_capelems * sizeof(void*));
+       memcpy(nvec->_mem, pvec->_mem, pvec->_elems * sizeof(void*));
+    return nvec;
+}
+//end generic vector structure
+
+static boolean cycle_contains_edge(vec* cycle, edge_t* edge)
+{
+       node_t* start = agtail(edge);
+       node_t* end = aghead(edge);
+       node_t* c_start;
+       node_t* c_end;
+
+       size_t cycle_len = vec_length(cycle);
+       size_t i;
+
+       for (i=0; i < cycle_len; ++i) {
+               if (i == 0) {
+                       c_start = (node_t*)vec_get(cycle, cycle_len-1);
+               } else {
+                       c_start = (node_t*)vec_get(cycle, i-1);
+               }
+       
+               c_end = (node_t*)vec_get(cycle, i);
+
+               if (c_start == start && c_end == end)
+                       return TRUE;
+       }
+
+
+       return FALSE;
+}
+
+static boolean is_cycle_unique(vec* cycles, vec* cycle) 
+{
+       size_t cycle_len = vec_length(cycle);
+       size_t n_cycles = vec_length(cycles);
+       size_t c; //cycles counter
+       size_t i; //node counter
+
+       vec* cur_cycle;
+       size_t cur_cycle_len;
+       void* cur_cycle_item;
+       boolean all_items_match;
+
+       for (c=0; c < n_cycles; ++c) {
+               cur_cycle = (vec*)vec_get(cycles, c);
+               cur_cycle_len = vec_length(cur_cycle);
+
+               //if all the items match in equal length cycles then we're not unique
+               if (cur_cycle_len == cycle_len) {
+                       all_items_match = TRUE;
+                       for (i=0; i < cur_cycle_len; ++i) {
+                               cur_cycle_item = vec_get(cur_cycle, i);
+                               if (!vec_contains(cycle, cur_cycle_item)) {
+                                       all_items_match = FALSE;
+                                       break;
+                               }
+                       }
+                       if (all_items_match)
+                               return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+static void dfs(graph_t *g, node_t* search, vec* visited, node_t* end, vec* cycles)
+{
+       edge_t* e;
+       node_t* n;
+
+       if (vec_contains(visited, search)) {
+               if (search == end) {
+                       if (is_cycle_unique(cycles, visited)) {
+                               vec* cycle = vec_copy(visited);
+                               vec_push_back(cycles, cycle);
+                       }
+               }
+       } else {
+               vec_push_back(visited, search);
+               for (e = agfstout(g, search); e; e = agnxtout(g, e)) {
+                       n = aghead(e);
+                       dfs(g, n, visited, end, cycles);
+               }
+               vec_pop(visited);
+       }
+}
+
+static vec* find_all_cycles(graph_t *g)
+{
+    node_t *n;
+
+    vec* alloced_cycles = vec_new(); //vector of vectors of nodes -- AKA cycles to delete
+    vec* cycles = vec_new(); //vector of vectors of nodes AKA a vector of cycles
+    vec* cycle;
+
+    for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
+               cycle = vec_new();
+               vec_push_back(alloced_cycles, cycle); //keep track of all items we allocate to clean up at the end of this function
+               
+               dfs(g, n, cycle, n, cycles);
+       }
+       
+       vec_delete(alloced_cycles); //cycles contains copied vecs
+    return cycles;
+}
+
+static vec* find_shortest_cycle_with_edge(vec* cycles, edge_t* edge, size_t min_size)
+{
+       size_t c; //cycle counter
+       size_t cycles_len = vec_length(cycles);
+       vec* cycle;
+       size_t cycle_len; 
+       vec* shortest = 0;
+
+       for (c=0; c < cycles_len; ++c) {
+               cycle = (vec*)vec_get(cycles, c);
+               cycle_len = vec_length(cycle);
+
+               if (cycle_len < min_size)
+                       continue;
+
+               if (!shortest || vec_length(shortest) > cycle_len) {
+                       if (cycle_contains_edge(cycle, edge)) {
+                               shortest = cycle;
+                       }
+               }
+       }
+       return shortest;
+}
+
+static pointf get_cycle_centroid(graph_t *g, edge_t* edge)
+{
+       static vec* cycles = 0;
+       static graph_t* ref_g = 0;
+
+       if (cycles == 0 || ref_g != g) {
+               cycles = find_all_cycles(g);
+               ref_g = g;
+       }
+
+       //find the center of the shortest cycle containing this edge
+       //cycles of length 2 do their own thing, we want 3 or
+       vec* cycle = find_shortest_cycle_with_edge(cycles, edge, 3);
+       size_t cycle_len;
+       size_t cnt = 0;
+    pointf sum = {0.0, 0.0};
+       size_t idx; //edge index
+       node_t *n;
+
+       if (!cycle)
+               return get_centroid(g);
+
+       cycle_len = vec_length(cycle);
+
+       for (idx=0; idx < cycle_len; ++idx) {
+               n = (node_t*)vec_get(cycle, idx);
+               sum.x += ND_coord(n).x;
+        sum.y += ND_coord(n).y;
+        cnt++;
+       }
+
+       sum.x = sum.x / cnt;
+    sum.y = sum.y / cnt;
+    return sum;
+}
+#endif
+
 static void bend(pointf spl[4], pointf centroid)
 {
     pointf  midpt,a;
@@ -980,7 +1218,11 @@ makeStraightEdges(graph_t * g, edge_t** edges, int e_cnt, int et, splineInfo* si
     p = dumb[1] = dumb[0] = add_pointf(ND_coord(n), ED_tail_port(e).p);
     q = dumb[2] = dumb[3] = add_pointf(ND_coord(head), ED_head_port(e).p);
     if ((e_cnt == 1) || Concentrate) {
-       if (curved) bend(dumb,get_centroid(g));
+#ifndef __CYCLE_CENTROID
+    if (curved) bend(dumb,get_centroid(g));
+#else
+       if (curved) bend(dumb,get_cycle_centroid(g, edges[0]));
+#endif
        clip_and_install(e, aghead(e), dumb, 4, sinfo);
        addEdgeLabels(g, e, p, q);
        return;