Graphviz 14.1.4~dev.20260320.0055
Loading...
Searching...
No Matches
ortho.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
12/* TODO:
13 * In dot, prefer bottom or top routing
14 * In general, prefer closest side to closest side routing.
15 * Edge labels
16 * Ports/compass points
17 * ordering attribute
18 * Weights on edges in nodes
19 * Edge concentrators?
20 */
21
22#include "config.h"
23
24#define DEBUG
25#include <assert.h>
26#include <float.h>
27#include <math.h>
28#include <stdbool.h>
29#include <stddef.h>
30#include <ortho/maze.h>
31#include <ortho/fPQ.h>
32#include <ortho/ortho.h>
33#include <common/geomprocs.h>
34#include <common/globals.h>
35#include <common/render.h>
36#include <common/pointset.h>
37#include <util/alloc.h>
38#include <util/exit.h>
39#include <util/gv_math.h>
40#include <util/list.h>
41#include <util/optional.h>
42#include <util/unused.h>
43
44typedef struct {
45 double d;
47} epair_t;
48
49static UNUSED void emitSearchGraph(FILE *fp, sgraph *sg);
50static UNUSED void emitGraph(FILE *fp, maze *mp, size_t n_edges,
51 route *route_list, epair_t[]);
52#ifdef DEBUG
54#endif
55
56#define CELL(n) ((cell*)ND_alg(n))
57
58static double MID(double a, double b) {
59 return (a + b) / 2.0;
60}
61
62/* cellOf:
63 * Given 2 snodes sharing a cell, return the cell.
64 */
65static cell*
67{
68 cell* cp = p->cells[0];
69 if (cp == q->cells[0] || cp == q->cells[1]) return cp;
70 return p->cells[1];
71}
72
73static pointf midPt(const cell *cp) {
74 return mid_pointf(cp->bb.LL, cp->bb.UR);
75}
76
77/* sidePt:
78 * Given a cell and an snode on one of its sides, return the
79 * midpoint of the side.
80 */
81static pointf sidePt(const snode ptr, const cell* cp) {
82 if (cp == ptr.cells[1]) {
83 if (ptr.isVert) {
84 return (pointf){.x = cp->bb.LL.x, .y = MID(cp->bb.LL.y, cp->bb.UR.y)};
85 }
86 return (pointf){.x = MID(cp->bb.LL.x, cp->bb.UR.x), .y = cp->bb.LL.y};
87 }
88 if (ptr.isVert) {
89 return (pointf){.x = cp->bb.UR.x, .y = MID(cp->bb.LL.y, cp->bb.UR.y)};
90 }
91 return (pointf){.x = MID(cp->bb.LL.x, cp->bb.UR.x), .y = cp->bb.UR.y};
92}
93
94/* setSet:
95 * Initialize and normalize segments.
96 * p1 stores smaller value
97 * Assume b1 != b2
98 */
99static segment setSeg(bool dir, double fix, double b1, double b2, int l1,
100 int l2) {
101 segment sp = {0};
102 sp.isVert = dir;
103 sp.comm_coord = fix;
104 if (b1 < b2) {
105 sp.p.p1 = b1;
106 sp.p.p2 = b2;
107 sp.l1 = l1;
108 sp.l2 = l2;
109 }
110 else {
111 sp.p.p2 = b1;
112 sp.p.p1 = b2;
113 sp.l2 = l1;
114 sp.l1 = l2;
115 }
116 return sp;
117}
118
119/* Convert route in shortest path graph to route
120 * of segments. This records the first and last cells,
121 * plus cells where the path bends.
122 * Note that the shortest path will always have at least 4 nodes:
123 * the two dummy nodes representing the center of the two real nodes,
124 * and the two nodes on the boundary of the two real nodes.
125 */
126static route
128{
129 snode* ptr;
130 snode* next;
131 snode* prev; /* node in shortest path just previous to next */
132 cell* cp;
133 cell* ncp;
134 double fix, b1, b2;
135 int l1, l2;
136 pointf bp1, prevbp = {0.0,0.0}; /* bend points */
137
138 LIST(segment) rte = {0};
139
140 ptr = prev = N_DAD(fst);
141 next = N_DAD(ptr);
142 if (IsNode(ptr->cells[0]))
143 cp = ptr->cells[1];
144 else
145 cp = ptr->cells[0];
146 bp1 = sidePt(*ptr, cp);
147 while (N_DAD(next)!=NULL) {
148 ncp = cellOf (prev, next);
149 updateWts (g, ncp, N_EDGE(ptr));
150
151 /* add seg if path bends or at end */
152 if (ptr->isVert != next->isVert || N_DAD(next) == lst) {
153 const pointf bp2 = ptr->isVert != next->isVert ? midPt (ncp) : sidePt(*next, ncp);
154 if (ptr->isVert) { /* horizontal segment */
155 if (ptr == N_DAD(fst)) l1 = B_NODE;
156 else if (prevbp.y > bp1.y) l1 = B_UP;
157 else l1 = B_DOWN;
158 if (ptr->isVert != next->isVert) {
159 if (next->cells[0] == ncp) l2 = B_UP;
160 else l2 = B_DOWN;
161 }
162 else l2 = B_NODE;
163 fix = cp->bb.LL.y;
164 b1 = cp->bb.LL.x;
165 b2 = ncp->bb.LL.x;
166 }
167 else { /* vertical segment */
168 if (ptr == N_DAD(fst)) l1 = B_NODE;
169 else if (prevbp.x > bp1.x) l1 = B_RIGHT;
170 else l1 = B_LEFT;
171 if (ptr->isVert != next->isVert) {
172 if (next->cells[0] == ncp) l2 = B_RIGHT;
173 else l2 = B_LEFT;
174 }
175 else l2 = B_NODE;
176 fix = cp->bb.LL.x;
177 b1 = cp->bb.LL.y;
178 b2 = ncp->bb.LL.y;
179 }
180 const segment seg = setSeg(!ptr->isVert, fix, b1, b2, l1, l2);
181 LIST_APPEND(&rte, seg);
182 cp = ncp;
183 prevbp = bp1;
184 bp1 = bp2;
185 if (ptr->isVert != next->isVert && N_DAD(next) == lst) {
186 l2 = B_NODE;
187 if (next->isVert) { /* horizontal segment */
188 if (prevbp.y > bp1.y) l1 = B_UP;
189 else l1 = B_DOWN;
190 fix = cp->bb.LL.y;
191 b1 = cp->bb.LL.x;
192 b2 = ncp->bb.LL.x;
193 }
194 else {
195 if (prevbp.x > bp1.x) l1 = B_RIGHT;
196 else l1 = B_LEFT;
197 fix = cp->bb.LL.x;
198 b1 = cp->bb.LL.y;
199 b2 = ncp->bb.LL.y;
200 }
201 const segment s = setSeg(!next->isVert, fix, b1, b2, l1, l2);
202 LIST_APPEND(&rte, s);
203 }
204 ptr = next;
205 }
206 prev = next;
207 next = N_DAD(next);
208 }
209
210 route ret = {0};
211 LIST_DETACH(&rte, &ret.segs, &ret.n);
212 for (size_t i = 0; i < ret.n; i++) {
213 if (i > 0)
214 ret.segs[i].prev = ret.segs + (i - 1);
215 if (i < ret.n - 1)
216 ret.segs[i].next = ret.segs + (i + 1);
217 }
218
219 return ret;
220}
221
222typedef struct {
224 double v;
226} chanItem;
227
228static void freeChannel(void *chan) {
229 channel *cp = chan;
230 free_graph (cp->G);
231 LIST_FREE(&cp->seg_list);
232 free (cp);
233}
234
235static void freeChanItem(void *item) {
236 chanItem *cp = item;
237 dtclose (cp->chans);
238 free (cp);
239}
240
241/* chancmpid:
242 * Compare intervals. Two intervals are equal if one contains
243 * the other. Otherwise, the one with the smaller p1 value is
244 * less.
245 * This combines two separate functions into one. Channels are
246 * disjoint, so we really only need to key on p1.
247 * When searching for a channel containing a segment, we rely on
248 * interval containment to return the correct channel.
249 */
250static int chancmpid(void *k1, void *k2) {
251 const paird *key1 = k1;
252 const paird *key2 = k2;
253 if (key1->p1 > key2->p1) {
254 if (key1->p2 <= key2->p2) return 0;
255 return 1;
256 }
257 if (key1->p1 < key2->p1) {
258 if (key1->p2 >= key2->p2) return 0;
259 return -1;
260 }
261 return 0;
262}
263
264static int dcmpid(void *k1, void *k2) {
265 const double *key1 = k1;
266 const double *key2 = k2;
267 return fcmp(*key1, *key2);
268}
269
271 offsetof(channel,p),
272 sizeof(paird),
273 offsetof(channel,link),
274 0,
276 chancmpid,
277};
278
280 offsetof(chanItem,v),
281 sizeof(double),
282 offsetof(chanItem,link),
283 0,
285 dcmpid,
286};
287
288static void
289addChan (Dt_t* chdict, channel* cp, double j)
290{
291 chanItem* subd = dtmatch (chdict, &j);
292
293 if (!subd) {
294 subd = gv_alloc(sizeof(chanItem));
295 subd->v = j;
296 subd->chans = dtopen (&chanDisc, Dtoset);
297 dtinsert (chdict, subd);
298 }
299 if (dtinsert(subd->chans, cp) != cp) {
300 free(cp);
301 }
302}
303
304static Dt_t*
306{
307 snode* np;
308 Dt_t* hchans = dtopen (&chanItemDisc, Dtoset);
309
310 for (size_t i = 0; i < mp->ncells; i++) {
311 channel* chp;
312 cell* cp = mp->cells+i;
313 cell* nextcp;
314 if (IsHScan(cp)) continue;
315
316 /* move left */
317 while ((np = cp->sides[M_LEFT]) && (nextcp = np->cells[0]) &&
318 !IsNode(nextcp)) {
319 cp = nextcp;
320 }
321
322 chp = gv_alloc(sizeof(channel));
323 chp->cp = cp;
324 chp->p.p1 = cp->bb.LL.x;
325
326 /* move right */
327 cp->flags |= MZ_HSCAN;
328 while ((np = cp->sides[M_RIGHT]) && (nextcp = np->cells[1]) &&
329 !IsNode(nextcp)) {
330 cp = nextcp;
331 cp->flags |= MZ_HSCAN;
332 }
333
334 chp->p.p2 = cp->bb.UR.x;
335 addChan (hchans, chp, chp->cp->bb.LL.y);
336 }
337 return hchans;
338}
339
340static Dt_t*
342{
343 snode* np;
344 Dt_t* vchans = dtopen (&chanItemDisc, Dtoset);
345
346 for (size_t i = 0; i < mp->ncells; i++) {
347 channel* chp;
348 cell* cp = mp->cells+i;
349 cell* nextcp;
350 if (IsVScan(cp)) continue;
351
352 /* move down */
353 while ((np = cp->sides[M_BOTTOM]) && (nextcp = np->cells[0]) &&
354 !IsNode(nextcp)) {
355 cp = nextcp;
356 }
357
358 chp = gv_alloc(sizeof(channel));
359 chp->cp = cp;
360 chp->p.p1 = cp->bb.LL.y;
361
362 /* move up */
363 cp->flags |= MZ_VSCAN;
364 while ((np = cp->sides[M_TOP]) && (nextcp = np->cells[1]) &&
365 !IsNode(nextcp)) {
366 cp = nextcp;
367 cp->flags |= MZ_VSCAN;
368 }
369
370 chp->p.p2 = cp->bb.UR.y;
371 addChan (vchans, chp, chp->cp->bb.LL.x);
372 }
373 return vchans;
374}
375
376static void
378{
379 OPTIONAL_SET(&seg->ind_no, LIST_SIZE(&chan->seg_list));
380 LIST_APPEND(&chan->seg_list, seg);
381}
382
383static channel*
384chanSearch (Dt_t* chans, segment* seg)
385{
386 channel* cp;
387 chanItem* chani = dtmatch (chans, &seg->comm_coord);
388 assert (chani);
389 cp = dtmatch (chani->chans, &seg->p);
390 assert (cp);
391 return cp;
392}
393
394static void
395assignSegs (size_t nrtes, route* route_list, maze* mp)
396{
397 channel* chan;
398
399 for (size_t i=0;i<nrtes;i++) {
400 route rte = route_list[i];
401 for (size_t j=0;j<rte.n;j++) {
402 segment* seg = rte.segs+j;
403 if (seg->isVert)
404 chan = chanSearch(mp->vchans, seg);
405 else
406 chan = chanSearch(mp->hchans, seg);
407 insertChan (chan, seg);
408 }
409 }
410}
411
412/* addLoop:
413 * Add two temporary nodes to sgraph corresponding to two ends of a loop at cell cp, i
414 * represented by dp and sp.
415 */
416static void
417addLoop (sgraph* sg, cell* cp, snode* dp, snode* sp)
418{
419 for (size_t i = 0; i < cp->nsides; i++) {
420 snode* onp = cp->sides[i];
421
422 if (onp->isVert) continue;
423 const bool onTop = onp->cells[0] == cp;
424 if (onTop)
425 createSEdge (sg, sp, onp, 0); /* FIX weight */
426 else
427 createSEdge (sg, dp, onp, 0); /* FIX weight */
428 }
429 sg->nnodes += 2;
430}
431
432/* addNodeEdges:
433 * Add temporary node to sgraph corresponding to cell cp, represented
434 * by np.
435 */
436static void
438{
439 for (size_t i = 0; i < cp->nsides; i++) {
440 snode* onp = cp->sides[i];
441
442 createSEdge (sg, np, onp, 0); /* FIX weight */
443 }
444 sg->nnodes++;
445#ifdef DEBUG
446 np->cells[0] = np->cells[1] = cp;
447#endif
448}
449
450static const char *bendToStr(bend b) {
451 switch (b) {
452 case B_NODE :
453 return "B_NODE";
454 case B_UP :
455 return "B_UP";
456 case B_LEFT :
457 return "B_LEFT";
458 case B_DOWN :
459 return "B_DOWN";
460 default:
461 assert(b == B_RIGHT);
462 break;
463 }
464 return "B_RIGHT";
465}
466
467static void putSeg (FILE* fp, segment* seg)
468{
469 if (seg->isVert)
470 fprintf (fp, "((%f,%f),(%f,%f)) %s %s", seg->comm_coord, seg->p.p1,
471 seg->comm_coord, seg->p.p2, bendToStr (seg->l1), bendToStr (seg->l2));
472 else
473 fprintf (fp, "((%f,%f),(%f,%f)) %s %s", seg->p.p1,seg->comm_coord,
474 seg->p.p2, seg->comm_coord, bendToStr (seg->l1), bendToStr (seg->l2));
475}
476
477static UNUSED void dumpChanG(channel *cp, double v) {
478 if (LIST_SIZE(&cp->seg_list) < 2) return;
479 fprintf (stderr, "channel %.0f (%f,%f)\n", v, cp->p.p1, cp->p.p2);
480 for (size_t k = 0; k < LIST_SIZE(&cp->seg_list); ++k) {
481 const adj_list_t adj = cp->G->vertices[k].adj_list;
482 if (LIST_IS_EMPTY(&adj)) continue;
483 putSeg(stderr, LIST_GET(&cp->seg_list, k));
484 fputs (" ->\n", stderr);
485 for (size_t i = 0; i < LIST_SIZE(&adj); ++i) {
486 fputs (" ", stderr);
487 putSeg(stderr, LIST_GET(&cp->seg_list, LIST_GET(&adj, i)));
488 fputs ("\n", stderr);
489 }
490 }
491}
492
493static void
495{
496 Dt_t* lp;
497 Dtlink_t* l1;
498 Dtlink_t* l2;
499 channel* cp;
500
501 for (l1 = dtflatten (chans); l1; l1 = dtlink(chans,l1)) {
502 lp = ((chanItem*)l1)->chans;
503 for (l2 = dtflatten (lp); l2; l2 = dtlink(lp,l2)) {
504 cp = (channel*)l2;
505 if (!LIST_IS_EMPTY(&cp->seg_list)) {
506#ifdef DEBUG
507 if (odb_flags & ODB_CHANG) dumpChanG (cp, ((chanItem*)l1)->v);
508#endif
509 top_sort (cp->G);
510 for (size_t k = 0; k < LIST_SIZE(&cp->seg_list); ++k)
511 OPTIONAL_SET(&LIST_GET(&cp->seg_list, k)->track_no,
512 cp->G->vertices[k].topsort_order + 1);
513 }
514 }
515 }
516}
517
518static void
520{
521 Dt_t* lp;
522 Dtlink_t* l1;
523 Dtlink_t* l2;
524 channel* cp;
525
526 for (l1 = dtflatten (chans); l1; l1 = dtlink(chans,l1)) {
527 lp = ((chanItem*)l1)->chans;
528 for (l2 = dtflatten (lp); l2; l2 = dtlink(lp,l2)) {
529 cp = (channel*)l2;
530 cp->G = make_graph(LIST_SIZE(&cp->seg_list));
531 }
532 }
533}
534
535static int
536eqEndSeg (bend S1l2, bend S2l2, bend T1, bend T2)
537{
538 if ((S1l2==T2 && S2l2!=T2) || (S1l2==B_NODE && S2l2==T1))
539 return 0;
540 else
541 return -1;
542}
543
544static int overlapSeg(const segment S1, const segment S2, bend T1, bend T2) {
545 if (S1.p.p2 < S2.p.p2) {
546 if (S1.l2 == T1 && S2.l1 == T2) return -1;
547 if (S1.l2 == T2 && S2.l1 == T1) return 1;
548 return 0;
549 }
550 if (S1.p.p2 > S2.p.p2) {
551 if (S2.l1 == T2 && S2.l2 == T2) return -1;
552 if (S2.l1 == T1 && S2.l2 == T1) return 1;
553 return 0;
554 }
555 if (S2.l1 == T2) return eqEndSeg(S1.l2, S2.l2, T1, T2);
556 return -1 * eqEndSeg(S2.l2, S1.l2, T1, T2);
557}
558
559static int
560ellSeg (bend S1l1, bend S1l2, bend T)
561{
562 if (S1l1 == T) {
563 if (S1l2== T) return -1;
564 return 0;
565 }
566 return 1;
567}
568
569static int segCmp(const segment S1, const segment S2, bend T1, bend T2) {
570 /* no overlap */
571 if (S1.p.p2 < S2.p.p1 || S1.p.p1 > S2.p.p2) return 0;
572 /* left endpoint of S2 inside S1 */
573 if (S1.p.p1 < S2.p.p1 && S2.p.p1 < S1.p.p2)
574 return overlapSeg(S1, S2, T1, T2);
575 /* left endpoint of S1 inside S2 */
576 if (S2.p.p1 < S1.p.p1 && S1.p.p1 < S2.p.p2)
577 return -1 * overlapSeg(S2, S1, T1, T2);
578 if (S1.p.p1 == S2.p.p1) {
579 if (S1.p.p2 < S2.p.p2) {
580 if (S1.l2 == T1)
581 return eqEndSeg(S2.l1, S1.l1, T1, T2);
582 return -1 * eqEndSeg(S2.l1, S1.l1, T1, T2);
583 }
584 if (S1.p.p2 > S2.p.p2) {
585 if (S2.l2 == T2)
586 return eqEndSeg(S1.l1, S2.l1, T1, T2);
587 return -1 * eqEndSeg(S1.l1, S2.l1, T1, T2);
588 }
589 if (S1.l1 == S2.l1 && S1.l2 == S2.l2)
590 return 0;
591 if (S2.l1 == S2.l2) {
592 if (S2.l1 == T1) return 1;
593 if (S2.l1 == T2) return -1;
594 if (S1.l1 != T1 && S1.l2 != T1) return 1;
595 if (S1.l1 != T2 && S1.l2 != T2) return -1;
596 return 0;
597 }
598 if (S2.l1 == T1 && S2.l2 == T2) {
599 if (S1.l1 != T1 && S1.l2 == T2) return 1;
600 if (S1.l1 == T1 && S1.l2 != T2) return -1;
601 return 0;
602 }
603 if (S2.l2 == T1 && S2.l1 == T2) {
604 if (S1.l2 != T1 && S1.l1 == T2) return 1;
605 if (S1.l2 == T1 && S1.l1 != T2) return -1;
606 return 0;
607 }
608 if (S2.l1 == B_NODE && S2.l2 == T1) {
609 return ellSeg(S1.l1, S1.l2, T1);
610 }
611 if (S2.l1 == B_NODE && S2.l2 == T2) {
612 return -1 * ellSeg(S1.l1, S1.l2, T2);
613 }
614 if (S2.l1 == T1 && S2.l2 == B_NODE) {
615 return ellSeg(S1.l2, S1.l1, T1);
616 }
617 // S2.l1 == T2 && S2.l2 == B_NODE
618 return -1 * ellSeg(S1.l2, S1.l1, T2);
619 }
620 if (S1.p.p2 == S2.p.p1) {
621 if (S1.l2 == S2.l1) return 0;
622 if (S1.l2 == T2) return 1;
623 return -1;
624 }
625 // S1.p.p1 == S2.p.p2
626 if (S1.l1 == S2.l2) return 0;
627 if (S1.l1 == T2) return 1;
628 return -1;
629}
630
631/* Function seg_cmp returns
632 * -1 if S1 HAS TO BE to the right/below S2 to avoid a crossing,
633 * 0 if a crossing is unavoidable or there is no crossing at all or
634 * the segments are parallel,
635 * 1 if S1 HAS TO BE to the left/above S2 to avoid a crossing
636 * -2 if S1 and S2 are incomparable
637 *
638 * Note: This definition means horizontal segments have track numbers
639 * increasing as y decreases, while vertical segments have track numbers
640 * increasing as x increases. It would be good to make this consistent,
641 * with horizontal track numbers increasing with y. This can be done by
642 * switching B_DOWN and B_UP in the first call to segCmp. At present,
643 * though, I'm not sure what assumptions are made in handling parallel
644 * segments, so we leave the code alone for the time being.
645 */
646static int seg_cmp(const segment S1, const segment S2) {
647 if (S1.isVert != S2.isVert || S1.comm_coord != S2.comm_coord) {
648 agerrorf("incomparable segments !! -- Aborting\n");
649 return -2;
650 }
651 if (S1.isVert)
652 return segCmp(S1, S2, B_RIGHT, B_LEFT);
653 return segCmp(S1, S2, B_DOWN, B_UP);
654}
655
656static int
658{
659 seg_list_t *seg_list = &cp->seg_list;
660 const size_t size = LIST_SIZE(&cp->seg_list);
661 rawgraph* G = cp->G;
662
663 for (size_t x = 0; x + 1 < size; ++x) {
664 for (size_t y = x + 1; y < size; ++y) {
665 const int cmp = seg_cmp(*LIST_GET(seg_list, x), *LIST_GET(seg_list, y));
666 if (cmp == -2) {
667 return -1;
668 } else if (cmp > 0) {
669 insert_edge(G,x,y);
670 } else if (cmp == -1) {
671 insert_edge(G,y,x);
672 }
673 }
674 }
675
676 return 0;
677}
678
679static int
681{
682 for (Dtlink_t *l1 = dtflatten(chans); l1; l1 = dtlink(chans, l1)) {
683 Dt_t *const lp = ((chanItem*)l1)->chans;
684 for (Dtlink_t *l2 = dtflatten(lp); l2; l2 = dtlink(lp, l2)) {
685 channel *const cp = (channel*)l2;
686 if (!LIST_IS_EMPTY(&cp->seg_list))
687 if (add_edges_in_G(cp)) {
688 return -1;
689 }
690 }
691 }
692
693 return 0;
694}
695
696static segment *next_seg(const segment seg, int dir) {
697 if (!dir)
698 return seg.prev;
699 return seg.next;
700}
701
702/* propagate_prec propagates the precedence relationship along
703 * a series of parallel segments on 2 edges
704 */
705static int propagate_prec(const segment seg, int prec, int hops, int dir) {
706 int x;
707 int ans=prec;
708
709 segment current = seg;
710 for(x=1;x<=hops;x++) {
711 const segment next = *next_seg(current, dir);
712 if (!current.isVert) {
713 if (next.comm_coord == current.p.p1) {
714 if (current.l1 == B_UP) ans *= -1;
715 }
716 else {
717 if (current.l2 == B_DOWN) ans *= -1;
718 }
719 }
720 else {
721 if (next.comm_coord == current.p.p1) {
722 if (current.l1 == B_RIGHT) ans *= -1;
723 }
724 else {
725 if (current.l2 == B_LEFT) ans *= -1;
726 }
727 }
728 current = next;
729 }
730 return ans;
731}
732
733static bool is_parallel(const segment s1, const segment s2) {
734 assert(s1.comm_coord == s2.comm_coord);
735 return s1.p.p1 == s2.p.p1 &&
736 s1.p.p2 == s2.p.p2 &&
737 s1.l1 == s2.l1 &&
738 s1.l2 == s2.l2;
739}
740
741/* decide_point returns (through ret) the number of hops needed in the given
742 * directions along the 2 edges to get to a deciding point (or NODES) and also
743 * puts into prec the appropriate dependency (follows same convention as
744 * seg_cmp)
745 */
746static int decide_point(pair *ret, segment si, segment sj, int dir1, int dir2) {
747 int prec = 0, ans = 0, temp;
748 segment* np1;
749 segment *np2 = NULL;
750
751 while ((np1 = next_seg(si, dir1)) && (np2 = next_seg(sj, dir2)) &&
752 is_parallel(*np1, *np2)) {
753 ans++;
754 si = *np1;
755 sj = *np2;
756 }
757 if (!np1)
758 prec = 0;
759 else if (!np2)
760 assert(0); /* FIXME */
761 else {
762 temp = seg_cmp(*np1, *np2);
763 if (temp == -2) {
764 return -1;
765 }
766 prec = propagate_prec(*np1, temp, ans+1, 1-dir1);
767 }
768
769 ret->a = ans;
770 ret->b = prec;
771 return 0;
772}
773
774/* sets the edges for a series of parallel segments along two edges starting
775 * from segment i, segment j. It is assumed that the edge should be from
776 * segment i to segment j - the dependency is appropriately propagated
777 */
778static void
779set_parallel_edges (segment* seg1, segment* seg2, int dir1, int dir2, int hops,
780 maze* mp)
781{
782 int x;
783 channel* chan;
784 channel* nchan;
785 segment* prev1;
786 segment* prev2;
787
788 if (seg1->isVert)
789 chan = chanSearch(mp->vchans, seg1);
790 else
791 chan = chanSearch(mp->hchans, seg1);
792 insert_edge(chan->G, OPTIONAL_VALUE(seg1->ind_no),
793 OPTIONAL_VALUE(seg2->ind_no));
794
795 for (x=1;x<=hops;x++) {
796 prev1 = next_seg(*seg1, dir1);
797 prev2 = next_seg(*seg2, dir2);
798 if(!seg1->isVert) {
799 nchan = chanSearch(mp->vchans, prev1);
800 if(prev1->comm_coord==seg1->p.p1) {
801 if(seg1->l1==B_UP) {
802 if (edge_exists(chan->G, OPTIONAL_VALUE(seg1->ind_no),
803 OPTIONAL_VALUE(seg2->ind_no)))
804 insert_edge(nchan->G, OPTIONAL_VALUE(prev2->ind_no),
805 OPTIONAL_VALUE(prev1->ind_no));
806 else
807 insert_edge(nchan->G, OPTIONAL_VALUE(prev1->ind_no),
808 OPTIONAL_VALUE(prev2->ind_no));
809 }
810 else {
811 if (edge_exists(chan->G, OPTIONAL_VALUE(seg1->ind_no),
812 OPTIONAL_VALUE(seg2->ind_no)))
813 insert_edge(nchan->G, OPTIONAL_VALUE(prev1->ind_no),
814 OPTIONAL_VALUE(prev2->ind_no));
815 else
816 insert_edge(nchan->G, OPTIONAL_VALUE(prev2->ind_no),
817 OPTIONAL_VALUE(prev1->ind_no));
818 }
819 }
820 else {
821 if(seg1->l2==B_UP) {
822 if (edge_exists(chan->G, OPTIONAL_VALUE(seg1->ind_no),
823 OPTIONAL_VALUE(seg2->ind_no)))
824 insert_edge(nchan->G, OPTIONAL_VALUE(prev1->ind_no),
825 OPTIONAL_VALUE(prev2->ind_no));
826 else
827 insert_edge(nchan->G, OPTIONAL_VALUE(prev2->ind_no),
828 OPTIONAL_VALUE(prev1->ind_no));
829 }
830 else {
831 if (edge_exists(chan->G, OPTIONAL_VALUE(seg1->ind_no),
832 OPTIONAL_VALUE(seg2->ind_no)))
833 insert_edge(nchan->G, OPTIONAL_VALUE(prev2->ind_no),
834 OPTIONAL_VALUE(prev1->ind_no));
835 else
836 insert_edge(nchan->G, OPTIONAL_VALUE(prev1->ind_no),
837 OPTIONAL_VALUE(prev2->ind_no));
838 }
839 }
840 }
841 else {
842 nchan = chanSearch(mp->hchans, prev1);
843 if(prev1->comm_coord==seg1->p.p1) {
844 if(seg1->l1==B_LEFT) {
845 if (edge_exists(chan->G, OPTIONAL_VALUE(seg1->ind_no),
846 OPTIONAL_VALUE(seg2->ind_no)))
847 insert_edge(nchan->G, OPTIONAL_VALUE(prev1->ind_no),
848 OPTIONAL_VALUE(prev2->ind_no));
849 else
850 insert_edge(nchan->G, OPTIONAL_VALUE(prev2->ind_no),
851 OPTIONAL_VALUE(prev1->ind_no));
852 }
853 else {
854 if (edge_exists(chan->G, OPTIONAL_VALUE(seg1->ind_no),
855 OPTIONAL_VALUE(seg2->ind_no)))
856 insert_edge(nchan->G, OPTIONAL_VALUE(prev2->ind_no),
857 OPTIONAL_VALUE(prev1->ind_no));
858 else
859 insert_edge(nchan->G, OPTIONAL_VALUE(prev1->ind_no),
860 OPTIONAL_VALUE(prev2->ind_no));
861 }
862 }
863 else {
864 if(seg1->l2==B_LEFT) {
865 if (edge_exists(chan->G, OPTIONAL_VALUE(seg1->ind_no),
866 OPTIONAL_VALUE(seg2->ind_no)))
867 insert_edge(nchan->G, OPTIONAL_VALUE(prev2->ind_no),
868 OPTIONAL_VALUE(prev1->ind_no));
869 else
870 insert_edge(nchan->G, OPTIONAL_VALUE(prev1->ind_no),
871 OPTIONAL_VALUE(prev2->ind_no));
872 }
873 else {
874 if (edge_exists(chan->G, OPTIONAL_VALUE(seg1->ind_no),
875 OPTIONAL_VALUE(seg2->ind_no)))
876 insert_edge(nchan->G, OPTIONAL_VALUE(prev1->ind_no),
877 OPTIONAL_VALUE(prev2->ind_no));
878 else
879 insert_edge(nchan->G, OPTIONAL_VALUE(prev2->ind_no),
880 OPTIONAL_VALUE(prev1->ind_no));
881 }
882 }
883 }
884 chan = nchan;
885 seg1 = prev1;
886 seg2 = prev2;
887 }
888}
889
890/* removes the edge between segments after the resolution of a conflict
891 */
892static void
893removeEdge(segment* seg1, segment* seg2, int dir, maze* mp)
894{
895 segment* ptr1;
896 segment* ptr2;
897 channel* chan;
898
899 ptr1 = seg1;
900 ptr2 = seg2;
901 while (is_parallel(*ptr1, *ptr2)) {
902 ptr1 = next_seg(*ptr1, 1);
903 ptr2 = next_seg(*ptr2, dir);
904 }
905 if(ptr1->isVert)
906 chan = chanSearch(mp->vchans, ptr1);
907 else
908 chan = chanSearch(mp->hchans, ptr1);
909 remove_redge(chan->G, OPTIONAL_VALUE(ptr1->ind_no),
910 OPTIONAL_VALUE(ptr2->ind_no));
911}
912
913static int
915{
916 /* dir[1,2] are used to figure out whether we should use prev
917 * pointers or next pointers -- 0 : decrease, 1 : increase
918 */
919 int dir;
920 /* number of hops along the route to get to the deciding points */
921 pair hops;
922 /* precedences of the deciding points : same convention as
923 * seg_cmp function
924 */
925 int prec1, prec2;
926 pair p;
927 rawgraph* G = cp->G;
928 seg_list_t *segs = &cp->seg_list;
929
930 for(size_t i = 0; i + 1 < LIST_SIZE(&cp->seg_list); ++i) {
931 for(size_t j = i + 1; j < LIST_SIZE(&cp->seg_list); ++j) {
932 if (!edge_exists(G,i,j) && !edge_exists(G,j,i)) {
933 if (is_parallel(*LIST_GET(segs, i), *LIST_GET(segs, j))) {
934 /* get_directions */
935 if (LIST_GET(segs, i)->prev == 0) {
936 if (LIST_GET(segs, j)->prev == 0)
937 dir = 0;
938 else
939 dir = 1;
940 }
941 else if (LIST_GET(segs, j)->prev == 0) {
942 dir = 1;
943 }
944 else {
945 if (LIST_GET(segs, i)->prev->comm_coord ==
946 LIST_GET(segs, j)->prev->comm_coord)
947 dir = 0;
948 else
949 dir = 1;
950 }
951
952 if (decide_point(&p, *LIST_GET(segs, i), *LIST_GET(segs, j), 0, dir)
953 != 0) {
954 return -1;
955 }
956 hops.a = p.a;
957 prec1 = p.b;
958 if (decide_point(&p, *LIST_GET(segs, i), *LIST_GET(segs, j), 1, 1 - dir)
959 != 0) {
960 return -1;
961 }
962 hops.b = p.a;
963 prec2 = p.b;
964
965 if (prec1 == -1) {
966 set_parallel_edges(LIST_GET(segs, j), LIST_GET(segs, i), dir, 0,
967 hops.a, mp);
968 set_parallel_edges(LIST_GET(segs, j), LIST_GET(segs, i), 1 - dir, 1,
969 hops.b, mp);
970 if(prec2==1)
971 removeEdge(LIST_GET(segs, i), LIST_GET(segs, j), 1 - dir, mp);
972 } else if (prec1 == 0) {
973 if (prec2 == -1) {
974 set_parallel_edges(LIST_GET(segs, j), LIST_GET(segs, i), dir, 0,
975 hops.a, mp);
976 set_parallel_edges(LIST_GET(segs, j), LIST_GET(segs, i), 1 - dir,
977 1, hops.b, mp);
978 } else if (prec2 == 0) {
979 set_parallel_edges(LIST_GET(segs, i), LIST_GET(segs, j), 0, dir,
980 hops.a, mp);
981 set_parallel_edges(LIST_GET(segs, i), LIST_GET(segs, j), 1,
982 1 - dir, hops.b, mp);
983 } else if (prec2 == 1) {
984 set_parallel_edges(LIST_GET(segs, i), LIST_GET(segs, j), 0, dir,
985 hops.a, mp);
986 set_parallel_edges(LIST_GET(segs, i), LIST_GET(segs, j), 1,
987 1 - dir, hops.b, mp);
988 }
989 } else if (prec1 == 1) {
990 set_parallel_edges(LIST_GET(segs, i), LIST_GET(segs, j), 0, dir,
991 hops.a, mp);
992 set_parallel_edges(LIST_GET(segs, i), LIST_GET(segs, j), 1, 1 - dir,
993 hops.b, mp);
994 if(prec2==-1)
995 removeEdge(LIST_GET(segs, i), LIST_GET(segs, j), 1 - dir, mp);
996 }
997 }
998 }
999 }
1000 }
1001
1002 return 0;
1003}
1004
1005static int
1006add_p_edges (Dt_t* chans, maze* mp)
1007{
1008 for (Dtlink_t *l1 = dtflatten(chans); l1; l1 = dtlink(chans, l1)) {
1009 Dt_t *const lp = ((chanItem*)l1)->chans;
1010 for (Dtlink_t *l2 = dtflatten(lp); l2; l2 = dtlink(lp, l2)) {
1011 if (addPEdges((channel*)l2, mp) != 0) {
1012 return -1;
1013 }
1014 }
1015 }
1016
1017 return 0;
1018}
1019
1020static int
1022{
1023 /* Create the graphs for each channel */
1024 create_graphs(mp->hchans);
1025 create_graphs(mp->vchans);
1026
1027 /* add edges between non-parallel segments */
1028 if (add_np_edges(mp->hchans) != 0) {
1029 return -1;
1030 }
1031 if (add_np_edges(mp->vchans) != 0) {
1032 return -1;
1033 }
1034
1035 /* add edges between parallel segments + remove appropriate edges */
1036 if (add_p_edges(mp->hchans, mp) != 0) {
1037 return -1;
1038 }
1039 if (add_p_edges(mp->vchans, mp) != 0) {
1040 return -1;
1041 }
1042
1043 /* Assign the tracks after a top sort */
1044 assignTrackNo (mp->hchans);
1045 assignTrackNo (mp->vchans);
1046
1047 return 0;
1048}
1049
1050static double
1052{
1053 channel* chp = chanSearch(m->vchans, seg);
1054 const double f =
1055 OPTIONAL_VALUE(seg->track_no) / ((double)LIST_SIZE(&chp->seg_list) + 1);
1056 const pointf interp = interpolate_pointf(f, chp->cp->bb.LL, chp->cp->bb.UR);
1057 return interp.x;
1058}
1059
1060static double htrack(segment *seg, maze *m) {
1061 channel* chp = chanSearch(m->hchans, seg);
1062 double f = 1.0 - OPTIONAL_VALUE(seg->track_no) /
1063 ((double)LIST_SIZE(&chp->seg_list) + 1);
1064 double lo = chp->cp->bb.LL.y;
1065 double hi = chp->cp->bb.UR.y;
1066 return round(lo + f * (hi - lo));
1067}
1068
1069static void attachOrthoEdges(maze *mp, size_t n_edges, route* route_list,
1070 splineInfo *sinfo, epair_t es[], bool doLbls) {
1071 LIST(pointf) ispline = {0};
1072 textlabel_t* lbl;
1073
1074 for (size_t irte = 0; irte < n_edges; irte++) {
1075 Agedge_t *const e = es[irte].e;
1076 const pointf p1 = add_pointf(ND_coord(agtail(e)), ED_tail_port(e).p);
1077 const pointf q1 = add_pointf(ND_coord(aghead(e)), ED_head_port(e).p);
1078
1079 route rte = route_list[irte];
1080 size_t npts = 1 + 3*rte.n;
1081 LIST_RESERVE(&ispline, npts);
1082
1083 segment *seg = rte.segs;
1084 if (seg == NULL) {
1085 continue;
1086 }
1087 pointf p;
1088 if (seg->isVert) {
1089 p = (pointf){.x = vtrack(seg, mp), .y = p1.y};
1090 }
1091 else {
1092 p = (pointf){.x = p1.x, .y = htrack(seg, mp)};
1093 }
1094 LIST_APPEND(&ispline, p);
1095 LIST_APPEND(&ispline, p);
1096
1097 for (size_t i = 1;i<rte.n;i++) {
1098 seg = rte.segs+i;
1099 if (seg->isVert)
1100 p.x = vtrack(seg, mp);
1101 else
1102 p.y = htrack(seg, mp);
1103 LIST_APPEND(&ispline, p);
1104 LIST_APPEND(&ispline, p);
1105 LIST_APPEND(&ispline, p);
1106 }
1107
1108 if (seg->isVert) {
1109 p = (pointf){.x = vtrack(seg, mp), .y = q1.y};
1110 }
1111 else {
1112 p = (pointf){.x = q1.x, .y = htrack(seg, mp)};
1113 }
1114 LIST_APPEND(&ispline, p);
1115 LIST_APPEND(&ispline, p);
1116 if (Verbose > 1)
1117 fprintf(stderr, "ortho %s %s\n", agnameof(agtail(e)),agnameof(aghead(e)));
1118 clip_and_install(e, aghead(e), LIST_FRONT(&ispline), LIST_SIZE(&ispline),
1119 sinfo);
1120 if (doLbls && (lbl = ED_label(e)) && !lbl->set)
1121 addEdgeLabels(e);
1122 LIST_CLEAR(&ispline);
1123 }
1124 LIST_FREE(&ispline);
1125}
1126
1127static double edgeLen(Agedge_t *e) {
1128 pointf p = ND_coord(agtail(e));
1129 pointf q = ND_coord(aghead(e));
1130 return DIST2(p, q);
1131}
1132
1133static int edgecmp(const void *x, const void *y) {
1134 const epair_t *e0 = x;
1135 const epair_t *e1 = y;
1136 if (e0->d > e1->d) {
1137 return 1;
1138 }
1139 if (e0->d < e1->d) {
1140 return -1;
1141 }
1142 return 0;
1143}
1144
1145static bool spline_merge(node_t * n)
1146{
1147 (void)n;
1148 return false;
1149}
1150
1151static bool swap_ends_p(edge_t * e)
1152{
1153 (void)e;
1154 return false;
1155}
1156
1157/* orthoEdges:
1158 * For edges without position information, construct an orthogonal routing.
1159 * If useLbls is true, use edge label info when available to guide routing,
1160 * and set label pos for those edges for which this info is not available.
1161 */
1162void orthoEdges(Agraph_t *g, bool useLbls) {
1163 epair_t* es = gv_calloc(agnedges(g), sizeof(epair_t));
1164 PointSet* ps = NULL;
1165 textlabel_t* lbl;
1166
1167 if (Concentrate)
1168 ps = newPS();
1169
1170#ifdef DEBUG
1171 {
1172 char* s = agget(g, "odb");
1173 char c;
1174 odb_flags = 0;
1175 if (s && *s != '\0') {
1176 while ((c = *s++)) {
1177 switch (c) {
1178 case 'c' :
1179 odb_flags |= ODB_CHANG; // emit channel graph
1180 break;
1181 case 'i' :
1182 odb_flags |= (ODB_SGRAPH|ODB_IGRAPH); // emit search graphs
1183 break;
1184 case 'm' :
1185 odb_flags |= ODB_MAZE; // emit maze
1186 break;
1187 case 'r' :
1188 odb_flags |= ODB_ROUTE; // emit routes in maze
1189 break;
1190 case 's' :
1191 odb_flags |= ODB_SGRAPH; // emit search graph
1192 break;
1193 default:
1194 break;
1195 }
1196 }
1197 }
1198 }
1199#endif
1200 if (useLbls) {
1201 agwarningf("Orthogonal edges do not currently handle edge labels. Try using xlabels.\n");
1202 useLbls = false;
1203 }
1204 maze *const mp = mkMaze(g);
1205 sgraph *const sg = mp->sg;
1206#ifdef DEBUG
1207 if (odb_flags & ODB_SGRAPH) emitSearchGraph (stderr, sg);
1208#endif
1209
1210 /* store edges to be routed in es, along with their lengths */
1211 size_t n_edges = 0;
1212 for (Agnode_t *n = agfstnode (g); n; n = agnxtnode(g, n)) {
1213 for (Agedge_t *e = agfstout(g, n); e; e = agnxtout(g,e)) {
1214 if (Nop == 2 && ED_spl(e)) continue;
1215 if (Concentrate) {
1216 int ti = AGSEQ(agtail(e));
1217 int hi = AGSEQ(aghead(e));
1218 if (ti <= hi) {
1219 if (isInPS (ps,ti,hi)) continue;
1220 addPS(ps,ti,hi);
1221 }
1222 else {
1223 if (isInPS (ps,hi,ti)) continue;
1224 addPS(ps,hi,ti);
1225 }
1226 }
1227 es[n_edges].e = e;
1228 es[n_edges].d = edgeLen (e);
1229 n_edges++;
1230 }
1231 }
1232
1233 route *const route_list = gv_calloc(n_edges, sizeof(route));
1234
1235 qsort(es, n_edges, sizeof(epair_t), edgecmp);
1236
1237 const int gstart = sg->nnodes;
1238 pq_t *const pq = PQgen(sg->nnodes + 2);
1239 snode *const sn = &sg->nodes[gstart];
1240 snode *const dn = &sg->nodes[gstart+1];
1241 for (size_t i = 0; i < n_edges; i++) {
1242#ifdef DEBUG
1243 if (i > 0 && (odb_flags & ODB_IGRAPH)) emitSearchGraph (stderr, sg);
1244#endif
1245 Agedge_t *const e = es[i].e;
1246 cell *const start = CELL(agtail(e));
1247 cell *const dest = CELL(aghead(e));
1248
1249 if (useLbls && (lbl = ED_label(e)) && lbl->set) {
1250 }
1251 else {
1252 if (start == dest)
1253 addLoop (sg, start, dn, sn);
1254 else {
1255 addNodeEdges (sg, dest, dn);
1256 addNodeEdges (sg, start, sn);
1257 }
1258 if (shortPath(pq, sg, dn, sn)) {
1259 PQfree(pq);
1260 goto orthofinish;
1261 }
1262 }
1263
1264 route_list[i] = convertSPtoRoute(sg, sn, dn);
1265 reset (sg);
1266 }
1267 PQfree(pq);
1268
1269 mp->hchans = extractHChans (mp);
1270 mp->vchans = extractVChans (mp);
1271 assignSegs (n_edges, route_list, mp);
1272 if (assignTracks(mp) != 0)
1273 goto orthofinish;
1274#ifdef DEBUG
1275 if (odb_flags & ODB_ROUTE) emitGraph (stderr, mp, n_edges, route_list, es);
1276#endif
1277 splineInfo sinfo = {swap_ends_p, spline_merge, true, true};
1278 attachOrthoEdges(mp, n_edges, route_list, &sinfo, es, useLbls);
1279
1280orthofinish:
1281 if (Concentrate)
1282 freePS (ps);
1283
1284 for (size_t i=0; i < n_edges; i++)
1285 free (route_list[i].segs);
1286 free (route_list);
1287 freeMaze (mp);
1288 free (es);
1289}
1290
1291#include <common/arith.h>
1292#define TRANS 10
1293
1294static const char prolog2[] =
1295"%%!PS-Adobe-2.0\n\
1296%%%%BoundingBox: (atend)\n\
1297/point {\n\
1298 /Y exch def\n\
1299 /X exch def\n\
1300 newpath\n\
1301 X Y 3 0 360 arc fill\n\
1302} def\n\
1303/cell {\n\
1304 /Y exch def\n\
1305 /X exch def\n\
1306 /y exch def\n\
1307 /x exch def\n\
1308 newpath\n\
1309 x y moveto\n\
1310 x Y lineto\n\
1311 X Y lineto\n\
1312 X y lineto\n\
1313 closepath stroke\n\
1314} def\n\
1315/node {\n\
1316 /u exch def\n\
1317 /r exch def\n\
1318 /d exch def\n\
1319 /l exch def\n\
1320 newpath l d moveto\n\
1321 r d lineto r u lineto l u lineto\n\
1322 closepath fill\n\
1323} def\n\
1324\n";
1325
1326static pointf coordOf(cell *cp, snode *np) {
1327 if (cp->sides[M_TOP] == np) {
1328 return (pointf){.x = (cp->bb.LL.x + cp->bb.UR.x) / 2, .y = cp->bb.UR.y};
1329 }
1330 if (cp->sides[M_BOTTOM] == np) {
1331 return (pointf){.x = (cp->bb.LL.x + cp->bb.UR.x) / 2, .y = cp->bb.LL.y};
1332 }
1333 if (cp->sides[M_LEFT] == np) {
1334 return (pointf){.x = cp->bb.LL.x, .y = (cp->bb.LL.y + cp->bb.UR.y) / 2};
1335 }
1336 if (cp->sides[M_RIGHT] == np) {
1337 return (pointf){.x = cp->bb.UR.x, .y = (cp->bb.LL.y + cp->bb.UR.y) / 2};
1338 }
1339 agerrorf("Node not adjacent to cell -- Aborting\n");
1340 graphviz_exit(EXIT_FAILURE);
1341}
1342
1343static boxf
1344emitEdge (FILE* fp, Agedge_t* e, route rte, maze* m, boxf bb)
1345{
1346 double x, y;
1347 boxf n = CELL(agtail(e))->bb;
1348 segment* seg = rte.segs;
1349 if (seg->isVert) {
1350 x = vtrack(seg, m);
1351 y = (n.UR.y + n.LL.y)/2;
1352 }
1353 else {
1354 y = htrack(seg, m);
1355 x = (n.UR.x + n.LL.x)/2;
1356 }
1357 bb.LL.x = fmin(bb.LL.x, x);
1358 bb.LL.y = fmin(bb.LL.y, y);
1359 bb.UR.x = fmax(bb.UR.x, x);
1360 bb.UR.y = fmax(bb.UR.y, y);
1361 fprintf(fp, "newpath %.0f %.0f moveto\n", x, y);
1362
1363 for (size_t i = 1;i<rte.n;i++) {
1364 seg = rte.segs+i;
1365 if (seg->isVert) {
1366 x = vtrack(seg, m);
1367 }
1368 else {
1369 y = htrack(seg, m);
1370 }
1371 bb.LL.x = fmin(bb.LL.x, x);
1372 bb.LL.y = fmin(bb.LL.y, y);
1373 bb.UR.x = fmax(bb.UR.x, x);
1374 bb.UR.y = fmax(bb.UR.y, y);
1375 fprintf(fp, "%.0f %.0f lineto\n", x, y);
1376 }
1377
1378 n = CELL(aghead(e))->bb;
1379 if (seg->isVert) {
1380 x = vtrack(seg, m);
1381 y = (n.UR.y + n.LL.y)/2;
1382 }
1383 else {
1384 y = htrack(seg, m);
1385 x = (n.LL.x + n.UR.x)/2;
1386 }
1387 bb.LL.x = fmin(bb.LL.x, x);
1388 bb.LL.y = fmin(bb.LL.y, y);
1389 bb.UR.x = fmax(bb.UR.x, x);
1390 bb.UR.y = fmax(bb.UR.y, y);
1391 fprintf(fp, "%.0f %.0f lineto stroke\n", x, y);
1392
1393 return bb;
1394}
1395
1406static UNUSED void emitSearchGraph(FILE *fp, sgraph *sg) {
1407 pointf p;
1408 fputs ("graph G {\n", fp);
1409 fputs (" node[shape=point]\n", fp);
1410 fputs (" layout=neato\n", fp);
1411 for (int i = 0; i < sg->nnodes; i++) {
1412 snode *const np = sg->nodes+i;
1413 cell *cp = np->cells[0];
1414 if (cp == np->cells[1]) {
1415 p = midPt(cp);
1416 }
1417 else {
1418 if (IsNode(cp)) cp = np->cells[1];
1419 p = coordOf (cp, np);
1420 }
1421 fprintf (fp, " %d [pos=\"%.0f,%.0f!\"]\n", i, p.x, p.y);
1422 }
1423 for (int i = 0; i < sg->nedges; i++) {
1424 sedge *const ep = sg->edges+i;
1425 fprintf (fp, " %d -- %d[label=\"%f\"]\n", ep->v1, ep->v2, ep->weight);
1426 }
1427 fputs ("}\n", fp);
1428}
1429
1430static UNUSED void emitGraph(FILE *fp, maze *mp, size_t n_edges,
1431 route *route_list, epair_t es[]) {
1432 boxf absbb = {.LL = {.x = DBL_MAX, .y = DBL_MAX},
1433 .UR = {.x = -DBL_MAX, .y = -DBL_MAX}};
1434
1435 fputs(prolog2, fp);
1436 fprintf (fp, "%d %d translate\n", TRANS, TRANS);
1437
1438 fputs ("0 0 1 setrgbcolor\n", fp);
1439 for (size_t i = 0; i < mp->ngcells; i++) {
1440 const boxf bb = mp->gcells[i].bb;
1441 fprintf (fp, "%f %f %f %f node\n", bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y);
1442 }
1443
1444 for (size_t i = 0; i < n_edges; i++) {
1445 absbb = emitEdge (fp, es[i].e, route_list[i], mp, absbb);
1446 }
1447
1448 fputs ("0.8 0.8 0.8 setrgbcolor\n", fp);
1449 for (size_t i = 0; i < mp->ncells; i++) {
1450 const boxf bb = mp->cells[i].bb;
1451 fprintf (fp, "%f %f %f %f cell\n", bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y);
1452 absbb.LL.x = fmin(absbb.LL.x, bb.LL.x);
1453 absbb.LL.y = fmin(absbb.LL.y, bb.LL.y);
1454 absbb.UR.x = fmax(absbb.UR.x, bb.UR.x);
1455 absbb.UR.y = fmax(absbb.UR.y, bb.UR.y);
1456 }
1457
1458 const boxf bbox = {
1459 .LL = {.x = absbb.LL.x + TRANS,
1460 .y = absbb.LL.y + TRANS},
1461 .UR = {.x = absbb.UR.x + TRANS,
1462 .y = absbb.UR.y + TRANS}};
1463 fprintf(fp, "showpage\n%%%%Trailer\n%%%%BoundingBox: %.f %.f %.f %.f\n",
1464 bbox.LL.x, bbox.LL.y, bbox.UR.x, bbox.UR.y);
1465}
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
CDT_API Dtlink_t * dtflatten(Dt_t *)
Definition dtflatten.c:12
#define dtmatch(d, o)
Definition cdt.h:185
#define dtlink(d, e)
Definition cdt.h:176
#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
static splineInfo sinfo
Definition dotsplines.c:125
static NORETURN void graphviz_exit(int status)
Definition exit.h:23
pq_t * PQgen(int sz)
Definition fPQ.c:26
void PQfree(pq_t *pq)
Definition fPQ.c:35
static int cmp(const void *key, const void *candidate)
#define G
Definition gdefs.h:7
struct pointf_s pointf
#define DIST2(p, q)
Definition geom.h:55
geometric functions (e.g. on points and boxes)
static WUR pointf mid_pointf(pointf p, pointf q)
Definition geomprocs.h:104
static WUR pointf add_pointf(pointf p, pointf q)
Definition geomprocs.h:88
static WUR pointf interpolate_pointf(double t, pointf p, pointf q)
Definition geomprocs.h:112
bool Concentrate
Definition globals.h:59
int Nop
Definition globals.h:55
static bool Verbose
Definition gml2gv.c:26
static void free_graph(gmlgraph *p)
Definition gmlparse.c:120
void free(void *)
node NULL
Definition grammar.y:181
int agnedges(Agraph_t *g)
Definition graph.c:163
char * agget(void *obj, char *name)
Definition attr.c:447
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 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
void agerrorf(const char *fmt,...)
Definition agerror.c:167
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_coord(n)
Definition types.h:490
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:145
#define AGSEQ(obj)
Definition cgraph.h:225
Arithmetic helper functions.
static int fcmp(double a, double b)
comparator for doubles
Definition gv_math.h:15
$2 prev
Definition htmlparse.y:291
type-generic dynamically expanding list
#define LIST_DETACH(list, datap, sizep)
Definition list.h:443
#define LIST(type)
Definition list.h:55
#define LIST_SIZE(list)
Definition list.h:80
#define LIST_CLEAR(list)
Definition list.h:240
#define LIST_APPEND(list, item)
Definition list.h:120
#define LIST_FREE(list)
Definition list.h:370
#define LIST_RESERVE(list, capacity)
Definition list.h:257
#define LIST_FRONT(list)
Definition list.h:180
#define LIST_IS_EMPTY(list)
Definition list.h:90
#define LIST_GET(list, index)
Definition list.h:155
maze * mkMaze(graph_t *g)
creates maze and fills maze::gcells and maze::cells. A subroutine of orthoEdges.
Definition maze.c:452
void updateWts(sgraph *g, cell *cp, sedge *ep)
updates sedge::weight of cell edges
Definition maze.c:171
void freeMaze(maze *mp)
Definition maze.c:504
makes maze with mkMaze for routing orthogonal edges
@ M_TOP
Definition maze.h:21
@ M_RIGHT
Definition maze.h:21
@ M_BOTTOM
Definition maze.h:21
@ M_LEFT
Definition maze.h:21
#define MZ_VSCAN
Definition maze.h:24
#define IsHScan(cp)
cell already inserted in horizontal channel
Definition maze.h:34
#define IsVScan(cp)
cell already inserted in vertical channel
Definition maze.h:32
#define IsNode(cp)
cell corresponds to node
Definition maze.h:30
#define MZ_HSCAN
Definition maze.h:25
#define N_DAD(n)
static boxf bbox(Ppoly_t **obsp, int npoly, int *np)
NEATOPROCS_API void s1(graph_t *, node_t *)
Definition stuff.c:651
C analog of C++’s std::optional
#define OPTIONAL_VALUE(me)
Definition optional.h:39
#define OPTIONAL_SET(me, value)
Definition optional.h:26
snode priority queue for shortPath in sgraph
#define N_EDGE(n)
Definition fPQ.h:24
static segment setSeg(bool dir, double fix, double b1, double b2, int l1, int l2)
Definition ortho.c:99
static pointf sidePt(const snode ptr, const cell *cp)
Definition ortho.c:81
static route convertSPtoRoute(sgraph *g, snode *fst, snode *lst)
Definition ortho.c:127
static Dt_t * extractVChans(maze *mp)
Definition ortho.c:341
static int add_edges_in_G(channel *cp)
Definition ortho.c:657
static int decide_point(pair *ret, segment si, segment sj, int dir1, int dir2)
Definition ortho.c:746
static int add_p_edges(Dt_t *chans, maze *mp)
Definition ortho.c:1006
static void removeEdge(segment *seg1, segment *seg2, int dir, maze *mp)
Definition ortho.c:893
static int eqEndSeg(bend S1l2, bend S2l2, bend T1, bend T2)
Definition ortho.c:536
static const char prolog2[]
Definition ortho.c:1294
static double htrack(segment *seg, maze *m)
Definition ortho.c:1060
static pointf coordOf(cell *cp, snode *np)
Definition ortho.c:1326
static UNUSED void emitGraph(FILE *fp, maze *mp, size_t n_edges, route *route_list, epair_t[])
Definition ortho.c:1430
static void putSeg(FILE *fp, segment *seg)
Definition ortho.c:467
static UNUSED void emitSearchGraph(FILE *fp, sgraph *sg)
dumps in dot format snode::cells and edges of sgraph for debugging
Definition ortho.c:1406
static int overlapSeg(const segment S1, const segment S2, bend T1, bend T2)
Definition ortho.c:544
#define CELL(n)
Definition ortho.c:56
static bool swap_ends_p(edge_t *e)
Definition ortho.c:1151
static int propagate_prec(const segment seg, int prec, int hops, int dir)
Definition ortho.c:705
static void set_parallel_edges(segment *seg1, segment *seg2, int dir1, int dir2, int hops, maze *mp)
Definition ortho.c:779
static bool spline_merge(node_t *n)
Definition ortho.c:1145
static int addPEdges(channel *cp, maze *mp)
Definition ortho.c:914
static double MID(double a, double b)
Definition ortho.c:58
static UNUSED void dumpChanG(channel *cp, double v)
Definition ortho.c:477
static void assignSegs(size_t nrtes, route *route_list, maze *mp)
Definition ortho.c:395
static cell * cellOf(snode *p, snode *q)
Definition ortho.c:66
static void create_graphs(Dt_t *chans)
Definition ortho.c:519
static void addChan(Dt_t *chdict, channel *cp, double j)
Definition ortho.c:289
static pointf midPt(const cell *cp)
Definition ortho.c:73
static int add_np_edges(Dt_t *chans)
Definition ortho.c:680
static void insertChan(channel *chan, segment *seg)
Definition ortho.c:377
static double vtrack(segment *seg, maze *m)
Definition ortho.c:1051
static Dt_t * extractHChans(maze *mp)
Definition ortho.c:305
static void freeChannel(void *chan)
Definition ortho.c:228
static void assignTrackNo(Dt_t *chans)
Definition ortho.c:494
static void attachOrthoEdges(maze *mp, size_t n_edges, route *route_list, splineInfo *sinfo, epair_t es[], bool doLbls)
Definition ortho.c:1069
void orthoEdges(Agraph_t *g, bool useLbls)
Definition ortho.c:1162
static bool is_parallel(const segment s1, const segment s2)
Definition ortho.c:733
static int dcmpid(void *k1, void *k2)
Definition ortho.c:264
static int ellSeg(bend S1l1, bend S1l2, bend T)
Definition ortho.c:560
#define TRANS
Definition ortho.c:1292
static void freeChanItem(void *item)
Definition ortho.c:235
static int chancmpid(void *k1, void *k2)
Definition ortho.c:250
static void addNodeEdges(sgraph *sg, cell *cp, snode *np)
Definition ortho.c:437
static int assignTracks(maze *mp)
Definition ortho.c:1021
static int seg_cmp(const segment S1, const segment S2)
Definition ortho.c:646
static const char * bendToStr(bend b)
Definition ortho.c:450
static Dtdisc_t chanDisc
Definition ortho.c:270
static channel * chanSearch(Dt_t *chans, segment *seg)
Definition ortho.c:384
static segment * next_seg(const segment seg, int dir)
Definition ortho.c:696
static void addLoop(sgraph *sg, cell *cp, snode *dp, snode *sp)
Definition ortho.c:417
static int edgecmp(const void *x, const void *y)
Definition ortho.c:1133
static double edgeLen(Agedge_t *e)
Definition ortho.c:1127
static int segCmp(const segment S1, const segment S2, bend T1, bend T2)
Definition ortho.c:569
int odb_flags
Definition ortho.c:53
static boxf emitEdge(FILE *fp, Agedge_t *e, route rte, maze *m, boxf bb)
Definition ortho.c:1344
static Dtdisc_t chanItemDisc
Definition ortho.c:279
void addPS(PointSet *ps, double x, double y)
Definition pointset.c:88
PointSet * newPS(void)
Definition pointset.c:70
void freePS(PointSet *ps)
Definition pointset.c:75
int isInPS(PointSet *ps, double x, double y)
Definition pointset.c:101
point containers PointSet and PointMap
rawgraph * make_graph(size_t n)
makes a graph with n vertices, 0 edges
Definition rawgraph.c:22
void insert_edge(rawgraph *g, size_t v1, size_t v2)
inserts edge FROM v1 to v2
Definition rawgraph.c:41
void remove_redge(rawgraph *g, size_t v1, size_t v2)
removes any edge between v1 to v2 – irrespective of direction
Definition rawgraph.c:47
void top_sort(rawgraph *g)
Definition rawgraph.c:77
bool edge_exists(rawgraph *g, size_t v1, size_t v2)
tests if there is an edge FROM v1 TO v2
Definition rawgraph.c:52
void clip_and_install(edge_t *fe, node_t *hn, pointf *ps, size_t pn, splineInfo *info)
Definition splines.c:236
void addEdgeLabels(edge_t *e)
Definition splines.c:1307
int shortPath(pq_t *pq, sgraph *g, snode *from, snode *to)
Definition sgraph.c:138
sedge * createSEdge(sgraph *g, snode *v1, snode *v2, double wt)
Definition sgraph.c:80
void reset(sgraph *G)
Definition sgraph.c:29
graph or subgraph
Definition cgraph.h:424
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
result of partitioning available space, part of maze
Definition grid.h:33
int flags
Definition maze.h:43
size_t nsides
Definition maze.h:54
boxf bb
Definition maze.h:56
snode ** sides
up to four sides: M_RIGHT, M_TOP, M_LEFT, M_BOTTOM
Definition maze.h:55
Dt_t * chans
Definition ortho.c:225
double v
Definition ortho.c:224
Dtlink_t link
Definition ortho.c:223
struct cell * cp
Definition structures.h:61
seg_list_t seg_list
segment pointers
Definition structures.h:59
paird p
Definition structures.h:58
rawgraph * G
Definition structures.h:60
Definition cdt.h:98
double d
Definition ortho.c:45
Agedge_t * e
Definition ortho.c:46
Definition utils.c:753
available channels for orthogonal edges around nodes of graph_t
Definition maze.h:66
cell * cells
cells not corresponding to graph nodes
Definition maze.h:69
cell * gcells
cells corresponding to graph nodes
Definition maze.h:70
size_t ncells
Definition maze.h:67
Dt_t * vchans
set of vertical channels, created by extractVChans
Definition maze.h:73
size_t ngcells
Definition maze.h:68
Dt_t * hchans
set of horizontal channels, created by extractHChans.
Definition maze.h:72
sgraph * sg
search graph
Definition maze.h:71
int a
Definition structures.h:26
int b
Definition structures.h:26
double p1
Definition structures.h:22
double p2
Definition structures.h:22
double x
Definition geom.h:29
double y
Definition geom.h:29
Definition heap.c:20
vertex * vertices
Definition rawgraph.h:27
segment * segs
Definition structures.h:51
size_t n
Definition structures.h:50
Definition sgraph.h:42
double weight
Definition sgraph.h:43
int v2
Definition sgraph.h:48
int v1
Definition sgraph.h:48
paird p
Definition structures.h:41
double comm_coord
Definition structures.h:40
struct segment * next
Definition structures.h:46
bend l2
Definition structures.h:42
bend l1
Definition structures.h:42
bool isVert
Definition structures.h:39
struct segment * prev
Definition structures.h:45
int nedges
Definition sgraph.h:52
sedge * edges
Definition sgraph.h:55
int nnodes
Definition sgraph.h:52
snode * nodes
Definition sgraph.h:54
a node of search graph sgraph, is created as a border segment between two adjusted cells of type cell...
Definition sgraph.h:26
bool isVert
Definition sgraph.h:39
struct cell * cells[2]
[0] - left or botom, [1] - top or right adjusted cell
Definition sgraph.h:32
bool set
Definition types.h:123
bend
Definition structures.h:33
@ B_LEFT
Definition structures.h:33
@ B_RIGHT
Definition structures.h:33
@ B_DOWN
Definition structures.h:33
@ B_UP
Definition structures.h:33
@ B_NODE
Definition structures.h:33
int topsort_order
Definition rawgraph.h:21
adj_list_t adj_list
adjacency list
Definition rawgraph.h:22
static clock_t T
Definition timing.c:19
Definition grammar.c:90
abstraction for squashing compiler warnings for unused symbols
#define UNUSED
Definition unused.h:25