/* new_virtual_edge: * Create and return a new virtual edge e attached to orig. * ED_to_orig(e) = orig * ED_to_virt(orig) = e if e is the first virtual edge attached. * orig might be an input edge, reverse of an input edge, or virtual edge */ edge_t *new_virtual_edge(node_t * u, node_t * v, edge_t * orig) { edge_t *e; e = NEW(edge_t); e->tail = u; e->head = v; ED_edge_type(e) = VIRTUAL; if (orig) { e->id = orig->id; ED_count(e) = ED_count(orig); ED_xpenalty(e) = ED_xpenalty(orig); ED_weight(e) = ED_weight(orig); ED_minlen(e) = ED_minlen(orig); if (e->tail == orig->tail) ED_tail_port(e) = ED_tail_port(orig); else if (e->tail == orig->head) ED_tail_port(e) = ED_head_port(orig); if (e->head == orig->head) ED_head_port(e) = ED_head_port(orig); else if (e->head == orig->tail) ED_head_port(e) = ED_tail_port(orig); if (ED_to_virt(orig) == NULL) ED_to_virt(orig) = e; ED_to_orig(e) = orig; } else ED_minlen(e) = ED_count(e) = ED_xpenalty(e) = ED_weight(e) = 1; return e; }
static bool bothdowncandidates(node_t * u, node_t * v) { edge_t *e, *f; e = ND_in(u).list[0]; f = ND_in(v).list[0]; if (downcandidate(v) && (e->tail == f->tail)) { return samedir(e, f) && (portcmp(ED_tail_port(e), ED_tail_port(f)) == 0); } return FALSE; }
/* getPath * Construct the shortest path from one endpoint of e to the other. * The obstacles and their number are given by obs and npoly. * vconfig is a precomputed data structure to help in the computation. * If chkPts is true, the function finds the polygons, if any, containing * the endpoints and tells the shortest path computation to ignore them. * Assumes this info is set in ND_lim, usually from _spline_edges. * Returns the shortest path. */ Ppolyline_t getPath(edge_t * e, vconfig_t * vconfig, int chkPts, Ppoly_t ** obs, int npoly) { Ppolyline_t line; int pp, qp; Ppoint_t p, q; p = add_pointf(ND_coord(agtail(e)), ED_tail_port(e).p); q = add_pointf(ND_coord(aghead(e)), ED_head_port(e).p); /* determine the polygons (if any) that contain the endpoints */ pp = qp = POLYID_NONE; if (chkPts) { pp = ND_lim(agtail(e)); qp = ND_lim(aghead(e)); /* for (i = 0; i < npoly; i++) { if ((pp == POLYID_NONE) && in_poly(*obs[i], p)) pp = i; if ((qp == POLYID_NONE) && in_poly(*obs[i], q)) qp = i; } */ } Pobspath(vconfig, p, pp, q, qp, &line); return line; }
/* equivEdge: * See if we have already encountered an edge between the same * node:port pairs. If so, return the earlier edge. If not, * this edge is added to map and returned. * We first have to canonicalize the key fields using a lexicographic * ordering. */ static edge_t *equivEdge(Dt_t * map, edge_t * e) { edgeinfo test; edgeitem dummy; edgeitem *ip; if (agtail(e) < aghead(e)) { test.n1 = agtail(e); test.p1 = ED_tail_port(e).p; test.n2 = aghead(e); test.p2 = ED_head_port(e).p; } else if (agtail(e) > aghead(e)) { test.n2 = agtail(e); test.p2 = ED_tail_port(e).p; test.n1 = aghead(e); test.p1 = ED_head_port(e).p; } else { pointf hp = ED_head_port(e).p; pointf tp = ED_tail_port(e).p; if (tp.x < hp.x) { test.p1 = tp; test.p2 = hp; } else if (tp.x > hp.x) { test.p1 = hp; test.p2 = tp; } else if (tp.y < hp.y) { test.p1 = tp; test.p2 = hp; } else if (tp.y > hp.y) { test.p1 = hp; test.p2 = tp; } else { test.p1 = test.p2 = tp; } test.n2 = test.n1 = agtail(e); } dummy.id = test; dummy.e = e; ip = dtinsert(map, &dummy); return ip->e; }
/* makeMultiSpline: * FIX: we don't really use the shortest path provided by ED_path, * so avoid in neato spline code. * Return 0 on success. */ int makeMultiSpline(graph_t* g, edge_t* e, router_t * rtr, int doPolyline) { Ppolyline_t line = ED_path(e); node_t *t = agtail(e); node_t *h = aghead(e); pointf t_p = line.ps[0]; pointf h_p = line.ps[line.pn - 1]; tripoly_t *poly; int idx; int *sp; int t_id = rtr->tn; int h_id = rtr->tn + 1; int ecnt = rtr->tg->nedges; PPQ pq; PQTYPE *idxs; PQVTYPE *vals; int ret; /* Add endpoints to triangle graph */ addEndpoint(rtr, t_p, t, t_id, ED_tail_port(e).side); addEndpoint(rtr, h_p, h, h_id, ED_head_port(e).side); /* Initialize priority queue */ PQgen(&pq.pq, rtr->tn + 2, -1); idxs = N_GNEW(pq.pq.PQsize + 1, PQTYPE); vals = N_GNEW(pq.pq.PQsize + 1, PQVTYPE); vals[0] = 0; pq.vals = vals + 1; pq.idxs = idxs + 1; /* Find shortest path of triangles */ sp = triPath(rtr->tg, rtr->tn+2, h_id, t_id, (PQ *) & pq); free(vals); free(idxs); PQfree(&(pq.pq), 0); /* Use path of triangles to generate guiding polygon */ poly = mkPoly(rtr, sp, h_id, t_id, h_p, t_p, &idx); free(sp); /* Generate multiple splines using polygon */ ret = genroute(g, poly, 0, idx, e, doPolyline); freeTripoly (poly); resetGraph(rtr->tg, rtr->tn, ecnt); return ret; }
/* html_path: * Return a box in a table containing the given endpoint. * If the top flow is text (no internal structure), return * the box of the flow * Else return the box of the subtable containing the point. * Because of spacing, the point might not be in any subtable. * In that case, return the top flow's box. * Note that box[0] must contain the edge point. Additional boxes * move out to the boundary. * * At present, unimplemented, since the label may be inside a * non-box node and we need to figure out what this means. */ int html_path(node_t * n, port* p, int side, box * rv, int *k) { #ifdef UNIMPL point p; tbl_t *info; tbl_t *t; box b; int i; info = (tbl_t *) ND_shape_info(n); assert(info->tbls); info = info->tbls[0]; /* top-level flow */ assert(IS_FLOW(info)); b = info->box; if (info->tbl) { info = info->tbl; if (pt == 1) p = ED_tail_port(e).p; else p = ED_head_port(e).p; p = flip_pt(p, GD_rankdir(n->graph)); /* move p to node's coordinate system */ for (i = 0; (t = info->tbls[i]) != 0; i++) if (INSIDE(p, t->box)) { b = t->box; break; } } /* move box into layout coordinate system */ if (GD_flip(n->graph)) b = flip_trans_box(b, ND_coord_i(n)); else b = move_box(b, ND_coord_i(n)); *k = 1; *rv = b; if (pt == 1) return BOTTOM; else return TOP; #endif return 0; }
/* makeStraightEdge: * * FIX: handle ports on boundary? */ void makeStraightEdge(graph_t * g, edge_t * e, int et, splineInfo* sinfo) { pointf dumb[4]; node_t *n = agtail(e); node_t *head = aghead(e); int e_cnt = ED_count(e); int curved = (et == ET_CURVED); pointf perp; pointf del; edge_t *e0; int i, j, xstep, dx; double l_perp; pointf dumber[4]; pointf p, q; 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)); clip_and_install(e, aghead(e), dumb, 4, sinfo); addEdgeLabels(g, e, p, q); return; } e0 = e; if (APPROXEQPT(dumb[0], dumb[3], MILLIPOINT)) { /* degenerate case */ dumb[1] = dumb[0]; dumb[2] = dumb[3]; del.x = 0; del.y = 0; } else { perp.x = dumb[0].y - dumb[3].y; perp.y = dumb[3].x - dumb[0].x; l_perp = LEN(perp.x, perp.y); xstep = GD_nodesep(g->root); dx = xstep * (e_cnt - 1) / 2; dumb[1].x = dumb[0].x + (dx * perp.x) / l_perp; dumb[1].y = dumb[0].y + (dx * perp.y) / l_perp; dumb[2].x = dumb[3].x + (dx * perp.x) / l_perp; dumb[2].y = dumb[3].y + (dx * perp.y) / l_perp; del.x = -xstep * perp.x / l_perp; del.y = -xstep * perp.y / l_perp; } for (i = 0; i < e_cnt; i++) { if (aghead(e0) == head) { p = dumb[0]; q = dumb[3]; for (j = 0; j < 4; j++) { dumber[j] = dumb[j]; } } else { p = dumb[3]; q = dumb[0]; for (j = 0; j < 4; j++) { dumber[3 - j] = dumb[j]; } } if (et == ET_PLINE) { Ppoint_t pts[4]; Ppolyline_t spl, line; line.pn = 4; line.ps = pts; for (j=0; j < 4; j++) { pts[j] = dumber[j]; } make_polyline (line, &spl); clip_and_install(e0, aghead(e0), spl.ps, spl.pn, sinfo); } else clip_and_install(e0, aghead(e0), dumber, 4, sinfo); addEdgeLabels(g, e0, p, q); e0 = ED_to_virt(e0); dumb[1].x += del.x; dumb[1].y += del.y; dumb[2].x += del.x; dumb[2].y += del.y; } }
/* strdup_and_subst_obj0: * Replace various escape sequences with the name of the associated * graph object. A double backslash \\ can be used to avoid a replacement. * If escBackslash is true, convert \\ to \; else leave alone. All other dyads * of the form \. are passed through unchanged. */ static char *strdup_and_subst_obj0 (char *str, void *obj, int escBackslash) { char c, *s, *p, *t, *newstr; char *tp_str = "", *hp_str = ""; char *g_str = "\\G", *n_str = "\\N", *e_str = "\\E", *h_str = "\\H", *t_str = "\\T", *l_str = "\\L"; int g_len = 2, n_len = 2, e_len = 2, h_len = 2, t_len = 2, l_len = 2, tp_len = 0, hp_len = 0; int newlen = 0; int isEdge = 0; textlabel_t *tl; port pt; /* prepare substitution strings */ switch (agobjkind(obj)) { case AGRAPH: g_str = agnameof((graph_t *)obj); g_len = strlen(g_str); tl = GD_label((graph_t *)obj); if (tl) { l_str = tl->text; if (str) l_len = strlen(l_str); } break; case AGNODE: g_str = agnameof(agraphof((node_t *)obj)); g_len = strlen(g_str); n_str = agnameof((node_t *)obj); n_len = strlen(n_str); tl = ND_label((node_t *)obj); if (tl) { l_str = tl->text; if (str) l_len = strlen(l_str); } break; case AGEDGE: isEdge = 1; g_str = agnameof(agroot(agraphof(agtail(((edge_t *)obj))))); g_len = strlen(g_str); t_str = agnameof(agtail(((edge_t *)obj))); t_len = strlen(t_str); pt = ED_tail_port((edge_t *)obj); if ((tp_str = pt.name)) tp_len = strlen(tp_str); h_str = agnameof(aghead(((edge_t *)obj))); h_len = strlen(h_str); pt = ED_head_port((edge_t *)obj); if ((hp_str = pt.name)) hp_len = strlen(hp_str); h_len = strlen(h_str); tl = ED_label((edge_t *)obj); if (tl) { l_str = tl->text; if (str) l_len = strlen(l_str); } if (agisdirected(agroot(agraphof(agtail(((edge_t*)obj)))))) e_str = "->"; else e_str = "--"; e_len = t_len + (tp_len?tp_len+1:0) + 2 + h_len + (hp_len?hp_len+1:0); break; } /* two passes over str. * * first pass prepares substitution strings and computes * total length for newstring required from malloc. */ for (s = str; (c = *s++);) { if (c == '\\') { switch (c = *s++) { case 'G': newlen += g_len; break; case 'N': newlen += n_len; break; case 'E': newlen += e_len; break; case 'H': newlen += h_len; break; case 'T': newlen += t_len; break; case 'L': newlen += l_len; break; case '\\': if (escBackslash) { newlen += 1; break; } /* Fall through */ default: /* leave other escape sequences unmodified, e.g. \n \l \r */ newlen += 2; } } else { newlen++; } } /* allocate new string */ newstr = gmalloc(newlen + 1); /* second pass over str assembles new string */ for (s = str, p = newstr; (c = *s++);) { if (c == '\\') { switch (c = *s++) { case 'G': for (t = g_str; (*p = *t++); p++); break; case 'N': for (t = n_str; (*p = *t++); p++); break; case 'E': if (isEdge) { for (t = t_str; (*p = *t++); p++); if (tp_len) { *p++ = ':'; for (t = tp_str; (*p = *t++); p++); } for (t = e_str; (*p = *t++); p++); for (t = h_str; (*p = *t++); p++); if (hp_len) { *p++ = ':'; for (t = hp_str; (*p = *t++); p++); } } break; case 'T': for (t = t_str; (*p = *t++); p++); break; case 'H': for (t = h_str; (*p = *t++); p++); break; case 'L': for (t = l_str; (*p = *t++); p++); break; case '\\': if (escBackslash) { *p++ = '\\'; break; } /* Fall through */ default: /* leave other escape sequences unmodified, e.g. \n \l \r */ *p++ = '\\'; *p++ = c; break; } } else { *p++ = c; } } *p++ = '\0'; return newstr; }
/* return TRUE if edge has label */ int common_init_edge(edge_t * e) { char *s; int html = 0, r = 0; struct fontinfo fi; struct fontinfo lfi; fi.fontname = NULL; lfi.fontname = NULL; if (E_label && (s = agxget(e, E_label->index)) && (s[0])) { r = 1; html = aghtmlstr(s); if (html) s = strdup(s); else s = strdup_and_subst_edge(s, e); initFontEdgeAttr(e, &fi); ED_label(e) = make_label(html, s, fi.fontsize, fi.fontname, fi.fontcolor, e->tail->graph); #ifdef ENABLE_HTML //maks if (html) { if (make_html_label(ED_label(e), e) == 1) edgeError(e, "label"); } #endif GD_has_labels(e->tail->graph) |= EDGE_LABEL; ED_label_ontop(e) = mapbool(late_string(e, E_label_float, "false")); } /* vladimir */ if (E_headlabel && (s = agxget(e, E_headlabel->index)) && (s[0])) { html = aghtmlstr(s); if (html) s = strdup(s); else s = strdup_and_subst_edge(s, e); initFontLabelEdgeAttr(e, &fi, &lfi); ED_head_label(e) = make_label(html, s, lfi.fontsize, lfi.fontname, lfi.fontcolor, e->tail->graph); #ifdef ENABLE_HTML //maks if (html) { if (make_html_label(ED_head_label(e), e) == 1) edgeError(e, "head label"); } #endif GD_has_labels(e->tail->graph) |= HEAD_LABEL; } if (E_taillabel && (s = agxget(e, E_taillabel->index)) && (s[0])) { html = aghtmlstr(s); if (html) s = strdup(s); else s = strdup_and_subst_edge(s, e); if (!lfi.fontname) initFontLabelEdgeAttr(e, &fi, &lfi); ED_tail_label(e) = make_label(html, s, lfi.fontsize, lfi.fontname, lfi.fontcolor, e->tail->graph); #ifdef ENABLE_HTML //maks if (html) { if (make_html_label(ED_tail_label(e), e) == 1) edgeError(e, "tail label"); } #endif GD_has_labels(e->tail->graph) |= TAIL_LABEL; } /* end vladimir */ /* We still accept ports beginning with colons but this is deprecated */ s = agget(e, TAIL_ID); if (s[0]) ND_has_port(e->tail) = TRUE; ED_tail_port(e) = chkPort (ND_shape(e->tail)->fns->portfn,e->tail, s); if (noClip(e, E_tailclip)) ED_tail_port(e).clip = FALSE; s = agget(e, HEAD_ID); if (s[0]) ND_has_port(e->head) = TRUE; ED_head_port(e) = chkPort(ND_shape(e->head)->fns->portfn,e->head, s); if (noClip(e, E_headclip)) ED_head_port(e).clip = FALSE; return r; }
static void attachOrthoEdges (Agraph_t* g, maze* mp, int n_edges, route* route_list, splineInfo *sinfo, epair_t es[], int doLbls) { int irte = 0; int i, ipt, npts; pointf* ispline = 0; int splsz = 0; pointf p, p1, q1; route rte; segment* seg; Agedge_t* e; textlabel_t* lbl; for (; irte < n_edges; irte++) { e = es[irte].e; p1 = addPoints(ND_coord(agtail(e)), ED_tail_port(e).p); q1 = addPoints(ND_coord(aghead(e)), ED_head_port(e).p); rte = route_list[irte]; npts = 1 + 3*rte.n; if (npts > splsz) { if (ispline) free (ispline); ispline = N_GNEW(npts, pointf); splsz = npts; } seg = rte.segs; if (seg->isVert) { p.x = vtrack(seg, mp); p.y = p1.y; } else { p.y = htrack(seg, mp); p.x = p1.x; } ispline[0] = ispline[1] = p; ipt = 2; for (i = 1;i<rte.n;i++) { seg = rte.segs+i; if (seg->isVert) p.x = vtrack(seg, mp); else p.y = htrack(seg, mp); ispline[ipt+2] = ispline[ipt+1] = ispline[ipt] = p; ipt += 3; } if (seg->isVert) { p.x = vtrack(seg, mp); p.y = q1.y; } else { p.y = htrack(seg, mp); p.x = q1.x; } ispline[ipt] = ispline[ipt+1] = p; if (Verbose > 1) fprintf(stderr, "ortho %s %s\n", agnameof(agtail(e)),agnameof(aghead(e))); clip_and_install(e, aghead(e), ispline, npts, sinfo); if (doLbls && (lbl = ED_label(e)) && !lbl->set) addEdgeLabels(g, e, p1, q1); } free(ispline); }
/* return true if edge has label */ int common_init_edge(edge_t * e) { char *str; int r = 0; struct fontinfo fi; struct fontinfo lfi; graph_t *sg = agraphof(agtail(e)); fi.fontname = NULL; lfi.fontname = NULL; if (E_label && (str = agxget(e, E_label)) && (str[0])) { r = 1; initFontEdgeAttr(e, &fi); ED_label(e) = make_label((void*)e, str, (aghtmlstr(str) ? LT_HTML : LT_NONE), fi.fontsize, fi.fontname, fi.fontcolor); GD_has_labels(sg) |= EDGE_LABEL; ED_label_ontop(e) = mapbool(late_string(e, E_label_float, "false")); } if (E_xlabel && (str = agxget(e, E_xlabel)) && (str[0])) { if (!fi.fontname) initFontEdgeAttr(e, &fi); ED_xlabel(e) = make_label((void*)e, str, (aghtmlstr(str) ? LT_HTML : LT_NONE), fi.fontsize, fi.fontname, fi.fontcolor); GD_has_labels(sg) |= EDGE_XLABEL; } /* vladimir */ if (E_headlabel && (str = agxget(e, E_headlabel)) && (str[0])) { initFontLabelEdgeAttr(e, &fi, &lfi); ED_head_label(e) = make_label((void*)e, str, (aghtmlstr(str) ? LT_HTML : LT_NONE), lfi.fontsize, lfi.fontname, lfi.fontcolor); GD_has_labels(sg) |= HEAD_LABEL; } if (E_taillabel && (str = agxget(e, E_taillabel)) && (str[0])) { if (!lfi.fontname) initFontLabelEdgeAttr(e, &fi, &lfi); ED_tail_label(e) = make_label((void*)e, str, (aghtmlstr(str) ? LT_HTML : LT_NONE), lfi.fontsize, lfi.fontname, lfi.fontcolor); GD_has_labels(sg) |= TAIL_LABEL; } /* end vladimir */ /* We still accept ports beginning with colons but this is deprecated * That is, we allow tailport = ":abc" as well as the preferred * tailport = "abc". */ str = agget(e, TAIL_ID); /* libgraph always defines tailport/headport; libcgraph doesn't */ if (!str) str = ""; if (str && str[0]) ND_has_port(agtail(e)) = TRUE; ED_tail_port(e) = chkPort (ND_shape(agtail(e))->fns->portfn, agtail(e), str); if (noClip(e, E_tailclip)) ED_tail_port(e).clip = FALSE; str = agget(e, HEAD_ID); /* libgraph always defines tailport/headport; libcgraph doesn't */ if (!str) str = ""; if (str && str[0]) ND_has_port(aghead(e)) = TRUE; ED_head_port(e) = chkPort(ND_shape(aghead(e))->fns->portfn, aghead(e), str); if (noClip(e, E_headclip)) ED_head_port(e).clip = FALSE; return r; }