Graphviz 16.1.1~dev.20261104.1411
Loading...
Searching...
No Matches
neatosplines.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include "config.h"
12
13#include <assert.h>
14#include <cgraph/cgraph.h>
15#include <limits.h>
16#include <math.h>
17#include <neatogen/neato.h>
18#include <neatogen/adjust.h>
19#include <pathplan/pathplan.h>
20#include <pathplan/vispath.h>
22#include <stdbool.h>
23#include <stddef.h>
24#include <util/alloc.h>
25#include <util/gv_math.h>
26#include <util/unreachable.h>
27
28#ifdef ORTHO
29#include <ortho/ortho.h>
30#endif
31
32
33static bool spline_merge(node_t * n)
34{
35 (void)n;
36 return false;
37}
38
39static bool swap_ends_p(edge_t * e)
40{
41 (void)e;
42 return false;
43}
44
46 .splineMerge = spline_merge};
47
48static void make_barriers(Ppoly_t **poly, int npoly, int pp, int qp,
49 Pedge_t **barriers, size_t *n_barriers) {
50 int i, j, k;
51
52 size_t n = 0;
53 for (i = 0; i < npoly; i++) {
54 if (i == pp)
55 continue;
56 if (i == qp)
57 continue;
58 n += poly[i]->pn;
59 }
60 Pedge_t *bar = gv_calloc(n, sizeof(Pedge_t));
61 size_t b = 0;
62 for (i = 0; i < npoly; i++) {
63 if (i == pp)
64 continue;
65 if (i == qp)
66 continue;
67 for (j = 0; j < (int)poly[i]->pn; j++) {
68 k = j + 1;
69 if (k >= (int)poly[i]->pn)
70 k = 0;
71 bar[b].a = poly[i]->ps[j];
72 bar[b].b = poly[i]->ps[k];
73 b++;
74 }
75 }
76 assert(b == n);
77 *barriers = bar;
78 *n_barriers = n;
79}
80
81static Ppoint_t genPt(double x, double y, pointf c)
82{
83 Ppoint_t p;
84
85 p.x = x + c.x;
86 p.y = y + c.y;
87 return p;
88}
89
90static Ppoint_t recPt(double x, double y, pointf c, expand_t* m)
91{
92 Ppoint_t p;
93
94 p.x = x * m->x + c.x;
95 p.y = y * m->y + c.y;
96 return p;
97}
98
110
111static void *newitem(void *p, Dtdisc_t *disc) {
112 edgeitem *obj = p;
113 edgeitem *newp;
114
115 (void)disc;
116 newp = gv_alloc(sizeof(edgeitem));
117 newp->id = obj->id;
118 newp->e = obj->e;
119 ED_count(newp->e) = 1;
120
121 return newp;
122}
123
124static int cmpitems(void *k1, void *k2) {
125 const edgeinfo *key1 = k1;
126 const edgeinfo *key2 = k2;
127 if (key1->n1 > key2->n1)
128 return 1;
129 if (key1->n1 < key2->n1)
130 return -1;
131 if (key1->n2 > key2->n2)
132 return 1;
133 if (key1->n2 < key2->n2)
134 return -1;
135
136 if (key1->p1.x > key2->p1.x)
137 return 1;
138 if (key1->p1.x < key2->p1.x)
139 return -1;
140 if (key1->p1.y > key2->p1.y)
141 return 1;
142 if (key1->p1.y < key2->p1.y)
143 return -1;
144 if (key1->p2.x > key2->p2.x)
145 return 1;
146 if (key1->p2.x < key2->p2.x)
147 return -1;
148 if (key1->p2.y > key2->p2.y)
149 return 1;
150 if (key1->p2.y < key2->p2.y)
151 return -1;
152 return 0;
153}
154
156 offsetof(edgeitem, id),
157 sizeof(edgeinfo),
158 offsetof(edgeitem, link),
159 newitem,
160 free,
161 cmpitems,
162};
163
164/* See if we have already encountered an edge between the same
165 * node:port pairs. If so, return the earlier edge. If not,
166 * this edge is added to map and returned.
167 * We first have to canonicalize the key fields using a lexicographic
168 * ordering.
169 */
170static edge_t *equivEdge(Dt_t * map, edge_t * e)
171{
172 edgeinfo test;
173 edgeitem dummy;
174 edgeitem *ip;
175
176 if (agtail(e) < aghead(e)) {
177 test.n1 = agtail(e);
178 test.p1 = ED_tail_port(e).p;
179 test.n2 = aghead(e);
180 test.p2 = ED_head_port(e).p;
181 } else if (agtail(e) > aghead(e)) {
182 test.n2 = agtail(e);
183 test.p2 = ED_tail_port(e).p;
184 test.n1 = aghead(e);
185 test.p1 = ED_head_port(e).p;
186 } else {
187 pointf hp = ED_head_port(e).p;
188 pointf tp = ED_tail_port(e).p;
189 if (tp.x < hp.x) {
190 test.p1 = tp;
191 test.p2 = hp;
192 } else if (tp.x > hp.x) {
193 test.p1 = hp;
194 test.p2 = tp;
195 } else if (tp.y < hp.y) {
196 test.p1 = tp;
197 test.p2 = hp;
198 } else if (tp.y > hp.y) {
199 test.p1 = hp;
200 test.p2 = tp;
201 } else {
202 test.p1 = test.p2 = tp;
203 }
204 test.n2 = test.n1 = agtail(e);
205 }
206 dummy.id = test;
207 dummy.e = e;
208 ip = dtinsert(map, &dummy);
209 return ip->e;
210}
211
212
213/* Generate loops. We use the library routine makeSelfEdge
214 * which also places the labels.
215 * We have to handle port labels here.
216 * as well as update the bbox from edge labels.
217 */
218void makeSelfArcs(edge_t * e, int stepx)
219{
220 assert(ED_count(e) >= 0);
221 const size_t cnt = (size_t)ED_count(e);
222
223 if (cnt == 1 || Concentrate) {
224 edge_t *edges1[1];
225 edges1[0] = e;
226 makeSelfEdge(edges1, 1, stepx, stepx, &sinfo);
227 if (ED_label(e))
230 } else if (cnt > 1) {
231 edge_t **edges = gv_calloc(cnt, sizeof(edge_t*));
232 for (size_t i = 0; i < cnt; i++) {
233 edges[i] = e;
234 e = ED_to_virt(e);
235 }
236 makeSelfEdge(edges, cnt, stepx, stepx, &sinfo);
237 for (size_t i = 0; i < cnt; i++) {
238 e = edges[i];
239 if (ED_label(e))
242 }
243 free(edges);
244 }
245}
246
271static double ellipse_tangent_slope(double a, double b, pointf p) {
272 assert(p.x != a &&
273 "cannot handle ellipse tangent slope in horizontal extreme point");
274 const double sign_y = p.y >= 0 ? 1 : -1;
275 const double m = -sign_y * (b * p.x) / (a * sqrt(a * a - p.x * p.x));
276 assert(isfinite(m) && "ellipse tangent slope is infinite");
277 return m;
278}
279
287 const double x =
288 (l0.m * l0.p.x - l0.p.y - l1.m * l1.p.x + l1.p.y) / (l0.m - l1.m);
289 const double y = l0.p.y + l0.m * (x - l0.p.x);
290 return (pointf){x, y};
291}
292
302 size_t i,
303 size_t nsides) {
304 const double angle0 = 2.0 * M_PI * ((double)i - 0.5) / (double)nsides;
305 const double angle1 = 2.0 * M_PI * ((double)i + 0.5) / (double)nsides;
306 const pointf p0 = {a * cos(angle0), b * sin(angle0)};
307 const pointf p1 = {a * cos(angle1), b * sin(angle1)};
308 const double m0 = ellipse_tangent_slope(a, b, p0);
309 const double m1 = ellipse_tangent_slope(a, b, p1);
310 const linef line0 = {{p0.x, p0.y}, m0};
311 const linef line1 = {{p1.x, p1.y}, m1};
312 return line_intersection(line0, line1);
313}
314
315/* Given a node, return an obstacle reflecting the
316 * node's geometry. pmargin specifies how much space to allow
317 * around the polygon.
318 * Returns the constructed polygon on success, NULL on failure.
319 * Failure means the node shape is not supported.
320 *
321 * If isOrtho is true, we have to use the bounding box of each node.
322 *
323 * The polygon has its vertices in CW order.
324 *
325 */
326Ppoly_t *makeObstacle(node_t * n, expand_t* pmargin, bool isOrtho)
327{
328 Ppoly_t *obs;
330 size_t sides;
331 pointf polyp;
332 boxf b;
333 pointf pt;
334 field_t *fld;
335 bool isPoly;
336 pointf* verts = NULL;
337 pointf vs[4];
338 pointf p;
339 pointf margin = {0};
340
341 switch (shapeOf(n)) {
342 case SH_POLY:
343 case SH_POINT:
344 obs = gv_alloc(sizeof(Ppoly_t));
345 poly = ND_shape_info(n);
346 if (isOrtho) {
347 isPoly = true;
348 sides = 4;
349 verts = vs;
350 /* For fixedshape, we can't use the width and height, as this includes
351 * the label. We only want to use the actual node shape.
352 */
353 if (poly->option.fixedshape) {
354 b = polyBB (poly);
355 vs[0] = b.LL;
356 vs[1].x = b.UR.x;
357 vs[1].y = b.LL.y;
358 vs[2] = b.UR;
359 vs[3].x = b.LL.x;
360 vs[3].y = b.UR.y;
361 } else {
362 const double width = ND_lw(n) + ND_rw(n);
363 const double outline_width = INCH2PS(ND_outline_width(n));
364 // scale lw and rw proportionally to sum to outline width
365 const double outline_lw = ND_lw(n) * outline_width / width;
366 const double outline_ht = INCH2PS(ND_outline_height(n));
367 p.x = -outline_lw;
368 p.y = -outline_ht / 2.0;
369 vs[0] = p;
370 p.x = outline_lw;
371 vs[1] = p;
372 p.y = outline_ht / 2.0;
373 vs[2] = p;
374 p.x = -outline_lw;
375 vs[3] = p;
376 }
377 }
378 else if (poly->sides >= 3) {
379 isPoly = true;
380 sides = poly->sides;
381 const double penwidth = late_double(n, N_penwidth, 1.0, 0.0);
382 // possibly use extra vertices representing the outline, i.e., the
383 // outermost periphery with penwidth taken into account
384 const size_t extra_peripheries = poly->peripheries >= 1 && penwidth > 0.0 ? 1 : 0;
385 const size_t outline_periphery = poly->peripheries + extra_peripheries;
386 const size_t vertices_offset = outline_periphery >= 1 ? (outline_periphery - 1) * sides : 0;
387 verts = poly->vertices + vertices_offset;
388 margin.x = pmargin->x;
389 margin.y = pmargin->y;
390 } else { /* ellipse */
391 isPoly = false;
392 sides = 8;
393 }
394 obs->pn = sides;
395 obs->ps = gv_calloc(sides, sizeof(Ppoint_t));
396 /* assuming polys are in CCW order, and pathplan needs CW */
397 for (size_t j = 0; j < sides; j++) {
398 double xmargin = 0, ymargin = 0;
399 if (isPoly) {
400 if (pmargin->doAdd) {
401 if (sides == 4) {
402 switch (j) {
403 case 0 :
404 xmargin = margin.x;
405 ymargin = margin.y;
406 break;
407 case 1 :
408 xmargin = -margin.x;
409 ymargin = margin.y;
410 break;
411 case 2 :
412 xmargin = -margin.x;
413 ymargin = -margin.y;
414 break;
415 case 3 :
416 xmargin = margin.x;
417 ymargin = -margin.y;
418 break;
419 default:
420 UNREACHABLE();
421 }
422 polyp.x = verts[j].x + xmargin;
423 polyp.y = verts[j].y + ymargin;
424 }
425 else {
426 const double h = hypot(verts[j].x, verts[j].y);
427 polyp.x = verts[j].x * (1.0 + margin.x/h);
428 polyp.y = verts[j].y * (1.0 + margin.y/h);
429 }
430 }
431 else {
432 polyp.x = verts[j].x * margin.x;
433 polyp.y = verts[j].y * margin.y;
434 }
435 } else {
436 const double width = INCH2PS(ND_outline_width(n));
437 const double height = INCH2PS(ND_outline_height(n));
438 margin = pmargin->doAdd ? (pointf){pmargin->x, pmargin->y} : (pointf){0};
439 const double ellipse_a = (width + margin.x) / 2.0;
440 const double ellipse_b = (height + margin.y) / 2.0;
441 polyp = circumscribed_polygon_corner_about_ellipse(ellipse_a, ellipse_b, j, sides);
442 }
443 obs->ps[sides - j - 1].x = polyp.x + ND_coord(n).x;
444 obs->ps[sides - j - 1].y = polyp.y + ND_coord(n).y;
445 }
446 break;
447 case SH_RECORD:
448 fld = ND_shape_info(n);
449 b = fld->b;
450 obs = gv_alloc(sizeof(Ppoly_t));
451 obs->pn = 4;
452 obs->ps = gv_calloc(4, sizeof(Ppoint_t));
453 /* CW order */
454 pt = ND_coord(n);
455 if (pmargin->doAdd) {
456 obs->ps[0] = genPt(b.LL.x-pmargin->x, b.LL.y-pmargin->y, pt);
457 obs->ps[1] = genPt(b.LL.x-pmargin->x, b.UR.y+pmargin->y, pt);
458 obs->ps[2] = genPt(b.UR.x+pmargin->x, b.UR.y+pmargin->y, pt);
459 obs->ps[3] = genPt(b.UR.x+pmargin->x, b.LL.y-pmargin->y, pt);
460 }
461 else {
462 obs->ps[0] = recPt(b.LL.x, b.LL.y, pt, pmargin);
463 obs->ps[1] = recPt(b.LL.x, b.UR.y, pt, pmargin);
464 obs->ps[2] = recPt(b.UR.x, b.UR.y, pt, pmargin);
465 obs->ps[3] = recPt(b.UR.x, b.LL.y, pt, pmargin);
466 }
467 break;
468 case SH_EPSF:
469 obs = gv_alloc(sizeof(Ppoly_t));
470 obs->pn = 4;
471 obs->ps = gv_calloc(4, sizeof(Ppoint_t));
472 /* CW order */
473 pt = ND_coord(n);
474 if (pmargin->doAdd) {
475 obs->ps[0] = genPt(-ND_lw(n)-pmargin->x, -ND_ht(n)-pmargin->y,pt);
476 obs->ps[1] = genPt(-ND_lw(n)-pmargin->x, ND_ht(n)+pmargin->y,pt);
477 obs->ps[2] = genPt(ND_rw(n)+pmargin->x, ND_ht(n)+pmargin->y,pt);
478 obs->ps[3] = genPt(ND_rw(n)+pmargin->x, -ND_ht(n)-pmargin->y,pt);
479 }
480 else {
481 obs->ps[0] = recPt(-ND_lw(n), -ND_ht(n), pt, pmargin);
482 obs->ps[1] = recPt(-ND_lw(n), ND_ht(n), pt, pmargin);
483 obs->ps[2] = recPt(ND_rw(n), ND_ht(n), pt, pmargin);
484 obs->ps[3] = recPt(ND_rw(n), -ND_ht(n), pt, pmargin);
485 }
486 break;
487 default:
488 obs = NULL;
489 break;
490 }
491 return obs;
492}
493
494/* Construct the shortest path from one endpoint of e to the other.
495 * vconfig is a precomputed data structure to help in the computation.
496 * If chkPts is true, the function finds the polygons, if any, containing
497 * the endpoints and tells the shortest path computation to ignore them.
498 * Assumes this info is set in ND_lim, usually from _spline_edges.
499 * Returns the shortest path.
500 */
501Ppolyline_t getPath(edge_t *e, vconfig_t *vconfig, bool chkPts) {
502 Ppolyline_t line;
503 int pp, qp;
504 Ppoint_t p, q;
505
506 p = add_pointf(ND_coord(agtail(e)), ED_tail_port(e).p);
507 q = add_pointf(ND_coord(aghead(e)), ED_head_port(e).p);
508
509 /* determine the polygons (if any) that contain the endpoints */
510 pp = qp = POLYID_NONE;
511 if (chkPts) {
512 pp = ND_lim(agtail(e));
513 qp = ND_lim(aghead(e));
514 }
515 Pobspath(vconfig, p, pp, q, qp, &line);
516 return line;
517}
518
519static void makePolyline(edge_t * e) {
520 Ppolyline_t spl, line = ED_path(e);
521
522 make_polyline (line, &spl);
523 if (Verbose > 1)
524 fprintf(stderr, "polyline %s %s\n", agnameof(agtail(e)), agnameof(aghead(e)));
525 clip_and_install(e, aghead(e), spl.ps, spl.pn, &sinfo);
526 addEdgeLabels(e);
527}
528
529/* Construct a spline connecting the endpoints of e, avoiding the npoly
530 * obstacles obs.
531 * The resultant spline is attached to the edge, the positions of any
532 * edge labels are computed, and the graph's bounding box is recomputed.
533 *
534 * If chkPts is true, the function checks if one or both of the endpoints
535 * is on or inside one of the obstacles and, if so, tells the shortest path
536 * computation to ignore them.
537 */
538void makeSpline(edge_t *e, Ppoly_t **obs, int npoly, bool chkPts) {
539 Ppolyline_t line, spline;
540 int i;
541 int pp, qp;
542 Ppoint_t p, q;
543 Pedge_t *barriers;
544
545 line = ED_path(e);
546 p = line.ps[0];
547 q = line.ps[line.pn - 1];
548 /* determine the polygons (if any) that contain the endpoints */
549 pp = qp = POLYID_NONE;
550 if (chkPts)
551 for (i = 0; i < npoly; i++) {
552 if (pp == POLYID_NONE && in_poly(*obs[i], p))
553 pp = i;
554 if (qp == POLYID_NONE && in_poly(*obs[i], q))
555 qp = i;
556 }
557
558 size_t n_barriers;
559 make_barriers(obs, npoly, pp, qp, &barriers, &n_barriers);
560 if (Proutespline(barriers, n_barriers, line, (Pvector_t[2]){0}, &spline) < 0) {
561 agerrorf("makeSpline: failed to make spline edge (%s,%s)\n", agnameof(agtail(e)), agnameof(aghead(e)));
562 return;
563 }
564
565 /* north why did you ever use int coords */
566 if (Verbose > 1)
567 fprintf(stderr, "spline %s %s\n", agnameof(agtail(e)), agnameof(aghead(e)));
568 clip_and_install(e, aghead(e), spline.ps, spline.pn, &sinfo);
569 free(barriers);
570 addEdgeLabels(e);
571}
572
573 /* True if either head or tail has a port on its boundary */
574#define BOUNDARY_PORT(e) ((ED_tail_port(e).side)||(ED_head_port(e).side))
575
576/* Basic default routine for creating edges.
577 * If splines are requested, we construct the obstacles.
578 * If not, or nodes overlap, the function reverts to line segments.
579 * NOTE: intra-cluster edges are not constrained to
580 * remain in the cluster's bounding box and, conversely, a cluster's box
581 * is not altered to reflect intra-cluster edges.
582 * If Nop > 1 and the spline exists, it is just copied.
583 * NOTE: if edgetype = EDGETYPE_NONE, we shouldn't be here.
584 */
585static int spline_edges_(graph_t *g, expand_t *pmargin, int edgetype) {
586 node_t *n;
587 edge_t *e;
588 edge_t *e0;
589 Ppoly_t **obs = 0;
590 Ppoly_t *obp;
591 int cnt, i = 0, npoly;
592 vconfig_t *vconfig = 0;
593 int useEdges = Nop > 1;
594 int legal = 0;
595
596#ifdef HAVE_GTS
597 router_t* rtr = 0;
598#endif
599
600 /* build configuration */
601 if (edgetype >= EDGETYPE_PLINE) {
602 obs = gv_calloc(agnnodes_z(g), sizeof(Ppoly_t*));
603 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
604 obp = makeObstacle(n, pmargin, edgetype == EDGETYPE_ORTHO);
605 if (obp) {
606 ND_lim(n) = i;
607 obs[i++] = obp;
608 }
609 else
610 ND_lim(n) = POLYID_NONE;
611 }
612 } else {
613 obs = 0;
614 }
615 npoly = i;
616 if (obs) {
617 if ((legal = Plegal_arrangement(obs, npoly))) {
618 if (edgetype != EDGETYPE_ORTHO) vconfig = Pobsopen(obs, npoly);
619 }
620 else {
621 if (edgetype == EDGETYPE_ORTHO)
622 agwarningf("the bounding boxes of some nodes touch - falling back to straight line edges\n");
623 else
624 agwarningf("some nodes with margin (%.02f,%.02f) touch - falling back to straight line edges\n", pmargin->x, pmargin->y);
625 }
626 }
627
628 /* route edges */
629 if (Verbose)
630 fprintf(stderr, "Creating edges using %s\n",
631 (legal && edgetype == EDGETYPE_ORTHO) ? "orthogonal lines" :
632 (vconfig ? (edgetype == EDGETYPE_SPLINE ? "splines" : "polylines") :
633 "line segments"));
634 if (vconfig) {
635 /* path-finding pass */
636 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
637 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
638 ED_path(e) = getPath(e, vconfig, true);
639 }
640 }
641 }
642#ifdef ORTHO
643 else if (legal && edgetype == EDGETYPE_ORTHO) {
644 orthoEdges(g, false);
645 useEdges = 1;
646 }
647#endif
648
649 /* spline-drawing pass */
650 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
651 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
652 node_t *head = aghead(e);
653 if (useEdges && ED_spl(e)) {
654 addEdgeLabels(e);
655 if (Nop == 3 && ED_spl(e)->size > 0) {
656 // use first bezier, start point and end point will lost
657 if (ED_spl(e)->size > 1) {
658 agwarningf("edge %s -> %s : set more than one spline. First used, other dropped.\n", agnameof(n), agnameof(head));
659 }
660 bezier *bez = ED_spl(e)->list;
661 size_t sz = bez->size;
662 bez->size = 0;
663 pointf *pb = bez->list;
664 bez->list = NULL;
666 clip_and_install(e, head, pb, sz, &sinfo);
667 free(pb);
668 }
669 }
670 else if (ED_count(e) == 0) continue; /* only do representative */
671 else if (n == head) { /* self arc */
673 } else if (vconfig) { /* EDGETYPE_SPLINE or EDGETYPE_PLINE */
674#ifdef HAVE_GTS
675 if (ED_count(e) > 1 || BOUNDARY_PORT(e)) {
676 int fail = 0;
677 if (ED_path(e).pn == 2 && !BOUNDARY_PORT(e))
678 /* if a straight line can connect the ends */
679 makeStraightEdge(g, e, edgetype, &sinfo);
680 else {
681 if (!rtr) rtr = mkRouter (obs, npoly);
682 fail = makeMultiSpline(e, rtr, edgetype == EDGETYPE_PLINE);
683 }
684 if (!fail) continue;
685 }
686 /* We can probably remove this branch and just use
687 * makeMultiSpline. It can also catch the makeStraightEdge
688 * case. We could then eliminate all of the vconfig stuff.
689 */
690#endif
691 cnt = ED_count(e);
692 if (Concentrate) cnt = 1; /* only do representative */
693 e0 = e;
694 for (i = 0; i < cnt; i++) {
695 if (edgetype == EDGETYPE_SPLINE)
696 makeSpline(e0, obs, npoly, true);
697 else
698 makePolyline(e0);
699 e0 = ED_to_virt(e0);
700 }
701 } else {
702 makeStraightEdge(g, e, edgetype, &sinfo);
703 }
704 }
705 }
706
707#ifdef HAVE_GTS
708 if (rtr)
709 freeRouter (rtr);
710#endif
711
712 if (vconfig)
713 Pobsclose (vconfig);
714 if (obs) {
715 for (i=0; i < npoly; i++) {
716 free (obs[i]->ps);
717 free (obs[i]);
718 }
719 free (obs);
720 }
721 return 0;
722}
723
724/* Main wrapper code for generating edges.
725 * Sets desired separation.
726 * Coalesces equivalent edges (edges * with the same endpoints).
727 * It then calls the edge generating function, and marks the
728 * spline phase complete.
729 * Returns 0 on success.
730 *
731 * The edge function is given the graph, the separation to be added
732 * around obstacles, and the type of edge. It must guarantee
733 * that all bounding boxes are current; in particular, the bounding box of
734 * g must reflect the addition of the edges.
735 */
736int
738 int edgetype)
739{
740 node_t *n;
741 edge_t *e;
742 expand_t margin;
743 Dt_t *map;
744
745 margin = esepFactor (g);
746
747 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
748 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
749 resolvePorts (e);
750 }
751 }
752
753 /* find equivalent edges */
754 map = dtopen(&edgeItemDisc, Dtoset);
755 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
756 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
757 if (Nop > 1 && ED_spl(e)) {
758 /* If Nop > 1 (use given edges) and e has a spline, it
759 * should have its own equivalence class.
760 */
761 ED_count(e)++;
762 } else {
763 edge_t *leader = equivEdge(map, e);
764 if (leader != e) {
765 ED_count(leader)++;
766 ED_to_virt(e) = ED_to_virt(leader);
767 ED_to_virt(leader) = e;
768 }
769 }
770 }
771 }
772 dtclose(map);
773
774 if (edgefn(g, &margin, edgetype))
775 return 1;
776
778 return 0;
779}
780
781/* Construct edges using default algorithm and given splines value.
782 * Return 0 on success.
783 */
784int spline_edges1(graph_t * g, int edgetype)
785{
786 return splineEdges(g, spline_edges_, edgetype);
787}
788
789/* Sets the graph's aspect ratio.
790 * Check splines attribute and construct edges using default algorithm.
791 * If the splines attribute is defined but equal to "", skip edge routing.
792 *
793 * Assumes u.bb for has been computed for g and all clusters
794 * (not just top-level clusters), and that GD_bb(g).LL is at the origin.
795 *
796 * This last criterion is, I believe, mainly to simplify the code
797 * in neato_set_aspect. It would be good to remove this constraint,
798 * as this would allow nodes pinned on input to have the same coordinates
799 * when output in dot or plain format.
800 *
801 */
803 int et = EDGE_TYPE (g);
805 if (et == EDGETYPE_NONE) return;
806#ifndef ORTHO
807 if (et == EDGETYPE_ORTHO) {
808 agwarningf("Orthogonal edges not yet supported\n");
809 et = EDGETYPE_PLINE;
810 GD_flags(g->root) &= ~EDGETYPE_ORTHO;
812 }
813#endif
814 spline_edges1(g, et);
815}
816
817static void
819{
820 int i;
821
822 for (i = 1; i <= GD_n_cluster(g); i++) {
823 shiftClusters (GD_clust(g)[i], offset);
824 }
825
826 GD_bb(g).UR.x -= offset.x;
827 GD_bb(g).UR.y -= offset.y;
828 GD_bb(g).LL.x -= offset.x;
829 GD_bb(g).LL.y -= offset.y;
830}
831
832/* Compute bounding box, translate graph to origin,
833 * then construct all edges.
834 */
836{
837 node_t *n;
838 pointf offset;
839
840 compute_bb(g);
841 offset.x = PS2INCH(GD_bb(g).LL.x);
842 offset.y = PS2INCH(GD_bb(g).LL.y);
843 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
844 ND_pos(n)[0] -= offset.x;
845 ND_pos(n)[1] -= offset.y;
846 }
847
848 shiftClusters (g, GD_bb(g).LL);
849 spline_edges0(g, true);
850}
851
852/* Scale edge by given factor.
853 * Assume ED_spl != NULL.
854 */
855static void scaleEdge(edge_t * e, double xf, double yf)
856{
857 pointf *pt;
858 bezier *bez;
859 pointf delh, delt;
860
861 delh.x = POINTS_PER_INCH * (ND_pos(aghead(e))[0] * (xf - 1.0));
862 delh.y = POINTS_PER_INCH * (ND_pos(aghead(e))[1] * (yf - 1.0));
863 delt.x = POINTS_PER_INCH * (ND_pos(agtail(e))[0] * (xf - 1.0));
864 delt.y = POINTS_PER_INCH * (ND_pos(agtail(e))[1] * (yf - 1.0));
865
866 bez = ED_spl(e)->list;
867 for (size_t i = 0; i < ED_spl(e)->size; i++) {
868 pt = bez->list;
869 for (size_t j = 0; j < bez->size; j++) {
870 if (i == 0 && j == 0) {
871 pt->x += delt.x;
872 pt->y += delt.y;
873 }
874 else if (i == ED_spl(e)->size-1 && j == bez->size-1) {
875 pt->x += delh.x;
876 pt->y += delh.y;
877 }
878 else {
879 pt->x *= xf;
880 pt->y *= yf;
881 }
882 pt++;
883 }
884 if (bez->sflag) {
885 bez->sp.x += delt.x;
886 bez->sp.y += delt.y;
887 }
888 if (bez->eflag) {
889 bez->ep.x += delh.x;
890 bez->ep.y += delh.y;
891 }
892 bez++;
893 }
894
895 if (ED_label(e) && ED_label(e)->set) {
896 ED_label(e)->pos.x *= xf;
897 ED_label(e)->pos.y *= yf;
898 }
899 if (ED_head_label(e) && ED_head_label(e)->set) {
900 ED_head_label(e)->pos.x += delh.x;
901 ED_head_label(e)->pos.y += delh.y;
902 }
903 if (ED_tail_label(e) && ED_tail_label(e)->set) {
904 ED_tail_label(e)->pos.x += delt.x;
905 ED_tail_label(e)->pos.y += delt.y;
906 }
907}
908
910static void scaleBB(graph_t * g, double xf, double yf)
911{
912 int i;
913
914 GD_bb(g).UR.x *= xf;
915 GD_bb(g).UR.y *= yf;
916 GD_bb(g).LL.x *= xf;
917 GD_bb(g).LL.y *= yf;
918
919 if (GD_label(g) && GD_label(g)->set) {
920 GD_label(g)->pos.x *= xf;
921 GD_label(g)->pos.y *= yf;
922 }
923
924 for (i = 1; i <= GD_n_cluster(g); i++)
925 scaleBB(GD_clust(g)[i], xf, yf);
926}
927
928/* Translate edge by offset.
929 * Assume ED_spl(e) != NULL
930 */
931static void translateE(edge_t * e, pointf offset)
932{
933 pointf *pt;
934 bezier *bez;
935
936 bez = ED_spl(e)->list;
937 for (size_t i = 0; i < ED_spl(e)->size; i++) {
938 pt = bez->list;
939 for (size_t j = 0; j < bez->size; j++) {
940 pt->x -= offset.x;
941 pt->y -= offset.y;
942 pt++;
943 }
944 if (bez->sflag) {
945 bez->sp.x -= offset.x;
946 bez->sp.y -= offset.y;
947 }
948 if (bez->eflag) {
949 bez->ep.x -= offset.x;
950 bez->ep.y -= offset.y;
951 }
952 bez++;
953 }
954
955 if (ED_label(e) && ED_label(e)->set) {
956 ED_label(e)->pos.x -= offset.x;
957 ED_label(e)->pos.y -= offset.y;
958 }
959 if (ED_xlabel(e) && ED_xlabel(e)->set) {
960 ED_xlabel(e)->pos.x -= offset.x;
961 ED_xlabel(e)->pos.y -= offset.y;
962 }
963 if (ED_head_label(e) && ED_head_label(e)->set) {
964 ED_head_label(e)->pos.x -= offset.x;
965 ED_head_label(e)->pos.y -= offset.y;
966 }
967 if (ED_tail_label(e) && ED_tail_label(e)->set) {
968 ED_tail_label(e)->pos.x -= offset.x;
969 ED_tail_label(e)->pos.y -= offset.y;
970 }
971}
972
973static void translateG(Agraph_t * g, pointf offset)
974{
975 int i;
976
977 GD_bb(g).UR.x -= offset.x;
978 GD_bb(g).UR.y -= offset.y;
979 GD_bb(g).LL.x -= offset.x;
980 GD_bb(g).LL.y -= offset.y;
981
982 if (GD_label(g) && GD_label(g)->set) {
983 GD_label(g)->pos.x -= offset.x;
984 GD_label(g)->pos.y -= offset.y;
985 }
986
987 for (i = 1; i <= GD_n_cluster(g); i++)
988 translateG(GD_clust(g)[i], offset);
989}
990
992{
993 node_t *n;
994 edge_t *e;
995 pointf offset;
996 pointf ll;
997
998 ll = GD_bb(g).LL;
999
1000 offset.x = PS2INCH(ll.x);
1001 offset.y = PS2INCH(ll.y);
1002 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1003 ND_pos(n)[0] -= offset.x;
1004 ND_pos(n)[1] -= offset.y;
1005 if (ND_xlabel(n) && ND_xlabel(n)->set) {
1006 ND_xlabel(n)->pos.x -= ll.x;
1007 ND_xlabel(n)->pos.y -= ll.y;
1008 }
1009 }
1010 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1011 for (e = agfstout(g, n); e; e = agnxtout(g, e))
1012 if (ED_spl(e))
1013 translateE(e, ll);
1014 }
1015 translateG(g, ll);
1016}
1017
1018/* Assume all bounding boxes are correct.
1019 * Return false if no transform is performed. This includes
1020 * the possibility that a translation was done.
1021 */
1023{
1024 double xf, yf, actual, desired;
1025 node_t *n;
1026 bool translated = false;
1027
1028 if (g->root != g)
1029 return false;
1030
1031 if (GD_drawing(g)->ratio_kind) {
1032 if (GD_bb(g).LL.x || GD_bb(g).LL.y) {
1033 translated = true;
1034 neato_translate (g);
1035 }
1036 /* normalize */
1037 if (GD_flip(g)) {
1038 GD_bb(g).UR = exch_xyf(GD_bb(g).UR);
1039 }
1040 if (GD_drawing(g)->ratio_kind == R_FILL) {
1041 /* fill is weird because both X and Y can stretch */
1042 if (GD_drawing(g)->size.x <= 0)
1043 return translated;
1044 xf = GD_drawing(g)->size.x / GD_bb(g).UR.x;
1045 yf = GD_drawing(g)->size.y / GD_bb(g).UR.y;
1046 /* handle case where one or more dimensions is too big */
1047 if (xf < 1.0 || yf < 1.0) {
1048 if (xf < yf) {
1049 yf /= xf;
1050 xf = 1.0;
1051 } else {
1052 xf /= yf;
1053 yf = 1.0;
1054 }
1055 }
1056 } else if (GD_drawing(g)->ratio_kind == R_EXPAND) {
1057 if (GD_drawing(g)->size.x <= 0)
1058 return translated;
1059 xf = GD_drawing(g)->size.x / GD_bb(g).UR.x;
1060 yf = GD_drawing(g)->size.y / GD_bb(g).UR.y;
1061 if (xf > 1.0 && yf > 1.0) {
1062 double scale = fmin(xf, yf);
1063 xf = yf = scale;
1064 } else
1065 return translated;
1066 } else if (GD_drawing(g)->ratio_kind == R_VALUE) {
1067 desired = GD_drawing(g)->ratio;
1068 actual = GD_bb(g).UR.y / GD_bb(g).UR.x;
1069 if (actual < desired) {
1070 yf = desired / actual;
1071 xf = 1.0;
1072 } else {
1073 xf = actual / desired;
1074 yf = 1.0;
1075 }
1076 } else
1077 return translated;
1078 if (GD_flip(g)) {
1079 SWAP(&xf, &yf);
1080 }
1081
1082 if (Nop > 1) {
1083 edge_t *e;
1084 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1085 for (e = agfstout(g, n); e; e = agnxtout(g, e))
1086 if (ED_spl(e))
1087 scaleEdge(e, xf, yf);
1088 }
1089 }
1090 /* Not relying on neato_nlist here allows us not to have to
1091 * allocate it in the root graph and the connected components.
1092 */
1093 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1094 ND_pos(n)[0] *= xf;
1095 ND_pos(n)[1] *= yf;
1096 }
1097 scaleBB(g, xf, yf);
1098 return true;
1099 }
1100 else
1101 return false;
1102}
1103
1104/* Sets aspect ratio if necessary; real work done in _neato_set_aspect;
1105 * This also copies the internal layout coordinates (ND_pos) to the
1106 * external ones (ND_coord).
1107 *
1108 * Return true if some node moved.
1109 */
1111{
1112 node_t *n;
1113 bool moved = false;
1114
1115 /* setting aspect ratio only makes sense on root graph */
1116 moved = _neato_set_aspect(g);
1117 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1118 ND_coord(n).x = POINTS_PER_INCH * ND_pos(n)[0];
1119 ND_coord(n).y = POINTS_PER_INCH * ND_pos(n)[1];
1120 }
1121 return moved;
1122}
1123
expand_t esepFactor(graph_t *g)
Definition adjust.c:1070
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 M_PI
Definition arith.h:41
#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
abstract graph C library, Cgraph API
boxf polyBB(polygon_t *poly)
Definition utils.c:557
double late_double(void *obj, attrsym_t *attr, double defaultValue, double minimum)
Definition utils.c:55
void updateBB(graph_t *g, textlabel_t *lp)
Definition utils.c:576
void compute_bb(graph_t *g)
Definition utils.c:584
#define EDGETYPE_SPLINE
Definition const.h:239
#define EDGETYPE_ORTHO
Definition const.h:238
#define EDGETYPE_PLINE
Definition const.h:237
#define EDGETYPE_NONE
Definition const.h:234
#define GVSPLINES
Definition const.h:164
vconfig_t * Pobsopen(Ppoly_t **obs, int n_obs)
Definition cvt.c:28
void Pobsclose(vconfig_t *config)
Definition cvt.c:89
void Pobspath(vconfig_t *config, Ppoint_t p0, int poly0, Ppoint_t p1, int poly1, Ppolyline_t *output_route)
Definition cvt.c:102
#define head
Definition dthdr.h:15
static Dtdisc_t disc
Definition exparse.y:209
#define PS2INCH(a_points)
Definition geom.h:64
struct pointf_s pointf
#define POINTS_PER_INCH
Definition geom.h:58
#define INCH2PS(a_inches)
Definition geom.h:63
static WUR pointf add_pointf(pointf p, pointf q)
Definition geomprocs.h:88
static WUR pointf exch_xyf(pointf p)
Definition geomprocs.h:128
static WUR pointf scale(double c, pointf p)
Definition geomprocs.h:148
int State
Definition globals.h:66
bool Concentrate
Definition globals.h:62
int Nop
Definition globals.h:57
Agsym_t * N_penwidth
Definition globals.h:81
static bool Verbose
Definition gml2gv.c:26
void free(void *)
node NULL
Definition grammar.y:181
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:204
size_t agnnodes_z(const Agraph_t *g)
Definition graph.c:161
#define ED_xlabel(e)
Definition types.h:590
#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 ED_count(e)
Definition types.h:580
#define agtail(e)
Definition cgraph.h:982
#define ED_path(e)
Definition types.h:593
#define ED_tail_label(e)
Definition types.h:596
#define aghead(e)
Definition cgraph.h:983
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
#define ED_to_virt(e)
Definition types.h:599
void agwarningf(const char *fmt,...)
Definition agerror.c:175
void agerrorf(const char *fmt,...)
Definition agerror.c:167
#define GD_drawing(g)
Definition types.h:353
#define GD_clust(g)
Definition types.h:360
#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_nodesep(g)
Definition types.h:394
#define GD_flip(g)
Definition types.h:378
#define ND_outline_width(n)
Definition types.h:516
#define ND_outline_height(n)
Definition types.h:517
#define ND_ht(n)
Definition types.h:500
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:50
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:43
#define ND_lim(n)
Definition types.h:504
#define ND_rw(n)
Definition types.h:525
#define ND_lw(n)
Definition types.h:506
#define ND_xlabel(n)
Definition types.h:503
#define ND_shape_info(n)
Definition types.h:529
#define ND_pos(n)
Definition types.h:520
#define ND_coord(n)
Definition types.h:490
Agraph_t * agraphof(void *obj)
Definition obj.c:187
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:145
Arithmetic helper functions.
#define SWAP(a, b)
Definition gv_math.h:133
static double penwidth[]
bool in_poly(const Ppoly_t poly, Ppoint_t q)
Definition inpoly.c:26
int Plegal_arrangement(Ppoly_t **polys, int n_polys)
Definition legal.c:357
#define EDGE_TYPE(g)
Definition macros.h:25
router_t * mkRouter(Ppoly_t **obsp, int npoly)
void freeRouter(router_t *rtr)
int makeMultiSpline(edge_t *e, router_t *rtr, int doPolyline)
static bool _neato_set_aspect(graph_t *g)
static int cmpitems(void *k1, void *k2)
static pointf line_intersection(linef l0, linef l1)
static void * newitem(void *p, Dtdisc_t *disc)
static void make_barriers(Ppoly_t **poly, int npoly, int pp, int qp, Pedge_t **barriers, size_t *n_barriers)
static int spline_edges_(graph_t *g, expand_t *pmargin, int edgetype)
static void translateE(edge_t *e, pointf offset)
static Ppoint_t recPt(double x, double y, pointf c, expand_t *m)
static bool swap_ends_p(edge_t *e)
int spline_edges1(graph_t *g, int edgetype)
static bool spline_merge(node_t *n)
Ppoly_t * makeObstacle(node_t *n, expand_t *pmargin, bool isOrtho)
Ppolyline_t getPath(edge_t *e, vconfig_t *vconfig, bool chkPts)
Dtdisc_t edgeItemDisc
void makeSpline(edge_t *e, Ppoly_t **obs, int npoly, bool chkPts)
void spline_edges0(graph_t *g, bool set_aspect)
static pointf circumscribed_polygon_corner_about_ellipse(double a, double b, size_t i, size_t nsides)
static double ellipse_tangent_slope(double a, double b, pointf p)
static void shiftClusters(graph_t *g, pointf offset)
static void scaleEdge(edge_t *e, double xf, double yf)
static void translateG(Agraph_t *g, pointf offset)
static void makePolyline(edge_t *e)
static void scaleBB(graph_t *g, double xf, double yf)
scale bounding box of clusters of g by given factors
static splineInfo sinfo
int splineEdges(graph_t *g, int(*edgefn)(graph_t *, expand_t *, int), int edgetype)
void makeSelfArcs(edge_t *e, int stepx)
void neato_translate(Agraph_t *g)
static edge_t * equivEdge(Dt_t *map, edge_t *e)
static Ppoint_t genPt(double x, double y, pointf c)
void spline_edges(graph_t *g)
#define BOUNDARY_PORT(e)
bool neato_set_aspect(graph_t *g)
int orthoEdges(Agraph_t *g, bool useLbls)
Definition ortho.c:1161
finds and smooths shortest paths
void make_polyline(Ppolyline_t line, Ppolyline_t *sline)
Definition util.c:44
int Proutespline(Pedge_t *barriers, size_t n_barriers, Ppolyline_t input_route, Pvector_t endpoint_slopes[2], Ppolyline_t *output_route)
static void set_aspect(graph_t *g)
Definition position.c:925
void makePortLabels(edge_t *e)
add head and tail labels if necessary and update bounding box
Definition splines.c:1168
void clip_and_install(edge_t *fe, node_t *hn, pointf *ps, size_t pn, splineInfo *info)
Definition splines.c:221
void gv_free_splines(edge_t *e)
Definition utils.c:1434
void resolvePorts(edge_t *e)
Definition shapes.c:4334
shape_kind shapeOf(node_t *)
Definition shapes.c:1909
void makeStraightEdge(graph_t *g, edge_t *e, int edgetype, splineInfo *info)
Definition routespl.c:954
void addEdgeLabels(edge_t *e)
Definition splines.c:1260
void makeSelfEdge(edge_t *edges[], size_t cnt, double sizex, double sizey, splineInfo *sinfo)
Definition splines.c:1129
graph or subgraph
Definition cgraph.h:424
Agraph_t * root
subgraphs - ancestors
Definition cgraph.h:433
Ppoint_t b
Definition pathgeom.h:53
Ppoint_t a
Definition pathgeom.h:53
size_t pn
Definition pathgeom.h:47
Ppoint_t * ps
Definition pathgeom.h:46
double x
Definition pathgeom.h:38
double y
Definition pathgeom.h:38
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
Definition cdt.h:98
node_t * n2
pointf p1
pointf p2
node_t * n1
edge_t * e
edgeinfo id
Dtlink_t link
double x
Definition adjust.h:42
double y
Definition adjust.h:42
bool doAdd
Definition adjust.h:43
boxf b
Definition types.h:237
Definition geom.h:31
pointf p
Definition geom.h:32
double m
Definition geom.h:33
double x
Definition geom.h:29
double y
Definition geom.h:29
bool(* swapEnds)(edge_t *e)
Definition types.h:67
struct poly_s poly
void(* edgefn)(Agraph_t *, Agedge_t *, glCompColor)
@ SH_EPSF
Definition types.h:187
@ SH_RECORD
Definition types.h:187
@ SH_POINT
Definition types.h:187
@ SH_POLY
Definition types.h:187
@ R_VALUE
Definition types.h:216
@ R_FILL
Definition types.h:216
@ R_EXPAND
Definition types.h:216
#define UNREACHABLE()
Definition unreachable.h:30
#define POLYID_NONE
Definition vispath.h:50