Graphviz 14.1.2~dev.20260118.2044
Loading...
Searching...
No Matches
utils.c
Go to the documentation of this file.
1
3/*************************************************************************
4 * Copyright (c) 2011 AT&T Intellectual Property
5 * All rights reserved. This program and the accompanying materials
6 * are made available under the terms of the Eclipse Public License v1.0
7 * which accompanies this distribution, and is available at
8 * https://www.eclipse.org/legal/epl-v10.html
9 *
10 * Contributors: Details at https://graphviz.org
11 *************************************************************************/
12
13#include "config.h"
14
15#include <common/render.h>
16#include <common/geomprocs.h>
17#include <common/htmltable.h>
18#include <common/entities.h>
19#include <limits.h>
20#include <math.h>
21#include <gvc/gvc.h>
22#include <stdatomic.h>
23#include <stddef.h>
24#include <stdbool.h>
25#include <stdint.h>
26#include <unistd.h>
27#include <util/agxbuf.h>
28#include <util/alloc.h>
29#include <util/gv_ctype.h>
30#include <util/gv_math.h>
31#include <util/list.h>
32#include <util/path.h>
33#include <util/startswith.h>
34#include <util/strcasecmp.h>
35#include <util/streq.h>
36#include <util/strview.h>
37#include <util/tokenize.h>
38
39int late_int(void *obj, attrsym_t *attr, int defaultValue, int minimum) {
40 if (attr == NULL)
41 return defaultValue;
42 char *p = agxget(obj, attr);
43 if (!p || p[0] == '\0')
44 return defaultValue;
45 char *endp;
46 long rv = strtol(p, &endp, 10);
47 if (p == endp || rv > INT_MAX)
48 return defaultValue; /* invalid int format */
49 if (rv < minimum)
50 return minimum;
51 return (int)rv;
52}
53
54double late_double(void *obj, attrsym_t *attr, double defaultValue,
55 double minimum) {
56 if (!attr || !obj)
57 return defaultValue;
58 char *p = agxget(obj, attr);
59 if (!p || p[0] == '\0')
60 return defaultValue;
61 char *endp;
62 double rv = strtod(p, &endp);
63 if (p == endp)
64 return defaultValue; /* invalid double format */
65 if (rv < minimum)
66 return minimum;
67 return rv;
68}
69
78 if (PSinputscale > 0) return PSinputscale; /* command line flag prevails */
79 double d = late_double(g, agfindgraphattr(g, "inputscale"), -1, 0);
80 if (is_exactly_zero(d)) return POINTS_PER_INCH;
81 return d;
82}
83
84char *late_string(void *obj, attrsym_t *attr, char *defaultValue) {
85 if (!attr || !obj)
86 return defaultValue;
87 return agxget(obj, attr);
88}
89
90char *late_nnstring(void *obj, attrsym_t *attr, char *defaultValue) {
91 char *rv = late_string(obj, attr, defaultValue);
92 if (!rv || (rv[0] == '\0'))
93 return defaultValue;
94 return rv;
95}
96
97bool late_bool(void *obj, attrsym_t *attr, bool defaultValue) {
98 if (attr == NULL)
99 return defaultValue;
100
101 return mapbool(agxget(obj, attr));
102}
103
105{
106 while (ND_UF_parent(n) && ND_UF_parent(n) != n) {
109 n = ND_UF_parent(n);
110 }
111 return n;
112}
113
115{
116 if (u == v)
117 return u;
118 if (ND_UF_parent(u) == NULL) {
119 ND_UF_parent(u) = u;
120 ND_UF_size(u) = 1;
121 } else
122 u = UF_find(u);
123 if (ND_UF_parent(v) == NULL) {
124 ND_UF_parent(v) = v;
125 ND_UF_size(v) = 1;
126 } else
127 v = UF_find(v);
128 /* if we have two copies of the same node, their union is just that node */
129 if (u == v)
130 return u;
131 if (ND_id(u) > ND_id(v)) {
132 ND_UF_parent(u) = v;
133 ND_UF_size(v) += ND_UF_size(u);
134 } else {
135 ND_UF_parent(v) = u;
136 ND_UF_size(u) += ND_UF_size(v);
137 v = u;
138 }
139 return v;
140}
141
143{
144 ND_UF_size(u) = 1;
145 ND_UF_parent(u) = NULL;
146 ND_ranktype(u) = NORMAL;
147}
148
150{
151 assert(u == UF_find(u));
152 ND_UF_parent(u) = v;
153 ND_UF_size(v) += ND_UF_size(u);
154}
155
157{
158 pointf r;
159
160 r.x = POINTS_PER_INCH * ND_pos(n)[0];
161 r.y = POINTS_PER_INCH * ND_pos(n)[1];
162 return r;
163}
164
165/* from Glassner's Graphics Gems */
166#define W_DEGREE 5
167
168/*
169 * Evaluate a Bézier curve at a particular parameter value
170 * Fill in control points for resulting sub-curves if "Left" and
171 * "Right" are non-null.
172 *
173 */
174pointf Bezier(const pointf *V, double t, pointf *Left, pointf *Right) {
175 const int degree = 3;
176 int i, j; /* Index variables */
177 pointf Vtemp[W_DEGREE + 1][W_DEGREE + 1];
178
179 /* Copy control points */
180 for (j = 0; j <= degree; j++) {
181 Vtemp[0][j] = V[j];
182 }
183
184 /* Triangle computation */
185 for (i = 1; i <= degree; i++) {
186 for (j = 0; j <= degree - i; j++) {
187 Vtemp[i][j].x =
188 (1.0 - t) * Vtemp[i - 1][j].x + t * Vtemp[i - 1][j + 1].x;
189 Vtemp[i][j].y =
190 (1.0 - t) * Vtemp[i - 1][j].y + t * Vtemp[i - 1][j + 1].y;
191 }
192 }
193
194 if (Left != NULL)
195 for (j = 0; j <= degree; j++)
196 Left[j] = Vtemp[j][0];
197 if (Right != NULL)
198 for (j = 0; j <= degree; j++)
199 Right[j] = Vtemp[degree - j][j];
200
201 return Vtemp[degree][0];
202}
203
204#ifdef DEBUG
205edge_t *debug_getedge(graph_t * g, char *s0, char *s1)
206{
207 node_t *n0, *n1;
208 n0 = agfindnode(g, s0);
209 n1 = agfindnode(g, s1);
210 if (n0 && n1)
211 return agfindedge(g, n0, n1);
212 return NULL;
213}
214Agraphinfo_t* GD_info(graph_t * g) { return ((Agraphinfo_t*)AGDATA(g));}
215Agnodeinfo_t* ND_info(node_t * n) { return ((Agnodeinfo_t*)AGDATA(n));}
216#endif
217
218/* safefile:
219 * Check to make sure it is okay to read in files.
220 * It returns NULL if the filename is trivial.
221 *
222 * If the application has set the SERVER_NAME environment variable,
223 * this indicates it is web-active.
224 *
225 * If filename contains multiple components, the user is
226 * warned, once, that everything to the left is ignored.
227 *
228 * For non-server applications, we use the path list in Gvimagepath to
229 * resolve relative pathnames.
230 *
231 * N.B. safefile uses a fixed buffer, so functions using it should use the
232 * value immediately or make a copy.
233 */
234#ifdef _WIN32
235#define PATHSEP ";"
236#else
237#define PATHSEP ":"
238#endif
239
240typedef LIST(strview_t) strviews_t;
241
242static strviews_t mkDirlist(const char *list) {
243 strviews_t dirs = {0};
244
245 for (tok_t t = tok(list, PATHSEP); !tok_end(&t); tok_next(&t)) {
246 strview_t dir = tok_get(&t);
247 LIST_APPEND(&dirs, dir);
248 }
249 return dirs;
250}
251
252static char *findPath(const strviews_t dirs, const char *str) {
253 static agxbuf safefilename;
254
255 for (size_t i = 0; i < LIST_SIZE(&dirs); ++i) {
256 const strview_t d = LIST_GET(&dirs, i);
257 agxbprint(&safefilename, "%.*s%c%s", (int)d.size, d.data, PATH_SEPARATOR, str);
258 char *filename = agxbuse(&safefilename);
259 if (access(filename, R_OK) == 0)
260 return filename;
261 }
262 return NULL;
263}
264
265const char *safefile(const char *filename)
266{
267 static bool onetime = true;
268 static char *pathlist = NULL;
269 static strviews_t dirs;
270
271 if (!filename || !filename[0])
272 return NULL;
273
274 if (HTTPServerEnVar) { /* If used as a server */
275 if (onetime) {
277 "file loading is disabled because the environment contains SERVER_NAME=\"%s\"\n",
279 onetime = false;
280 }
281 return NULL;
282 }
283
284 if (Gvfilepath != NULL) {
285 if (pathlist == NULL) {
286 LIST_FREE(&dirs);
287 pathlist = Gvfilepath;
288 dirs = mkDirlist(pathlist);
289 }
290
291 const char *str = filename;
292 for (const char *sep = "/\\:"; *sep != '\0'; ++sep) {
293 const char *p = strrchr(str, *sep);
294 if (p != NULL) {
295 str = ++p;
296 }
297 }
298
299 return findPath(dirs, str);
300 }
301
302 if (pathlist != Gvimagepath) {
303 LIST_FREE(&dirs);
304 pathlist = Gvimagepath;
305 if (pathlist && *pathlist)
306 dirs = mkDirlist(pathlist);
307 }
308
309 if (*filename == PATH_SEPARATOR || LIST_IS_EMPTY(&dirs))
310 return filename;
311
312 return findPath(dirs, filename);
313}
314
315int maptoken(char *p, char **name, int *val) {
316 char *q;
317
318 int i = 0;
319 for (; (q = name[i]) != 0; i++)
320 if (p && streq(p, q))
321 break;
322 return val[i];
323}
324
325bool mapBool(const char *p, bool defaultValue) {
326 if (!p || *p == '\0')
327 return defaultValue;
328 if (!strcasecmp(p, "false"))
329 return false;
330 if (!strcasecmp(p, "no"))
331 return false;
332 if (!strcasecmp(p, "true"))
333 return true;
334 if (!strcasecmp(p, "yes"))
335 return true;
336 if (gv_isdigit(*p))
337 return atoi(p) != 0;
338 return defaultValue;
339}
340
341bool mapbool(const char *p)
342{
343 return mapBool(p, false);
344}
345
347{
348 double bestdist2, d2, dlow2, dhigh2; /* squares of distances */
349 double low, high, t;
350 pointf c[4], pt2;
351 bezier bz;
352
353 size_t besti = SIZE_MAX;
354 size_t bestj = SIZE_MAX;
355 bestdist2 = 1e+38;
356 for (size_t i = 0; i < spl->size; i++) {
357 bz = spl->list[i];
358 for (size_t j = 0; j < bz.size; j++) {
359 pointf b;
360
361 b.x = bz.list[j].x;
362 b.y = bz.list[j].y;
363 d2 = DIST2(b, pt);
364 if (bestj == SIZE_MAX || d2 < bestdist2) {
365 besti = i;
366 bestj = j;
367 bestdist2 = d2;
368 }
369 }
370 }
371
372 bz = spl->list[besti];
373 /* Pick best Bézier. If bestj is the last point in the B-spline, decrement.
374 * Then set j to be the first point in the corresponding Bézier by dividing
375 * then multiplying be 3. Thus, 0,1,2 => 0; 3,4,5 => 3, etc.
376 */
377 if (bestj == bz.size-1)
378 bestj--;
379 const size_t j = 3 * (bestj / 3);
380 for (size_t k = 0; k < 4; k++) {
381 c[k].x = bz.list[j + k].x;
382 c[k].y = bz.list[j + k].y;
383 }
384 low = 0.0;
385 high = 1.0;
386 dlow2 = DIST2(c[0], pt);
387 dhigh2 = DIST2(c[3], pt);
388 do {
389 t = (low + high) / 2.0;
390 pt2 = Bezier(c, t, NULL, NULL);
391 if (fabs(dlow2 - dhigh2) < 1.0)
392 break;
393 if (fabs(high - low) < .00001)
394 break;
395 if (dlow2 < dhigh2) {
396 high = t;
397 dhigh2 = DIST2(pt2, pt);
398 } else {
399 low = t;
400 dlow2 = DIST2(pt2, pt);
401 }
402 } while (1);
403 return pt2;
404}
405
406static int Tflag;
407void gvToggle(int s)
408{
409 (void)s;
410 Tflag = !Tflag;
411#if !defined(_WIN32)
412 signal(SIGUSR1, gvToggle);
413#endif
414}
415
416int test_toggle(void)
417{
418 return Tflag;
419}
420
421struct fontinfo {
422 double fontsize;
423 char *fontname;
425};
426
428{
429 struct fontinfo fi;
430 char *str;
431 ND_width(n) =
433 ND_height(n) =
435 ND_shape(n) =
437 str = agxget(n, N_label);
442 fi.fontsize, fi.fontname, fi.fontcolor);
443 if (N_xlabel && (str = agxget(n, N_xlabel)) && str[0]) {
444 ND_xlabel(n) = make_label(n, str, aghtmlstr(str), false,
445 fi.fontsize, fi.fontname, fi.fontcolor);
447 }
448
449 {
450 const int showboxes = imin(late_int(n, N_showboxes, 0, 0), UCHAR_MAX);
451 ND_showboxes(n) = (unsigned char)showboxes;
452 }
453 ND_shape(n)->fns->initfn(n);
454}
455
462
463static void
465 struct fontinfo *lfi)
466{
467 if (!fi->fontname) initFontEdgeAttr(e, fi);
471}
472
474static bool
476{
477 char *str;
478 bool rv = false;
479
480 if (sym) { /* mapbool isn't a good fit, because we want "" to mean true */
481 str = agxget(e,sym);
482 if (str && str[0]) rv = !mapbool(str);
483 else rv = false;
484 }
485 return rv;
486}
487
488static port
489chkPort (port (*pf)(node_t*, char*, char*), node_t* n, char* s)
490{
491 port pt;
492 char* cp=NULL;
493 if(s)
494 cp= strchr(s,':');
495 if (cp) {
496 *cp = '\0';
497 pt = pf(n, s, cp+1);
498 *cp = ':';
499 pt.name = cp+1;
500 }
501 else {
502 pt = pf(n, s, NULL);
503 pt.name = s;
504 }
505 return pt;
506}
507
508/* return true if edge has label */
510 char *str;
511 struct fontinfo fi;
512 struct fontinfo lfi;
513 graph_t *sg = agraphof(agtail(e));
514
515 fi.fontname = NULL;
516 lfi.fontname = NULL;
517 if (E_label && (str = agxget(e, E_label)) && str[0]) {
518 initFontEdgeAttr(e, &fi);
519 ED_label(e) = make_label(e, str, aghtmlstr(str), false,
520 fi.fontsize, fi.fontname, fi.fontcolor);
523 }
524
525 if (E_xlabel && (str = agxget(e, E_xlabel)) && str[0]) {
526 if (!fi.fontname)
527 initFontEdgeAttr(e, &fi);
528 ED_xlabel(e) = make_label(e, str, aghtmlstr(str), false,
529 fi.fontsize, fi.fontname, fi.fontcolor);
531 }
532
533 if (E_headlabel && (str = agxget(e, E_headlabel)) && str[0]) {
534 initFontLabelEdgeAttr(e, &fi, &lfi);
535 ED_head_label(e) = make_label(e, str, aghtmlstr(str), false,
536 lfi.fontsize, lfi.fontname, lfi.fontcolor);
538 }
539 if (E_taillabel && (str = agxget(e, E_taillabel)) && str[0]) {
540 if (!lfi.fontname)
541 initFontLabelEdgeAttr(e, &fi, &lfi);
542 ED_tail_label(e) = make_label(e, str, aghtmlstr(str), false,
543 lfi.fontsize, lfi.fontname, lfi.fontcolor);
545 }
546
547 /* We still accept ports beginning with colons but this is deprecated
548 * That is, we allow tailport = ":abc" as well as the preferred
549 * tailport = "abc".
550 */
551 str = agget(e, TAIL_ID);
552 /* libgraph always defines tailport/headport; libcgraph doesn't */
553 if (!str) str = "";
554 if (str && str[0])
555 ND_has_port(agtail(e)) = true;
556 ED_tail_port(e) = chkPort (ND_shape(agtail(e))->fns->portfn, agtail(e), str);
557 if (noClip(e, E_tailclip))
558 ED_tail_port(e).clip = false;
559 str = agget(e, HEAD_ID);
560 /* libgraph always defines tailport/headport; libcgraph doesn't */
561 if (!str) str = "";
562 if (str && str[0])
563 ND_has_port(aghead(e)) = true;
564 ED_head_port(e) = chkPort(ND_shape(aghead(e))->fns->portfn, aghead(e), str);
565 if (noClip(e, E_headclip))
566 ED_head_port(e).clip = false;
567}
568
569static boxf addLabelBB(boxf bb, textlabel_t * lp, bool flipxy)
570{
571 double width, height;
572 pointf p = lp->pos;
573 double min, max;
574
575 if (flipxy) {
576 height = lp->dimen.x;
577 width = lp->dimen.y;
578 }
579 else {
580 width = lp->dimen.x;
581 height = lp->dimen.y;
582 }
583 min = p.x - width / 2.;
584 max = p.x + width / 2.;
585 if (min < bb.LL.x)
586 bb.LL.x = min;
587 if (max > bb.UR.x)
588 bb.UR.x = max;
589
590 min = p.y - height / 2.;
591 max = p.y + height / 2.;
592 if (min < bb.LL.y)
593 bb.LL.y = min;
594 if (max > bb.UR.y)
595 bb.UR.y = max;
596
597 return bb;
598}
599
603boxf
605{
606 const size_t sides = poly->sides;
607 const size_t peris = MAX(poly->peripheries, (size_t)1);
608 pointf* verts = poly->vertices + (peris-1)*sides;
609 boxf bb;
610
611 bb.LL = bb.UR = verts[0];
612 for (size_t i = 1; i < sides; i++) {
613 bb.LL.x = MIN(bb.LL.x,verts[i].x);
614 bb.LL.y = MIN(bb.LL.y,verts[i].y);
615 bb.UR.x = MAX(bb.UR.x,verts[i].x);
616 bb.UR.y = MAX(bb.UR.y,verts[i].y);
617 }
618 return bb;
619}
620
625{
626 GD_bb(g) = addLabelBB(GD_bb(g), lp, GD_flip(g));
627}
628
634{
635 node_t *n;
636 edge_t *e;
637 boxf b, bb;
638 boxf BF;
639 pointf ptf, s2;
640
641 if (agnnodes(g) == 0 && GD_n_cluster(g) == 0) {
642 bb.LL = (pointf){0};
643 bb.UR = (pointf){0};
644 return;
645 }
646
647 bb.LL = (pointf){INT_MAX, INT_MAX};
648 bb.UR = (pointf){-INT_MAX, -INT_MAX};
649 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
650 ptf = coord(n);
651 s2.x = ND_xsize(n) / 2.0;
652 s2.y = ND_ysize(n) / 2.0;
653 b.LL = sub_pointf(ptf, s2);
654 b.UR = add_pointf(ptf, s2);
655
656 EXPANDBB(&bb, b);
657 if (ND_xlabel(n) && ND_xlabel(n)->set) {
658 bb = addLabelBB(bb, ND_xlabel(n), GD_flip(g));
659 }
660 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
661 if (ED_spl(e) == 0)
662 continue;
663 for (size_t i = 0; i < ED_spl(e)->size; i++) {
664 for (size_t j = 0; j < (((Agedgeinfo_t*)AGDATA(e))->spl)->list[i].size; j++) {
665 ptf = ED_spl(e)->list[i].list[j];
666 expandbp(&bb, ptf);
667 }
668 }
669 if (ED_label(e) && ED_label(e)->set) {
670 bb = addLabelBB(bb, ED_label(e), GD_flip(g));
671 }
672 if (ED_head_label(e) && ED_head_label(e)->set) {
673 bb = addLabelBB(bb, ED_head_label(e), GD_flip(g));
674 }
675 if (ED_tail_label(e) && ED_tail_label(e)->set) {
676 bb = addLabelBB(bb, ED_tail_label(e), GD_flip(g));
677 }
678 if (ED_xlabel(e) && ED_xlabel(e)->set) {
679 bb = addLabelBB(bb, ED_xlabel(e), GD_flip(g));
680 }
681 }
682 }
683
684 for (int i = 1; i <= GD_n_cluster(g); i++) {
685 B2BF(GD_bb(GD_clust(g)[i]), BF);
686 EXPANDBB(&bb, BF);
687 }
688 if (GD_label(g) && GD_label(g)->set) {
689 bb = addLabelBB(bb, GD_label(g), GD_flip(g));
690 }
691
692 GD_bb(g) = bb;
693}
694
696{
697 return g == g->root || !strncasecmp(agnameof(g), "cluster", 7) ||
698 mapbool(agget(g, "cluster"));
699}
700
704Agsym_t *setAttr(graph_t * g, void *obj, char *name, char *value,
705 Agsym_t * ap)
706{
707 if (ap == NULL) {
708 switch (agobjkind(obj)) {
709 case AGRAPH:
710 ap = agattr_text(g, AGRAPH,name, "");
711 break;
712 case AGNODE:
713 ap = agattr_text(g,AGNODE, name, "");
714 break;
715 case AGEDGE:
716 ap = agattr_text(g,AGEDGE, name, "");
717 break;
718 }
719 }
720 agxset(obj, ap, value);
721 return ap;
722}
723
729static node_t *clustNode(node_t * n, graph_t * cg, agxbuf * xb,
730 graph_t * clg)
731{
732 node_t *cn;
733 static int idx = 0;
734
735 agxbprint(xb, "__%d:%s", idx++, agnameof(cg));
736
737 cn = agnode(agroot(cg), agxbuse(xb), 1);
738 agbindrec(cn, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true);
739
740 SET_CLUST_NODE(cn);
741 agsubnode(cg,cn,1);
742 agsubnode(clg,n,1);
743
744 /* set attributes */
745 N_label = setAttr(agraphof(cn), cn, "label", "", N_label);
746 N_style = setAttr(agraphof(cn), cn, "style", "invis", N_style);
747 N_shape = setAttr(agraphof(cn), cn, "shape", "box", N_shape);
748
749 return cn;
750}
751
752typedef struct {
753 Dtlink_t link; /* cdt data */
754 void *p[2]; /* key */
757} item;
758
759static int cmpItem(void *pp1, void *pp2) {
760 const void **p1 = pp1;
761 const void **p2 = pp2;
762 if ((uintptr_t)p1[0] < (uintptr_t)p2[0])
763 return -1;
764 if ((uintptr_t)p1[0] > (uintptr_t)p2[0])
765 return 1;
766 if ((uintptr_t)p1[1] < (uintptr_t)p2[1])
767 return -1;
768 if ((uintptr_t)p1[1] > (uintptr_t)p2[1])
769 return 1;
770 return 0;
771}
772
773static void *newItem(void *p, Dtdisc_t *disc) {
774 item *objp = p;
775 item *newp = gv_alloc(sizeof(item));
776
777 (void)disc;
778 newp->p[0] = objp->p[0];
779 newp->p[1] = objp->p[1];
780 newp->t = objp->t;
781 newp->h = objp->h;
782
783 return newp;
784}
785
787 .key = offsetof(item, p),
788 .size = sizeof(2 * sizeof(void *)),
789 .link = offsetof(item, link),
790 .makef = newItem,
791 .freef = free,
792 .comparf = cmpItem,
793};
794
796static edge_t *cloneEdge(edge_t * e, node_t * ct, node_t * ch)
797{
798 graph_t *g = agraphof(ct);
799 edge_t *ce = agedge(g, ct, ch,NULL,1);
800 agbindrec(ce, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true);
801 agcopyattr(e, ce);
802 ED_compound(ce) = true;
803
804 return ce;
805}
806
807static void insertEdge(Dt_t * map, void *t, void *h, edge_t * e)
808{
809 item dummy1 = {.p = {t, h}, .t = agtail(e), .h = aghead(e)};
810 dtinsert(map, &dummy1);
811
812 item dummy2 = {.p = {h, t}, .t = aghead(e), .h = agtail(e)};
813 dtinsert(map, &dummy2);
814}
815
817static item *mapEdge(Dt_t * map, edge_t * e)
818{
819 void *key[] = {agtail(e), aghead(e)};
820 return dtmatch(map, &key);
821}
822
823static graph_t *mapc(Dt_t *cmap, node_t *n) {
824 if (startswith(agnameof(n), "cluster")) {
825 return findCluster(cmap, agnameof(n));
826 }
827 return NULL;
828}
829
845static int
846checkCompound(edge_t * e, graph_t * clg, agxbuf * xb, Dt_t * map, Dt_t* cmap)
847{
848 node_t *cn;
849 node_t *cn1;
850 node_t *t = agtail(e);
851 node_t *h = aghead(e);
852 edge_t *ce;
853 item *ip;
854
855 if (IS_CLUST_NODE(h)) return 0;
856 graph_t *const tg = mapc(cmap, t);
857 graph_t *const hg = mapc(cmap, h);
858 if (!tg && !hg)
859 return 0;
860 if (tg == hg) {
861 agwarningf("cluster cycle %s -- %s not supported\n", agnameof(t),
862 agnameof(t));
863 return 0;
864 }
865 ip = mapEdge(map, e);
866 if (ip) {
867 cloneEdge(e, ip->t, ip->h);
868 return 1;
869 }
870
871 if (hg) {
872 if (tg) {
873 if (agcontains(hg, tg)) {
874 agwarningf("tail cluster %s inside head cluster %s\n",
875 agnameof(tg), agnameof(hg));
876 return 0;
877 }
878 if (agcontains(tg, hg)) {
879 agwarningf("head cluster %s inside tail cluster %s\n",
880 agnameof(hg),agnameof(tg));
881 return 0;
882 }
883 cn = clustNode(t, tg, xb, clg);
884 cn1 = clustNode(h, hg, xb, clg);
885 ce = cloneEdge(e, cn, cn1);
886 insertEdge(map, t, h, ce);
887 } else {
888 if (agcontains(hg, t)) {
889 agwarningf("tail node %s inside head cluster %s\n",
890 agnameof(t), agnameof(hg));
891 return 0;
892 }
893 cn = clustNode(h, hg, xb, clg);
894 ce = cloneEdge(e, t, cn);
895 insertEdge(map, t, h, ce);
896 }
897 } else {
898 if (agcontains(tg, h)) {
899 agwarningf("head node %s inside tail cluster %s\n", agnameof(h),
900 agnameof(tg));
901 return 0;
902 }
903 cn = clustNode(t, tg, xb, clg);
904 ce = cloneEdge(e, cn, h);
905 insertEdge(map, t, h, ce);
906 }
907 return 1;
908}
909
910typedef struct {
913} cl_edge_t;
914
915static int
917{
918 cl_edge_t* cl_info = (cl_edge_t*)HAS_CLUST_EDGE(g);
919 if (cl_info)
920 return cl_info->n_cluster_edges;
921 return 0;
922}
923
931{
932 int num_cl_edges = 0;
933 node_t *n;
934 node_t *nxt;
935 edge_t *e;
936 graph_t *clg;
937 agxbuf xb = {0};
938 Dt_t *map;
939 Dt_t *cmap = mkClustMap (g);
940
941 map = dtopen(&mapDisc, Dtoset);
942 clg = agsubg(g, "__clusternodes",1);
943 agbindrec(clg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
944 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
945 if (IS_CLUST_NODE(n)) continue;
946 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
947 num_cl_edges += checkCompound(e, clg, &xb, map, cmap);
948 }
949 }
950 agxbfree(&xb);
951 dtclose(map);
952 for (n = agfstnode(clg); n; n = nxt) {
953 nxt = agnxtnode(clg, n);
954 agdelete(g, n);
955 }
956 agclose(clg);
957 if (num_cl_edges) {
958 cl_edge_t* cl_info;
959 cl_info = agbindrec(g, CL_EDGE_TAG, sizeof(cl_edge_t), false);
960 cl_info->n_cluster_edges = num_cl_edges;
961 }
962 dtclose(cmap);
963}
964
972static node_t *mapN(node_t * n, graph_t * clg)
973{
974 node_t *nn;
975 char *name;
976 graph_t *g = agraphof(n);
977 Agsym_t *sym;
978
979 if (!IS_CLUST_NODE(n))
980 return n;
981 agsubnode(clg, n, 1);
982 name = strchr(agnameof(n), ':');
983 assert(name);
984 name++;
985 if ((nn = agfindnode(g, name)))
986 return nn;
987 nn = agnode(g, name, 1);
988 agbindrec(nn, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true);
989 SET_CLUST_NODE(nn);
990
991 /* Set all attributes to default */
992 for (sym = agnxtattr(g, AGNODE, NULL); sym; (sym = agnxtattr(g, AGNODE, sym))) {
993 if (agxget(nn, sym) != sym->defval)
994 agxset(nn, sym, sym->defval);
995 }
996 return nn;
997}
998
999static void undoCompound(edge_t * e, graph_t * clg)
1000{
1001 node_t *t = agtail(e);
1002 node_t *h = aghead(e);
1003 node_t *ntail;
1004 node_t *nhead;
1005 edge_t* ce;
1006
1007 ntail = mapN(t, clg);
1008 nhead = mapN(h, clg);
1009 ce = cloneEdge(e, ntail, nhead);
1010
1011 /* transfer drawing information */
1012 ED_spl(ce) = ED_spl(e);
1013 ED_spl(e) = NULL;
1014 ED_label(ce) = ED_label(e);
1015 ED_label(e) = NULL;
1016 ED_xlabel(ce) = ED_xlabel(e);
1017 ED_xlabel(e) = NULL;
1019 ED_head_label(e) = NULL;
1021 ED_tail_label(e) = NULL;
1022 gv_cleanup_edge(e);
1023}
1024
1030{
1031 node_t *n;
1032 node_t *nextn;
1033 edge_t *e;
1034 graph_t *clg;
1035 int ecnt = num_clust_edges(g);
1036 int i = 0;
1037
1038 if (!ecnt) return;
1039 clg = agsubg(g, "__clusternodes",1);
1040 agbindrec(clg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
1041 edge_t **edgelist = gv_calloc(ecnt, sizeof(edge_t*));
1042 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1043 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
1044 if (ED_compound(e))
1045 edgelist[i++] = e;
1046 }
1047 }
1048 assert(i == ecnt);
1049 for (i = 0; i < ecnt; i++)
1050 undoCompound(edgelist[i], clg);
1051 free (edgelist);
1052 for (n = agfstnode(clg); n; n = nextn) {
1053 nextn = agnxtnode(clg, n);
1054 gv_cleanup_node(n);
1055 agdelete(g, n);
1056 }
1057 agclose(clg);
1058}
1059
1064attrsym_t *safe_dcl(graph_t *g, int obj_kind, char *name, char *defaultValue) {
1065 attrsym_t *a = agattr_text(g,obj_kind,name, NULL);
1066 if (!a) /* attribute does not exist */
1067 a = agattr_text(g, obj_kind, name, defaultValue);
1068 return a;
1069}
1070
1071static int comp_entities(const void *e1, const void *e2) {
1072 const strview_t *key = e1;
1073 const struct entities_s *candidate = e2;
1074 return strview_cmp(*key, strview(candidate->name, '\0'));
1075}
1076
1081char* scanEntity (char* t, agxbuf* xb)
1082{
1083 const strview_t key = strview(t, ';');
1084 struct entities_s *res;
1085
1086 agxbputc(xb, '&');
1087 if (key.data[key.size] == '\0') return t;
1088 if (key.size > ENTITY_NAME_LENGTH_MAX || key.size < 2) return t;
1089 res = bsearch(&key, entities, NR_OF_ENTITIES,
1090 sizeof(entities[0]), comp_entities);
1091 if (!res) return t;
1092 agxbprint(xb, "#%d;", res->value);
1093 return t + key.size + 1;
1094}
1095
1102static int
1104{
1105 struct entities_s *res;
1106 unsigned char* str = *(unsigned char**)s;
1107 unsigned int byte;
1108 int i, n = 0;
1109
1110 byte = *str;
1111 if (byte == '#') {
1112 byte = *(str + 1);
1113 if (byte == 'x' || byte == 'X') {
1114 for (i = 2; i < 8; i++) {
1115 byte = *(str + i);
1116 if (byte >= 'A' && byte <= 'F')
1117 byte = byte - 'A' + 10;
1118 else if (byte >= 'a' && byte <= 'f')
1119 byte = byte - 'a' + 10;
1120 else if (byte >= '0' && byte <= '9')
1121 byte = byte - '0';
1122 else
1123 break;
1124 n = n * 16 + (int)byte;
1125 }
1126 }
1127 else {
1128 for (i = 1; i < 8; i++) {
1129 byte = *(str + i);
1130 if (byte >= '0' && byte <= '9')
1131 n = n * 10 + ((int)byte - '0');
1132 else
1133 break;
1134 }
1135 }
1136 if (byte == ';') {
1137 str += i+1;
1138 }
1139 else {
1140 n = 0;
1141 }
1142 }
1143 else {
1144 strview_t key = {.data = (char *)str};
1145 for (i = 0; i < ENTITY_NAME_LENGTH_MAX; i++) {
1146 byte = *(str + i);
1147 if (byte == '\0') break;
1148 if (byte == ';') {
1149 res = bsearch(&key, entities, NR_OF_ENTITIES,
1150 sizeof(entities[0]), comp_entities);
1151 if (res) {
1152 n = res->value;
1153 str += i+1;
1154 }
1155 break;
1156 }
1157 ++key.size;
1158 }
1159 }
1160 *s = (char*)str;
1161 return n;
1162}
1163
1164static unsigned char
1165cvtAndAppend (unsigned char c, agxbuf* xb)
1166{
1167 char buf[] = {c, '\0'};
1168 char *s = latin1ToUTF8(buf);
1169 char *p = s;
1170 size_t len = strlen(s);
1171 while (len-- > 1)
1172 agxbputc(xb, *p++);
1173 c = *p;
1174 free (s);
1175 return c;
1176}
1177
1182char* htmlEntityUTF8 (char* s, graph_t* g)
1183{
1184 static graph_t* lastg;
1185 static atomic_flag warned;
1186 unsigned char c;
1187 unsigned int v;
1188
1189 int uc;
1190 int ui;
1191
1192 if (lastg != g) {
1193 lastg = g;
1194 atomic_flag_clear(&warned);
1195 }
1196
1197 agxbuf xb = {0};
1198
1199 while ((c = *(unsigned char*)s++)) {
1200 if (c < 0xC0)
1201 /*
1202 * Handles properly formed UTF-8 characters between
1203 * 0x01 and 0x7F. Also treats \0 and naked trail
1204 * bytes 0x80 to 0xBF as valid characters representing
1205 * themselves.
1206 */
1207 uc = 0;
1208 else if (c < 0xE0)
1209 uc = 1;
1210 else if (c < 0xF0)
1211 uc = 2;
1212 else if (c < 0xF8)
1213 uc = 3;
1214 else {
1215 uc = -1;
1216 if (!atomic_flag_test_and_set(&warned)) {
1217 agwarningf("UTF8 codes > 4 bytes are not currently supported (graph %s) - treated as Latin-1. Perhaps \"-Gcharset=latin1\" is needed?\n", agnameof(g));
1218 }
1219 c = cvtAndAppend (c, &xb);
1220 }
1221
1222 if (uc == 0 && c == '&') {
1223 /* replace html entity sequences like: &amp;
1224 * and: &#123; with their UTF8 equivalents */
1225 v = htmlEntity (&s);
1226 if (v) {
1227 if (v < 0x7F) /* entity needs 1 byte in UTF8 */
1228 c = v;
1229 else if (v < 0x07FF) { /* entity needs 2 bytes in UTF8 */
1230 agxbputc(&xb, (char)((v >> 6) | 0xC0));
1231 c = (v & 0x3F) | 0x80;
1232 }
1233 else { /* entity needs 3 bytes in UTF8 */
1234 agxbputc(&xb, (char)((v >> 12) | 0xE0));
1235 agxbputc(&xb, (char)(((v >> 6) & 0x3F) | 0x80));
1236 c = (v & 0x3F) | 0x80;
1237 }
1238 }
1239 }
1240 else /* copy n byte UTF8 characters */
1241 for (ui = 0; ui < uc; ++ui)
1242 if ((*s & 0xC0) == 0x80) {
1243 agxbputc(&xb, (char)c);
1244 c = *(unsigned char*)s++;
1245 }
1246 else {
1247 if (!atomic_flag_test_and_set(&warned)) {
1248 agwarningf("Invalid %d-byte UTF8 found in input of graph %s - treated as Latin-1. Perhaps \"-Gcharset=latin1\" is needed?\n", uc + 1, agnameof(g));
1249 }
1250 c = cvtAndAppend (c, &xb);
1251 break;
1252 }
1253 agxbputc(&xb, (char)c);
1254 }
1255 return agxbdisown(&xb);
1256}
1257
1259char* latin1ToUTF8 (char* s)
1260{
1261 agxbuf xb = {0};
1262 unsigned int v;
1263
1264 /* Values are either a byte (<= 256) or come from htmlEntity, whose
1265 * values are all less than 0x07FF, so we need at most 3 bytes.
1266 */
1267 while ((v = *(unsigned char*)s++)) {
1268 if (v == '&') {
1269 v = htmlEntity (&s);
1270 if (!v) v = '&';
1271 }
1272 if (v < 0x7F)
1273 agxbputc(&xb, (char)v);
1274 else if (v < 0x07FF) {
1275 agxbputc(&xb, (char)((v >> 6) | 0xC0));
1276 agxbputc(&xb, (char)((v & 0x3F) | 0x80));
1277 }
1278 else {
1279 agxbputc(&xb, (char)((v >> 12) | 0xE0));
1280 agxbputc(&xb, (char)(((v >> 6) & 0x3F) | 0x80));
1281 agxbputc(&xb, (char)((v & 0x3F) | 0x80));
1282 }
1283 }
1284 return agxbdisown(&xb);
1285}
1286
1291char*
1293{
1294 agxbuf xb = {0};
1295 unsigned char c;
1296
1297 while ((c = *(unsigned char*)s++)) {
1298 if (c < 0x7F)
1299 agxbputc(&xb, (char)c);
1300 else {
1301 unsigned char outc = (c & 0x03) << 6;
1302 c = *(unsigned char *)s++;
1303 outc = outc | (c & 0x3F);
1304 agxbputc(&xb, (char)outc);
1305 }
1306 }
1307 return agxbdisown(&xb);
1308}
1309
1311 if (! OVERLAP(b, ND_bb(n)))
1312 return false;
1313
1314 /* FIXME - need to do something better about CLOSEENOUGH */
1315 pointf p = sub_pointf(ND_coord(n), mid_pointf(b.UR, b.LL));
1316
1317 inside_t ictxt = {.s.n = n};
1318
1319 return ND_shape(n)->fns->insidefn(&ictxt, p);
1320}
1321
1323{
1324 const pointf s = {.x = lp->dimen.x / 2.0, .y = lp->dimen.y / 2.0};
1325 boxf bb = {.LL = sub_pointf(lp->pos, s), .UR = add_pointf(lp->pos, s)};
1326 return OVERLAP(b, bb);
1327}
1328
1329static bool overlap_arrow(pointf p, pointf u, double scale, boxf b)
1330{
1331 // FIXME - check inside arrow shape
1332 return OVERLAP(b, arrow_bb(p, u, scale));
1333}
1334
1335static bool overlap_bezier(bezier bz, boxf b) {
1336 assert(bz.size);
1337 pointf u = bz.list[0];
1338 for (size_t i = 1; i < bz.size; i++) {
1339 pointf p = bz.list[i];
1340 if (lineToBox(p, u, b) != -1)
1341 return true;
1342 u = p;
1343 }
1344
1345 /* check arrows */
1346 if (bz.sflag) {
1347 if (overlap_arrow(bz.sp, bz.list[0], 1, b))
1348 return true;
1349 }
1350 if (bz.eflag) {
1351 if (overlap_arrow(bz.ep, bz.list[bz.size - 1], 1, b))
1352 return true;
1353 }
1354 return false;
1355}
1356
1358{
1359 splines *spl = ED_spl(e);
1360 if (spl && boxf_overlap(spl->bb, b))
1361 for (size_t i = 0; i < spl->size; i++)
1362 if (overlap_bezier(spl->list[i], b))
1363 return true;
1364
1365 textlabel_t *lp = ED_label(e);
1366 if (lp && overlap_label(lp, b))
1367 return true;
1368
1369 return false;
1370}
1371
1373static int edgeType(const char *s, int defaultValue) {
1374 if (s == NULL || strcmp(s, "") == 0) {
1375 return defaultValue;
1376 }
1377
1378 if (*s == '0') { /* false */
1379 return EDGETYPE_LINE;
1380 } else if (*s >= '1' && *s <= '9') { /* true */
1381 return EDGETYPE_SPLINE;
1382 } else if (strcasecmp(s, "curved") == 0) {
1383 return EDGETYPE_CURVED;
1384 } else if (strcasecmp(s, "compound") == 0) {
1385 return EDGETYPE_COMPOUND;
1386 } else if (strcasecmp(s, "false") == 0) {
1387 return EDGETYPE_LINE;
1388 } else if (strcasecmp(s, "line") == 0) {
1389 return EDGETYPE_LINE;
1390 } else if (strcasecmp(s, "none") == 0) {
1391 return EDGETYPE_NONE;
1392 } else if (strcasecmp(s, "no") == 0) {
1393 return EDGETYPE_LINE;
1394 } else if (strcasecmp(s, "ortho") == 0) {
1395 return EDGETYPE_ORTHO;
1396 } else if (strcasecmp(s, "polyline") == 0) {
1397 return EDGETYPE_PLINE;
1398 } else if (strcasecmp(s, "spline") == 0) {
1399 return EDGETYPE_SPLINE;
1400 } else if (strcasecmp(s, "true") == 0) {
1401 return EDGETYPE_SPLINE;
1402 } else if (strcasecmp(s, "yes") == 0) {
1403 return EDGETYPE_SPLINE;
1404 }
1405
1406 agwarningf("Unknown \"splines\" value: \"%s\" - ignored\n", s);
1407 return defaultValue;
1408}
1409
1422void setEdgeType(graph_t *g, int defaultValue) {
1423 char* s = agget(g, "splines");
1424 int et;
1425
1426 if (!s) {
1427 et = defaultValue;
1428 }
1429 else if (*s == '\0') {
1430 et = EDGETYPE_NONE;
1431 } else {
1432 et = edgeType(s, defaultValue);
1433 }
1434 GD_flags(g) |= et;
1435}
1436
1445void get_gradient_points(pointf *A, pointf *G, size_t n, double angle, int flags) {
1446 pointf min,max,center;
1447 int isRadial = flags & 1;
1448 int isRHS = flags & 2;
1449
1450 if (n == 2) {
1451 double rx = A[1].x - A[0].x;
1452 double ry = A[1].y - A[0].y;
1453 min.x = A[0].x - rx;
1454 max.x = A[0].x + rx;
1455 min.y = A[0].y - ry;
1456 max.y = A[0].y + ry;
1457 }
1458 else {
1459 min.x = max.x = A[0].x;
1460 min.y = max.y = A[0].y;
1461 for (size_t i = 0; i < n; i++) {
1462 min.x = MIN(A[i].x, min.x);
1463 min.y = MIN(A[i].y, min.y);
1464 max.x = MAX(A[i].x, max.x);
1465 max.y = MAX(A[i].y, max.y);
1466 }
1467 }
1468 center.x = min.x + (max.x - min.x)/2;
1469 center.y = min.y + (max.y - min.y)/2;
1470 if (isRadial) {
1471 double inner_r, outer_r;
1472 outer_r = hypot(center.x - min.x, center.y - min.y);
1473 inner_r = outer_r /4.;
1474 if (isRHS) {
1475 G[0].y = center.y;
1476 }
1477 else {
1478 G[0].y = -center.y;
1479 }
1480 G[0].x = center.x;
1481 G[1].x = inner_r;
1482 G[1].y = outer_r;
1483 }
1484 else {
1485 double half_x = max.x - center.x;
1486 double half_y = max.y - center.y;
1487 double sina = sin(angle);
1488 double cosa = cos(angle);
1489 if (isRHS) {
1490 G[0].y = center.y - half_y * sina;
1491 G[1].y = center.y + half_y * sina;
1492 }
1493 else {
1494 G[0].y = -center.y + (max.y - center.y) * sin(angle);
1495 G[1].y = -center.y - (center.y - min.y) * sin(angle);
1496 }
1497 G[0].x = center.x - half_x * cosa;
1498 G[1].x = center.x + half_x * cosa;
1499 }
1500}
1501
1503 if (ED_spl(e)) {
1504 for (size_t i = 0; i < ED_spl(e)->size; i++)
1505 free(ED_spl(e)->list[i].list);
1506 free(ED_spl(e)->list);
1507 free(ED_spl(e));
1508 }
1509 ED_spl(e) = NULL;
1510}
1511
1513{
1514 free(ED_path(e).ps);
1515 gv_free_splines(e);
1516 free_label(ED_label(e));
1520 /*FIX HERE , shallow cleaning may not be enough here */
1521 agdelrec(e, "Agedgeinfo_t");
1522}
1523
1525{
1526 free(ND_pos(n));
1527 if (ND_shape(n))
1528 ND_shape(n)->fns->freefn(n);
1529 free_label(ND_label(n));
1531 /*FIX HERE , shallow cleaning may not be enough here */
1532 agdelrec(n, "Agnodeinfo_t");
1533}
1534
1535void gv_nodesize(node_t *n, bool flip) {
1536 if (flip) {
1537 double w = INCH2PS(ND_height(n));
1538 ND_lw(n) = ND_rw(n) = w / 2;
1539 ND_ht(n) = INCH2PS(ND_width(n));
1540 }
1541 else {
1542 double w = INCH2PS(ND_width(n));
1543 ND_lw(n) = ND_rw(n) = w / 2;
1544 ND_ht(n) = INCH2PS(ND_height(n));
1545 }
1546}
1547
1548#ifndef HAVE_DRAND48
1549double drand48(void)
1550{
1551 double d;
1552 d = rand();
1553 d = d / RAND_MAX;
1554 return d;
1555}
1556#endif
1557typedef struct {
1559 char* name;
1561} clust_t;
1562
1564 .key = offsetof(clust_t, name),
1565 .size = -1,
1566 .link = offsetof(clust_t, link),
1567 .freef = free,
1568};
1569
1570static void fillMap (Agraph_t* g, Dt_t* map)
1571{
1572 for (int c = 1; c <= GD_n_cluster(g); c++) {
1573 Agraph_t *cl = GD_clust(g)[c];
1574 char *s = agnameof(cl);
1575 if (dtmatch(map, s)) {
1576 agwarningf("Two clusters named %s - the second will be ignored\n", s);
1577 } else {
1578 clust_t *ip = gv_alloc(sizeof(clust_t));
1579 ip->name = s;
1580 ip->clp = cl;
1581 dtinsert (map, ip);
1582 }
1583 fillMap (cl, map);
1584 }
1585}
1586
1592{
1593 Dt_t* map = dtopen (&strDisc, Dtoset);
1594
1595 fillMap (g, map);
1596
1597 return map;
1598}
1599
1600Agraph_t*
1601findCluster (Dt_t* map, char* name)
1602{
1603 clust_t* clp = dtmatch (map, name);
1604 if (clp)
1605 return clp->clp;
1606 return NULL;
1607}
1608
Dynamically expanding string buffers.
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:97
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:252
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:325
static int agxbputc(agxbuf *xb, char c)
add character to buffer
Definition agxbuf.h:295
static char * agxbdisown(agxbuf *xb)
Definition agxbuf.h:345
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void * gv_alloc(size_t size)
Definition alloc.h:47
#define MIN(a, b)
Definition arith.h:28
#define MAX(a, b)
Definition arith.h:33
boxf arrow_bb(pointf p, pointf u, double arrowsize)
Definition arrows.c:1113
#define dtmatch(d, o)
Definition cdt.h:185
#define dtinsert(d, o)
Definition cdt.h:186
CDT_API int dtclose(Dt_t *)
Definition dtclose.c:10
CDT_API Dtmethod_t * Dtoset
ordered set (self-adjusting tree)
Definition dttree.c:306
CDT_API Dt_t * dtopen(Dtdisc_t *, Dtmethod_t *)
Definition dtopen.c:11
void processClusterEdges(graph_t *g)
Definition utils.c:930
void undoClusterEdges(graph_t *g)
Definition utils.c:1029
char * late_nnstring(void *obj, attrsym_t *attr, char *defaultValue)
Definition utils.c:90
char * scanEntity(char *t, agxbuf *xb)
Definition utils.c:1081
#define W_DEGREE
Definition utils.c:166
static edge_t * cloneEdge(edge_t *e, node_t *ct, node_t *ch)
Make a copy of e in e's graph but using ct and ch as nodes.
Definition utils.c:796
bool mapbool(const char *p)
Definition utils.c:341
node_t * UF_union(node_t *u, node_t *v)
Definition utils.c:114
node_t * UF_find(node_t *n)
Definition utils.c:104
static int Tflag
Definition utils.c:406
static node_t * mapN(node_t *n, graph_t *clg)
Definition utils.c:972
Dt_t * mkClustMap(Agraph_t *g)
Definition utils.c:1591
void UF_setname(node_t *u, node_t *v)
Definition utils.c:149
static void undoCompound(edge_t *e, graph_t *clg)
Definition utils.c:999
void setEdgeType(graph_t *g, int defaultValue)
Definition utils.c:1422
static port chkPort(port(*pf)(node_t *, char *, char *), node_t *n, char *s)
Definition utils.c:489
char * late_string(void *obj, attrsym_t *attr, char *defaultValue)
Definition utils.c:84
void gv_free_splines(edge_t *e)
Definition utils.c:1502
boxf polyBB(polygon_t *poly)
Definition utils.c:604
int late_int(void *obj, attrsym_t *attr, int defaultValue, int minimum)
Definition utils.c:39
static Dtdisc_t mapDisc
Definition utils.c:786
void gv_cleanup_edge(edge_t *e)
Definition utils.c:1512
static void insertEdge(Dt_t *map, void *t, void *h, edge_t *e)
Definition utils.c:807
void common_init_node(node_t *n)
Definition utils.c:427
pointf Bezier(const pointf *V, double t, pointf *Left, pointf *Right)
Definition utils.c:174
bool overlap_label(textlabel_t *lp, boxf b)
Definition utils.c:1322
int maptoken(char *p, char **name, int *val)
Definition utils.c:315
double late_double(void *obj, attrsym_t *attr, double defaultValue, double minimum)
Definition utils.c:54
static void initFontLabelEdgeAttr(edge_t *e, struct fontinfo *fi, struct fontinfo *lfi)
Definition utils.c:464
bool overlap_node(node_t *n, boxf b)
Definition utils.c:1310
static int comp_entities(const void *e1, const void *e2)
Definition utils.c:1071
void common_init_edge(edge_t *e)
Definition utils.c:509
static bool noClip(edge_t *e, attrsym_t *sym)
Return true if head/tail end of edge should not be clipped to node.
Definition utils.c:475
const char * safefile(const char *filename)
Definition utils.c:265
pointf dotneato_closest(splines *spl, pointf pt)
Definition utils.c:346
static bool overlap_bezier(bezier bz, boxf b)
Definition utils.c:1335
pointf coord(node_t *n)
Definition utils.c:156
static char * findPath(const strviews_t dirs, const char *str)
Definition utils.c:252
static void * newItem(void *p, Dtdisc_t *disc)
Definition utils.c:773
attrsym_t * safe_dcl(graph_t *g, int obj_kind, char *name, char *defaultValue)
Definition utils.c:1064
void UF_singleton(node_t *u)
Definition utils.c:142
static void initFontEdgeAttr(edge_t *e, struct fontinfo *fi)
Definition utils.c:456
char * utf8ToLatin1(char *s)
Definition utils.c:1292
static int cmpItem(void *pp1, void *pp2)
Definition utils.c:759
char * latin1ToUTF8(char *s)
Converts string from Latin1 encoding to utf8. Also translates HTML entities.
Definition utils.c:1259
double get_inputscale(graph_t *g)
Definition utils.c:77
static int edgeType(const char *s, int defaultValue)
Convert string to edge type.
Definition utils.c:1373
static boxf addLabelBB(boxf bb, textlabel_t *lp, bool flipxy)
Definition utils.c:569
void updateBB(graph_t *g, textlabel_t *lp)
Definition utils.c:624
static bool overlap_arrow(pointf p, pointf u, double scale, boxf b)
Definition utils.c:1329
static int checkCompound(edge_t *e, graph_t *clg, agxbuf *xb, Dt_t *map, Dt_t *cmap)
Definition utils.c:846
bool overlap_edge(edge_t *e, boxf b)
Definition utils.c:1357
static unsigned char cvtAndAppend(unsigned char c, agxbuf *xb)
Definition utils.c:1165
void compute_bb(graph_t *g)
Definition utils.c:633
static void fillMap(Agraph_t *g, Dt_t *map)
Definition utils.c:1570
static Dtdisc_t strDisc
Definition utils.c:1563
static node_t * clustNode(node_t *n, graph_t *cg, agxbuf *xb, graph_t *clg)
Definition utils.c:729
void gv_cleanup_node(node_t *n)
Definition utils.c:1524
bool mapBool(const char *p, bool defaultValue)
Definition utils.c:325
char * htmlEntityUTF8(char *s, graph_t *g)
Definition utils.c:1182
static int num_clust_edges(graph_t *g)
Definition utils.c:916
#define PATHSEP
Definition utils.c:237
void get_gradient_points(pointf *A, pointf *G, size_t n, double angle, int flags)
Definition utils.c:1445
void gv_nodesize(node_t *n, bool flip)
Definition utils.c:1535
int test_toggle(void)
Definition utils.c:416
static item * mapEdge(Dt_t *map, edge_t *e)
Check if we already have cluster edge corresponding to t->h, and return it.
Definition utils.c:817
static int htmlEntity(char **s)
Definition utils.c:1103
bool is_a_cluster(Agraph_t *g)
Definition utils.c:695
static graph_t * mapc(Dt_t *cmap, node_t *n)
Definition utils.c:823
bool late_bool(void *obj, attrsym_t *attr, bool defaultValue)
Definition utils.c:97
Agraph_t * findCluster(Dt_t *map, char *name)
Definition utils.c:1601
double drand48(void)
Definition utils.c:1549
Agsym_t * setAttr(graph_t *g, void *obj, char *name, char *value, Agsym_t *ap)
Definition utils.c:704
#define HEAD_LABEL
Definition const.h:168
#define NORMAL
Definition const.h:24
#define EDGETYPE_SPLINE
Definition const.h:239
#define EDGE_XLABEL
Definition const.h:172
#define DEFAULT_NODEHEIGHT
Definition const.h:72
#define TAIL_LABEL
Definition const.h:169
#define EDGE_LABEL
Definition const.h:167
#define EDGETYPE_CURVED
Definition const.h:236
#define DEFAULT_COLOR
Definition const.h:48
#define DEFAULT_NODEWIDTH
Definition const.h:74
#define EDGETYPE_ORTHO
Definition const.h:238
#define MIN_NODEWIDTH
Definition const.h:75
#define DEFAULT_FONTSIZE
Definition const.h:61
#define MIN_FONTSIZE
Definition const.h:63
#define EDGETYPE_PLINE
Definition const.h:237
#define EDGETYPE_LINE
Definition const.h:235
#define DEFAULT_FONTNAME
Definition const.h:67
#define EDGETYPE_NONE
Definition const.h:234
#define EDGETYPE_COMPOUND
Definition const.h:240
#define DEFAULT_NODESHAPE
Definition const.h:76
#define NODE_XLABEL
Definition const.h:171
#define MIN_NODEHEIGHT
Definition const.h:73
#define ENTITY_NAME_LENGTH_MAX
Definition entities.h:274
static const struct entities_s entities[]
#define NR_OF_ENTITIES
Definition entities.h:275
static Dtdisc_t disc
Definition exparse.y:209
#define A(n, t)
Definition expr.h:76
static int flags
Definition gc.c:63
#define G
Definition gdefs.h:7
#define V
Definition gdefs.h:5
int lineToBox(pointf p, pointf q, boxf b)
Definition geom.c:47
#define B2BF(b, bf)
Definition geom.h:69
#define OVERLAP(b0, b1)
Definition geom.h:48
struct pointf_s pointf
#define DIST2(p, q)
Definition geom.h:55
#define POINTS_PER_INCH
Definition geom.h:58
#define INCH2PS(a_inches)
Definition geom.h:63
geometric functions (e.g. on points and boxes)
static void expandbp(boxf *b, pointf p)
expand box b as needed to enclose point p
Definition geomprocs.h:45
static WUR pointf mid_pointf(pointf p, pointf q)
Definition geomprocs.h:104
static WUR pointf sub_pointf(pointf p, pointf q)
Definition geomprocs.h:96
static WUR pointf add_pointf(pointf p, pointf q)
Definition geomprocs.h:88
#define EXPANDBB(b0, b1)
Definition geomprocs.h:65
static WUR pointf scale(double c, pointf p)
Definition geomprocs.h:148
static WUR bool boxf_overlap(boxf b0, boxf b1)
Definition geomprocs.h:136
Agsym_t * N_fontsize
Definition globals.h:75
Agsym_t * E_labelfontsize
Definition globals.h:89
Agsym_t * E_fontcolor
Definition globals.h:83
Agsym_t * N_width
Definition globals.h:74
Agsym_t * E_headclip
Definition globals.h:91
Agsym_t * E_headlabel
Definition globals.h:88
Agsym_t * N_showboxes
Definition globals.h:76
Agsym_t * N_fontname
Definition globals.h:75
Agsym_t * E_fontname
Definition globals.h:83
Agsym_t * N_style
Definition globals.h:76
char * HTTPServerEnVar
Definition globals.h:53
char * Gvimagepath
Definition globals.h:49
Agsym_t * E_label
Definition globals.h:84
double PSinputscale
Definition globals.h:56
char * Gvfilepath
Definition globals.h:48
Agsym_t * N_shape
Definition globals.h:74
Agsym_t * N_xlabel
Definition globals.h:76
Agsym_t * E_label_float
Definition globals.h:86
Agsym_t * E_taillabel
Definition globals.h:88
Agsym_t * N_label
Definition globals.h:76
Agsym_t * E_fontsize
Definition globals.h:83
Agsym_t * E_labelfontname
Definition globals.h:89
Agsym_t * E_xlabel
Definition globals.h:84
Agsym_t * N_fontcolor
Definition globals.h:75
Agsym_t * E_labelfontcolor
Definition globals.h:89
Agsym_t * E_tailclip
Definition globals.h:91
Agsym_t * N_height
Definition globals.h:74
static double len(glCompPoint p)
Definition glutils.c:138
void free(void *)
#define SIZE_MAX
Definition gmlscan.c:347
node NULL
Definition grammar.y:181
int agnnodes(Agraph_t *g)
Definition graph.c:157
Agsym_t * agattr_text(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up text attributes of a graph
Definition attr.c:336
Agsym_t * agnxtattr(Agraph_t *g, int kind, Agsym_t *attr)
permits traversing the list of attributes of a given type
Definition attr.c:365
int agxset(void *obj, Agsym_t *sym, const char *value)
Definition attr.c:524
char * agget(void *obj, char *name)
Definition attr.c:450
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:460
int agcopyattr(void *oldobj, void *newobj)
copies all of the attributes from one object to another
Definition attr.c:635
#define ED_compound(e)
Definition types.h:583
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:255
#define ED_xlabel(e)
Definition types.h:590
#define ED_label_ontop(e)
Definition types.h:591
#define ED_head_label(e)
Definition types.h:587
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:28
#define ED_spl(e)
Definition types.h:595
#define agtail(e)
Definition cgraph.h:977
#define ED_path(e)
Definition types.h:593
#define agfindedge(g, t, h)
Definition types.h:609
#define ED_tail_label(e)
Definition types.h:596
#define aghead(e)
Definition cgraph.h:978
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:43
#define ED_head_port(e)
Definition types.h:588
#define ED_label(e)
Definition types.h:589
#define ED_tail_port(e)
Definition types.h:597
void agwarningf(const char *fmt,...)
Definition agerror.c:175
#define agfindgraphattr(g, a)
Definition types.h:613
#define GD_has_labels(g)
Definition types.h:368
#define GD_clust(g)
Definition types.h:360
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:97
#define GD_flags(g)
Definition types.h:365
#define GD_bb(g)
Definition types.h:354
#define GD_n_cluster(g)
Definition types.h:389
#define GD_label(g)
Definition types.h:374
#define GD_flip(g)
Definition types.h:378
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:143
#define ND_ht(n)
Definition types.h:500
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:50
#define ND_bb(n)
Definition types.h:488
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:43
#define ND_showboxes(n)
Definition types.h:530
#define ND_has_port(n)
Definition types.h:495
#define ND_ysize(n)
Definition types.h:538
Agnode_t * agsubnode(Agraph_t *g, Agnode_t *n, int createflag)
Definition node.c:254
#define ND_label(n)
Definition types.h:502
#define ND_rw(n)
Definition types.h:525
#define ND_height(n)
Definition types.h:498
#define ND_width(n)
Definition types.h:536
#define ND_lw(n)
Definition types.h:506
#define ND_UF_parent(n)
Definition types.h:485
#define ND_xlabel(n)
Definition types.h:503
#define ND_UF_size(n)
Definition types.h:487
#define ND_pos(n)
Definition types.h:520
#define ND_ranktype(n)
Definition types.h:524
#define agfindnode(g, n)
Definition types.h:611
#define ND_coord(n)
Definition types.h:490
#define ND_shape(n)
Definition types.h:528
#define ND_xsize(n)
Definition types.h:537
Agraph_t * agraphof(void *obj)
Definition obj.c:187
#define AGDATA(obj)
returns Agrec_t
Definition cgraph.h:227
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:145
int agdelete(Agraph_t *g, void *obj)
deletes object. Equivalent to agclose, agdelnode, and agdeledge for obj being a graph,...
Definition obj.c:22
int agcontains(Agraph_t *, void *obj)
returns non-zero if obj is a member of (sub)graph
Definition obj.c:235
int agobjkind(void *obj)
Definition obj.c:254
Agraph_t * agroot(void *obj)
Definition obj.c:170
@ AGEDGE
Definition cgraph.h:207
@ AGNODE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
void * agbindrec(void *obj, const char *name, unsigned int recsize, int move_to_front)
attaches a new record of the given size to the object
Definition rec.c:91
int agdelrec(void *obj, const char *name)
deletes a named record from one object
Definition rec.c:139
int aghtmlstr(const char *)
Definition refstr.c:440
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:55
void gvToggle(int s)
Definition utils.c:407
replacements for ctype.h functions
static bool gv_isdigit(int c)
Definition gv_ctype.h:41
Arithmetic helper functions.
static bool is_exactly_zero(double v)
is a value precisely 0.0?
Definition gv_math.h:67
static int imin(int a, int b)
minimum of two integers
Definition gv_math.h:32
Graphviz context library.
static bool onetime
textitem scanner parser str
Definition htmlparse.y:218
textlabel_t * make_label(void *obj, char *str, bool is_html, bool is_record, double fontsize, char *fontname, char *fontcolor)
Definition labels.c:110
void free_label(textlabel_t *p)
Definition labels.c:204
type-generic dynamically expanding list
#define LIST(type)
Definition list.h:55
#define LIST_SIZE(list)
Definition list.h:80
#define LIST_APPEND(list, item)
Definition list.h:120
#define LIST_FREE(list)
Definition list.h:370
#define LIST_IS_EMPTY(list)
Definition list.h:90
#define LIST_GET(list, index)
Definition list.h:155
static int * ps
Definition lu.c:53
#define IS_CLUST_NODE(n)
Definition macros.h:23
#define SET_CLUST_NODE(n)
Definition macros.h:22
#define CL_EDGE_TAG
Definition macros.h:21
#define HAS_CLUST_EDGE(g)
Definition macros.h:24
#define ND_id(n)
Definition mm2gv.c:41
NEATOPROCS_API void s1(graph_t *, node_t *)
Definition stuff.c:651
File system path helpers.
#define PATH_SEPARATOR
character for separating directory components in a file system path
Definition path.h:10
shape_kind shapeOf(node_t *)
Definition shapes.c:1908
shape_desc * bind_shape(char *name, node_t *)
Definition shapes.c:3994
static double cg(SparseMatrix A, const double *precond, int n, int dim, double *x0, double *rhs, double tol, double maxit)
static bool startswith(const char *s, const char *prefix)
does the string s begin with the string prefix?
Definition startswith.h:11
platform abstraction for case-insensitive string functions
static bool streq(const char *a, const char *b)
are a and b equal?
Definition streq.h:11
graph or subgraph
Definition cgraph.h:424
Agraph_t * root
subgraphs - ancestors
Definition cgraph.h:433
implementation of Agrec_t
Definition cgraph.h:172
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:640
char * defval
Definition cgraph.h:643
Definition types.h:89
size_t size
Definition types.h:91
pointf sp
Definition types.h:94
pointf * list
Definition types.h:90
uint32_t eflag
Definition types.h:93
pointf ep
Definition types.h:95
uint32_t sflag
Definition types.h:92
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
Agrec_t hdr
Definition utils.c:911
int n_cluster_edges
Definition utils.c:912
Agraph_t * clp
Definition utils.c:1560
char * name
Definition utils.c:1559
Dtlink_t link
Definition utils.c:1558
Definition cdt.h:98
int key
Definition cdt.h:85
char * name
Definition entities.h:17
int value
Definition entities.h:18
double fontsize
Definition utils.c:422
char * fontcolor
Definition utils.c:424
char * fontname
Definition utils.c:423
Definition utils.c:752
node_t * t
Definition utils.c:755
node_t * h
Definition utils.c:756
void * p[2]
Definition utils.c:754
Dtlink_t link
Definition utils.c:753
int y
Definition geom.h:27
int x
Definition geom.h:27
double x
Definition geom.h:29
double y
Definition geom.h:29
Definition types.h:48
char * name
Definition types.h:63
bezier * list
Definition types.h:99
boxf bb
Definition types.h:101
size_t size
Definition types.h:100
a non-owning string reference
Definition strview.h:20
const char * data
start of the pointed to string
Definition strview.h:21
size_t size
extent of the string in bytes
Definition strview.h:22
pointf pos
Definition types.h:114
pointf dimen
Definition types.h:110
state for an in-progress string tokenization
Definition tokenize.h:36
Non-owning string references.
static int strview_cmp(strview_t a, strview_t b)
compare two string references
Definition strview.h:71
static strview_t strview(const char *referent, char terminator)
create a string reference
Definition strview.h:26
static point center(point vertex[], size_t n)
String tokenization.
static strview_t tok_get(const tok_t *t)
get the current token
Definition tokenize.h:76
static tok_t tok(const char *input, const char *separators)
begin tokenization of a new string
Definition tokenize.h:43
static bool tok_end(const tok_t *t)
is this tokenizer exhausted?
Definition tokenize.h:68
static void tok_next(tok_t *t)
advance to the next token in the string being scanned
Definition tokenize.h:85
#define TAIL_ID
Definition types.h:43
@ SH_RECORD
Definition types.h:187
#define HEAD_ID
Definition types.h:44
struct inside_t::@57 s
node_t * n
Definition types.h:160
Definition grammar.c:90
int(* pf)(void *, char *,...)
Definition xdot.c:398