Graphviz 14.1.6~dev.20260420.0039
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[]) {
1071 LIST(pointf) ispline = {0};
1072
1073 for (size_t irte = 0; irte < n_edges; irte++) {
1074 Agedge_t *const e = es[irte].e;
1075 const pointf p1 = add_pointf(ND_coord(agtail(e)), ED_tail_port(e).p);
1076 const pointf q1 = add_pointf(ND_coord(aghead(e)), ED_head_port(e).p);
1077
1078 route rte = route_list[irte];
1079 size_t npts = 1 + 3*rte.n;
1080 LIST_RESERVE(&ispline, npts);
1081
1082 segment *seg = rte.segs;
1083 if (seg == NULL) {
1084 continue;
1085 }
1086 pointf p;
1087 if (seg->isVert) {
1088 p = (pointf){.x = vtrack(seg, mp), .y = p1.y};
1089 }
1090 else {
1091 p = (pointf){.x = p1.x, .y = htrack(seg, mp)};
1092 }
1093 LIST_APPEND(&ispline, p);
1094 LIST_APPEND(&ispline, p);
1095
1096 for (size_t i = 1;i<rte.n;i++) {
1097 seg = rte.segs+i;
1098 if (seg->isVert)
1099 p.x = vtrack(seg, mp);
1100 else
1101 p.y = htrack(seg, mp);
1102 LIST_APPEND(&ispline, p);
1103 LIST_APPEND(&ispline, p);
1104 LIST_APPEND(&ispline, p);
1105 }
1106
1107 if (seg->isVert) {
1108 p = (pointf){.x = vtrack(seg, mp), .y = q1.y};
1109 }
1110 else {
1111 p = (pointf){.x = q1.x, .y = htrack(seg, mp)};
1112 }
1113 LIST_APPEND(&ispline, p);
1114 LIST_APPEND(&ispline, p);
1115 if (Verbose > 1)
1116 fprintf(stderr, "ortho %s %s\n", agnameof(agtail(e)),agnameof(aghead(e)));
1117 clip_and_install(e, aghead(e), LIST_FRONT(&ispline), LIST_SIZE(&ispline),
1118 sinfo);
1119 LIST_CLEAR(&ispline);
1120 }
1121 LIST_FREE(&ispline);
1122}
1123
1124static double edgeLen(Agedge_t *e) {
1125 pointf p = ND_coord(agtail(e));
1126 pointf q = ND_coord(aghead(e));
1127 return DIST2(p, q);
1128}
1129
1130static int edgecmp(const void *x, const void *y) {
1131 const epair_t *e0 = x;
1132 const epair_t *e1 = y;
1133 if (e0->d > e1->d) {
1134 return 1;
1135 }
1136 if (e0->d < e1->d) {
1137 return -1;
1138 }
1139 return 0;
1140}
1141
1142static bool spline_merge(node_t * n)
1143{
1144 (void)n;
1145 return false;
1146}
1147
1148static bool swap_ends_p(edge_t * e)
1149{
1150 (void)e;
1151 return false;
1152}
1153
1154/* orthoEdges:
1155 * For edges without position information, construct an orthogonal routing.
1156 * If useLbls is true, use edge label info when available to guide routing,
1157 * and set label pos for those edges for which this info is not available.
1158 */
1159void orthoEdges(Agraph_t *g, bool useLbls) {
1160 epair_t* es = gv_calloc(agnedges(g), sizeof(epair_t));
1161 PointSet* ps = NULL;
1162
1163 if (Concentrate)
1164 ps = newPS();
1165
1166#ifdef DEBUG
1167 {
1168 char* s = agget(g, "odb");
1169 char c;
1170 odb_flags = 0;
1171 if (s && *s != '\0') {
1172 while ((c = *s++)) {
1173 switch (c) {
1174 case 'c' :
1175 odb_flags |= ODB_CHANG; // emit channel graph
1176 break;
1177 case 'i' :
1178 odb_flags |= (ODB_SGRAPH|ODB_IGRAPH); // emit search graphs
1179 break;
1180 case 'm' :
1181 odb_flags |= ODB_MAZE; // emit maze
1182 break;
1183 case 'r' :
1184 odb_flags |= ODB_ROUTE; // emit routes in maze
1185 break;
1186 case 's' :
1187 odb_flags |= ODB_SGRAPH; // emit search graph
1188 break;
1189 default:
1190 break;
1191 }
1192 }
1193 }
1194 }
1195#endif
1196 if (useLbls) {
1197 agwarningf("Orthogonal edges do not currently handle edge labels. Try using xlabels.\n");
1198 useLbls = false;
1199 }
1200 maze *const mp = mkMaze(g);
1201 sgraph *const sg = mp->sg;
1202#ifdef DEBUG
1203 if (odb_flags & ODB_SGRAPH) emitSearchGraph (stderr, sg);
1204#endif
1205
1206 /* store edges to be routed in es, along with their lengths */
1207 size_t n_edges = 0;
1208 for (Agnode_t *n = agfstnode (g); n; n = agnxtnode(g, n)) {
1209 for (Agedge_t *e = agfstout(g, n); e; e = agnxtout(g,e)) {
1210 if (Nop == 2 && ED_spl(e)) continue;
1211 if (Concentrate) {
1212 int ti = AGSEQ(agtail(e));
1213 int hi = AGSEQ(aghead(e));
1214 if (ti <= hi) {
1215 if (isInPS (ps,ti,hi)) continue;
1216 addPS(ps,ti,hi);
1217 }
1218 else {
1219 if (isInPS (ps,hi,ti)) continue;
1220 addPS(ps,hi,ti);
1221 }
1222 }
1223 es[n_edges].e = e;
1224 es[n_edges].d = edgeLen (e);
1225 n_edges++;
1226 }
1227 }
1228
1229 route *const route_list = gv_calloc(n_edges, sizeof(route));
1230
1231 qsort(es, n_edges, sizeof(epair_t), edgecmp);
1232
1233 const int gstart = sg->nnodes;
1234 pq_t *const pq = PQgen(sg->nnodes + 2);
1235 snode *const sn = &sg->nodes[gstart];
1236 snode *const dn = &sg->nodes[gstart+1];
1237 for (size_t i = 0; i < n_edges; i++) {
1238#ifdef DEBUG
1239 if (i > 0 && (odb_flags & ODB_IGRAPH)) emitSearchGraph (stderr, sg);
1240#endif
1241 Agedge_t *const e = es[i].e;
1242 cell *const start = CELL(agtail(e));
1243 cell *const dest = CELL(aghead(e));
1244
1245 if (start == dest)
1246 addLoop (sg, start, dn, sn);
1247 else {
1248 addNodeEdges (sg, dest, dn);
1249 addNodeEdges (sg, start, sn);
1250 }
1251 if (shortPath(pq, sg, dn, sn)) {
1252 PQfree(pq);
1253 goto orthofinish;
1254 }
1255
1256 route_list[i] = convertSPtoRoute(sg, sn, dn);
1257 reset (sg);
1258 }
1259 PQfree(pq);
1260
1261 mp->hchans = extractHChans (mp);
1262 mp->vchans = extractVChans (mp);
1263 assignSegs (n_edges, route_list, mp);
1264 if (assignTracks(mp) != 0)
1265 goto orthofinish;
1266#ifdef DEBUG
1267 if (odb_flags & ODB_ROUTE) emitGraph (stderr, mp, n_edges, route_list, es);
1268#endif
1269 splineInfo sinfo = {swap_ends_p, spline_merge, true, true};
1270 attachOrthoEdges(mp, n_edges, route_list, &sinfo, es);
1271
1272orthofinish:
1273 if (Concentrate)
1274 freePS (ps);
1275
1276 for (size_t i=0; i < n_edges; i++)
1277 free (route_list[i].segs);
1278 free (route_list);
1279 freeMaze (mp);
1280 free (es);
1281}
1282
1283#include <common/arith.h>
1284#define TRANS 10
1285
1286static const char prolog2[] =
1287"%%!PS-Adobe-2.0\n\
1288%%%%BoundingBox: (atend)\n\
1289/point {\n\
1290 /Y exch def\n\
1291 /X exch def\n\
1292 newpath\n\
1293 X Y 3 0 360 arc fill\n\
1294} def\n\
1295/cell {\n\
1296 /Y exch def\n\
1297 /X exch def\n\
1298 /y exch def\n\
1299 /x exch def\n\
1300 newpath\n\
1301 x y moveto\n\
1302 x Y lineto\n\
1303 X Y lineto\n\
1304 X y lineto\n\
1305 closepath stroke\n\
1306} def\n\
1307/node {\n\
1308 /u exch def\n\
1309 /r exch def\n\
1310 /d exch def\n\
1311 /l exch def\n\
1312 newpath l d moveto\n\
1313 r d lineto r u lineto l u lineto\n\
1314 closepath fill\n\
1315} def\n\
1316\n";
1317
1318static pointf coordOf(cell *cp, snode *np) {
1319 if (cp->sides[M_TOP] == np) {
1320 return (pointf){.x = (cp->bb.LL.x + cp->bb.UR.x) / 2, .y = cp->bb.UR.y};
1321 }
1322 if (cp->sides[M_BOTTOM] == np) {
1323 return (pointf){.x = (cp->bb.LL.x + cp->bb.UR.x) / 2, .y = cp->bb.LL.y};
1324 }
1325 if (cp->sides[M_LEFT] == np) {
1326 return (pointf){.x = cp->bb.LL.x, .y = (cp->bb.LL.y + cp->bb.UR.y) / 2};
1327 }
1328 if (cp->sides[M_RIGHT] == np) {
1329 return (pointf){.x = cp->bb.UR.x, .y = (cp->bb.LL.y + cp->bb.UR.y) / 2};
1330 }
1331 agerrorf("Node not adjacent to cell -- Aborting\n");
1332 graphviz_exit(EXIT_FAILURE);
1333}
1334
1335static boxf
1336emitEdge (FILE* fp, Agedge_t* e, route rte, maze* m, boxf bb)
1337{
1338 double x, y;
1339 boxf n = CELL(agtail(e))->bb;
1340 segment* seg = rte.segs;
1341 if (seg->isVert) {
1342 x = vtrack(seg, m);
1343 y = (n.UR.y + n.LL.y)/2;
1344 }
1345 else {
1346 y = htrack(seg, m);
1347 x = (n.UR.x + n.LL.x)/2;
1348 }
1349 bb.LL.x = fmin(bb.LL.x, x);
1350 bb.LL.y = fmin(bb.LL.y, y);
1351 bb.UR.x = fmax(bb.UR.x, x);
1352 bb.UR.y = fmax(bb.UR.y, y);
1353 fprintf(fp, "newpath %.0f %.0f moveto\n", x, y);
1354
1355 for (size_t i = 1;i<rte.n;i++) {
1356 seg = rte.segs+i;
1357 if (seg->isVert) {
1358 x = vtrack(seg, m);
1359 }
1360 else {
1361 y = htrack(seg, m);
1362 }
1363 bb.LL.x = fmin(bb.LL.x, x);
1364 bb.LL.y = fmin(bb.LL.y, y);
1365 bb.UR.x = fmax(bb.UR.x, x);
1366 bb.UR.y = fmax(bb.UR.y, y);
1367 fprintf(fp, "%.0f %.0f lineto\n", x, y);
1368 }
1369
1370 n = CELL(aghead(e))->bb;
1371 if (seg->isVert) {
1372 x = vtrack(seg, m);
1373 y = (n.UR.y + n.LL.y)/2;
1374 }
1375 else {
1376 y = htrack(seg, m);
1377 x = (n.LL.x + n.UR.x)/2;
1378 }
1379 bb.LL.x = fmin(bb.LL.x, x);
1380 bb.LL.y = fmin(bb.LL.y, y);
1381 bb.UR.x = fmax(bb.UR.x, x);
1382 bb.UR.y = fmax(bb.UR.y, y);
1383 fprintf(fp, "%.0f %.0f lineto stroke\n", x, y);
1384
1385 return bb;
1386}
1387
1398static UNUSED void emitSearchGraph(FILE *fp, sgraph *sg) {
1399 pointf p;
1400 fputs ("graph G {\n", fp);
1401 fputs (" node[shape=point]\n", fp);
1402 fputs (" layout=neato\n", fp);
1403 for (int i = 0; i < sg->nnodes; i++) {
1404 snode *const np = sg->nodes+i;
1405 cell *cp = np->cells[0];
1406 if (cp == np->cells[1]) {
1407 p = midPt(cp);
1408 }
1409 else {
1410 if (IsNode(cp)) cp = np->cells[1];
1411 p = coordOf (cp, np);
1412 }
1413 fprintf (fp, " %d [pos=\"%.0f,%.0f!\"]\n", i, p.x, p.y);
1414 }
1415 for (int i = 0; i < sg->nedges; i++) {
1416 sedge *const ep = sg->edges+i;
1417 fprintf (fp, " %d -- %d[label=\"%f\"]\n", ep->v1, ep->v2, ep->weight);
1418 }
1419 fputs ("}\n", fp);
1420}
1421
1422static UNUSED void emitGraph(FILE *fp, maze *mp, size_t n_edges,
1423 route *route_list, epair_t es[]) {
1424 boxf absbb = {.LL = {.x = DBL_MAX, .y = DBL_MAX},
1425 .UR = {.x = -DBL_MAX, .y = -DBL_MAX}};
1426
1427 fputs(prolog2, fp);
1428 fprintf (fp, "%d %d translate\n", TRANS, TRANS);
1429
1430 fputs ("0 0 1 setrgbcolor\n", fp);
1431 for (size_t i = 0; i < mp->ngcells; i++) {
1432 const boxf bb = mp->gcells[i].bb;
1433 fprintf (fp, "%f %f %f %f node\n", bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y);
1434 }
1435
1436 for (size_t i = 0; i < n_edges; i++) {
1437 absbb = emitEdge (fp, es[i].e, route_list[i], mp, absbb);
1438 }
1439
1440 fputs ("0.8 0.8 0.8 setrgbcolor\n", fp);
1441 for (size_t i = 0; i < mp->ncells; i++) {
1442 const boxf bb = mp->cells[i].bb;
1443 fprintf (fp, "%f %f %f %f cell\n", bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y);
1444 absbb.LL.x = fmin(absbb.LL.x, bb.LL.x);
1445 absbb.LL.y = fmin(absbb.LL.y, bb.LL.y);
1446 absbb.UR.x = fmax(absbb.UR.x, bb.UR.x);
1447 absbb.UR.y = fmax(absbb.UR.y, bb.UR.y);
1448 }
1449
1450 const boxf bbox = {
1451 .LL = {.x = absbb.LL.x + TRANS,
1452 .y = absbb.LL.y + TRANS},
1453 .UR = {.x = absbb.UR.x + TRANS,
1454 .y = absbb.UR.y + TRANS}};
1455 fprintf(fp, "showpage\n%%%%Trailer\n%%%%BoundingBox: %.f %.f %.f %.f\n",
1456 bbox.LL.x, bbox.LL.y, bbox.UR.x, bbox.UR.y);
1457}
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:165
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_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:450
#define LIST_APPEND(list,...)
Definition list.h:124
#define LIST(type)
Definition list.h:55
#define LIST_SIZE(list)
Definition list.h:80
#define LIST_CLEAR(list)
Definition list.h:244
#define LIST_FREE(list)
Definition list.h:373
#define LIST_RESERVE(list, capacity)
Definition list.h:261
#define LIST_FRONT(list)
Definition list.h:184
#define LIST_IS_EMPTY(list)
Definition list.h:90
#define LIST_GET(list, index)
Definition list.h:159
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:1286
static double htrack(segment *seg, maze *m)
Definition ortho.c:1060
static pointf coordOf(cell *cp, snode *np)
Definition ortho.c:1318
static UNUSED void emitGraph(FILE *fp, maze *mp, size_t n_edges, route *route_list, epair_t[])
Definition ortho.c:1422
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:1398
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:1148
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:1142
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
void orthoEdges(Agraph_t *g, bool useLbls)
Definition ortho.c:1159
static bool is_parallel(const segment s1, const segment s2)
Definition ortho.c:733
static void attachOrthoEdges(maze *mp, size_t n_edges, route *route_list, splineInfo *sinfo, epair_t es[])
Definition ortho.c:1069
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:1284
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:1130
static double edgeLen(Agedge_t *e)
Definition ortho.c:1124
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:1336
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
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:752
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
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